Merge tag 'xilinx-for-v2021.01' of https://gitlab.denx.de/u-boot/custodians/u-boot...
[platform/kernel/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 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  */
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                 }
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
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 }