]> git.sur5r.net Git - u-boot/blob - fs/fs.c
94cdc37debfb39c0e813129bea976e81ed15d8e6
[u-boot] / fs / fs.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
4  */
5
6 #include <config.h>
7 #include <errno.h>
8 #include <common.h>
9 #include <mapmem.h>
10 #include <part.h>
11 #include <ext4fs.h>
12 #include <fat.h>
13 #include <fs.h>
14 #include <sandboxfs.h>
15 #include <ubifs_uboot.h>
16 #include <btrfs.h>
17 #include <asm/io.h>
18 #include <div64.h>
19 #include <linux/math64.h>
20
21 DECLARE_GLOBAL_DATA_PTR;
22
23 static struct blk_desc *fs_dev_desc;
24 static int fs_dev_part;
25 static disk_partition_t fs_partition;
26 static int fs_type = FS_TYPE_ANY;
27
28 static inline int fs_probe_unsupported(struct blk_desc *fs_dev_desc,
29                                       disk_partition_t *fs_partition)
30 {
31         printf("** Unrecognized filesystem type **\n");
32         return -1;
33 }
34
35 static inline int fs_ls_unsupported(const char *dirname)
36 {
37         return -1;
38 }
39
40 /* generic implementation of ls in terms of opendir/readdir/closedir */
41 __maybe_unused
42 static int fs_ls_generic(const char *dirname)
43 {
44         struct fs_dir_stream *dirs;
45         struct fs_dirent *dent;
46         int nfiles = 0, ndirs = 0;
47
48         dirs = fs_opendir(dirname);
49         if (!dirs)
50                 return -errno;
51
52         while ((dent = fs_readdir(dirs))) {
53                 if (dent->type == FS_DT_DIR) {
54                         printf("            %s/\n", dent->name);
55                         ndirs++;
56                 } else {
57                         printf(" %8lld   %s\n", dent->size, dent->name);
58                         nfiles++;
59                 }
60         }
61
62         fs_closedir(dirs);
63
64         printf("\n%d file(s), %d dir(s)\n\n", nfiles, ndirs);
65
66         return 0;
67 }
68
69 static inline int fs_exists_unsupported(const char *filename)
70 {
71         return 0;
72 }
73
74 static inline int fs_size_unsupported(const char *filename, loff_t *size)
75 {
76         return -1;
77 }
78
79 static inline int fs_read_unsupported(const char *filename, void *buf,
80                                       loff_t offset, loff_t len,
81                                       loff_t *actread)
82 {
83         return -1;
84 }
85
86 static inline int fs_write_unsupported(const char *filename, void *buf,
87                                       loff_t offset, loff_t len,
88                                       loff_t *actwrite)
89 {
90         return -1;
91 }
92
93 static inline void fs_close_unsupported(void)
94 {
95 }
96
97 static inline int fs_uuid_unsupported(char *uuid_str)
98 {
99         return -1;
100 }
101
102 static inline int fs_opendir_unsupported(const char *filename,
103                                          struct fs_dir_stream **dirs)
104 {
105         return -EACCES;
106 }
107
108 struct fstype_info {
109         int fstype;
110         char *name;
111         /*
112          * Is it legal to pass NULL as .probe()'s  fs_dev_desc parameter? This
113          * should be false in most cases. For "virtual" filesystems which
114          * aren't based on a U-Boot block device (e.g. sandbox), this can be
115          * set to true. This should also be true for the dumm entry at the end
116          * of fstypes[], since that is essentially a "virtual" (non-existent)
117          * filesystem.
118          */
119         bool null_dev_desc_ok;
120         int (*probe)(struct blk_desc *fs_dev_desc,
121                      disk_partition_t *fs_partition);
122         int (*ls)(const char *dirname);
123         int (*exists)(const char *filename);
124         int (*size)(const char *filename, loff_t *size);
125         int (*read)(const char *filename, void *buf, loff_t offset,
126                     loff_t len, loff_t *actread);
127         int (*write)(const char *filename, void *buf, loff_t offset,
128                      loff_t len, loff_t *actwrite);
129         void (*close)(void);
130         int (*uuid)(char *uuid_str);
131         /*
132          * Open a directory stream.  On success return 0 and directory
133          * stream pointer via 'dirsp'.  On error, return -errno.  See
134          * fs_opendir().
135          */
136         int (*opendir)(const char *filename, struct fs_dir_stream **dirsp);
137         /*
138          * Read next entry from directory stream.  On success return 0
139          * and directory entry pointer via 'dentp'.  On error return
140          * -errno.  See fs_readdir().
141          */
142         int (*readdir)(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
143         /* see fs_closedir() */
144         void (*closedir)(struct fs_dir_stream *dirs);
145 };
146
147 static struct fstype_info fstypes[] = {
148 #ifdef CONFIG_FS_FAT
149         {
150                 .fstype = FS_TYPE_FAT,
151                 .name = "fat",
152                 .null_dev_desc_ok = false,
153                 .probe = fat_set_blk_dev,
154                 .close = fat_close,
155                 .ls = fs_ls_generic,
156                 .exists = fat_exists,
157                 .size = fat_size,
158                 .read = fat_read_file,
159 #ifdef CONFIG_FAT_WRITE
160                 .write = file_fat_write,
161 #else
162                 .write = fs_write_unsupported,
163 #endif
164                 .uuid = fs_uuid_unsupported,
165                 .opendir = fat_opendir,
166                 .readdir = fat_readdir,
167                 .closedir = fat_closedir,
168         },
169 #endif
170 #ifdef CONFIG_FS_EXT4
171         {
172                 .fstype = FS_TYPE_EXT,
173                 .name = "ext4",
174                 .null_dev_desc_ok = false,
175                 .probe = ext4fs_probe,
176                 .close = ext4fs_close,
177                 .ls = ext4fs_ls,
178                 .exists = ext4fs_exists,
179                 .size = ext4fs_size,
180                 .read = ext4_read_file,
181 #ifdef CONFIG_CMD_EXT4_WRITE
182                 .write = ext4_write_file,
183 #else
184                 .write = fs_write_unsupported,
185 #endif
186                 .uuid = ext4fs_uuid,
187                 .opendir = fs_opendir_unsupported,
188         },
189 #endif
190 #ifdef CONFIG_SANDBOX
191         {
192                 .fstype = FS_TYPE_SANDBOX,
193                 .name = "sandbox",
194                 .null_dev_desc_ok = true,
195                 .probe = sandbox_fs_set_blk_dev,
196                 .close = sandbox_fs_close,
197                 .ls = sandbox_fs_ls,
198                 .exists = sandbox_fs_exists,
199                 .size = sandbox_fs_size,
200                 .read = fs_read_sandbox,
201                 .write = fs_write_sandbox,
202                 .uuid = fs_uuid_unsupported,
203                 .opendir = fs_opendir_unsupported,
204         },
205 #endif
206 #ifdef CONFIG_CMD_UBIFS
207         {
208                 .fstype = FS_TYPE_UBIFS,
209                 .name = "ubifs",
210                 .null_dev_desc_ok = true,
211                 .probe = ubifs_set_blk_dev,
212                 .close = ubifs_close,
213                 .ls = ubifs_ls,
214                 .exists = ubifs_exists,
215                 .size = ubifs_size,
216                 .read = ubifs_read,
217                 .write = fs_write_unsupported,
218                 .uuid = fs_uuid_unsupported,
219                 .opendir = fs_opendir_unsupported,
220         },
221 #endif
222 #ifdef CONFIG_FS_BTRFS
223         {
224                 .fstype = FS_TYPE_BTRFS,
225                 .name = "btrfs",
226                 .null_dev_desc_ok = false,
227                 .probe = btrfs_probe,
228                 .close = btrfs_close,
229                 .ls = btrfs_ls,
230                 .exists = btrfs_exists,
231                 .size = btrfs_size,
232                 .read = btrfs_read,
233                 .write = fs_write_unsupported,
234                 .uuid = btrfs_uuid,
235                 .opendir = fs_opendir_unsupported,
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                 debug("** %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