uboot/fs/btrfs/subvolume.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * BTRFS filesystem implementation for U-Boot
   4 *
   5 * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
   6 */
   7
   8#include <malloc.h>
   9#include "ctree.h"
  10#include "btrfs.h"
  11#include "disk-io.h"
  12
  13/*
  14 * Resolve the path of ino inside subvolume @root into @path_ret.
  15 *
  16 * @path_ret must be at least PATH_MAX size.
  17 */
  18static int get_path_in_subvol(struct btrfs_root *root, u64 ino, char *path_ret)
  19{
  20        struct btrfs_path path;
  21        struct btrfs_key key;
  22        char *tmp;
  23        u64 cur = ino;
  24        int ret = 0;
  25
  26        tmp = malloc(PATH_MAX);
  27        if (!tmp)
  28                return -ENOMEM;
  29        tmp[0] = '\0';
  30
  31        btrfs_init_path(&path);
  32        while (cur != BTRFS_FIRST_FREE_OBJECTID) {
  33                struct btrfs_inode_ref *iref;
  34                int name_len;
  35
  36                btrfs_release_path(&path);
  37                key.objectid = cur;
  38                key.type = BTRFS_INODE_REF_KEY;
  39                key.offset = (u64)-1;
  40
  41                ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
  42                /* Impossible */
  43                if (ret == 0)
  44                        ret = -EUCLEAN;
  45                if (ret < 0)
  46                        goto out;
  47                ret = btrfs_previous_item(root, &path, cur,
  48                                          BTRFS_INODE_REF_KEY);
  49                if (ret > 0)
  50                        ret = -ENOENT;
  51                if (ret < 0)
  52                        goto out;
  53
  54                strncpy(tmp, path_ret, PATH_MAX);
  55                iref = btrfs_item_ptr(path.nodes[0], path.slots[0],
  56                                      struct btrfs_inode_ref);
  57                name_len = btrfs_inode_ref_name_len(path.nodes[0],
  58                                                    iref);
  59                if (name_len > BTRFS_NAME_LEN) {
  60                        ret = -ENAMETOOLONG;
  61                        goto out;
  62                }
  63                read_extent_buffer(path.nodes[0], path_ret,
  64                                   (unsigned long)(iref + 1), name_len);
  65                path_ret[name_len] = '/';
  66                path_ret[name_len + 1] = '\0';
  67                strncat(path_ret, tmp, PATH_MAX);
  68
  69                btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
  70                cur = key.offset;
  71        }
  72out:
  73        btrfs_release_path(&path);
  74        free(tmp);
  75        return ret;
  76}
  77
  78static int list_one_subvol(struct btrfs_root *root, char *path_ret)
  79{
  80        struct btrfs_fs_info *fs_info = root->fs_info;
  81        struct btrfs_root *tree_root = fs_info->tree_root;
  82        struct btrfs_path path;
  83        struct btrfs_key key;
  84        char *tmp;
  85        u64 cur = root->root_key.objectid;
  86        int ret = 0;
  87
  88        tmp = malloc(PATH_MAX);
  89        if (!tmp)
  90                return -ENOMEM;
  91        tmp[0] = '\0';
  92        path_ret[0] = '\0';
  93        btrfs_init_path(&path);
  94        while (cur != BTRFS_FS_TREE_OBJECTID) {
  95                struct btrfs_root_ref *rr;
  96                struct btrfs_key location;
  97                int name_len;
  98                u64 ino;
  99
 100                key.objectid = cur;
 101                key.type = BTRFS_ROOT_BACKREF_KEY;
 102                key.offset = (u64)-1;
 103                btrfs_release_path(&path);
 104
 105                ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
 106                if (ret == 0)
 107                        ret = -EUCLEAN;
 108                if (ret < 0)
 109                        goto out;
 110                ret = btrfs_previous_item(tree_root, &path, cur,
 111                                          BTRFS_ROOT_BACKREF_KEY);
 112                if (ret > 0)
 113                        ret = -ENOENT;
 114                if (ret < 0)
 115                        goto out;
 116
 117                /* Get the subvolume name */
 118                rr = btrfs_item_ptr(path.nodes[0], path.slots[0],
 119                                    struct btrfs_root_ref);
 120                strncpy(tmp, path_ret, PATH_MAX);
 121                name_len = btrfs_root_ref_name_len(path.nodes[0], rr);
 122                if (name_len > BTRFS_NAME_LEN) {
 123                        ret = -ENAMETOOLONG;
 124                        goto out;
 125                }
 126                ino = btrfs_root_ref_dirid(path.nodes[0], rr);
 127                read_extent_buffer(path.nodes[0], path_ret,
 128                                   (unsigned long)(rr + 1), name_len);
 129                path_ret[name_len] = '/';
 130                path_ret[name_len + 1] = '\0';
 131                strncat(path_ret, tmp, PATH_MAX);
 132
 133                /* Get the path inside the parent subvolume */
 134                btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 135                location.objectid = key.offset;
 136                location.type = BTRFS_ROOT_ITEM_KEY;
 137                location.offset = (u64)-1;
 138                root = btrfs_read_fs_root(fs_info, &location);
 139                if (IS_ERR(root)) {
 140                        ret = PTR_ERR(root);
 141                        goto out;
 142                }
 143                ret = get_path_in_subvol(root, ino, path_ret);
 144                if (ret < 0)
 145                        goto out;
 146                cur = key.offset;
 147        }
 148        /* Add the leading '/' */
 149        strncpy(tmp, path_ret, PATH_MAX);
 150        strncpy(path_ret, "/", PATH_MAX);
 151        strncat(path_ret, tmp, PATH_MAX);
 152out:
 153        btrfs_release_path(&path);
 154        free(tmp);
 155        return ret;
 156}
 157
 158static int list_subvolums(struct btrfs_fs_info *fs_info)
 159{
 160        struct btrfs_root *tree_root = fs_info->tree_root;
 161        struct btrfs_root *root;
 162        struct btrfs_path path;
 163        struct btrfs_key key;
 164        char *result;
 165        int ret = 0;
 166
 167        result = malloc(PATH_MAX);
 168        if (!result)
 169                return -ENOMEM;
 170
 171        ret = list_one_subvol(fs_info->fs_root, result);
 172        if (ret < 0)
 173                goto out;
 174        root = fs_info->fs_root;
 175        printf("ID %llu gen %llu path %.*s\n",
 176                root->root_key.objectid, btrfs_root_generation(&root->root_item),
 177                PATH_MAX, result);
 178
 179        key.objectid = BTRFS_FIRST_FREE_OBJECTID;
 180        key.type = BTRFS_ROOT_ITEM_KEY;
 181        key.offset = 0;
 182        btrfs_init_path(&path);
 183        ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
 184        if (ret < 0)
 185                goto out;
 186        while (1) {
 187                if (path.slots[0] >= btrfs_header_nritems(path.nodes[0]))
 188                        goto next;
 189
 190                btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
 191                if (key.objectid > BTRFS_LAST_FREE_OBJECTID)
 192                        break;
 193                if (key.objectid < BTRFS_FIRST_FREE_OBJECTID ||
 194                    key.type != BTRFS_ROOT_ITEM_KEY)
 195                        goto next;
 196                key.offset = (u64)-1;
 197                root = btrfs_read_fs_root(fs_info, &key);
 198                if (IS_ERR(root)) {
 199                        ret = PTR_ERR(root);
 200                        if (ret == -ENOENT)
 201                                goto next;
 202                }
 203                ret = list_one_subvol(root, result);
 204                if (ret < 0)
 205                        goto out;
 206                printf("ID %llu gen %llu path %.*s\n",
 207                        root->root_key.objectid,
 208                        btrfs_root_generation(&root->root_item),
 209                        PATH_MAX, result);
 210next:
 211                ret = btrfs_next_item(tree_root, &path);
 212                if (ret < 0)
 213                        goto out;
 214                if (ret > 0) {
 215                        ret = 0;
 216                        break;
 217                }
 218        }
 219out:
 220        free(result);
 221        return ret;
 222}
 223
 224void btrfs_list_subvols(void)
 225{
 226        struct btrfs_fs_info *fs_info = current_fs_info;
 227        int ret;
 228
 229        if (!fs_info)
 230                return;
 231        ret = list_subvolums(fs_info);
 232        if (ret < 0)
 233                error("failed to list subvolume: %d", ret);
 234}
 235