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 12 u64 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; 43 out: 44 btrfs_free_path(&path); 45 return res; 46 } 47 48 int 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 82 out: 83 btrfs_free_path(&path); 84 return res; 85 } 86 87 int 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; 134 out: 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) */ 141 static 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 191 static 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 199 static 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 213 u64 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 320 u64 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 381 out: 382 btrfs_free_path(&path); 383 return rd_all; 384 } 385