From: Jan Kara <jack@suse.cz>
Subject: [PATCH] Implement repquota for filesystems without quota files
References: fate#310056

Some filesystem do not have quota files accessible from userspace. For these
implementing ->scan_dquots() used by repquota is problematic. What we do is
that we iterate over all users and ask for quota information for each of them.
XFS already does this so make its code generic and use it for all filesystems
using quotaio_meta.c - only OCFS2 these days.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 convertquota.c    |    2 +-
 quotaio.c         |   24 +++++-------------
 quotaio_generic.c |   63 +++++++++++++++++++++++++++++++++++++++++++++++++
 quotaio_generic.h |    6 ++++
 quotaio_meta.c    |    6 ++++
 quotaio_xfs.c     |   68 ++++++++++++----------------------------------------
 quotasys.h        |    2 +-
 repquota.c        |    4 +-
 warnquota.c       |    4 +-
 9 files changed, 104 insertions(+), 75 deletions(-)

Index: quota-tools/convertquota.c
===================================================================
--- quota-tools.orig/convertquota.c
+++ quota-tools/convertquota.c
@@ -282,7 +282,7 @@ static int convert_format(int type, stru
 	struct quota_handle *qo;
 	int ret = 0;
 	
-	if (!(qo = init_io(mnt, type, QF_VFSOLD, IOI_OPENFILE))) {
+	if (!(qo = init_io(mnt, type, QF_VFSOLD, IOI_INITSCAN))) {
 		errstr(_("Cannot open old format file for %ss on %s\n"),
 			type2name(type), mnt->mnt_dir);
 		return -1;
Index: quota-tools/quotaio.c
===================================================================
--- quota-tools.orig/quotaio.c
+++ quota-tools/quotaio.c
@@ -103,10 +103,6 @@ struct quota_handle *init_io(struct mnte
 			errstr(_("Quota not supported by the filesystem.\n"));
 			goto out_handle;
 		}
-		if (flags & IOI_OPENFILE) {
-			errstr(_("Operation not supported for filesystems with hidden quota files!\n"));
-			goto out_handle;
-		}
 		h->qh_fd = -1;
 		h->qh_fmt = fmt;
 		goto set_ops;
@@ -114,13 +110,13 @@ struct quota_handle *init_io(struct mnte
 
 	fmt = get_qf_name(mnt, type,
 			  (fmt == -1) ? ((1 << QF_VFSOLD) | (1 << QF_VFSV0)) : (1 << fmt),
-			  (!QIO_ENABLED(h) || flags & IOI_OPENFILE) ? NF_FORMAT : 0,
+			  (!QIO_ENABLED(h) || flags & IOI_INITSCAN) ? NF_FORMAT : 0,
 			  &qfname);
 	if (fmt < 0) {
 		errstr(_("Quota file not found or has wrong format.\n"));
 		goto out_handle;
 	}
-	if (!QIO_ENABLED(h) || flags & IOI_OPENFILE) {	/* Need to open file? */
+	if (!QIO_ENABLED(h) || flags & IOI_INITSCAN) {	/* Need to open file? */
 		/* We still need to open file for operations like 'repquota' */
 		if ((fd = open(qfname, QIO_RO(h) ? O_RDONLY : O_RDWR)) < 0) {
 			errstr(_("Cannot open quotafile %s: %s\n"),
Index: quota-tools/quotaio_generic.c
===================================================================
--- quota-tools.orig/quotaio_generic.c
+++ quota-tools/quotaio_generic.c
@@ -6,6 +6,9 @@
 
 #include <errno.h>
 #include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <stdlib.h>
 #include <sys/types.h>
 
 #include "pot.h"
@@ -96,3 +99,63 @@ int vfs_set_dquot(struct dquot *dquot, i
 	}
 	return 0;
 }
+
+static int scan_one_dquot(struct dquot *dquot, int (*get_dquot)(struct dquot *))
+{
+	int ret;
+	struct util_dqblk *dqb = &dquot->dq_dqb;
+
+	memset(dqb, 0, sizeof(struct util_dqblk));
+	ret = get_dquot(dquot);
+	if (ret < 0)
+		return ret;
+	if (!dqb->dqb_bhardlimit && !dqb->dqb_bsoftlimit && !dqb->dqb_ihardlimit && !dqb->dqb_isoftlimit && !dqb->dqb_curinodes && !dqb->dqb_curspace)
+		return 1;
+	return 0;
+}
+
+/* Generic quota scanning using passwd... */
+int generic_scan_dquots(struct quota_handle *h,
+			int (*process_dquot)(struct dquot *dquot, char *dqname),
+			int (*get_dquot)(struct dquot *dquot))
+{
+	struct dquot *dquot = get_empty_dquot();
+	int ret = 0;
+
+	dquot->dq_h = h;
+	if (h->qh_type == USRQUOTA) {
+		struct passwd *usr;
+
+		setpwent();
+		while ((usr = getpwent()) != NULL) {
+			dquot->dq_id = usr->pw_uid;
+			ret = scan_one_dquot(dquot, get_dquot);
+			if (ret < 0)
+				break;
+			if (ret > 0)
+				continue;
+			ret = process_dquot(dquot, usr->pw_name);
+			if (ret < 0)
+				break;
+		}
+		endpwent();
+	} else if (h->qh_type == GRPQUOTA) {
+		struct group *grp;
+
+		setgrent();
+		while ((grp = getgrent()) != NULL) {
+			dquot->dq_id = grp->gr_gid;
+			ret = scan_one_dquot(dquot, get_dquot);
+			if (ret < 0)
+				break;
+			if (ret > 0)
+				continue;
+			ret = process_dquot(dquot, grp->gr_name);
+			if (ret < 0)
+				break;
+		}
+		endgrent();
+	}
+	free(dquot);
+	return ret;
+}
Index: quota-tools/quotaio_generic.h
===================================================================
--- quota-tools.orig/quotaio_generic.h
+++ quota-tools/quotaio_generic.h
@@ -21,4 +21,10 @@ int vfs_get_dquot(struct dquot *dquot);
 /* Set dquot in kernel */
 int vfs_set_dquot(struct dquot *dquot, int flags);
 
+/* Generic routine for scanning dquots when quota format does not have
+ * better way */
+int generic_scan_dquots(struct quota_handle *h,
+			int (*process_dquot)(struct dquot *dquot, char *dqname),
+			int (*get_dquot)(struct dquot *dquot));
+
 #endif
Index: quota-tools/quotaio_meta.c
===================================================================
--- quota-tools.orig/quotaio_meta.c
+++ quota-tools/quotaio_meta.c
@@ -50,9 +50,15 @@ static int meta_commit_dquot(struct dquo
 	return vfs_set_dquot(dquot, flags);
 }
 
+static int meta_scan_dquots(struct quota_handle *h, int (*process_dquot)(struct dquot *dquot, char *dqname))
+{
+	return generic_scan_dquots(h, process_dquot, vfs_get_dquot);
+}
+
 struct quotafile_ops quotafile_ops_meta = {
 init_io:	meta_init_io,
 write_info:	meta_write_info,
 read_dquot:	meta_read_dquot,
 commit_dquot:	meta_commit_dquot,
+scan_dquots:	meta_scan_dquots,
 };
Index: quota-tools/quotaio_xfs.c
===================================================================
--- quota-tools.orig/quotaio_xfs.c
+++ quota-tools/quotaio_xfs.c
@@ -9,8 +9,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <pwd.h>
-#include <grp.h>
 
 #include "pot.h"
 #include "common.h"
@@ -18,6 +16,7 @@
 #include "quotaio.h"
 #include "quotasys.h"
 #include "dqblk_xfs.h"
+#include "quotaio_generic.h"
 
 #define XFS_USRQUOTA(h)	((h)->qh_type == USRQUOTA && \
 			(h)->qh_info.u.xfs_mdqi.qs_flags & XFS_QUOTA_UDQ_ACCT)
@@ -164,24 +163,21 @@ static int xfs_commit_dquot(struct dquot
 /*
  *	xfs_scan_dquots helper - processes a single dquot
  */
-static int xfs_scan_dquot(struct quota_handle *h,
-			  struct xfs_kern_dqblk *d,
-			  char *name, struct dquot *dq,
-			  int (*process_dquot) (struct dquot *dquot, char *dqname))
+static int xfs_get_dquot(struct dquot *dq)
 {
-	int qcmd = QCMD(Q_XFS_GETQUOTA, h->qh_type);
-
-	memset(d, 0, sizeof(struct xfs_kern_dqblk));
+	struct xfs_kern_dqblk d;
+	int qcmd = QCMD(Q_XFS_GETQUOTA, dq->dq_h->qh_type);
+	int ret;
 
-	if (quotactl(qcmd, h->qh_quotadev, dq->dq_id, (void *)d) < 0) {
-		return 0;
+	memset(&d, 0, sizeof(d));
+	ret = quotactl(qcmd, dq->dq_h->qh_quotadev, dq->dq_id, (void *)&d);
+	if (ret < 0) {
+		if (ret == -ENOENT)
+			return 0;
+		return ret;
 	}
-	if (d->d_blk_hardlimit == 0 &&
-	    d->d_blk_softlimit == 0 &&
-	    d->d_ino_hardlimit == 0 &&
-	    d->d_ino_softlimit == 0 && d->d_bcount == 0 && d->d_icount == 0) return 0;
-	xfs_kern2utildqblk(&dq->dq_dqb, d);
-	return process_dquot(dq, name);
+	xfs_kern2utildqblk(&dq->dq_dqb, &d);
+	return 0;
 }
 
 /*
@@ -189,42 +185,10 @@ static int xfs_scan_dquot(struct quota_h
  */
 static int xfs_scan_dquots(struct quota_handle *h, int (*process_dquot) (struct dquot *dquot, char *dqname))
 {
-	struct dquot *dq;
-	struct xfs_kern_dqblk d;
-	int rd = 0;
-
 	if (!XFS_USRQUOTA(h) && !XFS_GRPQUOTA(h))
-		return rd;
-
-	dq = get_empty_dquot();
-	dq->dq_h = h;
-	if (h->qh_type == USRQUOTA) {
-		struct passwd *usr;
-
-		setpwent();
-		while ((usr = getpwent()) != NULL) {
-			dq->dq_id = usr->pw_uid;
-			rd = xfs_scan_dquot(h, &d, usr->pw_name, dq, process_dquot);
-			if (rd < 0)
-				break;
-		}
-		endpwent();
-	}
-	else {			/* GRPQUOTA */
-		struct group *grp;
-
-		setgrent();
-		while ((grp = getgrent()) != NULL) {
-			dq->dq_id = grp->gr_gid;
-			rd = xfs_scan_dquot(h, &d, grp->gr_name, dq, process_dquot);
-			if (rd < 0)
-				break;
-		}
-		endgrent();
-	}
+		return 0;
 
-	free(dq);
-	return rd;
+	return generic_scan_dquots(h, process_dquot, xfs_get_dquot);
 }
 
 /*
Index: quota-tools/quotasys.h
===================================================================
--- quota-tools.orig/quotasys.h
+++ quota-tools/quotasys.h
@@ -20,7 +20,7 @@
 
 /* Flags for IO initialization */
 #define IOI_READONLY	0x1	/* Only readonly access */
-#define IOI_OPENFILE	0x2	/* Open file even if kernel has quotas turned on */
+#define IOI_INITSCAN	0x2	/* Prepare handle for scanning dquots */
 #define IOI_NFS_MIXED_PATHS	0x4	/* Trim leading / from NFSv4 mountpoints */
 
 #define KERN_KNOWN_QUOTA_VERSION (6*10000 + 5*100 + 1)
Index: quota-tools/repquota.c
===================================================================
--- quota-tools.orig/repquota.c
+++ quota-tools/repquota.c
@@ -309,9 +309,9 @@ static void report(int type)
 	int i;
 
 	if (flags & FL_ALL)
-		handles = create_handle_list(0, NULL, type, fmt, IOI_READONLY | IOI_OPENFILE, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
+		handles = create_handle_list(0, NULL, type, fmt, IOI_READONLY | IOI_INITSCAN, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
 	else
-		handles = create_handle_list(mntcnt, mnt, type, fmt, IOI_READONLY | IOI_OPENFILE, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
+		handles = create_handle_list(mntcnt, mnt, type, fmt, IOI_READONLY | IOI_INITSCAN, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
 	for (i = 0; handles[i]; i++)
 		report_it(handles[i], type);
 	dispose_handle_list(handles);
Index: quota-tools/warnquota.c
===================================================================
--- quota-tools.orig/warnquota.c
+++ quota-tools/warnquota.c
@@ -942,7 +942,7 @@ static void warn_quota(void)
 		wc_exit(1);
 
 	if (flags & FL_USER) {
-		handles = create_handle_list(0, NULL, USRQUOTA, -1, IOI_READONLY | IOI_OPENFILE, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
+		handles = create_handle_list(0, NULL, USRQUOTA, -1, IOI_READONLY | IOI_INITSCAN, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
 		if (!maildev[0] || !strcasecmp(maildev, "any"))
 			maildev_handle = NULL;
 		else
@@ -954,7 +954,7 @@ static void warn_quota(void)
 	if (flags & FL_GROUP) {
 		if (get_groupadmins() < 0)
 			wc_exit(1);
-		handles = create_handle_list(0, NULL, GRPQUOTA, -1, IOI_READONLY | IOI_OPENFILE, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
+		handles = create_handle_list(0, NULL, GRPQUOTA, -1, IOI_READONLY | IOI_INITSCAN, MS_LOCALONLY | (flags & FL_NOAUTOFS ? MS_NO_AUTOFS : 0));
 		if (!maildev[0] || !strcasecmp(maildev, "any"))
 			maildev_handle = NULL;
 		else
