lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250917032814.395887-3-tytso@mit.edu>
Date: Tue, 16 Sep 2025 23:28:13 -0400
From: "Theodore Ts'o" <tytso@....edu>
To: Ext4 Developers List <linux-ext4@...r.kernel.org>
Cc: "Theodore Ts'o" <tytso@....edu>
Subject: [PATCH 2/3] tune2fs: rework parse_extended_opts() so it only parses the option string

The parse_extended_opts() was doing two things: interpreting the
string passed into the command line and modifying the file system's
superblock.  Separate out the file system modification and move it out
from parse_extended_opts().

This allows the user to specify more than one -E command-line option,
and it also allows some of the file system changes to be modified via
an ioctl for a mounted file system.

Signed-off-by: Theodore Ts'o <tytso@....edu>
---
 misc/tune2fs.c | 211 +++++++++++++++++++++++++++----------------------
 1 file changed, 118 insertions(+), 93 deletions(-)

diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 1b3716e1..e752c328 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -123,8 +123,19 @@ extern int ask_yn(const char *string, int def);
 #define OPT_JOURNAL_OPTS	18
 #define OPT_MNTOPTS		19
 #define OPT_FEATURES		20
-#define OPT_EXTENDED_CMD	21
-#define MAX_OPTS		22
+#define OPT_CLEAR_MMP		21
+#define OPT_MMP_INTERVAL	22
+#define OPT_FORCE_FSCK		23
+#define OPT_TEST_FS		24
+#define OPT_CLEAR_TEST_FS	25
+#define OPT_RAID_STRIDE		26
+#define OPT_RAID_STRIPE_WIDTH	27
+#define OPT_HASH_ALG		28
+#define OPT_MOUNT_OPTS		29
+#define OPT_ENCODING		30
+#define OPT_ENCODING_FLAGS	31
+#define OPT_ORPHAN_FILE_SIZE	32
+#define MAX_OPTS		33
 static bool opts[MAX_OPTS];
 
 const char *program_name = "tune2fs";
@@ -132,7 +143,6 @@ char *device_name;
 char *new_label, *new_last_mounted, *requested_uuid;
 char *io_options;
 static int force, do_list_super, sparse_value = -1;
-static int clear_mmp;
 static time_t last_check_time;
 static int max_mount_count, mount_count, mount_flags;
 static unsigned long interval;
@@ -140,12 +150,16 @@ static blk64_t reserved_blocks;
 static double reserved_ratio;
 static unsigned long resgid, resuid;
 static unsigned short errors;
+static unsigned long mmp_interval;
+static int hash_alg;
+static char *hash_alg_str;
+static int encoding;
+static __u16 encoding_flags;
+static char *encoding_str, *encoding_flags_str;
 static int open_flag;
 static char *features_cmd;
 static char *mntopts_cmd;
 static int stride, stripe_width;
-static int stride_set, stripe_width_set;
-static char *extended_cmd;
 static unsigned long new_inode_size;
 static char *ext_mount_opts;
 static int quota_enable[MAXQUOTAS];
@@ -153,7 +167,6 @@ static int rewrite_checksums;
 static int feature_64bit;
 static int fsck_requested;
 static char *undo_file;
-int enabling_casefold;
 
 int journal_size, journal_fc_size, journal_flags;
 char *journal_device;
@@ -184,6 +197,8 @@ void do_findfs(int argc, char **argv);
 int journal_enable_debug = -1;
 #endif
 
+static int parse_extended_opts(const char *ext_opts);
+
 static void usage(void)
 {
 	fprintf(stderr,
@@ -1645,7 +1660,6 @@ mmp_error:
 		}
 		fs->super->s_encoding = EXT4_ENC_UTF8_12_1;
 		fs->super->s_encoding_flags = e2p_get_encoding_flags(EXT4_ENC_UTF8_12_1);
-		enabling_casefold = 1;
 	}
 
 	if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_CASEFOLD)) {
@@ -1661,7 +1675,6 @@ mmp_error:
 		}
 		fs->super->s_encoding = 0;
 		fs->super->s_encoding_flags = 0;
-		enabling_casefold = 0;
 	}
 
 	if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
@@ -2066,8 +2079,8 @@ static void parse_tune2fs_options(int argc, char **argv)
 			}
 			break;
 		case 'E':
-			opts[OPT_EXTENDED_CMD] = true;
-			extended_cmd = optarg;
+			if (parse_extended_opts(optarg))
+				exit(1);
 			break;
 		case 'f': /* Force */
 			force++;
