]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
0c936ee3 MB |
2 | /* |
3 | * BTRFS filesystem implementation for U-Boot | |
4 | * | |
5 | * 2017 Marek Behun, CZ.NIC, [email protected] | |
0c936ee3 MB |
6 | */ |
7 | ||
0c936ee3 MB |
8 | #include <config.h> |
9 | #include <malloc.h> | |
ba06b3c5 | 10 | #include <uuid.h> |
0c936ee3 | 11 | #include <linux/time.h> |
565a4147 QW |
12 | #include "btrfs.h" |
13 | #include "crypto/hash.h" | |
4aebb994 | 14 | #include "disk-io.h" |
0c936ee3 MB |
15 | |
16 | struct btrfs_info btrfs_info; | |
f06bfcf5 | 17 | struct btrfs_fs_info *current_fs_info; |
0c936ee3 | 18 | |
325dd1f6 QW |
19 | static int show_dir(struct btrfs_root *root, struct extent_buffer *eb, |
20 | struct btrfs_dir_item *di) | |
0c936ee3 | 21 | { |
325dd1f6 QW |
22 | struct btrfs_fs_info *fs_info = root->fs_info; |
23 | struct btrfs_inode_item ii; | |
24 | struct btrfs_key key; | |
25 | static const char* dir_item_str[] = { | |
26 | [BTRFS_FT_REG_FILE] = "FILE", | |
27 | [BTRFS_FT_DIR] = "DIR", | |
28 | [BTRFS_FT_CHRDEV] = "CHRDEV", | |
29 | [BTRFS_FT_BLKDEV] = "BLKDEV", | |
30 | [BTRFS_FT_FIFO] = "FIFO", | |
31 | [BTRFS_FT_SOCK] = "SOCK", | |
32 | [BTRFS_FT_SYMLINK] = "SYMLINK", | |
33 | [BTRFS_FT_XATTR] = "XATTR" | |
0c936ee3 | 34 | }; |
325dd1f6 QW |
35 | u8 type = btrfs_dir_type(eb, di); |
36 | char namebuf[BTRFS_NAME_LEN]; | |
37 | char *target = NULL; | |
38 | char filetime[32]; | |
0c936ee3 | 39 | time_t mtime; |
325dd1f6 | 40 | int ret; |
0c936ee3 | 41 | |
325dd1f6 | 42 | btrfs_dir_item_key_to_cpu(eb, di, &key); |
0c936ee3 | 43 | |
325dd1f6 QW |
44 | if (key.type == BTRFS_ROOT_ITEM_KEY) { |
45 | struct btrfs_root *subvol; | |
0c936ee3 | 46 | |
325dd1f6 QW |
47 | /* It's a subvolume, get its mtime from root item */ |
48 | subvol = btrfs_read_fs_root(fs_info, &key); | |
49 | if (IS_ERR(subvol)) { | |
50 | ret = PTR_ERR(subvol); | |
51 | error("Can't find root %llu", key.objectid); | |
52 | return ret; | |
0c936ee3 | 53 | } |
325dd1f6 QW |
54 | mtime = btrfs_stack_timespec_sec(&subvol->root_item.otime); |
55 | } else { | |
56 | struct btrfs_path path; | |
57 | ||
58 | /* It's regular inode, get its mtime from inode item */ | |
59 | btrfs_init_path(&path); | |
60 | ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); | |
61 | if (ret > 0) | |
62 | ret = -ENOENT; | |
63 | if (ret < 0) { | |
64 | error("Can't find inode %llu", key.objectid); | |
65 | btrfs_release_path(&path); | |
66 | return ret; | |
67 | } | |
68 | read_extent_buffer(path.nodes[0], &ii, | |
69 | btrfs_item_ptr_offset(path.nodes[0], path.slots[0]), | |
70 | sizeof(ii)); | |
71 | btrfs_release_path(&path); | |
72 | mtime = btrfs_stack_timespec_sec(&ii.mtime); | |
73 | } | |
74 | ctime_r(&mtime, filetime); | |
0c936ee3 | 75 | |
325dd1f6 QW |
76 | if (type == BTRFS_FT_SYMLINK) { |
77 | target = malloc(fs_info->sectorsize); | |
78 | if (!target) { | |
79 | error("Can't alloc memory for symlink %llu", | |
80 | key.objectid); | |
81 | return -ENOMEM; | |
82 | } | |
83 | ret = btrfs_readlink(root, key.objectid, target); | |
84 | if (ret < 0) { | |
85 | error("Failed to read symlink %llu", key.objectid); | |
86 | goto out; | |
87 | } | |
88 | target[ret] = '\0'; | |
0c936ee3 MB |
89 | } |
90 | ||
325dd1f6 QW |
91 | if (type < ARRAY_SIZE(dir_item_str) && dir_item_str[type]) |
92 | printf("<%s> ", dir_item_str[type]); | |
0c936ee3 | 93 | else |
325dd1f6 QW |
94 | printf("DIR_ITEM.%u", type); |
95 | if (type == BTRFS_FT_CHRDEV || type == BTRFS_FT_BLKDEV) { | |
96 | ASSERT(key.type == BTRFS_INODE_ITEM_KEY); | |
97 | printf("%4llu,%5llu ", btrfs_stack_inode_rdev(&ii) >> 20, | |
98 | btrfs_stack_inode_rdev(&ii) & 0xfffff); | |
99 | } else { | |
100 | if (key.type == BTRFS_INODE_ITEM_KEY) | |
101 | printf("%10llu ", btrfs_stack_inode_size(&ii)); | |
102 | else | |
103 | printf("%10llu ", 0ULL); | |
0c936ee3 MB |
104 | } |
105 | ||
325dd1f6 QW |
106 | read_extent_buffer(eb, namebuf, (unsigned long)(di + 1), |
107 | btrfs_dir_name_len(eb, di)); | |
108 | printf("%24.24s %.*s", filetime, btrfs_dir_name_len(eb, di), namebuf); | |
109 | if (type == BTRFS_FT_SYMLINK) | |
110 | printf(" -> %s", target ? target : "?"); | |
0c936ee3 | 111 | printf("\n"); |
325dd1f6 QW |
112 | out: |
113 | free(target); | |
114 | return ret; | |
0c936ee3 MB |
115 | } |
116 | ||
0528979f SG |
117 | int btrfs_probe(struct blk_desc *fs_dev_desc, |
118 | struct disk_partition *fs_partition) | |
0c936ee3 | 119 | { |
f06bfcf5 QW |
120 | struct btrfs_fs_info *fs_info; |
121 | int ret = -1; | |
122 | ||
0c936ee3 MB |
123 | btrfs_blk_desc = fs_dev_desc; |
124 | btrfs_part_info = fs_partition; | |
125 | ||
126 | memset(&btrfs_info, 0, sizeof(btrfs_info)); | |
127 | ||
128 | btrfs_hash_init(); | |
129 | if (btrfs_read_superblock()) | |
130 | return -1; | |
131 | ||
132 | if (btrfs_chunk_map_init()) { | |
133 | printf("%s: failed to init chunk map\n", __func__); | |
134 | return -1; | |
135 | } | |
136 | ||
137 | btrfs_info.tree_root.objectid = 0; | |
138 | btrfs_info.tree_root.bytenr = btrfs_info.sb.root; | |
139 | btrfs_info.chunk_root.objectid = 0; | |
140 | btrfs_info.chunk_root.bytenr = btrfs_info.sb.chunk_root; | |
141 | ||
57f24f10 | 142 | if (__btrfs_read_chunk_tree()) { |
0c936ee3 MB |
143 | printf("%s: failed to read chunk tree\n", __func__); |
144 | return -1; | |
145 | } | |
146 | ||
147 | if (btrfs_find_root(btrfs_get_default_subvol_objectid(), | |
148 | &btrfs_info.fs_root, NULL)) { | |
149 | printf("%s: failed to find default subvolume\n", __func__); | |
150 | return -1; | |
151 | } | |
152 | ||
f06bfcf5 QW |
153 | fs_info = open_ctree_fs_info(fs_dev_desc, fs_partition); |
154 | if (fs_info) { | |
155 | current_fs_info = fs_info; | |
156 | ret = 0; | |
157 | } | |
158 | return ret; | |
0c936ee3 MB |
159 | } |
160 | ||
161 | int btrfs_ls(const char *path) | |
162 | { | |
325dd1f6 QW |
163 | struct btrfs_fs_info *fs_info = current_fs_info; |
164 | struct btrfs_root *root = fs_info->fs_root; | |
165 | u64 ino = BTRFS_FIRST_FREE_OBJECTID; | |
0c936ee3 | 166 | u8 type; |
325dd1f6 | 167 | int ret; |
0c936ee3 | 168 | |
325dd1f6 QW |
169 | ASSERT(fs_info); |
170 | ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID, | |
171 | path, &root, &ino, &type, 40); | |
172 | if (ret < 0) { | |
0c936ee3 | 173 | printf("Cannot lookup path %s\n", path); |
325dd1f6 | 174 | return ret; |
0c936ee3 MB |
175 | } |
176 | ||
177 | if (type != BTRFS_FT_DIR) { | |
325dd1f6 QW |
178 | error("Not a directory: %s", path); |
179 | return -ENOENT; | |
0c936ee3 | 180 | } |
325dd1f6 QW |
181 | ret = btrfs_iter_dir(root, ino, show_dir); |
182 | if (ret < 0) { | |
183 | error("An error occured while listing directory %s", path); | |
184 | return ret; | |
0c936ee3 | 185 | } |
0c936ee3 MB |
186 | return 0; |
187 | } | |
188 | ||
189 | int btrfs_exists(const char *file) | |
190 | { | |
5bbb68d5 QW |
191 | struct btrfs_fs_info *fs_info = current_fs_info; |
192 | struct btrfs_root *root; | |
193 | u64 ino; | |
0c936ee3 | 194 | u8 type; |
5bbb68d5 QW |
195 | int ret; |
196 | ||
197 | ASSERT(fs_info); | |
0c936ee3 | 198 | |
5bbb68d5 QW |
199 | ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID, |
200 | file, &root, &ino, &type, 40); | |
201 | if (ret < 0) | |
202 | return 0; | |
0c936ee3 | 203 | |
5bbb68d5 QW |
204 | if (type == BTRFS_FT_REG_FILE) |
205 | return 1; | |
206 | return 0; | |
0c936ee3 MB |
207 | } |
208 | ||
209 | int btrfs_size(const char *file, loff_t *size) | |
210 | { | |
5bbb68d5 QW |
211 | struct btrfs_fs_info *fs_info = current_fs_info; |
212 | struct btrfs_inode_item *ii; | |
213 | struct btrfs_root *root; | |
214 | struct btrfs_path path; | |
215 | struct btrfs_key key; | |
216 | u64 ino; | |
0c936ee3 | 217 | u8 type; |
5bbb68d5 | 218 | int ret; |
0c936ee3 | 219 | |
5bbb68d5 QW |
220 | ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID, |
221 | file, &root, &ino, &type, 40); | |
222 | if (ret < 0) { | |
0c936ee3 | 223 | printf("Cannot lookup file %s\n", file); |
5bbb68d5 | 224 | return ret; |
0c936ee3 | 225 | } |
0c936ee3 MB |
226 | if (type != BTRFS_FT_REG_FILE) { |
227 | printf("Not a regular file: %s\n", file); | |
5bbb68d5 | 228 | return -ENOENT; |
0c936ee3 | 229 | } |
5bbb68d5 QW |
230 | btrfs_init_path(&path); |
231 | key.objectid = ino; | |
232 | key.type = BTRFS_INODE_ITEM_KEY; | |
233 | key.offset = 0; | |
0c936ee3 | 234 | |
5bbb68d5 QW |
235 | ret = btrfs_search_slot(NULL, root, &key, &path, 0, 0); |
236 | if (ret < 0) { | |
237 | printf("Cannot lookup ino %llu\n", ino); | |
238 | return ret; | |
239 | } | |
240 | if (ret > 0) { | |
241 | printf("Ino %llu does not exist\n", ino); | |
242 | ret = -ENOENT; | |
243 | goto out; | |
244 | } | |
245 | ii = btrfs_item_ptr(path.nodes[0], path.slots[0], | |
246 | struct btrfs_inode_item); | |
247 | *size = btrfs_inode_size(path.nodes[0], ii); | |
248 | out: | |
249 | btrfs_release_path(&path); | |
250 | return ret; | |
0c936ee3 MB |
251 | } |
252 | ||
253 | int btrfs_read(const char *file, void *buf, loff_t offset, loff_t len, | |
254 | loff_t *actread) | |
255 | { | |
e3427184 QW |
256 | struct btrfs_fs_info *fs_info = current_fs_info; |
257 | struct btrfs_root *root; | |
258 | loff_t real_size = 0; | |
259 | u64 ino; | |
0c936ee3 | 260 | u8 type; |
e3427184 | 261 | int ret; |
0c936ee3 | 262 | |
e3427184 QW |
263 | ASSERT(fs_info); |
264 | ret = btrfs_lookup_path(fs_info->fs_root, BTRFS_FIRST_FREE_OBJECTID, | |
265 | file, &root, &ino, &type, 40); | |
266 | if (ret < 0) { | |
267 | error("Cannot lookup file %s", file); | |
268 | return ret; | |
0c936ee3 MB |
269 | } |
270 | ||
271 | if (type != BTRFS_FT_REG_FILE) { | |
e3427184 QW |
272 | error("Not a regular file: %s", file); |
273 | return -EINVAL; | |
0c936ee3 MB |
274 | } |
275 | ||
e3427184 QW |
276 | if (!len) { |
277 | ret = btrfs_size(file, &real_size); | |
278 | if (ret < 0) { | |
279 | error("Failed to get inode size: %s", file); | |
280 | return ret; | |
281 | } | |
282 | len = real_size; | |
283 | } | |
0c936ee3 | 284 | |
e3427184 QW |
285 | if (len > real_size - offset) |
286 | len = real_size - offset; | |
0c936ee3 | 287 | |
e3427184 QW |
288 | ret = btrfs_file_read(root, ino, offset, len, buf); |
289 | if (ret < 0) { | |
290 | error("An error occured while reading file %s", file); | |
291 | return ret; | |
0c936ee3 MB |
292 | } |
293 | ||
e3427184 | 294 | *actread = len; |
0c936ee3 MB |
295 | return 0; |
296 | } | |
297 | ||
298 | void btrfs_close(void) | |
299 | { | |
300 | btrfs_chunk_map_exit(); | |
f06bfcf5 QW |
301 | if (current_fs_info) { |
302 | close_ctree_fs_info(current_fs_info); | |
303 | current_fs_info = NULL; | |
304 | } | |
0c936ee3 MB |
305 | } |
306 | ||
307 | int btrfs_uuid(char *uuid_str) | |
308 | { | |
309 | #ifdef CONFIG_LIB_UUID | |
5bbb68d5 QW |
310 | if (current_fs_info) |
311 | uuid_bin_to_str(current_fs_info->super_copy->fsid, uuid_str, | |
312 | UUID_STR_FORMAT_STD); | |
0c936ee3 MB |
313 | return 0; |
314 | #endif | |
315 | return -ENOSYS; | |
316 | } |