#include <exports.h>
#include <fat.h>
#include <fs.h>
+#include <log.h>
#include <asm/byteorder.h>
#include <part.h>
#include <malloc.h>
#include <memalign.h>
+#include <asm/cache.h>
#include <linux/compiler.h>
#include <linux/ctype.h>
}
static struct blk_desc *cur_dev;
-static disk_partition_t cur_part_info;
+static struct disk_partition cur_part_info;
#define DOS_BOOT_MAGIC_OFFSET 0x1fe
#define DOS_FS_TYPE_OFFSET 0x36
return ret;
}
-int fat_set_blk_dev(struct blk_desc *dev_desc, disk_partition_t *info)
+int fat_set_blk_dev(struct blk_desc *dev_desc, struct disk_partition *info)
{
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
int fat_register_device(struct blk_desc *dev_desc, int part_no)
{
- disk_partition_t info;
+ struct disk_partition info;
/* First close any currently found FAT filesystem */
cur_dev = NULL;
}
} else {
idx = size / mydata->sect_size;
- ret = disk_read(startsect, idx, buffer);
+ if (idx == 0)
+ ret = 0;
+ else
+ ret = disk_read(startsect, idx, buffer);
if (ret != idx) {
debug("Error reading data (got %d)\n", ret);
return -1;
return 0;
}
-/*
+/**
+ * get_contents() - read from file
+ *
* Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr'
- * into 'buffer'.
- * Update the number of bytes read in *gotsize or return -1 on fatal errors.
+ * into 'buffer'. Update the number of bytes read in *gotsize or return -1 on
+ * fatal errors.
+ *
+ * @mydata: file system description
+ * @dentprt: directory entry pointer
+ * @pos: position from where to read
+ * @buffer: buffer into which to read
+ * @maxsize: maximum number of bytes to read
+ * @gotsize: number of bytes actually read
+ * Return: -1 on error, otherwise 0
*/
static int get_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos,
__u8 *buffer, loff_t maxsize, loff_t *gotsize)
curclust = get_fatent(mydata, curclust);
if (CHECK_CLUST(curclust, mydata->fatsize)) {
debug("curclust: 0x%x\n", curclust);
- debug("Invalid FAT entry\n");
- return 0;
+ printf("Invalid FAT entry\n");
+ return -1;
}
actsize += bytesperclust;
}
tmp_buffer = malloc_cache_aligned(actsize);
if (!tmp_buffer) {
debug("Error: allocating buffer\n");
- return -ENOMEM;
+ return -1;
}
if (get_cluster(mydata, curclust, tmp_buffer, actsize) != 0) {
curclust = get_fatent(mydata, curclust);
if (CHECK_CLUST(curclust, mydata->fatsize)) {
debug("curclust: 0x%x\n", curclust);
- debug("Invalid FAT entry\n");
- return 0;
+ printf("Invalid FAT entry\n");
+ return -1;
}
}
goto getit;
if (CHECK_CLUST(newclust, mydata->fatsize)) {
debug("curclust: 0x%x\n", newclust);
- debug("Invalid FAT entry\n");
- return 0;
+ printf("Invalid FAT entry\n");
+ return -1;
}
endclust = newclust;
actsize += bytesperclust;
if (CHECK_CLUST(curclust, mydata->fatsize)) {
debug("curclust: 0x%x\n", curclust);
printf("Invalid FAT entry\n");
- return 0;
+ return -1;
}
actsize = bytesperclust;
endclust = curclust;
mydata->data_begin = mydata->rootdir_sect +
mydata->rootdir_size -
(mydata->clust_size * 2);
- mydata->root_cluster =
- sect_to_clust(mydata, mydata->rootdir_sect);
+
+ /*
+ * The root directory is not cluster-aligned and may be on a
+ * "negative" cluster, this will be handled specially in
+ * next_cluster().
+ */
+ mydata->root_cluster = 0;
}
mydata->fatbufnum = -1;
itr->last_cluster = 0;
}
-static void *next_cluster(fat_itr *itr)
+static void *next_cluster(fat_itr *itr, unsigned *nbytes)
{
fsdata *mydata = itr->fsdata; /* for silly macros */
int ret;
u32 sect;
+ u32 read_size;
/* have we reached the end? */
if (itr->last_cluster)
return NULL;
- sect = clust_to_sect(itr->fsdata, itr->next_clust);
+ if (itr->is_root && itr->fsdata->fatsize != 32) {
+ /*
+ * The root directory is located before the data area and
+ * cannot be indexed using the regular unsigned cluster
+ * numbers (it may start at a "negative" cluster or not at a
+ * cluster boundary at all), so consider itr->next_clust to be
+ * a offset in cluster-sized units from the start of rootdir.
+ */
+ unsigned sect_offset = itr->next_clust * itr->fsdata->clust_size;
+ unsigned remaining_sects = itr->fsdata->rootdir_size - sect_offset;
+ sect = itr->fsdata->rootdir_sect + sect_offset;
+ /* do not read past the end of rootdir */
+ read_size = min_t(u32, itr->fsdata->clust_size,
+ remaining_sects);
+ } else {
+ sect = clust_to_sect(itr->fsdata, itr->next_clust);
+ read_size = itr->fsdata->clust_size;
+ }
- debug("FAT read(sect=%d), clust_size=%d, DIRENTSPERBLOCK=%zd\n",
- sect, itr->fsdata->clust_size, DIRENTSPERBLOCK);
+ debug("FAT read(sect=%d), clust_size=%d, read_size=%u, DIRENTSPERBLOCK=%zd\n",
+ sect, itr->fsdata->clust_size, read_size, DIRENTSPERBLOCK);
/*
* NOTE: do_fat_read_at() had complicated logic to deal w/
* dent at a time and iteratively constructing the vfat long
* name.
*/
- ret = disk_read(sect, itr->fsdata->clust_size,
- itr->block);
+ ret = disk_read(sect, read_size, itr->block);
if (ret < 0) {
debug("Error: reading block\n");
return NULL;
}
+ *nbytes = read_size * itr->fsdata->sect_size;
itr->clust = itr->next_clust;
if (itr->is_root && itr->fsdata->fatsize != 32) {
itr->next_clust++;
- sect = clust_to_sect(itr->fsdata, itr->next_clust);
- if (sect - itr->fsdata->rootdir_sect >=
+ if (itr->next_clust * itr->fsdata->clust_size >=
itr->fsdata->rootdir_size) {
debug("nextclust: 0x%x\n", itr->next_clust);
itr->last_cluster = 1;
static dir_entry *next_dent(fat_itr *itr)
{
if (itr->remaining == 0) {
- struct dir_entry *dent = next_cluster(itr);
- unsigned nbytes = itr->fsdata->sect_size *
- itr->fsdata->clust_size;
+ unsigned nbytes;
+ struct dir_entry *dent = next_cluster(itr, &nbytes);
/* have we reached the last cluster? */
if (!dent) {
return NULL;
}
+ /*
+ * We are now at the short file name entry.
+ * If it is marked as deleted, just skip it.
+ */
+ if (dent->name[0] == DELETED_FLAG ||
+ dent->name[0] == aRING)
+ return NULL;
+
itr->l_name[n] = '\0';
chksum = mkcksum(dent->name, dent->ext);
itr->name = NULL;
+ /*
+ * One logical directory entry consist of following slots:
+ * name[0] Attributes
+ * dent[N - N]: LFN[N - 1] N|0x40 ATTR_VFAT
+ * ...
+ * dent[N - 2]: LFN[1] 2 ATTR_VFAT
+ * dent[N - 1]: LFN[0] 1 ATTR_VFAT
+ * dent[N]: SFN ATTR_ARCH
+ */
+
while (1) {
dent = next_dent(itr);
if (!dent)
return 0;
- if (dent->name[0] == DELETED_FLAG ||
- dent->name[0] == aRING)
+ if (dent->name[0] == DELETED_FLAG)
continue;
if (dent->attr & ATTR_VOLUME) {
if ((dent->attr & ATTR_VFAT) == ATTR_VFAT &&
(dent->name[0] & LAST_LONG_ENTRY_MASK)) {
+ /* long file name */
dent = extract_vfat_name(itr);
+ /*
+ * If succeeded, dent has a valid short file
+ * name entry for the current entry.
+ * If failed, itr points to a current bogus
+ * entry. So after fetching a next one,
+ * it may have a short file name entry
+ * for this bogus entry so that we can still
+ * check for a short name.
+ */
if (!dent)
continue;
itr->name = itr->l_name;
}
}
+ /* short file name */
break;
}
* expected to fail if passed a directory path:
*/
free(fsdata.fatbuf);
- fat_itr_root(itr, &fsdata);
- if (!fat_itr_resolve(itr, filename, TYPE_DIR)) {
+ ret = fat_itr_root(itr, &fsdata);
+ if (ret)
+ goto out_free_itr;
+ ret = fat_itr_resolve(itr, filename, TYPE_DIR);
+ if (!ret)
*size = 0;
- ret = 0;
- }
goto out_free_both;
}
/* For saving default max clustersize memory allocated to malloc pool */
dir_entry *dentptr = itr->dent;
- free(itr);
-
- itr = NULL;
-
ret = get_contents(&fsdata, dentptr, pos, buffer, maxsize, actread);
out_free_both: