]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
quota-fs: Added support for NFS group quota.
authorTimo Sirainen <tss@iki.fi>
Thu, 2 Apr 2009 19:02:58 +0000 (15:02 -0400)
committerTimo Sirainen <tss@iki.fi>
Thu, 2 Apr 2009 19:02:58 +0000 (15:02 -0400)
Based on patch by fandorin at rol.ru.

--HG--
branch : HEAD

src/plugins/quota/Makefile.am
src/plugins/quota/quota-fs.c
src/plugins/quota/rquota.x [new file with mode: 0644]

index ff510f2f230a56a5e532bb562d222c75b566e653..09c056711b47b6d85c4cd43a527b38784d920cee 100644 (file)
@@ -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 <rpc/rpc.h>'; \
index 2d4df8a78788061eda6dbf03a5c607783cdd7d2e..cd4135c258958196380e15efc39c1eb8332896bc 100644 (file)
@@ -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 (file)
index 0000000..3cd5c10
--- /dev/null
@@ -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;