mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-25 14:10:43 +00:00
c331efd087
If btrfs_read_fs_root() fails with -ENOENT, then we go to the next entry. Fine. But if it fails for a different reason then we need to clean up and return an error code. In the current code it doesn't clean up but instead dereferences "root" and crashes. Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org> Reviewed-by: Marek Behún <kabel@kernel.org> Reviewed-by: Qu Wenruo <wqu@suse.com>
235 lines
5.4 KiB
C
235 lines
5.4 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* BTRFS filesystem implementation for U-Boot
|
|
*
|
|
* 2017 Marek Behún, CZ.NIC, kabel@kernel.org
|
|
*/
|
|
|
|
#include <malloc.h>
|
|
#include "ctree.h"
|
|
#include "btrfs.h"
|
|
#include "disk-io.h"
|
|
|
|
/*
|
|
* Resolve the path of ino inside subvolume @root into @path_ret.
|
|
*
|
|
* @path_ret must be at least PATH_MAX size.
|
|
*/
|
|
static int get_path_in_subvol(struct btrfs_root *root, u64 ino, char *path_ret)
|
|
{
|
|
struct btrfs_path path;
|
|
struct btrfs_key key;
|
|
char *tmp;
|
|
u64 cur = ino;
|
|
int ret = 0;
|
|
|
|
tmp = malloc(PATH_MAX);
|
|
if (!tmp)
|
|
return -ENOMEM;
|
|
tmp[0] = '\0';
|
|
|
|
btrfs_init_path(&path);
|
|
while (cur != BTRFS_FIRST_FREE_OBJECTID) {
|
|
struct btrfs_inode_ref *iref;
|
|
int name_len;
|
|
|
|
btrfs_release_path(&path);
|
|
key.objectid = cur;
|
|
key.type = BTRFS_INODE_REF_KEY;
|
|
key.offset = (u64)-1;
|
|
|
|
ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
|
|
/* Impossible */
|
|
if (ret == 0)
|
|
ret = -EUCLEAN;
|
|
if (ret < 0)
|
|
goto out;
|
|
ret = btrfs_previous_item(root, &path, cur,
|
|
BTRFS_INODE_REF_KEY);
|
|
if (ret > 0)
|
|
ret = -ENOENT;
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
strncpy(tmp, path_ret, PATH_MAX);
|
|
iref = btrfs_item_ptr(path.nodes[0], path.slots[0],
|
|
struct btrfs_inode_ref);
|
|
name_len = btrfs_inode_ref_name_len(path.nodes[0],
|
|
iref);
|
|
if (name_len > BTRFS_NAME_LEN) {
|
|
ret = -ENAMETOOLONG;
|
|
goto out;
|
|
}
|
|
read_extent_buffer(path.nodes[0], path_ret,
|
|
(unsigned long)(iref + 1), name_len);
|
|
path_ret[name_len] = '/';
|
|
path_ret[name_len + 1] = '\0';
|
|
strncat(path_ret, tmp, PATH_MAX);
|
|
|
|
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
|
|
cur = key.offset;
|
|
}
|
|
out:
|
|
btrfs_release_path(&path);
|
|
free(tmp);
|
|
return ret;
|
|
}
|
|
|
|
static int list_one_subvol(struct btrfs_root *root, char *path_ret)
|
|
{
|
|
struct btrfs_fs_info *fs_info = root->fs_info;
|
|
struct btrfs_root *tree_root = fs_info->tree_root;
|
|
struct btrfs_path path;
|
|
struct btrfs_key key;
|
|
char *tmp;
|
|
u64 cur = root->root_key.objectid;
|
|
int ret = 0;
|
|
|
|
tmp = malloc(PATH_MAX);
|
|
if (!tmp)
|
|
return -ENOMEM;
|
|
tmp[0] = '\0';
|
|
path_ret[0] = '\0';
|
|
btrfs_init_path(&path);
|
|
while (cur != BTRFS_FS_TREE_OBJECTID) {
|
|
struct btrfs_root_ref *rr;
|
|
struct btrfs_key location;
|
|
int name_len;
|
|
u64 ino;
|
|
|
|
key.objectid = cur;
|
|
key.type = BTRFS_ROOT_BACKREF_KEY;
|
|
key.offset = (u64)-1;
|
|
btrfs_release_path(&path);
|
|
|
|
ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
|
|
if (ret == 0)
|
|
ret = -EUCLEAN;
|
|
if (ret < 0)
|
|
goto out;
|
|
ret = btrfs_previous_item(tree_root, &path, cur,
|
|
BTRFS_ROOT_BACKREF_KEY);
|
|
if (ret > 0)
|
|
ret = -ENOENT;
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
/* Get the subvolume name */
|
|
rr = btrfs_item_ptr(path.nodes[0], path.slots[0],
|
|
struct btrfs_root_ref);
|
|
strncpy(tmp, path_ret, PATH_MAX);
|
|
name_len = btrfs_root_ref_name_len(path.nodes[0], rr);
|
|
if (name_len > BTRFS_NAME_LEN) {
|
|
ret = -ENAMETOOLONG;
|
|
goto out;
|
|
}
|
|
ino = btrfs_root_ref_dirid(path.nodes[0], rr);
|
|
read_extent_buffer(path.nodes[0], path_ret,
|
|
(unsigned long)(rr + 1), name_len);
|
|
path_ret[name_len] = '/';
|
|
path_ret[name_len + 1] = '\0';
|
|
strncat(path_ret, tmp, PATH_MAX);
|
|
|
|
/* Get the path inside the parent subvolume */
|
|
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
|
|
location.objectid = key.offset;
|
|
location.type = BTRFS_ROOT_ITEM_KEY;
|
|
location.offset = (u64)-1;
|
|
root = btrfs_read_fs_root(fs_info, &location);
|
|
if (IS_ERR(root)) {
|
|
ret = PTR_ERR(root);
|
|
goto out;
|
|
}
|
|
ret = get_path_in_subvol(root, ino, path_ret);
|
|
if (ret < 0)
|
|
goto out;
|
|
cur = key.offset;
|
|
}
|
|
/* Add the leading '/' */
|
|
strncpy(tmp, path_ret, PATH_MAX);
|
|
strncpy(path_ret, "/", PATH_MAX);
|
|
strncat(path_ret, tmp, PATH_MAX);
|
|
out:
|
|
btrfs_release_path(&path);
|
|
free(tmp);
|
|
return ret;
|
|
}
|
|
|
|
static int list_subvolums(struct btrfs_fs_info *fs_info)
|
|
{
|
|
struct btrfs_root *tree_root = fs_info->tree_root;
|
|
struct btrfs_root *root;
|
|
struct btrfs_path path;
|
|
struct btrfs_key key;
|
|
char *result;
|
|
int ret = 0;
|
|
|
|
result = malloc(PATH_MAX);
|
|
if (!result)
|
|
return -ENOMEM;
|
|
|
|
ret = list_one_subvol(fs_info->fs_root, result);
|
|
if (ret < 0)
|
|
goto out;
|
|
root = fs_info->fs_root;
|
|
printf("ID %llu gen %llu path %.*s\n",
|
|
root->root_key.objectid, btrfs_root_generation(&root->root_item),
|
|
PATH_MAX, result);
|
|
|
|
key.objectid = BTRFS_FIRST_FREE_OBJECTID;
|
|
key.type = BTRFS_ROOT_ITEM_KEY;
|
|
key.offset = 0;
|
|
btrfs_init_path(&path);
|
|
ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
|
|
if (ret < 0)
|
|
goto out;
|
|
while (1) {
|
|
if (path.slots[0] >= btrfs_header_nritems(path.nodes[0]))
|
|
goto next;
|
|
|
|
btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
|
|
if (key.objectid > BTRFS_LAST_FREE_OBJECTID)
|
|
break;
|
|
if (key.objectid < BTRFS_FIRST_FREE_OBJECTID ||
|
|
key.type != BTRFS_ROOT_ITEM_KEY)
|
|
goto next;
|
|
key.offset = (u64)-1;
|
|
root = btrfs_read_fs_root(fs_info, &key);
|
|
if (IS_ERR(root)) {
|
|
ret = PTR_ERR(root);
|
|
if (ret == -ENOENT)
|
|
goto next;
|
|
goto out;
|
|
}
|
|
ret = list_one_subvol(root, result);
|
|
if (ret < 0)
|
|
goto out;
|
|
printf("ID %llu gen %llu path %.*s\n",
|
|
root->root_key.objectid,
|
|
btrfs_root_generation(&root->root_item),
|
|
PATH_MAX, result);
|
|
next:
|
|
ret = btrfs_next_item(tree_root, &path);
|
|
if (ret < 0)
|
|
goto out;
|
|
if (ret > 0) {
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
out:
|
|
free(result);
|
|
return ret;
|
|
}
|
|
|
|
void btrfs_list_subvols(void)
|
|
{
|
|
struct btrfs_fs_info *fs_info = current_fs_info;
|
|
int ret;
|
|
|
|
if (!fs_info)
|
|
return;
|
|
ret = list_subvolums(fs_info);
|
|
if (ret < 0)
|
|
error("failed to list subvolume: %d", ret);
|
|
}
|