Linux EXT4 FS development
 help / color / mirror / Atom feed
From: shuo chen <1289151713@qq.com>
To: tytso@mit.edu
Cc: adilger.kernel@dilger.ca, libaokun@linux.alibaba.com,
	jack@suse.cz, ojaswin@linux.ibm.com, yi.zhang@huawei.com,
	linux-ext4@vger.kernel.org, shuo chen <1289151713@qq.com>
Subject: [PATCH v6] ext4: rewrite ext4_convert_inline_data_nolock to make it safer
Date: Fri,  4 Sep 2026 09:30:27 +0800	[thread overview]
Message-ID: <tencent_3656CB4AA45EA01A8DC2B9865242BA18A709@qq.com> (raw)
In-Reply-To: <tencent_93CC3232AC6A9418BCE4372286770F91850A@qq.com>

Fix issues raised by sashiko-bot.
---
v5 -> v6:
> - [High] Zero-initialization of not_found causes out-of-bounds memory
> operations on uninitialized xattr space.
Use -ENODATA as the initial value.
> - [Critical] Silent data loss when ext4_ext_insert_extent() fails
> with errors like -ENOMEM or -EIO.
Revert the inode to inline data if the error is neither EDQUOT nor ENOSPC.
---

Signed-off-by: shuo chen <1289151713@qq.com>
---
 fs/ext4/inline.c | 211 +++++++++++++++++++++++++++++++++++------------
 1 file changed, 158 insertions(+), 53 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 8045e4ff270c..fef98acee688 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -14,6 +14,7 @@
 #include "ext4.h"
 #include "xattr.h"
 #include "truncate.h"
+#include "ext4_extents.h"
 
 #define EXT4_XATTR_SYSTEM_DATA	"data"
 #define EXT4_MIN_INLINE_DATA_SIZE	((sizeof(__le32) * EXT4_N_BLOCKS))
@@ -1070,21 +1071,123 @@ static int ext4_update_inline_dir(handle_t *handle, struct inode *dir,
 	return 0;
 }
 
