Skip to content

Commit a396560

Browse files
committed
Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 bug fixes from Ted Ts'o: "Ext4 bug fixes, including a regression fix" * tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: ext4: clarify impact of 'commit' mount option ext4: fix unused-but-set-variable warning in ext4_add_entry() jbd2: fix kernel-doc notation warning ext4: use RCU API in debug_print_tree ext4: validate the debug_want_extra_isize mount option at parse time ext4: reserve revoke credits in __ext4_new_inode ext4: unlock on error in ext4_expand_extra_isize() ext4: optimize __ext4_check_dir_entry() ext4: check for directory entries too close to block end ext4: fix ext4_empty_dir() for directories with holes
2 parents 44579f3 + 23f6b02 commit a396560

File tree

8 files changed

+116
-104
lines changed

8 files changed

+116
-104
lines changed

Documentation/admin-guide/ext4.rst

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -181,14 +181,17 @@ When mounting an ext4 filesystem, the following option are accepted:
181181
system after its metadata has been committed to the journal.
182182

183183
commit=nrsec (*)
184-
Ext4 can be told to sync all its data and metadata every 'nrsec'
185-
seconds. The default value is 5 seconds. This means that if you lose
186-
your power, you will lose as much as the latest 5 seconds of work (your
187-
filesystem will not be damaged though, thanks to the journaling). This
188-
default value (or any low value) will hurt performance, but it's good
189-
for data-safety. Setting it to 0 will have the same effect as leaving
190-
it at the default (5 seconds). Setting it to very large values will
191-
improve performance.
184+
This setting limits the maximum age of the running transaction to
185+
'nrsec' seconds. The default value is 5 seconds. This means that if
186+
you lose your power, you will lose as much as the latest 5 seconds of
187+
metadata changes (your filesystem will not be damaged though, thanks
188+
to the journaling). This default value (or any low value) will hurt
189+
performance, but it's good for data-safety. Setting it to 0 will have
190+
the same effect as leaving it at the default (5 seconds). Setting it
191+
to very large values will improve performance. Note that due to
192+
delayed allocation even older data can be lost on power failure since
193+
writeback of those data begins only after time set in
194+
/proc/sys/vm/dirty_expire_centisecs.
192195

193196
barrier=<0|1(*)>, barrier(*), nobarrier
194197
This enables/disables the use of write barriers in the jbd code.

fs/ext4/block_validity.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,17 +133,21 @@ static void debug_print_tree(struct ext4_sb_info *sbi)
133133
{
134134
struct rb_node *node;
135135
struct ext4_system_zone *entry;
136+
struct ext4_system_blocks *system_blks;
136137
int first = 1;
137138

138139
printk(KERN_INFO "System zones: ");
139-
node = rb_first(&sbi->system_blks->root);
140+
rcu_read_lock();
141+
system_blks = rcu_dereference(sbi->system_blks);
142+
node = rb_first(&system_blks->root);
140143
while (node) {
141144
entry = rb_entry(node, struct ext4_system_zone, node);
142145
printk(KERN_CONT "%s%llu-%llu", first ? "" : ", ",
143146
entry->start_blk, entry->start_blk + entry->count - 1);
144147
first = 0;
145148
node = rb_next(node);
146149
}
150+
rcu_read_unlock();
147151
printk(KERN_CONT "\n");
148152
}
149153

