171f95118Swdenk /* 271f95118Swdenk * fat.c 371f95118Swdenk * 471f95118Swdenk * R/O (V)FAT 12/16/32 filesystem implementation by Marcus Sundberg 571f95118Swdenk * 671f95118Swdenk * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6 771f95118Swdenk * 2003-03-10 - kharris@nexus-tech.net - ported to uboot 871f95118Swdenk * 971f95118Swdenk * See file CREDITS for list of people who contributed to this 1071f95118Swdenk * project. 1171f95118Swdenk * 1271f95118Swdenk * This program is free software; you can redistribute it and/or 1371f95118Swdenk * modify it under the terms of the GNU General Public License as 1471f95118Swdenk * published by the Free Software Foundation; either version 2 of 1571f95118Swdenk * the License, or (at your option) any later version. 1671f95118Swdenk * 1771f95118Swdenk * This program is distributed in the hope that it will be useful, 1871f95118Swdenk * but WITHOUT ANY WARRANTY; without even the implied warranty of 1971f95118Swdenk * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 2071f95118Swdenk * GNU General Public License for more details. 2171f95118Swdenk * 2271f95118Swdenk * You should have received a copy of the GNU General Public License 2371f95118Swdenk * along with this program; if not, write to the Free Software 2471f95118Swdenk * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 2571f95118Swdenk * MA 02111-1307 USA 2671f95118Swdenk */ 2771f95118Swdenk 2871f95118Swdenk #include <common.h> 2971f95118Swdenk #include <config.h> 3071f95118Swdenk #include <fat.h> 3171f95118Swdenk #include <asm/byteorder.h> 327205e407Swdenk #include <part.h> 3371f95118Swdenk 3471f95118Swdenk /* 3571f95118Swdenk * Convert a string to lowercase. 3671f95118Swdenk */ 377385c28eSWolfgang Denk static void downcase (char *str) 3871f95118Swdenk { 3971f95118Swdenk while (*str != '\0') { 4071f95118Swdenk TOLOWER(*str); 4171f95118Swdenk str++; 4271f95118Swdenk } 4371f95118Swdenk } 4471f95118Swdenk 457205e407Swdenk static block_dev_desc_t *cur_dev = NULL; 467385c28eSWolfgang Denk 477205e407Swdenk static unsigned long part_offset = 0; 487385c28eSWolfgang Denk 497205e407Swdenk static int cur_part = 1; 507205e407Swdenk 517205e407Swdenk #define DOS_PART_TBL_OFFSET 0x1be 527205e407Swdenk #define DOS_PART_MAGIC_OFFSET 0x1fe 537205e407Swdenk #define DOS_FS_TYPE_OFFSET 0x36 5466c2d73cSWolfgang Denk #define DOS_FS32_TYPE_OFFSET 0x52 5571f95118Swdenk 567385c28eSWolfgang Denk static int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr) 5771f95118Swdenk { 587205e407Swdenk if (cur_dev == NULL) 597205e407Swdenk return -1; 607385c28eSWolfgang Denk 617385c28eSWolfgang Denk startblock += part_offset; 627385c28eSWolfgang Denk 637205e407Swdenk if (cur_dev->block_read) { 647385c28eSWolfgang Denk return cur_dev->block_read(cur_dev->dev, startblock, getsize, 657385c28eSWolfgang Denk (unsigned long *) bufptr); 6671f95118Swdenk } 6771f95118Swdenk return -1; 6871f95118Swdenk } 6971f95118Swdenk 707385c28eSWolfgang Denk int fat_register_device (block_dev_desc_t * dev_desc, int part_no) 7171f95118Swdenk { 727205e407Swdenk unsigned char buffer[SECTOR_SIZE]; 737385c28eSWolfgang Denk 74566a494fSHeiko Schocher disk_partition_t info; 757205e407Swdenk 767205e407Swdenk if (!dev_desc->block_read) 777205e407Swdenk return -1; 787385c28eSWolfgang Denk 797205e407Swdenk cur_dev = dev_desc; 807205e407Swdenk /* check if we have a MBR (on floppies we have only a PBR) */ 817205e407Swdenk if (dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *)buffer) != 1) { 827385c28eSWolfgang Denk printf("** Can't read from device %d **\n", 837385c28eSWolfgang Denk dev_desc->dev); 847205e407Swdenk return -1; 857205e407Swdenk } 867205e407Swdenk if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 || 877205e407Swdenk buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) { 887205e407Swdenk /* no signature found */ 897205e407Swdenk return -1; 907205e407Swdenk } 91dd60d122SJon Loeliger #if (defined(CONFIG_CMD_IDE) || \ 9275eb82ecSunsik Kim defined(CONFIG_CMD_MG_DISK) || \ 938c5170a7SSonic Zhang defined(CONFIG_CMD_SATA) || \ 94dd60d122SJon Loeliger defined(CONFIG_CMD_SCSI) || \ 95dd60d122SJon Loeliger defined(CONFIG_CMD_USB) || \ 9602df4a27SAndy Fleming defined(CONFIG_MMC) || \ 97b0d8f5bfSPeter Pearse defined(CONFIG_SYSTEMACE) ) 987385c28eSWolfgang Denk /* First we assume there is a MBR */ 997205e407Swdenk if (!get_partition_info(dev_desc, part_no, &info)) { 1007205e407Swdenk part_offset = info.start; 1017205e407Swdenk cur_part = part_no; 1027385c28eSWolfgang Denk } else if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) || 1037385c28eSWolfgang Denk (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) { 104566a494fSHeiko Schocher /* ok, we assume we are on a PBR only */ 105566a494fSHeiko Schocher cur_part = 1; 106566a494fSHeiko Schocher part_offset = 0; 107566a494fSHeiko Schocher } else { 108bf1060eaSWolfgang Denk printf("** Partition %d not valid on device %d **\n", 109bf1060eaSWolfgang Denk part_no, dev_desc->dev); 1107205e407Swdenk return -1; 1117205e407Swdenk } 11202df4a27SAndy Fleming 1137205e407Swdenk #else 11466c2d73cSWolfgang Denk if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) || 11566c2d73cSWolfgang Denk (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) { 116566a494fSHeiko Schocher /* ok, we assume we are on a PBR only */ 117566a494fSHeiko Schocher cur_part = 1; 118566a494fSHeiko Schocher part_offset = 0; 119566a494fSHeiko Schocher info.start = part_offset; 120566a494fSHeiko Schocher } else { 1217205e407Swdenk /* FIXME we need to determine the start block of the 1227205e407Swdenk * partition where the DOS FS resides. This can be done 1237205e407Swdenk * by using the get_partition_info routine. For this 1247205e407Swdenk * purpose the libpart must be included. 1257205e407Swdenk */ 1267205e407Swdenk part_offset = 32; 1277205e407Swdenk cur_part = 1; 1287205e407Swdenk } 129566a494fSHeiko Schocher #endif 13071f95118Swdenk return 0; 13171f95118Swdenk } 13271f95118Swdenk 13371f95118Swdenk /* 13471f95118Swdenk * Get the first occurence of a directory delimiter ('/' or '\') in a string. 13571f95118Swdenk * Return index into string if found, -1 otherwise. 13671f95118Swdenk */ 1377385c28eSWolfgang Denk static int dirdelim (char *str) 13871f95118Swdenk { 13971f95118Swdenk char *start = str; 14071f95118Swdenk 14171f95118Swdenk while (*str != '\0') { 1427385c28eSWolfgang Denk if (ISDIRDELIM(*str)) 1437385c28eSWolfgang Denk return str - start; 14471f95118Swdenk str++; 14571f95118Swdenk } 14671f95118Swdenk return -1; 14771f95118Swdenk } 14871f95118Swdenk 14971f95118Swdenk /* 15071f95118Swdenk * Extract zero terminated short name from a directory entry. 15171f95118Swdenk */ 15271f95118Swdenk static void get_name (dir_entry *dirent, char *s_name) 15371f95118Swdenk { 15471f95118Swdenk char *ptr; 15571f95118Swdenk 15671f95118Swdenk memcpy(s_name, dirent->name, 8); 15771f95118Swdenk s_name[8] = '\0'; 15871f95118Swdenk ptr = s_name; 15971f95118Swdenk while (*ptr && *ptr != ' ') 16071f95118Swdenk ptr++; 16171f95118Swdenk if (dirent->ext[0] && dirent->ext[0] != ' ') { 16271f95118Swdenk *ptr = '.'; 16371f95118Swdenk ptr++; 16471f95118Swdenk memcpy(ptr, dirent->ext, 3); 16571f95118Swdenk ptr[3] = '\0'; 16671f95118Swdenk while (*ptr && *ptr != ' ') 16771f95118Swdenk ptr++; 16871f95118Swdenk } 16971f95118Swdenk *ptr = '\0'; 17071f95118Swdenk if (*s_name == DELETED_FLAG) 17171f95118Swdenk *s_name = '\0'; 17271f95118Swdenk else if (*s_name == aRING) 1733c2c2f42SRemy Bohmer *s_name = DELETED_FLAG; 17471f95118Swdenk downcase(s_name); 17571f95118Swdenk } 17671f95118Swdenk 17771f95118Swdenk /* 17871f95118Swdenk * Get the entry at index 'entry' in a FAT (12/16/32) table. 17971f95118Swdenk * On failure 0x00 is returned. 18071f95118Swdenk */ 1817385c28eSWolfgang Denk static __u32 get_fatent (fsdata *mydata, __u32 entry) 18271f95118Swdenk { 18371f95118Swdenk __u32 bufnum; 1847385c28eSWolfgang Denk __u32 off16, offset; 18571f95118Swdenk __u32 ret = 0x00; 1867385c28eSWolfgang Denk __u16 val1, val2; 18771f95118Swdenk 18871f95118Swdenk switch (mydata->fatsize) { 18971f95118Swdenk case 32: 19071f95118Swdenk bufnum = entry / FAT32BUFSIZE; 19171f95118Swdenk offset = entry - bufnum * FAT32BUFSIZE; 19271f95118Swdenk break; 19371f95118Swdenk case 16: 19471f95118Swdenk bufnum = entry / FAT16BUFSIZE; 19571f95118Swdenk offset = entry - bufnum * FAT16BUFSIZE; 19671f95118Swdenk break; 19771f95118Swdenk case 12: 19871f95118Swdenk bufnum = entry / FAT12BUFSIZE; 19971f95118Swdenk offset = entry - bufnum * FAT12BUFSIZE; 20071f95118Swdenk break; 20171f95118Swdenk 20271f95118Swdenk default: 20371f95118Swdenk /* Unsupported FAT size */ 20471f95118Swdenk return ret; 20571f95118Swdenk } 20671f95118Swdenk 2077385c28eSWolfgang Denk debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n", 2082aa98c66SWolfgang Denk mydata->fatsize, entry, entry, offset, offset); 2092aa98c66SWolfgang Denk 21071f95118Swdenk /* Read a new block of FAT entries into the cache. */ 21171f95118Swdenk if (bufnum != mydata->fatbufnum) { 212*3f270f42SErik Hansen __u32 getsize = FATBUFSIZE / FS_BLOCK_SIZE; 21371f95118Swdenk __u8 *bufptr = mydata->fatbuf; 21471f95118Swdenk __u32 fatlength = mydata->fatlength; 21571f95118Swdenk __u32 startblock = bufnum * FATBUFBLOCKS; 21671f95118Swdenk 21771f95118Swdenk fatlength *= SECTOR_SIZE; /* We want it in bytes now */ 21871f95118Swdenk startblock += mydata->fat_sect; /* Offset from start of disk */ 21971f95118Swdenk 2207385c28eSWolfgang Denk if (getsize > fatlength) 2217385c28eSWolfgang Denk getsize = fatlength; 22271f95118Swdenk if (disk_read(startblock, getsize, bufptr) < 0) { 2237385c28eSWolfgang Denk debug("Error reading FAT blocks\n"); 22471f95118Swdenk return ret; 22571f95118Swdenk } 22671f95118Swdenk mydata->fatbufnum = bufnum; 22771f95118Swdenk } 22871f95118Swdenk 22971f95118Swdenk /* Get the actual entry from the table */ 23071f95118Swdenk switch (mydata->fatsize) { 23171f95118Swdenk case 32: 23271f95118Swdenk ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]); 23371f95118Swdenk break; 23471f95118Swdenk case 16: 23571f95118Swdenk ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]); 23671f95118Swdenk break; 2377385c28eSWolfgang Denk case 12: 2387385c28eSWolfgang Denk off16 = (offset * 3) / 4; 23971f95118Swdenk 24071f95118Swdenk switch (offset & 0x3) { 24171f95118Swdenk case 0: 24271f95118Swdenk ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]); 24371f95118Swdenk ret &= 0xfff; 24471f95118Swdenk break; 24571f95118Swdenk case 1: 24671f95118Swdenk val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 24771f95118Swdenk val1 &= 0xf000; 24871f95118Swdenk val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); 24971f95118Swdenk val2 &= 0x00ff; 25071f95118Swdenk ret = (val2 << 4) | (val1 >> 12); 25171f95118Swdenk break; 25271f95118Swdenk case 2: 25371f95118Swdenk val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 25471f95118Swdenk val1 &= 0xff00; 25571f95118Swdenk val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); 25671f95118Swdenk val2 &= 0x000f; 25771f95118Swdenk ret = (val2 << 8) | (val1 >> 8); 25871f95118Swdenk break; 25971f95118Swdenk case 3: 2607385c28eSWolfgang Denk ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 26171f95118Swdenk ret = (ret & 0xfff0) >> 4; 26271f95118Swdenk break; 26371f95118Swdenk default: 26471f95118Swdenk break; 26571f95118Swdenk } 26671f95118Swdenk break; 26771f95118Swdenk } 2687385c28eSWolfgang Denk debug("FAT%d: ret: %08x, offset: %04x\n", 2692aa98c66SWolfgang Denk mydata->fatsize, ret, offset); 27071f95118Swdenk 27171f95118Swdenk return ret; 27271f95118Swdenk } 27371f95118Swdenk 27471f95118Swdenk /* 27571f95118Swdenk * Read at most 'size' bytes from the specified cluster into 'buffer'. 27671f95118Swdenk * Return 0 on success, -1 otherwise. 27771f95118Swdenk */ 27871f95118Swdenk static int 2797385c28eSWolfgang Denk get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer, 2807385c28eSWolfgang Denk unsigned long size) 28171f95118Swdenk { 282*3f270f42SErik Hansen __u32 idx = 0; 28371f95118Swdenk __u32 startsect; 28471f95118Swdenk 28571f95118Swdenk if (clustnum > 0) { 2867385c28eSWolfgang Denk startsect = mydata->data_begin + 2877385c28eSWolfgang Denk clustnum * mydata->clust_size; 28871f95118Swdenk } else { 28971f95118Swdenk startsect = mydata->rootdir_sect; 29071f95118Swdenk } 29171f95118Swdenk 2927385c28eSWolfgang Denk debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect); 2937385c28eSWolfgang Denk 2947205e407Swdenk if (disk_read(startsect, size / FS_BLOCK_SIZE, buffer) < 0) { 2957385c28eSWolfgang Denk debug("Error reading data\n"); 29671f95118Swdenk return -1; 29771f95118Swdenk } 2987205e407Swdenk if (size % FS_BLOCK_SIZE) { 29971f95118Swdenk __u8 tmpbuf[FS_BLOCK_SIZE]; 3007385c28eSWolfgang Denk 3017205e407Swdenk idx = size / FS_BLOCK_SIZE; 30271f95118Swdenk if (disk_read(startsect + idx, 1, tmpbuf) < 0) { 3037385c28eSWolfgang Denk debug("Error reading data\n"); 30471f95118Swdenk return -1; 30571f95118Swdenk } 3067205e407Swdenk buffer += idx * FS_BLOCK_SIZE; 30771f95118Swdenk 3087205e407Swdenk memcpy(buffer, tmpbuf, size % FS_BLOCK_SIZE); 30971f95118Swdenk return 0; 31071f95118Swdenk } 31171f95118Swdenk 31271f95118Swdenk return 0; 31371f95118Swdenk } 31471f95118Swdenk 31571f95118Swdenk /* 31671f95118Swdenk * Read at most 'maxsize' bytes from the file associated with 'dentptr' 31771f95118Swdenk * into 'buffer'. 31871f95118Swdenk * Return the number of bytes read or -1 on fatal errors. 31971f95118Swdenk */ 32071f95118Swdenk static long 32171f95118Swdenk get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer, 32271f95118Swdenk unsigned long maxsize) 32371f95118Swdenk { 32471f95118Swdenk unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0; 32571f95118Swdenk unsigned int bytesperclust = mydata->clust_size * SECTOR_SIZE; 32671f95118Swdenk __u32 curclust = START(dentptr); 3277205e407Swdenk __u32 endclust, newclust; 3287205e407Swdenk unsigned long actsize; 32971f95118Swdenk 3307385c28eSWolfgang Denk debug("Filesize: %ld bytes\n", filesize); 33171f95118Swdenk 3327385c28eSWolfgang Denk if (maxsize > 0 && filesize > maxsize) 3337385c28eSWolfgang Denk filesize = maxsize; 33471f95118Swdenk 3357385c28eSWolfgang Denk debug("%ld bytes\n", filesize); 33671f95118Swdenk 3377205e407Swdenk actsize = bytesperclust; 3387205e407Swdenk endclust = curclust; 3397385c28eSWolfgang Denk 34071f95118Swdenk do { 3417205e407Swdenk /* search for consecutive clusters */ 3427205e407Swdenk while (actsize < filesize) { 3437205e407Swdenk newclust = get_fatent(mydata, endclust); 3447205e407Swdenk if ((newclust - 1) != endclust) 3457205e407Swdenk goto getit; 3468ce4e5c2Smichael if (CHECK_CLUST(newclust, mydata->fatsize)) { 3477385c28eSWolfgang Denk debug("curclust: 0x%x\n", newclust); 3487385c28eSWolfgang Denk debug("Invalid FAT entry\n"); 3497205e407Swdenk return gotsize; 3507205e407Swdenk } 3517205e407Swdenk endclust = newclust; 3527205e407Swdenk actsize += bytesperclust; 3537205e407Swdenk } 3547385c28eSWolfgang Denk 3557205e407Swdenk /* actsize >= file size */ 3567205e407Swdenk actsize -= bytesperclust; 3577385c28eSWolfgang Denk 3587205e407Swdenk /* get remaining clusters */ 3597205e407Swdenk if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 3607385c28eSWolfgang Denk printf("Error reading cluster\n"); 36171f95118Swdenk return -1; 36271f95118Swdenk } 3637385c28eSWolfgang Denk 3647205e407Swdenk /* get remaining bytes */ 3657205e407Swdenk gotsize += (int)actsize; 3667205e407Swdenk filesize -= actsize; 3677205e407Swdenk buffer += actsize; 3687205e407Swdenk actsize = filesize; 3697205e407Swdenk if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) { 3707385c28eSWolfgang Denk printf("Error reading cluster\n"); 3717205e407Swdenk return -1; 3727205e407Swdenk } 3737205e407Swdenk gotsize += actsize; 3747205e407Swdenk return gotsize; 3757205e407Swdenk getit: 3767205e407Swdenk if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 3777385c28eSWolfgang Denk printf("Error reading cluster\n"); 3787205e407Swdenk return -1; 3797205e407Swdenk } 3807205e407Swdenk gotsize += (int)actsize; 3817205e407Swdenk filesize -= actsize; 3827205e407Swdenk buffer += actsize; 3837385c28eSWolfgang Denk 3847205e407Swdenk curclust = get_fatent(mydata, endclust); 3858ce4e5c2Smichael if (CHECK_CLUST(curclust, mydata->fatsize)) { 3867385c28eSWolfgang Denk debug("curclust: 0x%x\n", curclust); 3877385c28eSWolfgang Denk printf("Invalid FAT entry\n"); 38871f95118Swdenk return gotsize; 38971f95118Swdenk } 3907205e407Swdenk actsize = bytesperclust; 3917205e407Swdenk endclust = curclust; 39271f95118Swdenk } while (1); 39371f95118Swdenk } 39471f95118Swdenk 39571f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 39671f95118Swdenk /* 39771f95118Swdenk * Extract the file name information from 'slotptr' into 'l_name', 39871f95118Swdenk * starting at l_name[*idx]. 39971f95118Swdenk * Return 1 if terminator (zero byte) is found, 0 otherwise. 40071f95118Swdenk */ 4017385c28eSWolfgang Denk static int slot2str (dir_slot *slotptr, char *l_name, int *idx) 40271f95118Swdenk { 40371f95118Swdenk int j; 40471f95118Swdenk 40571f95118Swdenk for (j = 0; j <= 8; j += 2) { 40671f95118Swdenk l_name[*idx] = slotptr->name0_4[j]; 4077385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4087385c28eSWolfgang Denk return 1; 40971f95118Swdenk (*idx)++; 41071f95118Swdenk } 41171f95118Swdenk for (j = 0; j <= 10; j += 2) { 41271f95118Swdenk l_name[*idx] = slotptr->name5_10[j]; 4137385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4147385c28eSWolfgang Denk return 1; 41571f95118Swdenk (*idx)++; 41671f95118Swdenk } 41771f95118Swdenk for (j = 0; j <= 2; j += 2) { 41871f95118Swdenk l_name[*idx] = slotptr->name11_12[j]; 4197385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4207385c28eSWolfgang Denk return 1; 42171f95118Swdenk (*idx)++; 42271f95118Swdenk } 42371f95118Swdenk 42471f95118Swdenk return 0; 42571f95118Swdenk } 42671f95118Swdenk 42771f95118Swdenk /* 42871f95118Swdenk * Extract the full long filename starting at 'retdent' (which is really 42971f95118Swdenk * a slot) into 'l_name'. If successful also copy the real directory entry 43071f95118Swdenk * into 'retdent' 43171f95118Swdenk * Return 0 on success, -1 otherwise. 43271f95118Swdenk */ 4337e4b9b4fSBryan Wu __attribute__ ((__aligned__ (__alignof__ (dir_entry)))) 4345fa66df6Swdenk __u8 get_vfatname_block[MAX_CLUSTSIZE]; 4357385c28eSWolfgang Denk 43671f95118Swdenk static int 43771f95118Swdenk get_vfatname (fsdata *mydata, int curclust, __u8 *cluster, 43871f95118Swdenk dir_entry *retdent, char *l_name) 43971f95118Swdenk { 44071f95118Swdenk dir_entry *realdent; 44171f95118Swdenk dir_slot *slotptr = (dir_slot *)retdent; 4423831530dSMikhail Zolotaryov __u8 *buflimit = cluster + ((curclust == 0) ? 4433831530dSMikhail Zolotaryov LINEAR_PREFETCH_SIZE : 4443831530dSMikhail Zolotaryov (mydata->clust_size * SECTOR_SIZE) 4453831530dSMikhail Zolotaryov ); 4462d1a537dSwdenk __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; 44771f95118Swdenk int idx = 0; 44871f95118Swdenk 4493831530dSMikhail Zolotaryov if (counter > VFAT_MAXSEQ) { 4503831530dSMikhail Zolotaryov debug("Error: VFAT name is too long\n"); 4513831530dSMikhail Zolotaryov return -1; 4523831530dSMikhail Zolotaryov } 4533831530dSMikhail Zolotaryov 4543831530dSMikhail Zolotaryov while ((__u8 *)slotptr < buflimit) { 4557385c28eSWolfgang Denk if (counter == 0) 4567385c28eSWolfgang Denk break; 4572d1a537dSwdenk if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) 4582d1a537dSwdenk return -1; 45971f95118Swdenk slotptr++; 46071f95118Swdenk counter--; 46171f95118Swdenk } 46271f95118Swdenk 4633831530dSMikhail Zolotaryov if ((__u8 *)slotptr >= buflimit) { 46471f95118Swdenk dir_slot *slotptr2; 46571f95118Swdenk 4663831530dSMikhail Zolotaryov if (curclust == 0) 4673831530dSMikhail Zolotaryov return -1; 46871f95118Swdenk curclust = get_fatent(mydata, curclust); 4698ce4e5c2Smichael if (CHECK_CLUST(curclust, mydata->fatsize)) { 4707385c28eSWolfgang Denk debug("curclust: 0x%x\n", curclust); 4717385c28eSWolfgang Denk printf("Invalid FAT entry\n"); 47271f95118Swdenk return -1; 47371f95118Swdenk } 4747385c28eSWolfgang Denk 4755fa66df6Swdenk if (get_cluster(mydata, curclust, get_vfatname_block, 47671f95118Swdenk mydata->clust_size * SECTOR_SIZE) != 0) { 4777385c28eSWolfgang Denk debug("Error: reading directory block\n"); 47871f95118Swdenk return -1; 47971f95118Swdenk } 4807385c28eSWolfgang Denk 4815fa66df6Swdenk slotptr2 = (dir_slot *)get_vfatname_block; 4823831530dSMikhail Zolotaryov while (counter > 0) { 4833831530dSMikhail Zolotaryov if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK) 4843831530dSMikhail Zolotaryov & 0xff) != counter) 4853831530dSMikhail Zolotaryov return -1; 48671f95118Swdenk slotptr2++; 4873831530dSMikhail Zolotaryov counter--; 4883831530dSMikhail Zolotaryov } 4897385c28eSWolfgang Denk 49071f95118Swdenk /* Save the real directory entry */ 4913831530dSMikhail Zolotaryov realdent = (dir_entry *)slotptr2; 4923831530dSMikhail Zolotaryov while ((__u8 *)slotptr2 > get_vfatname_block) { 49371f95118Swdenk slotptr2--; 4943831530dSMikhail Zolotaryov slot2str(slotptr2, l_name, &idx); 49571f95118Swdenk } 49671f95118Swdenk } else { 49771f95118Swdenk /* Save the real directory entry */ 49871f95118Swdenk realdent = (dir_entry *)slotptr; 49971f95118Swdenk } 50071f95118Swdenk 50171f95118Swdenk do { 50271f95118Swdenk slotptr--; 5037385c28eSWolfgang Denk if (slot2str(slotptr, l_name, &idx)) 5047385c28eSWolfgang Denk break; 5052d1a537dSwdenk } while (!(slotptr->id & LAST_LONG_ENTRY_MASK)); 50671f95118Swdenk 50771f95118Swdenk l_name[idx] = '\0'; 5087385c28eSWolfgang Denk if (*l_name == DELETED_FLAG) 5097385c28eSWolfgang Denk *l_name = '\0'; 5107385c28eSWolfgang Denk else if (*l_name == aRING) 5117385c28eSWolfgang Denk *l_name = DELETED_FLAG; 51271f95118Swdenk downcase(l_name); 51371f95118Swdenk 51471f95118Swdenk /* Return the real directory entry */ 51571f95118Swdenk memcpy(retdent, realdent, sizeof(dir_entry)); 51671f95118Swdenk 51771f95118Swdenk return 0; 51871f95118Swdenk } 51971f95118Swdenk 52071f95118Swdenk /* Calculate short name checksum */ 5217385c28eSWolfgang Denk static __u8 mkcksum (const char *str) 52271f95118Swdenk { 52371f95118Swdenk int i; 5247385c28eSWolfgang Denk 52571f95118Swdenk __u8 ret = 0; 52671f95118Swdenk 52771f95118Swdenk for (i = 0; i < 11; i++) { 52871f95118Swdenk ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i]; 52971f95118Swdenk } 53071f95118Swdenk 53171f95118Swdenk return ret; 53271f95118Swdenk } 5337385c28eSWolfgang Denk #endif /* CONFIG_SUPPORT_VFAT */ 53471f95118Swdenk 53571f95118Swdenk /* 53671f95118Swdenk * Get the directory entry associated with 'filename' from the directory 53771f95118Swdenk * starting at 'startsect' 53871f95118Swdenk */ 5397e4b9b4fSBryan Wu __attribute__ ((__aligned__ (__alignof__ (dir_entry)))) 5405fa66df6Swdenk __u8 get_dentfromdir_block[MAX_CLUSTSIZE]; 5417385c28eSWolfgang Denk 54271f95118Swdenk static dir_entry *get_dentfromdir (fsdata *mydata, int startsect, 54371f95118Swdenk char *filename, dir_entry *retdent, 54471f95118Swdenk int dols) 54571f95118Swdenk { 54671f95118Swdenk __u16 prevcksum = 0xffff; 54771f95118Swdenk __u32 curclust = START(retdent); 54871f95118Swdenk int files = 0, dirs = 0; 54971f95118Swdenk 5507385c28eSWolfgang Denk debug("get_dentfromdir: %s\n", filename); 5517385c28eSWolfgang Denk 55271f95118Swdenk while (1) { 55371f95118Swdenk dir_entry *dentptr; 5547385c28eSWolfgang Denk 55571f95118Swdenk int i; 55671f95118Swdenk 5575fa66df6Swdenk if (get_cluster(mydata, curclust, get_dentfromdir_block, 55871f95118Swdenk mydata->clust_size * SECTOR_SIZE) != 0) { 5597385c28eSWolfgang Denk debug("Error: reading directory block\n"); 56071f95118Swdenk return NULL; 56171f95118Swdenk } 5627385c28eSWolfgang Denk 5635fa66df6Swdenk dentptr = (dir_entry *)get_dentfromdir_block; 5647385c28eSWolfgang Denk 56571f95118Swdenk for (i = 0; i < DIRENTSPERCLUST; i++) { 5663831530dSMikhail Zolotaryov char s_name[14], l_name[VFAT_MAXLEN_BYTES]; 56771f95118Swdenk 56871f95118Swdenk l_name[0] = '\0'; 569855a496fSwdenk if (dentptr->name[0] == DELETED_FLAG) { 570855a496fSwdenk dentptr++; 571855a496fSwdenk continue; 572855a496fSwdenk } 57371f95118Swdenk if ((dentptr->attr & ATTR_VOLUME)) { 57471f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 57571f95118Swdenk if ((dentptr->attr & ATTR_VFAT) && 5762d1a537dSwdenk (dentptr-> name[0] & LAST_LONG_ENTRY_MASK)) { 5777385c28eSWolfgang Denk prevcksum = ((dir_slot *)dentptr)->alias_checksum; 5787385c28eSWolfgang Denk get_vfatname(mydata, curclust, 5797385c28eSWolfgang Denk get_dentfromdir_block, 58071f95118Swdenk dentptr, l_name); 58171f95118Swdenk if (dols) { 5827385c28eSWolfgang Denk int isdir; 58371f95118Swdenk char dirc; 58471f95118Swdenk int doit = 0; 58571f95118Swdenk 5867385c28eSWolfgang Denk isdir = (dentptr->attr & ATTR_DIR); 5877385c28eSWolfgang Denk 58871f95118Swdenk if (isdir) { 58971f95118Swdenk dirs++; 59071f95118Swdenk dirc = '/'; 59171f95118Swdenk doit = 1; 59271f95118Swdenk } else { 59371f95118Swdenk dirc = ' '; 59471f95118Swdenk if (l_name[0] != 0) { 59571f95118Swdenk files++; 59671f95118Swdenk doit = 1; 59771f95118Swdenk } 59871f95118Swdenk } 59971f95118Swdenk if (doit) { 60071f95118Swdenk if (dirc == ' ') { 60171f95118Swdenk printf(" %8ld %s%c\n", 60271f95118Swdenk (long)FAT2CPU32(dentptr->size), 6037385c28eSWolfgang Denk l_name, 6047385c28eSWolfgang Denk dirc); 60571f95118Swdenk } else { 6067385c28eSWolfgang Denk printf(" %s%c\n", 6077385c28eSWolfgang Denk l_name, 6087385c28eSWolfgang Denk dirc); 60971f95118Swdenk } 61071f95118Swdenk } 61171f95118Swdenk dentptr++; 61271f95118Swdenk continue; 61371f95118Swdenk } 6147385c28eSWolfgang Denk debug("vfatname: |%s|\n", l_name); 61571f95118Swdenk } else 61671f95118Swdenk #endif 61771f95118Swdenk { 61871f95118Swdenk /* Volume label or VFAT entry */ 61971f95118Swdenk dentptr++; 62071f95118Swdenk continue; 62171f95118Swdenk } 62271f95118Swdenk } 62371f95118Swdenk if (dentptr->name[0] == 0) { 62471f95118Swdenk if (dols) { 6257385c28eSWolfgang Denk printf("\n%d file(s), %d dir(s)\n\n", 6267385c28eSWolfgang Denk files, dirs); 62771f95118Swdenk } 6287385c28eSWolfgang Denk debug("Dentname == NULL - %d\n", i); 62971f95118Swdenk return NULL; 63071f95118Swdenk } 63171f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 63271f95118Swdenk if (dols && mkcksum(dentptr->name) == prevcksum) { 63371f95118Swdenk dentptr++; 63471f95118Swdenk continue; 63571f95118Swdenk } 63671f95118Swdenk #endif 63771f95118Swdenk get_name(dentptr, s_name); 63871f95118Swdenk if (dols) { 63971f95118Swdenk int isdir = (dentptr->attr & ATTR_DIR); 64071f95118Swdenk char dirc; 64171f95118Swdenk int doit = 0; 64271f95118Swdenk 64371f95118Swdenk if (isdir) { 64471f95118Swdenk dirs++; 64571f95118Swdenk dirc = '/'; 64671f95118Swdenk doit = 1; 64771f95118Swdenk } else { 64871f95118Swdenk dirc = ' '; 64971f95118Swdenk if (s_name[0] != 0) { 65071f95118Swdenk files++; 65171f95118Swdenk doit = 1; 65271f95118Swdenk } 65371f95118Swdenk } 6547385c28eSWolfgang Denk 65571f95118Swdenk if (doit) { 65671f95118Swdenk if (dirc == ' ') { 65771f95118Swdenk printf(" %8ld %s%c\n", 6587385c28eSWolfgang Denk (long)FAT2CPU32(dentptr->size), 6597385c28eSWolfgang Denk s_name, dirc); 66071f95118Swdenk } else { 6617385c28eSWolfgang Denk printf(" %s%c\n", 6627385c28eSWolfgang Denk s_name, dirc); 66371f95118Swdenk } 66471f95118Swdenk } 6657385c28eSWolfgang Denk 66671f95118Swdenk dentptr++; 66771f95118Swdenk continue; 66871f95118Swdenk } 6697385c28eSWolfgang Denk 6707385c28eSWolfgang Denk if (strcmp(filename, s_name) 6717385c28eSWolfgang Denk && strcmp(filename, l_name)) { 6727385c28eSWolfgang Denk debug("Mismatch: |%s|%s|\n", s_name, l_name); 67371f95118Swdenk dentptr++; 67471f95118Swdenk continue; 67571f95118Swdenk } 6767385c28eSWolfgang Denk 67771f95118Swdenk memcpy(retdent, dentptr, sizeof(dir_entry)); 67871f95118Swdenk 6797385c28eSWolfgang Denk debug("DentName: %s", s_name); 6807385c28eSWolfgang Denk debug(", start: 0x%x", START(dentptr)); 6817385c28eSWolfgang Denk debug(", size: 0x%x %s\n", 68271f95118Swdenk FAT2CPU32(dentptr->size), 68371f95118Swdenk (dentptr->attr & ATTR_DIR) ? "(DIR)" : ""); 68471f95118Swdenk 68571f95118Swdenk return retdent; 68671f95118Swdenk } 6877385c28eSWolfgang Denk 68871f95118Swdenk curclust = get_fatent(mydata, curclust); 6898ce4e5c2Smichael if (CHECK_CLUST(curclust, mydata->fatsize)) { 6907385c28eSWolfgang Denk debug("curclust: 0x%x\n", curclust); 6917385c28eSWolfgang Denk printf("Invalid FAT entry\n"); 69271f95118Swdenk return NULL; 69371f95118Swdenk } 69471f95118Swdenk } 69571f95118Swdenk 69671f95118Swdenk return NULL; 69771f95118Swdenk } 69871f95118Swdenk 69971f95118Swdenk /* 70071f95118Swdenk * Read boot sector and volume info from a FAT filesystem 70171f95118Swdenk */ 70271f95118Swdenk static int 70371f95118Swdenk read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize) 70471f95118Swdenk { 70571f95118Swdenk __u8 block[FS_BLOCK_SIZE]; 7067385c28eSWolfgang Denk 70771f95118Swdenk volume_info *vistart; 70871f95118Swdenk 70971f95118Swdenk if (disk_read (0, 1, block) < 0) { 7107385c28eSWolfgang Denk debug("Error: reading block\n"); 71171f95118Swdenk return -1; 71271f95118Swdenk } 71371f95118Swdenk 71471f95118Swdenk memcpy(bs, block, sizeof(boot_sector)); 71571f95118Swdenk bs->reserved = FAT2CPU16(bs->reserved); 71671f95118Swdenk bs->fat_length = FAT2CPU16(bs->fat_length); 71771f95118Swdenk bs->secs_track = FAT2CPU16(bs->secs_track); 71871f95118Swdenk bs->heads = FAT2CPU16(bs->heads); 71971f95118Swdenk bs->total_sect = FAT2CPU32(bs->total_sect); 72071f95118Swdenk 72171f95118Swdenk /* FAT32 entries */ 72271f95118Swdenk if (bs->fat_length == 0) { 72371f95118Swdenk /* Assume FAT32 */ 72471f95118Swdenk bs->fat32_length = FAT2CPU32(bs->fat32_length); 72571f95118Swdenk bs->flags = FAT2CPU16(bs->flags); 72671f95118Swdenk bs->root_cluster = FAT2CPU32(bs->root_cluster); 72771f95118Swdenk bs->info_sector = FAT2CPU16(bs->info_sector); 72871f95118Swdenk bs->backup_boot = FAT2CPU16(bs->backup_boot); 72971f95118Swdenk vistart = (volume_info *)(block + sizeof(boot_sector)); 73071f95118Swdenk *fatsize = 32; 73171f95118Swdenk } else { 73271f95118Swdenk vistart = (volume_info *)&(bs->fat32_length); 73371f95118Swdenk *fatsize = 0; 73471f95118Swdenk } 73571f95118Swdenk memcpy(volinfo, vistart, sizeof(volume_info)); 73671f95118Swdenk 73771f95118Swdenk if (*fatsize == 32) { 7387385c28eSWolfgang Denk if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0) 73971f95118Swdenk return 0; 74071f95118Swdenk } else { 741651351feSTom Rix if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) { 74271f95118Swdenk *fatsize = 12; 74371f95118Swdenk return 0; 74471f95118Swdenk } 745651351feSTom Rix if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) { 74671f95118Swdenk *fatsize = 16; 74771f95118Swdenk return 0; 74871f95118Swdenk } 74971f95118Swdenk } 75071f95118Swdenk 7517385c28eSWolfgang Denk debug("Error: broken fs_type sign\n"); 75271f95118Swdenk return -1; 75371f95118Swdenk } 75471f95118Swdenk 7557e4b9b4fSBryan Wu __attribute__ ((__aligned__ (__alignof__ (dir_entry)))) 7567e4b9b4fSBryan Wu __u8 do_fat_read_block[MAX_CLUSTSIZE]; 7577385c28eSWolfgang Denk 75820cc00ddSstroese long 75971f95118Swdenk do_fat_read (const char *filename, void *buffer, unsigned long maxsize, 76071f95118Swdenk int dols) 76171f95118Swdenk { 76271f95118Swdenk char fnamecopy[2048]; 76371f95118Swdenk boot_sector bs; 76471f95118Swdenk volume_info volinfo; 76571f95118Swdenk fsdata datablock; 76671f95118Swdenk fsdata *mydata = &datablock; 76771f95118Swdenk dir_entry *dentptr; 76871f95118Swdenk __u16 prevcksum = 0xffff; 76971f95118Swdenk char *subname = ""; 770*3f270f42SErik Hansen __u32 cursect; 77171f95118Swdenk int idx, isdir = 0; 77271f95118Swdenk int files = 0, dirs = 0; 77371f95118Swdenk long ret = 0; 77471f95118Swdenk int firsttime; 775*3f270f42SErik Hansen __u32 root_cluster; 776*3f270f42SErik Hansen int rootdir_size = 0; 7772aa98c66SWolfgang Denk int j; 77871f95118Swdenk 77971f95118Swdenk if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { 7807385c28eSWolfgang Denk debug("Error: reading boot sector\n"); 78171f95118Swdenk return -1; 78271f95118Swdenk } 7837385c28eSWolfgang Denk 7842aa98c66SWolfgang Denk root_cluster = bs.root_cluster; 7857385c28eSWolfgang Denk 7867385c28eSWolfgang Denk if (mydata->fatsize == 32) 78771f95118Swdenk mydata->fatlength = bs.fat32_length; 7887385c28eSWolfgang Denk else 78971f95118Swdenk mydata->fatlength = bs.fat_length; 7907385c28eSWolfgang Denk 79171f95118Swdenk mydata->fat_sect = bs.reserved; 7927385c28eSWolfgang Denk 79371f95118Swdenk cursect = mydata->rootdir_sect 79471f95118Swdenk = mydata->fat_sect + mydata->fatlength * bs.fats; 7957385c28eSWolfgang Denk 79671f95118Swdenk mydata->clust_size = bs.cluster_size; 7977385c28eSWolfgang Denk 79871f95118Swdenk if (mydata->fatsize == 32) { 7997385c28eSWolfgang Denk mydata->data_begin = mydata->rootdir_sect - 8007385c28eSWolfgang Denk (mydata->clust_size * 2); 80171f95118Swdenk } else { 8027385c28eSWolfgang Denk rootdir_size = ((bs.dir_entries[1] * (int)256 + 8037385c28eSWolfgang Denk bs.dir_entries[0]) * 8047385c28eSWolfgang Denk sizeof(dir_entry)) / 8057385c28eSWolfgang Denk SECTOR_SIZE; 8067385c28eSWolfgang Denk mydata->data_begin = mydata->rootdir_sect + 8077385c28eSWolfgang Denk rootdir_size - 8087385c28eSWolfgang Denk (mydata->clust_size * 2); 80971f95118Swdenk } 8107385c28eSWolfgang Denk 81171f95118Swdenk mydata->fatbufnum = -1; 81271f95118Swdenk 8132aa98c66SWolfgang Denk #ifdef CONFIG_SUPPORT_VFAT 8147385c28eSWolfgang Denk debug("VFAT Support enabled\n"); 8152aa98c66SWolfgang Denk #endif 8167385c28eSWolfgang Denk debug("FAT%d, fat_sect: %d, fatlength: %d\n", 8177385c28eSWolfgang Denk mydata->fatsize, mydata->fat_sect, mydata->fatlength); 8187385c28eSWolfgang Denk debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n" 81971f95118Swdenk "Data begins at: %d\n", 8202aa98c66SWolfgang Denk root_cluster, 8212aa98c66SWolfgang Denk mydata->rootdir_sect, 8227385c28eSWolfgang Denk mydata->rootdir_sect * SECTOR_SIZE, mydata->data_begin); 8237385c28eSWolfgang Denk debug("Cluster size: %d\n", mydata->clust_size); 82471f95118Swdenk 82571f95118Swdenk /* "cwd" is always the root... */ 82671f95118Swdenk while (ISDIRDELIM(*filename)) 82771f95118Swdenk filename++; 8287385c28eSWolfgang Denk 82971f95118Swdenk /* Make a copy of the filename and convert it to lowercase */ 83071f95118Swdenk strcpy(fnamecopy, filename); 83171f95118Swdenk downcase(fnamecopy); 8327385c28eSWolfgang Denk 83371f95118Swdenk if (*fnamecopy == '\0') { 83471f95118Swdenk if (!dols) 83571f95118Swdenk return -1; 8367385c28eSWolfgang Denk 83771f95118Swdenk dols = LS_ROOT; 83871f95118Swdenk } else if ((idx = dirdelim(fnamecopy)) >= 0) { 83971f95118Swdenk isdir = 1; 84071f95118Swdenk fnamecopy[idx] = '\0'; 84171f95118Swdenk subname = fnamecopy + idx + 1; 8427385c28eSWolfgang Denk 84371f95118Swdenk /* Handle multiple delimiters */ 84471f95118Swdenk while (ISDIRDELIM(*subname)) 84571f95118Swdenk subname++; 84671f95118Swdenk } else if (dols) { 84771f95118Swdenk isdir = 1; 84871f95118Swdenk } 84971f95118Swdenk 8502aa98c66SWolfgang Denk j = 0; 85171f95118Swdenk while (1) { 85271f95118Swdenk int i; 85371f95118Swdenk 8547385c28eSWolfgang Denk debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%d\n", 8552aa98c66SWolfgang Denk cursect, mydata->clust_size, DIRENTSPERBLOCK); 8567385c28eSWolfgang Denk 8573831530dSMikhail Zolotaryov if (disk_read(cursect, 8583831530dSMikhail Zolotaryov (mydata->fatsize == 32) ? 8593831530dSMikhail Zolotaryov (mydata->clust_size) : 86011c8dd36SStefano Babic LINEAR_PREFETCH_SIZE / SECTOR_SIZE, 8613831530dSMikhail Zolotaryov do_fat_read_block) < 0) { 8627385c28eSWolfgang Denk debug("Error: reading rootdir block\n"); 86371f95118Swdenk return -1; 86471f95118Swdenk } 8657385c28eSWolfgang Denk 8665fa66df6Swdenk dentptr = (dir_entry *) do_fat_read_block; 8677385c28eSWolfgang Denk 86871f95118Swdenk for (i = 0; i < DIRENTSPERBLOCK; i++) { 8693831530dSMikhail Zolotaryov char s_name[14], l_name[VFAT_MAXLEN_BYTES]; 87071f95118Swdenk 87171f95118Swdenk l_name[0] = '\0'; 8723831530dSMikhail Zolotaryov if (dentptr->name[0] == DELETED_FLAG) { 8733831530dSMikhail Zolotaryov dentptr++; 8743831530dSMikhail Zolotaryov continue; 8753831530dSMikhail Zolotaryov } 87671f95118Swdenk if ((dentptr->attr & ATTR_VOLUME)) { 87771f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 87871f95118Swdenk if ((dentptr->attr & ATTR_VFAT) && 8792d1a537dSwdenk (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { 8807385c28eSWolfgang Denk prevcksum = 8817385c28eSWolfgang Denk ((dir_slot *)dentptr)->alias_checksum; 8827385c28eSWolfgang Denk 8833831530dSMikhail Zolotaryov get_vfatname(mydata, 8843831530dSMikhail Zolotaryov (mydata->fatsize == 32) ? 8853831530dSMikhail Zolotaryov root_cluster : 8863831530dSMikhail Zolotaryov 0, 8877385c28eSWolfgang Denk do_fat_read_block, 8887385c28eSWolfgang Denk dentptr, l_name); 8897385c28eSWolfgang Denk 89071f95118Swdenk if (dols == LS_ROOT) { 89171f95118Swdenk char dirc; 89271f95118Swdenk int doit = 0; 8937385c28eSWolfgang Denk int isdir = 8947385c28eSWolfgang Denk (dentptr->attr & ATTR_DIR); 89571f95118Swdenk 89671f95118Swdenk if (isdir) { 89771f95118Swdenk dirs++; 89871f95118Swdenk dirc = '/'; 89971f95118Swdenk doit = 1; 90071f95118Swdenk } else { 90171f95118Swdenk dirc = ' '; 90271f95118Swdenk if (l_name[0] != 0) { 90371f95118Swdenk files++; 90471f95118Swdenk doit = 1; 90571f95118Swdenk } 90671f95118Swdenk } 90771f95118Swdenk if (doit) { 90871f95118Swdenk if (dirc == ' ') { 90971f95118Swdenk printf(" %8ld %s%c\n", 91071f95118Swdenk (long)FAT2CPU32(dentptr->size), 9117385c28eSWolfgang Denk l_name, 9127385c28eSWolfgang Denk dirc); 91371f95118Swdenk } else { 9147385c28eSWolfgang Denk printf(" %s%c\n", 9157385c28eSWolfgang Denk l_name, 9167385c28eSWolfgang Denk dirc); 91771f95118Swdenk } 91871f95118Swdenk } 91971f95118Swdenk dentptr++; 92071f95118Swdenk continue; 92171f95118Swdenk } 9227385c28eSWolfgang Denk debug("Rootvfatname: |%s|\n", 9237385c28eSWolfgang Denk l_name); 92471f95118Swdenk } else 92571f95118Swdenk #endif 92671f95118Swdenk { 92771f95118Swdenk /* Volume label or VFAT entry */ 92871f95118Swdenk dentptr++; 92971f95118Swdenk continue; 93071f95118Swdenk } 93171f95118Swdenk } else if (dentptr->name[0] == 0) { 9327385c28eSWolfgang Denk debug("RootDentname == NULL - %d\n", i); 93371f95118Swdenk if (dols == LS_ROOT) { 9347385c28eSWolfgang Denk printf("\n%d file(s), %d dir(s)\n\n", 9357385c28eSWolfgang Denk files, dirs); 93671f95118Swdenk return 0; 93771f95118Swdenk } 93871f95118Swdenk return -1; 93971f95118Swdenk } 94071f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 9417385c28eSWolfgang Denk else if (dols == LS_ROOT && 9427385c28eSWolfgang Denk mkcksum(dentptr->name) == prevcksum) { 94371f95118Swdenk dentptr++; 94471f95118Swdenk continue; 94571f95118Swdenk } 94671f95118Swdenk #endif 94771f95118Swdenk get_name(dentptr, s_name); 9487385c28eSWolfgang Denk 94971f95118Swdenk if (dols == LS_ROOT) { 95071f95118Swdenk int isdir = (dentptr->attr & ATTR_DIR); 95171f95118Swdenk char dirc; 95271f95118Swdenk int doit = 0; 95371f95118Swdenk 95471f95118Swdenk if (isdir) { 95571f95118Swdenk dirc = '/'; 956a43278a4Swdenk if (s_name[0] != 0) { 957a43278a4Swdenk dirs++; 95871f95118Swdenk doit = 1; 959a43278a4Swdenk } 96071f95118Swdenk } else { 96171f95118Swdenk dirc = ' '; 96271f95118Swdenk if (s_name[0] != 0) { 96371f95118Swdenk files++; 96471f95118Swdenk doit = 1; 96571f95118Swdenk } 96671f95118Swdenk } 96771f95118Swdenk if (doit) { 96871f95118Swdenk if (dirc == ' ') { 96971f95118Swdenk printf(" %8ld %s%c\n", 9707385c28eSWolfgang Denk (long)FAT2CPU32(dentptr->size), 9717385c28eSWolfgang Denk s_name, dirc); 97271f95118Swdenk } else { 9737385c28eSWolfgang Denk printf(" %s%c\n", 9747385c28eSWolfgang Denk s_name, dirc); 97571f95118Swdenk } 97671f95118Swdenk } 97771f95118Swdenk dentptr++; 97871f95118Swdenk continue; 97971f95118Swdenk } 9807385c28eSWolfgang Denk 9817385c28eSWolfgang Denk if (strcmp(fnamecopy, s_name) 9827385c28eSWolfgang Denk && strcmp(fnamecopy, l_name)) { 9837385c28eSWolfgang Denk debug("RootMismatch: |%s|%s|\n", s_name, 9847385c28eSWolfgang Denk l_name); 98571f95118Swdenk dentptr++; 98671f95118Swdenk continue; 98771f95118Swdenk } 9887385c28eSWolfgang Denk 98971f95118Swdenk if (isdir && !(dentptr->attr & ATTR_DIR)) 99071f95118Swdenk return -1; 99171f95118Swdenk 9927385c28eSWolfgang Denk debug("RootName: %s", s_name); 9937385c28eSWolfgang Denk debug(", start: 0x%x", START(dentptr)); 9947385c28eSWolfgang Denk debug(", size: 0x%x %s\n", 9957385c28eSWolfgang Denk FAT2CPU32(dentptr->size), 9967385c28eSWolfgang Denk isdir ? "(DIR)" : ""); 99771f95118Swdenk 99871f95118Swdenk goto rootdir_done; /* We got a match */ 99971f95118Swdenk } 10007385c28eSWolfgang Denk debug("END LOOP: j=%d clust_size=%d\n", j, 10017385c28eSWolfgang Denk mydata->clust_size); 10022aa98c66SWolfgang Denk 10032aa98c66SWolfgang Denk /* 10042aa98c66SWolfgang Denk * On FAT32 we must fetch the FAT entries for the next 10052aa98c66SWolfgang Denk * root directory clusters when a cluster has been 10062aa98c66SWolfgang Denk * completely processed. 10072aa98c66SWolfgang Denk */ 1008*3f270f42SErik Hansen ++j; 1009*3f270f42SErik Hansen int fat32_end = 0; 1010*3f270f42SErik Hansen if ((mydata->fatsize == 32) && (j == mydata->clust_size)) { 1011*3f270f42SErik Hansen int nxtsect = 0; 1012*3f270f42SErik Hansen int nxt_clust = 0; 10132aa98c66SWolfgang Denk 10142aa98c66SWolfgang Denk nxt_clust = get_fatent(mydata, root_cluster); 1015*3f270f42SErik Hansen fat32_end = CHECK_CLUST(nxt_clust, 32); 10167385c28eSWolfgang Denk 10177385c28eSWolfgang Denk nxtsect = mydata->data_begin + 10187385c28eSWolfgang Denk (nxt_clust * mydata->clust_size); 10197385c28eSWolfgang Denk 10202aa98c66SWolfgang Denk root_cluster = nxt_clust; 10212aa98c66SWolfgang Denk 10222aa98c66SWolfgang Denk cursect = nxtsect; 10232aa98c66SWolfgang Denk j = 0; 10242aa98c66SWolfgang Denk } else { 102571f95118Swdenk cursect++; 102671f95118Swdenk } 1027*3f270f42SErik Hansen 1028*3f270f42SErik Hansen /* If end of rootdir reached */ 1029*3f270f42SErik Hansen if ((mydata->fatsize == 32 && fat32_end) || 1030*3f270f42SErik Hansen (mydata->fatsize != 32 && j == rootdir_size)) { 1031*3f270f42SErik Hansen if (dols == LS_ROOT) { 1032*3f270f42SErik Hansen printf("\n%d file(s), %d dir(s)\n\n", 1033*3f270f42SErik Hansen files, dirs); 1034*3f270f42SErik Hansen return 0; 1035*3f270f42SErik Hansen } else { 1036*3f270f42SErik Hansen return -1; 1037*3f270f42SErik Hansen } 1038*3f270f42SErik Hansen } 10392aa98c66SWolfgang Denk } 104071f95118Swdenk rootdir_done: 104171f95118Swdenk 104271f95118Swdenk firsttime = 1; 10437385c28eSWolfgang Denk 104471f95118Swdenk while (isdir) { 104571f95118Swdenk int startsect = mydata->data_begin 104671f95118Swdenk + START(dentptr) * mydata->clust_size; 104771f95118Swdenk dir_entry dent; 104871f95118Swdenk char *nextname = NULL; 104971f95118Swdenk 105071f95118Swdenk dent = *dentptr; 105171f95118Swdenk dentptr = &dent; 105271f95118Swdenk 105371f95118Swdenk idx = dirdelim(subname); 10547385c28eSWolfgang Denk 105571f95118Swdenk if (idx >= 0) { 105671f95118Swdenk subname[idx] = '\0'; 105771f95118Swdenk nextname = subname + idx + 1; 105871f95118Swdenk /* Handle multiple delimiters */ 105971f95118Swdenk while (ISDIRDELIM(*nextname)) 106071f95118Swdenk nextname++; 106171f95118Swdenk if (dols && *nextname == '\0') 106271f95118Swdenk firsttime = 0; 106371f95118Swdenk } else { 106471f95118Swdenk if (dols && firsttime) { 106571f95118Swdenk firsttime = 0; 106671f95118Swdenk } else { 106771f95118Swdenk isdir = 0; 106871f95118Swdenk } 106971f95118Swdenk } 107071f95118Swdenk 107171f95118Swdenk if (get_dentfromdir(mydata, startsect, subname, dentptr, 107271f95118Swdenk isdir ? 0 : dols) == NULL) { 107371f95118Swdenk if (dols && !isdir) 107471f95118Swdenk return 0; 107571f95118Swdenk return -1; 107671f95118Swdenk } 107771f95118Swdenk 107871f95118Swdenk if (idx >= 0) { 107971f95118Swdenk if (!(dentptr->attr & ATTR_DIR)) 108071f95118Swdenk return -1; 108171f95118Swdenk subname = nextname; 108271f95118Swdenk } 108371f95118Swdenk } 10847385c28eSWolfgang Denk 108571f95118Swdenk ret = get_contents(mydata, dentptr, buffer, maxsize); 10867385c28eSWolfgang Denk debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret); 108771f95118Swdenk 108871f95118Swdenk return ret; 108971f95118Swdenk } 109071f95118Swdenk 10917385c28eSWolfgang Denk int file_fat_detectfs (void) 109271f95118Swdenk { 109371f95118Swdenk boot_sector bs; 109471f95118Swdenk volume_info volinfo; 109571f95118Swdenk int fatsize; 10967205e407Swdenk char vol_label[12]; 109771f95118Swdenk 10987205e407Swdenk if (cur_dev == NULL) { 10997205e407Swdenk printf("No current device\n"); 11007205e407Swdenk return 1; 11017205e407Swdenk } 11027385c28eSWolfgang Denk 1103dd60d122SJon Loeliger #if defined(CONFIG_CMD_IDE) || \ 110475eb82ecSunsik Kim defined(CONFIG_CMD_MG_DISK) || \ 11058c5170a7SSonic Zhang defined(CONFIG_CMD_SATA) || \ 1106dd60d122SJon Loeliger defined(CONFIG_CMD_SCSI) || \ 1107dd60d122SJon Loeliger defined(CONFIG_CMD_USB) || \ 110821f6f963SAndy Fleming defined(CONFIG_MMC) 11097205e407Swdenk printf("Interface: "); 11107205e407Swdenk switch (cur_dev->if_type) { 11117385c28eSWolfgang Denk case IF_TYPE_IDE: 11127385c28eSWolfgang Denk printf("IDE"); 11137385c28eSWolfgang Denk break; 11147385c28eSWolfgang Denk case IF_TYPE_SATA: 11157385c28eSWolfgang Denk printf("SATA"); 11167385c28eSWolfgang Denk break; 11177385c28eSWolfgang Denk case IF_TYPE_SCSI: 11187385c28eSWolfgang Denk printf("SCSI"); 11197385c28eSWolfgang Denk break; 11207385c28eSWolfgang Denk case IF_TYPE_ATAPI: 11217385c28eSWolfgang Denk printf("ATAPI"); 11227385c28eSWolfgang Denk break; 11237385c28eSWolfgang Denk case IF_TYPE_USB: 11247385c28eSWolfgang Denk printf("USB"); 11257385c28eSWolfgang Denk break; 11267385c28eSWolfgang Denk case IF_TYPE_DOC: 11277385c28eSWolfgang Denk printf("DOC"); 11287385c28eSWolfgang Denk break; 11297385c28eSWolfgang Denk case IF_TYPE_MMC: 11307385c28eSWolfgang Denk printf("MMC"); 11317385c28eSWolfgang Denk break; 11327385c28eSWolfgang Denk default: 11337385c28eSWolfgang Denk printf("Unknown"); 11347205e407Swdenk } 11357385c28eSWolfgang Denk 11367205e407Swdenk printf("\n Device %d: ", cur_dev->dev); 11377205e407Swdenk dev_print(cur_dev); 11387205e407Swdenk #endif 11397385c28eSWolfgang Denk 11407205e407Swdenk if (read_bootsectandvi(&bs, &volinfo, &fatsize)) { 11417205e407Swdenk printf("\nNo valid FAT fs found\n"); 11427205e407Swdenk return 1; 11437205e407Swdenk } 11447385c28eSWolfgang Denk 11457205e407Swdenk memcpy(vol_label, volinfo.volume_label, 11); 11467205e407Swdenk vol_label[11] = '\0'; 11477205e407Swdenk volinfo.fs_type[5] = '\0'; 11487385c28eSWolfgang Denk 11497385c28eSWolfgang Denk printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part, 11507385c28eSWolfgang Denk volinfo.fs_type, vol_label); 11517385c28eSWolfgang Denk 11527205e407Swdenk return 0; 115371f95118Swdenk } 115471f95118Swdenk 11557385c28eSWolfgang Denk int file_fat_ls (const char *dir) 115671f95118Swdenk { 115771f95118Swdenk return do_fat_read(dir, NULL, 0, LS_YES); 115871f95118Swdenk } 115971f95118Swdenk 11607385c28eSWolfgang Denk long file_fat_read (const char *filename, void *buffer, unsigned long maxsize) 116171f95118Swdenk { 11627205e407Swdenk printf("reading %s\n", filename); 116371f95118Swdenk return do_fat_read(filename, buffer, maxsize, LS_NO); 116471f95118Swdenk } 1165