[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20250918174724.GH8084@frogsfrogsfrogs>
Date: Thu, 18 Sep 2025 10:47:24 -0700
From: "Darrick J. Wong" <djwong@...nel.org>
To: Theodore Ts'o <tytso@....edu>
Cc: Ext4 Developers List <linux-ext4@...r.kernel.org>
Subject: Re: [PATCH 3/3] tune2fs: try to use the SET_TUNE_SB_PARAM ioctl on
mounted file systems
On Tue, Sep 16, 2025 at 11:28:14PM -0400, Theodore Ts'o wrote:
> Try to uuse the new EXT4_IOC_GET_TUNE_SB_PARAM ioctl to update the
> superblock if the file system is mounted.
>
> Signed-off-by: Theodore Ts'o <tytso@....edu>
> ---
> misc/tune2fs.c | 352 +++++++++++++++++++++++++++++++++++++------------
> 1 file changed, 267 insertions(+), 85 deletions(-)
>
> diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> index e752c328..b1ec3991 100644
> --- a/misc/tune2fs.c
> +++ b/misc/tune2fs.c
> @@ -101,6 +101,64 @@ struct fsuuid {
> #define EXT4_IOC_SETFSUUID _IOW('f', 44, struct fsuuid)
> #endif
>
> +#if (!defined(EXT4_IOC_GET_TUNE_SB_PARAM) && defined(__linux__))
> +
> +struct ext4_tune_sb_params {
> + __u32 set_flags;
> + __u32 checkinterval;
> + __u16 errors_behavior;
> + __u16 mnt_count;
> + __u16 max_mnt_count;
> + __u16 raid_stride;
> + __u64 last_check_time;
> + __u64 reserved_blocks;
> + __u64 blocks_count;
> + __u32 default_mnt_opts;
> + __u32 reserved_uid;
> + __u32 reserved_gid;
> + __u32 raid_stripe_width;
> + __u16 encoding;
> + __u16 encoding_flags;
> + __u8 def_hash_alg;
> + __u8 pad_1;
> + __u16 pad_2;
> + __u32 feature_compat;
> + __u32 feature_incompat;
> + __u32 feature_ro_compat;
> + __u32 set_feature_compat_mask;
> + __u32 set_feature_incompat_mask;
> + __u32 set_feature_ro_compat_mask;
> + __u32 clear_feature_compat_mask;
> + __u32 clear_feature_incompat_mask;
> + __u32 clear_feature_ro_compat_mask;
> + __u8 mount_opts[64];
> + __u8 pad[64];
> +};
> +
> +#define EXT4_TUNE_FL_ERRORS_BEHAVIOR 0x00000001
> +#define EXT4_TUNE_FL_MNT_COUNT 0x00000002
> +#define EXT4_TUNE_FL_MAX_MNT_COUNT 0x00000004
> +#define EXT4_TUNE_FL_CHECKINTRVAL 0x00000008
> +#define EXT4_TUNE_FL_LAST_CHECK_TIME 0x00000010
> +#define EXT4_TUNE_FL_RESERVED_BLOCKS 0x00000020
> +#define EXT4_TUNE_FL_RESERVED_UID 0x00000040
> +#define EXT4_TUNE_FL_RESERVED_GID 0x00000080
> +#define EXT4_TUNE_FL_DEFAULT_MNT_OPTS 0x00000100
> +#define EXT4_TUNE_FL_DEF_HASH_ALG 0x00000200
> +#define EXT4_TUNE_FL_RAID_STRIDE 0x00000400
> +#define EXT4_TUNE_FL_RAID_STRIPE_WIDTH 0x00000800
> +#define EXT4_TUNE_FL_MOUNT_OPTS 0x00001000
> +#define EXT4_TUNE_FL_FEATURES 0x00002000
> +#define EXT4_TUNE_FL_EDIT_FEATURES 0x00004000
> +#define EXT4_TUNE_FL_FORCE_FSCK 0x00008000
> +#define EXT4_TUNE_FL_ENCODING 0x00010000
> +#define EXT4_TUNE_FL_ENCODING_FLAGS 0x00020000
> +
> +#define EXT4_IOC_GET_TUNE_SB_PARAM _IOR('f', 45, struct ext4_tune_sb_params)
> +#define EXT4_IOC_SET_TUNE_SB_PARAM _IOW('f', 46, struct ext4_tune_sb_params)
> +
> +#endif
> +
> extern int ask_yn(const char *string, int def);
>
> #define OPT_MAX_MOUNTCOUNT 1
> @@ -145,6 +203,8 @@ char *io_options;
> static int force, do_list_super, sparse_value = -1;
> static time_t last_check_time;
> static int max_mount_count, mount_count, mount_flags;
> +static int fs_fd = -1;
> +static char mntpt[PATH_MAX + 1];
> static unsigned long interval;
> static blk64_t reserved_blocks;
> static double reserved_ratio;
> @@ -2052,6 +2112,10 @@ static void parse_tune2fs_options(int argc, char **argv)
> }
> if (max_mount_count == 0)
> max_mount_count = -1;
> + else if (max_mount_count == 65536) {
> + max_mount_count = EXT2_DFL_MAX_MNT_COUNT +
> + (random() % EXT2_DFL_MAX_MNT_COUNT);
> + }
> break;
> case 'C':
> opts[OPT_MOUNTCOUNT] = true;
> @@ -2134,6 +2198,12 @@ static void parse_tune2fs_options(int argc, char **argv)
> _("bad interval - %s"), optarg);
> usage();
> }
> + if ((unsigned long long)interval >= (1ULL << 32)) {
> + com_err(program_name, 0,
> + _("interval between checks is too big (%lu)"),
> + interval);
> + exit(1);
> + }
>
> -/*
> - * Use FS_IOC_SETFSLABEL or FS_IOC_GETFSLABEL to set/get file system label
> - * Return: 0 on success
> - * 1 on error
> - * -1 when the old method should be used
> - */
> -static int handle_fslabel(int setlabel)
> +static int get_mount_flags()
> +{
> + errcode_t ret;
> +
> + ret = ext2fs_check_mount_point(device_name, &mount_flags,
> + mntpt, sizeof(mntpt));
> + if (ret) {
> + com_err("ext2fs_check_mount_point", ret,
> + _("while determining whether %s is mounted."),
> + device_name);
> + return -1;
> + }
> +
> +#ifdef __linux__
> + if ((ret == 0) &&
> + (mount_flags & EXT2_MF_MOUNTED) &&
> + mntpt[0])
> + fs_fd = open(mntpt, O_RDONLY);
> +#endif
> + return 0;
> +}
> +
> +static int try_mounted_tune2fs()
> {
> #ifdef __linux__
> errcode_t ret;
> - int mnt_flags, fd;
> char label[FSLABEL_MAX];
> - unsigned int maxlen = FSLABEL_MAX - 1;
> - char mntpt[PATH_MAX + 1];
> + struct ext4_tune_sb_params params;
> + __u64 fs_blocks_count;
> + __u32 fs_feature_array[3], kernel_set_mask[3], kernel_clear_mask[3];
> + __u32 default_mnt_opts;
> + int fs_set_ops = 0;
>
> - ret = ext2fs_check_mount_point(device_name, &mnt_flags,
> - mntpt, sizeof(mntpt));
> - if (ret)
> - return -1;
> + if (fs_fd < 0)
> + return 0;
>
> - if (!(mnt_flags & EXT2_MF_MOUNTED) ||
> - (setlabel && (mnt_flags & EXT2_MF_READONLY)))
> - return -1;
> + if (opts[OPT_PRINT_LABEL] &&
> + !ioctl(fs_fd, FS_IOC_GETFSLABEL, &label)) {
> + printf("%.*s\n", EXT2_LEN_STR(label));
> + opts[OPT_PRINT_LABEL] = false;
> + }
>
> - if (!mntpt[0])
> - return -1;
> + if (mount_flags & EXT2_MF_READONLY)
> + return 0;
>
> - fd = open(mntpt, O_RDONLY);
> - if (fd < 0)
> - return -1;
> + if (opts[OPT_LABEL]) {
> + unsigned int maxlen = FSLABEL_MAX - 1;
>
> - /* Get fs label */
> - if (!setlabel) {
> - if (ioctl(fd, FS_IOC_GETFSLABEL, &label)) {
> - close(fd);
> - if (errno == ENOTTY)
> - return -1;
> - com_err(mntpt, errno, _("while trying to get fs label"));
> - return 1;
> + /* If it's extN file system, truncate the label
> + to appropriate size */
> + if (mount_flags & EXT2_MF_EXTFS)
> + maxlen = EXT2_LABEL_LEN;
> + if (strlen(new_label) > maxlen) {
> + fputs(_("Warning: label too long, truncating.\n"),
> + stderr);
> + new_label[maxlen] = '\0';
> }
> - close(fd);
> - printf("%.*s\n", EXT2_LEN_STR(label));
> - return 0;
> + if (ioctl(fs_fd, FS_IOC_SETFSLABEL, new_label) == 0)
> + opts[OPT_LABEL] = false;
> }
>
> - /* If it's extN file system, truncate the label to appropriate size */
> - if (mnt_flags & EXT2_MF_EXTFS)
> - maxlen = EXT2_LABEL_LEN;
> - if (strlen(new_label) > maxlen) {
> - fputs(_("Warning: label too long, truncating.\n"),
> - stderr);
> - new_label[maxlen] = '\0';
> - }
> + if (ioctl(fs_fd, EXT4_IOC_GET_TUNE_SB_PARAM, ¶ms))
> + return 0;
>
> - /* Set fs label */
> - if (ioctl(fd, FS_IOC_SETFSLABEL, new_label)) {
> - close(fd);
> - if (errno == ENOTTY)
> + fs_set_ops = params.set_flags;
> + fs_blocks_count = params.blocks_count;
> + fs_feature_array[0] = params.feature_compat;
> + fs_feature_array[1] = params.feature_incompat;
> + fs_feature_array[2] = params.feature_ro_compat;
> + kernel_set_mask[0] = params.set_feature_compat_mask;
> + kernel_set_mask[1] = params.set_feature_incompat_mask;
> + kernel_set_mask[2] = params.set_feature_ro_compat_mask;
> + kernel_clear_mask[0] = params.clear_feature_compat_mask;
> + kernel_clear_mask[1] = params.clear_feature_incompat_mask;
> + kernel_clear_mask[2] = params.clear_feature_ro_compat_mask;
> + default_mnt_opts = params.default_mnt_opts;
> +
> + memset(¶ms, 0, sizeof(params));
> +
> +#define SIMPLE_SET_PARAM(OPT, FLAG, PARAM_FIELD, VALUE) \
> + if (opts[OPT] && (fs_set_ops & FLAG)) { \
> + params.set_flags |= FLAG; \
> + params.PARAM_FIELD = VALUE; \
> + }
> + SIMPLE_SET_PARAM(OPT_ERROR_BEHAVIOR, EXT4_TUNE_FL_ERRORS_BEHAVIOR,
> + errors_behavior, errors);
> + SIMPLE_SET_PARAM(OPT_MOUNTCOUNT, EXT4_TUNE_FL_MNT_COUNT,
> + set_flags, mount_count);
> + SIMPLE_SET_PARAM(OPT_MAX_MOUNTCOUNT, EXT4_TUNE_FL_MAX_MNT_COUNT,
> + set_flags, max_mount_count);
> + SIMPLE_SET_PARAM(OPT_CHECKINTERVAL, EXT4_TUNE_FL_CHECKINTRVAL,
> + set_flags, interval);
> + SIMPLE_SET_PARAM(OPT_CHECKTIME, EXT4_TUNE_FL_LAST_CHECK_TIME,
> + last_check_time, last_check_time);
> + SIMPLE_SET_PARAM(OPT_RESUID, EXT4_TUNE_FL_RESERVED_UID,
> + reserved_uid, resuid);
> + SIMPLE_SET_PARAM(OPT_RESGID, EXT4_TUNE_FL_RESERVED_GID,
> + reserved_gid, resgid);
> + if (opts[OPT_RESERVED_RATIO] && !opts[OPT_RESERVED_BLOCKS]) {
> + reserved_blocks = reserved_ratio * fs_blocks_count / 100.0;
> + opts[OPT_RESERVED_BLOCKS] = true;
> + }
> + SIMPLE_SET_PARAM(OPT_RESERVED_BLOCKS, EXT4_TUNE_FL_RESERVED_BLOCKS,
> + reserved_blocks, reserved_blocks);
> + SIMPLE_SET_PARAM(OPT_RAID_STRIDE, EXT4_TUNE_FL_RAID_STRIDE,
> + raid_stride, stride);
> + SIMPLE_SET_PARAM(OPT_RAID_STRIPE_WIDTH, EXT4_TUNE_FL_RAID_STRIPE_WIDTH,
> + raid_stripe_width, stripe_width);
> + SIMPLE_SET_PARAM(OPT_ENCODING, EXT4_TUNE_FL_ENCODING,
> + encoding, encoding);
> + SIMPLE_SET_PARAM(OPT_ENCODING_FLAGS, EXT4_TUNE_FL_ENCODING_FLAGS,
> + encoding_flags, encoding_flags);
> + if (opts[OPT_MNTOPTS] &&
> + (fs_set_ops & EXT4_TUNE_FL_DEFAULT_MNT_OPTS)) {
> + if (e2p_edit_mntopts(mntopts_cmd, &default_mnt_opts, ~0)) {
> + fprintf(stderr, _("Invalid mount option set: %s\n"),
> + mntopts_cmd);
> return -1;
> - com_err(mntpt, errno, _("while trying to set fs label"));
> - return 1;
> + }
> + params.set_flags |= EXT4_TUNE_FL_DEFAULT_MNT_OPTS;
> + params.default_mnt_opts = default_mnt_opts;
> + }
> + if (opts[OPT_MOUNT_OPTS] &&
> + (fs_set_ops & EXT4_TUNE_FL_MOUNT_OPTS)) {
> + params.set_flags |= EXT4_TUNE_FL_MOUNT_OPTS;
> + strncpy(params.mount_opts, ext_mount_opts,
> + sizeof(params.mount_opts));
> + params.mount_opts[sizeof(params.mount_opts) - 1] = 0;
> + }
> + if (opts[OPT_FEATURES] &&
> + (fs_set_ops & EXT4_TUNE_FL_FEATURES) &&
> + !e2p_edit_feature2(features_cmd, fs_feature_array,
> + kernel_set_mask, kernel_clear_mask,
> + NULL, NULL)) {
> + params.set_flags |= EXT4_TUNE_FL_FEATURES;
> + params.feature_compat = fs_feature_array[0];
> + params.feature_incompat = fs_feature_array[1];
> + params.feature_ro_compat = fs_feature_array[2];
> + }
> + if (opts[OPT_FORCE_FSCK] &&
> + (fs_set_ops & EXT4_TUNE_FL_FORCE_FSCK))
> + params.set_flags |= EXT4_TUNE_FL_FORCE_FSCK;
> +
> + if (ioctl(fs_fd, EXT4_IOC_SET_TUNE_SB_PARAM, ¶ms) == 0) {
> + if (opts[OPT_ERROR_BEHAVIOR])
> + printf(_("Setting error behavior to %d\n"), errors);
> + if (opts[OPT_MOUNTCOUNT])
> + printf(_("Setting current mount count to %d\n"),
> + mount_count);
> + if (opts[OPT_MAX_MOUNTCOUNT])
> + printf(_("Setting maximal mount count to %d\n"),
> + max_mount_count);
> + if (opts[OPT_CHECKINTERVAL])
> + printf(_("Setting interval between checks to %lu seconds\n"),
> + interval);
> + if (opts[OPT_CHECKTIME])
> + printf(_("Setting time filesystem last checked to %s\n"),
> + ctime(&last_check_time));
> + if (opts[OPT_RESUID])
> + printf(_("Setting reserved blocks uid to %lu\n"),
> + resuid);
> + if (opts[OPT_RESGID])
> + printf(_("Setting reserved blocks gid to %lu\n"),
> + resgid);
> + if (opts[OPT_RESERVED_BLOCKS])
> + printf(_("Setting reserved blocks count to %llu\n"),
> + (unsigned long long) reserved_blocks);
> + if (opts[OPT_RAID_STRIDE])
> + printf(_("Setting stride size to %d\n"), stride);
> + if (opts[OPT_RAID_STRIPE_WIDTH])
> + printf(_("Setting stripe width to %d\n"),
> + stripe_width);
> + if (opts[OPT_MOUNT_OPTS])
> + printf(_("Setting extended default mount options to '%s'\n"),
> + ext_mount_opts);
> + if (opts[OPT_ENCODING])
> + printf(_("Setting encoding to '%s'\n"), encoding_str);
> + if (opts[OPT_ENCODING_FLAGS])
> + printf(_("Setting encoding_flags to '%s'\n"),
> + encoding_flags_str);
> + if (opts[OPT_FORCE_FSCK])
> + printf(_("Setting filesystem error flag to force fsck.\n"));
> + opts[OPT_ERROR_BEHAVIOR] = opts[OPT_MOUNTCOUNT] =
> + opts[OPT_MAX_MOUNTCOUNT] = opts[OPT_CHECKINTERVAL] =
> + opts[OPT_CHECKTIME] = opts[OPT_RESUID] =
> + opts[OPT_RESGID] = opts[OPT_RESERVED_RATIO] =
> + opts[OPT_RESERVED_BLOCKS] = opts[OPT_MNTOPTS] =
> + opts[OPT_RAID_STRIDE] = opts[OPT_RAID_STRIPE_WIDTH] =
> + opts[OPT_MOUNT_OPTS] = opts[OPT_FEATURES] =
> + opts[OPT_FORCE_FSCK] = opts[OPT_ENCODING] =
> + opts[OPT_ENCODING_FLAGS] = false;
> + printf("online tune superblock succeeded\n");
> + } else {
> + perror("ioctl EXT4_IOC_SET_TUNE_SB_PARAM");
> + return -1;
> }
> - close(fd);
> - return 0;
> -#else
> - return -1;
Shouldn't this still return 1 if this isn't being built on __linux__?
> #endif
> + return 0;
> }
>
> #ifndef BUILD_AS_LIB
> @@ -3186,7 +3387,6 @@ int tune2fs_main(int argc, char **argv)
> io_manager io_ptr, io_ptr_orig = NULL;
> int rc = 0;
> char default_undo_file[1] = { 0 };
> - char mntpt[PATH_MAX + 1] = { 0 };
> int fd = -1;
> struct fsuuid *fsuuid = NULL;
>
> @@ -3220,19 +3420,21 @@ int tune2fs_main(int argc, char **argv)
> #endif
> io_ptr = unix_io_manager;
>
> - /*
> - * Try the get/set fs label using ioctls before we even attempt
> - * to open the file system.
> - */
> - if (opts[OPT_LABEL] || opts[OPT_PRINT_LABEL]) {
> - rc = handle_fslabel(opts[OPT_LABEL]);
> - if (rc != -1) {
> -#ifndef BUILD_AS_LIB
> - exit(rc);
> + if (get_mount_flags() < 0 || try_mounted_tune2fs() << 0) {
Why shift left here ^^ ??
> +#ifdef BUILD_AS_LIB
> + return -1;
> +#else
> + exit(1);
> +#endif
> + }
> +
> + if (!tune_opts_requested()) {
> + /* printf("No further tune opts left\n"); */
> +#ifdef BUILD_AS_LIB
> + return 0;
> +#else
> + exit(0);
> #endif
> - return rc;
> - }
> - rc = 0;
> }
>
> retry_open:
> @@ -3338,16 +3540,6 @@ retry_open:
> goto closefs;
> }
>
> - retval = ext2fs_check_mount_point(device_name, &mount_flags,
> - mntpt, sizeof(mntpt));
> - if (retval) {
> - com_err("ext2fs_check_mount_point", retval,
> - _("while determining whether %s is mounted."),
> - device_name);
> - rc = 1;
> - goto closefs;
> - }
> -
> #ifdef NO_RECOVERY
> /* Warn if file system needs recovery and it is opened for writing. */
> if ((open_flag & EXT2_FLAG_RW) && !(mount_flags & EXT2_MF_MOUNTED) &&
> @@ -3382,9 +3574,6 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
> fs->flags |= EXT2_FLAG_SUPER_ONLY;
>
> if (opts[OPT_MAX_MOUNTCOUNT]) {
> - if (max_mount_count == 65536)
> - max_mount_count = EXT2_DFL_MAX_MNT_COUNT +
> - (random() % EXT2_DFL_MAX_MNT_COUNT);
> sb->s_max_mnt_count = max_mount_count;
> ext2fs_mark_super_dirty(fs);
> printf(_("Setting maximal mount count to %d\n"),
> @@ -3410,13 +3599,6 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
> }
> }
> if (opts[OPT_CHECKINTERVAL]) {
> - if ((unsigned long long)interval >= (1ULL << 32)) {
> - com_err(program_name, 0,
> - _("interval between checks is too big (%lu)"),
> - interval);
> - rc = 1;
> - goto closefs;
> - }
> sb->s_checkinterval = interval;
> ext2fs_mark_super_dirty(fs);
> printf(_("Setting interval between checks to %lu seconds\n"),
> @@ -3494,7 +3676,7 @@ _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
> sizeof(sb->s_last_mounted));
> ext2fs_mark_super_dirty(fs);
> }
> - if (mntopts_cmd) {
> + if (opts[OPT_MNTOPTS]) {
> rc = update_mntopts(fs, mntopts_cmd);
> if (rc)
> goto closefs;
> --
> 2.51.0
>
>
Powered by blists - more mailing lists