fat.c 29.9 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * fat.c
 *
 * R/O (V)FAT 12/16/32 filesystem implementation by Marcus Sundberg
 *
 * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
 * 2003-03-10 - kharris@nexus-tech.net - ported to uboot
 *
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 17
#include <fat.h>
#include <asm/byteorder.h>
W
wdenk 已提交
18
#include <part.h>
19
#include <malloc.h>
20
#include <memalign.h>
21
#include <linux/compiler.h>
22
#include <linux/ctype.h>
23

24 25 26 27 28 29
#ifdef CONFIG_SUPPORT_VFAT
static const int vfat_enabled = 1;
#else
static const int vfat_enabled = 0;
#endif

30 31 32
/*
 * Convert a string to lowercase.
 */
33
static void downcase(char *str)
34 35
{
	while (*str != '\0') {
36
		*str = tolower(*str);
37 38 39 40
		str++;
	}
}

41
static struct blk_desc *cur_dev;
42
static disk_partition_t cur_part_info;
W
Wolfgang Denk 已提交
43

44
#define DOS_BOOT_MAGIC_OFFSET	0x1fe
W
wdenk 已提交
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 51
	ulong ret;

52
	if (!cur_dev)
W
wdenk 已提交
53
		return -1;
W
Wolfgang Denk 已提交
54

55
	ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf);
56

57
	if (ret != nr_blocks)
58 59 60
		return -1;

	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);
W
wdenk 已提交
66

67 68
	cur_dev = dev_desc;
	cur_part_info = *info;
69 70 71 72 73

	/* Make sure it has a valid FAT header */
	if (disk_read(0, 1, buffer) != 1) {
		cur_dev = NULL;
		return -1;
74
	}
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

	/* Check if it's actually a DOS volume */
	if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) {
		cur_dev = NULL;
		return -1;
	}

	/* Check for FAT12/FAT16/FAT32 filesystem */
	if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3))
		return 0;
	if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5))
		return 0;

	cur_dev = NULL;
	return -1;
90 91
}

92
int fat_register_device(struct blk_desc *dev_desc, int part_no)
93 94 95 96 97 98 99
{
	disk_partition_t info;

	/* First close any currently found FAT filesystem */
	cur_dev = NULL;

	/* Read the partition table, if present */
100
	if (part_get_info(dev_desc, part_no, &info)) {
101 102
		if (part_no != 0) {
			printf("** Partition %d not valid on device %d **\n",
103
					part_no, dev_desc->devnum);
104 105 106 107 108 109 110 111 112
			return -1;
		}

		info.start = 0;
		info.size = dev_desc->lba;
		info.blksz = dev_desc->blksz;
		info.name[0] = 0;
		info.type[0] = 0;
		info.bootable = 0;
113
#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
114 115 116 117 118 119
		info.uuid[0] = 0;
#endif
	}

	return fat_set_blk_dev(dev_desc, &info);
}
120

121 122 123 124
/*
 * Get the first occurence of a directory delimiter ('/' or '\') in a string.
 * Return index into string if found, -1 otherwise.
 */
125
static int dirdelim(char *str)
126 127 128 129
{
	char *start = str;

	while (*str != '\0') {
W
Wolfgang Denk 已提交
130 131
		if (ISDIRDELIM(*str))
			return str - start;
132 133 134 135 136 137 138 139
		str++;
	}
	return -1;
}

/*
 * Extract zero terminated short name from a directory entry.
 */
140
static void get_name(dir_entry *dirent, char *s_name)
141 142 143
{
	char *ptr;

W
Wolfgang Denk 已提交
144
	memcpy(s_name, dirent->name, 8);
145 146 147 148 149 150 151
	s_name[8] = '\0';
	ptr = s_name;
	while (*ptr && *ptr != ' ')
		ptr++;
	if (dirent->ext[0] && dirent->ext[0] != ' ') {
		*ptr = '.';
		ptr++;
W
Wolfgang Denk 已提交
152
		memcpy(ptr, dirent->ext, 3);
153 154 155 156 157 158 159 160
		ptr[3] = '\0';
		while (*ptr && *ptr != ' ')
			ptr++;
	}
	*ptr = '\0';
	if (*s_name == DELETED_FLAG)
		*s_name = '\0';
	else if (*s_name == aRING)
161
		*s_name = DELETED_FLAG;
W
Wolfgang Denk 已提交
162
	downcase(s_name);
163 164
}

165 166 167 168 169 170 171 172 173 174
static int flush_dirty_fat_buffer(fsdata *mydata);
#if !defined(CONFIG_FAT_WRITE)
/* Stub for read only operation */
int flush_dirty_fat_buffer(fsdata *mydata)
{
	(void)(mydata);
	return 0;
}
#endif

175 176 177 178
/*
 * Get the entry at index 'entry' in a FAT (12/16/32) table.
 * On failure 0x00 is returned.
 */
