1
0
mirror of https://github.com/samba-team/samba.git synced 2025-02-01 05:47:28 +03:00
samba-mirror/source3/lib/sysquotas.c
Uri Simchoni 465c3d9a4e smbd quotas: avoid stat of foreign file systems
When determining the block device of our file system, avoid
stat'ing paths which are definitely not the mount point of
our file system. This is done to avoid stalling smbd due to
unresponsive network file systems (e.g. NFS) which are not
related to the SMB shares.

See discussion in samba-technical for vfs_fileid:
https://lists.samba.org/archive/samba-technical/2016-January/111553.html

Signed-off-by: Uri Simchoni <uri@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>

Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Wed Jan 27 03:35:48 CET 2016 on sn-devel-144
2016-01-27 03:35:47 +01:00

579 lines
13 KiB
C

/*
Unix SMB/CIFS implementation.
System QUOTA function wrappers
Copyright (C) Stefan (metze) Metzmacher 2003
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_QUOTA
#ifdef HAVE_SYS_QUOTAS
#if defined(HAVE_QUOTACTL_4A)
/*#endif HAVE_QUOTACTL_4A */
#elif defined(HAVE_QUOTACTL_4B)
/*#endif HAVE_QUOTACTL_4B */
#elif defined(HAVE_QUOTACTL_3)
#error HAVE_QUOTACTL_3 not implemented
/* #endif HAVE_QUOTACTL_3 */
#else /* NO_QUOTACTL_USED */
#endif /* NO_QUOTACTL_USED */
#if defined(HAVE_MNTENT) && defined(HAVE_REALPATH)
static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
{
int ret = -1;
SMB_STRUCT_STAT S;
FILE *fp;
struct mntent *mnt = NULL;
SMB_DEV_T devno;
char *stat_mntpath = NULL;
char *p;
/* find the block device file */
(*mntpath) = NULL;
(*bdev) = NULL;
(*fs) = NULL;
if (sys_stat(path, &S, false) != 0) {
return -1;
}
devno = S.st_ex_dev ;
stat_mntpath = sys_realpath(path);
if (stat_mntpath == NULL) {
DBG_WARNING("realpath(%s) failed - %s\n", path,
strerror(errno));
goto out;
}
if (sys_stat(stat_mntpath, &S, false) != 0) {
DBG_WARNING("cannot stat real path %s - %s\n", stat_mntpath,
strerror(errno));
goto out;
}
if (S.st_ex_dev != devno) {
DBG_WARNING("device on real path has changed\n");
goto out;
}
while (true) {
p = strrchr(stat_mntpath, '/');
if (p == NULL) {
DBG_ERR("realpath for %s does not begin with a '/'\n",
path);
goto out;
}
if (p == stat_mntpath) {
++p;
}
*p = 0;
if (sys_stat(stat_mntpath, &S, false) != 0) {
DBG_WARNING("cannot stat real path component %s - %s\n",
stat_mntpath, strerror(errno));
goto out;
}
if (S.st_ex_dev != devno) {
*p = '/';
break;
}
if (p <= stat_mntpath + 1) {
break;
}
}
fp = setmntent(MOUNTED,"r");
if (fp == NULL) {
goto out;
}
while ((mnt = getmntent(fp))) {
if (!strequal(mnt->mnt_dir, stat_mntpath)) {
continue;
}
if ( sys_stat(mnt->mnt_dir, &S, false) == -1 )
continue ;
if (S.st_ex_dev == devno) {
(*mntpath) = SMB_STRDUP(mnt->mnt_dir);
(*bdev) = SMB_STRDUP(mnt->mnt_fsname);
(*fs) = SMB_STRDUP(mnt->mnt_type);
if ((*mntpath)&&(*bdev)&&(*fs)) {
ret = 0;
} else {
SAFE_FREE(*mntpath);
SAFE_FREE(*bdev);
SAFE_FREE(*fs);
ret = -1;
}
break;
}
}
endmntent(fp) ;
out:
SAFE_FREE(stat_mntpath);
return ret;
}
/* #endif HAVE_MNTENT */
#elif defined(HAVE_DEVNM)
/* we have this on HPUX, ... */
static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
{
int ret = -1;
char dev_disk[256];
SMB_STRUCT_STAT S;
if (!path||!mntpath||!bdev||!fs)
smb_panic("sys_path_to_bdev: called with NULL pointer");
(*mntpath) = NULL;
(*bdev) = NULL;
(*fs) = NULL;
/* find the block device file */
if ((ret=sys_stat(path, &S, false))!=0) {
return ret;
}
if ((ret=devnm(S_IFBLK, S.st_ex_dev, dev_disk, 256, 1))!=0) {
return ret;
}
/* we should get the mntpath right...
* but I don't know how
* --metze
*/
(*mntpath) = SMB_STRDUP(path);
(*bdev) = SMB_STRDUP(dev_disk);
if ((*mntpath)&&(*bdev)) {
ret = 0;
} else {
SAFE_FREE(*mntpath);
SAFE_FREE(*bdev);
ret = -1;
}
return ret;
}
/* #endif HAVE_DEVNM */
#else
/* we should fake this up...*/
static int sys_path_to_bdev(const char *path, char **mntpath, char **bdev, char **fs)
{
int ret = -1;
if (!path||!mntpath||!bdev||!fs)
smb_panic("sys_path_to_bdev: called with NULL pointer");
(*mntpath) = NULL;
(*bdev) = NULL;
(*fs) = NULL;
(*mntpath) = SMB_STRDUP(path);
if (*mntpath) {
ret = 0;
} else {
SAFE_FREE(*mntpath);
ret = -1;
}
return ret;
}
#endif
/*********************************************************************
Now the list of all filesystem specific quota systems we have found
**********************************************************************/
static struct {
const char *name;
int (*get_quota)(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp);
int (*set_quota)(const char *path, const char *bdev, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp);
} sys_quota_backends[] = {
#if defined HAVE_XFS_QUOTAS
{"xfs", sys_get_xfs_quota, sys_set_xfs_quota},
{"gfs", sys_get_xfs_quota, sys_set_xfs_quota},
{"gfs2", sys_get_xfs_quota, sys_set_xfs_quota},
#endif /* HAVE_XFS_QUOTAS */
#ifdef HAVE_NFS_QUOTAS
{"nfs", sys_get_nfs_quota, sys_set_nfs_quota},
{"nfs4", sys_get_nfs_quota, sys_set_nfs_quota},
#endif /* HAVE_NFS_QUOTAS */
{NULL, NULL, NULL}
};
static int command_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
{
const char *get_quota_command;
char **lines = NULL;
get_quota_command = lp_get_quota_command(talloc_tos());
if (get_quota_command && *get_quota_command) {
const char *p;
char *p2;
char *syscmd = NULL;
int _id = -1;
switch(qtype) {
case SMB_USER_QUOTA_TYPE:
case SMB_USER_FS_QUOTA_TYPE:
_id = id.uid;
break;
case SMB_GROUP_QUOTA_TYPE:
case SMB_GROUP_FS_QUOTA_TYPE:
_id = id.gid;
break;
default:
DEBUG(0,("invalid quota type.\n"));
return -1;
}
if (asprintf(&syscmd, "%s %s %d %d",
get_quota_command, path, qtype, _id) < 0) {
return -1;
}
DEBUG (3, ("get_quota: Running command %s\n", syscmd));
lines = file_lines_pload(syscmd, NULL);
SAFE_FREE(syscmd);
if (lines) {
char *line = lines[0];
DEBUG (3, ("Read output from get_quota, \"%s\"\n", line));
/* we need to deal with long long unsigned here, if supported */
dp->qflags = strtoul(line, &p2, 10);
p = p2;
while (p && *p && isspace(*p)) {
p++;
}
if (p && *p) {
dp->curblocks = STR_TO_SMB_BIG_UINT(p, &p);
} else {
goto invalid_param;
}
while (p && *p && isspace(*p)) {
p++;
}
if (p && *p) {
dp->softlimit = STR_TO_SMB_BIG_UINT(p, &p);
} else {
goto invalid_param;
}
while (p && *p && isspace(*p)) {
p++;
}
if (p && *p) {
dp->hardlimit = STR_TO_SMB_BIG_UINT(p, &p);
} else {
goto invalid_param;
}
while (p && *p && isspace(*p)) {
p++;
}
if (p && *p) {
dp->curinodes = STR_TO_SMB_BIG_UINT(p, &p);
} else {
goto invalid_param;
}
while (p && *p && isspace(*p)) {
p++;
}
if (p && *p) {
dp->isoftlimit = STR_TO_SMB_BIG_UINT(p, &p);
} else {
goto invalid_param;
}
while (p && *p && isspace(*p)) {
p++;
}
if (p && *p) {
dp->ihardlimit = STR_TO_SMB_BIG_UINT(p, &p);
} else {
goto invalid_param;
}
while (p && *p && isspace(*p)) {
p++;
}
if (p && *p) {
dp->bsize = STR_TO_SMB_BIG_UINT(p, NULL);
} else {
dp->bsize = 1024;
}
TALLOC_FREE(lines);
lines = NULL;
DEBUG (3, ("Parsed output of get_quota, ...\n"));
DEBUGADD (5,(
"qflags:%u curblocks:%llu softlimit:%llu hardlimit:%llu\n"
"curinodes:%llu isoftlimit:%llu ihardlimit:%llu bsize:%llu\n",
dp->qflags,(long long unsigned)dp->curblocks,
(long long unsigned)dp->softlimit,(long long unsigned)dp->hardlimit,
(long long unsigned)dp->curinodes,
(long long unsigned)dp->isoftlimit,(long long unsigned)dp->ihardlimit,
(long long unsigned)dp->bsize));
return 0;
}
DEBUG (0, ("get_quota_command failed!\n"));
return -1;
}
errno = ENOSYS;
return -1;
invalid_param:
TALLOC_FREE(lines);
DEBUG(0,("The output of get_quota_command is invalid!\n"));
return -1;
}
static int command_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
{
const char *set_quota_command;
set_quota_command = lp_set_quota_command(talloc_tos());
if (set_quota_command && *set_quota_command) {
char **lines = NULL;
char *syscmd = NULL;
int _id = -1;
switch(qtype) {
case SMB_USER_QUOTA_TYPE:
case SMB_USER_FS_QUOTA_TYPE:
_id = id.uid;
break;
case SMB_GROUP_QUOTA_TYPE:
case SMB_GROUP_FS_QUOTA_TYPE:
_id = id.gid;
break;
default:
return -1;
}
if (asprintf(&syscmd,
"%s %s %d %d "
"%u %llu %llu "
"%llu %llu %llu ",
set_quota_command, path, qtype, _id, dp->qflags,
(long long unsigned)dp->softlimit,(long long unsigned)dp->hardlimit,
(long long unsigned)dp->isoftlimit,(long long unsigned)dp->ihardlimit,
(long long unsigned)dp->bsize) < 0) {
return -1;
}
DEBUG (3, ("get_quota: Running command %s\n", syscmd));
lines = file_lines_pload(syscmd, NULL);
SAFE_FREE(syscmd);
if (lines) {
char *line = lines[0];
DEBUG (3, ("Read output from set_quota, \"%s\"\n", line));
TALLOC_FREE(lines);
return 0;
}
DEBUG (0, ("set_quota_command failed!\n"));
return -1;
}
errno = ENOSYS;
return -1;
}
int sys_get_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
{
int ret = -1;
int i;
bool ready = False;
char *mntpath = NULL;
char *bdev = NULL;
char *fs = NULL;
if (!path||!dp)
smb_panic("sys_get_quota: called with NULL pointer");
if (command_get_quota(path, qtype, id, dp)==0) {
return 0;
} else if (errno != ENOSYS) {
return -1;
}
if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
return ret;
}
errno = 0;
DEBUG(10,("sys_get_quota() uid(%u, %u), fs(%s)\n", (unsigned)getuid(), (unsigned)geteuid(), fs));
for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].get_quota);i++) {
if (strcmp(fs,sys_quota_backends[i].name)==0) {
ret = sys_quota_backends[i].get_quota(mntpath, bdev, qtype, id, dp);
if (ret!=0) {
DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
} else {
DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
}
ready = True;
break;
}
}
if (!ready) {
/* use the default vfs quota functions */
ret=sys_get_vfs_quota(mntpath, bdev, qtype, id, dp);
if (ret!=0) {
DEBUG(3,("sys_get_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s\n",
"vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
} else {
DEBUG(10,("sys_get_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
"vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
}
}
SAFE_FREE(mntpath);
SAFE_FREE(bdev);
SAFE_FREE(fs);
if ((ret!=0)&& (errno == EDQUOT)) {
DEBUG(10,("sys_get_quota() warning over quota!\n"));
return 0;
}
return ret;
}
int sys_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *dp)
{
int ret = -1;
int i;
bool ready = False;
char *mntpath = NULL;
char *bdev = NULL;
char *fs = NULL;
/* find the block device file */
if (!path||!dp)
smb_panic("get_smb_quota: called with NULL pointer");
if (command_set_quota(path, qtype, id, dp)==0) {
return 0;
} else if (errno != ENOSYS) {
return -1;
}
if ((ret=sys_path_to_bdev(path,&mntpath,&bdev,&fs))!=0) {
DEBUG(0,("sys_path_to_bdev() failed for path [%s]!\n",path));
return ret;
}
errno = 0;
DEBUG(10,("sys_set_quota() uid(%u, %u)\n", (unsigned)getuid(), (unsigned)geteuid()));
for (i=0;(fs && sys_quota_backends[i].name && sys_quota_backends[i].set_quota);i++) {
if (strcmp(fs,sys_quota_backends[i].name)==0) {
ret = sys_quota_backends[i].set_quota(mntpath, bdev, qtype, id, dp);
if (ret!=0) {
DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
} else {
DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
fs,mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
}
ready = True;
break;
}
}
if (!ready) {
/* use the default vfs quota functions */
ret=sys_set_vfs_quota(mntpath, bdev, qtype, id, dp);
if (ret!=0) {
DEBUG(3,("sys_set_%s_quota() failed for mntpath[%s] bdev[%s] qtype[%d] id[%d]: %s.\n",
"vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid),strerror(errno)));
} else {
DEBUG(10,("sys_set_%s_quota() called for mntpath[%s] bdev[%s] qtype[%d] id[%d].\n",
"vfs",mntpath,bdev,qtype,(qtype==SMB_GROUP_QUOTA_TYPE?id.gid:id.uid)));
}
}
SAFE_FREE(mntpath);
SAFE_FREE(bdev);
SAFE_FREE(fs);
if ((ret!=0)&& (errno == EDQUOT)) {
DEBUG(10,("sys_set_quota() warning over quota!\n"));
return 0;
}
return ret;
}
#else /* HAVE_SYS_QUOTAS */
void dummy_sysquotas_c(void);
void dummy_sysquotas_c(void)
{
return;
}
#endif /* HAVE_SYS_QUOTAS */