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