179
static __u32 get_fatent(fsdata *mydata, __u32 entry)
180 181
{
	__u32 bufnum;
182
	__u32 offset, off8;
183 184
	__u32 ret = 0x00;

185 186 187 188 189
	if (CHECK_CLUST(entry, mydata->fatsize)) {
		printf("Error: Invalid FAT entry: 0x%08x\n", entry);
		return ret;
	}

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
	switch (mydata->fatsize) {
	case 32:
		bufnum = entry / FAT32BUFSIZE;
		offset = entry - bufnum * FAT32BUFSIZE;
		break;
	case 16:
		bufnum = entry / FAT16BUFSIZE;
		offset = entry - bufnum * FAT16BUFSIZE;
		break;
	case 12:
		bufnum = entry / FAT12BUFSIZE;
		offset = entry - bufnum * FAT12BUFSIZE;
		break;

	default:
		/* Unsupported FAT size */
		return ret;
	}

209
	debug("FAT%d: entry: 0x%08x = %d, offset: 0x%04x = %d\n",
W
Wolfgang Denk 已提交
210
	       mydata->fatsize, entry, entry, offset, offset);
211

212 213
	/* Read a new block of FAT entries into the cache. */
	if (bufnum != mydata->fatbufnum) {
214
		__u32 getsize = FATBUFBLOCKS;
215 216 217 218
		__u8 *bufptr = mydata->fatbuf;
		__u32 fatlength = mydata->fatlength;
		__u32 startblock = bufnum * FATBUFBLOCKS;

219
		/* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
220 221
		if (startblock + getsize > fatlength)
			getsize = fatlength - startblock;
222

223 224
		startblock += mydata->fat_sect;	/* Offset from start of disk */

225 226 227 228
		/* Write back the fatbuf to the disk */
		if (flush_dirty_fat_buffer(mydata) < 0)
			return -1;

229
		if (disk_read(startblock, getsize, bufptr) < 0) {
W
Wolfgang Denk 已提交
230
			debug("Error reading FAT blocks\n");
231 232 233 234 235 236 237 238
			return ret;
		}
		mydata->fatbufnum = bufnum;
	}

	/* Get the actual entry from the table */
	switch (mydata->fatsize) {
	case 32:
W
Wolfgang Denk 已提交
239
		ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
240 241
		break;
	case 16:
W
Wolfgang Denk 已提交
242
		ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
243
		break;
W
Wolfgang Denk 已提交
244
	case 12:
245 246 247
		off8 = (offset * 3) / 2;
		/* fatbut + off8 may be unaligned, read in byte granularity */
		ret = mydata->fatbuf[off8] + (mydata->fatbuf[off8 + 1] << 8);
248 249 250 251

		if (offset & 0x1)
			ret >>= 4;
		ret &= 0xfff;
252
	}
253 254
	debug("FAT%d: ret: 0x%08x, entry: 0x%08x, offset: 0x%04x\n",
	       mydata->fatsize, ret, entry, offset);
255 256 257 258 259 260 261 262 263

	return ret;
}

/*
 * Read at most 'size' bytes from the specified cluster into 'buffer'.
 * Return 0 on success, -1 otherwise.
 */
static int
264
get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
265
{
E
Erik Hansen 已提交
266
	__u32 idx = 0;
267
	__u32 startsect;
K
Kyle Moffett 已提交
268
	int ret;
269 270

	if (clustnum > 0) {
W
Wolfgang Denk 已提交
271 272
		startsect = mydata->data_begin +
				clustnum * mydata->clust_size;
273 274 275 276
	} else {
		startsect = mydata->rootdir_sect;
	}

W
Wolfgang Denk 已提交
277 278
	debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);

279
	if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
280
		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
W
Wolfgang Denk 已提交
281

282 283 284 285 286 287 288 289 290 291 292 293 294 295
		printf("FAT: Misaligned buffer address (%p)\n", buffer);

		while (size >= mydata->sect_size) {
			ret = disk_read(startsect++, 1, tmpbuf);
			if (ret != 1) {
				debug("Error reading data (got %d)\n", ret);
				return -1;
			}

			memcpy(buffer, tmpbuf, mydata->sect_size);
			buffer += mydata->sect_size;
			size -= mydata->sect_size;
		}
	} else {
296
		idx = size / mydata->sect_size;
297 298 299 300 301 302 303 304 305 306 307 308 309 310
		ret = disk_read(startsect, idx, buffer);
		if (ret != idx) {
			debug("Error reading data (got %d)\n", ret);
			return -1;
		}
		startsect += idx;
		idx *= mydata->sect_size;
		buffer += idx;
		size -= idx;
	}
	if (size) {
		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);

		ret = disk_read(startsect, 1, tmpbuf);
K
Kyle Moffett 已提交
311 312
		if (ret != 1) {
			debug("Error reading data (got %d)\n", ret);
W
wdenk 已提交
313
			return -1;
314
		}
W
wdenk 已提交
315

316
		memcpy(buffer, tmpbuf, size);
317 318 319 320 321 322
	}

	return 0;
}

/*
323
 * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr'
324
 * into 'buffer'.
325
 * Update the number of bytes read in *gotsize or return -1 on fatal errors.
326
 */
327 328 329
__u8 get_contents_vfatname_block[MAX_CLUSTSIZE]
	__aligned(ARCH_DMA_MINALIGN);

330 331
static int get_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos,
			__u8 *buffer, loff_t maxsize, loff_t *gotsize)
