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 /* inode.c, musi vratit aj root stromu kde sa inoda najde */ 214 u64 btrfs_lookup_path(struct btrfs_root *root, u64 inr, const char *path, 215 u8 *type_p, struct btrfs_inode_item *inode_item_p, 216 int symlink_limit) 217 { 218 struct btrfs_dir_item item; 219 struct btrfs_inode_item inode_item; 220 u8 type = BTRFS_FT_DIR; 221 int len, have_inode = 0; 222 const char *cur = path; 223 224 if (*cur == '/') { 225 ++cur; 226 inr = root->root_dirid; 227 } 228 229 do { 230 cur = skip_current_directories(cur); 231 232 len = next_length(cur); 233 if (len > BTRFS_NAME_LEN) { 234 printf("%s: Name too long at \"%.*s\"\n", __func__, 235 BTRFS_NAME_LEN, cur); 236 return -1ULL; 237 } 238 239 if (len == 1 && cur[0] == '.') 240 break; 241 242 if (len == 2 && cur[0] == '.' && cur[1] == '.') { 243 cur += 2; 244 inr = get_parent_inode(root, inr, &inode_item); 245 if (inr == -1ULL) 246 return -1ULL; 247 248 type = BTRFS_FT_DIR; 249 continue; 250 } 251 252 if (!*cur) 253 break; 254 255 if (btrfs_lookup_dir_item(root, inr, cur, len, &item)) 256 return -1ULL; 257 258 type = item.type; 259 have_inode = 1; 260 if (btrfs_lookup_inode(root, &item.location, &inode_item, root)) 261 return -1ULL; 262 263 if (item.type == BTRFS_FT_SYMLINK && symlink_limit >= 0) { 264 char *target; 265 266 if (!symlink_limit) { 267 printf("%s: Too much symlinks!\n", __func__); 268 return -1ULL; 269 } 270 271 target = malloc(min(inode_item.size + 1, 272 (u64) btrfs_info.sb.sectorsize)); 273 if (!target) 274 return -1ULL; 275 276 if (btrfs_readlink(root, item.location.objectid, 277 target)) { 278 free(target); 279 return -1ULL; 280 } 281 282 inr = btrfs_lookup_path(root, inr, target, &type, 283 &inode_item, symlink_limit - 1); 284 285 free(target); 286 287 if (inr == -1ULL) 288 return -1ULL; 289 } else if (item.type != BTRFS_FT_DIR && cur[len]) { 290 printf("%s: \"%.*s\" not a directory\n", __func__, 291 (int) (cur - path + len), path); 292 return -1ULL; 293 } else { 294 inr = item.location.objectid; 295 } 296 297 cur += len; 298 } while (*cur); 299 300 if (type_p) 301 *type_p = type; 302 303 if (inode_item_p) { 304 if (!have_inode) { 305 struct btrfs_key key; 306 307 key.objectid = inr; 308 key.type = BTRFS_INODE_ITEM_KEY; 309 key.offset = 0; 310 311 if (btrfs_lookup_inode(root, &key, &inode_item, NULL)) 312 return -1ULL; 313 } 314 315 *inode_item_p = inode_item; 316 } 317 318 return inr; 319 } 320 321 u64 btrfs_file_read(const struct btrfs_root *root, u64 inr, u64 offset, 322 u64 size, char *buf) 323 { 324 struct btrfs_path path; 325 struct btrfs_key key; 326 struct btrfs_file_extent_item *extent; 327 int res; 328 u64 rd, rd_all = -1ULL; 329 330 key.objectid = inr; 331 key.type = BTRFS_EXTENT_DATA_KEY; 332 key.offset = offset; 333 334 if (btrfs_search_tree(root, &key, &path)) 335 return -1ULL; 336 337 if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path)) < 0) { 338 if (btrfs_prev_slot(&path)) 339 goto out; 340 341 if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path))) 342 goto out; 343 } 344 345 rd_all = 0; 346 347 do { 348 if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path))) 349 break; 350 351 extent = btrfs_path_item_ptr(&path, 352 struct btrfs_file_extent_item); 353 354 if (extent->type == BTRFS_FILE_EXTENT_INLINE) { 355 btrfs_file_extent_item_to_cpu_inl(extent); 356 rd = btrfs_read_extent_inline(&path, extent, offset, 357 size, buf); 358 } else { 359 btrfs_file_extent_item_to_cpu(extent); 360 rd = btrfs_read_extent_reg(&path, extent, offset, size, 361 buf); 362 } 363 364 if (rd == -1ULL) { 365 printf("%s: Error reading extent\n", __func__); 366 rd_all = -1; 367 goto out; 368 } 369 370 offset = 0; 371 buf += rd; 372 rd_all += rd; 373 size -= rd; 374 375 if (!size) 376 break; 377 } while (!(res = btrfs_next_slot(&path))); 378 379 if (res) 380 return -1ULL; 381 382 out: 383 btrfs_free_path(&path); 384 return rd_all; 385 } 386