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