uboot/fs/fat/fat.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * fat.c
   4 *
   5 * R/O (V)FAT 12/16/32 filesystem implementation by Marcus Sundberg
   6 *
   7 * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
   8 * 2003-03-10 - kharris@nexus-tech.net - ported to uboot
   9 */
  10
  11#include <common.h>
  12#include <blk.h>
  13#include <config.h>
  14#include <exports.h>
  15#include <fat.h>
  16#include <fs.h>
  17#include <asm/byteorder.h>
  18#include <part.h>
  19#include <malloc.h>
  20#include <memalign.h>
  21#include <linux/compiler.h>
  22#include <linux/ctype.h>
  23
  24/*
  25 * Convert a string to lowercase.  Converts at most 'len' characters,
  26 * 'len' may be larger than the length of 'str' if 'str' is NULL
  27 * terminated.
  28 */
  29static void downcase(char *str, size_t len)
  30{
  31        while (*str != '\0' && len--) {
  32                *str = tolower(*str);
  33                str++;
  34        }
  35}
  36
  37static struct blk_desc *cur_dev;
  38static disk_partition_t cur_part_info;
  39
  40#define DOS_BOOT_MAGIC_OFFSET   0x1fe
  41#define DOS_FS_TYPE_OFFSET      0x36
  42#define DOS_FS32_TYPE_OFFSET    0x52
  43
  44static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
  45{
  46        ulong ret;
  47
  48        if (!cur_dev)
  49                return -1;
  50
  51        ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf);
  52
  53        if (ret != nr_blocks)
  54                return -1;
  55
  56        return ret;
  57}
  58
  59int fat_set_blk_dev(struct blk_desc *dev_desc, disk_partition_t *info)
  60{
  61        ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
  62
  63        cur_dev = dev_desc;
  64        cur_part_info = *info;
  65
  66        /* Make sure it has a valid FAT header */
  67        if (disk_read(0, 1, buffer) != 1) {
  68                cur_dev = NULL;
  69                return -1;
  70        }
  71
  72        /* Check if it's actually a DOS volume */
  73        if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) {
  74                cur_dev = NULL;
  75                return -1;
  76        }
  77
  78        /* Check for FAT12/FAT16/FAT32 filesystem */
  79        if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3))
  80                return 0;
  81        if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5))
  82                return 0;
  83
  84        cur_dev = NULL;
  85        return -1;
  86}
  87
  88int fat_register_device(struct blk_desc *dev_desc, int part_no)
  89{
  90        disk_partition_t info;
  91
  92        /* First close any currently found FAT filesystem */
  93        cur_dev = NULL;
  94
  95        /* Read the partition table, if present */
  96        if (part_get_info(dev_desc, part_no, &info)) {
  97                if (part_no != 0) {
  98                        printf("** Partition %d not valid on device %d **\n",
  99                                        part_no, dev_desc->devnum);
 100                        return -1;
 101                }
 102
 103                info.start = 0;
 104                info.size = dev_desc->lba;
 105                info.blksz = dev_desc->blksz;
 106                info.name[0] = 0;
 107                info.type[0] = 0;
 108                info.bootable = 0;
 109#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
 110                info.uuid[0] = 0;
 111#endif
 112        }
 113
 114        return fat_set_blk_dev(dev_desc, &info);
 115}
 116
 117/*
 118 * Extract zero terminated short name from a directory entry.
 119 */
 120static void get_name(dir_entry *dirent, char *s_name)
 121{
 122        char *ptr;
 123
 124        memcpy(s_name, dirent->name, 8);
 125        s_name[8] = '\0';
 126        ptr = s_name;
 127        while (*ptr && *ptr != ' ')
 128                ptr++;
 129        if (dirent->lcase & CASE_LOWER_BASE)
 130                downcase(s_name, (unsigned)(ptr - s_name));
 131        if (dirent->ext[0] && dirent->ext[0] != ' ') {
 132                *ptr++ = '.';
 133                memcpy(ptr, dirent->ext, 3);
 134                if (dirent->lcase & CASE_LOWER_EXT)
 135                        downcase(ptr, 3);
 136                ptr[3] = '\0';
 137                while (*ptr && *ptr != ' ')
 138                        ptr++;
 139        }
 140        *ptr = '\0';
 141        if (*s_name == DELETED_FLAG)
 142                *s_name = '\0';
 143        else if (*s_name == aRING)
 144                *s_name = DELETED_FLAG;
 145}
 146
 147static int flush_dirty_fat_buffer(fsdata *mydata);
 148
 149#if !CONFIG_IS_ENABLED(FAT_WRITE)
 150/* Stub for read only operation */
 151int flush_dirty_fat_buffer(fsdata *mydata)
 152{
 153        (void)(mydata);
 154        return 0;
 155}
 156#endif
 157
 158/*
 159 * Get the entry at index 'entry' in a FAT (12/16/32) table.
 160 * On failure 0x00 is returned.
 161 */
 162static __u32 get_fatent(fsdata *mydata, __u32 entry)
 163{
 164        __u32 bufnum;
 165        __u32 offset, off8;
 166        __u32 ret = 0x00;
 167
 168        if (CHECK_CLUST(entry, mydata->fatsize)) {
 169                printf("Error: Invalid FAT entry: 0x%08x\n", entry);
 170                return ret;
 171        }
 172
 173        switch (mydata->fatsize) {
 174        case 32:
 175                bufnum = entry / FAT32BUFSIZE;
 176                offset = entry - bufnum * FAT32BUFSIZE;
 177                break;
 178        case 16:
 179                bufnum = entry / FAT16BUFSIZE;
 180                offset = entry - bufnum * FAT16BUFSIZE;
 181                break;
 182        case 12:
 183                bufnum = entry / FAT12BUFSIZE;
 184                offset = entry - bufnum * FAT12BUFSIZE;
 185                break;
 186
 187        default:
 188                /* Unsupported FAT size */
 189                return ret;
 190        }
 191
 192        debug("FAT%d: entry: 0x%08x = %d, offset: 0x%04x = %d\n",
 193               mydata->fatsize, entry, entry, offset, offset);
 194
 195        /* Read a new block of FAT entries into the cache. */
 196        if (bufnum != mydata->fatbufnum) {
 197                __u32 getsize = FATBUFBLOCKS;
 198                __u8 *bufptr = mydata->fatbuf;
 199                __u32 fatlength = mydata->fatlength;
 200                __u32 startblock = bufnum * FATBUFBLOCKS;
 201
 202                /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
 203                if (startblock + getsize > fatlength)
 204                        getsize = fatlength - startblock;
 205
 206                startblock += mydata->fat_sect; /* Offset from start of disk */
 207
 208                /* Write back the fatbuf to the disk */
 209                if (flush_dirty_fat_buffer(mydata) < 0)
 210                        return -1;
 211
 212                if (disk_read(startblock, getsize, bufptr) < 0) {
 213                        debug("Error reading FAT blocks\n");
 214                        return ret;
 215                }
 216                mydata->fatbufnum = bufnum;
 217        }
 218
 219        /* Get the actual entry from the table */
 220        switch (mydata->fatsize) {
 221        case 32:
 222                ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
 223                break;
 224        case 16:
 225                ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
 226                break;
 227        case 12:
 228                off8 = (offset * 3) / 2;
 229                /* fatbut + off8 may be unaligned, read in byte granularity */
 230                ret = mydata->fatbuf[off8] + (mydata->fatbuf[off8 + 1] << 8);
 231
 232                if (offset & 0x1)
 233                        ret >>= 4;
 234                ret &= 0xfff;
 235        }
 236        debug("FAT%d: ret: 0x%08x, entry: 0x%08x, offset: 0x%04x\n",
 237               mydata->fatsize, ret, entry, offset);
 238
 239        return ret;
 240}
 241
 242/*
 243 * Read at most 'size' bytes from the specified cluster into 'buffer'.
 244 * Return 0 on success, -1 otherwise.
 245 */
 246static int
 247get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
 248{
 249        __u32 idx = 0;
 250        __u32 startsect;
 251        int ret;
 252
 253        if (clustnum > 0) {
 254                startsect = clust_to_sect(mydata, clustnum);
 255        } else {
 256                startsect = mydata->rootdir_sect;
 257        }
 258
 259        debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
 260
 261        if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
 262                ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 263
 264                debug("FAT: Misaligned buffer address (%p)\n", buffer);
 265
 266                while (size >= mydata->sect_size) {
 267                        ret = disk_read(startsect++, 1, tmpbuf);
 268                        if (ret != 1) {
 269                                debug("Error reading data (got %d)\n", ret);
 270                                return -1;
 271                        }
 272
 273                        memcpy(buffer, tmpbuf, mydata->sect_size);
 274                        buffer += mydata->sect_size;
 275                        size -= mydata->sect_size;
 276                }
 277        } else {
 278                idx = size / mydata->sect_size;
 279                ret = disk_read(startsect, idx, buffer);
 280                if (ret != idx) {
 281                        debug("Error reading data (got %d)\n", ret);
 282                        return -1;
 283                }
 284                startsect += idx;
 285                idx *= mydata->sect_size;
 286                buffer += idx;
 287                size -= idx;
 288        }
 289        if (size) {
 290                ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 291
 292                ret = disk_read(startsect, 1, tmpbuf);
 293                if (ret != 1) {
 294                        debug("Error reading data (got %d)\n", ret);
 295                        return -1;
 296                }
 297
 298                memcpy(buffer, tmpbuf, size);
 299        }
 300
 301        return 0;
 302}
 303
 304/**
 305 * get_contents() - read from file
 306 *
 307 * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr'
 308 * into 'buffer'. Update the number of bytes read in *gotsize or return -1 on
 309 * fatal errors.
 310 *
 311 * @mydata:     file system description
 312 * @dentprt:    directory entry pointer
 313 * @pos:        position from where to read
 314 * @buffer:     buffer into which to read
 315 * @maxsize:    maximum number of bytes to read
 316 * @gotsize:    number of bytes actually read
 317 * Return:      -1 on error, otherwise 0
 318 */
 319static int get_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos,
 320                        __u8 *buffer, loff_t maxsize, loff_t *gotsize)
 321{
 322        loff_t filesize = FAT2CPU32(dentptr->size);
 323        unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
 324        __u32 curclust = START(dentptr);
 325        __u32 endclust, newclust;
 326        loff_t actsize;
 327
 328        *gotsize = 0;
 329        debug("Filesize: %llu bytes\n", filesize);
 330
 331        if (pos >= filesize) {
 332                debug("Read position past EOF: %llu\n", pos);
 333                return 0;
 334        }
 335
 336        if (maxsize > 0 && filesize > pos + maxsize)
 337                filesize = pos + maxsize;
 338
 339        debug("%llu bytes\n", filesize);
 340
 341        actsize = bytesperclust;
 342
 343        /* go to cluster at pos */
 344        while (actsize <= pos) {
 345                curclust = get_fatent(mydata, curclust);
 346                if (CHECK_CLUST(curclust, mydata->fatsize)) {
 347                        debug("curclust: 0x%x\n", curclust);
 348                        printf("Invalid FAT entry\n");
 349                        return -1;
 350                }
 351                actsize += bytesperclust;
 352        }
 353
 354        /* actsize > pos */
 355        actsize -= bytesperclust;
 356        filesize -= actsize;
 357        pos -= actsize;
 358
 359        /* align to beginning of next cluster if any */
 360        if (pos) {
 361                __u8 *tmp_buffer;
 362
 363                actsize = min(filesize, (loff_t)bytesperclust);
 364                tmp_buffer = malloc_cache_aligned(actsize);
 365                if (!tmp_buffer) {
 366                        debug("Error: allocating buffer\n");
 367                        return -1;
 368                }
 369
 370                if (get_cluster(mydata, curclust, tmp_buffer, actsize) != 0) {
 371                        printf("Error reading cluster\n");
 372                        free(tmp_buffer);
 373                        return -1;
 374                }
 375                filesize -= actsize;
 376                actsize -= pos;
 377                memcpy(buffer, tmp_buffer + pos, actsize);
 378                free(tmp_buffer);
 379                *gotsize += actsize;
 380                if (!filesize)
 381                        return 0;
 382                buffer += actsize;
 383
 384                curclust = get_fatent(mydata, curclust);
 385                if (CHECK_CLUST(curclust, mydata->fatsize)) {
 386                        debug("curclust: 0x%x\n", curclust);
 387                        printf("Invalid FAT entry\n");
 388                        return -1;
 389                }
 390        }
 391
 392        actsize = bytesperclust;
 393        endclust = curclust;
 394
 395        do {
 396                /* search for consecutive clusters */
 397                while (actsize < filesize) {
 398                        newclust = get_fatent(mydata, endclust);
 399                        if ((newclust - 1) != endclust)
 400                                goto getit;
 401                        if (CHECK_CLUST(newclust, mydata->fatsize)) {
 402                                debug("curclust: 0x%x\n", newclust);
 403                                printf("Invalid FAT entry\n");
 404                                return -1;
 405                        }
 406                        endclust = newclust;
 407                        actsize += bytesperclust;
 408                }
 409
 410                /* get remaining bytes */
 411                actsize = filesize;
 412                if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
 413                        printf("Error reading cluster\n");
 414                        return -1;
 415                }
 416                *gotsize += actsize;
 417                return 0;
 418getit:
 419                if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
 420                        printf("Error reading cluster\n");
 421                        return -1;
 422                }
 423                *gotsize += (int)actsize;
 424                filesize -= actsize;
 425                buffer += actsize;
 426
 427                curclust = get_fatent(mydata, endclust);
 428                if (CHECK_CLUST(curclust, mydata->fatsize)) {
 429                        debug("curclust: 0x%x\n", curclust);
 430                        printf("Invalid FAT entry\n");
 431                        return -1;
 432                }
 433                actsize = bytesperclust;
 434                endclust = curclust;
 435        } while (1);
 436}
 437
 438/*
 439 * Extract the file name information from 'slotptr' into 'l_name',
 440 * starting at l_name[*idx].
 441 * Return 1 if terminator (zero byte) is found, 0 otherwise.
 442 */
 443static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
 444{
 445        int j;
 446
 447        for (j = 0; j <= 8; j += 2) {
 448                l_name[*idx] = slotptr->name0_4[j];
 449                if (l_name[*idx] == 0x00)
 450                        return 1;
 451                (*idx)++;
 452        }
 453        for (j = 0; j <= 10; j += 2) {
 454                l_name[*idx] = slotptr->name5_10[j];
 455                if (l_name[*idx] == 0x00)
 456                        return 1;
 457                (*idx)++;
 458        }
 459        for (j = 0; j <= 2; j += 2) {
 460                l_name[*idx] = slotptr->name11_12[j];
 461                if (l_name[*idx] == 0x00)
 462                        return 1;
 463                (*idx)++;
 464        }
 465
 466        return 0;
 467}
 468
 469/* Calculate short name checksum */
 470static __u8 mkcksum(const char name[8], const char ext[3])
 471{
 472        int i;
 473
 474        __u8 ret = 0;
 475
 476        for (i = 0; i < 8; i++)
 477                ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + name[i];
 478        for (i = 0; i < 3; i++)
 479                ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + ext[i];
 480
 481        return ret;
 482}
 483
 484/*
 485 * Read boot sector and volume info from a FAT filesystem
 486 */
 487static int
 488read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
 489{
 490        __u8 *block;
 491        volume_info *vistart;
 492        int ret = 0;
 493
 494        if (cur_dev == NULL) {
 495                debug("Error: no device selected\n");
 496                return -1;
 497        }
 498
 499        block = malloc_cache_aligned(cur_dev->blksz);
 500        if (block == NULL) {
 501                debug("Error: allocating block\n");
 502                return -1;
 503        }
 504
 505        if (disk_read(0, 1, block) < 0) {
 506                debug("Error: reading block\n");
 507                goto fail;
 508        }
 509
 510        memcpy(bs, block, sizeof(boot_sector));
 511        bs->reserved = FAT2CPU16(bs->reserved);
 512        bs->fat_length = FAT2CPU16(bs->fat_length);
 513        bs->secs_track = FAT2CPU16(bs->secs_track);
 514        bs->heads = FAT2CPU16(bs->heads);
 515        bs->total_sect = FAT2CPU32(bs->total_sect);
 516
 517        /* FAT32 entries */
 518        if (bs->fat_length == 0) {
 519                /* Assume FAT32 */
 520                bs->fat32_length = FAT2CPU32(bs->fat32_length);
 521                bs->flags = FAT2CPU16(bs->flags);
 522                bs->root_cluster = FAT2CPU32(bs->root_cluster);
 523                bs->info_sector = FAT2CPU16(bs->info_sector);
 524                bs->backup_boot = FAT2CPU16(bs->backup_boot);
 525                vistart = (volume_info *)(block + sizeof(boot_sector));
 526                *fatsize = 32;
 527        } else {
 528                vistart = (volume_info *)&(bs->fat32_length);
 529                *fatsize = 0;
 530        }
 531        memcpy(volinfo, vistart, sizeof(volume_info));
 532
 533        if (*fatsize == 32) {
 534                if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
 535                        goto exit;
 536        } else {
 537                if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
 538                        *fatsize = 12;
 539                        goto exit;
 540                }
 541                if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
 542                        *fatsize = 16;
 543                        goto exit;
 544                }
 545        }
 546
 547        debug("Error: broken fs_type sign\n");
 548fail:
 549        ret = -1;
 550exit:
 551        free(block);
 552        return ret;
 553}
 554
 555static int get_fs_info(fsdata *mydata)
 556{
 557        boot_sector bs;
 558        volume_info volinfo;
 559        int ret;
 560
 561        ret = read_bootsectandvi(&bs, &volinfo, &mydata->fatsize);
 562        if (ret) {
 563                debug("Error: reading boot sector\n");
 564                return ret;
 565        }
 566
 567        if (mydata->fatsize == 32) {
 568                mydata->fatlength = bs.fat32_length;
 569                mydata->total_sect = bs.total_sect;
 570        } else {
 571                mydata->fatlength = bs.fat_length;
 572                mydata->total_sect = (bs.sectors[1] << 8) + bs.sectors[0];
 573                if (!mydata->total_sect)
 574                        mydata->total_sect = bs.total_sect;
 575        }
 576        if (!mydata->total_sect) /* unlikely */
 577                mydata->total_sect = (u32)cur_part_info.size;
 578
 579        mydata->fats = bs.fats;
 580        mydata->fat_sect = bs.reserved;
 581
 582        mydata->rootdir_sect = mydata->fat_sect + mydata->fatlength * bs.fats;
 583
 584        mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
 585        mydata->clust_size = bs.cluster_size;
 586        if (mydata->sect_size != cur_part_info.blksz) {
 587                printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
 588                                mydata->sect_size, cur_part_info.blksz);
 589                return -1;
 590        }
 591        if (mydata->clust_size == 0) {
 592                printf("Error: FAT cluster size not set\n");
 593                return -1;
 594        }
 595        if ((unsigned int)mydata->clust_size * mydata->sect_size >
 596            MAX_CLUSTSIZE) {
 597                printf("Error: FAT cluster size too big (cs=%u, max=%u)\n",
 598                       (unsigned int)mydata->clust_size * mydata->sect_size,
 599                       MAX_CLUSTSIZE);
 600                return -1;
 601        }
 602
 603        if (mydata->fatsize == 32) {
 604                mydata->data_begin = mydata->rootdir_sect -
 605                                        (mydata->clust_size * 2);
 606                mydata->root_cluster = bs.root_cluster;
 607        } else {
 608                mydata->rootdir_size = ((bs.dir_entries[1]  * (int)256 +
 609                                         bs.dir_entries[0]) *
 610                                         sizeof(dir_entry)) /
 611                                         mydata->sect_size;
 612                mydata->data_begin = mydata->rootdir_sect +
 613                                        mydata->rootdir_size -
 614                                        (mydata->clust_size * 2);
 615
 616                /*
 617                 * The root directory is not cluster-aligned and may be on a
 618                 * "negative" cluster, this will be handled specially in
 619                 * next_cluster().
 620                 */
 621                mydata->root_cluster = 0;
 622        }
 623
 624        mydata->fatbufnum = -1;
 625        mydata->fat_dirty = 0;
 626        mydata->fatbuf = malloc_cache_aligned(FATBUFSIZE);
 627        if (mydata->fatbuf == NULL) {
 628                debug("Error: allocating memory\n");
 629                return -1;
 630        }
 631
 632        debug("FAT%d, fat_sect: %d, fatlength: %d\n",
 633               mydata->fatsize, mydata->fat_sect, mydata->fatlength);
 634        debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
 635               "Data begins at: %d\n",
 636               mydata->root_cluster,
 637               mydata->rootdir_sect,
 638               mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
 639        debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
 640              mydata->clust_size);
 641
 642        return 0;
 643}
 644
 645
 646/*
 647 * Directory iterator, to simplify filesystem traversal
 648 *
 649 * Implements an iterator pattern to traverse directory tables,
 650 * transparently handling directory tables split across multiple
 651 * clusters, and the difference between FAT12/FAT16 root directory
 652 * (contiguous) and subdirectories + FAT32 root (chained).
 653 *
 654 * Rough usage:
 655 *
 656 *   for (fat_itr_root(&itr, fsdata); fat_itr_next(&itr); ) {
 657 *      // to traverse down to a subdirectory pointed to by
 658 *      // current iterator position:
 659 *      fat_itr_child(&itr, &itr);
 660 *   }
 661 *
 662 * For more complete example, see fat_itr_resolve()
 663 */
 664
 665typedef struct {
 666        fsdata    *fsdata;        /* filesystem parameters */
 667        unsigned   start_clust;   /* first cluster */
 668        unsigned   clust;         /* current cluster */
 669        unsigned   next_clust;    /* next cluster if remaining == 0 */
 670        int        last_cluster;  /* set once we've read last cluster */
 671        int        is_root;       /* is iterator at root directory */
 672        int        remaining;     /* remaining dent's in current cluster */
 673
 674        /* current iterator position values: */
 675        dir_entry *dent;          /* current directory entry */
 676        char       l_name[VFAT_MAXLEN_BYTES];    /* long (vfat) name */
 677        char       s_name[14];    /* short 8.3 name */
 678        char      *name;          /* l_name if there is one, else s_name */
 679
 680        /* storage for current cluster in memory: */
 681        u8         block[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN);
 682} fat_itr;
 683
 684static int fat_itr_isdir(fat_itr *itr);
 685
 686/**
 687 * fat_itr_root() - initialize an iterator to start at the root
 688 * directory
 689 *
 690 * @itr: iterator to initialize
 691 * @fsdata: filesystem data for the partition
 692 * @return 0 on success, else -errno
 693 */
 694static int fat_itr_root(fat_itr *itr, fsdata *fsdata)
 695{
 696        if (get_fs_info(fsdata))
 697                return -ENXIO;
 698
 699        itr->fsdata = fsdata;
 700        itr->start_clust = 0;
 701        itr->clust = fsdata->root_cluster;
 702        itr->next_clust = fsdata->root_cluster;
 703        itr->dent = NULL;
 704        itr->remaining = 0;
 705        itr->last_cluster = 0;
 706        itr->is_root = 1;
 707
 708        return 0;
 709}
 710
 711/**
 712 * fat_itr_child() - initialize an iterator to descend into a sub-
 713 * directory
 714 *
 715 * Initializes 'itr' to iterate the contents of the directory at
 716 * the current cursor position of 'parent'.  It is an error to
 717 * call this if the current cursor of 'parent' is pointing at a
 718 * regular file.
 719 *
 720 * Note that 'itr' and 'parent' can be the same pointer if you do
 721 * not need to preserve 'parent' after this call, which is useful
 722 * for traversing directory structure to resolve a file/directory.
 723 *
 724 * @itr: iterator to initialize
 725 * @parent: the iterator pointing at a directory entry in the
 726 *    parent directory of the directory to iterate
 727 */
 728static void fat_itr_child(fat_itr *itr, fat_itr *parent)
 729{
 730        fsdata *mydata = parent->fsdata;  /* for silly macros */
 731        unsigned clustnum = START(parent->dent);
 732
 733        assert(fat_itr_isdir(parent));
 734
 735        itr->fsdata = parent->fsdata;
 736        itr->start_clust = clustnum;
 737        if (clustnum > 0) {
 738                itr->clust = clustnum;
 739                itr->next_clust = clustnum;
 740                itr->is_root = 0;
 741        } else {
 742                itr->clust = parent->fsdata->root_cluster;
 743                itr->next_clust = parent->fsdata->root_cluster;
 744                itr->is_root = 1;
 745        }
 746        itr->dent = NULL;
 747        itr->remaining = 0;
 748        itr->last_cluster = 0;
 749}
 750
 751static void *next_cluster(fat_itr *itr, unsigned *nbytes)
 752{
 753        fsdata *mydata = itr->fsdata;  /* for silly macros */
 754        int ret;
 755        u32 sect;
 756        u32 read_size;
 757
 758        /* have we reached the end? */
 759        if (itr->last_cluster)
 760                return NULL;
 761
 762        if (itr->is_root && itr->fsdata->fatsize != 32) {
 763                /*
 764                 * The root directory is located before the data area and
 765                 * cannot be indexed using the regular unsigned cluster
 766                 * numbers (it may start at a "negative" cluster or not at a
 767                 * cluster boundary at all), so consider itr->next_clust to be
 768                 * a offset in cluster-sized units from the start of rootdir.
 769                 */
 770                unsigned sect_offset = itr->next_clust * itr->fsdata->clust_size;
 771                unsigned remaining_sects = itr->fsdata->rootdir_size - sect_offset;
 772                sect = itr->fsdata->rootdir_sect + sect_offset;
 773                /* do not read past the end of rootdir */
 774                read_size = min_t(u32, itr->fsdata->clust_size,
 775                                  remaining_sects);
 776        } else {
 777                sect = clust_to_sect(itr->fsdata, itr->next_clust);
 778                read_size = itr->fsdata->clust_size;
 779        }
 780
 781        debug("FAT read(sect=%d), clust_size=%d, read_size=%u, DIRENTSPERBLOCK=%zd\n",
 782              sect, itr->fsdata->clust_size, read_size, DIRENTSPERBLOCK);
 783
 784        /*
 785         * NOTE: do_fat_read_at() had complicated logic to deal w/
 786         * vfat names that span multiple clusters in the fat16 case,
 787         * which get_dentfromdir() probably also needed (and was
 788         * missing).  And not entirely sure what fat32 didn't have
 789         * the same issue..  We solve that by only caring about one
 790         * dent at a time and iteratively constructing the vfat long
 791         * name.
 792         */
 793        ret = disk_read(sect, read_size, itr->block);
 794        if (ret < 0) {
 795                debug("Error: reading block\n");
 796                return NULL;
 797        }
 798
 799        *nbytes = read_size * itr->fsdata->sect_size;
 800        itr->clust = itr->next_clust;
 801        if (itr->is_root && itr->fsdata->fatsize != 32) {
 802                itr->next_clust++;
 803                if (itr->next_clust * itr->fsdata->clust_size >=
 804                    itr->fsdata->rootdir_size) {
 805                        debug("nextclust: 0x%x\n", itr->next_clust);
 806                        itr->last_cluster = 1;
 807                }
 808        } else {
 809                itr->next_clust = get_fatent(itr->fsdata, itr->next_clust);
 810                if (CHECK_CLUST(itr->next_clust, itr->fsdata->fatsize)) {
 811                        debug("nextclust: 0x%x\n", itr->next_clust);
 812                        itr->last_cluster = 1;
 813                }
 814        }
 815
 816        return itr->block;
 817}
 818
 819static dir_entry *next_dent(fat_itr *itr)
 820{
 821        if (itr->remaining == 0) {
 822                unsigned nbytes;
 823                struct dir_entry *dent = next_cluster(itr, &nbytes);
 824
 825                /* have we reached the last cluster? */
 826                if (!dent) {
 827                        /* a sign for no more entries left */
 828                        itr->dent = NULL;
 829                        return NULL;
 830                }
 831
 832                itr->remaining = nbytes / sizeof(dir_entry) - 1;
 833                itr->dent = dent;
 834        } else {
 835                itr->remaining--;
 836                itr->dent++;
 837        }
 838
 839        /* have we reached the last valid entry? */
 840        if (itr->dent->name[0] == 0)
 841                return NULL;
 842
 843        return itr->dent;
 844}
 845
 846static dir_entry *extract_vfat_name(fat_itr *itr)
 847{
 848        struct dir_entry *dent = itr->dent;
 849        int seqn = itr->dent->name[0] & ~LAST_LONG_ENTRY_MASK;
 850        u8 chksum, alias_checksum = ((dir_slot *)dent)->alias_checksum;
 851        int n = 0;
 852
 853        while (seqn--) {
 854                char buf[13];
 855                int idx = 0;
 856
 857                slot2str((dir_slot *)dent, buf, &idx);
 858
 859                if (n + idx >= sizeof(itr->l_name))
 860                        return NULL;
 861
 862                /* shift accumulated long-name up and copy new part in: */
 863                memmove(itr->l_name + idx, itr->l_name, n);
 864                memcpy(itr->l_name, buf, idx);
 865                n += idx;
 866
 867                dent = next_dent(itr);
 868                if (!dent)
 869                        return NULL;
 870        }
 871
 872        /*
 873         * We are now at the short file name entry.
 874         * If it is marked as deleted, just skip it.
 875         */
 876        if (dent->name[0] == DELETED_FLAG ||
 877            dent->name[0] == aRING)
 878                return NULL;
 879
 880        itr->l_name[n] = '\0';
 881
 882        chksum = mkcksum(dent->name, dent->ext);
 883
 884        /* checksum mismatch could mean deleted file, etc.. skip it: */
 885        if (chksum != alias_checksum) {
 886                debug("** chksum=%x, alias_checksum=%x, l_name=%s, s_name=%8s.%3s\n",
 887                      chksum, alias_checksum, itr->l_name, dent->name, dent->ext);
 888                return NULL;
 889        }
 890
 891        return dent;
 892}
 893
 894/**
 895 * fat_itr_next() - step to the next entry in a directory
 896 *
 897 * Must be called once on a new iterator before the cursor is valid.
 898 *
 899 * @itr: the iterator to iterate
 900 * @return boolean, 1 if success or 0 if no more entries in the
 901 *    current directory
 902 */
 903static int fat_itr_next(fat_itr *itr)
 904{
 905        dir_entry *dent;
 906
 907        itr->name = NULL;
 908
 909        /*
 910         * One logical directory entry consist of following slots:
 911         *                              name[0] Attributes
 912         *   dent[N - N]: LFN[N - 1]    N|0x40  ATTR_VFAT
 913         *   ...
 914         *   dent[N - 2]: LFN[1]        2       ATTR_VFAT
 915         *   dent[N - 1]: LFN[0]        1       ATTR_VFAT
 916         *   dent[N]:     SFN                   ATTR_ARCH
 917         */
 918
 919        while (1) {
 920                dent = next_dent(itr);
 921                if (!dent)
 922                        return 0;
 923
 924                if (dent->name[0] == DELETED_FLAG ||
 925                    dent->name[0] == aRING)
 926                        continue;
 927
 928                if (dent->attr & ATTR_VOLUME) {
 929                        if ((dent->attr & ATTR_VFAT) == ATTR_VFAT &&
 930                            (dent->name[0] & LAST_LONG_ENTRY_MASK)) {
 931                                /* long file name */
 932                                dent = extract_vfat_name(itr);
 933                                /*
 934                                 * If succeeded, dent has a valid short file
 935                                 * name entry for the current entry.
 936                                 * If failed, itr points to a current bogus
 937                                 * entry. So after fetching a next one,
 938                                 * it may have a short file name entry
 939                                 * for this bogus entry so that we can still
 940                                 * check for a short name.
 941                                 */
 942                                if (!dent)
 943                                        continue;
 944                                itr->name = itr->l_name;
 945                                break;
 946                        } else {
 947                                /* Volume label or VFAT entry, skip */
 948                                continue;
 949                        }
 950                } else if (!(dent->attr & ATTR_ARCH) &&
 951                           !(dent->attr & ATTR_DIR))
 952                        continue;
 953
 954                /* short file name */
 955                break;
 956        }
 957
 958        get_name(dent, itr->s_name);
 959        if (!itr->name)
 960                itr->name = itr->s_name;
 961
 962        return 1;
 963}
 964
 965/**
 966 * fat_itr_isdir() - is current cursor position pointing to a directory
 967 *
 968 * @itr: the iterator
 969 * @return true if cursor is at a directory
 970 */
 971static int fat_itr_isdir(fat_itr *itr)
 972{
 973        return !!(itr->dent->attr & ATTR_DIR);
 974}
 975
 976/*
 977 * Helpers:
 978 */
 979
 980#define TYPE_FILE 0x1
 981#define TYPE_DIR  0x2
 982#define TYPE_ANY  (TYPE_FILE | TYPE_DIR)
 983
 984/**
 985 * fat_itr_resolve() - traverse directory structure to resolve the
 986 * requested path.
 987 *
 988 * Traverse directory structure to the requested path.  If the specified
 989 * path is to a directory, this will descend into the directory and
 990 * leave it iterator at the start of the directory.  If the path is to a
 991 * file, it will leave the iterator in the parent directory with current
 992 * cursor at file's entry in the directory.
 993 *
 994 * @itr: iterator initialized to root
 995 * @path: the requested path
 996 * @type: bitmask of allowable file types
 997 * @return 0 on success or -errno
 998 */
 999static int fat_itr_resolve(fat_itr *itr, const char *path, unsigned type)
