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