Mercurial > dovecot > original-hg > dovecot-1.2
changeset 8892:66a8cbe7f007 HEAD
quota-fs: Added support for NFS group quota.
Based on patch by fandorin at rol.ru.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 02 Apr 2009 15:02:58 -0400 |
parents | fbb2343b85d9 |
children | bf16646ec312 |
files | src/plugins/quota/Makefile.am src/plugins/quota/quota-fs.c src/plugins/quota/rquota.x |
diffstat | 3 files changed, 261 insertions(+), 11 deletions(-) [+] |
line wrap: on
line diff
--- a/src/plugins/quota/Makefile.am Thu Apr 02 14:25:27 2009 -0400 +++ b/src/plugins/quota/Makefile.am Thu Apr 02 15:02:58 2009 -0400 @@ -33,7 +33,8 @@ 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 <rpc/rpc.h>'; \
--- a/src/plugins/quota/quota-fs.c Thu Apr 02 14:25:27 2009 -0400 +++ b/src/plugins/quota/quota-fs.c Thu Apr 02 15:02:58 2009 -0400 @@ -163,6 +163,15 @@ #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 @@ } #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 @@ 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 @@ 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 @@ 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 @@ #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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/quota/rquota.x Thu Apr 02 15:02:58 2009 -0400 @@ -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<RQ_PATHLEN>; /* path to filesystem of interest */ + int gqa_uid; /* Inquire about quota for uid */ +}; + +struct setquota_args { + int sqa_qcmd; + string sqa_pathp<RQ_PATHLEN>; /* path to filesystem of interest */ + int sqa_id; /* Set quota for uid */ + sq_dqblk sqa_dqblk; +}; + +struct ext_getquota_args { + string gqa_pathp<RQ_PATHLEN>; /* 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<RQ_PATHLEN>; /* 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;