From: Theodore Ts'o tytso@mit.edu
mainline inclusion from mainline-5.6-rc1 commit cf2834a5ed57562d6a1a8170724704149f0ae0a4 category: bugfix bugzilla: 51832 CVE: NA ---------------------------
In commit 7963e5ac9012 ("ext4: treat buffers with write errors as containing valid data") we missed changing ext4_sb_bread() to use ext4_buffer_uptodate(). So fix this oversight.
Signed-off-by: Theodore Ts'o tytso@mit.edu
Conflicts: fs/ext4/super.c [we include d9befedaafcf("ext4: clear buffer verified flag if read meta block from disk") first]
Signed-off-by: yangerkun yangerkun@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/ext4/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 5f95ed3fbf7d3..2f5093d8536d6 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -161,7 +161,7 @@ ext4_sb_bread(struct super_block *sb, sector_t block, int op_flags)
if (bh == NULL) return ERR_PTR(-ENOMEM); - if (buffer_uptodate(bh)) + if (ext4_buffer_uptodate(bh)) return bh; clear_buffer_verified(bh); ll_rw_block(REQ_OP_READ, REQ_META | op_flags, 1, &bh);
From: "zhangyi (F)" yi.zhang@huawei.com
mainline inclusion from mainline-5.10-rc1 commit fa491b14cd9586ad703606ef0155cd43459d2b32 category: bugfix bugzilla: 51832 CVE: NA ---------------------------
The previous patch add clear_buffer_verified() before we read metadata block from disk again, but it's rather easy to miss clearing of this bit because currently we read metadata buffer through different open codes (e.g. ll_rw_block(), bh_submit_read() and invoke submit_bh() directly). So, it's time to add common helpers to unify in all the places reading metadata buffers instead. This patch add 3 helpers:
- ext4_read_bh_nowait(): async read metadata buffer if it's actually not uptodate, clear buffer_verified bit before read from disk. - ext4_read_bh(): sync version of read metadata buffer, it will wait until the read operation return and check the return status. - ext4_read_bh_lock(): try to lock the buffer before read buffer, it will skip reading if the buffer is already locked.
After this patch, we need to use these helpers in all the places reading metadata buffer instead of different open codes.
Signed-off-by: zhangyi (F) yi.zhang@huawei.com Suggested-by: Jan Kara jack@suse.cz Link: https://lore.kernel.org/r/20200924073337.861472-3-yi.zhang@huawei.com Signed-off-by: Theodore Ts'o tytso@mit.edu Signed-off-by: yangerkun yangerkun@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/ext4/ext4.h | 5 ++++ fs/ext4/super.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 66df21b4cd561..1ea777a66c8cb 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2722,6 +2722,11 @@ extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count); /* super.c */ extern struct buffer_head *ext4_sb_bread(struct super_block *sb, sector_t block, int op_flags); +extern void ext4_read_bh_nowait(struct buffer_head *bh, int op_flags, + bh_end_io_t *end_io); +extern int ext4_read_bh(struct buffer_head *bh, int op_flags, + bh_end_io_t *end_io); +extern int ext4_read_bh_lock(struct buffer_head *bh, int op_flags, bool wait); extern int ext4_seq_options_show(struct seq_file *seq, void *offset); extern int ext4_calculate_overhead(struct super_block *sb); extern void ext4_superblock_csum_set(struct super_block *sb); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 2f5093d8536d6..ed75bd2edb539 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -148,6 +148,68 @@ MODULE_ALIAS_FS("ext3"); MODULE_ALIAS("ext3"); #define IS_EXT3_SB(sb) ((sb)->s_bdev->bd_holder == &ext3_fs_type)
+ +static inline void __ext4_read_bh(struct buffer_head *bh, int op_flags, + bh_end_io_t *end_io) +{ + /* + * buffer's verified bit is no longer valid after reading from + * disk again due to write out error, clear it to make sure we + * recheck the buffer contents. + */ + clear_buffer_verified(bh); + + bh->b_end_io = end_io ? end_io : end_buffer_read_sync; + get_bh(bh); + submit_bh(REQ_OP_READ, op_flags, bh); +} + +void ext4_read_bh_nowait(struct buffer_head *bh, int op_flags, + bh_end_io_t *end_io) +{ + BUG_ON(!buffer_locked(bh)); + + if (ext4_buffer_uptodate(bh)) { + unlock_buffer(bh); + return; + } + __ext4_read_bh(bh, op_flags, end_io); +} + +int ext4_read_bh(struct buffer_head *bh, int op_flags, bh_end_io_t *end_io) +{ + BUG_ON(!buffer_locked(bh)); + + if (ext4_buffer_uptodate(bh)) { + unlock_buffer(bh); + return 0; + } + + __ext4_read_bh(bh, op_flags, end_io); + + wait_on_buffer(bh); + if (buffer_uptodate(bh)) + return 0; + return -EIO; +} + +int ext4_read_bh_lock(struct buffer_head *bh, int op_flags, bool wait) +{ + if (trylock_buffer(bh)) { + if (wait) + return ext4_read_bh(bh, op_flags, NULL); + ext4_read_bh_nowait(bh, op_flags, NULL); + return 0; + } + if (wait) { + wait_on_buffer(bh); + if (buffer_uptodate(bh)) + return 0; + return -EIO; + } + return 0; +} + /* * This works like sb_bread() except it uses ERR_PTR for error * returns. Currently with sb_bread it's impossible to distinguish
From: "zhangyi (F)" yi.zhang@huawei.com
mainline inclusion from mainline-5.10-rc1 commit 2d069c0889ef0decda7af6ecbdc63b680b767749 category: bugfix bugzilla: 51832 CVE: NA ---------------------------
Revome all open codes that read metadata buffers, switch to use ext4_read_bh_*() common helpers.
Signed-off-by: zhangyi (F) yi.zhang@huawei.com Suggested-by: Jan Kara jack@suse.cz Link: https://lore.kernel.org/r/20200924073337.861472-4-yi.zhang@huawei.com Signed-off-by: Theodore Ts'o tytso@mit.edu
Conflicts: fs/ext4/balloc.c fs/ext4/inode.c
Signed-off-by: yangerkun yangerkun@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/ext4/balloc.c | 5 +---- fs/ext4/extents.c | 3 +-- fs/ext4/ialloc.c | 6 +----- fs/ext4/indirect.c | 2 +- fs/ext4/inode.c | 35 ++++++++++++++--------------------- fs/ext4/mmp.c | 10 +++------- fs/ext4/move_extent.c | 2 +- fs/ext4/resize.c | 2 +- fs/ext4/super.c | 22 +++++++++++----------- 9 files changed, 34 insertions(+), 53 deletions(-)
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 7c92728276951..4fdcde64407b2 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -487,11 +487,8 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group) * submit the buffer_head for reading */ set_buffer_new(bh); - clear_buffer_verified(bh); trace_ext4_read_block_bitmap_load(sb, block_group); - bh->b_end_io = ext4_end_bitmap_read; - get_bh(bh); - submit_bh(REQ_OP_READ, REQ_META | REQ_PRIO, bh); + ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO, ext4_end_bitmap_read); return bh; verify: err = ext4_validate_block_bitmap(sb, desc, block_group, bh); diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 3bc2cb4cc5cc5..584c1ffc9a0c2 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -546,8 +546,7 @@ __read_extent_tree_block(const char *function, unsigned int line,
if (!bh_uptodate_or_lock(bh)) { trace_ext4_ext_load_extent(inode, pblk, _RET_IP_); - clear_buffer_verified(bh); - err = bh_submit_read(bh); + err = ext4_read_bh(bh, 0, NULL); if (err < 0) goto errout; } diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 141bfa55ed682..ab1d4590fcbb0 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -188,12 +188,8 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group) /* * submit the buffer_head for reading */ - clear_buffer_verified(bh); trace_ext4_load_inode_bitmap(sb, block_group); - bh->b_end_io = ext4_end_bitmap_read; - get_bh(bh); - submit_bh(REQ_OP_READ, REQ_META | REQ_PRIO, bh); - wait_on_buffer(bh); + ext4_read_bh(bh, REQ_META | REQ_PRIO, ext4_end_bitmap_read); ext4_simulate_fail_bh(sb, bh, EXT4_SIM_IBITMAP_EIO); if (!buffer_uptodate(bh)) { put_bh(bh); diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index de42b31728550..87bbbea02c748 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -163,7 +163,7 @@ static Indirect *ext4_get_branch(struct inode *inode, int depth, }
if (!bh_uptodate_or_lock(bh)) { - if (bh_submit_read(bh) < 0) { + if (ext4_read_bh(bh, 0, NULL) < 0) { put_bh(bh); goto failure; } diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 35b37a2ce8548..23c15124c9ab6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1023,19 +1023,20 @@ struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode, ext4_lblk_t block, int map_flags) { struct buffer_head *bh; + int ret;
bh = ext4_getblk(handle, inode, block, map_flags); if (IS_ERR(bh)) return bh; if (!bh || ext4_buffer_uptodate(bh)) return bh; - clear_buffer_verified(bh); - ll_rw_block(REQ_OP_READ, REQ_META | REQ_PRIO, 1, &bh); - wait_on_buffer(bh); - if (buffer_uptodate(bh)) - return bh; - put_bh(bh); - return ERR_PTR(-EIO); + + ret = ext4_read_bh_lock(bh, REQ_META | REQ_PRIO, true); + if (ret) { + put_bh(bh); + return ERR_PTR(ret); + } + return bh; }
/* Read a contiguous batch of blocks. */ @@ -1055,11 +1056,8 @@ int ext4_bread_batch(struct inode *inode, ext4_lblk_t block, int bh_count,
for (i = 0; i < bh_count; i++) /* Note that NULL bhs[i] is valid because of holes. */ - if (bhs[i] && !ext4_buffer_uptodate(bhs[i])) { - clear_buffer_verified(bhs[i]); - ll_rw_block(REQ_OP_READ, REQ_META | REQ_PRIO, 1, - &bhs[i]); - } + if (bhs[i] && !ext4_buffer_uptodate(bhs[i])) + ext4_read_bh_lock(bhs[i], REQ_META | REQ_PRIO, false);
if (!wait) return 0; @@ -1229,7 +1227,7 @@ static int ext4_block_write_begin(struct page *page, loff_t pos, unsigned len, if (!buffer_uptodate(bh) && !buffer_delay(bh) && !buffer_unwritten(bh) && (block_start < from || block_end > to)) { - ll_rw_block(REQ_OP_READ, 0, 1, &bh); + ext4_read_bh_lock(bh, 0, false); *wait_bh++ = bh; decrypt = ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode); @@ -4086,11 +4084,8 @@ static int __ext4_block_zero_page_range(handle_t *handle, set_buffer_uptodate(bh);
if (!buffer_uptodate(bh)) { - err = -EIO; - ll_rw_block(REQ_OP_READ, 0, 1, &bh); - wait_on_buffer(bh); - /* Uhhuh. Read error. Complain and punt. */ - if (!buffer_uptodate(bh)) + err = ext4_read_bh_lock(bh, 0, true); + if (err) goto unlock; if (S_ISREG(inode->i_mode) && ext4_encrypted_inode(inode)) { @@ -4730,9 +4725,7 @@ static int __ext4_get_inode_loc(struct inode *inode, * Read the block from disk. */ trace_ext4_load_inode(inode); - get_bh(bh); - bh->b_end_io = end_buffer_read_sync; - submit_bh(REQ_OP_READ, REQ_META | REQ_PRIO, bh); + ext4_read_bh_nowait(bh, REQ_META | REQ_PRIO, NULL); wait_on_buffer(bh); if (!buffer_uptodate(bh)) { simulate_eio: diff --git a/fs/ext4/mmp.c b/fs/ext4/mmp.c index d34cb8c466556..795c3ff2907c2 100644 --- a/fs/ext4/mmp.c +++ b/fs/ext4/mmp.c @@ -85,15 +85,11 @@ static int read_mmp_block(struct super_block *sb, struct buffer_head **bh, } }
- get_bh(*bh); lock_buffer(*bh); - (*bh)->b_end_io = end_buffer_read_sync; - submit_bh(REQ_OP_READ, REQ_META | REQ_PRIO, *bh); - wait_on_buffer(*bh); - if (!buffer_uptodate(*bh)) { - ret = -EIO; + ret = ext4_read_bh(*bh, REQ_META | REQ_PRIO, NULL); + if (ret) goto warn_exit; - } + mmp = (struct mmp_struct *)((*bh)->b_data); if (le32_to_cpu(mmp->mmp_magic) != EXT4_MMP_MAGIC) { ret = -EFSCORRUPTED; diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index c2b288cd78839..97512df281e37 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -215,7 +215,7 @@ mext_page_mkuptodate(struct page *page, unsigned from, unsigned to) for (i = 0; i < nr; i++) { bh = arr[i]; if (!bh_uptodate_or_lock(bh)) { - err = bh_submit_read(bh); + err = ext4_read_bh(bh, 0, NULL); if (err) return err; } diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index cb89381ac5dde..347388e7fae1a 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -1250,7 +1250,7 @@ static struct buffer_head *ext4_get_bitmap(struct super_block *sb, __u64 block) if (unlikely(!bh)) return NULL; if (!bh_uptodate_or_lock(bh)) { - if (bh_submit_read(bh) < 0) { + if (ext4_read_bh(bh, 0, NULL) < 0) { brelse(bh); return NULL; } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index ed75bd2edb539..24dc9137185ff 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -219,19 +219,21 @@ int ext4_read_bh_lock(struct buffer_head *bh, int op_flags, bool wait) struct buffer_head * ext4_sb_bread(struct super_block *sb, sector_t block, int op_flags) { - struct buffer_head *bh = sb_getblk(sb, block); + struct buffer_head *bh; + int ret;
+ bh = sb_getblk(sb, block); if (bh == NULL) return ERR_PTR(-ENOMEM); if (ext4_buffer_uptodate(bh)) return bh; - clear_buffer_verified(bh); - ll_rw_block(REQ_OP_READ, REQ_META | op_flags, 1, &bh); - wait_on_buffer(bh); - if (buffer_uptodate(bh)) - return bh; - put_bh(bh); - return ERR_PTR(-EIO); + + ret = ext4_read_bh_lock(bh, REQ_META | op_flags, true); + if (ret) { + put_bh(bh); + return ERR_PTR(ret); + } + return bh; }
static int ext4_verify_csum_type(struct super_block *sb, @@ -4988,9 +4990,7 @@ static journal_t *ext4_get_dev_journal(struct super_block *sb, goto out_bdev; } journal->j_private = sb; - ll_rw_block(REQ_OP_READ, REQ_META | REQ_PRIO, 1, &journal->j_sb_buffer); - wait_on_buffer(journal->j_sb_buffer); - if (!buffer_uptodate(journal->j_sb_buffer)) { + if (ext4_read_bh_lock(journal->j_sb_buffer, REQ_META | REQ_PRIO, true)) { ext4_msg(sb, KERN_ERR, "I/O error on journal device"); goto out_journal; }
From: "zhangyi (F)" yi.zhang@huawei.com
mainline inclusion from mainline-5.10-rc1 commit 60c776e50bc52a9334be8a49415479d5cb6ca2e2 category: bugfix bugzilla: 51832 CVE: NA ---------------------------
We have already introduced ext4_buffer_uptodate() to re-set the uptodate bit on buffer which has been failed to write out to disk. Just remove the redundant codes and switch to use ext4_buffer_uptodate() in __ext4_get_inode_loc().
Signed-off-by: zhangyi (F) yi.zhang@huawei.com Link: https://lore.kernel.org/r/20200924073337.861472-5-yi.zhang@huawei.com Signed-off-by: Theodore Ts'o tytso@mit.edu Signed-off-by: yangerkun yangerkun@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/ext4/inode.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 23c15124c9ab6..b27dcd80caf06 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4637,16 +4637,7 @@ static int __ext4_get_inode_loc(struct inode *inode, if (!buffer_uptodate(bh)) { lock_buffer(bh);
- /* - * If the buffer has the write error flag, we have failed - * to write out another inode in the same block. In this - * case, we don't have to read the block because we may - * read the old inode data successfully. - */ - if (buffer_write_io_error(bh) && !buffer_uptodate(bh)) - set_buffer_uptodate(bh); - - if (buffer_uptodate(bh)) { + if (ext4_buffer_uptodate(bh)) { /* someone brought it uptodate while we waited */ unlock_buffer(bh); goto has_buffer;
From: "zhangyi (F)" yi.zhang@huawei.com
mainline inclusion from mainline-5.10-rc1 commit 5df1d4123d53261d9d71c7d237d0f165add7ce72 category: bugfix bugzilla: 51832 CVE: NA ---------------------------
If we readahead inode tables in __ext4_get_inode_loc(), it may bypass buffer_write_io_error() check, so introduce ext4_sb_breadahead_unmovable() to handle this special case.
This patch also replace sb_breadahead_unmovable() in ext4_fill_super() for the sake of unification.
Signed-off-by: zhangyi (F) yi.zhang@huawei.com Link: https://lore.kernel.org/r/20200924073337.861472-6-yi.zhang@huawei.com Signed-off-by: Theodore Ts'o tytso@mit.edu Signed-off-by: yangerkun yangerkun@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/ext4/ext4.h | 1 + fs/ext4/inode.c | 2 +- fs/ext4/super.c | 12 +++++++++++- 3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 1ea777a66c8cb..03c660459d311 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2727,6 +2727,7 @@ extern void ext4_read_bh_nowait(struct buffer_head *bh, int op_flags, extern int ext4_read_bh(struct buffer_head *bh, int op_flags, bh_end_io_t *end_io); extern int ext4_read_bh_lock(struct buffer_head *bh, int op_flags, bool wait); +extern void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block); extern int ext4_seq_options_show(struct seq_file *seq, void *offset); extern int ext4_calculate_overhead(struct super_block *sb); extern void ext4_superblock_csum_set(struct super_block *sb); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index b27dcd80caf06..1761ba640a26d 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4707,7 +4707,7 @@ static int __ext4_get_inode_loc(struct inode *inode, if (end > table) end = table; while (b <= end) - sb_breadahead_unmovable(sb, b++); + ext4_sb_breadahead_unmovable(sb, b++); }
/* diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 24dc9137185ff..ec9df6f427688 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -236,6 +236,16 @@ ext4_sb_bread(struct super_block *sb, sector_t block, int op_flags) return bh; }
+void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block) +{ + struct buffer_head *bh = sb_getblk_gfp(sb, block, 0); + + if (likely(bh)) { + ext4_read_bh_lock(bh, REQ_RAHEAD, false); + brelse(bh); + } +} + static int ext4_verify_csum_type(struct super_block *sb, struct ext4_super_block *es) { @@ -4374,7 +4384,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) /* Pre-read the descriptors into the buffer cache */ for (i = 0; i < db_count; i++) { block = descriptor_loc(sb, logical_sb_block, i); - sb_breadahead_unmovable(sb, block); + ext4_sb_breadahead_unmovable(sb, block); }
for (i = 0; i < db_count; i++) {
From: "zhangyi (F)" yi.zhang@huawei.com
mainline inclusion from mainline-5.10-rc1 commit 0a846f496db1d3996341e140364aa58563d1ebe1 category: bugfix bugzilla: 51832 CVE: NA ---------------------------
We have already remove open codes that invoke helpers provide by fs/buffer.c in all places reading metadata buffers. This patch switch to use ext4_sb_bread() to replace all sb_bread() helpers, which is ext4_read_bh() helper back end.
Signed-off-by: zhangyi (F) yi.zhang@huawei.com Link: https://lore.kernel.org/r/20200924073337.861472-7-yi.zhang@huawei.com Signed-off-by: Theodore Ts'o tytso@mit.edu Signed-off-by: yangerkun yangerkun@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/ext4/indirect.c | 6 +++--- fs/ext4/resize.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 87bbbea02c748..cd3b4ebbbab3e 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -1042,14 +1042,14 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode, }
/* Go read the buffer for the next level down */ - bh = sb_bread(inode->i_sb, nr); + bh = ext4_sb_bread(inode->i_sb, nr, 0);
/* * A read failure? Report error and clear slot * (should be rare). */ - if (!bh) { - ext4_error_inode_block(inode, nr, EIO, + if (IS_ERR(bh)) { + ext4_error_inode_block(inode, nr, -PTR_ERR(bh), "Read failure"); continue; } diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 347388e7fae1a..08d67f79aed7b 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -1821,8 +1821,8 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es, o_blocks_count + add, add);
/* See if the device is actually as big as what was requested */ - bh = sb_bread(sb, o_blocks_count + add - 1); - if (!bh) { + bh = ext4_sb_bread(sb, o_blocks_count + add - 1, 0); + if (IS_ERR(bh)) { ext4_warning(sb, "can't read last block, resize aborted"); return -ENOSPC; } @@ -1950,8 +1950,8 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count) int meta_bg;
/* See if the device is actually as big as what was requested */ - bh = sb_bread(sb, n_blocks_count - 1); - if (!bh) { + bh = ext4_sb_bread(sb, n_blocks_count - 1, 0); + if (IS_ERR(bh)) { ext4_warning(sb, "can't read last block, resize aborted"); return -ENOSPC; }
From: "zhangyi (F)" yi.zhang@huawei.com
mainline inclusion from mainline-5.10-rc1 commit 8394a6abf3aeaa02e30fd33c248adab9fbe6fd6c category: bugfix bugzilla: 51832 CVE: NA ---------------------------
Now we only use sb_bread_unmovable() to read superblock and descriptor block at mount time, so there is no opportunity that we need to clear buffer verified bit and also handle buffer write_io error bit. But for the sake of unification, let's introduce ext4_sb_bread_unmovable() to replace all sb_bread_unmovable(). After this patch, we stop using read helpers in fs/buffer.c.
Signed-off-by: zhangyi (F) yi.zhang@huawei.com Link: https://lore.kernel.org/r/20200924073337.861472-8-yi.zhang@huawei.com Signed-off-by: Theodore Ts'o tytso@mit.edu Signed-off-by: yangerkun yangerkun@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/ext4/ext4.h | 2 ++ fs/ext4/super.c | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 03c660459d311..d4e98941c5ad4 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2722,6 +2722,8 @@ extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count); /* super.c */ extern struct buffer_head *ext4_sb_bread(struct super_block *sb, sector_t block, int op_flags); +extern struct buffer_head *ext4_sb_bread_unmovable(struct super_block *sb, + sector_t block); extern void ext4_read_bh_nowait(struct buffer_head *bh, int op_flags, bh_end_io_t *end_io); extern int ext4_read_bh(struct buffer_head *bh, int op_flags, diff --git a/fs/ext4/super.c b/fs/ext4/super.c index ec9df6f427688..50fb70caa0e9f 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -211,18 +211,19 @@ int ext4_read_bh_lock(struct buffer_head *bh, int op_flags, bool wait) }
/* - * This works like sb_bread() except it uses ERR_PTR for error + * This works like __bread_gfp() except it uses ERR_PTR for error * returns. Currently with sb_bread it's impossible to distinguish * between ENOMEM and EIO situations (since both result in a NULL * return. */ -struct buffer_head * -ext4_sb_bread(struct super_block *sb, sector_t block, int op_flags) +static struct buffer_head *__ext4_sb_bread_gfp(struct super_block *sb, + sector_t block, int op_flags, + gfp_t gfp) { struct buffer_head *bh; int ret;
- bh = sb_getblk(sb, block); + bh = sb_getblk_gfp(sb, block, gfp); if (bh == NULL) return ERR_PTR(-ENOMEM); if (ext4_buffer_uptodate(bh)) @@ -236,6 +237,18 @@ ext4_sb_bread(struct super_block *sb, sector_t block, int op_flags) return bh; }
+struct buffer_head *ext4_sb_bread(struct super_block *sb, sector_t block, + int op_flags) +{ + return __ext4_sb_bread_gfp(sb, block, op_flags, __GFP_MOVABLE); +} + +struct buffer_head *ext4_sb_bread_unmovable(struct super_block *sb, + sector_t block) +{ + return __ext4_sb_bread_gfp(sb, block, 0, 0); +} + void ext4_sb_breadahead_unmovable(struct super_block *sb, sector_t block) { struct buffer_head *bh = sb_getblk_gfp(sb, block, 0); @@ -3811,8 +3824,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) logical_sb_block = sb_block; }
- if (!(bh = sb_bread_unmovable(sb, logical_sb_block))) { + bh = ext4_sb_bread_unmovable(sb, logical_sb_block); + if (IS_ERR(bh)) { ext4_msg(sb, KERN_ERR, "unable to read superblock"); + ret = PTR_ERR(bh); + bh = NULL; goto out_fail; } /* @@ -4166,10 +4182,12 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) brelse(bh); logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE; offset = do_div(logical_sb_block, blocksize); - bh = sb_bread_unmovable(sb, logical_sb_block); - if (!bh) { + bh = ext4_sb_bread_unmovable(sb, logical_sb_block); + if (IS_ERR(bh)) { ext4_msg(sb, KERN_ERR, "Can't read superblock on 2nd try"); + ret = PTR_ERR(bh); + bh = NULL; goto failed_mount; } es = (struct ext4_super_block *)(bh->b_data + offset); @@ -4391,11 +4409,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) struct buffer_head *bh;
block = descriptor_loc(sb, logical_sb_block, i); - bh = sb_bread_unmovable(sb, block); - if (!bh) { + bh = ext4_sb_bread_unmovable(sb, block); + if (IS_ERR(bh)) { ext4_msg(sb, KERN_ERR, "can't read group descriptor %d", i); db_count = i; + ret = PTR_ERR(bh); + bh = NULL; goto failed_mount2; } rcu_read_lock();