From: Simon Glass <simon.glass@canonical.com> Add CONFIG_EXT4_INLINE_DATA for inline data support (stores small files in the inode). This depends on xattr and adds ~8K. Disabled by default; filesystems with inline_data are rejected if disabled. Add CONFIG_EXT4_INDIRECT for legacy indirect block mapping used by ext2/ext3. Modern ext4 uses extents instead. Adds ~5K. Enabled by default for ext2/ext3 compatibility. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/Kconfig | 22 +++++++++ fs/ext4l/Makefile | 4 +- fs/ext4l/ext4.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++ fs/ext4l/super.c | 16 +++++++ 4 files changed, 159 insertions(+), 1 deletion(-) diff --git a/fs/ext4l/Kconfig b/fs/ext4l/Kconfig index ee7b9bb2dd6..e494c66e8dc 100644 --- a/fs/ext4l/Kconfig +++ b/fs/ext4l/Kconfig @@ -88,3 +88,25 @@ config EXT4_XATTR and user-defined attributes. This adds about 14K to the image size. If unsure, say N. + +config EXT4_INLINE_DATA + bool "Enable ext4 inline data support" + depends on FS_EXT4L && EXT4_XATTR + help + Enable support for inline data, which stores small files + directly in the inode. This is an uncommon feature that + requires creating the filesystem with -O inline_data. + + Filesystems with inline_data feature will be rejected if + this is disabled. Adds about 8K. If unsure, say N. + +config EXT4_INDIRECT + bool "Enable ext4 indirect block support (ext2/3 compatibility)" + depends on FS_EXT4L + default y + help + Enable support for indirect block mapping used by ext2/ext3 + filesystems. Modern ext4 filesystems use extents instead. + + Filesystems without the extents feature will be rejected if + this is disabled. Adds about 5K. If unsure, say N. diff --git a/fs/ext4l/Makefile b/fs/ext4l/Makefile index 720181908f8..85ddaaa104e 100644 --- a/fs/ext4l/Makefile +++ b/fs/ext4l/Makefile @@ -8,7 +8,7 @@ obj-y := interface.o support.o stub.o # Core ext4 objects (always needed) obj-y += balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o extents.o \ extents_status.o file.o fsmap.o fsync.o hash.o ialloc.o \ - indirect.o inline.o inode.o mballoc.o mmp.o namei.o \ + inode.o mballoc.o mmp.o namei.o \ page-io.o readpage.o super.o symlink.o fast_commit.o orphan.o # Optional features @@ -17,3 +17,5 @@ obj-$(CONFIG_EXT4_MIGRATE) += migrate.o obj-$(CONFIG_EXT4_MOVE_EXTENT) += move_extent.o obj-$(CONFIG_EXT4_XATTR) += xattr.o xattr_hurd.o xattr_trusted.o \ xattr_user.o +obj-$(CONFIG_EXT4_INLINE_DATA) += inline.o +obj-$(CONFIG_EXT4_INDIRECT) += indirect.o diff --git a/fs/ext4l/ext4.h b/fs/ext4l/ext4.h index 26795a595df..d01955bf59a 100644 --- a/fs/ext4l/ext4.h +++ b/fs/ext4l/ext4.h @@ -3132,12 +3132,30 @@ static inline bool is_special_ino(struct super_block *sb, unsigned long ino) } /* indirect.c */ +#ifdef CONFIG_EXT4_INDIRECT extern int ext4_ind_map_blocks(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, int flags); extern int ext4_ind_trans_blocks(struct inode *inode, int nrblocks); extern void ext4_ind_truncate(handle_t *, struct inode *inode); extern int ext4_ind_remove_space(handle_t *handle, struct inode *inode, ext4_lblk_t start, ext4_lblk_t end); +#else +static inline int ext4_ind_map_blocks(handle_t *handle, struct inode *inode, + struct ext4_map_blocks *map, int flags) +{ + return -EOPNOTSUPP; +} +static inline int ext4_ind_trans_blocks(struct inode *inode, int nrblocks) +{ + return 0; +} +static inline void ext4_ind_truncate(handle_t *h, struct inode *inode) {} +static inline int ext4_ind_remove_space(handle_t *handle, struct inode *inode, + ext4_lblk_t start, ext4_lblk_t end) +{ + return -EOPNOTSUPP; +} +#endif /* ioctl.c */ extern long ext4_ioctl(struct file *, unsigned int, unsigned long); @@ -3675,6 +3693,7 @@ extern const struct file_operations ext4_file_operations; extern loff_t ext4_llseek(struct file *file, loff_t offset, int origin); /* inline.c */ +#ifdef CONFIG_EXT4_INLINE_DATA extern int ext4_get_max_inline_size(struct inode *inode); extern int ext4_find_inline_data_nolock(struct inode *inode); extern int ext4_destroy_inline_data(handle_t *handle, struct inode *inode); @@ -3733,6 +3752,105 @@ static inline int ext4_has_inline_data(struct inode *inode) return ext4_test_inode_flag(inode, EXT4_INODE_INLINE_DATA) && EXT4_I(inode)->i_inline_off; } +#else /* !CONFIG_EXT4_INLINE_DATA */ +static inline int ext4_get_max_inline_size(struct inode *inode) { return 0; } +static inline int ext4_find_inline_data_nolock(struct inode *inode) { return 0; } +static inline int ext4_destroy_inline_data(handle_t *h, struct inode *i) +{ + return 0; +} +static inline void ext4_update_final_de(void *de_buf, int old_size, + int new_size) {} +static inline int ext4_readpage_inline(struct inode *inode, struct folio *folio) +{ + return -EOPNOTSUPP; +} +static inline int ext4_try_to_write_inline_data(struct address_space *mapping, + struct inode *inode, + loff_t pos, unsigned len, + struct folio **foliop) +{ + return 0; +} +static inline int ext4_write_inline_data_end(struct inode *inode, loff_t pos, + unsigned len, unsigned copied, + struct folio *folio) +{ + return 0; +} +static inline int ext4_generic_write_inline_data(struct address_space *mapping, + struct inode *inode, + loff_t pos, unsigned len, + struct folio **foliop, + void **fsdata, bool da) +{ + return 0; +} +static inline int ext4_try_add_inline_entry(handle_t *handle, + struct ext4_filename *fname, + struct inode *dir, + struct inode *inode) +{ + return 0; +} +static inline int ext4_try_create_inline_dir(handle_t *handle, + struct inode *parent, + struct inode *inode) +{ + return 0; +} +static inline int ext4_read_inline_dir(struct file *filp, + struct dir_context *ctx, + int *has_inline_data) +{ + return 0; +} +static inline int ext4_inlinedir_to_tree(struct file *dir_file, + struct inode *dir, ext4_lblk_t block, + struct dx_hash_info *hinfo, + __u32 start_hash, __u32 start_minor_hash, + int *has_inline_data) +{ + return 0; +} +static inline struct buffer_head *ext4_find_inline_entry(struct inode *dir, + struct ext4_filename *fname, + struct ext4_dir_entry_2 **res_dir, + int *has_inline_data) +{ + return NULL; +} +static inline int ext4_delete_inline_entry(handle_t *handle, struct inode *dir, + struct ext4_dir_entry_2 *de_del, + struct buffer_head *bh, + int *has_inline_data) +{ + return 0; +} +static inline bool empty_inline_dir(struct inode *dir, int *has_inline_data) +{ + return true; +} +static inline struct buffer_head *ext4_get_first_inline_block(struct inode *in, + struct ext4_dir_entry_2 **parent_de, + int *retval) +{ + return NULL; +} +static inline void *ext4_read_inline_link(struct inode *inode) { return NULL; } +struct iomap; +static inline int ext4_inline_data_iomap(struct inode *inode, struct iomap *m) +{ + return 0; +} +static inline int ext4_inline_data_truncate(struct inode *inode, + int *has_inline) +{ + return 0; +} +static inline int ext4_convert_inline_data(struct inode *inode) { return 0; } +static inline int ext4_has_inline_data(struct inode *inode) { return 0; } +#endif /* CONFIG_EXT4_INLINE_DATA */ /* namei.c */ extern const struct inode_operations ext4_dir_inode_operations; diff --git a/fs/ext4l/super.c b/fs/ext4l/super.c index d3e1b85ac5d..4bad3677db3 100644 --- a/fs/ext4l/super.c +++ b/fs/ext4l/super.c @@ -3553,6 +3553,22 @@ int ext4_feature_set_ok(struct super_block *sb, int readonly) return 0; } + if (!IS_ENABLED(CONFIG_EXT4_INLINE_DATA) && + ext4_has_feature_inline_data(sb)) { + ext4_msg(sb, KERN_ERR, + "Filesystem with inline_data feature cannot be " + "mounted without CONFIG_EXT4_INLINE_DATA"); + return 0; + } + + if (!IS_ENABLED(CONFIG_EXT4_INDIRECT) && + !ext4_has_feature_extents(sb)) { + ext4_msg(sb, KERN_ERR, + "Filesystem without extents feature requires " + "CONFIG_EXT4_INDIRECT for indirect block support"); + return 0; + } + if (readonly) return 1; -- 2.43.0