diff -crN quota/quota-dirsize.c quota-rquotad/quota-dirsize.c *** quota/quota-dirsize.c Tue Mar 15 20:17:50 2005 --- quota-rquotad/quota-dirsize.c Mon Apr 25 10:19:11 2005 *************** *** 1,7 **** /* Copyright (C) 2005 Timo Sirainen */ /* Quota reporting based on simply summing sizes of all files in mailbox ! together. */ #include "lib.h" #include "str.h" --- 1,9 ---- /* Copyright (C) 2005 Timo Sirainen */ /* Quota reporting based on simply summing sizes of all files in mailbox ! together. Compile with: ! gcc -g -W -Wall -Wformat=2 -shared -DQUOTA_TYPE=dirsize_quota quota-plugin.c quota-storage.c quota.c quota-dirsize.c -o imap_quota.so -DHAVE_CONFIG_H -I../.. -I../lib -I../lib-mail -I../lib-imap -I../lib-storage -I../lib-index -I../imap -lrpcsvc ! */ #include "lib.h" #include "str.h" diff -crN quota/quota-plugin.c quota-rquotad/quota-plugin.c *** quota/quota-plugin.c Tue Apr 12 10:26:32 2005 --- quota-rquotad/quota-plugin.c Mon Apr 25 10:18:37 2005 *************** *** 1,6 **** /* ! gcc -g -W -Wall -Wformat=2 -shared quota-plugin.c quota-storage.c quota.c quota-dirsize.c -o /usr/local/lib/dovecot/imap/imap_quota.so -DHAVE_CONFIG_H -I../.. -I../lib -I../lib-mail -I../lib-imap -I../lib-storage -I../imap ! */ #include "common.h" #include "str.h" --- 1,6 ---- /* ! See quota-dirsize.c or other quota-implementation for compile instructions ! */ #include "common.h" #include "str.h" diff -crN quota/quota-rquotad.c quota-rquotad/quota-rquotad.c *** quota/quota-rquotad.c Thu Jan 1 01:00:00 1970 --- quota-rquotad/quota-rquotad.c Mon Apr 25 10:34:39 2005 *************** *** 0 **** --- 1,472 ---- + /* Copyright (C) 2005 Thomas Wouters */ + + /* Quota reporting based on rquotad, loosely based on FreeBSD's quota(1) + program. Compile with: + + gcc -g -W -Wall -Wformat=2 -shared -DQUOTA_TYPE=rquotad_quota quota-plugin.c quota-storage.c quota.c quota-rquotad.c -o imap_quota.so -DHAVE_CONFIG_H -I../.. -I../lib -I../lib-mail -I../lib-imap -I../lib-storage -I../lib-index -I../imap -lrpcsvc + + */ + + #include "lib.h" + #include "str.h" + #include "quota-private.h" + #include "index/index-storage.h" + + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include + #include + #include + #include + + 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; + }; + + 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; + + qroots = t_strsplit(data, ","); + for (qr_idx = 0; qroots[qr_idx] != NULL; qr_idx++) { + /* NOTHING */; + } + 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_root_init(struct rquotad_quota_root *root, + const char *name, + struct rquotad_quota *quota) + { + root->name = p_strdup(quota->pool, name); + root->root.quota = (struct quota *)quota; + root->pool = quota->pool; + root->state = RQUOTAD_UNKNOWN; + } + + static void rquotad_quota_deinit(struct quota *_quota) + { + struct rquotad_quota *quota = (struct rquotad_quota *)_quota; + + pool_unref(quota->pool); + } + + 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; + } + + iter = i_new(struct rquotad_quota_root_iter, 1); + iter->iter.quota = quota; + strncpy(iter->boxfs, statbuf.f_mntonname, MNAMELEN); + 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; + + for (; iter->offset < quota->nroots; iter->offset++) { + root = "a->roots[iter->offset]; + if (strcmp(iter->boxfs, root->name) == 0) { + iter->offset++; + return (struct quota_root *) root; + } + } + return NULL; + } + + static void rquotad_quota_root_iter_deinit(struct quota_root_iter *iter) + { + i_free(iter); + } + + 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 int + rquotad_quota_try_alloc(struct mailbox_transaction_context *ctx __attr_unused__, + struct quota *_quota __attr_unused__, + struct mail *mail __attr_unused__) + { + return 1; + /* struct rquotad_quota *quota = (struct rquotad_quota *)_quota; + struct rquotad_quota_root *root; + uoff_t size; + + root = GET_RIGHT_QUOTA_ROOT_SOMEHOW(quota, ctx); + + rquotad_quota_root_update(root); + if (root->state == RQUOTAD_ERR) { + return -1; + } + if (root->state == RQUOTAD_OFF) { + return 1; + } + + size = mail_get_physical_size(mail) / 1024; + + return (size + root->kbyte_usage) <= root->kbyte_limit; + */ + } + + 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 */ + } + + static void + rquotad_quota_recalculate(struct quota *quota __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_try_alloc, + rquotad_quota_alloc, + rquotad_quota_free, + rquotad_quota_recalculate, + + rquotad_quota_last_error + }; + + 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); + 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 quota/quota.c quota-rquotad/quota.c *** quota/quota.c Tue Jan 11 23:09:19 2005 --- quota-rquotad/quota.c Mon Apr 25 10:15:04 2005 *************** *** 3,13 **** #include "lib.h" #include "quota-private.h" ! extern struct quota dirsize_quota; struct quota *quota_init(const char *data) { ! return dirsize_quota.init(data); } void quota_deinit(struct quota *quota) --- 3,13 ---- #include "lib.h" #include "quota-private.h" ! extern struct quota QUOTA_TYPE; struct quota *quota_init(const char *data) { ! return QUOTA_TYPE.init(data); } void quota_deinit(struct quota *quota) diff -crN quota/quota.h quota-rquotad/quota.h *** quota/quota.h Tue Jan 11 23:12:57 2005 --- quota-rquotad/quota.h Wed Apr 20 17:35:53 2005 *************** *** 5,13 **** struct mailbox_transaction_context; /* Message storage size kilobytes. */ ! #define QUOTA_NAME_STORAGE "storage" /* Number of messages. */ ! #define QUOTA_NAME_MESSAGES "messages" struct quota; struct quota_root; --- 5,13 ---- struct mailbox_transaction_context; /* Message storage size kilobytes. */ ! #define QUOTA_NAME_STORAGE "STORAGE" /* Number of messages. */ ! #define QUOTA_NAME_MESSAGES "MESSAGES" struct quota; struct quota_root;