[Concept,11/21] ext4l: Add optional inline data and indirect-block support

Message ID 20260108185149.1995917-12-sjg@u-boot.org
State New
Headers
Series ext4l: Add Kconfig options to reduce binary size (part P) |

Commit Message

Simon Glass Jan. 8, 2026, 6:51 p.m. UTC
  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(-)
  

Patch

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;