-static void ext4_restore_inline_data(handle_t *handle, struct inode *inode,
-				     struct ext4_iloc *iloc,
-				     void *buf, int inline_size)
+static int ext4_set_inline_data_block(handle_t *handle, struct inode *inode, ext4_fsblk_t block,
+					unsigned int len, struct buffer_head *bh)
 {
-	int ret;
+	struct ext4_inode_info *ei = EXT4_I(inode);
+	struct ext4_xattr_ibody_find is = {
+		.s = { .not_found = -ENODATA, },
+	};
+	struct ext4_xattr_info i = {
+		.name_index = EXT4_XATTR_INDEX_SYSTEM,
+		.name = EXT4_XATTR_SYSTEM_DATA,
+		.value = NULL,
+		.value_len = 0,
+	};
+	int error;
+	void *i_block_buf = NULL;
+	void *xattr_buf = NULL;
+	void *header = NULL;
+	void *tail = NULL;
+	int xattr_size;
+	struct ext4_inode *raw_inode;
 
-	ret = ext4_create_inline_data(handle, inode, inline_size);
-	if (ret) {
-		ext4_msg(inode->i_sb, KERN_EMERG,
-			"error restoring inline_data for inode -- potential data loss! (inode %llu, error %d)",
-			inode->i_ino, ret);
-		return;
+	i_block_buf = kmalloc(EXT4_MIN_INLINE_DATA_SIZE, GFP_NOFS);
+	if (!i_block_buf) {
+		error = -ENOMEM;
+		down_write(&ei->i_data_sem);
+		goto error;
 	}
-	ext4_write_inline_data(inode, iloc, buf, 0, inline_size);
-	ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+	down_write(&ei->i_data_sem);
+	error = ext4_get_inode_loc(inode, &is.iloc);
+	if (error)
+		goto error;
+	raw_inode = ext4_raw_inode(&is.iloc);
+	header = IHDR(inode, raw_inode);
+	tail = ITAIL(inode, raw_inode);
+	xattr_size = tail - header;
+	xattr_buf = kmalloc(xattr_size, GFP_NOFS);
+	if (!xattr_buf) {
+		error = -ENOMEM;
+		goto error;
+	}
+	BUFFER_TRACE(is.iloc.bh, "get_write_access");
+	error = ext4_journal_get_write_access(handle, inode->i_sb, is.iloc.bh, EXT4_JTR_NONE);
+	if (error)
+		goto error;
+	memcpy(i_block_buf, (void *)raw_inode->i_block, EXT4_MIN_INLINE_DATA_SIZE);
+	memset((void *)raw_inode->i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
+	memset(ei->i_data, 0, EXT4_MIN_INLINE_DATA_SIZE);
+	memcpy(xattr_buf, header, xattr_size);
+	if (ext4_has_feature_extents(inode->i_sb) &&
+		(S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) || S_ISLNK(inode->i_mode))) {
+		ext4_set_inode_flag(inode, EXT4_INODE_EXTENTS);
+		ext4_ext_tree_init(handle, inode);
+		struct ext4_ext_path *path = ext4_find_extent(inode, 0, NULL, 0);
+
+		if (IS_ERR(path)) {
+			error = PTR_ERR(path);
+			goto recovery;
+		}
+		struct ext4_extent newex;
+
+		newex.ee_block = cpu_to_le32(0);
+		newex.ee_len = cpu_to_le16(1);
+		ext4_ext_store_pblock(&newex, block);
+		path = ext4_ext_insert_extent(handle, inode, path, &newex, 0);
+		if (IS_ERR(path)) {
+			error = PTR_ERR(path);
+			if (error == -EDQUOT || error == -ENOSPC)
+				goto recovery;
+			else
+				goto no_free;
+		} else {
+			ext4_free_ext_path(path);
+		}
+	} else {
+		EXT4_I(inode)->i_data[0] = cpu_to_le32(block);
+	}
+	error = ext4_xattr_ibody_find(inode, &i, &is);
+	if (error)
+		goto recovery;
+	if (!is.s.not_found)
+		error = ext4_xattr_ibody_set(handle, inode, &i, &is);
+recovery:
+	if (error) {
+		ext4_discard_preallocations(inode);
+		ext4_free_blocks(handle, inode, NULL, block, len, 0);
+		memcpy(header, xattr_buf, xattr_size);
+no_free:
+		ext4_forget(handle, 0, inode, bh, block);
+		memcpy((void *)raw_inode->i_block, i_block_buf, EXT4_MIN_INLINE_DATA_SIZE);
+		memcpy(ei->i_data, raw_inode->i_block, EXT4_MIN_INLINE_DATA_SIZE);
+		ext4_clear_inode_flag(inode, EXT4_INODE_EXTENTS);
+		get_bh(is.iloc.bh);
+		ext4_mark_iloc_dirty(handle, inode, &is.iloc);
+	} else {
+		ext4_clear_inode_flag(inode, EXT4_INODE_INLINE_DATA);
+		if (S_ISDIR(inode->i_mode)) {
+			i_size_write(inode, inode->i_sb->s_blocksize);
+			EXT4_I(inode)->i_disksize = inode->i_sb->s_blocksize;
+		}
+		get_bh(is.iloc.bh);
+		error = ext4_mark_iloc_dirty(handle, inode, &is.iloc);
+		EXT4_I(inode)->i_inline_off = 0;
+		EXT4_I(inode)->i_inline_size = 0;
+		ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+		brelse(bh);
+	}
+	goto out;
+error:
+	ext4_forget(handle, 0, inode, bh, block);
+	ext4_discard_preallocations(inode);
+	ext4_free_blocks(handle, inode, NULL, block, len, 0);
+out:
+	brelse(is.iloc.bh);
+	up_write(&ei->i_data_sem);
+	kfree(i_block_buf);
+	kfree(xattr_buf);
+	return error;
 }
 
 static int ext4_convert_inline_data_nolock(handle_t *handle,
@@ -1093,9 +1196,12 @@ static int ext4_convert_inline_data_nolock(handle_t *handle,
 {
 	int error;
 	void *buf = NULL;
-	struct buffer_head *data_bh = NULL;
-	struct ext4_map_blocks map;
+	struct buffer_head *bh = NULL;
 	int inline_size;
+	ext4_fsblk_t newblock = 0;
+	struct ext4_allocation_request ar;
+	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+	unsigned int allocated_block;
 
 	inline_size = ext4_get_inline_size(inode);
 	buf = kmalloc(inline_size, GFP_NOFS);
@@ -1120,63 +1226,62 @@ static int ext4_convert_inline_data_nolock(handle_t *handle,
 			goto out;
 	}
 
-	error = ext4_destroy_inline_data_nolock(handle, inode);
-	if (error)
-		goto out;
-
-	map.m_lblk = 0;
-	map.m_len = 1;
-	map.m_flags = 0;
-	error = ext4_map_blocks(handle, inode, &map, EXT4_GET_BLOCKS_CREATE);
+	memset(&ar, 0, sizeof(ar));
+	ar.inode = inode;
+	ar.logical = 0;
+	ar.len = 1;
+	if (S_ISREG(inode->i_mode))
+		ar.flags = EXT4_MB_HINT_DATA;
+	else
+		ar.flags = 0;
+	newblock = ext4_mb_new_blocks(handle, &ar, &error);
 	if (error < 0)
-		goto out_restore;
-	if (!(map.m_flags & EXT4_MAP_MAPPED)) {
-		error = -EIO;
-		goto out_restore;
-	}
-
-	data_bh = sb_getblk(inode->i_sb, map.m_pblk);
-	if (!data_bh) {
+		goto out;
+	allocated_block = EXT4_C2B(sbi, ar.len);
+	bh = sb_getblk(inode->i_sb, newblock);
+	if (!bh) {
 		error = -ENOMEM;
-		goto out_restore;
+		goto out_bh;
 	}
 
-	lock_buffer(data_bh);
-	error = ext4_journal_get_create_access(handle, inode->i_sb, data_bh,
+	lock_buffer(bh);
+	error = ext4_journal_get_create_access(handle, inode->i_sb, bh,
 					       EXT4_JTR_NONE);
 	if (error) {
-		unlock_buffer(data_bh);
+		unlock_buffer(bh);
 		error = -EIO;
-		goto out_restore;
+		goto out_bh;
 	}
-	memset(data_bh->b_data, 0, inode->i_sb->s_blocksize);
+	memset(bh->b_data, 0, inode->i_sb->s_blocksize);
 
 	if (!S_ISDIR(inode->i_mode)) {
-		memcpy(data_bh->b_data, buf, inline_size);
-		set_buffer_uptodate(data_bh);
-		unlock_buffer(data_bh);
+		memcpy(bh->b_data, buf, inline_size);
+		set_buffer_uptodate(bh);
+		unlock_buffer(bh);
 		error = ext4_handle_dirty_metadata(handle,
-						   inode, data_bh);
+						   inode, bh);
 	} else {
-		unlock_buffer(data_bh);
-		inode->i_size = inode->i_sb->s_blocksize;
-		i_size_write(inode, inode->i_sb->s_blocksize);
-		EXT4_I(inode)->i_disksize = inode->i_sb->s_blocksize;
-
-		error = ext4_init_dirblock(handle, inode, data_bh,
+		unlock_buffer(bh);
+		error = ext4_init_dirblock(handle, inode, bh,
 			  le32_to_cpu(((struct ext4_dir_entry_2 *)buf)->inode),
 			  buf + EXT4_INLINE_DOTDOT_SIZE,
 			  inline_size - EXT4_INLINE_DOTDOT_SIZE);
-		if (!error)
-			error = ext4_mark_inode_dirty(handle, inode);
 	}
+out_bh:
+	if (error) {
+		if (bh)
+			ext4_forget(handle, 0, inode, bh, newblock);
+		struct ext4_inode_info *ei = EXT4_I(inode);
 
-out_restore:
-	if (error)
-		ext4_restore_inline_data(handle, inode, iloc, buf, inline_size);
-
+		down_write(&ei->i_data_sem);
+		ext4_discard_preallocations(inode);
+		ext4_free_blocks(handle, inode, NULL, newblock, allocated_block, 0);
+		up_write(&ei->i_data_sem);
+	} else {
+		error = ext4_set_inline_data_block(handle, inode,
+				newblock, allocated_block, bh);
+	}
 out:
-	brelse(data_bh);
 	kfree(buf);
 	return error;
 }
-- 
2.55.0


  reply	other threads:[~2026-09-04  1:31 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-13  8:33 [PATCH] Add EXT4_STATE_MAY_INLINE_DATA check in ext4_readdir pipishuo
2026-08-13  8:49 ` sashiko-bot
2026-08-14  1:27   ` shuo chen
2026-08-14  3:31 ` Theodore Tso
2026-08-14  8:40   ` shuo chen
2026-08-14 13:57     ` Theodore Tso
2026-08-15  1:43       ` shuo chen
2026-08-16 15:02   ` [PATCH v2] " shuo chen
2026-08-16 15:16     ` sashiko-bot
2026-08-16 20:05     ` [syzbot ci] " syzbot ci
2026-08-17  3:12     ` [PATCH v2] " Theodore Tso
2026-08-17  8:24       ` shuo chen
2026-08-22 16:26       ` [PATCH] " shuo chen
2026-08-22 16:43         ` sashiko-bot
2026-08-22 16:28       ` [PATCH V3] " shuo chen
2026-08-22 16:45         ` sashiko-bot
2026-08-27  8:50           ` shuo chen
2026-08-31  2:20             ` [PATCH v4] ext4: rewrite ext4_convert_inline_data_nolock to make it safer shuo chen
2026-08-31  2:34               ` sashiko-bot
2026-08-31  6:34                 ` shuo chen
2026-09-02 10:10                   ` [PATCH v5] " shuo chen
2026-09-02 10:24                     ` sashiko-bot
2026-09-02 12:24                       ` shuo chen
2026-09-04  1:30                         ` shuo chen [this message]
2026-09-04  1:46                           ` [PATCH v6] " sashiko-bot
2026-09-04  2:06                             ` shuo chen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=tencent_3656CB4AA45EA01A8DC2B9865242BA18A709@qq.com \
    --to=1289151713@qq.com \
    --cc=adilger.kernel@dilger.ca \
    --cc=jack@suse.cz \
    --cc=libaokun@linux.alibaba.com \
    --cc=linux-ext4@vger.kernel.org \
    --cc=ojaswin@linux.ibm.com \
    --cc=tytso@mit.edu \
    --cc=yi.zhang@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox