/* 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) #error HAVE_QUOTACTL_4B not implemeted /*#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 */ #ifdef HAVE_MNTENT 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; SMB_DEV_T devno; /* find the block device file */ if (!path||!mntpath||!bdev||!fs) smb_panic("sys_path_to_bdev: called with NULL pointer"); (*mntpath) = NULL; (*bdev) = NULL; (*fs) = NULL; if ( sys_stat(path, &S) == -1 ) return (-1); devno = S.st_dev ; fp = setmntent(MOUNTED,"r"); if (fp == NULL) { return -1; } while ((mnt = getmntent(fp))) { if ( sys_stat(mnt->mnt_dir,&S) == -1 ) continue ; if (S.st_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) ; 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))!=0) { return ret; } if ((ret=devnm(S_IFBLK, S.st_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[] = { #ifdef HAVE_XFS_QUOTAS {"xfs", sys_get_xfs_quota, sys_set_xfs_quota}, #endif /* HAVE_XFS_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(); 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 = (enum SMB_QUOTA_TYPE)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; } file_lines_free(lines); lines = NULL; DEBUG (3, ("Parsed output of get_quota, ...\n")); #ifdef LARGE_SMB_OFF_T 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)); #else /* LARGE_SMB_OFF_T */ DEBUGADD (5,( "qflags:%u curblocks:%lu softlimit:%lu hardlimit:%lu\n" "curinodes:%lu isoftlimit:%lu ihardlimit:%lu bsize:%lu\n", dp->qflags,(long unsigned)dp->curblocks, (long unsigned)dp->softlimit,(long unsigned)dp->hardlimit, (long unsigned)dp->curinodes, (long unsigned)dp->isoftlimit,(long unsigned)dp->ihardlimit, (long unsigned)dp->bsize)); #endif /* LARGE_SMB_OFF_T */ return 0; } DEBUG (0, ("get_quota_command failed!\n")); return -1; } errno = ENOSYS; return -1; invalid_param: file_lines_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(); 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; } #ifdef LARGE_SMB_OFF_T 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; } #else /* LARGE_SMB_OFF_T */ if (asprintf(&syscmd, "%s \"%s\" %d %d " "%u %lu %lu " "%lu %lu %lu ", set_quota_command, path, qtype, _id, dp->qflags, (long unsigned)dp->softlimit,(long unsigned)dp->hardlimit, (long unsigned)dp->isoftlimit,(long unsigned)dp->ihardlimit, (long unsigned)dp->bsize) < 0) { return -1; } #endif /* LARGE_SMB_OFF_T */ 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)); file_lines_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)\n", (unsigned)getuid(), (unsigned)geteuid())); 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 */