]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
21a14fac MB |
2 | /* |
3 | * BTRFS filesystem implementation for U-Boot | |
4 | * | |
61143f74 | 5 | * 2017 Marek Behún, CZ.NIC, kabel@kernel.org |
21a14fac MB |
6 | */ |
7 | ||
21a14fac | 8 | #include <malloc.h> |
8098da70 QW |
9 | #include "ctree.h" |
10 | #include "btrfs.h" | |
f4861527 | 11 | #include "disk-io.h" |
8098da70 QW |
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 | */ | |
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 | } | |
21a14fac | 77 | |
f4861527 QW |
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 | ||
e8e95c7e QW |
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; | |
c331efd0 | 202 | goto out; |
e8e95c7e QW |
203 | } |
204 | ret = list_one_subvol(root, result); | |
205 | if (ret < 0) | |
206 | goto out; | |
207 | printf("ID %llu gen %llu path %.*s\n", | |
208 | root->root_key.objectid, | |
209 | btrfs_root_generation(&root->root_item), | |
210 | PATH_MAX, result); | |
211 | next: | |
212 | ret = btrfs_next_item(tree_root, &path); | |
213 | if (ret < 0) | |
214 | goto out; | |
215 | if (ret > 0) { | |
216 | ret = 0; | |
217 | break; | |
218 | } | |
219 | } | |
220 | out: | |
221 | free(result); | |
222 | return ret; | |
223 | } | |
224 | ||
21a14fac MB |
225 | void btrfs_list_subvols(void) |
226 | { | |
e8e95c7e QW |
227 | struct btrfs_fs_info *fs_info = current_fs_info; |
228 | int ret; | |
21a14fac | 229 | |
e8e95c7e QW |
230 | if (!fs_info) |
231 | return; | |
232 | ret = list_subvolums(fs_info); | |
233 | if (ret < 0) | |
234 | error("failed to list subvolume: %d", ret); | |
21a14fac | 235 | } |