changeset 19486:b4f2b8615da9

quota-fs: Recalculate relative quota rules when FS limit changes are detected.
author Timo Sirainen <tss@iki.fi>
date Tue, 08 Dec 2015 10:18:48 +0200
parents f979ca66a758
children ba0dd0dcc223
files src/plugins/quota/quota-fs.c
diffstat 1 files changed, 157 insertions(+), 146 deletions(-) [+]
line wrap: on
line diff
--- a/src/plugins/quota/quota-fs.c	Tue Dec 08 10:17:10 2015 +0200
+++ b/src/plugins/quota/quota-fs.c	Tue Dec 08 10:18:48 2015 +0200
@@ -318,32 +318,34 @@
 
 #ifdef HAVE_RQUOTA
 static void
-rquota_get_result(const rquota *rq, bool bytes,
-		  uint64_t *value_r, uint64_t *limit_r)
+rquota_get_result(const rquota *rq,
+		  uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
+		  uint64_t *count_value_r, uint64_t *count_limit_r)
 {
 	/* use soft limits if they exist, fallback to hard limits */
-	if (bytes) {
-		/* convert the results from blocks to bytes */
-		*value_r = (uint64_t)rq->rq_curblocks *
+
+	/* convert the results from blocks to bytes */
+	*bytes_value_r = (uint64_t)rq->rq_curblocks *
+		(uint64_t)rq->rq_bsize;
+	if (rq->rq_bsoftlimit != 0) {
+		*bytes_limit_r = (uint64_t)rq->rq_bsoftlimit *
 			(uint64_t)rq->rq_bsize;
-		if (rq->rq_bsoftlimit != 0) {
-			*limit_r = (uint64_t)rq->rq_bsoftlimit *
-				(uint64_t)rq->rq_bsize;
-		} else {
-			*limit_r = (uint64_t)rq->rq_bhardlimit *
-				(uint64_t)rq->rq_bsize;
-		}
 	} else {
-		*value_r = rq->rq_curfiles;
-		if (rq->rq_fsoftlimit != 0)
-			*limit_r = rq->rq_fsoftlimit;
-		else
-			*limit_r = rq->rq_fhardlimit;
+		*bytes_limit_r = (uint64_t)rq->rq_bhardlimit *
+			(uint64_t)rq->rq_bsize;
 	}
+
+	*count_value_r = rq->rq_curfiles;
+	if (rq->rq_fsoftlimit != 0)
+		*count_limit_r = rq->rq_fsoftlimit;
+	else
+		*count_limit_r = rq->rq_fhardlimit;
 }
 
-static int do_rquota_user(struct fs_quota_root *root, bool bytes,
-			  uint64_t *value_r, uint64_t *limit_r)
+static int
+do_rquota_user(struct fs_quota_root *root,
+	       uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
+	       uint64_t *count_value_r, uint64_t *count_limit_r)
 {
 	struct getquota_rslt result;
 	struct getquota_args args;
@@ -369,9 +371,8 @@
 	}
 
 	if (root->root.quota->set->debug) {
-		i_debug("quota-fs: host=%s, path=%s, uid=%s, %s",
-			host, path, dec2str(root->uid),
-			bytes ? "bytes" : "files");
+		i_debug("quota-fs: host=%s, path=%s, uid=%s",
+			host, path, dec2str(root->uid));
 	}
 
 	/* clnt_create() polls for a while to establish a connection */
@@ -411,13 +412,16 @@
 
 	switch (result.status) {
 	case Q_OK: {
-		rquota_get_result(&result.getquota_rslt_u.gqr_rquota, bytes,
-				  value_r, limit_r);
+		rquota_get_result(&result.getquota_rslt_u.gqr_rquota,
+				  bytes_value_r, bytes_limit_r,
+				  count_value_r, count_limit_r);
 		if (root->root.quota->set->debug) {
-			i_debug("quota-fs: uid=%s, value=%llu, limit=%llu",
+			i_debug("quota-fs: uid=%s, bytes=%llu/%llu files=%llu/%llu",
 				dec2str(root->uid),
-				(unsigned long long)*value_r,
-				(unsigned long long)*limit_r);
+				(unsigned long long)*bytes_value_r,
+				(unsigned long long)*bytes_limit_r,
+				(unsigned long long)*count_value_r,
+				(unsigned long long)*count_limit_r);
 		}
 		return 1;
 	}
@@ -438,8 +442,11 @@
 }
 
 static int
-do_rquota_group(struct fs_quota_root *root ATTR_UNUSED, bool bytes ATTR_UNUSED,
-		uint64_t *value_r ATTR_UNUSED, uint64_t *limit_r ATTR_UNUSED)
+do_rquota_group(struct fs_quota_root *root ATTR_UNUSED,
+		uint64_t *bytes_value_r ATTR_UNUSED,
+		uint64_t *bytes_limit_r ATTR_UNUSED,
+		uint64_t *count_value_r ATTR_UNUSED,
+		uint64_t *count_limit_r ATTR_UNUSED)
 {
 #if defined(EXT_RQUOTAVERS) && defined(GRPQUOTA)
 	struct getquota_rslt result;
@@ -458,9 +465,8 @@
 	path++;
 
 	if (root->root.quota->set->debug) {
-		i_debug("quota-fs: host=%s, path=%s, gid=%s, %s",
-			host, path, dec2str(root->gid),
-			bytes ? "bytes" : "files");
+		i_debug("quota-fs: host=%s, path=%s, gid=%s",
+			host, path, dec2str(root->gid));
 	}
 
 	/* clnt_create() polls for a while to establish a connection */
@@ -501,13 +507,16 @@
 
 	switch (result.status) {
 	case Q_OK: {
-		rquota_get_result(&result.getquota_rslt_u.gqr_rquota, bytes,
-				  value_r, limit_r);
+		rquota_get_result(&result.getquota_rslt_u.gqr_rquota,
+				  bytes_value_r, bytes_limit_r,
+				  count_value_r, count_limit_r);
 		if (root->root.quota->set->debug) {
-			i_debug("quota-fs: gid=%s, value=%llu, limit=%llu",
+			i_debug("quota-fs: gid=%s, bytes=%llu/%llu files=%llu/%llu",
 				dec2str(root->gid),
-				(unsigned long long)*value_r,
-				(unsigned long long)*limit_r);
+				(unsigned long long)*bytes_value_r,
+				(unsigned long long)*bytes_limit_r,
+				(unsigned long long)*count_value_r,
+				(unsigned long long)*count_limit_r);
 		}
 		return 1;
 	}
@@ -545,8 +554,9 @@
 
 #ifdef FS_QUOTA_LINUX
 static int
-fs_quota_get_linux(struct fs_quota_root *root, bool group, bool bytes,
-		   uint64_t *value_r, uint64_t *limit_r)
+fs_quota_get_linux(struct fs_quota_root *root, bool group,
+		   uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
+		   uint64_t *count_value_r, uint64_t *count_limit_r)
 {
 	struct dqblk dqblk;
 	int type, id;
@@ -570,19 +580,16 @@
 			return -1;
 		}
 
-		if (bytes) {
-			/* values always returned in 512 byte blocks */
-			*value_r = xdqblk.d_bcount * 512;
-			*limit_r = xdqblk.d_blk_softlimit * 512;
-			if (*limit_r == 0) {
-				*limit_r = xdqblk.d_blk_hardlimit * 512;
-			}
-		} else {
-			*value_r = xdqblk.d_icount;
-			*limit_r = xdqblk.d_ino_softlimit;
-			if (*limit_r == 0) {
-				*limit_r = xdqblk.d_ino_hardlimit;
-			}
+		/* values always returned in 512 byte blocks */
+		*bytes_value_r = xdqblk.d_bcount * 512;
+		*bytes_limit_r = xdqblk.d_blk_softlimit * 512;
+		if (*bytes_limit_r == 0) {
+			*bytes_limit_r = xdqblk.d_blk_hardlimit * 512;
+		}
+		*count_value_r = xdqblk.d_icount;
+		*count_limit_r = xdqblk.d_ino_softlimit;
+		if (*count_limit_r == 0) {
+			*count_limit_r = xdqblk.d_ino_hardlimit;
 		}
 	} else
 #endif
@@ -607,22 +614,19 @@
 			return -1;
 		}
 
-		if (bytes) {
 #if _LINUX_QUOTA_VERSION == 1
-			*value_r = dqblk.dqb_curblocks * 1024;
+		*bytes_value_r = dqblk.dqb_curblocks * 1024;
 #else
-			*value_r = dqblk.dqb_curblocks;
+		*bytes_value_r = dqblk.dqb_curblocks;
 #endif
-			*limit_r = dqblk.dqb_bsoftlimit * 1024;
-			if (*limit_r == 0) {
-				*limit_r = dqblk.dqb_bhardlimit * 1024;
-			}
-		} else {
-			*value_r = dqblk.dqb_curinodes;
-			*limit_r = dqblk.dqb_isoftlimit;
-			if (*limit_r == 0) {
-				*limit_r = dqblk.dqb_ihardlimit;
-			}
+		*bytes_limit_r = dqblk.dqb_bsoftlimit * 1024;
+		if (*bytes_limit_r == 0) {
+			*bytes_limit_r = dqblk.dqb_bhardlimit * 1024;
+		}
+		*count_value_r = dqblk.dqb_curinodes;
+		*count_limit_r = dqblk.dqb_isoftlimit;
+		if (*count_limit_r == 0) {
+			*count_limit_r = dqblk.dqb_ihardlimit;
 		}
 	}
 	return 1;
@@ -631,8 +635,9 @@
 
 #ifdef FS_QUOTA_BSDAIX
 static int
-fs_quota_get_bsdaix(struct fs_quota_root *root, bool group, bool bytes,
-		    uint64_t *value_r, uint64_t *limit_r)
+fs_quota_get_bsdaix(struct fs_quota_root *root, bool group,
+		    uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
+		    uint64_t *count_value_r, uint64_t *count_limit_r)
 {
 	struct dqblk dqblk;
 	int type, id;
@@ -650,18 +655,15 @@
 			root->mount->mount_path);
 		return -1;
 	}
-	if (bytes) {
-		*value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE;
-		*limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE;
-		if (*limit_r == 0) {
-			*limit_r = (uint64_t)dqblk.dqb_bhardlimit * DEV_BSIZE;
-		}
-	} else {
-		*value_r = dqblk.dqb_curinodes;
-		*limit_r = dqblk.dqb_isoftlimit;
-		if (*limit_r == 0) {
-			*limit_r = dqblk.dqb_ihardlimit;
-		}
+	*bytes_value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE;
+	*bytes_limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE;
+	if (*bytes_limit_r == 0) {
+		*bytes_limit_r = (uint64_t)dqblk.dqb_bhardlimit * DEV_BSIZE;
+	}
+	*count_value_r = dqblk.dqb_curinodes;
+	*count_limit_r = dqblk.dqb_isoftlimit;
+	if (*count_limit_r == 0) {
+		*count_limit_r = dqblk.dqb_ihardlimit;
 	}
 	return 1;
 }
@@ -669,8 +671,9 @@
 
 #ifdef FS_QUOTA_NETBSD
 static int
-fs_quota_get_netbsd(struct fs_quota_root *root, bool group, bool bytes,
-		    uint64_t *value_r, uint64_t *limit_r)
+fs_quota_get_netbsd(struct fs_quota_root *root, bool group,
+		    uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
+		    uint64_t *count_value_r, uint64_t *count_limit_r)
 {
 	struct quotakey qk;
 	struct quotaval qv;
@@ -686,23 +689,26 @@
 
 	qk.qk_idtype = group ? QUOTA_IDTYPE_GROUP : QUOTA_IDTYPE_USER;
 	qk.qk_id = group ? root->gid : root->uid;
-	qk.qk_objtype = bytes ? QUOTA_OBJTYPE_BLOCKS : QUOTA_OBJTYPE_FILES;
+
+	for (i = 0; i < 2; i++) {
+		qk.qk_objtype = i == 0 ? QUOTA_OBJTYPE_BLOCKS : QUOTA_OBJTYPE_FILES;
 
-	if (quota_get(qh, &qk, &qv) != 0) {
-		if (errno == ESRCH) {
-			fs_quota_root_disable(root, group);
-			return 0;
-		}
-		i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
-			root->mount->mount_path);
-		ret = -1;
-	} else {
-		if (bytes) {
-			*value_r = qv.qv_usage * DEV_BSIZE;
-			*limit_r = qv.qv_softlimit * DEV_BSIZE;
+		if (quota_get(qh, &qk, &qv) != 0) {
+			if (errno == ESRCH) {
+				fs_quota_root_disable(root, group);
+				return 0;
+			}
+			i_error("quotactl(Q_GETQUOTA, %s) failed: %m",
+				root->mount->mount_path);
+			ret = -1;
+			break;
+		} 
+		if (i == 0) {
+			*bytes_value_r = qv.qv_usage * DEV_BSIZE;
+			*bytes_limit_r = qv.qv_softlimit * DEV_BSIZE;
 		} else {
-			*value_r = qv.qv_usage;
-			*limit_r = qv.qv_softlimit;
+			*count_value_r = qv.qv_usage;
+			*count_limit_r = qv.qv_softlimit;
 		}
 		ret = 1;
 	}
@@ -713,8 +719,9 @@
 
 #ifdef FS_QUOTA_HPUX
 static int
-fs_quota_get_hpux(struct fs_quota_root *root, bool bytes,
-		  uint64_t *value_r, uint64_t *limit_r)
+fs_quota_get_hpux(struct fs_quota_root *root,
+		  uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
+		  uint64_t *count_value_r, uint64_t *count_limit_r)
 {
 	struct dqblk dqblk;
 
@@ -729,21 +736,18 @@
 		return -1;
 	}
 
-	if (bytes) {
-		*value_r = (uint64_t)dqblk.dqb_curblocks *
-			root->mount->block_size;
-		*limit_r = (uint64_t)dqblk.dqb_bsoftlimit *
+	*bytes_value_r = (uint64_t)dqblk.dqb_curblocks *
+		root->mount->block_size;
+	*bytes_limit_r = (uint64_t)dqblk.dqb_bsoftlimit *
+		root->mount->block_size;
+	if (*bytes_limit_r == 0) {
+		*bytes_limit_r = (uint64_t)dqblk.dqb_bhardlimit *
 			root->mount->block_size;
-		if (*limit_r == 0) {
-			*limit_r = (uint64_t)dqblk.dqb_bhardlimit *
-				root->mount->block_size;
-		}
-	} else {
-		*value_r = dqblk.dqb_curfiles;
-		*limit_r = dqblk.dqb_fsoftlimit;
-		if (*limit_r == 0) {
-			*limit_r = dqblk.dqb_fhardlimit;
-		}
+	}
+	*count_value_r = dqblk.dqb_curfiles;
+	*count_limit_r = dqblk.dqb_fsoftlimit;
+	if (*count_limit_r == 0) {
+		*count_limit_r = dqblk.dqb_fhardlimit;
 	}
 	return 1;
 }
@@ -751,8 +755,9 @@
 
 #ifdef FS_QUOTA_SOLARIS
 static int
-fs_quota_get_solaris(struct fs_quota_root *root, bool bytes,
-		     uint64_t *value_r, uint64_t *limit_r)
+fs_quota_get_solaris(struct fs_quota_root *root,
+		     uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
+		     uint64_t *count_value_r, uint64_t *count_limit_r)
 {
 	struct dqblk dqblk;
 	struct quotctl ctl;
@@ -767,26 +772,24 @@
 		i_error("ioctl(%s, Q_QUOTACTL) failed: %m", root->mount->path);
 		return -1;
 	}
-	if (bytes) {
-		*value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE;
-		*limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE;
-		if (*limit_r == 0) {
-			*limit_r = (uint64_t)dqblk.dqb_bhardlimit * DEV_BSIZE;
-		}
-	} else {
-		*value_r = dqblk.dqb_curfiles;
-		*limit_r = dqblk.dqb_fsoftlimit;
-		if (*limit_r == 0) {
-			*limit_r = dqblk.dqb_fhardlimit;
-		}
+	*bytes_value_r = (uint64_t)dqblk.dqb_curblocks * DEV_BSIZE;
+	*bytes_limit_r = (uint64_t)dqblk.dqb_bsoftlimit * DEV_BSIZE;
+	if (*bytes_limit_r == 0) {
+		*bytes_limit_r = (uint64_t)dqblk.dqb_bhardlimit * DEV_BSIZE;
+	}
+	*count_value_r = dqblk.dqb_curfiles;
+	*count_limit_r = dqblk.dqb_fsoftlimit;
+	if (*count_limit_r == 0) {
+		*count_limit_r = dqblk.dqb_fhardlimit;
 	}
 	return 1;
 }
 #endif
 
 static int
-fs_quota_get_one_resource(struct fs_quota_root *root, bool group, bool bytes,
-			  uint64_t *value_r, uint64_t *limit_r)
+fs_quota_get_resources(struct fs_quota_root *root, bool group,
+		       uint64_t *bytes_value_r, uint64_t *bytes_limit_r,
+		       uint64_t *count_value_r, uint64_t *count_limit_r)
 {
 	if (group) {
 		if (root->group_disabled)
@@ -796,20 +799,20 @@
 			return 0;
 	}
 #ifdef FS_QUOTA_LINUX
-	return fs_quota_get_linux(root, group, bytes, value_r, limit_r);
+	return fs_quota_get_linux(root, group, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r);
 #elif defined (FS_QUOTA_NETBSD)
-	return fs_quota_get_netbsd(root, group, bytes, value_r, limit_r);
+	return fs_quota_get_netbsd(root, group, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r);
 #elif defined (FS_QUOTA_BSDAIX)
-	return fs_quota_get_bsdaix(root, group, bytes, value_r, limit_r);
+	return fs_quota_get_bsdaix(root, group, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r);
 #else
 	if (group) {
 		/* not supported */
 		return 0;
 	}
 #ifdef FS_QUOTA_HPUX
-	return fs_quota_get_hpux(root, bytes, value_r, limit_r);
+	return fs_quota_get_hpux(root, bytes, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r);
 #else
-	return fs_quota_get_solaris(root, bytes, value_r, limit_r);
+	return fs_quota_get_solaris(root, bytes, bytes_value_r, bytes_limit_r, count_value_r, count_limit_r);
 #endif
 #endif
 }
@@ -852,8 +855,8 @@
 		      uint64_t *value_r)
 {
 	struct fs_quota_root *root = (struct fs_quota_root *)_root;
-	uint64_t limit = 0;
-	bool bytes;
+	uint64_t bytes_value, count_value;
+	uint64_t bytes_limit = 0, count_limit = 0;
 	int ret;
 
 	*value_r = 0;
@@ -862,34 +865,42 @@
 	    (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) != 0 &&
 	     strcasecmp(name, QUOTA_NAME_MESSAGES) != 0))
 		return 0;
-	bytes = strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) == 0;
 
 #ifdef HAVE_RQUOTA
 	if (mount_type_is_nfs(root->mount)) {
 		T_BEGIN {
 			ret = !root->user_disabled ?
-				do_rquota_user(root, bytes, value_r, &limit) :
-				do_rquota_group(root, bytes, value_r, &limit);
+				do_rquota_user(root, &bytes_value, &bytes_limit, &count_value, &count_limit) :
+				do_rquota_group(root, &bytes_value, &bytes_limit, &count_value, &count_limit);
 		} T_END;
 	} else
 #endif
 	{
-		ret = fs_quota_get_one_resource(root, FALSE, bytes,
-						value_r, &limit);
+		ret = fs_quota_get_resources(root, FALSE, &bytes_value, &bytes_limit,
+					     &count_value, &count_limit);
 		if (ret == 0) {
 			/* fallback to group quota */
-			ret = fs_quota_get_one_resource(root, TRUE, bytes,
-							value_r, &limit);
+			ret = fs_quota_get_resources(root, TRUE, &bytes_value, &bytes_limit,
+						     &count_value, &count_limit);
 		}
 	}
 	if (ret <= 0)
 		return ret;
 
-	/* update limit */
-	if (bytes)
-		_root->bytes_limit = limit;
+	if (strcasecmp(name, QUOTA_NAME_STORAGE_BYTES) == 0)
+		*value_r = bytes_value;
 	else
-		_root->count_limit = limit;
+		*value_r = count_value;
+	if (_root->bytes_limit != (int64_t)bytes_limit ||
+	    _root->count_limit != (int64_t)count_limit) {
+		/* update limit */
+		_root->bytes_limit = bytes_limit;
+		_root->count_limit = count_limit;
+
+		/* limits have changed, so we'll need to recalculate
+		   relative quota rules */
+		quota_root_recalculate_relative_rules(_root->set, bytes_limit, count_limit);
+	}
 	return 1;
 }