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 <malloc.h>
9 #include "ctree.h"
10 #include "btrfs.h"
11 #include "disk-io.h"
12 
13 /*
14  * Resolve the path of ino inside subvolume @root into @path_ret.
15  *
16  * @path_ret must be at least PATH_MAX size.
17  */
get_path_in_subvol(struct btrfs_root * root,u64 ino,char * path_ret)18 static int get_path_in_subvol(struct btrfs_root *root, u64 ino, char *path_ret)
19 {
20 	struct btrfs_path path;
21 	struct btrfs_key key;
22 	char *tmp;
23 	u64 cur = ino;
24 	int ret = 0;
25 
26 	tmp = malloc(PATH_MAX);
27 	if (!tmp)
28 		return -ENOMEM;
29 	tmp[0] = '\0';
30 
31 	btrfs_init_path(&path);
32 	while (cur != BTRFS_FIRST_FREE_OBJECTID) {
33 		struct btrfs_inode_ref *iref;
34 		int name_len;
35 
36 		btrfs_release_path(&path);
37 		key.objectid = cur;
38 		key.type = BTRFS_INODE_REF_KEY;
39 		key.offset = (u64)-1;
40 
41 		ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0);
42 		/* Impossible */
43 		if (ret == 0)
44 			ret = -EUCLEAN;
45 		if (ret < 0)
46 			goto out;
47 		ret = btrfs_previous_item(root, &path, cur,
48 					  BTRFS_INODE_REF_KEY);
49 		if (ret > 0)
50 			ret = -ENOENT;
51 		if (ret < 0)
52 			goto out;
53 
54 		strncpy(tmp, path_ret, PATH_MAX);
55 		iref = btrfs_item_ptr(path.nodes[0], path.slots[0],
56 				      struct btrfs_inode_ref);
57 		name_len = btrfs_inode_ref_name_len(path.nodes[0],
58 						    iref);
59 		if (name_len > BTRFS_NAME_LEN) {
60 			ret = -ENAMETOOLONG;
61 			goto out;
62 		}
63 		read_extent_buffer(path.nodes[0], path_ret,
64 				   (unsigned long)(iref + 1), name_len);
65 		path_ret[name_len] = '/';
66 		path_ret[name_len + 1] = '\0';
67 		strncat(path_ret, tmp, PATH_MAX);
68 
69 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
70 		cur = key.offset;
71 	}
72 out:
73 	btrfs_release_path(&path);
74 	free(tmp);
75 	return ret;
76 }
77 
list_one_subvol(struct btrfs_root * root,char * path_ret)78 static int list_one_subvol(struct btrfs_root *root, char *path_ret)
79 {
80 	struct btrfs_fs_info *fs_info = root->fs_info;
81 	struct btrfs_root *tree_root = fs_info->tree_root;
82 	struct btrfs_path path;
83 	struct btrfs_key key;
84 	char *tmp;
85 	u64 cur = root->root_key.objectid;
86 	int ret = 0;
87 
88 	tmp = malloc(PATH_MAX);
89 	if (!tmp)
90 		return -ENOMEM;
91 	tmp[0] = '\0';
92 	path_ret[0] = '\0';
93 	btrfs_init_path(&path);
94 	while (cur != BTRFS_FS_TREE_OBJECTID) {
95 		struct btrfs_root_ref *rr;
96 		struct btrfs_key location;
97 		int name_len;
98 		u64 ino;
99 
100 		key.objectid = cur;
101 		key.type = BTRFS_ROOT_BACKREF_KEY;
102 		key.offset = (u64)-1;
103 		btrfs_release_path(&path);
104 
105 		ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
106 		if (ret == 0)
107 			ret = -EUCLEAN;
108 		if (ret < 0)
109 			goto out;
110 		ret = btrfs_previous_item(tree_root, &path, cur,
111 					  BTRFS_ROOT_BACKREF_KEY);
112 		if (ret > 0)
113 			ret = -ENOENT;
114 		if (ret < 0)
115 			goto out;
116 
117 		/* Get the subvolume name */
118 		rr = btrfs_item_ptr(path.nodes[0], path.slots[0],
119 				    struct btrfs_root_ref);
120 		strncpy(tmp, path_ret, PATH_MAX);
121 		name_len = btrfs_root_ref_name_len(path.nodes[0], rr);
122 		if (name_len > BTRFS_NAME_LEN) {
123 			ret = -ENAMETOOLONG;
124 			goto out;
125 		}
126 		ino = btrfs_root_ref_dirid(path.nodes[0], rr);
127 		read_extent_buffer(path.nodes[0], path_ret,
128 				   (unsigned long)(rr + 1), name_len);
129 		path_ret[name_len] = '/';
130 		path_ret[name_len + 1] = '\0';
131 		strncat(path_ret, tmp, PATH_MAX);
132 
133 		/* Get the path inside the parent subvolume */
134 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
135 		location.objectid = key.offset;
136 		location.type = BTRFS_ROOT_ITEM_KEY;
137 		location.offset = (u64)-1;
138 		root = btrfs_read_fs_root(fs_info, &location);
139 		if (IS_ERR(root)) {
140 			ret = PTR_ERR(root);
141 			goto out;
142 		}
143 		ret = get_path_in_subvol(root, ino, path_ret);
144 		if (ret < 0)
145 			goto out;
146 		cur = key.offset;
147 	}
148 	/* Add the leading '/' */
149 	strncpy(tmp, path_ret, PATH_MAX);
150 	strncpy(path_ret, "/", PATH_MAX);
151 	strncat(path_ret, tmp, PATH_MAX);
152 out:
153 	btrfs_release_path(&path);
154 	free(tmp);
155 	return ret;
156 }
157 
list_subvolums(struct btrfs_fs_info * fs_info)158 static int list_subvolums(struct btrfs_fs_info *fs_info)
159 {
160 	struct btrfs_root *tree_root = fs_info->tree_root;
161 	struct btrfs_root *root;
162 	struct btrfs_path path;
163 	struct btrfs_key key;
164 	char *result;
165 	int ret = 0;
166 
167 	result = malloc(PATH_MAX);
168 	if (!result)
169 		return -ENOMEM;
170 
171 	ret = list_one_subvol(fs_info->fs_root, result);
172 	if (ret < 0)
173 		goto out;
174 	root = fs_info->fs_root;
175 	printf("ID %llu gen %llu path %.*s\n",
176 		root->root_key.objectid, btrfs_root_generation(&root->root_item),
177 		PATH_MAX, result);
178 
179 	key.objectid = BTRFS_FIRST_FREE_OBJECTID;
180 	key.type = BTRFS_ROOT_ITEM_KEY;
181 	key.offset = 0;
182 	btrfs_init_path(&path);
183 	ret = btrfs_search_slot(NULL, tree_root, &key, &path, 0, 0);
184 	if (ret < 0)
185 		goto out;
186 	while (1) {
187 		if (path.slots[0] >= btrfs_header_nritems(path.nodes[0]))
188 			goto next;
189 
190 		btrfs_item_key_to_cpu(path.nodes[0], &key, path.slots[0]);
191 		if (key.objectid > BTRFS_LAST_FREE_OBJECTID)
192 			break;
193 		if (key.objectid < BTRFS_FIRST_FREE_OBJECTID ||
194 		    key.type != BTRFS_ROOT_ITEM_KEY)
195 			goto next;
196 		key.offset = (u64)-1;
197 		root = btrfs_read_fs_root(fs_info, &key);
198 		if (IS_ERR(root)) {
199 			ret = PTR_ERR(root);
200 			if (ret == -ENOENT)
201 				goto next;
202 		}
203 		ret = list_one_subvol(root, result);
204 		if (ret < 0)
205 			goto out;
206 		printf("ID %llu gen %llu path %.*s\n",
207 			root->root_key.objectid,
208 			btrfs_root_generation(&root->root_item),
209 			PATH_MAX, result);
210 next:
211 		ret = btrfs_next_item(tree_root, &path);
212 		if (ret < 0)
213 			goto out;
214 		if (ret > 0) {
215 			ret = 0;
216 			break;
217 		}
218 	}
219 out:
220 	free(result);
221 	return ret;
222 }
223 
btrfs_list_subvols(void)224 void btrfs_list_subvols(void)
225 {
226 	struct btrfs_fs_info *fs_info = current_fs_info;
227 	int ret;
228 
229 	if (!fs_info)
230 		return;
231 	ret = list_subvolums(fs_info);
232 	if (ret < 0)
233 		error("failed to list subvolume: %d", ret);
234 }
235