]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - fs/fat/fat.c
Merge branch 'master' of git://git.denx.de/u-boot-ti
[karo-tx-uboot.git] / fs / fat / fat.c
index 19f6a8c0af63acbe5b95fc2aa822d823354625c9..561921fa2d364e548629fe8152cb953861699b3f 100644 (file)
@@ -6,23 +6,7 @@
  * 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
+ * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #include <part.h>
 #include <malloc.h>
 #include <linux/compiler.h>
+#include <linux/ctype.h>
+
+#ifdef CONFIG_SUPPORT_VFAT
+static const int vfat_enabled = 1;
+#else
+static const int vfat_enabled = 0;
+#endif
 
 /*
  * Convert a string to lowercase.
 static void downcase(char *str)
 {
        while (*str != '\0') {
-               TOLOWER(*str);
+               *str = tolower(*str);
                str++;
        }
 }
 
 static block_dev_desc_t *cur_dev;
-static unsigned int cur_part_nr;
 static disk_partition_t cur_part_info;
 
 #define DOS_BOOT_MAGIC_OFFSET  0x1fe
@@ -62,43 +52,12 @@ static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
                        cur_part_info.start + block, nr_blocks, buf);
 }
 
-int fat_register_device(block_dev_desc_t * dev_desc, int part_no)
+int fat_set_blk_dev(block_dev_desc_t *dev_desc, disk_partition_t *info)
 {
        ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
 
-       /* 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 != 1) {
-                       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));
-       }
+       cur_dev = dev_desc;
+       cur_part_info = *info;
 
        /* Make sure it has a valid FAT header */
        if (disk_read(0, 1, buffer) != 1) {
@@ -122,6 +81,34 @@ int fat_register_device(block_dev_desc_t * dev_desc, int part_no)
        return -1;
 }
 
+int fat_register_device(block_dev_desc_t *dev_desc, int part_no)
+{
+       disk_partition_t info;
+
+       /* First close any currently found FAT filesystem */
+       cur_dev = NULL;
+
+       /* Read the partition table, if present */
+       if (get_partition_info(dev_desc, part_no, &info)) {
+               if (part_no != 0) {
+                       printf("** Partition %d not valid on device %d **\n",
+                                       part_no, dev_desc->dev);
+                       return -1;
+               }
+
+               info.start = 0;
+               info.size = dev_desc->lba;
+               info.blksz = dev_desc->blksz;
+               info.name[0] = 0;
+               info.type[0] = 0;
+               info.bootable = 0;
+#ifdef CONFIG_PARTITION_UUIDS
+               info.uuid[0] = 0;
+#endif
+       }
+
+       return fat_set_blk_dev(dev_desc, &info);
+}
 
 /*
  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
@@ -328,13 +315,16 @@ get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
 }
 
 /*
- * Read at most 'maxsize' bytes from the file associated with 'dentptr'
+ * 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, __u8 *buffer,
-            unsigned long maxsize)
+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;
@@ -344,11 +334,58 @@ get_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
 
        debug("Filesize: %ld bytes\n", filesize);
 
-       if (maxsize > 0 && filesize > maxsize)
-               filesize = maxsize;
+       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;
 
@@ -395,7 +432,6 @@ getit:
        } while (1);
 }
 
-#ifdef CONFIG_SUPPORT_VFAT
 /*
  * Extract the file name information from 'slotptr' into 'l_name',
  * starting at l_name[*idx].
@@ -433,9 +469,6 @@ static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
  * into 'retdent'
  * Return 0 on success, -1 otherwise.
  */
-__u8 get_vfatname_block[MAX_CLUSTSIZE]
-       __aligned(ARCH_DMA_MINALIGN);
-
 static int
 get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
             dir_entry *retdent, char *l_name)
@@ -474,13 +507,13 @@ get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
                        return -1;
                }
 
-               if (get_cluster(mydata, curclust, get_vfatname_block,
+               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_vfatname_block;
+               slotptr2 = (dir_slot *)get_contents_vfatname_block;
                while (counter > 0) {
                        if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
                            & 0xff) != counter)
@@ -491,7 +524,7 @@ get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
 
                /* Save the real directory entry */
                realdent = (dir_entry *)slotptr2;
-               while ((__u8 *)slotptr2 > get_vfatname_block) {
+               while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
                        slotptr2--;
                        slot2str(slotptr2, l_name, &idx);
                }
@@ -520,19 +553,19 @@ get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
 }
 
 /* Calculate short name checksum */
-static __u8 mkcksum(const char *str)
+static __u8 mkcksum(const char name[8], const char ext[3])
 {
        int i;
 
        __u8 ret = 0;
 
-       for (i = 0; i < 11; i++) {
-               ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i];
-       }
+       for (i = 0; i < 8; i++)
+               ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + name[i];
+       for (i = 0; i < 3; i++)
+               ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + ext[i];
 
        return ret;
 }
-#endif /* CONFIG_SUPPORT_VFAT */
 
 /*
  * Get the directory entry associated with 'filename' from the directory
@@ -573,8 +606,8 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
                                continue;
                        }
                        if ((dentptr->attr & ATTR_VOLUME)) {
-#ifdef CONFIG_SUPPORT_VFAT
-                               if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
+                               if (vfat_enabled &&
+                                   (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
                                    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
                                        prevcksum = ((dir_slot *)dentptr)->alias_checksum;
                                        get_vfatname(mydata, curclust,
@@ -614,9 +647,7 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
                                                continue;
                                        }
                                        debug("vfatname: |%s|\n", l_name);
-                               } else
-#endif
-                               {
+                               } else {
                                        /* Volume label or VFAT entry */
                                        dentptr++;
                                        continue;
