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