Commit 115fada1 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull btrfs fixes from David Sterba:

 - fix missing btrfs_path release after printing a relocation error
   message

 - fix extent changeset leak on mmap write after failure to reserve
   metadata

 - fix fs devices list structure freeing, it could be potentially leaked
   under some circumstances

 - tree log fixes:
     - fix incremental directory logging where inodes for new dentries
       were incorrectly skipped
     - don't log conflicting inode if it's a directory moved in the
       current transaction

 - regression fixes:
     - fix incorrect btrfs_path freeing when it's auto-cleaned
     - revert commit simplifying preallocation of temporary structures
       in qgroup functions, some cases were not handled properly

* tag 'for-6.19-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: fix changeset leak on mmap write after failure to reserve metadata
  btrfs: fix memory leak of fs_devices in degraded seed device path
  btrfs: fix a potential path leak in print_data_reloc_error()
  Revert "btrfs: add ASSERTs on prealloc in qgroup functions"
  btrfs: do not skip logging new dentries when logging a new name
  btrfs: don't log conflicting inode if it's a dir moved in the current transaction
  btrfs: tests: fix double btrfs_path free in remove_extent_ref()
parents dbf89321 37343524
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -2019,13 +2019,14 @@ static vm_fault_t btrfs_page_mkwrite(struct vm_fault *vmf)
	else
		btrfs_delalloc_release_space(inode, data_reserved, page_start,
					     reserved_space, true);
	extent_changeset_free(data_reserved);
out_noreserve:
	if (only_release_metadata)
		btrfs_check_nocow_unlock(inode);

	sb_end_pagefault(inode->vfs_inode.i_sb);

	extent_changeset_free(data_reserved);

	if (ret < 0)
		return vmf_error(ret);

+1 −0
Original line number Diff line number Diff line
@@ -256,6 +256,7 @@ static void print_data_reloc_error(const struct btrfs_inode *inode, u64 file_off
	if (ret < 0) {
		btrfs_err_rl(fs_info, "failed to lookup extent item for logical %llu: %d",
			     logical, ret);
		btrfs_release_path(&path);
		return;
	}
	eb = path.nodes[0];
+4 −23
Original line number Diff line number Diff line
@@ -1243,14 +1243,7 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
		btrfs_end_transaction(trans);
	else if (trans)
		ret = btrfs_end_transaction(trans);

	/*
	 * At this point we either failed at allocating prealloc, or we
	 * succeeded and passed the ownership to it to add_qgroup_rb(). In any
	 * case, this needs to be NULL or there is something wrong.
	 */
	ASSERT(prealloc == NULL);

	kfree(prealloc);
	return ret;
}

@@ -1682,12 +1675,7 @@ int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid)
	ret = btrfs_sysfs_add_one_qgroup(fs_info, qgroup);
out:
	mutex_unlock(&fs_info->qgroup_ioctl_lock);
	/*
	 * At this point we either failed at allocating prealloc, or we
	 * succeeded and passed the ownership to it to add_qgroup_rb(). In any
	 * case, this needs to be NULL or there is something wrong.
	 */
	ASSERT(prealloc == NULL);
	kfree(prealloc);
	return ret;
}

@@ -3279,7 +3267,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
	struct btrfs_root *quota_root;
	struct btrfs_qgroup *srcgroup;
	struct btrfs_qgroup *dstgroup;
	struct btrfs_qgroup *prealloc = NULL;
	struct btrfs_qgroup *prealloc;
	struct btrfs_qgroup_list **qlist_prealloc = NULL;
	bool free_inherit = false;
	bool need_rescan = false;
@@ -3520,14 +3508,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
	}
	if (free_inherit)
		kfree(inherit);

	/*
	 * At this point we either failed at allocating prealloc, or we
	 * succeeded and passed the ownership to it to add_qgroup_rb(). In any
	 * case, this needs to be NULL or there is something wrong.
	 */
	ASSERT(prealloc == NULL);

	kfree(prealloc);
	return ret;
}

+0 −1
Original line number Diff line number Diff line
@@ -187,7 +187,6 @@ static int remove_extent_ref(struct btrfs_root *root, u64 bytenr,
	ret = btrfs_search_slot(&trans, root, &key, path, -1, 1);
	if (ret) {
		test_err("couldn't find backref %d", ret);
		btrfs_free_path(path);
		return ret;
	}
	btrfs_del_item(&trans, root, path);
+38 −8
Original line number Diff line number Diff line
@@ -5865,14 +5865,6 @@ static int log_new_dir_dentries(struct btrfs_trans_handle *trans,
	struct btrfs_inode *curr_inode = start_inode;
	int ret = 0;

	/*
	 * If we are logging a new name, as part of a link or rename operation,
	 * don't bother logging new dentries, as we just want to log the names
	 * of an inode and that any new parents exist.
	 */
	if (ctx->logging_new_name)
		return 0;

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;
@@ -6051,6 +6043,33 @@ static int conflicting_inode_is_dir(struct btrfs_root *root, u64 ino,
	return ret;
}

static bool can_log_conflicting_inode(const struct btrfs_trans_handle *trans,
				      const struct btrfs_inode *inode)
{
	if (!S_ISDIR(inode->vfs_inode.i_mode))
		return true;

	if (inode->last_unlink_trans < trans->transid)
		return true;

	/*
	 * If this is a directory and its unlink_trans is not from a past
	 * transaction then we must fallback to a transaction commit in order
	 * to avoid getting a directory with 2 hard links after log replay.
	 *
	 * This happens if a directory A is renamed, moved from one parent
	 * directory to another one, a new file is created in the old parent
	 * directory with the old name of our directory A, the new file is
	 * fsynced, then we moved the new file to some other parent directory
	 * and fsync again the new file. This results in a log tree where we
	 * logged that directory A existed, with the INODE_REF item for the
	 * new location but without having logged its old parent inode, so
	 * that on log replay we add a new link for the new location but the
	 * old link remains, resulting in a link count of 2.
	 */
	return false;
}

static int add_conflicting_inode(struct btrfs_trans_handle *trans,
				 struct btrfs_root *root,
				 struct btrfs_path *path,
@@ -6154,6 +6173,11 @@ static int add_conflicting_inode(struct btrfs_trans_handle *trans,
		return 0;
	}

	if (!can_log_conflicting_inode(trans, inode)) {
		btrfs_add_delayed_iput(inode);
		return BTRFS_LOG_FORCE_COMMIT;
	}

	btrfs_add_delayed_iput(inode);

	ino_elem = kmalloc(sizeof(*ino_elem), GFP_NOFS);
@@ -6218,6 +6242,12 @@ static int log_conflicting_inodes(struct btrfs_trans_handle *trans,
				break;
			}

			if (!can_log_conflicting_inode(trans, inode)) {
				btrfs_add_delayed_iput(inode);
				ret = BTRFS_LOG_FORCE_COMMIT;
				break;
			}

			/*
			 * Always log the directory, we cannot make this
			 * conditional on need_log_inode() because the directory
Loading