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 * 91a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 1071f95118Swdenk */ 1171f95118Swdenk 1271f95118Swdenk #include <common.h> 132a981dc2SSimon Glass #include <blk.h> 1471f95118Swdenk #include <config.h> 15ac497771SSergei Shtylyov #include <exports.h> 1671f95118Swdenk #include <fat.h> 17*60352d22SRob Clark #include <fs.h> 1871f95118Swdenk #include <asm/byteorder.h> 197205e407Swdenk #include <part.h> 209a800ac7SEric Nelson #include <malloc.h> 21cf92e05cSSimon Glass #include <memalign.h> 229a800ac7SEric Nelson #include <linux/compiler.h> 23fb7e16ccSRichard Genoud #include <linux/ctype.h> 2471f95118Swdenk 25cb940c7eSRichard Genoud #ifdef CONFIG_SUPPORT_VFAT 26cb940c7eSRichard Genoud static const int vfat_enabled = 1; 27cb940c7eSRichard Genoud #else 28cb940c7eSRichard Genoud static const int vfat_enabled = 0; 29cb940c7eSRichard Genoud #endif 30cb940c7eSRichard Genoud 3171f95118Swdenk /* 3271f95118Swdenk * Convert a string to lowercase. 3371f95118Swdenk */ 347385c28eSWolfgang Denk static void downcase(char *str) 3571f95118Swdenk { 3671f95118Swdenk while (*str != '\0') { 37fb7e16ccSRichard Genoud *str = tolower(*str); 3871f95118Swdenk str++; 3971f95118Swdenk } 4071f95118Swdenk } 4171f95118Swdenk 424101f687SSimon Glass static struct blk_desc *cur_dev; 439813b750SKyle Moffett static disk_partition_t cur_part_info; 447385c28eSWolfgang Denk 459813b750SKyle Moffett #define DOS_BOOT_MAGIC_OFFSET 0x1fe 467205e407Swdenk #define DOS_FS_TYPE_OFFSET 0x36 4766c2d73cSWolfgang Denk #define DOS_FS32_TYPE_OFFSET 0x52 4871f95118Swdenk 499813b750SKyle Moffett static int disk_read(__u32 block, __u32 nr_blocks, void *buf) 5071f95118Swdenk { 510a04ed86SŁukasz Majewski ulong ret; 520a04ed86SŁukasz Majewski 532a981dc2SSimon Glass if (!cur_dev) 547205e407Swdenk return -1; 557385c28eSWolfgang Denk 562a981dc2SSimon Glass ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf); 570a04ed86SŁukasz Majewski 5842a9f147STom Rini if (ret != nr_blocks) 590a04ed86SŁukasz Majewski return -1; 600a04ed86SŁukasz Majewski 610a04ed86SŁukasz Majewski return ret; 6271f95118Swdenk } 6371f95118Swdenk 644101f687SSimon Glass int fat_set_blk_dev(struct blk_desc *dev_desc, disk_partition_t *info) 6571f95118Swdenk { 669a800ac7SEric Nelson ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz); 677205e407Swdenk 689813b750SKyle Moffett cur_dev = dev_desc; 695e8f9831SStephen Warren cur_part_info = *info; 709813b750SKyle Moffett 719813b750SKyle Moffett /* Make sure it has a valid FAT header */ 729813b750SKyle Moffett if (disk_read(0, 1, buffer) != 1) { 739813b750SKyle Moffett cur_dev = NULL; 749813b750SKyle Moffett return -1; 757205e407Swdenk } 769813b750SKyle Moffett 779813b750SKyle Moffett /* Check if it's actually a DOS volume */ 789813b750SKyle Moffett if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) { 799813b750SKyle Moffett cur_dev = NULL; 809813b750SKyle Moffett return -1; 819813b750SKyle Moffett } 829813b750SKyle Moffett 839813b750SKyle Moffett /* Check for FAT12/FAT16/FAT32 filesystem */ 849813b750SKyle Moffett if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3)) 8571f95118Swdenk return 0; 869813b750SKyle Moffett if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5)) 879813b750SKyle Moffett return 0; 889813b750SKyle Moffett 899813b750SKyle Moffett cur_dev = NULL; 909813b750SKyle Moffett return -1; 9171f95118Swdenk } 9271f95118Swdenk 934101f687SSimon Glass int fat_register_device(struct blk_desc *dev_desc, int part_no) 945e8f9831SStephen Warren { 955e8f9831SStephen Warren disk_partition_t info; 965e8f9831SStephen Warren 975e8f9831SStephen Warren /* First close any currently found FAT filesystem */ 985e8f9831SStephen Warren cur_dev = NULL; 995e8f9831SStephen Warren 1005e8f9831SStephen Warren /* Read the partition table, if present */ 1013e8bd469SSimon Glass if (part_get_info(dev_desc, part_no, &info)) { 1025e8f9831SStephen Warren if (part_no != 0) { 1035e8f9831SStephen Warren printf("** Partition %d not valid on device %d **\n", 104bcce53d0SSimon Glass part_no, dev_desc->devnum); 1055e8f9831SStephen Warren return -1; 1065e8f9831SStephen Warren } 1075e8f9831SStephen Warren 1085e8f9831SStephen Warren info.start = 0; 1095e8f9831SStephen Warren info.size = dev_desc->lba; 1105e8f9831SStephen Warren info.blksz = dev_desc->blksz; 1115e8f9831SStephen Warren info.name[0] = 0; 1125e8f9831SStephen Warren info.type[0] = 0; 1135e8f9831SStephen Warren info.bootable = 0; 114b331cd62SPatrick Delaunay #if CONFIG_IS_ENABLED(PARTITION_UUIDS) 1155e8f9831SStephen Warren info.uuid[0] = 0; 1165e8f9831SStephen Warren #endif 1175e8f9831SStephen Warren } 1185e8f9831SStephen Warren 1195e8f9831SStephen Warren return fat_set_blk_dev(dev_desc, &info); 1205e8f9831SStephen Warren } 1219813b750SKyle Moffett 12271f95118Swdenk /* 12371f95118Swdenk * Extract zero terminated short name from a directory entry. 12471f95118Swdenk */ 12571f95118Swdenk static void get_name(dir_entry *dirent, char *s_name) 12671f95118Swdenk { 12771f95118Swdenk char *ptr; 12871f95118Swdenk 12971f95118Swdenk memcpy(s_name, dirent->name, 8); 13071f95118Swdenk s_name[8] = '\0'; 13171f95118Swdenk ptr = s_name; 13271f95118Swdenk while (*ptr && *ptr != ' ') 13371f95118Swdenk ptr++; 13471f95118Swdenk if (dirent->ext[0] && dirent->ext[0] != ' ') { 13571f95118Swdenk *ptr = '.'; 13671f95118Swdenk ptr++; 13771f95118Swdenk memcpy(ptr, dirent->ext, 3); 13871f95118Swdenk ptr[3] = '\0'; 13971f95118Swdenk while (*ptr && *ptr != ' ') 14071f95118Swdenk ptr++; 14171f95118Swdenk } 14271f95118Swdenk *ptr = '\0'; 14371f95118Swdenk if (*s_name == DELETED_FLAG) 14471f95118Swdenk *s_name = '\0'; 14571f95118Swdenk else if (*s_name == aRING) 1463c2c2f42SRemy Bohmer *s_name = DELETED_FLAG; 14771f95118Swdenk downcase(s_name); 14871f95118Swdenk } 14971f95118Swdenk 150b8948d2aSStefan Brüns static int flush_dirty_fat_buffer(fsdata *mydata); 151b8948d2aSStefan Brüns #if !defined(CONFIG_FAT_WRITE) 152b8948d2aSStefan Brüns /* Stub for read only operation */ 153b8948d2aSStefan Brüns int flush_dirty_fat_buffer(fsdata *mydata) 154b8948d2aSStefan Brüns { 155b8948d2aSStefan Brüns (void)(mydata); 156b8948d2aSStefan Brüns return 0; 157b8948d2aSStefan Brüns } 158b8948d2aSStefan Brüns #endif 159b8948d2aSStefan Brüns 16071f95118Swdenk /* 16171f95118Swdenk * Get the entry at index 'entry' in a FAT (12/16/32) table. 16271f95118Swdenk * On failure 0x00 is returned. 16371f95118Swdenk */ 1647385c28eSWolfgang Denk static __u32 get_fatent(fsdata *mydata, __u32 entry) 16571f95118Swdenk { 16671f95118Swdenk __u32 bufnum; 167b352caeaSStefan Brüns __u32 offset, off8; 16871f95118Swdenk __u32 ret = 0x00; 16971f95118Swdenk 170b8948d2aSStefan Brüns if (CHECK_CLUST(entry, mydata->fatsize)) { 171b8948d2aSStefan Brüns printf("Error: Invalid FAT entry: 0x%08x\n", entry); 172b8948d2aSStefan Brüns return ret; 173b8948d2aSStefan Brüns } 174b8948d2aSStefan Brüns 17571f95118Swdenk switch (mydata->fatsize) { 17671f95118Swdenk case 32: 17771f95118Swdenk bufnum = entry / FAT32BUFSIZE; 17871f95118Swdenk offset = entry - bufnum * FAT32BUFSIZE; 17971f95118Swdenk break; 18071f95118Swdenk case 16: 18171f95118Swdenk bufnum = entry / FAT16BUFSIZE; 18271f95118Swdenk offset = entry - bufnum * FAT16BUFSIZE; 18371f95118Swdenk break; 18471f95118Swdenk case 12: 18571f95118Swdenk bufnum = entry / FAT12BUFSIZE; 18671f95118Swdenk offset = entry - bufnum * FAT12BUFSIZE; 18771f95118Swdenk break; 18871f95118Swdenk 18971f95118Swdenk default: 19071f95118Swdenk /* Unsupported FAT size */ 19171f95118Swdenk return ret; 19271f95118Swdenk } 19371f95118Swdenk 194b8948d2aSStefan Brüns debug("FAT%d: entry: 0x%08x = %d, offset: 0x%04x = %d\n", 1952aa98c66SWolfgang Denk mydata->fatsize, entry, entry, offset, offset); 1962aa98c66SWolfgang Denk 19771f95118Swdenk /* Read a new block of FAT entries into the cache. */ 19871f95118Swdenk if (bufnum != mydata->fatbufnum) { 19960b36f0fSSergei Shtylyov __u32 getsize = FATBUFBLOCKS; 20071f95118Swdenk __u8 *bufptr = mydata->fatbuf; 20171f95118Swdenk __u32 fatlength = mydata->fatlength; 20271f95118Swdenk __u32 startblock = bufnum * FATBUFBLOCKS; 20371f95118Swdenk 2046c1a8080SStefan Brüns /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */ 2058006dd2eSBenoît Thébaudeau if (startblock + getsize > fatlength) 2068006dd2eSBenoît Thébaudeau getsize = fatlength - startblock; 20760b36f0fSSergei Shtylyov 20871f95118Swdenk startblock += mydata->fat_sect; /* Offset from start of disk */ 20971f95118Swdenk 210b8948d2aSStefan Brüns /* Write back the fatbuf to the disk */ 211b8948d2aSStefan Brüns if (flush_dirty_fat_buffer(mydata) < 0) 212b8948d2aSStefan Brüns return -1; 213b8948d2aSStefan Brüns 21471f95118Swdenk if (disk_read(startblock, getsize, bufptr) < 0) { 2157385c28eSWolfgang Denk debug("Error reading FAT blocks\n"); 21671f95118Swdenk return ret; 21771f95118Swdenk } 21871f95118Swdenk mydata->fatbufnum = bufnum; 21971f95118Swdenk } 22071f95118Swdenk 22171f95118Swdenk /* Get the actual entry from the table */ 22271f95118Swdenk switch (mydata->fatsize) { 22371f95118Swdenk case 32: 22471f95118Swdenk ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]); 22571f95118Swdenk break; 22671f95118Swdenk case 16: 22771f95118Swdenk ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]); 22871f95118Swdenk break; 2297385c28eSWolfgang Denk case 12: 230b352caeaSStefan Brüns off8 = (offset * 3) / 2; 231b352caeaSStefan Brüns /* fatbut + off8 may be unaligned, read in byte granularity */ 232b352caeaSStefan Brüns ret = mydata->fatbuf[off8] + (mydata->fatbuf[off8 + 1] << 8); 23371f95118Swdenk 2348d48c92bSStefan Brüns if (offset & 0x1) 2358d48c92bSStefan Brüns ret >>= 4; 23671f95118Swdenk ret &= 0xfff; 23771f95118Swdenk } 238b8948d2aSStefan Brüns debug("FAT%d: ret: 0x%08x, entry: 0x%08x, offset: 0x%04x\n", 239b8948d2aSStefan Brüns mydata->fatsize, ret, entry, offset); 24071f95118Swdenk 24171f95118Swdenk return ret; 24271f95118Swdenk } 24371f95118Swdenk 24471f95118Swdenk /* 24571f95118Swdenk * Read at most 'size' bytes from the specified cluster into 'buffer'. 24671f95118Swdenk * Return 0 on success, -1 otherwise. 24771f95118Swdenk */ 24871f95118Swdenk static int 2499795e07bSBenoît Thébaudeau get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size) 25071f95118Swdenk { 2513f270f42SErik Hansen __u32 idx = 0; 25271f95118Swdenk __u32 startsect; 25346236b14SKyle Moffett int ret; 25471f95118Swdenk 25571f95118Swdenk if (clustnum > 0) { 2567385c28eSWolfgang Denk startsect = mydata->data_begin + 2577385c28eSWolfgang Denk clustnum * mydata->clust_size; 25871f95118Swdenk } else { 25971f95118Swdenk startsect = mydata->rootdir_sect; 26071f95118Swdenk } 26171f95118Swdenk 2627385c28eSWolfgang Denk debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect); 2637385c28eSWolfgang Denk 264cc63b25eSBenoît Thébaudeau if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) { 2659a800ac7SEric Nelson ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 2667385c28eSWolfgang Denk 267cc63b25eSBenoît Thébaudeau printf("FAT: Misaligned buffer address (%p)\n", buffer); 268cc63b25eSBenoît Thébaudeau 269cc63b25eSBenoît Thébaudeau while (size >= mydata->sect_size) { 270cc63b25eSBenoît Thébaudeau ret = disk_read(startsect++, 1, tmpbuf); 27146236b14SKyle Moffett if (ret != 1) { 27246236b14SKyle Moffett debug("Error reading data (got %d)\n", ret); 27371f95118Swdenk return -1; 27471f95118Swdenk } 27571f95118Swdenk 276cc63b25eSBenoît Thébaudeau memcpy(buffer, tmpbuf, mydata->sect_size); 277cc63b25eSBenoît Thébaudeau buffer += mydata->sect_size; 278cc63b25eSBenoît Thébaudeau size -= mydata->sect_size; 279cc63b25eSBenoît Thébaudeau } 280cc63b25eSBenoît Thébaudeau } else { 281cc63b25eSBenoît Thébaudeau idx = size / mydata->sect_size; 282cc63b25eSBenoît Thébaudeau ret = disk_read(startsect, idx, buffer); 283cc63b25eSBenoît Thébaudeau if (ret != idx) { 284cc63b25eSBenoît Thébaudeau debug("Error reading data (got %d)\n", ret); 285cc63b25eSBenoît Thébaudeau return -1; 286cc63b25eSBenoît Thébaudeau } 287cc63b25eSBenoît Thébaudeau startsect += idx; 288cc63b25eSBenoît Thébaudeau idx *= mydata->sect_size; 289cc63b25eSBenoît Thébaudeau buffer += idx; 290cc63b25eSBenoît Thébaudeau size -= idx; 291cc63b25eSBenoît Thébaudeau } 292cc63b25eSBenoît Thébaudeau if (size) { 293cc63b25eSBenoît Thébaudeau ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size); 294cc63b25eSBenoît Thébaudeau 295cc63b25eSBenoît Thébaudeau ret = disk_read(startsect, 1, tmpbuf); 296cc63b25eSBenoît Thébaudeau if (ret != 1) { 297cc63b25eSBenoît Thébaudeau debug("Error reading data (got %d)\n", ret); 298cc63b25eSBenoît Thébaudeau return -1; 299cc63b25eSBenoît Thébaudeau } 300cc63b25eSBenoît Thébaudeau 301cc63b25eSBenoît Thébaudeau memcpy(buffer, tmpbuf, size); 30271f95118Swdenk } 30371f95118Swdenk 30471f95118Swdenk return 0; 30571f95118Swdenk } 30671f95118Swdenk 30771f95118Swdenk /* 3081170e634SBenoît Thébaudeau * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr' 30971f95118Swdenk * into 'buffer'. 3101ad0b98aSSuriyan Ramasami * Update the number of bytes read in *gotsize or return -1 on fatal errors. 31171f95118Swdenk */ 3121170e634SBenoît Thébaudeau __u8 get_contents_vfatname_block[MAX_CLUSTSIZE] 3131170e634SBenoît Thébaudeau __aligned(ARCH_DMA_MINALIGN); 3141170e634SBenoît Thébaudeau 3151ad0b98aSSuriyan Ramasami static int get_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, 3161ad0b98aSSuriyan Ramasami __u8 *buffer, loff_t maxsize, loff_t *gotsize) 31771f95118Swdenk { 3181ad0b98aSSuriyan Ramasami loff_t filesize = FAT2CPU32(dentptr->size); 319ac497771SSergei Shtylyov unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; 32071f95118Swdenk __u32 curclust = START(dentptr); 3217205e407Swdenk __u32 endclust, newclust; 3221ad0b98aSSuriyan Ramasami loff_t actsize; 32371f95118Swdenk 3241ad0b98aSSuriyan Ramasami *gotsize = 0; 3251ad0b98aSSuriyan Ramasami debug("Filesize: %llu bytes\n", filesize); 32671f95118Swdenk 3271170e634SBenoît Thébaudeau if (pos >= filesize) { 3281ad0b98aSSuriyan Ramasami debug("Read position past EOF: %llu\n", pos); 3291ad0b98aSSuriyan Ramasami return 0; 3301170e634SBenoît Thébaudeau } 3311170e634SBenoît Thébaudeau 3321170e634SBenoît Thébaudeau if (maxsize > 0 && filesize > pos + maxsize) 3331170e634SBenoît Thébaudeau filesize = pos + maxsize; 33471f95118Swdenk 3351ad0b98aSSuriyan Ramasami debug("%llu bytes\n", filesize); 33671f95118Swdenk 3377205e407Swdenk actsize = bytesperclust; 3381170e634SBenoît Thébaudeau 3391170e634SBenoît Thébaudeau /* go to cluster at pos */ 3401170e634SBenoît Thébaudeau while (actsize <= pos) { 3411170e634SBenoît Thébaudeau curclust = get_fatent(mydata, curclust); 3421170e634SBenoît Thébaudeau if (CHECK_CLUST(curclust, mydata->fatsize)) { 3431170e634SBenoît Thébaudeau debug("curclust: 0x%x\n", curclust); 3441170e634SBenoît Thébaudeau debug("Invalid FAT entry\n"); 3451ad0b98aSSuriyan Ramasami return 0; 3461170e634SBenoît Thébaudeau } 3471170e634SBenoît Thébaudeau actsize += bytesperclust; 3481170e634SBenoît Thébaudeau } 3491170e634SBenoît Thébaudeau 3501170e634SBenoît Thébaudeau /* actsize > pos */ 3511170e634SBenoît Thébaudeau actsize -= bytesperclust; 3521170e634SBenoît Thébaudeau filesize -= actsize; 3531170e634SBenoît Thébaudeau pos -= actsize; 3541170e634SBenoît Thébaudeau 3551170e634SBenoît Thébaudeau /* align to beginning of next cluster if any */ 3561170e634SBenoît Thébaudeau if (pos) { 3571ad0b98aSSuriyan Ramasami actsize = min(filesize, (loff_t)bytesperclust); 3581170e634SBenoît Thébaudeau if (get_cluster(mydata, curclust, get_contents_vfatname_block, 3591170e634SBenoît Thébaudeau (int)actsize) != 0) { 3601170e634SBenoît Thébaudeau printf("Error reading cluster\n"); 3611170e634SBenoît Thébaudeau return -1; 3621170e634SBenoît Thébaudeau } 3631170e634SBenoît Thébaudeau filesize -= actsize; 3641170e634SBenoît Thébaudeau actsize -= pos; 3651170e634SBenoît Thébaudeau memcpy(buffer, get_contents_vfatname_block + pos, actsize); 3661ad0b98aSSuriyan Ramasami *gotsize += actsize; 3671170e634SBenoît Thébaudeau if (!filesize) 3681ad0b98aSSuriyan Ramasami return 0; 3691170e634SBenoît Thébaudeau buffer += actsize; 3701170e634SBenoît Thébaudeau 3711170e634SBenoît Thébaudeau curclust = get_fatent(mydata, curclust); 3721170e634SBenoît Thébaudeau if (CHECK_CLUST(curclust, mydata->fatsize)) { 3731170e634SBenoît Thébaudeau debug("curclust: 0x%x\n", curclust); 3741170e634SBenoît Thébaudeau debug("Invalid FAT entry\n"); 3751ad0b98aSSuriyan Ramasami return 0; 3761170e634SBenoît Thébaudeau } 3771170e634SBenoît Thébaudeau } 3781170e634SBenoît Thébaudeau 3791170e634SBenoît Thébaudeau actsize = bytesperclust; 3807205e407Swdenk endclust = curclust; 3817385c28eSWolfgang Denk 38271f95118Swdenk do { 3837205e407Swdenk /* search for consecutive clusters */ 3847205e407Swdenk while (actsize < filesize) { 3857205e407Swdenk newclust = get_fatent(mydata, endclust); 3867205e407Swdenk if ((newclust - 1) != endclust) 3877205e407Swdenk goto getit; 3888ce4e5c2Smichael if (CHECK_CLUST(newclust, mydata->fatsize)) { 3897385c28eSWolfgang Denk debug("curclust: 0x%x\n", newclust); 3907385c28eSWolfgang Denk debug("Invalid FAT entry\n"); 3911ad0b98aSSuriyan Ramasami return 0; 3927205e407Swdenk } 3937205e407Swdenk endclust = newclust; 3947205e407Swdenk actsize += bytesperclust; 3957205e407Swdenk } 3967385c28eSWolfgang Denk 3977205e407Swdenk /* get remaining bytes */ 3987205e407Swdenk actsize = filesize; 3990880e5bbSBenoît Thébaudeau if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 4007385c28eSWolfgang Denk printf("Error reading cluster\n"); 4017205e407Swdenk return -1; 4027205e407Swdenk } 4031ad0b98aSSuriyan Ramasami *gotsize += actsize; 4041ad0b98aSSuriyan Ramasami return 0; 4057205e407Swdenk getit: 4067205e407Swdenk if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { 4077385c28eSWolfgang Denk printf("Error reading cluster\n"); 4087205e407Swdenk return -1; 4097205e407Swdenk } 4101ad0b98aSSuriyan Ramasami *gotsize += (int)actsize; 4117205e407Swdenk filesize -= actsize; 4127205e407Swdenk buffer += actsize; 4137385c28eSWolfgang Denk 4147205e407Swdenk curclust = get_fatent(mydata, endclust); 4158ce4e5c2Smichael if (CHECK_CLUST(curclust, mydata->fatsize)) { 4167385c28eSWolfgang Denk debug("curclust: 0x%x\n", curclust); 4177385c28eSWolfgang Denk printf("Invalid FAT entry\n"); 4181ad0b98aSSuriyan Ramasami return 0; 41971f95118Swdenk } 4207205e407Swdenk actsize = bytesperclust; 4217205e407Swdenk endclust = curclust; 42271f95118Swdenk } while (1); 42371f95118Swdenk } 42471f95118Swdenk 42571f95118Swdenk /* 42671f95118Swdenk * Extract the file name information from 'slotptr' into 'l_name', 42771f95118Swdenk * starting at l_name[*idx]. 42871f95118Swdenk * Return 1 if terminator (zero byte) is found, 0 otherwise. 42971f95118Swdenk */ 4307385c28eSWolfgang Denk static int slot2str(dir_slot *slotptr, char *l_name, int *idx) 43171f95118Swdenk { 43271f95118Swdenk int j; 43371f95118Swdenk 43471f95118Swdenk for (j = 0; j <= 8; j += 2) { 43571f95118Swdenk l_name[*idx] = slotptr->name0_4[j]; 4367385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4377385c28eSWolfgang Denk return 1; 43871f95118Swdenk (*idx)++; 43971f95118Swdenk } 44071f95118Swdenk for (j = 0; j <= 10; j += 2) { 44171f95118Swdenk l_name[*idx] = slotptr->name5_10[j]; 4427385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4437385c28eSWolfgang Denk return 1; 44471f95118Swdenk (*idx)++; 44571f95118Swdenk } 44671f95118Swdenk for (j = 0; j <= 2; j += 2) { 44771f95118Swdenk l_name[*idx] = slotptr->name11_12[j]; 4487385c28eSWolfgang Denk if (l_name[*idx] == 0x00) 4497385c28eSWolfgang Denk return 1; 45071f95118Swdenk (*idx)++; 45171f95118Swdenk } 45271f95118Swdenk 45371f95118Swdenk return 0; 45471f95118Swdenk } 45571f95118Swdenk 45671f95118Swdenk /* Calculate short name checksum */ 457ff04f6d1SMarek Vasut static __u8 mkcksum(const char name[8], const char ext[3]) 45871f95118Swdenk { 45971f95118Swdenk int i; 4607385c28eSWolfgang Denk 46171f95118Swdenk __u8 ret = 0; 46271f95118Swdenk 4636ad77d88SMarek Vasut for (i = 0; i < 8; i++) 464ff04f6d1SMarek Vasut ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + name[i]; 4656ad77d88SMarek Vasut for (i = 0; i < 3; i++) 466ff04f6d1SMarek Vasut ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + ext[i]; 46771f95118Swdenk 46871f95118Swdenk return ret; 46971f95118Swdenk } 47071f95118Swdenk 47171f95118Swdenk /* 472759bf728SRob Clark * TODO these should go away once fat_write is reworked to use the 473759bf728SRob Clark * directory iterator 47471f95118Swdenk */ 4759a800ac7SEric Nelson __u8 get_dentfromdir_block[MAX_CLUSTSIZE] 4769a800ac7SEric Nelson __aligned(ARCH_DMA_MINALIGN); 477759bf728SRob Clark __u8 do_fat_read_at_block[MAX_CLUSTSIZE] 478759bf728SRob Clark __aligned(ARCH_DMA_MINALIGN); 47971f95118Swdenk 48071f95118Swdenk /* 48171f95118Swdenk * Read boot sector and volume info from a FAT filesystem 48271f95118Swdenk */ 48371f95118Swdenk static int 48471f95118Swdenk read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize) 48571f95118Swdenk { 486ac497771SSergei Shtylyov __u8 *block; 48771f95118Swdenk volume_info *vistart; 488ac497771SSergei Shtylyov int ret = 0; 489ac497771SSergei Shtylyov 490ac497771SSergei Shtylyov if (cur_dev == NULL) { 491ac497771SSergei Shtylyov debug("Error: no device selected\n"); 492ac497771SSergei Shtylyov return -1; 493ac497771SSergei Shtylyov } 494ac497771SSergei Shtylyov 4959a800ac7SEric Nelson block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz); 496ac497771SSergei Shtylyov if (block == NULL) { 497ac497771SSergei Shtylyov debug("Error: allocating block\n"); 498ac497771SSergei Shtylyov return -1; 499ac497771SSergei Shtylyov } 50071f95118Swdenk 50171f95118Swdenk if (disk_read(0, 1, block) < 0) { 5027385c28eSWolfgang Denk debug("Error: reading block\n"); 503ac497771SSergei Shtylyov goto fail; 50471f95118Swdenk } 50571f95118Swdenk 50671f95118Swdenk memcpy(bs, block, sizeof(boot_sector)); 50771f95118Swdenk bs->reserved = FAT2CPU16(bs->reserved); 50871f95118Swdenk bs->fat_length = FAT2CPU16(bs->fat_length); 50971f95118Swdenk bs->secs_track = FAT2CPU16(bs->secs_track); 51071f95118Swdenk bs->heads = FAT2CPU16(bs->heads); 51171f95118Swdenk bs->total_sect = FAT2CPU32(bs->total_sect); 51271f95118Swdenk 51371f95118Swdenk /* FAT32 entries */ 51471f95118Swdenk if (bs->fat_length == 0) { 51571f95118Swdenk /* Assume FAT32 */ 51671f95118Swdenk bs->fat32_length = FAT2CPU32(bs->fat32_length); 51771f95118Swdenk bs->flags = FAT2CPU16(bs->flags); 51871f95118Swdenk bs->root_cluster = FAT2CPU32(bs->root_cluster); 51971f95118Swdenk bs->info_sector = FAT2CPU16(bs->info_sector); 52071f95118Swdenk bs->backup_boot = FAT2CPU16(bs->backup_boot); 52171f95118Swdenk vistart = (volume_info *)(block + sizeof(boot_sector)); 52271f95118Swdenk *fatsize = 32; 52371f95118Swdenk } else { 52471f95118Swdenk vistart = (volume_info *)&(bs->fat32_length); 52571f95118Swdenk *fatsize = 0; 52671f95118Swdenk } 52771f95118Swdenk memcpy(volinfo, vistart, sizeof(volume_info)); 52871f95118Swdenk 52971f95118Swdenk if (*fatsize == 32) { 5307385c28eSWolfgang Denk if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0) 531ac497771SSergei Shtylyov goto exit; 53271f95118Swdenk } else { 533651351feSTom Rix if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) { 53471f95118Swdenk *fatsize = 12; 535ac497771SSergei Shtylyov goto exit; 53671f95118Swdenk } 537651351feSTom Rix if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) { 53871f95118Swdenk *fatsize = 16; 539ac497771SSergei Shtylyov goto exit; 54071f95118Swdenk } 54171f95118Swdenk } 54271f95118Swdenk 5437385c28eSWolfgang Denk debug("Error: broken fs_type sign\n"); 544ac497771SSergei Shtylyov fail: 545ac497771SSergei Shtylyov ret = -1; 546ac497771SSergei Shtylyov exit: 547ac497771SSergei Shtylyov free(block); 548ac497771SSergei Shtylyov return ret; 54971f95118Swdenk } 55071f95118Swdenk 551f3068e75SRob Clark static int get_fs_info(fsdata *mydata) 55271f95118Swdenk { 55371f95118Swdenk boot_sector bs; 55471f95118Swdenk volume_info volinfo; 555f3068e75SRob Clark int ret; 55671f95118Swdenk 557f3068e75SRob Clark ret = read_bootsectandvi(&bs, &volinfo, &mydata->fatsize); 558f3068e75SRob Clark if (ret) { 5597385c28eSWolfgang Denk debug("Error: reading boot sector\n"); 560f3068e75SRob Clark return ret; 56171f95118Swdenk } 5627385c28eSWolfgang Denk 56340e21916SSergei Shtylyov if (mydata->fatsize == 32) { 56471f95118Swdenk mydata->fatlength = bs.fat32_length; 56540e21916SSergei Shtylyov } else { 56671f95118Swdenk mydata->fatlength = bs.fat_length; 56740e21916SSergei Shtylyov } 5687385c28eSWolfgang Denk 56971f95118Swdenk mydata->fat_sect = bs.reserved; 5707385c28eSWolfgang Denk 571f3068e75SRob Clark mydata->rootdir_sect = mydata->fat_sect + mydata->fatlength * bs.fats; 5727385c28eSWolfgang Denk 573ac497771SSergei Shtylyov mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0]; 57471f95118Swdenk mydata->clust_size = bs.cluster_size; 57546236b14SKyle Moffett if (mydata->sect_size != cur_part_info.blksz) { 57646236b14SKyle Moffett printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n", 57746236b14SKyle Moffett mydata->sect_size, cur_part_info.blksz); 57846236b14SKyle Moffett return -1; 57946236b14SKyle Moffett } 5807385c28eSWolfgang Denk 58171f95118Swdenk if (mydata->fatsize == 32) { 5827385c28eSWolfgang Denk mydata->data_begin = mydata->rootdir_sect - 5837385c28eSWolfgang Denk (mydata->clust_size * 2); 584fed831fcSRob Clark mydata->root_cluster = bs.root_cluster; 58571f95118Swdenk } else { 586f3068e75SRob Clark mydata->rootdir_size = ((bs.dir_entries[1] * (int)256 + 5877385c28eSWolfgang Denk bs.dir_entries[0]) * 5887385c28eSWolfgang Denk sizeof(dir_entry)) / 589ac497771SSergei Shtylyov mydata->sect_size; 5907385c28eSWolfgang Denk mydata->data_begin = mydata->rootdir_sect + 591f3068e75SRob Clark mydata->rootdir_size - 5927385c28eSWolfgang Denk (mydata->clust_size * 2); 593fed831fcSRob Clark mydata->root_cluster = (mydata->rootdir_sect - 594fed831fcSRob Clark mydata->data_begin) / 595fed831fcSRob Clark mydata->clust_size; 59671f95118Swdenk } 5977385c28eSWolfgang Denk 59871f95118Swdenk mydata->fatbufnum = -1; 5993c0ed9c3SStefan Brüns mydata->fat_dirty = 0; 6009a800ac7SEric Nelson mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE); 601ac497771SSergei Shtylyov if (mydata->fatbuf == NULL) { 602ac497771SSergei Shtylyov debug("Error: allocating memory\n"); 603ac497771SSergei Shtylyov return -1; 604ac497771SSergei Shtylyov } 60571f95118Swdenk 606cb940c7eSRichard Genoud if (vfat_enabled) 6077385c28eSWolfgang Denk debug("VFAT Support enabled\n"); 608cb940c7eSRichard Genoud 6097385c28eSWolfgang Denk debug("FAT%d, fat_sect: %d, fatlength: %d\n", 6107385c28eSWolfgang Denk mydata->fatsize, mydata->fat_sect, mydata->fatlength); 6117385c28eSWolfgang Denk debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n" 61271f95118Swdenk "Data begins at: %d\n", 613fed831fcSRob Clark mydata->root_cluster, 6142aa98c66SWolfgang Denk mydata->rootdir_sect, 615ac497771SSergei Shtylyov mydata->rootdir_sect * mydata->sect_size, mydata->data_begin); 616ac497771SSergei Shtylyov debug("Sector size: %d, cluster size: %d\n", mydata->sect_size, 617ac497771SSergei Shtylyov mydata->clust_size); 61871f95118Swdenk 619f3068e75SRob Clark return 0; 620f3068e75SRob Clark } 621f3068e75SRob Clark 622fed831fcSRob Clark 623fed831fcSRob Clark /* 624fed831fcSRob Clark * Directory iterator, to simplify filesystem traversal 625fed831fcSRob Clark * 626fed831fcSRob Clark * Implements an iterator pattern to traverse directory tables, 627fed831fcSRob Clark * transparently handling directory tables split across multiple 628fed831fcSRob Clark * clusters, and the difference between FAT12/FAT16 root directory 629fed831fcSRob Clark * (contiguous) and subdirectories + FAT32 root (chained). 630fed831fcSRob Clark * 631fed831fcSRob Clark * Rough usage: 632fed831fcSRob Clark * 633fed831fcSRob Clark * for (fat_itr_root(&itr, fsdata); fat_itr_next(&itr); ) { 634fed831fcSRob Clark * // to traverse down to a subdirectory pointed to by 635fed831fcSRob Clark * // current iterator position: 636fed831fcSRob Clark * fat_itr_child(&itr, &itr); 637fed831fcSRob Clark * } 638fed831fcSRob Clark * 639fed831fcSRob Clark * For more complete example, see fat_itr_resolve() 640fed831fcSRob Clark */ 641fed831fcSRob Clark 642fed831fcSRob Clark typedef struct { 643fed831fcSRob Clark fsdata *fsdata; /* filesystem parameters */ 644fed831fcSRob Clark unsigned clust; /* current cluster */ 645fed831fcSRob Clark int last_cluster; /* set once we've read last cluster */ 646fed831fcSRob Clark int is_root; /* is iterator at root directory */ 647fed831fcSRob Clark int remaining; /* remaining dent's in current cluster */ 648fed831fcSRob Clark 649fed831fcSRob Clark /* current iterator position values: */ 650fed831fcSRob Clark dir_entry *dent; /* current directory entry */ 651fed831fcSRob Clark char l_name[VFAT_MAXLEN_BYTES]; /* long (vfat) name */ 652fed831fcSRob Clark char s_name[14]; /* short 8.3 name */ 653fed831fcSRob Clark char *name; /* l_name if there is one, else s_name */ 654fed831fcSRob Clark 655fed831fcSRob Clark /* storage for current cluster in memory: */ 656fed831fcSRob Clark u8 block[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN); 657fed831fcSRob Clark } fat_itr; 658fed831fcSRob Clark 659fed831fcSRob Clark static int fat_itr_isdir(fat_itr *itr); 660fed831fcSRob Clark 661fed831fcSRob Clark /** 662fed831fcSRob Clark * fat_itr_root() - initialize an iterator to start at the root 663fed831fcSRob Clark * directory 664fed831fcSRob Clark * 665fed831fcSRob Clark * @itr: iterator to initialize 666fed831fcSRob Clark * @fsdata: filesystem data for the partition 667fed831fcSRob Clark * @return 0 on success, else -errno 668fed831fcSRob Clark */ 669fed831fcSRob Clark static int fat_itr_root(fat_itr *itr, fsdata *fsdata) 670fed831fcSRob Clark { 671fed831fcSRob Clark if (get_fs_info(fsdata)) 672fed831fcSRob Clark return -ENXIO; 673fed831fcSRob Clark 674fed831fcSRob Clark itr->fsdata = fsdata; 675fed831fcSRob Clark itr->clust = fsdata->root_cluster; 676fed831fcSRob Clark itr->dent = NULL; 677fed831fcSRob Clark itr->remaining = 0; 678fed831fcSRob Clark itr->last_cluster = 0; 679fed831fcSRob Clark itr->is_root = 1; 680fed831fcSRob Clark 681fed831fcSRob Clark return 0; 682fed831fcSRob Clark } 683fed831fcSRob Clark 684fed831fcSRob Clark /** 685fed831fcSRob Clark * fat_itr_child() - initialize an iterator to descend into a sub- 686fed831fcSRob Clark * directory 687fed831fcSRob Clark * 688fed831fcSRob Clark * Initializes 'itr' to iterate the contents of the directory at 689fed831fcSRob Clark * the current cursor position of 'parent'. It is an error to 690fed831fcSRob Clark * call this if the current cursor of 'parent' is pointing at a 691fed831fcSRob Clark * regular file. 692fed831fcSRob Clark * 693fed831fcSRob Clark * Note that 'itr' and 'parent' can be the same pointer if you do 694fed831fcSRob Clark * not need to preserve 'parent' after this call, which is useful 695fed831fcSRob Clark * for traversing directory structure to resolve a file/directory. 696fed831fcSRob Clark * 697fed831fcSRob Clark * @itr: iterator to initialize 698fed831fcSRob Clark * @parent: the iterator pointing at a directory entry in the 699fed831fcSRob Clark * parent directory of the directory to iterate 700fed831fcSRob Clark */ 701fed831fcSRob Clark static void fat_itr_child(fat_itr *itr, fat_itr *parent) 702fed831fcSRob Clark { 703fed831fcSRob Clark fsdata *mydata = parent->fsdata; /* for silly macros */ 704fed831fcSRob Clark unsigned clustnum = START(parent->dent); 705fed831fcSRob Clark 706fed831fcSRob Clark assert(fat_itr_isdir(parent)); 707fed831fcSRob Clark 708fed831fcSRob Clark itr->fsdata = parent->fsdata; 709fed831fcSRob Clark if (clustnum > 0) { 710fed831fcSRob Clark itr->clust = clustnum; 711fed831fcSRob Clark } else { 712fed831fcSRob Clark itr->clust = parent->fsdata->root_cluster; 713fed831fcSRob Clark } 714fed831fcSRob Clark itr->dent = NULL; 715fed831fcSRob Clark itr->remaining = 0; 716fed831fcSRob Clark itr->last_cluster = 0; 717fed831fcSRob Clark itr->is_root = 0; 718fed831fcSRob Clark } 719fed831fcSRob Clark 720fed831fcSRob Clark static void *next_cluster(fat_itr *itr) 721fed831fcSRob Clark { 722fed831fcSRob Clark fsdata *mydata = itr->fsdata; /* for silly macros */ 723fed831fcSRob Clark int ret; 724fed831fcSRob Clark u32 sect; 725fed831fcSRob Clark 726fed831fcSRob Clark /* have we reached the end? */ 727fed831fcSRob Clark if (itr->last_cluster) 728fed831fcSRob Clark return NULL; 729fed831fcSRob Clark 730fed831fcSRob Clark sect = clust_to_sect(itr->fsdata, itr->clust); 731fed831fcSRob Clark 732fed831fcSRob Clark debug("FAT read(sect=%d), clust_size=%d, DIRENTSPERBLOCK=%zd\n", 733fed831fcSRob Clark sect, itr->fsdata->clust_size, DIRENTSPERBLOCK); 734fed831fcSRob Clark 735fed831fcSRob Clark /* 736fed831fcSRob Clark * NOTE: do_fat_read_at() had complicated logic to deal w/ 737fed831fcSRob Clark * vfat names that span multiple clusters in the fat16 case, 738fed831fcSRob Clark * which get_dentfromdir() probably also needed (and was 739fed831fcSRob Clark * missing). And not entirely sure what fat32 didn't have 740fed831fcSRob Clark * the same issue.. We solve that by only caring about one 741fed831fcSRob Clark * dent at a time and iteratively constructing the vfat long 742fed831fcSRob Clark * name. 743fed831fcSRob Clark */ 744fed831fcSRob Clark ret = disk_read(sect, itr->fsdata->clust_size, 745fed831fcSRob Clark itr->block); 746fed831fcSRob Clark if (ret < 0) { 747fed831fcSRob Clark debug("Error: reading block\n"); 748fed831fcSRob Clark return NULL; 749fed831fcSRob Clark } 750fed831fcSRob Clark 751fed831fcSRob Clark if (itr->is_root && itr->fsdata->fatsize != 32) { 752fed831fcSRob Clark itr->clust++; 753fed831fcSRob Clark sect = clust_to_sect(itr->fsdata, itr->clust); 754fed831fcSRob Clark if (sect - itr->fsdata->rootdir_sect >= 755fed831fcSRob Clark itr->fsdata->rootdir_size) { 756fed831fcSRob Clark debug("cursect: 0x%x\n", itr->clust); 757fed831fcSRob Clark itr->last_cluster = 1; 758fed831fcSRob Clark } 759fed831fcSRob Clark } else { 760fed831fcSRob Clark itr->clust = get_fatent(itr->fsdata, itr->clust); 761fed831fcSRob Clark if (CHECK_CLUST(itr->clust, itr->fsdata->fatsize)) { 762fed831fcSRob Clark debug("cursect: 0x%x\n", itr->clust); 763fed831fcSRob Clark itr->last_cluster = 1; 764fed831fcSRob Clark } 765fed831fcSRob Clark } 766fed831fcSRob Clark 767fed831fcSRob Clark return itr->block; 768fed831fcSRob Clark } 769fed831fcSRob Clark 770fed831fcSRob Clark static dir_entry *next_dent(fat_itr *itr) 771fed831fcSRob Clark { 772fed831fcSRob Clark if (itr->remaining == 0) { 773fed831fcSRob Clark struct dir_entry *dent = next_cluster(itr); 774fed831fcSRob Clark unsigned nbytes = itr->fsdata->sect_size * 775fed831fcSRob Clark itr->fsdata->clust_size; 776fed831fcSRob Clark 777fed831fcSRob Clark /* have we reached the last cluster? */ 778fed831fcSRob Clark if (!dent) 779fed831fcSRob Clark return NULL; 780fed831fcSRob Clark 781fed831fcSRob Clark itr->remaining = nbytes / sizeof(dir_entry) - 1; 782fed831fcSRob Clark itr->dent = dent; 783fed831fcSRob Clark } else { 784fed831fcSRob Clark itr->remaining--; 785fed831fcSRob Clark itr->dent++; 786fed831fcSRob Clark } 787fed831fcSRob Clark 788fed831fcSRob Clark /* have we reached the last valid entry? */ 789fed831fcSRob Clark if (itr->dent->name[0] == 0) 790fed831fcSRob Clark return NULL; 791fed831fcSRob Clark 792fed831fcSRob Clark return itr->dent; 793fed831fcSRob Clark } 794fed831fcSRob Clark 795fed831fcSRob Clark static dir_entry *extract_vfat_name(fat_itr *itr) 796fed831fcSRob Clark { 797fed831fcSRob Clark struct dir_entry *dent = itr->dent; 798fed831fcSRob Clark int seqn = itr->dent->name[0] & ~LAST_LONG_ENTRY_MASK; 799fed831fcSRob Clark u8 chksum, alias_checksum = ((dir_slot *)dent)->alias_checksum; 800fed831fcSRob Clark int n = 0; 801fed831fcSRob Clark 802fed831fcSRob Clark while (seqn--) { 803fed831fcSRob Clark char buf[13]; 804fed831fcSRob Clark int idx = 0; 805fed831fcSRob Clark 806fed831fcSRob Clark slot2str((dir_slot *)dent, buf, &idx); 807fed831fcSRob Clark 808fed831fcSRob Clark /* shift accumulated long-name up and copy new part in: */ 809fed831fcSRob Clark memmove(itr->l_name + idx, itr->l_name, n); 810fed831fcSRob Clark memcpy(itr->l_name, buf, idx); 811fed831fcSRob Clark n += idx; 812fed831fcSRob Clark 813fed831fcSRob Clark dent = next_dent(itr); 814fed831fcSRob Clark if (!dent) 815fed831fcSRob Clark return NULL; 816fed831fcSRob Clark } 817fed831fcSRob Clark 818fed831fcSRob Clark itr->l_name[n] = '\0'; 819fed831fcSRob Clark 820fed831fcSRob Clark chksum = mkcksum(dent->name, dent->ext); 821fed831fcSRob Clark 822fed831fcSRob Clark /* checksum mismatch could mean deleted file, etc.. skip it: */ 823fed831fcSRob Clark if (chksum != alias_checksum) { 824fed831fcSRob Clark debug("** chksum=%x, alias_checksum=%x, l_name=%s, s_name=%8s.%3s\n", 825fed831fcSRob Clark chksum, alias_checksum, itr->l_name, dent->name, dent->ext); 826fed831fcSRob Clark return NULL; 827fed831fcSRob Clark } 828fed831fcSRob Clark 829fed831fcSRob Clark return dent; 830fed831fcSRob Clark } 831fed831fcSRob Clark 832fed831fcSRob Clark /** 833fed831fcSRob Clark * fat_itr_next() - step to the next entry in a directory 834fed831fcSRob Clark * 835fed831fcSRob Clark * Must be called once on a new iterator before the cursor is valid. 836fed831fcSRob Clark * 837fed831fcSRob Clark * @itr: the iterator to iterate 838fed831fcSRob Clark * @return boolean, 1 if success or 0 if no more entries in the 839fed831fcSRob Clark * current directory 840fed831fcSRob Clark */ 841fed831fcSRob Clark static int fat_itr_next(fat_itr *itr) 842fed831fcSRob Clark { 843fed831fcSRob Clark dir_entry *dent; 844fed831fcSRob Clark 845fed831fcSRob Clark itr->name = NULL; 846fed831fcSRob Clark 847fed831fcSRob Clark while (1) { 848fed831fcSRob Clark dent = next_dent(itr); 849fed831fcSRob Clark if (!dent) 850fed831fcSRob Clark return 0; 851fed831fcSRob Clark 852fed831fcSRob Clark if (dent->name[0] == DELETED_FLAG || 853fed831fcSRob Clark dent->name[0] == aRING) 854fed831fcSRob Clark continue; 855fed831fcSRob Clark 856fed831fcSRob Clark if (dent->attr & ATTR_VOLUME) { 857fed831fcSRob Clark if (vfat_enabled && 858fed831fcSRob Clark (dent->attr & ATTR_VFAT) == ATTR_VFAT && 859fed831fcSRob Clark (dent->name[0] & LAST_LONG_ENTRY_MASK)) { 860fed831fcSRob Clark dent = extract_vfat_name(itr); 861fed831fcSRob Clark if (!dent) 862fed831fcSRob Clark continue; 863fed831fcSRob Clark itr->name = itr->l_name; 864fed831fcSRob Clark break; 865fed831fcSRob Clark } else { 866fed831fcSRob Clark /* Volume label or VFAT entry, skip */ 867fed831fcSRob Clark continue; 868fed831fcSRob Clark } 869fed831fcSRob Clark } 870fed831fcSRob Clark 871fed831fcSRob Clark break; 872fed831fcSRob Clark } 873fed831fcSRob Clark 874fed831fcSRob Clark get_name(dent, itr->s_name); 875fed831fcSRob Clark if (!itr->name) 876fed831fcSRob Clark itr->name = itr->s_name; 877fed831fcSRob Clark 878fed831fcSRob Clark return 1; 879fed831fcSRob Clark } 880fed831fcSRob Clark 881fed831fcSRob Clark /** 882fed831fcSRob Clark * fat_itr_isdir() - is current cursor position pointing to a directory 883fed831fcSRob Clark * 884fed831fcSRob Clark * @itr: the iterator 885fed831fcSRob Clark * @return true if cursor is at a directory 886fed831fcSRob Clark */ 887fed831fcSRob Clark static int fat_itr_isdir(fat_itr *itr) 888fed831fcSRob Clark { 889fed831fcSRob Clark return !!(itr->dent->attr & ATTR_DIR); 890fed831fcSRob Clark } 891fed831fcSRob Clark 892fed831fcSRob Clark /* 893fed831fcSRob Clark * Helpers: 894fed831fcSRob Clark */ 895fed831fcSRob Clark 896fed831fcSRob Clark #define TYPE_FILE 0x1 897fed831fcSRob Clark #define TYPE_DIR 0x2 898fed831fcSRob Clark #define TYPE_ANY (TYPE_FILE | TYPE_DIR) 899fed831fcSRob Clark 900fed831fcSRob Clark /** 901fed831fcSRob Clark * fat_itr_resolve() - traverse directory structure to resolve the 902fed831fcSRob Clark * requested path. 903fed831fcSRob Clark * 904fed831fcSRob Clark * Traverse directory structure to the requested path. If the specified 905fed831fcSRob Clark * path is to a directory, this will descend into the directory and 906fed831fcSRob Clark * leave it iterator at the start of the directory. If the path is to a 907fed831fcSRob Clark * file, it will leave the iterator in the parent directory with current 908fed831fcSRob Clark * cursor at file's entry in the directory. 909fed831fcSRob Clark * 910fed831fcSRob Clark * @itr: iterator initialized to root 911fed831fcSRob Clark * @path: the requested path 912fed831fcSRob Clark * @type: bitmask of allowable file types 913fed831fcSRob Clark * @return 0 on success or -errno 914fed831fcSRob Clark */ 915fed831fcSRob Clark static int fat_itr_resolve(fat_itr *itr, const char *path, unsigned type) 916fed831fcSRob Clark { 917fed831fcSRob Clark const char *next; 918fed831fcSRob Clark 919fed831fcSRob Clark /* chomp any extra leading slashes: */ 920fed831fcSRob Clark while (path[0] && ISDIRDELIM(path[0])) 921fed831fcSRob Clark path++; 922fed831fcSRob Clark 923fed831fcSRob Clark /* are we at the end? */ 924fed831fcSRob Clark if (strlen(path) == 0) { 925fed831fcSRob Clark if (!(type & TYPE_DIR)) 926fed831fcSRob Clark return -ENOENT; 927fed831fcSRob Clark return 0; 928fed831fcSRob Clark } 929fed831fcSRob Clark 930fed831fcSRob Clark /* find length of next path entry: */ 931fed831fcSRob Clark next = path; 932fed831fcSRob Clark while (next[0] && !ISDIRDELIM(next[0])) 933fed831fcSRob Clark next++; 934fed831fcSRob Clark 935fed831fcSRob Clark while (fat_itr_next(itr)) { 936fed831fcSRob Clark int match = 0; 937fed831fcSRob Clark unsigned n = max(strlen(itr->name), (size_t)(next - path)); 938fed831fcSRob Clark 939fed831fcSRob Clark /* check both long and short name: */ 940fed831fcSRob Clark if (!strncasecmp(path, itr->name, n)) 941fed831fcSRob Clark match = 1; 942fed831fcSRob Clark else if (itr->name != itr->s_name && 943fed831fcSRob Clark !strncasecmp(path, itr->s_name, n)) 944fed831fcSRob Clark match = 1; 945fed831fcSRob Clark 946fed831fcSRob Clark if (!match) 947fed831fcSRob Clark continue; 948fed831fcSRob Clark 949fed831fcSRob Clark if (fat_itr_isdir(itr)) { 950fed831fcSRob Clark /* recurse into directory: */ 951fed831fcSRob Clark fat_itr_child(itr, itr); 952fed831fcSRob Clark return fat_itr_resolve(itr, next, type); 953fed831fcSRob Clark } else if (next[0]) { 954fed831fcSRob Clark /* 955fed831fcSRob Clark * If next is not empty then we have a case 956fed831fcSRob Clark * like: /path/to/realfile/nonsense 957fed831fcSRob Clark */ 958fed831fcSRob Clark debug("bad trailing path: %s\n", next); 959fed831fcSRob Clark return -ENOENT; 960fed831fcSRob Clark } else if (!(type & TYPE_FILE)) { 961fed831fcSRob Clark return -ENOTDIR; 962fed831fcSRob Clark } else { 963fed831fcSRob Clark return 0; 964fed831fcSRob Clark } 965fed831fcSRob Clark } 966fed831fcSRob Clark 967fed831fcSRob Clark return -ENOENT; 968fed831fcSRob Clark } 969fed831fcSRob Clark 9707385c28eSWolfgang Denk int file_fat_detectfs(void) 97171f95118Swdenk { 97271f95118Swdenk boot_sector bs; 97371f95118Swdenk volume_info volinfo; 97471f95118Swdenk int fatsize; 9757205e407Swdenk char vol_label[12]; 97671f95118Swdenk 9777205e407Swdenk if (cur_dev == NULL) { 9787205e407Swdenk printf("No current device\n"); 9797205e407Swdenk return 1; 9807205e407Swdenk } 9817385c28eSWolfgang Denk 982fc843a02SSimon Glass #if defined(CONFIG_IDE) || \ 98310e40d54SSimon Glass defined(CONFIG_SATA) || \ 984c649e3c9SSimon Glass defined(CONFIG_SCSI) || \ 985dd60d122SJon Loeliger defined(CONFIG_CMD_USB) || \ 98621f6f963SAndy Fleming defined(CONFIG_MMC) 9877205e407Swdenk printf("Interface: "); 9887205e407Swdenk switch (cur_dev->if_type) { 9897385c28eSWolfgang Denk case IF_TYPE_IDE: 9907385c28eSWolfgang Denk printf("IDE"); 9917385c28eSWolfgang Denk break; 9927385c28eSWolfgang Denk case IF_TYPE_SATA: 9937385c28eSWolfgang Denk printf("SATA"); 9947385c28eSWolfgang Denk break; 9957385c28eSWolfgang Denk case IF_TYPE_SCSI: 9967385c28eSWolfgang Denk printf("SCSI"); 9977385c28eSWolfgang Denk break; 9987385c28eSWolfgang Denk case IF_TYPE_ATAPI: 9997385c28eSWolfgang Denk printf("ATAPI"); 10007385c28eSWolfgang Denk break; 10017385c28eSWolfgang Denk case IF_TYPE_USB: 10027385c28eSWolfgang Denk printf("USB"); 10037385c28eSWolfgang Denk break; 10047385c28eSWolfgang Denk case IF_TYPE_DOC: 10057385c28eSWolfgang Denk printf("DOC"); 10067385c28eSWolfgang Denk break; 10077385c28eSWolfgang Denk case IF_TYPE_MMC: 10087385c28eSWolfgang Denk printf("MMC"); 10097385c28eSWolfgang Denk break; 10107385c28eSWolfgang Denk default: 10117385c28eSWolfgang Denk printf("Unknown"); 10127205e407Swdenk } 10137385c28eSWolfgang Denk 1014bcce53d0SSimon Glass printf("\n Device %d: ", cur_dev->devnum); 10157205e407Swdenk dev_print(cur_dev); 10167205e407Swdenk #endif 10177385c28eSWolfgang Denk 10187205e407Swdenk if (read_bootsectandvi(&bs, &volinfo, &fatsize)) { 10197205e407Swdenk printf("\nNo valid FAT fs found\n"); 10207205e407Swdenk return 1; 10217205e407Swdenk } 10227385c28eSWolfgang Denk 10237205e407Swdenk memcpy(vol_label, volinfo.volume_label, 11); 10247205e407Swdenk vol_label[11] = '\0'; 10257205e407Swdenk volinfo.fs_type[5] = '\0'; 10267385c28eSWolfgang Denk 1027461f86e6SStephen Warren printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label); 10287385c28eSWolfgang Denk 10297205e407Swdenk return 0; 103071f95118Swdenk } 103171f95118Swdenk 10327385c28eSWolfgang Denk int file_fat_ls(const char *dir) 103371f95118Swdenk { 1034759bf728SRob Clark fsdata fsdata; 1035759bf728SRob Clark fat_itr itrblock, *itr = &itrblock; 1036759bf728SRob Clark int files = 0, dirs = 0; 1037759bf728SRob Clark int ret; 10381ad0b98aSSuriyan Ramasami 1039759bf728SRob Clark ret = fat_itr_root(itr, &fsdata); 1040759bf728SRob Clark if (ret) 1041759bf728SRob Clark return ret; 1042759bf728SRob Clark 1043759bf728SRob Clark ret = fat_itr_resolve(itr, dir, TYPE_DIR); 1044759bf728SRob Clark if (ret) 1045759bf728SRob Clark return ret; 1046759bf728SRob Clark 1047759bf728SRob Clark while (fat_itr_next(itr)) { 1048759bf728SRob Clark if (fat_itr_isdir(itr)) { 1049759bf728SRob Clark printf(" %s/\n", itr->name); 1050759bf728SRob Clark dirs++; 1051759bf728SRob Clark } else { 1052759bf728SRob Clark printf(" %8u %s\n", 1053759bf728SRob Clark FAT2CPU32(itr->dent->size), 1054759bf728SRob Clark itr->name); 1055759bf728SRob Clark files++; 1056759bf728SRob Clark } 1057759bf728SRob Clark } 1058759bf728SRob Clark 1059759bf728SRob Clark printf("\n%d file(s), %d dir(s)\n\n", files, dirs); 1060759bf728SRob Clark 1061759bf728SRob Clark return 0; 106271f95118Swdenk } 106371f95118Swdenk 1064b7b5f319SStephen Warren int fat_exists(const char *filename) 1065b7b5f319SStephen Warren { 1066759bf728SRob Clark fsdata fsdata; 1067759bf728SRob Clark fat_itr itrblock, *itr = &itrblock; 10681ad0b98aSSuriyan Ramasami int ret; 10691ad0b98aSSuriyan Ramasami 1070759bf728SRob Clark ret = fat_itr_root(itr, &fsdata); 1071759bf728SRob Clark if (ret) 1072759bf728SRob Clark return 0; 1073759bf728SRob Clark 1074759bf728SRob Clark ret = fat_itr_resolve(itr, filename, TYPE_ANY); 10751ad0b98aSSuriyan Ramasami return ret == 0; 1076b7b5f319SStephen Warren } 1077b7b5f319SStephen Warren 1078d455d878SSuriyan Ramasami int fat_size(const char *filename, loff_t *size) 1079cf659819SStephen Warren { 1080759bf728SRob Clark fsdata fsdata; 1081759bf728SRob Clark fat_itr itrblock, *itr = &itrblock; 1082759bf728SRob Clark int ret; 1083759bf728SRob Clark 1084759bf728SRob Clark ret = fat_itr_root(itr, &fsdata); 1085759bf728SRob Clark if (ret) 1086759bf728SRob Clark return ret; 1087759bf728SRob Clark 1088759bf728SRob Clark ret = fat_itr_resolve(itr, filename, TYPE_FILE); 1089759bf728SRob Clark if (ret) { 1090759bf728SRob Clark /* 1091759bf728SRob Clark * Directories don't have size, but fs_size() is not 1092759bf728SRob Clark * expected to fail if passed a directory path: 1093759bf728SRob Clark */ 1094759bf728SRob Clark fat_itr_root(itr, &fsdata); 1095759bf728SRob Clark if (!fat_itr_resolve(itr, filename, TYPE_DIR)) { 1096759bf728SRob Clark *size = 0; 1097759bf728SRob Clark return 0; 1098759bf728SRob Clark } 1099759bf728SRob Clark return ret; 1100759bf728SRob Clark } 1101759bf728SRob Clark 1102759bf728SRob Clark *size = FAT2CPU32(itr->dent->size); 1103759bf728SRob Clark 1104759bf728SRob Clark return 0; 1105cf659819SStephen Warren } 1106cf659819SStephen Warren 11071ad0b98aSSuriyan Ramasami int file_fat_read_at(const char *filename, loff_t pos, void *buffer, 11081ad0b98aSSuriyan Ramasami loff_t maxsize, loff_t *actread) 110971f95118Swdenk { 1110759bf728SRob Clark fsdata fsdata; 1111759bf728SRob Clark fat_itr itrblock, *itr = &itrblock; 1112759bf728SRob Clark int ret; 1113759bf728SRob Clark 1114759bf728SRob Clark ret = fat_itr_root(itr, &fsdata); 1115759bf728SRob Clark if (ret) 1116759bf728SRob Clark return ret; 1117759bf728SRob Clark 1118759bf728SRob Clark ret = fat_itr_resolve(itr, filename, TYPE_FILE); 1119759bf728SRob Clark if (ret) 1120759bf728SRob Clark return ret; 1121759bf728SRob Clark 11227205e407Swdenk printf("reading %s\n", filename); 1123759bf728SRob Clark return get_contents(&fsdata, itr->dent, pos, buffer, maxsize, actread); 11241170e634SBenoît Thébaudeau } 11251170e634SBenoît Thébaudeau 11261ad0b98aSSuriyan Ramasami int file_fat_read(const char *filename, void *buffer, int maxsize) 11271170e634SBenoît Thébaudeau { 11281ad0b98aSSuriyan Ramasami loff_t actread; 11291ad0b98aSSuriyan Ramasami int ret; 11301ad0b98aSSuriyan Ramasami 11311ad0b98aSSuriyan Ramasami ret = file_fat_read_at(filename, 0, buffer, maxsize, &actread); 11321ad0b98aSSuriyan Ramasami if (ret) 11331ad0b98aSSuriyan Ramasami return ret; 11341ad0b98aSSuriyan Ramasami else 11351ad0b98aSSuriyan Ramasami return actread; 113671f95118Swdenk } 1137e6d52415SSimon Glass 1138d455d878SSuriyan Ramasami int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len, 1139d455d878SSuriyan Ramasami loff_t *actread) 1140e6d52415SSimon Glass { 11411ad0b98aSSuriyan Ramasami int ret; 1142e6d52415SSimon Glass 1143d455d878SSuriyan Ramasami ret = file_fat_read_at(filename, offset, buf, len, actread); 1144d455d878SSuriyan Ramasami if (ret) 1145e6d52415SSimon Glass printf("** Unable to read file %s **\n", filename); 1146e6d52415SSimon Glass 1147d455d878SSuriyan Ramasami return ret; 1148e6d52415SSimon Glass } 1149e6d52415SSimon Glass 1150*60352d22SRob Clark typedef struct { 1151*60352d22SRob Clark struct fs_dir_stream parent; 1152*60352d22SRob Clark struct fs_dirent dirent; 1153*60352d22SRob Clark fsdata fsdata; 1154*60352d22SRob Clark fat_itr itr; 1155*60352d22SRob Clark } fat_dir; 1156*60352d22SRob Clark 1157*60352d22SRob Clark int fat_opendir(const char *filename, struct fs_dir_stream **dirsp) 1158*60352d22SRob Clark { 1159*60352d22SRob Clark fat_dir *dir = malloc(sizeof(*dir)); 1160*60352d22SRob Clark int ret; 1161*60352d22SRob Clark 1162*60352d22SRob Clark if (!dir) 1163*60352d22SRob Clark return -ENOMEM; 1164*60352d22SRob Clark 1165*60352d22SRob Clark ret = fat_itr_root(&dir->itr, &dir->fsdata); 1166*60352d22SRob Clark if (ret) 1167*60352d22SRob Clark goto fail; 1168*60352d22SRob Clark 1169*60352d22SRob Clark ret = fat_itr_resolve(&dir->itr, filename, TYPE_DIR); 1170*60352d22SRob Clark if (ret) 1171*60352d22SRob Clark goto fail; 1172*60352d22SRob Clark 1173*60352d22SRob Clark *dirsp = (struct fs_dir_stream *)dir; 1174*60352d22SRob Clark return 0; 1175*60352d22SRob Clark 1176*60352d22SRob Clark fail: 1177*60352d22SRob Clark free(dir); 1178*60352d22SRob Clark return ret; 1179*60352d22SRob Clark } 1180*60352d22SRob Clark 1181*60352d22SRob Clark int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp) 1182*60352d22SRob Clark { 1183*60352d22SRob Clark fat_dir *dir = (fat_dir *)dirs; 1184*60352d22SRob Clark struct fs_dirent *dent = &dir->dirent; 1185*60352d22SRob Clark 1186*60352d22SRob Clark if (!fat_itr_next(&dir->itr)) 1187*60352d22SRob Clark return -ENOENT; 1188*60352d22SRob Clark 1189*60352d22SRob Clark memset(dent, 0, sizeof(*dent)); 1190*60352d22SRob Clark strcpy(dent->name, dir->itr.name); 1191*60352d22SRob Clark 1192*60352d22SRob Clark if (fat_itr_isdir(&dir->itr)) { 1193*60352d22SRob Clark dent->type = FS_DT_DIR; 1194*60352d22SRob Clark } else { 1195*60352d22SRob Clark dent->type = FS_DT_REG; 1196*60352d22SRob Clark dent->size = FAT2CPU32(dir->itr.dent->size); 1197*60352d22SRob Clark } 1198*60352d22SRob Clark 1199*60352d22SRob Clark *dentp = dent; 1200*60352d22SRob Clark 1201*60352d22SRob Clark return 0; 1202*60352d22SRob Clark } 1203*60352d22SRob Clark 1204*60352d22SRob Clark void fat_closedir(struct fs_dir_stream *dirs) 1205*60352d22SRob Clark { 1206*60352d22SRob Clark fat_dir *dir = (fat_dir *)dirs; 1207*60352d22SRob Clark free(dir); 1208*60352d22SRob Clark } 1209*60352d22SRob Clark 1210e6d52415SSimon Glass void fat_close(void) 1211e6d52415SSimon Glass { 1212e6d52415SSimon Glass } 1213