Skip to content

Commit 241d4ca

Browse files
committed
Merge tag 'ext4_for_linus-7.0-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 fixes from Ted Ts'o: - Update the MAINTAINERS file to add reviewers for the ext4 file system - Add a test issue an ext4 warning (not a WARN_ON) if there are still dirty pages attached to an evicted inode. - Fix a number of Syzkaller issues - Fix memory leaks on error paths - Replace some BUG and WARN with EFSCORRUPTED reporting - Fix a potential crash when disabling discard via remount followed by an immediate unmount. (Found by Sashiko) - Fix a corner case which could lead to allocating blocks for an indirect-mapped inode block numbers > 2**32 - Fix a race when reallocating a freed inode that could result in a deadlock - Fix a user-after-free in update_super_work when racing with umount - Fix build issues when trying to build ext4's kunit tests as a module - Fix a bug where ext4_split_extent_zeroout() could fail to pass back an error from ext4_ext_dirty() - Avoid allocating blocks from a corrupted block group in ext4_mb_find_by_goal() - Fix a percpu_counters list corruption BUG triggered by an ext4 extents kunit - Fix a potetial crash caused by the fast commit flush path potentially accessing the jinode structure before it is fully initialized - Fix fsync(2) in no-journal mode to make sure the dirtied inode is write to storage - Fix a bug when in no-journal mode, when ext4 tries to avoid using recently deleted inodes, if lazy itable initialization is enabled, can lead to an unitialized inode getting skipped and triggering an e2fsck complaint - Fix journal credit calculation when setting an xattr when both the encryption and ea_inode feeatures are enabled - Fix corner cases which could result in stale xarray tags after writeback - Fix generic/475 failures caused by ENOSPC errors while creating a symlink when the system crashes resulting to a file system inconsistency when replaying the fast commit journal * tag 'ext4_for_linus-7.0-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: (27 commits) ext4: always drain queued discard work in ext4_mb_release() ext4: handle wraparound when searching for blocks for indirect mapped blocks ext4: skip split extent recovery on corruption ext4: fix iloc.bh leak in ext4_fc_replay_inode() error paths ext4: fix deadlock on inode reallocation ext4: fix use-after-free in update_super_work when racing with umount ext4: fix the might_sleep() warnings in kvfree() ext4: reject mount if bigalloc with s_first_data_block != 0 ext4: fix extents-test.c is not compiled when EXT4_KUNIT_TESTS=M ext4: fix mballoc-test.c is not compiled when EXT4_KUNIT_TESTS=M ext4: introduce EXPORT_SYMBOL_FOR_EXT4_TEST() helper jbd2: gracefully abort on checkpointing state corruptions ext4: avoid infinite loops caused by residual data ext4: validate p_idx bounds in ext4_ext_correct_indexes ext4: test if inode's all dirty pages are submitted to disk ext4: minor fix for ext4_split_extent_zeroout() ext4: avoid allocate block from corrupted group in ext4_mb_find_by_goal() ext4: kunit: extents-test: lix percpu_counters list corruption ext4: publish jinode after initialization ext4: replace BUG_ON with proper error handling in ext4_read_inline_folio ...
2 parents b51ad67 + 9ee29d2 commit 241d4ca

19 files changed

Lines changed: 455 additions & 115 deletions

MAINTAINERS

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9619,7 +9619,12 @@ F: include/linux/ext2*
96199619

96209620
EXT4 FILE SYSTEM
96219621
M: "Theodore Ts'o" <tytso@mit.edu>
9622-
M: Andreas Dilger <adilger.kernel@dilger.ca>
9622+
R: Andreas Dilger <adilger.kernel@dilger.ca>
9623+
R: Baokun Li <libaokun@linux.alibaba.com>
9624+
R: Jan Kara <jack@suse.cz>
9625+
R: Ojaswin Mujoo <ojaswin@linux.ibm.com>
9626+
R: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
9627+
R: Zhang Yi <yi.zhang@huawei.com>
96239628
L: linux-ext4@vger.kernel.org
96249629
S: Maintained
96259630
W: http://ext4.wiki.kernel.org

