The attached patch is an updated version of my quota-rquotad patch for the
quota plugin. Like quota-fs, it only does quota reporting, not enforcing or
changing. It also only works on FreeBSD, and possibly other *BSD's. It lacks
any and all configure-time tests to see if RPC is even supported, and
hardcodes the (Free)BSD way of getting at the NFS server of an NFS mount.
I'm mostly mailing it around to avoid duplicate work, in case anyone is
looking for it.
I do intend to generalize the plugin-patch, and add proper configure checks,
I just haven't gotten around to testing it on other BSD systems. Linux
support would be nice, but getting at the NFS server of an NFS mount is
apparently quite a hassle, as it means reading /etc/mtab or
/proc/self/mounts. Yuck.
--
Thomas Wouters <thomas at xs4all.net>
Hi! I'm a .signature virus! copy me into your .signature file to help me
spread!
-------------- next part --------------
diff -crN dovecot-1.0.beta2/src/lib/failures.c
dovecot-1.0.beta2.mail.location+quota/src/lib/failures.c
*** dovecot-1.0.beta2/src/lib/failures.c Tue Jan 31 16:18:19 2006
--- dovecot-1.0.beta2.mail.location+quota/src/lib/failures.c Tue Jan 31 16:19:08
2006
***************
*** 219,224 ****
--- 219,229 ----
va_end(args);
}
+ void i_vinfo(const char *format, va_list args)
+ {
+ info_handler(format, args);
+ }
+
void i_set_panic_handler(failure_callback_t *callback __attr_noreturn__)
{
if (callback == NULL)
diff -crN dovecot-1.0.beta2/src/lib/failures.h
dovecot-1.0.beta2.mail.location+quota/src/lib/failures.h
*** dovecot-1.0.beta2/src/lib/failures.h Tue Jan 31 16:18:23 2006
--- dovecot-1.0.beta2.mail.location+quota/src/lib/failures.h Tue Jan 31 16:19:20
2006
***************
*** 22,27 ****
--- 22,28 ----
void i_error(const char *format, ...) __attr_format__(1, 2);
void i_warning(const char *format, ...) __attr_format__(1, 2);
void i_info(const char *format, ...) __attr_format__(1, 2);
+ void i_vinfo(const char *format, va_list args);
void i_fatal_status(int status, const char *format, ...)
__attr_format__(2, 3) __attr_noreturn__;
diff -crN dovecot-1.0.beta2/src/plugins/quota/Makefile.am
dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/Makefile.am
*** dovecot-1.0.beta2/src/plugins/quota/Makefile.am Wed Dec 14 22:50:01 2005
--- dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/Makefile.am Fri Feb
3 13:56:11 2006
***************
*** 2,10 ****
-I$(top_srcdir)/src/lib \
-I$(top_srcdir)/src/lib-dict \
-I$(top_srcdir)/src/lib-mail \
! -I$(top_srcdir)/src/lib-storage
! lib01_quota_plugin_la_LDFLAGS = -module -avoid-version
module_LTLIBRARIES = \
lib01_quota_plugin.la
--- 2,11 ----
-I$(top_srcdir)/src/lib \
-I$(top_srcdir)/src/lib-dict \
-I$(top_srcdir)/src/lib-mail \
! -I$(top_srcdir)/src/lib-storage \
! -I$(top_srcdir)/src/lib-index
! lib01_quota_plugin_la_LDFLAGS = -module -avoid-version -lrpcsvc
module_LTLIBRARIES = \
lib01_quota_plugin.la
***************
*** 15,21 ****
quota-dict.c \
quota-dirsize.c \
quota-plugin.c \
! quota-storage.c
noinst_HEADERS = \
quota.h \
--- 16,23 ----
quota-dict.c \
quota-dirsize.c \
quota-plugin.c \
! quota-storage.c \
! quota-rquotad.c
noinst_HEADERS = \
quota.h \
diff -crN dovecot-1.0.beta2/src/plugins/quota/Makefile.in
dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/Makefile.in
*** dovecot-1.0.beta2/src/plugins/quota/Makefile.in Tue Jan 31 16:40:29 2006
--- dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/Makefile.in Fri Feb
3 13:56:21 2006
***************
*** 60,66 ****
LTLIBRARIES = $(module_LTLIBRARIES)
lib01_quota_plugin_la_LIBADD am_lib01_quota_plugin_la_OBJECTS = quota.lo
quota-fs.lo quota-dict.lo \
! quota-dirsize.lo quota-plugin.lo quota-storage.lo
lib01_quota_plugin_la_OBJECTS = $(am_lib01_quota_plugin_la_OBJECTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
--- 60,67 ----
LTLIBRARIES = $(module_LTLIBRARIES)
lib01_quota_plugin_la_LIBADD am_lib01_quota_plugin_la_OBJECTS = quota.lo
quota-fs.lo quota-dict.lo \
! quota-dirsize.lo quota-plugin.lo quota-storage.lo \
! quota-rquotad.lo
lib01_quota_plugin_la_OBJECTS = $(am_lib01_quota_plugin_la_OBJECTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
***************
*** 205,213 ****
-I$(top_srcdir)/src/lib \
-I$(top_srcdir)/src/lib-dict \
-I$(top_srcdir)/src/lib-mail \
! -I$(top_srcdir)/src/lib-storage
! lib01_quota_plugin_la_LDFLAGS = -module -avoid-version
module_LTLIBRARIES = \
lib01_quota_plugin.la
--- 206,215 ----
-I$(top_srcdir)/src/lib \
-I$(top_srcdir)/src/lib-dict \
-I$(top_srcdir)/src/lib-mail \
! -I$(top_srcdir)/src/lib-storage \
! -I$(top_srcdir)/src/lib-index
! lib01_quota_plugin_la_LDFLAGS = -module -avoid-version -lrpcsvc
module_LTLIBRARIES = \
lib01_quota_plugin.la
***************
*** 217,223 ****
quota-dict.c \
quota-dirsize.c \
quota-plugin.c \
! quota-storage.c
noinst_HEADERS = \
quota.h \
--- 219,226 ----
quota-dict.c \
quota-dirsize.c \
quota-plugin.c \
! quota-storage.c \
! quota-rquotad.c
noinst_HEADERS = \
quota.h \
***************
*** 298,303 ****
--- 301,307 ----
@AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/quota-dirsize.Plo at
am__quote@
@AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/quota-fs.Plo at am__quote@
@AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/quota-plugin.Plo at
am__quote@
+ @AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/quota-rquotad.Plo at
am__quote@
@AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/quota-storage.Plo at
am__quote@
@AMDEP_TRUE@@am__include@ @am__quote at ./$(DEPDIR)/quota.Plo at am__quote@
diff -crN dovecot-1.0.beta2/src/plugins/quota/quota-rquotad.c
dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/quota-rquotad.c
*** dovecot-1.0.beta2/src/plugins/quota/quota-rquotad.c Thu Jan 1 01:00:00 1970
--- dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/quota-rquotad.c Fri
Feb 3 13:55:56 2006
***************
*** 0 ****
--- 1,500 ----
+ /* Copyright (C) 2005 Thomas Wouters */
+
+ /* Quota reporting based on rquotad, loosely based on FreeBSD's quota(1)
+ program */
+
+ #include "lib.h"
+ #include "str.h"
+ #include "index/index-mail.h"
+ #include "index/index-storage.h"
+ #include "quota-private.h"
+
+ #include <stdlib.h>
+ #include <unistd.h>
+ #include <dirent.h>
+ #include <sys/stat.h>
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/param.h>
+ #include <sys/mount.h>
+ #include <rpc/rpc.h>
+ #include <rpc/pmap_prot.h>
+ #include <rpcsvc/rquota.h>
+ #include <netdb.h>
+ #include <stdio.h>
+
+ struct rquotad_quota {
+ struct quota quota;
+
+ pool_t pool;
+ const char *error;
+
+ size_t nroots;
+ struct rquotad_quota_root *roots;
+
+ };
+
+ enum rquotad_states { RQUOTAD_UNKNOWN, RQUOTAD_ERR, RQUOTAD_OFF, RQUOTAD_ON };
+
+ struct rquotad_quota_root {
+ struct quota_root root;
+
+ pool_t pool;
+ const char *name;
+ enum rquotad_states state;
+ const char **resources;
+ uint64_t kbyte_usage;
+ uint64_t inode_usage;
+ uint64_t kbyte_limit;
+ uint64_t inode_limit;
+ };
+
+ struct rquotad_quota_root_iter {
+ struct quota_root_iter iter;
+ char boxfs[MNAMELEN];
+ size_t offset;
+ };
+
+ static void debugmsg(const char *msg, ...)
+ {
+ va_list args;
+ static int debug = -1;
+ if (debug < 0) {
+ if (getenv("DEBUG") != NULL)
+ debug = 1;
+ else
+ debug = 0;
+ }
+ if (!debug)
+ return;
+
+ va_start(args, msg);
+ i_vinfo(msg, args);
+ va_end(args);
+ }
+
+ extern struct quota rquotad_quota;
+ static void rquotad_quota_root_init(struct rquotad_quota_root *root,
+ const char *name,
+ struct rquotad_quota *quota);
+ static void rquotad_quota_root_update(struct rquotad_quota_root *root);
+
+ static struct quota *rquotad_quota_init(const char *data)
+ {
+ struct rquotad_quota *quota;
+ const char *const *qroots;
+ size_t qr_idx;
+ pool_t pool;
+
+ pool = pool_alloconly_create("quota", 1024);
+ quota = p_new(pool, struct rquotad_quota, 1);
+ quota->pool = pool;
+ quota->quota = rquotad_quota;
+
+ debugmsg("starting quota init");
+ qroots = t_strsplit(data, ",");
+ for (qr_idx = 0; qroots[qr_idx] != NULL; qr_idx++) {
+ /* NOTHING */;
+ }
+ debugmsg("found %d quotaroots", qr_idx);
+ quota->roots = p_new(pool, struct rquotad_quota_root, qr_idx);
+ quota->nroots = qr_idx;
+ for (qr_idx = 0; qroots[qr_idx] != NULL; qr_idx++) {
+ rquotad_quota_root_init(&(quota->roots[qr_idx]),
+ qroots[qr_idx], quota);
+ }
+ return "a->quota;
+ }
+
+ static void rquotad_quota_deinit(struct quota *_quota)
+ {
+ struct rquotad_quota *quota = (struct rquotad_quota *)_quota;
+
+ pool_unref(quota->pool);
+ }
+
+ static void rquotad_quota_root_init(struct rquotad_quota_root *root,
+ const char *name,
+ struct rquotad_quota *quota)
+ {
+ debugmsg("creating quotaroot %s", name);
+ root->name = p_strdup(quota->pool, name);
+ root->root.quota = (struct quota *)quota;
+ root->pool = quota->pool;
+ root->state = RQUOTAD_UNKNOWN;
+ }
+
+ static struct quota_root_iter *
+ rquotad_quota_root_iter_init(struct quota *quota,
+ struct mailbox *box)
+ {
+ struct rquotad_quota_root_iter *iter;
+ struct statfs statbuf;
+
+ if (statfs(((struct index_storage *)(mailbox_get_storage(box)))->dir,
&statbuf) < 0) {
+ return NULL;
+ }
+
+ debugmsg("getting quota for %s (%s)", box->name,
+ mailbox_get_storage(box)->name);
+ iter = i_new(struct rquotad_quota_root_iter, 1);
+ iter->iter.quota = quota;
+ strncpy(iter->boxfs, statbuf.f_mntonname, MNAMELEN);
+ debugmsg("mntonname %s", iter->boxfs);
+ return &iter->iter;
+ }
+
+ static struct quota_root *
+ rquotad_quota_root_iter_next(struct quota_root_iter *_iter)
+ {
+ struct rquotad_quota_root_iter *iter + (struct rquotad_quota_root_iter
*)_iter;
+ struct rquotad_quota *quota = (struct rquotad_quota *)_iter->quota;
+ struct rquotad_quota_root *root;
+
+ debugmsg("Iter step %d, nroots %d", iter->offset,
quota->nroots);
+ for (; iter->offset < quota->nroots; iter->offset++) {
+ root = "a->roots[iter->offset];
+ debugmsg("comparing root %d (%s)", iter->offset,
root->name);
+ if (strcmp(iter->boxfs, root->name) == 0) {
+ debugmsg("Returning root %d", iter->offset);
+ iter->offset++;
+ return (struct quota_root *) root;
+ }
+ }
+ return NULL;
+ }
+
+ static int rquotad_quota_root_iter_deinit(struct quota_root_iter *iter)
+ {
+ i_free(iter);
+ return 0;
+ }
+
+ static struct quota_root *
+ rquotad_quota_root_lookup(struct quota *_quota, const char *name)
+ {
+ struct rquotad_quota *quota = (struct rquotad_quota *)_quota;
+ struct rquotad_quota_root *root;
+ size_t i;
+
+ for (i = 0; i < quota->nroots; i++) {
+ root = "a->roots[i];
+ if (strcmp(root->name, name) == 0) {
+ return (struct quota_root *)root;
+ }
+ }
+ return NULL;
+ }
+
+ static const char *
+ rquotad_quota_root_get_name(struct quota_root *_root)
+ {
+ struct rquotad_quota_root *root = (struct rquotad_quota_root *)_root;
+ return root->name;
+ }
+
+ static const char *const *
+ rquotad_quota_root_get_resources(struct quota_root *_root)
+ {
+ struct rquotad_quota_root *root = (struct rquotad_quota_root *)_root;
+ int i = 0;
+
+ if (root->resources != NULL) {
+ return root->resources;
+ }
+ if (root->state == RQUOTAD_UNKNOWN) {
+ rquotad_quota_root_update(root);
+ }
+ if (root->state == RQUOTAD_ERR) {
+ return NULL;
+ }
+ if (root->state == RQUOTAD_OFF) {
+ root->resources = p_new(root->pool, const char *, 1);
+ root->resources[0] = NULL;
+ return root->resources;
+ }
+ /* Out of date is alright, we don't really care about up-to-date
+ * numbers, just whether quotas are active */
+ root->resources = p_new(root->pool, const char *, 3);
+ if (root->kbyte_limit != 0) {
+ root->resources[i++] = QUOTA_NAME_STORAGE;
+ }
+ if (root->inode_limit != 0) {
+ root->resources[i++] = QUOTA_NAME_MESSAGES;
+ }
+ root->resources[i] = NULL;
+
+ return root->resources;
+ }
+
+ static int
+ rquotad_quota_root_create(struct quota *_quota,
+ const char *name __attr_unused__,
+ struct quota_root **root_r __attr_unused__)
+ {
+ struct rquotad_quota *quota = (struct rquotad_quota *)_quota;
+
+ quota->error = "Permission denied";
+ return -1;
+ }
+
+ static int
+ rquotad_quota_get_resource(struct quota_root *_root, const char *name,
+ uint64_t *value_r, uint64_t *limit_r)
+ {
+ struct rquotad_quota_root *root = (struct rquotad_quota_root *)_root;
+ if (root->state == RQUOTAD_UNKNOWN || root->state == RQUOTAD_ON) {
+ rquotad_quota_root_update(root);
+ }
+ if (root->state == RQUOTAD_ERR) {
+ return -1;
+ }
+ *value_r = 0;
+ *limit_r = 0;
+ if (root->state == RQUOTAD_OFF) {
+ return 0;
+ }
+ if (strcmp(name, QUOTA_NAME_STORAGE) == 0) {
+ *value_r = root->kbyte_usage;
+ *limit_r = root->kbyte_limit;
+ return 1;
+ }
+ if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) {
+ *value_r = root->inode_usage;
+ *limit_r = root->inode_limit;
+ return 1;
+ }
+ return 0;
+ }
+
+ static int
+ rquotad_quota_set_resource(struct quota_root *root,
+ const char *name __attr_unused__,
+ uint64_t value __attr_unused__)
+ {
+ struct rquotad_quota *quota = (struct rquotad_quota *)root->quota;
+
+ quota->error = "Permission denied";
+ return -1;
+ }
+
+ static struct quota_transaction_context *
+ rquotad_quota_transaction_begin(struct quota *quota)
+ {
+ struct quota_transaction_context *ctx;
+
+ ctx = i_new(struct quota_transaction_context, 1);
+ ctx->quota = quota;
+ return ctx;
+ }
+
+ static int
+ rquotad_quota_transaction_commit(struct quota_transaction_context *ctx)
+ {
+ i_free(ctx);
+ return 0;
+ }
+
+ static void
+ rquotad_quota_transaction_rollback(struct quota_transaction_context *ctx)
+ {
+ i_free(ctx);
+ }
+
+ static int
+ rquotad_quota_try_alloc(struct mailbox_transaction_context *ctx
__attr_unused__,
+ struct quota *_quota __attr_unused__,
+ struct mail *mail __attr_unused__)
+ {
+ return 1;
+ }
+
+ static void
+ rquotad_quota_alloc(struct mailbox_transaction_context *ctx __attr_unused__,
+ struct quota *quota __attr_unused__,
+ struct mail *mail __attr_unused__)
+ {
+ /* no-op */
+ }
+
+ static void
+ rquotad_quota_free(struct mailbox_transaction_context *ctx __attr_unused__,
+ struct quota *quota __attr_unused__,
+ struct mail *mail __attr_unused__)
+ {
+ /* no-op */
+ }
+
+ const char *rquotad_quota_last_error(struct quota *_quota)
+ {
+ struct rquotad_quota *quota = (struct rquotad_quota *)_quota;
+
+ return quota->error;
+ }
+
+ struct quota rquotad_quota = {
+ "rquotad",
+
+ rquotad_quota_init,
+ rquotad_quota_deinit,
+
+ rquotad_quota_root_iter_init,
+ rquotad_quota_root_iter_next,
+ rquotad_quota_root_iter_deinit,
+
+ rquotad_quota_root_lookup,
+
+ rquotad_quota_root_get_name,
+ rquotad_quota_root_get_resources,
+
+ rquotad_quota_root_create,
+ rquotad_quota_get_resource,
+ rquotad_quota_set_resource,
+
+ rquotad_quota_transaction_begin,
+ rquotad_quota_transaction_commit,
+ rquotad_quota_transaction_rollback,
+
+ rquotad_quota_try_alloc,
+ rquotad_quota_alloc,
+ rquotad_quota_free,
+
+ rquotad_quota_last_error,
+
+ ARRAY_INIT
+ };
+
+ static int
+ rquota_get(char *host, getquota_args *qargs, getquota_rslt *qres)
+ {
+ struct sockaddr_in rhost;
+ struct hostent *hent;
+ struct timeval tout;
+ enum clnt_stat cstat;
+ CLIENT *clnt = NULL;
+ int sock = RPC_ANYSOCK;
+
+ hent = gethostbyname(host);
+ if (!hent)
+ return -1;
+ if (hent->h_length > (int)sizeof(rhost.sin_addr))
+ return -1;
+
+ tout.tv_sec = 6;
+ tout.tv_usec = 0;
+
+ *(long *)&rhost.sin_addr = *(long *)hent->h_addr;
+ rhost.sin_family = AF_INET;
+ rhost.sin_port = 0;
+
+ clnt = clntudp_create(&rhost, RQUOTAPROG, RQUOTAVERS, tout, &sock);
+ if (!clnt)
+ return rpc_createerr.cf_stat;
+
+ clnt->cl_auth = authunix_create_default();
+ tout.tv_sec = 25;
+ tout.tv_usec = 0;
+ cstat = clnt_call(clnt, RQUOTAPROC_GETQUOTA, xdr_getquota_args,
+ qargs, xdr_getquota_rslt, qres, tout);
+ clnt_destroy(clnt);
+ return cstat;
+ }
+
+ static void rquotad_quota_root_error_alloc(struct rquotad_quota_root *root,
+ const char *errmsg, ...)
+ {
+ struct rquotad_quota *quota = (struct rquotad_quota *)root->root.quota;
+ va_list args;
+ va_start(args, errmsg);
+ quota->error = p_strdup_vprintf(root->pool, errmsg, args);
+ va_end(args);
+ }
+
+ static void rquotad_quota_root_update(struct rquotad_quota_root *root)
+ {
+ struct statfs statfsbuf;
+ struct getquota_args qargs;
+ struct getquota_rslt qres;
+ char *remotehost, *remotepath;
+ char *c;
+
+ if (root->state == RQUOTAD_OFF) {
+ return;
+ }
+
+ if (statfs(root->name, &statfsbuf) < 0) {
+ rquotad_quota_root_error_alloc(root, strerror(errno));
+ root->state = RQUOTAD_ERR;
+ return;
+ }
+
+ if (statfsbuf.f_flags & MNT_LOCAL) {
+ root->state = RQUOTAD_OFF;
+ return;
+ }
+
+ /*
+ * must be some form of "hostname:/path"
+ */
+ c = strchr(statfsbuf.f_mntfromname, ':');
+ if (c == NULL) {
+ rquotad_quota_root_error_alloc(root,
+ "cannot find hostname for %s",
+ statfsbuf.f_mntfromname);
+ root->state = RQUOTAD_ERR;
+ return;
+ }
+
+ *c = '\0';
+ remotehost = strdup(statfsbuf.f_mntfromname);
+ remotepath = strdup(c + 1);
+ *c = ':';
+
+ if (remotepath[0] != '/') {
+ rquotad_quota_root_error_alloc(root, "weird filesystem %s",
+ statfsbuf.f_mntfromname);
+ root->state = RQUOTAD_ERR;
+ return;
+ }
+
+ qargs.gqa_pathp = remotepath;
+ qargs.gqa_uid = getuid();
+
+ if (rquota_get(remotehost, &qargs, &qres) != 0) {
+ return;
+ }
+
+ switch (qres.status) {
+ case Q_OK:
+ root->kbyte_limit + (qres.getquota_rslt_u.gqr_rquota.rq_bhardlimit
*
+ qres.getquota_rslt_u.gqr_rquota.rq_bsize) / 1024;
+ root->kbyte_usage + (qres.getquota_rslt_u.gqr_rquota.rq_curblocks *
+ qres.getquota_rslt_u.gqr_rquota.rq_bsize) / 1024;
+ /* inodes */
+ root->inode_limit + qres.getquota_rslt_u.gqr_rquota.rq_fhardlimit;
+ root->inode_usage + qres.getquota_rslt_u.gqr_rquota.rq_curfiles;
+ root->state = RQUOTAD_ON;
+ return;
+ case Q_NOQUOTA:
+ root->state = RQUOTAD_OFF;
+ return;
+ case Q_EPERM:
+ rquotad_quota_root_error_alloc(root,
+ "quota permission error, host/path %s/%s",
+ remotehost, remotepath);
+ root->state = RQUOTAD_ERR;
+ return;
+ }
+ rquotad_quota_root_error_alloc(root,
+ "bad rpc result %d, host/path %s/%s",
+ qres.status, remotehost, remotepath);
+ root->state = RQUOTAD_ERR;
+ }
+
+
+
diff -crN dovecot-1.0.beta2/src/plugins/quota/quota.c
dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/quota.c
*** dovecot-1.0.beta2/src/plugins/quota/quota.c Fri Jan 13 21:20:18 2006
--- dovecot-1.0.beta2.mail.location+quota/src/plugins/quota/quota.c Wed Feb 1
00:00:09 2006
***************
*** 10,22 ****
extern struct quota dirsize_quota;
extern struct quota dict_quota;
extern struct quota fs_quota;
static struct quota *quota_classes[] = {
&dirsize_quota,
&dict_quota,
#ifdef HAVE_FS_QUOTA
! &fs_quota
#endif
};
#define QUOTA_CLASS_COUNT (sizeof(quota_classes)/sizeof(quota_classes[0]))
--- 10,24 ----
extern struct quota dirsize_quota;
extern struct quota dict_quota;
extern struct quota fs_quota;
+ extern struct quota rquotad_quota;
static struct quota *quota_classes[] = {
&dirsize_quota,
&dict_quota,
#ifdef HAVE_FS_QUOTA
! &fs_quota,
#endif
+ &rquotad_quota,
};
#define QUOTA_CLASS_COUNT (sizeof(quota_classes)/sizeof(quota_classes[0]))