332
{
333
	loff_t filesize = FAT2CPU32(dentptr->size);
334
	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
335
	__u32 curclust = START(dentptr);
W
wdenk 已提交
336
	__u32 endclust, newclust;
337
	loff_t actsize;
338

339 340
	*gotsize = 0;
	debug("Filesize: %llu bytes\n", filesize);
341

342
	if (pos >= filesize) {
343 344
		debug("Read position past EOF: %llu\n", pos);
		return 0;
345 346 347 348
	}

	if (maxsize > 0 && filesize > pos + maxsize)
		filesize = pos + maxsize;
349

350
	debug("%llu bytes\n", filesize);
W
Wolfgang Denk 已提交
351

352 353 354 355 356 357 358 359
	actsize = bytesperclust;

	/* go to cluster at pos */
	while (actsize <= pos) {
		curclust = get_fatent(mydata, curclust);
		if (CHECK_CLUST(curclust, mydata->fatsize)) {
			debug("curclust: 0x%x\n", curclust);
			debug("Invalid FAT entry\n");
360
			return 0;
361 362 363 364 365 366 367 368 369 370 371
		}
		actsize += bytesperclust;
	}

	/* actsize > pos */
	actsize -= bytesperclust;
	filesize -= actsize;
	pos -= actsize;

	/* align to beginning of next cluster if any */
	if (pos) {
372
		actsize = min(filesize, (loff_t)bytesperclust);
373 374 375 376 377 378 379 380
		if (get_cluster(mydata, curclust, get_contents_vfatname_block,
				(int)actsize) != 0) {
			printf("Error reading cluster\n");
			return -1;
		}
		filesize -= actsize;
		actsize -= pos;
		memcpy(buffer, get_contents_vfatname_block + pos, actsize);
381
		*gotsize += actsize;
382
		if (!filesize)
383
			return 0;
384 385 386 387 388 389
		buffer += actsize;

		curclust = get_fatent(mydata, curclust);
		if (CHECK_CLUST(curclust, mydata->fatsize)) {
			debug("curclust: 0x%x\n", curclust);
			debug("Invalid FAT entry\n");
390
			return 0;
391 392 393
		}
	}

W
Wolfgang Denk 已提交
394 395
	actsize = bytesperclust;
	endclust = curclust;
396 397

	do {
W
wdenk 已提交
398
		/* search for consecutive clusters */
W
Wolfgang Denk 已提交
399
		while (actsize < filesize) {
W
wdenk 已提交
400
			newclust = get_fatent(mydata, endclust);
W
Wolfgang Denk 已提交
401
			if ((newclust - 1) != endclust)
W
wdenk 已提交
402
				goto getit;
M
michael 已提交
403
			if (CHECK_CLUST(newclust, mydata->fatsize)) {
W
Wolfgang Denk 已提交
404 405
				debug("curclust: 0x%x\n", newclust);
				debug("Invalid FAT entry\n");
406
				return 0;
W
wdenk 已提交
407
			}
W
Wolfgang Denk 已提交
408 409
			endclust = newclust;
			actsize += bytesperclust;
W
wdenk 已提交
410
		}
W
Wolfgang Denk 已提交
411

W
wdenk 已提交
412
		/* get remaining bytes */
W
Wolfgang Denk 已提交
413
		actsize = filesize;
B
Benoît Thébaudeau 已提交
414
		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
W
Wolfgang Denk 已提交
415
			printf("Error reading cluster\n");
W
wdenk 已提交
416 417
			return -1;
		}
418 419
		*gotsize += actsize;
		return 0;
W
wdenk 已提交
420 421
getit:
		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
W
Wolfgang Denk 已提交
422
			printf("Error reading cluster\n");
W
wdenk 已提交
423 424
			return -1;
		}
425
		*gotsize += (int)actsize;
W
wdenk 已提交
426 427
		filesize -= actsize;
		buffer += actsize;
W
Wolfgang Denk 已提交
428

W
wdenk 已提交
429
		curclust = get_fatent(mydata, endclust);
M
michael 已提交
430
		if (CHECK_CLUST(curclust, mydata->fatsize)) {
W
Wolfgang Denk 已提交
431 432
			debug("curclust: 0x%x\n", curclust);
			printf("Invalid FAT entry\n");
433
			return 0;
434
		}
W
Wolfgang Denk 已提交
435 436
		actsize = bytesperclust;
		endclust = curclust;
437 438 439 440 441 442 443 444
	} while (1);
}

/*
 * Extract the file name information from 'slotptr' into 'l_name',
 * starting at l_name[*idx].
 * Return 1 if terminator (zero byte) is found, 0 otherwise.
 */
445
static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
446 447 448 449 450
{
	int j;

	for (j = 0; j <= 8; j += 2) {
		l_name[*idx] = slotptr->name0_4[j];
W
Wolfgang Denk 已提交
451 452
		if (l_name[*idx] == 0x00)
			return 1;
453 454 455 456
		(*idx)++;
	}
	for (j = 0; j <= 10; j += 2) {
		l_name[*idx] = slotptr->name5_10[j];
W
Wolfgang Denk 已提交
457 458
		if (l_name[*idx] == 0x00)
			return 1;
459 460 461 462
		(*idx)++;
	}
	for (j = 0; j <= 2; j += 2) {
		l_name[*idx] = slotptr->name11_12[j];
W
Wolfgang Denk 已提交
463 464
		if (l_name[*idx] == 0x00)
			return 1;
465 466 467 468 469 470 471 472 473 474 475 476 477
		(*idx)++;
	}

	return 0;
}

/*
 * Extract the full long filename starting at 'retdent' (which is really
 * a slot) into 'l_name'. If successful also copy the real directory entry
 * into 'retdent'
 * Return 0 on success, -1 otherwise.
 */
static int
478 479
get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
	     dir_entry *retdent, char *l_name)
480 481
{
	dir_entry *realdent;
W
Wolfgang Denk 已提交
482
	dir_slot *slotptr = (dir_slot *)retdent;
483 484 485
	__u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
							PREFETCH_BLOCKS :
							mydata->clust_size);
W
Wolfgang Denk 已提交
486
	__u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
487 488
	int idx = 0;

489 490 491 492 493 494
	if (counter > VFAT_MAXSEQ) {
		debug("Error: VFAT name is too long\n");
		return -1;
	}

	while ((__u8 *)slotptr < buflimit) {
W
Wolfgang Denk 已提交
495 496
		if (counter == 0)
			break;
W
wdenk 已提交
497 498
		if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
			return -1;
499 500 501 502
		slotptr++;
		counter--;
	}

503
	if ((__u8 *)slotptr >= buflimit) {
504 505
		dir_slot *slotptr2;

506 507
		if (curclust == 0)
			return -1;
508
		curclust = get_fatent(mydata, curclust);
M
michael 已提交
509
		if (CHECK_CLUST(curclust, mydata->fatsize)) {
W
Wolfgang Denk 已提交
510 511
			debug("curclust: 0x%x\n", curclust);
			printf("Invalid FAT entry\n");
512 513
			return -1;
		}
W
Wolfgang Denk 已提交
514

515
		if (get_cluster(mydata, curclust, get_contents_vfatname_block,
516
				mydata->clust_size * mydata->sect_size) != 0) {
W
Wolfgang Denk 已提交
517
			debug("Error: reading directory block\n");
518 519
			return -1;
		}
W
Wolfgang Denk 已提交
520

521
		slotptr2 = (dir_slot *)get_contents_vfatname_block;
522 523 524 525
		while (counter > 0) {
			if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
			    & 0xff) != counter)
				return -1;
