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 | 328 | - s_checksum_type |
| 329 | 329 | - Metadata checksum algorithm type. The only valid value is 1 (crc32c). |
| 330 | 330 | * - 0x176 |
| 331 | − - __le16 | |
| 332 | − - s_reserved_pad | |
| 333 | − - | |
| 331 | + - \_\_u8 | |
| 332 | + - s\_encryption\_level | |
| 333 | + - Versioning level for encryption. | |
| 334 | + * - 0x177 | |
| 335 | + - \_\_u8 | |
| 336 | + - s\_reserved\_pad | |
| 337 | + - Padding to next 32bits. | |
| 334 | 338 | * - 0x178 |
| 335 | 339 | - __le64 |
| 336 | 340 | - s_kbytes_written |
| @@ -466,9 +470,13 @@ The ext4 superblock is laid out as follows in · 129 unmodified lines | ||
| 466 | 470 | - s_last_error_time_hi |
| 467 | 471 | - Upper 8 bits of the s_last_error_time field. |
| 468 | 472 | * - 0x27A |
| 469 | − - __u8 | |
| 470 | − - s_pad[2] | |
| 471 | − - Zero padding. | |
| 473 | + - \_\_u8 | |
| 474 | + - s\_first\_error\_errcode | |
| 475 | + - | |
| 476 | + * - 0x27B | |
| 477 | + - \_\_u8 | |
| 478 | + - s\_last\_error\_errcode | |
| 479 | + - | |
| 472 | 480 | * - 0x27C |
| 473 | 481 | - __le16 |
| 474 | 482 | - s_encoding |
| @@ -351,10 +351,9 @@ int ext4_check_blockref(const char *function, unsigned int line, · 350 unmodified lines | ||
| 351 | 351 | { |
| 352 | 352 | __le32 *bref = p; |
| 353 | 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 | − (inode->i_ino == | |
| 357 | − le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) | |
| 356 | + if (journal && inode == journal->j_inode) | |
| 358 | 357 | return 0; |
| 359 | 358 | |
| 360 | 359 | while (bref < p+max) { |
| @@ -386,10 +386,11 @@ static int __check_block_validity(struct inode *inode, const char *func, · 385 unmodified lines | ||
| 386 | 386 | unsigned int line, |
| 387 | 387 | struct ext4_map_blocks *map) |
| 388 | 388 | { |
| 389 | − if (ext4_has_feature_journal(inode->i_sb) && | |
| 390 | − (inode->i_ino == | |
| 391 | − le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) | |
| 389 | + journal_t *journal = EXT4_SB(inode->i_sb)->s_journal; | |
| 390 | + | |
| 391 | + if (journal && inode == journal->j_inode) | |
| 392 | 392 | return 0; |
| 393 | + | |
| 393 | 394 | if (!ext4_inode_block_valid(inode, map->m_pblk, map->m_len)) { |
| 394 | 395 | ext4_error_inode(inode, func, line, map->m_pblk, |
| 395 | 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 | 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 | − | |
| 4728 | +static int check_igot_inode(struct inode *inode, ext4_iget_flags flags, | |
| 4729 | + const char *function, unsigned int line) | |
| 4729 | 4730 | { |
| 4731 | + const char *err_str; | |
| 4732 | + | |
| 4730 | 4733 | if (flags & EXT4_IGET_EA_INODE) { |
| 4731 | − if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) | |
| 4732 | − return "missing EA_INODE flag"; | |
| 4734 | + if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) { | |
| 4735 | + err_str = "missing EA_INODE flag"; | |
| 4736 | + goto error; | |
| 4737 | + } | |
| 4733 | 4738 | if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) || |
| 4734 | − EXT4_I(inode)->i_file_acl) | |
| 4735 | − return "ea_inode with extended attributes"; | |
| 4739 | + EXT4_I(inode)->i_file_acl) { | |
| 4740 | + err_str = "ea_inode with extended attributes"; | |
| 4741 | + goto error; | |
| 4742 | + } | |
| 4736 | 4743 | } else { |
| 4737 | − if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) | |
| 4738 | − return "unexpected EA_INODE flag"; | |
| 4744 | + if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) { | |
| 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)) | |
| 4741 | − return "unexpected bad inode w/o EXT4_IGET_BAD"; | |
| 4742 | − return NULL; | |
| 4760 | + return 0; | |
| 4761 | + | |
| 4762 | +error: | |
| 4763 | + ext4_error_inode(inode, function, line, 0, err_str); | |
| 4764 | + return -EFSCORRUPTED; | |
| 4743 | 4765 | } |
| 4744 | 4766 | |
| 4745 | 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 | 4773 | struct ext4_inode_info *ei; |
| 4752 | 4774 | struct ext4_super_block *es = EXT4_SB(sb)->s_es; |
| 4753 | 4775 | struct inode *inode; |
| 4754 | − const char *err_str; | |
| 4755 | 4776 | journal_t *journal = EXT4_SB(sb)->s_journal; |
| 4756 | 4777 | long ret; |
| 4757 | 4778 | loff_t size; |
| @@ -4780,10 +4801,10 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, · 22 unmodified lines | ||
| 4780 | 4801 | if (!inode) |
| 4781 | 4802 | return ERR_PTR(-ENOMEM); |
| 4782 | 4803 | if (!(inode->i_state & I_NEW)) { |
| 4783 | − if ((err_str = check_igot_inode(inode, flags)) != NULL) { | |
| 4784 | − ext4_error_inode(inode, function, line, 0, err_str); | |
| 4804 | + ret = check_igot_inode(inode, flags, function, line); | |
| 4805 | + if (ret) { | |
| 4785 | 4806 | iput(inode); |
| 4786 | − return ERR_PTR(-EFSCORRUPTED); | |
| 4807 | + return ERR_PTR(ret); | |
| 4787 | 4808 | } |
| 4788 | 4809 | return inode; |
| 4789 | 4810 | } |
| @@ -5065,13 +5086,21 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, · 275 unmodified lines | ||
| 5065 | 5086 | ret = -EFSCORRUPTED; |
| 5066 | 5087 | goto bad_inode; |
| 5067 | 5088 | } |
| 5068 | − if ((err_str = check_igot_inode(inode, flags)) != NULL) { | |
| 5069 | − ext4_error_inode(inode, function, line, 0, err_str); | |
| 5070 | − ret = -EFSCORRUPTED; | |
| 5071 | − goto bad_inode; | |
| 5089 | + ret = check_igot_inode(inode, flags, function, line); | |
| 5090 | + /* | |
| 5091 | + * -ESTALE here means there is nothing inherently wrong with the 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 | 5102 | brelse(iloc.bh); |
| 5103 | + | |
| 5075 | 5104 | unlock_new_inode(inode); |
| 5076 | 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 | 3037 | unsigned char blocksize_bits = min_t(unsigned char, |
| 3038 | 3038 | sb->s_blocksize_bits, |
| 3039 | 3039 | EXT4_MAX_BLOCK_LOG_SIZE); |
| 3040 | − struct sg { | |
| 3041 | − struct ext4_group_info info; | |
| 3042 | − ext4_grpblk_t counters[EXT4_MAX_BLOCK_LOG_SIZE + 2]; | |
| 3043 | − } sg; | |
| 3040 | + DEFINE_RAW_FLEX(struct ext4_group_info, sg, bb_counters, | |
| 3041 | + EXT4_MAX_BLOCK_LOG_SIZE + 2); | |
| 3044 | 3042 | |
| 3045 | 3043 | group--; |
| 3046 | 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 | 3046 | " 2^0 2^1 2^2 2^3 2^4 2^5 2^6 " |
| 3049 | 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 | 3050 | sizeof(struct ext4_group_info); |
| 3053 | 3051 | |
| 3054 | 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 | 3066 | * We care only about free space counters in the group info and |
| 3069 | 3067 | * these are safe to access even after the buddy has been unloaded |
| 3070 | 3068 | */ |
| 3071 | − memcpy(&sg, grinfo, i); | |
| 3072 | − seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg.info.bb_free, | |
| 3073 | − sg.info.bb_fragments, sg.info.bb_first_free); | |
| 3069 | + memcpy(sg, grinfo, i); | |
| 3070 | + seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg->bb_free, | |
| 3071 | + sg->bb_fragments, sg->bb_first_free); | |
| 3074 | 3072 | for (i = 0; i <= 13; i++) |
| 3075 | 3073 | seq_printf(seq, " %-5u", i <= blocksize_bits + 1 ? |
| 3076 | − sg.info.bb_counters[i] : 0); | |
| 3074 | + sg->bb_counters[i] : 0); | |
| 3077 | 3075 | seq_puts(seq, " ]"); |
| 3078 | − if (EXT4_MB_GRP_BBITMAP_CORRUPT(&sg.info)) | |
| 3076 | + if (EXT4_MB_GRP_BBITMAP_CORRUPT(sg)) | |
| 3079 | 3077 | seq_puts(seq, " Block bitmap corrupted!"); |
| 3080 | 3078 | seq_putc(seq, '\n'); |
| 3081 | 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 | 1971 | * split it in half by count; each resulting block will have at least |
| 1972 | 1972 | * half the space free. |
| 1973 | 1973 | */ |
| 1974 | − if (i > 0) | |
| 1974 | + if (i >= 0) | |
| 1975 | 1975 | split = count - move; |
| 1976 | 1976 | else |
| 1977 | 1977 | split = count/2; |