]> git.ipfire.org Git - thirdparty/u-boot.git/blob - fs/btrfs/subvolume.c
command: Introduce functions to obtain command arguments
[thirdparty/u-boot.git] / fs / btrfs / subvolume.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * BTRFS filesystem implementation for U-Boot
4 *
5 * 2017 Marek BehĂșn, CZ.NIC, kabel@kernel.org
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 */
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
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
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 goto out;
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
225 void btrfs_list_subvols(void)
226 {
227 struct btrfs_fs_info *fs_info = current_fs_info;
228 int ret;
229
230 if (!fs_info)
231 return;
232 ret = list_subvolums(fs_info);
233 if (ret < 0)
234 error("failed to list subvolume: %d", ret);
235 }