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