Pull ext4 fixes from Ted Ts'o: "A few more miscellaneous ext4 bug fixes and cleanups including some syzbot failures and fixing a stale file handing refeencing an inode previously used as a regular file, but which has been deleted and reused as an ea_inode would result in ext4 erroneously considering this a case of fs corruption" * tag 'ext4_for_linus-6.15-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: ext4: fix off-by-one error in do_split ext4: make block validity check resistent to sb bh corruption ext4: avoid -Wflex-array-member-not-at-end warning Documentation: ext4: Add fields to ext4_super_block documentation ext4: don't treat fhandle lookup of ea_inode as FS corruption
Unified · Split
| @@ -328,9 +328,13 @@ The ext4 superblock is laid out as follows in · 327 unmodified lines | |||
| 328 | - s_checksum_type | 328 | - s_checksum_type |
| 329 | - Metadata checksum algorithm type. The only valid value is 1 (crc32c). | 329 | - Metadata checksum algorithm type. The only valid value is 1 (crc32c). |
| 330 | * - 0x176 | 330 | * - 0x176 |
| 331 | - __le16 | 331 | - \_\_u8 |
| 332 | - s_reserved_pad | 332 | - s\_encryption\_level |
| 333 | - | 333 | - Versioning level for encryption. |
| 334 | * - 0x177 | ||
| 335 | - \_\_u8 | ||
| 336 | - s\_reserved\_pad | ||
| 337 | - Padding to next 32bits. | ||
| 334 | * - 0x178 | 338 | * - 0x178 |
| 335 | - __le64 | 339 | - __le64 |
| 336 | - s_kbytes_written | 340 | - s_kbytes_written |
| @@ -466,9 +470,13 @@ The ext4 superblock is laid out as follows in · 129 unmodified lines | |||
| 466 | - s_last_error_time_hi | 470 | - s_last_error_time_hi |
| 467 | - Upper 8 bits of the s_last_error_time field. | 471 | - Upper 8 bits of the s_last_error_time field. |
| 468 | * - 0x27A | 472 | * - 0x27A |
| 469 | - __u8 | 473 | - \_\_u8 |
| 470 | - s_pad[2] | 474 | - s\_first\_error\_errcode |
| 471 | - Zero padding. | 475 | - |
| 476 | * - 0x27B | ||
| 477 | - \_\_u8 | ||
| 478 | - s\_last\_error\_errcode | ||
| 479 | - | ||
| 472 | * - 0x27C | 480 | * - 0x27C |
| 473 | - __le16 | 481 | - __le16 |
| 474 | - s_encoding | 482 | - s_encoding |
| @@ -351,10 +351,9 @@ int ext4_check_blockref(const char *function, unsigned int line, · 350 unmodified lines | |||
| 351 | { | 351 | { |
| 352 | __le32 *bref = p; | 352 | __le32 *bref = p; |
| 353 | unsigned int blk; | 353 | unsigned int blk; |
| 354 | journal_t *journal = EXT4_SB(inode->i_sb)->s_journal; | ||
| 354 | 355 | ||
| 355 | if (ext4_has_feature_journal(inode->i_sb) && | 356 | if (journal && inode == journal->j_inode) |
| 356 | (inode->i_ino == | ||
| 357 | le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) | ||
| 358 | return 0; | 357 | return 0; |
| 359 | 358 | ||
| 360 | while (bref < p+max) { | 359 | while (bref < p+max) { |
| @@ -386,10 +386,11 @@ static int __check_block_validity(struct inode *inode, const char *func, · 385 unmodified lines | |||
| 386 | unsigned int line, | 386 | unsigned int line, |
| 387 | struct ext4_map_blocks *map) | 387 | struct ext4_map_blocks *map) |
| 388 | { | 388 | { |
| 389 | if (ext4_has_feature_journal(inode->i_sb) && | 389 | journal_t *journal = EXT4_SB(inode->i_sb)->s_journal; |
| 390 | (inode->i_ino == | 390 | |
| 391 | le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) | 391 | if (journal && inode == journal->j_inode) |
| 392 | return 0; | 392 | return 0; |
| 393 | |||
| 393 | if (!ext4_inode_block_valid(inode, map->m_pblk, map->m_len)) { | 394 | if (!ext4_inode_block_valid(inode, map->m_pblk, map->m_len)) { |
| 394 | ext4_error_inode(inode, func, line, map->m_pblk, | 395 | ext4_error_inode(inode, func, line, map->m_pblk, |
| 395 | "lblock %lu mapped to illegal pblock %llu " | 396 | "lblock %lu mapped to illegal pblock %llu " |
| @@ -4724,22 +4725,43 @@ static inline void ext4_inode_set_iversion_queried(struct inode *inode, u64 val) · 4328 unmodified lines | |||
| 4724 | inode_set_iversion_queried(inode, val); | 4725 | inode_set_iversion_queried(inode, val); |
| 4725 | } | 4726 | } |
| 4726 | 4727 | ||
| 4727 | static const char *check_igot_inode(struct inode *inode, ext4_iget_flags flags) | 4728 | static int check_igot_inode(struct inode *inode, ext4_iget_flags flags, |
| 4728 | 4729 | const char *function, unsigned int line) | |
| 4729 | { | 4730 | { |
| 4731 | const char *err_str; | ||
| 4732 | |||
| 4730 | if (flags & EXT4_IGET_EA_INODE) { | 4733 | if (flags & EXT4_IGET_EA_INODE) { |
| 4731 | if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) | 4734 | if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) { |
| 4732 | return "missing EA_INODE flag"; | 4735 | err_str = "missing EA_INODE flag"; |
| 4736 | goto error; | ||
| 4737 | } | ||
| 4733 | if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) || | 4738 | if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) || |
| 4734 | EXT4_I(inode)->i_file_acl) | 4739 | EXT4_I(inode)->i_file_acl) { |
| 4735 | return "ea_inode with extended attributes"; | 4740 | err_str = "ea_inode with extended attributes"; |
| 4741 | goto error; | ||
| 4742 | } | ||
| 4736 | } else { | 4743 | } else { |
| 4737 | if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) | 4744 | if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) { |
| 4738 | return "unexpected EA_INODE flag"; | 4745 | /* |
| 4746 | * open_by_handle_at() could provide an old inode number | ||
| 4747 | * that has since been reused for an ea_inode; this does | ||
| 4748 | * not indicate filesystem corruption | ||
| 4749 | */ | ||
| 4750 | if (flags & EXT4_IGET_HANDLE) | ||
| 4751 | return -ESTALE; | ||
| 4752 | err_str = "unexpected EA_INODE flag"; | ||
| 4753 | goto error; | ||
| 4754 | } | ||
| 4755 | } | ||
| 4756 | if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) { | ||
| 4757 | err_str = "unexpected bad inode w/o EXT4_IGET_BAD"; | ||
| 4758 | goto error; | ||
| 4739 | } | 4759 | } |
| 4740 | if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) | 4760 | return 0; |
| 4741 | return "unexpected bad inode w/o EXT4_IGET_BAD"; | 4761 | |
| 4742 | return NULL; | 4762 | error: |
| 4763 | ext4_error_inode(inode, function, line, 0, err_str); | ||
| 4764 | return -EFSCORRUPTED; | ||
| 4743 | } | 4765 | } |
| 4744 | 4766 | ||
| 4745 | struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, | 4767 | struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, |
| @@ -4751,7 +4773,6 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, · 5 unmodified lines | |||
| 4751 | struct ext4_inode_info *ei; | 4773 | struct ext4_inode_info *ei; |
| 4752 | struct ext4_super_block *es = EXT4_SB(sb)->s_es; | 4774 | struct ext4_super_block *es = EXT4_SB(sb)->s_es; |
| 4753 | struct inode *inode; | 4775 | struct inode *inode; |
| 4754 | const char *err_str; | ||
| 4755 | journal_t *journal = EXT4_SB(sb)->s_journal; | 4776 | journal_t *journal = EXT4_SB(sb)->s_journal; |
| 4756 | long ret; | 4777 | long ret; |
| 4757 | loff_t size; | 4778 | loff_t size; |
| @@ -4780,10 +4801,10 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, · 22 unmodified lines | |||
| 4780 | if (!inode) | 4801 | if (!inode) |
| 4781 | return ERR_PTR(-ENOMEM); | 4802 | return ERR_PTR(-ENOMEM); |
| 4782 | if (!(inode->i_state & I_NEW)) { | 4803 | if (!(inode->i_state & I_NEW)) { |
| 4783 | if ((err_str = check_igot_inode(inode, flags)) != NULL) { | 4804 | ret = check_igot_inode(inode, flags, function, line); |
| 4784 | ext4_error_inode(inode, function, line, 0, err_str); | 4805 | if (ret) { |
| 4785 | iput(inode); | 4806 | iput(inode); |
| 4786 | return ERR_PTR(-EFSCORRUPTED); | 4807 | return ERR_PTR(ret); |
| 4787 | } | 4808 | } |
| 4788 | return inode; | 4809 | return inode; |
| 4789 | } | 4810 | } |
| @@ -5065,13 +5086,21 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, · 275 unmodified lines | |||
| 5065 | ret = -EFSCORRUPTED; | 5086 | ret = -EFSCORRUPTED; |
| 5066 | goto bad_inode; | 5087 | goto bad_inode; |
| 5067 | } | 5088 | } |
| 5068 | if ((err_str = check_igot_inode(inode, flags)) != NULL) { | 5089 | ret = check_igot_inode(inode, flags, function, line); |
| 5069 | ext4_error_inode(inode, function, line, 0, err_str); | 5090 | /* |
| 5070 | ret = -EFSCORRUPTED; | 5091 | * -ESTALE here means there is nothing inherently wrong with the inode, |
| 5071 | goto bad_inode; | 5092 | * it's just not an inode we can return for an fhandle lookup. |
| 5093 | */ | ||
| 5094 | if (ret == -ESTALE) { | ||
| 5095 | brelse(iloc.bh); | ||
| 5096 | unlock_new_inode(inode); | ||
| 5097 | iput(inode); | ||
| 5098 | return ERR_PTR(-ESTALE); | ||
| 5072 | } | 5099 | } |
| 5073 | 5100 | if (ret) | |
| 5101 | goto bad_inode; | ||
| 5074 | brelse(iloc.bh); | 5102 | brelse(iloc.bh); |
| 5103 | |||
| 5075 | unlock_new_inode(inode); | 5104 | unlock_new_inode(inode); |
| 5076 | return inode; | 5105 | return inode; |
| 5077 | 5106 | ||
| @@ -3037,10 +3037,8 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) · 3036 unmodified lines | |||
| 3037 | unsigned char blocksize_bits = min_t(unsigned char, | 3037 | unsigned char blocksize_bits = min_t(unsigned char, |
| 3038 | sb->s_blocksize_bits, | 3038 | sb->s_blocksize_bits, |
| 3039 | EXT4_MAX_BLOCK_LOG_SIZE); | 3039 | EXT4_MAX_BLOCK_LOG_SIZE); |
| 3040 | struct sg { | 3040 | DEFINE_RAW_FLEX(struct ext4_group_info, sg, bb_counters, |
| 3041 | struct ext4_group_info info; | 3041 | EXT4_MAX_BLOCK_LOG_SIZE + 2); |
| 3042 | ext4_grpblk_t counters[EXT4_MAX_BLOCK_LOG_SIZE + 2]; | ||
| 3043 | } sg; | ||
| 3044 | 3042 | ||
| 3045 | group--; | 3043 | group--; |
| 3046 | if (group == 0) | 3044 | if (group == 0) |
| @@ -3048,7 +3046,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) · 1 unmodified line | |||
| 3048 | " 2^0 2^1 2^2 2^3 2^4 2^5 2^6 " | 3046 | " 2^0 2^1 2^2 2^3 2^4 2^5 2^6 " |
| 3049 | " 2^7 2^8 2^9 2^10 2^11 2^12 2^13 ]\n"); | 3047 | " 2^7 2^8 2^9 2^10 2^11 2^12 2^13 ]\n"); |
| 3050 | 3048 | ||
| 3051 | i = (blocksize_bits + 2) * sizeof(sg.info.bb_counters[0]) + | 3049 | i = (blocksize_bits + 2) * sizeof(sg->bb_counters[0]) + |
| 3052 | sizeof(struct ext4_group_info); | 3050 | sizeof(struct ext4_group_info); |
| 3053 | 3051 | ||
| 3054 | grinfo = ext4_get_group_info(sb, group); | 3052 | grinfo = ext4_get_group_info(sb, group); |
| @@ -3068,14 +3066,14 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v) · 13 unmodified lines | |||
| 3068 | * We care only about free space counters in the group info and | 3066 | * We care only about free space counters in the group info and |
| 3069 | * these are safe to access even after the buddy has been unloaded | 3067 | * these are safe to access even after the buddy has been unloaded |
| 3070 | */ | 3068 | */ |
| 3071 | memcpy(&sg, grinfo, i); | 3069 | memcpy(sg, grinfo, i); |
| 3072 | seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg.info.bb_free, | 3070 | seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg->bb_free, |
| 3073 | sg.info.bb_fragments, sg.info.bb_first_free); | 3071 | sg->bb_fragments, sg->bb_first_free); |
| 3074 | for (i = 0; i <= 13; i++) | 3072 | for (i = 0; i <= 13; i++) |
| 3075 | seq_printf(seq, " %-5u", i <= blocksize_bits + 1 ? | 3073 | seq_printf(seq, " %-5u", i <= blocksize_bits + 1 ? |
| 3076 | sg.info.bb_counters[i] : 0); | 3074 | sg->bb_counters[i] : 0); |
| 3077 | seq_puts(seq, " ]"); | 3075 | seq_puts(seq, " ]"); |
| 3078 | if (EXT4_MB_GRP_BBITMAP_CORRUPT(&sg.info)) | 3076 | if (EXT4_MB_GRP_BBITMAP_CORRUPT(sg)) |
| 3079 | seq_puts(seq, " Block bitmap corrupted!"); | 3077 | seq_puts(seq, " Block bitmap corrupted!"); |
| 3080 | seq_putc(seq, '\n'); | 3078 | seq_putc(seq, '\n'); |
| 3081 | return 0; | 3079 | return 0; |
| @@ -1971,7 +1971,7 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, · 1970 unmodified lines | |||
| 1971 | * split it in half by count; each resulting block will have at least | 1971 | * split it in half by count; each resulting block will have at least |
| 1972 | * half the space free. | 1972 | * half the space free. |
| 1973 | */ | 1973 | */ |
| 1974 | if (i > 0) | 1974 | if (i >= 0) |
| 1975 | split = count - move; | 1975 | split = count - move; |
| 1976 | else | 1976 | else |
| 1977 | split = count/2; | 1977 | split = count/2; |