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 --- 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); + } break; case 'j': opts[OPT_JOURNAL_SIZE] = true; @@ -3105,73 +3175,204 @@ fs_update_journal_user(struct ext2_super_block *sb, __u8 old_uuid[UUID_SIZE]) return 0; } -/* - * 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; #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) { +#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