+// SPDX-License-Identifier: GPL-2.0+
/*
* fat.c
*
*
* 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
* 2003-03-10 - kharris@nexus-tech.net - ported to uboot
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#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;
}
static int flush_dirty_fat_buffer(fsdata *mydata);
-#if !defined(CONFIG_FAT_WRITE)
+
+#if !CONFIG_IS_ENABLED(FAT_WRITE)
/* Stub for read only operation */
int flush_dirty_fat_buffer(fsdata *mydata)
{
if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
- printf("FAT: Misaligned buffer address (%p)\n", buffer);
+ debug("FAT: Misaligned buffer address (%p)\n", buffer);
while (size >= mydata->sect_size) {
ret = disk_read(startsect++, 1, tmpbuf);
}
} 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
*/
-__u8 get_contents_vfatname_block[MAX_CLUSTSIZE]
- __aligned(ARCH_DMA_MINALIGN);
-
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;
}
/* align to beginning of next cluster if any */
if (pos) {
+ __u8 *tmp_buffer;
+
actsize = min(filesize, (loff_t)bytesperclust);
- if (get_cluster(mydata, curclust, get_contents_vfatname_block,
- (int)actsize) != 0) {
+ tmp_buffer = malloc_cache_aligned(actsize);
+ if (!tmp_buffer) {
+ debug("Error: allocating buffer\n");
+ return -1;
+ }
+
+ if (get_cluster(mydata, curclust, tmp_buffer, actsize) != 0) {
printf("Error reading cluster\n");
+ free(tmp_buffer);
return -1;
}
filesize -= actsize;
actsize -= pos;
- memcpy(buffer, get_contents_vfatname_block + pos, actsize);
+ memcpy(buffer, tmp_buffer + pos, actsize);
+ free(tmp_buffer);
*gotsize += actsize;
if (!filesize)
return 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;
}
/*
- * TODO these should go away once fat_write is reworked to use the
- * directory iterator
- */
-__u8 get_dentfromdir_block[MAX_CLUSTSIZE]
- __aligned(ARCH_DMA_MINALIGN);
-__u8 do_fat_read_at_block[MAX_CLUSTSIZE]
- __aligned(ARCH_DMA_MINALIGN);
-
-/*
* Read boot sector and volume info from a FAT filesystem
*/
static int
if (mydata->fatsize == 32) {
mydata->fatlength = bs.fat32_length;
+ mydata->total_sect = bs.total_sect;
} else {
mydata->fatlength = bs.fat_length;
+ mydata->total_sect = (bs.sectors[1] << 8) + bs.sectors[0];
+ if (!mydata->total_sect)
+ mydata->total_sect = bs.total_sect;
}
+ if (!mydata->total_sect) /* unlikely */
+ mydata->total_sect = (u32)cur_part_info.size;
+ mydata->fats = bs.fats;
mydata->fat_sect = bs.reserved;
mydata->rootdir_sect = mydata->fat_sect + mydata->fatlength * bs.fats;
mydata->sect_size, cur_part_info.blksz);
return -1;
}
+ if (mydata->clust_size == 0) {
+ printf("Error: FAT cluster size not set\n");
+ return -1;
+ }
+ if ((unsigned int)mydata->clust_size * mydata->sect_size >
+ MAX_CLUSTSIZE) {
+ printf("Error: FAT cluster size too big (cs=%u, max=%u)\n",
+ (unsigned int)mydata->clust_size * mydata->sect_size,
+ MAX_CLUSTSIZE);
+ return -1;
+ }
if (mydata->fatsize == 32) {
mydata->data_begin = mydata->rootdir_sect -
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;
typedef struct {
fsdata *fsdata; /* filesystem parameters */
+ unsigned start_clust; /* first cluster */
unsigned clust; /* current cluster */
+ unsigned next_clust; /* next cluster if remaining == 0 */
int last_cluster; /* set once we've read last cluster */
int is_root; /* is iterator at root directory */
int remaining; /* remaining dent's in current cluster */
return -ENXIO;
itr->fsdata = fsdata;
+ itr->start_clust = 0;
itr->clust = fsdata->root_cluster;
+ itr->next_clust = fsdata->root_cluster;
itr->dent = NULL;
itr->remaining = 0;
itr->last_cluster = 0;
assert(fat_itr_isdir(parent));
itr->fsdata = parent->fsdata;
+ itr->start_clust = clustnum;
if (clustnum > 0) {
itr->clust = clustnum;
+ itr->next_clust = clustnum;
itr->is_root = 0;
} else {
itr->clust = parent->fsdata->root_cluster;
+ itr->next_clust = parent->fsdata->root_cluster;
itr->is_root = 1;
}
itr->dent = NULL;
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->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->clust++;
- sect = clust_to_sect(itr->fsdata, itr->clust);
- if (sect - itr->fsdata->rootdir_sect >=
+ itr->next_clust++;
+ if (itr->next_clust * itr->fsdata->clust_size >=
itr->fsdata->rootdir_size) {
- debug("cursect: 0x%x\n", itr->clust);
+ debug("nextclust: 0x%x\n", itr->next_clust);
itr->last_cluster = 1;
}
} else {
- itr->clust = get_fatent(itr->fsdata, itr->clust);
- if (CHECK_CLUST(itr->clust, itr->fsdata->fatsize)) {
- debug("cursect: 0x%x\n", itr->clust);
+ itr->next_clust = get_fatent(itr->fsdata, itr->next_clust);
+ if (CHECK_CLUST(itr->next_clust, itr->fsdata->fatsize)) {
+ 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)
+ if (!dent) {
+ /* a sign for no more entries left */
+ itr->dent = NULL;
return NULL;
+ }
itr->remaining = nbytes / sizeof(dir_entry) - 1;
itr->dent = dent;
slot2str((dir_slot *)dent, buf, &idx);
+ if (n + idx >= sizeof(itr->l_name))
+ return NULL;
+
/* shift accumulated long-name up and copy new part in: */
memmove(itr->l_name + idx, itr->l_name, n);
memcpy(itr->l_name, buf, idx);
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;
}
while (next[0] && !ISDIRDELIM(next[0]))
next++;
+ if (itr->is_root) {
+ /* root dir doesn't have "." nor ".." */
+ if ((((next - path) == 1) && !strncmp(path, ".", 1)) ||
+ (((next - path) == 2) && !strncmp(path, "..", 2))) {
+ /* point back to itself */
+ itr->clust = itr->fsdata->root_cluster;
+ itr->next_clust = itr->fsdata->root_cluster;
+ itr->dent = NULL;
+ itr->remaining = 0;
+ itr->last_cluster = 0;
+
+ if (next[0] == 0) {
+ if (type & TYPE_DIR)
+ return 0;
+ else
+ return -ENOENT;
+ }
+
+ return fat_itr_resolve(itr, next, type);
+ }
+ }
+
while (fat_itr_next(itr)) {
int match = 0;
unsigned n = max(strlen(itr->name), (size_t)(next - path));
* 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;
}
if (ret)
goto out_free_both;
- debug("reading %s\n", filename);
- ret = get_contents(&fsdata, itr->dent, pos, buffer, maxsize, actread);
+ debug("reading %s at pos %llu\n", filename, pos);
+
+ /* For saving default max clustersize memory allocated to malloc pool */
+ dir_entry *dentptr = itr->dent;
+
+ ret = get_contents(&fsdata, dentptr, pos, buffer, maxsize, actread);
out_free_both:
free(fsdata.fatbuf);