@@ -2259,6 +2272,11 @@ static void parse_tune2fs_options(int argc, char **argv)
 			argv[optind]);
 		exit(1);
 	}
+	if (opts[OPT_ENCODING_FLAGS] && !opts[OPT_ENCODING]) {
+		fprintf(stderr, _("error: An encoding must be explicitly "
+				  "specified when passing encoding-flags\n"));
+		exit(1);
+	}
 }
 
 #ifdef CONFIG_BUILD_FINDFS
@@ -2282,23 +2300,22 @@ void do_findfs(int argc, char **argv)
 }
 #endif
 
-static int parse_extended_opts(ext2_filsys fs, const char *opts)
+#define member_size(type, member) (sizeof( ((type *)0)->member ))
+
+static int parse_extended_opts(const char *ext_opts)
 {
-	struct ext2_super_block *sb = fs->super;
 	char	*buf, *token, *next, *p, *arg;
-	int	len, hash_alg;
+	int	len;
 	int	r_usage = 0;
-	int encoding = 0;
-	char	*encoding_flags = NULL;
 
-	len = strlen(opts);
+	len = strlen(ext_opts);
 	buf = malloc(len+1);
 	if (!buf) {
 		fprintf(stderr, "%s",
 			_("Couldn't allocate memory to parse options!\n"));
 		return 1;
 	}
-	strcpy(buf, opts);
+	strcpy(buf, ext_opts);
 	for (token = buf; token && *token; token = next) {
 		p = strchr(token, ',');
 		next = 0;
@@ -2313,14 +2330,13 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
 		}
 		if (strcmp(token, "clear-mmp") == 0 ||
 		    strcmp(token, "clear_mmp") == 0) {
-			clear_mmp = 1;
+			opts[OPT_CLEAR_MMP] = true;
 		} else if (strcmp(token, "mmp_update_interval") == 0) {
-			unsigned long intv;
 			if (!arg) {
 				r_usage++;
 				continue;
 			}
-			intv = strtoul(arg, &p, 0);
+			mmp_interval = strtoul(arg, &p, 0);
 			if (*p) {
 				fprintf(stderr,
 					_("Invalid mmp_update_interval: %s\n"),
@@ -2328,34 +2344,22 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
 				r_usage++;
 				continue;
 			}
-			if (intv == 0) {
-				intv = EXT4_MMP_UPDATE_INTERVAL;
-			} else if (intv > EXT4_MMP_MAX_UPDATE_INTERVAL) {
+			if (mmp_interval == 0) {
+				mmp_interval = EXT4_MMP_UPDATE_INTERVAL;
+			} else if (mmp_interval > EXT4_MMP_MAX_UPDATE_INTERVAL) {
 				fprintf(stderr,
 					_("mmp_update_interval too big: %lu\n"),
-					intv);
+					mmp_interval);
 				r_usage++;
 				continue;
 			}
-			printf(P_("Setting multiple mount protection update "
-				  "interval to %lu second\n",
-				  "Setting multiple mount protection update "
-				  "interval to %lu seconds\n", intv),
-			       intv);
-			sb->s_mmp_update_interval = intv;
-			ext2fs_mark_super_dirty(fs);
+			opts[OPT_MMP_INTERVAL] = true;
 		} else if (!strcmp(token, "force_fsck")) {
-			sb->s_state |= EXT2_ERROR_FS;
-			printf(_("Setting filesystem error flag to force fsck.\n"));
-			ext2fs_mark_super_dirty(fs);
+			opts[OPT_FORCE_FSCK] = true;
 		} else if (!strcmp(token, "test_fs")) {
-			sb->s_flags |= EXT2_FLAGS_TEST_FILESYS;
-			printf("Setting test filesystem flag\n");
-			ext2fs_mark_super_dirty(fs);
+			opts[OPT_TEST_FS] = true;
 		} else if (!strcmp(token, "^test_fs")) {
-			sb->s_flags &= ~EXT2_FLAGS_TEST_FILESYS;
-			printf("Clearing test filesystem flag\n");
-			ext2fs_mark_super_dirty(fs);
+			opts[OPT_CLEAR_TEST_FS] = true;
 		} else if (strcmp(token, "stride") == 0) {
 			if (!arg) {
 				r_usage++;
@@ -2369,7 +2373,7 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
 				r_usage++;
 				continue;
 			}
-			stride_set = 1;
+			opts[OPT_RAID_STRIDE] = true;
 		} else if (strcmp(token, "stripe-width") == 0 ||
 			   strcmp(token, "stripe_width") == 0) {
 			if (!arg) {
@@ -2384,7 +2388,7 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
 				r_usage++;
 				continue;
 			}
-			stripe_width_set = 1;
+			opts[OPT_RAID_STRIPE_WIDTH] = true;
 		} else if (strcmp(token, "hash_alg") == 0 ||
 			   strcmp(token, "hash-alg") == 0) {
 			if (!arg) {
@@ -2399,21 +2403,21 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
 				r_usage++;
 				continue;
 			}
-			sb->s_def_hash_version = hash_alg;
-			printf(_("Setting default hash algorithm "
-				 "to %s (%d)\n"),
-			       arg, hash_alg);
-			ext2fs_mark_super_dirty(fs);
+			hash_alg_str = strdup(arg);
+			opts[OPT_HASH_ALG] = true;
 		} else if (!strcmp(token, "mount_opts")) {
 			if (!arg) {
 				r_usage++;
 				continue;
 			}
-			if (strlen(arg) >= sizeof(fs->super->s_mount_opts)) {
+			if (strlen(arg) >=
+			    member_size(struct ext2_super_block,
+					s_mount_opts)) {
 				fprintf(stderr,
 					"Extended mount options too long\n");
 				continue;
 			}
+			opts[OPT_MOUNT_OPTS] = true;
 			ext_mount_opts = strdup(arg);
 		} else if (!strcmp(token, "encoding")) {
 			if (!arg) {
@@ -2426,36 +2430,33 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
 				r_usage++;
 				continue;
 			}
-			if (ext2fs_has_feature_casefold(sb) && !enabling_casefold) {
-				fprintf(stderr, _("Cannot alter existing encoding\n"));
-				r_usage++;
-				continue;
-			}
 			encoding = e2p_str2encoding(arg);
 			if (encoding < 0) {
 				fprintf(stderr, _("Invalid encoding: %s\n"), arg);
 				r_usage++;
 				continue;
 			}
-			enabling_casefold = 1;
-			sb->s_encoding = encoding;
-			printf(_("Setting encoding to '%s'\n"), arg);
-			sb->s_encoding_flags =
-				e2p_get_encoding_flags(sb->s_encoding);
+			encoding_str = strdup(arg);
+			opts[OPT_ENCODING] = true;
 		} else if (!strcmp(token, "encoding_flags")) {
 			if (!arg) {
 				r_usage++;
 				continue;
 			}
-			encoding_flags = arg;
+			if (e2p_str2encoding_flags(EXT4_ENC_UTF8_12_1,
+						   arg, &encoding_flags)) {
+				fprintf(stderr,
+		_("error: Invalid encoding flag: %s\n"), arg);
+				r_usage++;
+			}
+			encoding_flags_str = strdup(arg);
+			opts[OPT_ENCODING_FLAGS] = true;
 		} else if (!strcmp(token, "orphan_file_size")) {
 			if (!arg) {
 				r_usage++;
 				continue;
 			}
-			orphan_file_blocks = parse_num_blocks2(arg,
-						 fs->super->s_log_block_size);
-
+			orphan_file_blocks = parse_num_blocks2(arg, 0);
 			if (orphan_file_blocks < 1) {
 				fprintf(stderr,
 					_("Invalid size of orphan file %s\n"),
@@ -2463,30 +2464,10 @@ static int parse_extended_opts(ext2_filsys fs, const char *opts)
 				r_usage++;
 				continue;
 			}
+			opts[OPT_ORPHAN_FILE_SIZE] = true;
 		} else
 			r_usage++;
 	}
-
-	if (encoding > 0 && !r_usage) {
-		sb->s_encoding_flags =
-			e2p_get_encoding_flags(sb->s_encoding);
-
-		if (encoding_flags &&
-		    e2p_str2encoding_flags(sb->s_encoding, encoding_flags,
-					   &sb->s_encoding_flags)) {
-			fprintf(stderr, _("error: Invalid encoding flag: %s\n"),
-					encoding_flags);
-			r_usage++;
-		} else if (encoding_flags)
-			printf(_("Setting encoding_flags to '%s'\n"),
-				 encoding_flags);
-		ext2fs_set_feature_casefold(sb);
-		ext2fs_mark_super_dirty(fs);
-	} else if (encoding_flags && !r_usage) {
-		fprintf(stderr, _("error: An encoding must be explicitly "
-				  "specified when passing encoding-flags\n"));
-		r_usage++;
-	}
 	if (r_usage) {
 		fprintf(stderr, "%s", _("\nBad options specified.\n\n"
 			"Extended options are separated by commas, "
@@ -3518,27 +3499,64 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
 		if (rc)
 			goto closefs;
 	}
+	if (ext2fs_has_feature_casefold(sb) && opts[OPT_ENCODING]) {
+		fprintf(stderr, _("Cannot alter existing encoding\n"));
+		rc = 1;
+		goto closefs;
+	}
 	if (features_cmd) {
 		rc = update_feature_set(fs, features_cmd);
 		if (rc)
 			goto closefs;
 	}
-	if (extended_cmd) {
-		rc = parse_extended_opts(fs, extended_cmd);
-		if (rc)
-			goto closefs;
-		if (clear_mmp && !force) {
+	if (opts[OPT_CLEAR_MMP]) {
+		if (!force) {
 			fputs(_("Error in using clear_mmp. "
 				"It must be used with -f\n"),
 			      stderr);
 			rc = 1;
 			goto closefs;
 		}
-	}
-	if (clear_mmp) {
 		rc = ext2fs_mmp_clear(fs);
 		goto closefs;
 	}
+	if (opts[OPT_MMP_INTERVAL]) {
+		printf(P_("Setting multiple mount protection update "
+			  "interval to %lu second\n",
+			  "Setting multiple mount protection update "
+			  "interval to %lu seconds\n", mmp_interval),
+		       mmp_interval);
+		sb->s_mmp_update_interval = mmp_interval;
+		ext2fs_mark_super_dirty(fs);
+	}
+	if (opts[OPT_FORCE_FSCK]) {
+		sb->s_state |= EXT2_ERROR_FS;
+		printf(_("Setting filesystem error flag to force fsck.\n"));
+		ext2fs_mark_super_dirty(fs);
+	}
+	if (opts[OPT_TEST_FS]) {
+		sb->s_flags |= EXT2_FLAGS_TEST_FILESYS;
+		printf("Setting test filesystem flag\n");
+		ext2fs_mark_super_dirty(fs);
+	}
+	if (opts[OPT_CLEAR_TEST_FS]) {
+		sb->s_flags &= ~EXT2_FLAGS_TEST_FILESYS;
+		printf("Clearing test filesystem flag\n");
+		ext2fs_mark_super_dirty(fs);
+	}
+	if (opts[OPT_ENCODING]) {
+		ext2fs_set_feature_casefold(sb);
+		sb->s_encoding = encoding;
+		printf(_("Setting encoding to '%s'\n"), encoding_str);
+		if (opts[OPT_ENCODING_FLAGS]) {
+			sb->s_encoding_flags = encoding_flags;
+			printf(_("Setting encoding_flags to '%s'\n"),
+			       encoding_flags_str);
+		} else
+			sb->s_encoding_flags =
+				e2p_get_encoding_flags(sb->s_encoding);
+			ext2fs_mark_super_dirty(fs);
+	}
 	if (journal_size || journal_device) {
 		rc = add_journal(fs);
 		if (rc)
@@ -3554,6 +3572,7 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
 			rc = 1;
 			goto closefs;
 		}
+		orphan_file_blocks >>= fs->super->s_log_block_size;
 		err = ext2fs_create_orphan_file(fs, orphan_file_blocks);
 		if (err) {
 			com_err(program_name, err, "%s",
@@ -3764,17 +3783,23 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
 
 	if (do_list_super)
 		list_super(sb);
-	if (stride_set) {
+	if (opts[OPT_RAID_STRIDE]) {
 		sb->s_raid_stride = stride;
 		ext2fs_mark_super_dirty(fs);
 		printf(_("Setting stride size to %d\n"), stride);
 	}
-	if (stripe_width_set) {
+	if (opts[OPT_RAID_STRIPE_WIDTH]) {
 		sb->s_raid_stripe_width = stripe_width;
 		ext2fs_mark_super_dirty(fs);
 		printf(_("Setting stripe width to %d\n"), stripe_width);
 	}
-	if (ext_mount_opts) {
+	if (opts[OPT_HASH_ALG]) {
+		sb->s_def_hash_version = hash_alg;
+		printf(_("Setting default hash algorithm to %s (%d)\n"),
+		       hash_alg_str, hash_alg);
+		ext2fs_mark_super_dirty(fs);
+	}
+	if (opts[OPT_MOUNT_OPTS]) {
 		strncpy((char *)(fs->super->s_mount_opts), ext_mount_opts,
 			sizeof(fs->super->s_mount_opts));
 		fs->super->s_mount_opts[sizeof(fs->super->s_mount_opts)-1] = 0;
-- 
2.51.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