From a52bf25b0b27d425c70960bb998430f3664a6bd9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Arkadiusz=20Mi=C5=9Bkiewicz?= <arekm@maven.pl>
Date: Thu, 9 Dec 2021 12:53:52 +0100
Subject: [PATCH] xfs_quota: Support XFS_GETNEXTQUOTA in range calls
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Use XFS_GETNEXTQUOTA in range call with -L/-U limits. This makes them
perform very fast comparing to current iteration over each (existing
and not existing) ID.

dump_file() and report_mount() take upper agrument which is taken
into account only when GETNEXTQUOTA_MATCH_FLAG is requested and don't
print anything if upper limit is crossed.

Signed-off-by: Arkadiusz Miśkiewicz <arekm@maven.pl>
---
 quota/quota.h  |   1 +
 quota/report.c | 118 ++++++++++++++++++++++++++++++-------------------
 2 files changed, 74 insertions(+), 45 deletions(-)

diff --git a/quota/quota.h b/quota/quota.h
index 78b0d66d..310812f4 100644
--- a/quota/quota.h
+++ b/quota/quota.h
@@ -65,6 +65,7 @@ enum {
 	ABSOLUTE_FLAG =		0x0200, /* absolute time, not related to now */
 	NO_LOOKUP_FLAG =	0x0400, /* skip name lookups, just report ID */
 	GETNEXTQUOTA_FLAG =	0x0800, /* use getnextquota quotactl */
+	GETNEXTQUOTA_MATCH_FLAG =	0x1000,	/* report only if matches range */
 };
 
 /*
diff --git a/quota/report.c b/quota/report.c
index 6ac55490..39b5286b 100644
--- a/quota/report.c
+++ b/quota/report.c
@@ -65,6 +65,7 @@ static int
 dump_file(
 	FILE		*fp,
 	uint		id,
+	uint		upper,
 	uint		*oid,
 	uint		type,
 	char		*dev,
@@ -91,6 +92,9 @@ dump_file(
 		/* Did kernelspace wrap? */
 		if (*oid < id)
 			return 0;
