Commit b51ad677 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull btrfs fixes from David Sterba:
 "A few more fixes. There's one that stands out in size as it fixes an
  edge case in fsync.

   - fix issue on fsync where file with zero size appears as a non-zero
     after log replay

   - in zlib compression, handle a crash when data alignment causes
     folio reference issues

   - fix possible crash with enabled tracepoints on a overlayfs mount

   - handle device stats update error

   - on zoned filesystems, fix kobject leak on sub-block groups

   - fix super block offset in an error message in validation"

* tag 'for-7.0-rc5-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: fix lost error when running device stats on multiple devices fs
  btrfs: tracepoints: get correct superblock from dentry in event btrfs_sync_file()
  btrfs: zlib: handle page aligned compressed size correctly
  btrfs: fix leak of kobject name for sub-group space_info
  btrfs: fix zero size inode with non-zero size after log replay
  btrfs: fix super block offset in error message in btrfs_validate_super()
parents 0bcb517f 1c37d896
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -4583,7 +4583,7 @@ static void check_removing_space_info(struct btrfs_space_info *space_info)
		for (int i = 0; i < BTRFS_SPACE_INFO_SUB_GROUP_MAX; i++) {
			if (space_info->sub_group[i]) {
				check_removing_space_info(space_info->sub_group[i]);
				kfree(space_info->sub_group[i]);
				btrfs_sysfs_remove_space_info(space_info->sub_group[i]);
				space_info->sub_group[i] = NULL;
			}
		}
