All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2
@ 2026-10-30  4:15 Qu Wenruo
  2026-10-30  4:15 ` [PATCH 01/12] btrfs-progs: kernel-shared: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths Qu Wenruo
                   ` (12 more replies)
  0 siblings, 13 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

This is the part 2, focusing on the on-stack btrfs_path.

Unlike heap-allocated btrfs_path, which needs proper btrfs_free_path()
call, or the btrfs_path itself along with the held ebs will leak.

The on-stack one only leaks the held ebs, but still we should fix them.

Just by the amount of code, one should know this is largely generated by
LLM, although I spent more time drafting the plan and review the final
patch.

There are some extra notes other than just converting existing
"btrfs_path" on-stack declaration to BTRFS_PATH_AUTO_RELEASE:

- If a label is only for cleanup, remove the label when possible
  As a lot of call sites can directly return without jumping to the
  cleanup label.

  Also cleanup some "ret = x; return ret;" patterns.

- Keep dedicated btrfs_release_path() if needed
  As our C code is not fully aligning the path lifespan with the scope,
  we still need to manually release path halfway for certain call sites.

  E.g. before calling btrfs_commit_transaction(),
  btrfs_abort_transaction(), btrfs_search_slot(), close_ctree() or
  before lowmem mode modifying the tree.

  Releasing the path before btrfs_commit_transaction() is to follow the
  kernel convension. There are a lot of existing call sites breaking it,
  so here we do not intentionally fix them.

  This will require a dedicated change in the future.

- Reduce the lifespan of btrfs_path when possible
  E.g. the path is only utilized inside a loop or if () branch, declare
  path there.

This applies to patch 01~08, which are all straightforward conversion
with minor cleanups.

After that, the code review process exposed some other bugs in the
touched functions, which are all pretty simple and low severity ones.
Patch 09~12 are to fix those minor bugs.

Qu Wenruo (12):
  btrfs-progs: kernel-shared: use BTRFS_PATH_AUTO_RELEASE() for on-stack
    paths
  btrfs-progs: common, convert, image: use BTRFS_PATH_AUTO_RELEASE() for
    on-stack paths
  btrfs-progs: mkfs: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths
  btrfs-progs: tune: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths
  btrfs-progs: cmds: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths
  btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for repair,
    qgroup-verify and mode-common
  btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for lowmem mode
  btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for original mode
  btrfs-progs: check: fix fixup_extent_flags() retry path
  btrfs-progs: fix missing transaction commit in
    truncate_free_ino_items()
  btrfs-progs: dump-tree: fix exit code and leak on open failure
  btrfs-progs: check: return error if fill_csum_tree_from_fs() fails to
    read a root

 check/main.c                     | 235 +++++++++++--------------------
 check/mode-common.c              | 141 +++++++------------
 check/mode-lowmem.c              | 202 +++++++++-----------------
 check/qgroup-verify.c            |  29 ++--
 check/repair.c                   |   5 +-
 cmds/device.c                    |  11 +-
 cmds/inspect-dump-tree.c         |  11 +-
 cmds/rescue-chunk-recover.c      |  43 ++----
 cmds/rescue-fix-data-checksum.c  |  10 +-
 cmds/rescue.c                    |   7 +-
 cmds/restore.c                   | 126 +++++++----------
 common/clear-cache.c             |  79 ++++-------
 common/root-tree-utils.c         |  10 +-
 convert/main.c                   |   9 +-
 convert/source-ext2.c            |   3 +-
 convert/source-fs.c              |   3 +-
 image/common.c                   |   2 +-
 image/image-create.c             |   2 +-
 image/image-restore.c            |  20 +--
 kernel-shared/extent-tree.c      |  62 +++-----
 kernel-shared/file.c             |   3 +-
 kernel-shared/free-space-cache.c |  24 ++--
 kernel-shared/inode.c            |   8 +-
 kernel-shared/print-tree.c       |   9 +-
 kernel-shared/volumes.c          |  15 +-
 mkfs/main.c                      |  42 ++----
 mkfs/rootdir.c                   |  36 ++---
 tests/btree-test.c               |   2 +-
 tune/change-csum.c               |  50 ++-----
 tune/change-uuid.c               |  38 ++---
 tune/convert-remap-tree.c        |  12 +-
 tune/quota.c                     |  19 ++-
 32 files changed, 447 insertions(+), 821 deletions(-)

