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