]> git.sur5r.net Git - u-boot/blob - fs/fs.c
84349f3039856bae87822a3d9b86551a7bde9b72
[u-boot] / fs / fs.c
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         },
237 #endif
238         {
239                 .fstype = FS_TYPE_ANY,
240                 .name = "unsupported",
241                 .null_dev_desc_ok = true,
242                 .probe = fs_probe_unsupported,
243                 .close = fs_close_unsupported,
244                 .ls = fs_ls_unsupported,
245                 .exists = fs_exists_unsupported,
246                 .size = fs_size_unsupported,
247                 .read = fs_read_unsupported,
248                 .write = fs_write_unsupported,
249                 .uuid = fs_uuid_unsupported,
250                 .opendir = fs_opendir_unsupported,
251         },
252 };
253
254 static struct fstype_info *fs_get_info(int fstype)
255 {
256         struct fstype_info *info;
257         int i;
258
259         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) {
260                 if (fstype == info->fstype)
261                         return info;
262         }
263
264         /* Return the 'unsupported' sentinel */
265         return info;
266 }
267
268 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
269 {
270         struct fstype_info *info;
271         int part, i;
272 #ifdef CONFIG_NEEDS_MANUAL_RELOC
273         static int relocated;
274
275         if (!relocated) {
276                 for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes);
277                                 i++, info++) {
278                         info->name += gd->reloc_off;
279                         info->probe += gd->reloc_off;
280                         info->close += gd->reloc_off;
281                         info->ls += gd->reloc_off;
282                         info->read += gd->reloc_off;
283                         info->write += gd->reloc_off;
284                 }
285                 relocated = 1;
286         }
287 #endif
288
289         part = blk_get_device_part_str(ifname, dev_part_str, &fs_dev_desc,
290                                         &fs_partition, 1);
291         if (part < 0)
292                 return -1;
293
294         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
295                 if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY &&
296                                 fstype != info->fstype)
297                         continue;
298
299                 if (!fs_dev_desc && !info->null_dev_desc_ok)
300                         continue;
301
302                 if (!info->probe(fs_dev_desc, &fs_partition)) {
303                         fs_type = info->fstype;
304                         fs_dev_part = part;
305                         return 0;
306                 }
307         }
308
309         return -1;
310 }
311
312 /* set current blk device w/ blk_desc + partition # */
313 int fs_set_blk_dev_with_part(struct blk_desc *desc, int part)
314 {
315         struct fstype_info *info;
316         int ret, i;
317
318         if (part >= 1)
319                 ret = part_get_info(desc, part, &fs_partition);
320         else
321                 ret = part_get_info_whole_disk(desc, &fs_partition);
322         if (ret)
323                 return ret;
324         fs_dev_desc = desc;
325
326         for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
327                 if (!info->probe(fs_dev_desc, &fs_partition)) {
328                         fs_type = info->fstype;
329                         return 0;
330                 }
331         }
332
333         return -1;
334 }
335
336 static void fs_close(void)
337 {
338         struct fstype_info *info = fs_get_info(fs_type);
339
340         info->close();
341
342         fs_type = FS_TYPE_ANY;
343 }
344
345 int fs_uuid(char *uuid_str)
346 {
347         struct fstype_info *info = fs_get_info(fs_type);
348
349         return info->uuid(uuid_str);
350 }
351
352 int fs_ls(const char *dirname)
353 {
354         int ret;
355
356         struct fstype_info *info = fs_get_info(fs_type);
357
358         ret = info->ls(dirname);
359
360         fs_type = FS_TYPE_ANY;
361         fs_close();
362
363         return ret;
364 }
365
366 int fs_exists(const char *filename)
367 {
368         int ret;
369
370         struct fstype_info *info = fs_get_info(fs_type);
371
372         ret = info->exists(filename);
373
374         fs_close();
375
376         return ret;
377 }
378
379 int fs_size(const char *filename, loff_t *size)
380 {
381         int ret;
382
383         struct fstype_info *info = fs_get_info(fs_type);
384
385         ret = info->size(filename, size);
386
387         fs_close();
388
389         return ret;
390 }
391
392 int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
393             loff_t *actread)
394 {
395         struct fstype_info *info = fs_get_info(fs_type);
396         void *buf;
397         int ret;
398
399         /*
400          * We don't actually know how many bytes are being read, since len==0
401          * means read the whole file.
402          */
403         buf = map_sysmem(addr, len);
404         ret = info->read(filename, buf, offset, len, actread);
405         unmap_sysmem(buf);
406
407         /* If we requested a specific number of bytes, check we got it */
408         if (ret == 0 && len && *actread != len)
409                 printf("** %s shorter than offset + len **\n", filename);
410         fs_close();
411
412         return ret;
413 }
414
415 int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
416              loff_t *actwrite)
417 {
418         struct fstype_info *info = fs_get_info(fs_type);
419         void *buf;
420         int ret;
421
422         buf = map_sysmem(addr, len);
423         ret = info->write(filename, buf, offset, len, actwrite);
424         unmap_sysmem(buf);
425
426         if (ret < 0 && len != *actwrite) {
427                 printf("** Unable to write file %s **\n", filename);
428                 ret = -1;
429         }
430         fs_close();
431
432         return ret;
433 }
434
435 struct fs_dir_stream *fs_opendir(const char *filename)
436 {
437         struct fstype_info *info = fs_get_info(fs_type);
438         struct fs_dir_stream *dirs = NULL;
439         int ret;
440
441         ret = info->opendir(filename, &dirs);
442         fs_close();
443         if (ret) {
444                 errno = -ret;
445                 return NULL;
446         }
447
448         dirs->desc = fs_dev_desc;
449         dirs->part = fs_dev_part;
450
451         return dirs;
452 }
453
454 struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs)
455 {
456         struct fstype_info *info;
457         struct fs_dirent *dirent;
458         int ret;
459
460         fs_set_blk_dev_with_part(dirs->desc, dirs->part);
461         info = fs_get_info(fs_type);
462
463         ret = info->readdir(dirs, &dirent);
464         fs_close();
465         if (ret) {
466                 errno = -ret;
467                 return NULL;
468         }
469
470         return dirent;
471 }
472
473 void fs_closedir(struct fs_dir_stream *dirs)
474 {
475         struct fstype_info *info;
476
477         if (!dirs)
478                 return;
479
480         fs_set_blk_dev_with_part(dirs->desc, dirs->part);
481         info = fs_get_info(fs_type);
482
483         info->closedir(dirs);
484         fs_close();
485 }
486
487
488 int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
489                 int fstype)
490 {
491         loff_t size;
492
493         if (argc != 4)
494                 return CMD_RET_USAGE;
495
496         if (fs_set_blk_dev(argv[1], argv[2], fstype))
497                 return 1;
498
499         if (fs_size(argv[3], &size) < 0)
500                 return CMD_RET_FAILURE;
501
502         env_set_hex("filesize", size);
503
504         return 0;
505 }
506
507 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
508                 int fstype)
509 {
510         unsigned long addr;
511         const char *addr_str;
512         const char *filename;
513         loff_t bytes;
514         loff_t pos;
515         loff_t len_read;
516         int ret;
517         unsigned long time;
518         char *ep;
519
520         if (argc < 2)
521                 return CMD_RET_USAGE;
522         if (argc > 7)
523                 return CMD_RET_USAGE;
524
525         if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
526                 return 1;
527
528         if (argc >= 4) {
529                 addr = simple_strtoul(argv[3], &ep, 16);
530                 if (ep == argv[3] || *ep != '\0')
531                         return CMD_RET_USAGE;
532         } else {
533                 addr_str = env_get("loadaddr");
534                 if (addr_str != NULL)
535                         addr = simple_strtoul(addr_str, NULL, 16);
536                 else
537                         addr = CONFIG_SYS_LOAD_ADDR;
538         }
539         if (argc >= 5) {
540                 filename = argv[4];
541         } else {
542                 filename = env_get("bootfile");
543                 if (!filename) {
544                         puts("** No boot file defined **\n");
545                         return 1;
546                 }
547         }
548         if (argc >= 6)
549                 bytes = simple_strtoul(argv[5], NULL, 16);
550         else
551                 bytes = 0;
552         if (argc >= 7)
553                 pos = simple_strtoul(argv[6], NULL, 16);
554         else
555                 pos = 0;
556
557         time = get_timer(0);
558         ret = fs_read(filename, addr, pos, bytes, &len_read);
559         time = get_timer(time);
560         if (ret < 0)
561                 return 1;
562
563         printf("%llu bytes read in %lu ms", len_read, time);
564         if (time > 0) {
565                 puts(" (");
566                 print_size(div_u64(len_read, time) * 1000, "/s");
567                 puts(")");
568         }
569         puts("\n");
570
571         env_set_hex("fileaddr", addr);
572         env_set_hex("filesize", len_read);
573
574         return 0;
575 }
576
577 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
578         int fstype)
579 {
580         if (argc < 2)
581                 return CMD_RET_USAGE;
582         if (argc > 4)
583                 return CMD_RET_USAGE;
584
585         if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
586                 return 1;
587
588         if (fs_ls(argc >= 4 ? argv[3] : "/"))
589                 return 1;
590
591         return 0;
592 }
593
594 int file_exists(const char *dev_type, const char *dev_part, const char *file,
595                 int fstype)
596 {
597         if (fs_set_blk_dev(dev_type, dev_part, fstype))
598                 return 0;
599
600         return fs_exists(file);
601 }
602
603 int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
604                 int fstype)
605 {
606         unsigned long addr;
607         const char *filename;
608         loff_t bytes;
609         loff_t pos;
610         loff_t len;
611         int ret;
612         unsigned long time;
613
614         if (argc < 6 || argc > 7)
615                 return CMD_RET_USAGE;
616
617         if (fs_set_blk_dev(argv[1], argv[2], fstype))
618                 return 1;
619
620         addr = simple_strtoul(argv[3], NULL, 16);
621         filename = argv[4];
622         bytes = simple_strtoul(argv[5], NULL, 16);
623         if (argc >= 7)
624                 pos = simple_strtoul(argv[6], NULL, 16);
625         else
626                 pos = 0;
627
628         time = get_timer(0);
629         ret = fs_write(filename, addr, pos, bytes, &len);
630         time = get_timer(time);
631         if (ret < 0)
632                 return 1;
633
634         printf("%llu bytes written in %lu ms", len, time);
635         if (time > 0) {
636                 puts(" (");
637                 print_size(div_u64(len, time) * 1000, "/s");
638                 puts(")");
639         }
640         puts("\n");
641
642         return 0;
643 }
644
645 int do_fs_uuid(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
646                 int fstype)
647 {
648         int ret;
649         char uuid[37];
650         memset(uuid, 0, sizeof(uuid));
651
652         if (argc < 3 || argc > 4)
653                 return CMD_RET_USAGE;
654
655         if (fs_set_blk_dev(argv[1], argv[2], fstype))
656                 return 1;
657
658         ret = fs_uuid(uuid);
659         if (ret)
660                 return CMD_RET_FAILURE;
661
662         if (argc == 4)
663                 env_set(argv[3], uuid);
664         else
665                 printf("%s\n", uuid);
666
667         return CMD_RET_SUCCESS;
668 }
669
670 int do_fs_type(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
671 {
672         struct fstype_info *info;
673
674         if (argc < 3 || argc > 4)
675                 return CMD_RET_USAGE;
676
677         if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY))
678                 return 1;
679
680         info = fs_get_info(fs_type);
681
682         if (argc == 4)
683                 env_set(argv[3], info->name);
684         else
685                 printf("%s\n", info->name);
686
687         return CMD_RET_SUCCESS;
688 }
689