[Dovecot] Re: Group-based filesystem quota
Geo Carncross
geocar-dovecot at internetconnection.net
Fri Jun 23 17:53:58 EEST 2006
I posted some code on June 12 that can support v2 quota on systems that
don't have headers for it.
On Fri, 2006-06-23 at 15:36 +0900, Alan Premselaar wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Scott,
>
> Do you know if this will address issues with v2 quota support on RHEL 3
> using ext3 filesystems?
>
> alan
>
> Scott Alter wrote:
> >> Apparently, there is still a bug - for the group support to work, the
> >> user name and the group name must currently be the same.
> >
> > Sorry for all of the emails, but I just fixed this bug. It should
> > really work now. Attached are quota-fs.c and the working patch to the
> > latest CVS version. Please let me know if you have any problems.
> >
> > -Scott
> >
> >
> > ------------------------------------------------------------------------
> >
> > --- quota-fs.c.xfs 2006-06-23 01:36:55.000000000 -0400
> > +++ quota-fs.c 2006-06-23 01:39:26.000000000 -0400
> > @@ -41,6 +41,7 @@
> > struct quota_root root;
> >
> > uid_t uid;
> > + gid_t gid;
> > struct fs_quota_mountpoint *mount;
> > };
> >
> > @@ -61,6 +62,7 @@
> > root->root.name = i_strdup(name);
> > root->root.v = quota_backend_fs.v;
> > root->uid = geteuid();
> > + root->gid = getegid();
> >
> > return &root->root;
> > }
> > @@ -166,7 +168,11 @@
> > static const char *const *
> > fs_quota_root_get_resources(struct quota_root *root __attr_unused__)
> > {
> > - static const char *resources[] = { QUOTA_NAME_STORAGE, NULL };
> > + static const char *resources[] = {
> > + QUOTA_NAME_STORAGE,
> > + QUOTA_NAME_MESSAGES,
> > + NULL
> > + };
> >
> > return resources;
> > }
> > @@ -180,72 +186,100 @@
> > #ifdef HAVE_Q_QUOTACTL
> > struct quotctl ctl;
> > #endif
> > + int value_r_t, limit_r_t, value_r_c=0, limit_r_c=0;
> > + char args[] = {USRQUOTA , GRPQUOTA};
> > + int what[] = {root->uid, root->gid};
> > + short i;
> >
> > *value_r = 0;
> > *limit_r = 0;
> >
> > - if (strcasecmp(name, QUOTA_NAME_STORAGE) != 0 || root->mount == NULL)
> > + if (root->mount == NULL)
> > return 0;
> >
> > +
> > + for (i = 0; i < 2; i++) {
> > +
> > #if defined (HAVE_QUOTACTL) && defined(HAVE_SYS_QUOTA_H)
> > - /* Linux */
> > + /* Linux */
> > #ifdef HAVE_LINUX_DQBLK_XFS_H
> > - if (strcmp(root->mount->type, "xfs") == 0) {
> > - /* XFS */
> > - struct fs_disk_quota xdqblk;
> > + if (strcmp(root->mount->type, "xfs") == 0) {
> > + /* XFS */
> > + struct fs_disk_quota xdqblk;
> >
> > - if (quotactl(QCMD(Q_XGETQUOTA, USRQUOTA),
> > - root->mount->device_path,
> > - root->uid, (void *)&xdqblk) < 0) {
> > - i_error("quotactl(Q_XGETQUOTA, %s) failed: %m",
> > - root->mount->device_path);
> > - quota_set_error(_root->setup->quota,
> > - "Internal quota error");
> > - return -1;
> > - }
> > - dqblk.dqb_curblocks = xdqblk.d_bcount << 9;
> > - dqblk.dqb_bsoftlimit = xdqblk.d_blk_softlimit >> 1;
> > - } else
> > + if (quotactl(QCMD(Q_XGETQUOTA, args[i]),
> > + root->mount->device_path,
> > + what[i], (void *)&xdqblk) < 0) {
> > + i_error("quotactl(Q_XGETQUOTA, %s) failed: %m",
> > + root->mount->device_path);
> > + quota_set_error(_root->setup->quota,
> > + "Internal quota error");
> > + return -1;
> > + }
> > + dqblk.dqb_curblocks = xdqblk.d_bcount << 9;
> > + dqblk.dqb_curinodes = xdqblk.d_icount << 9;
> > + dqblk.dqb_bsoftlimit = xdqblk.d_blk_softlimit >> 1;
> > + dqblk.dqb_isoftlimit = xdqblk.d_ino_softlimit >> 1;
> > + } else
> > #endif
> > - {
> > - /* ext2, ext3 */
> > - if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA),
> > - root->mount->device_path,
> > - root->uid, (void *)&dqblk) < 0) {
> > + {
> > + /* ext2, ext3 */
> > + if (quotactl(QCMD(Q_GETQUOTA, args[i]),
> > + root->mount->device_path,
> > + what[i], (void *)&dqblk) < 0) {
> > + i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
> > + root->mount->device_path);
> > + quota_set_error(_root->setup->quota,
> > + "Internal quota error");
> > + return -1;
> > + }
> > + }
> > +#elif defined(HAVE_QUOTACTL)
> > + /* BSD, AIX */
> > + if (quotactl(root->mount->device_path, QCMD(Q_GETQUOTA, args[i]),
> > + what[i], (void *)&dqblk) < 0) {
> > i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
> > root->mount->device_path);
> > - quota_set_error(_root->setup->quota,
> > - "Internal quota error");
> > + quota_set_error(_root->setup->quota, "Internal quota error");
> > return -1;
> > }
> > - }
> > -#elif defined(HAVE_QUOTACTL)
> > - /* BSD, AIX */
> > - if (quotactl(root->mount->device_path, QCMD(Q_GETQUOTA, USRQUOTA),
> > - root->uid, (void *)&dqblk) < 0) {
> > - i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
> > - root->mount->device_path);
> > - quota_set_error(_root->setup->quota, "Internal quota error");
> > - return -1;
> > - }
> > #else
> > - /* Solaris */
> > - if (root->mount->fd == -1)
> > - return 0;
> > + /* Solaris */
> > + if (root->mount->fd == -1)
> > + return 0;
> >
> > - ctl.op = Q_GETQUOTA;
> > - ctl.uid = root->uid;
> > - ctl.addr = (caddr_t)&dqblk;
> > - if (ioctl(root->mount->fd, Q_QUOTACTL, &ctl) < 0) {
> > - i_error("ioctl(%s, Q_QUOTACTL) failed: %m", root->mount->path);
> > - quota_set_error(_root->setup->quota, "Internal quota error");
> > - return -1;
> > - }
> > + ctl.op = Q_GETQUOTA;
> > + ctl.uid = root->uid;
> > + ctl.addr = (caddr_t)&dqblk;
> > + if (ioctl(root->mount->fd, Q_QUOTACTL, &ctl) < 0) {
> > + i_error("ioctl(%s, Q_QUOTACTL) failed: %m", root->mount->path);
> > + quota_set_error(_root->setup->quota, "Internal quota error");
> > + return -1;
> > + }
> > #endif
> > - *value_r = (uint64_t)dqblk.dqb_curblocks *
> > - (uint64_t)root->mount->blk_size / 1024;
> > - *limit_r = (uint64_t)dqblk.dqb_bsoftlimit *
> > - (uint64_t)root->mount->blk_size / 1024;
> > + if (strcmp(name, QUOTA_NAME_STORAGE) == 0) {
> > + value_r_t = (uint64_t)dqblk.dqb_curblocks *
> > + (uint64_t)root->mount->blk_size / 1024 / 4096;
> > + limit_r_t = (uint64_t)dqblk.dqb_bsoftlimit *
> > + (uint64_t)root->mount->blk_size / 1024 / 4;
> > + } else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) {
> > + value_r_t = (uint64_t)dqblk.dqb_curinodes;
> > + limit_r_t = (uint64_t)dqblk.dqb_isoftlimit;
> > + } else {
> > + return 0;
> > + }
> > +
> > + if ((limit_r_c == 0 && limit_r_t >= 0) ||
> > + (limit_r_t - value_r_t) < (limit_r_c - value_r_c)) {
> > + limit_r_c = limit_r_t;
> > + value_r_c = value_r_t;
> > + }
> > +
> > + }
> > +
> > + *limit_r = limit_r_c;
> > + *value_r = value_r_c;
> > +
> > return 1;
> > }
> >
> >
> >
> > ------------------------------------------------------------------------
> >
> > /* Copyright (C) 2005-2006 Timo Sirainen */
> >
> > /* Only for reporting filesystem quota */
> >
> > #include "lib.h"
> > #include "array.h"
> > #include "str.h"
> > #include "mountpoint.h"
> > #include "quota-private.h"
> > #include "quota-fs.h"
> >
> > #ifdef HAVE_FS_QUOTA
> >
> > #include <stdio.h>
> > #include <stdlib.h>
> > #include <fcntl.h>
> > #include <unistd.h>
> > #include <sys/stat.h>
> > #ifdef HAVE_LINUX_DQBLK_XFS_H
> > # include <linux/dqblk_xfs.h>
> > #endif
> >
> > #ifdef HAVE_STRUCT_DQBLK_CURSPACE
> > # define dqb_curblocks dqb_curspace
> > #endif
> >
> > struct fs_quota_mountpoint {
> > char *mount_path;
> > char *device_path;
> > char *type;
> >
> > unsigned int blk_size;
> >
> > #ifdef HAVE_Q_QUOTACTL
> > int fd;
> > char *path;
> > #endif
> > };
> >
> > struct fs_quota_root {
> > struct quota_root root;
> >
> > uid_t uid;
> > gid_t gid;
> > struct fs_quota_mountpoint *mount;
> > };
> >
> > struct fs_quota_root_iter {
> > struct quota_root_iter iter;
> >
> > bool sent;
> > };
> >
> > extern struct quota_backend quota_backend_fs;
> >
> > static struct quota_root *
> > fs_quota_init(struct quota_setup *setup __attr_unused__, const char *name)
> > {
> > struct fs_quota_root *root;
> >
> > root = i_new(struct fs_quota_root, 1);
> > root->root.name = i_strdup(name);
> > root->root.v = quota_backend_fs.v;
> > root->uid = geteuid();
> > root->gid = getegid();
> >
> > return &root->root;
> > }
> >
> > static void fs_quota_mountpoint_free(struct fs_quota_mountpoint *mount)
> > {
> > #ifdef HAVE_Q_QUOTACTL
> > if (mount->fd != -1) {
> > if (close(mount->fd) < 0)
> > i_error("close(%s) failed: %m", mount->path);
> > }
> > i_free(mount->path);
> > #endif
> >
> > i_free(mount->device_path);
> > i_free(mount->mount_path);
> > i_free(mount->type);
> > i_free(mount);
> > }
> >
> > static void fs_quota_deinit(struct quota_root *_root)
> > {
> > struct fs_quota_root *root = (struct fs_quota_root *)_root;
> >
> > if (root->mount != NULL)
> > fs_quota_mountpoint_free(root->mount);
> > i_free(root->root.name);
> > i_free(root);
> > }
> >
> > static struct fs_quota_mountpoint *fs_quota_mountpoint_get(const char *dir)
> > {
> > struct fs_quota_mountpoint *mount;
> > struct mountpoint point;
> > int ret;
> >
> > ret = mountpoint_get(dir, default_pool, &point);
> > if (ret <= 0)
> > return NULL;
> >
> > mount = i_new(struct fs_quota_mountpoint, 1);
> > mount->blk_size = point.block_size;
> > mount->device_path = point.device_path;
> > mount->mount_path = point.mount_path;
> > mount->type = point.type;
> > return mount;
> > }
> >
> > static bool fs_quota_add_storage(struct quota_root *_root,
> > struct mail_storage *storage)
> > {
> > struct fs_quota_root *root = (struct fs_quota_root *)_root;
> > struct fs_quota_mountpoint *mount;
> > const char *dir;
> > bool is_file;
> >
> > dir = mail_storage_get_mailbox_path(storage, "", &is_file);
> >
> > if (getenv("DEBUG") != NULL)
> > i_info("fs quota add storage dir = %s", dir);
> >
> > mount = fs_quota_mountpoint_get(dir);
> > if (root->mount == NULL) {
> > if (mount == NULL) {
> > /* Not found */
> > return TRUE;
> > }
> > root->mount = mount;
> > } else {
> > bool match = strcmp(root->mount->mount_path,
> > mount->mount_path) == 0;
> >
> > fs_quota_mountpoint_free(mount);
> > if (!match) {
> > /* different mountpoints, can't use this */
> > return FALSE;
> > }
> > mount = root->mount;
> > }
> >
> > if (getenv("DEBUG") != NULL) {
> > i_info("fs quota block device = %s", mount->device_path);
> > i_info("fs quota mount point = %s", mount->mount_path);
> > }
> >
> > #ifdef HAVE_Q_QUOTACTL
> > if (mount->path == NULL) {
> > mount->path = i_strconcat(mount->mount_path, "/quotas", NULL);
> > mount->fd = open(mount->path, O_RDONLY);
> > if (mount->fd == -1 && errno != ENOENT)
> > i_error("open(%s) failed: %m", mount->path);
> > }
> > #endif
> > return TRUE;
> > }
> >
> > static void
> > fs_quota_remove_storage(struct quota_root *root __attr_unused__,
> > struct mail_storage *storage __attr_unused__)
> > {
> > }
> >
> > static const char *const *
> > fs_quota_root_get_resources(struct quota_root *root __attr_unused__)
> > {
> > static const char *resources[] = {
> > QUOTA_NAME_STORAGE,
> > QUOTA_NAME_MESSAGES,
> > NULL
> > };
> >
> > return resources;
> > }
> >
> > static int
> > fs_quota_get_resource(struct quota_root *_root, const char *name,
> > uint64_t *value_r, uint64_t *limit_r)
> > {
> > struct fs_quota_root *root = (struct fs_quota_root *)_root;
> > struct dqblk dqblk;
> > #ifdef HAVE_Q_QUOTACTL
> > struct quotctl ctl;
> > #endif
> > int value_r_t, limit_r_t, value_r_c=0, limit_r_c=0;
> > char args[] = {USRQUOTA , GRPQUOTA};
> > int what[] = {root->uid, root->gid};
> > short i;
> >
> > *value_r = 0;
> > *limit_r = 0;
> >
> > if (root->mount == NULL)
> > return 0;
> >
> >
> > for (i = 0; i < 2; i++) {
> >
> > #if defined (HAVE_QUOTACTL) && defined(HAVE_SYS_QUOTA_H)
> > /* Linux */
> > #ifdef HAVE_LINUX_DQBLK_XFS_H
> > if (strcmp(root->mount->type, "xfs") == 0) {
> > /* XFS */
> > struct fs_disk_quota xdqblk;
> >
> > if (quotactl(QCMD(Q_XGETQUOTA, args[i]),
> > root->mount->device_path,
> > what[i], (void *)&xdqblk) < 0) {
> > i_error("quotactl(Q_XGETQUOTA, %s) failed: %m",
> > root->mount->device_path);
> > quota_set_error(_root->setup->quota,
> > "Internal quota error");
> > return -1;
> > }
> > dqblk.dqb_curblocks = xdqblk.d_bcount << 9;
> > dqblk.dqb_curinodes = xdqblk.d_icount << 9;
> > dqblk.dqb_bsoftlimit = xdqblk.d_blk_softlimit >> 1;
> > dqblk.dqb_isoftlimit = xdqblk.d_ino_softlimit >> 1;
> > } else
> > #endif
> > {
> > /* ext2, ext3 */
> > if (quotactl(QCMD(Q_GETQUOTA, args[i]),
> > root->mount->device_path,
> > what[i], (void *)&dqblk) < 0) {
> > i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
> > root->mount->device_path);
> > quota_set_error(_root->setup->quota,
> > "Internal quota error");
> > return -1;
> > }
> > }
> > #elif defined(HAVE_QUOTACTL)
> > /* BSD, AIX */
> > if (quotactl(root->mount->device_path, QCMD(Q_GETQUOTA, args[i]),
> > what[i], (void *)&dqblk) < 0) {
> > i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
> > root->mount->device_path);
> > quota_set_error(_root->setup->quota, "Internal quota error");
> > return -1;
> > }
> > #else
> > /* Solaris */
> > if (root->mount->fd == -1)
> > return 0;
> >
> > ctl.op = Q_GETQUOTA;
> > ctl.uid = root->uid;
> > ctl.addr = (caddr_t)&dqblk;
> > if (ioctl(root->mount->fd, Q_QUOTACTL, &ctl) < 0) {
> > i_error("ioctl(%s, Q_QUOTACTL) failed: %m", root->mount->path);
> > quota_set_error(_root->setup->quota, "Internal quota error");
> > return -1;
> > }
> > #endif
> > if (strcmp(name, QUOTA_NAME_STORAGE) == 0) {
> > value_r_t = (uint64_t)dqblk.dqb_curblocks *
> > (uint64_t)root->mount->blk_size / 1024 / 4096;
> > limit_r_t = (uint64_t)dqblk.dqb_bsoftlimit *
> > (uint64_t)root->mount->blk_size / 1024 / 4;
> > } else if (strcmp(name, QUOTA_NAME_MESSAGES) == 0) {
> > value_r_t = (uint64_t)dqblk.dqb_curinodes;
> > limit_r_t = (uint64_t)dqblk.dqb_isoftlimit;
> > } else {
> > return 0;
> > }
> >
> > if ((limit_r_c == 0 && limit_r_t >= 0) ||
> > (limit_r_t - value_r_t) < (limit_r_c - value_r_c)) {
> > limit_r_c = limit_r_t;
> > value_r_c = value_r_t;
> > }
> >
> > }
> >
> > *limit_r = limit_r_c;
> > *value_r = value_r_c;
> >
> > return 1;
> > }
> >
> > static int
> > fs_quota_set_resource(struct quota_root *root,
> > const char *name __attr_unused__,
> > uint64_t value __attr_unused__)
> > {
> > quota_set_error(root->setup->quota, MAIL_STORAGE_ERR_NO_PERMISSION);
> > return -1;
> > }
> >
> > static struct quota_root_transaction_context *
> > fs_quota_transaction_begin(struct quota_root *root,
> > struct quota_transaction_context *ctx)
> > {
> > struct quota_root_transaction_context *root_ctx;
> >
> > root_ctx = i_new(struct quota_root_transaction_context, 1);
> > root_ctx->root = root;
> > root_ctx->ctx = ctx;
> > root_ctx->disabled = TRUE;
> > return root_ctx;
> > }
> >
> > static int
> > fs_quota_transaction_commit(struct quota_root_transaction_context *ctx)
> > {
> > i_free(ctx);
> > return 0;
> > }
> >
> > struct quota_backend quota_backend_fs = {
> > "fs",
> >
> > {
> > fs_quota_init,
> > fs_quota_deinit,
> >
> > fs_quota_add_storage,
> > fs_quota_remove_storage,
> >
> > fs_quota_root_get_resources,
> >
> > fs_quota_get_resource,
> > fs_quota_set_resource,
> >
> > fs_quota_transaction_begin,
> > fs_quota_transaction_commit,
> > quota_default_transaction_rollback,
> >
> > quota_default_try_alloc,
> > quota_default_try_alloc_bytes,
> > quota_default_test_alloc_bytes,
> > quota_default_alloc,
> > quota_default_free
> > }
> > };
> >
> > #endif
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.1 (Darwin)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
>
> iD8DBQFEm4vQE2gsBSKjZHQRAhedAKCs+fugNvqM7/k1pNA9UWCzvMhjWwCg58qD
> B1+9+tTV/rFGaBu4XE4vi8M=
> =JzdC
> -----END PGP SIGNATURE-----
--
Internet Connection High Quality Web Hosting
http://www.internetconnection.net/
More information about the dovecot
mailing list