Blob Blame Raw
From fa920711b4bbb9e61358a7f488f0a645ef221321 Mon Sep 17 00:00:00 2001
From: Abhijith Das <adas@redhat.com>
Date: Fri, 4 Feb 2011 12:40:58 +0100
Subject: [PATCH] Implement quotacheck for GFS2

Although GFS2 maintains quota as metadata, it sometimes might need to
check quota usage (most notably when quota is enabled for the first time).
So add support for GFS2 in quotacheck.

Signed-off-by: Jan Kara <jack@suse.cz>

Petr Pisar: Remove MNTTYPE_NEXT3 test
---
 quotacheck.c  |   82 +++++++++++++++++++++++++++++++++++++++++++--------------
 quotaio_xfs.c |   12 +++++++-
 quotaio_xfs.h |    9 ++++++
 3 files changed, 82 insertions(+), 21 deletions(-)

diff --git a/quotacheck.c b/quotacheck.c
index fec9ec3..7ecf62d 100644
--- a/quotacheck.c
+++ b/quotacheck.c
@@ -404,10 +404,6 @@ static void parse_options(int argcnt, char **argstr)
 		fputs(_("Bad number of arguments.\n"), stderr);
 		usage();
 	}
-	if (fmt == QF_XFS) {
-		fputs(_("XFS quota format needs no checking.\n"), stderr);
-		exit(0);
-	}
 	if (flags & FL_VERBOSE && flags & FL_DEBUG)
 		flags &= ~FL_VERBOSE;
 	if (!(flags & FL_ALL))
@@ -689,6 +685,9 @@ static int rename_files(struct mntent *mnt, int type)
 	int fd;
 #endif
 
+	if (cfmt == QF_XFS) /* No renaming for xfs/gfs2 */
+		return 0;
+
 	debug(FL_DEBUG, _("Renaming new files to proper names.\n"));
 	if (get_qf_name(mnt, type, cfmt, 0, &filename) < 0)
 		die(2, _("Cannot get name of old quotafile on %s.\n"), mnt->mnt_dir);
@@ -787,25 +786,42 @@ static int dump_to_file(struct mntent *mnt, int type)
 	struct dquot *dquot;
 	uint i;
 	struct quota_handle *h;
+	unsigned int commit = 0;
 
 	debug(FL_DEBUG, _("Dumping gathered data for %ss.\n"), type2name(type));
