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