fs/ext4/dir.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,19 @@ int __ext4_check_dir_entry(const char *function, unsigned int line,
7272
const char *error_msg = NULL;
7373
const int rlen = ext4_rec_len_from_disk(de->rec_len,
7474
dir->i_sb->s_blocksize);
75+
const int next_offset = ((char *) de - buf) + rlen;
7576

7677
if (unlikely(rlen < EXT4_DIR_REC_LEN(1)))
7778
error_msg = "rec_len is smaller than minimal";
7879
else if (unlikely(rlen % 4 != 0))
7980
error_msg = "rec_len % 4 != 0";
8081
else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len)))
8182
error_msg = "rec_len is too small for name_len";
82-
else if (unlikely(((char *) de - buf) + rlen > size))
83+
else if (unlikely(next_offset > size))
8384
error_msg = "directory entry overrun";
85+
else if (unlikely(next_offset > size - EXT4_DIR_REC_LEN(1) &&
86+
next_offset != size))
87+
error_msg = "directory entry too close to block end";
8488
else if (unlikely(le32_to_cpu(de->inode) >
8589
le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
8690
error_msg = "inode out of bounds";

fs/ext4/ialloc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -921,8 +921,8 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
921921
if (!handle) {
922922
BUG_ON(nblocks <= 0);
923923
handle = __ext4_journal_start_sb(dir->i_sb, line_no,
924-
handle_type, nblocks,
925-
0, 0);
924+
handle_type, nblocks, 0,
925+
ext4_trans_default_revoke_credits(sb));
926926
if (IS_ERR(handle)) {
927927
err = PTR_ERR(handle);
928928
ext4_std_error(sb, err);

fs/ext4/inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5692,7 +5692,7 @@ int ext4_expand_extra_isize(struct inode *inode,
56925692
error = ext4_journal_get_write_access(handle, iloc->bh);
56935693
if (error) {
56945694
brelse(iloc->bh);
5695-
goto out_stop;
5695+
goto out_unlock;
56965696
}
56975697

56985698
error = __ext4_expand_extra_isize(inode, new_extra_isize, iloc,
@@ -5702,8 +5702,8 @@ int ext4_expand_extra_isize(struct inode *inode,
57025702
if (!error)
57035703
error = rc;
57045704

5705+
out_unlock:
57055706
ext4_write_unlock_xattr(inode, &no_expand);
5706-
out_stop:
57075707
ext4_journal_stop(handle);
57085708
return error;
57095709
}

fs/ext4/namei.c

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2164,7 +2164,9 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
21642164
struct buffer_head *bh = NULL;
21652165
struct ext4_dir_entry_2 *de;
21662166
struct super_block *sb;
2167+
#ifdef CONFIG_UNICODE
21672168
struct ext4_sb_info *sbi;
2169+
#endif
21682170
struct ext4_filename fname;
21692171
int retval;
21702172
int dx_fallback=0;
@@ -2176,12 +2178,12 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
21762178
csum_size = sizeof(struct ext4_dir_entry_tail);
21772179

21782180
sb = dir->i_sb;
2179-
sbi = EXT4_SB(sb);
21802181
blocksize = sb->s_blocksize;
21812182
if (!dentry->d_name.len)
21822183
return -EINVAL;
21832184

21842185
#ifdef CONFIG_UNICODE
2186+
sbi = EXT4_SB(sb);
21852187
if (ext4_has_strict_mode(sbi) && IS_CASEFOLDED(dir) &&
21862188
sbi->s_encoding && utf8_validate(sbi->s_encoding, &dentry->d_name))
21872189
return -EINVAL;
@@ -2822,7 +2824,7 @@ bool ext4_empty_dir(struct inode *inode)
28222824
{
28232825
unsigned int offset;
28242826
struct buffer_head *bh;
2825-
struct ext4_dir_entry_2 *de, *de1;
2827+
struct ext4_dir_entry_2 *de;
28262828
struct super_block *sb;
28272829

28282830
if (ext4_has_inline_data(inode)) {
@@ -2847,19 +2849,25 @@ bool ext4_empty_dir(struct inode *inode)
28472849
return true;
28482850

28492851
de = (struct ext4_dir_entry_2 *) bh->b_data;
2850-
de1 = ext4_next_entry(de, sb->s_blocksize);
2851-
if (le32_to_cpu(de->inode) != inode->i_ino ||
2852-
le32_to_cpu(de1->inode) == 0 ||
2853-
strcmp(".", de->name) || strcmp("..", de1->name)) {
2854-
ext4_warning_inode(inode, "directory missing '.' and/or '..'");
2852+
if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size,
2853+
0) ||
2854+
le32_to_cpu(de->inode) != inode->i_ino || strcmp(".", de->name)) {
2855+
ext4_warning_inode(inode, "directory missing '.'");
2856+
brelse(bh);
2857+
return true;
2858+
}
2859+
offset = ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize);
2860+
de = ext4_next_entry(de, sb->s_blocksize);
2861+
if (ext4_check_dir_entry(inode, NULL, de, bh, bh->b_data, bh->b_size,
2862+
offset) ||
2863+
le32_to_cpu(de->inode) == 0 || strcmp("..", de->name)) {
2864+
ext4_warning_inode(inode, "directory missing '..'");
28552865
brelse(bh);
28562866
return true;
28572867
}
2858-
offset = ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize) +
2859-
ext4_rec_len_from_disk(de1->rec_len, sb->s_blocksize);
2860-
de = ext4_next_entry(de1, sb->s_blocksize);
2868+
offset += ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize);
28612869
while (offset < inode->i_size) {
2862-
if ((void *) de >= (void *) (bh->b_data+sb->s_blocksize)) {
2870+
if (!(offset & (sb->s_blocksize - 1))) {
28632871
unsigned int lblock;
28642872
brelse(bh);
28652873
lblock = offset >> EXT4_BLOCK_SIZE_BITS(sb);
@@ -2870,12 +2878,11 @@ bool ext4_empty_dir(struct inode *inode)
28702878
}
28712879
if (IS_ERR(bh))
28722880
return true;
2873-
de = (struct ext4_dir_entry_2 *) bh->b_data;
28742881
}
2882+
de = (struct ext4_dir_entry_2 *) (bh->b_data +
2883+
(offset & (sb->s_blocksize - 1)));
28752884
if (ext4_check_dir_entry(inode, NULL, de, bh,
28762885
bh->b_data, bh->b_size, offset)) {
2877-
de = (struct ext4_dir_entry_2 *)(bh->b_data +
2878-
sb->s_blocksize);
28792886
offset = (offset | (sb->s_blocksize - 1)) + 1;
28802887
continue;
28812888
}
@@ -2884,7 +2891,6 @@ bool ext4_empty_dir(struct inode *inode)
28842891
return false;
28852892
}
28862893
offset += ext4_rec_len_from_disk(de->rec_len, sb->s_blocksize);
2887-
de = ext4_next_entry(de, sb->s_blocksize);
28882894
}
28892895
brelse(bh);
28902896
return true;

fs/ext4/super.c

Lines changed: 69 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,6 +1900,13 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
19001900
}
19011901
sbi->s_commit_interval = HZ * arg;
19021902
} else if (token == Opt_debug_want_extra_isize) {
1903+
if ((arg & 1) ||
1904+
(arg < 4) ||
1905+
(arg > (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE))) {
1906+
ext4_msg(sb, KERN_ERR,
1907+
"Invalid want_extra_isize %d", arg);
1908+
return -1;
1909+
}
19031910
sbi->s_want_extra_isize = arg;
19041911
} else if (token == Opt_max_batch_time) {
19051912
sbi->s_max_batch_time = arg;
@@ -3554,40 +3561,6 @@ int ext4_calculate_overhead(struct super_block *sb)
35543561
return 0;
35553562
}
35563563

