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> 349a800ac7SEric Nelson #include <malloc.h> 359a800ac7SEric Nelson #include <linux/compiler.h> 3671f95118Swdenk 3771f95118Swdenk /* 3871f95118Swdenk * Convert a string to lowercase. 3971f95118Swdenk */ 407385c28eSWolfgang Denk static void downcase(char *str) 4171f95118Swdenk { 4271f95118Swdenk while (*str != '\0') { 4371f95118Swdenk TOLOWER(*str); 4471f95118Swdenk str++; 4571f95118Swdenk } 4671f95118Swdenk } 4771f95118Swdenk 489813b750SKyle Moffett static block_dev_desc_t *cur_dev; 499813b750SKyle Moffett static unsigned int cur_part_nr; 509813b750SKyle Moffett static disk_partition_t cur_part_info; 517385c28eSWolfgang Denk 529813b750SKyle Moffett #define DOS_BOOT_MAGIC_OFFSET 0x1fe 537205e407Swdenk #define DOS_FS_TYPE_OFFSET 0x36 5466c2d73cSWolfgang Denk #define DOS_FS32_TYPE_OFFSET 0x52 5571f95118Swdenk 569813b750SKyle Moffett static int disk_read(__u32 block, __u32 nr_blocks, void *buf) 5771f95118Swdenk { 589813b750SKyle Moffett if (!cur_dev || !cur_dev->block_read) 597205e407Swdenk return -1; 607385c28eSWolfgang Denk 619813b750SKyle Moffett return cur_dev->block_read(cur_dev->dev, 629813b750SKyle Moffett cur_part_info.start + block, nr_blocks, buf); 6371f95118Swdenk } 6471f95118Swdenk 657385c28eSWolfgang Denk int fat_register_device(block_dev_desc_t * dev_desc, int part_no) 6671f95118Swdenk { 679a800ac7SEric Nelson ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz); 687205e407Swdenk 699813b750SKyle Moffett /* First close any currently found FAT filesystem */ 709813b750SKyle Moffett cur_dev = NULL; 717385c28eSWolfgang Denk 72dd60d122SJon Loeliger #if (defined(CONFIG_CMD_IDE) || \ 738c5170a7SSonic Zhang defined(CONFIG_CMD_SATA) || \ 74dd60d122SJon Loeliger defined(CONFIG_CMD_SCSI) || \ 75dd60d122SJon Loeliger defined(CONFIG_CMD_USB) || \ 7602df4a27SAndy Fleming defined(CONFIG_MMC) || \ 77b0d8f5bfSPeter Pearse defined(CONFIG_SYSTEMACE) ) 786471ada5SWolfgang Denk 799813b750SKyle Moffett /* Read the partition table, if present */ 809813b750SKyle Moffett if (!get_partition_info(dev_desc, part_no, &cur_part_info)) { 819813b750SKyle Moffett cur_dev = dev_desc; 829813b750SKyle Moffett cur_part_nr = part_no; 839813b750SKyle Moffett } 849813b750SKyle Moffett #endif 859813b750SKyle Moffett 869813b750SKyle Moffett /* Otherwise it might be a superfloppy (whole-disk FAT filesystem) */ 879813b750SKyle Moffett if (!cur_dev) { 889813b750SKyle Moffett if (part_no != 1) { 89bf1060eaSWolfgang Denk printf("** Partition %d not valid on device %d **\n", 90bf1060eaSWolfgang Denk part_no, dev_desc->dev); 917205e407Swdenk return -1; 927205e407Swdenk } 939813b750SKyle Moffett 949813b750SKyle Moffett cur_dev = dev_desc; 959813b750SKyle Moffett cur_part_nr = 1; 969813b750SKyle Moffett cur_part_info.start = 0; 979813b750SKyle Moffett cur_part_info.size = dev_desc->lba; 989813b750SKyle Moffett cur_part_info.blksz = dev_desc->blksz; 999813b750SKyle Moffett memset(cur_part_info.name, 0, sizeof(cur_part_info.name)); 1009813b750SKyle Moffett memset(cur_part_info.type, 0, sizeof(cur_part_info.type)); 1016471ada5SWolfgang Denk } 1029813b750SKyle Moffett 1039813b750SKyle Moffett /* Make sure it has a valid FAT header */ 1049813b750SKyle Moffett if (disk_read(0, 1, buffer) != 1) { 1059813b750SKyle Moffett cur_dev = NULL; 1069813b750SKyle Moffett return -1; 1077205e407Swdenk } 1089813b750SKyle Moffett 1099813b750SKyle Moffett /* Check if it's actually a DOS volume */ 1109813b750SKyle Moffett if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) { 1119813b750SKyle Moffett cur_dev = NULL; 1129813b750SKyle Moffett return -1; 1139813b750SKyle Moffett } 1149813b750SKyle Moffett 1159813b750SKyle Moffett /* Check for FAT12/FAT16/FAT32 filesystem */ 1169813b750SKyle Moffett if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3)) 11771f95118Swdenk return 0; 1189813b750SKyle Moffett if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5)) 1199813b750SKyle Moffett return 0; 1209813b750SKyle Moffett 1219813b750SKyle Moffett cur_dev = NULL; 1229813b750SKyle Moffett return -1; 12371f95118Swdenk } 12471f95118Swdenk 1259813b750SKyle Moffett 12671f95118Swdenk /* 12771f95118Swdenk * Get the first occurence of a directory delimiter ('/' or '\') in a string. 12871f95118Swdenk * Return index into string if found, -1 otherwise. 12971f95118Swdenk */ 1307385c28eSWolfgang Denk static int dirdelim(char *str) 13171f95118Swdenk { 13271f95118Swdenk char *start = str; 13371f95118Swdenk 13471f95118Swdenk while (*str != '\0') { 1357385c28eSWolfgang Denk if (ISDIRDELIM(*str)) 1367385c28eSWolfgang Denk return str - start; 13771f95118Swdenk str++; 13871f95118Swdenk } 13971f95118Swdenk return -1; 14071f95118Swdenk } 14171f95118Swdenk 14271f95118Swdenk /* 14371f95118Swdenk * Extract zero terminated short name from a directory entry. 14471f95118Swdenk */ 14571f95118Swdenk static void get_name(dir_entry *dirent, char *s_name) 14671f95118Swdenk { 14771f95118Swdenk char *ptr; 14871f95118Swdenk 14971f95118Swdenk memcpy(s_name, dirent->name, 8); 15071f95118Swdenk s_name[8] = '\0'; 15171f95118Swdenk ptr = s_name; 15271f95118Swdenk while (*ptr && *ptr != ' ') 15371f95118Swdenk ptr++; 15471f95118Swdenk if (dirent->ext[0] && dirent->ext[0] != ' ') { 15571f95118Swdenk *ptr = '.'; 15671f95118Swdenk ptr++; 15771f95118Swdenk memcpy(ptr, dirent->ext, 3); 15871f95118Swdenk ptr[3] = '\0'; 15971f95118Swdenk while (*ptr && *ptr != ' ') 16071f95118Swdenk ptr++; 16171f95118Swdenk } 16271f95118Swdenk *ptr = '\0'; 16371f95118Swdenk if (*s_name == DELETED_FLAG) 16471f95118Swdenk *s_name = '\0'; 16571f95118Swdenk else if (*s_name == aRING) 1663c2c2f42SRemy Bohmer *s_name = DELETED_FLAG; 16771f95118Swdenk downcase(s_name); 16871f95118Swdenk } 16971f95118Swdenk 17071f95118Swdenk /* 17171f95118Swdenk * Get the entry at index 'entry' in a FAT (12/16/32) table. 17271f95118Swdenk * On failure 0x00 is returned. 17371f95118Swdenk */ 1747385c28eSWolfgang Denk static __u32 get_fatent(fsdata *mydata, __u32 entry) 17571f95118Swdenk { 17671f95118Swdenk __u32 bufnum; 1777385c28eSWolfgang Denk __u32 off16, offset; 17871f95118Swdenk __u32 ret = 0x00; 1797385c28eSWolfgang Denk __u16 val1, val2; 18071f95118Swdenk 18171f95118Swdenk switch (mydata->fatsize) { 18271f95118Swdenk case 32: 18371f95118Swdenk bufnum = entry / FAT32BUFSIZE; 18471f95118Swdenk offset = entry - bufnum * FAT32BUFSIZE; 18571f95118Swdenk break; 18671f95118Swdenk case 16: 18771f95118Swdenk bufnum = entry / FAT16BUFSIZE; 18871f95118Swdenk offset = entry - bufnum * FAT16BUFSIZE; 18971f95118Swdenk break; 19071f95118Swdenk case 12: 19171f95118Swdenk bufnum = entry / FAT12BUFSIZE; 19271f95118Swdenk offset = entry - bufnum * FAT12BUFSIZE; 19371f95118Swdenk break; 19471f95118Swdenk 19571f95118Swdenk default: 19671f95118Swdenk /* Unsupported FAT size */ 19771f95118Swdenk return ret; 19871f95118Swdenk } 19971f95118Swdenk 2007385c28eSWolfgang Denk debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n", 2012aa98c66SWolfgang Denk mydata->fatsize, entry, entry, offset, offset); 2022aa98c66SWolfgang Denk 20371f95118Swdenk /* Read a new block of FAT entries into the cache. */ 20471f95118Swdenk if (bufnum != mydata->fatbufnum) { 20560b36f0fSSergei Shtylyov __u32 getsize = FATBUFBLOCKS; 20671f95118Swdenk __u8 *bufptr = mydata->fatbuf; 20771f95118Swdenk __u32 fatlength = mydata->fatlength; 20871f95118Swdenk __u32 startblock = bufnum * FATBUFBLOCKS; 20971f95118Swdenk 2108006dd2eSBenoît Thébaudeau if (startblock + getsize > fatlength) 2118006dd2eSBenoît Thébaudeau getsize = fatlength - startblock; 21260b36f0fSSergei Shtylyov 21371f95118Swdenk startblock += mydata->fat_sect; /* Offset from start of disk */ 21471f95118Swdenk 21571f95118Swdenk if (disk_read(startblock, getsize, bufptr) < 0) { 2167385c28eSWolfgang Denk debug("Error reading FAT blocks\n"); 21771f95118Swdenk return ret; 21871f95118Swdenk } 21971f95118Swdenk mydata->fatbufnum = bufnum; 22071f95118Swdenk } 22171f95118Swdenk 22271f95118Swdenk /* Get the actual entry from the table */ 22371f95118Swdenk switch (mydata->fatsize) { 22471f95118Swdenk case 32: 22571f95118Swdenk ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]); 22671f95118Swdenk break; 22771f95118Swdenk case 16: 22871f95118Swdenk ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]); 22971f95118Swdenk break; 2307385c28eSWolfgang Denk case 12: 2317385c28eSWolfgang Denk off16 = (offset * 3) / 4; 23271f95118Swdenk 23371f95118Swdenk switch (offset & 0x3) { 23471f95118Swdenk case 0: 23571f95118Swdenk ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]); 23671f95118Swdenk ret &= 0xfff; 23771f95118Swdenk break; 23871f95118Swdenk case 1: 23971f95118Swdenk val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 24071f95118Swdenk val1 &= 0xf000; 24171f95118Swdenk val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); 24271f95118Swdenk val2 &= 0x00ff; 24371f95118Swdenk ret = (val2 << 4) | (val1 >> 12); 24471f95118Swdenk break; 24571f95118Swdenk case 2: 24671f95118Swdenk val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 24771f95118Swdenk val1 &= 0xff00; 24871f95118Swdenk val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); 24971f95118Swdenk val2 &= 0x000f; 25071f95118Swdenk ret = (val2 << 8) | (val1 >> 8); 25171f95118Swdenk break; 25271f95118Swdenk case 3: 2537385c28eSWolfgang Denk ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); 25471f95118Swdenk ret = (ret & 0xfff0) >> 4; 25571f95118Swdenk break; 25671f95118Swdenk default: 25771f95118Swdenk break; 25871f95118Swdenk } 25971f95118Swdenk break; 26071f95118Swdenk } 2617385c28eSWolfgang Denk debug("FAT%d: ret: %08x, offset: %04x\n", 2622aa98c66SWolfgang Denk mydata->fatsize, ret, offset); 26371f95118Swdenk 26471f95118Swdenk return ret; 26571f95118Swdenk } 26671f95118Swdenk 26771f95118Swdenk /* 26871f95118Swdenk * Read at most 'size' bytes from the specified cluster into 'buffer'. 26971f95118Swdenk * Return 0 on success, -1 otherwise. 27071f95118Swdenk */ 27171f95118Swdenk static int 2729795e07bSBenoît Thébaudeau get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size) 27371f95118Swdenk { 2743f270f42SErik Hansen __u32 idx = 0; 27571f95118Swdenk __u32 startsect; 27646236b14SKyle Moffett int ret; 27771f95118Swdenk 27871f95118Swdenk if (clustnum > 0) { 2797385c28eSWolfgang Denk startsect = mydata->data_begin + 2807385c28eSWolfgang Denk clustnum * mydata->clust_size; 28171f95118Swdenk } else { 28271f95118Swdenk startsect = mydata->rootdir_sect; 28371f95118Swdenk } 28471f95118Swdenk 2857385c28eSWolfgang Denk debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect); 2867385c28eSWolfgang Denk 287cc63b25eSBenoît Thébaudeau if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) { 2889a800ac7SEric Nelson ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 2897385c28eSWolfgang Denk 290cc63b25eSBenoît Thébaudeau printf("FAT: Misaligned buffer address (%p)\n", buffer); 291cc63b25eSBenoît Thébaudeau 292cc63b25eSBenoît Thébaudeau while (size >= mydata->sect_size) { 293cc63b25eSBenoît Thébaudeau ret = disk_read(startsect++, 1, tmpbuf); 29446236b14SKyle Moffett if (ret != 1) { 29546236b14SKyle Moffett debug("Error reading data (got %d)\n", ret); 29671f95118Swdenk return -1; 29771f95118Swdenk } 29871f95118Swdenk 299cc63b25eSBenoît Thébaudeau memcpy(buffer, tmpbuf, mydata->sect_size); 300cc63b25eSBenoît Thébaudeau buffer += mydata->sect_size; 301cc63b25eSBenoît Thébaudeau size -= mydata->sect_size; 302cc63b25eSBenoît Thébaudeau } 303cc63b25eSBenoît Thébaudeau } else { 304cc63b25eSBenoît Thébaudeau idx = size / mydata->sect_size; 305cc63b25eSBenoît Thébaudeau ret = disk_read(startsect, idx, buffer); 306cc63b25eSBenoît Thébaudeau if (ret != idx) { 307cc63b25eSBenoît Thébaudeau debug("Error reading data (got %d)\n", ret); 308cc63b25eSBenoît Thébaudeau return -1; 309cc63b25eSBenoît Thébaudeau } 310cc63b25eSBenoît Thébaudeau startsect += idx; 311cc63b25eSBenoît Thébaudeau idx *= mydata->sect_size; 312cc63b25eSBenoît Thébaudeau buffer += idx; 313cc63b25eSBenoît Thébaudeau size -= idx; 314cc63b25eSBenoît Thébaudeau } 315cc63b25eSBenoît Thébaudeau if (size) { 316cc63b25eSBenoît Thébaudeau ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 317cc63b25eSBenoît Thébaudeau 318cc63b25eSBenoît Thébaudeau ret = disk_read(startsect, 1, tmpbuf); 319cc63b25eSBenoît Thébaudeau if (ret != 1) { 320cc63b25eSBenoît Thébaudeau debug("Error reading data (got %d)\n", ret); 321cc63b25eSBenoît Thébaudeau return -1; 322cc63b25eSBenoît Thébaudeau } 323cc63b25eSBenoît Thébaudeau 324cc63b25eSBenoît Thébaudeau memcpy(buffer, tmpbuf, size); 32571f95118Swdenk } 32671f95118Swdenk 32771f95118Swdenk return 0; 32871f95118Swdenk } 32971f95118Swdenk 33071f95118Swdenk /* 33171f95118Swdenk * Read at most 'maxsize' bytes from the file associated with 'dentptr' 33271f95118Swdenk * into 'buffer'. 33371f95118Swdenk * Return the number of bytes read or -1 on fatal errors. 33471f95118Swdenk */ 33571f95118Swdenk static long 33671f95118Swdenk get_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, 33771f95118Swdenk unsigned long maxsize) 33871f95118Swdenk { 33971f95118Swdenk unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0; 340ac497771SSergei Shtylyov unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 34171f95118Swdenk __u32 curclust = START(dentptr); 3427205e407Swdenk __u32 endclust, newclust; 3437205e407Swdenk unsigned long actsize; 34471f95118Swdenk 3457385c28eSWolfgang Denk debug("Filesize: %ld bytes\n", filesize); 34671f95118Swdenk 3477385c28eSWolfgang Denk if (maxsize > 0 && filesize > maxsize) 3487385c28eSWolfgang Denk filesize = maxsize; 34971f95118Swdenk 3507385c28eSWolfgang Denk debug("%ld bytes\n", filesize); 35171f95118Swdenk 3527205e407Swdenk actsize = bytesperclust; 3537205e407Swdenk endclust = curclust; 3547385c28eSWolfgang Denk 35571f95118Swdenk do { 3567205e407Swdenk /* search for consecutive clusters */ 3577205e407Swdenk while (actsize < filesize) { 3587205e407Swdenk newclust = get_fatent(mydata, endclust); 3597205e407Swdenk if ((newclust - 1) != endclust) 3607205e407Swdenk goto getit; 3618ce4e5c2Smichael if (CHECK_CLUST(newclust, mydata->fatsize)) { 3627385c28eSWolfgang Denk debug("curclust: 0x%x\n", newclust); 3637385c28eSWolfgang Denk debug("Invalid FAT entry\n"); 3647205e407Swdenk return gotsize; 3657205e407Swdenk } 3667205e407Swdenk endclust = newclust; 3677205e407Swdenk actsize += bytesperclust; 3687205e407Swdenk } 3697385c28eSWolfgang Denk 3707205e407Swdenk /* get remaining bytes */ 3717205e407Swdenk actsize = filesize; 3720880e5bbSBenoît Thébaudeau if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 3737385c28eSWolfgang Denk printf("Error reading cluster\n"); 3747205e407Swdenk return -1; 3757205e407Swdenk } 3767205e407Swdenk gotsize += actsize; 3777205e407Swdenk return gotsize; 3787205e407Swdenk getit: 3797205e407Swdenk if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 3807385c28eSWolfgang Denk printf("Error reading cluster\n"); 3817205e407Swdenk return -1; 3827205e407Swdenk } 3837205e407Swdenk gotsize += (int)actsize; 3847205e407Swdenk filesize -= actsize; 3857205e407Swdenk buffer += actsize; 3867385c28eSWolfgang Denk 3877205e407Swdenk curclust = get_fatent(mydata, endclust); 3888ce4e5c2Smichael if (CHECK_CLUST(curclust, mydata->fatsize)) { 3897385c28eSWolfgang Denk debug("curclust: 0x%x\n", curclust); 3907385c28eSWolfgang Denk printf("Invalid FAT entry\n"); 39171f95118Swdenk return gotsize; 39271f95118Swdenk } 3937205e407Swdenk actsize = bytesperclust; 3947205e407Swdenk endclust = curclust; 39571f95118Swdenk } while (1); 39671f95118Swdenk } 39771f95118Swdenk 39871f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 39971f95118Swdenk /* 40071f95118Swdenk * Extract the file name information from 'slotptr' into 'l_name', 40171f95118Swdenk * starting at l_name[*idx]. 40271f95118Swdenk * Return 1 if terminator (zero byte) is found, 0 otherwise. 40371f95118Swdenk */ 4047385c28eSWolfgang Denk static int slot2str(dir_slot *slotptr, char *l_name, int *idx) 40571f95118Swdenk { 40671f95118Swdenk int j; 40771f95118Swdenk 40871f95118Swdenk for (j = 0; j <= 8; j += 2) { 40971f95118Swdenk l_name[*idx] = slotptr->name0_4[j]; 4107385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4117385c28eSWolfgang Denk return 1; 41271f95118Swdenk (*idx)++; 41371f95118Swdenk } 41471f95118Swdenk for (j = 0; j <= 10; j += 2) { 41571f95118Swdenk l_name[*idx] = slotptr->name5_10[j]; 4167385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4177385c28eSWolfgang Denk return 1; 41871f95118Swdenk (*idx)++; 41971f95118Swdenk } 42071f95118Swdenk for (j = 0; j <= 2; j += 2) { 42171f95118Swdenk l_name[*idx] = slotptr->name11_12[j]; 4227385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4237385c28eSWolfgang Denk return 1; 42471f95118Swdenk (*idx)++; 42571f95118Swdenk } 42671f95118Swdenk 42771f95118Swdenk return 0; 42871f95118Swdenk } 42971f95118Swdenk 43071f95118Swdenk /* 43171f95118Swdenk * Extract the full long filename starting at 'retdent' (which is really 43271f95118Swdenk * a slot) into 'l_name'. If successful also copy the real directory entry 43371f95118Swdenk * into 'retdent' 43471f95118Swdenk * Return 0 on success, -1 otherwise. 43571f95118Swdenk */ 4369a800ac7SEric Nelson __u8 get_vfatname_block[MAX_CLUSTSIZE] 4379a800ac7SEric Nelson __aligned(ARCH_DMA_MINALIGN); 4387385c28eSWolfgang Denk 43971f95118Swdenk static int 44071f95118Swdenk get_vfatname(fsdata *mydata, int curclust, __u8 *cluster, 44171f95118Swdenk dir_entry *retdent, char *l_name) 44271f95118Swdenk { 44371f95118Swdenk dir_entry *realdent; 44471f95118Swdenk dir_slot *slotptr = (dir_slot *)retdent; 445025421eaSSergei Shtylyov __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ? 446025421eaSSergei Shtylyov PREFETCH_BLOCKS : 447025421eaSSergei Shtylyov mydata->clust_size); 4482d1a537dSwdenk __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; 44971f95118Swdenk int idx = 0; 45071f95118Swdenk 4513831530dSMikhail Zolotaryov if (counter > VFAT_MAXSEQ) { 4523831530dSMikhail Zolotaryov debug("Error: VFAT name is too long\n"); 4533831530dSMikhail Zolotaryov return -1; 4543831530dSMikhail Zolotaryov } 4553831530dSMikhail Zolotaryov 4563831530dSMikhail Zolotaryov while ((__u8 *)slotptr < buflimit) { 4577385c28eSWolfgang Denk if (counter == 0) 4587385c28eSWolfgang Denk break; 4592d1a537dSwdenk if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) 4602d1a537dSwdenk return -1; 46171f95118Swdenk slotptr++; 46271f95118Swdenk counter--; 46371f95118Swdenk } 46471f95118Swdenk 4653831530dSMikhail Zolotaryov if ((__u8 *)slotptr >= buflimit) { 46671f95118Swdenk dir_slot *slotptr2; 46771f95118Swdenk 4683831530dSMikhail Zolotaryov if (curclust == 0) 4693831530dSMikhail Zolotaryov return -1; 47071f95118Swdenk curclust = get_fatent(mydata, curclust); 4718ce4e5c2Smichael if (CHECK_CLUST(curclust, mydata->fatsize)) { 4727385c28eSWolfgang Denk debug("curclust: 0x%x\n", curclust); 4737385c28eSWolfgang Denk printf("Invalid FAT entry\n"); 47471f95118Swdenk return -1; 47571f95118Swdenk } 4767385c28eSWolfgang Denk 4775fa66df6Swdenk if (get_cluster(mydata, curclust, get_vfatname_block, 478ac497771SSergei Shtylyov mydata->clust_size * mydata->sect_size) != 0) { 4797385c28eSWolfgang Denk debug("Error: reading directory block\n"); 48071f95118Swdenk return -1; 48171f95118Swdenk } 4827385c28eSWolfgang Denk 4835fa66df6Swdenk slotptr2 = (dir_slot *)get_vfatname_block; 4843831530dSMikhail Zolotaryov while (counter > 0) { 4853831530dSMikhail Zolotaryov if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK) 4863831530dSMikhail Zolotaryov & 0xff) != counter) 4873831530dSMikhail Zolotaryov return -1; 48871f95118Swdenk slotptr2++; 4893831530dSMikhail Zolotaryov counter--; 4903831530dSMikhail Zolotaryov } 4917385c28eSWolfgang Denk 49271f95118Swdenk /* Save the real directory entry */ 4933831530dSMikhail Zolotaryov realdent = (dir_entry *)slotptr2; 4943831530dSMikhail Zolotaryov while ((__u8 *)slotptr2 > get_vfatname_block) { 49571f95118Swdenk slotptr2--; 4963831530dSMikhail Zolotaryov slot2str(slotptr2, l_name, &idx); 49771f95118Swdenk } 49871f95118Swdenk } else { 49971f95118Swdenk /* Save the real directory entry */ 50071f95118Swdenk realdent = (dir_entry *)slotptr; 50171f95118Swdenk } 50271f95118Swdenk 50371f95118Swdenk do { 50471f95118Swdenk slotptr--; 5057385c28eSWolfgang Denk if (slot2str(slotptr, l_name, &idx)) 5067385c28eSWolfgang Denk break; 5072d1a537dSwdenk } while (!(slotptr->id & LAST_LONG_ENTRY_MASK)); 50871f95118Swdenk 50971f95118Swdenk l_name[idx] = '\0'; 5107385c28eSWolfgang Denk if (*l_name == DELETED_FLAG) 5117385c28eSWolfgang Denk *l_name = '\0'; 5127385c28eSWolfgang Denk else if (*l_name == aRING) 5137385c28eSWolfgang Denk *l_name = DELETED_FLAG; 51471f95118Swdenk downcase(l_name); 51571f95118Swdenk 51671f95118Swdenk /* Return the real directory entry */ 51771f95118Swdenk memcpy(retdent, realdent, sizeof(dir_entry)); 51871f95118Swdenk 51971f95118Swdenk return 0; 52071f95118Swdenk } 52171f95118Swdenk 52271f95118Swdenk /* Calculate short name checksum */ 5237385c28eSWolfgang Denk static __u8 mkcksum(const char *str) 52471f95118Swdenk { 52571f95118Swdenk int i; 5267385c28eSWolfgang Denk 52771f95118Swdenk __u8 ret = 0; 52871f95118Swdenk 52971f95118Swdenk for (i = 0; i < 11; i++) { 53071f95118Swdenk ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i]; 53171f95118Swdenk } 53271f95118Swdenk 53371f95118Swdenk return ret; 53471f95118Swdenk } 5357385c28eSWolfgang Denk #endif /* CONFIG_SUPPORT_VFAT */ 53671f95118Swdenk 53771f95118Swdenk /* 53871f95118Swdenk * Get the directory entry associated with 'filename' from the directory 53971f95118Swdenk * starting at 'startsect' 54071f95118Swdenk */ 5419a800ac7SEric Nelson __u8 get_dentfromdir_block[MAX_CLUSTSIZE] 5429a800ac7SEric Nelson __aligned(ARCH_DMA_MINALIGN); 5437385c28eSWolfgang Denk 54471f95118Swdenk static dir_entry *get_dentfromdir(fsdata *mydata, int startsect, 54571f95118Swdenk char *filename, dir_entry *retdent, 54671f95118Swdenk int dols) 54771f95118Swdenk { 54871f95118Swdenk __u16 prevcksum = 0xffff; 54971f95118Swdenk __u32 curclust = START(retdent); 55071f95118Swdenk int files = 0, dirs = 0; 55171f95118Swdenk 5527385c28eSWolfgang Denk debug("get_dentfromdir: %s\n", filename); 5537385c28eSWolfgang Denk 55471f95118Swdenk while (1) { 55571f95118Swdenk dir_entry *dentptr; 5567385c28eSWolfgang Denk 55771f95118Swdenk int i; 55871f95118Swdenk 5595fa66df6Swdenk if (get_cluster(mydata, curclust, get_dentfromdir_block, 560ac497771SSergei Shtylyov mydata->clust_size * mydata->sect_size) != 0) { 5617385c28eSWolfgang Denk debug("Error: reading directory block\n"); 56271f95118Swdenk return NULL; 56371f95118Swdenk } 5647385c28eSWolfgang Denk 5655fa66df6Swdenk dentptr = (dir_entry *)get_dentfromdir_block; 5667385c28eSWolfgang Denk 56771f95118Swdenk for (i = 0; i < DIRENTSPERCLUST; i++) { 5683831530dSMikhail Zolotaryov char s_name[14], l_name[VFAT_MAXLEN_BYTES]; 56971f95118Swdenk 57071f95118Swdenk l_name[0] = '\0'; 571855a496fSwdenk if (dentptr->name[0] == DELETED_FLAG) { 572855a496fSwdenk dentptr++; 573855a496fSwdenk continue; 574855a496fSwdenk } 57571f95118Swdenk if ((dentptr->attr & ATTR_VOLUME)) { 57671f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 577206d68fdSJ. Vijayanand if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT && 5782d1a537dSwdenk (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { 5797385c28eSWolfgang Denk prevcksum = ((dir_slot *)dentptr)->alias_checksum; 5807385c28eSWolfgang Denk get_vfatname(mydata, curclust, 5817385c28eSWolfgang Denk get_dentfromdir_block, 58271f95118Swdenk dentptr, l_name); 58371f95118Swdenk if (dols) { 5847385c28eSWolfgang Denk int isdir; 58571f95118Swdenk char dirc; 58671f95118Swdenk int doit = 0; 58771f95118Swdenk 5887385c28eSWolfgang Denk isdir = (dentptr->attr & ATTR_DIR); 5897385c28eSWolfgang Denk 59071f95118Swdenk if (isdir) { 59171f95118Swdenk dirs++; 59271f95118Swdenk dirc = '/'; 59371f95118Swdenk doit = 1; 59471f95118Swdenk } else { 59571f95118Swdenk dirc = ' '; 59671f95118Swdenk if (l_name[0] != 0) { 59771f95118Swdenk files++; 59871f95118Swdenk doit = 1; 59971f95118Swdenk } 60071f95118Swdenk } 60171f95118Swdenk if (doit) { 60271f95118Swdenk if (dirc == ' ') { 60371f95118Swdenk printf(" %8ld %s%c\n", 60471f95118Swdenk (long)FAT2CPU32(dentptr->size), 6057385c28eSWolfgang Denk l_name, 6067385c28eSWolfgang Denk dirc); 60771f95118Swdenk } else { 6087385c28eSWolfgang Denk printf(" %s%c\n", 6097385c28eSWolfgang Denk l_name, 6107385c28eSWolfgang Denk dirc); 61171f95118Swdenk } 61271f95118Swdenk } 61371f95118Swdenk dentptr++; 61471f95118Swdenk continue; 61571f95118Swdenk } 6167385c28eSWolfgang Denk debug("vfatname: |%s|\n", l_name); 61771f95118Swdenk } else 61871f95118Swdenk #endif 61971f95118Swdenk { 62071f95118Swdenk /* Volume label or VFAT entry */ 62171f95118Swdenk dentptr++; 62271f95118Swdenk continue; 62371f95118Swdenk } 62471f95118Swdenk } 62571f95118Swdenk if (dentptr->name[0] == 0) { 62671f95118Swdenk if (dols) { 6277385c28eSWolfgang Denk printf("\n%d file(s), %d dir(s)\n\n", 6287385c28eSWolfgang Denk files, dirs); 62971f95118Swdenk } 6307385c28eSWolfgang Denk debug("Dentname == NULL - %d\n", i); 63171f95118Swdenk return NULL; 63271f95118Swdenk } 63371f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 63471f95118Swdenk if (dols && mkcksum(dentptr->name) == prevcksum) { 635bf34e7d9SSergei Shtylyov prevcksum = 0xffff; 63671f95118Swdenk dentptr++; 63771f95118Swdenk continue; 63871f95118Swdenk } 63971f95118Swdenk #endif 64071f95118Swdenk get_name(dentptr, s_name); 64171f95118Swdenk if (dols) { 64271f95118Swdenk int isdir = (dentptr->attr & ATTR_DIR); 64371f95118Swdenk char dirc; 64471f95118Swdenk int doit = 0; 64571f95118Swdenk 64671f95118Swdenk if (isdir) { 64771f95118Swdenk dirs++; 64871f95118Swdenk dirc = '/'; 64971f95118Swdenk doit = 1; 65071f95118Swdenk } else { 65171f95118Swdenk dirc = ' '; 65271f95118Swdenk if (s_name[0] != 0) { 65371f95118Swdenk files++; 65471f95118Swdenk doit = 1; 65571f95118Swdenk } 65671f95118Swdenk } 6577385c28eSWolfgang Denk 65871f95118Swdenk if (doit) { 65971f95118Swdenk if (dirc == ' ') { 66071f95118Swdenk printf(" %8ld %s%c\n", 6617385c28eSWolfgang Denk (long)FAT2CPU32(dentptr->size), 6627385c28eSWolfgang Denk s_name, dirc); 66371f95118Swdenk } else { 6647385c28eSWolfgang Denk printf(" %s%c\n", 6657385c28eSWolfgang Denk s_name, dirc); 66671f95118Swdenk } 66771f95118Swdenk } 6687385c28eSWolfgang Denk 66971f95118Swdenk dentptr++; 67071f95118Swdenk continue; 67171f95118Swdenk } 6727385c28eSWolfgang Denk 6737385c28eSWolfgang Denk if (strcmp(filename, s_name) 6747385c28eSWolfgang Denk && strcmp(filename, l_name)) { 6757385c28eSWolfgang Denk debug("Mismatch: |%s|%s|\n", s_name, l_name); 67671f95118Swdenk dentptr++; 67771f95118Swdenk continue; 67871f95118Swdenk } 6797385c28eSWolfgang Denk 68071f95118Swdenk memcpy(retdent, dentptr, sizeof(dir_entry)); 68171f95118Swdenk 6827385c28eSWolfgang Denk debug("DentName: %s", s_name); 6837385c28eSWolfgang Denk debug(", start: 0x%x", START(dentptr)); 6847385c28eSWolfgang Denk debug(", size: 0x%x %s\n", 68571f95118Swdenk FAT2CPU32(dentptr->size), 68671f95118Swdenk (dentptr->attr & ATTR_DIR) ? "(DIR)" : ""); 68771f95118Swdenk 68871f95118Swdenk return retdent; 68971f95118Swdenk } 6907385c28eSWolfgang Denk 69171f95118Swdenk curclust = get_fatent(mydata, curclust); 6928ce4e5c2Smichael if (CHECK_CLUST(curclust, mydata->fatsize)) { 6937385c28eSWolfgang Denk debug("curclust: 0x%x\n", curclust); 6947385c28eSWolfgang Denk printf("Invalid FAT entry\n"); 69571f95118Swdenk return NULL; 69671f95118Swdenk } 69771f95118Swdenk } 69871f95118Swdenk 69971f95118Swdenk return NULL; 70071f95118Swdenk } 70171f95118Swdenk 70271f95118Swdenk /* 70371f95118Swdenk * Read boot sector and volume info from a FAT filesystem 70471f95118Swdenk */ 70571f95118Swdenk static int 70671f95118Swdenk read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize) 70771f95118Swdenk { 708ac497771SSergei Shtylyov __u8 *block; 70971f95118Swdenk volume_info *vistart; 710ac497771SSergei Shtylyov int ret = 0; 711ac497771SSergei Shtylyov 712ac497771SSergei Shtylyov if (cur_dev == NULL) { 713ac497771SSergei Shtylyov debug("Error: no device selected\n"); 714ac497771SSergei Shtylyov return -1; 715ac497771SSergei Shtylyov } 716ac497771SSergei Shtylyov 7179a800ac7SEric Nelson block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz); 718ac497771SSergei Shtylyov if (block == NULL) { 719ac497771SSergei Shtylyov debug("Error: allocating block\n"); 720ac497771SSergei Shtylyov return -1; 721ac497771SSergei Shtylyov } 72271f95118Swdenk 72371f95118Swdenk if (disk_read(0, 1, block) < 0) { 7247385c28eSWolfgang Denk debug("Error: reading block\n"); 725ac497771SSergei Shtylyov goto fail; 72671f95118Swdenk } 72771f95118Swdenk 72871f95118Swdenk memcpy(bs, block, sizeof(boot_sector)); 72971f95118Swdenk bs->reserved = FAT2CPU16(bs->reserved); 73071f95118Swdenk bs->fat_length = FAT2CPU16(bs->fat_length); 73171f95118Swdenk bs->secs_track = FAT2CPU16(bs->secs_track); 73271f95118Swdenk bs->heads = FAT2CPU16(bs->heads); 73371f95118Swdenk bs->total_sect = FAT2CPU32(bs->total_sect); 73471f95118Swdenk 73571f95118Swdenk /* FAT32 entries */ 73671f95118Swdenk if (bs->fat_length == 0) { 73771f95118Swdenk /* Assume FAT32 */ 73871f95118Swdenk bs->fat32_length = FAT2CPU32(bs->fat32_length); 73971f95118Swdenk bs->flags = FAT2CPU16(bs->flags); 74071f95118Swdenk bs->root_cluster = FAT2CPU32(bs->root_cluster); 74171f95118Swdenk bs->info_sector = FAT2CPU16(bs->info_sector); 74271f95118Swdenk bs->backup_boot = FAT2CPU16(bs->backup_boot); 74371f95118Swdenk vistart = (volume_info *)(block + sizeof(boot_sector)); 74471f95118Swdenk *fatsize = 32; 74571f95118Swdenk } else { 74671f95118Swdenk vistart = (volume_info *)&(bs->fat32_length); 74771f95118Swdenk *fatsize = 0; 74871f95118Swdenk } 74971f95118Swdenk memcpy(volinfo, vistart, sizeof(volume_info)); 75071f95118Swdenk 75171f95118Swdenk if (*fatsize == 32) { 7527385c28eSWolfgang Denk if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0) 753ac497771SSergei Shtylyov goto exit; 75471f95118Swdenk } else { 755651351feSTom Rix if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) { 75671f95118Swdenk *fatsize = 12; 757ac497771SSergei Shtylyov goto exit; 75871f95118Swdenk } 759651351feSTom Rix if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) { 76071f95118Swdenk *fatsize = 16; 761ac497771SSergei Shtylyov goto exit; 76271f95118Swdenk } 76371f95118Swdenk } 76471f95118Swdenk 7657385c28eSWolfgang Denk debug("Error: broken fs_type sign\n"); 766ac497771SSergei Shtylyov fail: 767ac497771SSergei Shtylyov ret = -1; 768ac497771SSergei Shtylyov exit: 769ac497771SSergei Shtylyov free(block); 770ac497771SSergei Shtylyov return ret; 77171f95118Swdenk } 77271f95118Swdenk 7739a800ac7SEric Nelson __u8 do_fat_read_block[MAX_CLUSTSIZE] 7749a800ac7SEric Nelson __aligned(ARCH_DMA_MINALIGN); 7757385c28eSWolfgang Denk 77620cc00ddSstroese long 7779795e07bSBenoît Thébaudeau do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols) 77871f95118Swdenk { 77971f95118Swdenk char fnamecopy[2048]; 78071f95118Swdenk boot_sector bs; 78171f95118Swdenk volume_info volinfo; 78271f95118Swdenk fsdata datablock; 78371f95118Swdenk fsdata *mydata = &datablock; 784cd1b042cSBenoît Thébaudeau dir_entry *dentptr = NULL; 78571f95118Swdenk __u16 prevcksum = 0xffff; 78671f95118Swdenk char *subname = ""; 7873f270f42SErik Hansen __u32 cursect; 78871f95118Swdenk int idx, isdir = 0; 78971f95118Swdenk int files = 0, dirs = 0; 790ac497771SSergei Shtylyov long ret = -1; 79171f95118Swdenk int firsttime; 79240e21916SSergei Shtylyov __u32 root_cluster = 0; 7933f270f42SErik Hansen int rootdir_size = 0; 7942aa98c66SWolfgang Denk int j; 79571f95118Swdenk 79671f95118Swdenk if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { 7977385c28eSWolfgang Denk debug("Error: reading boot sector\n"); 79871f95118Swdenk return -1; 79971f95118Swdenk } 8007385c28eSWolfgang Denk 80140e21916SSergei Shtylyov if (mydata->fatsize == 32) { 8022aa98c66SWolfgang Denk root_cluster = bs.root_cluster; 80371f95118Swdenk mydata->fatlength = bs.fat32_length; 80440e21916SSergei Shtylyov } else { 80571f95118Swdenk mydata->fatlength = bs.fat_length; 80640e21916SSergei Shtylyov } 8077385c28eSWolfgang Denk 80871f95118Swdenk mydata->fat_sect = bs.reserved; 8097385c28eSWolfgang Denk 81071f95118Swdenk cursect = mydata->rootdir_sect 81171f95118Swdenk = mydata->fat_sect + mydata->fatlength * bs.fats; 8127385c28eSWolfgang Denk 813ac497771SSergei Shtylyov mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; 81471f95118Swdenk mydata->clust_size = bs.cluster_size; 81546236b14SKyle Moffett if (mydata->sect_size != cur_part_info.blksz) { 81646236b14SKyle Moffett printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n", 81746236b14SKyle Moffett mydata->sect_size, cur_part_info.blksz); 81846236b14SKyle Moffett return -1; 81946236b14SKyle Moffett } 8207385c28eSWolfgang Denk 82171f95118Swdenk if (mydata->fatsize == 32) { 8227385c28eSWolfgang Denk mydata->data_begin = mydata->rootdir_sect - 8237385c28eSWolfgang Denk (mydata->clust_size * 2); 82471f95118Swdenk } else { 8257385c28eSWolfgang Denk rootdir_size = ((bs.dir_entries[1] * (int)256 + 8267385c28eSWolfgang Denk bs.dir_entries[0]) * 8277385c28eSWolfgang Denk sizeof(dir_entry)) / 828ac497771SSergei Shtylyov mydata->sect_size; 8297385c28eSWolfgang Denk mydata->data_begin = mydata->rootdir_sect + 8307385c28eSWolfgang Denk rootdir_size - 8317385c28eSWolfgang Denk (mydata->clust_size * 2); 83271f95118Swdenk } 8337385c28eSWolfgang Denk 83471f95118Swdenk mydata->fatbufnum = -1; 8359a800ac7SEric Nelson mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE); 836ac497771SSergei Shtylyov if (mydata->fatbuf == NULL) { 837ac497771SSergei Shtylyov debug("Error: allocating memory\n"); 838ac497771SSergei Shtylyov return -1; 839ac497771SSergei Shtylyov } 84071f95118Swdenk 8412aa98c66SWolfgang Denk #ifdef CONFIG_SUPPORT_VFAT 8427385c28eSWolfgang Denk debug("VFAT Support enabled\n"); 8432aa98c66SWolfgang Denk #endif 8447385c28eSWolfgang Denk debug("FAT%d, fat_sect: %d, fatlength: %d\n", 8457385c28eSWolfgang Denk mydata->fatsize, mydata->fat_sect, mydata->fatlength); 8467385c28eSWolfgang Denk debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n" 84771f95118Swdenk "Data begins at: %d\n", 8482aa98c66SWolfgang Denk root_cluster, 8492aa98c66SWolfgang Denk mydata->rootdir_sect, 850ac497771SSergei Shtylyov mydata->rootdir_sect * mydata->sect_size, mydata->data_begin); 851ac497771SSergei Shtylyov debug("Sector size: %d, cluster size: %d\n", mydata->sect_size, 852ac497771SSergei Shtylyov mydata->clust_size); 85371f95118Swdenk 85471f95118Swdenk /* "cwd" is always the root... */ 85571f95118Swdenk while (ISDIRDELIM(*filename)) 85671f95118Swdenk filename++; 8577385c28eSWolfgang Denk 85871f95118Swdenk /* Make a copy of the filename and convert it to lowercase */ 85971f95118Swdenk strcpy(fnamecopy, filename); 86071f95118Swdenk downcase(fnamecopy); 8617385c28eSWolfgang Denk 86271f95118Swdenk if (*fnamecopy == '\0') { 86371f95118Swdenk if (!dols) 864ac497771SSergei Shtylyov goto exit; 8657385c28eSWolfgang Denk 86671f95118Swdenk dols = LS_ROOT; 86771f95118Swdenk } else if ((idx = dirdelim(fnamecopy)) >= 0) { 86871f95118Swdenk isdir = 1; 86971f95118Swdenk fnamecopy[idx] = '\0'; 87071f95118Swdenk subname = fnamecopy + idx + 1; 8717385c28eSWolfgang Denk 87271f95118Swdenk /* Handle multiple delimiters */ 87371f95118Swdenk while (ISDIRDELIM(*subname)) 87471f95118Swdenk subname++; 87571f95118Swdenk } else if (dols) { 87671f95118Swdenk isdir = 1; 87771f95118Swdenk } 87871f95118Swdenk 8792aa98c66SWolfgang Denk j = 0; 88071f95118Swdenk while (1) { 88171f95118Swdenk int i; 88271f95118Swdenk 883cd1b042cSBenoît Thébaudeau if (j == 0) { 8842d1b83b3SAndreas Bießmann debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n", 8852aa98c66SWolfgang Denk cursect, mydata->clust_size, DIRENTSPERBLOCK); 8867385c28eSWolfgang Denk 8873831530dSMikhail Zolotaryov if (disk_read(cursect, 8883831530dSMikhail Zolotaryov (mydata->fatsize == 32) ? 8893831530dSMikhail Zolotaryov (mydata->clust_size) : 890025421eaSSergei Shtylyov PREFETCH_BLOCKS, 8913831530dSMikhail Zolotaryov do_fat_read_block) < 0) { 8927385c28eSWolfgang Denk debug("Error: reading rootdir block\n"); 893ac497771SSergei Shtylyov goto exit; 89471f95118Swdenk } 8957385c28eSWolfgang Denk 8965fa66df6Swdenk dentptr = (dir_entry *) do_fat_read_block; 897cd1b042cSBenoît Thébaudeau } 8987385c28eSWolfgang Denk 89971f95118Swdenk for (i = 0; i < DIRENTSPERBLOCK; i++) { 9003831530dSMikhail Zolotaryov char s_name[14], l_name[VFAT_MAXLEN_BYTES]; 90171f95118Swdenk 90271f95118Swdenk l_name[0] = '\0'; 9033831530dSMikhail Zolotaryov if (dentptr->name[0] == DELETED_FLAG) { 9043831530dSMikhail Zolotaryov dentptr++; 9053831530dSMikhail Zolotaryov continue; 9063831530dSMikhail Zolotaryov } 90771f95118Swdenk if ((dentptr->attr & ATTR_VOLUME)) { 90871f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 909206d68fdSJ. Vijayanand if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT && 9102d1a537dSwdenk (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { 9117385c28eSWolfgang Denk prevcksum = 9127385c28eSWolfgang Denk ((dir_slot *)dentptr)->alias_checksum; 9137385c28eSWolfgang Denk 9143831530dSMikhail Zolotaryov get_vfatname(mydata, 91540e21916SSergei Shtylyov root_cluster, 9167385c28eSWolfgang Denk do_fat_read_block, 9177385c28eSWolfgang Denk dentptr, l_name); 9187385c28eSWolfgang Denk 91971f95118Swdenk if (dols == LS_ROOT) { 92071f95118Swdenk char dirc; 92171f95118Swdenk int doit = 0; 9227385c28eSWolfgang Denk int isdir = 9237385c28eSWolfgang Denk (dentptr->attr & ATTR_DIR); 92471f95118Swdenk 92571f95118Swdenk if (isdir) { 92671f95118Swdenk dirs++; 92771f95118Swdenk dirc = '/'; 92871f95118Swdenk doit = 1; 92971f95118Swdenk } else { 93071f95118Swdenk dirc = ' '; 93171f95118Swdenk if (l_name[0] != 0) { 93271f95118Swdenk files++; 93371f95118Swdenk doit = 1; 93471f95118Swdenk } 93571f95118Swdenk } 93671f95118Swdenk if (doit) { 93771f95118Swdenk if (dirc == ' ') { 93871f95118Swdenk printf(" %8ld %s%c\n", 93971f95118Swdenk (long)FAT2CPU32(dentptr->size), 9407385c28eSWolfgang Denk l_name, 9417385c28eSWolfgang Denk dirc); 94271f95118Swdenk } else { 9437385c28eSWolfgang Denk printf(" %s%c\n", 9447385c28eSWolfgang Denk l_name, 9457385c28eSWolfgang Denk dirc); 94671f95118Swdenk } 94771f95118Swdenk } 94871f95118Swdenk dentptr++; 94971f95118Swdenk continue; 95071f95118Swdenk } 9517385c28eSWolfgang Denk debug("Rootvfatname: |%s|\n", 9527385c28eSWolfgang Denk l_name); 95371f95118Swdenk } else 95471f95118Swdenk #endif 95571f95118Swdenk { 95671f95118Swdenk /* Volume label or VFAT entry */ 95771f95118Swdenk dentptr++; 95871f95118Swdenk continue; 95971f95118Swdenk } 96071f95118Swdenk } else if (dentptr->name[0] == 0) { 9617385c28eSWolfgang Denk debug("RootDentname == NULL - %d\n", i); 96271f95118Swdenk if (dols == LS_ROOT) { 9637385c28eSWolfgang Denk printf("\n%d file(s), %d dir(s)\n\n", 9647385c28eSWolfgang Denk files, dirs); 965ac497771SSergei Shtylyov ret = 0; 96671f95118Swdenk } 967ac497771SSergei Shtylyov goto exit; 96871f95118Swdenk } 96971f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT 9707385c28eSWolfgang Denk else if (dols == LS_ROOT && 9717385c28eSWolfgang Denk mkcksum(dentptr->name) == prevcksum) { 972bf34e7d9SSergei Shtylyov prevcksum = 0xffff; 97371f95118Swdenk dentptr++; 97471f95118Swdenk continue; 97571f95118Swdenk } 97671f95118Swdenk #endif 97771f95118Swdenk get_name(dentptr, s_name); 9787385c28eSWolfgang Denk 97971f95118Swdenk if (dols == LS_ROOT) { 98071f95118Swdenk int isdir = (dentptr->attr & ATTR_DIR); 98171f95118Swdenk char dirc; 98271f95118Swdenk int doit = 0; 98371f95118Swdenk 98471f95118Swdenk if (isdir) { 98571f95118Swdenk dirc = '/'; 986a43278a4Swdenk if (s_name[0] != 0) { 987a43278a4Swdenk dirs++; 98871f95118Swdenk doit = 1; 989a43278a4Swdenk } 99071f95118Swdenk } else { 99171f95118Swdenk dirc = ' '; 99271f95118Swdenk if (s_name[0] != 0) { 99371f95118Swdenk files++; 99471f95118Swdenk doit = 1; 99571f95118Swdenk } 99671f95118Swdenk } 99771f95118Swdenk if (doit) { 99871f95118Swdenk if (dirc == ' ') { 99971f95118Swdenk printf(" %8ld %s%c\n", 10007385c28eSWolfgang Denk (long)FAT2CPU32(dentptr->size), 10017385c28eSWolfgang Denk s_name, dirc); 100271f95118Swdenk } else { 10037385c28eSWolfgang Denk printf(" %s%c\n", 10047385c28eSWolfgang Denk s_name, dirc); 100571f95118Swdenk } 100671f95118Swdenk } 100771f95118Swdenk dentptr++; 100871f95118Swdenk continue; 100971f95118Swdenk } 10107385c28eSWolfgang Denk 10117385c28eSWolfgang Denk if (strcmp(fnamecopy, s_name) 10127385c28eSWolfgang Denk && strcmp(fnamecopy, l_name)) { 10137385c28eSWolfgang Denk debug("RootMismatch: |%s|%s|\n", s_name, 10147385c28eSWolfgang Denk l_name); 101571f95118Swdenk dentptr++; 101671f95118Swdenk continue; 101771f95118Swdenk } 10187385c28eSWolfgang Denk 101971f95118Swdenk if (isdir && !(dentptr->attr & ATTR_DIR)) 1020ac497771SSergei Shtylyov goto exit; 102171f95118Swdenk 10227385c28eSWolfgang Denk debug("RootName: %s", s_name); 10237385c28eSWolfgang Denk debug(", start: 0x%x", START(dentptr)); 10247385c28eSWolfgang Denk debug(", size: 0x%x %s\n", 10257385c28eSWolfgang Denk FAT2CPU32(dentptr->size), 10267385c28eSWolfgang Denk isdir ? "(DIR)" : ""); 102771f95118Swdenk 102871f95118Swdenk goto rootdir_done; /* We got a match */ 102971f95118Swdenk } 10307385c28eSWolfgang Denk debug("END LOOP: j=%d clust_size=%d\n", j, 10317385c28eSWolfgang Denk mydata->clust_size); 10322aa98c66SWolfgang Denk 10332aa98c66SWolfgang Denk /* 10342aa98c66SWolfgang Denk * On FAT32 we must fetch the FAT entries for the next 10352aa98c66SWolfgang Denk * root directory clusters when a cluster has been 10362aa98c66SWolfgang Denk * completely processed. 10372aa98c66SWolfgang Denk */ 10383f270f42SErik Hansen ++j; 1039cd1b042cSBenoît Thébaudeau int rootdir_end = 0; 1040cd1b042cSBenoît Thébaudeau if (mydata->fatsize == 32) { 1041cd1b042cSBenoît Thébaudeau if (j == mydata->clust_size) { 10423f270f42SErik Hansen int nxtsect = 0; 10433f270f42SErik Hansen int nxt_clust = 0; 10442aa98c66SWolfgang Denk 10452aa98c66SWolfgang Denk nxt_clust = get_fatent(mydata, root_cluster); 1046cd1b042cSBenoît Thébaudeau rootdir_end = CHECK_CLUST(nxt_clust, 32); 10477385c28eSWolfgang Denk 10487385c28eSWolfgang Denk nxtsect = mydata->data_begin + 10497385c28eSWolfgang Denk (nxt_clust * mydata->clust_size); 10507385c28eSWolfgang Denk 10512aa98c66SWolfgang Denk root_cluster = nxt_clust; 10522aa98c66SWolfgang Denk 10532aa98c66SWolfgang Denk cursect = nxtsect; 10542aa98c66SWolfgang Denk j = 0; 1055cd1b042cSBenoît Thébaudeau } 10562aa98c66SWolfgang Denk } else { 1057cd1b042cSBenoît Thébaudeau if (j == PREFETCH_BLOCKS) 1058cd1b042cSBenoît Thébaudeau j = 0; 1059cd1b042cSBenoît Thébaudeau 1060cd1b042cSBenoît Thébaudeau rootdir_end = (++cursect - mydata->rootdir_sect >= 1061cd1b042cSBenoît Thébaudeau rootdir_size); 106271f95118Swdenk } 10633f270f42SErik Hansen 10643f270f42SErik Hansen /* If end of rootdir reached */ 1065cd1b042cSBenoît Thébaudeau if (rootdir_end) { 10663f270f42SErik Hansen if (dols == LS_ROOT) { 10673f270f42SErik Hansen printf("\n%d file(s), %d dir(s)\n\n", 10683f270f42SErik Hansen files, dirs); 1069ac497771SSergei Shtylyov ret = 0; 10703f270f42SErik Hansen } 1071ac497771SSergei Shtylyov goto exit; 10723f270f42SErik Hansen } 10732aa98c66SWolfgang Denk } 107471f95118Swdenk rootdir_done: 107571f95118Swdenk 107671f95118Swdenk firsttime = 1; 10777385c28eSWolfgang Denk 107871f95118Swdenk while (isdir) { 107971f95118Swdenk int startsect = mydata->data_begin 108071f95118Swdenk + START(dentptr) * mydata->clust_size; 108171f95118Swdenk dir_entry dent; 108271f95118Swdenk char *nextname = NULL; 108371f95118Swdenk 108471f95118Swdenk dent = *dentptr; 108571f95118Swdenk dentptr = &dent; 108671f95118Swdenk 108771f95118Swdenk idx = dirdelim(subname); 10887385c28eSWolfgang Denk 108971f95118Swdenk if (idx >= 0) { 109071f95118Swdenk subname[idx] = '\0'; 109171f95118Swdenk nextname = subname + idx + 1; 109271f95118Swdenk /* Handle multiple delimiters */ 109371f95118Swdenk while (ISDIRDELIM(*nextname)) 109471f95118Swdenk nextname++; 109571f95118Swdenk if (dols && *nextname == '\0') 109671f95118Swdenk firsttime = 0; 109771f95118Swdenk } else { 109871f95118Swdenk if (dols && firsttime) { 109971f95118Swdenk firsttime = 0; 110071f95118Swdenk } else { 110171f95118Swdenk isdir = 0; 110271f95118Swdenk } 110371f95118Swdenk } 110471f95118Swdenk 110571f95118Swdenk if (get_dentfromdir(mydata, startsect, subname, dentptr, 110671f95118Swdenk isdir ? 0 : dols) == NULL) { 110771f95118Swdenk if (dols && !isdir) 1108ac497771SSergei Shtylyov ret = 0; 1109ac497771SSergei Shtylyov goto exit; 111071f95118Swdenk } 111171f95118Swdenk 1112*7ee46cebSBenoît Thébaudeau if (isdir && !(dentptr->attr & ATTR_DIR)) 1113ac497771SSergei Shtylyov goto exit; 1114*7ee46cebSBenoît Thébaudeau 1115*7ee46cebSBenoît Thébaudeau if (idx >= 0) 111671f95118Swdenk subname = nextname; 111771f95118Swdenk } 11187385c28eSWolfgang Denk 111971f95118Swdenk ret = get_contents(mydata, dentptr, buffer, maxsize); 11207385c28eSWolfgang Denk debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret); 112171f95118Swdenk 1122ac497771SSergei Shtylyov exit: 1123ac497771SSergei Shtylyov free(mydata->fatbuf); 112471f95118Swdenk return ret; 112571f95118Swdenk } 112671f95118Swdenk 11277385c28eSWolfgang Denk int file_fat_detectfs(void) 112871f95118Swdenk { 112971f95118Swdenk boot_sector bs; 113071f95118Swdenk volume_info volinfo; 113171f95118Swdenk int fatsize; 11327205e407Swdenk char vol_label[12]; 113371f95118Swdenk 11347205e407Swdenk if (cur_dev == NULL) { 11357205e407Swdenk printf("No current device\n"); 11367205e407Swdenk return 1; 11377205e407Swdenk } 11387385c28eSWolfgang Denk 1139dd60d122SJon Loeliger #if defined(CONFIG_CMD_IDE) || \ 11408c5170a7SSonic Zhang defined(CONFIG_CMD_SATA) || \ 1141dd60d122SJon Loeliger defined(CONFIG_CMD_SCSI) || \ 1142dd60d122SJon Loeliger defined(CONFIG_CMD_USB) || \ 114321f6f963SAndy Fleming defined(CONFIG_MMC) 11447205e407Swdenk printf("Interface: "); 11457205e407Swdenk switch (cur_dev->if_type) { 11467385c28eSWolfgang Denk case IF_TYPE_IDE: 11477385c28eSWolfgang Denk printf("IDE"); 11487385c28eSWolfgang Denk break; 11497385c28eSWolfgang Denk case IF_TYPE_SATA: 11507385c28eSWolfgang Denk printf("SATA"); 11517385c28eSWolfgang Denk break; 11527385c28eSWolfgang Denk case IF_TYPE_SCSI: 11537385c28eSWolfgang Denk printf("SCSI"); 11547385c28eSWolfgang Denk break; 11557385c28eSWolfgang Denk case IF_TYPE_ATAPI: 11567385c28eSWolfgang Denk printf("ATAPI"); 11577385c28eSWolfgang Denk break; 11587385c28eSWolfgang Denk case IF_TYPE_USB: 11597385c28eSWolfgang Denk printf("USB"); 11607385c28eSWolfgang Denk break; 11617385c28eSWolfgang Denk case IF_TYPE_DOC: 11627385c28eSWolfgang Denk printf("DOC"); 11637385c28eSWolfgang Denk break; 11647385c28eSWolfgang Denk case IF_TYPE_MMC: 11657385c28eSWolfgang Denk printf("MMC"); 11667385c28eSWolfgang Denk break; 11677385c28eSWolfgang Denk default: 11687385c28eSWolfgang Denk printf("Unknown"); 11697205e407Swdenk } 11707385c28eSWolfgang Denk 11717205e407Swdenk printf("\n Device %d: ", cur_dev->dev); 11727205e407Swdenk dev_print(cur_dev); 11737205e407Swdenk #endif 11747385c28eSWolfgang Denk 11757205e407Swdenk if (read_bootsectandvi(&bs, &volinfo, &fatsize)) { 11767205e407Swdenk printf("\nNo valid FAT fs found\n"); 11777205e407Swdenk return 1; 11787205e407Swdenk } 11797385c28eSWolfgang Denk 11807205e407Swdenk memcpy(vol_label, volinfo.volume_label, 11); 11817205e407Swdenk vol_label[11] = '\0'; 11827205e407Swdenk volinfo.fs_type[5] = '\0'; 11837385c28eSWolfgang Denk 11849813b750SKyle Moffett printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part_nr, 11857385c28eSWolfgang Denk volinfo.fs_type, vol_label); 11867385c28eSWolfgang Denk 11877205e407Swdenk return 0; 118871f95118Swdenk } 118971f95118Swdenk 11907385c28eSWolfgang Denk int file_fat_ls(const char *dir) 119171f95118Swdenk { 119271f95118Swdenk return do_fat_read(dir, NULL, 0, LS_YES); 119371f95118Swdenk } 119471f95118Swdenk 11957385c28eSWolfgang Denk long file_fat_read(const char *filename, void *buffer, unsigned long maxsize) 119671f95118Swdenk { 11977205e407Swdenk printf("reading %s\n", filename); 119871f95118Swdenk return do_fat_read(filename, buffer, maxsize, LS_NO); 119971f95118Swdenk } 1200