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