// SPDX-License-Identifier: GPL-2.0+ /* * BTRFS filesystem implementation for U-Boot * * 2017 Marek BehĂșn, CZ.NIC, kabel@kernel.org */ #include #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); }