quotacheck,quotaon: Suggest using quota feature for ext4

Ext4 supports quota using internal quota files for quite some time.
Suggest using this quota feature instead of external quota files if the
kernel is new enough since external quota files on ext4 will be
deprecated.

Signed-off-by: Jan Kara <jack@suse.cz>
diff --git a/quotacheck.c b/quotacheck.c
index 1311c10..5810ced 100644
--- a/quotacheck.c
+++ b/quotacheck.c
@@ -1130,8 +1130,8 @@
 	return !!strcmp(fstype, MNTTYPE_GFS2);
 }
 
-/* Parse kernel version and warn if not using journaled quotas */
-static void warn_if_jquota_supported(void)
+/* Parse kernel version and return 1 if journaled quota is supported */
+static int kernel_supports_jquota(void)
 {
 	struct utsname stats;
 	int v;
@@ -1139,28 +1139,25 @@
 
 	if (uname(&stats) < 0) {
 		errstr(_("Cannot get system info: %s\n"), strerror(errno));
-		return;
+		return 0;
 	}
 	if (strcmp(stats.sysname, "Linux"))
-		return;
+		return 0;
 
 	v = strtol(stats.release, &errch, 10);
 	if (v < 2)
-		return;
+		return 0;
 	if (v >= 3)
-		goto warn;
+		return 1;
 	if (*errch != '.')
-		return;
+		return 0;
 	v = strtol(errch + 1, &errch, 10);
 	if (*errch != '.' || v < 6)
-		return;
+		return 0;
 	v = strtol(errch + 1, &errch, 10);
 	if (v < 11)
-		return;
-warn:
-	errstr(_("Your kernel probably supports journaled quota but you are "
-		 "not using it. Consider switching to journaled quota to avoid"
-		 " running quotacheck after an unclean shutdown.\n"));
+		return 0;
+	return 1;
 }
 
 /* Return 0 in case of success, non-zero otherwise. */
@@ -1202,17 +1199,31 @@
 			debug(FL_DEBUG, _("Detected quota format %s\n"), fmt2name(cfmt));
 		}
 
-		if (flags & (FL_VERBOSE | FL_DEBUG) &&
-		    !str_hasmntopt(mnt->me_opts, MNTOPT_USRJQUOTA) &&
-		    !str_hasmntopt(mnt->me_opts, MNTOPT_GRPJQUOTA) &&
-		    !warned &&
-		    (!strcmp(mnt->me_type, MNTTYPE_EXT3) ||
-		     !strcmp(mnt->me_type, MNTTYPE_EXT4) ||
-		     !strcmp(mnt->me_type, MNTTYPE_NEXT3) ||
-		     !strcmp(mnt->me_type, MNTTYPE_EXT4DEV) ||
-		     !strcmp(mnt->me_type, MNTTYPE_REISER))) {
-			warned = 1;
-			warn_if_jquota_supported();
+		if (flags & (FL_VERBOSE | FL_DEBUG) && !warned) {
+			if (!strcmp(mnt->me_type, MNTTYPE_EXT4) &&
+			    ext4_supports_quota_feature()) {
+				warned = 1;
+				errstr(_("Your kernel probably supports ext4 "
+					 "quota feature but you are using "
+					 "external quota files. Please switch "
+					 "your filesystem to use ext4 quota "
+					 "feature as external quota files on "
+					 "ext4 are deprecated.\n"));
+			} else if (!str_hasmntopt(mnt->me_opts, MNTOPT_USRJQUOTA) &&
+				   !str_hasmntopt(mnt->me_opts, MNTOPT_GRPJQUOTA) &&
+				   (!strcmp(mnt->me_type, MNTTYPE_EXT3) ||
+				    !strcmp(mnt->me_type, MNTTYPE_EXT4) ||
+				    !strcmp(mnt->me_type, MNTTYPE_NEXT3) ||
+				    !strcmp(mnt->me_type, MNTTYPE_EXT4DEV) ||
+				    !strcmp(mnt->me_type, MNTTYPE_REISER)) &&
+				   kernel_supports_jquota()) {
+				warned = 1;
+				errstr(_("Your kernel probably supports "
+					 "journaled quota but you are not "
+					 "using it. Consider switching to "
+					 "journaled quota to avoid running "
+					 "quotacheck after an unclean shutdown.\n"));
+			}
 		}
 
 		checked++;
diff --git a/quotaon.c b/quotaon.c
index 508e57e..aceb6ec 100644
--- a/quotaon.c
+++ b/quotaon.c
@@ -243,6 +243,7 @@
  */
 static int newstate(struct mount_entry *mnt, int type, char *extra)
 {
+	static int warned;
 	int sflags, ret = 0;
 
 	sflags = flags & FL_OFF ? STATEFLAG_OFF : STATEFLAG_ON;
@@ -269,6 +270,16 @@
 
 		if (!me_hasquota(mnt, type))
 			return 0;
+		if (flags & FL_VERBOSE && !warned &&
+		    !strcmp(mnt->me_type, MNTTYPE_EXT4) &&
+		    ext4_supports_quota_feature()) {
+			warned = 1;
+			errstr(_("Your kernel probably supports ext4 quota "
+				 "feature but you are using external quota "
+				 "files. Please switch your filesystem to use "
+				 "ext4 quota feature as external quota files "
+				 "on ext4 are deprecated.\n"));
+		}
 		if (fmt == -1) {
 			if (get_qf_name(mnt, type, QF_VFSV0,
 					NF_FORMAT, &extra) >= 0)
diff --git a/quotasys.c b/quotasys.c
index d8c0e48..885fb1f 100644
--- a/quotasys.c
+++ b/quotasys.c
@@ -24,6 +24,7 @@
 #include <sys/stat.h>
 #include <sys/vfs.h>
 #include <stdint.h>
+#include <sys/utsname.h>
 
 #include "pot.h"
 #include "bylabel.h"
@@ -1597,3 +1598,29 @@
 	check_dirs = NULL;
 	check_dirs_cnt = 0;
 }
+
+/* Parse kernel version and return 1 if ext4 supports quota feature */
+int ext4_supports_quota_feature(void)
+{
+	struct utsname stats;
+	int v;
+	char *errch;
+
+	if (uname(&stats) < 0) {
+		errstr(_("Cannot get system info: %s\n"), strerror(errno));
+		return 0;
+	}
+	if (strcmp(stats.sysname, "Linux"))
+		return 0;
+	v = strtol(stats.release, &errch, 10);
+	if (v < 4)
+		return 0;
+	if (v > 4)
+		return 1;
+	if (*errch != '.')
+		return 0;
+	v = strtol(errch + 1, &errch, 10);
+	if (*errch != '.' || v < 9)
+		return 0;
+	return 1;
+}
diff --git a/quotasys.h b/quotasys.h
index 05f8ad5..841251e 100644
--- a/quotasys.h
+++ b/quotasys.h
@@ -203,6 +203,9 @@
 /* Free all structures associated with mountpoints scan */
 void end_mounts_scan(void);
 
+/* Parse kernel version and return 1 if ext4 supports quota feature */
+int ext4_supports_quota_feature(void);
+
 /* Quota output formats */
 #define QOF_ERROR	-1
 #define QOF_DEFAULT	0