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 "btrfs.h"
9 #include <config.h>
10 #include <malloc.h>
11 #include <linux/time.h>
12
13 struct btrfs_info btrfs_info;
14
readdir_callback(const struct btrfs_root * root,struct btrfs_dir_item * item)15 static int readdir_callback(const struct btrfs_root *root,
16 struct btrfs_dir_item *item)
17 {
18 static const char typestr[BTRFS_FT_MAX][4] = {
19 [BTRFS_FT_UNKNOWN] = " ? ",
20 [BTRFS_FT_REG_FILE] = " ",
21 [BTRFS_FT_DIR] = "DIR",
22 [BTRFS_FT_CHRDEV] = "CHR",
23 [BTRFS_FT_BLKDEV] = "BLK",
24 [BTRFS_FT_FIFO] = "FIF",
25 [BTRFS_FT_SOCK] = "SCK",
26 [BTRFS_FT_SYMLINK] = "SYM",
27 [BTRFS_FT_XATTR] = " ? ",
28 };
29 struct btrfs_inode_item inode;
30 const char *name = (const char *) (item + 1);
31 char filetime[32], *target = NULL;
32 time_t mtime;
33
34 if (btrfs_lookup_inode(root, &item->location, &inode, NULL)) {
35 printf("%s: Cannot find inode item for directory entry %.*s!\n",
36 __func__, item->name_len, name);
37 return 0;
38 }
39
40 mtime = inode.mtime.sec;
41 ctime_r(&mtime, filetime);
42
43 if (item->type == BTRFS_FT_SYMLINK) {
44 target = malloc(min(inode.size + 1,
45 (u64) btrfs_info.sb.sectorsize));
46
47 if (target && btrfs_readlink(root, item->location.objectid,
48 target)) {
49 free(target);
50 target = NULL;
51 }
52
53 if (!target)
54 printf("%s: Cannot read symlink target!\n", __func__);
55 }
56
57 printf("<%s> ", typestr[item->type]);
58 if (item->type == BTRFS_FT_CHRDEV || item->type == BTRFS_FT_BLKDEV)
59 printf("%4u,%5u ", (unsigned int) (inode.rdev >> 20),
60 (unsigned int) (inode.rdev & 0xfffff));
61 else
62 printf("%10llu ", inode.size);
63
64 printf("%24.24s %.*s", filetime, item->name_len, name);
65
66 if (item->type == BTRFS_FT_SYMLINK) {
67 printf(" -> %s", target ? target : "?");
68 if (target)
69 free(target);
70 }
71
72 printf("\n");
73
74 return 0;
75 }
76
btrfs_probe(struct blk_desc * fs_dev_desc,disk_partition_t * fs_partition)77 int btrfs_probe(struct blk_desc *fs_dev_desc, disk_partition_t *fs_partition)
78 {
79 btrfs_blk_desc = fs_dev_desc;
80 btrfs_part_info = fs_partition;
81
82 memset(&btrfs_info, 0, sizeof(btrfs_info));
83
84 btrfs_hash_init();
85 if (btrfs_read_superblock())
86 return -1;
87
88 if (btrfs_chunk_map_init()) {
89 printf("%s: failed to init chunk map\n", __func__);
90 return -1;
91 }
92
93 btrfs_info.tree_root.objectid = 0;
94 btrfs_info.tree_root.bytenr = btrfs_info.sb.root;
95 btrfs_info.chunk_root.objectid = 0;
96 btrfs_info.chunk_root.bytenr = btrfs_info.sb.chunk_root;
97
98 if (btrfs_read_chunk_tree()) {
99 printf("%s: failed to read chunk tree\n", __func__);
100 return -1;
101 }
102
103 if (btrfs_find_root(btrfs_get_default_subvol_objectid(),
104 &btrfs_info.fs_root, NULL)) {
105 printf("%s: failed to find default subvolume\n", __func__);
106 return -1;
107 }
108
109 return 0;
110 }
111
btrfs_ls(const char * path)112 int btrfs_ls(const char *path)
113 {
114 struct btrfs_root root = btrfs_info.fs_root;
115 u64 inr;
116 u8 type;
117
118 inr = btrfs_lookup_path(&root, root.root_dirid, path, &type, NULL, 40);
119
120 if (inr == -1ULL) {
121 printf("Cannot lookup path %s\n", path);
122 return -1;
123 }
124
125 if (type != BTRFS_FT_DIR) {
126 printf("Not a directory: %s\n", path);
127 return -1;
128 }
129
130 if (btrfs_readdir(&root, inr, readdir_callback)) {
131 printf("An error occured while listing directory %s\n", path);
132 return -1;
133 }
134
135 return 0;
136 }
137
btrfs_exists(const char * file)138 int btrfs_exists(const char *file)
139 {
140 struct btrfs_root root = btrfs_info.fs_root;
141 u64 inr;
142 u8 type;
143
144 inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, NULL, 40);
145
146 return (inr != -1ULL && type == BTRFS_FT_REG_FILE);
147 }
148
btrfs_size(const char * file,loff_t * size)149 int btrfs_size(const char *file, loff_t *size)
150 {
151 struct btrfs_root root = btrfs_info.fs_root;
152 struct btrfs_inode_item inode;
153 u64 inr;
154 u8 type;
155
156 inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, &inode,
157 40);
158
159 if (inr == -1ULL) {
160 printf("Cannot lookup file %s\n", file);
161 return -1;
162 }
163
164 if (type != BTRFS_FT_REG_FILE) {
165 printf("Not a regular file: %s\n", file);
166 return -1;
167 }
168
169 *size = inode.size;
170 return 0;
171 }
172
btrfs_read(const char * file,void * buf,loff_t offset,loff_t len,loff_t * actread)173 int btrfs_read(const char *file, void *buf, loff_t offset, loff_t len,
174 loff_t *actread)
175 {
176 struct btrfs_root root = btrfs_info.fs_root;
177 struct btrfs_inode_item inode;
178 u64 inr, rd;
179 u8 type;
180
181 inr = btrfs_lookup_path(&root, root.root_dirid, file, &type, &inode,
182 40);
183
184 if (inr == -1ULL) {
185 printf("Cannot lookup file %s\n", file);
186 return -1;
187 }
188
189 if (type != BTRFS_FT_REG_FILE) {
190 printf("Not a regular file: %s\n", file);
191 return -1;
192 }
193
194 if (!len)
195 len = inode.size;
196
197 if (len > inode.size - offset)
198 len = inode.size - offset;
199
200 rd = btrfs_file_read(&root, inr, offset, len, buf);
201 if (rd == -1ULL) {
202 printf("An error occured while reading file %s\n", file);
203 return -1;
204 }
205
206 *actread = rd;
207 return 0;
208 }
209
btrfs_close(void)210 void btrfs_close(void)
211 {
212 btrfs_chunk_map_exit();
213 }
214
btrfs_uuid(char * uuid_str)215 int btrfs_uuid(char *uuid_str)
216 {
217 #ifdef CONFIG_LIB_UUID
218 uuid_bin_to_str(btrfs_info.sb.fsid, uuid_str, UUID_STR_FORMAT_STD);
219 return 0;
220 #endif
221 return -ENOSYS;
222 }
223