--
2.56.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 01/12] btrfs-progs: kernel-shared: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:15 ` [PATCH 02/12] btrfs-progs: common, convert, image: " Qu Wenruo
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

Convert all on-stack btrfs_path in kernel-shared/ (and the btree-test
tool) to BTRFS_PATH_AUTO_RELEASE(), and drop the btrfs_release_path()
calls that only existed as function exit cleanup.

This also fixes the path leaks on error paths of:

- btrfs_stripe_tree_logical_to_physical()
- check_dev_extent_beyond_bytenr()
- get_last_converted_bg()

where btrfs_search_slot() failure returned without releasing the path.

Explicit releases which are not at function exit (e.g. before
re-searching with the same path) are kept.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 kernel-shared/extent-tree.c      | 62 ++++++++++++--------------------
 kernel-shared/file.c             |  3 +-
 kernel-shared/free-space-cache.c | 24 ++++++-------
 kernel-shared/inode.c            |  8 ++---
 kernel-shared/print-tree.c       |  9 ++---
 kernel-shared/volumes.c          | 15 +++-----
 tests/btree-test.c               |  2 +-
 7 files changed, 45 insertions(+), 78 deletions(-)

diff --git a/kernel-shared/extent-tree.c b/kernel-shared/extent-tree.c
index bac44654eab8..b73fe8c63431 100644
--- a/kernel-shared/extent-tree.c
+++ b/kernel-shared/extent-tree.c
@@ -2782,7 +2782,7 @@ static int read_one_block_group(struct btrfs_fs_info *fs_info,
 static int get_last_converted_bg(struct btrfs_fs_info *fs_info)
 {
 	struct btrfs_root *bg_root = fs_info->block_group_root;
-	struct btrfs_path path = {0};
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key = {0};
 	int ret;
 
@@ -2809,7 +2809,7 @@ static int get_last_converted_bg(struct btrfs_fs_info *fs_info)
 
 			btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 			fs_info->last_converted_bg_bytenr = key.objectid;
-			goto out;
+			return ret;
 		}
 		btrfs_release_path(&path);
 
@@ -2820,21 +2820,20 @@ static int get_last_converted_bg(struct btrfs_fs_info *fs_info)
 
 		ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
 		if (ret < 0)
-			goto out;
+			return ret;
 		ASSERT(ret > 0);
 		ret = btrfs_previous_item(extent_root, &path, 0, BTRFS_BLOCK_GROUP_ITEM_KEY);
 		if (ret < 0)
-			goto out;
+			return ret;
 
 		/* No converted bg item in extent tree.*/
 		if (ret > 0) {
-			ret = 0;
 			fs_info->last_converted_bg_bytenr = (u64)-1;
-			goto out;
+			return 0;
 		}
 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 		fs_info->last_converted_bg_bytenr = key.objectid;
-		goto out;
+		return ret;
 	}
 	/* Load the first bg in bg tree, that would be our last converted bg. */
 	ret = btrfs_search_slot(NULL, bg_root, &key, &path, 0, 0);
@@ -2847,14 +2846,11 @@ static int get_last_converted_bg(struct btrfs_fs_info *fs_info)
 	/* Empty bg tree, no converted bg item at all. */
 	if (btrfs_header_nritems(path.nodes[0]) == 0) {
 		fs_info->last_converted_bg_bytenr = (u64)-1;
-		ret = 0;
-		goto out;
+		return 0;
 	}
 	btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 	ASSERT(key.type == BTRFS_BLOCK_GROUP_ITEM_KEY);
 	fs_info->last_converted_bg_bytenr = key.objectid;
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -2870,7 +2866,7 @@ out:
 static int read_old_block_groups_from_root(struct btrfs_fs_info *fs_info,
 					   struct btrfs_root *root)
 {
-	struct btrfs_path path = {0};
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key = { 0 };
 	int ret;
 
@@ -2878,18 +2874,15 @@ static int read_old_block_groups_from_root(struct btrfs_fs_info *fs_info,
 
 	while (true) {
 		ret = find_first_block_group(root, &path, &key, fs_info->last_converted_bg_bytenr);
-		if (ret > 0) {
-			ret = 0;
-			goto out;
-		}
-		if (ret != 0) {
-			goto out;
-		}
+		if (ret > 0)
+			return 0;
+		if (ret < 0)
+			return ret;
 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 
 		ret = read_one_block_group(fs_info, &path);
 		if (ret < 0 && ret != -ENOENT)
-			goto out;
+			return ret;
 
 		if (key.offset == 0)
 			key.objectid++;
@@ -2898,17 +2891,14 @@ static int read_old_block_groups_from_root(struct btrfs_fs_info *fs_info,
 		key.offset = 0;
 		btrfs_release_path(&path);
 	}
-	ret = 0;
-out:
-	btrfs_release_path(&path);
-	return ret;
+	return 0;
 }
 
 /* Helper to read all block groups items from specified root. */
 static int read_block_groups_from_root(struct btrfs_fs_info *fs_info,
 					   struct btrfs_root *root)
 {
-	struct btrfs_path path = {0};
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key = {0};
 	int ret;
 
@@ -2916,18 +2906,15 @@ static int read_block_groups_from_root(struct btrfs_fs_info *fs_info,
 
 	while (true) {
 		ret = find_first_block_group(root, &path, &key, (u64)-1);
-		if (ret > 0) {
-			ret = 0;
-			goto out;
-		}
-		if (ret != 0) {
-			goto out;
-		}
+		if (ret > 0)
+			return 0;
+		if (ret < 0)
+			return ret;
 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 
 		ret = read_one_block_group(fs_info, &path);
 		if (ret < 0 && ret != -ENOENT)
-			goto out;
+			return ret;
 
 		if (key.offset == 0)
 			key.objectid++;
@@ -2936,10 +2923,7 @@ static int read_block_groups_from_root(struct btrfs_fs_info *fs_info,
 		key.offset = 0;
 		btrfs_release_path(&path);
 	}
-	ret = 0;
-out:
-	btrfs_release_path(&path);
-	return ret;
+	return 0;
 }
 
 static int read_converting_block_groups(struct btrfs_fs_info *fs_info)
@@ -3472,7 +3456,7 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
 {
 	struct btrfs_fs_info *fs_info = trans->fs_info;
 	struct btrfs_block_group *block_group;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret = 0;
 
 	block_group = btrfs_lookup_block_group(fs_info, bytenr);
@@ -3893,7 +3877,7 @@ int btrfs_convert_one_bg(struct btrfs_trans_handle *trans, u64 bytenr)
 	struct btrfs_root *new_root = fs_info->block_group_root;
 	struct btrfs_root *old_root = btrfs_extent_root(fs_info, 0);
 	struct btrfs_block_group *bg;
-	struct btrfs_path path = {0};
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret;
 
 	ASSERT(new_root);
diff --git a/kernel-shared/file.c b/kernel-shared/file.c
index 546e161abfdb..7fe5ae3b7595 100644
--- a/kernel-shared/file.c
+++ b/kernel-shared/file.c
@@ -192,7 +192,7 @@ int btrfs_read_file(struct btrfs_root *root, u64 ino, u64 start, int len,
 {
 	struct btrfs_fs_info *fs_info = root->fs_info;
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_inode_item *ii;
 	u64 isize;
@@ -331,7 +331,6 @@ next:
 		read = min_t(u64, isize - start, len);
 	}
 out:
-	btrfs_release_path(&path);
 	if (!ret)
 		ret = read;
 	return ret;
diff --git a/kernel-shared/free-space-cache.c b/kernel-shared/free-space-cache.c
index d80fe5707eb5..bc511b442f23 100644
--- a/kernel-shared/free-space-cache.c
+++ b/kernel-shared/free-space-cache.c
@@ -917,7 +917,7 @@ int btrfs_clear_free_space_cache(struct btrfs_trans_handle *trans,
 {
 	struct btrfs_fs_info *fs_info = trans->fs_info;
 	struct btrfs_root *tree_root = fs_info->tree_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_disk_key location;
 	struct btrfs_free_space_header *sc_header;
@@ -931,12 +931,10 @@ int btrfs_clear_free_space_cache(struct btrfs_trans_handle *trans,
 	key.offset = bg->start;
 
 	ret = btrfs_search_slot(trans, tree_root, &key, &path, -1, 1);
-	if (ret > 0) {
-		ret = 0;
-		goto out;
-	}
+	if (ret > 0)
+		return 0;
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	node = path.nodes[0];
 	slot = path.slots[0];
@@ -949,7 +947,7 @@ int btrfs_clear_free_space_cache(struct btrfs_trans_handle *trans,
 	if (ret < 0) {
 		error("failed to remove free space header for block group %llu: %d",
 		      bg->start, ret);
-		goto out;
+		return ret;
 	}
 	btrfs_release_path(&path);
 
@@ -961,7 +959,7 @@ int btrfs_clear_free_space_cache(struct btrfs_trans_handle *trans,
 	if (ret < 0) {
 		error("failed to locate free space cache extent for block group %llu: %d",
 		      bg->start, ret);
-		goto out;
+		return ret;
 	}
 	while (1) {
 		struct btrfs_file_extent_item *fi;
@@ -978,7 +976,7 @@ int btrfs_clear_free_space_cache(struct btrfs_trans_handle *trans,
 			error(
 	"failed to locate free space cache extent for block group %llu: %d",
 				bg->start, ret);
-			goto out;
+			return ret;
 		}
 		node = path.nodes[0];
 		slot = path.slots[0];
@@ -992,14 +990,14 @@ int btrfs_clear_free_space_cache(struct btrfs_trans_handle *trans,
 		if (ret < 0) {
 			error("failed to remove backref for disk bytenr %llu: %d",
 			      disk_bytenr, ret);
-			goto out;
+			return ret;
 		}
 		ret = btrfs_del_item(trans, tree_root, &path);
 		if (ret < 0) {
 			error(
 	"failed to remove free space extent data for ino %llu offset %llu: %d",
 			      ino, key.offset, ret);
-			goto out;
+			return ret;
 		}
 	}
 	btrfs_release_path(&path);
@@ -1016,7 +1014,7 @@ int btrfs_clear_free_space_cache(struct btrfs_trans_handle *trans,
 		error(
 	"failed to locate free space cache inode %llu for block group %llu: %d",
 		      ino, bg->start, ret);
-		goto out;
+		return ret;
 	}
 	ret = btrfs_del_item(trans, tree_root, &path);
 	if (ret < 0) {
@@ -1024,7 +1022,5 @@ int btrfs_clear_free_space_cache(struct btrfs_trans_handle *trans,
 	"failed to delete free space cache inode %llu for block group %llu: %d",
 		      ino, bg->start, ret);
 	}
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
diff --git a/kernel-shared/inode.c b/kernel-shared/inode.c
index f77157a2df14..d75e6fa3dd98 100644
--- a/kernel-shared/inode.c
+++ b/kernel-shared/inode.c
@@ -162,7 +162,7 @@ out:
 static int inherit_inode_flags(struct btrfs_trans_handle *trans,
 			       struct btrfs_root *root, u64 ino, u64 parent_ino)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_inode_item *iitem;
 	u64 parent_inode_flags;
@@ -177,7 +177,7 @@ static int inherit_inode_flags(struct btrfs_trans_handle *trans,
 	if (ret > 0)
 		ret = -ENOENT;
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	iitem = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_inode_item);
 	parent_inode_flags = btrfs_inode_flags(path.nodes[0], iitem);
@@ -189,7 +189,7 @@ static int inherit_inode_flags(struct btrfs_trans_handle *trans,
 	if (ret > 0)
 		ret = -ENOENT;
 	if (ret < 0)
-		goto out;
+		return ret;
 	iitem = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_inode_item);
 	child_inode_flags = btrfs_inode_flags(path.nodes[0], iitem);
 
@@ -206,8 +206,6 @@ static int inherit_inode_flags(struct btrfs_trans_handle *trans,
 			child_inode_flags |= BTRFS_INODE_NODATASUM;
 	}
 	btrfs_set_inode_flags(path.nodes[0], iitem, child_inode_flags);
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
diff --git a/kernel-shared/print-tree.c b/kernel-shared/print-tree.c
index 4b3ad093f278..832ce0678e71 100644
--- a/kernel-shared/print-tree.c
+++ b/kernel-shared/print-tree.c
@@ -1768,7 +1768,7 @@ static int next_sibling_tree_block(struct btrfs_fs_info *fs_info,
 static void bfs_print_children(struct extent_buffer *root_eb, unsigned int mode)
 {
 	struct btrfs_fs_info *fs_info = root_eb->fs_info;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int root_level = btrfs_header_level(root_eb);
 	int cur_level;
 	int ret;
@@ -1790,23 +1790,20 @@ static void bfs_print_children(struct extent_buffer *root_eb, unsigned int mode)
 		/* Use the leftmost tree block as a starting point */
 		ret = search_leftmost_tree_block(fs_info, &path, root_level);
 		if (ret < 0)
-			goto out;
+			return;
 
 		/* Print all sibling tree blocks */
 		while (1) {
 			btrfs_print_tree(path.nodes[cur_level], mode);
 			ret = next_sibling_tree_block(fs_info, &path);
 			if (ret < 0)
-				goto out;
+				return;
 			if (ret > 0) {
 				ret = 0;
 				break;
 			}
 		}
 	}
-out:
-	btrfs_release_path(&path);
-	return;
 }
 
 static void dfs_print_children(struct extent_buffer *root_eb, unsigned int mode)
diff --git a/kernel-shared/volumes.c b/kernel-shared/volumes.c
index fedcaa5f9216..ed9b769b42fc 100644
--- a/kernel-shared/volumes.c
+++ b/kernel-shared/volumes.c
@@ -2033,7 +2033,7 @@ static int btrfs_stripe_tree_logical_to_physical(struct btrfs_fs_info *fs_info,
 						struct btrfs_bio_stripe *stripe)
 {
 	struct btrfs_root *root = fs_info->stripe_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct extent_buffer *leaf;
 	int slot;
@@ -2061,7 +2061,7 @@ static int btrfs_stripe_tree_logical_to_physical(struct btrfs_fs_info *fs_info,
 			if (ret == 0)
 				continue;
 			if (ret < 0)
-				goto error;
+				return ret;
 			break;
 		}
 
@@ -2080,15 +2080,12 @@ static int btrfs_stripe_tree_logical_to_physical(struct btrfs_fs_info *fs_info,
 			    btrfs_raid_stride_devid_nr(leaf, extent, i))
 				continue;
 			stripe->physical = btrfs_raid_stride_physical_nr(leaf, extent, i);
-			btrfs_release_path(&path);
 			return 0;
 		}
 next:
 		path.slots[0]++;
 	}
 
-	btrfs_release_path(&path);
-error:
 	return ret;
 }
 
@@ -3053,7 +3050,7 @@ static int check_dev_extent_beyond_bytenr(struct btrfs_fs_info *fs_info,
 					  u64 physical)
 {
 	struct btrfs_root *root = fs_info->dev_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_dev_extent *dext;
 	struct btrfs_key key;
 	u64 dext_len;
@@ -3088,7 +3085,6 @@ static int check_dev_extent_beyond_bytenr(struct btrfs_fs_info *fs_info,
 	last_dev_extent_end = dext_len + key.offset;
 
 out:
-	btrfs_release_path(&path);
 	if (ret < 0)
 		return ret;
 	if (last_dev_extent_end <= physical)
@@ -3102,7 +3098,7 @@ static int reset_device_item_total_bytes(struct btrfs_fs_info *fs_info,
 {
 	struct btrfs_trans_handle *trans;
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_root *chunk_root = fs_info->chunk_root;
 	struct btrfs_dev_item *di;
 	u64 old_bytes = device->total_bytes;
@@ -3143,10 +3139,8 @@ static int reset_device_item_total_bytes(struct btrfs_fs_info *fs_info,
 	if (ret < 0) {
 		errno = -ret;
 		error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
-		btrfs_release_path(&path);
 		return ret;
 	}
-	btrfs_release_path(&path);
 	printf("Fixed device size for devid %llu, old size: %llu new size: %llu\n",
 		device->devid, old_bytes, device->total_bytes);
 	return 1;
@@ -3154,7 +3148,6 @@ static int reset_device_item_total_bytes(struct btrfs_fs_info *fs_info,
 err:
 	/* We haven't modified anything, it's OK to commit current trans */
 	btrfs_commit_transaction(trans, chunk_root);
-	btrfs_release_path(&path);
 	return ret;
 }
 
diff --git a/tests/btree-test.c b/tests/btree-test.c
index ddb13472e2f1..7dbbc1685aeb 100644
--- a/tests/btree-test.c
+++ b/tests/btree-test.c
@@ -41,7 +41,7 @@ int main(int argc, char **argv) {
 	int run_size = 300000;
 	int max_key =  100000000;
 	int tree_size = 2;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_root *root;
 	struct btrfs_trans_handle *trans;
 
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 02/12] btrfs-progs: common, convert, image: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
  2026-10-30  4:15 ` [PATCH 01/12] btrfs-progs: kernel-shared: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:15 ` [PATCH 03/12] btrfs-progs: mkfs: " Qu Wenruo
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

Convert all on-stack btrfs_path in common/, convert/ and image/ to
BTRFS_PATH_AUTO_RELEASE(), and drop the btrfs_release_path() calls that
only existed as function exit cleanup.

Where the path is only used inside one loop (rescan_uuid_tree(),
record_file_blocks(), truncate_free_ino_items()) the variable is declared
in the loop body, so the path is released as early as possible.

This also fixes the path leaks on error paths of:

- has_running_replace_or_balance()
- truncate_free_ino_items()
- find_next_root()
- rescan_uuid_tree()
- ext2_create_inode()
- update_disk_super_on_device()
- fixup_device_size()

Explicit releases which are not at function exit are kept, including the
ones before close_ctree() in create_metadump() and do_rollback(), as the
path must not outlive the extent buffer cache.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 common/clear-cache.c     | 73 +++++++++++++---------------------------
 common/root-tree-utils.c | 10 +++---
 convert/main.c           |  9 ++---
 convert/source-ext2.c    |  3 +-
 convert/source-fs.c      |  3 +-
 image/common.c           |  2 +-
 image/image-create.c     |  2 +-
 image/image-restore.c    | 20 ++++-------
 8 files changed, 41 insertions(+), 81 deletions(-)

diff --git a/common/clear-cache.c b/common/clear-cache.c
index e8740eaca085..bfefd325d700 100644
--- a/common/clear-cache.c
+++ b/common/clear-cache.c
@@ -109,7 +109,7 @@ int btrfs_clear_v1_cache(struct btrfs_fs_info *fs_info)
 static int has_reloc_trees(struct btrfs_fs_info *fs_info)
 {
 	struct btrfs_root *tree_root = fs_info->tree_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	unsigned int found = 0;
 	int ret;
@@ -164,7 +164,6 @@ static int has_reloc_trees(struct btrfs_fs_info *fs_info)
 	}
 
 out:
-	btrfs_release_path(&path);
 	if (ret < 0)
 		return ret;
 	return found;
@@ -180,7 +179,7 @@ out:
 int has_running_replace_or_balance(struct btrfs_fs_info *fs_info)
 {
 	struct btrfs_root *dev_root = fs_info->dev_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_dev_replace_item *dri;
 	u64 replace_state;
@@ -198,10 +197,8 @@ int has_running_replace_or_balance(struct btrfs_fs_info *fs_info)
 	ret = btrfs_search_slot(NULL, dev_root, &key, &path, 0, 0);
 	if (ret < 0)
 		return ret;
-	if (ret > 0) {
-		btrfs_release_path(&path);
+	if (ret > 0)
 		return 0;
-	}
 
 	/*
 	 * We still need to check if the dev_replace item to determine if there
@@ -209,7 +206,6 @@ int has_running_replace_or_balance(struct btrfs_fs_info *fs_info)
 	 */
 	dri = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_dev_replace_item);
 	replace_state = btrfs_dev_replace_replace_state(path.nodes[0], dri);
-	btrfs_release_path(&path);
 	if (replace_state == BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED ||
 	    replace_state == BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED)
 		return 1;
@@ -384,7 +380,7 @@ static int check_free_space_tree(struct btrfs_root *root)
 {
 	struct btrfs_fs_info *fs_info = root->fs_info;
 	struct btrfs_key key = { 0 };
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret = 0;
 	bool found_orphan = false;
 
@@ -394,7 +390,7 @@ static int check_free_space_tree(struct btrfs_root *root)
 
 		ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 		if (ret < 0)
-			goto out;
+			return ret;
 
 		/*
 		 * We should be landing on an item, so if we're above the
@@ -409,8 +405,7 @@ static int check_free_space_tree(struct btrfs_root *root)
 			fprintf(stderr,
 			"Failed to find a space info key at %llu [%llu %u %llu]\n",
 				cur_start, key.objectid, key.type, key.offset);
-			ret = -EINVAL;
-			goto out;
+			return -EINVAL;
 		}
 
 		bg = btrfs_lookup_block_group(fs_info, key.objectid);
@@ -435,8 +430,6 @@ static int check_free_space_tree(struct btrfs_root *root)
 		ret = -EINVAL;
 	else
 		ret = 0;
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -563,7 +556,7 @@ static int check_cache_range(struct btrfs_root *root,
 static int find_next_identity_remap_entry(struct btrfs_fs_info *fs_info, u64 *start, u64 *end)
 {
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	int ret;
 
@@ -572,10 +565,8 @@ static int find_next_identity_remap_entry(struct btrfs_fs_info *fs_info, u64 *st
 	key.offset = 0;
 
 	ret = btrfs_search_slot(NULL, fs_info->remap_root, &key, &path, 0, 0);
-	if (ret < 0) {
-		btrfs_release_path(&path);
+	if (ret < 0)
 		return ret;
-	}
 
 	while (1) {
 		leaf = path.nodes[0];
@@ -591,22 +582,19 @@ static int find_next_identity_remap_entry(struct btrfs_fs_info *fs_info, u64 *st
 		if (key.type == BTRFS_IDENTITY_REMAP_KEY) {
 			*start = key.objectid;
 			*end = key.objectid + key.offset - 1;
-			btrfs_release_path(&path);
 			return 0;
 		}
 
 		path.slots[0]++;
 	}
 
-	btrfs_release_path(&path);
-
 	return -ENOENT;
 }
 
 static int find_next_remap_backref_entry(struct btrfs_fs_info *fs_info, u64 *start, u64 *end)
 {
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	int ret;
 
@@ -615,10 +603,8 @@ static int find_next_remap_backref_entry(struct btrfs_fs_info *fs_info, u64 *sta
 	key.offset = 0;
 
 	ret = btrfs_search_slot(NULL, fs_info->remap_root, &key, &path, 0, 0);
-	if (ret < 0) {
-		btrfs_release_path(&path);
+	if (ret < 0)
 		return ret;
-	}
 
 	btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 
@@ -636,15 +622,12 @@ static int find_next_remap_backref_entry(struct btrfs_fs_info *fs_info, u64 *sta
 		if (key.type == BTRFS_REMAP_BACKREF_KEY) {
 			*start = key.objectid;
 			*end = key.objectid + key.offset - 1;
-			btrfs_release_path(&path);
 			return 0;
 		}
 
 		path.slots[0]++;
 	}
 
-	btrfs_release_path(&path);
-
 	return -ENOENT;
 }
 
@@ -886,20 +869,19 @@ int truncate_free_ino_items(struct btrfs_root *root)
 		struct btrfs_file_extent_item *fi;
 		struct btrfs_root *csum_root;
 		struct btrfs_key found_key;
-		struct btrfs_path path = { 0 };
+		BTRFS_PATH_AUTO_RELEASE(path);
 		u8 found_type;
 
 		ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
 		if (ret < 0) {
+			btrfs_release_path(&path);
 			btrfs_abort_transaction(trans, ret);
-			goto out;
+			return ret;
 		} else if (ret > 0) {
 			ret = 0;
 			/* No more items, finished truncating */
-			if (path.slots[0] == 0) {
-				btrfs_release_path(&path);
-				goto out;
-			}
+			if (path.slots[0] == 0)
+				return ret;
 			path.slots[0]--;
 		}
 		fi = NULL;
@@ -910,7 +892,6 @@ int truncate_free_ino_items(struct btrfs_root *root)
 		/* Ino cache also has free space bitmaps in the fs stree */
 		if (found_key.objectid != BTRFS_FREE_INO_OBJECTID &&
 		    found_key.objectid != BTRFS_FREE_SPACE_OBJECTID) {
-			btrfs_release_path(&path);
 			/* Now delete the FREE_SPACE_OBJECTID */
 			if (key.objectid == BTRFS_FREE_INO_OBJECTID) {
 				key.objectid = BTRFS_FREE_SPACE_OBJECTID;
@@ -939,9 +920,9 @@ int truncate_free_ino_items(struct btrfs_root *root)
 						root->objectid,
 						BTRFS_FREE_INO_OBJECTID, 0);
 			if (ret < 0) {
-				btrfs_abort_transaction(trans, ret);
 				btrfs_release_path(&path);
-				goto out;
+				btrfs_abort_transaction(trans, ret);
+				return ret;
 			}
 
 			csum_root = btrfs_csum_root(trans->fs_info,
@@ -950,23 +931,21 @@ int truncate_free_ino_items(struct btrfs_root *root)
 					      extent_disk_bytenr,
 					      extent_num_bytes);
 			if (ret < 0) {
-				btrfs_abort_transaction(trans, ret);
 				btrfs_release_path(&path);
-				goto out;
+				btrfs_abort_transaction(trans, ret);
+				return ret;
 			}
 		}
 
 		ret = btrfs_del_item(trans, root, &path);
 		if (ret < 0) {
-			btrfs_abort_transaction(trans, ret);
 			btrfs_release_path(&path);
-			goto out;
+			btrfs_abort_transaction(trans, ret);
+			return ret;
 		}
-		btrfs_release_path(&path);
 	}
 
 	btrfs_commit_transaction(trans, root);
-out:
 	return ret;
 }
 
@@ -986,7 +965,7 @@ static int find_next_root(struct btrfs_fs_info *fs_info, u64 rootid,
 		.type = BTRFS_ROOT_ITEM_KEY,
 		.offset = 0,
 	};
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret;
 
 	ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, &path, 0, 0);
@@ -996,16 +975,12 @@ static int find_next_root(struct btrfs_fs_info *fs_info, u64 rootid,
 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 		if (key.type == BTRFS_ROOT_ITEM_KEY && key.objectid >= rootid) {
 			memcpy(found_key, &key, sizeof(key));
-			ret = 0;
-			goto out;
+			return 0;
 		}
 		ret = btrfs_next_item(fs_info->tree_root, &path);
 		if (ret)
-			goto out;
+			return ret;
 	}
-out:
-	btrfs_release_path(&path);
-	return ret;
 }
 
 int clear_ino_cache_items(struct btrfs_fs_info *fs_info)
diff --git a/common/root-tree-utils.c b/common/root-tree-utils.c
index ad5a9557ff23..bb27a894443b 100644
--- a/common/root-tree-utils.c
+++ b/common/root-tree-utils.c
@@ -139,7 +139,7 @@ int btrfs_link_subvolume(struct btrfs_trans_handle *trans,
 			 int namelen, struct btrfs_root *subvol)
 {
 	struct btrfs_root *tree_root = trans->fs_info->tree_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_inode_item *ii;
 	u64 index;
@@ -159,10 +159,8 @@ int btrfs_link_subvolume(struct btrfs_trans_handle *trans,
 	ret = btrfs_search_slot(NULL, parent_root, &key, &path, 0, 0);
 	if (ret > 0)
 		ret = -ENOENT;
-	if (ret < 0) {
-		btrfs_release_path(&path);
+	if (ret < 0)
 		return ret;
-	}
 	ii = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_inode_item);
 	imode = btrfs_inode_mode(path.nodes[0], ii);
 	btrfs_release_path(&path);
@@ -337,7 +335,6 @@ static int rescan_uuid_tree(struct btrfs_fs_info *fs_info)
 	struct btrfs_root *tree_root = fs_info->tree_root;
 	struct btrfs_root *uuid_root = fs_info->uuid_root;
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
 	struct btrfs_key key = { 0 };
 	int ret;
 
@@ -356,6 +353,7 @@ static int rescan_uuid_tree(struct btrfs_fs_info *fs_info)
 	while (true) {
 		struct btrfs_key found_key;
 		struct extent_buffer *leaf;
+		BTRFS_PATH_AUTO_RELEASE(path);
 		int slot;
 
 		/* No more subvolume. */
@@ -367,6 +365,7 @@ static int rescan_uuid_tree(struct btrfs_fs_info *fs_info)
 		if (ret < 0) {
 			errno = -ret;
 			error_msg(ERROR_MSG_READ, "iterate subvolumes: %m");
+			btrfs_release_path(&path);
 			btrfs_abort_transaction(trans, ret);
 			return ret;
 		}
@@ -384,7 +383,6 @@ static int rescan_uuid_tree(struct btrfs_fs_info *fs_info)
 			/* No more subvolume. */
 			if (ret > 0) {
 				ret = 0;
-				btrfs_release_path(&path);
 				break;
 			}
 		}
diff --git a/convert/main.c b/convert/main.c
index 2e0b764fab56..0e7f24162947 100644
--- a/convert/main.c
+++ b/convert/main.c
@@ -802,7 +802,7 @@ static int create_image(struct btrfs_root *root,
 {
 	struct btrfs_inode_item buf;
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct cache_extent *cache;
 	struct cache_tree used_tmp;
@@ -1510,7 +1510,7 @@ static int check_convert_image(struct btrfs_root *image_root, u64 ino,
 			       u64 total_size, char *reserved_ranges[])
 {
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_fs_info *fs_info = image_root->fs_info;
 	u64 checked_bytes = 0;
 	int ret;
@@ -1529,7 +1529,6 @@ static int check_convert_image(struct btrfs_root *image_root, u64 ino,
 	if (ret < 0) {
 		errno = -ret;
 		error("failed to iterate file extents at offset 0: %m");
-		btrfs_release_path(&path);
 		return ret;
 	}
 
@@ -1674,7 +1673,7 @@ static int do_rollback(const char *devname)
 	struct btrfs_root *image_root;
 	struct btrfs_fs_info *fs_info;
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_dir_item *dir;
 	struct btrfs_inode_item *inode_item;
 	struct btrfs_root_ref *root_ref_item;
@@ -1775,7 +1774,6 @@ static int do_rollback(const char *devname)
 			image_name, strlen(image_name), 0);
 
 	if (!dir || IS_ERR(dir)) {
-		btrfs_release_path(&path);
 		if (dir)
 			ret = PTR_ERR(dir);
 		else
@@ -1793,7 +1791,6 @@ static int do_rollback(const char *devname)
 	ret = btrfs_lookup_inode(NULL, image_root, &path, &key, 0);
 
 	if (ret < 0) {
-		btrfs_release_path(&path);
 		errno = -ret;
 		error("unable to find inode %llu: %m", ino);
 		goto close_fs;
diff --git a/convert/source-ext2.c b/convert/source-ext2.c
index 647e64411c06..3d68bcf5e5c1 100644
--- a/convert/source-ext2.c
+++ b/convert/source-ext2.c
@@ -978,7 +978,7 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans,
 	int s_inode_size;
 	struct btrfs_inode_item btrfs_inode;
 	struct btrfs_key inode_key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 
 	inode_key.objectid = objectid;
 	inode_key.type = BTRFS_INODE_ITEM_KEY;
@@ -1062,7 +1062,6 @@ static int ext2_copy_single_inode(struct btrfs_trans_handle *trans,
 	write_extent_buffer(path.nodes[0], &btrfs_inode,
 			    btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
 			    sizeof(btrfs_inode));
-	btrfs_release_path(&path);
 	return 0;
 }
 
diff --git a/convert/source-fs.c b/convert/source-fs.c
index 8a296bd9c7f4..cc998d245d96 100644
--- a/convert/source-fs.c
+++ b/convert/source-fs.c
@@ -251,7 +251,6 @@ int record_file_blocks(struct blk_iterate_data *data,
 	int ret = 0;
 	struct btrfs_root *root = data->root;
 	struct btrfs_root *convert_root = data->convert_root;
-	struct btrfs_path path = { 0 };
 	u32 sectorsize = root->fs_info->sectorsize;
 	u64 file_pos = file_block * sectorsize;
 	u64 old_disk_bytenr = disk_block * sectorsize;
@@ -269,6 +268,7 @@ int record_file_blocks(struct blk_iterate_data *data,
 	 */
 	while (cur_off < old_disk_bytenr + num_bytes) {
 		struct btrfs_key key;
+		BTRFS_PATH_AUTO_RELEASE(path);
 		struct btrfs_file_extent_item *fi;
 		struct extent_buffer *node;
 		int slot;
@@ -328,7 +328,6 @@ int record_file_blocks(struct blk_iterate_data *data,
 		 * need to waste CPU cycles now.
 		 */
 	}
-	btrfs_release_path(&path);
 	return ret;
 }
 
diff --git a/image/common.c b/image/common.c
index 77fb95580049..ec8fb29379cc 100644
--- a/image/common.c
+++ b/image/common.c
@@ -148,7 +148,7 @@ int update_disk_super_on_device(struct btrfs_fs_info *info,
 {
 	struct btrfs_key key;
 	struct extent_buffer *leaf;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_dev_item *dev_item;
 	struct btrfs_super_block disk_super;
 	char dev_uuid[BTRFS_UUID_SIZE];
diff --git a/image/image-create.c b/image/image-create.c
index 97348f37acaa..09300b167044 100644
--- a/image/image-create.c
+++ b/image/image-create.c
@@ -779,7 +779,7 @@ int create_metadump(const char *input, FILE *out, int num_threads,
 		    int walk_trees, bool dump_data)
 {
 	struct btrfs_root *root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct metadump_struct metadump;
 	int ret;
 	int err = 0;
diff --git a/image/image-restore.c b/image/image-restore.c
index 3d76440599ec..cd74a2d66431 100644
--- a/image/image-restore.c
+++ b/image/image-restore.c
@@ -1506,7 +1506,7 @@ static int remove_all_dev_extents(struct btrfs_trans_handle *trans)
 {
 	struct btrfs_fs_info *fs_info = trans->fs_info;
 	struct btrfs_root *root = fs_info->dev_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct extent_buffer *leaf;
 	int slot;
@@ -1531,12 +1531,10 @@ static int remove_all_dev_extents(struct btrfs_trans_handle *trans)
 			if (ret < 0) {
 				errno = -ret;
 				error("failed to search dev tree: %m");
-				goto out;
-			}
-			if (ret > 0) {
-				ret = 0;
-				goto out;
+				return ret;
 			}
+			if (ret > 0)
+				return 0;
 		}
 
 		btrfs_item_key_to_cpu(leaf, &key, slot);
@@ -1547,11 +1545,9 @@ static int remove_all_dev_extents(struct btrfs_trans_handle *trans)
 			errno = -ret;
 			error("failed to delete dev extent %llu, %llu: %m",
 				key.objectid, key.offset);
-			goto out;
+			return ret;
 		}
 	}
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -1653,7 +1649,7 @@ static int fixup_device_size(struct btrfs_trans_handle *trans,
 	struct btrfs_dev_item *dev_item;
 	struct btrfs_dev_extent *dev_ext;
 	struct btrfs_device *dev;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_root *root = fs_info->chunk_root;
 	struct btrfs_key key;
@@ -1677,12 +1673,10 @@ static int fixup_device_size(struct btrfs_trans_handle *trans,
 		errno = -ret;
 		error("failed to locate last dev extent of devid %llu: %m",
 			devid);
-		btrfs_release_path(&path);
 		return ret;
 	}
 	if (ret == 0) {
 		error("found invalid dev extent devid %llu offset -1", devid);
-		btrfs_release_path(&path);
 		return -EUCLEAN;
 	}
 	ret = btrfs_previous_item(fs_info->dev_root, &path, devid,
@@ -1693,7 +1687,6 @@ static int fixup_device_size(struct btrfs_trans_handle *trans,
 		errno = -ret;
 		error("failed to locate last dev extent of devid %llu: %m",
 			devid);
-		btrfs_release_path(&path);
 		return ret;
 	}
 
@@ -1776,7 +1769,6 @@ again:
 		path.slots[0]++;
 	}
 
-	btrfs_release_path(&path);
 	return 0;
 }
 
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 03/12] btrfs-progs: mkfs: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
  2026-10-30  4:15 ` [PATCH 01/12] btrfs-progs: kernel-shared: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths Qu Wenruo
  2026-10-30  4:15 ` [PATCH 02/12] btrfs-progs: common, convert, image: " Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:15 ` [PATCH 04/12] btrfs-progs: tune: " Qu Wenruo
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

Convert all on-stack btrfs_path in mkfs/ to BTRFS_PATH_AUTO_RELEASE(),
and drop the btrfs_release_path() calls that only existed as function
exit cleanup.

For the block group cleanup loop in the mkfs allocation fixup code, the
path is only used inside the loop body, so declare it there and let it be
released at the end of each iteration, before the transaction commit.

This also fixes the path leaks on error paths of:

- __recow_root()
- setup_quota_root()
- discard_free_space()

Explicit releases which are not at function exit are kept.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 mkfs/main.c    | 42 ++++++++++++++----------------------------
 mkfs/rootdir.c | 36 ++++++++++++------------------------
 2 files changed, 26 insertions(+), 52 deletions(-)

diff --git a/mkfs/main.c b/mkfs/main.c
index b7f0bb4521e0..d61af04e055e 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -266,7 +266,7 @@ err:
 
 static int __recow_root(struct btrfs_trans_handle *trans, struct btrfs_root *root)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -299,7 +299,7 @@ static int __recow_root(struct btrfs_trans_handle *trans, struct btrfs_root *roo
 		/* This will ensure this leaf and all its parent get COWed */
 		ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
 		if (ret < 0)
-			goto out;
+			return ret;
 		ret = 0;
 		btrfs_item_key_to_cpu(path.nodes[0], &found_key, 0);
 		UASSERT(btrfs_comp_cpu_keys(&key, &found_key) == 0);
@@ -307,15 +307,10 @@ static int __recow_root(struct btrfs_trans_handle *trans, struct btrfs_root *roo
 next:
 		ret = btrfs_next_leaf(root, &path);
 		if (ret < 0)
-			goto out;
-		if (ret > 0) {
-			ret = 0;
-			goto out;
-		}
+			return ret;
+		if (ret > 0)
+			return 0;
 	}
-out:
-	btrfs_release_path(&path);
-	return ret;
 }
 
 static int recow_global_roots(struct btrfs_trans_handle *trans)
@@ -712,7 +707,6 @@ static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info,
 	struct btrfs_root *root = btrfs_block_group_root(fs_info);
 	struct btrfs_key key;
 	struct btrfs_key found_key;
-	struct btrfs_path path = { 0 };
 	int ret = 0;
 
 	trans = btrfs_start_transaction(root, 1);
