xref: /openbmc/u-boot/fs/fs.c (revision 9d466f2f)
1 /*
2  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * SPDX-License-Identifier:	GPL-2.0
5  */
6 
7 #include <config.h>
8 #include <errno.h>
9 #include <common.h>
10 #include <mapmem.h>
11 #include <part.h>
12 #include <ext4fs.h>
13 #include <fat.h>
14 #include <fs.h>
15 #include <sandboxfs.h>
16 #include <ubifs_uboot.h>
17 #include <btrfs.h>
18 #include <asm/io.h>
19 #include <div64.h>
20 #include <linux/math64.h>
21 
22 DECLARE_GLOBAL_DATA_PTR;
23 
24 static struct blk_desc *fs_dev_desc;
25 static int fs_dev_part;
26 static disk_partition_t fs_partition;
27 static int fs_type = FS_TYPE_ANY;
28 
29 static inline int fs_probe_unsupported(struct blk_desc *fs_dev_desc,
30 				      disk_partition_t *fs_partition)
31 {
32 	printf("** Unrecognized filesystem type **\n");
33 	return -1;
34 }
35 
36 static inline int fs_ls_unsupported(const char *dirname)
37 {
38 	return -1;
39 }
40 
41 /* generic implementation of ls in terms of opendir/readdir/closedir */
42 __maybe_unused
43 static int fs_ls_generic(const char *dirname)
44 {
45 	struct fs_dir_stream *dirs;
46 	struct fs_dirent *dent;
47 	int nfiles = 0, ndirs = 0;
48 
49 	dirs = fs_opendir(dirname);
50 	if (!dirs)
51 		return -errno;
52 
53 	while ((dent = fs_readdir(dirs))) {
54 		if (dent->type == FS_DT_DIR) {
55 			printf("            %s/\n", dent->name);
56 			ndirs++;
57 		} else {
58 			printf(" %8lld   %s\n", dent->size, dent->name);
59 			nfiles++;
60 		}
61 	}
62 
63 	fs_closedir(dirs);
64 
65 	printf("\n%d file(s), %d dir(s)\n\n", nfiles, ndirs);
66 
67 	return 0;
68 }
69 
70 static inline int fs_exists_unsupported(const char *filename)
71 {
72 	return 0;
73 }
74 
75 static inline int fs_size_unsupported(const char *filename, loff_t *size)
76 {
77 	return -1;
78 }
79 
80 static inline int fs_read_unsupported(const char *filename, void *buf,
81 				      loff_t offset, loff_t len,
82 				      loff_t *actread)
83 {
84 	return -1;
85 }
86 
87 static inline int fs_write_unsupported(const char *filename, void *buf,
88 				      loff_t offset, loff_t len,
89 				      loff_t *actwrite)
90 {
91 	return -1;
92 }
93 
94 static inline void fs_close_unsupported(void)
95 {
96 }
97 
98 static inline int fs_uuid_unsupported(char *uuid_str)
99 {
100 	return -1;
101 }
102 
103 static inline int fs_opendir_unsupported(const char *filename,
104 					 struct fs_dir_stream **dirs)
105 {
106 	return -EACCES;
107 }
108 
109 struct fstype_info {
110 	int fstype;
111 	char *name;
112 	/*
113 	 * Is it legal to pass NULL as .probe()'s  fs_dev_desc parameter? This
114 	 * should be false in most cases. For "virtual" filesystems which
115 	 * aren't based on a U-Boot block device (e.g. sandbox), this can be
116 	 * set to true. This should also be true for the dumm entry at the end
117 	 * of fstypes[], since that is essentially a "virtual" (non-existent)
118 	 * filesystem.
119 	 */
120 	bool null_dev_desc_ok;
121 	int (*probe)(struct blk_desc *fs_dev_desc,
122 		     disk_partition_t *fs_partition);
123 	int (*ls)(const char *dirname);
124 	int (*exists)(const char *filename);
125 	int (*size)(const char *filename, loff_t *size);
126 	int (*read)(const char *filename, void *buf, loff_t offset,
127 		    loff_t len, loff_t *actread);
128 	int (*write)(const char *filename, void *buf, loff_t offset,
129 		     loff_t len, loff_t *actwrite);
130 	void (*close)(void);
131 	int (*uuid)(char *uuid_str);
132 	/*
133 	 * Open a directory stream.  On success return 0 and directory
134 	 * stream pointer via 'dirsp'.  On error, return -errno.  See
135 	 * fs_opendir().
136 	 */
137 	int (*opendir)(const char *filename, struct fs_dir_stream **dirsp);
138 	/*
139 	 * Read next entry from directory stream.  On success return 0
140 	 * and directory entry pointer via 'dentp'.  On error return
141 	 * -errno.  See fs_readdir().
142 	 */
143 	int (*readdir)(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
144 	/* see fs_closedir() */
145 	void (*closedir)(struct fs_dir_stream *dirs);
146 };
147 
148 static struct fstype_info fstypes[] = {
149 #ifdef CONFIG_FS_FAT
150 	{
151 		.fstype = FS_TYPE_FAT,
152 		.name = "fat",
153 		.null_dev_desc_ok = false,
154 		.probe = fat_set_blk_dev,
155 		.close = fat_close,
156 		.ls = fs_ls_generic,
157 		.exists = fat_exists,
158 		.size = fat_size,
159 		.read = fat_read_file,
160 #ifdef CONFIG_FAT_WRITE
161 		.write = file_fat_write,
162 #else
163 		.write = fs_write_unsupported,
164 #endif
165 		.uuid = fs_uuid_unsupported,
166 		.opendir = fat_opendir,
167 		.readdir = fat_readdir,
168 		.closedir = fat_closedir,
169 	},
170 #endif
171 #ifdef CONFIG_FS_EXT4
172 	{
173 		.fstype = FS_TYPE_EXT,
174 		.name = "ext4",
175 		.null_dev_desc_ok = false,
176 		.probe = ext4fs_probe,
177 		.close = ext4fs_close,
178 		.ls = ext4fs_ls,
179 		.exists = ext4fs_exists,
180 		.size = ext4fs_size,
181 		.read = ext4_read_file,
182 #ifdef CONFIG_CMD_EXT4_WRITE
183 		.write = ext4_write_file,
184 #else
185 		.write = fs_write_unsupported,
186 #endif
187 		.uuid = ext4fs_uuid,
188 		.opendir = fs_opendir_unsupported,
189 	},
190 #endif
191 #ifdef CONFIG_SANDBOX
192 	{
193 		.fstype = FS_TYPE_SANDBOX,
194 		.name = "sandbox",
195 		.null_dev_desc_ok = true,
196 		.probe = sandbox_fs_set_blk_dev,
197 		.close = sandbox_fs_close,
198 		.ls = sandbox_fs_ls,
199 		.exists = sandbox_fs_exists,
200 		.size = sandbox_fs_size,
201 		.read = fs_read_sandbox,
202 		.write = fs_write_sandbox,
203 		.uuid = fs_uuid_unsupported,
204 		.opendir = fs_opendir_unsupported,
205 	},
206 #endif
207 #ifdef CONFIG_CMD_UBIFS
208 	{
209 		.fstype = FS_TYPE_UBIFS,
210 		.name = "ubifs",
211 		.null_dev_desc_ok = true,
212 		.probe = ubifs_set_blk_dev,
213 		.close = ubifs_close,
214 		.ls = ubifs_ls,
215 		.exists = ubifs_exists,
216 		.size = ubifs_size,
217 		.read = ubifs_read,
218 		.write = fs_write_unsupported,
219 		.uuid = fs_uuid_unsupported,
220 		.opendir = fs_opendir_unsupported,
221 	},
222 #endif
223 #ifdef CONFIG_FS_BTRFS
224 	{
225 		.fstype = FS_TYPE_BTRFS,
226 		.name = "btrfs",
227 		.null_dev_desc_ok = false,
228 		.probe = btrfs_probe,
229 		.close = btrfs_close,
230 		.ls = btrfs_ls,
231 		.exists = btrfs_exists,
232 		.size = btrfs_size,
233 		.read = btrfs_read,
234 		.write = fs_write_unsupported,
235 		.uuid = btrfs_uuid,
236 		.opendir = fs_opendir_unsupported,
237 	},
238 #endif
239 	{
240 		.fstype = FS_TYPE_ANY,
241 		.name = "unsupported",
242 		.null_dev_desc_ok = true,
243 		.probe = fs_probe_unsupported,
244 		.close = fs_close_unsupported,
245 		.ls = fs_ls_unsupported,
246 		.exists = fs_exists_unsupported,
247 		.size = fs_size_unsupported,
248 		.read = fs_read_unsupported,
249 		.write = fs_write_unsupported,
250 		.uuid = fs_uuid_unsupported,
251 		.opendir = fs_opendir_unsupported,
252 	},
253 };
254 
255 static struct fstype_info *fs_get_info(int fstype)
256 {
257 	struct fstype_info *info;
258 	int i;
259 
260 	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) {
261 		if (fstype == info->fstype)
262 			return info;
263 	}
264 
265 	/* Return the 'unsupported' sentinel */
266 	return info;
267 }
268 
269 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
270 {
271 	struct fstype_info *info;
272 	int part, i;
273 #ifdef CONFIG_NEEDS_MANUAL_RELOC
274 	static int relocated;
275 
276 	if (!relocated) {
277 		for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes);
278 				i++, info++) {
279 			info->name += gd->reloc_off;
280 			info->probe += gd->reloc_off;
281 			info->close += gd->reloc_off;
282 			info->ls += gd->reloc_off;
283 			info->read += gd->reloc_off;
284 			info->write += gd->reloc_off;
285 		}
286 		relocated = 1;
287 	}
288 #endif
289 
290 	part = blk_get_device_part_str(ifname, dev_part_str, &fs_dev_desc,
291 					&fs_partition, 1);
292 	if (part < 0)
293 		return -1;
294 
295 	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
296 		if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY &&
297 				fstype != info->fstype)
298 			continue;
299 
300 		if (!fs_dev_desc && !info->null_dev_desc_ok)
301 			continue;
302 
303 		if (!info->probe(fs_dev_desc, &fs_partition)) {
304 			fs_type = info->fstype;
305 			fs_dev_part = part;
306 			return 0;
307 		}
308 	}
309 
310 	return -1;
311 }
312 
313 /* set current blk device w/ blk_desc + partition # */
314 int fs_set_blk_dev_with_part(struct blk_desc *desc, int part)
315 {
316 	struct fstype_info *info;
317 	int ret, i;
318 
319 	if (part >= 1)
320 		ret = part_get_info(desc, part, &fs_partition);
321 	else
322 		ret = part_get_info_whole_disk(desc, &fs_partition);
323 	if (ret)
324 		return ret;
325 	fs_dev_desc = desc;
326 
327 	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
328 		if (!info->probe(fs_dev_desc, &fs_partition)) {
329 			fs_type = info->fstype;
330 			return 0;
331 		}
332 	}
333 
334 	return -1;
335 }
336 
337 static void fs_close(void)
338 {
339 	struct fstype_info *info = fs_get_info(fs_type);
340 
341 	info->close();
342 
343 	fs_type = FS_TYPE_ANY;
344 }
345 
346 int fs_uuid(char *uuid_str)
347 {
348 	struct fstype_info *info = fs_get_info(fs_type);
349 
350 	return info->uuid(uuid_str);
351 }
352 
353 int fs_ls(const char *dirname)
354 {
355 	int ret;
356 
357 	struct fstype_info *info = fs_get_info(fs_type);
358 
359 	ret = info->ls(dirname);
360 
361 	fs_type = FS_TYPE_ANY;
362 	fs_close();
363 
364 	return ret;
365 }
366 
367 int fs_exists(const char *filename)
368 {
369 	int ret;
370 
371 	struct fstype_info *info = fs_get_info(fs_type);
372 
373 	ret = info->exists(filename);
374 
375 	fs_close();
376 
377 	return ret;
378 }
379 
380 int fs_size(const char *filename, loff_t *size)
381 {
382 	int ret;
383 
384 	struct fstype_info *info = fs_get_info(fs_type);
385 
386 	ret = info->size(filename, size);
387 
388 	fs_close();
389 
390 	return ret;
391 }
392 
393 int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
394 	    loff_t *actread)
395 {
396 	struct fstype_info *info = fs_get_info(fs_type);
397 	void *buf;
398 	int ret;
399 
400 	/*
401 	 * We don't actually know how many bytes are being read, since len==0
402 	 * means read the whole file.
403 	 */
404 	buf = map_sysmem(addr, len);
405 	ret = info->read(filename, buf, offset, len, actread);
406 	unmap_sysmem(buf);
407 
408 	/* If we requested a specific number of bytes, check we got it */
409 	if (ret == 0 && len && *actread != len)
410 		debug("** %s shorter than offset + len **\n", filename);
411 	fs_close();
412 
413 	return ret;
414 }
415 
416 int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
417 	     loff_t *actwrite)
418 {
419 	struct fstype_info *info = fs_get_info(fs_type);
420 	void *buf;
421 	int ret;
422 
423 	buf = map_sysmem(addr, len);
424 	ret = info->write(filename, buf, offset, len, actwrite);
425 	unmap_sysmem(buf);
426 
427 	if (ret < 0 && len != *actwrite) {
428 		printf("** Unable to write file %s **\n", filename);
429 		ret = -1;
430 	}
431 	fs_close();
432 
433 	return ret;
434 }
435 
436 struct fs_dir_stream *fs_opendir(const char *filename)
437 {
438 	struct fstype_info *info = fs_get_info(fs_type);
439 	struct fs_dir_stream *dirs = NULL;
440 	int ret;
441 
442 	ret = info->opendir(filename, &dirs);
443 	fs_close();
444 	if (ret) {
445 		errno = -ret;
446 		return NULL;
447 	}
448 
449 	dirs->desc = fs_dev_desc;
450 	dirs->part = fs_dev_part;
451 
452 	return dirs;
453 }
454 
455 struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs)
456 {
457 	struct fstype_info *info;
458 	struct fs_dirent *dirent;
459 	int ret;
460 
461 	fs_set_blk_dev_with_part(dirs->desc, dirs->part);
462 	info = fs_get_info(fs_type);
463 
464 	ret = info->readdir(dirs, &dirent);
465 	fs_close();
466 	if (ret) {
467 		errno = -ret;
468 		return NULL;
469 	}
470 
471 	return dirent;
472 }
473 
474 void fs_closedir(struct fs_dir_stream *dirs)
475 {
476 	struct fstype_info *info;
477 
478 	if (!dirs)
479 		return;
480 
481 	fs_set_blk_dev_with_part(dirs->desc, dirs->part);
482 	info = fs_get_info(fs_type);
483 
484 	info->closedir(dirs);
485 	fs_close();
486 }
487 
488 
489 int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
490 		int fstype)
491 {
492 	loff_t size;
493 
494 	if (argc != 4)
495 		return CMD_RET_USAGE;
496 
497 	if (fs_set_blk_dev(argv[1], argv[2], fstype))
498 		return 1;
499 
500 	if (fs_size(argv[3], &size) < 0)
501 		return CMD_RET_FAILURE;
502 
503 	env_set_hex("filesize", size);
504 
505 	return 0;
506 }
507 
508 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
509 		int fstype)
510 {
511 	unsigned long addr;
512 	const char *addr_str;
513 	const char *filename;
514 	loff_t bytes;
515 	loff_t pos;
516 	loff_t len_read;
517 	int ret;
518 	unsigned long time;
519 	char *ep;
520 
521 	if (argc < 2)
522 		return CMD_RET_USAGE;
523 	if (argc > 7)
524 		return CMD_RET_USAGE;
525 
526 	if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
527 		return 1;
528 
529 	if (argc >= 4) {
530 		addr = simple_strtoul(argv[3], &ep, 16);
531 		if (ep == argv[3] || *ep != '\0')
532 			return CMD_RET_USAGE;
533 	} else {
534 		addr_str = env_get("loadaddr");
535 		if (addr_str != NULL)
536 			addr = simple_strtoul(addr_str, NULL, 16);
537 		else
538 			addr = CONFIG_SYS_LOAD_ADDR;
539 	}
540 	if (argc >= 5) {
541 		filename = argv[4];
542 	} else {
543 		filename = env_get("bootfile");
544 		if (!filename) {
545 			puts("** No boot file defined **\n");
546 			return 1;
547 		}
548 	}
549 	if (argc >= 6)
550 		bytes = simple_strtoul(argv[5], NULL, 16);
551 	else
552 		bytes = 0;
553 	if (argc >= 7)
554 		pos = simple_strtoul(argv[6], NULL, 16);
555 	else
556 		pos = 0;
557 
558 	time = get_timer(0);
559 	ret = fs_read(filename, addr, pos, bytes, &len_read);
560 	time = get_timer(time);
561 	if (ret < 0)
562 		return 1;
563 
564 	printf("%llu bytes read in %lu ms", len_read, time);
565 	if (time > 0) {
566 		puts(" (");
567 		print_size(div_u64(len_read, time) * 1000, "/s");
568 		puts(")");
569 	}
570 	puts("\n");
571 
572 	env_set_hex("fileaddr", addr);
573 	env_set_hex("filesize", len_read);
574 
575 	return 0;
576 }
577 
578 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
579 	int fstype)
580 {
581 	if (argc < 2)
582 		return CMD_RET_USAGE;
583 	if (argc > 4)
584 		return CMD_RET_USAGE;
585 
586 	if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
587 		return 1;
588 
589 	if (fs_ls(argc >= 4 ? argv[3] : "/"))
590 		return 1;
591 
592 	return 0;
593 }
594 
595 int file_exists(const char *dev_type, const char *dev_part, const char *file,
596 		int fstype)
597 {
598 	if (fs_set_blk_dev(dev_type, dev_part, fstype))
599 		return 0;
600 
601 	return fs_exists(file);
602 }
603 
604 int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
605 		int fstype)
606 {
607 	unsigned long addr;
608 	const char *filename;
609 	loff_t bytes;
610 	loff_t pos;
611 	loff_t len;
612 	int ret;
613 	unsigned long time;
614 
615 	if (argc < 6 || argc > 7)
616 		return CMD_RET_USAGE;
617 
618 	if (fs_set_blk_dev(argv[1], argv[2], fstype))
619 		return 1;
620 
621 	addr = simple_strtoul(argv[3], NULL, 16);
622 	filename = argv[4];
623 	bytes = simple_strtoul(argv[5], NULL, 16);
624 	if (argc >= 7)
625 		pos = simple_strtoul(argv[6], NULL, 16);
626 	else
627 		pos = 0;
628 
629 	time = get_timer(0);
630 	ret = fs_write(filename, addr, pos, bytes, &len);
631 	time = get_timer(time);
632 	if (ret < 0)
633 		return 1;
634 
635 	printf("%llu bytes written in %lu ms", len, time);
636 	if (time > 0) {
637 		puts(" (");
638 		print_size(div_u64(len, time) * 1000, "/s");
639 		puts(")");
640 	}
641 	puts("\n");
642 
643 	return 0;
644 }
645 
646 int do_fs_uuid(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
647 		int fstype)
648 {
649 	int ret;
650 	char uuid[37];
651 	memset(uuid, 0, sizeof(uuid));
652 
653 	if (argc < 3 || argc > 4)
654 		return CMD_RET_USAGE;
655 
656 	if (fs_set_blk_dev(argv[1], argv[2], fstype))
657 		return 1;
658 
659 	ret = fs_uuid(uuid);
660 	if (ret)
661 		return CMD_RET_FAILURE;
662 
663 	if (argc == 4)
664 		env_set(argv[3], uuid);
665 	else
666 		printf("%s\n", uuid);
667 
668 	return CMD_RET_SUCCESS;
669 }
670 
671 int do_fs_type(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
672 {
673 	struct fstype_info *info;
674 
675 	if (argc < 3 || argc > 4)
676 		return CMD_RET_USAGE;
677 
678 	if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY))
679 		return 1;
680 
681 	info = fs_get_info(fs_type);
682 
683 	if (argc == 4)
684 		env_set(argv[3], info->name);
685 	else
686 		printf("%s\n", info->name);
687 
688 	return CMD_RET_SUCCESS;
689 }
690 
691