[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