Merge tag 'v2021.10-rc5' into next
[platform/kernel/u-boot.git] / fs / fs.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
4  */
5
6 #define LOG_CATEGORY LOGC_CORE
7
8 #include <command.h>
9 #include <config.h>
10 #include <errno.h>
11 #include <common.h>
12 #include <env.h>
13 #include <lmb.h>
14 #include <log.h>
15 #include <mapmem.h>
16 #include <part.h>
17 #include <ext4fs.h>
18 #include <fat.h>
19 #include <fs.h>
20 #include <sandboxfs.h>
21 #include <ubifs_uboot.h>
22 #include <btrfs.h>
23 #include <asm/global_data.h>
24 #include <asm/io.h>
25 #include <div64.h>
26 #include <linux/math64.h>
27 #include <efi_loader.h>
28 #include <squashfs.h>
29
30 DECLARE_GLOBAL_DATA_PTR;
31
32 static struct blk_desc *fs_dev_desc;
33 static int fs_dev_part;
34 static struct disk_partition fs_partition;
35 static int fs_type = FS_TYPE_ANY;
36
37 static inline int fs_probe_unsupported(struct blk_desc *fs_dev_desc,
38                                       struct disk_partition *fs_partition)
39 {
40         log_debug("Unrecognized filesystem type\n");
41         return -1;
42 }
43
44 static inline int fs_ls_unsupported(const char *dirname)
45 {
46         return -1;
47 }
48
49 /* generic implementation of ls in terms of opendir/readdir/closedir */
50 __maybe_unused
51 static int fs_ls_generic(const char *dirname)
52 {
53         struct fs_dir_stream *dirs;
54         struct fs_dirent *dent;
55         int nfiles = 0, ndirs = 0;
56
57         dirs = fs_opendir(dirname);
58         if (!dirs)
59                 return -errno;
60
61         while ((dent = fs_readdir(dirs))) {
62                 if (dent->type == FS_DT_DIR) {
63                         printf("            %s/\n", dent->name);
64                         ndirs++;
65                 } else if (dent->type == FS_DT_LNK) {
66                         printf("    <SYM>   %s\n", dent->name);
67                         nfiles++;
68                 } else {
69                         printf(" %8lld   %s\n", dent->size, dent->name);
70                         nfiles++;
71                 }
72         }
73
74         fs_closedir(dirs);
75
76         printf("\n%d file(s), %d dir(s)\n\n", nfiles, ndirs);
77
78         return 0;
79 }
80
81 static inline int fs_exists_unsupported(const char *filename)
82 {
83         return 0;
84 }
85
86 static inline int fs_size_unsupported(const char *filename, loff_t *size)
87 {
88         return -1;
89 }
90
91 static inline int fs_read_unsupported(const char *filename, void *buf,
92                                       loff_t offset, loff_t len,
93                                       loff_t *actread)
94 {
95         return -1;
96 }
97
98 static inline int fs_write_unsupported(const char *filename, void *buf,
99                                       loff_t offset, loff_t len,
100                                       loff_t *actwrite)
101 {
102         return -1;
103 }
104
105 static inline int fs_ln_unsupported(const char *filename, const char *target)
106 {
107         return -1;
108 }
109
110 static inline void fs_close_unsupported(void)
111 {
112 }
113
114 static inline int fs_uuid_unsupported(char *uuid_str)
115 {
116         return -1;
117 }
118
119 static inline int fs_opendir_unsupported(const char *filename,
120                                          struct fs_dir_stream **dirs)
121 {
122         return -EACCES;
123 }
124
125 static inline int fs_unlink_unsupported(const char *filename)
126 {
127         return -1;
128 }
129
130 static inline int fs_mkdir_unsupported(const char *dirname)
131 {
132         return -1;
133 }
134
135 struct fstype_info {
136         int fstype;
137         char *name;
138         /*
139          * Is it legal to pass NULL as .probe()'s  fs_dev_desc parameter? This
140          * should be false in most cases. For "virtual" filesystems which
141          * aren't based on a U-Boot block device (e.g. sandbox), this can be
142          * set to true. This should also be true for the dummy entry at the end
143          * of fstypes[], since that is essentially a "virtual" (non-existent)
144          * filesystem.
145          */
146         bool null_dev_desc_ok;
147         int (*probe)(struct blk_desc *fs_dev_desc,
148                      struct disk_partition *fs_partition);
149         int (*ls)(const char *dirname);
150         int (*exists)(const char *filename);
151         int (*size)(const char *filename, loff_t *size);
152         int (*read)(const char *filename, void *buf, loff_t offset,
153                     loff_t len, loff_t *actread);
154         int (*write)(const char *filename, void *buf, loff_t offset,
155                      loff_t len, loff_t *actwrite);
156         void (*close)(void);
157         int (*uuid)(char *uuid_str);
158         /*
159          * Open a directory stream.  On success return 0 and directory
160          * stream pointer via 'dirsp'.  On error, return -errno.  See
161          * fs_opendir().
162          */
163         int (*opendir)(const char *filename, struct fs_dir_stream **dirsp);
164         /*
165          * Read next entry from directory stream.  On success return 0
166          * and directory entry pointer via 'dentp'.  On error return
167          * -errno.  See fs_readdir().
168          */
169         int (*readdir)(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
170         /* see fs_closedir() */
171         void (*closedir)(struct fs_dir_stream *dirs);
172         int (*unlink)(const char *filename);
173         int (*mkdir)(const char *dirname);
174         int (*ln)(const char *filename, const char *target);
175 };
176
177 static struct fstype_info fstypes[] = {
178 #ifdef CONFIG_FS_FAT
179         {
180                 .fstype = FS_TYPE_FAT,
181                 .name = "fat",
182                 .null_dev_desc_ok = false,
183                 .probe = fat_set_blk_dev,
184                 .close = fat_close,
185                 .ls = fs_ls_generic,
186                 .exists = fat_exists,
187                 .size = fat_size,
188                 .read = fat_read_file,
189 #if CONFIG_IS_ENABLED(FAT_WRITE)
190                 .write = file_fat_write,
191                 .unlink = fat_unlink,
192                 .mkdir = fat_mkdir,
193 #else
194                 .write = fs_write_unsupported,
195                 .unlink = fs_unlink_unsupported,
196                 .mkdir = fs_mkdir_unsupported,
197 #endif
198                 .uuid = fat_uuid,
199                 .opendir = fat_opendir,
200                 .readdir = fat_readdir,
201                 .closedir = fat_closedir,
202                 .ln = fs_ln_unsupported,
203         },
204 #endif
205
206 #if CONFIG_IS_ENABLED(FS_EXT4)
207         {
208                 .fstype = FS_TYPE_EXT,
209                 .name = "ext4",
210                 .null_dev_desc_ok = false,
211                 .probe = ext4fs_probe,
212                 .close = ext4fs_close,
213                 .ls = ext4fs_ls,
214                 .exists = ext4fs_exists,
215                 .size = ext4fs_size,
216                 .read = ext4_read_file,
217 #ifdef CONFIG_CMD_EXT4_WRITE
218                 .write = ext4_write_file,
219                 .ln = ext4fs_create_link,
220 #else
221                 .write = fs_write_unsupported,
222                 .ln = fs_ln_unsupported,
223 #endif
224                 .uuid = ext4fs_uuid,
225                 .opendir = fs_opendir_unsupported,
226                 .unlink = fs_unlink_unsupported,
227                 .mkdir = fs_mkdir_unsupported,
228         },
229 #endif
230 #ifdef CONFIG_SANDBOX
231         {
232                 .fstype = FS_TYPE_SANDBOX,
233                 .name = "sandbox",
234                 .null_dev_desc_ok = true,
235                 .probe = sandbox_fs_set_blk_dev,
236                 .close = sandbox_fs_close,
237                 .ls = sandbox_fs_ls,
238                 .exists = sandbox_fs_exists,
239                 .size = sandbox_fs_size,
240                 .read = fs_read_sandbox,
241                 .write = fs_write_sandbox,
242                 .uuid = fs_uuid_unsupported,
243                 .opendir = fs_opendir_unsupported,
244                 .unlink = fs_unlink_unsupported,
245                 .mkdir = fs_mkdir_unsupported,
246                 .ln = fs_ln_unsupported,
247         },
248 #endif
249 #ifdef CONFIG_CMD_UBIFS
250         {
251                 .fstype = FS_TYPE_UBIFS,
252                 .name = "ubifs",
253                 .null_dev_desc_ok = true,
254                 .probe = ubifs_set_blk_dev,
255                 .close = ubifs_close,
256                 .ls = ubifs_ls,
257                 .exists = ubifs_exists,
258                 .size = ubifs_size,
259                 .read = ubifs_read,
260                 .write = fs_write_unsupported,
261                 .uuid = fs_uuid_unsupported,
262                 .opendir = fs_opendir_unsupported,
263                 .unlink = fs_unlink_unsupported,
264                 .mkdir = fs_mkdir_unsupported,
265                 .ln = fs_ln_unsupported,
266         },
267 #endif
268 #ifdef CONFIG_FS_BTRFS
269         {
270                 .fstype = FS_TYPE_BTRFS,
271                 .name = "btrfs",
272                 .null_dev_desc_ok = false,
273                 .probe = btrfs_probe,
274                 .close = btrfs_close,
275                 .ls = btrfs_ls,
276                 .exists = btrfs_exists,
277                 .size = btrfs_size,
278                 .read = btrfs_read,
279                 .write = fs_write_unsupported,
280                 .uuid = btrfs_uuid,
281                 .opendir = fs_opendir_unsupported,
282                 .unlink = fs_unlink_unsupported,
283                 .mkdir = fs_mkdir_unsupported,
284                 .ln = fs_ln_unsupported,
285         },
286 #endif
287 #if IS_ENABLED(CONFIG_FS_SQUASHFS)
288         {
289                 .fstype = FS_TYPE_SQUASHFS,
290                 .name = "squashfs",
291                 .null_dev_desc_ok = false,
292                 .probe = sqfs_probe,
293                 .opendir = sqfs_opendir,
294                 .readdir = sqfs_readdir,
295                 .ls = fs_ls_generic,
296                 .read = sqfs_read,
297                 .size = sqfs_size,
298                 .close = sqfs_close,
299                 .closedir = sqfs_closedir,
300                 .exists = sqfs_exists,
301                 .uuid = fs_uuid_unsupported,
302                 .write = fs_write_unsupported,
303                 .ln = fs_ln_unsupported,
304                 .unlink = fs_unlink_unsupported,
305                 .mkdir = fs_mkdir_unsupported,
306         },
307 #endif
308         {
309                 .fstype = FS_TYPE_ANY,
310                 .name = "unsupported",
311                 .null_dev_desc_ok = true,
312                 .probe = fs_probe_unsupported,
313                 .close = fs_close_unsupported,
314                 .ls = fs_ls_unsupported,
315                 .exists = fs_exists_unsupported,
316                 .size = fs_size_unsupported,
317                 .read = fs_read_unsupported,
318                 .write = fs_write_unsupported,
319                 .uuid = fs_uuid_unsupported,
320                 .opendir = fs_opendir_unsupported,
321                 .unlink = fs_unlink_unsupported,
322                 .mkdir = fs_mkdir_unsupported,
323                 .ln = fs_ln_unsupported,
324         },
325 };
326
327 static struct fstype_info *fs_get_info(int fstype)
328 {
329         struct fstype_info *info;
330         int i;
331
332         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) {
333                 if (fstype == info->fstype)
334                         return info;
335         }
336
337         /* Return the 'unsupported' sentinel */
338         return info;
339 }
340
341 /**
342  * fs_get_type() - Get type of current filesystem
343  *
344  * Return: filesystem type
345  *
346  * Returns filesystem type representing the current filesystem, or
347  * FS_TYPE_ANY for any unrecognised filesystem.
348  */
349 int fs_get_type(void)
350 {
351         return fs_type;
352 }
353
354 /**
355  * fs_get_type_name() - Get type of current filesystem
356  *
357  * Return: Pointer to filesystem name
358  *
359  * Returns a string describing the current filesystem, or the sentinel
360  * "unsupported" for any unrecognised filesystem.
361  */
362 const char *fs_get_type_name(void)
363 {
364         return fs_get_info(fs_type)->name;
365 }
366
367 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
368 {
369         struct fstype_info *info;
370         int part, i;
371 #ifdef CONFIG_NEEDS_MANUAL_RELOC
372         static int relocated;
373
374         if (!relocated) {
375                 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes);
376                                 i++, info++) {
377                         info->name += gd->reloc_off;
378                         info->probe += gd->reloc_off;
379                         info->close += gd->reloc_off;
380                         info->ls += gd->reloc_off;
381                         info->read += gd->reloc_off;
382                         info->write += gd->reloc_off;
383                 }
384                 relocated = 1;
385         }
386 #endif
387
388         part = part_get_info_by_dev_and_name_or_num(ifname, dev_part_str, &fs_dev_desc,
389                                                     &fs_partition, 1);
390         if (part < 0)
391                 return -1;
392
393         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
394                 if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY &&
395                                 fstype != info->fstype)
396                         continue;
397
398                 if (!fs_dev_desc && !info->null_dev_desc_ok)
399                         continue;
400
401                 if (!info->probe(fs_dev_desc, &fs_partition)) {
402                         fs_type = info->fstype;
403                         fs_dev_part = part;
404                         return 0;
405                 }
406         }
407
408         return -1;
409 }
410
411 /* set current blk device w/ blk_desc + partition # */
412 int fs_set_blk_dev_with_part(struct blk_desc *desc, int part)
413 {
414         struct fstype_info *info;
415         int ret, i;
416
417         if (part >= 1)
418                 ret = part_get_info(desc, part, &fs_partition);
419         else
420                 ret = part_get_info_whole_disk(desc, &fs_partition);
421         if (ret)
422                 return ret;
423         fs_dev_desc = desc;
424
425         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
426                 if (!info->probe(fs_dev_desc, &fs_partition)) {
427                         fs_type = info->fstype;
428                         fs_dev_part = part;
429                         return 0;
430                 }
431         }
432
433         return -1;
434 }
435
436 void fs_close(void)
437 {
438         struct fstype_info *info = fs_get_info(fs_type);
439
440         info->close();
441
442         fs_type = FS_TYPE_ANY;
443 }
444
445 int fs_uuid(char *uuid_str)
446 {
447         struct fstype_info *info = fs_get_info(fs_type);
448
449         return info->uuid(uuid_str);
450 }
451
452 int fs_ls(const char *dirname)
453 {
454         int ret;
455
456         struct fstype_info *info = fs_get_info(fs_type);
457
458         ret = info->ls(dirname);
459
460         fs_close();
461
462         return ret;
463 }
464
465 int fs_exists(const char *filename)
466 {
467         int ret;
468
469         struct fstype_info *info = fs_get_info(fs_type);
470
471         ret = info->exists(filename);
472
473         fs_close();
474
475         return ret;
476 }
477
478 int fs_size(const char *filename, loff_t *size)
479 {
480         int ret;
481
482         struct fstype_info *info = fs_get_info(fs_type);
483
484         ret = info->size(filename, size);
485
486         fs_close();
487
488         return ret;
489 }
490
491 #ifdef CONFIG_LMB
492 /* Check if a file may be read to the given address */
493 static int fs_read_lmb_check(const char *filename, ulong addr, loff_t offset,
494                              loff_t len, struct fstype_info *info)
495 {
496         struct lmb lmb;
497         int ret;
498         loff_t size;
499         loff_t read_len;
500
501         /* get the actual size of the file */
502         ret = info->size(filename, &size);
503         if (ret)
504                 return ret;
505         if (offset >= size) {
506                 /* offset >= EOF, no bytes will be written */
507                 return 0;
508         }
509         read_len = size - offset;
510
511         /* limit to 'len' if it is smaller */
512         if (len && len < read_len)
513                 read_len = len;
514
515         lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob);
516         lmb_dump_all(&lmb);
517
518         if (lmb_alloc_addr(&lmb, addr, read_len) == addr)
519                 return 0;
520
521         log_err("** Reading file would overwrite reserved memory **\n");
522         return -ENOSPC;
523 }
524 #endif
525
526 static int _fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
527                     int do_lmb_check, loff_t *actread)
528 {
529         struct fstype_info *info = fs_get_info(fs_type);
530         void *buf;
531         int ret;
532
533 #ifdef CONFIG_LMB
534         if (do_lmb_check) {
535                 ret = fs_read_lmb_check(filename, addr, offset, len, info);
536                 if (ret)
537                         return ret;
538         }
539 #endif
540
541         /*
542          * We don't actually know how many bytes are being read, since len==0
543          * means read the whole file.
544          */
545         buf = map_sysmem(addr, len);
546         ret = info->read(filename, buf, offset, len, actread);
547         unmap_sysmem(buf);
548
549         /* If we requested a specific number of bytes, check we got it */
550         if (ret == 0 && len && *actread != len)
551                 log_debug("** %s shorter than offset + len **\n", filename);
552         fs_close();
553
554         return ret;
555 }
556
557 int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
558             loff_t *actread)
559 {
560         return _fs_read(filename, addr, offset, len, 0, actread);
561 }
562
563 int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
564              loff_t *actwrite)
565 {
566         struct fstype_info *info = fs_get_info(fs_type);
567         void *buf;
568         int ret;
569
570         buf = map_sysmem(addr, len);
571         ret = info->write(filename, buf, offset, len, actwrite);
572         unmap_sysmem(buf);
573
574         if (ret < 0 && len != *actwrite) {
575                 log_err("** Unable to write file %s **\n", filename);
576                 ret = -1;
577         }
578         fs_close();
579
580         return ret;
581 }
582
583 struct fs_dir_stream *fs_opendir(const char *filename)
584 {
585         struct fstype_info *info = fs_get_info(fs_type);
586         struct fs_dir_stream *dirs = NULL;
587         int ret;
588
589         ret = info->opendir(filename, &dirs);
590         fs_close();
591         if (ret) {
592                 errno = -ret;
593                 return NULL;
594         }
595
596         dirs->desc = fs_dev_desc;
597         dirs->part = fs_dev_part;
598
599         return dirs;
600 }
601
602 struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs)
603 {
604         struct fstype_info *info;
605         struct fs_dirent *dirent;
606         int ret;
607
608         fs_set_blk_dev_with_part(dirs->desc, dirs->part);
609         info = fs_get_info(fs_type);
610
611         ret = info->readdir(dirs, &dirent);
612         fs_close();
613         if (ret) {
614                 errno = -ret;
615                 return NULL;
616         }
617
618         return dirent;
619 }
620
621 void fs_closedir(struct fs_dir_stream *dirs)
622 {
623         struct fstype_info *info;
624
625         if (!dirs)
626                 return;
627
628         fs_set_blk_dev_with_part(dirs->desc, dirs->part);
629         info = fs_get_info(fs_type);
630
631         info->closedir(dirs);
632         fs_close();
633 }
634
635 int fs_unlink(const char *filename)
636 {
637         int ret;
638
639         struct fstype_info *info = fs_get_info(fs_type);
640
641         ret = info->unlink(filename);
642
643         fs_close();
644
645         return ret;
646 }
647
648 int fs_mkdir(const char *dirname)
649 {
650         int ret;
651
652         struct fstype_info *info = fs_get_info(fs_type);
653
654         ret = info->mkdir(dirname);
655
656         fs_close();
657
658         return ret;
659 }
660
661 int fs_ln(const char *fname, const char *target)
662 {
663         struct fstype_info *info = fs_get_info(fs_type);
664         int ret;
665
666         ret = info->ln(fname, target);
667
668         if (ret < 0) {
669                 log_err("** Unable to create link %s -> %s **\n", fname, target);
670                 ret = -1;
671         }
672         fs_close();
673
674         return ret;
675 }
676
677 int do_size(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
678             int fstype)
679 {
680         loff_t size;
681
682         if (argc != 4)
683                 return CMD_RET_USAGE;
684
685         if (fs_set_blk_dev(argv[1], argv[2], fstype))
686                 return 1;
687
688         if (fs_size(argv[3], &size) < 0)
689                 return CMD_RET_FAILURE;
690
691         env_set_hex("filesize", size);
692
693         return 0;
694 }
695
696 int do_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
697             int fstype)
698 {
699         unsigned long addr;
700         const char *addr_str;
701         const char *filename;
702         loff_t bytes;
703         loff_t pos;
704         loff_t len_read;
705         int ret;
706         unsigned long time;
707         char *ep;
708
709         if (argc < 2)
710                 return CMD_RET_USAGE;
711         if (argc > 7)
712                 return CMD_RET_USAGE;
713
714         if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype)) {
715                 log_err("Can't set block device\n");
716                 return 1;
717         }
718
719         if (argc >= 4) {
720                 addr = hextoul(argv[3], &ep);
721                 if (ep == argv[3] || *ep != '\0')
722                         return CMD_RET_USAGE;
723         } else {
724                 addr_str = env_get("loadaddr");
725                 if (addr_str != NULL)
726                         addr = hextoul(addr_str, NULL);
727                 else
728                         addr = CONFIG_SYS_LOAD_ADDR;
729         }
730         if (argc >= 5) {
731                 filename = argv[4];
732         } else {
733                 filename = env_get("bootfile");
734                 if (!filename) {
735                         puts("** No boot file defined **\n");
736                         return 1;
737                 }
738         }
739         if (argc >= 6)
740                 bytes = hextoul(argv[5], NULL);
741         else
742                 bytes = 0;
743         if (argc >= 7)
744                 pos = hextoul(argv[6], NULL);
745         else
746                 pos = 0;
747
748         time = get_timer(0);
749         ret = _fs_read(filename, addr, pos, bytes, 1, &len_read);
750         time = get_timer(time);
751         if (ret < 0) {
752                 log_err("Failed to load '%s'\n", filename);
753                 return 1;
754         }
755
756         if (IS_ENABLED(CONFIG_CMD_BOOTEFI))
757                 efi_set_bootdev(argv[1], (argc > 2) ? argv[2] : "",
758                                 (argc > 4) ? argv[4] : "", map_sysmem(addr, 0),
759                                 len_read);
760
761         printf("%llu bytes read in %lu ms", len_read, time);
762         if (time > 0) {
763                 puts(" (");
764                 print_size(div_u64(len_read, time) * 1000, "/s");
765                 puts(")");
766         }
767         puts("\n");
768
769         env_set_hex("fileaddr", addr);
770         env_set_hex("filesize", len_read);
771
772         return 0;
773 }
774
775 int do_ls(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
776           int fstype)
777 {
778         if (argc < 2)
779                 return CMD_RET_USAGE;
780         if (argc > 4)
781                 return CMD_RET_USAGE;
782
783         if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
784                 return 1;
785
786         if (fs_ls(argc >= 4 ? argv[3] : "/"))
787                 return 1;
788
789         return 0;
790 }
791
792 int file_exists(const char *dev_type, const char *dev_part, const char *file,
793                 int fstype)
794 {
795         if (fs_set_blk_dev(dev_type, dev_part, fstype))
796                 return 0;
797
798         return fs_exists(file);
799 }
800
801 int do_save(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
802             int fstype)
803 {
804         unsigned long addr;
805         const char *filename;
806         loff_t bytes;
807         loff_t pos;
808         loff_t len;
809         int ret;
810         unsigned long time;
811
812         if (argc < 6 || argc > 7)
813                 return CMD_RET_USAGE;
814
815         if (fs_set_blk_dev(argv[1], argv[2], fstype))
816                 return 1;
817
818         addr = hextoul(argv[3], NULL);
819         filename = argv[4];
820         bytes = hextoul(argv[5], NULL);
821         if (argc >= 7)
822                 pos = hextoul(argv[6], NULL);
823         else
824                 pos = 0;
825
826         time = get_timer(0);
827         ret = fs_write(filename, addr, pos, bytes, &len);
828         time = get_timer(time);
829         if (ret < 0)
830                 return 1;
831
832         printf("%llu bytes written in %lu ms", len, time);
833         if (time > 0) {
834                 puts(" (");
835                 print_size(div_u64(len, time) * 1000, "/s");
836                 puts(")");
837         }
838         puts("\n");
839
840         return 0;
841 }
842
843 int do_fs_uuid(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
844                int fstype)
845 {
846         int ret;
847         char uuid[37];
848         memset(uuid, 0, sizeof(uuid));
849
850         if (argc < 3 || argc > 4)
851                 return CMD_RET_USAGE;
852
853         if (fs_set_blk_dev(argv[1], argv[2], fstype))
854                 return 1;
855
856         ret = fs_uuid(uuid);
857         if (ret)
858                 return CMD_RET_FAILURE;
859
860         if (argc == 4)
861                 env_set(argv[3], uuid);
862         else
863                 printf("%s\n", uuid);
864
865         return CMD_RET_SUCCESS;
866 }
867
868 int do_fs_type(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
869 {
870         struct fstype_info *info;
871
872         if (argc < 3 || argc > 4)
873                 return CMD_RET_USAGE;
874
875         if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY))
876                 return 1;
877
878         info = fs_get_info(fs_type);
879
880         if (argc == 4)
881                 env_set(argv[3], info->name);
882         else
883                 printf("%s\n", info->name);
884
885         fs_close();
886
887         return CMD_RET_SUCCESS;
888 }
889
890 int do_rm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
891           int fstype)
892 {
893         if (argc != 4)
894                 return CMD_RET_USAGE;
895
896         if (fs_set_blk_dev(argv[1], argv[2], fstype))
897                 return 1;
898
899         if (fs_unlink(argv[3]))
900                 return 1;
901
902         return 0;
903 }
904
905 int do_mkdir(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
906              int fstype)
907 {
908         int ret;
909
910         if (argc != 4)
911                 return CMD_RET_USAGE;
912
913         if (fs_set_blk_dev(argv[1], argv[2], fstype))
914                 return 1;
915
916         ret = fs_mkdir(argv[3]);
917         if (ret) {
918                 log_err("** Unable to create a directory \"%s\" **\n", argv[3]);
919                 return 1;
920         }
921
922         return 0;
923 }
924
925 int do_ln(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
926           int fstype)
927 {
928         if (argc != 5)
929                 return CMD_RET_USAGE;
930
931         if (fs_set_blk_dev(argv[1], argv[2], fstype))
932                 return 1;
933
934         if (fs_ln(argv[3], argv[4]))
935                 return 1;
936
937         return 0;
938 }
939
940 int do_fs_types(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
941 {
942         struct fstype_info *drv = fstypes;
943         const int n_ents = ARRAY_SIZE(fstypes);
944         struct fstype_info *entry;
945         int i = 0;
946
947         puts("Supported filesystems");
948         for (entry = drv; entry != drv + n_ents; entry++) {
949                 if (entry->fstype != FS_TYPE_ANY) {
950                         printf("%c %s", i ? ',' : ':', entry->name);
951                         i++;
952                 }
953         }
954         if (!i)
955                 puts(": <none>");
956         puts("\n");
957         return CMD_RET_SUCCESS;
958 }