// SPDX-License-Identifier: GPL-2.0+ /* * BTRFS filesystem implementation for U-Boot * * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz */ #include "btrfs.h" #include <malloc.h> struct chunk_map_item { struct rb_node node; u64 logical; u64 length; u64 physical; }; static int add_chunk_mapping(struct btrfs_key *key, struct btrfs_chunk *chunk) { struct btrfs_stripe *stripe; u64 block_profile = chunk->type & BTRFS_BLOCK_GROUP_PROFILE_MASK; struct rb_node **new = &(btrfs_info.chunks_root.rb_node), *prnt = NULL; struct chunk_map_item *map_item; if (block_profile && block_profile != BTRFS_BLOCK_GROUP_DUP) { printf("%s: unsupported chunk profile %llu\n", __func__, block_profile); return -1; } else if (!chunk->length) { printf("%s: zero length chunk\n", __func__); return -1; } stripe = &chunk->stripe; btrfs_stripe_to_cpu(stripe); while (*new) { struct chunk_map_item *this; this = rb_entry(*new, struct chunk_map_item, node); prnt = *new; if (key->offset < this->logical) { new = &((*new)->rb_left); } else if (key->offset > this->logical) { new = &((*new)->rb_right); } else { debug("%s: Logical address %llu already in map!\n", __func__, key->offset); return 0; } } map_item = malloc(sizeof(struct chunk_map_item)); if (!map_item) return -1; map_item->logical = key->offset; map_item->length = chunk->length; map_item->physical = le64_to_cpu(chunk->stripe.offset); rb_link_node(&map_item->node, prnt, new); rb_insert_color(&map_item->node, &btrfs_info.chunks_root); debug("%s: Mapping %llu to %llu\n", __func__, map_item->logical, map_item->physical); return 0; } u64 btrfs_map_logical_to_physical(u64 logical) { struct rb_node *node = btrfs_info.chunks_root.rb_node; while (node) { struct chunk_map_item *item; item = rb_entry(node, struct chunk_map_item, node); if (item->logical > logical) node = node->rb_left; else if (logical > item->logical + item->length) node = node->rb_right; else return item->physical + logical - item->logical; } printf("%s: Cannot map logical address %llu to physical\n", __func__, logical); return -1ULL; } void btrfs_chunk_map_exit(void) { struct rb_node *now, *next; struct chunk_map_item *item; for (now = rb_first_postorder(&btrfs_info.chunks_root); now; now = next) { item = rb_entry(now, struct chunk_map_item, node); next = rb_next_postorder(now); free(item); } } int btrfs_chunk_map_init(void) { u8 sys_chunk_array_copy[sizeof(btrfs_info.sb.sys_chunk_array)]; u8 * const start = sys_chunk_array_copy; u8 * const end = start + btrfs_info.sb.sys_chunk_array_size; u8 *cur; struct btrfs_key *key; struct btrfs_chunk *chunk; btrfs_info.chunks_root = RB_ROOT; memcpy(sys_chunk_array_copy, btrfs_info.sb.sys_chunk_array, sizeof(sys_chunk_array_copy)); for (cur = start; cur < end;) { key = (struct btrfs_key *) cur; cur += sizeof(struct btrfs_key); chunk = (struct btrfs_chunk *) cur; btrfs_key_to_cpu(key); btrfs_chunk_to_cpu(chunk); if (key->type != BTRFS_CHUNK_ITEM_KEY) { printf("%s: invalid key type %u\n", __func__, key->type); return -1; } if (add_chunk_mapping(key, chunk)) return -1; cur += sizeof(struct btrfs_chunk); cur += sizeof(struct btrfs_stripe) * (chunk->num_stripes - 1); } return 0; } int btrfs_read_chunk_tree(void) { struct btrfs_path path; struct btrfs_key key, *found_key; struct btrfs_chunk *chunk; int res = 0; key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID; key.type = BTRFS_CHUNK_ITEM_KEY; key.offset = 0; if (btrfs_search_tree(&btrfs_info.chunk_root, &key, &path)) return -1; do { found_key = btrfs_path_leaf_key(&path); if (btrfs_comp_keys_type(&key, found_key)) continue; chunk = btrfs_path_item_ptr(&path, struct btrfs_chunk); btrfs_chunk_to_cpu(chunk); if (add_chunk_mapping(found_key, chunk)) { res = -1; break; } } while (!(res = btrfs_next_slot(&path))); btrfs_free_path(&path); if (res < 0) return -1; return 0; }