Home | History | Annotate | Download | only in btrfs
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * BTRFS filesystem implementation for U-Boot
      4  *
      5  * 2017 Marek Behun, CZ.NIC, marek.behun (at) 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