X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=fs%2Ffat%2Ffat.c;h=465a6875edf509a17054adeefa6af73985543bdc;hb=8197d92843952b376915fdbcbf67c723feab1532;hp=561921fa2d364e548629fe8152cb953861699b3f;hpb=5a1095a830299aef8dd32495e505e92ab1749e89;p=oweals%2Fu-boot.git diff --git a/fs/fat/fat.c b/fs/fat/fat.c index 561921fa2d..465a6875ed 100644 --- a/fs/fat/fat.c +++ b/fs/fat/fat.c @@ -10,12 +10,14 @@ */ #include +#include #include #include #include #include #include #include +#include #include #include @@ -36,7 +38,7 @@ static void downcase(char *str) } } -static block_dev_desc_t *cur_dev; +static struct blk_desc *cur_dev; static disk_partition_t cur_part_info; #define DOS_BOOT_MAGIC_OFFSET 0x1fe @@ -45,14 +47,20 @@ static disk_partition_t cur_part_info; static int disk_read(__u32 block, __u32 nr_blocks, void *buf) { - if (!cur_dev || !cur_dev->block_read) + ulong ret; + + if (!cur_dev) + return -1; + + ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf); + + if (ret != nr_blocks) return -1; - return cur_dev->block_read(cur_dev->dev, - cur_part_info.start + block, nr_blocks, buf); + return ret; } -int fat_set_blk_dev(block_dev_desc_t *dev_desc, disk_partition_t *info) +int fat_set_blk_dev(struct blk_desc *dev_desc, disk_partition_t *info) { ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz); @@ -81,7 +89,7 @@ int fat_set_blk_dev(block_dev_desc_t *dev_desc, disk_partition_t *info) return -1; } -int fat_register_device(block_dev_desc_t *dev_desc, int part_no) +int fat_register_device(struct blk_desc *dev_desc, int part_no) { disk_partition_t info; @@ -89,10 +97,10 @@ int fat_register_device(block_dev_desc_t *dev_desc, int part_no) cur_dev = NULL; /* Read the partition table, if present */ - if (get_partition_info(dev_desc, part_no, &info)) { + if (part_get_info(dev_desc, part_no, &info)) { if (part_no != 0) { printf("** Partition %d not valid on device %d **\n", - part_no, dev_desc->dev); + part_no, dev_desc->devnum); return -1; } @@ -102,7 +110,7 @@ int fat_register_device(block_dev_desc_t *dev_desc, int part_no) info.name[0] = 0; info.type[0] = 0; info.bootable = 0; -#ifdef CONFIG_PARTITION_UUIDS +#if CONFIG_IS_ENABLED(PARTITION_UUIDS) info.uuid[0] = 0; #endif } @@ -154,6 +162,16 @@ static void get_name(dir_entry *dirent, char *s_name) downcase(s_name); } +static int flush_dirty_fat_buffer(fsdata *mydata); +#if !defined(CONFIG_FAT_WRITE) +/* Stub for read only operation */ +int flush_dirty_fat_buffer(fsdata *mydata) +{ + (void)(mydata); + return 0; +} +#endif + /* * Get the entry at index 'entry' in a FAT (12/16/32) table. * On failure 0x00 is returned. @@ -161,9 +179,13 @@ static void get_name(dir_entry *dirent, char *s_name) static __u32 get_fatent(fsdata *mydata, __u32 entry) { __u32 bufnum; - __u32 off16, offset; + __u32 offset, off8; __u32 ret = 0x00; - __u16 val1, val2; + + if (CHECK_CLUST(entry, mydata->fatsize)) { + printf("Error: Invalid FAT entry: 0x%08x\n", entry); + return ret; + } switch (mydata->fatsize) { case 32: @@ -184,7 +206,7 @@ static __u32 get_fatent(fsdata *mydata, __u32 entry) return ret; } - debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n", + debug("FAT%d: entry: 0x%08x = %d, offset: 0x%04x = %d\n", mydata->fatsize, entry, entry, offset, offset); /* Read a new block of FAT entries into the cache. */ @@ -194,11 +216,16 @@ static __u32 get_fatent(fsdata *mydata, __u32 entry) __u32 fatlength = mydata->fatlength; __u32 startblock = bufnum * FATBUFBLOCKS; + /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */ if (startblock + getsize > fatlength) getsize = fatlength - startblock; startblock += mydata->fat_sect; /* Offset from start of disk */ + /* Write back the fatbuf to the disk */ + if (flush_dirty_fat_buffer(mydata) < 0) + return -1; + if (disk_read(startblock, getsize, bufptr) < 0) { debug("Error reading FAT blocks\n"); return ret; @@ -215,38 +242,16 @@ static __u32 get_fatent(fsdata *mydata, __u32 entry) ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]); break; case 12: - off16 = (offset * 3) / 4; + off8 = (offset * 3) / 2; + /* fatbut + off8 may be unaligned, read in byte granularity */ + ret = mydata->fatbuf[off8] + (mydata->fatbuf[off8 + 1] << 8); - switch (offset & 0x3) { - case 0: - ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]); - ret &= 0xfff; - break; - case 1: - val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); - val1 &= 0xf000; - val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); - val2 &= 0x00ff; - ret = (val2 << 4) | (val1 >> 12); - break; - case 2: - val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); - val1 &= 0xff00; - val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]); - val2 &= 0x000f; - ret = (val2 << 8) | (val1 >> 8); - break; - case 3: - ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]); - ret = (ret & 0xfff0) >> 4; - break; - default: - break; - } - break; + if (offset & 0x1) + ret >>= 4; + ret &= 0xfff; } - debug("FAT%d: ret: %08x, offset: %04x\n", - mydata->fatsize, ret, offset); + debug("FAT%d: ret: 0x%08x, entry: 0x%08x, offset: 0x%04x\n", + mydata->fatsize, ret, entry, offset); return ret; } @@ -317,32 +322,32 @@ get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size) /* * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr' * into 'buffer'. - * Return the number of bytes read or -1 on fatal errors. + * Update the number of bytes read in *gotsize or return -1 on fatal errors. */ __u8 get_contents_vfatname_block[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN); -static long -get_contents(fsdata *mydata, dir_entry *dentptr, unsigned long pos, - __u8 *buffer, unsigned long maxsize) +static int get_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, + __u8 *buffer, loff_t maxsize, loff_t *gotsize) { - unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0; + loff_t filesize = FAT2CPU32(dentptr->size); unsigned int bytesperclust = mydata->clust_size * mydata->sect_size; __u32 curclust = START(dentptr); __u32 endclust, newclust; - unsigned long actsize; + loff_t actsize; - debug("Filesize: %ld bytes\n", filesize); + *gotsize = 0; + debug("Filesize: %llu bytes\n", filesize); if (pos >= filesize) { - debug("Read position past EOF: %lu\n", pos); - return gotsize; + debug("Read position past EOF: %llu\n", pos); + return 0; } if (maxsize > 0 && filesize > pos + maxsize) filesize = pos + maxsize; - debug("%ld bytes\n", filesize); + debug("%llu bytes\n", filesize); actsize = bytesperclust; @@ -352,7 +357,7 @@ get_contents(fsdata *mydata, dir_entry *dentptr, unsigned long pos, if (CHECK_CLUST(curclust, mydata->fatsize)) { debug("curclust: 0x%x\n", curclust); debug("Invalid FAT entry\n"); - return gotsize; + return 0; } actsize += bytesperclust; } @@ -364,7 +369,7 @@ get_contents(fsdata *mydata, dir_entry *dentptr, unsigned long pos, /* align to beginning of next cluster if any */ if (pos) { - actsize = min(filesize, bytesperclust); + actsize = min(filesize, (loff_t)bytesperclust); if (get_cluster(mydata, curclust, get_contents_vfatname_block, (int)actsize) != 0) { printf("Error reading cluster\n"); @@ -373,16 +378,16 @@ get_contents(fsdata *mydata, dir_entry *dentptr, unsigned long pos, filesize -= actsize; actsize -= pos; memcpy(buffer, get_contents_vfatname_block + pos, actsize); - gotsize += actsize; + *gotsize += actsize; if (!filesize) - return gotsize; + return 0; buffer += actsize; curclust = get_fatent(mydata, curclust); if (CHECK_CLUST(curclust, mydata->fatsize)) { debug("curclust: 0x%x\n", curclust); debug("Invalid FAT entry\n"); - return gotsize; + return 0; } } @@ -398,7 +403,7 @@ get_contents(fsdata *mydata, dir_entry *dentptr, unsigned long pos, if (CHECK_CLUST(newclust, mydata->fatsize)) { debug("curclust: 0x%x\n", newclust); debug("Invalid FAT entry\n"); - return gotsize; + return 0; } endclust = newclust; actsize += bytesperclust; @@ -410,14 +415,14 @@ get_contents(fsdata *mydata, dir_entry *dentptr, unsigned long pos, printf("Error reading cluster\n"); return -1; } - gotsize += actsize; - return gotsize; + *gotsize += actsize; + return 0; getit: if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { printf("Error reading cluster\n"); return -1; } - gotsize += (int)actsize; + *gotsize += (int)actsize; filesize -= actsize; buffer += actsize; @@ -425,7 +430,7 @@ getit: if (CHECK_CLUST(curclust, mydata->fatsize)) { debug("curclust: 0x%x\n", curclust); printf("Invalid FAT entry\n"); - return gotsize; + return 0; } actsize = bytesperclust; endclust = curclust; @@ -633,8 +638,8 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect, } if (doit) { if (dirc == ' ') { - printf(" %8ld %s%c\n", - (long)FAT2CPU32(dentptr->size), + printf(" %8u %s%c\n", + FAT2CPU32(dentptr->size), l_name, dirc); } else { @@ -690,8 +695,8 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect, if (doit) { if (dirc == ' ') { - printf(" %8ld %s%c\n", - (long)FAT2CPU32(dentptr->size), + printf(" %8u %s%c\n", + FAT2CPU32(dentptr->size), s_name, dirc); } else { printf(" %s%c\n", @@ -806,9 +811,8 @@ exit: __u8 do_fat_read_at_block[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN); -long -do_fat_read_at(const char *filename, unsigned long pos, void *buffer, - unsigned long maxsize, int dols, int dogetsize) +int do_fat_read_at(const char *filename, loff_t pos, void *buffer, + loff_t maxsize, int dols, int dogetsize, loff_t *size) { char fnamecopy[2048]; boot_sector bs; @@ -821,11 +825,14 @@ do_fat_read_at(const char *filename, unsigned long pos, void *buffer, __u32 cursect; int idx, isdir = 0; int files = 0, dirs = 0; - long ret = -1; + int ret = -1; int firsttime; __u32 root_cluster = 0; + __u32 read_blk; int rootdir_size = 0; - int j; + int buffer_blk_cnt; + int do_read; + __u8 *dir_ptr; if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) { debug("Error: reading boot sector\n"); @@ -866,6 +873,7 @@ do_fat_read_at(const char *filename, unsigned long pos, void *buffer, } mydata->fatbufnum = -1; + mydata->fat_dirty = 0; mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE); if (mydata->fatbuf == NULL) { debug("Error: allocating memory\n"); @@ -893,6 +901,7 @@ do_fat_read_at(const char *filename, unsigned long pos, void *buffer, strcpy(fnamecopy, filename); downcase(fnamecopy); +root_reparse: if (*fnamecopy == '\0') { if (!dols) goto exit; @@ -910,24 +919,54 @@ do_fat_read_at(const char *filename, unsigned long pos, void *buffer, isdir = 1; } - j = 0; + buffer_blk_cnt = 0; + firsttime = 1; while (1) { int i; - if (j == 0) { - debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n", - cursect, mydata->clust_size, DIRENTSPERBLOCK); + if (mydata->fatsize == 32 || firsttime) { + dir_ptr = do_fat_read_at_block; + firsttime = 0; + } else { + /** + * FAT16 sector buffer modification: + * Each loop, the second buffered block is moved to + * the buffer begin, and two next sectors are read + * next to the previously moved one. So the sector + * buffer keeps always 3 sectors for fat16. + * And the current sector is the buffer second sector + * beside the "firsttime" read, when it is the first one. + * + * PREFETCH_BLOCKS is 2 for FAT16 == loop[0:1] + * n = computed root dir sector + * loop | cursect-1 | cursect | cursect+1 | + * 0 | sector n+0 | sector n+1 | none | + * 1 | none | sector n+0 | sector n+1 | + * 0 | sector n+1 | sector n+2 | sector n+3 | + * 1 | sector n+3 | ... + */ + dir_ptr = (do_fat_read_at_block + mydata->sect_size); + memcpy(do_fat_read_at_block, dir_ptr, mydata->sect_size); + } + + do_read = 1; + + if (mydata->fatsize == 32 && buffer_blk_cnt) + do_read = 0; + + if (do_read) { + read_blk = (mydata->fatsize == 32) ? + mydata->clust_size : PREFETCH_BLOCKS; + + debug("FAT read(sect=%d, cnt:%d), clust_size=%d, DIRENTSPERBLOCK=%zd\n", + cursect, read_blk, mydata->clust_size, DIRENTSPERBLOCK); - if (disk_read(cursect, - (mydata->fatsize == 32) ? - (mydata->clust_size) : - PREFETCH_BLOCKS, - do_fat_read_at_block) < 0) { + if (disk_read(cursect, read_blk, dir_ptr) < 0) { debug("Error: reading rootdir block\n"); goto exit; } - dentptr = (dir_entry *) do_fat_read_at_block; + dentptr = (dir_entry *)dir_ptr; } for (i = 0; i < DIRENTSPERBLOCK; i++) { @@ -952,7 +991,7 @@ do_fat_read_at(const char *filename, unsigned long pos, void *buffer, get_vfatname(mydata, root_cluster, - do_fat_read_at_block, + dir_ptr, dentptr, l_name); if (dols == LS_ROOT) { @@ -974,8 +1013,8 @@ do_fat_read_at(const char *filename, unsigned long pos, void *buffer, } if (doit) { if (dirc == ' ') { - printf(" %8ld %s%c\n", - (long)FAT2CPU32(dentptr->size), + printf(" %8u %s%c\n", + FAT2CPU32(dentptr->size), l_name, dirc); } else { @@ -1032,8 +1071,8 @@ do_fat_read_at(const char *filename, unsigned long pos, void *buffer, } if (doit) { if (dirc == ' ') { - printf(" %8ld %s%c\n", - (long)FAT2CPU32(dentptr->size), + printf(" %8u %s%c\n", + FAT2CPU32(dentptr->size), s_name, dirc); } else { printf(" %s%c\n", @@ -1063,7 +1102,7 @@ do_fat_read_at(const char *filename, unsigned long pos, void *buffer, goto rootdir_done; /* We got a match */ } - debug("END LOOP: j=%d clust_size=%d\n", j, + debug("END LOOP: buffer_blk_cnt=%d clust_size=%d\n", buffer_blk_cnt, mydata->clust_size); /* @@ -1071,10 +1110,10 @@ do_fat_read_at(const char *filename, unsigned long pos, void *buffer, * root directory clusters when a cluster has been * completely processed. */ - ++j; + ++buffer_blk_cnt; int rootdir_end = 0; if (mydata->fatsize == 32) { - if (j == mydata->clust_size) { + if (buffer_blk_cnt == mydata->clust_size) { int nxtsect = 0; int nxt_clust = 0; @@ -1087,11 +1126,11 @@ do_fat_read_at(const char *filename, unsigned long pos, void *buffer, root_cluster = nxt_clust; cursect = nxtsect; - j = 0; + buffer_blk_cnt = 0; } } else { - if (j == PREFETCH_BLOCKS) - j = 0; + if (buffer_blk_cnt == PREFETCH_BLOCKS) + buffer_blk_cnt = 0; rootdir_end = (++cursect - mydata->rootdir_sect >= rootdir_size); @@ -1102,7 +1141,7 @@ do_fat_read_at(const char *filename, unsigned long pos, void *buffer, if (dols == LS_ROOT) { printf("\n%d file(s), %d dir(s)\n\n", files, dirs); - ret = 0; + *size = 0; } goto exit; } @@ -1141,32 +1180,62 @@ rootdir_done: if (get_dentfromdir(mydata, startsect, subname, dentptr, isdir ? 0 : dols) == NULL) { if (dols && !isdir) - ret = 0; + *size = 0; goto exit; } if (isdir && !(dentptr->attr & ATTR_DIR)) goto exit; + /* + * If we are looking for a directory, and found a directory + * type entry, and the entry is for the root directory (as + * denoted by a cluster number of 0), jump back to the start + * of the function, since at least on FAT12/16, the root dir + * lives in a hard-coded location and needs special handling + * to parse, rather than simply following the cluster linked + * list in the FAT, like other directories. + */ + if (isdir && (dentptr->attr & ATTR_DIR) && !START(dentptr)) { + /* + * Modify the filename to remove the prefix that gets + * back to the root directory, so the initial root dir + * parsing code can continue from where we are without + * confusion. + */ + strcpy(fnamecopy, nextname ?: ""); + /* + * Set up state the same way as the function does when + * first started. This is required for the root dir + * parsing code operates in its expected environment. + */ + subname = ""; + cursect = mydata->rootdir_sect; + isdir = 0; + goto root_reparse; + } + if (idx >= 0) subname = nextname; } - if (dogetsize) - ret = FAT2CPU32(dentptr->size); - else - ret = get_contents(mydata, dentptr, pos, buffer, maxsize); - debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret); + if (dogetsize) { + *size = FAT2CPU32(dentptr->size); + ret = 0; + } else { + ret = get_contents(mydata, dentptr, pos, buffer, maxsize, size); + } + debug("Size: %u, got: %llu\n", FAT2CPU32(dentptr->size), *size); exit: free(mydata->fatbuf); return ret; } -long -do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols) +int do_fat_read(const char *filename, void *buffer, loff_t maxsize, int dols, + loff_t *actread) { - return do_fat_read_at(filename, 0, buffer, maxsize, dols, 0); + return do_fat_read_at(filename, 0, buffer, maxsize, dols, 0, actread); } int file_fat_detectfs(void) @@ -1181,9 +1250,9 @@ int file_fat_detectfs(void) return 1; } -#if defined(CONFIG_CMD_IDE) || \ - defined(CONFIG_CMD_SATA) || \ - defined(CONFIG_CMD_SCSI) || \ +#if defined(CONFIG_IDE) || \ + defined(CONFIG_SATA) || \ + defined(CONFIG_SCSI) || \ defined(CONFIG_CMD_USB) || \ defined(CONFIG_MMC) printf("Interface: "); @@ -1213,7 +1282,7 @@ int file_fat_detectfs(void) printf("Unknown"); } - printf("\n Device %d: ", cur_dev->dev); + printf("\n Device %d: ", cur_dev->devnum); dev_print(cur_dev); #endif @@ -1233,44 +1302,55 @@ int file_fat_detectfs(void) int file_fat_ls(const char *dir) { - return do_fat_read(dir, NULL, 0, LS_YES); + loff_t size; + + return do_fat_read(dir, NULL, 0, LS_YES, &size); } int fat_exists(const char *filename) { - int sz; - sz = do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1); - return sz >= 0; + int ret; + loff_t size; + + ret = do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, &size); + return ret == 0; } -int fat_size(const char *filename) +int fat_size(const char *filename, loff_t *size) { - return do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1); + return do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, size); } -long file_fat_read_at(const char *filename, unsigned long pos, void *buffer, - unsigned long maxsize) +int file_fat_read_at(const char *filename, loff_t pos, void *buffer, + loff_t maxsize, loff_t *actread) { printf("reading %s\n", filename); - return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO, 0); + return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO, 0, + actread); } -long file_fat_read(const char *filename, void *buffer, unsigned long maxsize) +int file_fat_read(const char *filename, void *buffer, int maxsize) { - return file_fat_read_at(filename, 0, buffer, maxsize); + loff_t actread; + int ret; + + ret = file_fat_read_at(filename, 0, buffer, maxsize, &actread); + if (ret) + return ret; + else + return actread; } -int fat_read_file(const char *filename, void *buf, int offset, int len) +int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len, + loff_t *actread) { - int len_read; + int ret; - len_read = file_fat_read_at(filename, offset, buf, len); - if (len_read == -1) { + ret = file_fat_read_at(filename, offset, buf, len, actread); + if (ret) printf("** Unable to read file %s **\n", filename); - return -1; - } - return len_read; + return ret; } void fat_close(void)