526
			slotptr2++;
527 528
			counter--;
		}
W
Wolfgang Denk 已提交
529

530
		/* Save the real directory entry */
531
		realdent = (dir_entry *)slotptr2;
532
		while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
533
			slotptr2--;
534
			slot2str(slotptr2, l_name, &idx);
535 536 537
		}
	} else {
		/* Save the real directory entry */
W
Wolfgang Denk 已提交
538
		realdent = (dir_entry *)slotptr;
539 540 541 542
	}

	do {
		slotptr--;
W
Wolfgang Denk 已提交
543 544
		if (slot2str(slotptr, l_name, &idx))
			break;
W
wdenk 已提交
545
	} while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
546 547

	l_name[idx] = '\0';
W
Wolfgang Denk 已提交
548 549 550 551
	if (*l_name == DELETED_FLAG)
		*l_name = '\0';
	else if (*l_name == aRING)
		*l_name = DELETED_FLAG;
552 553 554 555 556 557 558 559 560
	downcase(l_name);

	/* Return the real directory entry */
	memcpy(retdent, realdent, sizeof(dir_entry));

	return 0;
}

/* Calculate short name checksum */
561
static __u8 mkcksum(const char name[8], const char ext[3])
562 563
{
	int i;
W
Wolfgang Denk 已提交
564

565 566
	__u8 ret = 0;

M
Marek Vasut 已提交
567
	for (i = 0; i < 8; i++)
568
		ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + name[i];
M
Marek Vasut 已提交
569
	for (i = 0; i < 3; i++)
570
		ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + ext[i];
571 572 573 574 575 576 577 578

	return ret;
}

/*
 * Get the directory entry associated with 'filename' from the directory
 * starting at 'startsect'
 */
579 580
__u8 get_dentfromdir_block[MAX_CLUSTSIZE]
	__aligned(ARCH_DMA_MINALIGN);
W
Wolfgang Denk 已提交
581

582 583 584
static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
				  char *filename, dir_entry *retdent,
				  int dols)
