Commit 5aaaedb0 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'ext4_for_linus-6.15-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

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
parents 051ea726 94824ac9
Loading
Loading
Loading
Loading
+14 −6
Original line number Diff line number Diff line
@@ -328,9 +328,13 @@ The ext4 superblock is laid out as follows in
     - s_checksum_type
     - Metadata checksum algorithm type. The only valid value is 1 (crc32c).
   * - 0x176
     - __le16
     - s_reserved_pad
     -
     - \_\_u8
     - s\_encryption\_level
     - Versioning level for encryption.
   * - 0x177
     - \_\_u8
     - s\_reserved\_pad
     - Padding to next 32bits.
   * - 0x178
     - __le64
     - s_kbytes_written
@@ -466,9 +470,13 @@ The ext4 superblock is laid out as follows in
     - s_last_error_time_hi
     - Upper 8 bits of the s_last_error_time field.
   * - 0x27A
     - __u8
     - s_pad[2]
     - Zero padding.
     - \_\_u8
     - s\_first\_error\_errcode
     -
   * - 0x27B
     - \_\_u8
     - s\_last\_error\_errcode
     -
   * - 0x27C
     - __le16
     - s_encoding
+2 −3
Original line number Diff line number Diff line
@@ -351,10 +351,9 @@ int ext4_check_blockref(const char *function, unsigned int line,
{
	__le32 *bref = p;
	unsigned int blk;
	journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;

	if (ext4_has_feature_journal(inode->i_sb) &&
	    (inode->i_ino ==
	     le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum)))
	if (journal && inode == journal->j_inode)
		return 0;

	while (bref < p+max) {
+52 −23
Original line number Diff line number Diff line
@@ -386,10 +386,11 @@ static int __check_block_validity(struct inode *inode, const char *func,
				unsigned int line,
				struct ext4_map_blocks *map)
{
	if (ext4_has_feature_journal(inode->i_sb) &&
	    (inode->i_ino ==
	     le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum)))
	journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;

	if (journal && inode == journal->j_inode)
		return 0;

	if (!ext4_inode_block_valid(inode, map->m_pblk, map->m_len)) {
		ext4_error_inode(inode, func, line, map->m_pblk,
				 "lblock %lu mapped to illegal pblock %llu "
@@ -4724,22 +4725,43 @@ static inline void ext4_inode_set_iversion_queried(struct inode *inode, u64 val)
		inode_set_iversion_queried(inode, val);
}

static const char *check_igot_inode(struct inode *inode, ext4_iget_flags flags)

static int check_igot_inode(struct inode *inode, ext4_iget_flags flags,
			    const char *function, unsigned int line)
{
	const char *err_str;

	if (flags & EXT4_IGET_EA_INODE) {
		if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL))
			return "missing EA_INODE flag";
		if (!(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) {
			err_str = "missing EA_INODE flag";
			goto error;
		}
		if (ext4_test_inode_state(inode, EXT4_STATE_XATTR) ||
		    EXT4_I(inode)->i_file_acl)
			return "ea_inode with extended attributes";
		    EXT4_I(inode)->i_file_acl) {
			err_str = "ea_inode with extended attributes";
			goto error;
		}
	} else {
		if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL))
			return "unexpected EA_INODE flag";
		if ((EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)) {
			/*
			 * open_by_handle_at() could provide an old inode number
			 * that has since been reused for an ea_inode; this does
			 * not indicate filesystem corruption
			 */
			if (flags & EXT4_IGET_HANDLE)
				return -ESTALE;
			err_str = "unexpected EA_INODE flag";
			goto error;
		}
	}
	if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD)) {
		err_str = "unexpected bad inode w/o EXT4_IGET_BAD";
		goto error;
	}
	if (is_bad_inode(inode) && !(flags & EXT4_IGET_BAD))
		return "unexpected bad inode w/o EXT4_IGET_BAD";
	return NULL;
	return 0;

error:
	ext4_error_inode(inode, function, line, 0, err_str);
	return -EFSCORRUPTED;
}

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,
	struct ext4_inode_info *ei;
	struct ext4_super_block *es = EXT4_SB(sb)->s_es;
	struct inode *inode;
	const char *err_str;
	journal_t *journal = EXT4_SB(sb)->s_journal;
	long ret;
	loff_t size;
