diff options
Diffstat (limited to 'fs/btrfs/subvolume.c')
-rw-r--r-- | fs/btrfs/subvolume.c | 310 |
1 files changed, 207 insertions, 103 deletions
diff --git a/fs/btrfs/subvolume.c b/fs/btrfs/subvolume.c index 06e54f3310..2815673bcd 100644 --- a/fs/btrfs/subvolume.c +++ b/fs/btrfs/subvolume.c @@ -5,126 +5,230 @@ * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz */ -#include "btrfs.h" #include <malloc.h> +#include "ctree.h" +#include "btrfs.h" +#include "disk-io.h" -static int get_subvol_name(u64 subvolid, char *name, int max_len) +/* + * 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_root_ref rref; - struct btrfs_inode_ref iref; - struct btrfs_root root; - u64 dir; - char tmp[max(BTRFS_VOL_NAME_MAX, BTRFS_NAME_MAX)]; - char *ptr; - - ptr = name + max_len - 1; - *ptr = '\0'; - - while (subvolid != BTRFS_FS_TREE_OBJECTID) { - subvolid = btrfs_lookup_root_ref(subvolid, &rref, tmp); - - if (subvolid == -1ULL) - return -1; - - ptr -= rref.name_len + 1; - if (ptr < name) - goto too_long; - - memcpy(ptr + 1, tmp, rref.name_len); - *ptr = '/'; - - if (btrfs_find_root(subvolid, &root, NULL)) - return -1; - - dir = rref.dirid; - - while (dir != BTRFS_FIRST_FREE_OBJECTID) { - dir = btrfs_lookup_inode_ref(&root, dir, &iref, tmp); - - if (dir == -1ULL) - return -1; - - ptr -= iref.name_len + 1; - if (ptr < name) - goto too_long; - - memcpy(ptr + 1, tmp, iref.name_len); - *ptr = '/'; + 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; } - - if (ptr == name + max_len - 1) { - name[0] = '/'; - name[1] = '\0'; - } else { - memmove(name, ptr, name + max_len - ptr); - } - - return 0; - -too_long: - printf("%s: subvolume name too long\n", __func__); - return -1; +out: + btrfs_release_path(&path); + free(tmp); + return ret; } -u64 btrfs_get_default_subvol_objectid(void) +static int list_one_subvol(struct btrfs_root *root, char *path_ret) { - struct btrfs_dir_item item; - - if (btrfs_lookup_dir_item(&btrfs_info.tree_root, - btrfs_info.sb.root_dir_objectid, "default", 7, - &item)) - return BTRFS_FS_TREE_OBJECTID; - return item.location.objectid; + 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 void list_subvols(u64 tree, char *nameptr, int max_name_len, int level) +static int list_subvolums(struct btrfs_fs_info *fs_info) { - struct btrfs_key key, *found_key; + struct btrfs_root *tree_root = fs_info->tree_root; + struct btrfs_root *root; struct btrfs_path path; - struct btrfs_root_ref *ref; - int res; - - key.objectid = tree; - key.type = BTRFS_ROOT_REF_KEY; + 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; - - if (btrfs_search_tree(&btrfs_info.tree_root, &key, &path)) - return; - - do { - found_key = btrfs_path_leaf_key(&path); - if (btrfs_comp_keys_type(&key, found_key)) + 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; - - ref = btrfs_path_item_ptr(&path, struct btrfs_root_ref); - btrfs_root_ref_to_cpu(ref); - - printf("ID %llu parent %llu name ", found_key->offset, tree); - if (nameptr && !get_subvol_name(found_key->offset, nameptr, - max_name_len)) - printf("%s\n", nameptr); - else - printf("%.*s\n", (int) ref->name_len, - (const char *) (ref + 1)); - - if (level > 0) - list_subvols(found_key->offset, nameptr, max_name_len, - level - 1); - else - printf("%s: Too much recursion, maybe skipping some " - "subvolumes\n", __func__); - } while (!(res = btrfs_next_slot(&path))); - - btrfs_free_path(&path); + 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; + } + 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) { - char *nameptr = malloc(4096); + struct btrfs_fs_info *fs_info = current_fs_info; + int ret; - list_subvols(BTRFS_FS_TREE_OBJECTID, nameptr, nameptr ? 4096 : 0, 40); - - if (nameptr) - free(nameptr); + if (!fs_info) + return; + ret = list_subvolums(fs_info); + if (ret < 0) + error("failed to list subvolume: %d", ret); } |