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