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