fs/ext4/Makefile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ ext4-y := balloc.o bitmap.o block_validity.o dir.o ext4_jbd2.o extents.o \
1414

1515
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
1616
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
17-
ext4-inode-test-objs += inode-test.o
18-
obj-$(CONFIG_EXT4_KUNIT_TESTS) += ext4-inode-test.o
17+
ext4-test-objs += inode-test.o mballoc-test.o \
18+
extents-test.o
19+
obj-$(CONFIG_EXT4_KUNIT_TESTS) += ext4-test.o
1920
ext4-$(CONFIG_FS_VERITY) += verity.o
2021
ext4-$(CONFIG_FS_ENCRYPTION) += crypto.o

fs/ext4/crypto.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,17 @@ static int ext4_set_context(struct inode *inode, const void *ctx, size_t len,
163163
*/
164164

165165
if (handle) {
166+
/*
167+
* Since the inode is new it is ok to pass the
168+
* XATTR_CREATE flag. This is necessary to match the
169+
* remaining journal credits check in the set_handle
170+
* function with the credits allocated for the new
171+
* inode.
172+
*/
166173
res = ext4_xattr_set_handle(handle, inode,
167174
EXT4_XATTR_INDEX_ENCRYPTION,
168175
EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
169-
ctx, len, 0);
176+
ctx, len, XATTR_CREATE);
170177
if (!res) {
171178
ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT);
172179
ext4_clear_inode_state(inode,

fs/ext4/ext4.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,7 @@ struct ext4_sb_info {
15701570
struct proc_dir_entry *s_proc;
15711571
struct kobject s_kobj;
15721572
struct completion s_kobj_unregister;
1573+
struct mutex s_error_notify_mutex; /* protects sysfs_notify vs kobject_del */
15731574
struct super_block *s_sb;
15741575
struct buffer_head *s_mmp_bh;
15751576

@@ -3944,6 +3945,11 @@ static inline bool ext4_inode_can_atomic_write(struct inode *inode)
39443945
extern int ext4_block_write_begin(handle_t *handle, struct folio *folio,
39453946
loff_t pos, unsigned len,
39463947
get_block_t *get_block);
3948+
3949+
#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
3950+
#define EXPORT_SYMBOL_FOR_EXT4_TEST(sym) \
3951+
EXPORT_SYMBOL_FOR_MODULES(sym, "ext4-test")
3952+
#endif
39473953
#endif /* __KERNEL__ */
39483954

39493955
#endif /* _EXT4_H */

fs/ext4/ext4_extents.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,5 +264,17 @@ static inline void ext4_idx_store_pblock(struct ext4_extent_idx *ix,
264264
0xffff);
265265
}
266266

267+
extern int __ext4_ext_dirty(const char *where, unsigned int line,
268+
handle_t *handle, struct inode *inode,
269+
struct ext4_ext_path *path);
270+
extern int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex);
271+
#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
272+
extern int ext4_ext_space_root_idx_test(struct inode *inode, int check);
273+
extern struct ext4_ext_path *ext4_split_convert_extents_test(
274+
handle_t *handle, struct inode *inode,
275+
struct ext4_map_blocks *map,
276+
struct ext4_ext_path *path,
277+
int flags, unsigned int *allocated);
278+
#endif
267279
#endif /* _EXT4_EXTENTS */
268280

fs/ext4/extents-test.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,10 @@ static struct file_system_type ext_fs_type = {
142142

143143
static void extents_kunit_exit(struct kunit *test)
144144
{
145-
struct ext4_sb_info *sbi = k_ctx.k_ei->vfs_inode.i_sb->s_fs_info;
145+
struct super_block *sb = k_ctx.k_ei->vfs_inode.i_sb;
146+
struct ext4_sb_info *sbi = sb->s_fs_info;
146147

148+
ext4_es_unregister_shrinker(sbi);
147149
kfree(sbi);
148150
kfree(k_ctx.k_ei);
149151
kfree(k_ctx.k_data);
@@ -280,8 +282,8 @@ static int extents_kunit_init(struct kunit *test)
280282
eh->eh_depth = 0;
281283
eh->eh_entries = cpu_to_le16(1);
282284
eh->eh_magic = EXT4_EXT_MAGIC;
283-
eh->eh_max =
284-
cpu_to_le16(ext4_ext_space_root_idx(&k_ctx.k_ei->vfs_inode, 0));
285+
eh->eh_max = cpu_to_le16(ext4_ext_space_root_idx_test(
286+
&k_ctx.k_ei->vfs_inode, 0));
285287
eh->eh_generation = 0;
286288

287289
/*
@@ -384,8 +386,8 @@ static void test_split_convert(struct kunit *test)
384386

385387
switch (param->type) {
386388
case TEST_SPLIT_CONVERT:
387-
path = ext4_split_convert_extents(NULL, inode, &map, path,
388-
param->split_flags, NULL);
389+
path = ext4_split_convert_extents_test(NULL, inode, &map,
390+
path, param->split_flags, NULL);
389391
break;
390392
case TEST_CREATE_BLOCKS:
391393
ext4_map_create_blocks_helper(test, inode, &map, param->split_flags);

fs/ext4/extents.c

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,9 @@ static int ext4_ext_get_access(handle_t *handle, struct inode *inode,
184184
* - ENOMEM
185185
* - EIO
186186
*/
187-
static int __ext4_ext_dirty(const char *where, unsigned int line,
188-
handle_t *handle, struct inode *inode,
189-
struct ext4_ext_path *path)
187+
int __ext4_ext_dirty(const char *where, unsigned int line,
188+
handle_t *handle, struct inode *inode,
189+
struct ext4_ext_path *path)
190190
{
191191
int err;
192192

@@ -1736,6 +1736,13 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
17361736
err = ext4_ext_get_access(handle, inode, path + k);
17371737
if (err)
17381738
return err;
1739+
if (unlikely(path[k].p_idx > EXT_LAST_INDEX(path[k].p_hdr))) {
1740+
EXT4_ERROR_INODE(inode,
1741+
"path[%d].p_idx %p > EXT_LAST_INDEX %p",
1742+
k, path[k].p_idx,
1743+
EXT_LAST_INDEX(path[k].p_hdr));
1744+
return -EFSCORRUPTED;
1745+
}
17391746
path[k].p_idx->ei_block = border;
17401747
err = ext4_ext_dirty(handle, inode, path + k);
17411748
if (err)
@@ -1748,6 +1755,14 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
17481755
err = ext4_ext_get_access(handle, inode, path + k);
17491756
if (err)
17501757
goto clean;
1758+
if (unlikely(path[k].p_idx > EXT_LAST_INDEX(path[k].p_hdr))) {
1759+
EXT4_ERROR_INODE(inode,
1760+
"path[%d].p_idx %p > EXT_LAST_INDEX %p",
1761+
k, path[k].p_idx,
1762+
EXT_LAST_INDEX(path[k].p_hdr));
1763+
err = -EFSCORRUPTED;
1764+
goto clean;
1765+
}
17511766
path[k].p_idx->ei_block = border;
17521767
err = ext4_ext_dirty(handle, inode, path + k);
17531768
if (err)
@@ -3144,7 +3159,7 @@ static void ext4_zeroout_es(struct inode *inode, struct ext4_extent *ex)
31443159
}
31453160

31463161
/* FIXME!! we need to try to merge to left or right after zero-out */
3147-
static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
3162+
int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
31483163
{
31493164
ext4_fsblk_t ee_pblock;
31503165
unsigned int ee_len;
@@ -3239,6 +3254,9 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
32393254

32403255
insert_err = PTR_ERR(path);
32413256
err = 0;
3257+
if (insert_err != -ENOSPC && insert_err != -EDQUOT &&
3258+
insert_err != -ENOMEM)
3259+
goto out_path;
32423260

32433261
/*
32443262
* Get a new path to try to zeroout or fix the extent length.
@@ -3255,13 +3273,20 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
32553273
goto out_path;
32563274
}
32573275

3276+
depth = ext_depth(inode);
3277+
ex = path[depth].p_ext;
3278+
if (!ex) {
3279+
EXT4_ERROR_INODE(inode,
3280+
"bad extent address lblock: %lu, depth: %d pblock %llu",
3281+
(unsigned long)ee_block, depth, path[depth].p_block);
3282+
err = -EFSCORRUPTED;
3283+
goto out;
3284+
}
3285+
32583286
err = ext4_ext_get_access(handle, inode, path + depth);
32593287
if (err)
32603288
goto out;
32613289

3262-
depth = ext_depth(inode);
3263-
ex = path[depth].p_ext;
3264-
32653290
fix_extent_len:
32663291
ex->ee_len = orig_ex.ee_len;
32673292
err = ext4_ext_dirty(handle, inode, path + path->p_depth);
@@ -3363,7 +3388,7 @@ static int ext4_split_extent_zeroout(handle_t *handle, struct inode *inode,
33633388

33643389
ext4_ext_mark_initialized(ex);
33653390

3366-
ext4_ext_dirty(handle, inode, path + depth);
3391+
err = ext4_ext_dirty(handle, inode, path + depth);
33673392
if (err)
33683393
return err;
33693394

@@ -4457,9 +4482,13 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
44574482
path = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
44584483
if (IS_ERR(path)) {
44594484
err = PTR_ERR(path);
4460-
if (allocated_clusters) {
4485+
/*
4486+
* Gracefully handle out of space conditions. If the filesystem
4487+
* is inconsistent, we'll just leak allocated blocks to avoid
4488+
* causing even more damage.
4489+
*/
4490+
if (allocated_clusters && (err == -EDQUOT || err == -ENOSPC)) {
44614491
int fb_flags = 0;
4462-
44634492
/*
44644493
* free data blocks we just allocated.
44654494
* not a good idea to call discard here directly,
@@ -6238,6 +6267,33 @@ int ext4_ext_clear_bb(struct inode *inode)
62386267
return 0;
62396268
}
62406269

6241-
#ifdef CONFIG_EXT4_KUNIT_TESTS
6242-
#include "extents-test.c"
6270+
#if IS_ENABLED(CONFIG_EXT4_KUNIT_TESTS)
6271+
int ext4_ext_space_root_idx_test(struct inode *inode, int check)
6272+
{
6273+
return ext4_ext_space_root_idx(inode, check);
6274+
}
6275+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_space_root_idx_test);
6276+
6277+
struct ext4_ext_path *ext4_split_convert_extents_test(handle_t *handle,
6278+
struct inode *inode, struct ext4_map_blocks *map,
6279+
struct ext4_ext_path *path, int flags,
6280+
unsigned int *allocated)
6281+
{
6282+
return ext4_split_convert_extents(handle, inode, map, path,
6283+
flags, allocated);
6284+
}
6285+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_split_convert_extents_test);
6286+
6287+
EXPORT_SYMBOL_FOR_EXT4_TEST(__ext4_ext_dirty);
6288+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_zeroout);
6289+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_register_shrinker);
6290+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_unregister_shrinker);
6291+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_map_create_blocks);
6292+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_init_tree);
6293+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_lookup_extent);
6294+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_es_insert_extent);
6295+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_ext_insert_extent);
6296+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_find_extent);
6297+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_issue_zeroout);
6298+
EXPORT_SYMBOL_FOR_EXT4_TEST(ext4_map_query_blocks);
62436299
#endif

fs/ext4/fast_commit.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -975,13 +975,13 @@ static int ext4_fc_flush_data(journal_t *journal)
975975
int ret = 0;
976976

977977
list_for_each_entry(ei, &sbi->s_fc_q[FC_Q_MAIN], i_fc_list) {
978-
ret = jbd2_submit_inode_data(journal, ei->jinode);
978+
ret = jbd2_submit_inode_data(journal, READ_ONCE(ei->jinode));
979979
if (ret)
980980
return ret;
981981
}
982982

983983
list_for_each_entry(ei, &sbi->s_fc_q[FC_Q_MAIN], i_fc_list) {
984-
ret = jbd2_wait_inode_data(journal, ei->jinode);
984+
ret = jbd2_wait_inode_data(journal, READ_ONCE(ei->jinode));
985985
if (ret)
986986
return ret;
987987
}
@@ -1613,19 +1613,21 @@ static int ext4_fc_replay_inode(struct super_block *sb,
16131613
/* Immediately update the inode on disk. */
16141614
ret = ext4_handle_dirty_metadata(NULL, NULL, iloc.bh);
16151615
if (ret)
1616-
goto out;
1616+
goto out_brelse;
16171617
ret = sync_dirty_buffer(iloc.bh);
16181618
if (ret)
1619-
goto out;
1619+
goto out_brelse;
16201620
ret = ext4_mark_inode_used(sb, ino);
16211621
if (ret)
1622-
goto out;
1622+
goto out_brelse;
16231623

16241624
/* Given that we just wrote the inode on disk, this SHOULD succeed. */
16251625
inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL);
16261626
if (IS_ERR(inode)) {
16271627
ext4_debug("Inode not found.");
1628-
return -EFSCORRUPTED;
1628+
inode = NULL;
1629+
ret = -EFSCORRUPTED;
1630+
goto out_brelse;
16291631
}
16301632

16311633
/*
@@ -1642,13 +1644,14 @@ static int ext4_fc_replay_inode(struct super_block *sb,
16421644
ext4_inode_csum_set(inode, ext4_raw_inode(&iloc), EXT4_I(inode));
16431645
ret = ext4_handle_dirty_metadata(NULL, NULL, iloc.bh);
16441646
sync_dirty_buffer(iloc.bh);
1647+
out_brelse:
16451648
brelse(iloc.bh);
16461649
out:
16471650
iput(inode);
16481651
if (!ret)
16491652
blkdev_issue_flush(sb->s_bdev);
16501653

1651-
return 0;
1654+
return ret;
16521655
}
16531656

16541657
/*

fs/ext4/fsync.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,23 @@ static int ext4_fsync_nojournal(struct file *file, loff_t start, loff_t end,
8383
int datasync, bool *needs_barrier)
8484
{
8585
struct inode *inode = file->f_inode;
86+
struct writeback_control wbc = {
87+
.sync_mode = WB_SYNC_ALL,
88+
.nr_to_write = 0,
89+
};
8690
int ret;
8791

8892
ret = generic_buffers_fsync_noflush(file, start, end, datasync);
89-
if (!ret)
90-
ret = ext4_sync_parent(inode);
93+
if (ret)
94+
return ret;
95+
96+
/* Force writeout of inode table buffer to disk */
97+
ret = ext4_write_inode(inode, &wbc);
98+
if (ret)
99+
return ret;
100+
101+
ret = ext4_sync_parent(inode);
102+
91103
if (test_opt(inode->i_sb, BARRIER))
92104
*needs_barrier = true;
93105

fs/ext4/ialloc.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,12 @@ static int recently_deleted(struct super_block *sb, ext4_group_t group, int ino)
686686
if (unlikely(!gdp))
687687
return 0;
688688

689+
/* Inode was never used in this filesystem? */
690+
if (ext4_has_group_desc_csum(sb) &&
691+
(gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT) ||
692+
ino >= EXT4_INODES_PER_GROUP(sb) - ext4_itable_unused_count(sb, gdp)))
693+
return 0;
694+
689695
bh = sb_find_get_block(sb, ext4_inode_table(sb, gdp) +
690696
(ino / inodes_per_block));
691697
if (!bh || !buffer_uptodate(bh))

0 commit comments

Comments
 (0)