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