-	if (!(h = new_io(mnt, type, cfmt))) {
-		errstr(_("Cannot initialize IO on new quotafile: %s\n"),
-			strerror(errno));
-		return -1;
-	}
-	if (!(flags & FL_NEWFILE)) {
-		h->qh_info.dqi_bgrace = old_info[type].dqi_bgrace;
-		h->qh_info.dqi_igrace = old_info[type].dqi_igrace;
-		if (is_tree_qfmt(cfmt))
-			v2_merge_info(&h->qh_info, old_info + type);
-		mark_quotafile_info_dirty(h);
+	if (cfmt == QF_XFS) {
+		if (!(h = init_io(mnt, type, cfmt, IOI_READONLY))) {
+			errstr(_("Cannot initialize IO on xfs/gfs2 quotafile: %s\n"),
+			       strerror(errno));
+			return -1;
+		}
+	} else {
+		if (!(h = new_io(mnt, type, cfmt))) {
+			errstr(_("Cannot initialize IO on new quotafile: %s\n"),
+			       strerror(errno));
+			return -1;
+		}
+		if (!(flags & FL_NEWFILE)) {
+			h->qh_info.dqi_bgrace = old_info[type].dqi_bgrace;
+			h->qh_info.dqi_igrace = old_info[type].dqi_igrace;
+			if (is_tree_qfmt(cfmt))
+				v2_merge_info(&h->qh_info, old_info + type);
+			mark_quotafile_info_dirty(h);
+		}
 	}
 	for (i = 0; i < DQUOTHASHSIZE; i++)
 		for (dquot = dquot_hash[type][i]; dquot; dquot = dquot->dq_next) {
 			dquot->dq_h = h;
+			/* For XFS/GFS2, we don't bother with actually checking
+			 * what the usage value is in the internal quota file.
+			 * We simply attempt to update the usage for every quota
+			 * we find in the fs scan. The filesystem decides in the
+			 * quotactl handler whether to update the usage in the 
+			 * quota file or not.
+			 */
+			commit = cfmt == QF_XFS ? COMMIT_USAGE : COMMIT_ALL;
 			update_grace_times(dquot);
-			h->qh_ops->commit_dquot(dquot, COMMIT_ALL);
+			h->qh_ops->commit_dquot(dquot, commit);
 		}
 	if (end_io(h) < 0) {
 		errstr(_("Cannot finish IO on new quotafile: %s\n"), strerror(errno));
@@ -893,6 +909,14 @@ static void check_dir(struct mntent *mnt)
 		die(2, _("Mountpoint %s is not a directory?!\n"), mnt->mnt_dir);
 	cur_dev = st.st_dev;
 	files_done = dirs_done = 0;
+	/*
+	 * For gfs2, we scan the fs first and then tell the kernel about the new usage.
+	 * So, there's no need to load any information. We also don't remount the
+	 * filesystem read-only because for a clustering filesystem it won't stop
+	 * modifications from other nodes anyway.
+	 */
+	if (cfmt == QF_XFS)
+		goto start_scan;
 	if (ucheck)
 		if (process_file(mnt, USRQUOTA) < 0)
 			ucheck = 0;
@@ -923,6 +947,7 @@ Please stop all programs writing to filesystem or use -m flag to force checking.
 			remounted = 1;
 		debug(FL_DEBUG, _("Filesystem remounted read-only\n"));
 	}
+start_scan:
 	debug(FL_VERBOSE, _("Scanning %s [%s] "), mnt->mnt_fsname, mnt->mnt_dir);
 #if defined(EXT2_DIRECT)
 	if (!strcmp(mnt->mnt_type, MNTTYPE_EXT2) || !strcmp(mnt->mnt_type, MNTTYPE_EXT3)) {
@@ -973,6 +998,10 @@ static int detect_filename_format(struct mntent *mnt, int type)
 	int journal = 0;
 	int fmt;
 
+	if (strcmp(mnt->mnt_type, MNTTYPE_XFS) == 0 ||
+	    strcmp(mnt->mnt_type, MNTTYPE_GFS2) == 0)
+		return QF_XFS;
+
 	if (type == USRQUOTA) {
 		if ((option = hasmntopt(mnt, MNTOPT_USRQUOTA)))
 			option += strlen(MNTOPT_USRQUOTA);
@@ -1040,6 +1069,22 @@ jquota_err:
 	return -1;
 }
 
+static int compatible_fs_qfmt(char *fstype, int fmt)
+{
+	/* We never check XFS, NFS, and filesystems supporting VFS metaformat */
+	if (!strcmp(fstype, MNTTYPE_XFS) || nfs_fstype(fstype) ||
+	    meta_qf_fstype(fstype))
+		return 0;
+	/* In all other cases we can pick a format... */
+	if (fmt == -1)
+		return 1;
+	/* XFS format is supported only by GFS2 */
+	if (fmt == QF_XFS)
+		return !strcmp(fstype, MNTTYPE_GFS2);
+	/* Anything but GFS2 supports all other formats */
+	return !!strcmp(fstype, MNTTYPE_GFS2);
+}
+
 static void check_all(void)
 {
 	struct mntent *mnt;
@@ -1051,10 +1096,7 @@ static void check_all(void)
 	while ((mnt = get_next_mount())) {
 		if (flags & FL_ALL && flags & FL_NOROOT && !strcmp(mnt->mnt_dir, "/"))
 			continue;
-		if (!strcmp(mnt->mnt_type, MNTTYPE_XFS) ||
-		    !strcmp(mnt->mnt_type, MNTTYPE_GFS2) ||
-		    nfs_fstype(mnt->mnt_type) ||
-		    meta_qf_fstype(mnt->mnt_type)) {
+		if (!compatible_fs_qfmt(mnt->mnt_type, fmt)) {
 			debug(FL_DEBUG | FL_VERBOSE, _("Skipping %s [%s]\n"), mnt->mnt_fsname, mnt->mnt_dir);
 			continue;
 		}
diff --git a/quotaio_xfs.c b/quotaio_xfs.c
index 4729317..62075b5 100644
--- a/quotaio_xfs.c
+++ b/quotaio_xfs.c
@@ -151,7 +151,17 @@ static int xfs_commit_dquot(struct dquot *dquot, int flags)
 		return 0;
 
 	xfs_util2kerndqblk(&xdqblk, &dquot->dq_dqb);
-	xdqblk.d_fieldmask |= FS_DQ_LIMIT_MASK;
+	xdqblk.d_flags |= XFS_USRQUOTA(h) ? XFS_USER_QUOTA : XFS_GROUP_QUOTA;
+	xdqblk.d_id = id;
+	if (strcmp(h->qh_fstype, MNTTYPE_GFS2) == 0) {
+		if (flags & COMMIT_LIMITS) /* warn/limit */
+			xdqblk.d_fieldmask |= FS_DQ_BSOFT | FS_DQ_BHARD;
+		if (flags & COMMIT_USAGE) /* block usage */
+			xdqblk.d_fieldmask |= FS_DQ_BCOUNT;
+	} else {
+		xdqblk.d_fieldmask |= FS_DQ_LIMIT_MASK;
+	}
+
 	qcmd = QCMD(Q_XFS_SETQLIM, h->qh_type);
 	if (quotactl(qcmd, h->qh_quotadev, id, (void *)&xdqblk) < 0) {
 		;
diff --git a/quotaio_xfs.h b/quotaio_xfs.h
index cf89973..54725b0 100644
--- a/quotaio_xfs.h
+++ b/quotaio_xfs.h
@@ -105,6 +105,15 @@ typedef struct fs_disk_quota {
 #define FS_DQ_TIMER_MASK	(FS_DQ_BTIMER | FS_DQ_ITIMER | FS_DQ_RTBTIMER)
 
 /*
+ * Accounting values.  These can only be set for filesystem with
+ * non-transactional quotas that require quotacheck(8) in userspace.                   
+ */
+#define FS_DQ_BCOUNT            (1<<12)
+#define FS_DQ_ICOUNT            (1<<13)
+#define FS_DQ_RTBCOUNT          (1<<14)
+#define FS_DQ_ACCT_MASK         (FS_DQ_BCOUNT | FS_DQ_ICOUNT | FS_DQ_RTBCOUNT)
+
+/*
  * Various flags related to quotactl(2).  Only relevant to XFS filesystems.
  */
 #define XFS_QUOTA_UDQ_ACCT	(1<<0)	/* user quota accounting */
-- 
1.7.4