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