]> git.ipfire.org Git - thirdparty/u-boot.git/blame - fs/btrfs/subvolume.c
fs: btrfs: Prevent error pointer dereference in list_subvolums()
[thirdparty/u-boot.git] / fs / btrfs / subvolume.c
CommitLineData
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 */
18static 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 }
72out:
73 btrfs_release_path(&path);
74 free(tmp);
75 return ret;
76}
21a14fac 77
f4861527
QW
78static 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);
152out:
153 btrfs_release_path(&path);
154 free(tmp);
155 return ret;
156}
157
e8e95c7e
QW
158static 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);
211next:
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 }
220out:
221 free(result);
222 return ret;
223}
224
21a14fac
MB
225void 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}