@@ -728,6 +722,8 @@ static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info,
 	key.offset = 0;
 
 	while (1) {
+		BTRFS_PATH_AUTO_RELEASE(path);
+
 		/*
 		 * as the rest of the loop may modify the tree, we need to
 		 * start a new search each time.
@@ -789,7 +785,6 @@ static int cleanup_temp_chunks(struct btrfs_fs_info *fs_info,
 				  BTRFS_BLOCK_GROUP_DATA))
 				alloc->mixed -= found_key.offset;
 		}
-		btrfs_release_path(&path);
 		key.objectid = found_key.objectid + found_key.offset;
 	}
 out:
@@ -800,7 +795,6 @@ out:
 			error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
 		}
 	}
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -887,7 +881,7 @@ static int insert_qgroup_items(struct btrfs_trans_handle *trans,
 			       struct btrfs_fs_info *fs_info,
 			       u64 qgroupid)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_root *quota_root = fs_info->quota_root;
 	struct btrfs_key key;
 	int ret;
@@ -912,7 +906,6 @@ static int insert_qgroup_items(struct btrfs_trans_handle *trans,
 	key.offset = qgroupid;
 	ret = btrfs_insert_empty_item(trans, quota_root, &path, &key,
 				      sizeof(struct btrfs_qgroup_limit_item));
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -929,7 +922,7 @@ static int touch_root_subvol(struct btrfs_fs_info *fs_info)
 	};
 	struct extent_buffer *leaf;
 	int slot;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret;
 
 	trans = btrfs_start_transaction(fs_info->fs_root, 1);
@@ -952,11 +945,9 @@ static int touch_root_subvol(struct btrfs_fs_info *fs_info)
 		error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
 		return ret;
 	}
-	btrfs_release_path(&path);
 	return 0;
 fail:
 	btrfs_abort_transaction(trans, ret);
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -965,7 +956,7 @@ static int setup_quota_root(struct btrfs_fs_info *fs_info)
 	struct btrfs_trans_handle *trans;
 	struct btrfs_qgroup_status_item *qsi;
 	struct btrfs_root *quota_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int qgroup_repaired = 0;
 	bool simple = btrfs_fs_incompat(fs_info, SIMPLE_QUOTA);
@@ -1405,7 +1396,7 @@ static int discard_all_devices(struct btrfs_fs_info *fs_info)
 static int discard_free_space(struct btrfs_fs_info *fs_info, u64 metadata_profile)
 {
 	struct btrfs_root *free_space_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	int ret;
 	struct btrfs_key key = {
@@ -1453,7 +1444,7 @@ static int discard_free_space(struct btrfs_fs_info *fs_info, u64 metadata_profil
 		if (key.type == BTRFS_FREE_SPACE_EXTENT_KEY) {
 			ret = queue_discard_logical(fs_info, key.objectid, key.offset);
 			if (ret < 0)
-				goto out;
+				return ret;
 		} else if (key.type == BTRFS_FREE_SPACE_BITMAP_KEY) {
 			int size, nrbits;
 			void *bitmap;
@@ -1462,8 +1453,7 @@ static int discard_free_space(struct btrfs_fs_info *fs_info, u64 metadata_profil
 			size = btrfs_item_size(leaf, path.slots[0]);
 			bitmap = malloc(size);
 			if (!bitmap) {
-				ret = -ENOMEM;
-				goto out;
+				return -ENOMEM;
 			}
 
 			read_extent_buffer(leaf, bitmap,
@@ -1483,7 +1473,7 @@ static int discard_free_space(struct btrfs_fs_info *fs_info, u64 metadata_profil
 				ret = queue_discard_logical(fs_info, addr, length);
 				if (ret < 0) {
 					free(bitmap);
-					goto out;
+					return ret;
 				}
 
 				start_bit = find_next_bit_le(bitmap, nrbits, end_bit);
@@ -1498,10 +1488,6 @@ static int discard_free_space(struct btrfs_fs_info *fs_info, u64 metadata_profil
 
 	/* Every discard range is properly queued. Now submit the real discard request. */
 	return discard_all_devices(fs_info);
-
-out:
-	btrfs_release_path(&path);
-	return ret;
 }
 
 int BOX_MAIN(mkfs)(int argc, char **argv)
diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c
index 0b5dbee12cca..47837ab067fa 100644
--- a/mkfs/rootdir.c
+++ b/mkfs/rootdir.c
@@ -1682,7 +1682,7 @@ static int update_inode_item(struct btrfs_trans_handle *trans,
 			     const struct btrfs_inode_item *inode_item,
 			     u64 ino)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key = {
 		.objectid = ino,
 		.type = BTRFS_INODE_ITEM_KEY,
@@ -1694,14 +1694,11 @@ static int update_inode_item(struct btrfs_trans_handle *trans,
 	ret = btrfs_lookup_inode(trans, root, &path, &key, 1);
 	if (ret > 0)
 		ret = -ENOENT;
-	if (ret < 0) {
-		btrfs_release_path(&path);
+	if (ret < 0)
 		return ret;
-	}
 	item_ptr_off = btrfs_item_ptr_offset(path.nodes[0], path.slots[0]);
 	write_extent_buffer(path.nodes[0], inode_item, item_ptr_off, sizeof(*inode_item));
 	btrfs_mark_buffer_dirty(path.nodes[0]);
-	btrfs_release_path(&path);
 	return 0;
 }
 
@@ -1850,7 +1847,7 @@ out:
 static int read_inode_item(struct btrfs_root *root, struct btrfs_inode_item *inode_item,
 			   u64 ino)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -1862,14 +1859,12 @@ static int read_inode_item(struct btrfs_root *root, struct btrfs_inode_item *ino
 	if (ret > 0)
 		ret = -ENOENT;
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	read_extent_buffer(path.nodes[0], inode_item,
 			   btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
 			   sizeof(*inode_item));
-out:
-	btrfs_release_path(&path);
-	return ret;
+	return 0;
 }
 
 static int ftw_add_inode(const char *full_path, const struct stat *st,
@@ -2123,7 +2118,7 @@ static int ftw_add_inode(const char *full_path, const struct stat *st,
 
 static int set_default_subvolume(struct btrfs_trans_handle *trans)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_dir_item *di;
 	struct btrfs_key location;
 	struct extent_buffer *leaf;
@@ -2134,8 +2129,6 @@ static int set_default_subvolume(struct btrfs_trans_handle *trans)
 				   btrfs_super_root_dir(trans->fs_info->super_copy),
 				   "default", 7, 1);
 	if (IS_ERR_OR_NULL(di)) {
-		btrfs_release_path(&path);
-
 		if (di)
 			return PTR_ERR(di);
 		else
@@ -2388,7 +2381,7 @@ static int get_device_extent_end(struct btrfs_fs_info *fs_info,
 {
 	struct btrfs_root *dev_root = fs_info->dev_root;
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_dev_extent *de;
 	int ret;
 
@@ -2399,29 +2392,25 @@ static int get_device_extent_end(struct btrfs_fs_info *fs_info,
 	ret = btrfs_search_slot(NULL, dev_root, &key, &path, 0, 0);
 	if (ret == 0) {
 		error("DEV_EXTENT for devid %llu not found", devid);
-		ret = -EUCLEAN;
-		goto out;
+		return -EUCLEAN;
 	}
 
 	ret = btrfs_previous_item(dev_root, &path, devid, BTRFS_DEV_EXTENT_KEY);
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	/* No dev_extent at all, not really possible for rootdir case */
 	if (ret > 0) {
 		*size_ret = 0;
-		ret = -EUCLEAN;
-		goto out;
+		return -EUCLEAN;
 	}
 
 	btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 	de = btrfs_item_ptr(path.nodes[0], path.slots[0],
 			    struct btrfs_dev_extent);
 	*size_ret = key.offset + btrfs_dev_extent_length(path.nodes[0], de);
-out:
-	btrfs_release_path(&path);
 
-	return ret;
+	return 0;
 }
 
 /*
@@ -2439,7 +2428,7 @@ static int set_device_size(struct btrfs_fs_info *fs_info,
 	struct btrfs_root *chunk_root = fs_info->chunk_root;
 	struct btrfs_trans_handle *trans;
 	struct btrfs_dev_item *di;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -2486,7 +2475,6 @@ static int set_device_size(struct btrfs_fs_info *fs_info,
 		errno = -ret;
 		error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
 	}
-	btrfs_release_path(&path);
 	return ret;
 
 err:
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 04/12] btrfs-progs: tune: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
                   ` (2 preceding siblings ...)
  2026-10-30  4:15 ` [PATCH 03/12] btrfs-progs: mkfs: " Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:15 ` [PATCH 05/12] btrfs-progs: cmds: " Qu Wenruo
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

Convert all on-stack btrfs_path in tune/ to BTRFS_PATH_AUTO_RELEASE(),
and drop the btrfs_release_path() calls that only existed as function
exit cleanup.

Where the path is only used inside one loop (generate_new_data_csums_range(),
delete_old_data_csums(), change_csum_objectids(),
extend_block_group_items()), the variable is declared in the loop body,
so the path is released as early as possible.

This also fixes the path leaks on error paths of:

- get_last_csum_bytenr()
- generate_new_data_csums_range()
- delete_old_data_csums()
- change_meta_csums()
- get_csum_items_range()
- btrfs_change_csum_type() helpers (resume_csum_change())
- extend_block_group_items()
- remove_owner_refs()
- enable_quota()

Explicit releases which are not at function exit are kept.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 tune/change-csum.c        | 50 +++++++++++----------------------------
 tune/change-uuid.c        | 38 ++++++++++-------------------
 tune/convert-remap-tree.c | 12 ++++------
 tune/quota.c              | 19 +++++++--------
 4 files changed, 39 insertions(+), 80 deletions(-)

diff --git a/tune/change-csum.c b/tune/change-csum.c
index 1efb6fd60758..1e01a983ea17 100644
--- a/tune/change-csum.c
+++ b/tune/change-csum.c
@@ -40,7 +40,7 @@ static int check_csum_change_requreiment(struct btrfs_fs_info *fs_info, u16 new_
 {
 	struct btrfs_root *tree_root = fs_info->tree_root;
 	struct btrfs_root *dev_root = fs_info->dev_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -73,7 +73,6 @@ static int check_csum_change_requreiment(struct btrfs_fs_info *fs_info, u16 new_
 	key.offset = 0;
 	ret = btrfs_search_slot(NULL, dev_root, &key, &path, 0, 0);
 	if (ret < 0) {
-		btrfs_release_path(&path);
 		errno = -ret;
 		error("failed to check the dev-replace status: %m");
 		return ret;
@@ -86,7 +85,6 @@ static int check_csum_change_requreiment(struct btrfs_fs_info *fs_info, u16 new_
 		state = btrfs_dev_replace_replace_state(path.nodes[0], ptr);
 		if (state == BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED ||
 		    state == BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED) {
-			btrfs_release_path(&path);
 			error(
 	"running/suspended dev-replace detected, please finish or cancel it");
 			return -EINVAL;
@@ -105,7 +103,7 @@ static int check_csum_change_requreiment(struct btrfs_fs_info *fs_info, u16 new_
 static int get_last_csum_bytenr(struct btrfs_fs_info *fs_info, u64 *result)
 {
 	struct btrfs_root *csum_root = btrfs_csum_root(fs_info, 0);
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -127,13 +125,11 @@ static int get_last_csum_bytenr(struct btrfs_fs_info *fs_info, u64 *result)
 	 */
 	if (ret > 0) {
 		*result = 0;
-		btrfs_release_path(&path);
 		return 0;
 	}
 	btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 	*result = key.offset + btrfs_item_size(path.nodes[0], path.slots[0]) /
 			       fs_info->csum_size * fs_info->sectorsize;
-	btrfs_release_path(&path);
 	return 0;
 }
 
@@ -239,7 +235,6 @@ static int generate_new_data_csums_range(struct btrfs_fs_info *fs_info, u64 star
 	const unsigned int nr_items = calc_csum_change_nr_items(fs_info, new_csum_type);
 	struct btrfs_root *csum_root = btrfs_csum_root(fs_info, 0);
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
 	struct btrfs_key key;
 	void *csum_buffer;
 	u64 converted_bytes = 0;
@@ -266,6 +261,7 @@ static int generate_new_data_csums_range(struct btrfs_fs_info *fs_info, u64 star
 	}
 
 	while (cur < last_csum) {
+		BTRFS_PATH_AUTO_RELEASE(path);
 		u64 csum_start;
 		u64 len;
 		u32 item_size;
@@ -282,13 +278,10 @@ static int generate_new_data_csums_range(struct btrfs_fs_info *fs_info, u64 star
 			ret = btrfs_next_leaf(csum_root, &path);
 			if (ret > 0) {
 				ret = 0;
-				btrfs_release_path(&path);
 				break;
 			}
-			if (ret < 0) {
-				btrfs_release_path(&path);
+			if (ret < 0)
 				goto out;
-			}
 		}
 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 		UASSERT(key.offset >= cur);
@@ -333,7 +326,7 @@ static int generate_new_data_csums(struct btrfs_fs_info *fs_info, u16 new_csum_t
 {
 	struct btrfs_root *tree_root = fs_info->tree_root;
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -376,7 +369,6 @@ static int delete_old_data_csums(struct btrfs_fs_info *fs_info)
 {
 	struct btrfs_root *csum_root = btrfs_csum_root(fs_info, 0);
 	struct btrfs_trans_handle *trans = 0;
-	struct btrfs_path path = { 0 };
 	struct btrfs_key last_key;
 	unsigned int deleted_leaves = 0;
 	int ret;
@@ -386,6 +378,7 @@ static int delete_old_data_csums(struct btrfs_fs_info *fs_info)
 	last_key.offset = (u64)-1;
 
 	while (true) {
+		BTRFS_PATH_AUTO_RELEASE(path);
 		int start_slot;
 		int nr;
 
@@ -444,10 +437,8 @@ static int delete_old_data_csums(struct btrfs_fs_info *fs_info)
 			error("failed to delete items: %m");
 			break;
 		}
-		btrfs_release_path(&path);
 		deleted_leaves++;
 	}
-	btrfs_release_path(&path);
 	if (ret < 0)
 		btrfs_abort_transaction(trans, ret);
 	ret = btrfs_commit_transaction(trans, csum_root);
@@ -464,7 +455,6 @@ static int change_csum_objectids(struct btrfs_fs_info *fs_info)
 {
 	struct btrfs_root *csum_root = btrfs_csum_root(fs_info, 0);
 	struct btrfs_trans_handle *trans = NULL;
-	struct btrfs_path path = { 0 };
 	struct btrfs_key last_key;
 	unsigned int changed_leaves = 0;
 	u64 super_flags;
@@ -475,6 +465,7 @@ static int change_csum_objectids(struct btrfs_fs_info *fs_info)
 	last_key.offset = (u64)-1;
 
 	while (true) {
+		BTRFS_PATH_AUTO_RELEASE(path);
 		struct btrfs_key found_key;
 		int nr;
 
@@ -527,10 +518,8 @@ static int change_csum_objectids(struct btrfs_fs_info *fs_info)
 			path.slots[0] = i;
 			btrfs_set_item_key_safe(fs_info, &path, &found_key);
 		}
-		btrfs_release_path(&path);
 	}
 out:
-	btrfs_release_path(&path);
 	if (ret < 0) {
 		btrfs_abort_transaction(trans, ret);
 		return ret;
@@ -610,7 +599,7 @@ out:
 static int change_meta_csums(struct btrfs_fs_info *fs_info, u16 new_csum_type)
 {
 	struct btrfs_root *extent_root = btrfs_extent_root(fs_info, 0);
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	u64 super_flags;
 	int ret;
@@ -754,7 +743,7 @@ static int get_csum_items_range(struct btrfs_fs_info *fs_info,
 				u32 *last_item_size)
 {
 	struct btrfs_root *csum_root = btrfs_csum_root(fs_info, 0);
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -773,17 +762,14 @@ static int get_csum_items_range(struct btrfs_fs_info *fs_info,
 		if (ret < 0) {
 			errno = -ret;
 			error("failed to search csum tree: %m");
-			btrfs_release_path(&path);
 			return ret;
 		}
 		/*
 		 * There is no next leaf, meaning we didn't find any csum item
 		 * with given objectid.
 		 */
-		if (ret > 0) {
-			btrfs_release_path(&path);
+		if (ret > 0)
 			return ret;
-		}
 	}
 
 	btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
@@ -808,16 +794,12 @@ static int get_csum_items_range(struct btrfs_fs_info *fs_info,
 	if (ret < 0) {
 		errno = -ret;
 		error("failed to search csum tree: %m");
-		btrfs_release_path(&path);
 		return ret;
 	}
-	if (ret > 0) {
-		btrfs_release_path(&path);
+	if (ret > 0)
 		return 1;
-	}
 	btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 	*last_item_size = btrfs_item_size(path.nodes[0], path.slots[0]);
-	btrfs_release_path(&path);
 	*last_ret = key.offset;
 	return 0;
 }
@@ -834,7 +816,7 @@ static int determine_csum_type(struct btrfs_fs_info *fs_info, u64 logical,
 			       u16 csum_type)
 {
 	struct btrfs_root *csum_root = btrfs_csum_root(fs_info, logical);
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	u16 csum_size = btrfs_csum_type_size(csum_type);
 	u8 csum_expected[BTRFS_CSUM_SIZE];
@@ -851,7 +833,6 @@ static int determine_csum_type(struct btrfs_fs_info *fs_info, u64 logical,
 	if (ret < 0) {
 		errno = -ret;
 		error("failed to search csum tree: %m");
-		btrfs_release_path(&path);
 		return ret;
 	}
 
@@ -859,10 +840,8 @@ static int determine_csum_type(struct btrfs_fs_info *fs_info, u64 logical,
 	 * The csum item size is smaller than expected csum size, no
 	 * more need to check.
 	 */
-	if (btrfs_item_size(path.nodes[0], path.slots[0]) < csum_size) {
-		btrfs_release_path(&path);
+	if (btrfs_item_size(path.nodes[0], path.slots[0]) < csum_size)
 		return 1;
-	}
 	read_extent_buffer(path.nodes[0], csum_expected,
 			   btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
 			   csum_size);
@@ -1009,7 +988,7 @@ static int resume_csum_change(struct btrfs_fs_info *fs_info, u16 new_csum_type)
 {
 	const u64 super_flags = btrfs_super_flags(fs_info->super_copy);
 	struct btrfs_root *tree_root = fs_info->tree_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -1039,7 +1018,6 @@ static int resume_csum_change(struct btrfs_fs_info *fs_info, u16 new_csum_type)
 	if (ret < 0) {
 		errno = -ret;
 		error("failed to locate the csum change item: %m");
-		btrfs_release_path(&path);
 		return ret;
 	}
 	btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
diff --git a/tune/change-uuid.c b/tune/change-uuid.c
index c24cba7080bf..db63e446ac8f 100644
--- a/tune/change-uuid.c
+++ b/tune/change-uuid.c
@@ -84,7 +84,7 @@ static int change_buffer_header_uuid(struct extent_buffer *eb, uuid_t new_fsid)
 static int change_extent_tree_uuid(struct btrfs_fs_info *fs_info, uuid_t new_fsid)
 {
 	struct btrfs_root *root = btrfs_extent_root(fs_info, 0);
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key = {0, 0, 0};
 	int ret = 0;
 
@@ -94,7 +94,7 @@ static int change_extent_tree_uuid(struct btrfs_fs_info *fs_info, uuid_t new_fsi
 	 */
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	while (1) {
 		struct btrfs_extent_item *ei;
@@ -117,29 +117,22 @@ static int change_extent_tree_uuid(struct btrfs_fs_info *fs_info, uuid_t new_fsi
 		eb = read_tree_block(fs_info, bytenr, &check);
 		if (IS_ERR(eb)) {
 			error("failed to read tree block: %llu", bytenr);
-			ret = PTR_ERR(eb);
-			goto out;
+			return PTR_ERR(eb);
 		}
 		ret = change_buffer_header_uuid(eb, new_fsid);
 		free_extent_buffer(eb);
 		if (ret < 0) {
 			error("failed to change uuid of tree block: %llu",
 				bytenr);
-			goto out;
+			return ret;
 		}
 next:
 		ret = btrfs_next_item(root, &path);
 		if (ret < 0)
-			goto out;
-		if (ret > 0) {
-			ret = 0;
-			goto out;
-		}
+			return ret;
+		if (ret > 0)
+			return 0;
 	}
-
-out:
-	btrfs_release_path(&path);
-	return ret;
 }
 
 static int change_device_uuid(struct extent_buffer *eb, int slot,
@@ -164,14 +157,14 @@ static int change_device_uuid(struct extent_buffer *eb, int slot,
 
 static int change_chunk_tree_uuid(struct btrfs_root *root, uuid_t new_fsid)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key = {0, 0, 0};
 	int ret = 0;
 
 	/* No transaction again */
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	while (1) {
 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
@@ -181,19 +174,14 @@ static int change_chunk_tree_uuid(struct btrfs_root *root, uuid_t new_fsid)
 		ret = change_device_uuid(path.nodes[0], path.slots[0],
 					 new_fsid);
 		if (ret < 0)
-			goto out;
+			return ret;
 next:
 		ret = btrfs_next_item(root, &path);
 		if (ret < 0)
-			goto out;
-		if (ret > 0) {
-			ret = 0;
-			goto out;
-		}
+			return ret;
+		if (ret > 0)
+			return 0;
 	}
-out:
-	btrfs_release_path(&path);
-	return ret;
 }
 
 static int change_fsid_done(struct btrfs_fs_info *fs_info)
diff --git a/tune/convert-remap-tree.c b/tune/convert-remap-tree.c
index 21f1f4e2ffc5..6bc8f14fd7e7 100644
--- a/tune/convert-remap-tree.c
+++ b/tune/convert-remap-tree.c
@@ -98,7 +98,6 @@ static int create_remap_tree(struct btrfs_trans_handle *trans)
 static int extend_block_group_items(struct btrfs_trans_handle *trans)
 {
 	struct btrfs_fs_info *fs_info = trans->fs_info;
-	struct btrfs_path path = { 0 };
 	struct btrfs_key key = { 0 };
 	struct extent_buffer *leaf;
 	struct btrfs_block_group_item_v2 *bgi;
@@ -106,6 +105,8 @@ static int extend_block_group_items(struct btrfs_trans_handle *trans)
 	u32 size;
 
 	while (true) {
+		BTRFS_PATH_AUTO_RELEASE(path);
+
 		ret = btrfs_search_slot(trans, fs_info->block_group_root, &key,
 					&path, -1, 1);
 		if (ret < 0)
@@ -113,10 +114,8 @@ static int extend_block_group_items(struct btrfs_trans_handle *trans)
 
 		leaf = path.nodes[0];
 
-		if (path.slots[0] >= btrfs_header_nritems(leaf)) {
-			btrfs_release_path(&path);
+		if (path.slots[0] >= btrfs_header_nritems(leaf))
 			break;
-		}
 
 		while (path.slots[0] < btrfs_header_nritems(leaf)) {
 			btrfs_item_key_to_cpu(leaf, &key, path.slots[0]);
@@ -143,8 +142,6 @@ static int extend_block_group_items(struct btrfs_trans_handle *trans)
 
 		key.objectid++;
 		key.offset = 0;
-
-		btrfs_release_path(&path);
 	}
 
 	return 0;
@@ -154,7 +151,7 @@ int convert_to_remap_tree(struct btrfs_fs_info *fs_info)
 {
 	struct btrfs_super_block *sb = fs_info->super_copy;
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -166,7 +163,6 @@ int convert_to_remap_tree(struct btrfs_fs_info *fs_info)
 		return ret;
 
 	if (!ret) {
-		btrfs_release_path(&path);
 		error("cannot convert filesystem that has balance already in progress");
 		return -EINVAL;
 	}
diff --git a/tune/quota.c b/tune/quota.c
index 746c0ef0a258..a5f7fb40c73f 100644
--- a/tune/quota.c
+++ b/tune/quota.c
@@ -86,7 +86,7 @@ static int remove_owner_refs(struct btrfs_fs_info *fs_info)
 	struct btrfs_root *extent_root;
 	struct btrfs_key key;
 	struct extent_buffer *leaf;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int slot;
 	int ret;
 
@@ -170,7 +170,7 @@ static int create_qgroup(struct btrfs_fs_info *fs_info,
 			 struct btrfs_trans_handle *trans,
 			 u64 qgroupid)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_root *quota_root = fs_info->quota_root;
 	struct btrfs_key key;
 	int ret;
@@ -195,7 +195,6 @@ static int create_qgroup(struct btrfs_fs_info *fs_info,
 	key.offset = qgroupid;
 	ret = btrfs_insert_empty_item(trans, quota_root, &path, &key,
 				      sizeof(struct btrfs_qgroup_limit_item));
-	btrfs_release_path(&path);
 
 	printf("created qgroup for %llu\n", qgroupid);
 	return ret;
@@ -209,7 +208,7 @@ static int create_qgroups(struct btrfs_fs_info *fs_info,
 		.type = BTRFS_ROOT_REF_KEY,
 		.offset = 0,
 	};
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	int slot;
 	struct btrfs_root *tree_root = fs_info->tree_root;
@@ -218,11 +217,11 @@ static int create_qgroups(struct btrfs_fs_info *fs_info,
 
 	ret = create_qgroup(fs_info, trans, BTRFS_FS_TREE_OBJECTID);
 	if (ret)
-		goto out;
+		return ret;
 
 	ret = btrfs_search_slot_for_read(tree_root, &key, &path, 1, 0);
 	if (ret)
-		goto out;
+		return ret;
 
 	while (1) {
 		slot = path.slots[0];
@@ -231,19 +230,17 @@ static int create_qgroups(struct btrfs_fs_info *fs_info,
 		if (key.type == BTRFS_ROOT_REF_KEY) {
 			ret = create_qgroup(fs_info, trans, key.offset);
 			if (ret)
-				goto out;
+				return ret;
 		}
 		ret = btrfs_next_item(tree_root, &path);
 		if (ret < 0) {
 			error("failed to advance to next item");
-			goto out;
+			return ret;
 		}
 		if (ret)
 			break;
 	}
 
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -254,7 +251,7 @@ int enable_quota(struct btrfs_fs_info *fs_info, bool simple)
 	int super_flags = btrfs_super_incompat_flags(sb);
 	struct btrfs_qgroup_status_item *qsi;
 	struct btrfs_root *quota_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int flags;
 	int ret;
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 05/12] btrfs-progs: cmds: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
                   ` (3 preceding siblings ...)
  2026-10-30  4:15 ` [PATCH 04/12] btrfs-progs: tune: " Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:15 ` [PATCH 06/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for repair, qgroup-verify and mode-common Qu Wenruo
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

Convert the on-stack btrfs_path in cmds/ to BTRFS_PATH_AUTO_RELEASE(), and
drop the btrfs_release_path() calls that only existed as function exit
cleanup.

Where the path is only used inside one loop (clear_uuid_tree(),
rebuild_block_group()), the variable is declared in the loop body, so the
path is released as early as possible.

This also fixes the path leaks on error paths of:

- clear_uuid_tree()
- iterate_csum_root()
- cmd_inspect_dump_tree()

Explicit releases which are not at function exit are kept, including the
one before close_ctree() in cmd_inspect_dump_tree(), as the path must not
outlive the extent buffer cache.

calc_root_size() in inspect-tree-stats.c is intentionally left untouched:
its path only borrows the extent buffers without holding a reference, thus
releasing it would drop references it never took.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 cmds/device.c                   |  11 +--
 cmds/inspect-dump-tree.c        |  10 +--
 cmds/rescue-chunk-recover.c     |  43 ++++-------
 cmds/rescue-fix-data-checksum.c |  10 +--
 cmds/rescue.c                   |   7 +-
 cmds/restore.c                  | 126 +++++++++++++-------------------
 6 files changed, 78 insertions(+), 129 deletions(-)

diff --git a/cmds/device.c b/cmds/device.c
index 8d33569816f4..20e4d79a5fa9 100644
--- a/cmds/device.c
+++ b/cmds/device.c
@@ -712,7 +712,7 @@ static int get_device_stats_offline(struct btrfs_fs_info *fs_info, u64 devid,
 				    struct btrfs_ioctl_get_dev_stats *stats_args)
 {
 	int ret = 0;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_dev_stats_item *stats_item;
 
@@ -721,11 +721,10 @@ static int get_device_stats_offline(struct btrfs_fs_info *fs_info, u64 devid,
 	key.offset = devid;
 	ret = btrfs_search_slot(NULL, fs_info->dev_root, &key, &path, 0, 0);
 	if (ret < 0)
-		goto out;
+		return ret;
 	if (ret > 0) {
 		pr_debug("no device stats found for devid %llu\n", devid);
-		ret = 0;
-		goto out;
+		return 0;
 	}
 
 	memset(stats_args->values, 0, stats_args->nr_items * sizeof(u64));
@@ -742,9 +741,7 @@ static int get_device_stats_offline(struct btrfs_fs_info *fs_info, u64 devid,
 	stats_args->values[BTRFS_DEV_STAT_GENERATION_ERRS] =
 		btrfs_dev_stats_value(path.nodes[0], stats_item, BTRFS_DEV_STAT_GENERATION_ERRS);
 
-out:
-	btrfs_release_path(&path);
-	return ret;
+	return 0;
 }
 
 /*
diff --git a/cmds/inspect-dump-tree.c b/cmds/inspect-dump-tree.c
index 424c7ec292b3..09ac9e181376 100644
--- a/cmds/inspect-dump-tree.c
+++ b/cmds/inspect-dump-tree.c
@@ -272,7 +272,7 @@ static int cmd_inspect_dump_tree(const struct cmd_struct *cmd,
 {
 	struct btrfs_root *root;
 	struct btrfs_fs_info *info;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_root_item ri;
 	struct extent_buffer *leaf;
@@ -364,7 +364,7 @@ static int cmd_inspect_dump_tree(const struct cmd_struct *cmd,
 			block_bytenr = arg_strtou64(optarg);
 			ret = dump_add_tree_block(&block_root, block_bytenr);
 			if (ret < 0)
-				goto out;
+				return !!ret;
 			break;
 		case 't':
 			ret = parse_tree_id(optarg, &tree_id);
@@ -412,7 +412,7 @@ static int cmd_inspect_dump_tree(const struct cmd_struct *cmd,
 	info = open_ctree_fs_info(&oca);
 	if (!info) {
 		error("unable to open %s", argv[optind]);
-		goto out;
+		return !!ret;
 	}
 
 	print_mode = follow | traverse | csum_mode;
@@ -426,7 +426,7 @@ static int cmd_inspect_dump_tree(const struct cmd_struct *cmd,
 	root = info->fs_root;
 	if (!root) {
 		error("unable to open %s", argv[optind]);
-		goto out;
+		return !!ret;
 	}
 
 	if (!(extent_only || uuid_tree_only || tree_id)) {
@@ -698,8 +698,8 @@ no_node:
 	uuid_unparse(info->super_copy->fsid, uuidbuf);
 	pr_default("uuid %s\n", uuidbuf);
 close_root:
+	btrfs_release_path(&path);
 	ret = close_ctree(root);
-out:
 	return !!ret;
 }
 DEFINE_SIMPLE_COMMAND(inspect_dump_tree, "dump-tree");
diff --git a/cmds/rescue-chunk-recover.c b/cmds/rescue-chunk-recover.c
index e00ec66791e2..e879208c462b 100644
--- a/cmds/rescue-chunk-recover.c
+++ b/cmds/rescue-chunk-recover.c
@@ -584,7 +584,7 @@ static int check_chunk_by_metadata(struct recover_control *rc,
 	int ret;
 	int i;
 	int slot;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_root *dev_root;
 	struct stripe *stripe;
@@ -607,14 +607,12 @@ static int check_chunk_by_metadata(struct recover_control *rc,
 		if (ret < 0) {
 			fprintf(stderr, "Search device extent failed(%d)\n",
 				ret);
-			btrfs_release_path(&path);
 			return ret;
 		} else if (ret > 0) {
 			if (rc->verbose)
 				fprintf(stderr,
 					"No device extent[%llu, %llu]\n",
 					stripe->devid, stripe->offset);
-			btrfs_release_path(&path);
 			return -ENOENT;
 		}
 		l = path.nodes[0];
@@ -629,7 +627,6 @@ static int check_chunk_by_metadata(struct recover_control *rc,
 								dev_extent),
 					btrfs_dev_extent_length(l, dev_extent),
 					chunk->offset, chunk->length);
-			btrfs_release_path(&path);
 			return -ENOENT;
 		}
 		btrfs_release_path(&path);
@@ -645,13 +642,11 @@ bg_check:
 				0, 0);
 	if (ret < 0) {
 		fprintf(stderr, "Search block group failed(%d)\n", ret);
-		btrfs_release_path(&path);
 		return ret;
 	} else if (ret > 0) {
 		if (rc->verbose)
 			fprintf(stderr, "No block group[%llu, %llu]\n",
 				key.objectid, key.offset);
-		btrfs_release_path(&path);
 		return -ENOENT;
 	}
 
@@ -664,10 +659,8 @@ bg_check:
 				"Chunk[%llu, %llu]'s type(%llu) is different with Block Group's type(%llu)\n",
 				chunk->offset, chunk->length, chunk->type_flags,
 				btrfs_block_group_flags(l, bg_ptr));
-		btrfs_release_path(&path);
 		return -ENOENT;
 	}
-	btrfs_release_path(&path);
 	return 0;
 }
 
@@ -1028,7 +1021,7 @@ static int block_group_remove_all_extent_items(struct btrfs_trans_handle *trans,
 {
 	struct btrfs_fs_info *fs_info = root->fs_info;
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	u64 start = bg->objectid;
 	u64 end = bg->objectid + bg->offset;
@@ -1046,7 +1039,7 @@ static int block_group_remove_all_extent_items(struct btrfs_trans_handle *trans,
 again:
 	ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
 	if (ret < 0)
-		goto err;
+		return ret;
 	else if (ret > 0)
 		ret = 0;
 
@@ -1054,22 +1047,19 @@ again:
 	nitems = btrfs_header_nritems(leaf);
 	if (!nitems) {
 		/* The tree is empty. */
-		ret = 0;
-		goto err;
+		return 0;
 	}
 
 	if (path.slots[0] >= nitems) {
 		ret = btrfs_next_leaf(root, &path);
 		if (ret < 0)
-			goto err;
-		if (ret > 0) {
-			ret = 0;
-			goto err;
-		}
+			return ret;
+		if (ret > 0)
+			return 0;
 		leaf = path.nodes[0];
 		btrfs_item_key_to_cpu(leaf, &key, 0);
 		if (key.objectid >= end)
-			goto err;
+			return ret;
 		btrfs_release_path(&path);
 		goto again;
 	}
@@ -1106,7 +1096,7 @@ again:
 	if (del_nr) {
 		ret = btrfs_del_items(trans, root, &path, del_s, del_nr);
 		if (ret)
-			goto err;
+			return ret;
 	}
 
 	if (key.objectid < end) {
@@ -1118,8 +1108,6 @@ again:
 		btrfs_release_path(&path);
 		goto again;
 	}
-err:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -1425,7 +1413,6 @@ static int rebuild_block_group(struct btrfs_trans_handle *trans,
 	struct btrfs_fs_info *fs_info = root->fs_info;
 	struct chunk_record *chunk_rec;
 	struct btrfs_key search_key;
-	struct btrfs_path path = { 0 };
 	u64 used = 0;
 	int ret = 0;
 
@@ -1433,13 +1420,15 @@ static int rebuild_block_group(struct btrfs_trans_handle *trans,
 		return 0;
 
 	list_for_each_entry(chunk_rec, &rc->rebuild_chunks, list) {
+		BTRFS_PATH_AUTO_RELEASE(path);
+
 		search_key.objectid = chunk_rec->offset;
 		search_key.type = BTRFS_EXTENT_ITEM_KEY;
 		search_key.offset = 0;
 		root = btrfs_extent_root(fs_info, chunk_rec->offset);
 		ret = btrfs_search_slot(NULL, root, &search_key, &path, 0, 0);
 		if (ret < 0)
-			goto out;
+			return ret;
 		ret = calculate_bg_used(root, chunk_rec, &path, &used);
 		/*
 		 * Extent tree is damaged, better to rebuild the whole extent
@@ -1458,10 +1447,8 @@ static int rebuild_block_group(struct btrfs_trans_handle *trans,
 		btrfs_release_path(&path);
 		ret = __insert_block_group(trans, chunk_rec, root, used);
 		if (ret < 0)
-			goto out;
+			return ret;
 	}
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -2008,7 +1995,7 @@ static int rebuild_raid_data_chunk_stripes(struct recover_control *rc,
 	int i;
 	int ret = 0;
 	int slot;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key prev_key;
 	struct btrfs_key key;
 	struct btrfs_root *csum_root;
@@ -2131,7 +2118,6 @@ next_csum:
 		num_unordered = count_devext_records(&unordered);
 		if (chunk->type_flags & BTRFS_BLOCK_GROUP_RAID6
 					&& num_unordered == 2) {
-			btrfs_release_path(&path);
 			ret = fill_chunk_up(chunk, &unordered, rc);
 			return ret;
 		}
@@ -2186,7 +2172,6 @@ fail_out:
 	ret = !!ret || (list_empty(&unordered) ? 0 : 1);
 	list_splice_init(&candidates, &chunk->dextents);
 	list_splice_init(&unordered, &chunk->dextents);
-	btrfs_release_path(&path);
 
 	return ret;
 }
diff --git a/cmds/rescue-fix-data-checksum.c b/cmds/rescue-fix-data-checksum.c
index 121fb0b46ca8..b5927280b190 100644
--- a/cmds/rescue-fix-data-checksum.c
+++ b/cmds/rescue-fix-data-checksum.c
@@ -190,7 +190,7 @@ static int print_filenames(u64 ino, u64 offset, u64 rootid, void *ctx)
 	struct btrfs_root *root;
 	struct btrfs_key key;
 	struct inode_fs_paths *ipath;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret;
 
 	key.objectid = rootid;
@@ -230,7 +230,7 @@ out:
 
 static int iterate_csum_root(struct btrfs_fs_info *fs_info, struct btrfs_root *csum_root)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -263,7 +263,6 @@ next:
 			error("failed to get next csum item: %m");
 		}
 	}
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -333,7 +332,7 @@ static int update_csum_item(struct btrfs_fs_info *fs_info, u64 logical,
 {
 	struct btrfs_trans_handle *trans;
 	struct btrfs_root *csum_root = btrfs_csum_root(fs_info, logical);
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_csum_item *citem;
 	u64 read_len = fs_info->sectorsize;
 	u8 csum[BTRFS_CSUM_SIZE] = { 0 };
@@ -378,7 +377,6 @@ static int update_csum_item(struct btrfs_fs_info *fs_info, u64 logical,
 		logical, mirror);
 out:
 	free(buf);
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -387,7 +385,7 @@ static void report_corrupted_blocks(struct btrfs_fs_info *fs_info,
 				    unsigned int mirror)
 {
 	struct corrupted_block *entry;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	enum fix_data_checksum_action_value action;
 
 	if (list_empty(&corrupted_blocks)) {
diff --git a/cmds/rescue.c b/cmds/rescue.c
index d10dea393a59..a03b0bd537b1 100644
--- a/cmds/rescue.c
+++ b/cmds/rescue.c
@@ -374,7 +374,6 @@ static int clear_uuid_tree(struct btrfs_fs_info *fs_info)
 {
 	struct btrfs_root *uuid_root = fs_info->uuid_root;
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = {};
 	struct btrfs_key key = {};
 	int ret;
 
@@ -387,6 +386,7 @@ static int clear_uuid_tree(struct btrfs_fs_info *fs_info)
 		return PTR_ERR(trans);
 
 	while (1) {
+		BTRFS_PATH_AUTO_RELEASE(path);
 		int nr;
 
 		ret = btrfs_search_slot(trans, uuid_root, &key, &path, -1, 1);
@@ -396,13 +396,10 @@ static int clear_uuid_tree(struct btrfs_fs_info *fs_info)
 		UASSERT(path.slots[0] == 0);
 
 		nr = btrfs_header_nritems(path.nodes[0]);
-		if (nr == 0) {
-			btrfs_release_path(&path);
+		if (nr == 0)
 			break;
-		}
 
 		ret = btrfs_del_items(trans, uuid_root, &path, 0, nr);
-		btrfs_release_path(&path);
 		if (ret < 0)
 			goto out;
 	}
diff --git a/cmds/restore.c b/cmds/restore.c
index b982f2de4699..77de65bf1528 100644
--- a/cmds/restore.c
+++ b/cmds/restore.c
@@ -488,7 +488,7 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode,
 			   int fd, const char *file_name)
 {
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_dir_item *di;
 	u32 name_len = 0;
@@ -574,7 +574,6 @@ static int set_file_xattrs(struct btrfs_root *root, u64 inode,
 	}
 	ret = 0;
 out:
-	btrfs_release_path(&path);
 	free(name);
 	free(data);
 
@@ -584,7 +583,7 @@ out:
 static int copy_metadata(struct btrfs_root *root, int fd,
 		struct btrfs_key *key)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_inode_item *inode_item;
 	int ret;
 
@@ -600,13 +599,13 @@ static int copy_metadata(struct btrfs_root *root, int fd,
 				btrfs_inode_gid(path.nodes[0], inode_item));
 		if (ret) {
 			error("failed to change owner: %m");
-			goto out;
+			return ret;
 		}
 
 		ret = fchmod(fd, btrfs_inode_mode(path.nodes[0], inode_item));
 		if (ret) {
 			error("failed to change mode: %m");
-			goto out;
+			return ret;
 		}
 
 		bts = btrfs_inode_atime(inode_item);
@@ -620,11 +619,9 @@ static int copy_metadata(struct btrfs_root *root, int fd,
 		ret = futimens(fd, times);
 		if (ret) {
 			error("failed to set times: %m");
-			goto out;
+			return ret;
 		}
 	}
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -632,7 +629,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
 		     const char *file)
 {
 	struct extent_buffer *leaf;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_file_extent_item *fi;
 	struct btrfs_inode_item *inode_item;
 	struct btrfs_timespec *bts;
@@ -659,11 +656,11 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
 			ret = fchown(fd, btrfs_inode_uid(path.nodes[0], inode_item),
 					btrfs_inode_gid(path.nodes[0], inode_item));
 			if (ret && !ignore_errors)
-				goto out;
+				return ret;
 
 			ret = fchmod(fd, btrfs_inode_mode(path.nodes[0], inode_item));
 			if (ret && !ignore_errors)
-				goto out;
+				return ret;
 
 			bts = btrfs_inode_atime(inode_item);
 			times[0].tv_sec = btrfs_timespec_sec(path.nodes[0], bts);
@@ -683,7 +680,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
 	ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
 	if (ret < 0) {
 		error("searching extent data returned %d", ret);
-		goto out;
+		return ret;
 	}
 
 	leaf = path.nodes[0];
@@ -691,11 +688,10 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
 		ret = next_leaf(root, &path);
 		if (ret < 0) {
 			error("cannot get next leaf: %d", ret);
-			goto out;
+			return ret;
 		} else if (ret > 0) {
 			/* No more leaves to search */
-			ret = 0;
-			goto out;
+			return 0;
 		}
 		leaf = path.nodes[0];
 	}
@@ -706,7 +702,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
 				ret = next_leaf(root, &path);
 				if (ret < 0) {
 					error("search to next leaf failed: %d", ret);
-					goto out;
+					return ret;
 				} else if (ret) {
 					/* No more leaves to search */
 					btrfs_release_path(&path);
@@ -728,8 +724,7 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
 		if (compression >= BTRFS_NR_COMPRESS_TYPES) {
 			warning("compression type %d not supported",
 				compression);
-			ret = -1;
-			goto out;
+			return -1;
 		}
 
 		if (extent_type == BTRFS_FILE_EXTENT_PREALLOC)
@@ -737,12 +732,12 @@ static int copy_file(struct btrfs_root *root, int fd, struct btrfs_key *key,
 		if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
 			ret = copy_one_inline(root, fd, &path, found_key.offset);
 			if (ret)
-				goto out;
+				return ret;
 		} else if (extent_type == BTRFS_FILE_EXTENT_REG) {
 			ret = copy_one_extent(root, fd, leaf, fi,
 					      found_key.offset);
 			if (ret)
-				goto out;
+				return ret;
 		} else {
 			warning("weird extent type %d", extent_type);
 		}
@@ -768,9 +763,6 @@ set_size:
 			return ret;
 	}
 	return 0;
-
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -809,7 +801,7 @@ static int overwrite_ok(const char * path)
 static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
 		     const char *file)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_file_extent_item *extent_item;
 	struct btrfs_inode_item *inode_item;
@@ -836,13 +828,12 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
 	key->offset = 0;
 	ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	leaf = path.nodes[0];
 	if (!leaf) {
 		error("failed to get leaf for symlink '%s'", file);
-		ret = -1;
-		goto out;
+		return -1;
 	}
 
 	extent_item = btrfs_item_ptr(leaf, path.slots[0],
@@ -852,8 +843,7 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
 	if (len >= PATH_MAX) {
 		error("symlink '%s' target length %d is longer than PATH_MAX",
 				fs_name, len);
-		ret = -1;
-		goto out;
+		return -1;
 	}
 
 	name_offset = (unsigned long) extent_item
@@ -866,7 +856,7 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
 		ret = symlink(symlink_target, path_name);
 		if (ret < 0) {
 			error("failed to restore symlink '%s': %m", path_name);
-			goto out;
+			return ret;
 		}
 	}
 
@@ -875,7 +865,7 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
 
 	ret = 0;
 	if (!restore_metadata)
-		goto out;
+		return ret;
 
 	/*
 	 * Symlink metadata operates differently than files/directories, so do
@@ -889,7 +879,7 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
 	ret = btrfs_lookup_inode(NULL, root, &path, key, 0);
 	if (ret) {
 		error("failed to lookup inode for '%s'", file);
-		goto out;
+		return ret;
 	}
 
 	inode_item = btrfs_item_ptr(path.nodes[0], path.slots[0],
@@ -900,7 +890,7 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
 				   AT_SYMLINK_NOFOLLOW);
 	if (ret) {
 		error("failed to change owner of '%s': %m", file);
-		goto out;
+		return ret;
 	}
 
 	bts = btrfs_inode_atime(inode_item);
@@ -914,8 +904,6 @@ static int copy_symlink(struct btrfs_root *root, struct btrfs_key *key,
 	ret = utimensat(AT_FDCWD, file, times, AT_SYMLINK_NOFOLLOW);
 	if (ret)
 		error("failed to set times for '%s': %m", file);
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -923,7 +911,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 		      const char *output_rootdir, const char *in_dir,
 		      const regex_t *mreg)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_dir_item *dir_item;
 	struct btrfs_key found_key, location;
@@ -939,7 +927,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 	ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
 	if (ret < 0) {
 		error("search for next directory entry failed: %d", ret);
-		goto out;
+		return ret;
 	}
 
 	ret = 0;
@@ -950,12 +938,11 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 		ret = next_leaf(root, &path);
 		if (ret < 0) {
 			error("search for next leaf failed: %d", ret);
-			goto out;
+			return ret;
 		} else if (ret > 0) {
 			/* No more leaves to search */
 			pr_info("Reached the end of the tree looking for the directory\n");
-			ret = 0;
-			goto out;
+			return 0;
 		}
 		leaf = path.nodes[0];
 	}
@@ -966,12 +953,11 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 				ret = next_leaf(root, &path);
 				if (ret < 0) {
 					error("search for next leaf failed: %d", ret);
-					goto out;
+					return ret;
 				} else if (ret > 0) {
 					/* No more leaves to search */
 					pr_info("Reached the end of the tree searching the directory\n");
-					ret = 0;
-					goto out;
+					return 0;
 				}
 				leaf = path.nodes[0];
 			} while (!leaf);
@@ -1001,7 +987,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 		ret = path_cat_out(fs_name, in_dir, filename);
 		if (ret < 0) {
 			error("invalid file path %s/%s", in_dir, filename);
-			goto out;
+			return ret;
 		}
 
 		if (mreg && REG_NOMATCH == regexec(mreg, fs_name, 0, NULL, 0))
@@ -1011,7 +997,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 		ret = path_cat_out(path_name, output_rootdir, fs_name);
 		if (ret < 0) {
 			error("invalid full path %s%s", output_rootdir, fs_name);
-			goto out;
+			return ret;
 		}
 
 		/*
@@ -1029,8 +1015,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 				error("creating '%s' failed: %m", path_name);
 				if (ignore_errors)
 					goto next;
-				ret = -1;
-				goto out;
+				return -1;
 			}
 			ret = copy_file(root, fd, &location, path_name);
 			close(fd);
@@ -1038,7 +1023,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 				error("copying data for %s failed", path_name);
 				if (ignore_errors)
 					goto next;
-				goto out;
+				return ret;
 			}
 		} else if (type == BTRFS_FT_DIR) {
 			struct btrfs_root *search_root = root;
@@ -1046,8 +1031,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 
 			if (!dir) {
 				error_mem(NULL);
-				ret = -ENOMEM;
-				goto out;
+				return -ENOMEM;
 			}
 
 			if (location.type == BTRFS_ROOT_ITEM_KEY) {
@@ -1070,8 +1054,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 						path_name, PTR_ERR(search_root));
 					if (ignore_errors)
 						goto next;
-					ret = PTR_ERR(search_root);
-					goto out;
+					return PTR_ERR(search_root);
 				}
 
 				/*
@@ -1099,8 +1082,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 				error("failed mkdir %s: %m", path_name);
 				if (ignore_errors)
 					goto next;
-				ret = -1;
-				goto out;
+				return -1;
 			}
 			ret = search_dir(search_root, &location,
 					 output_rootdir, dir, mreg);
@@ -1110,7 +1092,7 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 					path_name, ret);
 				if (ignore_errors)
 					goto next;
-				goto out;
+				return ret;
 			}
 		} else if (type == BTRFS_FT_SYMLINK) {
 			if (restore_symlinks)
@@ -1118,7 +1100,6 @@ static int search_dir(struct btrfs_root *root, struct btrfs_key *key,
 			if (ret < 0) {
 				if (ignore_errors)
 					goto next;
-				btrfs_release_path(&path);
 				return ret;
 			}
 		}
@@ -1130,16 +1111,14 @@ next:
 		ret = path_cat_out(path_name, output_rootdir, in_dir);
 		if (ret < 0) {
 			error("invalid path %s%s", output_rootdir, in_dir);
-			goto out;
+			return ret;
 		}
 		fd = open(path_name, O_RDONLY);
 		if (fd < 0) {
 			error("failed to access '%s' to restore metadata/xattrs: %m",
 					path_name);
-			if (!ignore_errors) {
-				ret = -1;
-				goto out;
-			}
+			if (!ignore_errors)
+				return -1;
 		} else {
 			/*
 			 * Set owner/mode/time on the directory as well
@@ -1149,7 +1128,7 @@ next:
 				ret = copy_metadata(root, fd, key);
 				if (ret && !ignore_errors) {
 					close(fd);
-					goto out;
+					return ret;
 				}
 			}
 
@@ -1162,13 +1141,11 @@ next:
 			}
 			close(fd);
 			if (ret && !ignore_errors)
-				goto out;
+				return ret;
 		}
 	}
 
 	pr_info("Done searching %s\n", in_dir);
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -1177,7 +1154,7 @@ static int do_list_roots(struct btrfs_root *root)
 	struct btrfs_key key;
 	struct btrfs_key found_key;
 	struct btrfs_disk_key disk_key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_root_item ri;
 	unsigned long offset;
@@ -1192,7 +1169,6 @@ static int do_list_roots(struct btrfs_root *root)
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 	if (ret < 0) {
 		error("failed search next root item: %d", ret);
-		btrfs_release_path(&path);
 		return -1;
 	}
 
@@ -1222,7 +1198,6 @@ static int do_list_roots(struct btrfs_root *root)
 		       btrfs_root_level(&ri));
 		path.slots[0]++;
 	}
-	btrfs_release_path(&path);
 
 	return 0;
 }
@@ -1306,7 +1281,7 @@ static struct btrfs_root *open_fs(const char *dev, u64 root_location,
 
 static int find_first_dir(struct btrfs_root *root, u64 *objectid)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key found_key;
 	struct btrfs_key key;
 	int ret = -1;
@@ -1318,12 +1293,12 @@ static int find_first_dir(struct btrfs_root *root, u64 *objectid)
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 	if (ret < 0) {
 		error("searching next directory entry failed: %d", ret);
-		goto out;
+		return ret;
 	}
 
 	if (!path.nodes[0]) {
 		error("no leaf when looking for directory");
-		goto out;
+		return ret;
 	}
 again:
 	for (i = path.slots[0];
@@ -1335,24 +1310,21 @@ again:
 		printf("Using objectid %llu for first dir\n",
 		       found_key.objectid);
 		*objectid = found_key.objectid;
-		ret = 0;
-		goto out;
+		return 0;
 	}
 	do {
 		ret = next_leaf(root, &path);
 		if (ret < 0) {
 			error("search for next leaf failed: %d", ret);
-			goto out;
+			return ret;
 		} else if (ret > 0) {
 			error("no more leaves to search");
-			goto out;
+			return ret;
 		}
 	} while (!path.nodes[0]);
 	if (path.nodes[0])
 		goto again;
 	printf("Couldn't find a dir index item\n");
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 06/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for repair, qgroup-verify and mode-common
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
                   ` (4 preceding siblings ...)
  2026-10-30  4:15 ` [PATCH 05/12] btrfs-progs: cmds: " Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:15 ` [PATCH 07/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for lowmem mode Qu Wenruo
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

Convert all on-stack btrfs_path in check/repair.c, check/qgroup-verify.c
and check/mode-common.c to BTRFS_PATH_AUTO_RELEASE(), and drop the
btrfs_release_path() calls that only existed as function exit cleanup.

This also fixes the path leaks on error paths of:

- populate_used_from_extent_root()
- get_num_devs_in_chunk_tree()

Explicit releases which are not at function exit are kept.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 check/mode-common.c   | 135 +++++++++++++++---------------------------
 check/qgroup-verify.c |  29 ++++-----
 check/repair.c        |   5 +-
 3 files changed, 62 insertions(+), 107 deletions(-)

diff --git a/check/mode-common.c b/check/mode-common.c
index eb82bb99e827..7d829661eb3d 100644
--- a/check/mode-common.c
+++ b/check/mode-common.c
@@ -55,17 +55,15 @@ static int check_prealloc_data_ref(u64 disk_bytenr,
 	u64 offset = btrfs_extent_data_ref_offset(eb, dref);
 	struct btrfs_root *root;
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret;
 
 	key.objectid = rootid;
 	key.type = BTRFS_ROOT_ITEM_KEY;
 	key.offset = (u64)-1;
 	root = btrfs_read_fs_root(gfs_info, &key);
-	if (IS_ERR(root)) {
-		ret = PTR_ERR(root);
-		goto out;
-	}
+	if (IS_ERR(root))
+		return PTR_ERR(root);
 
 	key.objectid = objectid;
 	key.type = BTRFS_EXTENT_DATA_KEY;
@@ -78,7 +76,7 @@ static int check_prealloc_data_ref(u64 disk_bytenr,
 		ret = -ENOENT;
 	}
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	while (true) {
 		struct btrfs_file_extent_item *fi;
@@ -87,7 +85,7 @@ static int check_prealloc_data_ref(u64 disk_bytenr,
 		if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
 			ret = btrfs_next_leaf(root, &path);
 			if (ret < 0)
-				goto out;
+				return ret;
 			if (ret > 0)
 				break;
 		}
@@ -108,16 +106,12 @@ static int check_prealloc_data_ref(u64 disk_bytenr,
 		    disk_bytenr)
 			break;
 
-		if (extent_type == BTRFS_FILE_EXTENT_REG) {
-			ret = 1;
-			goto out;
-		}
+		if (extent_type == BTRFS_FILE_EXTENT_REG)
+			return 1;
 next:
 		path.slots[0]++;
 	}
 	ret = 0;
- out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -193,7 +187,7 @@ int check_prealloc_extent_written(u64 disk_bytenr, u64 num_bytes)
 {
 	struct btrfs_root *extent_root = btrfs_extent_root(gfs_info,
 							   disk_bytenr);
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 	struct btrfs_extent_item *ei;
@@ -213,7 +207,7 @@ int check_prealloc_extent_written(u64 disk_bytenr, u64 num_bytes)
 		ret = -ENOENT;
 	}
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	/* First check all inline refs. */
 	ei = btrfs_item_ptr(path.nodes[0], path.slots[0],
@@ -237,7 +231,7 @@ int check_prealloc_extent_written(u64 disk_bytenr, u64 num_bytes)
 			ret = check_prealloc_data_ref(disk_bytenr,
 						      dref, path.nodes[0]);
 			if (ret != 0)
-				goto out;
+				return ret;
 		} else if (type == BTRFS_SHARED_DATA_REF_KEY) {
 			u64 parent;
 
@@ -246,7 +240,7 @@ int check_prealloc_extent_written(u64 disk_bytenr, u64 num_bytes)
 			ret = check_prealloc_shared_data_ref(parent,
 							     disk_bytenr);
 			if (ret != 0)
-				goto out;
+				return ret;
 		}
 
 		ptr += btrfs_extent_inline_ref_size(type);
@@ -258,7 +252,7 @@ int check_prealloc_extent_written(u64 disk_bytenr, u64 num_bytes)
 		if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
 			ret = btrfs_next_leaf(extent_root, &path);
 			if (ret < 0)
-				goto out;
+				return ret;
 			if (ret > 0) {
 				ret = 0;
 				break;
@@ -277,18 +271,16 @@ int check_prealloc_extent_written(u64 disk_bytenr, u64 num_bytes)
 			ret = check_prealloc_data_ref(disk_bytenr,
 						      dref, path.nodes[0]);
 			if (ret != 0)
-				goto out;
+				return ret;
 		} else if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
 			ret = check_prealloc_shared_data_ref(key.offset,
 							     disk_bytenr);
 			if (ret != 0)
-				goto out;
+				return ret;
 		}
 
 		path.slots[0]++;
 	}
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -305,7 +297,7 @@ int count_csum_range(u64 start, u64 len, u64 *found)
 {
 	struct btrfs_root *csum_root = btrfs_csum_root(gfs_info, start);
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	int ret;
 	size_t size;
@@ -364,7 +356,6 @@ int count_csum_range(u64 start, u64 len, u64 *found)
 		path.slots[0]++;
 	}
 out:
-	btrfs_release_path(&path);
 	if (ret < 0)
 		return ret;
 	return 0;
@@ -653,7 +644,7 @@ int delete_corrupted_dir_item(struct btrfs_trans_handle *trans,
 			      u32 namelen)
 {
 	struct btrfs_dir_item *di_item;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret;
 
 	ret = btrfs_search_slot(trans, root, di_key, &path, 0, 1);
@@ -661,13 +652,12 @@ int delete_corrupted_dir_item(struct btrfs_trans_handle *trans,
 		error("key (%llu %u %llu) doesn't exist in root %llu",
 			di_key->objectid, di_key->type, di_key->offset,
 			root->root_key.objectid);
-		ret = -ENOENT;
-		goto out;
+		return -ENOENT;
 	}
 	if (ret < 0) {
 		error("failed to search root %llu: %d",
 			root->root_key.objectid, ret);
-		goto out;
+		return ret;
 	}
 
 	di_item = btrfs_match_dir_item_name(root, &path, namebuf, namelen);
@@ -679,15 +669,11 @@ int delete_corrupted_dir_item(struct btrfs_trans_handle *trans,
 		error("no dir item named '%s' found with key (%llu %u %llu)",
 			namebuf, di_key->objectid, di_key->type,
 			di_key->offset);
-		ret = -ENOENT;
-		goto out;
+		return -ENOENT;
 	}
 	ret = btrfs_delete_one_dir_name(trans, root, &path, di_item);
 	if (ret < 0)
 		error("failed to delete one dir name: %d", ret);
-
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -734,7 +720,7 @@ static int find_file_type_dir_index(struct btrfs_root *root, u64 ino, u64 dirid,
 				    u64 index, const char *name, u32 name_len,
 				    u32 *imode_ret)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_key location;
 	struct btrfs_dir_item *di;
@@ -775,7 +761,6 @@ static int find_file_type_dir_index(struct btrfs_root *root, u64 ino, u64 dirid,
 	found = true;
 	*imode_ret = btrfs_type_to_imode(filetype);
 out:
-	btrfs_release_path(&path);
 	if (!found && !ret)
 		ret = -ENOENT;
 	return ret;
@@ -785,7 +770,7 @@ static int find_file_type_dir_item(struct btrfs_root *root, u64 ino, u64 dirid,
 				   const char *name, u32 name_len,
 				   u32 *imode_ret)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_key location;
 	struct btrfs_dir_item *di;
@@ -837,7 +822,6 @@ static int find_file_type_dir_item(struct btrfs_root *root, u64 ino, u64 dirid,
 		goto out;
 	}
 out:
-	btrfs_release_path(&path);
 	if (!found && !ret)
 		ret = -ENOENT;
 	return ret;
@@ -1052,7 +1036,7 @@ int check_repair_free_space_inode(struct btrfs_path *path)
 
 int recow_extent_buffer(struct btrfs_root *root, struct extent_buffer *eb)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_trans_handle *trans;
 	struct btrfs_key key;
 	int ret;
@@ -1085,7 +1069,6 @@ int recow_extent_buffer(struct btrfs_root *root, struct extent_buffer *eb)
 
 	ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
 	btrfs_commit_transaction(trans, root);
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -1099,7 +1082,7 @@ int get_extent_item_generation(u64 bytenr, u64 *gen_ret)
 {
 	struct btrfs_root *root = btrfs_extent_root(gfs_info, bytenr);
 	struct btrfs_extent_item *ei;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -1112,12 +1095,12 @@ int get_extent_item_generation(u64 bytenr, u64 *gen_ret)
 	if (ret == 0)
 		ret = -EUCLEAN;
 	if (ret < 0)
-		goto out;
+		return ret;
 	ret = btrfs_previous_extent_item(root, &path, bytenr);
 	if (ret > 0)
 		ret = -ENOENT;
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	ei = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_extent_item);
 
@@ -1142,8 +1125,6 @@ int get_extent_item_generation(u64 bytenr, u64 *gen_ret)
 		 */
 		ret = -ENOTSUP;
 	}
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -1232,7 +1213,7 @@ static int fill_csum_tree_from_one_fs_root(struct btrfs_trans_handle *trans,
 					   struct btrfs_root *cur_root)
 {
 	struct btrfs_root *csum_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct extent_buffer *node;
 	struct btrfs_file_extent_item *fi;
@@ -1331,14 +1312,13 @@ next:
 	}
 
 out:
-	btrfs_release_path(&path);
 	free(buf);
 	return ret;
 }
 
 static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_root *tree_root = gfs_info->tree_root;
 	struct btrfs_root *cur_root;
 	struct extent_buffer *node;
@@ -1351,18 +1331,16 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans)
 	key.offset = 0;
 	ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
 	if (ret < 0)
-		goto out;
-	if (ret > 0) {
-		ret = -ENOENT;
-		goto out;
-	}
+		return ret;
+	if (ret > 0)
+		return -ENOENT;
 
 	while (1) {
 		node = path.nodes[0];
 		slot = path.slots[0];
 		btrfs_item_key_to_cpu(node, &key, slot);
 		if (key.objectid > BTRFS_LAST_FREE_OBJECTID)
-			goto out;
+			return ret;
 		if (key.type != BTRFS_ROOT_ITEM_KEY)
 			goto next;
 		if (!is_fstree(key.objectid))
@@ -1373,23 +1351,18 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans)
 		if (IS_ERR(cur_root) || !cur_root) {
 			fprintf(stderr, "Fail to read fs/subvol tree: %lld\n",
 				key.objectid);
-			goto out;
+			return ret;
 		}
 		ret = fill_csum_tree_from_one_fs_root(trans, cur_root);
 		if (ret < 0)
-			goto out;
+			return ret;
 next:
 		ret = btrfs_next_item(tree_root, &path);
-		if (ret > 0) {
-			ret = 0;
-			goto out;
-		}
+		if (ret > 0)
+			return 0;
 		if (ret < 0)
-			goto out;
+			return ret;
 	}
-
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -1399,7 +1372,7 @@ static int remove_csum_for_file_extent(u64 ino, u64 offset, u64 rootid, void *ct
 	struct btrfs_fs_info *fs_info = trans->fs_info;
 	struct btrfs_file_extent_item *fi;
 	struct btrfs_inode_item *ii;
-	struct btrfs_path path = {};
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_root *root;
 	bool nocsum = false;
@@ -1412,10 +1385,8 @@ static int remove_csum_for_file_extent(u64 ino, u64 offset, u64 rootid, void *ct
 	key.type = BTRFS_ROOT_ITEM_KEY;
 	key.offset = (u64)-1;
 	root = btrfs_read_fs_root(fs_info, &key);
-	if (IS_ERR(root)) {
-		ret = PTR_ERR(root);
-		goto out;
-	}
+	if (IS_ERR(root))
+		return PTR_ERR(root);
 
 	/* Check if the inode has NODATASUM flag */
 	key.objectid = ino;
@@ -1425,7 +1396,7 @@ static int remove_csum_for_file_extent(u64 ino, u64 offset, u64 rootid, void *ct
 	if (ret > 0)
 		ret = -ENOENT;
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	ii = btrfs_item_ptr(path.nodes[0], path.slots[0],
 			    struct btrfs_inode_item);
@@ -1442,24 +1413,24 @@ static int remove_csum_for_file_extent(u64 ino, u64 offset, u64 rootid, void *ct
 	if (ret > 0)
 		ret = -ENOENT;
 	if (ret < 0)
-		goto out;
+		return ret;
 	fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
 			    struct btrfs_file_extent_item);
 	type = btrfs_file_extent_type(path.nodes[0], fi);
 
 	if (btrfs_file_extent_disk_bytenr(path.nodes[0], fi) == 0)
-		goto out;
+		return ret;
 
 	/* Compressed extent should have csum, skip it */
 	if (btrfs_file_extent_compression(path.nodes[0], fi) !=
 	    BTRFS_COMPRESS_NONE)
-		goto out;
+		return ret;
 	/*
 	 * We only want to delete the csum range if the inode has NODATASUM
 	 * flag or it's a preallocated extent.
 	 */
 	if (!(nocsum || type == BTRFS_FILE_EXTENT_PREALLOC))
-		goto out;
+		return ret;
 
 	/* If NODATASUM, we need to remove all csum for the extent */
 	if (nocsum) {
@@ -1475,8 +1446,6 @@ static int remove_csum_for_file_extent(u64 ino, u64 offset, u64 rootid, void *ct
 	/* Now delete the csum for the preallocated or nodatasum range */
 	root = btrfs_csum_root(fs_info, disk_bytenr);
 	ret = btrfs_del_csums(trans, root, disk_bytenr, disk_len);
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -1484,7 +1453,7 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans,
 				      struct btrfs_root *extent_root)
 {
 	struct btrfs_root *csum_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_extent_item *ei;
 	struct extent_buffer *leaf;
 	char *buf;
@@ -1495,16 +1464,12 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans,
 	key.type = BTRFS_EXTENT_ITEM_KEY;
 	key.offset = 0;
 	ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
-	if (ret < 0) {
-		btrfs_release_path(&path);
+	if (ret < 0)
 		return ret;
-	}
 
 	buf = malloc(gfs_info->sectorsize);
-	if (!buf) {
-		btrfs_release_path(&path);
+	if (!buf)
 		return -ENOMEM;
-	}
 
 	while (1) {
 		if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
@@ -1557,7 +1522,6 @@ static int fill_csum_tree_from_extent(struct btrfs_trans_handle *trans,
 		path.slots[0]++;
 	}
 
-	btrfs_release_path(&path);
 	free(buf);
 	return ret;
 }
@@ -1597,7 +1561,7 @@ int fill_csum_tree(struct btrfs_trans_handle *trans, bool search_fs_tree)
 static int get_num_devs_in_chunk_tree(struct btrfs_fs_info *fs_info)
 {
 	struct btrfs_root *chunk_root = fs_info->chunk_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key = { 0 };
 	int found_devs = 0;
 	int ret;
@@ -1630,7 +1594,6 @@ static int get_num_devs_in_chunk_tree(struct btrfs_fs_info *fs_info)
 		}
 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 	}
-	btrfs_release_path(&path);
 	if (ret < 0)
 		return ret;
 	return found_devs;
@@ -1677,7 +1640,7 @@ int repair_subvol_orphan_item(struct btrfs_fs_info *fs_info, u64 rootid)
 {
 	struct btrfs_root *tree_root = fs_info->tree_root;
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret;
 
 	trans = btrfs_start_transaction(tree_root, 1);
diff --git a/check/qgroup-verify.c b/check/qgroup-verify.c
index 63902ffa6f5b..b58ef36669b2 100644
--- a/check/qgroup-verify.c
+++ b/check/qgroup-verify.c
@@ -993,7 +993,7 @@ static int load_quota_info(struct btrfs_fs_info *info)
 {
 	int ret;
 	struct btrfs_root *root = info->quota_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct extent_buffer *leaf;
 
@@ -1008,7 +1008,7 @@ static int load_quota_info(struct btrfs_fs_info *info)
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 	if (ret < 0) {
 		error("couldn't search slot: %d", ret);
-		goto out;
+		return ret;
 	}
 
 	while (1) {
@@ -1016,17 +1016,15 @@ static int load_quota_info(struct btrfs_fs_info *info)
 
 		ret = load_one_quota_leaf(leaf);
 		if (ret < 0)
-			goto out;
+			return ret;
 		ret = btrfs_next_leaf(root, &path);
 		if (ret < 0)
-			goto out;
+			return ret;
 		if (ret > 0) {
 			ret = 0;
 			break;
 		}
 	}
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -1217,7 +1215,7 @@ static int scan_extents(struct btrfs_fs_info *info,
 	int ret, i, nr, level;
 	struct btrfs_root *root = btrfs_extent_root(info, start);
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_disk_key disk_key;
 	struct extent_buffer *leaf;
 	u64 bytenr = 0, num_bytes = 0;
@@ -1229,7 +1227,7 @@ static int scan_extents(struct btrfs_fs_info *info,
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 	if (ret < 0) {
 		error("couldn't search slot: %d", ret);
-		goto out;
+		return ret;
 	}
 	path.reada = READA_BACK;
 
@@ -1263,7 +1261,7 @@ static int scan_extents(struct btrfs_fs_info *info,
 				ret = add_inline_refs(info, leaf, i, bytenr,
 						      num_bytes, meta);
 				if (ret)
-					goto out;
+					return ret;
 
 				level = get_tree_block_level(&key, leaf, i);
 				if (level) {
@@ -1292,23 +1290,20 @@ static int scan_extents(struct btrfs_fs_info *info,
 			ret = add_keyed_ref(info, &key, leaf, i, bytenr,
 					    num_bytes);
 			if (ret)
-				goto out;
+				return ret;
 		}
 
 		ret = btrfs_next_leaf(root, &path);
 		if (ret != 0) {
 			if (ret < 0) {
 				error("next leaf failed: %d", ret);
-				goto out;
+				return ret;
 			}
 			break;
 		}
 	}
 done:
 	ret = 0;
-out:
-	btrfs_release_path(&path);
-
 	return ret;
 }
 
@@ -1626,7 +1621,7 @@ static int repair_qgroup_info(struct btrfs_fs_info *info,
 	int ret;
 	struct btrfs_root *root = info->quota_root;
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_qgroup_info_item *info_item;
 	struct btrfs_key key;
 
@@ -1672,7 +1667,6 @@ static int repair_qgroup_info(struct btrfs_fs_info *info,
 
 out:
 	btrfs_commit_transaction(trans, root);
-	btrfs_release_path(&path);
 
 	return ret;
 }
@@ -1682,7 +1676,7 @@ static int repair_qgroup_status(struct btrfs_fs_info *info, bool silent)
 	int ret;
 	struct btrfs_root *root = info->quota_root;
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_qgroup_status_item *status_item;
 	bool simple = btrfs_fs_incompat(info, SIMPLE_QUOTA);
@@ -1721,7 +1715,6 @@ static int repair_qgroup_status(struct btrfs_fs_info *info, bool silent)
 
 out:
 	btrfs_commit_transaction(trans, root);
-	btrfs_release_path(&path);
 
 	return ret;
 }
diff --git a/check/repair.c b/check/repair.c
index 214b30301f78..4948416f66c4 100644
--- a/check/repair.c
+++ b/check/repair.c
@@ -224,7 +224,7 @@ static int populate_used_from_extent_root(struct btrfs_root *root,
 {
 	struct btrfs_fs_info *fs_info = root->fs_info;
 	struct extent_buffer *leaf;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int slot;
 	int ret;
@@ -272,7 +272,6 @@ static int populate_used_from_extent_root(struct btrfs_root *root,
 
 		path.slots[0]++;
 	}
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -414,7 +413,7 @@ int btrfs_remove_dev_extent(struct btrfs_fs_info *fs_info, u64 devid, u64 physic
 		.type = BTRFS_DEV_EXTENT_KEY,
 		.offset = physical,
 	};
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_dev_extent *dext;
 	struct btrfs_device *dev;
 	u64 length;
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 07/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for lowmem mode
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
                   ` (5 preceding siblings ...)
  2026-10-30  4:15 ` [PATCH 06/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for repair, qgroup-verify and mode-common Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:15 ` [PATCH 08/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for original mode Qu Wenruo
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

Convert all on-stack btrfs_path in check/mode-lowmem.c to
BTRFS_PATH_AUTO_RELEASE(), and drop the btrfs_release_path() calls that
only existed as function exit cleanup.

This also fixes the path leak on the search failure path of
find_dir_index().

Explicit releases which are not at function exit are kept, including the
ones in check_fs_first_inode() and check_dev_extent_item() which must
happen before the repair routines modify the trees.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 check/mode-lowmem.c | 202 +++++++++++++++-----------------------------
 1 file changed, 66 insertions(+), 136 deletions(-)

diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c
index e5c4c4ab39b7..7d0fad9c557f 100644
--- a/check/mode-lowmem.c
+++ b/check/mode-lowmem.c
@@ -284,7 +284,7 @@ static int modify_block_groups_cache(u64 flags, int cache)
 {
 	struct btrfs_root *root = btrfs_block_group_root(gfs_info);
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_block_group *bg_cache;
 	struct btrfs_block_group_item *bi;
 	struct btrfs_block_group_item bg_item;
@@ -300,7 +300,7 @@ static int modify_block_groups_cache(u64 flags, int cache)
 	if (ret < 0) {
 		errno = -ret;
 		error("fail to search block groups due to %m");
-		goto out;
+		return ret;
 	}
 
 	while (1) {
@@ -308,10 +308,8 @@ static int modify_block_groups_cache(u64 flags, int cache)
 		slot = path.slots[0];
 		btrfs_item_key_to_cpu(eb, &key, slot);
 		bg_cache = btrfs_lookup_block_group(gfs_info, key.objectid);
-		if (!bg_cache) {
-			ret = -ENOENT;
-			goto out;
-		}
+		if (!bg_cache)
+			return -ENOENT;
 
 		bi = btrfs_item_ptr(eb, slot, struct btrfs_block_group_item);
 		read_extent_buffer(eb, &bg_item, (unsigned long)bi,
@@ -320,17 +318,11 @@ static int modify_block_groups_cache(u64 flags, int cache)
 			modify_block_group_cache(bg_cache, cache);
 
 		ret = btrfs_next_item(root, &path);
-		if (ret > 0) {
-			ret = 0;
-			goto out;
-		}
+		if (ret > 0)
+			return 0;
 		if (ret < 0)
-			goto out;
+			return ret;
 	}
-
-out:
-	btrfs_release_path(&path);
-	return ret;
 }
 
 static int mark_block_groups_full(u64 flags)
@@ -432,7 +424,7 @@ err:
  */
 static int is_chunk_almost_full(u64 start)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_root *root = btrfs_block_group_root(gfs_info);
 	struct btrfs_block_group_item *bi;
@@ -452,22 +444,19 @@ static int is_chunk_almost_full(u64 start)
 	if (!ret)
 		ret = -EIO;
 	if (ret < 0)
-		goto out;
+		return ret;
 	ret = btrfs_previous_item(root, &path, start,
 				  BTRFS_BLOCK_GROUP_ITEM_KEY);
 	if (ret) {
 		error("failed to find block group %llu", start);
-		ret = -ENOENT;
-		goto out;
+		return -ENOENT;
 	}
 
 	eb = path.nodes[0];
 	slot = path.slots[0];
 	btrfs_item_key_to_cpu(eb, &key, slot);
-	if (key.objectid != start) {
-		ret = -ENOENT;
-		goto out;
-	}
+	if (key.objectid != start)
+		return -ENOENT;
 
 	total = key.offset;
 	bi = btrfs_item_ptr(eb, slot, struct btrfs_block_group_item);
@@ -485,8 +474,6 @@ static int is_chunk_almost_full(u64 start)
 		ret = 0;
 	else
 		ret = 1;
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -638,7 +625,7 @@ static int repair_tree_block_ref(struct btrfs_root *root,
 {
 	struct btrfs_trans_handle *trans = NULL;
 	struct btrfs_root *extent_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_extent_item *ei;
 	struct btrfs_tree_block_info *bi;
 	struct btrfs_key key;
@@ -763,7 +750,6 @@ static int repair_tree_block_ref(struct btrfs_root *root,
 out:
 	if (trans)
 		btrfs_commit_transaction(trans, extent_root);
-	btrfs_release_path(&path);
 	if (ret) {
 		errno = -ret;
 		error(
@@ -820,7 +806,7 @@ static int find_dir_index(struct btrfs_root *root, u64 dirid, u64 location_id,
 			  u64 *index_ret, char *namebuf, u32 name_len,
 			  u8 file_type)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *node;
 	struct btrfs_dir_item *di;
 	struct btrfs_key key;
@@ -851,7 +837,7 @@ loop:
 	if (ret) {
 		ret = -ENOENT;
 		*index_ret = (64)-1;
-		goto out;
+		return ret;
 	}
 	/* Check whether inode_id/filetype/name match */
 	node = path.nodes[0];
@@ -882,17 +868,13 @@ loop:
 
 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 		*index_ret = key.offset;
-		ret = 0;
-		goto out;
+		return 0;
 next:
 		len += sizeof(*di) + data_len;
 		di = (struct btrfs_dir_item *)((char *)di + len);
 		cur += len;
 	}
 	goto loop;
-
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -918,7 +900,7 @@ static int find_dir_item(struct btrfs_root *root, struct btrfs_key *key,
 			 struct btrfs_key *location_key, char *name,
 			 u32 namelen, u8 file_type)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *node;
 	struct btrfs_dir_item *di;
 	struct btrfs_key location;
@@ -945,7 +927,7 @@ static int find_dir_item(struct btrfs_root *root, struct btrfs_key *key,
 	if (ret) {
 		ret = key->type == BTRFS_DIR_ITEM_KEY ? DIR_ITEM_MISSING :
 			DIR_INDEX_MISSING;
-		goto out;
+		return ret;
 	}
 
 	/* Check whether inode_id/filetype/name match */
@@ -983,16 +965,13 @@ static int find_dir_item(struct btrfs_root *root, struct btrfs_key *key,
 		if (len != namelen || strncmp(namebuf, name, len))
 			goto next;
 
-		ret = 0;
-		goto out;
+		return 0;
 next:
 		len += sizeof(*di) + data_len;
 		di = (struct btrfs_dir_item *)((char *)di + len);
 		cur += len;
 	}
 
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -1347,7 +1326,7 @@ static int find_inode_ref(struct btrfs_root *root, struct btrfs_key *key,
 			  char *name, int namelen, u64 *index_ret)
 
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_inode_ref *ref;
 	struct btrfs_inode_extref *extref;
 	struct extent_buffer *node;
@@ -1408,8 +1387,7 @@ static int find_inode_ref(struct btrfs_root *root, struct btrfs_key *key,
 			goto next_ref;
 
 		*index_ret = ref_index;
-		ret = 0;
-		goto out;
+		return 0;
 next_ref:
 		len = sizeof(*ref) + ref_namelen;
 		ref = (struct btrfs_inode_ref *)((char *)ref + len);
@@ -1420,7 +1398,7 @@ extref:
 
 	/* Skip if not support EXTENDED_IREF feature */
 	if (!btrfs_fs_incompat(gfs_info, EXTENDED_IREF))
-		goto out;
+		return ret;
 
 	btrfs_release_path(&path);
 
@@ -1429,10 +1407,8 @@ extref:
 	key->offset = btrfs_extref_hash(dir_id, name, namelen);
 
 	ret = btrfs_search_slot(NULL, root, key, &path, 0, 0);
-	if (ret) {
-		ret = INODE_REF_MISSING;
-		goto out;
-	}
+	if (ret)
+		return INODE_REF_MISSING;
 
 	node = path.nodes[0];
 	slot = path.slots[0];
@@ -1471,8 +1447,7 @@ extref:
 			goto next_extref;
 
 		*index_ret = ref_index;
-		ret = 0;
-		goto out;
+		return 0;
 
 next_extref:
 		len = sizeof(*extref) + ref_namelen;
@@ -1480,8 +1455,6 @@ next_extref:
 		cur += len;
 
 	}
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -1505,7 +1478,7 @@ static int repair_inode_item_missing(struct btrfs_root *root, u64 ino,
 {
 	struct btrfs_key key;
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret;
 
 	key.objectid = ino;
@@ -1531,7 +1504,6 @@ out:
 	if (ret)
 		error("failed to repair root %llu INODE ITEM[%llu] missing",
 		      root->objectid, ino);
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -1570,7 +1542,7 @@ static int lowmem_delete_corrupted_dir_item(struct btrfs_root *root,
 static int try_repair_imode(struct btrfs_root *root, u64 ino)
 {
 	struct btrfs_inode_item *iitem;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -1582,7 +1554,7 @@ static int try_repair_imode(struct btrfs_root *root, u64 ino)
 	if (ret > 0)
 		ret = -ENOENT;
 	if (ret < 0)
-		goto out;
+		return ret;
 	iitem = btrfs_item_ptr(path.nodes[0], path.slots[0],
 			       struct btrfs_inode_item);
 	if (!is_valid_imode(btrfs_inode_mode(path.nodes[0], iitem))) {
@@ -1590,8 +1562,6 @@ static int try_repair_imode(struct btrfs_root *root, u64 ino)
 	} else {
 		ret = -ENOTTY;
 	}
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -2259,7 +2229,7 @@ static int __count_dir_isize(struct btrfs_root *root, u64 ino, int type,
 		u64 *size_ret)
 {
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	u32 len;
 	struct btrfs_dir_item *di;
 	int ret;
@@ -2274,10 +2244,8 @@ static int __count_dir_isize(struct btrfs_root *root, u64 ino, int type,
 	key.offset = (u64)-1;
 
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
-	if (ret < 0) {
-		ret = -EIO;
-		goto out;
-	}
+	if (ret < 0)
+		return -EIO;
 	/* If found, go to special case. */
 	if (ret == 0)
 		goto special_case;
@@ -2285,10 +2253,8 @@ static int __count_dir_isize(struct btrfs_root *root, u64 ino, int type,
 loop:
 	ret = btrfs_previous_item(root, &path, ino, type);
 
-	if (ret) {
-		ret = 0;
-		goto out;
-	}
+	if (ret)
+		return 0;
 
 special_case:
 	di = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_dir_item);
@@ -2307,10 +2273,6 @@ special_case:
 		cur += len;
 	}
 	goto loop;
-
-out:
-	btrfs_release_path(&path);
-	return ret;
 }
 
 static int count_dir_isize(struct btrfs_root *root, u64 ino, u64 *size)
@@ -2588,7 +2550,7 @@ out:
 
 static bool has_orphan_item(struct btrfs_root *root, u64 ino)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -3094,7 +3056,7 @@ static int check_tree_block_ref(struct btrfs_root *root,
 {
 	struct btrfs_key key;
 	struct btrfs_root *extent_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_extent_item *ei;
 	struct btrfs_extent_inline_ref *iref;
 	struct extent_buffer *leaf;
@@ -3283,7 +3245,6 @@ static int check_tree_block_ref(struct btrfs_root *root,
 	if (!found_ref)
 		err |= BACKREF_MISSING;
 out:
-	btrfs_release_path(&path);
 	if (nrefs && strict &&
 	    level < root_level && nrefs->full_backref[level + 1])
 		parent = nrefs->bytenr[level + 1];
@@ -3313,7 +3274,7 @@ static int repair_extent_data_item(struct btrfs_root *root,
 	struct btrfs_key fi_key;
 	struct btrfs_key key;
 	struct btrfs_extent_item *ei;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_root *extent_root;
 	struct extent_buffer *eb;
 	u64 size;
@@ -3353,7 +3314,7 @@ static int repair_extent_data_item(struct btrfs_root *root,
 		ret = delete_item(root, pathp);
 		if (!ret)
 			err = 0;
-		goto out_no_release;
+		goto out;
 	}
 
 	/* now repair only adds backref */
@@ -3427,8 +3388,6 @@ static int repair_extent_data_item(struct btrfs_root *root,
 out:
 	if (trans)
 		btrfs_commit_transaction(trans, root);
-	btrfs_release_path(&path);
-out_no_release:
 	if (ret)
 		error("can't repair root %llu extent data item[%llu %llu]",
 		      root->objectid, disk_bytenr, num_bytes);
@@ -3438,7 +3397,7 @@ out_no_release:
 static int read_inode_item(struct btrfs_root *root,
 			   u64 ino, struct btrfs_inode_item *ret_ii)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key = {
 		.objectid = ino,
 		.type = BTRFS_INODE_ITEM_KEY,
@@ -3450,13 +3409,11 @@ static int read_inode_item(struct btrfs_root *root,
 	if (ret > 0)
 		ret = -ENOENT;
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	read_extent_buffer(path.nodes[0], ret_ii,
 			   btrfs_item_ptr_offset(path.nodes[0], path.slots[0]),
 			   sizeof(*ret_ii));
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -3472,7 +3429,7 @@ static int check_extent_data_item(struct btrfs_root *root,
 {
 	struct btrfs_file_extent_item *fi;
 	struct extent_buffer *eb = pathp->nodes[0];
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_root *extent_root;
 	struct btrfs_key fi_key;
 	struct btrfs_key dbref_key;
@@ -3664,7 +3621,6 @@ static int check_extent_data_item(struct btrfs_root *root,
 out:
 	if (!found_dbackref)
 		err |= BACKREF_MISSING;
-	btrfs_release_path(&path);
 	if (err & BACKREF_MISSING) {
 		error(
 		"file extent[%llu %llu] root %llu owner %llu backref lost",
@@ -3683,7 +3639,7 @@ static int check_block_group_item(struct extent_buffer *eb, int slot)
 	struct btrfs_root *chunk_root = gfs_info->chunk_root;
 	struct btrfs_block_group_item *bi;
 	struct btrfs_block_group_item bg_item;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key bg_key;
 	struct btrfs_key chunk_key;
 	struct btrfs_key extent_key;
@@ -3789,8 +3745,6 @@ next:
 	}
 
 out:
-	btrfs_release_path(&path);
-
 	total_used += used;
 
 	if (total != used) {
@@ -3811,7 +3765,7 @@ static int query_tree_block_level(u64 bytenr)
 {
 	struct btrfs_root *extent_root;
 	struct extent_buffer *eb;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_extent_item *ei;
 	struct btrfs_tree_parent_check check = { 0 };
@@ -3829,23 +3783,19 @@ static int query_tree_block_level(u64 bytenr)
 	extent_root = btrfs_extent_root(gfs_info, bytenr);
 	ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
 	if (ret < 0)
-		goto release_out;
+		return ret;
 	ret = btrfs_previous_extent_item(extent_root, &path, bytenr);
 	if (ret < 0)
-		goto release_out;
-	if (ret > 0) {
-		ret = -ENOENT;
-		goto release_out;
-	}
+		return ret;
+	if (ret > 0)
+		return -ENOENT;
 
 	btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 	ei = btrfs_item_ptr(path.nodes[0], path.slots[0],
 			    struct btrfs_extent_item);
 	flags = btrfs_extent_flags(path.nodes[0], ei);
-	if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK)) {
-		ret = -ENOENT;
-		goto release_out;
-	}
+	if (!(flags & BTRFS_EXTENT_FLAG_TREE_BLOCK))
+		return -ENOENT;
 
 	/* Get transid for later read_tree_block() check */
 	transid = btrfs_extent_generation(path.nodes[0], ei);
@@ -3874,10 +3824,6 @@ static int query_tree_block_level(u64 bytenr)
 	if (header_level != backref_level)
 		return -EIO;
 	return header_level;
-
-release_out:
-	btrfs_release_path(&path);
-	return ret;
 }
 
 /*
@@ -3889,7 +3835,7 @@ static int check_tree_block_backref(u64 root_id, u64 bytenr, int level)
 {
 	struct btrfs_root *root;
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *eb;
 	struct extent_buffer *node;
 	struct btrfs_tree_parent_check check = {
@@ -3943,7 +3889,7 @@ static int check_tree_block_backref(u64 root_id, u64 bytenr, int level)
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 	if (ret < 0) {
 		err |= REFERENCER_MISSING;
-		goto release_out;
+		goto out;
 	}
 
 	node = path.nodes[level];
@@ -3962,8 +3908,6 @@ static int check_tree_block_backref(u64 root_id, u64 bytenr, int level)
 		err |= REFERENCER_MISMATCH;
 	}
 
-release_out:
-	btrfs_release_path(&path);
 out:
 	if (err & REFERENCER_MISSING) {
 		if (level < 0)
@@ -4063,7 +4007,7 @@ static int has_inline_shared_backref(u64 data_bytenr, u64 data_len, u64 parent)
 	struct btrfs_root *extent_root = btrfs_extent_root(gfs_info, data_bytenr);
 	struct btrfs_extent_inline_ref *iref;
 	struct btrfs_extent_item *ei;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_key key;
 	unsigned long ptr;
@@ -4131,7 +4075,6 @@ next:
 	}
 
 out:
-	btrfs_release_path(&path);
 	if (ret < 0)
 		return ret;
 	return found;
@@ -4140,7 +4083,7 @@ out:
 static int has_keyed_shared_backref(u64 data_bytenr, u64 parent)
 {
 	struct btrfs_root *extent_root = btrfs_extent_root(gfs_info, data_bytenr);
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -4198,7 +4141,7 @@ static int check_extent_data_backref(u64 root_id, u64 objectid, u64 offset,
 	struct btrfs_root *root;
 	struct btrfs_root *extent_root;
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_file_extent_item *fi;
 	u32 found_count = 0;
@@ -4299,7 +4242,6 @@ next:
 			break;
 	}
 out:
-	btrfs_release_path(&path);
 	if (found_count != count) {
 		error(
 "extent[%llu, %llu] referencer count mismatch (root: %llu, owner: %llu, offset: %llu) wanted: %u, have: %u",
@@ -4732,7 +4674,7 @@ static int check_dev_extent_item(struct extent_buffer *eb, int slot)
 {
 	struct btrfs_root *chunk_root = gfs_info->chunk_root;
 	struct btrfs_dev_extent *ptr;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key chunk_key;
 	struct btrfs_key devext_key;
 	struct btrfs_chunk *chunk;
@@ -4799,7 +4741,7 @@ static int check_dev_item(struct extent_buffer *eb, int slot,
 {
 	struct btrfs_root *dev_root = gfs_info->dev_root;
 	struct btrfs_dev_item *dev_item;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_dev_extent *ptr;
 	struct btrfs_device *dev;
@@ -4833,7 +4775,6 @@ static int check_dev_item(struct extent_buffer *eb, int slot,
 		btrfs_item_key_to_cpu(eb, &key, slot);
 		error("cannot find any related dev extent for dev[%llu, %u, %llu]",
 			key.objectid, key.type, key.offset);
-		btrfs_release_path(&path);
 		return REFERENCER_MISSING;
 	}
 
@@ -4867,7 +4808,6 @@ static int check_dev_item(struct extent_buffer *eb, int slot,
 "dev extent devid %llu offset %llu len %llu overlap with previous dev extent end %llu",
 			      devid, physical_offset, physical_len,
 			      prev_dev_ext_end);
-			btrfs_release_path(&path);
 			return ACCOUNTING_MISMATCH;
 		}
 		if (physical_offset + physical_len > total_bytes) {
@@ -4875,7 +4815,6 @@ static int check_dev_item(struct extent_buffer *eb, int slot,
 "dev extent devid %llu offset %llu len %llu is beyond device boundary %llu",
 			      devid, physical_offset, physical_len,
 			      total_bytes);
-			btrfs_release_path(&path);
 			return ACCOUNTING_MISMATCH;
 		}
 		prev_devid = devid;
@@ -4978,7 +4917,7 @@ out:
 static int check_chunk_item(struct extent_buffer *eb, int slot)
 {
 	struct btrfs_root *dev_root = gfs_info->dev_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key chunk_key;
 	struct btrfs_key devext_key;
 	struct btrfs_chunk *chunk;
@@ -5005,7 +4944,7 @@ static int check_chunk_item(struct extent_buffer *eb, int slot)
 			error("chunk[%llu %llu) is not fully aligned to BTRFS_STRIPE_LEN (%u)",
 				chunk_key.offset, length, BTRFS_STRIPE_LEN);
 			err |= BYTES_UNALIGNED;
-			goto out;
+			return err;
 		}
 		warning("chunk[%llu %llu) is not fully aligned to BTRFS_STRIPE_LEN (%u)",
 			chunk_key.offset, length, BTRFS_STRIPE_LEN);
@@ -5015,7 +4954,7 @@ static int check_chunk_item(struct extent_buffer *eb, int slot)
 		error("chunk[%llu %llu) is invalid", chunk_key.offset,
 			chunk_end);
 		err |= BYTES_UNALIGNED | UNKNOWN_TYPE;
-		goto out;
+		return err;
 	}
 	type = btrfs_chunk_type(eb, chunk);
 
@@ -5053,8 +4992,6 @@ not_match_dev:
 			chunk_key.objectid, chunk_end, i);
 		continue;
 	}
-	btrfs_release_path(&path);
-out:
 	return err;
 }
 
@@ -5427,7 +5364,7 @@ static int repair_fs_first_inode(struct btrfs_root *root, int err)
 {
 	struct btrfs_trans_handle *trans;
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int filetype = BTRFS_FT_DIR;
 	int ret = 0;
 
@@ -5473,7 +5410,6 @@ trans_fail:
 out:
 	if (ret)
 		error("fail to repair first inode");
-	btrfs_release_path(&path);
 	return err;
 }
 
@@ -5486,7 +5422,7 @@ out:
  */
 static int check_fs_first_inode(struct btrfs_root *root)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_inode_item *ii;
 	u64 index;
@@ -5557,7 +5493,7 @@ out:
  */
 static int check_btrfs_root(struct btrfs_root *root, int check_all)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct node_refs nrefs;
 	struct btrfs_root_item *root_item = &root->root_item;
 	u64 super_generation = btrfs_super_generation(gfs_info->super_copy);
@@ -5628,7 +5564,7 @@ static int check_btrfs_root(struct btrfs_root *root, int check_all)
 		path.lowest_level = level;
 		ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 		if (ret < 0)
-			goto out;
+			return ret;
 		ret = 0;
 	}
 
@@ -5652,8 +5588,6 @@ static int check_btrfs_root(struct btrfs_root *root, int check_all)
 		}
 	}
 
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -5682,7 +5616,7 @@ static int check_fs_root(struct btrfs_root *root)
 static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
 			  struct extent_buffer *node, int slot)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_root_ref *ref;
 	struct btrfs_root_ref *backref;
@@ -5726,7 +5660,7 @@ static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
 		      ref_key->type == BTRFS_ROOT_REF_KEY ?
 		      "ROOT_REF" : "ROOT_BACKREF",
 		      ref_key->objectid, ref_key->offset);
-		goto out;
+		return err;
 	}
 
 	backref = btrfs_item_ptr(path.nodes[0], path.slots[0],
@@ -5756,8 +5690,6 @@ static int check_root_ref(struct btrfs_root *root, struct btrfs_key *ref_key,
 		      "ROOT_REF" : "ROOT_BACKREF",
 		      ref_key->objectid, ref_key->offset);
 	}
-out:
-	btrfs_release_path(&path);
 	return err;
 }
 
@@ -5773,7 +5705,7 @@ int check_fs_roots_lowmem(void)
 {
 	struct btrfs_root *tree_root = gfs_info->tree_root;
 	struct btrfs_root *cur_root = NULL;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct extent_buffer *node;
 	int slot;
@@ -5869,7 +5801,6 @@ next:
 	}
 
 out:
-	btrfs_release_path(&path);
 	if (found_free_ino_cache)
 		pr_default("deprecated inode cache can be removed by 'btrfs rescue clear-ino-cache'\n");
 	return err;
@@ -5880,7 +5811,7 @@ out:
  */
 int check_chunks_and_extents_lowmem(void)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key old_key;
 	struct btrfs_key key;
 	struct btrfs_root *root;
@@ -5964,6 +5895,5 @@ out:
 			err &= ~(BG_ACCOUNTING_ERROR | SUPER_BYTES_USED_ERROR);
 	}
 
-	btrfs_release_path(&path);
 	return err;
 }
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 08/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for original mode
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
                   ` (6 preceding siblings ...)
  2026-10-30  4:15 ` [PATCH 07/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for lowmem mode Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:15 ` [PATCH 09/12] btrfs-progs: check: fix fixup_extent_flags() retry path Qu Wenruo
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

Convert all on-stack btrfs_path in check/main.c to
BTRFS_PATH_AUTO_RELEASE(), and drop the btrfs_release_path() calls that
only existed as function exit cleanup.

This also fixes the path leaks on error paths of:

- is_child_root()
- check_owner_ref()
- check_range_csummed()
- check_log_root()
- parse_tree_roots()

Explicit releases which are not at function exit are kept, including the
ones in check_fs_root() and repair_root_items() which must happen before
the repair routines modify the trees.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 check/main.c | 218 +++++++++++++++++----------------------------------
 1 file changed, 71 insertions(+), 147 deletions(-)

diff --git a/check/main.c b/check/main.c
index 322855173d1c..b3641cd39169 100644
--- a/check/main.c
+++ b/check/main.c
@@ -906,7 +906,7 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache,
 
 static int check_orphan_item(struct btrfs_root *root, u64 ino)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	int ret;
 
@@ -1372,7 +1372,7 @@ static int leave_shared_node(struct btrfs_root *root,
 static int is_child_root(struct btrfs_root *root, u64 parent_root_id,
 			 u64 child_root_id)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct extent_buffer *leaf;
 	int has_parent = 0;
@@ -1413,15 +1413,12 @@ static int is_child_root(struct btrfs_root *root, u64 parent_root_id,
 
 		has_parent = 1;
 
-		if (key.offset == parent_root_id) {
-			btrfs_release_path(&path);
+		if (key.offset == parent_root_id)
 			return 1;
-		}
 
 		path.slots[0]++;
 	}
 out:
-	btrfs_release_path(&path);
 	if (ret < 0)
 		return ret;
 	return has_parent ? 0 : 2;
@@ -2231,7 +2228,7 @@ static int add_missing_dir_index(struct btrfs_root *root,
 				 struct inode_record *rec,
 				 struct inode_backref *backref)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_trans_handle *trans;
 	struct btrfs_dir_item *dir_item;
 	struct extent_buffer *leaf;
@@ -2295,7 +2292,7 @@ static int delete_dir_index(struct btrfs_root *root,
 {
 	struct btrfs_trans_handle *trans;
 	struct btrfs_dir_item *di;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret = 0;
 
 	trans = btrfs_start_transaction(root, 1);
@@ -2713,7 +2710,7 @@ out:
  */
 static int find_normal_file_extent(struct btrfs_root *root, u64 ino)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_key found_key;
 	struct btrfs_file_extent_item *fi;
@@ -2725,16 +2722,12 @@ static int find_normal_file_extent(struct btrfs_root *root, u64 ino)
 	key.offset = 0;
 
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
-	if (ret < 0) {
-		ret = 0;
-		goto out;
-	}
+	if (ret < 0)
+		return 0;
 	if (ret && path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
 		ret = btrfs_next_leaf(root, &path);
-		if (ret) {
-			ret = 0;
-			goto out;
-		}
+		if (ret)
+			return 0;
 	}
 	while (1) {
 		btrfs_item_key_to_cpu(path.nodes[0], &found_key,
@@ -2745,13 +2738,9 @@ static int find_normal_file_extent(struct btrfs_root *root, u64 ino)
 		fi = btrfs_item_ptr(path.nodes[0], path.slots[0],
 				    struct btrfs_file_extent_item);
 		type = btrfs_file_extent_type(path.nodes[0], fi);
-		if (type != BTRFS_FILE_EXTENT_INLINE) {
-			ret = 1;
-			goto out;
-		}
+		if (type != BTRFS_FILE_EXTENT_INLINE)
+			return 1;
 	}
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -2932,7 +2921,7 @@ static int repair_mismatch_dir_hash(struct btrfs_trans_handle *trans,
 static int btrfs_delete_item(struct btrfs_trans_handle *trans,
 		struct btrfs_root *root, struct btrfs_key *key)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret = 0;
 
 	ret = btrfs_search_slot(trans, root, key, &path, -1, 1);
@@ -2940,13 +2929,11 @@ static int btrfs_delete_item(struct btrfs_trans_handle *trans,
 		if (ret > 0)
 			ret = -ENOENT;
 
-		btrfs_release_path(&path);
 		return ret;
 	}
 
 	ret = btrfs_del_item(trans, root, &path);
 
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -2954,7 +2941,7 @@ static int find_file_extent_offset_by_bytenr(struct btrfs_root *root,
 		u64 owner, u64 bytenr, u64 *offset_ret)
 {
 	int ret = 0;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_key found_key;
 	struct btrfs_file_extent_item *fi;
@@ -2970,7 +2957,6 @@ static int find_file_extent_offset_by_bytenr(struct btrfs_root *root,
 	if (ret) {
 		if (ret > 0)
 			ret = -ENOENT;
-		btrfs_release_path(&path);
 		return ret;
 	}
 
@@ -2981,10 +2967,8 @@ static int find_file_extent_offset_by_bytenr(struct btrfs_root *root,
 	key.offset = 0;
 
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
-	if (ret < 0) {
-		btrfs_release_path(&path);
+	if (ret < 0)
 		return ret;
-	}
 
 	while (1) {
 		leaf = path.nodes[0];
@@ -3019,7 +3003,6 @@ static int find_file_extent_offset_by_bytenr(struct btrfs_root *root,
 		path.slots[0]++;
 	}
 
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -3133,7 +3116,7 @@ static int repair_inode_gen_original(struct btrfs_trans_handle *trans,
 static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
 {
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	int ret = 0;
 
 	/* unaligned extent recs always lead to csum missing error, clean it */
@@ -3193,7 +3176,6 @@ static int try_repair_inode(struct btrfs_root *root, struct inode_record *rec)
 	if (!ret && rec->errors & I_ERR_INVALID_GEN)
 		ret = repair_inode_gen_original(trans, root, &path, rec);
 	btrfs_commit_transaction(trans, root);
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -3727,7 +3709,7 @@ static int repair_btree(struct btrfs_root *root,
 			struct cache_tree *corrupt_blocks)
 {
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_corrupt_block *corrupt;
 	struct cache_extent *cache;
 	struct btrfs_key key;
@@ -3797,7 +3779,6 @@ static int repair_btree(struct btrfs_root *root,
 	}
 out:
 	btrfs_commit_transaction(trans, root);
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -3811,7 +3792,7 @@ static int check_fs_root(struct btrfs_root *root,
 	int wret;
 	int level;
 	u64 super_generation;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct shared_node root_node;
 	struct root_record *rec;
 	struct btrfs_root_item *root_item = &root->root_item;
@@ -3989,7 +3970,7 @@ skip_walking:
 
 static int check_fs_roots(struct cache_tree *root_cache)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct walk_control wc;
 	struct extent_buffer *leaf, *tree_node;
@@ -4093,7 +4074,6 @@ next:
 		path.slots[0]++;
 	}
 out:
-	btrfs_release_path(&path);
 	if (err)
 		free_extent_cache_tree(&wc.shared);
 	if (!cache_tree_empty(&wc.shared))
@@ -4418,7 +4398,7 @@ static bool check_owner_ref(struct btrfs_root *root,
 	struct tree_backref *back;
 	struct btrfs_root *ref_root;
 	struct btrfs_key key;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *parent;
 	int level;
 	bool found = false;
@@ -4468,7 +4448,6 @@ static bool check_owner_ref(struct btrfs_root *root,
 							path.slots[level + 1]))
 		found = true;
 
-	btrfs_release_path(&path);
 	return !found;
 }
 
@@ -4714,7 +4693,7 @@ static int try_to_fix_bad_block(struct btrfs_root *root,
 	struct ulist *roots;
 	struct ulist_node *node;
 	struct btrfs_root *search_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct ulist_iterator iter;
 	struct btrfs_key root_key, key;
 	int ret;
@@ -4777,7 +4756,6 @@ static int try_to_fix_bad_block(struct btrfs_root *root,
 		btrfs_commit_transaction(trans, search_root);
 	}
 	ulist_free(roots);
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -6021,7 +5999,7 @@ static int check_extent_exists(struct btrfs_root *root, u64 bytenr,
 			       u64 num_bytes)
 {
 	struct btrfs_root *extent_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_key key;
 	int ret;
@@ -6036,7 +6014,6 @@ again:
 				0, 0);
 	if (ret < 0) {
 		fprintf(stderr, "Error looking up extent record %d\n", ret);
-		btrfs_release_path(&path);
 		return ret;
 	} else if (ret) {
 		if (path.slots[0] > 0) {
@@ -6083,7 +6060,6 @@ again:
 			if (ret < 0) {
 				fprintf(stderr, "Error going to next leaf "
 					"%d\n", ret);
-				btrfs_release_path(&path);
 				return ret;
 			} else if (ret) {
 				break;
@@ -6155,13 +6131,12 @@ out:
 		ret = 1;
 	}
 
-	btrfs_release_path(&path);
 	return ret;
 }
 
 static int check_csum_root(struct btrfs_root *root)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_key key;
 	u64 last_data_end = 0;
@@ -6188,7 +6163,6 @@ static int check_csum_root(struct btrfs_root *root)
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 	if (ret < 0) {
 		fprintf(stderr, "Error searching csum tree %d\n", ret);
-		btrfs_release_path(&path);
 		return ret;
 	}
 
@@ -6276,7 +6250,6 @@ skip_csum_check:
 		path.slots[0]++;
 	}
 
-	btrfs_release_path(&path);
 	return errors;
 }
 
@@ -7640,7 +7613,7 @@ static int delete_duplicate_records(struct btrfs_root *root,
 {
 	struct btrfs_trans_handle *trans;
 	LIST_HEAD(delete_list);
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_record *tmp, *good, *n;
 	int nr_del = 0;
 	int ret = 0, err;
@@ -7726,8 +7699,6 @@ out:
 		free(tmp);
 	}
 
-	btrfs_release_path(&path);
-
 	if (!ret && !nr_del)
 		rec->num_duplicates = 0;
 
@@ -7743,7 +7714,7 @@ static int __find_possible_backrefs(struct btrfs_root *root,
 		u64 *bytes_ret)
 {
 	int ret = 0;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_key found_key;
 	struct btrfs_file_extent_item *fi;
@@ -7758,10 +7729,8 @@ static int __find_possible_backrefs(struct btrfs_root *root,
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
 	if (ret > 0)
 		ret = -ENOENT;
-	if (ret) {
-		btrfs_release_path(&path);
+	if (ret)
 		return ret;
-	}
 
 	btrfs_release_path(&path);
 
@@ -7770,10 +7739,8 @@ static int __find_possible_backrefs(struct btrfs_root *root,
 	key.offset = 0;
 
 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
-	if (ret < 0) {
-		btrfs_release_path(&path);
+	if (ret < 0)
 		return ret;
-	}
 
 	while (1) {
 		leaf = path.nodes[0];
@@ -7811,7 +7778,6 @@ static int __find_possible_backrefs(struct btrfs_root *root,
 		path.slots[0]++;
 	}
 
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -7904,7 +7870,7 @@ static int fixup_extent_refs(struct cache_tree *extent_cache,
 {
 	struct btrfs_trans_handle *trans = NULL;
 	int ret;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct cache_extent *cache;
 	struct extent_backref *back, *tmp;
 	int allocated = 0;
@@ -7981,7 +7947,6 @@ out:
 	if (!ret)
 		fprintf(stderr, "Repaired extent references for %llu\n", rec->start);
 
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -7989,7 +7954,7 @@ static int fixup_extent_flags(struct extent_record *rec)
 {
 	struct btrfs_trans_handle *trans;
 	struct btrfs_root *root = btrfs_extent_root(gfs_info, rec->start);
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_extent_item *ei;
 	struct btrfs_key key;
 	u64 flags;
@@ -8058,7 +8023,7 @@ static int prune_one_block(struct btrfs_trans_handle *trans,
 {
 	struct btrfs_root *extent_root;
 	int ret;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *eb;
 	u64 found;
 	int slot;
@@ -8074,13 +8039,11 @@ again:
 				-1, 1);
 
 	if (ret < 0)
-		goto out;
+		return ret;
 
 	eb = path.nodes[level];
-	if (!eb) {
-		ret = -ENOENT;
-		goto out;
-	}
+	if (!eb)
+		return -ENOENT;
 
 	/*
 	 * hopefully the search gave us the block we want to prune,
@@ -8104,8 +8067,7 @@ again:
 	 * TODO: search all the nodes for pointers to this block
 	 */
 	if (eb == extent_root->node) {
-		ret = -ENOENT;
-		goto out;
+		return -ENOENT;
 	} else {
 		level++;
 		btrfs_release_path(&path);
@@ -8115,9 +8077,6 @@ again:
 del_ptr:
 	printk("deleting pointer to block %llu\n", corrupt->cache.start);
 	btrfs_del_ptr(trans, extent_root, &path, level, slot);
-
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -8248,7 +8207,7 @@ static int record_unaligned_extent_rec(struct extent_record *rec)
 static int repair_extent_item_generation(struct extent_record *rec)
 {
 	struct btrfs_trans_handle *trans;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_extent_item *ei;
 	struct btrfs_root *extent_root = btrfs_extent_root(gfs_info,
@@ -9066,7 +9025,7 @@ static int check_block_groups(struct block_group_tree *bg_cache)
 static int parse_tree_roots(struct list_head *normal_trees,
 			    struct list_head *dropping_trees)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_key found_key;
 	struct btrfs_root_item ri;
@@ -9079,7 +9038,7 @@ static int parse_tree_roots(struct list_head *normal_trees,
 	key.offset = 0;
 	ret = btrfs_search_slot(NULL, gfs_info->tree_root, &key, &path, 0, 0);
 	if (ret < 0)
-		goto out;
+		return ret;
 	while (1) {
 		leaf = path.nodes[0];
 		slot = path.slots[0];
@@ -9125,8 +9084,6 @@ static int parse_tree_roots(struct list_head *normal_trees,
 		path.slots[0]++;
 	}
 
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -9147,7 +9104,7 @@ static int check_zoned_active_zones(void)
 
 	list_for_each_entry(dev, &gfs_info->fs_devices->devices, dev_list) {
 		struct btrfs_zoned_device_info *zinfo = dev->zone_info;
-		struct btrfs_path path = { 0 };
+		BTRFS_PATH_AUTO_RELEASE(path);
 		struct btrfs_key key;
 		unsigned long *covered;
 		int shift;
@@ -9175,7 +9132,6 @@ static int check_zoned_active_zones(void)
 		key.offset = 0;
 		ret = btrfs_search_slot(NULL, dev_root, &key, &path, 0, 0);
 		if (ret < 0) {
-			btrfs_release_path(&path);
 			free(covered);
 			return ret;
 		}
@@ -9187,7 +9143,6 @@ static int check_zoned_active_zones(void)
 			if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
 				ret = btrfs_next_leaf(dev_root, &path);
 				if (ret < 0) {
-					btrfs_release_path(&path);
 					free(covered);
 					return ret;
 				}
@@ -9283,7 +9238,7 @@ static int check_zoned(void)
  */
 static int check_dev_extents(void)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct btrfs_root *dev_root = gfs_info->dev_root;
 	int ret;
@@ -9298,19 +9253,17 @@ static int check_dev_extents(void)
 	if (ret < 0) {
 		errno = -ret;
 		error("failed to search device tree: %m");
-		goto out;
+		return ret;
 	}
 	if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
 		ret = btrfs_next_leaf(dev_root, &path);
 		if (ret < 0) {
 			errno = -ret;
 			error("failed to find next leaf: %m");
-			goto out;
-		}
-		if (ret > 0) {
-			ret = 0;
-			goto out;
+			return ret;
 		}
+		if (ret > 0)
+			return 0;
 	}
 
 	while (1) {
@@ -9332,23 +9285,20 @@ static int check_dev_extents(void)
 		dev = btrfs_find_device(gfs_info, devid, NULL, NULL);
 		if (!dev) {
 			error("failed to find device with devid %llu", devid);
-			ret = -EUCLEAN;
-			goto out;
+			return -EUCLEAN;
 		}
 		if (prev_devid == devid && prev_dev_ext_end > physical_offset) {
 			error(
 "dev extent devid %llu physical offset %llu overlap with previous dev extent end %llu",
 			      devid, physical_offset, prev_dev_ext_end);
-			ret = -EUCLEAN;
-			goto out;
+			return -EUCLEAN;
 		}
 		if (physical_offset + physical_len > dev->total_bytes) {
 			error(
 "dev extent devid %llu physical offset %llu len %llu is beyond device boundary %llu",
 			      devid, physical_offset, physical_len,
 			      dev->total_bytes);
-			ret = -EUCLEAN;
-			goto out;
+			return -EUCLEAN;
 		}
 		prev_devid = devid;
 		prev_dev_ext_end = physical_offset + physical_len;
@@ -9357,15 +9307,13 @@ static int check_dev_extents(void)
 		if (ret < 0) {
 			errno = -ret;
 			error("failed to find next leaf: %m");
-			goto out;
+			return ret;
 		}
 		if (ret > 0) {
 			ret = 0;
 			break;
 		}
 	}
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -9660,7 +9608,7 @@ static int btrfs_fsck_reinit_root(struct btrfs_trans_handle *trans,
 static int reset_block_groups(void)
 {
 	struct btrfs_block_group *cache;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_chunk *chunk;
 	struct btrfs_key key;
@@ -9671,10 +9619,8 @@ static int reset_block_groups(void)
 	key.type = BTRFS_CHUNK_ITEM_KEY;
 	key.offset = 0;
 	ret = btrfs_search_slot(NULL, gfs_info->chunk_root, &key, &path, 0, 0);
-	if (ret < 0) {
-		btrfs_release_path(&path);
+	if (ret < 0)
 		return ret;
-	}
 
 	/*
 	 * We do this in case the block groups were screwed up and had alloc
@@ -9689,10 +9635,8 @@ static int reset_block_groups(void)
 	while (1) {
 		if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
 			ret = btrfs_next_leaf(gfs_info->chunk_root, &path);
-			if (ret < 0) {
-				btrfs_release_path(&path);
+			if (ret < 0)
 				return ret;
-			}
 			if (ret) {
 				ret = 0;
 				break;
@@ -9723,14 +9667,13 @@ static int reset_block_groups(void)
 		start = cache->start + cache->length;
 	}
 
-	btrfs_release_path(&path);
 	return 0;
 }
 
 static int reset_balance(struct btrfs_trans_handle *trans)
 {
 	struct btrfs_root *root = gfs_info->tree_root;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct extent_buffer *leaf;
 	struct btrfs_key key;
 	int del_slot, del_nr = 0;
@@ -9747,12 +9690,12 @@ static int reset_balance(struct btrfs_trans_handle *trans)
 		if (!ret)
 			goto reinit_data_reloc;
 		else
-			goto out;
+			return ret;
 	}
 
 	ret = btrfs_del_item(trans, root, &path);
 	if (ret)
-		goto out;
+		return ret;
 	btrfs_release_path(&path);
 
 	key.objectid = BTRFS_TREE_RELOC_OBJECTID;
@@ -9760,7 +9703,7 @@ static int reset_balance(struct btrfs_trans_handle *trans)
 	key.offset = 0;
 	ret = btrfs_search_slot(trans, root, &key, &path, -1, 1);
 	if (ret < 0)
-		goto out;
+		return ret;
 	while (1) {
 		if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
 			if (!found)
@@ -9771,7 +9714,7 @@ static int reset_balance(struct btrfs_trans_handle *trans)
 						      del_slot, del_nr);
 				del_nr = 0;
 				if (ret)
-					goto out;
+					return ret;
 			}
 			key.offset++;
 			btrfs_release_path(&path);
@@ -9780,7 +9723,7 @@ static int reset_balance(struct btrfs_trans_handle *trans)
 			ret = btrfs_search_slot(trans, root, &key, &path,
 						-1, 1);
 			if (ret < 0)
-				goto out;
+				return ret;
 			continue;
 		}
 		found = 1;
@@ -9804,7 +9747,7 @@ static int reset_balance(struct btrfs_trans_handle *trans)
 	if (del_nr) {
 		ret = btrfs_del_items(trans, root, &path, del_slot, del_nr);
 		if (ret)
-			goto out;
+			return ret;
 	}
 	btrfs_release_path(&path);
 
@@ -9815,16 +9758,13 @@ reinit_data_reloc:
 	root = btrfs_read_fs_root(gfs_info, &key);
 	if (IS_ERR(root)) {
 		fprintf(stderr, "Error reading data reloc tree\n");
-		ret = PTR_ERR(root);
-		goto out;
+		return PTR_ERR(root);
 	}
 	record_root_in_trans(trans, root);
 	ret = btrfs_fsck_reinit_root(trans, root);
 	if (ret)
-		goto out;
+		return ret;
 	ret = btrfs_make_root_dir(trans, root, BTRFS_FIRST_FREE_OBJECTID);
-out:
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -9835,7 +9775,7 @@ static int reinit_global_roots(struct btrfs_trans_handle *trans, u64 objectid)
 		.type = BTRFS_ROOT_ITEM_KEY,
 		.offset = 0,
 	};
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_root *tree_root = gfs_info->tree_root;
 	struct btrfs_root *root;
 	int ret;
@@ -9869,7 +9809,6 @@ static int reinit_global_roots(struct btrfs_trans_handle *trans, u64 objectid)
 			break;
 		key.offset++;
 	}
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -9992,7 +9931,7 @@ again:
 
 static int delete_bad_item(struct btrfs_root *root, struct bad_item *bad)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_trans_handle *trans;
 	struct btrfs_key key;
 	int ret;
@@ -10027,7 +9966,6 @@ static int delete_bad_item(struct btrfs_root *root, struct bad_item *bad)
 	ret = btrfs_del_item(trans, root, &path);
 out:
 	btrfs_commit_transaction(trans, root);
-	btrfs_release_path(&path);
 	return ret;
 }
 
@@ -10044,7 +9982,7 @@ static int zero_log_tree(struct btrfs_root *root)
 
 static int check_range_csummed(struct btrfs_root *root, u64 addr, u64 length)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key = {
 		.objectid = BTRFS_EXTENT_CSUM_OBJECTID,
 		.type = BTRFS_EXTENT_CSUM_KEY,
@@ -10106,8 +10044,6 @@ next:
 		path.slots[0]++;
 	}
 
-	btrfs_release_path(&path);
-
 	if (ret >= 0)
 		ret = (length != 0);
 
@@ -10116,7 +10052,7 @@ next:
 
 static int check_log_root(struct btrfs_root *root, struct cache_tree *root_cache)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key = {
 		.objectid = BTRFS_FIRST_FREE_OBJECTID,
 		.type = BTRFS_INODE_ITEM_KEY,
@@ -10204,8 +10140,6 @@ next:
 		path.slots[0]++;
 	}
 
-	btrfs_release_path(&path);
-
 	return err;
 }
 
@@ -10253,7 +10187,7 @@ static int load_log_root(u64 root_id, struct btrfs_path *path, struct btrfs_root
 
 static int check_log(struct cache_tree *root_cache)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key = {
 		.objectid = BTRFS_TREE_LOG_OBJECTID,
 		.type = BTRFS_ROOT_ITEM_KEY,
@@ -10265,10 +10199,8 @@ static int check_log(struct cache_tree *root_cache)
 	int err = 0;
 
 	ret = btrfs_search_slot(NULL, log_root, &key, &path, 0, 0);
-	if (ret < 0) {
-		err = 1;
-		goto out;
-	}
+	if (ret < 0)
+		return 1;
 
 	while (1) {
 		leaf = path.nodes[0];
@@ -10311,8 +10243,6 @@ next:
 
 		path.slots[0]++;
 	}
-out:
-	btrfs_release_path(&path);
 
 	return err;
 }
@@ -10344,7 +10274,7 @@ static int build_roots_info_cache(void)
 	int ret = 0;
 	struct btrfs_key key;
 	struct extent_buffer *leaf;
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 
 	if (!roots_info_cache) {
 		roots_info_cache = malloc(sizeof(*roots_info_cache));
@@ -10358,7 +10288,7 @@ static int build_roots_info_cache(void)
 	key.offset = 0;
 	ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
 	if (ret < 0)
-		goto out;
+		return ret;
 	leaf = path.nodes[0];
 
 	while (1) {
@@ -10433,10 +10363,8 @@ static int build_roots_info_cache(void)
 		entry = lookup_cache_extent(roots_info_cache, root_id, 1);
 		if (!entry) {
 			rii = malloc(sizeof(struct root_item_info));
-			if (!rii) {
-				ret = -ENOMEM;
-				goto out;
-			}
+			if (!rii)
+				return -ENOMEM;
 			rii->cache_extent.start = root_id;
 			rii->cache_extent.size = 1;
 			rii->level = (u8)-1;
@@ -10462,10 +10390,6 @@ static int build_roots_info_cache(void)
 next:
 		path.slots[0]++;
 	}
-
-out:
-	btrfs_release_path(&path);
-
 	return ret;
 }
 
@@ -10559,7 +10483,7 @@ static int maybe_repair_root_item(struct btrfs_path *path,
  */
 static int repair_root_items(void)
 {
-	struct btrfs_path path = { 0 };
+	BTRFS_PATH_AUTO_RELEASE(path);
 	struct btrfs_key key;
 	struct extent_buffer *leaf;
 	struct btrfs_trans_handle *trans = NULL;
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 09/12] btrfs-progs: check: fix fixup_extent_flags() retry path
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
                   ` (7 preceding siblings ...)
  2026-10-30  4:15 ` [PATCH 08/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for original mode Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:15 ` [PATCH 10/12] btrfs-progs: fix missing transaction commit in truncate_free_ino_items() Qu Wenruo
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

If the skinny metadata item can't be found, fixup_extent_flags() jumps back
to the retry label to search for the non-skinny extent item.

But this retry path has two problems:

- The path is not released before the next search
  Thus the tree blocks held by the path are leaked.

- A new transaction is started while the previous one is still running
  btrfs_start_transaction() refuses to start a transaction over an
  already running one, thus the retry always fails with -EINVAL.

Fix both by starting the transaction before the retry label, and releasing
the path before jumping back.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 check/main.c | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/check/main.c b/check/main.c
index b3641cd39169..194d04ce017f 100644
--- a/check/main.c
+++ b/check/main.c
@@ -7963,6 +7963,14 @@ static int fixup_extent_flags(struct extent_record *rec)
 
 	if (!btrfs_fs_incompat(gfs_info, SKINNY_METADATA))
 		metadata_item = false;
+
+	trans = btrfs_start_transaction(root, 0);
+	if (IS_ERR(trans)) {
+		ret = PTR_ERR(trans);
+		errno = -ret;
+		error_msg(ERROR_MSG_START_TRANS, "%m");
+		return ret;
+	}
 retry:
 	key.objectid = rec->start;
 	if (metadata_item) {
@@ -7973,14 +7981,6 @@ retry:
 		key.offset = rec->max_size;
 	}
 
-	trans = btrfs_start_transaction(root, 0);
-	if (IS_ERR(trans)) {
-		ret = PTR_ERR(trans);
-		errno = -ret;
-		error_msg(ERROR_MSG_START_TRANS, "%m");
-		return ret;
-	}
-
 	ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
 	if (ret < 0) {
 		btrfs_release_path(&path);
@@ -7989,6 +7989,7 @@ retry:
 	} else if (ret) {
 		if (key.type == BTRFS_METADATA_ITEM_KEY) {
 			metadata_item = false;
+			btrfs_release_path(&path);
 			goto retry;
 		}
 		fprintf(stderr, "Didn't find extent for %llu\n", rec->start);
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 10/12] btrfs-progs: fix missing transaction commit in truncate_free_ino_items()
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
                   ` (8 preceding siblings ...)
  2026-10-30  4:15 ` [PATCH 09/12] btrfs-progs: check: fix fixup_extent_flags() retry path Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:15 ` [PATCH 11/12] btrfs-progs: dump-tree: fix exit code and leak on open failure Qu Wenruo
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

When truncate_free_ino_items() finds that there are no more items to
delete, it directly returns, without committing nor aborting the
transaction it started.

Thus all the items deleted by the previous iterations are lost, and the
transaction handle is leaked.

This is a very low risk bug, as there are very few ino-cache users (if
any), so this ino items removal can be considered almost dead-code.

Fix it by breaking out of the loop, so the transaction is committed like
the other exit path.

Also since we're here, also report btrfs_commit_transaction() error.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 common/clear-cache.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/common/clear-cache.c b/common/clear-cache.c
index bfefd325d700..d0a6700b0e81 100644
--- a/common/clear-cache.c
+++ b/common/clear-cache.c
@@ -881,7 +881,7 @@ int truncate_free_ino_items(struct btrfs_root *root)
 			ret = 0;
 			/* No more items, finished truncating */
 			if (path.slots[0] == 0)
-				return ret;
+				break;
 			path.slots[0]--;
 		}
 		fi = NULL;
@@ -945,7 +945,11 @@ int truncate_free_ino_items(struct btrfs_root *root)
 		}
 	}
 
-	btrfs_commit_transaction(trans, root);
+	ret = btrfs_commit_transaction(trans, root);
+	if (ret < 0) {
+		errno = -ret;
+		error_msg(ERROR_MSG_COMMIT_TRANS, "%m");
+	}
 	return ret;
 }
 
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 11/12] btrfs-progs: dump-tree: fix exit code and leak on open failure
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
                   ` (9 preceding siblings ...)
  2026-10-30  4:15 ` [PATCH 10/12] btrfs-progs: fix missing transaction commit in truncate_free_ino_items() Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:15 ` [PATCH 12/12] btrfs-progs: check: return error if fill_csum_tree_from_fs() fails to read a root Qu Wenruo
  2026-10-30  4:19 ` [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

If open_ctree_fs_info() fails, or the fs root can not be opened,
cmd_inspect_dump_tree() prints an error but returns !!ret, and ret is
always 0 at that point.

So the command exits with 0 on such failure. Furthermore, if the fs root is
missing, the opened fs_info is never closed.

Fix it by returning 1 for both cases, and close the fs_info for the second
one.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 cmds/inspect-dump-tree.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/cmds/inspect-dump-tree.c b/cmds/inspect-dump-tree.c
index 09ac9e181376..a9b369b4aba2 100644
--- a/cmds/inspect-dump-tree.c
+++ b/cmds/inspect-dump-tree.c
@@ -412,7 +412,7 @@ static int cmd_inspect_dump_tree(const struct cmd_struct *cmd,
 	info = open_ctree_fs_info(&oca);
 	if (!info) {
 		error("unable to open %s", argv[optind]);
-		return !!ret;
+		return 1;
 	}
 
 	print_mode = follow | traverse | csum_mode;
@@ -426,7 +426,8 @@ static int cmd_inspect_dump_tree(const struct cmd_struct *cmd,
 	root = info->fs_root;
 	if (!root) {
 		error("unable to open %s", argv[optind]);
-		return !!ret;
+		close_ctree(info->chunk_root);
+		return 1;
 	}
 
 	if (!(extent_only || uuid_tree_only || tree_id)) {
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 12/12] btrfs-progs: check: return error if fill_csum_tree_from_fs() fails to read a root
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
                   ` (10 preceding siblings ...)
  2026-10-30  4:15 ` [PATCH 11/12] btrfs-progs: dump-tree: fix exit code and leak on open failure Qu Wenruo
@ 2026-10-30  4:15 ` Qu Wenruo
  2026-10-30  4:19 ` [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:15 UTC (permalink / raw)
  To: linux-btrfs

If fill_csum_tree_from_fs() can not read a fs/subvolume tree, it prints an
error message but returns @ret, which is 0 at that point.

Thus the caller will consider the csum tree rebuild as successful, even if
the csum items of that subvolume are not generated.

Return the proper error instead.

Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 check/mode-common.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/check/mode-common.c b/check/mode-common.c
index 7d829661eb3d..cda682313f49 100644
--- a/check/mode-common.c
+++ b/check/mode-common.c
@@ -1348,8 +1348,10 @@ static int fill_csum_tree_from_fs(struct btrfs_trans_handle *trans)
 		key.offset = (u64)-1;
 
 		cur_root = btrfs_read_fs_root(gfs_info, &key);
-		if (IS_ERR(cur_root) || !cur_root) {
-			fprintf(stderr, "Fail to read fs/subvol tree: %lld\n",
+		if (IS_ERR(cur_root)) {
+			ret = PTR_ERR(cur_root);
+			errno = -ret;
+			fprintf(stderr, "Fail to read fs/subvol tree %lld: %m\n",
 				key.objectid);
 			return ret;
 		}
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2
  2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
                   ` (11 preceding siblings ...)
  2026-10-30  4:15 ` [PATCH 12/12] btrfs-progs: check: return error if fill_csum_tree_from_fs() fails to read a root Qu Wenruo
@ 2026-10-30  4:19 ` Qu Wenruo
  12 siblings, 0 replies; 14+ messages in thread
From: Qu Wenruo @ 2026-10-30  4:19 UTC (permalink / raw)
  To: linux-btrfs



在 2026/10/30 14:45, Qu Wenruo 写道:
> This is the part 2, focusing on the on-stack btrfs_path.

This obviously rely on the part 1.

For the full patchset series, one can fetch it from the github PR:

https://github.com/kdave/btrfs-progs/pull/1191

> 
> Unlike heap-allocated btrfs_path, which needs proper btrfs_free_path()
> call, or the btrfs_path itself along with the held ebs will leak.
> 
> The on-stack one only leaks the held ebs, but still we should fix them.
> 
> Just by the amount of code, one should know this is largely generated by
> LLM, although I spent more time drafting the plan and review the final
> patch.
> 
> There are some extra notes other than just converting existing
> "btrfs_path" on-stack declaration to BTRFS_PATH_AUTO_RELEASE:
> 
> - If a label is only for cleanup, remove the label when possible
>    As a lot of call sites can directly return without jumping to the
>    cleanup label.
> 
>    Also cleanup some "ret = x; return ret;" patterns.
> 
> - Keep dedicated btrfs_release_path() if needed
>    As our C code is not fully aligning the path lifespan with the scope,
>    we still need to manually release path halfway for certain call sites.
> 
>    E.g. before calling btrfs_commit_transaction(),
>    btrfs_abort_transaction(), btrfs_search_slot(), close_ctree() or
>    before lowmem mode modifying the tree.
> 
>    Releasing the path before btrfs_commit_transaction() is to follow the
>    kernel convension. There are a lot of existing call sites breaking it,
>    so here we do not intentionally fix them.
> 
>    This will require a dedicated change in the future.
> 
> - Reduce the lifespan of btrfs_path when possible
>    E.g. the path is only utilized inside a loop or if () branch, declare
>    path there.
> 
> This applies to patch 01~08, which are all straightforward conversion
> with minor cleanups.
> 
> After that, the code review process exposed some other bugs in the
> touched functions, which are all pretty simple and low severity ones.
> Patch 09~12 are to fix those minor bugs.
> 
> Qu Wenruo (12):
>    btrfs-progs: kernel-shared: use BTRFS_PATH_AUTO_RELEASE() for on-stack
>      paths
>    btrfs-progs: common, convert, image: use BTRFS_PATH_AUTO_RELEASE() for
>      on-stack paths
>    btrfs-progs: mkfs: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths
>    btrfs-progs: tune: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths
>    btrfs-progs: cmds: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths
>    btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for repair,
>      qgroup-verify and mode-common
>    btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for lowmem mode
>    btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for original mode
>    btrfs-progs: check: fix fixup_extent_flags() retry path
>    btrfs-progs: fix missing transaction commit in
>      truncate_free_ino_items()
>    btrfs-progs: dump-tree: fix exit code and leak on open failure
>    btrfs-progs: check: return error if fill_csum_tree_from_fs() fails to
>      read a root
> 
>   check/main.c                     | 235 +++++++++++--------------------
>   check/mode-common.c              | 141 +++++++------------
>   check/mode-lowmem.c              | 202 +++++++++-----------------
>   check/qgroup-verify.c            |  29 ++--
>   check/repair.c                   |   5 +-
>   cmds/device.c                    |  11 +-
>   cmds/inspect-dump-tree.c         |  11 +-
>   cmds/rescue-chunk-recover.c      |  43 ++----
>   cmds/rescue-fix-data-checksum.c  |  10 +-
>   cmds/rescue.c                    |   7 +-
>   cmds/restore.c                   | 126 +++++++----------
>   common/clear-cache.c             |  79 ++++-------
>   common/root-tree-utils.c         |  10 +-
>   convert/main.c                   |   9 +-
>   convert/source-ext2.c            |   3 +-
>   convert/source-fs.c              |   3 +-
>   image/common.c                   |   2 +-
>   image/image-create.c             |   2 +-
>   image/image-restore.c            |  20 +--
>   kernel-shared/extent-tree.c      |  62 +++-----
>   kernel-shared/file.c             |   3 +-
>   kernel-shared/free-space-cache.c |  24 ++--
>   kernel-shared/inode.c            |   8 +-
>   kernel-shared/print-tree.c       |   9 +-
>   kernel-shared/volumes.c          |  15 +-
>   mkfs/main.c                      |  42 ++----
>   mkfs/rootdir.c                   |  36 ++---
>   tests/btree-test.c               |   2 +-
>   tune/change-csum.c               |  50 ++-----
>   tune/change-uuid.c               |  38 ++---
>   tune/convert-remap-tree.c        |  12 +-
>   tune/quota.c                     |  19 ++-
>   32 files changed, 447 insertions(+), 821 deletions(-)
> 
> --
> 2.56.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-10-30  4:19 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-30  4:15 [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo
2026-10-30  4:15 ` [PATCH 01/12] btrfs-progs: kernel-shared: use BTRFS_PATH_AUTO_RELEASE() for on-stack paths Qu Wenruo
2026-10-30  4:15 ` [PATCH 02/12] btrfs-progs: common, convert, image: " Qu Wenruo
2026-10-30  4:15 ` [PATCH 03/12] btrfs-progs: mkfs: " Qu Wenruo
2026-10-30  4:15 ` [PATCH 04/12] btrfs-progs: tune: " Qu Wenruo
2026-10-30  4:15 ` [PATCH 05/12] btrfs-progs: cmds: " Qu Wenruo
2026-10-30  4:15 ` [PATCH 06/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for repair, qgroup-verify and mode-common Qu Wenruo
2026-10-30  4:15 ` [PATCH 07/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for lowmem mode Qu Wenruo
2026-10-30  4:15 ` [PATCH 08/12] btrfs-progs: check: use BTRFS_PATH_AUTO_RELEASE() for original mode Qu Wenruo
2026-10-30  4:15 ` [PATCH 09/12] btrfs-progs: check: fix fixup_extent_flags() retry path Qu Wenruo
2026-10-30  4:15 ` [PATCH 10/12] btrfs-progs: fix missing transaction commit in truncate_free_ino_items() Qu Wenruo
2026-10-30  4:15 ` [PATCH 11/12] btrfs-progs: dump-tree: fix exit code and leak on open failure Qu Wenruo
2026-10-30  4:15 ` [PATCH 12/12] btrfs-progs: check: return error if fill_csum_tree_from_fs() fails to read a root Qu Wenruo
2026-10-30  4:19 ` [PATCH 00/12] btrfs-progs: fix btrfs_path leak, part 2 Qu Wenruo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.