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