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