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