Btrfs: leak in btrfs_find_item and cleanups
Coverity for btrfs-progs spotted a variable leak in btrfs_find_item though the same error is not reported in the kernel project and the code is the same. The leak is very rare though and does not need to go to stable immediatelly. The rest of patches are cleanups in the related codepaths.
You can also pull the changes from
git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git fix/find-item-path-leak
Top commit: 1d4c08e0a60be3561
Base: 3.19-rc4
David Sterba (5):
btrfs: fix leak of path in btrfs_find_item
btrfs: cleanup, remove inode_item_info helper
btrfs: cleanup, remove inode_ref_info helper
btrfs: simplify insert_orphan_item
btrfs: expand btrfs_find_item if found_key is NULL
fs/btrfs/backref.c | 28 ++++++---------------------- fs/btrfs/backref.h | 3 --- fs/btrfs/ctree.c | 18 +++++------------- fs/btrfs/disk-io.c | 15 +++++++++++++-- fs/btrfs/inode.c | 10 +++++++--- fs/btrfs/scrub.c | 10 +++++++++- fs/btrfs/tree-log.c | 11 ++++++----- 7 files changed, 46 insertions(+), 49 deletions(-)
Signature · 1 line
1.8.4.5
PATCH 1/5 btrfs: fix leak of path in btrfs_find_item
If btrfs_find_item is called with NULL path it allocates one locally but does not free it. Affected paths are inserting an orphan item for a file and for a subvol root.
Move the path allocation to the callers.
--- fs/btrfs/ctree.c | 17 ++++------------- fs/btrfs/disk-io.c | 9 ++++++++- fs/btrfs/tree-log.c | 11 ++++++++++- 3 files changed, 22 insertions(+), 15 deletions(-)
fs/btrfs/ctree.c +4 -13
@@ -2609,32 +2609,23 @@ static int key_search(struct extent_buffer *b, struct btrfs_key *key,return 0 ; } -int btrfs_find_item (struct btrfs_root *fs_root,struct btrfs_path *found_path, +int btrfs_find_item (struct btrfs_root *fs_root,struct btrfs_path *path,u64 iobjectid,u64 ioff,u8 key_type,struct btrfs_key *found_key) {int ret;struct btrfs_key key;struct extent_buffer *eb; -struct btrfs_path *path; + +ASSERT (path); key.type = key_type; key.objectid = iobjectid; key.offset = ioff; -if (found_path ==NULL ) { - path =btrfs_alloc_path (); -if (!path) -return -ENOMEM ; - }else - path = found_path; - ret =btrfs_search_slot (NULL , fs_root, &key, path,0 ,0 ); -if ((ret <0 ) || (found_key ==NULL )) { -if (path != found_path) -btrfs_free_path (path); +if ((ret <0 ) || (found_key ==NULL ))return ret; - } eb = path->nodes[0 ];if (ret && path->slots[0 ] >=btrfs_header_nritems (eb)) {
fs/btrfs/disk-io.c +8 -1
@@ -1630,6 +1630,7 @@ struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info, bool check_ref) {struct btrfs_root *root; +struct btrfs_path *path;int ret;if (location->objectid ==BTRFS_ROOT_TREE_OBJECTID )
@@ -1669,8 +1670,14 @@ again:if (ret) goto fail; - ret =btrfs_find_item (fs_info->tree_root,NULL ,BTRFS_ORPHAN_OBJECTID , + path =btrfs_alloc_path (); +if (!path) { + ret = -ENOMEM ; + goto fail; + } + ret =btrfs_find_item (fs_info->tree_root, path,BTRFS_ORPHAN_OBJECTID , location->objectid,BTRFS_ORPHAN_ITEM_KEY ,NULL ); +btrfs_free_path (path);if (ret <0 ) goto fail;if (ret ==0 )
fs/btrfs/tree-log.c +10 -1
@@ -1257,10 +1257,19 @@ static int insert_orphan_item(struct btrfs_trans_handle *trans, struct btrfs_root *root, u64 offset) { int ret; - ret =btrfs_find_item (root,NULL ,BTRFS_ORPHAN_OBJECTID , +struct btrfs_path *path; + + path =btrfs_alloc_path (); +if (!path) +return -ENOMEM ; + + ret =btrfs_find_item (root, path,BTRFS_ORPHAN_OBJECTID , offset,BTRFS_ORPHAN_ITEM_KEY ,NULL );if (ret >0 ) ret =btrfs_insert_orphan_item (trans, root, offset); + +btrfs_free_path (path); +return ret; }
Signature · 1 line
1.8.4.5
PATCH 2/5 btrfs: cleanup, remove inode_item_info helper
In reply to David SterbaIt's only a simple wrapper around btrfs_find_item, the locally defined key is not used.
--- fs/btrfs/backref.c | 11 ----------- fs/btrfs/backref.h | 3 --- fs/btrfs/scrub.c | 6 +++++- 3 files changed, 5 insertions(+), 15 deletions(-)
fs/btrfs/backref.c +0 -11
@@ -1246,17 +1246,6 @@ int btrfs_check_shared(struct btrfs_trans_handle *trans,return ret; } -/* -* this makes the path point to (inum INODE_ITEM ioff) -*/ -int inode_item_info (u64 inum,u64 ioff,struct btrfs_root *fs_root, -struct btrfs_path *path) -{ -struct btrfs_key key; -return btrfs_find_item (fs_root, path, inum, ioff, -BTRFS_INODE_ITEM_KEY , &key); -} -static int inode_ref_info (u64 inum,u64 ioff,struct btrfs_root *fs_root,struct btrfs_path *path,struct btrfs_key *found_key)
fs/btrfs/backref.h +0 -3
@@ -32,9 +32,6 @@ struct inode_fs_paths {typedef int (iterate_extent_inodes_t )(u64 inum,u64 offset,u64 root,void *ctx); -int inode_item_info (u64 inum,u64 ioff,struct btrfs_root *fs_root, -struct btrfs_path *path); -int extent_from_logical (struct btrfs_fs_info *fs_info,u64 logical,struct btrfs_path *path,struct btrfs_key *found_key,u64 *flags);
fs/btrfs/scrub.c +5 -1
@@ -530,7 +530,11 @@ static int scrub_print_warning_inode(u64 inum, u64 offset, u64 root, goto err; } - ret =inode_item_info (inum,0 , local_root, swarn->path); +/* +* this makes the path point to (inum INODE_ITEM ioff) +*/ + ret =btrfs_find_item (local_root, swarn->path, inum,0 , +BTRFS_INODE_ITEM_KEY ,NULL );if (ret) {btrfs_release_path (swarn->path); goto err;
Signature · 1 line
1.8.4.5
PATCH 3/5 btrfs: cleanup, remove inode_ref_info helper
In reply to David SterbaA simple wrapper around btrfs_find_item.
--- fs/btrfs/backref.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-)
fs/btrfs/backref.c +6 -11
@@ -1246,14 +1246,6 @@ int btrfs_check_shared(struct btrfs_trans_handle *trans,return ret; } -static int inode_ref_info (u64 inum,u64 ioff,struct btrfs_root *fs_root, -struct btrfs_path *path, -struct btrfs_key *found_key) -{ -return btrfs_find_item (fs_root, path, inum, ioff, -BTRFS_INODE_REF_KEY , found_key); -} - int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid, u64 start_off, struct btrfs_path *path, struct btrfs_inode_extref **ret_extref,
@@ -1363,7 +1355,8 @@ char *btrfs_ref_to_path(struct btrfs_root *fs_root, struct btrfs_path *path,btrfs_tree_read_unlock_blocking (eb);free_extent_buffer (eb); } - ret =inode_ref_info (parent,0 , fs_root, path, &found_key); + ret =btrfs_find_item (fs_root, path, parent,0 , +BTRFS_INODE_REF_KEY , &found_key);if (ret >0 ) ret = -ENOENT ;if (ret)
@@ -1709,8 +1702,10 @@ static int iterate_inode_refs(u64 inum, struct btrfs_root *fs_root,struct btrfs_key found_key;while (!ret) { - ret =inode_ref_info (inum, parent ? parent+1 :0 , fs_root, path, - &found_key); + ret =btrfs_find_item (fs_root, path, inum, + parent ? parent +1 :0 ,BTRFS_INODE_REF_KEY , + &found_key); +if (ret <0 )break ;if (ret) {
Signature · 1 line
1.8.4.5
PATCH 4/5 btrfs: simplify insert_orphan_item
In reply to David SterbaWe can search and add the orphan item in one go, btrfs_insert_orphan_item will find out if the item already exists.
--- fs/btrfs/tree-log.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-)
fs/btrfs/tree-log.c +4 -12
@@ -1254,21 +1254,13 @@ out: }static int insert_orphan_item (struct btrfs_trans_handle *trans, -struct btrfs_root *root,u64 offset) +struct btrfs_root *root,u64 ino) {int ret; -struct btrfs_path *path; - - path =btrfs_alloc_path (); -if (!path) -return -ENOMEM ; - ret =btrfs_find_item (root, path,BTRFS_ORPHAN_OBJECTID , - offset,BTRFS_ORPHAN_ITEM_KEY ,NULL ); -if (ret >0 ) - ret =btrfs_insert_orphan_item (trans, root, offset); - -btrfs_free_path (path); + ret =btrfs_insert_orphan_item (trans, root, ino); +if (ret == -EEXIST ) + ret =0 ;return ret; }
Signature · 1 line
1.8.4.5
PATCH 5/5 btrfs: expand btrfs_find_item if found_key is NULL
In reply to David SterbaIf the found_key is NULL, then btrfs_find_item becomes a verbose wrapper for simple btrfs_search_slot.
After we've removed all such callers, passing a NULL key is not valid anymore.
--- fs/btrfs/ctree.c | 3 ++- fs/btrfs/disk-io.c | 8 ++++++-- fs/btrfs/inode.c | 10 +++++++--- fs/btrfs/scrub.c | 8 ++++++-- 4 files changed, 21 insertions(+), 8 deletions(-)
fs/btrfs/ctree.c +2 -1
@@ -2618,13 +2618,14 @@ int btrfs_find_item(struct btrfs_root *fs_root, struct btrfs_path *path,struct extent_buffer *eb;ASSERT (path); +ASSERT (found_key); key.type = key_type; key.objectid = iobjectid; key.offset = ioff; ret =btrfs_search_slot (NULL , fs_root, &key, path,0 ,0 ); -if ((ret <0 ) || (found_key ==NULL )) +if (ret <0 )return ret; eb = path->nodes[0 ];
fs/btrfs/disk-io.c +6 -2
@@ -1631,6 +1631,7 @@ struct btrfs_root *btrfs_get_fs_root(struct btrfs_fs_info *fs_info, {struct btrfs_root *root;struct btrfs_path *path; +struct btrfs_key key;int ret;if (location->objectid ==BTRFS_ROOT_TREE_OBJECTID )
@@ -1675,8 +1676,11 @@ again: ret = -ENOMEM ; goto fail; } - ret =btrfs_find_item (fs_info->tree_root, path,BTRFS_ORPHAN_OBJECTID , - location->objectid,BTRFS_ORPHAN_ITEM_KEY ,NULL ); + key.objectid =BTRFS_ORPHAN_OBJECTID ; + key.type =BTRFS_ORPHAN_ITEM_KEY ; + key.offset = location->objectid; + + ret =btrfs_search_slot (NULL , fs_info->tree_root, &key, path,0 ,0 );btrfs_free_path (path);if (ret <0 ) goto fail;
fs/btrfs/inode.c +7 -3
@@ -5009,6 +5009,7 @@ static int fixup_tree_root_location(struct btrfs_root *root,struct btrfs_root *new_root;struct btrfs_root_ref *ref;struct extent_buffer *leaf; +struct btrfs_key key;int ret;int err =0 ;
@@ -5019,9 +5020,12 @@ static int fixup_tree_root_location(struct btrfs_root *root, } err = -ENOENT ; - ret =btrfs_find_item (root->fs_info->tree_root, path, -BTRFS_I (dir)->root->root_key.objectid, - location->objectid,BTRFS_ROOT_REF_KEY ,NULL ); + key.objectid =BTRFS_I (dir)->root->root_key.objectid; + key.type =BTRFS_ROOT_REF_KEY ; + key.offset = location->objectid; + + ret =btrfs_search_slot (NULL , root->fs_info->tree_root, &key, path, +0 ,0 );if (ret) {if (ret <0 ) err = ret;
fs/btrfs/scrub.c +6 -2
@@ -520,6 +520,7 @@ static int scrub_print_warning_inode(u64 inum, u64 offset, u64 root,struct inode_fs_paths *ipath =NULL ;struct btrfs_root *local_root;struct btrfs_key root_key; +struct btrfs_key key; root_key.objectid = root; root_key.type =BTRFS_ROOT_ITEM_KEY ;
@@ -533,8 +534,11 @@ static int scrub_print_warning_inode(u64 inum, u64 offset, u64 root,/* * this makes the path point to (inum INODE_ITEM ioff) */ - ret =btrfs_find_item (local_root, swarn->path, inum,0 , -BTRFS_INODE_ITEM_KEY ,NULL ); + key.objectid = inum; + key.type =BTRFS_INODE_ITEM_KEY ; + key.offset =0 ; + + ret =btrfs_search_slot (NULL , local_root, &key, swarn->path,0 ,0 );if (ret) {btrfs_release_path (swarn->path); goto err;
Signature · 1 line
1.8.4.5