* Fix CONFIG_NET_MULTI support in include/net.h
[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 <fat.h>
31 #include <asm/byteorder.h>
32
33 #if (CONFIG_COMMANDS & CFG_CMD_FAT)
34
35 /*
36  * Convert a string to lowercase.
37  */
38 static void
39 downcase(char *str)
40 {
41         while (*str != '\0') {
42                 TOLOWER(*str);
43                 str++;
44         }
45 }
46
47 int (*dev_block_read)(int device, __u32 blknr, __u32 blkcnt, __u8 *buffer) = 0;
48
49 int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr)
50 {
51         /* FIXME we need to determine the start block of the
52          * partition where the DOS FS resides
53          */
54         startblock += 32;
55
56         if (dev_block_read) {
57                 return dev_block_read (0, startblock, getsize, bufptr);
58         }
59         return -1;
60 }
61
62
63 int
64 fat_register_read (int (*block_read)(int, __u32, __u32, __u8 *))
65 {
66         dev_block_read = block_read;
67         return 0;
68 }
69
70
71 /*
72  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
73  * Return index into string if found, -1 otherwise.
74  */
75 static int
76 dirdelim(char *str)
77 {
78         char *start = str;
79
80         while (*str != '\0') {
81                 if (ISDIRDELIM(*str)) return str - start;
82                 str++;
83         }
84         return -1;
85 }
86
87
88 /*
89  * Match volume_info fs_type strings.
90  * Return 0 on match, -1 otherwise.
91  */
92 static int
93 compare_sign(char *str1, char *str2)
94 {
95         char *end = str1+SIGNLEN;
96
97         while (str1 != end) {
98                 if (*str1 != *str2) {
99                         return -1;
100                 }
101                 str1++;
102                 str2++;
103         }
104
105         return 0;
106 }
107
108
109 /*
110  * Extract zero terminated short name from a directory entry.
111  */
112 static void get_name (dir_entry *dirent, char *s_name)
113 {
114         char *ptr;
115
116         memcpy (s_name, dirent->name, 8);
117         s_name[8] = '\0';
118         ptr = s_name;
119         while (*ptr && *ptr != ' ')
120                 ptr++;
121         if (dirent->ext[0] && dirent->ext[0] != ' ') {
122                 *ptr = '.';
123                 ptr++;
124                 memcpy (ptr, dirent->ext, 3);
125                 ptr[3] = '\0';
126                 while (*ptr && *ptr != ' ')
127                         ptr++;
128         }
129         *ptr = '\0';
130         if (*s_name == DELETED_FLAG)
131                 *s_name = '\0';
132         else if (*s_name == aRING)
133                 *s_name = 'å';
134         downcase (s_name);
135 }
136
137 /*
138  * Get the entry at index 'entry' in a FAT (12/16/32) table.
139  * On failure 0x00 is returned.
140  */
141 static __u32
142 get_fatent(fsdata *mydata, __u32 entry)
143 {
144         __u32 bufnum;
145         __u32 offset;
146         __u32 ret = 0x00;
147
148         switch (mydata->fatsize) {
149         case 32:
150                 bufnum = entry / FAT32BUFSIZE;
151                 offset = entry - bufnum * FAT32BUFSIZE;
152                 break;
153         case 16:
154                 bufnum = entry / FAT16BUFSIZE;
155                 offset = entry - bufnum * FAT16BUFSIZE;
156                 break;
157         case 12:
158                 bufnum = entry / FAT12BUFSIZE;
159                 offset = entry - bufnum * FAT12BUFSIZE;
160                 break;
161
162         default:
163                 /* Unsupported FAT size */
164                 return ret;
165         }
166
167         /* Read a new block of FAT entries into the cache. */
168         if (bufnum != mydata->fatbufnum) {
169                 int getsize = FATBUFSIZE/FS_BLOCK_SIZE;
170                 __u8 *bufptr = mydata->fatbuf;
171                 __u32 fatlength = mydata->fatlength;
172                 __u32 startblock = bufnum * FATBUFBLOCKS;
173
174                 fatlength *= SECTOR_SIZE;       /* We want it in bytes now */
175                 startblock += mydata->fat_sect; /* Offset from start of disk */
176
177                 if (getsize > fatlength) getsize = fatlength;
178                 if (disk_read(startblock, getsize, bufptr) < 0) {
179                         FAT_DPRINT("Error reading FAT blocks\n");
180                         return ret;
181                 }
182                 mydata->fatbufnum = bufnum;
183         }
184
185         /* Get the actual entry from the table */
186         switch (mydata->fatsize) {
187         case 32:
188                 ret = FAT2CPU32(((__u32*)mydata->fatbuf)[offset]);
189                 break;
190         case 16:
191                 ret = FAT2CPU16(((__u16*)mydata->fatbuf)[offset]);
192                 break;
193         case 12: {
194                 __u32 off16 = (offset*3)/4;
195                 __u16 val1, val2;
196
197                 switch (offset & 0x3) {
198                 case 0:
199                         ret = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);
200                         ret &= 0xfff;
201                         break;
202                 case 1:
203                         val1 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);
204                         val1 &= 0xf000;
205                         val2 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16+1]);
206                         val2 &= 0x00ff;
207                         ret = (val2 << 4) | (val1 >> 12);
208                         break;
209                 case 2:
210                         val1 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);
211                         val1 &= 0xff00;
212                         val2 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16+1]);
213                         val2 &= 0x000f;
214                         ret = (val2 << 8) | (val1 >> 8);
215                         break;
216                 case 3:
217                         ret = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);;
218                         ret = (ret & 0xfff0) >> 4;
219                         break;
220                 default:
221                         break;
222                 }
223         }
224         break;
225         }
226         FAT_DPRINT("ret: %d, offset: %d\n", ret, offset);
227
228         return ret;
229 }
230
231
232 /*
233  * Read at most 'size' bytes from the specified cluster into 'buffer'.
234  * Return 0 on success, -1 otherwise.
235  */
236 static int
237 get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
238 {
239         int idx = 0;
240         __u32 startsect;
241
242         if (clustnum > 0) {
243                 startsect = mydata->data_begin + clustnum*mydata->clust_size;
244         } else {
245                 startsect = mydata->rootdir_sect;
246         }
247
248         FAT_DPRINT("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
249         while (size > 0) {
250                 if (size >= FS_BLOCK_SIZE) {
251                         if (disk_read(startsect + idx, 1, buffer) < 0) {
252                                 FAT_DPRINT("Error reading data\n");
253                                 return -1;
254                         }
255                 } else {
256                         __u8 tmpbuf[FS_BLOCK_SIZE];
257                         if (disk_read(startsect + idx, 1, tmpbuf) < 0) {
258                                 FAT_DPRINT("Error reading data\n");
259                                 return -1;
260                         }
261                         memcpy(buffer, tmpbuf, size);
262
263                         return 0;
264                 }
265                 buffer += FS_BLOCK_SIZE;
266                 size -= FS_BLOCK_SIZE;
267                 idx++;
268         }
269
270         return 0;
271 }
272
273
274 /*
275  * Read at most 'maxsize' bytes from the file associated with 'dentptr'
276  * into 'buffer'.
277  * Return the number of bytes read or -1 on fatal errors.
278  */
279 static long
280 get_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
281              unsigned long maxsize)
282 {
283         unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
284         unsigned int bytesperclust = mydata->clust_size * SECTOR_SIZE;
285         __u32 curclust = START(dentptr);
286
287         FAT_DPRINT("Filesize: %ld bytes\n", filesize);
288
289         if (maxsize > 0 && filesize > maxsize) filesize = maxsize;
290
291         FAT_DPRINT("Reading: %ld bytes\n", filesize);
292
293         do {
294                 int getsize = (filesize > bytesperclust) ? bytesperclust :
295                         filesize;
296
297                 if (get_cluster(mydata, curclust, buffer, getsize) != 0) {
298                         FAT_ERROR("Error reading cluster\n");
299                         return -1;
300                 }
301                 gotsize += getsize;
302                 filesize -= getsize;
303                 if (filesize <= 0) return gotsize;
304                 buffer += getsize;
305
306                 curclust = get_fatent(mydata, curclust);
307                 if (curclust <= 0x0001 || curclust >= 0xfff0) {
308                         FAT_DPRINT("curclust: 0x%x\n", curclust);
309                         FAT_ERROR("Invalid FAT entry\n");
310                         return gotsize;
311                 }
312         } while (1);
313 }
314
315
316 #ifdef CONFIG_SUPPORT_VFAT
317 /*
318  * Extract the file name information from 'slotptr' into 'l_name',
319  * starting at l_name[*idx].
320  * Return 1 if terminator (zero byte) is found, 0 otherwise.
321  */
322 static int
323 slot2str(dir_slot *slotptr, char *l_name, int *idx)
324 {
325         int j;
326
327         for (j = 0; j <= 8; j += 2) {
328                 l_name[*idx] = slotptr->name0_4[j];
329                 if (l_name[*idx] == 0x00) return 1;
330                 (*idx)++;
331         }
332         for (j = 0; j <= 10; j += 2) {
333                 l_name[*idx] = slotptr->name5_10[j];
334                 if (l_name[*idx] == 0x00) return 1;
335                 (*idx)++;
336         }
337         for (j = 0; j <= 2; j += 2) {
338                 l_name[*idx] = slotptr->name11_12[j];
339                 if (l_name[*idx] == 0x00) return 1;
340                 (*idx)++;
341         }
342
343         return 0;
344 }
345
346
347 /*
348  * Extract the full long filename starting at 'retdent' (which is really
349  * a slot) into 'l_name'. If successful also copy the real directory entry
350  * into 'retdent'
351  * Return 0 on success, -1 otherwise.
352  */
353 static int
354 get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
355              dir_entry *retdent, char *l_name)
356 {
357         dir_entry *realdent;
358         dir_slot  *slotptr = (dir_slot*) retdent;
359         __u8      *nextclust = cluster + mydata->clust_size * SECTOR_SIZE;
360         __u8       counter = slotptr->id & 0xf;
361         int idx = 0;
362
363         while ((__u8*)slotptr < nextclust) {
364                 if (counter == 0) break;
365                 if ((slotptr->id & 0x0f) != counter) return -1;
366                 slotptr++;
367                 counter--;
368         }
369
370         if ((__u8*)slotptr >= nextclust) {
371                 __u8     block[MAX_CLUSTSIZE];
372                 dir_slot *slotptr2;
373
374                 slotptr--;
375                 curclust = get_fatent(mydata, curclust);
376                 if (curclust <= 0x0001 || curclust >= 0xfff0) {
377                         FAT_DPRINT("curclust: 0x%x\n", curclust);
378                         FAT_ERROR("Invalid FAT entry\n");
379                         return -1;
380                 }
381                 if (get_cluster(mydata, curclust, block,
382                                 mydata->clust_size * SECTOR_SIZE) != 0) {
383                         FAT_DPRINT("Error: reading directory block\n");
384                         return -1;
385                 }
386                 slotptr2 = (dir_slot*) block;
387                 while (slotptr2->id > 0x01) {
388                         slotptr2++;
389                 }
390                 /* Save the real directory entry */
391                 realdent = (dir_entry*)slotptr2 + 1;
392                 while ((__u8*)slotptr2 >= block) {
393                         slot2str(slotptr2, l_name, &idx);
394                         slotptr2--;
395                 }
396         } else {
397                 /* Save the real directory entry */
398                 realdent = (dir_entry*)slotptr;
399         }
400
401         do {
402                 slotptr--;
403                 if (slot2str(slotptr, l_name, &idx)) break;
404         } while (!(slotptr->id & 0x40));
405
406         l_name[idx] = '\0';
407         if (*l_name == DELETED_FLAG) *l_name = '\0';
408         else if (*l_name == aRING) *l_name = 'å';
409         downcase(l_name);
410
411         /* Return the real directory entry */
412         memcpy(retdent, realdent, sizeof(dir_entry));
413
414         return 0;
415 }
416
417
418 /* Calculate short name checksum */
419 static __u8
420 mkcksum(const char *str)
421 {
422         int i;
423         __u8 ret = 0;
424
425         for (i = 0; i < 11; i++) {
426                 ret = (((ret&1)<<7)|((ret&0xfe)>>1)) + str[i];
427         }
428
429         return ret;
430 }
431 #endif
432
433
434 /*
435  * Get the directory entry associated with 'filename' from the directory
436  * starting at 'startsect'
437  */
438 static dir_entry *get_dentfromdir (fsdata * mydata, int startsect,
439                                    char *filename, dir_entry * retdent,
440                                    int dols)
441 {
442     __u16 prevcksum = 0xffff;
443     __u8 block[MAX_CLUSTSIZE];
444     __u32 curclust = START (retdent);
445     int files = 0, dirs = 0;
446
447     FAT_DPRINT ("get_dentfromdir: %s\n", filename);
448     while (1) {
449         dir_entry *dentptr;
450         int i;
451
452         if (get_cluster (mydata, curclust, block,
453                  mydata->clust_size * SECTOR_SIZE) != 0) {
454             FAT_DPRINT ("Error: reading directory block\n");
455             return NULL;
456         }
457         dentptr = (dir_entry *) block;
458         for (i = 0; i < DIRENTSPERCLUST; i++) {
459             char s_name[14], l_name[256];
460
461             l_name[0] = '\0';
462             if ((dentptr->attr & ATTR_VOLUME)) {
463 #ifdef CONFIG_SUPPORT_VFAT
464                 if ((dentptr->attr & ATTR_VFAT) &&
465                     (dentptr->name[0] & 0x40)) {
466                     prevcksum = ((dir_slot *) dentptr)
467                             ->alias_checksum;
468                     get_vfatname (mydata, curclust, block,
469                                   dentptr, l_name);
470                     if (dols) {
471                         int isdir = (dentptr->attr & ATTR_DIR);
472                         char dirc;
473                         int doit = 0;
474
475                         if (isdir) {
476                             dirs++;
477                             dirc = '/';
478                             doit = 1;
479                         } else {
480                             dirc = ' ';
481                             if (l_name[0] != 0) {
482                                 files++;
483                                 doit = 1;
484                             }
485                         }
486                         if (doit) {
487                             if (dirc == ' ') {
488                                 printf (" %8ld   %s%c\n",
489                                         (long) FAT2CPU32 (dentptr->size),
490                                         l_name, dirc);
491                             } else {
492                                 printf ("            %s%c\n", l_name, dirc);
493                             }
494                         }
495                         dentptr++;
496                         continue;
497                     }
498                     FAT_DPRINT ("vfatname: |%s|\n", l_name);
499                 } else
500 #endif
501                 {
502                     /* Volume label or VFAT entry */
503                     dentptr++;
504                     continue;
505                 }
506             }
507             if (dentptr->name[0] == 0) {
508                 if (dols) {
509                     printf ("\n%d file(s), %d dir(s)\n\n", files, dirs);
510                 }
511                 FAT_DPRINT ("Dentname == NULL - %d\n", i);
512                 return NULL;
513             }
514 #ifdef CONFIG_SUPPORT_VFAT
515             if (dols && mkcksum (dentptr->name) == prevcksum) {
516                 dentptr++;
517                 continue;
518             }
519 #endif
520             get_name (dentptr, s_name);
521             if (dols) {
522                 int isdir = (dentptr->attr & ATTR_DIR);
523                 char dirc;
524                 int doit = 0;
525
526                 if (isdir) {
527                     dirs++;
528                     dirc = '/';
529                     doit = 1;
530                 } else {
531                     dirc = ' ';
532                     if (s_name[0] != 0) {
533                         files++;
534                         doit = 1;
535                     }
536                 }
537                 if (doit) {
538                     if (dirc == ' ') {
539                         printf (" %8ld   %s%c\n",
540                                 (long) FAT2CPU32 (dentptr->size), s_name,
541                                 dirc);
542                     } else {
543                         printf ("            %s%c\n", s_name, dirc);
544                     }
545                 }
546                 dentptr++;
547                 continue;
548             }
549             if (strcmp (filename, s_name) && strcmp (filename, l_name)) {
550                 FAT_DPRINT ("Mismatch: |%s|%s|\n", s_name, l_name);
551                 dentptr++;
552                 continue;
553             }
554             memcpy (retdent, dentptr, sizeof (dir_entry));
555
556             FAT_DPRINT ("DentName: %s", s_name);
557             FAT_DPRINT (", start: 0x%x", START (dentptr));
558             FAT_DPRINT (", size:  0x%x %s\n",
559                         FAT2CPU32 (dentptr->size),
560                         (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
561
562             return retdent;
563         }
564         curclust = get_fatent (mydata, curclust);
565         if (curclust <= 0x0001 || curclust >= 0xfff0) {
566             FAT_DPRINT ("curclust: 0x%x\n", curclust);
567             FAT_ERROR ("Invalid FAT entry\n");
568             return NULL;
569         }
570     }
571
572     return NULL;
573 }
574
575
576 /*
577  * Read boot sector and volume info from a FAT filesystem
578  */
579 static int
580 read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
581 {
582         __u8 block[FS_BLOCK_SIZE];
583         volume_info *vistart;
584
585         if (disk_read(0, 1, block) < 0) {
586                 FAT_DPRINT("Error: reading block\n");
587                 return -1;
588         }
589
590         memcpy(bs, block, sizeof(boot_sector));
591         bs->reserved    = FAT2CPU16(bs->reserved);
592         bs->fat_length  = FAT2CPU16(bs->fat_length);
593         bs->secs_track  = FAT2CPU16(bs->secs_track);
594         bs->heads       = FAT2CPU16(bs->heads);
595 #if 0 /* UNUSED */
596         bs->hidden      = FAT2CPU32(bs->hidden);
597 #endif
598         bs->total_sect  = FAT2CPU32(bs->total_sect);
599
600         /* FAT32 entries */
601         if (bs->fat_length == 0) {
602                 /* Assume FAT32 */
603                 bs->fat32_length = FAT2CPU32(bs->fat32_length);
604                 bs->flags        = FAT2CPU16(bs->flags);
605                 bs->root_cluster = FAT2CPU32(bs->root_cluster);
606                 bs->info_sector  = FAT2CPU16(bs->info_sector);
607                 bs->backup_boot  = FAT2CPU16(bs->backup_boot);
608                 vistart = (volume_info*) (block + sizeof(boot_sector));
609                 *fatsize = 32;
610         } else {
611                 vistart = (volume_info*) &(bs->fat32_length);
612                 *fatsize = 0;
613         }
614         memcpy(volinfo, vistart, sizeof(volume_info));
615
616         /* Terminate fs_type string. Writing past the end of vistart
617            is ok - it's just the buffer. */
618         vistart->fs_type[8] = '\0';
619
620         if (*fatsize == 32) {
621                 if (compare_sign(FAT32_SIGN, vistart->fs_type) == 0) {
622                         return 0;
623                 }
624         } else {
625                 if (compare_sign(FAT12_SIGN, vistart->fs_type) == 0) {
626                         *fatsize = 12;
627                         return 0;
628                 }
629                 if (compare_sign(FAT16_SIGN, vistart->fs_type) == 0) {
630                         *fatsize = 16;
631                         return 0;
632                 }
633         }
634
635         FAT_DPRINT("Error: broken fs_type sign\n");
636         return -1;
637 }
638
639
640 static long
641 do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
642              int dols)
643 {
644     __u8 block[FS_BLOCK_SIZE];  /* Block buffer */
645     char fnamecopy[2048];
646     boot_sector bs;
647     volume_info volinfo;
648     fsdata datablock;
649     fsdata *mydata = &datablock;
650     dir_entry *dentptr;
651     __u16 prevcksum = 0xffff;
652     char *subname = "";
653     int rootdir_size, cursect;
654     int idx, isdir = 0;
655     int files = 0, dirs = 0;
656     long ret = 0;
657     int firsttime;
658
659     if (read_bootsectandvi (&bs, &volinfo, &mydata->fatsize)) {
660         FAT_DPRINT ("Error: reading boot sector\n");
661         return -1;
662     }
663     if (mydata->fatsize == 32) {
664         mydata->fatlength = bs.fat32_length;
665     } else {
666         mydata->fatlength = bs.fat_length;
667     }
668     mydata->fat_sect = bs.reserved;
669     cursect = mydata->rootdir_sect
670             = mydata->fat_sect + mydata->fatlength * bs.fats;
671     mydata->clust_size = bs.cluster_size;
672     if (mydata->fatsize == 32) {
673         rootdir_size = mydata->clust_size;
674         mydata->data_begin = mydata->rootdir_sect   /* + rootdir_size */
675                 - (mydata->clust_size * 2);
676     } else {
677         rootdir_size = ((bs.dir_entries[1] * (int) 256 + bs.dir_entries[0])
678                         * sizeof (dir_entry)) / SECTOR_SIZE;
679         mydata->data_begin = mydata->rootdir_sect + rootdir_size
680                 - (mydata->clust_size * 2);
681     }
682     mydata->fatbufnum = -1;
683
684     FAT_DPRINT ("FAT%d, fatlength: %d\n", mydata->fatsize,
685                 mydata->fatlength);
686     FAT_DPRINT ("Rootdir begins at sector: %d, offset: %x, size: %d\n"
687                 "Data begins at: %d\n",
688                 mydata->rootdir_sect, mydata->rootdir_sect * SECTOR_SIZE,
689                 rootdir_size, mydata->data_begin);
690     FAT_DPRINT ("Cluster size: %d\n", mydata->clust_size);
691
692     /* "cwd" is always the root... */
693     while (ISDIRDELIM (*filename))
694         filename++;
695     /* Make a copy of the filename and convert it to lowercase */
696     strcpy (fnamecopy, filename);
697     downcase (fnamecopy);
698     if (*fnamecopy == '\0') {
699         if (!dols)
700             return -1;
701         dols = LS_ROOT;
702     } else if ((idx = dirdelim (fnamecopy)) >= 0) {
703         isdir = 1;
704         fnamecopy[idx] = '\0';
705         subname = fnamecopy + idx + 1;
706         /* Handle multiple delimiters */
707         while (ISDIRDELIM (*subname))
708             subname++;
709     } else if (dols) {
710         isdir = 1;
711     }
712
713     while (1) {
714         int i;
715
716         if (disk_read (cursect, 1, block) < 0) {
717             FAT_DPRINT ("Error: reading rootdir block\n");
718             return -1;
719         }
720         dentptr = (dir_entry *) block;
721         for (i = 0; i < DIRENTSPERBLOCK; i++) {
722             char s_name[14], l_name[256];
723
724             l_name[0] = '\0';
725             if ((dentptr->attr & ATTR_VOLUME)) {
726 #ifdef CONFIG_SUPPORT_VFAT
727                 if ((dentptr->attr & ATTR_VFAT) &&
728                     (dentptr->name[0] & 0x40)) {
729                     prevcksum = ((dir_slot *) dentptr)->alias_checksum;
730                     get_vfatname (mydata, 0, block, dentptr, l_name);
731                     if (dols == LS_ROOT) {
732                         int isdir = (dentptr->attr & ATTR_DIR);
733                         char dirc;
734                         int doit = 0;
735
736                         if (isdir) {
737                             dirs++;
738                             dirc = '/';
739                             doit = 1;
740                         } else {
741                             dirc = ' ';
742                             if (l_name[0] != 0) {
743                                 files++;
744                                 doit = 1;
745                             }
746                         }
747                         if (doit) {
748                             if (dirc == ' ') {
749                                 printf (" %8ld   %s%c\n",
750                                         (long) FAT2CPU32 (dentptr->size),
751                                         l_name, dirc);
752                             } else {
753                                 printf ("            %s%c\n", l_name, dirc);
754                             }
755                         }
756                         dentptr++;
757                         continue;
758                     }
759                     FAT_DPRINT ("Rootvfatname: |%s|\n", l_name);
760                 } else
761 #endif
762                 {
763                     /* Volume label or VFAT entry */
764                     dentptr++;
765                     continue;
766                 }
767             } else if (dentptr->name[0] == 0) {
768                 FAT_DPRINT ("RootDentname == NULL - %d\n", i);
769                 if (dols == LS_ROOT) {
770                     printf ("\n%d file(s), %d dir(s)\n\n", files, dirs);
771                     return 0;
772                 }
773                 return -1;
774             }
775 #ifdef CONFIG_SUPPORT_VFAT
776             else if (dols == LS_ROOT
777                      && mkcksum (dentptr->name) == prevcksum) {
778                 dentptr++;
779                 continue;
780             }
781 #endif
782             get_name (dentptr, s_name);
783             if (dols == LS_ROOT) {
784                 int isdir = (dentptr->attr & ATTR_DIR);
785                 char dirc;
786                 int doit = 0;
787
788                 if (isdir) {
789                     dirs++;
790                     dirc = '/';
791                     doit = 1;
792                 } else {
793                     dirc = ' ';
794                     if (s_name[0] != 0) {
795                         files++;
796                         doit = 1;
797                     }
798                 }
799                 if (doit) {
800                     if (dirc == ' ') {
801                         printf (" %8ld   %s%c\n",
802                                 (long) FAT2CPU32 (dentptr->size), s_name,
803                                 dirc);
804                     } else {
805                         printf ("            %s%c\n", s_name, dirc);
806                     }
807                 }
808                 dentptr++;
809                 continue;
810             }
811             if (strcmp (fnamecopy, s_name) && strcmp (fnamecopy, l_name)) {
812                 FAT_DPRINT ("RootMismatch: |%s|%s|\n", s_name, l_name);
813                 dentptr++;
814                 continue;
815             }
816             if (isdir && !(dentptr->attr & ATTR_DIR))
817                 return -1;
818
819             FAT_DPRINT ("RootName: %s", s_name);
820             FAT_DPRINT (", start: 0x%x", START (dentptr));
821             FAT_DPRINT (", size:  0x%x %s\n",
822                         FAT2CPU32 (dentptr->size), isdir ? "(DIR)" : "");
823
824             goto rootdir_done;  /* We got a match */
825         }
826         cursect++;
827     }
828   rootdir_done:
829
830     firsttime = 1;
831     while (isdir) {
832         int startsect = mydata->data_begin
833                 + START (dentptr) * mydata->clust_size;
834         dir_entry dent;
835         char *nextname = NULL;
836
837         dent = *dentptr;
838         dentptr = &dent;
839
840         idx = dirdelim (subname);
841         if (idx >= 0) {
842             subname[idx] = '\0';
843             nextname = subname + idx + 1;
844             /* Handle multiple delimiters */
845             while (ISDIRDELIM (*nextname))
846                 nextname++;
847             if (dols && *nextname == '\0')
848                 firsttime = 0;
849         } else {
850             if (dols && firsttime) {
851                 firsttime = 0;
852             } else {
853                 isdir = 0;
854             }
855         }
856
857         if (get_dentfromdir (mydata, startsect, subname, dentptr,
858                              isdir ? 0 : dols) == NULL) {
859             if (dols && !isdir)
860                 return 0;
861             return -1;
862         }
863
864         if (idx >= 0) {
865             if (!(dentptr->attr & ATTR_DIR))
866                 return -1;
867             subname = nextname;
868         }
869     }
870     ret = get_contents (mydata, dentptr, buffer, maxsize);
871     FAT_DPRINT ("Size: %d, got: %ld\n", FAT2CPU32 (dentptr->size), ret);
872
873     return ret;
874 }
875
876
877 int
878 file_fat_detectfs(void)
879 {
880         boot_sector     bs;
881         volume_info     volinfo;
882         int             fatsize;
883
884         return read_bootsectandvi(&bs, &volinfo, &fatsize);
885 }
886
887
888 int
889 file_fat_ls(const char *dir)
890 {
891         return do_fat_read(dir, NULL, 0, LS_YES);
892 }
893
894
895 long
896 file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
897 {
898         return do_fat_read(filename, buffer, maxsize, LS_NO);
899 }
900
901 #endif /* #if (CONFIG_COMMANDS & CFG_CMD_FAT) */