@@ -4780,10 +4801,10 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
	if (!inode)
		return ERR_PTR(-ENOMEM);
	if (!(inode->i_state & I_NEW)) {
		if ((err_str = check_igot_inode(inode, flags)) != NULL) {
			ext4_error_inode(inode, function, line, 0, err_str);
		ret = check_igot_inode(inode, flags, function, line);
		if (ret) {
			iput(inode);
			return ERR_PTR(-EFSCORRUPTED);
			return ERR_PTR(ret);
		}
		return inode;
	}
@@ -5065,13 +5086,21 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
		ret = -EFSCORRUPTED;
		goto bad_inode;
	}
	if ((err_str = check_igot_inode(inode, flags)) != NULL) {
		ext4_error_inode(inode, function, line, 0, err_str);
		ret = -EFSCORRUPTED;
		goto bad_inode;
	ret = check_igot_inode(inode, flags, function, line);
	/*
	 * -ESTALE here means there is nothing inherently wrong with the inode,
	 * it's just not an inode we can return for an fhandle lookup.
	 */
	if (ret == -ESTALE) {
		brelse(iloc.bh);
		unlock_new_inode(inode);
		iput(inode);
		return ERR_PTR(-ESTALE);
	}

	if (ret)
		goto bad_inode;
	brelse(iloc.bh);

	unlock_new_inode(inode);
	return inode;

+8 −10
Original line number Diff line number Diff line
@@ -3037,10 +3037,8 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
	unsigned char blocksize_bits = min_t(unsigned char,
					     sb->s_blocksize_bits,
					     EXT4_MAX_BLOCK_LOG_SIZE);
	struct sg {
		struct ext4_group_info info;
		ext4_grpblk_t counters[EXT4_MAX_BLOCK_LOG_SIZE + 2];
	} sg;
	DEFINE_RAW_FLEX(struct ext4_group_info, sg, bb_counters,
			EXT4_MAX_BLOCK_LOG_SIZE + 2);

	group--;
	if (group == 0)
@@ -3048,7 +3046,7 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
			      " 2^0   2^1   2^2   2^3   2^4   2^5   2^6  "
			      " 2^7   2^8   2^9   2^10  2^11  2^12  2^13  ]\n");

	i = (blocksize_bits + 2) * sizeof(sg.info.bb_counters[0]) +
	i = (blocksize_bits + 2) * sizeof(sg->bb_counters[0]) +
		sizeof(struct ext4_group_info);

	grinfo = ext4_get_group_info(sb, group);
@@ -3068,14 +3066,14 @@ static int ext4_mb_seq_groups_show(struct seq_file *seq, void *v)
	 * We care only about free space counters in the group info and
	 * these are safe to access even after the buddy has been unloaded
	 */
	memcpy(&sg, grinfo, i);
	seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg.info.bb_free,
			sg.info.bb_fragments, sg.info.bb_first_free);
	memcpy(sg, grinfo, i);
	seq_printf(seq, "#%-5u: %-5u %-5u %-5u [", group, sg->bb_free,
			sg->bb_fragments, sg->bb_first_free);
	for (i = 0; i <= 13; i++)
		seq_printf(seq, " %-5u", i <= blocksize_bits + 1 ?
				sg.info.bb_counters[i] : 0);
				sg->bb_counters[i] : 0);
	seq_puts(seq, " ]");
	if (EXT4_MB_GRP_BBITMAP_CORRUPT(&sg.info))
	if (EXT4_MB_GRP_BBITMAP_CORRUPT(sg))
		seq_puts(seq, " Block bitmap corrupted!");
	seq_putc(seq, '\n');
	return 0;
+1 −1
Original line number Diff line number Diff line
@@ -1971,7 +1971,7 @@ static struct ext4_dir_entry_2 *do_split(handle_t *handle, struct inode *dir,
	 * split it in half by count; each resulting block will have at least
	 * half the space free.
	 */
	if (i > 0)
	if (i >= 0)
		split = count - move;
	else
		split = count/2;