*
* R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim
*
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/byteorder.h>
#include <part.h>
#include <linux/ctype.h>
+#include <div64.h>
+#include <linux/math64.h>
#include "fat.c"
static void uppercase(char *str, int len)
static int total_sector;
static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
{
+ ulong ret;
+
if (!cur_dev || !cur_dev->block_write)
return -1;
return -1;
}
- return cur_dev->block_write(cur_dev->dev,
- cur_part_info.start + block, nr_blocks, buf);
+ ret = cur_dev->block_write(cur_dev->dev,
+ cur_part_info.start + block,
+ nr_blocks, buf);
+ if (nr_blocks && ret == 0)
+ return -1;
+
+ return ret;
}
/*
if (len == 0)
return;
- memcpy(s_name, filename, len);
+ strcpy(s_name, filename);
uppercase(s_name, len);
period = strchr(s_name, '.');
__u8 *bufptr = mydata->fatbuf;
__u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
- fatlength *= mydata->sect_size;
startblock += mydata->fat_sect;
if (getsize > fatlength)
__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:
bufnum = entry / FAT32BUFSIZE;
return ret;
}
-#ifdef CONFIG_SUPPORT_VFAT
/*
* Set the file name information from 'name' into 'slotptr',
*/
return 0;
}
-#endif
-
/*
* Set the entry at index 'entry' in a FAT (16/32) table.
*/
set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
unsigned long size)
{
- int idx = 0;
+ __u32 idx = 0;
__u32 startsect;
+ int ret;
if (clustnum > 0)
startsect = mydata->data_begin +
debug("clustnum: %d, startsect: %d\n", clustnum, startsect);
- if (disk_write(startsect, size / mydata->sect_size, buffer) < 0) {
- debug("Error writing data\n");
- return -1;
- }
+ if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
+ ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
- if (size % mydata->sect_size) {
- __u8 tmpbuf[mydata->sect_size];
+ printf("FAT: Misaligned buffer address (%p)\n", buffer);
- idx = size / mydata->sect_size;
- buffer += idx * mydata->sect_size;
- memcpy(tmpbuf, buffer, size % mydata->sect_size);
+ while (size >= mydata->sect_size) {
+ memcpy(tmpbuf, buffer, mydata->sect_size);
+ ret = disk_write(startsect++, 1, tmpbuf);
+ if (ret != 1) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
- if (disk_write(startsect + idx, 1, tmpbuf) < 0) {
- debug("Error writing data\n");
+ buffer += mydata->sect_size;
+ size -= mydata->sect_size;
+ }
+ } else if (size >= mydata->sect_size) {
+ idx = size / mydata->sect_size;
+ ret = disk_write(startsect, idx, buffer);
+ if (ret != idx) {
+ debug("Error writing data (got %d)\n", ret);
return -1;
}
- return 0;
+ startsect += idx;
+ idx *= mydata->sect_size;
+ buffer += idx;
+ size -= idx;
+ }
+
+ if (size) {
+ ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
+
+ memcpy(tmpbuf, buffer, size);
+ ret = disk_write(startsect, 1, tmpbuf);
+ if (ret != 1) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
}
return 0;
/*
* Write at most 'maxsize' bytes from 'buffer' into
* the file associated with 'dentptr'
- * Return the number of bytes read or -1 on fatal errors.
+ * Update the number of bytes written in *gotsize and return 0
+ * or return -1 on fatal errors.
*/
static int
set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
- unsigned long maxsize)
+ 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 = 0, newclust = 0;
- unsigned long actsize;
+ loff_t actsize;
- debug("Filesize: %ld bytes\n", filesize);
+ *gotsize = 0;
+ debug("Filesize: %llu bytes\n", filesize);
if (maxsize > 0 && filesize > maxsize)
filesize = maxsize;
- debug("%ld bytes\n", filesize);
+ debug("%llu bytes\n", filesize);
actsize = bytesperclust;
endclust = curclust;
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;
}
/* set remaining bytes */
- gotsize += (int)actsize;
+ *gotsize += actsize;
filesize -= actsize;
buffer += actsize;
actsize = filesize;
debug("error: writing cluster\n");
return -1;
}
- gotsize += actsize;
+ *gotsize += actsize;
/* Mark end of file in FAT */
if (mydata->fatsize == 16)
newclust = 0xfffffff;
set_fatent_value(mydata, endclust, newclust);
- return gotsize;
+ return 0;
getit:
if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
debug("error: writing cluster\n");
return -1;
}
- gotsize += (int)actsize;
+ *gotsize += actsize;
filesize -= actsize;
buffer += actsize;
if (CHECK_CLUST(curclust, mydata->fatsize)) {
debug("curclust: 0x%x\n", curclust);
debug("Invalid FAT entry\n");
- return gotsize;
+ return 0;
}
actsize = bytesperclust;
curclust = endclust = newclust;
* exceed the size of the block device
* Return -1 when overflow occurs, otherwise return 0
*/
-static int check_overflow(fsdata *mydata, __u32 clustnum, unsigned long size)
+static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
{
- __u32 startsect, sect_num;
+ __u32 startsect, sect_num, offset;
if (clustnum > 0) {
startsect = mydata->data_begin +
startsect = mydata->rootdir_sect;
}
- sect_num = size / mydata->sect_size;
- if (size % mydata->sect_size)
+ sect_num = div_u64_rem(size, mydata->sect_size, &offset);
+
+ if (offset != 0)
sect_num++;
if (startsect + sect_num > cur_part_info.start + total_sector)
return -1;
-
return 0;
}
continue;
}
if ((dentptr->attr & ATTR_VOLUME)) {
-#ifdef CONFIG_SUPPORT_VFAT
- if ((dentptr->attr & ATTR_VFAT) &&
+ if (vfat_enabled &&
+ (dentptr->attr & ATTR_VFAT) &&
(dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
get_long_file_name(mydata, curclust,
get_dentfromdir_block,
&dentptr, l_name);
debug("vfatname: |%s|\n", l_name);
- } else
-#endif
- {
+ } else {
/* Volume label or VFAT entry */
dentptr++;
if (is_next_clust(mydata, dentptr))
return dentptr;
}
+ /*
+ * In FAT16/12, the root dir is locate before data area, shows
+ * in following:
+ * -------------------------------------------------------------
+ * | Boot | FAT1 & 2 | Root dir | Data (start from cluster #2) |
+ * -------------------------------------------------------------
+ *
+ * As a result if curclust is in Root dir, it is a negative
+ * number or 0, 1.
+ *
+ */
+ if (mydata->fatsize != 32 && (int)curclust <= 1) {
+ /* Current clust is in root dir, set to next clust */
+ curclust++;
+ if ((int)curclust <= 1)
+ continue; /* continue to find */
+
+ /* Reach the end of root dir */
+ empty_dentptr = dentptr;
+ return NULL;
+ }
+
curclust = get_fatent_value(mydata, dir_curclust);
- if ((curclust >= 0xffffff8) || (curclust >= 0xfff8)) {
+ if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
empty_dentptr = dentptr;
return NULL;
}
return NULL;
}
-static int do_fat_write(const char *filename, void *buffer,
- unsigned long size)
+static int do_fat_write(const char *filename, void *buffer, loff_t size,
+ loff_t *actwrite)
{
dir_entry *dentptr, *retdent;
__u32 startsect;
int cursect;
int ret = -1, name_len;
char l_filename[VFAT_MAXLEN_BYTES];
- int write_size = size;
+ *actwrite = size;
dir_curclust = 0;
if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
total_sector = bs.total_sect;
if (total_sector == 0)
- total_sector = cur_part_info.size;
+ total_sector = (int)cur_part_info.size; /* cast of lbaint_t */
if (mydata->fatsize == 32)
mydata->fatlength = bs.fat32_length;
}
mydata->fatbufnum = -1;
- mydata->fatbuf = malloc(FATBUFSIZE);
+ mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
if (mydata->fatbuf == NULL) {
debug("Error: allocating memory\n");
return -1;
ret = check_overflow(mydata, start_cluster, size);
if (ret) {
- printf("Error: %ld overflow\n", size);
+ printf("Error: %llu overflow\n", size);
goto exit;
}
goto exit;
}
- ret = set_contents(mydata, retdent, buffer, size);
+ ret = set_contents(mydata, retdent, buffer, size, actwrite);
if (ret < 0) {
printf("Error: writing contents\n");
goto exit;
}
- write_size = ret;
- debug("attempt to write 0x%x bytes\n", write_size);
+ debug("attempt to write 0x%llx bytes\n", *actwrite);
/* Flush fat buffer */
ret = flush_fat_buffer(mydata);
ret = check_overflow(mydata, start_cluster, size);
if (ret) {
- printf("Error: %ld overflow\n", size);
+ printf("Error: %llu overflow\n", size);
goto exit;
}
fill_dentry(mydata, empty_dentptr, filename,
start_cluster, size, 0x20);
- ret = set_contents(mydata, empty_dentptr, buffer, size);
+ ret = set_contents(mydata, empty_dentptr, buffer, size,
+ actwrite);
if (ret < 0) {
printf("Error: writing contents\n");
goto exit;
}
- write_size = ret;
- debug("attempt to write 0x%x bytes\n", write_size);
+ debug("attempt to write 0x%llx bytes\n", *actwrite);
/* Flush fat buffer */
ret = flush_fat_buffer(mydata);
exit:
free(mydata->fatbuf);
- return ret < 0 ? ret : write_size;
+ return ret;
}
-int file_fat_write(const char *filename, void *buffer, unsigned long maxsize)
+int file_fat_write(const char *filename, void *buffer, loff_t offset,
+ loff_t maxsize, loff_t *actwrite)
{
+ if (offset != 0) {
+ printf("Error: non zero offset is currently not suported.\n");
+ return -1;
+ }
+
printf("writing %s\n", filename);
- return do_fat_write(filename, buffer, maxsize);
+ return do_fat_write(filename, buffer, maxsize, actwrite);
}