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 "disk-io.h"
10 
verify_dir_item(struct btrfs_root * root,struct extent_buffer * leaf,struct btrfs_dir_item * dir_item)11 static int verify_dir_item(struct btrfs_root *root,
12 		    struct extent_buffer *leaf,
13 		    struct btrfs_dir_item *dir_item)
14 {
15 	u16 namelen = BTRFS_NAME_LEN;
16 	u8 type = btrfs_dir_type(leaf, dir_item);
17 
18 	if (type == BTRFS_FT_XATTR)
19 		namelen = XATTR_NAME_MAX;
20 
21 	if (btrfs_dir_name_len(leaf, dir_item) > namelen) {
22 		fprintf(stderr, "invalid dir item name len: %u\n",
23 			(unsigned)btrfs_dir_data_len(leaf, dir_item));
24 		return 1;
25 	}
26 
27 	/* BTRFS_MAX_XATTR_SIZE is the same for all dir items */
28 	if ((btrfs_dir_data_len(leaf, dir_item) +
29 	     btrfs_dir_name_len(leaf, dir_item)) >
30 			BTRFS_MAX_XATTR_SIZE(root->fs_info)) {
31 		fprintf(stderr, "invalid dir item name + data len: %u + %u\n",
32 			(unsigned)btrfs_dir_name_len(leaf, dir_item),
33 			(unsigned)btrfs_dir_data_len(leaf, dir_item));
34 		return 1;
35 	}
36 
37 	return 0;
38 }
39 
btrfs_match_dir_item_name(struct btrfs_root * root,struct btrfs_path * path,const char * name,int name_len)40 struct btrfs_dir_item *btrfs_match_dir_item_name(struct btrfs_root *root,
41 			      struct btrfs_path *path,
42 			      const char *name, int name_len)
43 {
44 	struct btrfs_dir_item *dir_item;
45 	unsigned long name_ptr;
46 	u32 total_len;
47 	u32 cur = 0;
48 	u32 this_len;
49 	struct extent_buffer *leaf;
50 
51 	leaf = path->nodes[0];
52 	dir_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dir_item);
53 	total_len = btrfs_item_size_nr(leaf, path->slots[0]);
54 	if (verify_dir_item(root, leaf, dir_item))
55 		return NULL;
56 
57 	while(cur < total_len) {
58 		this_len = sizeof(*dir_item) +
59 			btrfs_dir_name_len(leaf, dir_item) +
60 			btrfs_dir_data_len(leaf, dir_item);
61 		if (this_len > (total_len - cur)) {
62 			fprintf(stderr, "invalid dir item size\n");
63 			return NULL;
64 		}
65 
66 		name_ptr = (unsigned long)(dir_item + 1);
67 
68 		if (btrfs_dir_name_len(leaf, dir_item) == name_len &&
69 		    memcmp_extent_buffer(leaf, name, name_ptr, name_len) == 0)
70 			return dir_item;
71 
72 		cur += this_len;
73 		dir_item = (struct btrfs_dir_item *)((char *)dir_item +
74 						     this_len);
75 	}
76 	return NULL;
77 }
78 
btrfs_lookup_dir_item(struct btrfs_trans_handle * trans,struct btrfs_root * root,struct btrfs_path * path,u64 dir,const char * name,int name_len,int mod)79 struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
80 					     struct btrfs_root *root,
81 					     struct btrfs_path *path, u64 dir,
82 					     const char *name, int name_len,
83 					     int mod)
84 {
85 	int ret;
86 	struct btrfs_key key;
87 	int ins_len = mod < 0 ? -1 : 0;
88 	int cow = mod != 0;
89 	struct btrfs_key found_key;
90 	struct extent_buffer *leaf;
91 
92 	key.objectid = dir;
93 	key.type = BTRFS_DIR_ITEM_KEY;
94 
95 	key.offset = btrfs_name_hash(name, name_len);
96 
97 	ret = btrfs_search_slot(trans, root, &key, path, ins_len, cow);
98 	if (ret < 0)
99 		return ERR_PTR(ret);
100 	if (ret > 0) {
101 		if (path->slots[0] == 0)
102 			return NULL;
103 		path->slots[0]--;
104 	}
105 
106 	leaf = path->nodes[0];
107 	btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
108 
109 	if (found_key.objectid != dir ||
110 	    found_key.type != BTRFS_DIR_ITEM_KEY ||
111 	    found_key.offset != key.offset)
112 		return NULL;
113 
114 	return btrfs_match_dir_item_name(root, path, name, name_len);
115 }
116 
btrfs_iter_dir(struct btrfs_root * root,u64 ino,btrfs_iter_dir_callback_t callback)117 int btrfs_iter_dir(struct btrfs_root *root, u64 ino,
118 		   btrfs_iter_dir_callback_t callback)
119 {
120 	struct btrfs_path path;
121 	struct btrfs_key key;
122 	int ret;
123 
124 	btrfs_init_path(&path);
125 	key.objectid = ino;
126 	key.type = BTRFS_DIR_INDEX_KEY;
127 	key.offset = 0;
128 
129 	ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
130 	if (ret < 0)
131 		return ret;
132 	/* Should not happen */
133 	if (ret == 0) {
134 		ret = -EUCLEAN;
135 		goto out;
136 	}
137 	if (path.slots[0] >= btrfs_header_nritems(path.nodes[0])) {
138 		ret = btrfs_next_leaf(root, &path);
139 		if (ret < 0)
140 			goto out;
141 		if (ret > 0) {
142 			ret = 0;
143 			goto out;
144 		}
145 	}
146 	do {
147 		struct btrfs_dir_item *di;
148 
149 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
150 		if (key.objectid != ino || key.type != BTRFS_DIR_INDEX_KEY)
151 			break;
152 		di = btrfs_item_ptr(path.nodes[0], path.slots[0],
153 				    struct btrfs_dir_item);
154 		if (verify_dir_item(root, path.nodes[0], di)) {
155 			ret = -EUCLEAN;
156 			goto out;
157 		}
158 		ret = callback(root, path.nodes[0], di);
159 		if (ret < 0)
160 			goto out;
161 	} while (!(ret = btrfs_next_item(root, &path)));
162 
163 	if (ret > 0)
164 		ret = 0;
165 out:
166 	btrfs_release_path(&path);
167 	return ret;
168 }
169