Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | // 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; } |