uboot/fs/fat/fat.c
<<
>>
Prefs
   1/*
   2 * fat.c
   3 *
   4 * R/O (V)FAT 12/16/32 filesystem implementation by Marcus Sundberg
   5 *
   6 * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
   7 * 2003-03-10 - kharris@nexus-tech.net - ported to uboot
   8 *
   9 * SPDX-License-Identifier:     GPL-2.0+
  10 */
  11
  12#include <common.h>
  13#include <config.h>
  14#include <exports.h>
  15#include <fat.h>
  16#include <asm/byteorder.h>
  17#include <part.h>
  18#include <malloc.h>
  19#include <linux/compiler.h>
  20#include <linux/ctype.h>
  21
  22#ifdef CONFIG_SUPPORT_VFAT
  23static const int vfat_enabled = 1;
  24#else
  25static const int vfat_enabled = 0;
  26#endif
  27
  28/*
  29 * Convert a string to lowercase.
  30 */
  31static void downcase(char *str)
  32{
  33        while (*str != '\0') {
  34                *str = tolower(*str);
  35                str++;
  36        }
  37}
  38
  39static block_dev_desc_t *cur_dev;
  40static disk_partition_t 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        if (!cur_dev || !cur_dev->block_read)
  49                return -1;
  50
  51        return cur_dev->block_read(cur_dev->dev,
  52                        cur_part_info.start + block, nr_blocks, buf);
  53}
  54
  55int fat_set_blk_dev(block_dev_desc_t *dev_desc, disk_partition_t *info)
  56{
  57        ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
  58
  59        cur_dev = dev_desc;
  60        cur_part_info = *info;
  61
  62        /* Make sure it has a valid FAT header */
  63        if (disk_read(0, 1, buffer) != 1) {
  64                cur_dev = NULL;
  65                return -1;
  66        }
  67
  68        /* Check if it's actually a DOS volume */
  69        if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) {
  70                cur_dev = NULL;
  71                return -1;
  72        }
  73
  74        /* Check for FAT12/FAT16/FAT32 filesystem */
  75        if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3))
  76                return 0;
  77        if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5))
  78                return 0;
  79
  80        cur_dev = NULL;
  81        return -1;
  82}
  83
  84int fat_register_device(block_dev_desc_t *dev_desc, int part_no)
  85{
  86        disk_partition_t info;
  87
  88        /* First close any currently found FAT filesystem */
  89        cur_dev = NULL;
  90
  91        /* Read the partition table, if present */
  92        if (get_partition_info(dev_desc, part_no, &info)) {
  93                if (part_no != 0) {
  94                        printf("** Partition %d not valid on device %d **\n",
  95                                        part_no, dev_desc->dev);
  96                        return -1;
  97                }
  98
  99                info.start = 0;
 100                info.size = dev_desc->lba;
 101                info.blksz = dev_desc->blksz;
 102                info.name[0] = 0;
 103                info.type[0] = 0;
 104                info.bootable = 0;
 105#ifdef CONFIG_PARTITION_UUIDS
 106                info.uuid[0] = 0;
 107#endif
 108        }
 109
 110        return fat_set_blk_dev(dev_desc, &info);
 111}
 112
 113/*
 114 * Get the first occurence of a directory delimiter ('/' or '\') in a string.
 115 * Return index into string if found, -1 otherwise.
 116 */
 117static int dirdelim(char *str)
 118{
 119        char *start = str;
 120
 121        while (*str != '\0') {
 122                if (ISDIRDELIM(*str))
 123                        return str - start;
 124                str++;
 125        }
 126        return -1;
 127}
 128
 129/*
 130 * Extract zero terminated short name from a directory entry.
 131 */
 132static void get_name(dir_entry *dirent, char *s_name)
 133{
 134        char *ptr;
 135
 136        memcpy(s_name, dirent->name, 8);
 137        s_name[8] = '\0';
 138        ptr = s_name;
 139        while (*ptr && *ptr != ' ')
 140                ptr++;
 141        if (dirent->ext[0] && dirent->ext[0] != ' ') {
 142                *ptr = '.';
 143                ptr++;
 144                memcpy(ptr, dirent->ext, 3);
 145                ptr[3] = '\0';
 146                while (*ptr && *ptr != ' ')
 147                        ptr++;
 148        }
 149        *ptr = '\0';
 150        if (*s_name == DELETED_FLAG)
 151                *s_name = '\0';
 152        else if (*s_name == aRING)
 153                *s_name = DELETED_FLAG;
 154        downcase(s_name);
 155}
 156
 157/*
 158 * Get the entry at index 'entry' in a FAT (12/16/32) table.
 159 * On failure 0x00 is returned.
 160 */
 161static __u32 get_fatent(fsdata *mydata, __u32 entry)
 162{
 163        __u32 bufnum;
 164        __u32 off16, offset;
 165        __u32 ret = 0x00;
 166        __u16 val1, val2;
 167
 168        switch (mydata->fatsize) {
 169        case 32:
 170                bufnum = entry / FAT32BUFSIZE;
 171                offset = entry - bufnum * FAT32BUFSIZE;
 172                break;
 173        case 16:
 174                bufnum = entry / FAT16BUFSIZE;
 175                offset = entry - bufnum * FAT16BUFSIZE;
 176                break;
 177        case 12:
 178                bufnum = entry / FAT12BUFSIZE;
 179                offset = entry - bufnum * FAT12BUFSIZE;
 180                break;
 181
 182        default:
 183                /* Unsupported FAT size */
 184                return ret;
 185        }
 186
 187        debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n",
 188               mydata->fatsize, entry, entry, offset, offset);
 189
 190        /* Read a new block of FAT entries into the cache. */
 191        if (bufnum != mydata->fatbufnum) {
 192                __u32 getsize = FATBUFBLOCKS;
 193                __u8 *bufptr = mydata->fatbuf;
 194                __u32 fatlength = mydata->fatlength;
 195                __u32 startblock = bufnum * FATBUFBLOCKS;
 196
 197                if (startblock + getsize > fatlength)
 198                        getsize = fatlength - startblock;
 199
 200                startblock += mydata->fat_sect; /* Offset from start of disk */
 201
 202                if (disk_read(startblock, getsize, bufptr) < 0) {
 203                        debug("Error reading FAT blocks\n");
 204                        return ret;
 205                }
 206                mydata->fatbufnum = bufnum;
 207        }
 208
 209        /* Get the actual entry from the table */
 210        switch (mydata->fatsize) {
 211        case 32:
 212                ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
 213                break;
 214        case 16:
 215                ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
 216                break;
 217        case 12:
 218                off16 = (offset * 3) / 4;
 219
 220                switch (offset & 0x3) {
 221                case 0:
 222                        ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]);
 223                        ret &= 0xfff;
 224                        break;
 225                case 1:
 226                        val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
 227                        val1 &= 0xf000;
 228                        val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
 229                        val2 &= 0x00ff;
 230                        ret = (val2 << 4) | (val1 >> 12);
 231                        break;
 232                case 2:
 233                        val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
 234                        val1 &= 0xff00;
 235                        val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
 236                        val2 &= 0x000f;
 237                        ret = (val2 << 8) | (val1 >> 8);
 238                        break;
 239                case 3:
 240                        ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
 241                        ret = (ret & 0xfff0) >> 4;
 242                        break;
 243                default:
 244                        break;
 245                }
 246                break;
 247        }
 248        debug("FAT%d: ret: %08x, offset: %04x\n",
 249               mydata->fatsize, ret, offset);
 250
 251        return ret;
 252}
 253
 254/*
 255 * Read at most 'size' bytes from the specified cluster into 'buffer'.
 256 * Return 0 on success, -1 otherwise.
 257 */
 258static int
 259get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
 260{
 261        __u32 idx = 0;
 262        __u32 startsect;
 263        int ret;
 264
 265        if (clustnum > 0) {
 266                startsect = mydata->data_begin +
 267                                clustnum * mydata->clust_size;
 268        } else {
 269                startsect = mydata->rootdir_sect;
 270        }
 271
 272        debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
 273
 274        if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
 275                ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 276
 277                printf("FAT: Misaligned buffer address (%p)\n", buffer);
 278
 279                while (size >= mydata->sect_size) {
 280                        ret = disk_read(startsect++, 1, tmpbuf);
 281                        if (ret != 1) {
 282                                debug("Error reading data (got %d)\n", ret);
 283                                return -1;
 284                        }
 285
 286                        memcpy(buffer, tmpbuf, mydata->sect_size);
 287                        buffer += mydata->sect_size;
 288                        size -= mydata->sect_size;
 289                }
 290        } else {
 291                idx = size / mydata->sect_size;
 292                ret = disk_read(startsect, idx, buffer);
 293                if (ret != idx) {
 294                        debug("Error reading data (got %d)\n", ret);
 295                        return -1;
 296                }
 297                startsect += idx;
 298                idx *= mydata->sect_size;
 299                buffer += idx;
 300                size -= idx;
 301        }
 302        if (size) {
 303                ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 304
 305                ret = disk_read(startsect, 1, tmpbuf);
 306                if (ret != 1) {
 307                        debug("Error reading data (got %d)\n", ret);
 308                        return -1;
 309                }
 310
 311                memcpy(buffer, tmpbuf, size);
 312        }
 313
 314        return 0;
 315}
 316
 317/*
 318 * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr'
 319 * into 'buffer'.
 320 * Return the number of bytes read or -1 on fatal errors.
 321 */
 322__u8 get_contents_vfatname_block[MAX_CLUSTSIZE]
 323        __aligned(ARCH_DMA_MINALIGN);
 324
 325static long
 326get_contents(fsdata *mydata, dir_entry *dentptr, unsigned long pos,
 327             __u8 *buffer, unsigned long maxsize)
 328{
 329        unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
 330        unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
 331        __u32 curclust = START(dentptr);
 332        __u32 endclust, newclust;
 333        unsigned long actsize;
 334
 335        debug("Filesize: %ld bytes\n", filesize);
 336
 337        if (pos >= filesize) {
 338                debug("Read position past EOF: %lu\n", pos);
 339                return gotsize;
 340        }
 341
 342        if (maxsize > 0 && filesize > pos + maxsize)
 343                filesize = pos + maxsize;
 344
 345        debug("%ld bytes\n", filesize);
 346
 347        actsize = bytesperclust;
 348
 349        /* go to cluster at pos */
 350        while (actsize <= pos) {
 351                curclust = get_fatent(mydata, curclust);
 352                if (CHECK_CLUST(curclust, mydata->fatsize)) {
 353                        debug("curclust: 0x%x\n", curclust);
 354                        debug("Invalid FAT entry\n");
 355                        return gotsize;
 356                }
 357                actsize += bytesperclust;
 358        }
 359
 360        /* actsize > pos */
 361        actsize -= bytesperclust;
 362        filesize -= actsize;
 363        pos -= actsize;
 364
 365        /* align to beginning of next cluster if any */
 366        if (pos) {
 367                actsize = min(filesize, bytesperclust);
 368                if (get_cluster(mydata, curclust, get_contents_vfatname_block,
 369                                (int)actsize) != 0) {
 370                        printf("Error reading cluster\n");
 371                        return -1;
 372                }
 373                filesize -= actsize;
 374                actsize -= pos;
 375                memcpy(buffer, get_contents_vfatname_block + pos, actsize);
 376                gotsize += actsize;
 377                if (!filesize)
 378                        return gotsize;
 379                buffer += actsize;
 380
 381                curclust = get_fatent(mydata, curclust);
 382                if (CHECK_CLUST(curclust, mydata->fatsize)) {
 383                        debug("curclust: 0x%x\n", curclust);
 384                        debug("Invalid FAT entry\n");
 385                        return gotsize;
 386                }
 387        }
 388
 389        actsize = bytesperclust;
 390        endclust = curclust;
 391
 392        do {
 393                /* search for consecutive clusters */
 394                while (actsize < filesize) {
 395                        newclust = get_fatent(mydata, endclust);
 396                        if ((newclust - 1) != endclust)
 397                                goto getit;
 398                        if (CHECK_CLUST(newclust, mydata->fatsize)) {
 399                                debug("curclust: 0x%x\n", newclust);
 400                                debug("Invalid FAT entry\n");
 401                                return gotsize;
 402                        }
 403                        endclust = newclust;
 404                        actsize += bytesperclust;
 405                }
 406
 407                /* get remaining bytes */
 408                actsize = filesize;
 409                if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
 410                        printf("Error reading cluster\n");
 411                        return -1;
 412                }
 413                gotsize += actsize;
 414                return gotsize;
 415getit:
 416                if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
 417                        printf("Error reading cluster\n");
 418                        return -1;
 419                }
 420                gotsize += (int)actsize;
 421                filesize -= actsize;
 422                buffer += actsize;
 423
 424                curclust = get_fatent(mydata, endclust);
 425                if (CHECK_CLUST(curclust, mydata->fatsize)) {
 426                        debug("curclust: 0x%x\n", curclust);
 427                        printf("Invalid FAT entry\n");
 428                        return gotsize;
 429                }
 430                actsize = bytesperclust;
 431                endclust = curclust;
 432        } while (1);
 433}
 434
 435/*
 436 * Extract the file name information from 'slotptr' into 'l_name',
 437 * starting at l_name[*idx].
 438 * Return 1 if terminator (zero byte) is found, 0 otherwise.
 439 */
 440static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
 441{
 442        int j;
 443
 444        for (j = 0; j <= 8; j += 2) {
 445                l_name[*idx] = slotptr->name0_4[j];
 446                if (l_name[*idx] == 0x00)
 447                        return 1;
 448                (*idx)++;
 449        }
 450        for (j = 0; j <= 10; j += 2) {
 451                l_name[*idx] = slotptr->name5_10[j];
 452                if (l_name[*idx] == 0x00)
 453                        return 1;
 454                (*idx)++;
 455        }
 456        for (j = 0; j <= 2; j += 2) {
 457                l_name[*idx] = slotptr->name11_12[j];
 458                if (l_name[*idx] == 0x00)
 459                        return 1;
 460                (*idx)++;
 461        }
 462
 463        return 0;
 464}
 465
 466/*
 467 * Extract the full long filename starting at 'retdent' (which is really
 468 * a slot) into 'l_name'. If successful also copy the real directory entry
 469 * into 'retdent'
 470 * Return 0 on success, -1 otherwise.
 471 */
 472static int
 473get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
 474             dir_entry *retdent, char *l_name)
 475{
 476        dir_entry *realdent;
 477        dir_slot *slotptr = (dir_slot *)retdent;
 478        __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
 479                                                        PREFETCH_BLOCKS :
 480                                                        mydata->clust_size);
 481        __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
 482        int idx = 0;
 483
 484        if (counter > VFAT_MAXSEQ) {
 485                debug("Error: VFAT name is too long\n");
 486                return -1;
 487        }
 488
 489        while ((__u8 *)slotptr < buflimit) {
 490                if (counter == 0)
 491                        break;
 492                if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
 493                        return -1;
 494                slotptr++;
 495                counter--;
 496        }
 497
 498        if ((__u8 *)slotptr >= buflimit) {
 499                dir_slot *slotptr2;
 500
 501                if (curclust == 0)
 502                        return -1;
 503                curclust = get_fatent(mydata, curclust);
 504                if (CHECK_CLUST(curclust, mydata->fatsize)) {
 505                        debug("curclust: 0x%x\n", curclust);
 506                        printf("Invalid FAT entry\n");
 507                        return -1;
 508                }
 509
 510                if (get_cluster(mydata, curclust, get_contents_vfatname_block,
 511                                mydata->clust_size * mydata->sect_size) != 0) {
 512                        debug("Error: reading directory block\n");
 513                        return -1;
 514                }
 515
 516                slotptr2 = (dir_slot *)get_contents_vfatname_block;
 517                while (counter > 0) {
 518                        if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
 519                            & 0xff) != counter)
 520                                return -1;
 521                        slotptr2++;
 522                        counter--;
 523                }
 524
 525                /* Save the real directory entry */
 526                realdent = (dir_entry *)slotptr2;
 527                while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
 528                        slotptr2--;
 529                        slot2str(slotptr2, l_name, &idx);
 530                }
 531        } else {
 532                /* Save the real directory entry */
 533                realdent = (dir_entry *)slotptr;
 534        }
 535
 536        do {
 537                slotptr--;
 538                if (slot2str(slotptr, l_name, &idx))
 539                        break;
 540        } while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
 541
 542        l_name[idx] = '\0';
 543        if (*l_name == DELETED_FLAG)
 544                *l_name = '\0';
 545        else if (*l_name == aRING)
 546                *l_name = DELETED_FLAG;
 547        downcase(l_name);
 548
 549        /* Return the real directory entry */
 550        memcpy(retdent, realdent, sizeof(dir_entry));
 551
 552        return 0;
 553}
 554
 555/* Calculate short name checksum */
 556static __u8 mkcksum(const char name[8], const char ext[3])
 557{
 558        int i;
 559
 560        __u8 ret = 0;
 561
 562        for (i = 0; i < 8; i++)
 563                ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + name[i];
 564        for (i = 0; i < 3; i++)
 565                ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + ext[i];
 566
 567        return ret;
 568}
 569
 570/*
 571 * Get the directory entry associated with 'filename' from the directory
 572 * starting at 'startsect'
 573 */
 574__u8 get_dentfromdir_block[MAX_CLUSTSIZE]
 575        __aligned(ARCH_DMA_MINALIGN);
 576
 577static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
 578                                  char *filename, dir_entry *retdent,
 579                                  int dols)
 580{
 581        __u16 prevcksum = 0xffff;
 582        __u32 curclust = START(retdent);
 583        int files = 0, dirs = 0;
 584
 585        debug("get_dentfromdir: %s\n", filename);
 586
 587        while (1) {
 588                dir_entry *dentptr;
 589
 590                int i;
 591
 592                if (get_cluster(mydata, curclust, get_dentfromdir_block,
 593                                mydata->clust_size * mydata->sect_size) != 0) {
 594                        debug("Error: reading directory block\n");
 595                        return NULL;
 596                }
 597
 598                dentptr = (dir_entry *)get_dentfromdir_block;
 599
 600                for (i = 0; i < DIRENTSPERCLUST; i++) {
 601                        char s_name[14], l_name[VFAT_MAXLEN_BYTES];
 602
 603                        l_name[0] = '\0';
 604                        if (dentptr->name[0] == DELETED_FLAG) {
 605                                dentptr++;
 606                                continue;
 607                        }
 608                        if ((dentptr->attr & ATTR_VOLUME)) {
 609                                if (vfat_enabled &&
 610                                    (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
 611                                    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
 612                                        prevcksum = ((dir_slot *)dentptr)->alias_checksum;
 613                                        get_vfatname(mydata, curclust,
 614                                                     get_dentfromdir_block,
 615                                                     dentptr, l_name);
 616                                        if (dols) {
 617                                                int isdir;
 618                                                char dirc;
 619                                                int doit = 0;
 620
 621                                                isdir = (dentptr->attr & ATTR_DIR);
 622
 623                                                if (isdir) {
 624                                                        dirs++;
 625                                                        dirc = '/';
 626                                                        doit = 1;
 627                                                } else {
 628                                                        dirc = ' ';
 629                                                        if (l_name[0] != 0) {
 630                                                                files++;
 631                                                                doit = 1;
 632                                                        }
 633                                                }
 634                                                if (doit) {
 635                                                        if (dirc == ' ') {
 636                                                                printf(" %8ld   %s%c\n",
 637                                                                        (long)FAT2CPU32(dentptr->size),
 638                                                                        l_name,
 639                                                                        dirc);
 640                                                        } else {
 641                                                                printf("            %s%c\n",
 642                                                                        l_name,
 643                                                                        dirc);
 644                                                        }
 645                                                }
 646                                                dentptr++;
 647                                                continue;
 648                                        }
 649                                        debug("vfatname: |%s|\n", l_name);
 650                                } else {
 651                                        /* Volume label or VFAT entry */
 652                                        dentptr++;
 653                                        continue;
 654                                }
 655                        }
 656                        if (dentptr->name[0] == 0) {
 657                                if (dols) {
 658                                        printf("\n%d file(s), %d dir(s)\n\n",
 659                                                files, dirs);
 660                                }
 661                                debug("Dentname == NULL - %d\n", i);
 662                                return NULL;
 663                        }
 664                        if (vfat_enabled) {
 665                                __u8 csum = mkcksum(dentptr->name, dentptr->ext);
 666                                if (dols && csum == prevcksum) {
 667                                        prevcksum = 0xffff;
 668                                        dentptr++;
 669                                        continue;
 670                                }
 671                        }
 672
 673                        get_name(dentptr, s_name);
 674                        if (dols) {
 675                                int isdir = (dentptr->attr & ATTR_DIR);
 676                                char dirc;
 677                                int doit = 0;
 678
 679                                if (isdir) {
 680                                        dirs++;
 681                                        dirc = '/';
 682                                        doit = 1;
 683                                } else {
 684                                        dirc = ' ';
 685                                        if (s_name[0] != 0) {
 686                                                files++;
 687                                                doit = 1;
 688                                        }
 689                                }
 690
 691                                if (doit) {
 692                                        if (dirc == ' ') {
 693                                                printf(" %8ld   %s%c\n",
 694                                                        (long)FAT2CPU32(dentptr->size),
 695                                                        s_name, dirc);
 696                                        } else {
 697                                                printf("            %s%c\n",
 698                                                        s_name, dirc);
 699                                        }
 700                                }
 701
 702                                dentptr++;
 703                                continue;
 704                        }
 705
 706                        if (strcmp(filename, s_name)
 707                            && strcmp(filename, l_name)) {
 708                                debug("Mismatch: |%s|%s|\n", s_name, l_name);
 709                                dentptr++;
 710                                continue;
 711                        }
 712
 713                        memcpy(retdent, dentptr, sizeof(dir_entry));
 714
 715                        debug("DentName: %s", s_name);
 716                        debug(", start: 0x%x", START(dentptr));
 717                        debug(", size:  0x%x %s\n",
 718                              FAT2CPU32(dentptr->size),
 719                              (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
 720
 721                        return retdent;
 722                }
 723
 724                curclust = get_fatent(mydata, curclust);
 725                if (CHECK_CLUST(curclust, mydata->fatsize)) {
 726                        debug("curclust: 0x%x\n", curclust);
 727                        printf("Invalid FAT entry\n");
 728                        return NULL;
 729                }
 730        }
 731
 732        return NULL;
 733}
 734
 735/*
 736 * Read boot sector and volume info from a FAT filesystem
 737 */
 738static int
 739read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
 740{
 741        __u8 *block;
 742        volume_info *vistart;
 743        int ret = 0;
 744
 745        if (cur_dev == NULL) {
 746                debug("Error: no device selected\n");
 747                return -1;
 748        }
 749
 750        block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz);
 751        if (block == NULL) {
 752                debug("Error: allocating block\n");
 753                return -1;
 754        }
 755
 756        if (disk_read(0, 1, block) < 0) {
 757                debug("Error: reading block\n");
 758                goto fail;
 759        }
 760
 761        memcpy(bs, block, sizeof(boot_sector));
 762        bs->reserved = FAT2CPU16(bs->reserved);
 763        bs->fat_length = FAT2CPU16(bs->fat_length);
 764        bs->secs_track = FAT2CPU16(bs->secs_track);
 765        bs->heads = FAT2CPU16(bs->heads);
 766        bs->total_sect = FAT2CPU32(bs->total_sect);
 767
 768        /* FAT32 entries */
 769        if (bs->fat_length == 0) {
 770                /* Assume FAT32 */
 771                bs->fat32_length = FAT2CPU32(bs->fat32_length);
 772                bs->flags = FAT2CPU16(bs->flags);
 773                bs->root_cluster = FAT2CPU32(bs->root_cluster);
 774                bs->info_sector = FAT2CPU16(bs->info_sector);
 775                bs->backup_boot = FAT2CPU16(bs->backup_boot);
 776                vistart = (volume_info *)(block + sizeof(boot_sector));
 777                *fatsize = 32;
 778        } else {
 779                vistart = (volume_info *)&(bs->fat32_length);
 780                *fatsize = 0;
 781        }
 782        memcpy(volinfo, vistart, sizeof(volume_info));
 783
 784        if (*fatsize == 32) {
 785                if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
 786                        goto exit;
 787        } else {
 788                if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
 789                        *fatsize = 12;
 790                        goto exit;
 791                }
 792                if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
 793                        *fatsize = 16;
 794                        goto exit;
 795                }
 796        }
 797
 798        debug("Error: broken fs_type sign\n");
 799fail:
 800        ret = -1;
 801exit:
 802        free(block);
 803        return ret;
 804}
 805
 806__u8 do_fat_read_at_block[MAX_CLUSTSIZE]
 807        __aligned(ARCH_DMA_MINALIGN);
 808
 809long
 810do_fat_read_at(const char *filename, unsigned long pos, void *buffer,
 811               unsigned long maxsize, int dols, int dogetsize)
 812{
 813        char fnamecopy[2048];
 814        boot_sector bs;
 815        volume_info volinfo;
 816        fsdata datablock;
 817        fsdata *mydata = &datablock;
 818        dir_entry *dentptr = NULL;
 819        __u16 prevcksum = 0xffff;
 820        char *subname = "";
 821        __u32 cursect;
 822        int idx, isdir = 0;
 823        int files = 0, dirs = 0;
 824        long ret = -1;
 825        int firsttime;
 826        __u32 root_cluster = 0;
 827        int rootdir_size = 0;
 828        int j;
 829
 830        if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
 831                debug("Error: reading boot sector\n");
 832                return -1;
 833        }
 834
 835        if (mydata->fatsize == 32) {
 836                root_cluster = bs.root_cluster;
 837                mydata->fatlength = bs.fat32_length;
 838        } else {
 839                mydata->fatlength = bs.fat_length;
 840        }
 841
 842        mydata->fat_sect = bs.reserved;
 843
 844        cursect = mydata->rootdir_sect
 845                = mydata->fat_sect + mydata->fatlength * bs.fats;
 846
 847        mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
 848        mydata->clust_size = bs.cluster_size;
 849        if (mydata->sect_size != cur_part_info.blksz) {
 850                printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
 851                                mydata->sect_size, cur_part_info.blksz);
 852                return -1;
 853        }
 854
 855        if (mydata->fatsize == 32) {
 856                mydata->data_begin = mydata->rootdir_sect -
 857                                        (mydata->clust_size * 2);
 858        } else {
 859                rootdir_size = ((bs.dir_entries[1]  * (int)256 +
 860                                 bs.dir_entries[0]) *
 861                                 sizeof(dir_entry)) /
 862                                 mydata->sect_size;
 863                mydata->data_begin = mydata->rootdir_sect +
 864                                        rootdir_size -
 865                                        (mydata->clust_size * 2);
 866        }
 867
 868        mydata->fatbufnum = -1;
 869        mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
 870        if (mydata->fatbuf == NULL) {
 871                debug("Error: allocating memory\n");
 872                return -1;
 873        }
 874
 875        if (vfat_enabled)
 876                debug("VFAT Support enabled\n");
 877
 878        debug("FAT%d, fat_sect: %d, fatlength: %d\n",
 879               mydata->fatsize, mydata->fat_sect, mydata->fatlength);
 880        debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
 881               "Data begins at: %d\n",
 882               root_cluster,
 883               mydata->rootdir_sect,
 884               mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
 885        debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
 886              mydata->clust_size);
 887
 888        /* "cwd" is always the root... */
 889        while (ISDIRDELIM(*filename))
 890                filename++;
 891
 892        /* Make a copy of the filename and convert it to lowercase */
 893        strcpy(fnamecopy, filename);
 894        downcase(fnamecopy);
 895
 896        if (*fnamecopy == '\0') {
 897                if (!dols)
 898                        goto exit;
 899
 900                dols = LS_ROOT;
 901        } else if ((idx = dirdelim(fnamecopy)) >= 0) {
 902                isdir = 1;
 903                fnamecopy[idx] = '\0';
 904                subname = fnamecopy + idx + 1;
 905
 906                /* Handle multiple delimiters */
 907                while (ISDIRDELIM(*subname))
 908                        subname++;
 909        } else if (dols) {
 910                isdir = 1;
 911        }
 912
 913        j = 0;
 914        while (1) {
 915                int i;
 916
 917                if (j == 0) {
 918                        debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n",
 919                                cursect, mydata->clust_size, DIRENTSPERBLOCK);
 920
 921                        if (disk_read(cursect,
 922                                        (mydata->fatsize == 32) ?
 923                                        (mydata->clust_size) :
 924                                        PREFETCH_BLOCKS,
 925                                        do_fat_read_at_block) < 0) {
 926                                debug("Error: reading rootdir block\n");
 927                                goto exit;
 928                        }
 929
 930                        dentptr = (dir_entry *) do_fat_read_at_block;
 931                }
 932
 933                for (i = 0; i < DIRENTSPERBLOCK; i++) {
 934                        char s_name[14], l_name[VFAT_MAXLEN_BYTES];
 935                        __u8 csum;
 936
 937                        l_name[0] = '\0';
 938                        if (dentptr->name[0] == DELETED_FLAG) {
 939                                dentptr++;
 940                                continue;
 941                        }
 942
 943                        if (vfat_enabled)
 944                                csum = mkcksum(dentptr->name, dentptr->ext);
 945
 946                        if (dentptr->attr & ATTR_VOLUME) {
 947                                if (vfat_enabled &&
 948                                    (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
 949                                    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
 950                                        prevcksum =
 951                                                ((dir_slot *)dentptr)->alias_checksum;
 952
 953                                        get_vfatname(mydata,
 954                                                     root_cluster,
 955                                                     do_fat_read_at_block,
 956                                                     dentptr, l_name);
 957
 958                                        if (dols == LS_ROOT) {
 959                                                char dirc;
 960                                                int doit = 0;
 961                                                int isdir =
 962                                                        (dentptr->attr & ATTR_DIR);
 963
 964                                                if (isdir) {
 965                                                        dirs++;
 966                                                        dirc = '/';
 967                                                        doit = 1;
 968                                                } else {
 969                                                        dirc = ' ';
 970                                                        if (l_name[0] != 0) {
 971                                                                files++;
 972                                                                doit = 1;
 973                                                        }
 974                                                }
 975                                                if (doit) {
 976                                                        if (dirc == ' ') {
 977                                                                printf(" %8ld   %s%c\n",
 978                                                                        (long)FAT2CPU32(dentptr->size),
 979                                                                        l_name,
 980                                                                        dirc);
 981                                                        } else {
 982                                                                printf("            %s%c\n",
 983                                                                        l_name,
 984                                                                        dirc);
 985                                                        }
 986                                                }
 987                                                dentptr++;
 988                                                continue;
 989                                        }
 990                                        debug("Rootvfatname: |%s|\n",
 991                                               l_name);
 992                                } else {
 993                                        /* Volume label or VFAT entry */
 994                                        dentptr++;
 995                                        continue;
 996                                }
 997                        } else if (dentptr->name[0] == 0) {
 998                                debug("RootDentname == NULL - %d\n", i);
 999                                if (dols == LS_ROOT) {
1000                                        printf("\n%d file(s), %d dir(s)\n\n",
1001                                                files, dirs);
1002                                        ret = 0;
1003                                }
1004                                goto exit;
1005                        }
1006                        else if (vfat_enabled &&
1007                                 dols == LS_ROOT && csum == prevcksum) {
1008                                prevcksum = 0xffff;
1009                                dentptr++;
1010                                continue;
1011                        }
1012
1013                        get_name(dentptr, s_name);
1014
1015                        if (dols == LS_ROOT) {
1016                                int isdir = (dentptr->attr & ATTR_DIR);
1017                                char dirc;
1018                                int doit = 0;
1019
1020                                if (isdir) {
1021                                        dirc = '/';
1022                                        if (s_name[0] != 0) {
1023                                                dirs++;
1024                                                doit = 1;
1025                                        }
1026                                } else {
1027                                        dirc = ' ';
1028                                        if (s_name[0] != 0) {
1029                                                files++;
1030                                                doit = 1;
1031                                        }
1032                                }
1033                                if (doit) {
1034                                        if (dirc == ' ') {
1035                                                printf(" %8ld   %s%c\n",
1036                                                        (long)FAT2CPU32(dentptr->size),
1037                                                        s_name, dirc);
1038                                        } else {
1039                                                printf("            %s%c\n",
1040                                                        s_name, dirc);
1041                                        }
1042                                }
1043                                dentptr++;
1044                                continue;
1045                        }
1046
1047                        if (strcmp(fnamecopy, s_name)
1048                            && strcmp(fnamecopy, l_name)) {
1049                                debug("RootMismatch: |%s|%s|\n", s_name,
1050                                       l_name);
1051                                dentptr++;
1052                                continue;
1053                        }
1054
1055                        if (isdir && !(dentptr->attr & ATTR_DIR))
1056                                goto exit;
1057
1058                        debug("RootName: %s", s_name);
1059                        debug(", start: 0x%x", START(dentptr));
1060                        debug(", size:  0x%x %s\n",
1061                               FAT2CPU32(dentptr->size),
1062                               isdir ? "(DIR)" : "");
1063
1064                        goto rootdir_done;      /* We got a match */
1065                }
1066                debug("END LOOP: j=%d   clust_size=%d\n", j,
1067                       mydata->clust_size);
1068
1069                /*
1070                 * On FAT32 we must fetch the FAT entries for the next
1071                 * root directory clusters when a cluster has been
1072                 * completely processed.
1073                 */
1074                ++j;
1075                int rootdir_end = 0;
1076                if (mydata->fatsize == 32) {
1077                        if (j == mydata->clust_size) {
1078                                int nxtsect = 0;
1079                                int nxt_clust = 0;
1080
1081                                nxt_clust = get_fatent(mydata, root_cluster);
1082                                rootdir_end = CHECK_CLUST(nxt_clust, 32);
1083
1084                                nxtsect = mydata->data_begin +
1085                                        (nxt_clust * mydata->clust_size);
1086
1087                                root_cluster = nxt_clust;
1088
1089                                cursect = nxtsect;
1090                                j = 0;
1091                        }
1092                } else {
1093                        if (j == PREFETCH_BLOCKS)
1094                                j = 0;
1095
1096                        rootdir_end = (++cursect - mydata->rootdir_sect >=
1097                                       rootdir_size);
1098                }
1099
1100                /* If end of rootdir reached */
1101                if (rootdir_end) {
1102                        if (dols == LS_ROOT) {
1103                                printf("\n%d file(s), %d dir(s)\n\n",
1104                                       files, dirs);
1105                                ret = 0;
1106                        }
1107                        goto exit;
1108                }
1109        }
1110rootdir_done:
1111
1112        firsttime = 1;
1113
1114        while (isdir) {
1115                int startsect = mydata->data_begin
1116                        + START(dentptr) * mydata->clust_size;
1117                dir_entry dent;
1118                char *nextname = NULL;
1119
1120                dent = *dentptr;
1121                dentptr = &dent;
1122
1123                idx = dirdelim(subname);
1124
1125                if (idx >= 0) {
1126                        subname[idx] = '\0';
1127                        nextname = subname + idx + 1;
1128                        /* Handle multiple delimiters */
1129                        while (ISDIRDELIM(*nextname))
1130                                nextname++;
1131                        if (dols && *nextname == '\0')
1132                                firsttime = 0;
1133                } else {
1134                        if (dols && firsttime) {
1135                                firsttime = 0;
1136                        } else {
1137                                isdir = 0;
1138                        }
1139                }
1140
1141                if (get_dentfromdir(mydata, startsect, subname, dentptr,
1142                                     isdir ? 0 : dols) == NULL) {
1143                        if (dols && !isdir)
1144                                ret = 0;
1145                        goto exit;
1146                }
1147
1148                if (isdir && !(dentptr->attr & ATTR_DIR))
1149                        goto exit;
1150
1151                if (idx >= 0)
1152                        subname = nextname;
1153        }
1154
1155        if (dogetsize)
1156                ret = FAT2CPU32(dentptr->size);
1157        else
1158                ret = get_contents(mydata, dentptr, pos, buffer, maxsize);
1159        debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
1160
1161exit:
1162        free(mydata->fatbuf);
1163        return ret;
1164}
1165
1166long
1167do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
1168{
1169        return do_fat_read_at(filename, 0, buffer, maxsize, dols, 0);
1170}
1171
1172int file_fat_detectfs(void)
1173{
1174        boot_sector bs;
1175        volume_info volinfo;
1176        int fatsize;
1177        char vol_label[12];
1178
1179        if (cur_dev == NULL) {
1180                printf("No current device\n");
1181                return 1;
1182        }
1183
1184#if defined(CONFIG_CMD_IDE) || \
1185    defined(CONFIG_CMD_SATA) || \
1186    defined(CONFIG_CMD_SCSI) || \
1187    defined(CONFIG_CMD_USB) || \
1188    defined(CONFIG_MMC)
1189        printf("Interface:  ");
1190        switch (cur_dev->if_type) {
1191        case IF_TYPE_IDE:
1192                printf("IDE");
1193                break;
1194        case IF_TYPE_SATA:
1195                printf("SATA");
1196                break;
1197        case IF_TYPE_SCSI:
1198                printf("SCSI");
1199                break;
1200        case IF_TYPE_ATAPI:
1201                printf("ATAPI");
1202                break;
1203        case IF_TYPE_USB:
1204                printf("USB");
1205                break;
1206        case IF_TYPE_DOC:
1207                printf("DOC");
1208                break;
1209        case IF_TYPE_MMC:
1210                printf("MMC");
1211                break;
1212        default:
1213                printf("Unknown");
1214        }
1215
1216        printf("\n  Device %d: ", cur_dev->dev);
1217        dev_print(cur_dev);
1218#endif
1219
1220        if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
1221                printf("\nNo valid FAT fs found\n");
1222                return 1;
1223        }
1224
1225        memcpy(vol_label, volinfo.volume_label, 11);
1226        vol_label[11] = '\0';
1227        volinfo.fs_type[5] = '\0';
1228
1229        printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
1230
1231        return 0;
1232}
1233
1234int file_fat_ls(const char *dir)
1235{
1236        return do_fat_read(dir, NULL, 0, LS_YES);
1237}
1238
1239int fat_exists(const char *filename)
1240{
1241        int sz;
1242        sz = do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1);
1243        return sz >= 0;
1244}
1245
1246int fat_size(const char *filename)
1247{
1248        return do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1);
1249}
1250
1251long file_fat_read_at(const char *filename, unsigned long pos, void *buffer,
1252                      unsigned long maxsize)
1253{
1254        printf("reading %s\n", filename);
1255        return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO, 0);
1256}
1257
1258long file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
1259{
1260        return file_fat_read_at(filename, 0, buffer, maxsize);
1261}
1262
1263int fat_read_file(const char *filename, void *buf, int offset, int len)
1264{
1265        int len_read;
1266
1267        len_read = file_fat_read_at(filename, offset, buf, len);
1268        if (len_read == -1) {
1269                printf("** Unable to read file %s **\n", filename);
1270                return -1;
1271        }
1272
1273        return len_read;
1274}
1275
1276void fat_close(void)
1277{
1278}
1279