From: Timo Sirainen Date: Thu, 2 Apr 2009 19:02:58 +0000 (-0400) Subject: quota-fs: Added support for NFS group quota. X-Git-Tag: 1.2.rc1~16 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4c7caf83adc56d54e2ecec0a803f5ca9b6d0498d;p=thirdparty%2Fdovecot%2Fcore.git quota-fs: Added support for NFS group quota. Based on patch by fandorin at rol.ru. --HG-- branch : HEAD --- diff --git a/src/plugins/quota/Makefile.am b/src/plugins/quota/Makefile.am index ff510f2f23..09c056711b 100644 --- a/src/plugins/quota/Makefile.am +++ b/src/plugins/quota/Makefile.am @@ -33,7 +33,8 @@ lib10_quota_plugin_la_SOURCES = \ if HAVE_RQUOTA RQUOTA_XDR = rquota_xdr.c -RQUOTA_X = /usr/include/rpcsvc/rquota.x +#RQUOTA_X = /usr/include/rpcsvc/rquota.x +RQUOTA_X = rquota.x rquota_xdr.c: Makefile $(RQUOTA_X) (echo '#include "lib.h"'; \ echo '#include '; \ diff --git a/src/plugins/quota/quota-fs.c b/src/plugins/quota/quota-fs.c index 2d4df8a787..cd4135c258 100644 --- a/src/plugins/quota/quota-fs.c +++ b/src/plugins/quota/quota-fs.c @@ -163,6 +163,15 @@ static struct fs_quota_mountpoint *fs_quota_mountpoint_get(const char *dir) #ifdef FS_QUOTA_SOLARIS mount->fd = -1; #endif + + if (strcmp(mount->type, "nfs") == 0) { + if (strchr(mount->device_path, ':') == NULL) { + i_error("quota-fs: %s is not a valid NFS device path", + mount->device_path); + fs_quota_mountpoint_free(mount); + return NULL; + } + } return mount; } @@ -287,9 +296,8 @@ fs_quota_root_get_resources(struct quota_root *_root) } #ifdef HAVE_RQUOTA -/* retrieve user quota from a remote host */ -static int do_rquota(struct fs_quota_root *root, bool bytes, - uint64_t *value_r, uint64_t *limit_r) +static int do_rquota_user(struct fs_quota_root *root, bool bytes, + uint64_t *value_r, uint64_t *limit_r) { struct getquota_rslt result; struct getquota_args args; @@ -301,11 +309,7 @@ static int do_rquota(struct fs_quota_root *root, bool bytes, char *path; path = strchr(mount->device_path, ':'); - if (path == NULL) { - i_error("quota-fs: %s is not a valid NFS device path", - mount->device_path); - return -1; - } + i_assert(path != NULL); host = t_strdup_until(mount->device_path, path); path++; @@ -353,7 +357,7 @@ static int do_rquota(struct fs_quota_root *root, bool bytes, switch (result.status) { case Q_OK: { /* convert the results from blocks to bytes */ - rquota *rq = &result.getquota_rslt_u.gqr_rquota; + const rquota *rq = &result.getquota_rslt_u.gqr_rquota; if (rq->rq_active) { if (bytes) { @@ -389,6 +393,110 @@ static int do_rquota(struct fs_quota_root *root, bool bytes, return -1; } } + +static int do_rquota_group(struct fs_quota_root *root, bool bytes, + uint64_t *value_r, uint64_t *limit_r) +{ +#ifdef EXT_RQUOTAVERS + struct getquota_rslt result; + ext_getquota_args args; + struct timeval timeout; + enum clnt_stat call_status; + CLIENT *cl; + struct fs_quota_mountpoint *mount = root->mount; + const char *host; + char *path; + + path = strchr(mount->device_path, ':'); + i_assert(path != NULL); + + host = t_strdup_until(mount->device_path, path); + path++; + + if (root->root.quota->set->debug) { + i_info("quota-fs: host=%s, path=%s, gid=%s", + host, path, dec2str(root->gid)); + } + + /* clnt_create() polls for a while to establish a connection */ + cl = clnt_create(host, RQUOTAPROG, EXT_RQUOTAVERS, "udp"); + if (cl == NULL) { + i_error("quota-fs: could not contact RPC service on %s (group)", + host); + return -1; + } + + /* Establish some RPC credentials */ + auth_destroy(cl->cl_auth); + cl->cl_auth = authunix_create_default(); + + /* make the rquota call on the remote host */ + args.gqa_pathp = path; + args.gqa_id = root->gid; + args.gqa_type = GRPQUOTA; + timeout.tv_sec = RQUOTA_GETQUOTA_TIMEOUT_SECS; + timeout.tv_usec = 0; + + call_status = clnt_call(cl, RQUOTAPROC_GETQUOTA, + (xdrproc_t)xdr_ext_getquota_args, (char *)&args, + (xdrproc_t)xdr_getquota_rslt, (char *)&result, + timeout); + + /* the result has been deserialized, let the client go */ + auth_destroy(cl->cl_auth); + clnt_destroy(cl); + + if (call_status != RPC_SUCCESS) { + const char *rpc_error_msg = clnt_sperrno(call_status); + + i_error("quota-fs: remote ext rquota call failed: %s", + rpc_error_msg); + return -1; + } + + switch (result.status) { + case Q_OK: { + /* convert the results from blocks to bytes */ + const rquota *rq = &result.getquota_rslt_u.gqr_rquota; + + if (rq->rq_active) { + if (bytes) { + *value_r = (uint64_t)rq->rq_curblocks * + (uint64_t)rq->rq_bsize; + *limit_r = (uint64_t)rq->rq_bsoftlimit * + (uint64_t)rq->rq_bsize; + } else { + *value_r = rq->rq_curfiles; + *limit_r = rq->rq_fsoftlimit; + } + } + if (root->root.quota->set->debug) { + i_info("quota-fs: gid=%s, value=%llu, " + "limit=%llu, active=%d", dec2str(root->gid), + (unsigned long long)*value_r, + (unsigned long long)*limit_r, rq->rq_active); + } + return 1; + } + case Q_NOQUOTA: + if (root->root.quota->set->debug) { + i_info("quota-fs: gid=%s, limit=unlimited", + dec2str(root->gid)); + } + return 1; + case Q_EPERM: + i_error("quota-fs: permission denied to ext rquota service"); + return -1; + default: + i_error("quota-fs: unrecognized status code (%d) " + "from ext rquota service", result.status); + return -1; + } +#else + i_error("quota-fs: rquota not compiled with group support"); + return -1; +#endif +} #endif #if defined(FS_QUOTA_LINUX) || defined(FS_QUOTA_BSDAIX) @@ -643,7 +751,9 @@ fs_quota_get_resource(struct quota_root *_root, const char *name, #ifdef HAVE_RQUOTA if (strcmp(root->mount->type, "nfs") == 0) { T_BEGIN { - ret = do_rquota(root, bytes, value_r, &limit); + ret = root->group_disabled ? + do_rquota_user(root, bytes, value_r, &limit) : + do_rquota_group(root, bytes, value_r, &limit); } T_END; } else #endif diff --git a/src/plugins/quota/rquota.x b/src/plugins/quota/rquota.x new file mode 100644 index 0000000000..3cd5c10d66 --- /dev/null +++ b/src/plugins/quota/rquota.x @@ -0,0 +1,139 @@ +/* @(#)rquota.x 2.1 88/08/01 4.0 RPCSRC */ +/* @(#)rquota.x 1.2 87/09/20 Copyr 1987 Sun Micro */ + +/* + * Remote quota protocol + * Requires unix authentication + */ + +const RQ_PATHLEN = 1024; + +struct sq_dqblk { + unsigned int rq_bhardlimit; /* absolute limit on disk blks alloc */ + unsigned int rq_bsoftlimit; /* preferred limit on disk blks */ + unsigned int rq_curblocks; /* current block count */ + unsigned int rq_fhardlimit; /* absolute limit on allocated files */ + unsigned int rq_fsoftlimit; /* preferred file limit */ + unsigned int rq_curfiles; /* current # allocated files */ + unsigned int rq_btimeleft; /* time left for excessive disk use */ + unsigned int rq_ftimeleft; /* time left for excessive files */ +}; + +struct getquota_args { + string gqa_pathp; /* path to filesystem of interest */ + int gqa_uid; /* Inquire about quota for uid */ +}; + +struct setquota_args { + int sqa_qcmd; + string sqa_pathp; /* path to filesystem of interest */ + int sqa_id; /* Set quota for uid */ + sq_dqblk sqa_dqblk; +}; + +struct ext_getquota_args { + string gqa_pathp; /* path to filesystem of interest */ + int gqa_type; /* Type of quota info is needed about */ + int gqa_id; /* Inquire about quota for id */ +}; + +struct ext_setquota_args { + int sqa_qcmd; + string sqa_pathp; /* path to filesystem of interest */ + int sqa_id; /* Set quota for id */ + int sqa_type; /* Type of quota to set */ + sq_dqblk sqa_dqblk; +}; + +/* + * remote quota structure + */ +struct rquota { + int rq_bsize; /* block size for block counts */ + bool rq_active; /* indicates whether quota is active */ + unsigned int rq_bhardlimit; /* absolute limit on disk blks alloc */ + unsigned int rq_bsoftlimit; /* preferred limit on disk blks */ + unsigned int rq_curblocks; /* current block count */ + unsigned int rq_fhardlimit; /* absolute limit on allocated files */ + unsigned int rq_fsoftlimit; /* preferred file limit */ + unsigned int rq_curfiles; /* current # allocated files */ + unsigned int rq_btimeleft; /* time left for excessive disk use */ + unsigned int rq_ftimeleft; /* time left for excessive files */ +}; + +enum qr_status { + Q_OK = 1, /* quota returned */ + Q_NOQUOTA = 2, /* noquota for uid */ + Q_EPERM = 3 /* no permission to access quota */ +}; + +union getquota_rslt switch (qr_status status) { +case Q_OK: + rquota gqr_rquota; /* valid if status == Q_OK */ +case Q_NOQUOTA: + void; +case Q_EPERM: + void; +}; + +union setquota_rslt switch (qr_status status) { +case Q_OK: + rquota sqr_rquota; /* valid if status == Q_OK */ +case Q_NOQUOTA: + void; +case Q_EPERM: + void; +}; + +program RQUOTAPROG { + version RQUOTAVERS { + /* + * Get all quotas + */ + getquota_rslt + RQUOTAPROC_GETQUOTA(getquota_args) = 1; + + /* + * Get active quotas only + */ + getquota_rslt + RQUOTAPROC_GETACTIVEQUOTA(getquota_args) = 2; + + /* + * Set all quotas + */ + setquota_rslt + RQUOTAPROC_SETQUOTA(setquota_args) = 3; + + /* + * Get active quotas only + */ + setquota_rslt + RQUOTAPROC_SETACTIVEQUOTA(setquota_args) = 4; + } = 1; + version EXT_RQUOTAVERS { + /* + * Get all quotas + */ + getquota_rslt + RQUOTAPROC_GETQUOTA(ext_getquota_args) = 1; + + /* + * Get active quotas only + */ + getquota_rslt + RQUOTAPROC_GETACTIVEQUOTA(ext_getquota_args) = 2; + + /* + * Set all quotas + */ + setquota_rslt + RQUOTAPROC_SETQUOTA(ext_setquota_args) = 3; + + /* + * Set active quotas only + */ + setquota_rslt + RQUOTAPROC_SETACTIVEQUOTA(ext_setquota_args) = 4; + } = 2; +} = 100011;