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> 30ac497771SSergei Shtylyov #include <exports.h> 3171f95118Swdenk #include <fat.h> 3271f95118Swdenk #include <asm/byteorder.h> 337205e407Swdenk #include <part.h> 3471f95118Swdenk 3571f95118Swdenk /* 3671f95118Swdenk * Convert a string to lowercase. 3771f95118Swdenk */ 387385c28eSWolfgang Denk static void downcase (char *str) 3971f95118Swdenk { 4071f95118Swdenk while (*str != '\0') { 4171f95118Swdenk TOLOWER(*str); 4271f95118Swdenk str++; 4371f95118Swdenk } 4471f95118Swdenk } 4571f95118Swdenk 467205e407Swdenk static block_dev_desc_t *cur_dev = NULL; 477385c28eSWolfgang Denk 487205e407Swdenk static unsigned long part_offset = 0; 497385c28eSWolfgang Denk 507205e407Swdenk static int cur_part = 1; 517205e407Swdenk 527205e407Swdenk #define DOS_PART_TBL_OFFSET 0x1be 537205e407Swdenk #define DOS_PART_MAGIC_OFFSET 0x1fe 547205e407Swdenk #define DOS_FS_TYPE_OFFSET 0x36 5566c2d73cSWolfgang Denk #define DOS_FS32_TYPE_OFFSET 0x52 5671f95118Swdenk 577385c28eSWolfgang Denk static int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr) 5871f95118Swdenk { 597205e407Swdenk if (cur_dev == NULL) 607205e407Swdenk return -1; 617385c28eSWolfgang Denk 627385c28eSWolfgang Denk startblock += part_offset; 637385c28eSWolfgang Denk 647205e407Swdenk if (cur_dev->block_read) { 657385c28eSWolfgang Denk return cur_dev->block_read(cur_dev->dev, startblock, getsize, 667385c28eSWolfgang Denk (unsigned long *) bufptr); 6771f95118Swdenk } 6871f95118Swdenk return -1; 6971f95118Swdenk } 7071f95118Swdenk 717385c28eSWolfgang Denk int fat_register_device (block_dev_desc_t * dev_desc, int part_no) 7271f95118Swdenk { 73ac497771SSergei Shtylyov unsigned char buffer[dev_desc->blksz]; 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*60b36f0fSSergei Shtylyov __u32 getsize = FATBUFBLOCKS; 21371f95118Swdenk __u8 *bufptr = mydata->fatbuf; 21471f95118Swdenk __u32 fatlength = mydata->fatlength; 21571f95118Swdenk __u32 startblock = bufnum * FATBUFBLOCKS; 21671f95118Swdenk 217*60b36f0fSSergei Shtylyov if (getsize > fatlength) 218*60b36f0fSSergei Shtylyov getsize = fatlength; 219*60b36f0fSSergei Shtylyov 220ac497771SSergei Shtylyov fatlength *= mydata->sect_size; /* We want it in bytes now */ 22171f95118Swdenk startblock += mydata->fat_sect; /* Offset from start of disk */ 22271f95118Swdenk 22371f95118Swdenk if (disk_read(startblock, getsize, bufptr) < 0) { 2247385c28eSWolfgang Denk debug("Error reading FAT blocks\n"); 22571f95118Swdenk return ret; 22671f95118Swdenk } 22771f95118Swdenk mydata->fatbufnum = bufnum; 22871f95118Swdenk } 22971f95118Swdenk 23071f95118Swdenk /* Get the actual entry from the table */ 23171f95118Swdenk switch (mydata->fatsize) { 23271f95118Swdenk case 32: 23371f95118Swdenk ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]); 23471f95118Swdenk break; 23571f95118Swdenk case 16: 23671f95118Swdenk ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]); 23771f95118Swdenk break; 2387385c28eSWolfgang Denk case 12: 2397385c28eSWolfgang Denk off16 = (offset * 3) / 4; 24071f95118Swdenk 24171f95118Swdenk switch (offset & 0x3) { 24271f95118Swdenk case 0: 24371f95118Swdenk ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]); 24471f95118Swdenk ret &= 0xfff; 24571f95118Swdenk break; 24671f95118Swdenk case 1: 24771f95118Swdenk val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 24871f95118Swdenk val1 &= 0xf000; 24971f95118Swdenk val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); 25071f95118Swdenk val2 &= 0x00ff; 25171f95118Swdenk ret = (val2 << 4) | (val1 >> 12); 25271f95118Swdenk break; 25371f95118Swdenk case 2: 25471f95118Swdenk val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 25571f95118Swdenk val1 &= 0xff00; 25671f95118Swdenk val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); 25771f95118Swdenk val2 &= 0x000f; 25871f95118Swdenk ret = (val2 << 8) | (val1 >> 8); 25971f95118Swdenk break; 26071f95118Swdenk case 3: 2617385c28eSWolfgang Denk ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 26271f95118Swdenk ret = (ret & 0xfff0) >> 4; 26371f95118Swdenk break; 26471f95118Swdenk default: 26571f95118Swdenk break; 26671f95118Swdenk } 26771f95118Swdenk break; 26871f95118Swdenk } 2697385c28eSWolfgang Denk debug("FAT%d: ret: %08x, offset: %04x\n", 2702aa98c66SWolfgang Denk mydata->fatsize, ret, offset); 27171f95118Swdenk 27271f95118Swdenk return ret; 27371f95118Swdenk } 27471f95118Swdenk 27571f95118Swdenk /* 27671f95118Swdenk * Read at most 'size' bytes from the specified cluster into 'buffer'. 27771f95118Swdenk * Return 0 on success, -1 otherwise. 27871f95118Swdenk */ 27971f95118Swdenk static int 2807385c28eSWolfgang Denk get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer, 2817385c28eSWolfgang Denk unsigned long size) 28271f95118Swdenk { 2833f270f42SErik Hansen __u32 idx = 0; 28471f95118Swdenk __u32 startsect; 28571f95118Swdenk 28671f95118Swdenk if (clustnum > 0) { 2877385c28eSWolfgang Denk startsect = mydata->data_begin + 2887385c28eSWolfgang Denk clustnum * mydata->clust_size; 28971f95118Swdenk } else { 29071f95118Swdenk startsect = mydata->rootdir_sect; 29171f95118Swdenk } 29271f95118Swdenk 2937385c28eSWolfgang Denk debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect); 2947385c28eSWolfgang Denk 295ac497771SSergei Shtylyov if (disk_read(startsect, size / mydata->sect_size, buffer) < 0) { 2967385c28eSWolfgang Denk debug("Error reading data\n"); 29771f95118Swdenk return -1; 29871f95118Swdenk } 299ac497771SSergei Shtylyov if (size % mydata->sect_size) { 300ac497771SSergei Shtylyov __u8 tmpbuf[mydata->sect_size]; 3017385c28eSWolfgang Denk 302ac497771SSergei Shtylyov idx = size / mydata->sect_size; 30371f95118Swdenk if (disk_read(startsect + idx, 1, tmpbuf) < 0) { 3047385c28eSWolfgang Denk debug("Error reading data\n"); 30571f95118Swdenk return -1; 30671f95118Swdenk } 307ac497771SSergei Shtylyov buffer += idx * mydata->sect_size; 30871f95118Swdenk 309ac497771SSergei Shtylyov memcpy(buffer, tmpbuf, size % mydata->sect_size); 31071f95118Swdenk return 0; 31171f95118Swdenk } 31271f95118Swdenk 31371f95118Swdenk return 0; 31471f95118Swdenk } 31571f95118Swdenk 31671f95118Swdenk /* 31771f95118Swdenk * Read at most 'maxsize' bytes from the file associated with 'dentptr' 31871f95118Swdenk * into 'buffer'. 31971f95118Swdenk * Return the number of bytes read or -1 on fatal errors. 32071f95118Swdenk */ 32171f95118Swdenk static long 32271f95118Swdenk get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer, 32371f95118Swdenk unsigned long maxsize) 32471f95118Swdenk { 32571f95118Swdenk unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0; 326ac497771SSergei Shtylyov unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 32771f95118Swdenk __u32 curclust = START(dentptr); 3287205e407Swdenk __u32 endclust, newclust; 3297205e407Swdenk unsigned long actsize; 33071f95118Swdenk 3317385c28eSWolfgang Denk debug("Filesize: %ld bytes\n", filesize); 33271f95118Swdenk 3337385c28eSWolfgang Denk if (maxsize > 0 && filesize > maxsize) 3347385c28eSWolfgang Denk filesize = maxsize; 33571f95118Swdenk 3367385c28eSWolfgang Denk debug("%ld bytes\n", filesize); 33771f95118Swdenk 3387205e407Swdenk actsize = bytesperclust; 3397205e407Swdenk endclust = curclust; 3407385c28eSWolfgang Denk 34171f95118Swdenk do { 3427205e407Swdenk /* search for consecutive clusters */ 3437205e407Swdenk while (actsize < filesize) { 3447205e407Swdenk newclust = get_fatent(mydata, endclust); 3457205e407Swdenk if ((newclust - 1) != endclust) 3467205e407Swdenk goto getit; 3478ce4e5c2Smichael if (CHECK_CLUST(newclust, mydata->fatsize)) { 3487385c28eSWolfgang Denk debug("curclust: 0x%x\n", newclust); 3497385c28eSWolfgang Denk debug("Invalid FAT entry\n"); 3507205e407Swdenk return gotsize; 3517205e407Swdenk } 3527205e407Swdenk endclust = newclust; 3537205e407Swdenk actsize += bytesperclust; 3547205e407Swdenk } 3557385c28eSWolfgang Denk 3567205e407Swdenk /* actsize >= file size */ 3577205e407Swdenk actsize -= bytesperclust; 3587385c28eSWolfgang Denk 3597205e407Swdenk /* get remaining clusters */ 3607205e407Swdenk if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 3617385c28eSWolfgang Denk printf("Error reading cluster\n"); 36271f95118Swdenk return -1; 36371f95118Swdenk } 3647385c28eSWolfgang Denk 3657205e407Swdenk /* get remaining bytes */ 3667205e407Swdenk gotsize += (int)actsize; 3677205e407Swdenk filesize -= actsize; 3687205e407Swdenk buffer += actsize; 3697205e407Swdenk actsize = filesize; 3707205e407Swdenk if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) { 3717385c28eSWolfgang Denk printf("Error reading cluster\n"); 3727205e407Swdenk return -1; 3737205e407Swdenk } 3747205e407Swdenk gotsize += actsize; 3757205e407Swdenk return gotsize; 3767205e407Swdenk getit: 3777205e407Swdenk if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 3787385c28eSWolfgang Denk printf("Error reading cluster\n"); 3797205e407Swdenk return -1; 3807205e407Swdenk } 3817205e407Swdenk gotsize += (int)actsize; 3827205e407Swdenk filesize -= actsize; 3837205e407Swdenk buffer += actsize; 3847385c28eSWolfgang Denk 3857205e407Swdenk curclust = get_fatent(mydata, endclust); 3868ce4e5c2Smichael if (CHECK_CLUST(curclust, mydata->fatsize)) { 3877385c28eSWolfgang Denk debug("curclust: 0x%x\n", curclust); 3887385c28eSWolfgang Denk printf("Invalid FAT entry\n"); 38971f95118Swdenk return gotsize; 39071f95118Swdenk } 3917205e407Swdenk actsize = bytesperclust; 3927205e407Swdenk endclust = curclust; 39371f95118Swdenk } while (1); 39471f95118Swdenk } 39571f95118Swdenk 39671f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 39771f95118Swdenk /* 39871f95118Swdenk * Extract the file name information from 'slotptr' into 'l_name', 39971f95118Swdenk * starting at l_name[*idx]. 40071f95118Swdenk * Return 1 if terminator (zero byte) is found, 0 otherwise. 40171f95118Swdenk */ 4027385c28eSWolfgang Denk static int slot2str (dir_slot *slotptr, char *l_name, int *idx) 40371f95118Swdenk { 40471f95118Swdenk int j; 40571f95118Swdenk 40671f95118Swdenk for (j = 0; j <= 8; j += 2) { 40771f95118Swdenk l_name[*idx] = slotptr->name0_4[j]; 4087385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4097385c28eSWolfgang Denk return 1; 41071f95118Swdenk (*idx)++; 41171f95118Swdenk } 41271f95118Swdenk for (j = 0; j <= 10; j += 2) { 41371f95118Swdenk l_name[*idx] = slotptr->name5_10[j]; 4147385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4157385c28eSWolfgang Denk return 1; 41671f95118Swdenk (*idx)++; 41771f95118Swdenk } 41871f95118Swdenk for (j = 0; j <= 2; j += 2) { 41971f95118Swdenk l_name[*idx] = slotptr->name11_12[j]; 4207385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4217385c28eSWolfgang Denk return 1; 42271f95118Swdenk (*idx)++; 42371f95118Swdenk } 42471f95118Swdenk 42571f95118Swdenk return 0; 42671f95118Swdenk } 42771f95118Swdenk 42871f95118Swdenk /* 42971f95118Swdenk * Extract the full long filename starting at 'retdent' (which is really 43071f95118Swdenk * a slot) into 'l_name'. If successful also copy the real directory entry 43171f95118Swdenk * into 'retdent' 43271f95118Swdenk * Return 0 on success, -1 otherwise. 43371f95118Swdenk */ 4347e4b9b4fSBryan Wu __attribute__ ((__aligned__ (__alignof__ (dir_entry)))) 4355fa66df6Swdenk __u8 get_vfatname_block[MAX_CLUSTSIZE]; 4367385c28eSWolfgang Denk 43771f95118Swdenk static int 43871f95118Swdenk get_vfatname (fsdata *mydata, int curclust, __u8 *cluster, 43971f95118Swdenk dir_entry *retdent, char *l_name) 44071f95118Swdenk { 44171f95118Swdenk dir_entry *realdent; 44271f95118Swdenk dir_slot *slotptr = (dir_slot *)retdent; 4433831530dSMikhail Zolotaryov __u8 *buflimit = cluster + ((curclust == 0) ? 4443831530dSMikhail Zolotaryov LINEAR_PREFETCH_SIZE : 445ac497771SSergei Shtylyov (mydata->clust_size * mydata->sect_size) 4463831530dSMikhail Zolotaryov ); 4472d1a537dSwdenk __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; 44871f95118Swdenk int idx = 0; 44971f95118Swdenk 4503831530dSMikhail Zolotaryov if (counter > VFAT_MAXSEQ) { 4513831530dSMikhail Zolotaryov debug("Error: VFAT name is too long\n"); 4523831530dSMikhail Zolotaryov return -1; 4533831530dSMikhail Zolotaryov } 4543831530dSMikhail Zolotaryov 4553831530dSMikhail Zolotaryov while ((__u8 *)slotptr < buflimit) { 4567385c28eSWolfgang Denk if (counter == 0) 4577385c28eSWolfgang Denk break; 4582d1a537dSwdenk if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) 4592d1a537dSwdenk return -1; 46071f95118Swdenk slotptr++; 46171f95118Swdenk counter--; 46271f95118Swdenk } 46371f95118Swdenk 4643831530dSMikhail Zolotaryov if ((__u8 *)slotptr >= buflimit) { 46571f95118Swdenk dir_slot *slotptr2; 46671f95118Swdenk 4673831530dSMikhail Zolotaryov if (curclust == 0) 4683831530dSMikhail Zolotaryov return -1; 46971f95118Swdenk curclust = get_fatent(mydata, curclust); 4708ce4e5c2Smichael if (CHECK_CLUST(curclust, mydata->fatsize)) { 4717385c28eSWolfgang Denk debug("curclust: 0x%x\n", curclust); 4727385c28eSWolfgang Denk printf("Invalid FAT entry\n"); 47371f95118Swdenk return -1; 47471f95118Swdenk } 4757385c28eSWolfgang Denk 4765fa66df6Swdenk if (get_cluster(mydata, curclust, get_vfatname_block, 477ac497771SSergei Shtylyov mydata->clust_size * mydata->sect_size) != 0) { 4787385c28eSWolfgang Denk debug("Error: reading directory block\n"); 47971f95118Swdenk return -1; 48071f95118Swdenk } 4817385c28eSWolfgang Denk 4825fa66df6Swdenk slotptr2 = (dir_slot *)get_vfatname_block; 4833831530dSMikhail Zolotaryov while (counter > 0) { 4843831530dSMikhail Zolotaryov if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK) 4853831530dSMikhail Zolotaryov & 0xff) != counter) 4863831530dSMikhail Zolotaryov return -1; 48771f95118Swdenk slotptr2++; 4883831530dSMikhail Zolotaryov counter--; 4893831530dSMikhail Zolotaryov } 4907385c28eSWolfgang Denk 49171f95118Swdenk /* Save the real directory entry */ 4923831530dSMikhail Zolotaryov realdent = (dir_entry *)slotptr2; 4933831530dSMikhail Zolotaryov while ((__u8 *)slotptr2 > get_vfatname_block) { 49471f95118Swdenk slotptr2--; 4953831530dSMikhail Zolotaryov slot2str(slotptr2, l_name, &idx); 49671f95118Swdenk } 49771f95118Swdenk } else { 49871f95118Swdenk /* Save the real directory entry */ 49971f95118Swdenk realdent = (dir_entry *)slotptr; 50071f95118Swdenk } 50171f95118Swdenk 50271f95118Swdenk do { 50371f95118Swdenk slotptr--; 5047385c28eSWolfgang Denk if (slot2str(slotptr, l_name, &idx)) 5057385c28eSWolfgang Denk break; 5062d1a537dSwdenk } while (!(slotptr->id & LAST_LONG_ENTRY_MASK)); 50771f95118Swdenk 50871f95118Swdenk l_name[idx] = '\0'; 5097385c28eSWolfgang Denk if (*l_name == DELETED_FLAG) 5107385c28eSWolfgang Denk *l_name = '\0'; 5117385c28eSWolfgang Denk else if (*l_name == aRING) 5127385c28eSWolfgang Denk *l_name = DELETED_FLAG; 51371f95118Swdenk downcase(l_name); 51471f95118Swdenk 51571f95118Swdenk /* Return the real directory entry */ 51671f95118Swdenk memcpy(retdent, realdent, sizeof(dir_entry)); 51771f95118Swdenk 51871f95118Swdenk return 0; 51971f95118Swdenk } 52071f95118Swdenk 52171f95118Swdenk /* Calculate short name checksum */ 5227385c28eSWolfgang Denk static __u8 mkcksum (const char *str) 52371f95118Swdenk { 52471f95118Swdenk int i; 5257385c28eSWolfgang Denk 52671f95118Swdenk __u8 ret = 0; 52771f95118Swdenk 52871f95118Swdenk for (i = 0; i < 11; i++) { 52971f95118Swdenk ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i]; 53071f95118Swdenk } 53171f95118Swdenk 53271f95118Swdenk return ret; 53371f95118Swdenk } 5347385c28eSWolfgang Denk #endif /* CONFIG_SUPPORT_VFAT */ 53571f95118Swdenk 53671f95118Swdenk /* 53771f95118Swdenk * Get the directory entry associated with 'filename' from the directory 53871f95118Swdenk * starting at 'startsect' 53971f95118Swdenk */ 5407e4b9b4fSBryan Wu __attribute__ ((__aligned__ (__alignof__ (dir_entry)))) 5415fa66df6Swdenk __u8 get_dentfromdir_block[MAX_CLUSTSIZE]; 5427385c28eSWolfgang Denk 54371f95118Swdenk static dir_entry *get_dentfromdir (fsdata *mydata, int startsect, 54471f95118Swdenk char *filename, dir_entry *retdent, 54571f95118Swdenk int dols) 54671f95118Swdenk { 54771f95118Swdenk __u16 prevcksum = 0xffff; 54871f95118Swdenk __u32 curclust = START(retdent); 54971f95118Swdenk int files = 0, dirs = 0; 55071f95118Swdenk 5517385c28eSWolfgang Denk debug("get_dentfromdir: %s\n", filename); 5527385c28eSWolfgang Denk 55371f95118Swdenk while (1) { 55471f95118Swdenk dir_entry *dentptr; 5557385c28eSWolfgang Denk 55671f95118Swdenk int i; 55771f95118Swdenk 5585fa66df6Swdenk if (get_cluster(mydata, curclust, get_dentfromdir_block, 559ac497771SSergei Shtylyov mydata->clust_size * mydata->sect_size) != 0) { 5607385c28eSWolfgang Denk debug("Error: reading directory block\n"); 56171f95118Swdenk return NULL; 56271f95118Swdenk } 5637385c28eSWolfgang Denk 5645fa66df6Swdenk dentptr = (dir_entry *)get_dentfromdir_block; 5657385c28eSWolfgang Denk 56671f95118Swdenk for (i = 0; i < DIRENTSPERCLUST; i++) { 5673831530dSMikhail Zolotaryov char s_name[14], l_name[VFAT_MAXLEN_BYTES]; 56871f95118Swdenk 56971f95118Swdenk l_name[0] = '\0'; 570855a496fSwdenk if (dentptr->name[0] == DELETED_FLAG) { 571855a496fSwdenk dentptr++; 572855a496fSwdenk continue; 573855a496fSwdenk } 57471f95118Swdenk if ((dentptr->attr & ATTR_VOLUME)) { 57571f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 57671f95118Swdenk if ((dentptr->attr & ATTR_VFAT) && 5772d1a537dSwdenk (dentptr-> name[0] & LAST_LONG_ENTRY_MASK)) { 5787385c28eSWolfgang Denk prevcksum = ((dir_slot *)dentptr)->alias_checksum; 5797385c28eSWolfgang Denk get_vfatname(mydata, curclust, 5807385c28eSWolfgang Denk get_dentfromdir_block, 58171f95118Swdenk dentptr, l_name); 58271f95118Swdenk if (dols) { 5837385c28eSWolfgang Denk int isdir; 58471f95118Swdenk char dirc; 58571f95118Swdenk int doit = 0; 58671f95118Swdenk 5877385c28eSWolfgang Denk isdir = (dentptr->attr & ATTR_DIR); 5887385c28eSWolfgang Denk 58971f95118Swdenk if (isdir) { 59071f95118Swdenk dirs++; 59171f95118Swdenk dirc = '/'; 59271f95118Swdenk doit = 1; 59371f95118Swdenk } else { 59471f95118Swdenk dirc = ' '; 59571f95118Swdenk if (l_name[0] != 0) { 59671f95118Swdenk files++; 59771f95118Swdenk doit = 1; 59871f95118Swdenk } 59971f95118Swdenk } 60071f95118Swdenk if (doit) { 60171f95118Swdenk if (dirc == ' ') { 60271f95118Swdenk printf(" %8ld %s%c\n", 60371f95118Swdenk (long)FAT2CPU32(dentptr->size), 6047385c28eSWolfgang Denk l_name, 6057385c28eSWolfgang Denk dirc); 60671f95118Swdenk } else { 6077385c28eSWolfgang Denk printf(" %s%c\n", 6087385c28eSWolfgang Denk l_name, 6097385c28eSWolfgang Denk dirc); 61071f95118Swdenk } 61171f95118Swdenk } 61271f95118Swdenk dentptr++; 61371f95118Swdenk continue; 61471f95118Swdenk } 6157385c28eSWolfgang Denk debug("vfatname: |%s|\n", l_name); 61671f95118Swdenk } else 61771f95118Swdenk #endif 61871f95118Swdenk { 61971f95118Swdenk /* Volume label or VFAT entry */ 62071f95118Swdenk dentptr++; 62171f95118Swdenk continue; 62271f95118Swdenk } 62371f95118Swdenk } 62471f95118Swdenk if (dentptr->name[0] == 0) { 62571f95118Swdenk if (dols) { 6267385c28eSWolfgang Denk printf("\n%d file(s), %d dir(s)\n\n", 6277385c28eSWolfgang Denk files, dirs); 62871f95118Swdenk } 6297385c28eSWolfgang Denk debug("Dentname == NULL - %d\n", i); 63071f95118Swdenk return NULL; 63171f95118Swdenk } 63271f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 63371f95118Swdenk if (dols && mkcksum(dentptr->name) == prevcksum) { 63471f95118Swdenk dentptr++; 63571f95118Swdenk continue; 63671f95118Swdenk } 63771f95118Swdenk #endif 63871f95118Swdenk get_name(dentptr, s_name); 63971f95118Swdenk if (dols) { 64071f95118Swdenk int isdir = (dentptr->attr & ATTR_DIR); 64171f95118Swdenk char dirc; 64271f95118Swdenk int doit = 0; 64371f95118Swdenk 64471f95118Swdenk if (isdir) { 64571f95118Swdenk dirs++; 64671f95118Swdenk dirc = '/'; 64771f95118Swdenk doit = 1; 64871f95118Swdenk } else { 64971f95118Swdenk dirc = ' '; 65071f95118Swdenk if (s_name[0] != 0) { 65171f95118Swdenk files++; 65271f95118Swdenk doit = 1; 65371f95118Swdenk } 65471f95118Swdenk } 6557385c28eSWolfgang Denk 65671f95118Swdenk if (doit) { 65771f95118Swdenk if (dirc == ' ') { 65871f95118Swdenk printf(" %8ld %s%c\n", 6597385c28eSWolfgang Denk (long)FAT2CPU32(dentptr->size), 6607385c28eSWolfgang Denk s_name, dirc); 66171f95118Swdenk } else { 6627385c28eSWolfgang Denk printf(" %s%c\n", 6637385c28eSWolfgang Denk s_name, dirc); 66471f95118Swdenk } 66571f95118Swdenk } 6667385c28eSWolfgang Denk 66771f95118Swdenk dentptr++; 66871f95118Swdenk continue; 66971f95118Swdenk } 6707385c28eSWolfgang Denk 6717385c28eSWolfgang Denk if (strcmp(filename, s_name) 6727385c28eSWolfgang Denk && strcmp(filename, l_name)) { 6737385c28eSWolfgang Denk debug("Mismatch: |%s|%s|\n", s_name, l_name); 67471f95118Swdenk dentptr++; 67571f95118Swdenk continue; 67671f95118Swdenk } 6777385c28eSWolfgang Denk 67871f95118Swdenk memcpy(retdent, dentptr, sizeof(dir_entry)); 67971f95118Swdenk 6807385c28eSWolfgang Denk debug("DentName: %s", s_name); 6817385c28eSWolfgang Denk debug(", start: 0x%x", START(dentptr)); 6827385c28eSWolfgang Denk debug(", size: 0x%x %s\n", 68371f95118Swdenk FAT2CPU32(dentptr->size), 68471f95118Swdenk (dentptr->attr & ATTR_DIR) ? "(DIR)" : ""); 68571f95118Swdenk 68671f95118Swdenk return retdent; 68771f95118Swdenk } 6887385c28eSWolfgang Denk 68971f95118Swdenk curclust = get_fatent(mydata, curclust); 6908ce4e5c2Smichael if (CHECK_CLUST(curclust, mydata->fatsize)) { 6917385c28eSWolfgang Denk debug("curclust: 0x%x\n", curclust); 6927385c28eSWolfgang Denk printf("Invalid FAT entry\n"); 69371f95118Swdenk return NULL; 69471f95118Swdenk } 69571f95118Swdenk } 69671f95118Swdenk 69771f95118Swdenk return NULL; 69871f95118Swdenk } 69971f95118Swdenk 70071f95118Swdenk /* 70171f95118Swdenk * Read boot sector and volume info from a FAT filesystem 70271f95118Swdenk */ 70371f95118Swdenk static int 70471f95118Swdenk read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize) 70571f95118Swdenk { 706ac497771SSergei Shtylyov __u8 *block; 70771f95118Swdenk volume_info *vistart; 708ac497771SSergei Shtylyov int ret = 0; 709ac497771SSergei Shtylyov 710ac497771SSergei Shtylyov if (cur_dev == NULL) { 711ac497771SSergei Shtylyov debug("Error: no device selected\n"); 712ac497771SSergei Shtylyov return -1; 713ac497771SSergei Shtylyov } 714ac497771SSergei Shtylyov 715ac497771SSergei Shtylyov block = malloc(cur_dev->blksz); 716ac497771SSergei Shtylyov if (block == NULL) { 717ac497771SSergei Shtylyov debug("Error: allocating block\n"); 718ac497771SSergei Shtylyov return -1; 719ac497771SSergei Shtylyov } 72071f95118Swdenk 72171f95118Swdenk if (disk_read (0, 1, block) < 0) { 7227385c28eSWolfgang Denk debug("Error: reading block\n"); 723ac497771SSergei Shtylyov goto fail; 72471f95118Swdenk } 72571f95118Swdenk 72671f95118Swdenk memcpy(bs, block, sizeof(boot_sector)); 72771f95118Swdenk bs->reserved = FAT2CPU16(bs->reserved); 72871f95118Swdenk bs->fat_length = FAT2CPU16(bs->fat_length); 72971f95118Swdenk bs->secs_track = FAT2CPU16(bs->secs_track); 73071f95118Swdenk bs->heads = FAT2CPU16(bs->heads); 73171f95118Swdenk bs->total_sect = FAT2CPU32(bs->total_sect); 73271f95118Swdenk 73371f95118Swdenk /* FAT32 entries */ 73471f95118Swdenk if (bs->fat_length == 0) { 73571f95118Swdenk /* Assume FAT32 */ 73671f95118Swdenk bs->fat32_length = FAT2CPU32(bs->fat32_length); 73771f95118Swdenk bs->flags = FAT2CPU16(bs->flags); 73871f95118Swdenk bs->root_cluster = FAT2CPU32(bs->root_cluster); 73971f95118Swdenk bs->info_sector = FAT2CPU16(bs->info_sector); 74071f95118Swdenk bs->backup_boot = FAT2CPU16(bs->backup_boot); 74171f95118Swdenk vistart = (volume_info *)(block + sizeof(boot_sector)); 74271f95118Swdenk *fatsize = 32; 74371f95118Swdenk } else { 74471f95118Swdenk vistart = (volume_info *)&(bs->fat32_length); 74571f95118Swdenk *fatsize = 0; 74671f95118Swdenk } 74771f95118Swdenk memcpy(volinfo, vistart, sizeof(volume_info)); 74871f95118Swdenk 74971f95118Swdenk if (*fatsize == 32) { 7507385c28eSWolfgang Denk if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0) 751ac497771SSergei Shtylyov goto exit; 75271f95118Swdenk } else { 753651351feSTom Rix if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) { 75471f95118Swdenk *fatsize = 12; 755ac497771SSergei Shtylyov goto exit; 75671f95118Swdenk } 757651351feSTom Rix if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) { 75871f95118Swdenk *fatsize = 16; 759ac497771SSergei Shtylyov goto exit; 76071f95118Swdenk } 76171f95118Swdenk } 76271f95118Swdenk 7637385c28eSWolfgang Denk debug("Error: broken fs_type sign\n"); 764ac497771SSergei Shtylyov fail: 765ac497771SSergei Shtylyov ret = -1; 766ac497771SSergei Shtylyov exit: 767ac497771SSergei Shtylyov free(block); 768ac497771SSergei Shtylyov return ret; 76971f95118Swdenk } 77071f95118Swdenk 7717e4b9b4fSBryan Wu __attribute__ ((__aligned__ (__alignof__ (dir_entry)))) 7727e4b9b4fSBryan Wu __u8 do_fat_read_block[MAX_CLUSTSIZE]; 7737385c28eSWolfgang Denk 77420cc00ddSstroese long 77571f95118Swdenk do_fat_read (const char *filename, void *buffer, unsigned long maxsize, 77671f95118Swdenk int dols) 77771f95118Swdenk { 77871f95118Swdenk char fnamecopy[2048]; 77971f95118Swdenk boot_sector bs; 78071f95118Swdenk volume_info volinfo; 78171f95118Swdenk fsdata datablock; 78271f95118Swdenk fsdata *mydata = &datablock; 78371f95118Swdenk dir_entry *dentptr; 78471f95118Swdenk __u16 prevcksum = 0xffff; 78571f95118Swdenk char *subname = ""; 7863f270f42SErik Hansen __u32 cursect; 78771f95118Swdenk int idx, isdir = 0; 78871f95118Swdenk int files = 0, dirs = 0; 789ac497771SSergei Shtylyov long ret = -1; 79071f95118Swdenk int firsttime; 7913f270f42SErik Hansen __u32 root_cluster; 7923f270f42SErik Hansen int rootdir_size = 0; 7932aa98c66SWolfgang Denk int j; 79471f95118Swdenk 79571f95118Swdenk if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { 7967385c28eSWolfgang Denk debug("Error: reading boot sector\n"); 79771f95118Swdenk return -1; 79871f95118Swdenk } 7997385c28eSWolfgang Denk 8002aa98c66SWolfgang Denk root_cluster = bs.root_cluster; 8017385c28eSWolfgang Denk 8027385c28eSWolfgang Denk if (mydata->fatsize == 32) 80371f95118Swdenk mydata->fatlength = bs.fat32_length; 8047385c28eSWolfgang Denk else 80571f95118Swdenk mydata->fatlength = bs.fat_length; 8067385c28eSWolfgang Denk 80771f95118Swdenk mydata->fat_sect = bs.reserved; 8087385c28eSWolfgang Denk 80971f95118Swdenk cursect = mydata->rootdir_sect 81071f95118Swdenk = mydata->fat_sect + mydata->fatlength * bs.fats; 8117385c28eSWolfgang Denk 812ac497771SSergei Shtylyov mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; 81371f95118Swdenk mydata->clust_size = bs.cluster_size; 8147385c28eSWolfgang Denk 81571f95118Swdenk if (mydata->fatsize == 32) { 8167385c28eSWolfgang Denk mydata->data_begin = mydata->rootdir_sect - 8177385c28eSWolfgang Denk (mydata->clust_size * 2); 81871f95118Swdenk } else { 8197385c28eSWolfgang Denk rootdir_size = ((bs.dir_entries[1] * (int)256 + 8207385c28eSWolfgang Denk bs.dir_entries[0]) * 8217385c28eSWolfgang Denk sizeof(dir_entry)) / 822ac497771SSergei Shtylyov mydata->sect_size; 8237385c28eSWolfgang Denk mydata->data_begin = mydata->rootdir_sect + 8247385c28eSWolfgang Denk rootdir_size - 8257385c28eSWolfgang Denk (mydata->clust_size * 2); 82671f95118Swdenk } 8277385c28eSWolfgang Denk 82871f95118Swdenk mydata->fatbufnum = -1; 829ac497771SSergei Shtylyov mydata->fatbuf = malloc(FATBUFSIZE); 830ac497771SSergei Shtylyov if (mydata->fatbuf == NULL) { 831ac497771SSergei Shtylyov debug("Error: allocating memory\n"); 832ac497771SSergei Shtylyov return -1; 833ac497771SSergei Shtylyov } 83471f95118Swdenk 8352aa98c66SWolfgang Denk #ifdef CONFIG_SUPPORT_VFAT 8367385c28eSWolfgang Denk debug("VFAT Support enabled\n"); 8372aa98c66SWolfgang Denk #endif 8387385c28eSWolfgang Denk debug("FAT%d, fat_sect: %d, fatlength: %d\n", 8397385c28eSWolfgang Denk mydata->fatsize, mydata->fat_sect, mydata->fatlength); 8407385c28eSWolfgang Denk debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n" 84171f95118Swdenk "Data begins at: %d\n", 8422aa98c66SWolfgang Denk root_cluster, 8432aa98c66SWolfgang Denk mydata->rootdir_sect, 844ac497771SSergei Shtylyov mydata->rootdir_sect * mydata->sect_size, mydata->data_begin); 845ac497771SSergei Shtylyov debug("Sector size: %d, cluster size: %d\n", mydata->sect_size, 846ac497771SSergei Shtylyov mydata->clust_size); 84771f95118Swdenk 84871f95118Swdenk /* "cwd" is always the root... */ 84971f95118Swdenk while (ISDIRDELIM(*filename)) 85071f95118Swdenk filename++; 8517385c28eSWolfgang Denk 85271f95118Swdenk /* Make a copy of the filename and convert it to lowercase */ 85371f95118Swdenk strcpy(fnamecopy, filename); 85471f95118Swdenk downcase(fnamecopy); 8557385c28eSWolfgang Denk 85671f95118Swdenk if (*fnamecopy == '\0') { 85771f95118Swdenk if (!dols) 858ac497771SSergei Shtylyov goto exit; 8597385c28eSWolfgang Denk 86071f95118Swdenk dols = LS_ROOT; 86171f95118Swdenk } else if ((idx = dirdelim(fnamecopy)) >= 0) { 86271f95118Swdenk isdir = 1; 86371f95118Swdenk fnamecopy[idx] = '\0'; 86471f95118Swdenk subname = fnamecopy + idx + 1; 8657385c28eSWolfgang Denk 86671f95118Swdenk /* Handle multiple delimiters */ 86771f95118Swdenk while (ISDIRDELIM(*subname)) 86871f95118Swdenk subname++; 86971f95118Swdenk } else if (dols) { 87071f95118Swdenk isdir = 1; 87171f95118Swdenk } 87271f95118Swdenk 8732aa98c66SWolfgang Denk j = 0; 87471f95118Swdenk while (1) { 87571f95118Swdenk int i; 87671f95118Swdenk 8777385c28eSWolfgang Denk debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%d\n", 8782aa98c66SWolfgang Denk cursect, mydata->clust_size, DIRENTSPERBLOCK); 8797385c28eSWolfgang Denk 8803831530dSMikhail Zolotaryov if (disk_read(cursect, 8813831530dSMikhail Zolotaryov (mydata->fatsize == 32) ? 8823831530dSMikhail Zolotaryov (mydata->clust_size) : 883ac497771SSergei Shtylyov LINEAR_PREFETCH_SIZE / mydata->sect_size, 8843831530dSMikhail Zolotaryov do_fat_read_block) < 0) { 8857385c28eSWolfgang Denk debug("Error: reading rootdir block\n"); 886ac497771SSergei Shtylyov goto exit; 88771f95118Swdenk } 8887385c28eSWolfgang Denk 8895fa66df6Swdenk dentptr = (dir_entry *) do_fat_read_block; 8907385c28eSWolfgang Denk 89171f95118Swdenk for (i = 0; i < DIRENTSPERBLOCK; i++) { 8923831530dSMikhail Zolotaryov char s_name[14], l_name[VFAT_MAXLEN_BYTES]; 89371f95118Swdenk 89471f95118Swdenk l_name[0] = '\0'; 8953831530dSMikhail Zolotaryov if (dentptr->name[0] == DELETED_FLAG) { 8963831530dSMikhail Zolotaryov dentptr++; 8973831530dSMikhail Zolotaryov continue; 8983831530dSMikhail Zolotaryov } 89971f95118Swdenk if ((dentptr->attr & ATTR_VOLUME)) { 90071f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 90171f95118Swdenk if ((dentptr->attr & ATTR_VFAT) && 9022d1a537dSwdenk (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { 9037385c28eSWolfgang Denk prevcksum = 9047385c28eSWolfgang Denk ((dir_slot *)dentptr)->alias_checksum; 9057385c28eSWolfgang Denk 9063831530dSMikhail Zolotaryov get_vfatname(mydata, 9073831530dSMikhail Zolotaryov (mydata->fatsize == 32) ? 9083831530dSMikhail Zolotaryov root_cluster : 9093831530dSMikhail Zolotaryov 0, 9107385c28eSWolfgang Denk do_fat_read_block, 9117385c28eSWolfgang Denk dentptr, l_name); 9127385c28eSWolfgang Denk 91371f95118Swdenk if (dols == LS_ROOT) { 91471f95118Swdenk char dirc; 91571f95118Swdenk int doit = 0; 9167385c28eSWolfgang Denk int isdir = 9177385c28eSWolfgang Denk (dentptr->attr & ATTR_DIR); 91871f95118Swdenk 91971f95118Swdenk if (isdir) { 92071f95118Swdenk dirs++; 92171f95118Swdenk dirc = '/'; 92271f95118Swdenk doit = 1; 92371f95118Swdenk } else { 92471f95118Swdenk dirc = ' '; 92571f95118Swdenk if (l_name[0] != 0) { 92671f95118Swdenk files++; 92771f95118Swdenk doit = 1; 92871f95118Swdenk } 92971f95118Swdenk } 93071f95118Swdenk if (doit) { 93171f95118Swdenk if (dirc == ' ') { 93271f95118Swdenk printf(" %8ld %s%c\n", 93371f95118Swdenk (long)FAT2CPU32(dentptr->size), 9347385c28eSWolfgang Denk l_name, 9357385c28eSWolfgang Denk dirc); 93671f95118Swdenk } else { 9377385c28eSWolfgang Denk printf(" %s%c\n", 9387385c28eSWolfgang Denk l_name, 9397385c28eSWolfgang Denk dirc); 94071f95118Swdenk } 94171f95118Swdenk } 94271f95118Swdenk dentptr++; 94371f95118Swdenk continue; 94471f95118Swdenk } 9457385c28eSWolfgang Denk debug("Rootvfatname: |%s|\n", 9467385c28eSWolfgang Denk l_name); 94771f95118Swdenk } else 94871f95118Swdenk #endif 94971f95118Swdenk { 95071f95118Swdenk /* Volume label or VFAT entry */ 95171f95118Swdenk dentptr++; 95271f95118Swdenk continue; 95371f95118Swdenk } 95471f95118Swdenk } else if (dentptr->name[0] == 0) { 9557385c28eSWolfgang Denk debug("RootDentname == NULL - %d\n", i); 95671f95118Swdenk if (dols == LS_ROOT) { 9577385c28eSWolfgang Denk printf("\n%d file(s), %d dir(s)\n\n", 9587385c28eSWolfgang Denk files, dirs); 959ac497771SSergei Shtylyov ret = 0; 96071f95118Swdenk } 961ac497771SSergei Shtylyov goto exit; 96271f95118Swdenk } 96371f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 9647385c28eSWolfgang Denk else if (dols == LS_ROOT && 9657385c28eSWolfgang Denk mkcksum(dentptr->name) == prevcksum) { 96671f95118Swdenk dentptr++; 96771f95118Swdenk continue; 96871f95118Swdenk } 96971f95118Swdenk #endif 97071f95118Swdenk get_name(dentptr, s_name); 9717385c28eSWolfgang Denk 97271f95118Swdenk if (dols == LS_ROOT) { 97371f95118Swdenk int isdir = (dentptr->attr & ATTR_DIR); 97471f95118Swdenk char dirc; 97571f95118Swdenk int doit = 0; 97671f95118Swdenk 97771f95118Swdenk if (isdir) { 97871f95118Swdenk dirc = '/'; 979a43278a4Swdenk if (s_name[0] != 0) { 980a43278a4Swdenk dirs++; 98171f95118Swdenk doit = 1; 982a43278a4Swdenk } 98371f95118Swdenk } else { 98471f95118Swdenk dirc = ' '; 98571f95118Swdenk if (s_name[0] != 0) { 98671f95118Swdenk files++; 98771f95118Swdenk doit = 1; 98871f95118Swdenk } 98971f95118Swdenk } 99071f95118Swdenk if (doit) { 99171f95118Swdenk if (dirc == ' ') { 99271f95118Swdenk printf(" %8ld %s%c\n", 9937385c28eSWolfgang Denk (long)FAT2CPU32(dentptr->size), 9947385c28eSWolfgang Denk s_name, dirc); 99571f95118Swdenk } else { 9967385c28eSWolfgang Denk printf(" %s%c\n", 9977385c28eSWolfgang Denk s_name, dirc); 99871f95118Swdenk } 99971f95118Swdenk } 100071f95118Swdenk dentptr++; 100171f95118Swdenk continue; 100271f95118Swdenk } 10037385c28eSWolfgang Denk 10047385c28eSWolfgang Denk if (strcmp(fnamecopy, s_name) 10057385c28eSWolfgang Denk && strcmp(fnamecopy, l_name)) { 10067385c28eSWolfgang Denk debug("RootMismatch: |%s|%s|\n", s_name, 10077385c28eSWolfgang Denk l_name); 100871f95118Swdenk dentptr++; 100971f95118Swdenk continue; 101071f95118Swdenk } 10117385c28eSWolfgang Denk 101271f95118Swdenk if (isdir && !(dentptr->attr & ATTR_DIR)) 1013ac497771SSergei Shtylyov goto exit; 101471f95118Swdenk 10157385c28eSWolfgang Denk debug("RootName: %s", s_name); 10167385c28eSWolfgang Denk debug(", start: 0x%x", START(dentptr)); 10177385c28eSWolfgang Denk debug(", size: 0x%x %s\n", 10187385c28eSWolfgang Denk FAT2CPU32(dentptr->size), 10197385c28eSWolfgang Denk isdir ? "(DIR)" : ""); 102071f95118Swdenk 102171f95118Swdenk goto rootdir_done; /* We got a match */ 102271f95118Swdenk } 10237385c28eSWolfgang Denk debug("END LOOP: j=%d clust_size=%d\n", j, 10247385c28eSWolfgang Denk mydata->clust_size); 10252aa98c66SWolfgang Denk 10262aa98c66SWolfgang Denk /* 10272aa98c66SWolfgang Denk * On FAT32 we must fetch the FAT entries for the next 10282aa98c66SWolfgang Denk * root directory clusters when a cluster has been 10292aa98c66SWolfgang Denk * completely processed. 10302aa98c66SWolfgang Denk */ 10313f270f42SErik Hansen ++j; 10323f270f42SErik Hansen int fat32_end = 0; 10333f270f42SErik Hansen if ((mydata->fatsize == 32) && (j == mydata->clust_size)) { 10343f270f42SErik Hansen int nxtsect = 0; 10353f270f42SErik Hansen int nxt_clust = 0; 10362aa98c66SWolfgang Denk 10372aa98c66SWolfgang Denk nxt_clust = get_fatent(mydata, root_cluster); 10383f270f42SErik Hansen fat32_end = CHECK_CLUST(nxt_clust, 32); 10397385c28eSWolfgang Denk 10407385c28eSWolfgang Denk nxtsect = mydata->data_begin + 10417385c28eSWolfgang Denk (nxt_clust * mydata->clust_size); 10427385c28eSWolfgang Denk 10432aa98c66SWolfgang Denk root_cluster = nxt_clust; 10442aa98c66SWolfgang Denk 10452aa98c66SWolfgang Denk cursect = nxtsect; 10462aa98c66SWolfgang Denk j = 0; 10472aa98c66SWolfgang Denk } else { 104871f95118Swdenk cursect++; 104971f95118Swdenk } 10503f270f42SErik Hansen 10513f270f42SErik Hansen /* If end of rootdir reached */ 10523f270f42SErik Hansen if ((mydata->fatsize == 32 && fat32_end) || 10533f270f42SErik Hansen (mydata->fatsize != 32 && j == rootdir_size)) { 10543f270f42SErik Hansen if (dols == LS_ROOT) { 10553f270f42SErik Hansen printf("\n%d file(s), %d dir(s)\n\n", 10563f270f42SErik Hansen files, dirs); 1057ac497771SSergei Shtylyov ret = 0; 10583f270f42SErik Hansen } 1059ac497771SSergei Shtylyov goto exit; 10603f270f42SErik Hansen } 10612aa98c66SWolfgang Denk } 106271f95118Swdenk rootdir_done: 106371f95118Swdenk 106471f95118Swdenk firsttime = 1; 10657385c28eSWolfgang Denk 106671f95118Swdenk while (isdir) { 106771f95118Swdenk int startsect = mydata->data_begin 106871f95118Swdenk + START(dentptr) * mydata->clust_size; 106971f95118Swdenk dir_entry dent; 107071f95118Swdenk char *nextname = NULL; 107171f95118Swdenk 107271f95118Swdenk dent = *dentptr; 107371f95118Swdenk dentptr = &dent; 107471f95118Swdenk 107571f95118Swdenk idx = dirdelim(subname); 10767385c28eSWolfgang Denk 107771f95118Swdenk if (idx >= 0) { 107871f95118Swdenk subname[idx] = '\0'; 107971f95118Swdenk nextname = subname + idx + 1; 108071f95118Swdenk /* Handle multiple delimiters */ 108171f95118Swdenk while (ISDIRDELIM(*nextname)) 108271f95118Swdenk nextname++; 108371f95118Swdenk if (dols && *nextname == '\0') 108471f95118Swdenk firsttime = 0; 108571f95118Swdenk } else { 108671f95118Swdenk if (dols && firsttime) { 108771f95118Swdenk firsttime = 0; 108871f95118Swdenk } else { 108971f95118Swdenk isdir = 0; 109071f95118Swdenk } 109171f95118Swdenk } 109271f95118Swdenk 109371f95118Swdenk if (get_dentfromdir(mydata, startsect, subname, dentptr, 109471f95118Swdenk isdir ? 0 : dols) == NULL) { 109571f95118Swdenk if (dols && !isdir) 1096ac497771SSergei Shtylyov ret = 0; 1097ac497771SSergei Shtylyov goto exit; 109871f95118Swdenk } 109971f95118Swdenk 110071f95118Swdenk if (idx >= 0) { 110171f95118Swdenk if (!(dentptr->attr & ATTR_DIR)) 1102ac497771SSergei Shtylyov goto exit; 110371f95118Swdenk subname = nextname; 110471f95118Swdenk } 110571f95118Swdenk } 11067385c28eSWolfgang Denk 110771f95118Swdenk ret = get_contents(mydata, dentptr, buffer, maxsize); 11087385c28eSWolfgang Denk debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret); 110971f95118Swdenk 1110ac497771SSergei Shtylyov exit: 1111ac497771SSergei Shtylyov free(mydata->fatbuf); 111271f95118Swdenk return ret; 111371f95118Swdenk } 111471f95118Swdenk 11157385c28eSWolfgang Denk int file_fat_detectfs (void) 111671f95118Swdenk { 111771f95118Swdenk boot_sector bs; 111871f95118Swdenk volume_info volinfo; 111971f95118Swdenk int fatsize; 11207205e407Swdenk char vol_label[12]; 112171f95118Swdenk 11227205e407Swdenk if (cur_dev == NULL) { 11237205e407Swdenk printf("No current device\n"); 11247205e407Swdenk return 1; 11257205e407Swdenk } 11267385c28eSWolfgang Denk 1127dd60d122SJon Loeliger #if defined(CONFIG_CMD_IDE) || \ 112875eb82ecSunsik Kim defined(CONFIG_CMD_MG_DISK) || \ 11298c5170a7SSonic Zhang defined(CONFIG_CMD_SATA) || \ 1130dd60d122SJon Loeliger defined(CONFIG_CMD_SCSI) || \ 1131dd60d122SJon Loeliger defined(CONFIG_CMD_USB) || \ 113221f6f963SAndy Fleming defined(CONFIG_MMC) 11337205e407Swdenk printf("Interface: "); 11347205e407Swdenk switch (cur_dev->if_type) { 11357385c28eSWolfgang Denk case IF_TYPE_IDE: 11367385c28eSWolfgang Denk printf("IDE"); 11377385c28eSWolfgang Denk break; 11387385c28eSWolfgang Denk case IF_TYPE_SATA: 11397385c28eSWolfgang Denk printf("SATA"); 11407385c28eSWolfgang Denk break; 11417385c28eSWolfgang Denk case IF_TYPE_SCSI: 11427385c28eSWolfgang Denk printf("SCSI"); 11437385c28eSWolfgang Denk break; 11447385c28eSWolfgang Denk case IF_TYPE_ATAPI: 11457385c28eSWolfgang Denk printf("ATAPI"); 11467385c28eSWolfgang Denk break; 11477385c28eSWolfgang Denk case IF_TYPE_USB: 11487385c28eSWolfgang Denk printf("USB"); 11497385c28eSWolfgang Denk break; 11507385c28eSWolfgang Denk case IF_TYPE_DOC: 11517385c28eSWolfgang Denk printf("DOC"); 11527385c28eSWolfgang Denk break; 11537385c28eSWolfgang Denk case IF_TYPE_MMC: 11547385c28eSWolfgang Denk printf("MMC"); 11557385c28eSWolfgang Denk break; 11567385c28eSWolfgang Denk default: 11577385c28eSWolfgang Denk printf("Unknown"); 11587205e407Swdenk } 11597385c28eSWolfgang Denk 11607205e407Swdenk printf("\n Device %d: ", cur_dev->dev); 11617205e407Swdenk dev_print(cur_dev); 11627205e407Swdenk #endif 11637385c28eSWolfgang Denk 11647205e407Swdenk if (read_bootsectandvi(&bs, &volinfo, &fatsize)) { 11657205e407Swdenk printf("\nNo valid FAT fs found\n"); 11667205e407Swdenk return 1; 11677205e407Swdenk } 11687385c28eSWolfgang Denk 11697205e407Swdenk memcpy(vol_label, volinfo.volume_label, 11); 11707205e407Swdenk vol_label[11] = '\0'; 11717205e407Swdenk volinfo.fs_type[5] = '\0'; 11727385c28eSWolfgang Denk 11737385c28eSWolfgang Denk printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part, 11747385c28eSWolfgang Denk volinfo.fs_type, vol_label); 11757385c28eSWolfgang Denk 11767205e407Swdenk return 0; 117771f95118Swdenk } 117871f95118Swdenk 11797385c28eSWolfgang Denk int file_fat_ls (const char *dir) 118071f95118Swdenk { 118171f95118Swdenk return do_fat_read(dir, NULL, 0, LS_YES); 118271f95118Swdenk } 118371f95118Swdenk 11847385c28eSWolfgang Denk long file_fat_read (const char *filename, void *buffer, unsigned long maxsize) 118571f95118Swdenk { 11867205e407Swdenk printf("reading %s\n", filename); 118771f95118Swdenk return do_fat_read(filename, buffer, maxsize, LS_NO); 118871f95118Swdenk } 1189