* [PATCH v2 0/3] ext4: Add support for mounted updates to the superblock via an ioctl @ 2025-09-17 3:22 Theodore Ts'o via B4 Relay 2025-09-17 3:22 ` [PATCH v2 1/3] ext4: avoid potential buffer over-read in parse_apply_sb_mount_options() Theodore Ts'o via B4 Relay ` (3 more replies) 0 siblings, 4 replies; 10+ messages in thread From: Theodore Ts'o via B4 Relay @ 2025-09-17 3:22 UTC (permalink / raw) To: tytso; +Cc: linux-ext4, linux-api, stable This patch series enables a future version of tune2fs to be able to modify certain parts of the ext4 superblock without to write to the block device. The first patch fixes a potential buffer overrun caused by a maliciously moified superblock. The second patch adds support for 32-bit uid and gid's which can have access to the reserved blocks pool. The last patch adds the ioctl's which will be used by tune2fs. Signed-off-by: Theodore Ts'o <tytso@mit.edu> --- Changes in v2: - fix bugs that were detected using sparse - remove tune (unsafe) ability to clear certain compat faatures - add the ability to set the encoding and encoding flags for case folding - Link to v1: https://lore.kernel.org/r/20250908-tune2fs-v1-0-e3a6929f3355@mit.edu --- Theodore Ts'o (3): ext4: avoid potential buffer over-read in parse_apply_sb_mount_options() ext4: add support for 32-bit default reserved uid and gid values ext4: implemet new ioctls to set and get superblock parameters fs/ext4/ext4.h | 16 +++- fs/ext4/ioctl.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- fs/ext4/super.c | 25 +++---- include/uapi/linux/ext4.h | 53 +++++++++++++ 4 files changed, 382 insertions(+), 24 deletions(-) --- base-commit: b320789d6883cc00ac78ce83bccbfe7ed58afcf0 change-id: 20250830-tune2fs-3376beb72403 Best regards, -- Theodore Ts'o <tytso@mit.edu> ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 1/3] ext4: avoid potential buffer over-read in parse_apply_sb_mount_options() 2025-09-17 3:22 [PATCH v2 0/3] ext4: Add support for mounted updates to the superblock via an ioctl Theodore Ts'o via B4 Relay @ 2025-09-17 3:22 ` Theodore Ts'o via B4 Relay 2025-09-17 16:05 ` Jan Kara 2025-09-17 3:22 ` [PATCH v2 2/3] ext4: add support for 32-bit default reserved uid and gid values Theodore Ts'o via B4 Relay ` (2 subsequent siblings) 3 siblings, 1 reply; 10+ messages in thread From: Theodore Ts'o via B4 Relay @ 2025-09-17 3:22 UTC (permalink / raw) To: tytso; +Cc: linux-ext4, linux-api, stable From: Theodore Ts'o <tytso@mit.edu> Unlike other strings in the ext4 superblock, we rely on tune2fs to make sure s_mount_opts is NUL terminated. Harden parse_apply_sb_mount_options() by treating s_mount_opts as a potential __nonstring. Cc: stable@vger.kernel.org Fixes: 8b67f04ab9de ("ext4: Add mount options in superblock") Signed-off-by: Theodore Ts'o <tytso@mit.edu> --- fs/ext4/super.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 699c15db28a82f26809bf68533454a242596f0fd..94c98446c84f9a4614971d246ca7f001de610a8a 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2460,7 +2460,7 @@ static int parse_apply_sb_mount_options(struct super_block *sb, struct ext4_fs_context *m_ctx) { struct ext4_sb_info *sbi = EXT4_SB(sb); - char *s_mount_opts = NULL; + char s_mount_opts[65]; struct ext4_fs_context *s_ctx = NULL; struct fs_context *fc = NULL; int ret = -ENOMEM; @@ -2468,15 +2468,11 @@ static int parse_apply_sb_mount_options(struct super_block *sb, if (!sbi->s_es->s_mount_opts[0]) return 0; - s_mount_opts = kstrndup(sbi->s_es->s_mount_opts, - sizeof(sbi->s_es->s_mount_opts), - GFP_KERNEL); - if (!s_mount_opts) - return ret; + strscpy_pad(s_mount_opts, sbi->s_es->s_mount_opts); fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL); if (!fc) - goto out_free; + return -ENOMEM; s_ctx = kzalloc(sizeof(struct ext4_fs_context), GFP_KERNEL); if (!s_ctx) @@ -2508,11 +2504,8 @@ static int parse_apply_sb_mount_options(struct super_block *sb, ret = 0; out_free: - if (fc) { - ext4_fc_free(fc); - kfree(fc); - } - kfree(s_mount_opts); + ext4_fc_free(fc); + kfree(fc); return ret; } -- 2.51.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/3] ext4: avoid potential buffer over-read in parse_apply_sb_mount_options() 2025-09-17 3:22 ` [PATCH v2 1/3] ext4: avoid potential buffer over-read in parse_apply_sb_mount_options() Theodore Ts'o via B4 Relay @ 2025-09-17 16:05 ` Jan Kara 2025-09-18 16:55 ` Darrick J. Wong 0 siblings, 1 reply; 10+ messages in thread From: Jan Kara @ 2025-09-17 16:05 UTC (permalink / raw) To: tytso; +Cc: linux-ext4, linux-api, stable On Tue 16-09-25 23:22:47, Theodore Ts'o via B4 Relay wrote: > From: Theodore Ts'o <tytso@mit.edu> > > Unlike other strings in the ext4 superblock, we rely on tune2fs to > make sure s_mount_opts is NUL terminated. Harden > parse_apply_sb_mount_options() by treating s_mount_opts as a potential > __nonstring. > > Cc: stable@vger.kernel.org > Fixes: 8b67f04ab9de ("ext4: Add mount options in superblock") > Signed-off-by: Theodore Ts'o <tytso@mit.edu> Looks good. Feel free to add: Reviewed-by: Jan Kara <jack@suse.cz> Honza > --- > fs/ext4/super.c | 17 +++++------------ > 1 file changed, 5 insertions(+), 12 deletions(-) > > diff --git a/fs/ext4/super.c b/fs/ext4/super.c > index 699c15db28a82f26809bf68533454a242596f0fd..94c98446c84f9a4614971d246ca7f001de610a8a 100644 > --- a/fs/ext4/super.c > +++ b/fs/ext4/super.c > @@ -2460,7 +2460,7 @@ static int parse_apply_sb_mount_options(struct super_block *sb, > struct ext4_fs_context *m_ctx) > { > struct ext4_sb_info *sbi = EXT4_SB(sb); > - char *s_mount_opts = NULL; > + char s_mount_opts[65]; > struct ext4_fs_context *s_ctx = NULL; > struct fs_context *fc = NULL; > int ret = -ENOMEM; > @@ -2468,15 +2468,11 @@ static int parse_apply_sb_mount_options(struct super_block *sb, > if (!sbi->s_es->s_mount_opts[0]) > return 0; > > - s_mount_opts = kstrndup(sbi->s_es->s_mount_opts, > - sizeof(sbi->s_es->s_mount_opts), > - GFP_KERNEL); > - if (!s_mount_opts) > - return ret; > + strscpy_pad(s_mount_opts, sbi->s_es->s_mount_opts); > > fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL); > if (!fc) > - goto out_free; > + return -ENOMEM; > > s_ctx = kzalloc(sizeof(struct ext4_fs_context), GFP_KERNEL); > if (!s_ctx) > @@ -2508,11 +2504,8 @@ static int parse_apply_sb_mount_options(struct super_block *sb, > ret = 0; > > out_free: > - if (fc) { > - ext4_fc_free(fc); > - kfree(fc); > - } > - kfree(s_mount_opts); > + ext4_fc_free(fc); > + kfree(fc); > return ret; > } > > > -- > 2.51.0 > > > -- Jan Kara <jack@suse.com> SUSE Labs, CR ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/3] ext4: avoid potential buffer over-read in parse_apply_sb_mount_options() 2025-09-17 16:05 ` Jan Kara @ 2025-09-18 16:55 ` Darrick J. Wong 0 siblings, 0 replies; 10+ messages in thread From: Darrick J. Wong @ 2025-09-18 16:55 UTC (permalink / raw) To: Jan Kara; +Cc: tytso, linux-ext4, linux-api, stable On Wed, Sep 17, 2025 at 06:05:06PM +0200, Jan Kara wrote: > On Tue 16-09-25 23:22:47, Theodore Ts'o via B4 Relay wrote: > > From: Theodore Ts'o <tytso@mit.edu> > > > > Unlike other strings in the ext4 superblock, we rely on tune2fs to > > make sure s_mount_opts is NUL terminated. Harden > > parse_apply_sb_mount_options() by treating s_mount_opts as a potential > > __nonstring. > > > > Cc: stable@vger.kernel.org > > Fixes: 8b67f04ab9de ("ext4: Add mount options in superblock") > > Signed-off-by: Theodore Ts'o <tytso@mit.edu> > > Looks good. Feel free to add: > > Reviewed-by: Jan Kara <jack@suse.cz> Looks fine to me too, Reviewed-by: "Darrick J. Wong" <djwong@kernel.org> --D > > Honza > > > --- > > fs/ext4/super.c | 17 +++++------------ > > 1 file changed, 5 insertions(+), 12 deletions(-) > > > > diff --git a/fs/ext4/super.c b/fs/ext4/super.c > > index 699c15db28a82f26809bf68533454a242596f0fd..94c98446c84f9a4614971d246ca7f001de610a8a 100644 > > --- a/fs/ext4/super.c > > +++ b/fs/ext4/super.c > > @@ -2460,7 +2460,7 @@ static int parse_apply_sb_mount_options(struct super_block *sb, > > struct ext4_fs_context *m_ctx) > > { > > struct ext4_sb_info *sbi = EXT4_SB(sb); > > - char *s_mount_opts = NULL; > > + char s_mount_opts[65]; > > struct ext4_fs_context *s_ctx = NULL; > > struct fs_context *fc = NULL; > > int ret = -ENOMEM; > > @@ -2468,15 +2468,11 @@ static int parse_apply_sb_mount_options(struct super_block *sb, > > if (!sbi->s_es->s_mount_opts[0]) > > return 0; > > > > - s_mount_opts = kstrndup(sbi->s_es->s_mount_opts, > > - sizeof(sbi->s_es->s_mount_opts), > > - GFP_KERNEL); > > - if (!s_mount_opts) > > - return ret; > > + strscpy_pad(s_mount_opts, sbi->s_es->s_mount_opts); > > > > fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL); > > if (!fc) > > - goto out_free; > > + return -ENOMEM; > > > > s_ctx = kzalloc(sizeof(struct ext4_fs_context), GFP_KERNEL); > > if (!s_ctx) > > @@ -2508,11 +2504,8 @@ static int parse_apply_sb_mount_options(struct super_block *sb, > > ret = 0; > > > > out_free: > > - if (fc) { > > - ext4_fc_free(fc); > > - kfree(fc); > > - } > > - kfree(s_mount_opts); > > + ext4_fc_free(fc); > > + kfree(fc); > > return ret; > > } > > > > > > -- > > 2.51.0 > > > > > > > -- > Jan Kara <jack@suse.com> > SUSE Labs, CR > ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 2/3] ext4: add support for 32-bit default reserved uid and gid values 2025-09-17 3:22 [PATCH v2 0/3] ext4: Add support for mounted updates to the superblock via an ioctl Theodore Ts'o via B4 Relay 2025-09-17 3:22 ` [PATCH v2 1/3] ext4: avoid potential buffer over-read in parse_apply_sb_mount_options() Theodore Ts'o via B4 Relay @ 2025-09-17 3:22 ` Theodore Ts'o via B4 Relay 2025-09-17 16:10 ` Jan Kara 2025-09-17 3:22 ` [PATCH v2 3/3] ext4: implemet new ioctls to set and get superblock parameters Theodore Ts'o via B4 Relay 2025-09-26 21:47 ` [PATCH v2 0/3] ext4: Add support for mounted updates to the superblock via an ioctl Theodore Ts'o 3 siblings, 1 reply; 10+ messages in thread From: Theodore Ts'o via B4 Relay @ 2025-09-17 3:22 UTC (permalink / raw) To: tytso; +Cc: linux-ext4, linux-api From: Theodore Ts'o <tytso@mit.edu> Support for specifying the default user id and group id that is allowed to use the reserved block space was added way back when Linux only supported 16-bit uid's and gid's. (Yeah, that long ago.) It's not a commonly used feature, but let's add support for 32-bit user and group id's. Signed-off-by: Theodore Ts'o <tytso@mit.edu> --- fs/ext4/ext4.h | 16 +++++++++++++++- fs/ext4/super.c | 8 ++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 01a6e2de7fc3ef0e20b039d3200b9c9bd656f59f..4bfcd5f0c74fda30db4009ee28fbee00a2f6b76f 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1442,7 +1442,9 @@ struct ext4_super_block { __le16 s_encoding; /* Filename charset encoding */ __le16 s_encoding_flags; /* Filename charset encoding flags */ __le32 s_orphan_file_inum; /* Inode for tracking orphan inodes */ - __le32 s_reserved[94]; /* Padding to the end of the block */ + __le16 s_def_resuid_hi; + __le16 s_def_resgid_hi; + __le32 s_reserved[93]; /* Padding to the end of the block */ __le32 s_checksum; /* crc32c(superblock) */ }; @@ -1812,6 +1814,18 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino) ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count)); } +static inline int ext4_get_resuid(struct ext4_super_block *es) +{ + return(le16_to_cpu(es->s_def_resuid) | + (le16_to_cpu(es->s_def_resuid_hi) << 16)); +} + +static inline int ext4_get_resgid(struct ext4_super_block *es) +{ + return(le16_to_cpu(es->s_def_resgid) | + (le16_to_cpu(es->s_def_resgid_hi) << 16)); +} + /* * Returns: sbi->field[index] * Used to access an array element from the following sbi fields which require diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 94c98446c84f9a4614971d246ca7f001de610a8a..0256c8f7c6cee2b8d9295f2fa9a7acd904382e83 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2951,11 +2951,11 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb, } if (nodefs || !uid_eq(sbi->s_resuid, make_kuid(&init_user_ns, EXT4_DEF_RESUID)) || - le16_to_cpu(es->s_def_resuid) != EXT4_DEF_RESUID) + ext4_get_resuid(es) != EXT4_DEF_RESUID) SEQ_OPTS_PRINT("resuid=%u", from_kuid_munged(&init_user_ns, sbi->s_resuid)); if (nodefs || !gid_eq(sbi->s_resgid, make_kgid(&init_user_ns, EXT4_DEF_RESGID)) || - le16_to_cpu(es->s_def_resgid) != EXT4_DEF_RESGID) + ext4_get_resgid(es) != EXT4_DEF_RESGID) SEQ_OPTS_PRINT("resgid=%u", from_kgid_munged(&init_user_ns, sbi->s_resgid)); def_errors = nodefs ? -1 : le16_to_cpu(es->s_errors); @@ -5270,8 +5270,8 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb) ext4_set_def_opts(sb, es); - sbi->s_resuid = make_kuid(&init_user_ns, le16_to_cpu(es->s_def_resuid)); - sbi->s_resgid = make_kgid(&init_user_ns, le16_to_cpu(es->s_def_resgid)); + sbi->s_resuid = make_kuid(&init_user_ns, ext4_get_resuid(es)); + sbi->s_resgid = make_kgid(&init_user_ns, ext4_get_resuid(es)); sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE * HZ; sbi->s_min_batch_time = EXT4_DEF_MIN_BATCH_TIME; sbi->s_max_batch_time = EXT4_DEF_MAX_BATCH_TIME; -- 2.51.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/3] ext4: add support for 32-bit default reserved uid and gid values 2025-09-17 3:22 ` [PATCH v2 2/3] ext4: add support for 32-bit default reserved uid and gid values Theodore Ts'o via B4 Relay @ 2025-09-17 16:10 ` Jan Kara 0 siblings, 0 replies; 10+ messages in thread From: Jan Kara @ 2025-09-17 16:10 UTC (permalink / raw) To: tytso; +Cc: linux-ext4, linux-api On Tue 16-09-25 23:22:48, Theodore Ts'o via B4 Relay wrote: > From: Theodore Ts'o <tytso@mit.edu> > > Support for specifying the default user id and group id that is > allowed to use the reserved block space was added way back when Linux > only supported 16-bit uid's and gid's. (Yeah, that long ago.) It's > not a commonly used feature, but let's add support for 32-bit user and > group id's. > > Signed-off-by: Theodore Ts'o <tytso@mit.edu> > --- > fs/ext4/ext4.h | 16 +++++++++++++++- > fs/ext4/super.c | 8 ++++---- > 2 files changed, 19 insertions(+), 5 deletions(-) > > diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h > index 01a6e2de7fc3ef0e20b039d3200b9c9bd656f59f..4bfcd5f0c74fda30db4009ee28fbee00a2f6b76f 100644 > --- a/fs/ext4/ext4.h > +++ b/fs/ext4/ext4.h > @@ -1442,7 +1442,9 @@ struct ext4_super_block { > __le16 s_encoding; /* Filename charset encoding */ > __le16 s_encoding_flags; /* Filename charset encoding flags */ > __le32 s_orphan_file_inum; /* Inode for tracking orphan inodes */ > - __le32 s_reserved[94]; /* Padding to the end of the block */ > + __le16 s_def_resuid_hi; > + __le16 s_def_resgid_hi; > + __le32 s_reserved[93]; /* Padding to the end of the block */ > __le32 s_checksum; /* crc32c(superblock) */ > }; > > @@ -1812,6 +1814,18 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino) > ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count)); > } > > +static inline int ext4_get_resuid(struct ext4_super_block *es) > +{ > + return(le16_to_cpu(es->s_def_resuid) | > + (le16_to_cpu(es->s_def_resuid_hi) << 16)); > +} I'd prefer a style like: return le16_to_cpu(es->s_def_resuid) | (le16_to_cpu(es->s_def_resuid_hi) << 16); but whatever... > @@ -5270,8 +5270,8 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb) > > ext4_set_def_opts(sb, es); > > - sbi->s_resuid = make_kuid(&init_user_ns, le16_to_cpu(es->s_def_resuid)); > - sbi->s_resgid = make_kgid(&init_user_ns, le16_to_cpu(es->s_def_resgid)); > + sbi->s_resuid = make_kuid(&init_user_ns, ext4_get_resuid(es)); > + sbi->s_resgid = make_kgid(&init_user_ns, ext4_get_resuid(es)); ^^^^ ext4_get_resgid() here. > sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE * HZ; > sbi->s_min_batch_time = EXT4_DEF_MIN_BATCH_TIME; > sbi->s_max_batch_time = EXT4_DEF_MAX_BATCH_TIME; Honza -- Jan Kara <jack@suse.com> SUSE Labs, CR ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 3/3] ext4: implemet new ioctls to set and get superblock parameters 2025-09-17 3:22 [PATCH v2 0/3] ext4: Add support for mounted updates to the superblock via an ioctl Theodore Ts'o via B4 Relay 2025-09-17 3:22 ` [PATCH v2 1/3] ext4: avoid potential buffer over-read in parse_apply_sb_mount_options() Theodore Ts'o via B4 Relay 2025-09-17 3:22 ` [PATCH v2 2/3] ext4: add support for 32-bit default reserved uid and gid values Theodore Ts'o via B4 Relay @ 2025-09-17 3:22 ` Theodore Ts'o via B4 Relay 2025-09-17 16:22 ` Jan Kara 2025-09-18 16:57 ` Darrick J. Wong 2025-09-26 21:47 ` [PATCH v2 0/3] ext4: Add support for mounted updates to the superblock via an ioctl Theodore Ts'o 3 siblings, 2 replies; 10+ messages in thread From: Theodore Ts'o via B4 Relay @ 2025-09-17 3:22 UTC (permalink / raw) To: tytso; +Cc: linux-ext4, linux-api From: Theodore Ts'o <tytso@mit.edu> Implement the EXT4_IOC_GET_TUNE_SB_PARAM and EXT4_IOC_SET_TUNE_SB_PARAM ioctls, which allow certains superblock parameters to be set while the file system is mounted, without needing write access to the block device. Signed-off-by: Theodore Ts'o <tytso@mit.edu> --- fs/ext4/ioctl.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- include/uapi/linux/ext4.h | 53 +++++++++++++ 2 files changed, 358 insertions(+), 7 deletions(-) diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 84e3c73952d72e436429489f5fc8b7ae1c01c7a1..a93a7baae990cc5580d2ddb3ffcc72fe15246978 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -27,14 +27,16 @@ #include "fsmap.h" #include <trace/events/ext4.h> -typedef void ext4_update_sb_callback(struct ext4_super_block *es, - const void *arg); +typedef void ext4_update_sb_callback(struct ext4_sb_info *sbi, + struct ext4_super_block *es, + const void *arg); /* * Superblock modification callback function for changing file system * label */ -static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg) +static void ext4_sb_setlabel(struct ext4_sb_info *sbi, + struct ext4_super_block *es, const void *arg) { /* Sanity check, this should never happen */ BUILD_BUG_ON(sizeof(es->s_volume_name) < EXT4_LABEL_MAX); @@ -46,7 +48,8 @@ static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg) * Superblock modification callback function for changing file system * UUID. */ -static void ext4_sb_setuuid(struct ext4_super_block *es, const void *arg) +static void ext4_sb_setuuid(struct ext4_sb_info *sbi, + struct ext4_super_block *es, const void *arg) { memcpy(es->s_uuid, (__u8 *)arg, UUID_SIZE); } @@ -71,7 +74,7 @@ int ext4_update_primary_sb(struct super_block *sb, handle_t *handle, goto out_err; lock_buffer(bh); - func(es, arg); + func(sbi, es, arg); ext4_superblock_csum_set(sb); unlock_buffer(bh); @@ -149,7 +152,7 @@ static int ext4_update_backup_sb(struct super_block *sb, unlock_buffer(bh); goto out_bh; } - func(es, arg); + func(EXT4_SB(sb), es, arg); if (ext4_has_feature_metadata_csum(sb)) es->s_checksum = ext4_superblock_csum(es); set_buffer_uptodate(bh); @@ -1230,6 +1233,295 @@ static int ext4_ioctl_setuuid(struct file *filp, return ret; } + +#define TUNE_OPS_SUPPORTED (EXT4_TUNE_FL_ERRORS_BEHAVIOR | \ + EXT4_TUNE_FL_MNT_COUNT | EXT4_TUNE_FL_MAX_MNT_COUNT | \ + EXT4_TUNE_FL_CHECKINTRVAL | EXT4_TUNE_FL_LAST_CHECK_TIME | \ + EXT4_TUNE_FL_RESERVED_BLOCKS | EXT4_TUNE_FL_RESERVED_UID | \ + EXT4_TUNE_FL_RESERVED_GID | EXT4_TUNE_FL_DEFAULT_MNT_OPTS | \ + EXT4_TUNE_FL_DEF_HASH_ALG | EXT4_TUNE_FL_RAID_STRIDE | \ + EXT4_TUNE_FL_RAID_STRIPE_WIDTH | EXT4_TUNE_FL_MOUNT_OPTS | \ + EXT4_TUNE_FL_FEATURES | EXT4_TUNE_FL_EDIT_FEATURES | \ + EXT4_TUNE_FL_FORCE_FSCK | EXT4_TUNE_FL_ENCODING | \ + EXT4_TUNE_FL_ENCODING_FLAGS) + +#define EXT4_TUNE_SET_COMPAT_SUPP \ + (EXT4_FEATURE_COMPAT_DIR_INDEX | \ + EXT4_FEATURE_COMPAT_STABLE_INODES) +#define EXT4_TUNE_SET_INCOMPAT_SUPP \ + (EXT4_FEATURE_INCOMPAT_EXTENTS | \ + EXT4_FEATURE_INCOMPAT_EA_INODE | \ + EXT4_FEATURE_INCOMPAT_ENCRYPT | \ + EXT4_FEATURE_INCOMPAT_CSUM_SEED | \ + EXT4_FEATURE_INCOMPAT_LARGEDIR | \ + EXT4_FEATURE_INCOMPAT_CASEFOLD) +#define EXT4_TUNE_SET_RO_COMPAT_SUPP \ + (EXT4_FEATURE_RO_COMPAT_LARGE_FILE | \ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK | \ + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \ + EXT4_FEATURE_RO_COMPAT_PROJECT | \ + EXT4_FEATURE_RO_COMPAT_VERITY) + +#define EXT4_TUNE_CLEAR_COMPAT_SUPP (0) +#define EXT4_TUNE_CLEAR_INCOMPAT_SUPP (0) +#define EXT4_TUNE_CLEAR_RO_COMPAT_SUPP (0) + +#define SB_ENC_SUPP_MASK (SB_ENC_STRICT_MODE_FL | \ + SB_ENC_NO_COMPAT_FALLBACK_FL) + +static int ext4_ioctl_get_tune_sb(struct ext4_sb_info *sbi, + struct ext4_tune_sb_params __user *params) +{ + struct ext4_tune_sb_params ret; + struct ext4_super_block *es = sbi->s_es; + + memset(&ret, 0, sizeof(ret)); + ret.set_flags = TUNE_OPS_SUPPORTED; + ret.errors_behavior = le16_to_cpu(es->s_errors); + ret.mnt_count = le16_to_cpu(es->s_mnt_count); + ret.max_mnt_count = le16_to_cpu(es->s_max_mnt_count); + ret.checkinterval = le32_to_cpu(es->s_checkinterval); + ret.last_check_time = le32_to_cpu(es->s_lastcheck); + ret.reserved_blocks = ext4_r_blocks_count(es); + ret.blocks_count = ext4_blocks_count(es); + ret.reserved_uid = ext4_get_resuid(es); + ret.reserved_gid = ext4_get_resgid(es); + ret.default_mnt_opts = le32_to_cpu(es->s_default_mount_opts); + ret.def_hash_alg = es->s_def_hash_version; + ret.raid_stride = le16_to_cpu(es->s_raid_stride); + ret.raid_stripe_width = le32_to_cpu(es->s_raid_stripe_width); + ret.encoding = le16_to_cpu(es->s_encoding); + ret.encoding_flags = le16_to_cpu(es->s_encoding_flags); + strscpy_pad(ret.mount_opts, es->s_mount_opts); + ret.feature_compat = le32_to_cpu(es->s_feature_compat); + ret.feature_incompat = le32_to_cpu(es->s_feature_incompat); + ret.feature_ro_compat = le32_to_cpu(es->s_feature_ro_compat); + ret.set_feature_compat_mask = EXT4_TUNE_SET_COMPAT_SUPP; + ret.set_feature_incompat_mask = EXT4_TUNE_SET_INCOMPAT_SUPP; + ret.set_feature_ro_compat_mask = EXT4_TUNE_SET_RO_COMPAT_SUPP; + ret.clear_feature_compat_mask = EXT4_TUNE_CLEAR_COMPAT_SUPP; + ret.clear_feature_incompat_mask = EXT4_TUNE_CLEAR_INCOMPAT_SUPP; + ret.clear_feature_ro_compat_mask = EXT4_TUNE_CLEAR_RO_COMPAT_SUPP; + if (copy_to_user(params, &ret, sizeof(ret))) + return -EFAULT; + return 0; +} + +static void ext4_sb_setparams(struct ext4_sb_info *sbi, + struct ext4_super_block *es, const void *arg) +{ + const struct ext4_tune_sb_params *params = arg; + + if (params->set_flags & EXT4_TUNE_FL_ERRORS_BEHAVIOR) + es->s_errors = cpu_to_le16(params->errors_behavior); + if (params->set_flags & EXT4_TUNE_FL_MNT_COUNT) + es->s_mnt_count = cpu_to_le16(params->mnt_count); + if (params->set_flags & EXT4_TUNE_FL_MAX_MNT_COUNT) + es->s_max_mnt_count = cpu_to_le16(params->max_mnt_count); + if (params->set_flags & EXT4_TUNE_FL_CHECKINTRVAL) + es->s_checkinterval = cpu_to_le32(params->checkinterval); + if (params->set_flags & EXT4_TUNE_FL_LAST_CHECK_TIME) + es->s_lastcheck = cpu_to_le32(params->last_check_time); + if (params->set_flags & EXT4_TUNE_FL_RESERVED_BLOCKS) { + ext4_fsblk_t blk = params->reserved_blocks; + + es->s_r_blocks_count_lo = cpu_to_le32((u32)blk); + es->s_r_blocks_count_hi = cpu_to_le32(blk >> 32); + } + if (params->set_flags & EXT4_TUNE_FL_RESERVED_UID) { + int uid = params->reserved_uid; + + es->s_def_resuid = cpu_to_le16(uid & 0xFFFF); + es->s_def_resuid_hi = cpu_to_le16(uid >> 16); + } + if (params->set_flags & EXT4_TUNE_FL_RESERVED_GID) { + int gid = params->reserved_gid; + + es->s_def_resgid = cpu_to_le16(gid & 0xFFFF); + es->s_def_resgid_hi = cpu_to_le16(gid >> 16); + } + if (params->set_flags & EXT4_TUNE_FL_DEFAULT_MNT_OPTS) + es->s_default_mount_opts = cpu_to_le32(params->default_mnt_opts); + if (params->set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) + es->s_def_hash_version = params->def_hash_alg; + if (params->set_flags & EXT4_TUNE_FL_RAID_STRIDE) + es->s_raid_stride = cpu_to_le16(params->raid_stride); + if (params->set_flags & EXT4_TUNE_FL_RAID_STRIPE_WIDTH) + es->s_raid_stripe_width = + cpu_to_le32(params->raid_stripe_width); + if (params->set_flags & EXT4_TUNE_FL_ENCODING) + es->s_encoding = cpu_to_le16(params->encoding); + if (params->set_flags & EXT4_TUNE_FL_ENCODING_FLAGS) + es->s_encoding_flags = cpu_to_le16(params->encoding_flags); + strscpy_pad(es->s_mount_opts, params->mount_opts); + if (params->set_flags & EXT4_TUNE_FL_EDIT_FEATURES) { + es->s_feature_compat |= + cpu_to_le32(params->set_feature_compat_mask); + es->s_feature_incompat |= + cpu_to_le32(params->set_feature_incompat_mask); + es->s_feature_ro_compat |= + cpu_to_le32(params->set_feature_ro_compat_mask); + es->s_feature_compat &= + ~cpu_to_le32(params->clear_feature_compat_mask); + es->s_feature_incompat &= + ~cpu_to_le32(params->clear_feature_incompat_mask); + es->s_feature_ro_compat &= + ~cpu_to_le32(params->clear_feature_ro_compat_mask); + if (params->set_feature_compat_mask & + EXT4_FEATURE_COMPAT_DIR_INDEX) + es->s_def_hash_version = sbi->s_def_hash_version; + if (params->set_feature_incompat_mask & + EXT4_FEATURE_INCOMPAT_CSUM_SEED) + es->s_checksum_seed = cpu_to_le32(sbi->s_csum_seed); + } + if (params->set_flags & EXT4_TUNE_FL_FORCE_FSCK) + es->s_state |= cpu_to_le16(EXT4_ERROR_FS); +} + +static int ext4_ioctl_set_tune_sb(struct file *filp, + struct ext4_tune_sb_params __user *in) +{ + struct ext4_tune_sb_params params; + struct super_block *sb = file_inode(filp)->i_sb; + struct ext4_sb_info *sbi = EXT4_SB(sb); + struct ext4_super_block *es = sbi->s_es; + int enabling_casefold = 0; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (copy_from_user(¶ms, in, sizeof(params))) + return -EFAULT; + + if ((params.set_flags & ~TUNE_OPS_SUPPORTED) != 0) + return -EOPNOTSUPP; + + if ((params.set_flags & EXT4_TUNE_FL_ERRORS_BEHAVIOR) && + (params.errors_behavior > EXT4_ERRORS_PANIC)) + return -EINVAL; + + if ((params.set_flags & EXT4_TUNE_FL_RESERVED_BLOCKS) && + (params.reserved_blocks > ext4_blocks_count(sbi->s_es) / 2)) + return -EINVAL; + if ((params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) && + ((params.def_hash_alg > DX_HASH_LAST) || + (params.def_hash_alg == DX_HASH_SIPHASH))) + return -EINVAL; + if ((params.set_flags & EXT4_TUNE_FL_FEATURES) && + (params.set_flags & EXT4_TUNE_FL_EDIT_FEATURES)) + return -EINVAL; + + if (params.set_flags & EXT4_TUNE_FL_FEATURES) { + params.set_feature_compat_mask = + params.feature_compat & + ~le32_to_cpu(es->s_feature_compat); + params.set_feature_incompat_mask = + params.feature_incompat & + ~le32_to_cpu(es->s_feature_incompat); + params.set_feature_ro_compat_mask = + params.feature_ro_compat & + ~le32_to_cpu(es->s_feature_ro_compat); + params.clear_feature_compat_mask = + ~params.feature_compat & + le32_to_cpu(es->s_feature_compat); + params.clear_feature_incompat_mask = + ~params.feature_incompat & + le32_to_cpu(es->s_feature_incompat); + params.clear_feature_ro_compat_mask = + ~params.feature_ro_compat & + le32_to_cpu(es->s_feature_ro_compat); + params.set_flags |= EXT4_TUNE_FL_EDIT_FEATURES; + } + if (params.set_flags & EXT4_TUNE_FL_EDIT_FEATURES) { + if ((params.set_feature_compat_mask & + ~EXT4_TUNE_SET_COMPAT_SUPP) || + (params.set_feature_incompat_mask & + ~EXT4_TUNE_SET_INCOMPAT_SUPP) || + (params.set_feature_ro_compat_mask & + ~EXT4_TUNE_SET_RO_COMPAT_SUPP) || + (params.clear_feature_compat_mask & + ~EXT4_TUNE_CLEAR_COMPAT_SUPP) || + (params.clear_feature_incompat_mask & + ~EXT4_TUNE_CLEAR_INCOMPAT_SUPP) || + (params.clear_feature_ro_compat_mask & + ~EXT4_TUNE_CLEAR_RO_COMPAT_SUPP)) + return -EOPNOTSUPP; + + /* + * Filter out the features that are already set from + * the set_mask. + */ + params.set_feature_compat_mask &= + ~le32_to_cpu(es->s_feature_compat); + params.set_feature_incompat_mask &= + ~le32_to_cpu(es->s_feature_incompat); + params.set_feature_ro_compat_mask &= + ~le32_to_cpu(es->s_feature_ro_compat); + if ((params.set_feature_incompat_mask & + EXT4_FEATURE_INCOMPAT_CASEFOLD)) { + enabling_casefold = 1; + if (!(params.set_flags & EXT4_TUNE_FL_ENCODING)) { + params.encoding = EXT4_ENC_UTF8_12_1; + params.set_flags |= EXT4_TUNE_FL_ENCODING; + } + if (!(params.set_flags & EXT4_TUNE_FL_ENCODING_FLAGS)) { + params.encoding_flags = 0; + params.set_flags |= EXT4_TUNE_FL_ENCODING_FLAGS; + } + } + if ((params.set_feature_compat_mask & + EXT4_FEATURE_COMPAT_DIR_INDEX)) { + uuid_t uu; + + memcpy(&uu, sbi->s_hash_seed, UUID_SIZE); + if (uuid_is_null(&uu)) + generate_random_uuid((char *) + &sbi->s_hash_seed); + if (params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) + sbi->s_def_hash_version = params.def_hash_alg; + else if (sbi->s_def_hash_version == 0) + sbi->s_def_hash_version = DX_HASH_HALF_MD4; + if (!(es->s_flags & + cpu_to_le32(EXT2_FLAGS_UNSIGNED_HASH)) && + !(es->s_flags & + cpu_to_le32(EXT2_FLAGS_SIGNED_HASH))) { +#ifdef __CHAR_UNSIGNED__ + sbi->s_hash_unsigned = 3; +#else + sbi->s_hash_unsigned = 0; +#endif + } + } + } + if (params.set_flags & EXT4_TUNE_FL_ENCODING) { + if (!enabling_casefold) + return -EINVAL; + if (params.encoding == 0) + params.encoding = EXT4_ENC_UTF8_12_1; + else if (params.encoding != EXT4_ENC_UTF8_12_1) + return -EINVAL; + } + if (params.set_flags & EXT4_TUNE_FL_ENCODING_FLAGS) { + if (!enabling_casefold) + return -EINVAL; + if (params.encoding_flags & ~SB_ENC_SUPP_MASK) + return -EINVAL; + } + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + ret = ext4_update_superblocks_fn(sb, ext4_sb_setparams, ¶ms); + mnt_drop_write_file(filp); + + if (params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) + sbi->s_def_hash_version = params.def_hash_alg; + + return ret; +} + static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); @@ -1616,6 +1908,11 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return ext4_ioctl_getuuid(EXT4_SB(sb), (void __user *)arg); case EXT4_IOC_SETFSUUID: return ext4_ioctl_setuuid(filp, (const void __user *)arg); + case EXT4_IOC_GET_TUNE_SB_PARAM: + return ext4_ioctl_get_tune_sb(EXT4_SB(sb), + (void __user *)arg); + case EXT4_IOC_SET_TUNE_SB_PARAM: + return ext4_ioctl_set_tune_sb(filp, (void __user *)arg); default: return -ENOTTY; } @@ -1703,7 +2000,8 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } #endif -static void set_overhead(struct ext4_super_block *es, const void *arg) +static void set_overhead(struct ext4_sb_info *sbi, + struct ext4_super_block *es, const void *arg) { es->s_overhead_clusters = cpu_to_le32(*((unsigned long *) arg)); } diff --git a/include/uapi/linux/ext4.h b/include/uapi/linux/ext4.h index 1c4c2dd29112cda9f7dc91d917492cffc33ee524..411dcc1e4a35c8c6a10f3768d17b8cc50cff4c34 100644 --- a/include/uapi/linux/ext4.h +++ b/include/uapi/linux/ext4.h @@ -33,6 +33,8 @@ #define EXT4_IOC_CHECKPOINT _IOW('f', 43, __u32) #define EXT4_IOC_GETFSUUID _IOR('f', 44, struct fsuuid) #define EXT4_IOC_SETFSUUID _IOW('f', 44, struct fsuuid) +#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) #define EXT4_IOC_SHUTDOWN _IOR('X', 125, __u32) @@ -108,6 +110,57 @@ struct ext4_new_group_input { __u16 unused; }; +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 + /* * Returned by EXT4_IOC_GET_ES_CACHE as an additional possible flag. * It indicates that the entry in extent status cache is for a hole. -- 2.51.0 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH v2 3/3] ext4: implemet new ioctls to set and get superblock parameters 2025-09-17 3:22 ` [PATCH v2 3/3] ext4: implemet new ioctls to set and get superblock parameters Theodore Ts'o via B4 Relay @ 2025-09-17 16:22 ` Jan Kara 2025-09-18 16:57 ` Darrick J. Wong 1 sibling, 0 replies; 10+ messages in thread From: Jan Kara @ 2025-09-17 16:22 UTC (permalink / raw) To: tytso; +Cc: linux-ext4, linux-api On Tue 16-09-25 23:22:49, Theodore Ts'o via B4 Relay wrote: > From: Theodore Ts'o <tytso@mit.edu> > > Implement the EXT4_IOC_GET_TUNE_SB_PARAM and > EXT4_IOC_SET_TUNE_SB_PARAM ioctls, which allow certains superblock > parameters to be set while the file system is mounted, without needing > write access to the block device. > > Signed-off-by: Theodore Ts'o <tytso@mit.edu> Looks good. Feel free to add: Reviewed-by: Jan Kara <jack@suse.cz> Honza > --- > fs/ext4/ioctl.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- > include/uapi/linux/ext4.h | 53 +++++++++++++ > 2 files changed, 358 insertions(+), 7 deletions(-) > > diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c > index 84e3c73952d72e436429489f5fc8b7ae1c01c7a1..a93a7baae990cc5580d2ddb3ffcc72fe15246978 100644 > --- a/fs/ext4/ioctl.c > +++ b/fs/ext4/ioctl.c > @@ -27,14 +27,16 @@ > #include "fsmap.h" > #include <trace/events/ext4.h> > > -typedef void ext4_update_sb_callback(struct ext4_super_block *es, > - const void *arg); > +typedef void ext4_update_sb_callback(struct ext4_sb_info *sbi, > + struct ext4_super_block *es, > + const void *arg); > > /* > * Superblock modification callback function for changing file system > * label > */ > -static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg) > +static void ext4_sb_setlabel(struct ext4_sb_info *sbi, > + struct ext4_super_block *es, const void *arg) > { > /* Sanity check, this should never happen */ > BUILD_BUG_ON(sizeof(es->s_volume_name) < EXT4_LABEL_MAX); > @@ -46,7 +48,8 @@ static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg) > * Superblock modification callback function for changing file system > * UUID. > */ > -static void ext4_sb_setuuid(struct ext4_super_block *es, const void *arg) > +static void ext4_sb_setuuid(struct ext4_sb_info *sbi, > + struct ext4_super_block *es, const void *arg) > { > memcpy(es->s_uuid, (__u8 *)arg, UUID_SIZE); > } > @@ -71,7 +74,7 @@ int ext4_update_primary_sb(struct super_block *sb, handle_t *handle, > goto out_err; > > lock_buffer(bh); > - func(es, arg); > + func(sbi, es, arg); > ext4_superblock_csum_set(sb); > unlock_buffer(bh); > > @@ -149,7 +152,7 @@ static int ext4_update_backup_sb(struct super_block *sb, > unlock_buffer(bh); > goto out_bh; > } > - func(es, arg); > + func(EXT4_SB(sb), es, arg); > if (ext4_has_feature_metadata_csum(sb)) > es->s_checksum = ext4_superblock_csum(es); > set_buffer_uptodate(bh); > @@ -1230,6 +1233,295 @@ static int ext4_ioctl_setuuid(struct file *filp, > return ret; > } > > + > +#define TUNE_OPS_SUPPORTED (EXT4_TUNE_FL_ERRORS_BEHAVIOR | \ > + EXT4_TUNE_FL_MNT_COUNT | EXT4_TUNE_FL_MAX_MNT_COUNT | \ > + EXT4_TUNE_FL_CHECKINTRVAL | EXT4_TUNE_FL_LAST_CHECK_TIME | \ > + EXT4_TUNE_FL_RESERVED_BLOCKS | EXT4_TUNE_FL_RESERVED_UID | \ > + EXT4_TUNE_FL_RESERVED_GID | EXT4_TUNE_FL_DEFAULT_MNT_OPTS | \ > + EXT4_TUNE_FL_DEF_HASH_ALG | EXT4_TUNE_FL_RAID_STRIDE | \ > + EXT4_TUNE_FL_RAID_STRIPE_WIDTH | EXT4_TUNE_FL_MOUNT_OPTS | \ > + EXT4_TUNE_FL_FEATURES | EXT4_TUNE_FL_EDIT_FEATURES | \ > + EXT4_TUNE_FL_FORCE_FSCK | EXT4_TUNE_FL_ENCODING | \ > + EXT4_TUNE_FL_ENCODING_FLAGS) > + > +#define EXT4_TUNE_SET_COMPAT_SUPP \ > + (EXT4_FEATURE_COMPAT_DIR_INDEX | \ > + EXT4_FEATURE_COMPAT_STABLE_INODES) > +#define EXT4_TUNE_SET_INCOMPAT_SUPP \ > + (EXT4_FEATURE_INCOMPAT_EXTENTS | \ > + EXT4_FEATURE_INCOMPAT_EA_INODE | \ > + EXT4_FEATURE_INCOMPAT_ENCRYPT | \ > + EXT4_FEATURE_INCOMPAT_CSUM_SEED | \ > + EXT4_FEATURE_INCOMPAT_LARGEDIR | \ > + EXT4_FEATURE_INCOMPAT_CASEFOLD) > +#define EXT4_TUNE_SET_RO_COMPAT_SUPP \ > + (EXT4_FEATURE_RO_COMPAT_LARGE_FILE | \ > + EXT4_FEATURE_RO_COMPAT_DIR_NLINK | \ > + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \ > + EXT4_FEATURE_RO_COMPAT_PROJECT | \ > + EXT4_FEATURE_RO_COMPAT_VERITY) > + > +#define EXT4_TUNE_CLEAR_COMPAT_SUPP (0) > +#define EXT4_TUNE_CLEAR_INCOMPAT_SUPP (0) > +#define EXT4_TUNE_CLEAR_RO_COMPAT_SUPP (0) > + > +#define SB_ENC_SUPP_MASK (SB_ENC_STRICT_MODE_FL | \ > + SB_ENC_NO_COMPAT_FALLBACK_FL) > + > +static int ext4_ioctl_get_tune_sb(struct ext4_sb_info *sbi, > + struct ext4_tune_sb_params __user *params) > +{ > + struct ext4_tune_sb_params ret; > + struct ext4_super_block *es = sbi->s_es; > + > + memset(&ret, 0, sizeof(ret)); > + ret.set_flags = TUNE_OPS_SUPPORTED; > + ret.errors_behavior = le16_to_cpu(es->s_errors); > + ret.mnt_count = le16_to_cpu(es->s_mnt_count); > + ret.max_mnt_count = le16_to_cpu(es->s_max_mnt_count); > + ret.checkinterval = le32_to_cpu(es->s_checkinterval); > + ret.last_check_time = le32_to_cpu(es->s_lastcheck); > + ret.reserved_blocks = ext4_r_blocks_count(es); > + ret.blocks_count = ext4_blocks_count(es); > + ret.reserved_uid = ext4_get_resuid(es); > + ret.reserved_gid = ext4_get_resgid(es); > + ret.default_mnt_opts = le32_to_cpu(es->s_default_mount_opts); > + ret.def_hash_alg = es->s_def_hash_version; > + ret.raid_stride = le16_to_cpu(es->s_raid_stride); > + ret.raid_stripe_width = le32_to_cpu(es->s_raid_stripe_width); > + ret.encoding = le16_to_cpu(es->s_encoding); > + ret.encoding_flags = le16_to_cpu(es->s_encoding_flags); > + strscpy_pad(ret.mount_opts, es->s_mount_opts); > + ret.feature_compat = le32_to_cpu(es->s_feature_compat); > + ret.feature_incompat = le32_to_cpu(es->s_feature_incompat); > + ret.feature_ro_compat = le32_to_cpu(es->s_feature_ro_compat); > + ret.set_feature_compat_mask = EXT4_TUNE_SET_COMPAT_SUPP; > + ret.set_feature_incompat_mask = EXT4_TUNE_SET_INCOMPAT_SUPP; > + ret.set_feature_ro_compat_mask = EXT4_TUNE_SET_RO_COMPAT_SUPP; > + ret.clear_feature_compat_mask = EXT4_TUNE_CLEAR_COMPAT_SUPP; > + ret.clear_feature_incompat_mask = EXT4_TUNE_CLEAR_INCOMPAT_SUPP; > + ret.clear_feature_ro_compat_mask = EXT4_TUNE_CLEAR_RO_COMPAT_SUPP; > + if (copy_to_user(params, &ret, sizeof(ret))) > + return -EFAULT; > + return 0; > +} > + > +static void ext4_sb_setparams(struct ext4_sb_info *sbi, > + struct ext4_super_block *es, const void *arg) > +{ > + const struct ext4_tune_sb_params *params = arg; > + > + if (params->set_flags & EXT4_TUNE_FL_ERRORS_BEHAVIOR) > + es->s_errors = cpu_to_le16(params->errors_behavior); > + if (params->set_flags & EXT4_TUNE_FL_MNT_COUNT) > + es->s_mnt_count = cpu_to_le16(params->mnt_count); > + if (params->set_flags & EXT4_TUNE_FL_MAX_MNT_COUNT) > + es->s_max_mnt_count = cpu_to_le16(params->max_mnt_count); > + if (params->set_flags & EXT4_TUNE_FL_CHECKINTRVAL) > + es->s_checkinterval = cpu_to_le32(params->checkinterval); > + if (params->set_flags & EXT4_TUNE_FL_LAST_CHECK_TIME) > + es->s_lastcheck = cpu_to_le32(params->last_check_time); > + if (params->set_flags & EXT4_TUNE_FL_RESERVED_BLOCKS) { > + ext4_fsblk_t blk = params->reserved_blocks; > + > + es->s_r_blocks_count_lo = cpu_to_le32((u32)blk); > + es->s_r_blocks_count_hi = cpu_to_le32(blk >> 32); > + } > + if (params->set_flags & EXT4_TUNE_FL_RESERVED_UID) { > + int uid = params->reserved_uid; > + > + es->s_def_resuid = cpu_to_le16(uid & 0xFFFF); > + es->s_def_resuid_hi = cpu_to_le16(uid >> 16); > + } > + if (params->set_flags & EXT4_TUNE_FL_RESERVED_GID) { > + int gid = params->reserved_gid; > + > + es->s_def_resgid = cpu_to_le16(gid & 0xFFFF); > + es->s_def_resgid_hi = cpu_to_le16(gid >> 16); > + } > + if (params->set_flags & EXT4_TUNE_FL_DEFAULT_MNT_OPTS) > + es->s_default_mount_opts = cpu_to_le32(params->default_mnt_opts); > + if (params->set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) > + es->s_def_hash_version = params->def_hash_alg; > + if (params->set_flags & EXT4_TUNE_FL_RAID_STRIDE) > + es->s_raid_stride = cpu_to_le16(params->raid_stride); > + if (params->set_flags & EXT4_TUNE_FL_RAID_STRIPE_WIDTH) > + es->s_raid_stripe_width = > + cpu_to_le32(params->raid_stripe_width); > + if (params->set_flags & EXT4_TUNE_FL_ENCODING) > + es->s_encoding = cpu_to_le16(params->encoding); > + if (params->set_flags & EXT4_TUNE_FL_ENCODING_FLAGS) > + es->s_encoding_flags = cpu_to_le16(params->encoding_flags); > + strscpy_pad(es->s_mount_opts, params->mount_opts); > + if (params->set_flags & EXT4_TUNE_FL_EDIT_FEATURES) { > + es->s_feature_compat |= > + cpu_to_le32(params->set_feature_compat_mask); > + es->s_feature_incompat |= > + cpu_to_le32(params->set_feature_incompat_mask); > + es->s_feature_ro_compat |= > + cpu_to_le32(params->set_feature_ro_compat_mask); > + es->s_feature_compat &= > + ~cpu_to_le32(params->clear_feature_compat_mask); > + es->s_feature_incompat &= > + ~cpu_to_le32(params->clear_feature_incompat_mask); > + es->s_feature_ro_compat &= > + ~cpu_to_le32(params->clear_feature_ro_compat_mask); > + if (params->set_feature_compat_mask & > + EXT4_FEATURE_COMPAT_DIR_INDEX) > + es->s_def_hash_version = sbi->s_def_hash_version; > + if (params->set_feature_incompat_mask & > + EXT4_FEATURE_INCOMPAT_CSUM_SEED) > + es->s_checksum_seed = cpu_to_le32(sbi->s_csum_seed); > + } > + if (params->set_flags & EXT4_TUNE_FL_FORCE_FSCK) > + es->s_state |= cpu_to_le16(EXT4_ERROR_FS); > +} > + > +static int ext4_ioctl_set_tune_sb(struct file *filp, > + struct ext4_tune_sb_params __user *in) > +{ > + struct ext4_tune_sb_params params; > + struct super_block *sb = file_inode(filp)->i_sb; > + struct ext4_sb_info *sbi = EXT4_SB(sb); > + struct ext4_super_block *es = sbi->s_es; > + int enabling_casefold = 0; > + int ret; > + > + if (!capable(CAP_SYS_ADMIN)) > + return -EPERM; > + > + if (copy_from_user(¶ms, in, sizeof(params))) > + return -EFAULT; > + > + if ((params.set_flags & ~TUNE_OPS_SUPPORTED) != 0) > + return -EOPNOTSUPP; > + > + if ((params.set_flags & EXT4_TUNE_FL_ERRORS_BEHAVIOR) && > + (params.errors_behavior > EXT4_ERRORS_PANIC)) > + return -EINVAL; > + > + if ((params.set_flags & EXT4_TUNE_FL_RESERVED_BLOCKS) && > + (params.reserved_blocks > ext4_blocks_count(sbi->s_es) / 2)) > + return -EINVAL; > + if ((params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) && > + ((params.def_hash_alg > DX_HASH_LAST) || > + (params.def_hash_alg == DX_HASH_SIPHASH))) > + return -EINVAL; > + if ((params.set_flags & EXT4_TUNE_FL_FEATURES) && > + (params.set_flags & EXT4_TUNE_FL_EDIT_FEATURES)) > + return -EINVAL; > + > + if (params.set_flags & EXT4_TUNE_FL_FEATURES) { > + params.set_feature_compat_mask = > + params.feature_compat & > + ~le32_to_cpu(es->s_feature_compat); > + params.set_feature_incompat_mask = > + params.feature_incompat & > + ~le32_to_cpu(es->s_feature_incompat); > + params.set_feature_ro_compat_mask = > + params.feature_ro_compat & > + ~le32_to_cpu(es->s_feature_ro_compat); > + params.clear_feature_compat_mask = > + ~params.feature_compat & > + le32_to_cpu(es->s_feature_compat); > + params.clear_feature_incompat_mask = > + ~params.feature_incompat & > + le32_to_cpu(es->s_feature_incompat); > + params.clear_feature_ro_compat_mask = > + ~params.feature_ro_compat & > + le32_to_cpu(es->s_feature_ro_compat); > + params.set_flags |= EXT4_TUNE_FL_EDIT_FEATURES; > + } > + if (params.set_flags & EXT4_TUNE_FL_EDIT_FEATURES) { > + if ((params.set_feature_compat_mask & > + ~EXT4_TUNE_SET_COMPAT_SUPP) || > + (params.set_feature_incompat_mask & > + ~EXT4_TUNE_SET_INCOMPAT_SUPP) || > + (params.set_feature_ro_compat_mask & > + ~EXT4_TUNE_SET_RO_COMPAT_SUPP) || > + (params.clear_feature_compat_mask & > + ~EXT4_TUNE_CLEAR_COMPAT_SUPP) || > + (params.clear_feature_incompat_mask & > + ~EXT4_TUNE_CLEAR_INCOMPAT_SUPP) || > + (params.clear_feature_ro_compat_mask & > + ~EXT4_TUNE_CLEAR_RO_COMPAT_SUPP)) > + return -EOPNOTSUPP; > + > + /* > + * Filter out the features that are already set from > + * the set_mask. > + */ > + params.set_feature_compat_mask &= > + ~le32_to_cpu(es->s_feature_compat); > + params.set_feature_incompat_mask &= > + ~le32_to_cpu(es->s_feature_incompat); > + params.set_feature_ro_compat_mask &= > + ~le32_to_cpu(es->s_feature_ro_compat); > + if ((params.set_feature_incompat_mask & > + EXT4_FEATURE_INCOMPAT_CASEFOLD)) { > + enabling_casefold = 1; > + if (!(params.set_flags & EXT4_TUNE_FL_ENCODING)) { > + params.encoding = EXT4_ENC_UTF8_12_1; > + params.set_flags |= EXT4_TUNE_FL_ENCODING; > + } > + if (!(params.set_flags & EXT4_TUNE_FL_ENCODING_FLAGS)) { > + params.encoding_flags = 0; > + params.set_flags |= EXT4_TUNE_FL_ENCODING_FLAGS; > + } > + } > + if ((params.set_feature_compat_mask & > + EXT4_FEATURE_COMPAT_DIR_INDEX)) { > + uuid_t uu; > + > + memcpy(&uu, sbi->s_hash_seed, UUID_SIZE); > + if (uuid_is_null(&uu)) > + generate_random_uuid((char *) > + &sbi->s_hash_seed); > + if (params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) > + sbi->s_def_hash_version = params.def_hash_alg; > + else if (sbi->s_def_hash_version == 0) > + sbi->s_def_hash_version = DX_HASH_HALF_MD4; > + if (!(es->s_flags & > + cpu_to_le32(EXT2_FLAGS_UNSIGNED_HASH)) && > + !(es->s_flags & > + cpu_to_le32(EXT2_FLAGS_SIGNED_HASH))) { > +#ifdef __CHAR_UNSIGNED__ > + sbi->s_hash_unsigned = 3; > +#else > + sbi->s_hash_unsigned = 0; > +#endif > + } > + } > + } > + if (params.set_flags & EXT4_TUNE_FL_ENCODING) { > + if (!enabling_casefold) > + return -EINVAL; > + if (params.encoding == 0) > + params.encoding = EXT4_ENC_UTF8_12_1; > + else if (params.encoding != EXT4_ENC_UTF8_12_1) > + return -EINVAL; > + } > + if (params.set_flags & EXT4_TUNE_FL_ENCODING_FLAGS) { > + if (!enabling_casefold) > + return -EINVAL; > + if (params.encoding_flags & ~SB_ENC_SUPP_MASK) > + return -EINVAL; > + } > + > + ret = mnt_want_write_file(filp); > + if (ret) > + return ret; > + > + ret = ext4_update_superblocks_fn(sb, ext4_sb_setparams, ¶ms); > + mnt_drop_write_file(filp); > + > + if (params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) > + sbi->s_def_hash_version = params.def_hash_alg; > + > + return ret; > +} > + > static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) > { > struct inode *inode = file_inode(filp); > @@ -1616,6 +1908,11 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) > return ext4_ioctl_getuuid(EXT4_SB(sb), (void __user *)arg); > case EXT4_IOC_SETFSUUID: > return ext4_ioctl_setuuid(filp, (const void __user *)arg); > + case EXT4_IOC_GET_TUNE_SB_PARAM: > + return ext4_ioctl_get_tune_sb(EXT4_SB(sb), > + (void __user *)arg); > + case EXT4_IOC_SET_TUNE_SB_PARAM: > + return ext4_ioctl_set_tune_sb(filp, (void __user *)arg); > default: > return -ENOTTY; > } > @@ -1703,7 +2000,8 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) > } > #endif > > -static void set_overhead(struct ext4_super_block *es, const void *arg) > +static void set_overhead(struct ext4_sb_info *sbi, > + struct ext4_super_block *es, const void *arg) > { > es->s_overhead_clusters = cpu_to_le32(*((unsigned long *) arg)); > } > diff --git a/include/uapi/linux/ext4.h b/include/uapi/linux/ext4.h > index 1c4c2dd29112cda9f7dc91d917492cffc33ee524..411dcc1e4a35c8c6a10f3768d17b8cc50cff4c34 100644 > --- a/include/uapi/linux/ext4.h > +++ b/include/uapi/linux/ext4.h > @@ -33,6 +33,8 @@ > #define EXT4_IOC_CHECKPOINT _IOW('f', 43, __u32) > #define EXT4_IOC_GETFSUUID _IOR('f', 44, struct fsuuid) > #define EXT4_IOC_SETFSUUID _IOW('f', 44, struct fsuuid) > +#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) > > #define EXT4_IOC_SHUTDOWN _IOR('X', 125, __u32) > > @@ -108,6 +110,57 @@ struct ext4_new_group_input { > __u16 unused; > }; > > +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 > + > /* > * Returned by EXT4_IOC_GET_ES_CACHE as an additional possible flag. > * It indicates that the entry in extent status cache is for a hole. > > -- > 2.51.0 > > > -- Jan Kara <jack@suse.com> SUSE Labs, CR ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 3/3] ext4: implemet new ioctls to set and get superblock parameters 2025-09-17 3:22 ` [PATCH v2 3/3] ext4: implemet new ioctls to set and get superblock parameters Theodore Ts'o via B4 Relay 2025-09-17 16:22 ` Jan Kara @ 2025-09-18 16:57 ` Darrick J. Wong 1 sibling, 0 replies; 10+ messages in thread From: Darrick J. Wong @ 2025-09-18 16:57 UTC (permalink / raw) To: tytso; +Cc: linux-ext4, linux-api On Tue, Sep 16, 2025 at 11:22:49PM -0400, Theodore Ts'o via B4 Relay wrote: > From: Theodore Ts'o <tytso@mit.edu> > > Implement the EXT4_IOC_GET_TUNE_SB_PARAM and > EXT4_IOC_SET_TUNE_SB_PARAM ioctls, which allow certains superblock > parameters to be set while the file system is mounted, without needing > write access to the block device. > > Signed-off-by: Theodore Ts'o <tytso@mit.edu> Looks good to me now, Reviewed-by: "Darrick J. Wong" <djwong@kernel.org> --D > --- > fs/ext4/ioctl.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- > include/uapi/linux/ext4.h | 53 +++++++++++++ > 2 files changed, 358 insertions(+), 7 deletions(-) > > diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c > index 84e3c73952d72e436429489f5fc8b7ae1c01c7a1..a93a7baae990cc5580d2ddb3ffcc72fe15246978 100644 > --- a/fs/ext4/ioctl.c > +++ b/fs/ext4/ioctl.c > @@ -27,14 +27,16 @@ > #include "fsmap.h" > #include <trace/events/ext4.h> > > -typedef void ext4_update_sb_callback(struct ext4_super_block *es, > - const void *arg); > +typedef void ext4_update_sb_callback(struct ext4_sb_info *sbi, > + struct ext4_super_block *es, > + const void *arg); > > /* > * Superblock modification callback function for changing file system > * label > */ > -static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg) > +static void ext4_sb_setlabel(struct ext4_sb_info *sbi, > + struct ext4_super_block *es, const void *arg) > { > /* Sanity check, this should never happen */ > BUILD_BUG_ON(sizeof(es->s_volume_name) < EXT4_LABEL_MAX); > @@ -46,7 +48,8 @@ static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg) > * Superblock modification callback function for changing file system > * UUID. > */ > -static void ext4_sb_setuuid(struct ext4_super_block *es, const void *arg) > +static void ext4_sb_setuuid(struct ext4_sb_info *sbi, > + struct ext4_super_block *es, const void *arg) > { > memcpy(es->s_uuid, (__u8 *)arg, UUID_SIZE); > } > @@ -71,7 +74,7 @@ int ext4_update_primary_sb(struct super_block *sb, handle_t *handle, > goto out_err; > > lock_buffer(bh); > - func(es, arg); > + func(sbi, es, arg); > ext4_superblock_csum_set(sb); > unlock_buffer(bh); > > @@ -149,7 +152,7 @@ static int ext4_update_backup_sb(struct super_block *sb, > unlock_buffer(bh); > goto out_bh; > } > - func(es, arg); > + func(EXT4_SB(sb), es, arg); > if (ext4_has_feature_metadata_csum(sb)) > es->s_checksum = ext4_superblock_csum(es); > set_buffer_uptodate(bh); > @@ -1230,6 +1233,295 @@ static int ext4_ioctl_setuuid(struct file *filp, > return ret; > } > > + > +#define TUNE_OPS_SUPPORTED (EXT4_TUNE_FL_ERRORS_BEHAVIOR | \ > + EXT4_TUNE_FL_MNT_COUNT | EXT4_TUNE_FL_MAX_MNT_COUNT | \ > + EXT4_TUNE_FL_CHECKINTRVAL | EXT4_TUNE_FL_LAST_CHECK_TIME | \ > + EXT4_TUNE_FL_RESERVED_BLOCKS | EXT4_TUNE_FL_RESERVED_UID | \ > + EXT4_TUNE_FL_RESERVED_GID | EXT4_TUNE_FL_DEFAULT_MNT_OPTS | \ > + EXT4_TUNE_FL_DEF_HASH_ALG | EXT4_TUNE_FL_RAID_STRIDE | \ > + EXT4_TUNE_FL_RAID_STRIPE_WIDTH | EXT4_TUNE_FL_MOUNT_OPTS | \ > + EXT4_TUNE_FL_FEATURES | EXT4_TUNE_FL_EDIT_FEATURES | \ > + EXT4_TUNE_FL_FORCE_FSCK | EXT4_TUNE_FL_ENCODING | \ > + EXT4_TUNE_FL_ENCODING_FLAGS) > + > +#define EXT4_TUNE_SET_COMPAT_SUPP \ > + (EXT4_FEATURE_COMPAT_DIR_INDEX | \ > + EXT4_FEATURE_COMPAT_STABLE_INODES) > +#define EXT4_TUNE_SET_INCOMPAT_SUPP \ > + (EXT4_FEATURE_INCOMPAT_EXTENTS | \ > + EXT4_FEATURE_INCOMPAT_EA_INODE | \ > + EXT4_FEATURE_INCOMPAT_ENCRYPT | \ > + EXT4_FEATURE_INCOMPAT_CSUM_SEED | \ > + EXT4_FEATURE_INCOMPAT_LARGEDIR | \ > + EXT4_FEATURE_INCOMPAT_CASEFOLD) > +#define EXT4_TUNE_SET_RO_COMPAT_SUPP \ > + (EXT4_FEATURE_RO_COMPAT_LARGE_FILE | \ > + EXT4_FEATURE_RO_COMPAT_DIR_NLINK | \ > + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \ > + EXT4_FEATURE_RO_COMPAT_PROJECT | \ > + EXT4_FEATURE_RO_COMPAT_VERITY) > + > +#define EXT4_TUNE_CLEAR_COMPAT_SUPP (0) > +#define EXT4_TUNE_CLEAR_INCOMPAT_SUPP (0) > +#define EXT4_TUNE_CLEAR_RO_COMPAT_SUPP (0) > + > +#define SB_ENC_SUPP_MASK (SB_ENC_STRICT_MODE_FL | \ > + SB_ENC_NO_COMPAT_FALLBACK_FL) > + > +static int ext4_ioctl_get_tune_sb(struct ext4_sb_info *sbi, > + struct ext4_tune_sb_params __user *params) > +{ > + struct ext4_tune_sb_params ret; > + struct ext4_super_block *es = sbi->s_es; > + > + memset(&ret, 0, sizeof(ret)); > + ret.set_flags = TUNE_OPS_SUPPORTED; > + ret.errors_behavior = le16_to_cpu(es->s_errors); > + ret.mnt_count = le16_to_cpu(es->s_mnt_count); > + ret.max_mnt_count = le16_to_cpu(es->s_max_mnt_count); > + ret.checkinterval = le32_to_cpu(es->s_checkinterval); > + ret.last_check_time = le32_to_cpu(es->s_lastcheck); > + ret.reserved_blocks = ext4_r_blocks_count(es); > + ret.blocks_count = ext4_blocks_count(es); > + ret.reserved_uid = ext4_get_resuid(es); > + ret.reserved_gid = ext4_get_resgid(es); > + ret.default_mnt_opts = le32_to_cpu(es->s_default_mount_opts); > + ret.def_hash_alg = es->s_def_hash_version; > + ret.raid_stride = le16_to_cpu(es->s_raid_stride); > + ret.raid_stripe_width = le32_to_cpu(es->s_raid_stripe_width); > + ret.encoding = le16_to_cpu(es->s_encoding); > + ret.encoding_flags = le16_to_cpu(es->s_encoding_flags); > + strscpy_pad(ret.mount_opts, es->s_mount_opts); > + ret.feature_compat = le32_to_cpu(es->s_feature_compat); > + ret.feature_incompat = le32_to_cpu(es->s_feature_incompat); > + ret.feature_ro_compat = le32_to_cpu(es->s_feature_ro_compat); > + ret.set_feature_compat_mask = EXT4_TUNE_SET_COMPAT_SUPP; > + ret.set_feature_incompat_mask = EXT4_TUNE_SET_INCOMPAT_SUPP; > + ret.set_feature_ro_compat_mask = EXT4_TUNE_SET_RO_COMPAT_SUPP; > + ret.clear_feature_compat_mask = EXT4_TUNE_CLEAR_COMPAT_SUPP; > + ret.clear_feature_incompat_mask = EXT4_TUNE_CLEAR_INCOMPAT_SUPP; > + ret.clear_feature_ro_compat_mask = EXT4_TUNE_CLEAR_RO_COMPAT_SUPP; > + if (copy_to_user(params, &ret, sizeof(ret))) > + return -EFAULT; > + return 0; > +} > + > +static void ext4_sb_setparams(struct ext4_sb_info *sbi, > + struct ext4_super_block *es, const void *arg) > +{ > + const struct ext4_tune_sb_params *params = arg; > + > + if (params->set_flags & EXT4_TUNE_FL_ERRORS_BEHAVIOR) > + es->s_errors = cpu_to_le16(params->errors_behavior); > + if (params->set_flags & EXT4_TUNE_FL_MNT_COUNT) > + es->s_mnt_count = cpu_to_le16(params->mnt_count); > + if (params->set_flags & EXT4_TUNE_FL_MAX_MNT_COUNT) > + es->s_max_mnt_count = cpu_to_le16(params->max_mnt_count); > + if (params->set_flags & EXT4_TUNE_FL_CHECKINTRVAL) > + es->s_checkinterval = cpu_to_le32(params->checkinterval); > + if (params->set_flags & EXT4_TUNE_FL_LAST_CHECK_TIME) > + es->s_lastcheck = cpu_to_le32(params->last_check_time); > + if (params->set_flags & EXT4_TUNE_FL_RESERVED_BLOCKS) { > + ext4_fsblk_t blk = params->reserved_blocks; > + > + es->s_r_blocks_count_lo = cpu_to_le32((u32)blk); > + es->s_r_blocks_count_hi = cpu_to_le32(blk >> 32); > + } > + if (params->set_flags & EXT4_TUNE_FL_RESERVED_UID) { > + int uid = params->reserved_uid; > + > + es->s_def_resuid = cpu_to_le16(uid & 0xFFFF); > + es->s_def_resuid_hi = cpu_to_le16(uid >> 16); > + } > + if (params->set_flags & EXT4_TUNE_FL_RESERVED_GID) { > + int gid = params->reserved_gid; > + > + es->s_def_resgid = cpu_to_le16(gid & 0xFFFF); > + es->s_def_resgid_hi = cpu_to_le16(gid >> 16); > + } > + if (params->set_flags & EXT4_TUNE_FL_DEFAULT_MNT_OPTS) > + es->s_default_mount_opts = cpu_to_le32(params->default_mnt_opts); > + if (params->set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) > + es->s_def_hash_version = params->def_hash_alg; > + if (params->set_flags & EXT4_TUNE_FL_RAID_STRIDE) > + es->s_raid_stride = cpu_to_le16(params->raid_stride); > + if (params->set_flags & EXT4_TUNE_FL_RAID_STRIPE_WIDTH) > + es->s_raid_stripe_width = > + cpu_to_le32(params->raid_stripe_width); > + if (params->set_flags & EXT4_TUNE_FL_ENCODING) > + es->s_encoding = cpu_to_le16(params->encoding); > + if (params->set_flags & EXT4_TUNE_FL_ENCODING_FLAGS) > + es->s_encoding_flags = cpu_to_le16(params->encoding_flags); > + strscpy_pad(es->s_mount_opts, params->mount_opts); > + if (params->set_flags & EXT4_TUNE_FL_EDIT_FEATURES) { > + es->s_feature_compat |= > + cpu_to_le32(params->set_feature_compat_mask); > + es->s_feature_incompat |= > + cpu_to_le32(params->set_feature_incompat_mask); > + es->s_feature_ro_compat |= > + cpu_to_le32(params->set_feature_ro_compat_mask); > + es->s_feature_compat &= > + ~cpu_to_le32(params->clear_feature_compat_mask); > + es->s_feature_incompat &= > + ~cpu_to_le32(params->clear_feature_incompat_mask); > + es->s_feature_ro_compat &= > + ~cpu_to_le32(params->clear_feature_ro_compat_mask); > + if (params->set_feature_compat_mask & > + EXT4_FEATURE_COMPAT_DIR_INDEX) > + es->s_def_hash_version = sbi->s_def_hash_version; > + if (params->set_feature_incompat_mask & > + EXT4_FEATURE_INCOMPAT_CSUM_SEED) > + es->s_checksum_seed = cpu_to_le32(sbi->s_csum_seed); > + } > + if (params->set_flags & EXT4_TUNE_FL_FORCE_FSCK) > + es->s_state |= cpu_to_le16(EXT4_ERROR_FS); > +} > + > +static int ext4_ioctl_set_tune_sb(struct file *filp, > + struct ext4_tune_sb_params __user *in) > +{ > + struct ext4_tune_sb_params params; > + struct super_block *sb = file_inode(filp)->i_sb; > + struct ext4_sb_info *sbi = EXT4_SB(sb); > + struct ext4_super_block *es = sbi->s_es; > + int enabling_casefold = 0; > + int ret; > + > + if (!capable(CAP_SYS_ADMIN)) > + return -EPERM; > + > + if (copy_from_user(¶ms, in, sizeof(params))) > + return -EFAULT; > + > + if ((params.set_flags & ~TUNE_OPS_SUPPORTED) != 0) > + return -EOPNOTSUPP; > + > + if ((params.set_flags & EXT4_TUNE_FL_ERRORS_BEHAVIOR) && > + (params.errors_behavior > EXT4_ERRORS_PANIC)) > + return -EINVAL; > + > + if ((params.set_flags & EXT4_TUNE_FL_RESERVED_BLOCKS) && > + (params.reserved_blocks > ext4_blocks_count(sbi->s_es) / 2)) > + return -EINVAL; > + if ((params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) && > + ((params.def_hash_alg > DX_HASH_LAST) || > + (params.def_hash_alg == DX_HASH_SIPHASH))) > + return -EINVAL; > + if ((params.set_flags & EXT4_TUNE_FL_FEATURES) && > + (params.set_flags & EXT4_TUNE_FL_EDIT_FEATURES)) > + return -EINVAL; > + > + if (params.set_flags & EXT4_TUNE_FL_FEATURES) { > + params.set_feature_compat_mask = > + params.feature_compat & > + ~le32_to_cpu(es->s_feature_compat); > + params.set_feature_incompat_mask = > + params.feature_incompat & > + ~le32_to_cpu(es->s_feature_incompat); > + params.set_feature_ro_compat_mask = > + params.feature_ro_compat & > + ~le32_to_cpu(es->s_feature_ro_compat); > + params.clear_feature_compat_mask = > + ~params.feature_compat & > + le32_to_cpu(es->s_feature_compat); > + params.clear_feature_incompat_mask = > + ~params.feature_incompat & > + le32_to_cpu(es->s_feature_incompat); > + params.clear_feature_ro_compat_mask = > + ~params.feature_ro_compat & > + le32_to_cpu(es->s_feature_ro_compat); > + params.set_flags |= EXT4_TUNE_FL_EDIT_FEATURES; > + } > + if (params.set_flags & EXT4_TUNE_FL_EDIT_FEATURES) { > + if ((params.set_feature_compat_mask & > + ~EXT4_TUNE_SET_COMPAT_SUPP) || > + (params.set_feature_incompat_mask & > + ~EXT4_TUNE_SET_INCOMPAT_SUPP) || > + (params.set_feature_ro_compat_mask & > + ~EXT4_TUNE_SET_RO_COMPAT_SUPP) || > + (params.clear_feature_compat_mask & > + ~EXT4_TUNE_CLEAR_COMPAT_SUPP) || > + (params.clear_feature_incompat_mask & > + ~EXT4_TUNE_CLEAR_INCOMPAT_SUPP) || > + (params.clear_feature_ro_compat_mask & > + ~EXT4_TUNE_CLEAR_RO_COMPAT_SUPP)) > + return -EOPNOTSUPP; > + > + /* > + * Filter out the features that are already set from > + * the set_mask. > + */ > + params.set_feature_compat_mask &= > + ~le32_to_cpu(es->s_feature_compat); > + params.set_feature_incompat_mask &= > + ~le32_to_cpu(es->s_feature_incompat); > + params.set_feature_ro_compat_mask &= > + ~le32_to_cpu(es->s_feature_ro_compat); > + if ((params.set_feature_incompat_mask & > + EXT4_FEATURE_INCOMPAT_CASEFOLD)) { > + enabling_casefold = 1; > + if (!(params.set_flags & EXT4_TUNE_FL_ENCODING)) { > + params.encoding = EXT4_ENC_UTF8_12_1; > + params.set_flags |= EXT4_TUNE_FL_ENCODING; > + } > + if (!(params.set_flags & EXT4_TUNE_FL_ENCODING_FLAGS)) { > + params.encoding_flags = 0; > + params.set_flags |= EXT4_TUNE_FL_ENCODING_FLAGS; > + } > + } > + if ((params.set_feature_compat_mask & > + EXT4_FEATURE_COMPAT_DIR_INDEX)) { > + uuid_t uu; > + > + memcpy(&uu, sbi->s_hash_seed, UUID_SIZE); > + if (uuid_is_null(&uu)) > + generate_random_uuid((char *) > + &sbi->s_hash_seed); > + if (params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) > + sbi->s_def_hash_version = params.def_hash_alg; > + else if (sbi->s_def_hash_version == 0) > + sbi->s_def_hash_version = DX_HASH_HALF_MD4; > + if (!(es->s_flags & > + cpu_to_le32(EXT2_FLAGS_UNSIGNED_HASH)) && > + !(es->s_flags & > + cpu_to_le32(EXT2_FLAGS_SIGNED_HASH))) { > +#ifdef __CHAR_UNSIGNED__ > + sbi->s_hash_unsigned = 3; > +#else > + sbi->s_hash_unsigned = 0; > +#endif > + } > + } > + } > + if (params.set_flags & EXT4_TUNE_FL_ENCODING) { > + if (!enabling_casefold) > + return -EINVAL; > + if (params.encoding == 0) > + params.encoding = EXT4_ENC_UTF8_12_1; > + else if (params.encoding != EXT4_ENC_UTF8_12_1) > + return -EINVAL; > + } > + if (params.set_flags & EXT4_TUNE_FL_ENCODING_FLAGS) { > + if (!enabling_casefold) > + return -EINVAL; > + if (params.encoding_flags & ~SB_ENC_SUPP_MASK) > + return -EINVAL; > + } > + > + ret = mnt_want_write_file(filp); > + if (ret) > + return ret; > + > + ret = ext4_update_superblocks_fn(sb, ext4_sb_setparams, ¶ms); > + mnt_drop_write_file(filp); > + > + if (params.set_flags & EXT4_TUNE_FL_DEF_HASH_ALG) > + sbi->s_def_hash_version = params.def_hash_alg; > + > + return ret; > +} > + > static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) > { > struct inode *inode = file_inode(filp); > @@ -1616,6 +1908,11 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) > return ext4_ioctl_getuuid(EXT4_SB(sb), (void __user *)arg); > case EXT4_IOC_SETFSUUID: > return ext4_ioctl_setuuid(filp, (const void __user *)arg); > + case EXT4_IOC_GET_TUNE_SB_PARAM: > + return ext4_ioctl_get_tune_sb(EXT4_SB(sb), > + (void __user *)arg); > + case EXT4_IOC_SET_TUNE_SB_PARAM: > + return ext4_ioctl_set_tune_sb(filp, (void __user *)arg); > default: > return -ENOTTY; > } > @@ -1703,7 +2000,8 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) > } > #endif > > -static void set_overhead(struct ext4_super_block *es, const void *arg) > +static void set_overhead(struct ext4_sb_info *sbi, > + struct ext4_super_block *es, const void *arg) > { > es->s_overhead_clusters = cpu_to_le32(*((unsigned long *) arg)); > } > diff --git a/include/uapi/linux/ext4.h b/include/uapi/linux/ext4.h > index 1c4c2dd29112cda9f7dc91d917492cffc33ee524..411dcc1e4a35c8c6a10f3768d17b8cc50cff4c34 100644 > --- a/include/uapi/linux/ext4.h > +++ b/include/uapi/linux/ext4.h > @@ -33,6 +33,8 @@ > #define EXT4_IOC_CHECKPOINT _IOW('f', 43, __u32) > #define EXT4_IOC_GETFSUUID _IOR('f', 44, struct fsuuid) > #define EXT4_IOC_SETFSUUID _IOW('f', 44, struct fsuuid) > +#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) > > #define EXT4_IOC_SHUTDOWN _IOR('X', 125, __u32) > > @@ -108,6 +110,57 @@ struct ext4_new_group_input { > __u16 unused; > }; > > +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 > + > /* > * Returned by EXT4_IOC_GET_ES_CACHE as an additional possible flag. > * It indicates that the entry in extent status cache is for a hole. > > -- > 2.51.0 > > > ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 0/3] ext4: Add support for mounted updates to the superblock via an ioctl 2025-09-17 3:22 [PATCH v2 0/3] ext4: Add support for mounted updates to the superblock via an ioctl Theodore Ts'o via B4 Relay ` (2 preceding siblings ...) 2025-09-17 3:22 ` [PATCH v2 3/3] ext4: implemet new ioctls to set and get superblock parameters Theodore Ts'o via B4 Relay @ 2025-09-26 21:47 ` Theodore Ts'o 3 siblings, 0 replies; 10+ messages in thread From: Theodore Ts'o @ 2025-09-26 21:47 UTC (permalink / raw) To: Theodore Ts'o; +Cc: linux-ext4, linux-api, stable On Tue, 16 Sep 2025 23:22:46 -0400, Theodore Ts'o wrote: > This patch series enables a future version of tune2fs to be able to > modify certain parts of the ext4 superblock without to write to the > block device. > > The first patch fixes a potential buffer overrun caused by a > maliciously moified superblock. The second patch adds support for > 32-bit uid and gid's which can have access to the reserved blocks pool. > The last patch adds the ioctl's which will be used by tune2fs. > > [...] Applied, thanks! [1/3] ext4: avoid potential buffer over-read in parse_apply_sb_mount_options() commit: 8ecb790ea8c3fc69e77bace57f14cf0d7c177bd8 [2/3] ext4: add support for 32-bit default reserved uid and gid values commit: 12c84dd4d308551568d85203fd6ed2685e861fda [3/3] ext4: implemet new ioctls to set and get superblock parameters commit: 04a91570ac67760301e5458d65eaf1342ecca314 Best regards, -- Theodore Ts'o <tytso@mit.edu> ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2025-09-26 21:48 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-09-17 3:22 [PATCH v2 0/3] ext4: Add support for mounted updates to the superblock via an ioctl Theodore Ts'o via B4 Relay 2025-09-17 3:22 ` [PATCH v2 1/3] ext4: avoid potential buffer over-read in parse_apply_sb_mount_options() Theodore Ts'o via B4 Relay 2025-09-17 16:05 ` Jan Kara 2025-09-18 16:55 ` Darrick J. Wong 2025-09-17 3:22 ` [PATCH v2 2/3] ext4: add support for 32-bit default reserved uid and gid values Theodore Ts'o via B4 Relay 2025-09-17 16:10 ` Jan Kara 2025-09-17 3:22 ` [PATCH v2 3/3] ext4: implemet new ioctls to set and get superblock parameters Theodore Ts'o via B4 Relay 2025-09-17 16:22 ` Jan Kara 2025-09-18 16:57 ` Darrick J. Wong 2025-09-26 21:47 ` [PATCH v2 0/3] ext4: Add support for mounted updates to the superblock via an ioctl Theodore Ts'o
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).