[Dovecot] quota-fs: get nfs GROUP quota (patch)
Hi all. Unfortunately, the existing quota-fs does not know how to get GROUP quota with NFS storage. But there is a tool for Linux quota-tools (http://slackware.rol.ru/slackware/slackware-12.2/source/a/quota/). This patch is made on the basis quota-tools. The patch was successful alpha testing. Suggestions and comments are welcome. diff -ruN dovecot-1.1.11.orig/src/plugins/quota/quota-fs.c dovecot-1.1.11/src/plugins/quota/quota-fs.c --- dovecot-1.1.11.orig/src/plugins/quota/quota-fs.c 2009-01-06 17:33:51.000000000 +0300 +++ dovecot-1.1.11/src/plugins/quota/quota-fs.c 2009-02-11 12:57:55.000000000 +0300 @@ -288,17 +288,26 @@ #ifdef HAVE_RQUOTA /* retrieve user quota from a remote host */ -static int do_rquota(struct fs_quota_root *root, bool bytes, +static int do_rquota(struct fs_quota_root *root, bool group, bool bytes, uint64_t *value_r, uint64_t *limit_r) { - struct getquota_rslt result; - struct getquota_args args; - struct timeval timeout; + struct getquota_rslt *result; + static struct getquota_rslt clnt_res; enum clnt_stat call_status; - CLIENT *cl; + CLIENT *clnt; struct fs_quota_mountpoint *mount = root->mount; const char *host; char *path; + int type, id; + struct timeval timeout = { 2, 0 }; + + union { + getquota_args arg; + ext_getquota_args ext_arg; + } args; + + type = group ? GRPQUOTA : USRQUOTA; + id = group ? root->gid : root->uid; path = strchr(mount->device_path, ':'); if (path == NULL) { @@ -311,82 +320,211 @@ path++; if (getenv("DEBUG") != NULL) { + if (group) { + i_info("quota-fs: host=%s, path=%s, gid=%s", + host, path, dec2str(root->gid)); + } else { i_info("quota-fs: host=%s, path=%s, uid=%s", host, path, dec2str(root->uid)); + } } - /* clnt_create() polls for a while to establish a connection */ - cl = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp"); - if (cl == NULL) { - i_error("quota-fs: could not contact RPC service on %s", - 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_uid = root->uid; - - timeout.tv_sec = RQUOTA_GETQUOTA_TIMEOUT_SECS; - timeout.tv_usec = 0; - call_status = clnt_call(cl, RQUOTAPROC_GETQUOTA, - (xdrproc_t)xdr_getquota_args, (char *)&args, - (xdrproc_t)xdr_getquota_rslt, (char *)&result, - timeout); + /* + * First try EXT_RQUOTAPROG (Extended (LINUX) RPC quota program) + */ + args.ext_arg.gqa_pathp = path; + args.ext_arg.gqa_id = id; + args.ext_arg.gqa_type = type; - /* 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); - + /* + * Create a RPC client. + */ + + if ((clnt = clnt_create(host, RQUOTAPROG, EXT_RQUOTAVERS, "udp")) != NULL) { + /* + * Initialize unix authentication + */ + clnt->cl_auth = authunix_create_default(); + + /* + * Setup protocol timeout. + */ + clnt_control(clnt, CLSET_TIMEOUT, (caddr_t) & timeout); + + /* + * Do RPC call and check result. + */ + + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + call_status = clnt_call (clnt, RQUOTAPROC_GETQUOTA, + (xdrproc_t) xdr_ext_getquota_args, (caddr_t) &args.ext_arg, + (xdrproc_t) xdr_getquota_rslt, (caddr_t) &clnt_res, + timeout); + result = &clnt_res; + /* + * Destroy unix authentication and RPC client structure. + */ + auth_destroy(clnt->cl_auth); + clnt_destroy(clnt); + + if (call_status != RPC_SUCCESS) { + const char *rpc_error_msg = clnt_sperrno(call_status); i_error("quota-fs: remote rquota call failed: %s", rpc_error_msg); return -1; - } - - switch (result.status) { - case Q_OK: { + } + + switch (result->status) { + case Q_OK: { /* convert the results from blocks to bytes */ - rquota *rq = &result.getquota_rslt_u.gqr_rquota; + 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 (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 (getenv("DEBUG") != NULL) { + if (group) { + 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); + } else { i_info("quota-fs: uid=%s, value=%llu, " "limit=%llu, active=%d", dec2str(root->uid), (unsigned long long)*value_r, (unsigned long long)*limit_r, rq->rq_active); + } } return 1; - } - case Q_NOQUOTA: + } + case Q_NOQUOTA: if (getenv("DEBUG") != NULL) { + if (group) { + i_info("quota-fs: gid=%s, limit=unlimited", + dec2str(root->gid)); + } else { i_info("quota-fs: uid=%s, limit=unlimited", dec2str(root->uid)); + } } return 1; - case Q_EPERM: + case Q_EPERM: i_error("quota-fs: permission denied to rquota service"); return -1; - default: + default: i_error("quota-fs: unrecognized status code (%d) " - "from rquota service", result.status); + "from rquota service", result->status); return -1; + } + } else { + result = NULL; + } + + if (result == NULL || !result->status) { + if (type == USRQUOTA) { + /* + * Try RQUOTAPROG because server doesn't seem to understand EXT_RQUOTAPROG. (NON-LINUX servers.) + */ + args.arg.gqa_pathp = path; + args.arg.gqa_uid = id; + + /* + * Create a RPC client. + */ + if ((clnt = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp")) != NULL) { + /* + * Initialize unix authentication + */ + clnt->cl_auth = authunix_create_default(); + + /* + * Setup protocol timeout. + */ + clnt_control(clnt, CLSET_TIMEOUT, (caddr_t) & timeout); + + /* + * Do RPC call and check result. + */ + memset((char *)&clnt_res, 0, sizeof(clnt_res)); + call_status = clnt_call (clnt, RQUOTAPROC_GETQUOTA, + (xdrproc_t) xdr_getquota_args, (caddr_t) &args.arg, + (xdrproc_t) xdr_getquota_rslt, (caddr_t) &clnt_res, + timeout); + result = &clnt_res; + /* + * Destroy unix authentication and RPC client structure. + */ + auth_destroy(clnt->cl_auth); + clnt_destroy(clnt); + + if (call_status != RPC_SUCCESS) { + const char *rpc_error_msg = clnt_sperrno(call_status); + i_error("quota-fs: remote rquota call failed: %s", + rpc_error_msg); + return -1; + } + + switch (result->status) { + case Q_OK: { + /* convert the results from blocks to bytes */ + 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 (getenv("DEBUG") != NULL) { + if (group) { + 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); + } else { + i_info("quota-fs: uid=%s, value=%llu, " + "limit=%llu, active=%d", dec2str(root->uid), + (unsigned long long)*value_r, + (unsigned long long)*limit_r, rq->rq_active); + } + } + return 1; + } + case Q_NOQUOTA: + if (getenv("DEBUG") != NULL) { + if (group) { + i_info("quota-fs: gid=%s, limit=unlimited", + dec2str(root->gid)); + } else { + i_info("quota-fs: uid=%s, limit=unlimited", + dec2str(root->uid)); + } + } + return 1; + case Q_EPERM: + i_error("quota-fs: permission denied to rquota service"); + return -1; + default: + i_error("quota-fs: unrecognized status code (%d) " + "from rquota service", result->status); + return -1; + } + + } + } } } #endif @@ -602,6 +740,7 @@ uint64_t limit = 0; bool bytes; int ret; + bool group; *value_r = 0; @@ -614,7 +753,12 @@ #ifdef HAVE_RQUOTA if (strcmp(root->mount->type, "nfs") == 0) { T_BEGIN { - ret = do_rquota(root, bytes, value_r, &limit); + if (root->group_disabled){ + group = FALSE; + } else { + group = TRUE; + } + ret = do_rquota(root, group, bytes, value_r, &limit); } T_END; } else #endif
On Tue, 2009-02-17 at 11:14 +0300, fandorin@rol.ru wrote:
Hi all. Unfortunately, the existing quota-fs does not know how to get GROUP quota with NFS storage. But there is a tool for Linux quota-tools (http://slackware.rol.ru/slackware/slackware-12.2/source/a/quota/). This patch is made on the basis quota-tools. The patch was successful alpha testing. Suggestions and comments are welcome.
There's a bit too much copy&pasted code. For example the i_info() calls are duplicated. And I think you could keep the clnt open when fallbacking to the non-ext lookup instead of having to recreate it in the middle. It would also need to have some #ifdef EXT_RQUOTAPROG checks so that the code would compile with other systems than Linux too. And it should probably remember if the non-ext lookup failed the previous time, so it wouldn't waste time every time doing it twice.
Timo Sirainen wrote:
On Tue, 2009-02-17 at 11:14 +0300, fandorin@rol.ru wrote:
Hi all. Unfortunately, the existing quota-fs does not know how to get GROUP quota with NFS storage. But there is a tool for Linux quota-tools (http://slackware.rol.ru/slackware/slackware-12.2/source/a/quota/). This patch is made on the basis quota-tools. The patch was successful alpha testing. Suggestions and comments are welcome.
There's a bit too much copy&pasted code. For example the i_info() calls are duplicated. And I think you could keep the clnt open when fallbacking to the non-ext lookup instead of having to recreate it in the middle. It would also need to have some #ifdef EXT_RQUOTAPROG checks so that the code would compile with other systems than Linux too. And it should probably remember if the non-ext lookup failed the previous time, so it wouldn't waste time every time doing it twice.
Hi. Thanks for your comments. I made a new version of the patch for GROUP quota with NFS storage. But I do not know how better to define EXT_RQUOTAPROG: automatically or using option for configure script. What do you think? Are you interested in this patch for the basic version of the dovecot? diff -ruN -bE dovecot-1.1.11.orig/src/plugins/quota/quota-fs.c dovecot-1.1.11/src/plugins/quota/quota-fs.c --- dovecot-1.1.11.orig/src/plugins/quota/quota-fs.c 2009-01-06 17:33:51.000000000 +0300 +++ dovecot-1.1.11/src/plugins/quota/quota-fs.c 2009-02-27 15:56:24.000000000 +0300 @@ -288,17 +288,25 @@ #ifdef HAVE_RQUOTA /* retrieve user quota from a remote host */ -static int do_rquota(struct fs_quota_root *root, bool bytes, +static int do_rquota(struct fs_quota_root *root, bool group, bool bytes, uint64_t *value_r, uint64_t *limit_r) { struct getquota_rslt result; - struct 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; + int type, id; + + union { + getquota_args arg; + ext_getquota_args ext_arg; + } args; + + type = group ? GRPQUOTA : USRQUOTA; + id = group ? root->gid : root->uid; path = strchr(mount->device_path, ':'); if (path == NULL) { @@ -311,10 +319,40 @@ path++; if (getenv("DEBUG") != NULL) { - i_info("quota-fs: host=%s, path=%s, uid=%s", - host, path, dec2str(root->uid)); + i_info("quota-fs: host=%s, path=%s, %cid=%s", + host, path, group ? 'g' : 'u', dec2str(id)); + } + + if (group) { + /* 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", + 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.ext_arg.gqa_pathp = path; + args.ext_arg.gqa_id = id; + args.ext_arg.gqa_type = type; + + 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.ext_arg, + (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); + } else { /* clnt_create() polls for a while to establish a connection */ cl = clnt_create(host, RQUOTAPROG, RQUOTAVERS, "udp"); if (cl == NULL) { @@ -328,8 +366,8 @@ cl->cl_auth = authunix_create_default(); /* make the rquota call on the remote host */ - args.gqa_pathp = path; - args.gqa_uid = root->uid; + args.arg.gqa_pathp = path; + args.arg.gqa_uid = id; timeout.tv_sec = RQUOTA_GETQUOTA_TIMEOUT_SECS; timeout.tv_usec = 0; @@ -341,7 +379,7 @@ /* 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); @@ -367,8 +405,9 @@ } } if (getenv("DEBUG") != NULL) { - i_info("quota-fs: uid=%s, value=%llu, " - "limit=%llu, active=%d", dec2str(root->uid), + i_info("quota-fs: %cid=%s, value=%llu, " + "limit=%llu, active=%d", group ? 'g' : 'u', + dec2str(id), (unsigned long long)*value_r, (unsigned long long)*limit_r, rq->rq_active); } @@ -376,8 +415,9 @@ } case Q_NOQUOTA: if (getenv("DEBUG") != NULL) { - i_info("quota-fs: uid=%s, limit=unlimited", - dec2str(root->uid)); + i_info("quota-fs: %cid=%s, limit=unlimited", + group ? 'g' : 'u', + dec2str(id)); } return 1; case Q_EPERM: @@ -614,7 +654,8 @@ #ifdef HAVE_RQUOTA if (strcmp(root->mount->type, "nfs") == 0) { T_BEGIN { - ret = do_rquota(root, bytes, value_r, &limit); + ret = do_rquota(root, root->group_disabled ? FALSE : TRUE, + bytes, value_r, &limit); } T_END; } else #endif
On Tue, 2009-03-24 at 15:34 +0300, fandorin@rol.ru wrote:
There's a bit too much copy&pasted code. For example the i_info() calls are duplicated. And I think you could keep the clnt open when fallbacking to the non-ext lookup instead of having to recreate it in the middle. It would also need to have some #ifdef EXT_RQUOTAPROG checks so that the code would compile with other systems than Linux too. And it should probably remember if the non-ext lookup failed the previous time, so it wouldn't waste time every time doing it twice.
Hi. Thanks for your comments. I made a new version of the patch for GROUP quota with NFS storage. But I do not know how better to define EXT_RQUOTAPROG: automatically or using option for configure script.
What do you think? Are you interested in this patch for the basic version of the dovecot?
I committed it now to v1.2 branch. The same patch applies cleanly to v1.1 too. Could you try that it still works? http://hg.dovecot.org/dovecot-1.2/rev/66a8cbe7f007
I changed my mind about copy&pasting after all. Most of the logging strings are different anyway in them, so it's just cleaner to copy them instead of adding those %cid kind of things.
participants (2)
-
fandorin@rol.ru
-
Timo Sirainen