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