[<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