585
{
W
Wolfgang Denk 已提交
586 587 588
	__u16 prevcksum = 0xffff;
	__u32 curclust = START(retdent);
	int files = 0, dirs = 0;
589

W
Wolfgang Denk 已提交
590
	debug("get_dentfromdir: %s\n", filename);
591

W
Wolfgang Denk 已提交
592 593 594 595 596 597
	while (1) {
		dir_entry *dentptr;

		int i;

		if (get_cluster(mydata, curclust, get_dentfromdir_block,
598
				mydata->clust_size * mydata->sect_size) != 0) {
W
Wolfgang Denk 已提交
599 600 601 602 603 604 605
			debug("Error: reading directory block\n");
			return NULL;
		}

		dentptr = (dir_entry *)get_dentfromdir_block;

		for (i = 0; i < DIRENTSPERCLUST; i++) {
606
			char s_name[14], l_name[VFAT_MAXLEN_BYTES];
W
Wolfgang Denk 已提交
607 608 609 610 611 612 613

			l_name[0] = '\0';
			if (dentptr->name[0] == DELETED_FLAG) {
				dentptr++;
				continue;
			}
			if ((dentptr->attr & ATTR_VOLUME)) {
614 615
				if (vfat_enabled &&
				    (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
J
J. Vijayanand 已提交
616
				    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
W
Wolfgang Denk 已提交
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
					prevcksum = ((dir_slot *)dentptr)->alias_checksum;
					get_vfatname(mydata, curclust,
						     get_dentfromdir_block,
						     dentptr, l_name);
					if (dols) {
						int isdir;
						char dirc;
						int doit = 0;

						isdir = (dentptr->attr & ATTR_DIR);

						if (isdir) {
							dirs++;
							dirc = '/';
							doit = 1;
						} else {
							dirc = ' ';
							if (l_name[0] != 0) {
								files++;
								doit = 1;
							}
						}
						if (doit) {
							if (dirc == ' ') {
641 642
								printf(" %8u   %s%c\n",
								       FAT2CPU32(dentptr->size),
W
Wolfgang Denk 已提交
643 644 645 646 647 648 649 650 651 652 653 654
									l_name,
									dirc);
							} else {
								printf("            %s%c\n",
									l_name,
									dirc);
							}
						}
						dentptr++;
						continue;
					}
					debug("vfatname: |%s|\n", l_name);
655
				} else {
W
Wolfgang Denk 已提交
656 657 658 659
					/* Volume label or VFAT entry */
					dentptr++;
					continue;
				}
660
			}
W
Wolfgang Denk 已提交
661 662 663 664 665 666 667
			if (dentptr->name[0] == 0) {
				if (dols) {
					printf("\n%d file(s), %d dir(s)\n\n",
						files, dirs);
				}
				debug("Dentname == NULL - %d\n", i);
				return NULL;
668
			}
669 670 671 672 673 674 675
			if (vfat_enabled) {
				__u8 csum = mkcksum(dentptr->name, dentptr->ext);
				if (dols && csum == prevcksum) {
					prevcksum = 0xffff;
					dentptr++;
					continue;
				}
W
Wolfgang Denk 已提交
676
			}
677

W
Wolfgang Denk 已提交
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
			get_name(dentptr, s_name);
			if (dols) {
				int isdir = (dentptr->attr & ATTR_DIR);
				char dirc;
				int doit = 0;

				if (isdir) {
					dirs++;
					dirc = '/';
					doit = 1;
				} else {
					dirc = ' ';
					if (s_name[0] != 0) {
						files++;
						doit = 1;
					}
				}

				if (doit) {
					if (dirc == ' ') {
698 699
						printf(" %8u   %s%c\n",
						       FAT2CPU32(dentptr->size),
W
Wolfgang Denk 已提交
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
							s_name, dirc);
					} else {
						printf("            %s%c\n",
							s_name, dirc);
					}
				}

				dentptr++;
				continue;
			}

			if (strcmp(filename, s_name)
			    && strcmp(filename, l_name)) {
				debug("Mismatch: |%s|%s|\n", s_name, l_name);
				dentptr++;
				continue;
			}

			memcpy(retdent, dentptr, sizeof(dir_entry));

			debug("DentName: %s", s_name);
			debug(", start: 0x%x", START(dentptr));
			debug(", size:  0x%x %s\n",
			      FAT2CPU32(dentptr->size),
			      (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");

			return retdent;
727
		}
W
Wolfgang Denk 已提交
728 729 730 731 732 733

		curclust = get_fatent(mydata, curclust);
		if (CHECK_CLUST(curclust, mydata->fatsize)) {
			debug("curclust: 0x%x\n", curclust);
			printf("Invalid FAT entry\n");
			return NULL;
734 735 736
		}
	}

W
Wolfgang Denk 已提交
737
	return NULL;
738 739 740 741 742 743
}

/*
 * Read boot sector and volume info from a FAT filesystem
 */
static int
744
read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
745
{
746
	__u8 *block;
747
	volume_info *vistart;
748 749 750 751 752 753 754
	int ret = 0;

	if (cur_dev == NULL) {
		debug("Error: no device selected\n");
		return -1;
	}

755
	block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz);
756 757 758 759
	if (block == NULL) {
		debug("Error: allocating block\n");
		return -1;
	}
760

761
	if (disk_read(0, 1, block) < 0) {
W
Wolfgang Denk 已提交
762
		debug("Error: reading block\n");
763
		goto fail;
764 765 766
	}

	memcpy(bs, block, sizeof(boot_sector));
W
Wolfgang Denk 已提交
767 768 769 770 771
	bs->reserved = FAT2CPU16(bs->reserved);
	bs->fat_length = FAT2CPU16(bs->fat_length);
	bs->secs_track = FAT2CPU16(bs->secs_track);
	bs->heads = FAT2CPU16(bs->heads);
	bs->total_sect = FAT2CPU32(bs->total_sect);
772 773 774 775 776

	/* FAT32 entries */
	if (bs->fat_length == 0) {
		/* Assume FAT32 */
		bs->fat32_length = FAT2CPU32(bs->fat32_length);
W
Wolfgang Denk 已提交
777
		bs->flags = FAT2CPU16(bs->flags);
778
		bs->root_cluster = FAT2CPU32(bs->root_cluster);
W
Wolfgang Denk 已提交
779 780 781
		bs->info_sector = FAT2CPU16(bs->info_sector);
		bs->backup_boot = FAT2CPU16(bs->backup_boot);
		vistart = (volume_info *)(block + sizeof(boot_sector));
782 783
		*fatsize = 32;
	} else {
W
Wolfgang Denk 已提交
784
		vistart = (volume_info *)&(bs->fat32_length);
785 786 787 788 789
		*fatsize = 0;
	}
	memcpy(volinfo, vistart, sizeof(volume_info));

	if (*fatsize == 32) {
W
Wolfgang Denk 已提交
790
		if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
791
			goto exit;
792
	} else {
T
Tom Rix 已提交
793
		if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
794
			*fatsize = 12;
795
			goto exit;
796
		}
T
Tom Rix 已提交
797
		if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
798
			*fatsize = 16;
799
			goto exit;
800 801 802
		}
	}

W
Wolfgang Denk 已提交
803
	debug("Error: broken fs_type sign\n");
804 805 806 807 808
fail:
	ret = -1;
exit:
	free(block);
	return ret;
809 810
}

811
__u8 do_fat_read_at_block[MAX_CLUSTSIZE]
812
	__aligned(ARCH_DMA_MINALIGN);
W
Wolfgang Denk 已提交
813

814 815
int do_fat_read_at(const char *filename, loff_t pos, void *buffer,
		   loff_t maxsize, int dols, int dogetsize, loff_t *size)
816
{
W
Wolfgang Denk 已提交
817 818 819 820 821
	char fnamecopy[2048];
	boot_sector bs;
	volume_info volinfo;
	fsdata datablock;
	fsdata *mydata = &datablock;
822
	dir_entry *dentptr = NULL;
W
Wolfgang Denk 已提交
823 824
	__u16 prevcksum = 0xffff;
	char *subname = "";
E
Erik Hansen 已提交
825
	__u32 cursect;
W
Wolfgang Denk 已提交
826 827
	int idx, isdir = 0;
	int files = 0, dirs = 0;
828
	int ret = -1;
W
Wolfgang Denk 已提交
829
	int firsttime;
830
	__u32 root_cluster = 0;
831
	__u32 read_blk;
E
Erik Hansen 已提交
832
	int rootdir_size = 0;
833 834 835
	int buffer_blk_cnt;
	int do_read;
	__u8 *dir_ptr;
W
Wolfgang Denk 已提交
836 837 838 839 840 841

	if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
		debug("Error: reading boot sector\n");
		return -1;
	}

842 843
	if (mydata->fatsize == 32) {
		root_cluster = bs.root_cluster;
W
Wolfgang Denk 已提交
844
		mydata->fatlength = bs.fat32_length;
845
	} else {
W
Wolfgang Denk 已提交
846
		mydata->fatlength = bs.fat_length;
847
	}
W
Wolfgang Denk 已提交
848 849 850 851 852 853

	mydata->fat_sect = bs.reserved;

	cursect = mydata->rootdir_sect
		= mydata->fat_sect + mydata->fatlength * bs.fats;

854
	mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
W
Wolfgang Denk 已提交
855
	mydata->clust_size = bs.cluster_size;
K
Kyle Moffett 已提交
856 857 858 859 860
	if (mydata->sect_size != cur_part_info.blksz) {
		printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
				mydata->sect_size, cur_part_info.blksz);
		return -1;
	}
