summaryrefslogtreecommitdiff
path: root/fs/fat
diff options
context:
space:
mode:
authorKevin2014-11-15 11:48:36 +0800
committerKevin2014-11-15 11:48:36 +0800
commitd04075478d378d9e15f3e1abfd14b0bd124077d4 (patch)
tree733dd964582f388b9e3e367c249946cd32a2851f /fs/fat
downloadFOSSEE-netbook-uboot-source-d04075478d378d9e15f3e1abfd14b0bd124077d4.tar.gz
FOSSEE-netbook-uboot-source-d04075478d378d9e15f3e1abfd14b0bd124077d4.tar.bz2
FOSSEE-netbook-uboot-source-d04075478d378d9e15f3e1abfd14b0bd124077d4.zip
init commit via android 4.4 uboot
Diffstat (limited to 'fs/fat')
-rwxr-xr-xfs/fat/Makefile46
-rwxr-xr-xfs/fat/fat.c3023
-rwxr-xr-xfs/fat/file.c208
3 files changed, 3277 insertions, 0 deletions
diff --git a/fs/fat/Makefile b/fs/fat/Makefile
new file mode 100755
index 0000000..e462757
--- /dev/null
+++ b/fs/fat/Makefile
@@ -0,0 +1,46 @@
+#
+#
+# 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
+#
+
+TOPDIR=../../
+
+include $(TOPDIR)/config.mk
+
+LIB = libfat.a
+
+AOBJS =
+COBJS = fat.o file.o
+
+OBJS = $(AOBJS) $(COBJS)
+
+all: $(LIB) $(AOBJS)
+
+$(LIB): .depend $(OBJS)
+ $(AR) crv $@ $(OBJS)
+
+
+#########################################################################
+
+.depend: Makefile $(AOBJS:.o=.S) $(COBJS:.o=.c)
+ $(CC) -M $(CFLAGS) $(AOBJS:.o=.S) $(COBJS:.o=.c) > $@
+
+sinclude .depend
+
+#########################################################################
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
new file mode 100755
index 0000000..26c4668
--- /dev/null
+++ b/fs/fat/fat.c
@@ -0,0 +1,3023 @@
+/*++
+Copyright (c) 2010 WonderMedia Technologies, Inc.
+
+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, see http://www.gnu.org/licenses/>.
+
+WonderMedia Technologies, Inc.
+10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
+--*/
+/*
+ * fat.c
+ *
+ * R/O (V)FAT 12/16/32 filesystem implementation by Marcus Sundberg
+ *
+ * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
+ * 2003-03-10 - kharris@nexus-tech.net - ported to uboot
+ *
+ * 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
+ */
+
+#include <common.h>
+#include <config.h>
+#include <exports.h>
+#include <fat.h>
+#include <asm/byteorder.h>
+#include <part.h>
+#include <malloc.h>
+#include <linux/compiler.h>
+
+/*
+ * Convert a string to lowercase.
+ */
+static void downcase(char *str)
+{
+ while (*str != '\0') {
+ TOLOWER(*str);
+ str++;
+ }
+}
+
+static block_dev_desc_t *cur_dev;
+static unsigned int cur_part_nr;
+static disk_partition_t cur_part_info;
+static int total_sector;
+
+
+#define DOS_BOOT_MAGIC_OFFSET 0x1fe
+#define DOS_FS_TYPE_OFFSET 0x36
+#define DOS_FS32_TYPE_OFFSET 0x52
+
+static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
+{
+ 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);
+}
+
+
+
+
+int fatpre_register_device(block_dev_desc_t * dev_desc, int part_no)
+{
+ /* First close any currently found FAT filesystem */
+ cur_dev = NULL;
+
+#if (defined(CONFIG_CMD_IDE) || \
+ defined(CONFIG_CMD_SATA) || \
+ defined(CONFIG_CMD_SCSI) || \
+ defined(CONFIG_CMD_USB) || \
+ defined(CONFIG_MMC) || \
+ defined(CONFIG_SYSTEMACE) )
+
+ /* Read the partition table, if present */
+ if (!get_partition_info(dev_desc, part_no, &cur_part_info)) {
+ cur_dev = dev_desc;
+ cur_part_nr = part_no;
+ }
+#endif
+
+ /* Otherwise it might be a superfloppy (whole-disk FAT filesystem) */
+ if (!cur_dev) {
+ /*if (part_no != 0) {
+ printf("** Partition %d not valid on device %d **\n",
+ part_no, dev_desc->dev);
+ return -1;
+ }*/
+
+ cur_dev = dev_desc;
+ cur_part_nr = 1;
+ cur_part_info.start = 0;
+ cur_part_info.size = dev_desc->lba;
+ cur_part_info.blksz = dev_desc->blksz;
+ memset(cur_part_info.name, 0, sizeof(cur_part_info.name));
+ memset(cur_part_info.type, 0, sizeof(cur_part_info.type));
+ }
+ total_sector = cur_part_info.size;
+ return 0;
+}
+
+int fat_register_device(block_dev_desc_t * dev_desc, int part_no)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
+#if 0
+ /* First close any currently found FAT filesystem */
+ cur_dev = NULL;
+
+#if (defined(CONFIG_CMD_IDE) || \
+ defined(CONFIG_CMD_SATA) || \
+ defined(CONFIG_CMD_SCSI) || \
+ defined(CONFIG_CMD_USB) || \
+ defined(CONFIG_MMC) || \
+ defined(CONFIG_SYSTEMACE) )
+
+ /* Read the partition table, if present */
+ if (!get_partition_info(dev_desc, part_no, &cur_part_info)) {
+ cur_dev = dev_desc;
+ cur_part_nr = part_no;
+ }
+#endif
+
+ /* Otherwise it might be a superfloppy (whole-disk FAT filesystem) */
+ if (!cur_dev) {
+ if (part_no != 0) {
+ printf("** Partition %d not valid on device %d **\n",
+ part_no, dev_desc->dev);
+ return -1;
+ }
+
+ cur_dev = dev_desc;
+ cur_part_nr = 1;
+ cur_part_info.start = 0;
+ cur_part_info.size = dev_desc->lba;
+ cur_part_info.blksz = dev_desc->blksz;
+ memset(cur_part_info.name, 0, sizeof(cur_part_info.name));
+ memset(cur_part_info.type, 0, sizeof(cur_part_info.type));
+ }
+#endif
+ if (fatpre_register_device(dev_desc, part_no))
+ return -1;
+
+ /* Make sure it has a valid FAT header */
+ if (disk_read(0, 1, buffer) != 1) {
+ cur_dev = NULL;
+ printf("Cannot read from disk\n");
+ return -1;
+ }
+
+ /* Check if it's actually a DOS volume */
+ if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) {
+ printf("It is not a DOS volume\n");
+ cur_dev = NULL;
+ return -1;
+ }
+
+ /* Check for FAT12/FAT16/FAT32 filesystem */
+ if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3))
+ return 0;
+ if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5))
+ return 0;
+
+ printf("The volume cannot be recognized\n");
+ cur_dev = NULL;
+ return -1;
+}
+
+/*
+ * Get the first occurence of a directory delimiter ('/' or '\') in a string.
+ * Return index into string if found, -1 otherwise.
+ */
+static int dirdelim(char *str)
+{
+ char *start = str;
+
+ while (*str != '\0') {
+ if (ISDIRDELIM(*str))
+ return str - start;
+ str++;
+ }
+ return -1;
+}
+
+/*
+ * Extract zero terminated short name from a directory entry.
+ */
+static void get_name(dir_entry *dirent, char *s_name)
+{
+ char *ptr;
+
+ memcpy(s_name, dirent->name, 8);
+ s_name[8] = '\0';
+ ptr = s_name;
+ while (*ptr && *ptr != ' ')
+ ptr++;
+ if (dirent->ext[0] && dirent->ext[0] != ' ') {
+ *ptr = '.';
+ ptr++;
+ memcpy(ptr, dirent->ext, 3);
+ ptr[3] = '\0';
+ while (*ptr && *ptr != ' ')
+ ptr++;
+ }
+ *ptr = '\0';
+ if (*s_name == DELETED_FLAG)
+ *s_name = '\0';
+ else if (*s_name == aRING)
+ *s_name = DELETED_FLAG;
+ downcase(s_name);
+}
+
+/*
+ * Get the entry at index 'entry' in a FAT (12/16/32) table.
+ * On failure 0x00 is returned.
+ */
+static __u32 get_fatent(fsdata *mydata, __u32 entry)
+{
+ __u32 bufnum;
+ __u32 off16, offset;
+ __u32 ret = 0x00;
+ __u16 val1, val2;
+
+ switch (mydata->fatsize) {
+ case 32:
+ bufnum = entry / FAT32BUFSIZE;
+ offset = entry - bufnum * FAT32BUFSIZE;
+ break;
+ case 16:
+ bufnum = entry / FAT16BUFSIZE;
+ offset = entry - bufnum * FAT16BUFSIZE;
+ break;
+ case 12:
+ bufnum = entry / FAT12BUFSIZE;
+ offset = entry - bufnum * FAT12BUFSIZE;
+ break;
+
+ default:
+ /* Unsupported FAT size */
+ return ret;
+ }
+
+ debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n",
+ mydata->fatsize, entry, entry, offset, offset);
+
+ /* Read a new block of FAT entries into the cache. */
+ if (bufnum != mydata->fatbufnum) {
+ __u32 getsize = FATBUFBLOCKS;
+ __u8 *bufptr = mydata->fatbuf;
+ __u32 fatlength = mydata->fatlength;
+ __u32 startblock = bufnum * FATBUFBLOCKS;
+
+ if (startblock + getsize > fatlength)
+ getsize = fatlength - startblock;
+
+ startblock += mydata->fat_sect; /* Offset from start of disk */
+
+ if (disk_read(startblock, getsize, bufptr) < 0) {
+ debug("Error reading FAT blocks\n");
+ return ret;
+ }
+ mydata->fatbufnum = bufnum;
+ }
+
+ /* Get the actual entry from the table */
+ switch (mydata->fatsize) {
+ case 32:
+ ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
+ break;
+ case 16:
+ ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
+ break;
+ case 12:
+ off16 = (offset * 3) / 4;
+
+ 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;
+ }
+ debug("FAT%d: ret: %08x, offset: %04x\n",
+ mydata->fatsize, ret, offset);
+
+ return ret;
+}
+
+/*
+ * Read at most 'size' bytes from the specified cluster into 'buffer'.
+ * Return 0 on success, -1 otherwise.
+ */
+static int
+get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
+{
+ __u32 idx = 0;
+ __u32 startsect;
+ int ret;
+
+ if (clustnum > 0) {
+ startsect = mydata->data_begin +
+ clustnum * mydata->clust_size;
+ if (mydata->fatsize!=32) {//this is for windows format
+ if (startsect < mydata->rootdir_sect){
+ debug("get_cluster\n");
+ startsect=mydata->rootdir_sect;
+ }
+ }
+ } else {
+ startsect = mydata->rootdir_sect;
+ }
+
+ debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
+
+ 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);
+
+ while (size >= mydata->sect_size) {
+ ret = disk_read(startsect++, 1, tmpbuf);
+ if (ret != 1) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+
+ memcpy(buffer, tmpbuf, mydata->sect_size);
+ buffer += mydata->sect_size;
+ size -= mydata->sect_size;
+ }
+ } else {
+ idx = size / mydata->sect_size;
+ ret = disk_read(startsect, idx, buffer);
+ if (ret != idx) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+ startsect += idx;
+ idx *= mydata->sect_size;
+ buffer += idx;
+ size -= idx;
+ }
+ if (size) {
+ ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
+
+ ret = disk_read(startsect, 1, tmpbuf);
+ if (ret != 1) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+
+ memcpy(buffer, tmpbuf, size);
+ }
+
+ return 0;
+}
+
+/*
+ * 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.
+ */
+__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)
+{
+ unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
+ unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
+ __u32 curclust = START(dentptr);
+ __u32 endclust, newclust;
+ unsigned long actsize;
+
+ debug("Filesize: %ld bytes\n", filesize);
+
+ if (pos >= filesize) {
+ debug("Read position past EOF: %lu\n", pos);
+ return gotsize;
+ }
+
+ if (maxsize > 0 && filesize > pos + maxsize)
+ filesize = pos + maxsize;
+
+ debug("%ld bytes\n", filesize);
+
+ actsize = bytesperclust;
+
+ /* go to cluster at pos */
+ while (actsize <= pos) {
+ curclust = get_fatent(mydata, curclust);
+ if (CHECK_CLUST(curclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return gotsize;
+ }
+ actsize += bytesperclust;
+ }
+
+ /* actsize > pos */
+ actsize -= bytesperclust;
+ filesize -= actsize;
+ pos -= actsize;
+
+ /* align to beginning of next cluster if any */
+ if (pos) {
+ actsize = min(filesize, bytesperclust);
+ if (get_cluster(mydata, curclust, get_contents_vfatname_block,
+ (int)actsize) != 0) {
+ printf("Error reading cluster\n");
+ return -1;
+ }
+ filesize -= actsize;
+ actsize -= pos;
+ memcpy(buffer, get_contents_vfatname_block + pos, actsize);
+ gotsize += actsize;
+ if (!filesize)
+ return gotsize;
+ 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;
+ }
+ }
+
+ actsize = bytesperclust;
+ endclust = curclust;
+
+ do {
+ /* search for consecutive clusters */
+ while (actsize < filesize) {
+ newclust = get_fatent(mydata, endclust);
+ if ((newclust - 1) != endclust)
+ goto getit;
+ if (CHECK_CLUST(newclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", newclust);
+ debug("Invalid FAT entry\n");
+ return gotsize;
+ }
+ endclust = newclust;
+ actsize += bytesperclust;
+ }
+
+ /* get remaining bytes */
+ actsize = filesize;
+ if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
+ printf("Error reading cluster\n");
+ return -1;
+ }
+ gotsize += actsize;
+ return gotsize;
+getit:
+ if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
+ printf("Error reading cluster\n");
+ return -1;
+ }
+ gotsize += (int)actsize;
+ filesize -= actsize;
+ buffer += actsize;
+
+ curclust = get_fatent(mydata, endclust);
+ if (CHECK_CLUST(curclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ printf("Invalid FAT entry\n");
+ return gotsize;
+ }
+ actsize = bytesperclust;
+ endclust = curclust;
+ } while (1);
+}
+
+#ifdef CONFIG_SUPPORT_VFAT
+/*
+ * Extract the file name information from 'slotptr' into 'l_name',
+ * starting at l_name[*idx].
+ * Return 1 if terminator (zero byte) is found, 0 otherwise.
+ */
+static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
+{
+ int j;
+
+ for (j = 0; j <= 8; j += 2) {
+ l_name[*idx] = slotptr->name0_4[j];
+ if (l_name[*idx] == 0x00)
+ return 1;
+ (*idx)++;
+ }
+ for (j = 0; j <= 10; j += 2) {
+ l_name[*idx] = slotptr->name5_10[j];
+ if (l_name[*idx] == 0x00)
+ return 1;
+ (*idx)++;
+ }
+ for (j = 0; j <= 2; j += 2) {
+ l_name[*idx] = slotptr->name11_12[j];
+ if (l_name[*idx] == 0x00)
+ return 1;
+ (*idx)++;
+ }
+
+ return 0;
+}
+
+/*
+ * Extract the full long filename starting at 'retdent' (which is really
+ * a slot) into 'l_name'. If successful also copy the real directory entry
+ * into 'retdent'
+ * Return 0 on success, -1 otherwise.
+ */
+static int
+get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
+ dir_entry *retdent, char *l_name)
+{
+ dir_entry *realdent;
+ dir_slot *slotptr = (dir_slot *)retdent;
+ __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
+ PREFETCH_BLOCKS :
+ mydata->clust_size);
+ __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
+ int idx = 0;
+
+ if (counter > VFAT_MAXSEQ) {
+ debug("Error: VFAT name is too long\n");
+ return -1;
+ }
+
+ while ((__u8 *)slotptr < buflimit) {
+ if (counter == 0)
+ break;
+ if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
+ return -1;
+ slotptr++;
+ counter--;
+ }
+
+ if ((__u8 *)slotptr >= buflimit) {
+ dir_slot *slotptr2;
+
+ if (curclust == 0)
+ return -1;
+ curclust = get_fatent(mydata, curclust);
+ if (CHECK_CLUST(curclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ printf("Invalid FAT entry\n");
+ return -1;
+ }
+
+ if (get_cluster(mydata, curclust, get_contents_vfatname_block,
+ mydata->clust_size * mydata->sect_size) != 0) {
+ debug("Error: reading directory block\n");
+ return -1;
+ }
+
+ slotptr2 = (dir_slot *)get_contents_vfatname_block;
+ while (counter > 0) {
+ if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
+ & 0xff) != counter)
+ return -1;
+ slotptr2++;
+ counter--;
+ }
+
+ /* Save the real directory entry */
+ realdent = (dir_entry *)slotptr2;
+ while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
+ slotptr2--;
+ slot2str(slotptr2, l_name, &idx);
+ }
+ } else {
+ /* Save the real directory entry */
+ realdent = (dir_entry *)slotptr;
+ }
+
+ do {
+ slotptr--;
+ if (slot2str(slotptr, l_name, &idx))
+ break;
+ } while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
+
+ l_name[idx] = '\0';
+ if (*l_name == DELETED_FLAG)
+ *l_name = '\0';
+ else if (*l_name == aRING)
+ *l_name = DELETED_FLAG;
+ downcase(l_name);
+
+ /* Return the real directory entry */
+ memcpy(retdent, realdent, sizeof(dir_entry));
+
+ return 0;
+}
+
+/* Calculate short name checksum */
+static __u8 mkcksum(const char *str)
+{
+ int i;
+
+ __u8 ret = 0;
+
+ for (i = 0; i < 11; i++) {
+ ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i];
+ }
+
+ return ret;
+}
+#endif /* CONFIG_SUPPORT_VFAT */
+
+/*
+ * Get the directory entry associated with 'filename' from the directory
+ * starting at 'startsect'
+ */
+__u8 get_dentfromdir_block[MAX_CLUSTSIZE]
+ __aligned(ARCH_DMA_MINALIGN);
+char fat_fwc[260];
+
+static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
+ char *filename, dir_entry *retdent,
+ int dols)
+{
+ __u16 prevcksum = 0xffff;
+ __u32 curclust = START(retdent);
+ int files = 0, dirs = 0;
+
+ debug("get_dentfromdir: %s\n", filename);
+
+ while (1) {
+ dir_entry *dentptr;
+
+ int i;
+
+ if (get_cluster(mydata, curclust, get_dentfromdir_block,
+ mydata->clust_size * mydata->sect_size) != 0) {
+ debug("Error: reading directory block\n");
+ return NULL;
+ }
+
+ dentptr = (dir_entry *)get_dentfromdir_block;
+
+ for (i = 0; i < DIRENTSPERCLUST; i++) {
+ char s_name[14], l_name[VFAT_MAXLEN_BYTES];
+
+ l_name[0] = '\0';
+ if (dentptr->name[0] == DELETED_FLAG) {
+ dentptr++;
+ continue;
+ }
+ if ((dentptr->attr & ATTR_VOLUME)) {
+#ifdef CONFIG_SUPPORT_VFAT
+ if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
+ (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
+ prevcksum = ((dir_slot *)dentptr)->alias_checksum;
+ get_vfatname(mydata, curclust,
+ get_dentfromdir_block,
+ dentptr, l_name);
+ if (dols) {
+ int isdir;
+ char dirc;
+ int doit = 0;
+
+ isdir = (dentptr->attr & ATTR_DIR);
+
+ if (isdir) {
+ dirs++;
+ dirc = '/';
+ doit = 1;
+ } else {
+ dirc = ' ';
+ if (l_name[0] != 0) {
+ files++;
+ doit = 1;
+ }
+ }
+ if (doit) {
+ if (dirc == ' ') {
+ printf(" %8ld %s%c\n",
+ (long)FAT2CPU32(dentptr->size),
+ l_name,
+ dirc);
+ if ((l_name[0] == '+') && (strstr(l_name, ".fwc") > 0)) {
+ strcpy(fat_fwc, l_name);
+ }
+ } else {
+ printf(" %s%c\n",
+ l_name,
+ dirc);
+ }
+ }
+ dentptr++;
+ continue;
+ }
+ debug("vfatname: |%s|\n", l_name);
+ } else
+#endif
+ {
+ /* Volume label or VFAT entry */
+ dentptr++;
+ continue;
+ }
+ }
+ if (dentptr->name[0] == 0) {
+ if (dols) {
+ printf("\n%d file(s), %d dir(s)\n\n",
+ files, dirs);
+ }
+ debug("Dentname == NULL - %d\n", i);
+ return NULL;
+ }
+#ifdef CONFIG_SUPPORT_VFAT
+ if (dols && mkcksum(dentptr->name) == prevcksum) {
+ prevcksum = 0xffff;
+ dentptr++;
+ continue;
+ }
+#endif
+ get_name(dentptr, s_name);
+ if (dols) {
+ int isdir = (dentptr->attr & ATTR_DIR);
+ char dirc;
+ int doit = 0;
+
+ if (isdir) {
+ dirs++;
+ dirc = '/';
+ doit = 1;
+ } else {
+ dirc = ' ';
+ if (s_name[0] != 0) {
+ files++;
+ doit = 1;
+ }
+ }
+
+ if (doit) {
+ if (dirc == ' ') {
+ printf(" %8ld %s%c\n",
+ (long)FAT2CPU32(dentptr->size),
+ s_name, dirc);
+ } else {
+ printf(" %s%c\n",
+ s_name, dirc);
+ }
+ }
+
+ dentptr++;
+ continue;
+ }
+
+ if (strcmp(filename, s_name)
+ && strcmp(filename, l_name)) {
+ debug("Mismatch: |%s|%s|\n", s_name, l_name);
+ dentptr++;
+ continue;
+ }
+
+ memcpy(retdent, dentptr, sizeof(dir_entry));
+
+ debug("DentName: %s", s_name);
+ debug(", start: 0x%x", START(dentptr));
+ debug(", size: 0x%x %s\n",
+ FAT2CPU32(dentptr->size),
+ (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
+
+ return retdent;
+ }
+
+ curclust = get_fatent(mydata, curclust);
+ if (CHECK_CLUST(curclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ printf("Invalid FAT entry\n");
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Read boot sector and volume info from a FAT filesystem
+ */
+static int
+read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
+{
+ __u8 *block;
+ volume_info *vistart;
+ int ret = 0;
+
+ if (cur_dev == NULL) {
+ debug("Error: no device selected\n");
+ return -1;
+ }
+
+ block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz);
+ if (block == NULL) {
+ debug("Error: allocating block\n");
+ return -1;
+ }
+
+ if (disk_read(0, 1, block) < 0) {
+ debug("Error: reading block\n");
+ goto fail;
+ }
+
+ memcpy(bs, block, sizeof(boot_sector));
+ bs->reserved = FAT2CPU16(bs->reserved);
+ bs->fat_length = FAT2CPU16(bs->fat_length);
+ bs->secs_track = FAT2CPU16(bs->secs_track);
+ bs->heads = FAT2CPU16(bs->heads);
+ bs->total_sect = FAT2CPU32(bs->total_sect);
+
+ /* FAT32 entries */
+ if (bs->fat_length == 0) {
+ /* Assume FAT32 */
+ bs->fat32_length = FAT2CPU32(bs->fat32_length);
+ bs->flags = FAT2CPU16(bs->flags);
+ bs->root_cluster = FAT2CPU32(bs->root_cluster);
+ bs->info_sector = FAT2CPU16(bs->info_sector);
+ bs->backup_boot = FAT2CPU16(bs->backup_boot);
+ vistart = (volume_info *)(block + sizeof(boot_sector));
+ *fatsize = 32;
+ } else {
+ vistart = (volume_info *)&(bs->fat32_length);
+ *fatsize = 0;
+ }
+ memcpy(volinfo, vistart, sizeof(volume_info));
+
+ if (*fatsize == 32) {
+ if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
+ goto exit;
+ } else {
+ if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
+ *fatsize = 12;
+ goto exit;
+ }
+ if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
+ *fatsize = 16;
+ goto exit;
+ }
+ }
+
+ debug("Error: broken fs_type sign\n");
+fail:
+ ret = -1;
+exit:
+ free(block);
+ return ret;
+}
+
+__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)
+{
+ char fnamecopy[2048];
+ boot_sector bs;
+ volume_info volinfo;
+ fsdata datablock;
+ fsdata *mydata = &datablock;
+ dir_entry *dentptr = NULL;
+ __u16 prevcksum = 0xffff;
+ char *subname = "";
+ __u32 cursect;
+ int idx, isdir = 0;
+ int files = 0, dirs = 0;
+ long ret = -1;
+ int firsttime;
+ __u32 root_cluster = 0;
+ int rootdir_size = 0;
+ int j;
+
+ if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
+ debug("Error: reading boot sector\n");
+ return -1;
+ }
+
+ if (mydata->fatsize == 32) {
+ root_cluster = bs.root_cluster;
+ mydata->fatlength = bs.fat32_length;
+ } else {
+ mydata->fatlength = bs.fat_length;
+ }
+
+ mydata->fat_sect = bs.reserved;
+
+ cursect = mydata->rootdir_sect
+ = mydata->fat_sect + mydata->fatlength * bs.fats;
+
+ mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
+ mydata->clust_size = bs.cluster_size;
+ if (mydata->sect_size != cur_part_info.blksz) {
+ printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
+ mydata->sect_size, cur_part_info.blksz);
+ return -1;
+ }
+
+ if (mydata->fatsize == 32) {
+ mydata->data_begin = mydata->rootdir_sect -
+ (mydata->clust_size * 2);
+ } else {
+ rootdir_size = ((bs.dir_entries[1] * (int)256 +
+ bs.dir_entries[0]) *
+ sizeof(dir_entry)) /
+ mydata->sect_size;
+ mydata->data_begin = mydata->rootdir_sect +
+ rootdir_size -
+ (mydata->clust_size * 2);
+ }
+
+ mydata->fatbufnum = -1;
+ mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
+ if (mydata->fatbuf == NULL) {
+ debug("Error: allocating memory\n");
+ return -1;
+ }
+
+#ifdef CONFIG_SUPPORT_VFAT
+ debug("VFAT Support enabled\n");
+#endif
+ debug("FAT%d, fat_sect: %d, fatlength: %d\n",
+ mydata->fatsize, mydata->fat_sect, mydata->fatlength);
+ debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
+ "Data begins at: %d\n",
+ root_cluster,
+ mydata->rootdir_sect,
+ mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
+ debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
+ mydata->clust_size);
+
+ /* "cwd" is always the root... */
+ while (ISDIRDELIM(*filename))
+ filename++;
+
+ /* Make a copy of the filename and convert it to lowercase */
+ strcpy(fnamecopy, filename);
+ downcase(fnamecopy);
+
+ if (*fnamecopy == '\0') {
+ if (!dols)
+ goto exit;
+
+ dols = LS_ROOT;
+ } else if ((idx = dirdelim(fnamecopy)) >= 0) {
+ isdir = 1;
+ fnamecopy[idx] = '\0';
+ subname = fnamecopy + idx + 1;
+
+ /* Handle multiple delimiters */
+ while (ISDIRDELIM(*subname))
+ subname++;
+ } else if (dols) {
+ isdir = 1;
+ }
+
+ j = 0;
+ while (1) {
+ int i;
+
+ if (j == 0) {
+ debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n",
+ cursect, mydata->clust_size, DIRENTSPERBLOCK);
+
+ if (disk_read(cursect,
+ (mydata->fatsize == 32) ?
+ (mydata->clust_size) :
+ PREFETCH_BLOCKS,
+ do_fat_read_at_block) < 0) {
+ debug("Error: reading rootdir block\n");
+ goto exit;
+ }
+
+ dentptr = (dir_entry *) do_fat_read_at_block;
+ }
+
+ for (i = 0; i < DIRENTSPERBLOCK; i++) {
+ char s_name[14], l_name[VFAT_MAXLEN_BYTES];
+
+ l_name[0] = '\0';
+ if (dentptr->name[0] == DELETED_FLAG) {
+ dentptr++;
+ continue;
+ }
+ if ((dentptr->attr & ATTR_VOLUME)) {
+#ifdef CONFIG_SUPPORT_VFAT
+ if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
+ (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
+ prevcksum =
+ ((dir_slot *)dentptr)->alias_checksum;
+
+ get_vfatname(mydata,
+ root_cluster,
+ do_fat_read_at_block,
+ dentptr, l_name);
+
+ if (dols == LS_ROOT) {
+ char dirc;
+ int doit = 0;
+ int isdir =
+ (dentptr->attr & ATTR_DIR);
+
+ if (isdir) {
+ dirs++;
+ dirc = '/';
+ doit = 1;
+ } else {
+ dirc = ' ';
+ if (l_name[0] != 0) {
+ files++;
+ doit = 1;
+ }
+ }
+ if (doit) {
+ if (dirc == ' ') {
+ printf(" %8ld %s%c\n",
+ (long)FAT2CPU32(dentptr->size),
+ l_name,
+ dirc);
+ } else {
+ printf(" %s%c\n",
+ l_name,
+ dirc);
+ }
+ }
+ dentptr++;
+ continue;
+ }
+ debug("Rootvfatname: |%s|\n",
+ l_name);
+ } else
+#endif
+ {
+ /* Volume label or VFAT entry */
+ dentptr++;
+ continue;
+ }
+ } else if (dentptr->name[0] == 0) {
+ debug("RootDentname == NULL - %d\n", i);
+ if (dols == LS_ROOT) {
+ printf("\n%d file(s), %d dir(s)\n\n",
+ files, dirs);
+ ret = 0;
+ }
+ goto exit;
+ }
+#ifdef CONFIG_SUPPORT_VFAT
+ else if (dols == LS_ROOT &&
+ mkcksum(dentptr->name) == prevcksum) {
+ prevcksum = 0xffff;
+ dentptr++;
+ continue;
+ }
+#endif
+ get_name(dentptr, s_name);
+
+ if (dols == LS_ROOT) {
+ int isdir = (dentptr->attr & ATTR_DIR);
+ char dirc;
+ int doit = 0;
+
+ if (isdir) {
+ dirc = '/';
+ if (s_name[0] != 0) {
+ dirs++;
+ doit = 1;
+ }
+ } else {
+ dirc = ' ';
+ if (s_name[0] != 0) {
+ files++;
+ doit = 1;
+ }
+ }
+ if (doit) {
+ if (dirc == ' ') {
+ printf(" %8ld %s%c\n",
+ (long)FAT2CPU32(dentptr->size),
+ s_name, dirc);
+ } else {
+ printf(" %s%c\n",
+ s_name, dirc);
+ }
+ }
+ dentptr++;
+ continue;
+ }
+
+ if (strcmp(fnamecopy, s_name)
+ && strcmp(fnamecopy, l_name)) {
+ debug("RootMismatch: |%s|%s|\n", s_name,
+ l_name);
+ dentptr++;
+ continue;
+ }
+
+ if (isdir && !(dentptr->attr & ATTR_DIR))
+ goto exit;
+
+ debug("RootName: %s", s_name);
+ debug(", start: 0x%x", START(dentptr));
+ debug(", size: 0x%x %s\n",
+ FAT2CPU32(dentptr->size),
+ isdir ? "(DIR)" : "");
+
+ goto rootdir_done; /* We got a match */
+ }
+ debug("END LOOP: j=%d clust_size=%d\n", j,
+ mydata->clust_size);
+
+ /*
+ * On FAT32 we must fetch the FAT entries for the next
+ * root directory clusters when a cluster has been
+ * completely processed.
+ */
+ ++j;
+ int rootdir_end = 0;
+ if (mydata->fatsize == 32) {
+ if (j == mydata->clust_size) {
+ int nxtsect = 0;
+ int nxt_clust = 0;
+
+ nxt_clust = get_fatent(mydata, root_cluster);
+ rootdir_end = CHECK_CLUST(nxt_clust, 32);
+
+ nxtsect = mydata->data_begin +
+ (nxt_clust * mydata->clust_size);
+
+ root_cluster = nxt_clust;
+
+ cursect = nxtsect;
+ j = 0;
+ }
+ } else {
+ if (j == PREFETCH_BLOCKS)
+ j = 0;
+
+ rootdir_end = (++cursect - mydata->rootdir_sect >=
+ rootdir_size);
+ }
+
+ /* If end of rootdir reached */
+ if (rootdir_end) {
+ if (dols == LS_ROOT) {
+ printf("\n%d file(s), %d dir(s)\n\n",
+ files, dirs);
+ ret = 0;
+ }
+ goto exit;
+ }
+ }
+rootdir_done:
+
+ firsttime = 1;
+
+ while (isdir) {
+ int startsect = mydata->data_begin
+ + START(dentptr) * mydata->clust_size;
+ dir_entry dent;
+ char *nextname = NULL;
+
+ dent = *dentptr;
+ dentptr = &dent;
+
+ idx = dirdelim(subname);
+
+ if (idx >= 0) {
+ subname[idx] = '\0';
+ nextname = subname + idx + 1;
+ /* Handle multiple delimiters */
+ while (ISDIRDELIM(*nextname))
+ nextname++;
+ if (dols && *nextname == '\0')
+ firsttime = 0;
+ } else {
+ if (dols && firsttime) {
+ firsttime = 0;
+ } else {
+ isdir = 0;
+ }
+ }
+
+ if (get_dentfromdir(mydata, startsect, subname, dentptr,
+ isdir ? 0 : dols) == NULL) {
+ if (dols && !isdir)
+ ret = 0;
+ goto exit;
+ }
+
+ if (isdir && !(dentptr->attr & ATTR_DIR))
+ goto exit;
+
+ if (idx >= 0)
+ subname = nextname;
+ }
+
+ ret = get_contents(mydata, dentptr, pos, buffer, maxsize);
+ debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
+
+exit:
+ free(mydata->fatbuf);
+ return ret;
+}
+
+long
+do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
+{
+ return do_fat_read_at(filename, 0, buffer, maxsize, dols);
+}
+
+int file_fat_detectfs(void)
+{
+ boot_sector bs;
+ volume_info volinfo;
+ int fatsize;
+ char vol_label[12];
+
+ if (cur_dev == NULL) {
+ printf("No current device\n");
+ return 1;
+ }
+
+#if defined(CONFIG_CMD_IDE) || \
+ defined(CONFIG_CMD_SATA) || \
+ defined(CONFIG_CMD_SCSI) || \
+ defined(CONFIG_CMD_USB) || \
+ defined(CONFIG_MMC)
+ printf("Interface: ");
+ switch (cur_dev->if_type) {
+ case IF_TYPE_IDE:
+ printf("IDE");
+ break;
+ case IF_TYPE_SATA:
+ printf("SATA");
+ break;
+ case IF_TYPE_SCSI:
+ printf("SCSI");
+ break;
+ case IF_TYPE_ATAPI:
+ printf("ATAPI");
+ break;
+ case IF_TYPE_USB:
+ printf("USB");
+ break;
+ case IF_TYPE_DOC:
+ printf("DOC");
+ break;
+ case IF_TYPE_MMC:
+ printf("MMC");
+ break;
+ default:
+ printf("Unknown");
+ }
+
+ printf("\n Device %d: ", cur_dev->dev);
+ dev_print(cur_dev);
+#endif
+
+ if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
+ printf("\nNo valid FAT fs found\n");
+ return 1;
+ }
+
+ memcpy(vol_label, volinfo.volume_label, 11);
+ vol_label[11] = '\0';
+ volinfo.fs_type[5] = '\0';
+
+ printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part_nr,
+ volinfo.fs_type, vol_label);
+
+ return 0;
+}
+
+int file_fat_ls(const char *dir)
+{
+ return do_fat_read(dir, NULL, 0, LS_YES);
+}
+
+long file_fat_read_at(const char *filename, unsigned long pos, void *buffer,
+ unsigned long maxsize)
+{
+ printf("reading %s\n", filename);
+ return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO);
+}
+
+long file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
+{
+ return file_fat_read_at(filename, 0, buffer, maxsize);
+}
+
+static void uppercase(char *str, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ TOUPPER(*str);
+ str++;
+ }
+}
+
+static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
+{
+ if (!cur_dev || !cur_dev->block_write)
+ return -1;
+
+ if (cur_part_info.start + block + nr_blocks >
+ cur_part_info.start + total_sector) {
+ printf("error: overflow occurs\n");
+ return -1;
+ }
+
+ return cur_dev->block_write(cur_dev->dev,
+ cur_part_info.start + block, nr_blocks, buf);
+}
+
+/*
+ * Set short name in directory entry
+ */
+static void set_name(dir_entry *dirent, const char *filename)
+{
+ char s_name[VFAT_MAXLEN_BYTES];
+ char *period;
+ int period_location, len, i, ext_num;
+
+ if (filename == NULL)
+ return;
+
+ len = strlen(filename);
+ if (len == 0)
+ return;
+
+ memcpy(s_name, filename, len);
+ uppercase(s_name, len);
+
+ period = strchr(s_name, '.');
+ if (period == NULL) {
+ period_location = len;
+ ext_num = 0;
+ } else {
+ period_location = period - s_name;
+ ext_num = len - period_location - 1;
+ }
+
+ /* Pad spaces when the length of file name is shorter than eight */
+ if (period_location < 8) {
+ memcpy(dirent->name, s_name, period_location);
+ for (i = period_location; i < 8; i++)
+ dirent->name[i] = ' ';
+ } else if (period_location == 8) {
+ memcpy(dirent->name, s_name, period_location);
+ } else {
+ memcpy(dirent->name, s_name, 6);
+ dirent->name[6] = '~';
+ dirent->name[7] = '1';
+ }
+
+ if (ext_num < 3) {
+ memcpy(dirent->ext, s_name + period_location + 1, ext_num);
+ for (i = ext_num; i < 3; i++)
+ dirent->ext[i] = ' ';
+ } else
+ memcpy(dirent->ext, s_name + period_location + 1, 3);
+
+ debug("name : %s\n", dirent->name);
+ debug("ext : %s\n", dirent->ext);
+}
+
+static __u8 num_of_fats;
+/*
+ * Write fat buffer into block device
+ */
+static int flush_fat_buffer(fsdata *mydata)
+{
+ int getsize = FATBUFBLOCKS;
+ __u32 fatlength = mydata->fatlength;
+ __u8 *bufptr = mydata->fatbuf;
+ __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
+
+ fatlength *= mydata->sect_size;
+ startblock += mydata->fat_sect;
+
+ if (getsize > fatlength)
+ getsize = fatlength;
+
+ /* Write FAT buf */
+ if (disk_write(startblock, getsize, bufptr) < 0) {
+ debug("error: writing FAT blocks\n");
+ return -1;
+ }
+
+ if (num_of_fats == 2) {
+ /* Update corresponding second FAT blocks */
+ startblock += mydata->fatlength;
+ if (disk_write(startblock, getsize, bufptr) < 0) {
+ debug("error: writing second FAT blocks\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Get the entry at index 'entry' in a FAT (12/16/32) table.
+ * On failure 0x00 is returned.
+ * When bufnum is changed, write back the previous fatbuf to the disk.
+ */
+static __u32 get_fatent_value(fsdata *mydata, __u32 entry)
+{
+ __u32 bufnum;
+ __u32 off16, offset;
+ __u32 ret = 0x00;
+ __u16 val1, val2;
+
+ switch (mydata->fatsize) {
+ case 32:
+ bufnum = entry / FAT32BUFSIZE;
+ offset = entry - bufnum * FAT32BUFSIZE;
+ break;
+ case 16:
+ bufnum = entry / FAT16BUFSIZE;
+ offset = entry - bufnum * FAT16BUFSIZE;
+ break;
+ case 12:
+ bufnum = entry / FAT12BUFSIZE;
+ offset = entry - bufnum * FAT12BUFSIZE;
+ break;
+
+ default:
+ /* Unsupported FAT size */
+ return ret;
+ }
+
+ debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n",
+ mydata->fatsize, entry, entry, offset, offset);
+
+ /* Read a new block of FAT entries into the cache. */
+ if (bufnum != mydata->fatbufnum) {
+ int getsize = FATBUFBLOCKS;
+ __u8 *bufptr = mydata->fatbuf;
+ __u32 fatlength = mydata->fatlength;
+ __u32 startblock = bufnum * FATBUFBLOCKS;
+
+ if (getsize > fatlength)
+ getsize = fatlength;
+
+ fatlength *= mydata->sect_size; /* We want it in bytes now */
+ startblock += mydata->fat_sect; /* Offset from start of disk */
+
+ /* Write back the fatbuf to the disk */
+ if (mydata->fatbufnum != -1) {
+ if (flush_fat_buffer(mydata) < 0)
+ return -1;
+ }
+
+ if (disk_read(startblock, getsize, bufptr) < 0) {
+ debug("Error reading FAT blocks\n");
+ return ret;
+ }
+ mydata->fatbufnum = bufnum;
+ }
+
+ /* Get the actual entry from the table */
+ switch (mydata->fatsize) {
+ case 32:
+ ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
+ break;
+ case 16:
+ ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
+ break;
+ case 12:
+ off16 = (offset * 3) / 4;
+
+ 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;
+ }
+ debug("FAT%d: ret: %08x, entry: %08x, offset: %04x\n",
+ mydata->fatsize, ret, entry, offset);
+
+ return ret;
+}
+
+#ifdef CONFIG_SUPPORT_VFAT
+/*
+ * Set the file name information from 'name' into 'slotptr',
+ */
+static int str2slot(dir_slot *slotptr, const char *name, int *idx)
+{
+ int j, end_idx = 0;
+
+ for (j = 0; j <= 8; j += 2) {
+ if (name[*idx] == 0x00) {
+ slotptr->name0_4[j] = 0;
+ slotptr->name0_4[j + 1] = 0;
+ end_idx++;
+ goto name0_4;
+ }
+ slotptr->name0_4[j] = name[*idx];
+ (*idx)++;
+ end_idx++;
+ }
+ for (j = 0; j <= 10; j += 2) {
+ if (name[*idx] == 0x00) {
+ slotptr->name5_10[j] = 0;
+ slotptr->name5_10[j + 1] = 0;
+ end_idx++;
+ goto name5_10;
+ }
+ slotptr->name5_10[j] = name[*idx];
+ (*idx)++;
+ end_idx++;
+ }
+ for (j = 0; j <= 2; j += 2) {
+ if (name[*idx] == 0x00) {
+ slotptr->name11_12[j] = 0;
+ slotptr->name11_12[j + 1] = 0;
+ end_idx++;
+ goto name11_12;
+ }
+ slotptr->name11_12[j] = name[*idx];
+ (*idx)++;
+ end_idx++;
+ }
+
+ if (name[*idx] == 0x00)
+ return 1;
+
+ return 0;
+/* Not used characters are filled with 0xff 0xff */
+name0_4:
+ for (; end_idx < 5; end_idx++) {
+ slotptr->name0_4[end_idx * 2] = 0xff;
+ slotptr->name0_4[end_idx * 2 + 1] = 0xff;
+ }
+ end_idx = 5;
+name5_10:
+ end_idx -= 5;
+ for (; end_idx < 6; end_idx++) {
+ slotptr->name5_10[end_idx * 2] = 0xff;
+ slotptr->name5_10[end_idx * 2 + 1] = 0xff;
+ }
+ end_idx = 11;
+name11_12:
+ end_idx -= 11;
+ for (; end_idx < 2; end_idx++) {
+ slotptr->name11_12[end_idx * 2] = 0xff;
+ slotptr->name11_12[end_idx * 2 + 1] = 0xff;
+ }
+
+ return 1;
+}
+
+static int is_next_clust(fsdata *mydata, dir_entry *dentptr);
+static void flush_dir_table(fsdata *mydata, dir_entry **dentptr);
+
+/*
+ * Fill dir_slot entries with appropriate name, id, and attr
+ * The real directory entry is returned by 'dentptr'
+ */
+static void
+fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name)
+{
+ dir_slot *slotptr = (dir_slot *)get_contents_vfatname_block;
+ __u8 counter = 0, checksum;
+ int idx = 0, ret;
+ char s_name[16];
+
+ /* Get short file name and checksum value */
+ strncpy(s_name, (*dentptr)->name, 16);
+ checksum = mkcksum(s_name);
+
+ do {
+ memset(slotptr, 0x00, sizeof(dir_slot));
+ ret = str2slot(slotptr, l_name, &idx);
+ slotptr->id = ++counter;
+ slotptr->attr = ATTR_VFAT;
+ slotptr->alias_checksum = checksum;
+ slotptr++;
+ } while (ret == 0);
+
+ slotptr--;
+ slotptr->id |= LAST_LONG_ENTRY_MASK;
+
+ while (counter >= 1) {
+ if (is_next_clust(mydata, *dentptr)) {
+ /* A new cluster is allocated for directory table */
+ flush_dir_table(mydata, dentptr);
+ }
+ memcpy(*dentptr, slotptr, sizeof(dir_slot));
+ (*dentptr)++;
+ slotptr--;
+ counter--;
+ }
+
+ if (is_next_clust(mydata, *dentptr)) {
+ /* A new cluster is allocated for directory table */
+ flush_dir_table(mydata, dentptr);
+ }
+}
+
+static __u32 dir_curclust;
+
+/*
+ * Extract the full long filename starting at 'retdent' (which is really
+ * a slot) into 'l_name'. If successful also copy the real directory entry
+ * into 'retdent'
+ * If additional adjacent cluster for directory entries is read into memory,
+ * then 'get_contents_vfatname_block' is copied into 'get_dentfromdir_block' and
+ * the location of the real directory entry is returned by 'retdent'
+ * Return 0 on success, -1 otherwise.
+ */
+static int
+get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster,
+ dir_entry **retdent, char *l_name)
+{
+ dir_entry *realdent;
+ dir_slot *slotptr = (dir_slot *)(*retdent);
+ dir_slot *slotptr2 = NULL;
+ __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
+ PREFETCH_BLOCKS :
+ mydata->clust_size);
+ __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
+ int idx = 0, cur_position = 0;
+
+ if (counter > VFAT_MAXSEQ) {
+ debug("Error: VFAT name is too long\n");
+ return -1;
+ }
+
+ while ((__u8 *)slotptr < buflimit) {
+ if (counter == 0)
+ break;
+ if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
+ return -1;
+ slotptr++;
+ counter--;
+ }
+
+ if ((__u8 *)slotptr >= buflimit) {
+ if (curclust == 0)
+ return -1;
+ curclust = get_fatent_value(mydata, dir_curclust);
+ if (CHECK_CLUST(curclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ printf("Invalid FAT entry\n");
+ return -1;
+ }
+
+ dir_curclust = curclust;
+
+ if (get_cluster(mydata, curclust, get_contents_vfatname_block,
+ mydata->clust_size * mydata->sect_size) != 0) {
+ debug("Error: reading directory block\n");
+ return -1;
+ }
+
+ slotptr2 = (dir_slot *)get_contents_vfatname_block;
+ while (counter > 0) {
+ if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
+ & 0xff) != counter)
+ return -1;
+ slotptr2++;
+ counter--;
+ }
+
+ /* Save the real directory entry */
+ realdent = (dir_entry *)slotptr2;
+ while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
+ slotptr2--;
+ slot2str(slotptr2, l_name, &idx);
+ }
+ } else {
+ /* Save the real directory entry */
+ realdent = (dir_entry *)slotptr;
+ }
+
+ do {
+ slotptr--;
+ if (slot2str(slotptr, l_name, &idx))
+ break;
+ } while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
+
+ l_name[idx] = '\0';
+ if (*l_name == DELETED_FLAG)
+ *l_name = '\0';
+ else if (*l_name == aRING)
+ *l_name = DELETED_FLAG;
+ downcase(l_name);
+
+ /* Return the real directory entry */
+ *retdent = realdent;
+
+ if (slotptr2) {
+ memcpy(get_dentfromdir_block, get_contents_vfatname_block,
+ mydata->clust_size * mydata->sect_size);
+ cur_position = (__u8 *)realdent - get_contents_vfatname_block;
+ *retdent = (dir_entry *) &get_dentfromdir_block[cur_position];
+ }
+
+ return 0;
+}
+
+#endif
+
+/*
+ * Set the entry at index 'entry' in a FAT (16/32) table.
+ */
+static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
+{
+ __u32 bufnum, offset;
+ __u32 off16;
+ __u16 val;
+
+ switch (mydata->fatsize) {
+ case 32:
+ bufnum = entry / FAT32BUFSIZE;
+ offset = entry - bufnum * FAT32BUFSIZE;
+ break;
+ case 16:
+ bufnum = entry / FAT16BUFSIZE;
+ offset = entry - bufnum * FAT16BUFSIZE;
+ break;
+ case 12:
+ bufnum = entry / FAT12BUFSIZE;
+ offset = entry - bufnum * FAT12BUFSIZE;
+ break;
+ default:
+ /* Unsupported FAT size */
+ return -1;
+ }
+
+ /* Read a new block of FAT entries into the cache. */
+ if (bufnum != mydata->fatbufnum) {
+ int getsize = FATBUFBLOCKS;
+ __u8 *bufptr = mydata->fatbuf;
+ __u32 fatlength = mydata->fatlength;
+ __u32 startblock = bufnum * FATBUFBLOCKS;
+
+ fatlength *= mydata->sect_size;
+ startblock += mydata->fat_sect;
+
+ if (getsize > fatlength)
+ getsize = fatlength;
+
+ if (mydata->fatbufnum != -1) {
+ if (flush_fat_buffer(mydata) < 0)
+ return -1;
+ }
+
+ if (disk_read(startblock, getsize, bufptr) < 0) {
+ debug("Error reading FAT blocks\n");
+ return -1;
+ }
+ mydata->fatbufnum = bufnum;
+ }
+
+ /* Set the actual entry */
+ switch (mydata->fatsize) {
+ case 32:
+ ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
+ break;
+ case 16:
+ ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
+ break;
+ case 12:
+ off16 = (offset * 3) / 4;
+
+ switch (offset & 0x3) {
+ case 0:
+ val = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]&0xf000);
+ ((__u16 *) mydata->fatbuf)[off16]= val|(cpu_to_le16(entry_value)&0x0fff);
+ break;
+ case 1:
+ val = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]&0x0fff);
+ ((__u16 *)mydata->fatbuf)[off16] =val|((cpu_to_le16(entry_value)&0x000f)<<12);
+ val = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]&0xff00);
+ ((__u16 *)mydata->fatbuf)[off16+1] =val|((cpu_to_le16(entry_value)&0x0ff0)>>4);
+ break;
+ case 2:
+ val = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]&0x00ff);
+ ((__u16 *)mydata->fatbuf)[off16] =val|((cpu_to_le16(entry_value)&0x00ff)<<8);
+ val = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]&0xfff0);
+ ((__u16 *)mydata->fatbuf)[off16+1] =val|((cpu_to_le16(entry_value)&0x0f00)>>8);
+ break;
+ case 3:
+ val = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]&0x000f);
+ ((__u16 *) mydata->fatbuf)[off16]= val|((cpu_to_le16(entry_value)&0x0fff)<<4);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Determine the entry value at index 'entry' in a FAT (16/32) table
+ */
+static __u32 determine_fatent(fsdata *mydata, __u32 entry)
+{
+ __u32 next_fat, next_entry = entry + 1;
+
+ while (1) {
+ next_fat = get_fatent_value(mydata, next_entry);
+ if (next_fat == 0) {
+ set_fatent_value(mydata, entry, next_entry);
+ break;
+ }
+ next_entry++;
+ }
+ debug("FAT%d: entry: %08x, entry_value: %04x\n",
+ mydata->fatsize, entry, next_entry);
+
+ return next_entry;
+}
+
+/*
+ * Write at most 'size' bytes from 'buffer' into the specified cluster.
+ * Return 0 on success, -1 otherwise.
+ */
+static int
+set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
+ unsigned long size)
+{
+ int idx = 0;
+ __u32 startsect;
+
+ if (clustnum > 0) {
+ startsect = mydata->data_begin +
+ clustnum * mydata->clust_size;
+
+ if (mydata->fatsize!=32) {//this is for windows format
+ if (startsect < mydata->rootdir_sect){
+ debug("set_cluster\n");
+ startsect=mydata->rootdir_sect;
+ }
+ }
+ }
+ else
+ startsect = mydata->rootdir_sect;
+
+ 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 (size % mydata->sect_size) {
+ __u8 tmpbuf[mydata->sect_size];
+
+ idx = size / mydata->sect_size;
+ buffer += idx * mydata->sect_size;
+ memcpy(tmpbuf, buffer, size % mydata->sect_size);
+
+ if (disk_write(startsect + idx, 1, tmpbuf) < 0) {
+ debug("Error writing data\n");
+ return -1;
+ }
+
+ return 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Find the first empty cluster
+ */
+static int find_empty_cluster(fsdata *mydata)
+{
+ __u32 fat_val, entry = 3;
+
+ while (1) {
+ fat_val = get_fatent_value(mydata, entry);
+ if (fat_val == 0)
+ break;
+ entry++;
+ }
+
+ return entry;
+}
+
+/*
+ * Write directory entries in 'get_dentfromdir_block' to block device
+ */
+static void flush_dir_table(fsdata *mydata, dir_entry **dentptr)
+{
+ int dir_newclust = 0;
+
+ if (set_cluster(mydata, dir_curclust,
+ get_dentfromdir_block,
+ mydata->clust_size * mydata->sect_size) != 0) {
+ printf("error: wrinting directory entry\n");
+ return;
+ }
+ dir_newclust = find_empty_cluster(mydata);
+ set_fatent_value(mydata, dir_curclust, dir_newclust);
+ if (mydata->fatsize == 32)
+ set_fatent_value(mydata, dir_newclust, 0xffffff8);
+ else if (mydata->fatsize == 16)
+ set_fatent_value(mydata, dir_newclust, 0xfff8);
+ else if (mydata->fatsize == 12)
+ set_fatent_value(mydata, dir_newclust, 0xff8);
+
+ dir_curclust = dir_newclust;
+
+ if (flush_fat_buffer(mydata) < 0)
+ return;
+
+ memset(get_dentfromdir_block, 0x00,
+ mydata->clust_size * mydata->sect_size);
+
+ *dentptr = (dir_entry *) get_dentfromdir_block;
+}
+
+/*
+ * Set empty cluster from 'entry' to the end of a file
+ */
+static int clear_fatent(fsdata *mydata, __u32 entry)
+{
+ __u32 fat_val;
+
+ while (1) {
+ fat_val = get_fatent_value(mydata, entry);
+ if (fat_val != 0)
+ set_fatent_value(mydata, entry, 0);
+ else
+ break;
+
+ if (fat_val == 0xfffffff || fat_val == 0xffff)
+ break;
+
+ entry = fat_val;
+ }
+
+ /* Flush fat buffer */
+ if (flush_fat_buffer(mydata) < 0)
+ 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.
+ */
+static int
+set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
+ unsigned long maxsize)
+{
+ unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
+ unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
+ __u32 curclust = START(dentptr);
+ __u32 endclust = 0, newclust = 0;
+ unsigned long actsize;
+
+ debug("Filesize: %ld bytes\n", filesize);
+ if (maxsize > 0 && filesize > maxsize)
+ filesize = maxsize;
+
+ debug("%ld bytes\n", filesize);
+
+ actsize = bytesperclust;
+ endclust = curclust;
+ do {
+ /* search for consecutive clusters */
+ while (actsize < filesize) {
+ newclust = determine_fatent(mydata, endclust);
+
+ if ((newclust - 1) != endclust)
+ goto getit;
+
+ if (CHECK_CLUST(newclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", newclust);
+ debug("Invalid FAT entry\n");
+ return gotsize;
+ }
+ endclust = newclust;
+ actsize += bytesperclust;
+ }
+ /* actsize >= file size */
+ actsize -= bytesperclust;
+ /* set remaining clusters */
+ if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
+ debug("error: writing cluster\n");
+ return -1;
+ }
+
+ /* set remaining bytes */
+ gotsize += (int)actsize;
+ filesize -= actsize;
+ buffer += actsize;
+ actsize = filesize;
+
+ if (set_cluster(mydata, endclust, buffer, (int)actsize) != 0) {
+ debug("error: writing cluster\n");
+ return -1;
+ }
+ gotsize += actsize;
+
+ /* Mark end of file in FAT */
+ if (mydata->fatsize == 16)
+ newclust = 0xffff;
+ else if (mydata->fatsize == 32)
+ newclust = 0xfffffff;
+ else if (mydata->fatsize == 12)
+ newclust = 0x0fff;
+ debug("fatsize %d\n", mydata->fatsize);
+ debug("data_begin 0x%x fatlength 0x%x fat_sect 0x%x clust_size 0x%x\n",
+ mydata->data_begin, mydata->fatlength, mydata->fat_sect,
+ mydata->clust_size);
+
+
+ set_fatent_value(mydata, endclust, newclust);
+ debug("rootdir_sect 0x%x\n", mydata->rootdir_sect);
+
+ return gotsize;
+getit:
+ if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
+ debug("error: writing cluster\n");
+ return -1;
+ }
+ gotsize += (int)actsize;
+ filesize -= actsize;
+ buffer += actsize;
+
+ if (CHECK_CLUST(curclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return gotsize;
+ }
+ actsize = bytesperclust;
+ curclust = endclust = newclust;
+ } while (1);
+}
+
+/*
+ * Fill dir_entry
+ */
+static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
+ const char *filename, __u32 start_cluster, __u32 size, __u8 attr)
+{
+ if (mydata->fatsize == 32)
+ dentptr->starthi =
+ cpu_to_le16((start_cluster & 0xffff0000) >> 16);
+ dentptr->start = cpu_to_le16(start_cluster & 0xffff);
+ dentptr->size = cpu_to_le32(size);
+
+ dentptr->attr = attr;
+
+ set_name(dentptr, filename);
+}
+
+/*
+ * Check whether adding a file makes the file system to
+ * 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)
+{
+ __u32 startsect, sect_num;
+
+ if (clustnum > 0) {
+ startsect = mydata->data_begin +
+ clustnum * mydata->clust_size;
+
+ if (mydata->fatsize!=32) {//this is for windows format
+ if (startsect < mydata->rootdir_sect) {
+ debug("check_overflow\n");
+ startsect=mydata->rootdir_sect;
+ }
+ }
+ } else {
+ startsect = mydata->rootdir_sect;
+ }
+
+ sect_num = size / mydata->sect_size;
+ if (size % mydata->sect_size)
+ sect_num++;
+
+ if (startsect + sect_num > cur_part_info.start + total_sector)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Check if adding several entries exceed one cluster boundary
+ */
+static int is_next_clust(fsdata *mydata, dir_entry *dentptr)
+{
+ int cur_position;
+
+ cur_position = (__u8 *)dentptr - get_dentfromdir_block;
+
+ if (cur_position >= mydata->clust_size * mydata->sect_size)
+ return 1;
+ else
+ return 0;
+}
+
+static dir_entry *empty_dentptr;
+/*
+ * Find a directory entry based on filename or start cluster number
+ * If the directory entry is not found,
+ * the new position for writing a directory entry will be returned
+ */
+static dir_entry *find_directory_entry(fsdata *mydata, int startsect,
+ char *filename, dir_entry *retdent, __u32 start)
+{
+ __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size;
+ //if (mydata->fatsize != 32) {
+ // curclust=0;
+ //}
+ debug("get_dentfromdir: %s\n", filename);
+
+ while (1) {
+ dir_entry *dentptr;
+
+ int i;
+
+ if (get_cluster(mydata, curclust, get_dentfromdir_block,
+ mydata->clust_size * mydata->sect_size) != 0) {
+ printf("Error: reading directory block\n");
+ return NULL;
+ }
+
+ dentptr = (dir_entry *)get_dentfromdir_block;
+
+ dir_curclust = curclust;
+
+ for (i = 0; i < DIRENTSPERCLUST; i++) {
+ char s_name[14], l_name[VFAT_MAXLEN_BYTES];
+
+ l_name[0] = '\0';
+ if (dentptr->name[0] == DELETED_FLAG) {
+ dentptr++;
+ if (is_next_clust(mydata, dentptr))
+ break;
+ continue;
+ }
+ if ((dentptr->attr & ATTR_VOLUME)) {
+#ifdef CONFIG_SUPPORT_VFAT
+ if ((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
+ {
+ /* Volume label or VFAT entry */
+ dentptr++;
+ if (is_next_clust(mydata, dentptr))
+ break;
+ continue;
+ }
+ }
+ if (dentptr->name[0] == 0) {
+ debug("Dentname == NULL - %d\n", i);
+ empty_dentptr = dentptr;
+ return NULL;
+ }
+
+ get_name(dentptr, s_name);
+
+ if (strcmp(filename, s_name)
+ && strcmp(filename, l_name)) {
+ debug("Mismatch: |%s|%s|\n",
+ s_name, l_name);
+ dentptr++;
+ if (is_next_clust(mydata, dentptr))
+ break;
+ continue;
+ }
+
+ memcpy(retdent, dentptr, sizeof(dir_entry));
+
+ debug("DentName: %s", s_name);
+ debug(", start: 0x%x", START(dentptr));
+ debug(", size: 0x%x %s\n",
+ FAT2CPU32(dentptr->size),
+ (dentptr->attr & ATTR_DIR) ?
+ "(DIR)" : "");
+
+ return dentptr;
+ }
+
+ curclust = get_fatent_value(mydata, dir_curclust);
+ if ((curclust >= 0xffffff8) || (curclust >= 0xfff8)) {
+ empty_dentptr = dentptr;
+ return NULL;
+ }
+ if (CHECK_CLUST(curclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static int do_fat_write(const char *filename, void *buffer,
+ unsigned long size)
+{
+ dir_entry *dentptr, *retdent;
+ __u32 startsect;
+ __u32 start_cluster;
+ boot_sector bs;
+ volume_info volinfo;
+ fsdata datablock;
+ fsdata *mydata = &datablock;
+ int cursect;
+ int ret = -1, name_len;
+ char l_filename[VFAT_MAXLEN_BYTES];
+ int write_size = size;
+
+ dir_curclust = 0;
+
+ if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
+ debug("error: reading boot sector\n");
+ return -1;
+ }
+
+ total_sector = bs.total_sect;
+ if (total_sector == 0)
+ total_sector = cur_part_info.size;
+
+ if (mydata->fatsize == 32)
+ mydata->fatlength = bs.fat32_length;
+ else
+ mydata->fatlength = bs.fat_length;
+
+ mydata->fat_sect = bs.reserved;
+
+ cursect = mydata->rootdir_sect
+ = mydata->fat_sect + mydata->fatlength * bs.fats;
+ debug("fat_sect %d numfat %d\n", mydata->fat_sect, bs.fats);
+ num_of_fats = bs.fats;
+
+ mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
+ mydata->clust_size = bs.cluster_size;
+
+ if (mydata->fatsize == 32) {
+ mydata->data_begin = mydata->rootdir_sect -
+ (mydata->clust_size * 2);
+ } else {
+ int rootdir_size;
+
+ rootdir_size = ((bs.dir_entries[1] * (int)256 +
+ bs.dir_entries[0]) *
+ sizeof(dir_entry)) /
+ mydata->sect_size;
+ debug("rootdir_size 0x%x\n",rootdir_size);
+ mydata->data_begin = mydata->rootdir_sect +
+ rootdir_size -
+ (mydata->clust_size * 2);
+ debug("pre data_begin 0x%x\n", mydata->data_begin);
+ }
+
+ mydata->fatbufnum = -1;
+ mydata->fatbuf = malloc(FATBUFSIZE);
+ if (mydata->fatbuf == NULL) {
+ debug("Error: allocating memory\n");
+ return -1;
+ }
+
+ if (disk_read(cursect,
+ (mydata->fatsize == 32) ?
+ (mydata->clust_size) :
+ PREFETCH_BLOCKS, do_fat_read_at_block) < 0) {
+ debug("Error: reading rootdir block\n");
+ goto exit;
+ }
+ dentptr = (dir_entry *) do_fat_read_at_block;
+
+ name_len = strlen(filename);
+ if (name_len >= VFAT_MAXLEN_BYTES)
+ name_len = VFAT_MAXLEN_BYTES - 1;
+
+ memcpy(l_filename, filename, name_len);
+ l_filename[name_len] = 0; /* terminate the string */
+ downcase(l_filename);
+
+ startsect = mydata->rootdir_sect;
+ debug("mydata->roodir_sect %d\n", startsect);
+ retdent = find_directory_entry(mydata, startsect,
+ l_filename, dentptr, 0);
+ if (retdent) {
+ /* Update file size and start_cluster in a directory entry */
+ retdent->size = cpu_to_le32(size);
+ start_cluster = FAT2CPU16(retdent->start);
+ if (mydata->fatsize == 32)
+ start_cluster |=
+ (FAT2CPU16(retdent->starthi) << 16);
+
+ ret = check_overflow(mydata, start_cluster, size);
+ if (ret) {
+ printf("Error: %ld overflow\n", size);
+ goto exit;
+ }
+
+ ret = clear_fatent(mydata, start_cluster);
+ if (ret) {
+ printf("Error: clearing FAT entries\n");
+ goto exit;
+ }
+
+ ret = set_contents(mydata, retdent, buffer, size);
+ if (ret < 0) {
+ printf("Error: writing contents\n");
+ goto exit;
+ }
+ write_size = ret;
+ debug("attempt to write 0x%x bytes\n", write_size);
+
+ /* Flush fat buffer */
+ ret = flush_fat_buffer(mydata);
+ if (ret) {
+ printf("Error: flush fat buffer\n");
+ goto exit;
+ }
+
+ /* Write directory table to device */
+ ret = set_cluster(mydata, dir_curclust,
+ get_dentfromdir_block,
+ mydata->clust_size * mydata->sect_size);
+ if (ret) {
+ printf("Error: writing directory entry\n");
+ goto exit;
+ }
+ } else {
+ /* Set short name to set alias checksum field in dir_slot */
+ set_name(empty_dentptr, filename);
+ fill_dir_slot(mydata, &empty_dentptr, filename);
+
+ ret = start_cluster = find_empty_cluster(mydata);
+ if (ret < 0) {
+ printf("Error: finding empty cluster\n");
+ goto exit;
+ }
+
+ ret = check_overflow(mydata, start_cluster, size);
+ if (ret) {
+ printf("Error: %ld overflow\n", size);
+ goto exit;
+ }
+
+ /* Set attribute as archieve for regular file */
+ fill_dentry(mydata, empty_dentptr, filename,
+ start_cluster, size, 0x20);
+
+ ret = set_contents(mydata, empty_dentptr, buffer, size);
+ if (ret < 0) {
+ printf("Error: writing contents\n");
+ goto exit;
+ }
+ write_size = ret;
+ debug("attempt to write 0x%x bytes\n", write_size);
+
+ /* Flush fat buffer */
+ ret = flush_fat_buffer(mydata);
+ if (ret) {
+ printf("Error: flush fat buffer\n");
+ goto exit;
+ }
+
+ /* Write directory table to device */
+ ret = set_cluster(mydata, dir_curclust,
+ get_dentfromdir_block,
+ mydata->clust_size * mydata->sect_size);
+ if (ret) {
+ printf("Error: writing directory entry\n");
+ goto exit;
+ }
+ }
+
+exit:
+ free(mydata->fatbuf);
+ return ret < 0 ? ret : write_size;
+}
+
+int file_fat_write(const char *filename, void *buffer, unsigned long maxsize)
+{
+ printf("writing %s\n", filename);
+ return do_fat_write(filename, buffer, maxsize);
+}
+
+
+
+
+
+enum {
+// Perhaps this should remain constant
+ info_sector_number = 1,
+// TODO: make these cmdline options
+// dont forget sanity check: backup_boot_sector + 3 <= reserved_sect
+ backup_boot_sector = 3,
+ reserved_sect = 6,
+};
+
+#define NUM_FATS 2
+#define MAX_CLUST_32 0x0FFFFFF0
+#define MAX_CLUST_12 0x0FF0
+
+#define FAT_FSINFO_SIG1 0x41615252
+#define FAT_FSINFO_SIG2 0x61417272
+#define BOOT_SIGN 0xAA55
+#define MARK_CLUSTER(cluster, value) \
+ ((uint32_t *)fat)[cluster] = cpu_to_le32(value)
+#define EOF_FAT32 0x0FFFFFF8
+#define reserved_sect16or12 1
+
+int
+format_fat32(void)
+{
+ uint32_t sect_per_clust=1;
+ uint32_t sect_per_fat=1;
+ uint32_t total_clust;
+ const char *volume_label = "";
+ unsigned bufsize = reserved_sect;
+ void *buffer;
+ boot_sector32 *bs;
+ fat32_fsinfo *fsinfo;
+
+ if ((!strcmp(cur_part_info.type, "5")) ||
+ (!strcmp(cur_part_info.type, "15"))||
+ (!strcmp(cur_part_info.type, "133"))){
+ printf("Cannot format extended partition!\n");
+ return -1;
+ }
+
+
+
+ /* For FAT32, try to do the same as M$'s format command
+ * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20):
+ * fs size <= 260M: 0.5k clusters
+ * fs size <= 8G: 4k clusters
+ * fs size <= 16G: 8k clusters
+ * fs size > 16G: 16k clusters
+ */
+ if (cur_part_info.size< 32.5*1024*2) {
+ printf("The size of the device is too small for FAT32, please use FAT16/FAT12\n");
+ return -1;
+ } else if (cur_part_info.size> 260*1024*2) {
+ sect_per_clust=8;
+ if (cur_part_info.size>8*1024*1024*2) {
+ sect_per_clust=16;
+ if (cur_part_info.size>16*1024*1024*2) {
+ sect_per_clust=32;
+ if (cur_part_info.size>32*1024*1024*2) {
+ sect_per_clust=64;
+ if (cur_part_info.size>2*1024*1024*1024*2) {
+ printf("FAT32 doesn't support larger than 2T\n");
+ return -1;
+ }
+ }
+ }
+ }
+
+ }
+ sect_per_fat = 1;
+ while (1) {
+ while (1) {
+ int spf_adj;
+ unsigned int tcl = (cur_part_info.size - reserved_sect - NUM_FATS * sect_per_fat) / sect_per_clust;
+ // tcl may be > MAX_CLUST_32 here, but it may be
+ // because sect_per_fat is underestimated,
+ // and with increased sect_per_fat it still may become
+ // <= MAX_CLUST_32. Therefore, we do not check
+ // against MAX_CLUST_32, but against a bigger const:
+ if (tcl > 0x80ffffff)
+ goto next;
+ total_clust = tcl; // fits in uint32_t
+ // Every cluster needs 4 bytes in FAT. +2 entries since
+ // FAT has space for non-existent clusters 0 and 1.
+ // Let's see how many sectors that needs.
+ //May overflow at "*4":
+ //spf_adj = ((total_clust+2) * 4 + bytes_per_sect-1) / bytes_per_sect - sect_per_fat;
+ //Same in the more obscure, non-overflowing form:
+ spf_adj = ((total_clust+2) + (512/4)-1) / (512/4) - sect_per_fat;
+#if 0
+ printf("sect_per_clust:%u sect_per_fat:%u total_clust:%u",
+ sect_per_clust, sect_per_fat, (int)tcl);
+ printf("adjust to sect_per_fat:%d", spf_adj);
+#endif
+ if (spf_adj <= 0) {
+ // do not need to adjust sect_per_fat.
+ // so, was total_clust too big after all?
+ if (total_clust <= MAX_CLUST_32)
+ goto found_total_clust; // no
+ // yes, total_clust is _a bit_ too big
+ goto next;
+ }
+ // adjust sect_per_fat, go back and recalc total_clust
+ // (note: just "sect_per_fat += spf_adj" isn't ok)
+ sect_per_fat += ((unsigned)spf_adj / 2) | 1;
+ }
+ next:
+ if (sect_per_clust == 128) {
+ printf("can't make FAT32 with >128 sectors/cluster");
+ return -1;
+ }
+ sect_per_clust *= 2;
+ sect_per_fat = (sect_per_fat / 2) | 1;
+ }
+found_total_clust:
+
+
+ bufsize |= 2; // use this instead
+ bufsize |= sect_per_clust;
+ //printf("buffer size is 0x%x\n", bufsize);
+ buffer = memalign(ARCH_DMA_MINALIGN, bufsize*512);
+ if (buffer == NULL) {
+ printf("Error: allocating block\n");
+ return -1;
+ }
+
+
+ memset(buffer, 0, bufsize*512);
+ bs=(void*)buffer;
+ fsinfo=(void*)(buffer + 512);
+
+ strcpy(bs->ignored, "\xeb\x58\x90" "MSWIN4.1"); // system_id[8] included :)
+ STORE_LE(bs->sector_size[0], 0x00);//(__u16)cur_part_info.blksz);
+ STORE_LE(bs->sector_size[1], 0x02);//(__u16)cur_part_info.blksz);
+ STORE_LE(bs->cluster_size, (__u8)sect_per_clust);
+ // cast in needed on big endian to suppress a warning
+ STORE_LE(bs->reserved, (uint16_t)reserved_sect);
+ STORE_LE(bs->fats, 2);
+ //STORE_LE(bs->dir_entries[0], 0); // for FAT32, stays 0
+ //STORE_LE(bs->dir_entries[1], 0); // for FAT32, stays 0
+ //if (cur_part_info.size <= 0xffff) {
+ // STORE_LE(bs->sectors[0], (__u8)(cur_part_info.size&0xff));
+ // STORE_LE(bs->sectors[1], (__u8)((cur_part_info.size&0xff00)>>8));
+ //}
+ STORE_LE(bs->media, 0xf8);
+ //STORE_LE(bs->fat_length, 0);
+ STORE_LE(bs->secs_track, 63);
+ STORE_LE(bs->heads, 255);
+ STORE_LE(bs->hidden, 0);
+ STORE_LE(bs->total_sect, cur_part_info.size);
+ STORE_LE(bs->fat32_length, sect_per_fat);
+ STORE_LE(bs->flags, 0);
+ //STORE_LE(bs->version[0], 0);
+ //STORE_LE(bs->version[1], 0);
+ STORE_LE(bs->root_cluster, 2);
+ STORE_LE(bs->info_sector, info_sector_number);
+ STORE_LE(bs->backup_boot, backup_boot_sector);
+ //STORE_LE(bs->reserved2[0], 0);
+ //STORE_LE(bs->reserved2[1], 0);
+ //STORE_LE(bs->reserved2[2], 0);
+ //STORE_LE(bs->reserved2[3], 0);
+ //STORE_LE(bs->reserved2[4], 0);
+ //STORE_LE(bs->reserved2[5], 0);
+ STORE_LE(bs->boot_signature, 0x29);
+ //STORE_LE(bs->volumeID[0], 0);
+ //STORE_LE(bs->volumeID[1], 0);
+ //STORE_LE(bs->volumeID[2], 0);
+ //STORE_LE(bs->volumeID[3], 0);
+ strncpy(bs->filesystype, "FAT32 ", sizeof(bs->filesystype));
+ strncpy(bs->volumelabel, volume_label, sizeof(bs->volumelabel));
+
+ STORE_LE(fsinfo->signature1, FAT_FSINFO_SIG1);
+ STORE_LE(fsinfo->signature2, FAT_FSINFO_SIG2);
+ // we've allocated cluster 2 for the root dir
+ STORE_LE(fsinfo->free_clusters, (total_clust - 1));
+ STORE_LE(fsinfo->next_cluster, 2);
+ STORE_LE(fsinfo->boot_sign, BOOT_SIGN);
+
+ //set reserved region
+ {
+ *((__u8*)bs+510)=0x55;
+ *((__u8*)bs+511)=0xAA;
+ *((__u8*)fsinfo+510)=0x55;
+ *((__u8*)fsinfo+511)=0xAA;
+ if (disk_write(0, backup_boot_sector, bs) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ if (disk_write(backup_boot_sector, reserved_sect - backup_boot_sector, bs) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ }
+
+ //set fat
+ {
+ unsigned i=0,j=0;
+ unsigned char *fat = (void*)buffer;
+ memset(bs, 0, 512 * 2);
+ // initial FAT entries
+ MARK_CLUSTER(0, 0x0fffff00 | 0xf8);
+ MARK_CLUSTER(1, 0xffffffff);
+ // mark cluster 2 as EOF (used for root dir)
+ MARK_CLUSTER(2, EOF_FAT32);
+ for (i = 0; i < NUM_FATS; i++) {
+ if (disk_write(reserved_sect+i*sect_per_fat, 1, buffer) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ for (j = 1; j < sect_per_fat; j++)
+ if (disk_write(reserved_sect+i*sect_per_fat+j, 1, buffer+512) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ }
+ memset(buffer, 0, sect_per_clust * 512);
+ for(j=0;j<sect_per_clust;j++)
+ if (disk_write(reserved_sect+i*sect_per_fat+j, 1, buffer) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ }
+
+ free(buffer);
+ printf("succeed\n");
+ return 0;
+fail:
+ printf("failed\n");
+ free(buffer);
+ return -1;
+}
+
+static void mark_FAT_cluster(char size_fat, char* fat, int cluster, unsigned int value)
+{
+ switch (size_fat) {
+ case 12:
+ value &= 0x0fff;
+ if (((cluster * 3) & 0x1) == 0) {
+ fat[3 * cluster / 2] = (unsigned char)(value & 0x00ff);
+ fat[(3 * cluster / 2) + 1] =
+ (unsigned char)((fat[(3 * cluster / 2) + 1] & 0x00f0)
+ | ((value & 0x0f00) >> 8));
+ } else {
+ fat[3 * cluster / 2] =
+ (unsigned char)((fat[3 * cluster / 2] & 0x000f) |
+ ((value & 0x000f) << 4));
+ fat[(3 * cluster / 2) + 1] = (unsigned char)((value & 0x0ff0) >> 4);
+ }
+ break;
+
+ case 16:
+ value &= 0xffff;
+ fat[2 * cluster] = (unsigned char)(value & 0x00ff);
+ fat[(2 * cluster) + 1] = (unsigned char)(value >> 8);
+ break;
+
+ case 32:
+ value &= 0xfffffff;
+ fat[4 * cluster] = (unsigned char)(value & 0x000000ff);
+ fat[(4 * cluster) + 1] = (unsigned char)((value & 0x0000ff00) >> 8);
+ fat[(4 * cluster) + 2] = (unsigned char)((value & 0x00ff0000) >> 16);
+ fat[(4 * cluster) + 3] = (unsigned char)((value & 0xff000000) >> 24);
+ break;
+
+ default:
+ printf("Bad FAT size (not 12, 16, or 32)\n");
+ }
+}
+
+
+int
+format_fat16(void)
+{
+ uint32_t sect_per_clust=1;
+ uint32_t sect_per_fat=1;
+ uint32_t total_clust;
+ const char *volume_label = "";
+ unsigned bufsize = reserved_sect;
+ void *buffer;
+ boot_sector16or12 *bs;
+
+ if ((!strcmp(cur_part_info.type, "5")) ||
+ (!strcmp(cur_part_info.type, "15"))||
+ (!strcmp(cur_part_info.type, "133"))){
+ printf("Cannot format extended partition!\n");
+ return -1;
+ }
+
+
+ /* For FAT32, try to do the same as M$'s format command
+ * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20):
+ * fs size <= 260M: 0.5k clusters
+ * fs size <= 8G: 4k clusters
+ * fs size <= 16G: 8k clusters
+ * fs size > 16G: 16k clusters
+ */
+ if (cur_part_info.size< 4.1*1024*2) {
+ printf("The size of the device is too small for FAT16, please use FAT12\n");
+ return -1;
+ } else if (cur_part_info.size> 16*1024*2) {
+ sect_per_clust=4;
+ if (cur_part_info.size>128*1024*2) {
+ sect_per_clust=8;
+ if (cur_part_info.size>256*1024*2) {
+ sect_per_clust=16;
+ if (cur_part_info.size>512*1024*2) {
+ sect_per_clust=32;
+ if (cur_part_info.size>1024*1024*2) {
+ sect_per_clust=64;
+ if (cur_part_info.size>2*1024*1024*2){
+ printf("FAT16 doesn't support larger than 2G\n");
+ return -1;
+ }
+ }
+
+ }
+ }
+ }
+
+ }
+ //we have to decide sect_per_fat
+ {
+ unsigned int RootDirSectors = ((512*32)+(512-1))/512;
+ unsigned int TmpVal1 = cur_part_info.size-(1+RootDirSectors);
+ unsigned int TmpVal2 = (256*sect_per_clust)+2;;
+ unsigned int FATSz = (TmpVal1+TmpVal2-1)/TmpVal2;
+ sect_per_fat = FATSz;
+ }
+
+
+ debug("sect_per_fat 0x%x\n", sect_per_fat);
+ debug("sect_per_clust 0x%x\n", sect_per_clust);
+ bufsize |= 2; // use this instead
+ bufsize |= sect_per_clust;
+ buffer = memalign(ARCH_DMA_MINALIGN, bufsize*512);
+ if (buffer == NULL) {
+ printf("Error: allocating block\n");
+ return -1;
+ }
+
+
+ memset(buffer, 0, bufsize*512);
+ bs=(void*)buffer;
+
+ strcpy(bs->ignored, "\xeb\x58\x90" "MSWIN4.1"); // system_id[8] included :)
+ STORE_LE(bs->sector_size[0], 0x00);//(__u16)cur_part_info.blksz);
+ STORE_LE(bs->sector_size[1], 0x02);//(__u16)cur_part_info.blksz);
+ STORE_LE(bs->cluster_size, (__u8)sect_per_clust);
+ // cast in needed on big endian to suppress a warning
+ STORE_LE(bs->reserved, 1);
+ STORE_LE(bs->fats, 2);
+ STORE_LE(bs->dir_entries[0], 0x00);
+ STORE_LE(bs->dir_entries[1], 0x02);
+ if (cur_part_info.size <= 0xffff) {
+ STORE_LE(bs->sectors[0], (__u8)(cur_part_info.size&0xff));
+ STORE_LE(bs->sectors[1], (__u8)((cur_part_info.size&0xff00)>>8));
+ }
+ STORE_LE(bs->media, 0xf8);
+ STORE_LE(bs->fat_length, sect_per_fat);
+ STORE_LE(bs->secs_track, 63);
+ STORE_LE(bs->heads, 255);
+ STORE_LE(bs->hidden, 0);
+ STORE_LE(bs->total_sect, cur_part_info.size);
+ STORE_LE(bs->drive_number, 0);
+ STORE_LE(bs->boot_signature, 0x29);
+ //STORE_LE(bs->volumeID[0], 0);
+ //STORE_LE(bs->volumeID[1], 0);
+ //STORE_LE(bs->volumeID[2], 0);
+ //STORE_LE(bs->volumeID[3], 0);
+ strncpy(bs->filesystype, "FAT16 ", sizeof(bs->filesystype));
+ strncpy(bs->volumelabel, volume_label, sizeof(bs->volumelabel));
+
+
+ //set reserved region
+ {
+ *((__u8*)bs+510)=0x55;
+ *((__u8*)bs+511)=0xAA;
+ if (disk_write(0, 1, bs) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ }
+
+
+ {
+ //set fat
+ unsigned i=0,j=0;
+ unsigned char *fat = (void*)buffer;
+ memset(bs, 0, 512 * 2);
+ // initial FAT entries
+ mark_FAT_cluster(16, fat, 0, 0xfffffff8);
+ mark_FAT_cluster(16, fat, 1, 0xffffffff);
+ for (i = 0; i < NUM_FATS; i++) {
+ if (disk_write(1+i*sect_per_fat, 1, buffer) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ for (j = 1; j < sect_per_fat; j++)
+ if (disk_write(1+i*sect_per_fat+j, 1, buffer+512) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ }
+
+ //set root directory region
+
+ memset(buffer, 0, 512);
+ for(j=0;j<32;j++)
+ if (disk_write(1+NUM_FATS*sect_per_fat+j, 1, buffer) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ }
+
+ free(buffer);
+ printf("succeed\n");
+ return 0;
+fail:
+ printf("failed\n");
+ free(buffer);
+ return -1;
+}
+
+int
+format_fat12(void)
+{
+ uint32_t sect_per_clust=0x40;
+ uint32_t sect_per_fat=1;
+ uint32_t total_clust;
+ const char *volume_label = "";
+ unsigned bufsize = reserved_sect;
+ void *buffer;
+ boot_sector16or12 *bs;
+
+ if ((!strcmp(cur_part_info.type, "5")) ||
+ (!strcmp(cur_part_info.type, "15"))||
+ (!strcmp(cur_part_info.type, "133"))){
+ printf("Cannot format extended partition!\n");
+ return -1;
+ }
+
+
+
+ /* For FAT32, try to do the same as M$'s format command
+ * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20):
+ * fs size <= 260M: 0.5k clusters
+ * fs size <= 8G: 4k clusters
+ * fs size <= 16G: 8k clusters
+ * fs size > 16G: 16k clusters
+ */
+ if (cur_part_info.size > 255*63) {
+ printf("The size is larger than 7.8MB, Please use the FAT16/FAT32\n");
+ return -1;
+ }
+
+ //we have to decide sect_per_fat
+ sect_per_fat=0x0c;
+ sect_per_clust=4;
+
+ bufsize |= 2; // use this instead
+ bufsize |= sect_per_clust;
+ buffer = memalign(ARCH_DMA_MINALIGN, bufsize*512);
+ if (buffer == NULL) {
+ printf("Error: allocating block\n");
+ return -1;
+ }
+
+
+ memset(buffer, 0, bufsize*512);
+ bs=(void*)buffer;
+
+ strcpy(bs->ignored, "\xeb\x58\x90" "MSWIN4.1"); // system_id[8] included :)
+ STORE_LE(bs->sector_size[0], 0x00);//(__u16)cur_part_info.blksz);
+ STORE_LE(bs->sector_size[1], 0x02);//(__u16)cur_part_info.blksz);
+ STORE_LE(bs->cluster_size, (__u8)sect_per_clust);
+ // cast in needed on big endian to suppress a warning
+ STORE_LE(bs->reserved, sect_per_clust);
+ STORE_LE(bs->fats, 2);
+ STORE_LE(bs->dir_entries[0], 0x00);
+ STORE_LE(bs->dir_entries[1], 0x02);
+ if (cur_part_info.size <= 0xffff) {
+ STORE_LE(bs->sectors[0], (__u8)(cur_part_info.size&0xff));
+ STORE_LE(bs->sectors[1], (__u8)((cur_part_info.size&0xff00)>>8));
+ }
+ STORE_LE(bs->media, 0xf8);
+ STORE_LE(bs->fat_length, sect_per_fat);
+ STORE_LE(bs->secs_track, 63);
+ STORE_LE(bs->heads, 255);
+ STORE_LE(bs->hidden, 0);
+ STORE_LE(bs->total_sect, cur_part_info.size);
+ STORE_LE(bs->drive_number, 0);
+ STORE_LE(bs->boot_signature, 0x29);
+ //STORE_LE(bs->volumeID[0], 0);
+ //STORE_LE(bs->volumeID[1], 0);
+ //STORE_LE(bs->volumeID[2], 0);
+ //STORE_LE(bs->volumeID[3], 0);
+ strncpy(bs->filesystype, "FAT12 ", sizeof(bs->filesystype));
+ strncpy(bs->volumelabel, volume_label, sizeof(bs->volumelabel));
+
+
+ //set reserved region
+ {
+ *((__u8*)bs+510)=0x55;
+ *((__u8*)bs+511)=0xAA;
+ if (disk_write(0, 1, bs) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ }
+
+
+ {
+ //set fat
+ unsigned i=0,j=0;
+ unsigned int reserver12=sect_per_clust;
+ unsigned char *fat = (void*)buffer;
+ memset(bs, 0, 512 * 2);
+ // initial FAT entries
+ mark_FAT_cluster(12, fat, 0, 0xfffffff8);
+ mark_FAT_cluster(12, fat, 1, 0xffffffff);
+
+ for (i = 0; i < NUM_FATS; i++) {
+ if (disk_write(reserver12+i*sect_per_fat, 1, buffer) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ for (j = 1; j < sect_per_fat; j++)
+ if (disk_write(reserver12+i*sect_per_fat+j, 1, buffer+512) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ }
+
+ //set root directory region
+
+ memset(buffer, 0, 512);
+ for(j=0;j<32;j++)
+ if (disk_write(reserver12+NUM_FATS*sect_per_fat+j, 1, buffer) < 0) {
+ printf("Error: writing block\n");
+ goto fail;
+ }
+ }
+
+ free(buffer);
+ printf("succeed\n");
+ return 0;
+ fail:
+ printf("failed\n");
+ free(buffer);
+ return -1;
+
+}
+
diff --git a/fs/fat/file.c b/fs/fat/file.c
new file mode 100755
index 0000000..f999ac5
--- /dev/null
+++ b/fs/fat/file.c
@@ -0,0 +1,208 @@
+/*
+ * file.c
+ *
+ * Mini "VFS" by Marcus Sundberg
+ *
+ * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
+ * 2003-03-10 - kharris@nexus-tech.net - ported to uboot
+ *
+ * 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
+ */
+
+#include <common.h>
+#include <config.h>
+#include <malloc.h>
+#include <fat.h>
+#include <linux/stat.h>
+#include <linux/time.h>
+
+#if (CONFIG_COMMANDS & CFG_CMD_FAT)
+
+/* Supported filesystems */
+static const struct filesystem filesystems[] = {
+ { file_fat_detectfs, file_fat_ls, file_fat_read, "FAT" },
+};
+#define NUM_FILESYS (sizeof(filesystems)/sizeof(struct filesystem))
+
+/* The filesystem which was last detected */
+static int current_filesystem = FSTYPE_NONE;
+
+/* The current working directory */
+#define CWD_LEN 511
+char file_cwd[CWD_LEN+1] = "/";
+
+const char *
+file_getfsname(int idx)
+{
+ if (idx < 0 || idx >= NUM_FILESYS) return NULL;
+
+ return filesystems[idx].name;
+}
+
+
+static void
+pathcpy(char *dest, const char *src)
+{
+ char *origdest = dest;
+
+ do {
+ if (dest-file_cwd >= CWD_LEN) {
+ *dest = '\0';
+ return;
+ }
+ *(dest) = *(src);
+ if (*src == '\0') {
+ if (dest-- != origdest && ISDIRDELIM(*dest)) {
+ *dest = '\0';
+ }
+ return;
+ }
+ ++dest;
+ if (ISDIRDELIM(*src)) {
+ while (ISDIRDELIM(*src)) src++;
+ } else {
+ src++;
+ }
+ } while (1);
+}
+
+
+int
+file_cd(const char *path)
+{
+ if (ISDIRDELIM(*path)) {
+ while (ISDIRDELIM(*path)) path++;
+ strncpy(file_cwd+1, path, CWD_LEN-1);
+ } else {
+ const char *origpath = path;
+ char *tmpstr = file_cwd;
+ int back = 0;
+
+ while (*tmpstr != '\0') tmpstr++;
+ do {
+ tmpstr--;
+ } while (ISDIRDELIM(*tmpstr));
+
+ while (*path == '.') {
+ path++;
+ while (*path == '.') {
+ path++;
+ back++;
+ }
+ if (*path != '\0' && !ISDIRDELIM(*path)) {
+ path = origpath;
+ back = 0;
+ break;
+ }
+ while (ISDIRDELIM(*path)) path++;
+ origpath = path;
+ }
+
+ while (back--) {
+ /* Strip off path component */
+ while (!ISDIRDELIM(*tmpstr)) {
+ tmpstr--;
+ }
+ if (tmpstr == file_cwd) {
+ /* Incremented again right after the loop. */
+ tmpstr--;
+ break;
+ }
+ /* Skip delimiters */
+ while (ISDIRDELIM(*tmpstr)) tmpstr--;
+ }
+ tmpstr++;
+ if (*path == '\0') {
+ if (tmpstr == file_cwd) {
+ *tmpstr = '/';
+ tmpstr++;
+ }
+ *tmpstr = '\0';
+ return 0;
+ }
+ *tmpstr = '/';
+ pathcpy(tmpstr+1, path);
+ }
+
+ return 0;
+}
+
+
+int
+file_detectfs(void)
+{
+ int i;
+
+ current_filesystem = FSTYPE_NONE;
+
+ for (i = 0; i < NUM_FILESYS; i++) {
+ if (filesystems[i].detect() == 0) {
+ strcpy(file_cwd, "/");
+ current_filesystem = i;
+ break;
+ }
+ }
+
+ return current_filesystem;
+}
+
+
+int
+file_ls(const char *dir)
+{
+ char fullpath[1024];
+ const char *arg;
+
+ if (current_filesystem == FSTYPE_NONE) {
+ printf("Can't list files without a filesystem!\n");
+ return -1;
+ }
+
+ if (ISDIRDELIM(*dir)) {
+ arg = dir;
+ } else {
+ sprintf(fullpath, "%s/%s", file_cwd, dir);
+ arg = fullpath;
+ }
+ return filesystems[current_filesystem].ls(arg);
+}
+
+
+long
+file_read(const char *filename, void *buffer, unsigned long maxsize)
+{
+ char fullpath[1024];
+ const char *arg;
+
+ if (current_filesystem == FSTYPE_NONE) {
+ printf("Can't load file without a filesystem!\n");
+ return -1;
+ }
+
+ if (ISDIRDELIM(*filename)) {
+ arg = filename;
+ } else {
+ sprintf(fullpath, "%s/%s", file_cwd, filename);
+ arg = fullpath;
+ }
+
+ return filesystems[current_filesystem].read(arg, buffer, maxsize);
+}
+
+#endif /* #if (CONFIG_COMMANDS & CFG_CMD_FAT) */