+		/* Range limit was requested */
+		if (flags & GETNEXTQUOTA_MATCH_FLAG && *oid > upper)
+			return 0;
 	}
 
 	if (!d.d_blk_softlimit && !d.d_blk_hardlimit &&
@@ -137,29 +141,36 @@ dump_limits_any_type(
 		return;
 	}
 
-	/* Range was specified; query everything in it */
-	if (upper) {
-		for (id = lower; id <= upper; id++)
-			dump_file(fp, id, NULL, type, mount->fs_name, 0);
-		return;
-	}
+	/* Range was specified */
+	if (lower)
+		id = lower;
 
 	/* Use GETNEXTQUOTA if it's available */
-	if (dump_file(fp, id, &oid, type, mount->fs_name, GETNEXTQUOTA_FLAG)) {
+	if (dump_file(fp, id, upper, &oid, type, mount->fs_name,
+				GETNEXTQUOTA_FLAG|GETNEXTQUOTA_MATCH_FLAG)) {
 		id = oid + 1;
-		while (dump_file(fp, id, &oid, type, mount->fs_name,
-				 GETNEXTQUOTA_FLAG))
+		while (dump_file(fp, id, upper, &oid, type, mount->fs_name,
+				 GETNEXTQUOTA_FLAG|GETNEXTQUOTA_MATCH_FLAG))
 			id = oid + 1;
 		return;
         }
 
 	/* Otherwise fall back to iterating over each uid/gid/prjid */
+
+	/* Range was specified; query everything in it */
+	if (upper) {
+		for (id = lower; id <= upper; id++)
+			dump_file(fp, id, 0, NULL, type, mount->fs_name, 0);
+		return;
+	}
+
+	/* No range */
 	switch (type) {
 	case XFS_GROUP_QUOTA: {
 			struct group *g;
 			setgrent();
 			while ((g = getgrent()) != NULL)
-				dump_file(fp, g->gr_gid, NULL, type,
+				dump_file(fp, 0, g->gr_gid, NULL, type,
 					  mount->fs_name, 0);
 			endgrent();
 			break;
@@ -168,7 +179,7 @@ dump_limits_any_type(
 			struct fs_project *p;
 			setprent();
 			while ((p = getprent()) != NULL)
-				dump_file(fp, p->pr_prid, NULL, type,
+				dump_file(fp, 0, p->pr_prid, NULL, type,
 					  mount->fs_name, 0);
 			endprent();
 			break;
@@ -177,7 +188,7 @@ dump_limits_any_type(
 			struct passwd *u;
 			setpwent();
 			while ((u = getpwent()) != NULL)
-				dump_file(fp, u->pw_uid, NULL, type,
+				dump_file(fp, 0, u->pw_uid, NULL, type,
 					  mount->fs_name, 0);
 			endpwent();
 			break;
@@ -322,6 +333,7 @@ static int
 report_mount(
 	FILE		*fp,
 	uint32_t	id,
+	uint32_t	upper,
 	char		*name,
 	uint32_t	*oid,
 	uint		form,
@@ -355,6 +367,9 @@ report_mount(
 		/* Did kernelspace wrap? */
 		if (* oid < id)
 			return 0;
+		 /* Upper range was specified */
+		if (upper && * oid > upper)
+			return 0;
 	}
 
 	if (flags & TERSE_FLAG) {
@@ -479,26 +494,31 @@ report_user_mount(
 	struct passwd	*u;
 	uint		id = 0, oid;
 
-	if (upper) {	/* identifier range specified */
-		for (id = lower; id <= upper; id++) {
-			if (report_mount(fp, id, NULL, NULL,
-					form, XFS_USER_QUOTA, mount, flags))
-				flags |= NO_HEADER_FLAG;
-		}
-	} else if (report_mount(fp, id, NULL, &oid, form,
+	/* Range was specified */
+	if (lower)
+		id = lower;
+	if (upper)
+		flags |= GETNEXTQUOTA_MATCH_FLAG;
+
+	if (report_mount(fp, id, upper, NULL, &oid, form,
 				XFS_USER_QUOTA, mount,
 				flags|GETNEXTQUOTA_FLAG)) {
 		id = oid + 1;
 		flags |= GETNEXTQUOTA_FLAG;
 		flags |= NO_HEADER_FLAG;
-		while (report_mount(fp, id, NULL, &oid, form, XFS_USER_QUOTA,
-				    mount, flags)) {
+		while (report_mount(fp, id, upper, NULL, &oid, form, XFS_USER_QUOTA,
+				    mount, flags))
 			id = oid + 1;
+	} else if (upper) {	/* identifier range specified */
+		for (id = lower; id <= upper; id++) {
+			if (report_mount(fp, id, upper, NULL, NULL,
+					form, XFS_USER_QUOTA, mount, flags))
+				flags |= NO_HEADER_FLAG;
 		}
 	} else {
 		setpwent();
 		while ((u = getpwent()) != NULL) {
-			if (report_mount(fp, u->pw_uid, u->pw_name, NULL,
+			if (report_mount(fp, u->pw_uid, 0, u->pw_name, NULL,
 					form, XFS_USER_QUOTA, mount, flags))
 				flags |= NO_HEADER_FLAG;
 		}
@@ -521,26 +541,30 @@ report_group_mount(
 	struct group	*g;
 	uint		id = 0, oid;
 
-	if (upper) {	/* identifier range specified */
-		for (id = lower; id <= upper; id++) {
-			if (report_mount(fp, id, NULL, NULL,
-					form, XFS_GROUP_QUOTA, mount, flags))
-				flags |= NO_HEADER_FLAG;
-		}
-	} else if (report_mount(fp, id, NULL, &oid, form,
-				XFS_GROUP_QUOTA, mount,
+	/* Range was specified */
+	if (lower)
+		id = lower;
+	if (upper)
+		flags |= GETNEXTQUOTA_MATCH_FLAG;
+
+	if (report_mount(fp, id, upper, NULL, &oid, form, XFS_GROUP_QUOTA, mount,
 				flags|GETNEXTQUOTA_FLAG)) {
 		id = oid + 1;
 		flags |= GETNEXTQUOTA_FLAG;
 		flags |= NO_HEADER_FLAG;
-		while (report_mount(fp, id, NULL, &oid, form, XFS_GROUP_QUOTA,
-				    mount, flags)) {
+		while (report_mount(fp, id, upper, NULL, &oid, form, XFS_GROUP_QUOTA,
+				    mount, flags))
 			id = oid + 1;
+	}  else if (upper) {	/* identifier range specified */
+		for (id = lower; id <= upper; id++) {
+			if (report_mount(fp, id, upper, NULL, NULL,
+					form, XFS_GROUP_QUOTA, mount, flags))
+				flags |= NO_HEADER_FLAG;
 		}
 	} else {
 		setgrent();
 		while ((g = getgrent()) != NULL) {
-			if (report_mount(fp, g->gr_gid, g->gr_name, NULL,
+			if (report_mount(fp, g->gr_gid, 0, g->gr_name, NULL,
 					form, XFS_GROUP_QUOTA, mount, flags))
 				flags |= NO_HEADER_FLAG;
 		}
@@ -562,21 +586,25 @@ report_project_mount(
 	fs_project_t	*p;
 	uint		id = 0, oid;
 
-	if (upper) {	/* identifier range specified */
-		for (id = lower; id <= upper; id++) {
-			if (report_mount(fp, id, NULL, NULL,
-					form, XFS_PROJ_QUOTA, mount, flags))
-				flags |= NO_HEADER_FLAG;
-		}
-	} else if (report_mount(fp, id, NULL, &oid, form,
-				XFS_PROJ_QUOTA, mount,
+	/* Range was specified */
+	if (lower)
+		id = lower;
+	if (upper)
+		flags |= GETNEXTQUOTA_MATCH_FLAG;
+
+	if (report_mount(fp, id, upper, NULL, &oid, form, XFS_PROJ_QUOTA, mount,
 				flags|GETNEXTQUOTA_FLAG)) {
 		id = oid + 1;
 		flags |= GETNEXTQUOTA_FLAG;
 		flags |= NO_HEADER_FLAG;
-		while (report_mount(fp, id, NULL, &oid, form, XFS_PROJ_QUOTA,
-				    mount, flags)) {
+		while (report_mount(fp, id, upper, NULL, &oid, form, XFS_PROJ_QUOTA,
+				    mount, flags))
 			id = oid + 1;
+	} else if (upper) {	/* identifier range specified */
+		for (id = lower; id <= upper; id++) {
+			if (report_mount(fp, id, upper, NULL, NULL,
+					form, XFS_PROJ_QUOTA, mount, flags))
+				flags |= NO_HEADER_FLAG;
 		}
 	} else {
 		if (!getprprid(0)) {
@@ -584,14 +612,14 @@ report_project_mount(
 			 * Print default project quota, even if projid 0
 			 * isn't defined
 			 */
-			if (report_mount(fp, 0, NULL, NULL,
+			if (report_mount(fp, 0, 0, NULL, NULL,
 					form, XFS_PROJ_QUOTA, mount, flags))
 				flags |= NO_HEADER_FLAG;
 		}
 
 		setprent();
 		while ((p = getprent()) != NULL) {
-			if (report_mount(fp, p->pr_prid, p->pr_name, NULL,
+			if (report_mount(fp, p->pr_prid, 0, p->pr_name, NULL,
 					form, XFS_PROJ_QUOTA, mount, flags))
 				flags |= NO_HEADER_FLAG;
 		}
-- 
2.33.1