W
Wolfgang Denk 已提交
861 862 863 864 865 866 867 868

	if (mydata->fatsize == 32) {
		mydata->data_begin = mydata->rootdir_sect -
					(mydata->clust_size * 2);
	} else {
		rootdir_size = ((bs.dir_entries[1]  * (int)256 +
				 bs.dir_entries[0]) *
				 sizeof(dir_entry)) /
869
				 mydata->sect_size;
W
Wolfgang Denk 已提交
870 871 872 873 874 875
		mydata->data_begin = mydata->rootdir_sect +
					rootdir_size -
					(mydata->clust_size * 2);
	}

	mydata->fatbufnum = -1;
876
	mydata->fat_dirty = 0;
877
	mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
878 879 880 881
	if (mydata->fatbuf == NULL) {
		debug("Error: allocating memory\n");
		return -1;
	}
882

883 884 885
	if (vfat_enabled)
		debug("VFAT Support enabled\n");

W
Wolfgang Denk 已提交
886 887 888 889 890 891
	debug("FAT%d, fat_sect: %d, fatlength: %d\n",
	       mydata->fatsize, mydata->fat_sect, mydata->fatlength);
	debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
	       "Data begins at: %d\n",
	       root_cluster,
	       mydata->rootdir_sect,
892 893 894
	       mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
	debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
	      mydata->clust_size);
W
Wolfgang Denk 已提交
895 896 897 898 899 900 901 902 903

	/* "cwd" is always the root... */
	while (ISDIRDELIM(*filename))
		filename++;

	/* Make a copy of the filename and convert it to lowercase */
	strcpy(fnamecopy, filename);
	downcase(fnamecopy);

904
root_reparse:
W
Wolfgang Denk 已提交
905 906
	if (*fnamecopy == '\0') {
		if (!dols)
907
			goto exit;
908

W
Wolfgang Denk 已提交
909 910 911 912 913 914 915 916 917 918 919
		dols = LS_ROOT;
	} else if ((idx = dirdelim(fnamecopy)) >= 0) {
		isdir = 1;
		fnamecopy[idx] = '\0';
		subname = fnamecopy + idx + 1;

		/* Handle multiple delimiters */
		while (ISDIRDELIM(*subname))
			subname++;
	} else if (dols) {
		isdir = 1;
920 921
	}

922 923
	buffer_blk_cnt = 0;
	firsttime = 1;
