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