Commit bb7c241f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Pull ext4 fixes from Ted Ts'o:
 "Some ext4 bug fixes (mostly to address Syzbot reports)"

* tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: bail out of ext4_xattr_ibody_get() fails for any reason
  ext4: add bounds checking in get_max_inline_xattr_value_size()
  ext4: add indication of ro vs r/w mounts in the mount message
  ext4: fix deadlock when converting an inline directory in nojournal mode
  ext4: improve error recovery code paths in __ext4_remount()
  ext4: improve error handling from ext4_dirhash()
  ext4: don't clear SB_RDONLY when remounting r/w until quota is re-enabled
  ext4: check iomap type only if ext4_iomap_begin() does not fail
  ext4: avoid a potential slab-out-of-bounds in ext4_group_desc_csum
  ext4: fix data races when using cached status extents
  ext4: avoid deadlock in fs reclaim with page writeback
  ext4: fix invalid free tracking in ext4_xattr_move_to_block()
  ext4: remove a BUG_ON in ext4_mb_release_group_pa()
  ext4: allow ext4_get_group_info() to fail
  ext4: fix lockdep warning when enabling MMP
  ext4: fix WARNING in mb_find_extent
parents adfbf653 2a534e1d
Loading
Loading
Loading
Loading
+42 −1
Original line number Diff line number Diff line
@@ -305,6 +305,38 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb,
	return desc;
}

static ext4_fsblk_t ext4_valid_block_bitmap_padding(struct super_block *sb,
						    ext4_group_t block_group,
						    struct buffer_head *bh)
{
	ext4_grpblk_t next_zero_bit;
	unsigned long bitmap_size = sb->s_blocksize * 8;
	unsigned int offset = num_clusters_in_group(sb, block_group);

	if (bitmap_size <= offset)
		return 0;

	next_zero_bit = ext4_find_next_zero_bit(bh->b_data, bitmap_size, offset);

	return (next_zero_bit < bitmap_size ? next_zero_bit : 0);
}

struct ext4_group_info *ext4_get_group_info(struct super_block *sb,
					    ext4_group_t group)
{
	 struct ext4_group_info **grp_info;
	 long indexv, indexh;

	 if (unlikely(group >= EXT4_SB(sb)->s_groups_count)) {
		 ext4_error(sb, "invalid group %u", group);
		 return NULL;
	 }
	 indexv = group >> (EXT4_DESC_PER_BLOCK_BITS(sb));
	 indexh = group & ((EXT4_DESC_PER_BLOCK(sb)) - 1);
	 grp_info = sbi_array_rcu_deref(EXT4_SB(sb), s_group_info, indexv);
	 return grp_info[indexh];
}

