#include <asm/byteorder.h>
#include <part.h>
#include <malloc.h>
+#include <memalign.h>
#include <linux/compiler.h>
#include <linux/ctype.h>
}
}
-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
static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
{
+ ulong ret;
+
if (!cur_dev || !cur_dev->block_read)
return -1;
- return cur_dev->block_read(cur_dev->dev,
- cur_part_info.start + block, nr_blocks, buf);
+ ret = cur_dev->block_read(cur_dev, cur_part_info.start + block,
+ nr_blocks, buf);
+
+ if (nr_blocks && ret == 0)
+ return -1;
+
+ 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);
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;
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");
strcpy(fnamecopy, filename);
downcase(fnamecopy);
+root_reparse:
if (*fnamecopy == '\0') {
if (!dols)
goto exit;
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);
+ }
- if (disk_read(cursect,
- (mydata->fatsize == 32) ?
- (mydata->clust_size) :
- PREFETCH_BLOCKS,
- do_fat_read_at_block) < 0) {
+ 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, 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++) {
get_vfatname(mydata,
root_cluster,
- do_fat_read_at_block,
+ dir_ptr,
dentptr, l_name);
if (dols == LS_ROOT) {
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);
/*
* 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;
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);
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;
}