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 11 u64 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; 42 out: 43 btrfs_free_path(&path); 44 return res; 45 } 46 47 int 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 81 out: 82 btrfs_free_path(&path); 83 return res; 84 } 85 86 int 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; 133 out: 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) */ 140 static 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 190 static 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 198 static 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 212 u64 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 319 u64 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 380 out: 381 btrfs_free_path(&path); 382 return rd_all; 383 } 384