[Dovecot] Re: Group-based filesystem quota
Alan Premselaar
alien at 12inch.com
Fri Jun 23 09:36:01 EEST 2006
-----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-----
More information about the dovecot
mailing list