FAT: get_cluster: Add buffer bouncing
[platform/kernel/u-boot.git] / 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         int ret;
277
278         if (clustnum > 0) {
279                 startsect = mydata->data_begin +
280                                 clustnum * mydata->clust_size;
281         } else {
282                 startsect = mydata->rootdir_sect;
283         }
284
285         debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
286
287         if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
288                 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
289
290                 printf("FAT: Misaligned buffer address (%p)\n", buffer);
291
292                 while (size >= mydata->sect_size) {
293                         ret = disk_read(startsect++, 1, tmpbuf);
294                         if (ret != 1) {
295                                 debug("Error reading data (got %d)\n", ret);
296                                 return -1;
297                         }
298
299                         memcpy(buffer, tmpbuf, mydata->sect_size);
300                         buffer += mydata->sect_size;
301                         size -= mydata->sect_size;
302                 }
303         } else {
304                 idx = size / mydata->sect_size;
305                 ret = disk_read(startsect, idx, buffer);
306                 if (ret != idx) {
307                         debug("Error reading data (got %d)\n", ret);
308                         return -1;
309                 }
310                 startsect += idx;
311                 idx *= mydata->sect_size;
312                 buffer += idx;
313                 size -= idx;
314         }
315         if (size) {
316                 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
317
318                 ret = disk_read(startsect, 1, tmpbuf);
319                 if (ret != 1) {
320                         debug("Error reading data (got %d)\n", ret);
321                         return -1;
322                 }
323
324                 memcpy(buffer, tmpbuf, size);
325         }
326
327         return 0;
328 }
329
330 /*
331  * Read at most 'maxsize' bytes from the file associated with 'dentptr'
332  * into 'buffer'.
333  * Return the number of bytes read or -1 on fatal errors.
334  */
335 static long
336 get_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
337              unsigned long maxsize)
338 {
339         unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
340         unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
341         __u32 curclust = START(dentptr);
342         __u32 endclust, newclust;
343         unsigned long actsize;
344
345         debug("Filesize: %ld bytes\n", filesize);
346
347         if (maxsize > 0 && filesize > maxsize)
348                 filesize = maxsize;
349
350         debug("%ld bytes\n", filesize);
351
352         actsize = bytesperclust;
353         endclust = curclust;
354
355         do {
356                 /* search for consecutive clusters */
357                 while (actsize < filesize) {
358                         newclust = get_fatent(mydata, endclust);
359                         if ((newclust - 1) != endclust)
360                                 goto getit;
361                         if (CHECK_CLUST(newclust, mydata->fatsize)) {
362                                 debug("curclust: 0x%x\n", newclust);
363                                 debug("Invalid FAT entry\n");
364                                 return gotsize;
365                         }
366                         endclust = newclust;
367                         actsize += bytesperclust;
368                 }
369
370                 /* actsize >= file size */
371                 actsize -= bytesperclust;
372
373                 /* get remaining clusters */
374                 if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
375                         printf("Error reading cluster\n");
376                         return -1;
377                 }
378
379                 /* get remaining bytes */
380                 gotsize += (int)actsize;
381                 filesize -= actsize;
382                 buffer += actsize;
383                 actsize = filesize;
384                 if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) {
385                         printf("Error reading cluster\n");
386                         return -1;
387                 }
388                 gotsize += actsize;
389                 return gotsize;
390 getit:
391                 if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
392                         printf("Error reading cluster\n");
393                         return -1;
394                 }
395                 gotsize += (int)actsize;
396                 filesize -= actsize;
397                 buffer += actsize;
398
399                 curclust = get_fatent(mydata, endclust);
400                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
401                         debug("curclust: 0x%x\n", curclust);
402                         printf("Invalid FAT entry\n");
403                         return gotsize;
404                 }
405                 actsize = bytesperclust;
406                 endclust = curclust;
407         } while (1);
408 }
409
410 #ifdef CONFIG_SUPPORT_VFAT
411 /*
412  * Extract the file name information from 'slotptr' into 'l_name',
413  * starting at l_name[*idx].
414  * Return 1 if terminator (zero byte) is found, 0 otherwise.
415  */
416 static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
417 {
418         int j;
419
420         for (j = 0; j <= 8; j += 2) {
421                 l_name[*idx] = slotptr->name0_4[j];
422                 if (l_name[*idx] == 0x00)
423                         return 1;
424                 (*idx)++;
425         }
426         for (j = 0; j <= 10; j += 2) {
427                 l_name[*idx] = slotptr->name5_10[j];
428                 if (l_name[*idx] == 0x00)
429                         return 1;
430                 (*idx)++;
431         }
432         for (j = 0; j <= 2; j += 2) {
433                 l_name[*idx] = slotptr->name11_12[j];
434                 if (l_name[*idx] == 0x00)
435                         return 1;
436                 (*idx)++;
437         }
438
439         return 0;
440 }
441
442 /*
443  * Extract the full long filename starting at 'retdent' (which is really
444  * a slot) into 'l_name'. If successful also copy the real directory entry
445  * into 'retdent'
446  * Return 0 on success, -1 otherwise.
447  */
448 __u8 get_vfatname_block[MAX_CLUSTSIZE]
449         __aligned(ARCH_DMA_MINALIGN);
450
451 static int
452 get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
453              dir_entry *retdent, char *l_name)
454 {
455         dir_entry *realdent;
456         dir_slot *slotptr = (dir_slot *)retdent;
457         __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
458                                                         PREFETCH_BLOCKS :
459                                                         mydata->clust_size);
460         __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
461         int idx = 0;
462
463         if (counter > VFAT_MAXSEQ) {
464                 debug("Error: VFAT name is too long\n");
465                 return -1;
466         }
467
468         while ((__u8 *)slotptr < buflimit) {
469                 if (counter == 0)
470                         break;
471                 if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
472                         return -1;
473                 slotptr++;
474                 counter--;
475         }
476
477         if ((__u8 *)slotptr >= buflimit) {
478                 dir_slot *slotptr2;
479
480                 if (curclust == 0)
481                         return -1;
482                 curclust = get_fatent(mydata, curclust);
483                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
484                         debug("curclust: 0x%x\n", curclust);
485                         printf("Invalid FAT entry\n");
486                         return -1;
487                 }
488
489                 if (get_cluster(mydata, curclust, get_vfatname_block,
490                                 mydata->clust_size * mydata->sect_size) != 0) {
491                         debug("Error: reading directory block\n");
492                         return -1;
493                 }
494
495                 slotptr2 = (dir_slot *)get_vfatname_block;
496                 while (counter > 0) {
497                         if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
498                             & 0xff) != counter)
499                                 return -1;
500                         slotptr2++;
501                         counter--;
502                 }
503
504                 /* Save the real directory entry */
505                 realdent = (dir_entry *)slotptr2;
506                 while ((__u8 *)slotptr2 > get_vfatname_block) {
507                         slotptr2--;
508                         slot2str(slotptr2, l_name, &idx);
509                 }
510         } else {
511                 /* Save the real directory entry */
512                 realdent = (dir_entry *)slotptr;
513         }
514
515         do {
516                 slotptr--;
517                 if (slot2str(slotptr, l_name, &idx))
518                         break;
519         } while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
520
521         l_name[idx] = '\0';
522         if (*l_name == DELETED_FLAG)
523                 *l_name = '\0';
524         else if (*l_name == aRING)
525                 *l_name = DELETED_FLAG;
526         downcase(l_name);
527
528         /* Return the real directory entry */
529         memcpy(retdent, realdent, sizeof(dir_entry));
530
531         return 0;
532 }
533
534 /* Calculate short name checksum */
535 static __u8 mkcksum(const char *str)
536 {
537         int i;
538
539         __u8 ret = 0;
540
541         for (i = 0; i < 11; i++) {
542                 ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i];
543         }
544
545         return ret;
546 }
547 #endif  /* CONFIG_SUPPORT_VFAT */
548
549 /*
550  * Get the directory entry associated with 'filename' from the directory
551  * starting at 'startsect'
552  */
553 __u8 get_dentfromdir_block[MAX_CLUSTSIZE]
554         __aligned(ARCH_DMA_MINALIGN);
555
556 static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
557                                   char *filename, dir_entry *retdent,
558                                   int dols)
559 {
560         __u16 prevcksum = 0xffff;
561         __u32 curclust = START(retdent);
562         int files = 0, dirs = 0;
563
564         debug("get_dentfromdir: %s\n", filename);
565
566         while (1) {
567                 dir_entry *dentptr;
568
569                 int i;
570
571                 if (get_cluster(mydata, curclust, get_dentfromdir_block,
572                                 mydata->clust_size * mydata->sect_size) != 0) {
573                         debug("Error: reading directory block\n");
574                         return NULL;
575                 }
576
577                 dentptr = (dir_entry *)get_dentfromdir_block;
578
579                 for (i = 0; i < DIRENTSPERCLUST; i++) {
580                         char s_name[14], l_name[VFAT_MAXLEN_BYTES];
581
582                         l_name[0] = '\0';
583                         if (dentptr->name[0] == DELETED_FLAG) {
584                                 dentptr++;
585                                 continue;
586                         }
587                         if ((dentptr->attr & ATTR_VOLUME)) {
588 #ifdef CONFIG_SUPPORT_VFAT
589                                 if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
590                                     (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
591                                         prevcksum = ((dir_slot *)dentptr)->alias_checksum;
592                                         get_vfatname(mydata, curclust,
593                                                      get_dentfromdir_block,
594                                                      dentptr, l_name);
595                                         if (dols) {
596                                                 int isdir;
597                                                 char dirc;
598                                                 int doit = 0;
599
600                                                 isdir = (dentptr->attr & ATTR_DIR);
601
602                                                 if (isdir) {
603                                                         dirs++;
604                                                         dirc = '/';
605                                                         doit = 1;
606                                                 } else {
607                                                         dirc = ' ';
608                                                         if (l_name[0] != 0) {
609                                                                 files++;
610                                                                 doit = 1;
611                                                         }
612                                                 }
613                                                 if (doit) {
614                                                         if (dirc == ' ') {
615                                                                 printf(" %8ld   %s%c\n",
616                                                                         (long)FAT2CPU32(dentptr->size),
617                                                                         l_name,
618                                                                         dirc);
619                                                         } else {
620                                                                 printf("            %s%c\n",
621                                                                         l_name,
622                                                                         dirc);
623                                                         }
624                                                 }
625                                                 dentptr++;
626                                                 continue;
627                                         }
628                                         debug("vfatname: |%s|\n", l_name);
629                                 } else
630 #endif
631                                 {
632                                         /* Volume label or VFAT entry */
633                                         dentptr++;
634                                         continue;
635                                 }
636                         }
637                         if (dentptr->name[0] == 0) {
638                                 if (dols) {
639                                         printf("\n%d file(s), %d dir(s)\n\n",
640                                                 files, dirs);
641                                 }
642                                 debug("Dentname == NULL - %d\n", i);
643                                 return NULL;
644                         }
645 #ifdef CONFIG_SUPPORT_VFAT
646                         if (dols && mkcksum(dentptr->name) == prevcksum) {
647                                 prevcksum = 0xffff;
648                                 dentptr++;
649                                 continue;
650                         }
651 #endif
652                         get_name(dentptr, s_name);
653                         if (dols) {
654                                 int isdir = (dentptr->attr & ATTR_DIR);
655                                 char dirc;
656                                 int doit = 0;
657
658                                 if (isdir) {
659                                         dirs++;
660                                         dirc = '/';
661                                         doit = 1;
662                                 } else {
663                                         dirc = ' ';
664                                         if (s_name[0] != 0) {
665                                                 files++;
666                                                 doit = 1;
667                                         }
668                                 }
669
670                                 if (doit) {
671                                         if (dirc == ' ') {
672                                                 printf(" %8ld   %s%c\n",
673                                                         (long)FAT2CPU32(dentptr->size),
674                                                         s_name, dirc);
675                                         } else {
676                                                 printf("            %s%c\n",
677                                                         s_name, dirc);
678                                         }
679                                 }
680
681                                 dentptr++;
682                                 continue;
683                         }
684
685                         if (strcmp(filename, s_name)
686                             && strcmp(filename, l_name)) {
687                                 debug("Mismatch: |%s|%s|\n", s_name, l_name);
688                                 dentptr++;
689                                 continue;
690                         }
691
692                         memcpy(retdent, dentptr, sizeof(dir_entry));
693
694                         debug("DentName: %s", s_name);
695                         debug(", start: 0x%x", START(dentptr));
696                         debug(", size:  0x%x %s\n",
697                               FAT2CPU32(dentptr->size),
698                               (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
699
700                         return retdent;
701                 }
702
703                 curclust = get_fatent(mydata, curclust);
704                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
705                         debug("curclust: 0x%x\n", curclust);
706                         printf("Invalid FAT entry\n");
707                         return NULL;
708                 }
709         }
710
711         return NULL;
712 }
713
714 /*
715  * Read boot sector and volume info from a FAT filesystem
716  */
717 static int
718 read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
719 {
720         __u8 *block;
721         volume_info *vistart;
722         int ret = 0;
723
724         if (cur_dev == NULL) {
725                 debug("Error: no device selected\n");
726                 return -1;
727         }
728
729         block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz);
730         if (block == NULL) {
731                 debug("Error: allocating block\n");
732                 return -1;
733         }
734
735         if (disk_read(0, 1, block) < 0) {
736                 debug("Error: reading block\n");
737                 goto fail;
738         }
739
740         memcpy(bs, block, sizeof(boot_sector));
741         bs->reserved = FAT2CPU16(bs->reserved);
742         bs->fat_length = FAT2CPU16(bs->fat_length);
743         bs->secs_track = FAT2CPU16(bs->secs_track);
744         bs->heads = FAT2CPU16(bs->heads);
745         bs->total_sect = FAT2CPU32(bs->total_sect);
746
747         /* FAT32 entries */
748         if (bs->fat_length == 0) {
749                 /* Assume FAT32 */
750                 bs->fat32_length = FAT2CPU32(bs->fat32_length);
751                 bs->flags = FAT2CPU16(bs->flags);
752                 bs->root_cluster = FAT2CPU32(bs->root_cluster);
753                 bs->info_sector = FAT2CPU16(bs->info_sector);
754                 bs->backup_boot = FAT2CPU16(bs->backup_boot);
755                 vistart = (volume_info *)(block + sizeof(boot_sector));
756                 *fatsize = 32;
757         } else {
758                 vistart = (volume_info *)&(bs->fat32_length);
759                 *fatsize = 0;
760         }
761         memcpy(volinfo, vistart, sizeof(volume_info));
762
763         if (*fatsize == 32) {
764                 if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
765                         goto exit;
766         } else {
767                 if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
768                         *fatsize = 12;
769                         goto exit;
770                 }
771                 if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
772                         *fatsize = 16;
773                         goto exit;
774                 }
775         }
776
777         debug("Error: broken fs_type sign\n");
778 fail:
779         ret = -1;
780 exit:
781         free(block);
782         return ret;
783 }
784
785 __u8 do_fat_read_block[MAX_CLUSTSIZE]
786         __aligned(ARCH_DMA_MINALIGN);
787
788 long
789 do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
790 {
791         char fnamecopy[2048];
792         boot_sector bs;
793         volume_info volinfo;
794         fsdata datablock;
795         fsdata *mydata = &datablock;
796         dir_entry *dentptr = NULL;
797         __u16 prevcksum = 0xffff;
798         char *subname = "";
799         __u32 cursect;
800         int idx, isdir = 0;
801         int files = 0, dirs = 0;
802         long ret = -1;
803         int firsttime;
804         __u32 root_cluster = 0;
805         int rootdir_size = 0;
806         int j;
807
808         if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
809                 debug("Error: reading boot sector\n");
810                 return -1;
811         }
812
813         if (mydata->fatsize == 32) {
814                 root_cluster = bs.root_cluster;
815                 mydata->fatlength = bs.fat32_length;
816         } else {
817                 mydata->fatlength = bs.fat_length;
818         }
819
820         mydata->fat_sect = bs.reserved;
821
822         cursect = mydata->rootdir_sect
823                 = mydata->fat_sect + mydata->fatlength * bs.fats;
824
825         mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
826         mydata->clust_size = bs.cluster_size;
827         if (mydata->sect_size != cur_part_info.blksz) {
828                 printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
829                                 mydata->sect_size, cur_part_info.blksz);
830                 return -1;
831         }
832
833         if (mydata->fatsize == 32) {
834                 mydata->data_begin = mydata->rootdir_sect -
835                                         (mydata->clust_size * 2);
836         } else {
837                 rootdir_size = ((bs.dir_entries[1]  * (int)256 +
838                                  bs.dir_entries[0]) *
839                                  sizeof(dir_entry)) /
840                                  mydata->sect_size;
841                 mydata->data_begin = mydata->rootdir_sect +
842                                         rootdir_size -
843                                         (mydata->clust_size * 2);
844         }
845
846         mydata->fatbufnum = -1;
847         mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
848         if (mydata->fatbuf == NULL) {
849                 debug("Error: allocating memory\n");
850                 return -1;
851         }
852
853 #ifdef CONFIG_SUPPORT_VFAT
854         debug("VFAT Support enabled\n");
855 #endif
856         debug("FAT%d, fat_sect: %d, fatlength: %d\n",
857                mydata->fatsize, mydata->fat_sect, mydata->fatlength);
858         debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
859                "Data begins at: %d\n",
860                root_cluster,
861                mydata->rootdir_sect,
862                mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
863         debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
864               mydata->clust_size);
865
866         /* "cwd" is always the root... */
867         while (ISDIRDELIM(*filename))
868                 filename++;
869
870         /* Make a copy of the filename and convert it to lowercase */
871         strcpy(fnamecopy, filename);
872         downcase(fnamecopy);
873
874         if (*fnamecopy == '\0') {
875                 if (!dols)
876                         goto exit;
877
878                 dols = LS_ROOT;
879         } else if ((idx = dirdelim(fnamecopy)) >= 0) {
880                 isdir = 1;
881                 fnamecopy[idx] = '\0';
882                 subname = fnamecopy + idx + 1;
883
884                 /* Handle multiple delimiters */
885                 while (ISDIRDELIM(*subname))
886                         subname++;
887         } else if (dols) {
888                 isdir = 1;
889         }
890
891         j = 0;
892         while (1) {
893                 int i;
894
895                 if (j == 0) {
896                         debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n",
897                                 cursect, mydata->clust_size, DIRENTSPERBLOCK);
898
899                         if (disk_read(cursect,
900                                         (mydata->fatsize == 32) ?
901                                         (mydata->clust_size) :
902                                         PREFETCH_BLOCKS,
903                                         do_fat_read_block) < 0) {
904                                 debug("Error: reading rootdir block\n");
905                                 goto exit;
906                         }
907
908                         dentptr = (dir_entry *) do_fat_read_block;
909                 }
910
911                 for (i = 0; i < DIRENTSPERBLOCK; i++) {
912                         char s_name[14], l_name[VFAT_MAXLEN_BYTES];
913
914                         l_name[0] = '\0';
915                         if (dentptr->name[0] == DELETED_FLAG) {
916                                 dentptr++;
917                                 continue;
918                         }
919                         if ((dentptr->attr & ATTR_VOLUME)) {
920 #ifdef CONFIG_SUPPORT_VFAT
921                                 if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
922                                     (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
923                                         prevcksum =
924                                                 ((dir_slot *)dentptr)->alias_checksum;
925
926                                         get_vfatname(mydata,
927                                                      root_cluster,
928                                                      do_fat_read_block,
929                                                      dentptr, l_name);
930
931                                         if (dols == LS_ROOT) {
932                                                 char dirc;
933                                                 int doit = 0;
934                                                 int isdir =
935                                                         (dentptr->attr & ATTR_DIR);
936
937                                                 if (isdir) {
938                                                         dirs++;
939                                                         dirc = '/';
940                                                         doit = 1;
941                                                 } else {
942                                                         dirc = ' ';
943                                                         if (l_name[0] != 0) {
944                                                                 files++;
945                                                                 doit = 1;
946                                                         }
947                                                 }
948                                                 if (doit) {
949                                                         if (dirc == ' ') {
950                                                                 printf(" %8ld   %s%c\n",
951                                                                         (long)FAT2CPU32(dentptr->size),
952                                                                         l_name,
953                                                                         dirc);
954                                                         } else {
955                                                                 printf("            %s%c\n",
956                                                                         l_name,
957                                                                         dirc);
958                                                         }
959                                                 }
960                                                 dentptr++;
961                                                 continue;
962                                         }
963                                         debug("Rootvfatname: |%s|\n",
964                                                l_name);
965                                 } else
966 #endif
967                                 {
968                                         /* Volume label or VFAT entry */
969                                         dentptr++;
970                                         continue;
971                                 }
972                         } else if (dentptr->name[0] == 0) {
973                                 debug("RootDentname == NULL - %d\n", i);
974                                 if (dols == LS_ROOT) {
975                                         printf("\n%d file(s), %d dir(s)\n\n",
976                                                 files, dirs);
977                                         ret = 0;
978                                 }
979                                 goto exit;
980                         }
981 #ifdef CONFIG_SUPPORT_VFAT
982                         else if (dols == LS_ROOT &&
983                                  mkcksum(dentptr->name) == prevcksum) {
984                                 prevcksum = 0xffff;
985                                 dentptr++;
986                                 continue;
987                         }
988 #endif
989                         get_name(dentptr, s_name);
990
991                         if (dols == LS_ROOT) {
992                                 int isdir = (dentptr->attr & ATTR_DIR);
993                                 char dirc;
994                                 int doit = 0;
995
996                                 if (isdir) {
997                                         dirc = '/';
998                                         if (s_name[0] != 0) {
999                                                 dirs++;
1000                                                 doit = 1;
1001                                         }
1002                                 } else {
1003                                         dirc = ' ';
1004                                         if (s_name[0] != 0) {
1005                                                 files++;
1006                                                 doit = 1;
1007                                         }
1008                                 }
1009                                 if (doit) {
1010                                         if (dirc == ' ') {
1011                                                 printf(" %8ld   %s%c\n",
1012                                                         (long)FAT2CPU32(dentptr->size),
1013                                                         s_name, dirc);
1014                                         } else {
1015                                                 printf("            %s%c\n",
1016                                                         s_name, dirc);
1017                                         }
1018                                 }
1019                                 dentptr++;
1020                                 continue;
1021                         }
1022
1023                         if (strcmp(fnamecopy, s_name)
1024                             && strcmp(fnamecopy, l_name)) {
1025                                 debug("RootMismatch: |%s|%s|\n", s_name,
1026                                        l_name);
1027                                 dentptr++;
1028                                 continue;
1029                         }
1030
1031                         if (isdir && !(dentptr->attr & ATTR_DIR))
1032                                 goto exit;
1033
1034                         debug("RootName: %s", s_name);
1035                         debug(", start: 0x%x", START(dentptr));
1036                         debug(", size:  0x%x %s\n",
1037                                FAT2CPU32(dentptr->size),
1038                                isdir ? "(DIR)" : "");
1039
1040                         goto rootdir_done;      /* We got a match */
1041                 }
1042                 debug("END LOOP: j=%d   clust_size=%d\n", j,
1043                        mydata->clust_size);
1044
1045                 /*
1046                  * On FAT32 we must fetch the FAT entries for the next
1047                  * root directory clusters when a cluster has been
1048                  * completely processed.
1049                  */
1050                 ++j;
1051                 int rootdir_end = 0;
1052                 if (mydata->fatsize == 32) {
1053                         if (j == mydata->clust_size) {
1054                                 int nxtsect = 0;
1055                                 int nxt_clust = 0;
1056
1057                                 nxt_clust = get_fatent(mydata, root_cluster);
1058                                 rootdir_end = CHECK_CLUST(nxt_clust, 32);
1059
1060                                 nxtsect = mydata->data_begin +
1061                                         (nxt_clust * mydata->clust_size);
1062
1063                                 root_cluster = nxt_clust;
1064
1065                                 cursect = nxtsect;
1066                                 j = 0;
1067                         }
1068                 } else {
1069                         if (j == PREFETCH_BLOCKS)
1070                                 j = 0;
1071
1072                         rootdir_end = (++cursect - mydata->rootdir_sect >=
1073                                        rootdir_size);
1074                 }
1075
1076                 /* If end of rootdir reached */
1077                 if (rootdir_end) {
1078                         if (dols == LS_ROOT) {
1079                                 printf("\n%d file(s), %d dir(s)\n\n",
1080                                        files, dirs);
1081                                 ret = 0;
1082                         }
1083                         goto exit;
1084                 }
1085         }
1086 rootdir_done:
1087
1088         firsttime = 1;
1089
1090         while (isdir) {
1091                 int startsect = mydata->data_begin
1092                         + START(dentptr) * mydata->clust_size;
1093                 dir_entry dent;
1094                 char *nextname = NULL;
1095
1096                 dent = *dentptr;
1097                 dentptr = &dent;
1098
1099                 idx = dirdelim(subname);
1100
1101                 if (idx >= 0) {
1102                         subname[idx] = '\0';
1103                         nextname = subname + idx + 1;
1104                         /* Handle multiple delimiters */
1105                         while (ISDIRDELIM(*nextname))
1106                                 nextname++;
1107                         if (dols && *nextname == '\0')
1108                                 firsttime = 0;
1109                 } else {
1110                         if (dols && firsttime) {
1111                                 firsttime = 0;
1112                         } else {
1113                                 isdir = 0;
1114                         }
1115                 }
1116
1117                 if (get_dentfromdir(mydata, startsect, subname, dentptr,
1118                                      isdir ? 0 : dols) == NULL) {
1119                         if (dols && !isdir)
1120                                 ret = 0;
1121                         goto exit;
1122                 }
1123
1124                 if (idx >= 0) {
1125                         if (!(dentptr->attr & ATTR_DIR))
1126                                 goto exit;
1127                         subname = nextname;
1128                 }
1129         }
1130
1131         ret = get_contents(mydata, dentptr, buffer, maxsize);
1132         debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
1133
1134 exit:
1135         free(mydata->fatbuf);
1136         return ret;
1137 }
1138
1139 int file_fat_detectfs(void)
1140 {
1141         boot_sector bs;
1142         volume_info volinfo;
1143         int fatsize;
1144         char vol_label[12];
1145
1146         if (cur_dev == NULL) {
1147                 printf("No current device\n");
1148                 return 1;
1149         }
1150
1151 #if defined(CONFIG_CMD_IDE) || \
1152     defined(CONFIG_CMD_SATA) || \
1153     defined(CONFIG_CMD_SCSI) || \
1154     defined(CONFIG_CMD_USB) || \
1155     defined(CONFIG_MMC)
1156         printf("Interface:  ");
1157         switch (cur_dev->if_type) {
1158         case IF_TYPE_IDE:
1159                 printf("IDE");
1160                 break;
1161         case IF_TYPE_SATA:
1162                 printf("SATA");
1163                 break;
1164         case IF_TYPE_SCSI:
1165                 printf("SCSI");
1166                 break;
1167         case IF_TYPE_ATAPI:
1168                 printf("ATAPI");
1169                 break;
1170         case IF_TYPE_USB:
1171                 printf("USB");
1172                 break;
1173         case IF_TYPE_DOC:
1174                 printf("DOC");
1175                 break;
1176         case IF_TYPE_MMC:
1177                 printf("MMC");
1178                 break;
1179         default:
1180                 printf("Unknown");
1181         }
1182
1183         printf("\n  Device %d: ", cur_dev->dev);
1184         dev_print(cur_dev);
1185 #endif
1186
1187         if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
1188                 printf("\nNo valid FAT fs found\n");
1189                 return 1;
1190         }
1191
1192         memcpy(vol_label, volinfo.volume_label, 11);
1193         vol_label[11] = '\0';
1194         volinfo.fs_type[5] = '\0';
1195
1196         printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part_nr,
1197                 volinfo.fs_type, vol_label);
1198
1199         return 0;
1200 }
1201
1202 int file_fat_ls(const char *dir)
1203 {
1204         return do_fat_read(dir, NULL, 0, LS_YES);
1205 }
1206
1207 long file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
1208 {
1209         printf("reading %s\n", filename);
1210         return do_fat_read(filename, buffer, maxsize, LS_NO);
1211 }