1000{
1001        const char *next;
1002
1003        /* chomp any extra leading slashes: */
1004        while (path[0] && ISDIRDELIM(path[0]))
1005                path++;
1006
1007        /* are we at the end? */
1008        if (strlen(path) == 0) {
1009                if (!(type & TYPE_DIR))
1010                        return -ENOENT;
1011                return 0;
1012        }
1013
1014        /* find length of next path entry: */
1015        next = path;
1016        while (next[0] && !ISDIRDELIM(next[0]))
1017                next++;
1018
1019        if (itr->is_root) {
1020                /* root dir doesn't have "." nor ".." */
1021                if ((((next - path) == 1) && !strncmp(path, ".", 1)) ||
1022                    (((next - path) == 2) && !strncmp(path, "..", 2))) {
1023                        /* point back to itself */
1024                        itr->clust = itr->fsdata->root_cluster;
1025                        itr->next_clust = itr->fsdata->root_cluster;
1026                        itr->dent = NULL;
1027                        itr->remaining = 0;
1028                        itr->last_cluster = 0;
1029
1030                        if (next[0] == 0) {
1031                                if (type & TYPE_DIR)
1032                                        return 0;
1033                                else
1034                                        return -ENOENT;
1035                        }
1036
1037                        return fat_itr_resolve(itr, next, type);
1038                }
1039        }
1040
1041        while (fat_itr_next(itr)) {
1042                int match = 0;
1043                unsigned n = max(strlen(itr->name), (size_t)(next - path));
1044
1045                /* check both long and short name: */
1046                if (!strncasecmp(path, itr->name, n))
1047                        match = 1;
1048                else if (itr->name != itr->s_name &&
1049                         !strncasecmp(path, itr->s_name, n))
1050                        match = 1;
1051
1052                if (!match)
1053                        continue;
1054
1055                if (fat_itr_isdir(itr)) {
1056                        /* recurse into directory: */
1057                        fat_itr_child(itr, itr);
1058                        return fat_itr_resolve(itr, next, type);
1059                } else if (next[0]) {
1060                        /*
1061                         * If next is not empty then we have a case
1062                         * like: /path/to/realfile/nonsense
1063                         */
1064                        debug("bad trailing path: %s\n", next);
1065                        return -ENOENT;
1066                } else if (!(type & TYPE_FILE)) {
1067                        return -ENOTDIR;
1068                } else {
1069                        return 0;
1070                }
1071        }
1072
1073        return -ENOENT;
1074}
1075
1076int file_fat_detectfs(void)
1077{
1078        boot_sector bs;
1079        volume_info volinfo;
1080        int fatsize;
1081        char vol_label[12];
1082
1083        if (cur_dev == NULL) {
1084                printf("No current device\n");
1085                return 1;
1086        }
1087
1088#if defined(CONFIG_IDE) || \
1089    defined(CONFIG_SATA) || \
1090    defined(CONFIG_SCSI) || \
1091    defined(CONFIG_CMD_USB) || \
1092    defined(CONFIG_MMC)
1093        printf("Interface:  ");
1094        switch (cur_dev->if_type) {
1095        case IF_TYPE_IDE:
1096                printf("IDE");
1097                break;
1098        case IF_TYPE_SATA:
1099                printf("SATA");
1100                break;
1101        case IF_TYPE_SCSI:
1102                printf("SCSI");
1103                break;
1104        case IF_TYPE_ATAPI:
1105                printf("ATAPI");
1106                break;
1107        case IF_TYPE_USB:
1108                printf("USB");
1109                break;
1110        case IF_TYPE_DOC:
1111                printf("DOC");
1112                break;
1113        case IF_TYPE_MMC:
1114                printf("MMC");
1115                break;
1116        default:
1117                printf("Unknown");
1118        }
1119
1120        printf("\n  Device %d: ", cur_dev->devnum);
1121        dev_print(cur_dev);
1122#endif
1123
1124        if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
1125                printf("\nNo valid FAT fs found\n");
1126                return 1;
1127        }
1128
1129        memcpy(vol_label, volinfo.volume_label, 11);
1130        vol_label[11] = '\0';
1131        volinfo.fs_type[5] = '\0';
1132
1133        printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
1134
1135        return 0;
1136}
1137
1138int fat_exists(const char *filename)
1139{
1140        fsdata fsdata;
1141        fat_itr *itr;
1142        int ret;
1143
1144        itr = malloc_cache_aligned(sizeof(fat_itr));
1145        if (!itr)
1146                return 0;
1147        ret = fat_itr_root(itr, &fsdata);
1148        if (ret)
1149                goto out;
1150
1151        ret = fat_itr_resolve(itr, filename, TYPE_ANY);
1152        free(fsdata.fatbuf);
1153out:
1154        free(itr);
1155        return ret == 0;
1156}
1157
1158int fat_size(const char *filename, loff_t *size)
1159{
1160        fsdata fsdata;
1161        fat_itr *itr;
1162        int ret;
1163
1164        itr = malloc_cache_aligned(sizeof(fat_itr));
1165        if (!itr)
1166                return -ENOMEM;
1167        ret = fat_itr_root(itr, &fsdata);
1168        if (ret)
1169                goto out_free_itr;
1170
1171        ret = fat_itr_resolve(itr, filename, TYPE_FILE);
1172        if (ret) {
1173                /*
1174                 * Directories don't have size, but fs_size() is not
1175                 * expected to fail if passed a directory path:
1176                 */
1177                free(fsdata.fatbuf);
1178                ret = fat_itr_root(itr, &fsdata);
1179                if (ret)
1180                        goto out_free_itr;
1181                ret = fat_itr_resolve(itr, filename, TYPE_DIR);
1182                if (!ret)
1183                        *size = 0;
1184                goto out_free_both;
1185        }
1186
1187        *size = FAT2CPU32(itr->dent->size);
1188out_free_both:
1189        free(fsdata.fatbuf);
1190out_free_itr:
1191        free(itr);
1192        return ret;
1193}
1194
1195int file_fat_read_at(const char *filename, loff_t pos, void *buffer,
1196                     loff_t maxsize, loff_t *actread)
1197{
1198        fsdata fsdata;
1199        fat_itr *itr;
1200        int ret;
1201
1202        itr = malloc_cache_aligned(sizeof(fat_itr));
1203        if (!itr)
1204                return -ENOMEM;
1205        ret = fat_itr_root(itr, &fsdata);
1206        if (ret)
1207                goto out_free_itr;
1208
1209        ret = fat_itr_resolve(itr, filename, TYPE_FILE);
1210        if (ret)
1211                goto out_free_both;
1212
1213        debug("reading %s at pos %llu\n", filename, pos);
1214
1215        /* For saving default max clustersize memory allocated to malloc pool */
1216        dir_entry *dentptr = itr->dent;
1217
1218        ret = get_contents(&fsdata, dentptr, pos, buffer, maxsize, actread);
1219
1220out_free_both:
1221        free(fsdata.fatbuf);
1222out_free_itr:
1223        free(itr);
1224        return ret;
1225}
1226
1227int file_fat_read(const char *filename, void *buffer, int maxsize)
1228{
1229        loff_t actread;
1230        int ret;
1231
1232        ret =  file_fat_read_at(filename, 0, buffer, maxsize, &actread);
1233        if (ret)
1234                return ret;
1235        else
1236                return actread;
1237}
1238
1239int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
1240                  loff_t *actread)
1241{
1242        int ret;
1243
1244        ret = file_fat_read_at(filename, offset, buf, len, actread);
1245        if (ret)
1246                printf("** Unable to read file %s **\n", filename);
1247
1248        return ret;
1249}
1250
1251typedef struct {
1252        struct fs_dir_stream parent;
1253        struct fs_dirent dirent;
1254        fsdata fsdata;
1255        fat_itr itr;
1256} fat_dir;
1257
1258int fat_opendir(const char *filename, struct fs_dir_stream **dirsp)
1259{
1260        fat_dir *dir;
1261        int ret;
1262
1263        dir = malloc_cache_aligned(sizeof(*dir));
1264        if (!dir)
1265                return -ENOMEM;
1266        memset(dir, 0, sizeof(*dir));
1267
1268        ret = fat_itr_root(&dir->itr, &dir->fsdata);
1269        if (ret)
1270                goto fail_free_dir;
1271
1272        ret = fat_itr_resolve(&dir->itr, filename, TYPE_DIR);
1273        if (ret)
1274                goto fail_free_both;
1275
1276        *dirsp = (struct fs_dir_stream *)dir;
1277        return 0;
1278
1279fail_free_both:
1280        free(dir->fsdata.fatbuf);
1281fail_free_dir:
1282        free(dir);
1283        return ret;
1284}
1285
1286int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp)
1287{
1288        fat_dir *dir = (fat_dir *)dirs;
1289        struct fs_dirent *dent = &dir->dirent;
1290
1291        if (!fat_itr_next(&dir->itr))
1292                return -ENOENT;
1293
1294        memset(dent, 0, sizeof(*dent));
1295        strcpy(dent->name, dir->itr.name);
1296
1297        if (fat_itr_isdir(&dir->itr)) {
1298                dent->type = FS_DT_DIR;
1299        } else {
1300                dent->type = FS_DT_REG;
1301                dent->size = FAT2CPU32(dir->itr.dent->size);
1302        }
1303
1304        *dentp = dent;
1305
1306        return 0;
1307}
1308
1309void fat_closedir(struct fs_dir_stream *dirs)
1310{
1311        fat_dir *dir = (fat_dir *)dirs;
1312        free(dir->fsdata.fatbuf);
1313        free(dir);
1314}
1315
1316void fat_close(void)
1317{
1318}
1319