W
Wolfgang Denk 已提交
924 925 926
	while (1) {
		int i;

927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
		if (mydata->fatsize == 32 || firsttime) {
			dir_ptr = do_fat_read_at_block;
			firsttime = 0;
		} else {
			/**
			 * FAT16 sector buffer modification:
			 * Each loop, the second buffered block is moved to
			 * the buffer begin, and two next sectors are read
			 * next to the previously moved one. So the sector
			 * buffer keeps always 3 sectors for fat16.
			 * And the current sector is the buffer second sector
			 * beside the "firsttime" read, when it is the first one.
			 *
			 * PREFETCH_BLOCKS is 2 for FAT16 == loop[0:1]
			 * n = computed root dir sector
			 * loop |  cursect-1  | cursect    | cursect+1  |
			 *   0  |  sector n+0 | sector n+1 | none       |
			 *   1  |  none       | sector n+0 | sector n+1 |
			 *   0  |  sector n+1 | sector n+2 | sector n+3 |
			 *   1  |  sector n+3 | ...
			*/
			dir_ptr = (do_fat_read_at_block + mydata->sect_size);
			memcpy(do_fat_read_at_block, dir_ptr, mydata->sect_size);
		}

		do_read = 1;

		if (mydata->fatsize == 32 && buffer_blk_cnt)
			do_read = 0;

		if (do_read) {
			read_blk = (mydata->fatsize == 32) ?
				    mydata->clust_size : PREFETCH_BLOCKS;

			debug("FAT read(sect=%d, cnt:%d), clust_size=%d, DIRENTSPERBLOCK=%zd\n",
				cursect, read_blk, mydata->clust_size, DIRENTSPERBLOCK);
W
Wolfgang Denk 已提交
963

964
			if (disk_read(cursect, read_blk, dir_ptr) < 0) {
965 966 967
				debug("Error: reading rootdir block\n");
				goto exit;
			}
W
Wolfgang Denk 已提交
968

969
			dentptr = (dir_entry *)dir_ptr;
970
		}
W
Wolfgang Denk 已提交
971 972

		for (i = 0; i < DIRENTSPERBLOCK; i++) {
973
			char s_name[14], l_name[VFAT_MAXLEN_BYTES];
974
			__u8 csum;
W
Wolfgang Denk 已提交
975 976

			l_name[0] = '\0';
977 978 979 980
			if (dentptr->name[0] == DELETED_FLAG) {
				dentptr++;
				continue;
			}
981

982 983 984
			if (vfat_enabled)
				csum = mkcksum(dentptr->name, dentptr->ext);

985
			if (dentptr->attr & ATTR_VOLUME) {
986 987
				if (vfat_enabled &&
				    (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
W
Wolfgang Denk 已提交
988 989 990 991
				    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
					prevcksum =
						((dir_slot *)dentptr)->alias_checksum;

992
					get_vfatname(mydata,
993
						     root_cluster,
994
						     dir_ptr,
W
Wolfgang Denk 已提交
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
						     dentptr, l_name);

					if (dols == LS_ROOT) {
						char dirc;
						int doit = 0;
						int isdir =
							(dentptr->attr & ATTR_DIR);

						if (isdir) {
							dirs++;
							dirc = '/';
							doit = 1;
						} else {
							dirc = ' ';
							if (l_name[0] != 0) {
								files++;
								doit = 1;
							}
						}
						if (doit) {
							if (dirc == ' ') {
1016 1017
								printf(" %8u   %s%c\n",
								       FAT2CPU32(dentptr->size),
W
Wolfgang Denk 已提交
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
									l_name,
									dirc);
							} else {
								printf("            %s%c\n",
									l_name,
									dirc);
							}
						}
						dentptr++;
						continue;
					}
					debug("Rootvfatname: |%s|\n",
					       l_name);
1031
				} else {
W
Wolfgang Denk 已提交
1032 1033 1034 1035 1036 1037 1038 1039 1040
					/* Volume label or VFAT entry */
					dentptr++;
					continue;
				}
			} else if (dentptr->name[0] == 0) {
				debug("RootDentname == NULL - %d\n", i);
				if (dols == LS_ROOT) {
					printf("\n%d file(s), %d dir(s)\n\n",
						files, dirs);
1041
					ret = 0;
W
Wolfgang Denk 已提交
1042
				}
1043
				goto exit;
1044
			}
1045 1046
			else if (vfat_enabled &&
				 dols == LS_ROOT && csum == prevcksum) {
1047
				prevcksum = 0xffff;
W
Wolfgang Denk 已提交
1048 1049
				dentptr++;
				continue;
1050
			}
1051

W
Wolfgang Denk 已提交
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
			get_name(dentptr, s_name);

			if (dols == LS_ROOT) {
				int isdir = (dentptr->attr & ATTR_DIR);
				char dirc;
				int doit = 0;

				if (isdir) {
					dirc = '/';
					if (s_name[0] != 0) {
						dirs++;
						doit = 1;
					}
				} else {
					dirc = ' ';
					if (s_name[0] != 0) {
						files++;
						doit = 1;
					}
				}
				if (doit) {
					if (dirc == ' ') {
1074 1075
						printf(" %8u   %s%c\n",
						       FAT2CPU32(dentptr->size),
W
Wolfgang Denk 已提交
1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
							s_name, dirc);
					} else {
						printf("            %s%c\n",
							s_name, dirc);
					}
				}
				dentptr++;
				continue;
			}

			if (strcmp(fnamecopy, s_name)
			    && strcmp(fnamecopy, l_name)) {
				debug("RootMismatch: |%s|%s|\n", s_name,
				       l_name);
				dentptr++;
				continue;
			}

			if (isdir && !(dentptr->attr & ATTR_DIR))
1095
				goto exit;
W
Wolfgang Denk 已提交
1096 1097 1098 1099 1100 1101 1102 1103

			debug("RootName: %s", s_name);
			debug(", start: 0x%x", START(dentptr));
			debug(", size:  0x%x %s\n",
			       FAT2CPU32(dentptr->size),
			       isdir ? "(DIR)" : "");

			goto rootdir_done;	/* We got a match */
1104
		}
1105
		debug("END LOOP: buffer_blk_cnt=%d   clust_size=%d\n", buffer_blk_cnt,
W
Wolfgang Denk 已提交
1106 1107 1108 1109 1110 1111 1112
		       mydata->clust_size);

		/*
		 * On FAT32 we must fetch the FAT entries for the next
		 * root directory clusters when a cluster has been
		 * completely processed.
		 */
1113
		++buffer_blk_cnt;
1114 1115
		int rootdir_end = 0;
		if (mydata->fatsize == 32) {
1116
			if (buffer_blk_cnt == mydata->clust_size) {
1117 1118
				int nxtsect = 0;
				int nxt_clust = 0;
W
Wolfgang Denk 已提交
1119

1120 1121
				nxt_clust = get_fatent(mydata, root_cluster);
				rootdir_end = CHECK_CLUST(nxt_clust, 32);
W
Wolfgang Denk 已提交
1122

1123 1124
				nxtsect = mydata->data_begin +
					(nxt_clust * mydata->clust_size);
W
Wolfgang Denk 已提交
1125

1126
				root_cluster = nxt_clust;
W
Wolfgang Denk 已提交
1127

1128
				cursect = nxtsect;
1129
				buffer_blk_cnt = 0;
1130
			}
1131
		} else {
1132 1133
			if (buffer_blk_cnt == PREFETCH_BLOCKS)
				buffer_blk_cnt = 0;
1134 1135 1136

			rootdir_end = (++cursect - mydata->rootdir_sect >=
				       rootdir_size);
1137
		}
E
Erik Hansen 已提交
1138 1139

		/* If end of rootdir reached */
1140
		if (rootdir_end) {
E
Erik Hansen 已提交
1141 1142 1143
			if (dols == LS_ROOT) {
				printf("\n%d file(s), %d dir(s)\n\n",
				       files, dirs);
1144
				*size = 0;
E
Erik Hansen 已提交
1145
			}
1146
			goto exit;
E
Erik Hansen 已提交
1147
		}
W
Wolfgang Denk 已提交
1148 1149
	}
rootdir_done:
1150

W
Wolfgang Denk 已提交
1151
	firsttime = 1;
1152

W
Wolfgang Denk 已提交
1153 1154 1155 1156 1157
	while (isdir) {
		int startsect = mydata->data_begin
			+ START(dentptr) * mydata->clust_size;
		dir_entry dent;
		char *nextname = NULL;
1158

W
Wolfgang Denk 已提交
1159 1160
		dent = *dentptr;
		dentptr = &dent;
1161

W
Wolfgang Denk 已提交
1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182
		idx = dirdelim(subname);

		if (idx >= 0) {
			subname[idx] = '\0';
			nextname = subname + idx + 1;
			/* Handle multiple delimiters */
			while (ISDIRDELIM(*nextname))
				nextname++;
			if (dols && *nextname == '\0')
				firsttime = 0;
		} else {
			if (dols && firsttime) {
				firsttime = 0;
			} else {
				isdir = 0;
			}
		}

		if (get_dentfromdir(mydata, startsect, subname, dentptr,
				     isdir ? 0 : dols) == NULL) {
			if (dols && !isdir)
1183
				*size = 0;
1184
			goto exit;
W
Wolfgang Denk 已提交
1185 1186
		}

1187 1188 1189
		if (isdir && !(dentptr->attr & ATTR_DIR))
			goto exit;

1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
		/*
		 * If we are looking for a directory, and found a directory
		 * type entry, and the entry is for the root directory (as
		 * denoted by a cluster number of 0), jump back to the start
		 * of the function, since at least on FAT12/16, the root dir
		 * lives in a hard-coded location and needs special handling
		 * to parse, rather than simply following the cluster linked
		 * list in the FAT, like other directories.
		 */
		if (isdir && (dentptr->attr & ATTR_DIR) && !START(dentptr)) {
			/*
			 * Modify the filename to remove the prefix that gets
			 * back to the root directory, so the initial root dir
			 * parsing code can continue from where we are without
			 * confusion.
			 */
			strcpy(fnamecopy, nextname ?: "");
			/*
			 * Set up state the same way as the function does when
			 * first started. This is required for the root dir
			 * parsing code operates in its expected environment.
			 */
			subname = "";
			cursect = mydata->rootdir_sect;
			isdir = 0;
			goto root_reparse;
		}

1218
		if (idx >= 0)
W
Wolfgang Denk 已提交
1219
			subname = nextname;
1220 1221
	}

1222 1223 1224 1225 1226 1227 1228
	if (dogetsize) {
		*size = FAT2CPU32(dentptr->size);
		ret = 0;
	} else {
		ret = get_contents(mydata, dentptr, pos, buffer, maxsize, size);
	}
	debug("Size: %u, got: %llu\n", FAT2CPU32(dentptr->size), *size);
1229

1230 1231
exit:
	free(mydata->fatbuf);
W
Wolfgang Denk 已提交
1232 1233
	return ret;
}
1234