3557-
static void ext4_clamp_want_extra_isize(struct super_block *sb)
3558-
{
3559-
struct ext4_sb_info *sbi = EXT4_SB(sb);
3560-
struct ext4_super_block *es = sbi->s_es;
3561-
unsigned def_extra_isize = sizeof(struct ext4_inode) -
3562-
EXT4_GOOD_OLD_INODE_SIZE;
3563-
3564-
if (sbi->s_inode_size == EXT4_GOOD_OLD_INODE_SIZE) {
3565-
sbi->s_want_extra_isize = 0;
3566-
return;
3567-
}
3568-
if (sbi->s_want_extra_isize < 4) {
3569-
sbi->s_want_extra_isize = def_extra_isize;
3570-
if (ext4_has_feature_extra_isize(sb)) {
3571-
if (sbi->s_want_extra_isize <
3572-
le16_to_cpu(es->s_want_extra_isize))
3573-
sbi->s_want_extra_isize =
3574-
le16_to_cpu(es->s_want_extra_isize);
3575-
if (sbi->s_want_extra_isize <
3576-
le16_to_cpu(es->s_min_extra_isize))
3577-
sbi->s_want_extra_isize =
3578-
le16_to_cpu(es->s_min_extra_isize);
3579-
}
3580-
}
3581-
/* Check if enough inode space is available */
3582-
if ((sbi->s_want_extra_isize > sbi->s_inode_size) ||
3583-
(EXT4_GOOD_OLD_INODE_SIZE + sbi->s_want_extra_isize >
3584-
sbi->s_inode_size)) {
3585-
sbi->s_want_extra_isize = def_extra_isize;
3586-
ext4_msg(sb, KERN_INFO,
3587-
"required extra inode space not available");
3588-
}
3589-
}
3590-
35913564
static void ext4_set_resv_clusters(struct super_block *sb)
35923565
{
35933566
ext4_fsblk_t resv_clusters;
@@ -3795,6 +3768,68 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
37953768
*/
37963769
sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
37973770

3771+
if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV) {
3772+
sbi->s_inode_size = EXT4_GOOD_OLD_INODE_SIZE;
3773+
sbi->s_first_ino = EXT4_GOOD_OLD_FIRST_INO;
3774+
} else {
3775+
sbi->s_inode_size = le16_to_cpu(es->s_inode_size);
3776+
sbi->s_first_ino = le32_to_cpu(es->s_first_ino);
3777+
if (sbi->s_first_ino < EXT4_GOOD_OLD_FIRST_INO) {
3778+
ext4_msg(sb, KERN_ERR, "invalid first ino: %u",
3779+
sbi->s_first_ino);
3780+
goto failed_mount;
3781+
}
3782+
if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) ||
3783+
(!is_power_of_2(sbi->s_inode_size)) ||
3784+
(sbi->s_inode_size > blocksize)) {
3785+
ext4_msg(sb, KERN_ERR,
3786+
"unsupported inode size: %d",
3787+
sbi->s_inode_size);
3788+
goto failed_mount;
3789+
}
3790+
/*
3791+
* i_atime_extra is the last extra field available for
3792+
* [acm]times in struct ext4_inode. Checking for that
3793+
* field should suffice to ensure we have extra space
3794+
* for all three.
3795+
*/
3796+
if (sbi->s_inode_size >= offsetof(struct ext4_inode, i_atime_extra) +
3797+
sizeof(((struct ext4_inode *)0)->i_atime_extra)) {
3798+
sb->s_time_gran = 1;
3799+
sb->s_time_max = EXT4_EXTRA_TIMESTAMP_MAX;
3800+
} else {
3801+
sb->s_time_gran = NSEC_PER_SEC;
3802+
sb->s_time_max = EXT4_NON_EXTRA_TIMESTAMP_MAX;
3803+
}
3804+
sb->s_time_min = EXT4_TIMESTAMP_MIN;
3805+
}
3806+
if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) {
3807+
sbi->s_want_extra_isize = sizeof(struct ext4_inode) -
3808+
EXT4_GOOD_OLD_INODE_SIZE;
3809+
if (ext4_has_feature_extra_isize(sb)) {
3810+
unsigned v, max = (sbi->s_inode_size -
3811+
EXT4_GOOD_OLD_INODE_SIZE);
3812+
3813+
v = le16_to_cpu(es->s_want_extra_isize);
3814+
if (v > max) {
3815+
ext4_msg(sb, KERN_ERR,
3816+
"bad s_want_extra_isize: %d", v);
3817+
goto failed_mount;
3818+
}
3819+
if (sbi->s_want_extra_isize < v)
3820+
sbi->s_want_extra_isize = v;
3821+
3822+
v = le16_to_cpu(es->s_min_extra_isize);
3823+
if (v > max) {
3824+
ext4_msg(sb, KERN_ERR,
3825+
"bad s_min_extra_isize: %d", v);
3826+
goto failed_mount;
3827+
}
3828+
if (sbi->s_want_extra_isize < v)
3829+
sbi->s_want_extra_isize = v;
3830+
}
3831+
}
3832+
37983833
if (sbi->s_es->s_mount_opts[0]) {
37993834
char *s_mount_opts = kstrndup(sbi->s_es->s_mount_opts,
38003835
sizeof(sbi->s_es->s_mount_opts),
@@ -4033,42 +4068,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
40334068
has_huge_files);
40344069
sb->s_maxbytes = ext4_max_size(sb->s_blocksize_bits, has_huge_files);
40354070

4036-
if (le32_to_cpu(es->s_rev_level) == EXT4_GOOD_OLD_REV) {
4037-
sbi->s_inode_size = EXT4_GOOD_OLD_INODE_SIZE;
4038-
sbi->s_first_ino = EXT4_GOOD_OLD_FIRST_INO;
4039-
} else {
4040-
sbi->s_inode_size = le16_to_cpu(es->s_inode_size);
4041-
sbi->s_first_ino = le32_to_cpu(es->s_first_ino);
4042-
if (sbi->s_first_ino < EXT4_GOOD_OLD_FIRST_INO) {
4043-
ext4_msg(sb, KERN_ERR, "invalid first ino: %u",
4044-
sbi->s_first_ino);
4045-
goto failed_mount;
4046-
}
4047-
if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) ||
4048-
(!is_power_of_2(sbi->s_inode_size)) ||
4049-
(sbi->s_inode_size > blocksize)) {
4050-
ext4_msg(sb, KERN_ERR,
4051-
"unsupported inode size: %d",
4052-
sbi->s_inode_size);
4053-
goto failed_mount;
4054-
}
4055-
/*
4056-
* i_atime_extra is the last extra field available for [acm]times in
4057-
* struct ext4_inode. Checking for that field should suffice to ensure
4058-
* we have extra space for all three.
4059-
*/
4060-
if (sbi->s_inode_size >= offsetof(struct ext4_inode, i_atime_extra) +
4061-
sizeof(((struct ext4_inode *)0)->i_atime_extra)) {
4062-
sb->s_time_gran = 1;
4063-
sb->s_time_max = EXT4_EXTRA_TIMESTAMP_MAX;
4064-
} else {
4065-
sb->s_time_gran = NSEC_PER_SEC;
4066-
sb->s_time_max = EXT4_NON_EXTRA_TIMESTAMP_MAX;
4067-
}
4068-
4069-
sb->s_time_min = EXT4_TIMESTAMP_MIN;
4070-
}
4071-
40724071
sbi->s_desc_size = le16_to_cpu(es->s_desc_size);
40734072
if (ext4_has_feature_64bit(sb)) {
40744073
if (sbi->s_desc_size < EXT4_MIN_DESC_SIZE_64BIT ||
@@ -4517,8 +4516,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
45174516
} else if (ret)
45184517
goto failed_mount4a;
45194518

4520-
ext4_clamp_want_extra_isize(sb);
4521-
45224519
ext4_set_resv_clusters(sb);
45234520

45244521
err = ext4_setup_system_zone(sb);
@@ -5306,8 +5303,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
53065303
goto restore_opts;
53075304
}
53085305

5309-
ext4_clamp_want_extra_isize(sb);
5310-
53115306
if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^
53125307
test_opt(sb, JOURNAL_CHECKSUM)) {
53135308
ext4_msg(sb, KERN_ERR, "changing journal_checksum "

include/linux/jbd2.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ struct jbd2_revoke_table_s;
457457
* @h_journal: Which journal handle belongs to - used iff h_reserved set.
458458
* @h_rsv_handle: Handle reserved for finishing the logical operation.
459459
* @h_total_credits: Number of remaining buffers we are allowed to add to
460-
journal. These are dirty buffers and revoke descriptor blocks.
460+
* journal. These are dirty buffers and revoke descriptor blocks.
461461
* @h_revoke_credits: Number of remaining revoke records available for handle
462462
* @h_ref: Reference count on this handle.
463463
* @h_err: Field for caller's use to track errors through large fs operations.

0 commit comments

Comments
 (0)