@@ -630,13 +661,15 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
                                debug("Dentname == NULL - %d\n", i);
                                return NULL;
                        }
-#ifdef CONFIG_SUPPORT_VFAT
-                       if (dols && mkcksum(dentptr->name) == prevcksum) {
-                               prevcksum = 0xffff;
-                               dentptr++;
-                               continue;
+                       if (vfat_enabled) {
+                               __u8 csum = mkcksum(dentptr->name, dentptr->ext);
+                               if (dols && csum == prevcksum) {
+                                       prevcksum = 0xffff;
+                                       dentptr++;
+                                       continue;
+                               }
                        }
-#endif
+
                        get_name(dentptr, s_name);
                        if (dols) {
                                int isdir = (dentptr->attr & ATTR_DIR);
@@ -770,11 +803,12 @@ exit:
        return ret;
 }
 
-__u8 do_fat_read_block[MAX_CLUSTSIZE]
+__u8 do_fat_read_at_block[MAX_CLUSTSIZE]
        __aligned(ARCH_DMA_MINALIGN);
 
 long
-do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
+do_fat_read_at(const char *filename, unsigned long pos, void *buffer,
+              unsigned long maxsize, int dols, int dogetsize)
 {
        char fnamecopy[2048];
        boot_sector bs;
@@ -838,9 +872,9 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
                return -1;
        }
 
-#ifdef CONFIG_SUPPORT_VFAT
-       debug("VFAT Support enabled\n");
-#endif
+       if (vfat_enabled)
+               debug("VFAT Support enabled\n");
+
        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"
@@ -888,32 +922,37 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
                                        (mydata->fatsize == 32) ?
                                        (mydata->clust_size) :
                                        PREFETCH_BLOCKS,
-                                       do_fat_read_block) < 0) {
+                                       do_fat_read_at_block) < 0) {
                                debug("Error: reading rootdir block\n");
                                goto exit;
                        }
 
-                       dentptr = (dir_entry *) do_fat_read_block;
+                       dentptr = (dir_entry *) do_fat_read_at_block;
                }
 
                for (i = 0; i < DIRENTSPERBLOCK; i++) {
                        char s_name[14], l_name[VFAT_MAXLEN_BYTES];
+                       __u8 csum;
 
                        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 &&
+
+                       if (vfat_enabled)
+                               csum = mkcksum(dentptr->name, dentptr->ext);
+
+                       if (dentptr->attr & ATTR_VOLUME) {
+                               if (vfat_enabled &&
+                                   (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_block,
+                                                    do_fat_read_at_block,
                                                     dentptr, l_name);
 
                                        if (dols == LS_ROOT) {
@@ -950,9 +989,7 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
                                        }
                                        debug("Rootvfatname: |%s|\n",
                                               l_name);
-                               } else
-#endif
-                               {
+                               } else {
                                        /* Volume label or VFAT entry */
                                        dentptr++;
                                        continue;
@@ -966,14 +1003,13 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
                                }
                                goto exit;
                        }
-#ifdef CONFIG_SUPPORT_VFAT
-                       else if (dols == LS_ROOT &&
-                                mkcksum(dentptr->name) == prevcksum) {
+                       else if (vfat_enabled &&
+                                dols == LS_ROOT && csum == prevcksum) {
                                prevcksum = 0xffff;
                                dentptr++;
                                continue;
                        }
-#endif
+
                        get_name(dentptr, s_name);
 
                        if (dols == LS_ROOT) {
@@ -1116,7 +1152,10 @@ rootdir_done:
                        subname = nextname;
        }
 
-       ret = get_contents(mydata, dentptr, buffer, maxsize);
+       if (dogetsize)
+               ret = FAT2CPU32(dentptr->size);
+       else
+               ret = get_contents(mydata, dentptr, pos, buffer, maxsize);
        debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
 
 exit:
@@ -1124,6 +1163,12 @@ exit:
        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, 0);
+}
+
 int file_fat_detectfs(void)
 {
        boot_sector bs;
@@ -1181,8 +1226,7 @@ int file_fat_detectfs(void)
        vol_label[11] = '\0';
        volinfo.fs_type[5] = '\0';
 
-       printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part_nr,
-               volinfo.fs_type, vol_label);
+       printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
 
        return 0;
 }
@@ -1192,8 +1236,43 @@ int file_fat_ls(const char *dir)
        return do_fat_read(dir, NULL, 0, LS_YES);
 }
 
-long file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
+int fat_exists(const char *filename)
+{
+       int sz;
+       sz = do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1);
+       return sz >= 0;
+}
+
+int fat_size(const char *filename)
+{
+       return do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1);
+}
+
+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(filename, buffer, maxsize, LS_NO);
+       return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO, 0);
+}
+
+long file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
+{
+       return file_fat_read_at(filename, 0, buffer, maxsize);
+}
+
+int fat_read_file(const char *filename, void *buf, int offset, int len)
+{
+       int len_read;
+
+       len_read = file_fat_read_at(filename, offset, buf, len);
+       if (len_read == -1) {
+               printf("** Unable to read file %s **\n", filename);
+               return -1;
+       }
+
+       return len_read;
+}
+
+void fat_close(void)
+{
 }