]> git.sur5r.net Git - u-boot/blob - fs/fat/fat.c
cmd_bootm.c: Correct BOOTM_ERR_OVERLAP handling
[u-boot] / fs / fat / fat.c
1 /*
2  * fat.c
3  *
4  * R/O (V)FAT 12/16/32 filesystem implementation by Marcus Sundberg
5  *
6  * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
7  * 2003-03-10 - kharris@nexus-tech.net - ported to uboot
8  *
9  * See file CREDITS for list of people who contributed to this
10  * project.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License as
14  * published by the Free Software Foundation; either version 2 of
15  * the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25  * MA 02111-1307 USA
26  */
27
28 #include <common.h>
29 #include <config.h>
30 #include <exports.h>
31 #include <fat.h>
32 #include <asm/byteorder.h>
33 #include <part.h>
34 #include <malloc.h>
35 #include <linux/compiler.h>
36 #include <linux/ctype.h>
37
38 #ifdef CONFIG_SUPPORT_VFAT
39 static const int vfat_enabled = 1;
40 #else
41 static const int vfat_enabled = 0;
42 #endif
43
44 /*
45  * Convert a string to lowercase.
46  */
47 static void downcase(char *str)
48 {
49         while (*str != '\0') {
50                 *str = tolower(*str);
51                 str++;
52         }
53 }
54
55 static block_dev_desc_t *cur_dev;
56 static disk_partition_t cur_part_info;
57
58 #define DOS_BOOT_MAGIC_OFFSET   0x1fe
59 #define DOS_FS_TYPE_OFFSET      0x36
60 #define DOS_FS32_TYPE_OFFSET    0x52
61
62 static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
63 {
64         if (!cur_dev || !cur_dev->block_read)
65                 return -1;
66
67         return cur_dev->block_read(cur_dev->dev,
68                         cur_part_info.start + block, nr_blocks, buf);
69 }
70
71 int fat_set_blk_dev(block_dev_desc_t *dev_desc, disk_partition_t *info)
72 {
73         ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
74
75         cur_dev = dev_desc;
76         cur_part_info = *info;
77
78         /* Make sure it has a valid FAT header */
79         if (disk_read(0, 1, buffer) != 1) {
80                 cur_dev = NULL;
81                 return -1;
82         }
83
84         /* Check if it's actually a DOS volume */
85         if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) {
86                 cur_dev = NULL;
87                 return -1;
88         }
89
90         /* Check for FAT12/FAT16/FAT32 filesystem */
91         if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3))
92                 return 0;
93         if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5))
94                 return 0;
95
96         cur_dev = NULL;
97         return -1;
98 }
99
100 int fat_register_device(block_dev_desc_t *dev_desc, int part_no)
101 {
102         disk_partition_t info;
103
104         /* First close any currently found FAT filesystem */
105         cur_dev = NULL;
106
107         /* Read the partition table, if present */
108         if (get_partition_info(dev_desc, part_no, &info)) {
109                 if (part_no != 0) {
110                         printf("** Partition %d not valid on device %d **\n",
111                                         part_no, dev_desc->dev);
112                         return -1;
113                 }
114
115                 info.start = 0;
116                 info.size = dev_desc->lba;
117                 info.blksz = dev_desc->blksz;
118                 info.name[0] = 0;
119                 info.type[0] = 0;
120                 info.bootable = 0;
121 #ifdef CONFIG_PARTITION_UUIDS
122                 info.uuid[0] = 0;
123 #endif
124         }
125
126         return fat_set_blk_dev(dev_desc, &info);
127 }
128
129 /*
130  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
131  * Return index into string if found, -1 otherwise.
132  */
133 static int dirdelim(char *str)
134 {
135         char *start = str;
136
137         while (*str != '\0') {
138                 if (ISDIRDELIM(*str))
139                         return str - start;
140                 str++;
141         }
142         return -1;
143 }
144
145 /*
146  * Extract zero terminated short name from a directory entry.
147  */
148 static void get_name(dir_entry *dirent, char *s_name)
149 {
150         char *ptr;
151
152         memcpy(s_name, dirent->name, 8);
153         s_name[8] = '\0';
154         ptr = s_name;
155         while (*ptr && *ptr != ' ')
156                 ptr++;
157         if (dirent->ext[0] && dirent->ext[0] != ' ') {
158                 *ptr = '.';
159                 ptr++;
160                 memcpy(ptr, dirent->ext, 3);
161                 ptr[3] = '\0';
162                 while (*ptr && *ptr != ' ')
163                         ptr++;
164         }
165         *ptr = '\0';
166         if (*s_name == DELETED_FLAG)
167                 *s_name = '\0';
168         else if (*s_name == aRING)
169                 *s_name = DELETED_FLAG;
170         downcase(s_name);
171 }
172
173 /*
174  * Get the entry at index 'entry' in a FAT (12/16/32) table.
175  * On failure 0x00 is returned.
176  */
177 static __u32 get_fatent(fsdata *mydata, __u32 entry)
178 {
179         __u32 bufnum;
180         __u32 off16, offset;
181         __u32 ret = 0x00;
182         __u16 val1, val2;
183
184         switch (mydata->fatsize) {
185         case 32:
186                 bufnum = entry / FAT32BUFSIZE;
187                 offset = entry - bufnum * FAT32BUFSIZE;
188                 break;
189         case 16:
190                 bufnum = entry / FAT16BUFSIZE;
191                 offset = entry - bufnum * FAT16BUFSIZE;
192                 break;
193         case 12:
194                 bufnum = entry / FAT12BUFSIZE;
195                 offset = entry - bufnum * FAT12BUFSIZE;
196                 break;
197
198         default:
199                 /* Unsupported FAT size */
200                 return ret;
201         }
202
203         debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n",
204                mydata->fatsize, entry, entry, offset, offset);
205
206         /* Read a new block of FAT entries into the cache. */
207         if (bufnum != mydata->fatbufnum) {
208                 __u32 getsize = FATBUFBLOCKS;
209                 __u8 *bufptr = mydata->fatbuf;
210                 __u32 fatlength = mydata->fatlength;
211                 __u32 startblock = bufnum * FATBUFBLOCKS;
212
213                 if (startblock + getsize > fatlength)
214                         getsize = fatlength - startblock;
215
216                 startblock += mydata->fat_sect; /* Offset from start of disk */
217
218                 if (disk_read(startblock, getsize, bufptr) < 0) {
219                         debug("Error reading FAT blocks\n");
220                         return ret;
221                 }
222                 mydata->fatbufnum = bufnum;
223         }
224
225         /* Get the actual entry from the table */
226         switch (mydata->fatsize) {
227         case 32:
228                 ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
229                 break;
230         case 16:
231                 ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
232                 break;
233         case 12:
234                 off16 = (offset * 3) / 4;
235
236                 switch (offset & 0x3) {
237                 case 0:
238                         ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]);
239                         ret &= 0xfff;
240                         break;
241                 case 1:
242                         val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
243                         val1 &= 0xf000;
244                         val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
245                         val2 &= 0x00ff;
246                         ret = (val2 << 4) | (val1 >> 12);
247                         break;
248                 case 2:
249                         val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
250                         val1 &= 0xff00;
251                         val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
252                         val2 &= 0x000f;
253                         ret = (val2 << 8) | (val1 >> 8);
254                         break;
255                 case 3:
256                         ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
257                         ret = (ret & 0xfff0) >> 4;
258                         break;
259                 default:
260                         break;
261                 }
262                 break;
263         }
264         debug("FAT%d: ret: %08x, offset: %04x\n",
265                mydata->fatsize, ret, offset);
266
267         return ret;
268 }
269
270 /*
271  * Read at most 'size' bytes from the specified cluster into 'buffer'.
272  * Return 0 on success, -1 otherwise.
273  */
274 static int
275 get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
276 {
277         __u32 idx = 0;
278         __u32 startsect;
279         int ret;
280
281         if (clustnum > 0) {
282                 startsect = mydata->data_begin +
283                                 clustnum * mydata->clust_size;
284         } else {
285                 startsect = mydata->rootdir_sect;
286         }
287
288         debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
289
290         if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
291                 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
292
293                 printf("FAT: Misaligned buffer address (%p)\n", buffer);
294
295                 while (size >= mydata->sect_size) {
296                         ret = disk_read(startsect++, 1, tmpbuf);
297                         if (ret != 1) {
298                                 debug("Error reading data (got %d)\n", ret);
299                                 return -1;
300                         }
301
302                         memcpy(buffer, tmpbuf, mydata->sect_size);
303                         buffer += mydata->sect_size;
304                         size -= mydata->sect_size;
305                 }
306         } else {
307                 idx = size / mydata->sect_size;
308                 ret = disk_read(startsect, idx, buffer);
309                 if (ret != idx) {
310                         debug("Error reading data (got %d)\n", ret);
311                         return -1;
312                 }
313                 startsect += idx;
314                 idx *= mydata->sect_size;
315                 buffer += idx;
316                 size -= idx;
317         }
318         if (size) {
319                 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
320
321                 ret = disk_read(startsect, 1, tmpbuf);
322                 if (ret != 1) {
323                         debug("Error reading data (got %d)\n", ret);
324                         return -1;
325                 }
326
327                 memcpy(buffer, tmpbuf, size);
328         }
329
330         return 0;
331 }
332
333 /*
334  * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr'
335  * into 'buffer'.
336  * Return the number of bytes read or -1 on fatal errors.
337  */
338 __u8 get_contents_vfatname_block[MAX_CLUSTSIZE]
339         __aligned(ARCH_DMA_MINALIGN);
340
341 static long
342 get_contents(fsdata *mydata, dir_entry *dentptr, unsigned long pos,
343              __u8 *buffer, unsigned long maxsize)
344 {
345         unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
346         unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
347         __u32 curclust = START(dentptr);
348         __u32 endclust, newclust;
349         unsigned long actsize;
350
351         debug("Filesize: %ld bytes\n", filesize);
352
353         if (pos >= filesize) {
354                 debug("Read position past EOF: %lu\n", pos);
355                 return gotsize;
356         }
357
358         if (maxsize > 0 && filesize > pos + maxsize)
359                 filesize = pos + maxsize;
360
361         debug("%ld bytes\n", filesize);
362
363         actsize = bytesperclust;
364
365         /* go to cluster at pos */
366         while (actsize <= pos) {
367                 curclust = get_fatent(mydata, curclust);
368                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
369                         debug("curclust: 0x%x\n", curclust);
370                         debug("Invalid FAT entry\n");
371                         return gotsize;
372                 }
373                 actsize += bytesperclust;
374         }
375
376         /* actsize > pos */
377         actsize -= bytesperclust;
378         filesize -= actsize;
379         pos -= actsize;
380
381         /* align to beginning of next cluster if any */
382         if (pos) {
383                 actsize = min(filesize, bytesperclust);
384                 if (get_cluster(mydata, curclust, get_contents_vfatname_block,
385                                 (int)actsize) != 0) {
386                         printf("Error reading cluster\n");
387                         return -1;
388                 }
389                 filesize -= actsize;
390                 actsize -= pos;
391                 memcpy(buffer, get_contents_vfatname_block + pos, actsize);
392                 gotsize += actsize;
393                 if (!filesize)
394                         return gotsize;
395                 buffer += actsize;
396
397                 curclust = get_fatent(mydata, curclust);
398                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
399                         debug("curclust: 0x%x\n", curclust);
400                         debug("Invalid FAT entry\n");
401                         return gotsize;
402                 }
403         }
404
405         actsize = bytesperclust;
406         endclust = curclust;
407
408         do {
409                 /* search for consecutive clusters */
410                 while (actsize < filesize) {
411                         newclust = get_fatent(mydata, endclust);
412                         if ((newclust - 1) != endclust)
413                                 goto getit;
414                         if (CHECK_CLUST(newclust, mydata->fatsize)) {
415                                 debug("curclust: 0x%x\n", newclust);
416                                 debug("Invalid FAT entry\n");
417                                 return gotsize;
418                         }
419                         endclust = newclust;
420                         actsize += bytesperclust;
421                 }
422
423                 /* get remaining bytes */
424                 actsize = filesize;
425                 if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
426                         printf("Error reading cluster\n");
427                         return -1;
428                 }
429                 gotsize += actsize;
430                 return gotsize;
431 getit:
432                 if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
433                         printf("Error reading cluster\n");
434                         return -1;
435                 }
436                 gotsize += (int)actsize;
437                 filesize -= actsize;
438                 buffer += actsize;
439
440                 curclust = get_fatent(mydata, endclust);
441                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
442                         debug("curclust: 0x%x\n", curclust);
443                         printf("Invalid FAT entry\n");
444                         return gotsize;
445                 }
446                 actsize = bytesperclust;
447                 endclust = curclust;
448         } while (1);
449 }
450
451 /*
452  * Extract the file name information from 'slotptr' into 'l_name',
453  * starting at l_name[*idx].
454  * Return 1 if terminator (zero byte) is found, 0 otherwise.
455  */
456 static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
457 {
458         int j;
459
460         for (j = 0; j <= 8; j += 2) {
461                 l_name[*idx] = slotptr->name0_4[j];
462                 if (l_name[*idx] == 0x00)
463                         return 1;
464                 (*idx)++;
465         }
466         for (j = 0; j <= 10; j += 2) {
467                 l_name[*idx] = slotptr->name5_10[j];
468                 if (l_name[*idx] == 0x00)
469                         return 1;
470                 (*idx)++;
471         }
472         for (j = 0; j <= 2; j += 2) {
473                 l_name[*idx] = slotptr->name11_12[j];
474                 if (l_name[*idx] == 0x00)
475                         return 1;
476                 (*idx)++;
477         }
478
479         return 0;
480 }
481
482 /*
483  * Extract the full long filename starting at 'retdent' (which is really
484  * a slot) into 'l_name'. If successful also copy the real directory entry
485  * into 'retdent'
486  * Return 0 on success, -1 otherwise.
487  */
488 static int
489 get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
490              dir_entry *retdent, char *l_name)
491 {
492         dir_entry *realdent;
493         dir_slot *slotptr = (dir_slot *)retdent;
494         __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
495                                                         PREFETCH_BLOCKS :
496                                                         mydata->clust_size);
497         __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
498         int idx = 0;
499
500         if (counter > VFAT_MAXSEQ) {
501                 debug("Error: VFAT name is too long\n");
502                 return -1;
503         }
504
505         while ((__u8 *)slotptr < buflimit) {
506                 if (counter == 0)
507                         break;
508                 if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
509                         return -1;
510                 slotptr++;
511                 counter--;
512         }
513
514         if ((__u8 *)slotptr >= buflimit) {
515                 dir_slot *slotptr2;
516
517                 if (curclust == 0)
518                         return -1;
519                 curclust = get_fatent(mydata, curclust);
520                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
521                         debug("curclust: 0x%x\n", curclust);
522                         printf("Invalid FAT entry\n");
523                         return -1;
524                 }
525
526                 if (get_cluster(mydata, curclust, get_contents_vfatname_block,
527                                 mydata->clust_size * mydata->sect_size) != 0) {
528                         debug("Error: reading directory block\n");
529                         return -1;
530                 }
531
532                 slotptr2 = (dir_slot *)get_contents_vfatname_block;
533                 while (counter > 0) {
534                         if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
535                             & 0xff) != counter)
536                                 return -1;
537                         slotptr2++;
538                         counter--;
539                 }
540
541                 /* Save the real directory entry */
542                 realdent = (dir_entry *)slotptr2;
543                 while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
544                         slotptr2--;
545                         slot2str(slotptr2, l_name, &idx);
546                 }
547         } else {
548                 /* Save the real directory entry */
549                 realdent = (dir_entry *)slotptr;
550         }
551
552         do {
553                 slotptr--;
554                 if (slot2str(slotptr, l_name, &idx))
555                         break;
556         } while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
557
558         l_name[idx] = '\0';
559         if (*l_name == DELETED_FLAG)
560                 *l_name = '\0';
561         else if (*l_name == aRING)
562                 *l_name = DELETED_FLAG;
563         downcase(l_name);
564
565         /* Return the real directory entry */
566         memcpy(retdent, realdent, sizeof(dir_entry));
567
568         return 0;
569 }
570
571 /* Calculate short name checksum */
572 static __u8 mkcksum(const char name[8], const char ext[3])
573 {
574         int i;
575
576         __u8 ret = 0;
577
578         for (i = 0; i < 8; i++)
579                 ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + name[i];
580         for (i = 0; i < 3; i++)
581                 ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + ext[i];
582
583         return ret;
584 }
585
586 /*
587  * Get the directory entry associated with 'filename' from the directory
588  * starting at 'startsect'
589  */
590 __u8 get_dentfromdir_block[MAX_CLUSTSIZE]
591         __aligned(ARCH_DMA_MINALIGN);
592
593 static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
594                                   char *filename, dir_entry *retdent,
595                                   int dols)
596 {
597         __u16 prevcksum = 0xffff;
598         __u32 curclust = START(retdent);
599         int files = 0, dirs = 0;
600
601         debug("get_dentfromdir: %s\n", filename);
602
603         while (1) {
604                 dir_entry *dentptr;
605
606                 int i;
607
608                 if (get_cluster(mydata, curclust, get_dentfromdir_block,
609                                 mydata->clust_size * mydata->sect_size) != 0) {
610                         debug("Error: reading directory block\n");
611                         return NULL;
612                 }
613
614                 dentptr = (dir_entry *)get_dentfromdir_block;
615
616                 for (i = 0; i < DIRENTSPERCLUST; i++) {
617                         char s_name[14], l_name[VFAT_MAXLEN_BYTES];
618
619                         l_name[0] = '\0';
620                         if (dentptr->name[0] == DELETED_FLAG) {
621                                 dentptr++;
622                                 continue;
623                         }
624                         if ((dentptr->attr & ATTR_VOLUME)) {
625                                 if (vfat_enabled &&
626                                     (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
627                                     (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
628                                         prevcksum = ((dir_slot *)dentptr)->alias_checksum;
629                                         get_vfatname(mydata, curclust,
630                                                      get_dentfromdir_block,
631                                                      dentptr, l_name);
632                                         if (dols) {
633                                                 int isdir;
634                                                 char dirc;
635                                                 int doit = 0;
636
637                                                 isdir = (dentptr->attr & ATTR_DIR);
638
639                                                 if (isdir) {
640                                                         dirs++;
641                                                         dirc = '/';
642                                                         doit = 1;
643                                                 } else {
644                                                         dirc = ' ';
645                                                         if (l_name[0] != 0) {
646                                                                 files++;
647                                                                 doit = 1;
648                                                         }
649                                                 }
650                                                 if (doit) {
651                                                         if (dirc == ' ') {
652                                                                 printf(" %8ld   %s%c\n",
653                                                                         (long)FAT2CPU32(dentptr->size),
654                                                                         l_name,
655                                                                         dirc);
656                                                         } else {
657                                                                 printf("            %s%c\n",
658                                                                         l_name,
659                                                                         dirc);
660                                                         }
661                                                 }
662                                                 dentptr++;
663                                                 continue;
664                                         }
665                                         debug("vfatname: |%s|\n", l_name);
666                                 } else {
667                                         /* Volume label or VFAT entry */
668                                         dentptr++;
669                                         continue;
670                                 }
671                         }
672                         if (dentptr->name[0] == 0) {
673                                 if (dols) {
674                                         printf("\n%d file(s), %d dir(s)\n\n",
675                                                 files, dirs);
676                                 }
677                                 debug("Dentname == NULL - %d\n", i);
678                                 return NULL;
679                         }
680                         if (vfat_enabled) {
681                                 __u8 csum = mkcksum(dentptr->name, dentptr->ext);
682                                 if (dols && csum == prevcksum) {
683                                         prevcksum = 0xffff;
684                                         dentptr++;
685                                         continue;
686                                 }
687                         }
688
689                         get_name(dentptr, s_name);
690                         if (dols) {
691                                 int isdir = (dentptr->attr & ATTR_DIR);
692                                 char dirc;
693                                 int doit = 0;
694
695                                 if (isdir) {
696                                         dirs++;
697                                         dirc = '/';
698                                         doit = 1;
699                                 } else {
700                                         dirc = ' ';
701                                         if (s_name[0] != 0) {
702                                                 files++;
703                                                 doit = 1;
704                                         }
705                                 }
706
707                                 if (doit) {
708                                         if (dirc == ' ') {
709                                                 printf(" %8ld   %s%c\n",
710                                                         (long)FAT2CPU32(dentptr->size),
711                                                         s_name, dirc);
712                                         } else {
713                                                 printf("            %s%c\n",
714                                                         s_name, dirc);
715                                         }
716                                 }
717
718                                 dentptr++;
719                                 continue;
720                         }
721
722                         if (strcmp(filename, s_name)
723                             && strcmp(filename, l_name)) {
724                                 debug("Mismatch: |%s|%s|\n", s_name, l_name);
725                                 dentptr++;
726                                 continue;
727                         }
728
729                         memcpy(retdent, dentptr, sizeof(dir_entry));
730
731                         debug("DentName: %s", s_name);
732                         debug(", start: 0x%x", START(dentptr));
733                         debug(", size:  0x%x %s\n",
734                               FAT2CPU32(dentptr->size),
735                               (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
736
737                         return retdent;
738                 }
739
740                 curclust = get_fatent(mydata, curclust);
741                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
742                         debug("curclust: 0x%x\n", curclust);
743                         printf("Invalid FAT entry\n");
744                         return NULL;
745                 }
746         }
747
748         return NULL;
749 }
750
751 /*
752  * Read boot sector and volume info from a FAT filesystem
753  */
754 static int
755 read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
756 {
757         __u8 *block;
758         volume_info *vistart;
759         int ret = 0;
760
761         if (cur_dev == NULL) {
762                 debug("Error: no device selected\n");
763                 return -1;
764         }
765
766         block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz);
767         if (block == NULL) {
768                 debug("Error: allocating block\n");
769                 return -1;
770         }
771
772         if (disk_read(0, 1, block) < 0) {
773                 debug("Error: reading block\n");
774                 goto fail;
775         }
776
777         memcpy(bs, block, sizeof(boot_sector));
778         bs->reserved = FAT2CPU16(bs->reserved);
779         bs->fat_length = FAT2CPU16(bs->fat_length);
780         bs->secs_track = FAT2CPU16(bs->secs_track);
781         bs->heads = FAT2CPU16(bs->heads);
782         bs->total_sect = FAT2CPU32(bs->total_sect);
783
784         /* FAT32 entries */
785         if (bs->fat_length == 0) {
786                 /* Assume FAT32 */
787                 bs->fat32_length = FAT2CPU32(bs->fat32_length);
788                 bs->flags = FAT2CPU16(bs->flags);
789                 bs->root_cluster = FAT2CPU32(bs->root_cluster);
790                 bs->info_sector = FAT2CPU16(bs->info_sector);
791                 bs->backup_boot = FAT2CPU16(bs->backup_boot);
792                 vistart = (volume_info *)(block + sizeof(boot_sector));
793                 *fatsize = 32;
794         } else {
795                 vistart = (volume_info *)&(bs->fat32_length);
796                 *fatsize = 0;
797         }
798         memcpy(volinfo, vistart, sizeof(volume_info));
799
800         if (*fatsize == 32) {
801                 if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
802                         goto exit;
803         } else {
804                 if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
805                         *fatsize = 12;
806                         goto exit;
807                 }
808                 if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
809                         *fatsize = 16;
810                         goto exit;
811                 }
812         }
813
814         debug("Error: broken fs_type sign\n");
815 fail:
816         ret = -1;
817 exit:
818         free(block);
819         return ret;
820 }
821
822 __u8 do_fat_read_at_block[MAX_CLUSTSIZE]
823         __aligned(ARCH_DMA_MINALIGN);
824
825 long
826 do_fat_read_at(const char *filename, unsigned long pos, void *buffer,
827                unsigned long maxsize, int dols)
828 {
829         char fnamecopy[2048];
830         boot_sector bs;
831         volume_info volinfo;
832         fsdata datablock;
833         fsdata *mydata = &datablock;
834         dir_entry *dentptr = NULL;
835         __u16 prevcksum = 0xffff;
836         char *subname = "";
837         __u32 cursect;
838         int idx, isdir = 0;
839         int files = 0, dirs = 0;
840         long ret = -1;
841         int firsttime;
842         __u32 root_cluster = 0;
843         int rootdir_size = 0;
844         int j;
845
846         if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
847                 debug("Error: reading boot sector\n");
848                 return -1;
849         }
850
851         if (mydata->fatsize == 32) {
852                 root_cluster = bs.root_cluster;
853                 mydata->fatlength = bs.fat32_length;
854         } else {
855                 mydata->fatlength = bs.fat_length;
856         }
857
858         mydata->fat_sect = bs.reserved;
859
860         cursect = mydata->rootdir_sect
861                 = mydata->fat_sect + mydata->fatlength * bs.fats;
862
863         mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
864         mydata->clust_size = bs.cluster_size;
865         if (mydata->sect_size != cur_part_info.blksz) {
866                 printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
867                                 mydata->sect_size, cur_part_info.blksz);
868                 return -1;
869         }
870
871         if (mydata->fatsize == 32) {
872                 mydata->data_begin = mydata->rootdir_sect -
873                                         (mydata->clust_size * 2);
874         } else {
875                 rootdir_size = ((bs.dir_entries[1]  * (int)256 +
876                                  bs.dir_entries[0]) *
877                                  sizeof(dir_entry)) /
878                                  mydata->sect_size;
879                 mydata->data_begin = mydata->rootdir_sect +
880                                         rootdir_size -
881                                         (mydata->clust_size * 2);
882         }
883
884         mydata->fatbufnum = -1;
885         mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
886         if (mydata->fatbuf == NULL) {
887                 debug("Error: allocating memory\n");
888                 return -1;
889         }
890
891         if (vfat_enabled)
892                 debug("VFAT Support enabled\n");
893
894         debug("FAT%d, fat_sect: %d, fatlength: %d\n",
895                mydata->fatsize, mydata->fat_sect, mydata->fatlength);
896         debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
897                "Data begins at: %d\n",
898                root_cluster,
899                mydata->rootdir_sect,
900                mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
901         debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
902               mydata->clust_size);
903
904         /* "cwd" is always the root... */
905         while (ISDIRDELIM(*filename))
906                 filename++;
907
908         /* Make a copy of the filename and convert it to lowercase */
909         strcpy(fnamecopy, filename);
910         downcase(fnamecopy);
911
912         if (*fnamecopy == '\0') {
913                 if (!dols)
914                         goto exit;
915
916                 dols = LS_ROOT;
917         } else if ((idx = dirdelim(fnamecopy)) >= 0) {
918                 isdir = 1;
919                 fnamecopy[idx] = '\0';
920                 subname = fnamecopy + idx + 1;
921
922                 /* Handle multiple delimiters */
923                 while (ISDIRDELIM(*subname))
924                         subname++;
925         } else if (dols) {
926                 isdir = 1;
927         }
928
929         j = 0;
930         while (1) {
931                 int i;
932
933                 if (j == 0) {
934                         debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n",
935                                 cursect, mydata->clust_size, DIRENTSPERBLOCK);
936
937                         if (disk_read(cursect,
938                                         (mydata->fatsize == 32) ?
939                                         (mydata->clust_size) :
940                                         PREFETCH_BLOCKS,
941                                         do_fat_read_at_block) < 0) {
942                                 debug("Error: reading rootdir block\n");
943                                 goto exit;
944                         }
945
946                         dentptr = (dir_entry *) do_fat_read_at_block;
947                 }
948
949                 for (i = 0; i < DIRENTSPERBLOCK; i++) {
950                         char s_name[14], l_name[VFAT_MAXLEN_BYTES];
951                         __u8 csum;
952
953                         l_name[0] = '\0';
954                         if (dentptr->name[0] == DELETED_FLAG) {
955                                 dentptr++;
956                                 continue;
957                         }
958
959                         if (vfat_enabled)
960                                 csum = mkcksum(dentptr->name, dentptr->ext);
961
962                         if (dentptr->attr & ATTR_VOLUME) {
963                                 if (vfat_enabled &&
964                                     (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
965                                     (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
966                                         prevcksum =
967                                                 ((dir_slot *)dentptr)->alias_checksum;
968
969                                         get_vfatname(mydata,
970                                                      root_cluster,
971                                                      do_fat_read_at_block,
972                                                      dentptr, l_name);
973
974                                         if (dols == LS_ROOT) {
975                                                 char dirc;
976                                                 int doit = 0;
977                                                 int isdir =
978                                                         (dentptr->attr & ATTR_DIR);
979
980                                                 if (isdir) {
981                                                         dirs++;
982                                                         dirc = '/';
983                                                         doit = 1;
984                                                 } else {
985                                                         dirc = ' ';
986                                                         if (l_name[0] != 0) {
987                                                                 files++;
988                                                                 doit = 1;
989                                                         }
990                                                 }
991                                                 if (doit) {
992                                                         if (dirc == ' ') {
993                                                                 printf(" %8ld   %s%c\n",
994                                                                         (long)FAT2CPU32(dentptr->size),
995                                                                         l_name,
996                                                                         dirc);
997                                                         } else {
998                                                                 printf("            %s%c\n",
999                                                                         l_name,
1000                                                                         dirc);
1001                                                         }
1002                                                 }
1003                                                 dentptr++;
1004                                                 continue;
1005                                         }
1006                                         debug("Rootvfatname: |%s|\n",
1007                                                l_name);
1008                                 } else {
1009                                         /* Volume label or VFAT entry */
1010                                         dentptr++;
1011                                         continue;
1012                                 }
1013                         } else if (dentptr->name[0] == 0) {
1014                                 debug("RootDentname == NULL - %d\n", i);
1015                                 if (dols == LS_ROOT) {
1016                                         printf("\n%d file(s), %d dir(s)\n\n",
1017                                                 files, dirs);
1018                                         ret = 0;
1019                                 }
1020                                 goto exit;
1021                         }
1022                         else if (vfat_enabled &&
1023                                  dols == LS_ROOT && csum == prevcksum) {
1024                                 prevcksum = 0xffff;
1025                                 dentptr++;
1026                                 continue;
1027                         }
1028
1029                         get_name(dentptr, s_name);
1030
1031                         if (dols == LS_ROOT) {
1032                                 int isdir = (dentptr->attr & ATTR_DIR);
1033                                 char dirc;
1034                                 int doit = 0;
1035
1036                                 if (isdir) {
1037                                         dirc = '/';
1038                                         if (s_name[0] != 0) {
1039                                                 dirs++;
1040                                                 doit = 1;
1041                                         }
1042                                 } else {
1043                                         dirc = ' ';
1044                                         if (s_name[0] != 0) {
1045                                                 files++;
1046                                                 doit = 1;
1047                                         }
1048                                 }
1049                                 if (doit) {
1050                                         if (dirc == ' ') {
1051                                                 printf(" %8ld   %s%c\n",
1052                                                         (long)FAT2CPU32(dentptr->size),
1053                                                         s_name, dirc);
1054                                         } else {
1055                                                 printf("            %s%c\n",
1056                                                         s_name, dirc);
1057                                         }
1058                                 }
1059                                 dentptr++;
1060                                 continue;
1061                         }
1062
1063                         if (strcmp(fnamecopy, s_name)
1064                             && strcmp(fnamecopy, l_name)) {
1065                                 debug("RootMismatch: |%s|%s|\n", s_name,
1066                                        l_name);
1067                                 dentptr++;
1068                                 continue;
1069                         }
1070
1071                         if (isdir && !(dentptr->attr & ATTR_DIR))
1072                                 goto exit;
1073
1074                         debug("RootName: %s", s_name);
1075                         debug(", start: 0x%x", START(dentptr));
1076                         debug(", size:  0x%x %s\n",
1077                                FAT2CPU32(dentptr->size),
1078                                isdir ? "(DIR)" : "");
1079
1080                         goto rootdir_done;      /* We got a match */
1081                 }
1082                 debug("END LOOP: j=%d   clust_size=%d\n", j,
1083                        mydata->clust_size);
1084
1085                 /*
1086                  * On FAT32 we must fetch the FAT entries for the next
1087                  * root directory clusters when a cluster has been
1088                  * completely processed.
1089                  */
1090                 ++j;
1091                 int rootdir_end = 0;
1092                 if (mydata->fatsize == 32) {
1093                         if (j == mydata->clust_size) {
1094                                 int nxtsect = 0;
1095                                 int nxt_clust = 0;
1096
1097                                 nxt_clust = get_fatent(mydata, root_cluster);
1098                                 rootdir_end = CHECK_CLUST(nxt_clust, 32);
1099
1100                                 nxtsect = mydata->data_begin +
1101                                         (nxt_clust * mydata->clust_size);
1102
1103                                 root_cluster = nxt_clust;
1104
1105                                 cursect = nxtsect;
1106                                 j = 0;
1107                         }
1108                 } else {
1109                         if (j == PREFETCH_BLOCKS)
1110                                 j = 0;
1111
1112                         rootdir_end = (++cursect - mydata->rootdir_sect >=
1113                                        rootdir_size);
1114                 }
1115
1116                 /* If end of rootdir reached */
1117                 if (rootdir_end) {
1118                         if (dols == LS_ROOT) {
1119                                 printf("\n%d file(s), %d dir(s)\n\n",
1120                                        files, dirs);
1121                                 ret = 0;
1122                         }
1123                         goto exit;
1124                 }
1125         }
1126 rootdir_done:
1127
1128         firsttime = 1;
1129
1130         while (isdir) {
1131                 int startsect = mydata->data_begin
1132                         + START(dentptr) * mydata->clust_size;
1133                 dir_entry dent;
1134                 char *nextname = NULL;
1135
1136                 dent = *dentptr;
1137                 dentptr = &dent;
1138
1139                 idx = dirdelim(subname);
1140
1141                 if (idx >= 0) {
1142                         subname[idx] = '\0';
1143                         nextname = subname + idx + 1;
1144                         /* Handle multiple delimiters */
1145                         while (ISDIRDELIM(*nextname))
1146                                 nextname++;
1147                         if (dols && *nextname == '\0')
1148                                 firsttime = 0;
1149                 } else {
1150                         if (dols && firsttime) {
1151                                 firsttime = 0;
1152                         } else {
1153                                 isdir = 0;
1154                         }
1155                 }
1156
1157                 if (get_dentfromdir(mydata, startsect, subname, dentptr,
1158                                      isdir ? 0 : dols) == NULL) {
1159                         if (dols && !isdir)
1160                                 ret = 0;
1161                         goto exit;
1162                 }
1163
1164                 if (isdir && !(dentptr->attr & ATTR_DIR))
1165                         goto exit;
1166
1167                 if (idx >= 0)
1168                         subname = nextname;
1169         }
1170
1171         ret = get_contents(mydata, dentptr, pos, buffer, maxsize);
1172         debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
1173
1174 exit:
1175         free(mydata->fatbuf);
1176         return ret;
1177 }
1178
1179 long
1180 do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
1181 {
1182         return do_fat_read_at(filename, 0, buffer, maxsize, dols);
1183 }
1184
1185 int file_fat_detectfs(void)
1186 {
1187         boot_sector bs;
1188         volume_info volinfo;
1189         int fatsize;
1190         char vol_label[12];
1191
1192         if (cur_dev == NULL) {
1193                 printf("No current device\n");
1194                 return 1;
1195         }
1196
1197 #if defined(CONFIG_CMD_IDE) || \
1198     defined(CONFIG_CMD_SATA) || \
1199     defined(CONFIG_CMD_SCSI) || \
1200     defined(CONFIG_CMD_USB) || \
1201     defined(CONFIG_MMC)
1202         printf("Interface:  ");
1203         switch (cur_dev->if_type) {
1204         case IF_TYPE_IDE:
1205                 printf("IDE");
1206                 break;
1207         case IF_TYPE_SATA:
1208                 printf("SATA");
1209                 break;
1210         case IF_TYPE_SCSI:
1211                 printf("SCSI");
1212                 break;
1213         case IF_TYPE_ATAPI:
1214                 printf("ATAPI");
1215                 break;
1216         case IF_TYPE_USB:
1217                 printf("USB");
1218                 break;
1219         case IF_TYPE_DOC:
1220                 printf("DOC");
1221                 break;
1222         case IF_TYPE_MMC:
1223                 printf("MMC");
1224                 break;
1225         default:
1226                 printf("Unknown");
1227         }
1228
1229         printf("\n  Device %d: ", cur_dev->dev);
1230         dev_print(cur_dev);
1231 #endif
1232
1233         if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
1234                 printf("\nNo valid FAT fs found\n");
1235                 return 1;
1236         }
1237
1238         memcpy(vol_label, volinfo.volume_label, 11);
1239         vol_label[11] = '\0';
1240         volinfo.fs_type[5] = '\0';
1241
1242         printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
1243
1244         return 0;
1245 }
1246
1247 int file_fat_ls(const char *dir)
1248 {
1249         return do_fat_read(dir, NULL, 0, LS_YES);
1250 }
1251
1252 long file_fat_read_at(const char *filename, unsigned long pos, void *buffer,
1253                       unsigned long maxsize)
1254 {
1255         printf("reading %s\n", filename);
1256         return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO);
1257 }
1258
1259 long file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
1260 {
1261         return file_fat_read_at(filename, 0, buffer, maxsize);
1262 }
1263
1264 int fat_read_file(const char *filename, void *buf, int offset, int len)
1265 {
1266         int len_read;
1267
1268         len_read = file_fat_read_at(filename, offset, buf, len);
1269         if (len_read == -1) {
1270                 printf("** Unable to read file %s **\n", filename);
1271                 return -1;
1272         }
1273
1274         return len_read;
1275 }
1276
1277 void fat_close(void)
1278 {
1279 }