/*
 * Return the block number which was discovered to be invalid, or 0 if
 * the block bitmap is valid.
@@ -379,7 +411,7 @@ static int ext4_validate_block_bitmap(struct super_block *sb,

	if (buffer_verified(bh))
		return 0;
	if (EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
	if (!grp || EXT4_MB_GRP_BBITMAP_CORRUPT(grp))
		return -EFSCORRUPTED;

	ext4_lock_group(sb, block_group);
@@ -402,6 +434,15 @@ static int ext4_validate_block_bitmap(struct super_block *sb,
					EXT4_GROUP_INFO_BBITMAP_CORRUPT);
		return -EFSCORRUPTED;
	}
	blk = ext4_valid_block_bitmap_padding(sb, block_group, bh);
	if (unlikely(blk != 0)) {
		ext4_unlock_group(sb, block_group);
		ext4_error(sb, "bg %u: block %llu: padding at end of block bitmap is not set",
			   block_group, blk);
		ext4_mark_group_bitmap_corrupted(sb, block_group,
						 EXT4_GROUP_INFO_BBITMAP_CORRUPT);
		return -EFSCORRUPTED;
	}
	set_buffer_verified(bh);
verified:
	ext4_unlock_group(sb, block_group);
+26 −13
Original line number Diff line number Diff line
@@ -1684,6 +1684,30 @@ static inline struct ext4_inode_info *EXT4_I(struct inode *inode)
	return container_of(inode, struct ext4_inode_info, vfs_inode);
}

static inline int ext4_writepages_down_read(struct super_block *sb)
{
	percpu_down_read(&EXT4_SB(sb)->s_writepages_rwsem);
	return memalloc_nofs_save();
}

static inline void ext4_writepages_up_read(struct super_block *sb, int ctx)
{
	memalloc_nofs_restore(ctx);
	percpu_up_read(&EXT4_SB(sb)->s_writepages_rwsem);
}

static inline int ext4_writepages_down_write(struct super_block *sb)
{
	percpu_down_write(&EXT4_SB(sb)->s_writepages_rwsem);
	return memalloc_nofs_save();
}

static inline void ext4_writepages_up_write(struct super_block *sb, int ctx)
{
	memalloc_nofs_restore(ctx);
	percpu_up_write(&EXT4_SB(sb)->s_writepages_rwsem);
}

static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
{
	return ino == EXT4_ROOT_INO ||
@@ -2625,6 +2649,8 @@ extern void ext4_check_blocks_bitmap(struct super_block *);
extern struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb,
						    ext4_group_t block_group,
						    struct buffer_head ** bh);
extern struct ext4_group_info *ext4_get_group_info(struct super_block *sb,
						   ext4_group_t group);
extern int ext4_should_retry_alloc(struct super_block *sb, int *retries);

extern struct buffer_head *ext4_read_block_bitmap_nowait(struct super_block *sb,
@@ -3232,19 +3258,6 @@ static inline void ext4_isize_set(struct ext4_inode *raw_inode, loff_t i_size)
	raw_inode->i_size_high = cpu_to_le32(i_size >> 32);
}

static inline
struct ext4_group_info *ext4_get_group_info(struct super_block *sb,
					    ext4_group_t group)
{
	 struct ext4_group_info **grp_info;
	 long indexv, indexh;
	 BUG_ON(group >= EXT4_SB(sb)->s_groups_count);
	 indexv = group >> (EXT4_DESC_PER_BLOCK_BITS(sb));
	 indexh = group & ((EXT4_DESC_PER_BLOCK(sb)) - 1);
	 grp_info = sbi_array_rcu_deref(EXT4_SB(sb), s_group_info, indexv);
	 return grp_info[indexh];
}

/*
 * Reading s_groups_count requires using smp_rmb() afterwards.  See
 * the locking protocol documented in the comments of ext4_group_add()
+13 −17
Original line number Diff line number Diff line
@@ -267,15 +267,13 @@ static void __es_find_extent_range(struct inode *inode,

	/* see if the extent has been cached */
	es->es_lblk = es->es_len = es->es_pblk = 0;
	if (tree->cache_es) {
		es1 = tree->cache_es;
		if (in_range(lblk, es1->es_lblk, es1->es_len)) {
	es1 = READ_ONCE(tree->cache_es);
	if (es1 && in_range(lblk, es1->es_lblk, es1->es_len)) {
		es_debug("%u cached by [%u/%u) %llu %x\n",
			 lblk, es1->es_lblk, es1->es_len,
			 ext4_es_pblock(es1), ext4_es_status(es1));
		goto out;
	}
	}

	es1 = __es_tree_search(&tree->root, lblk);

@@ -293,7 +291,7 @@ static void __es_find_extent_range(struct inode *inode,
	}

	if (es1 && matching_fn(es1)) {
		tree->cache_es = es1;
		WRITE_ONCE(tree->cache_es, es1);
		es->es_lblk = es1->es_lblk;
		es->es_len = es1->es_len;
		es->es_pblk = es1->es_pblk;
@@ -931,15 +929,13 @@ int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,

	/* find extent in cache firstly */
	es->es_lblk = es->es_len = es->es_pblk = 0;
	if (tree->cache_es) {
		es1 = tree->cache_es;
		if (in_range(lblk, es1->es_lblk, es1->es_len)) {
	es1 = READ_ONCE(tree->cache_es);
	if (es1 && in_range(lblk, es1->es_lblk, es1->es_len)) {
		es_debug("%u cached by [%u/%u)\n",
			 lblk, es1->es_lblk, es1->es_len);
		found = 1;
		goto out;
	}
	}

	node = tree->root.rb_node;
	while (node) {
+5 −1
Original line number Diff line number Diff line
@@ -277,7 +277,11 @@ static int __ext4fs_dirhash(const struct inode *dir, const char *name, int len,
	}
	default:
		hinfo->hash = 0;
		return -1;
		hinfo->minor_hash = 0;
		ext4_warning(dir->i_sb,
			     "invalid/unsupported hash tree version %u",
			     hinfo->hash_version);
		return -EINVAL;
	}
	hash = hash & ~1;
	if (hash == (EXT4_HTREE_EOF_32BIT << 1))
+8 −4
Original line number Diff line number Diff line
@@ -91,7 +91,7 @@ static int ext4_validate_inode_bitmap(struct super_block *sb,

	if (buffer_verified(bh))
		return 0;
	if (EXT4_MB_GRP_IBITMAP_CORRUPT(grp))
	if (!grp || EXT4_MB_GRP_IBITMAP_CORRUPT(grp))
		return -EFSCORRUPTED;

	ext4_lock_group(sb, block_group);
@@ -293,7 +293,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
	}
	if (!(sbi->s_mount_state & EXT4_FC_REPLAY)) {
		grp = ext4_get_group_info(sb, block_group);
		if (unlikely(EXT4_MB_GRP_IBITMAP_CORRUPT(grp))) {
		if (!grp || unlikely(EXT4_MB_GRP_IBITMAP_CORRUPT(grp))) {
			fatal = -EFSCORRUPTED;
			goto error_return;
		}
@@ -1046,7 +1046,7 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,
			 * Skip groups with already-known suspicious inode
			 * tables
			 */
			if (EXT4_MB_GRP_IBITMAP_CORRUPT(grp))
			if (!grp || EXT4_MB_GRP_IBITMAP_CORRUPT(grp))
				goto next_group;
		}

@@ -1183,6 +1183,10 @@ struct inode *__ext4_new_inode(struct mnt_idmap *idmap,

		if (!(sbi->s_mount_state & EXT4_FC_REPLAY)) {
			grp = ext4_get_group_info(sb, group);
			if (!grp) {
				err = -EFSCORRUPTED;
				goto out;
			}
			down_read(&grp->alloc_sem); /*
						     * protect vs itable
						     * lazyinit
@@ -1526,7 +1530,7 @@ int ext4_init_inode_table(struct super_block *sb, ext4_group_t group,
	}

	gdp = ext4_get_group_desc(sb, group, &group_desc_bh);
	if (!gdp)
	if (!gdp || !grp)
		goto out;

	/*
Loading