+2 −2
Original line number Diff line number Diff line
@@ -2531,8 +2531,8 @@ int btrfs_validate_super(const struct btrfs_fs_info *fs_info,

	if (mirror_num >= 0 &&
	    btrfs_super_bytenr(sb) != btrfs_sb_offset(mirror_num)) {
		btrfs_err(fs_info, "super offset mismatch %llu != %u",
			  btrfs_super_bytenr(sb), BTRFS_SUPER_INFO_OFFSET);
		btrfs_err(fs_info, "super offset mismatch %llu != %llu",
			  btrfs_super_bytenr(sb), btrfs_sb_offset(mirror_num));
		ret = -EINVAL;
	}

+65 −33
Original line number Diff line number Diff line
@@ -4616,21 +4616,32 @@ static void fill_inode_item(struct btrfs_trans_handle *trans,
			    struct inode *inode, bool log_inode_only,
			    u64 logged_isize)
{
	u64 gen = BTRFS_I(inode)->generation;
	u64 flags;

	if (log_inode_only) {
		/* set the generation to zero so the recover code
		 * can tell the difference between an logging
		 * just to say 'this inode exists' and a logging
		 * to say 'update this inode with these values'
		/*
		 * Set the generation to zero so the recover code can tell the
		 * difference between a logging just to say 'this inode exists'
		 * and a logging to say 'update this inode with these values'.
		 * But only if the inode was not already logged before.
		 * We access ->logged_trans directly since it was already set
		 * up in the call chain by btrfs_log_inode(), and data_race()
		 * to avoid false alerts from KCSAN and since it was set already
		 * and one can set it to 0 since that only happens on eviction
		 * and we are holding a ref on the inode.
		 */
		btrfs_set_inode_generation(leaf, item, 0);
		ASSERT(data_race(BTRFS_I(inode)->logged_trans) > 0);
		if (data_race(BTRFS_I(inode)->logged_trans) < trans->transid)
			gen = 0;

		btrfs_set_inode_size(leaf, item, logged_isize);
	} else {
		btrfs_set_inode_generation(leaf, item, BTRFS_I(inode)->generation);
		btrfs_set_inode_size(leaf, item, inode->i_size);
	}

	btrfs_set_inode_generation(leaf, item, gen);

	btrfs_set_inode_uid(leaf, item, i_uid_read(inode));
	btrfs_set_inode_gid(leaf, item, i_gid_read(inode));
	btrfs_set_inode_mode(leaf, item, inode->i_mode);
@@ -5448,41 +5459,62 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,
	return 0;
}

static int logged_inode_size(struct btrfs_root *log, struct btrfs_inode *inode,
static int get_inode_size_to_log(struct btrfs_trans_handle *trans,
				 struct btrfs_inode *inode,
				 struct btrfs_path *path, u64 *size_ret)
{
	struct btrfs_key key;
	struct btrfs_inode_item *item;
	int ret;

	key.objectid = btrfs_ino(inode);
	key.type = BTRFS_INODE_ITEM_KEY;
	key.offset = 0;

	ret = btrfs_search_slot(NULL, log, &key, path, 0, 0);
	if (ret < 0) {
		return ret;
	} else if (ret > 0) {
		*size_ret = 0;
	/*
	 * Our caller called inode_logged(), so logged_trans is up to date.
	 * Use data_race() to silence any warning from KCSAN. Once logged_trans
	 * is set, it can only be reset to 0 after inode eviction.
	 */
	if (data_race(inode->logged_trans) == trans->transid) {
		ret = btrfs_search_slot(NULL, inode->root->log_root, &key, path, 0, 0);
	} else if (inode->generation < trans->transid) {
		path->search_commit_root = true;
		path->skip_locking = true;
		ret = btrfs_search_slot(NULL, inode->root, &key, path, 0, 0);
		path->search_commit_root = false;
		path->skip_locking = false;

	} else {
		struct btrfs_inode_item *item;
		*size_ret = 0;
		return 0;
	}

	/*
	 * If the inode was logged before or is from a past transaction, then
	 * its inode item must exist in the log root or in the commit root.
	 */
	ASSERT(ret <= 0);
	if (WARN_ON_ONCE(ret > 0))
		ret = -ENOENT;

	if (ret < 0)
		return ret;

	item = btrfs_item_ptr(path->nodes[0], path->slots[0],
			      struct btrfs_inode_item);
	*size_ret = btrfs_inode_size(path->nodes[0], item);
	/*
		 * If the in-memory inode's i_size is smaller then the inode
		 * size stored in the btree, return the inode's i_size, so
		 * that we get a correct inode size after replaying the log
		 * when before a power failure we had a shrinking truncate
		 * followed by addition of a new name (rename / new hard link).
		 * Otherwise return the inode size from the btree, to avoid
		 * data loss when replaying a log due to previously doing a
		 * write that expands the inode's size and logging a new name
		 * immediately after.
	 * If the in-memory inode's i_size is smaller then the inode size stored
	 * in the btree, return the inode's i_size, so that we get a correct
	 * inode size after replaying the log when before a power failure we had
	 * a shrinking truncate followed by addition of a new name (rename / new
	 * hard link). Otherwise return the inode size from the btree, to avoid
	 * data loss when replaying a log due to previously doing a write that
	 * expands the inode's size and logging a new name immediately after.
	 */
	if (*size_ret > inode->vfs_inode.i_size)
		*size_ret = inode->vfs_inode.i_size;
	}

	btrfs_release_path(path);
	return 0;
@@ -6996,7 +7028,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
			ret = drop_inode_items(trans, log, path, inode,
					       BTRFS_XATTR_ITEM_KEY);
	} else {
		if (inode_only == LOG_INODE_EXISTS && ctx->logged_before) {
		if (inode_only == LOG_INODE_EXISTS) {
			/*
			 * Make sure the new inode item we write to the log has
			 * the same isize as the current one (if it exists).
@@ -7010,7 +7042,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
			 * (zeroes), as if an expanding truncate happened,
			 * instead of getting a file of 4Kb only.
			 */
			ret = logged_inode_size(log, inode, path, &logged_isize);
			ret = get_inode_size_to_log(trans, inode, path, &logged_isize);
			if (ret)
				goto out_unlock;
		}
+3 −2
Original line number Diff line number Diff line
@@ -8099,7 +8099,8 @@ int btrfs_run_dev_stats(struct btrfs_trans_handle *trans)
		smp_rmb();

		ret = update_dev_stat_item(trans, device);
		if (!ret)
		if (ret)
			break;
		atomic_sub(stats_cnt, &device->dev_stats_ccnt);
	}
	mutex_unlock(&fs_devices->device_list_mutex);
+3 −1
Original line number Diff line number Diff line
@@ -308,7 +308,9 @@ int zlib_compress_bio(struct list_head *ws, struct compressed_bio *cb)
	}
	/* Queue the remaining part of the folio. */
	if (workspace->strm.total_out > bio->bi_iter.bi_size) {
		u32 cur_len = offset_in_folio(out_folio, workspace->strm.total_out);
		const u32 cur_len = workspace->strm.total_out - bio->bi_iter.bi_size;

		ASSERT(cur_len <= folio_size(out_folio));

		if (!bio_add_folio(bio, out_folio, cur_len, 0)) {
			ret = -E2BIG;
Loading