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