uboot/fs/btrfs/inode.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 "btrfs.h"
   9#include <malloc.h>
  10
  11u64 btrfs_lookup_inode_ref(struct btrfs_root *root, u64 inr,
  12                           struct btrfs_inode_ref *refp, char *name)
  13{
  14        struct btrfs_path path;
  15        struct btrfs_key *key;
  16        struct btrfs_inode_ref *ref;
  17        u64 res = -1ULL;
  18
  19        key = btrfs_search_tree_key_type(root, inr, BTRFS_INODE_REF_KEY,
  20                                               &path);
  21
  22        if (!key)
  23                return -1ULL;
  24
  25        ref = btrfs_path_item_ptr(&path, struct btrfs_inode_ref);
  26        btrfs_inode_ref_to_cpu(ref);
  27
  28        if (refp)
  29                *refp = *ref;
  30
  31        if (name) {
  32                if (ref->name_len > BTRFS_NAME_MAX) {
  33                        printf("%s: inode name too long: %u\n", __func__,
  34                                ref->name_len);
  35                        goto out;
  36                }
  37
  38                memcpy(name, ref + 1, ref->name_len);
  39        }
  40
  41        res = key->offset;
  42out:
  43        btrfs_free_path(&path);
  44        return res;
  45}
  46
  47int btrfs_lookup_inode(const struct btrfs_root *root,
  48                       struct btrfs_key *location,
  49                       struct btrfs_inode_item *item,
  50                       struct btrfs_root *new_root)
  51{
  52        struct btrfs_root tmp_root = *root;
  53        struct btrfs_path path;
  54        int res = -1;
  55
  56        if (location->type == BTRFS_ROOT_ITEM_KEY) {
  57                if (btrfs_find_root(location->objectid, &tmp_root, NULL))
  58                        return -1;
  59
  60                location->objectid = tmp_root.root_dirid;
  61                location->type = BTRFS_INODE_ITEM_KEY;
  62                location->offset = 0;
  63        }
  64
  65        if (btrfs_search_tree(&tmp_root, location, &path))
  66                return res;
  67
  68        if (btrfs_comp_keys(location, btrfs_path_leaf_key(&path)))
  69                goto out;
  70
  71        if (item) {
  72                *item = *btrfs_path_item_ptr(&path, struct btrfs_inode_item);
  73                btrfs_inode_item_to_cpu(item);
  74        }
  75
  76        if (new_root)
  77                *new_root = tmp_root;
  78
  79        res = 0;
  80
  81out:
  82        btrfs_free_path(&path);
  83        return res;
  84}
  85
  86int btrfs_readlink(const struct btrfs_root *root, u64 inr, char *target)
  87{
  88        struct btrfs_path path;
  89        struct btrfs_key key;
  90        struct btrfs_file_extent_item *extent;
  91        const char *data_ptr;
  92        int res = -1;
  93
  94        key.objectid = inr;
  95        key.type = BTRFS_EXTENT_DATA_KEY;
  96        key.offset = 0;
  97
  98        if (btrfs_search_tree(root, &key, &path))
  99                return -1;
 100
 101        if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path)))
 102                goto out;
 103
 104        extent = btrfs_path_item_ptr(&path, struct btrfs_file_extent_item);
 105        if (extent->type != BTRFS_FILE_EXTENT_INLINE) {
 106                printf("%s: Extent for symlink %llu not of INLINE type\n",
 107                       __func__, inr);
 108                goto out;
 109        }
 110
 111        btrfs_file_extent_item_to_cpu_inl(extent);
 112
 113        if (extent->compression != BTRFS_COMPRESS_NONE) {
 114                printf("%s: Symlink %llu extent data compressed!\n", __func__,
 115                       inr);
 116                goto out;
 117        } else if (extent->encryption != 0) {
 118                printf("%s: Symlink %llu extent data encrypted!\n", __func__,
 119                       inr);
 120                goto out;
 121        } else if (extent->ram_bytes >= btrfs_info.sb.sectorsize) {
 122                printf("%s: Symlink %llu extent data too long (%llu)!\n",
 123                       __func__, inr, extent->ram_bytes);
 124                goto out;
 125        }
 126
 127        data_ptr = (const char *) extent
 128                   + offsetof(struct btrfs_file_extent_item, disk_bytenr);
 129
 130        memcpy(target, data_ptr, extent->ram_bytes);
 131        target[extent->ram_bytes] = '\0';
 132        res = 0;
 133out:
 134        btrfs_free_path(&path);
 135        return res;
 136}
 137
 138/* inr must be a directory (for regular files with multiple hard links this
 139   function returns only one of the parents of the file) */
 140static u64 get_parent_inode(struct btrfs_root *root, u64 inr,
 141                            struct btrfs_inode_item *inode_item)
 142{
 143        struct btrfs_key key;
 144        u64 res;
 145
 146        if (inr == BTRFS_FIRST_FREE_OBJECTID) {
 147                if (root->objectid != btrfs_info.fs_root.objectid) {
 148                        u64 parent;
 149                        struct btrfs_root_ref ref;
 150
 151                        parent = btrfs_lookup_root_ref(root->objectid, &ref,
 152                                                       NULL);
 153                        if (parent == -1ULL)
 154                                return -1ULL;
 155
 156                        if (btrfs_find_root(parent, root, NULL))
 157                                return -1ULL;
 158
 159                        inr = ref.dirid;
 160                }
 161
 162                if (inode_item) {
 163                        key.objectid = inr;
 164                        key.type = BTRFS_INODE_ITEM_KEY;
 165                        key.offset = 0;
 166
 167                        if (btrfs_lookup_inode(root, &key, inode_item, NULL))
 168                                return -1ULL;
 169                }
 170
 171                return inr;
 172        }
 173
 174        res = btrfs_lookup_inode_ref(root, inr, NULL, NULL);
 175        if (res == -1ULL)
 176                return -1ULL;
 177
 178        if (inode_item) {
 179                key.objectid = res;
 180                key.type = BTRFS_INODE_ITEM_KEY;
 181                key.offset = 0;
 182
 183                if (btrfs_lookup_inode(root, &key, inode_item, NULL))
 184                        return -1ULL;
 185        }
 186
 187        return res;
 188}
 189
 190static inline int next_length(const char *path)
 191{
 192        int res = 0;
 193        while (*path != '\0' && *path != '/' && res <= BTRFS_NAME_LEN)
 194                ++res, ++path;
 195        return res;
 196}
 197
 198static inline const char *skip_current_directories(const char *cur)
 199{
 200        while (1) {
 201                if (cur[0] == '/')
 202                        ++cur;
 203                else if (cur[0] == '.' && cur[1] == '/')
 204                        cur += 2;
 205                else
 206                        break;
 207        }
 208
 209        return cur;
 210}
 211
 212u64 btrfs_lookup_path(struct btrfs_root *root, u64 inr, const char *path,
 213                      u8 *type_p, struct btrfs_inode_item *inode_item_p,
 214                      int symlink_limit)
 215{
 216        struct btrfs_dir_item item;
 217        struct btrfs_inode_item inode_item;
 218        u8 type = BTRFS_FT_DIR;
 219        int len, have_inode = 0;
 220        const char *cur = path;
 221
 222        if (*cur == '/') {
 223                ++cur;
 224                inr = root->root_dirid;
 225        }
 226
 227        do {
 228                cur = skip_current_directories(cur);
 229
 230                len = next_length(cur);
 231                if (len > BTRFS_NAME_LEN) {
 232                        printf("%s: Name too long at \"%.*s\"\n", __func__,
 233                               BTRFS_NAME_LEN, cur);
 234                        return -1ULL;
 235                }
 236
 237                if (len == 1 && cur[0] == '.')
 238                        break;
 239
 240                if (len == 2 && cur[0] == '.' && cur[1] == '.') {
 241                        cur += 2;
 242                        inr = get_parent_inode(root, inr, &inode_item);
 243                        if (inr == -1ULL)
 244                                return -1ULL;
 245
 246                        type = BTRFS_FT_DIR;
 247                        continue;
 248                }
 249
 250                if (!*cur)
 251                        break;
 252                
 253                if (btrfs_lookup_dir_item(root, inr, cur, len, &item))
 254                        return -1ULL;
 255
 256                type = item.type;
 257                have_inode = 1;
 258                if (btrfs_lookup_inode(root, &item.location, &inode_item, root))
 259                        return -1ULL;
 260
 261                if (item.type == BTRFS_FT_SYMLINK && symlink_limit >= 0) {
 262                        char *target;
 263
 264                        if (!symlink_limit) {
 265                                printf("%s: Too much symlinks!\n", __func__);
 266                                return -1ULL;
 267                        }
 268
 269                        target = malloc(min(inode_item.size + 1,
 270                                            (u64) btrfs_info.sb.sectorsize));
 271                        if (!target)
 272                                return -1ULL;
 273
 274                        if (btrfs_readlink(root, item.location.objectid,
 275                                           target)) {
 276                                free(target);
 277                                return -1ULL;
 278                        }
 279
 280                        inr = btrfs_lookup_path(root, inr, target, &type,
 281                                                &inode_item, symlink_limit - 1);
 282
 283                        free(target);
 284
 285                        if (inr == -1ULL)
 286                                return -1ULL;
 287                } else if (item.type != BTRFS_FT_DIR && cur[len]) {
 288                        printf("%s: \"%.*s\" not a directory\n", __func__,
 289                               (int) (cur - path + len), path);
 290                        return -1ULL;
 291                } else {
 292                        inr = item.location.objectid;
 293                }
 294
 295                cur += len;
 296        } while (*cur);
 297
 298        if (type_p)
 299                *type_p = type;
 300
 301        if (inode_item_p) {
 302                if (!have_inode) {
 303                        struct btrfs_key key;
 304
 305                        key.objectid = inr;
 306                        key.type = BTRFS_INODE_ITEM_KEY;
 307                        key.offset = 0;
 308
 309                        if (btrfs_lookup_inode(root, &key, &inode_item, NULL))
 310                                return -1ULL;
 311                }
 312
 313                *inode_item_p = inode_item;
 314        }
 315
 316        return inr;
 317}
 318
 319u64 btrfs_file_read(const struct btrfs_root *root, u64 inr, u64 offset,
 320                    u64 size, char *buf)
 321{
 322        struct btrfs_path path;
 323        struct btrfs_key key;
 324        struct btrfs_file_extent_item *extent;
 325        int res = 0;
 326        u64 rd, rd_all = -1ULL;
 327
 328        key.objectid = inr;
 329        key.type = BTRFS_EXTENT_DATA_KEY;
 330        key.offset = offset;
 331
 332        if (btrfs_search_tree(root, &key, &path))
 333                return -1ULL;
 334
 335        if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path)) < 0) {
 336                if (btrfs_prev_slot(&path))
 337                        goto out;
 338
 339                if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
 340                        goto out;
 341        }
 342
 343        rd_all = 0;
 344
 345        do {
 346                if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
 347                        break;
 348
 349                extent = btrfs_path_item_ptr(&path,
 350                                             struct btrfs_file_extent_item);
 351
 352                if (extent->type == BTRFS_FILE_EXTENT_INLINE) {
 353                        btrfs_file_extent_item_to_cpu_inl(extent);
 354                        rd = btrfs_read_extent_inline(&path, extent, offset,
 355                                                      size, buf);
 356                } else {
 357                        btrfs_file_extent_item_to_cpu(extent);
 358                        rd = btrfs_read_extent_reg(&path, extent, offset, size,
 359                                                   buf);
 360                }
 361
 362                if (rd == -1ULL) {
 363                        printf("%s: Error reading extent\n", __func__);
 364                        rd_all = -1;
 365                        goto out;
 366                }
 367
 368                offset = 0;
 369                buf += rd;
 370                rd_all += rd;
 371                size -= rd;
 372
 373                if (!size)
 374                        break;
 375        } while (!(res = btrfs_next_slot(&path)));
 376
 377        if (res)
 378                return -1ULL;
 379
 380out:
 381        btrfs_free_path(&path);
 382        return rd_all;
 383}
 384