[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