1235 1236
int do_fat_read(const char *filename, void *buffer, loff_t maxsize, int dols,
		loff_t *actread)
1237
{
1238
	return do_fat_read_at(filename, 0, buffer, maxsize, dols, 0, actread);
1239 1240
}

1241
int file_fat_detectfs(void)
1242
{
W
Wolfgang Denk 已提交
1243 1244 1245 1246
	boot_sector bs;
	volume_info volinfo;
	int fatsize;
	char vol_label[12];
1247

W
Wolfgang Denk 已提交
1248
	if (cur_dev == NULL) {
W
wdenk 已提交
1249 1250 1251
		printf("No current device\n");
		return 1;
	}
W
Wolfgang Denk 已提交
1252

S
Simon Glass 已提交
1253
#if defined(CONFIG_IDE) || \
1254
    defined(CONFIG_SATA) || \
1255
    defined(CONFIG_SCSI) || \
1256
    defined(CONFIG_CMD_USB) || \
A
Andy Fleming 已提交
1257
    defined(CONFIG_MMC)
W
wdenk 已提交
1258
	printf("Interface:  ");
W
Wolfgang Denk 已提交
1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
	switch (cur_dev->if_type) {
	case IF_TYPE_IDE:
		printf("IDE");
		break;
	case IF_TYPE_SATA:
		printf("SATA");
		break;
	case IF_TYPE_SCSI:
		printf("SCSI");
		break;
	case IF_TYPE_ATAPI:
		printf("ATAPI");
		break;
	case IF_TYPE_USB:
		printf("USB");
		break;
	case IF_TYPE_DOC:
		printf("DOC");
		break;
	case IF_TYPE_MMC:
		printf("MMC");
		break;
	default:
		printf("Unknown");
W
wdenk 已提交
1283
	}
W
Wolfgang Denk 已提交
1284

1285
	printf("\n  Device %d: ", cur_dev->devnum);
W
wdenk 已提交
1286 1287
	dev_print(cur_dev);
#endif
W
Wolfgang Denk 已提交
1288 1289

	if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
W
wdenk 已提交
1290 1291 1292
		printf("\nNo valid FAT fs found\n");
		return 1;
	}
W
Wolfgang Denk 已提交
1293 1294

	memcpy(vol_label, volinfo.volume_label, 11);
W
wdenk 已提交
1295
	vol_label[11] = '\0';
W
Wolfgang Denk 已提交
1296 1297
	volinfo.fs_type[5] = '\0';

S
Stephen Warren 已提交
1298
	printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
W
Wolfgang Denk 已提交
1299

W
wdenk 已提交
1300
	return 0;
1301 1302
}

1303
int file_fat_ls(const char *dir)
1304
{
1305 1306 1307
	loff_t size;

	return do_fat_read(dir, NULL, 0, LS_YES, &size);
1308 1309
}

1310 1311
int fat_exists(const char *filename)
{
1312 1313 1314 1315 1316
	int ret;
	loff_t size;

	ret = do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, &size);
	return ret == 0;
1317 1318
}

1319
int fat_size(const char *filename, loff_t *size)
1320
{
1321
	return do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, size);
1322 1323
}

1324 1325
int file_fat_read_at(const char *filename, loff_t pos, void *buffer,
		     loff_t maxsize, loff_t *actread)
1326
{
W
Wolfgang Denk 已提交
1327
	printf("reading %s\n", filename);
1328 1329
	return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO, 0,
			      actread);
1330 1331
}

1332
int file_fat_read(const char *filename, void *buffer, int maxsize)
1333
{
1334 1335 1336 1337 1338 1339 1340 1341
	loff_t actread;
	int ret;

	ret =  file_fat_read_at(filename, 0, buffer, maxsize, &actread);
	if (ret)
		return ret;
	else
		return actread;
1342
}
1343

1344 1345
int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
		  loff_t *actread)
1346
{
1347
	int ret;
1348

1349 1350
	ret = file_fat_read_at(filename, offset, buf, len, actread);
	if (ret)
1351 1352
		printf("** Unable to read file %s **\n", filename);

1353
	return ret;
1354 1355 1356 1357 1358
}

void fat_close(void)
{
}