fs.c 13.4 KB
Newer Older
1 2 3
/*
 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
 *
T
Tom Rini 已提交
4
 * SPDX-License-Identifier:	GPL-2.0
5 6 7
 */

#include <config.h>
8
#include <errno.h>
9
#include <common.h>
10
#include <mapmem.h>
11 12 13 14
#include <part.h>
#include <ext4fs.h>
#include <fat.h>
#include <fs.h>
S
Simon Glass 已提交
15
#include <sandboxfs.h>
H
Hans de Goede 已提交
16
#include <ubifs_uboot.h>
S
Simon Glass 已提交
17
#include <asm/io.h>
18 19
#include <div64.h>
#include <linux/math64.h>
20

21 22
DECLARE_GLOBAL_DATA_PTR;

23
static struct blk_desc *fs_dev_desc;
R
Rob Clark 已提交
24
static int fs_dev_part;
25 26 27
static disk_partition_t fs_partition;
static int fs_type = FS_TYPE_ANY;

28
static inline int fs_probe_unsupported(struct blk_desc *fs_dev_desc,
29
				      disk_partition_t *fs_partition)
30 31 32 33 34
{
	printf("** Unrecognized filesystem type **\n");
	return -1;
}

35 36 37 38 39
static inline int fs_ls_unsupported(const char *dirname)
{
	return -1;
}

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
/* generic implementation of ls in terms of opendir/readdir/closedir */
__maybe_unused
static int fs_ls_generic(const char *dirname)
{
	struct fs_dir_stream *dirs;
	struct fs_dirent *dent;
	int nfiles = 0, ndirs = 0;

	dirs = fs_opendir(dirname);
	if (!dirs)
		return -errno;

	while ((dent = fs_readdir(dirs))) {
		if (dent->type == FS_DT_DIR) {
			printf("            %s/\n", dent->name);
			ndirs++;
		} else {
			printf(" %8lld   %s\n", dent->size, dent->name);
			nfiles++;
		}
	}

	fs_closedir(dirs);

	printf("\n%d file(s), %d dir(s)\n\n", nfiles, ndirs);

	return 0;
}

69 70 71 72 73
static inline int fs_exists_unsupported(const char *filename)
{
	return 0;
}

74
static inline int fs_size_unsupported(const char *filename, loff_t *size)
75 76 77 78
{
	return -1;
}

S
Simon Glass 已提交
79
static inline int fs_read_unsupported(const char *filename, void *buf,
80 81
				      loff_t offset, loff_t len,
				      loff_t *actread)
82 83 84 85
{
	return -1;
}

86
static inline int fs_write_unsupported(const char *filename, void *buf,
87 88
				      loff_t offset, loff_t len,
				      loff_t *actwrite)
89 90 91 92
{
	return -1;
}

93 94 95 96
static inline void fs_close_unsupported(void)
{
}

97 98 99 100 101
static inline int fs_uuid_unsupported(char *uuid_str)
{
	return -1;
}

R
Rob Clark 已提交
102 103 104 105 106 107
static inline int fs_opendir_unsupported(const char *filename,
					 struct fs_dir_stream **dirs)
{
	return -EACCES;
}

108
struct fstype_info {
109
	int fstype;
110
	char *name;
111 112 113 114 115 116 117 118 119
	/*
	 * Is it legal to pass NULL as .probe()'s  fs_dev_desc parameter? This
	 * should be false in most cases. For "virtual" filesystems which
	 * aren't based on a U-Boot block device (e.g. sandbox), this can be
	 * set to true. This should also be true for the dumm entry at the end
	 * of fstypes[], since that is essentially a "virtual" (non-existent)
	 * filesystem.
	 */
	bool null_dev_desc_ok;
120
	int (*probe)(struct blk_desc *fs_dev_desc,
121
		     disk_partition_t *fs_partition);
122
	int (*ls)(const char *dirname);
123
	int (*exists)(const char *filename);
124 125 126 127 128
	int (*size)(const char *filename, loff_t *size);
	int (*read)(const char *filename, void *buf, loff_t offset,
		    loff_t len, loff_t *actread);
	int (*write)(const char *filename, void *buf, loff_t offset,
		     loff_t len, loff_t *actwrite);
129
	void (*close)(void);
130
	int (*uuid)(char *uuid_str);
R
Rob Clark 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144
	/*
	 * Open a directory stream.  On success return 0 and directory
	 * stream pointer via 'dirsp'.  On error, return -errno.  See
	 * fs_opendir().
	 */
	int (*opendir)(const char *filename, struct fs_dir_stream **dirsp);
	/*
	 * Read next entry from directory stream.  On success return 0
	 * and directory entry pointer via 'dentp'.  On error return
	 * -errno.  See fs_readdir().
	 */
	int (*readdir)(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
	/* see fs_closedir() */
	void (*closedir)(struct fs_dir_stream *dirs);
145 146 147 148
};

static struct fstype_info fstypes[] = {
#ifdef CONFIG_FS_FAT
149 150
	{
		.fstype = FS_TYPE_FAT,
151
		.name = "fat",
152
		.null_dev_desc_ok = false,
153 154
		.probe = fat_set_blk_dev,
		.close = fat_close,
155
		.ls = fs_ls_generic,
156
		.exists = fat_exists,
157
		.size = fat_size,
158
		.read = fat_read_file,
159 160 161
#ifdef CONFIG_FAT_WRITE
		.write = file_fat_write,
#else
162
		.write = fs_write_unsupported,
163
#endif
164
		.uuid = fs_uuid_unsupported,
165 166 167
		.opendir = fat_opendir,
		.readdir = fat_readdir,
		.closedir = fat_closedir,
168
	},
169 170
#endif
#ifdef CONFIG_FS_EXT4
171 172
	{
		.fstype = FS_TYPE_EXT,
173
		.name = "ext4",
174
		.null_dev_desc_ok = false,
175 176
		.probe = ext4fs_probe,
		.close = ext4fs_close,
177
		.ls = ext4fs_ls,
178
		.exists = ext4fs_exists,
179
		.size = ext4fs_size,
180
		.read = ext4_read_file,
181 182 183
#ifdef CONFIG_CMD_EXT4_WRITE
		.write = ext4_write_file,
#else
184
		.write = fs_write_unsupported,
185
#endif
186
		.uuid = ext4fs_uuid,
R
Rob Clark 已提交
187
		.opendir = fs_opendir_unsupported,
188
	},
S
Simon Glass 已提交
189 190 191 192
#endif
#ifdef CONFIG_SANDBOX
	{
		.fstype = FS_TYPE_SANDBOX,
193
		.name = "sandbox",
194
		.null_dev_desc_ok = true,
S
Simon Glass 已提交
195 196 197
		.probe = sandbox_fs_set_blk_dev,
		.close = sandbox_fs_close,
		.ls = sandbox_fs_ls,
198
		.exists = sandbox_fs_exists,
199
		.size = sandbox_fs_size,
S
Simon Glass 已提交
200
		.read = fs_read_sandbox,
201
		.write = fs_write_sandbox,
202
		.uuid = fs_uuid_unsupported,
R
Rob Clark 已提交
203
		.opendir = fs_opendir_unsupported,
S
Simon Glass 已提交
204
	},
H
Hans de Goede 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218
#endif
#ifdef CONFIG_CMD_UBIFS
	{
		.fstype = FS_TYPE_UBIFS,
		.name = "ubifs",
		.null_dev_desc_ok = true,
		.probe = ubifs_set_blk_dev,
		.close = ubifs_close,
		.ls = ubifs_ls,
		.exists = ubifs_exists,
		.size = ubifs_size,
		.read = ubifs_read,
		.write = fs_write_unsupported,
		.uuid = fs_uuid_unsupported,
R
Rob Clark 已提交
219
		.opendir = fs_opendir_unsupported,
H
Hans de Goede 已提交
220
	},
221 222 223
#endif
	{
		.fstype = FS_TYPE_ANY,
224
		.name = "unsupported",
225
		.null_dev_desc_ok = true,
226 227 228
		.probe = fs_probe_unsupported,
		.close = fs_close_unsupported,
		.ls = fs_ls_unsupported,
229
		.exists = fs_exists_unsupported,
230
		.size = fs_size_unsupported,
231
		.read = fs_read_unsupported,
232
		.write = fs_write_unsupported,
233
		.uuid = fs_uuid_unsupported,
R
Rob Clark 已提交
234
		.opendir = fs_opendir_unsupported,
235 236 237
	},
};

238 239 240 241 242 243 244 245 246 247 248 249 250 251
static struct fstype_info *fs_get_info(int fstype)
{
	struct fstype_info *info;
	int i;

	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes) - 1; i++, info++) {
		if (fstype == info->fstype)
			return info;
	}

	/* Return the 'unsupported' sentinel */
	return info;
}

252 253
int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
{
254
	struct fstype_info *info;
255
	int part, i;
256 257 258 259
#ifdef CONFIG_NEEDS_MANUAL_RELOC
	static int relocated;

	if (!relocated) {
260 261
		for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes);
				i++, info++) {
262
			info->name += gd->reloc_off;
263 264 265 266
			info->probe += gd->reloc_off;
			info->close += gd->reloc_off;
			info->ls += gd->reloc_off;
			info->read += gd->reloc_off;
267
			info->write += gd->reloc_off;
268
		}
269 270 271
		relocated = 1;
	}
#endif
272

273
	part = blk_get_device_part_str(ifname, dev_part_str, &fs_dev_desc,
274 275 276 277
					&fs_partition, 1);
	if (part < 0)
		return -1;

278 279 280
	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
		if (fstype != FS_TYPE_ANY && info->fstype != FS_TYPE_ANY &&
				fstype != info->fstype)
281 282
			continue;

283 284 285
		if (!fs_dev_desc && !info->null_dev_desc_ok)
			continue;

R
Rob Clark 已提交
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
		if (!info->probe(fs_dev_desc, &fs_partition)) {
			fs_type = info->fstype;
			fs_dev_part = part;
			return 0;
		}
	}

	return -1;
}

/* set current blk device w/ blk_desc + partition # */
int fs_set_blk_dev_with_part(struct blk_desc *desc, int part)
{
	struct fstype_info *info;
	int ret, i;

	if (part >= 1)
		ret = part_get_info(desc, part, &fs_partition);
	else
		ret = part_get_info_whole_disk(desc, &fs_partition);
	if (ret)
		return ret;
	fs_dev_desc = desc;

	for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
311
		if (!info->probe(fs_dev_desc, &fs_partition)) {
312
			fs_type = info->fstype;
313 314 315 316 317 318 319 320 321
			return 0;
		}
	}

	return -1;
}

static void fs_close(void)
{
322
	struct fstype_info *info = fs_get_info(fs_type);
323

324
	info->close();
325

326 327 328
	fs_type = FS_TYPE_ANY;
}

329 330 331 332 333 334 335
int fs_uuid(char *uuid_str)
{
	struct fstype_info *info = fs_get_info(fs_type);

	return info->uuid(uuid_str);
}

336 337 338 339
int fs_ls(const char *dirname)
{
	int ret;

340 341 342
	struct fstype_info *info = fs_get_info(fs_type);

	ret = info->ls(dirname);
343

344
	fs_type = FS_TYPE_ANY;
345 346 347 348 349
	fs_close();

	return ret;
}

350 351 352 353 354 355 356 357 358 359 360 361 362
int fs_exists(const char *filename)
{
	int ret;

	struct fstype_info *info = fs_get_info(fs_type);

	ret = info->exists(filename);

	fs_close();

	return ret;
}

363
int fs_size(const char *filename, loff_t *size)
364 365 366 367 368
{
	int ret;

	struct fstype_info *info = fs_get_info(fs_type);

369
	ret = info->size(filename, size);
370 371 372 373 374 375

	fs_close();

	return ret;
}

376 377
int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
	    loff_t *actread)
378
{
379
	struct fstype_info *info = fs_get_info(fs_type);
S
Simon Glass 已提交
380
	void *buf;
381 382
	int ret;

S
Simon Glass 已提交
383 384 385 386 387
	/*
	 * We don't actually know how many bytes are being read, since len==0
	 * means read the whole file.
	 */
	buf = map_sysmem(addr, len);
388
	ret = info->read(filename, buf, offset, len, actread);
S
Simon Glass 已提交
389
	unmap_sysmem(buf);
390

391
	/* If we requested a specific number of bytes, check we got it */
392 393
	if (ret == 0 && len && *actread != len)
		printf("** %s shorter than offset + len **\n", filename);
394 395 396 397 398
	fs_close();

	return ret;
}

399 400
int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
	     loff_t *actwrite)
401 402 403 404 405 406
{
	struct fstype_info *info = fs_get_info(fs_type);
	void *buf;
	int ret;

	buf = map_sysmem(addr, len);
407
	ret = info->write(filename, buf, offset, len, actwrite);
408 409
	unmap_sysmem(buf);

410
	if (ret < 0 && len != *actwrite) {
411 412 413 414 415 416 417 418
		printf("** Unable to write file %s **\n", filename);
		ret = -1;
	}
	fs_close();

	return ret;
}

R
Rob Clark 已提交
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
struct fs_dir_stream *fs_opendir(const char *filename)
{
	struct fstype_info *info = fs_get_info(fs_type);
	struct fs_dir_stream *dirs = NULL;
	int ret;

	ret = info->opendir(filename, &dirs);
	fs_close();
	if (ret) {
		errno = -ret;
		return NULL;
	}

	dirs->desc = fs_dev_desc;
	dirs->part = fs_dev_part;

	return dirs;
}

struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs)
{
	struct fstype_info *info;
	struct fs_dirent *dirent;
	int ret;

	fs_set_blk_dev_with_part(dirs->desc, dirs->part);
	info = fs_get_info(fs_type);

	ret = info->readdir(dirs, &dirent);
	fs_close();
	if (ret) {
		errno = -ret;
		return NULL;
	}

	return dirent;
}

void fs_closedir(struct fs_dir_stream *dirs)
{
	struct fstype_info *info;

	if (!dirs)
		return;

	fs_set_blk_dev_with_part(dirs->desc, dirs->part);
	info = fs_get_info(fs_type);

	info->closedir(dirs);
	fs_close();
}


472 473 474
int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
		int fstype)
{
475
	loff_t size;
476 477 478 479 480 481 482

	if (argc != 4)
		return CMD_RET_USAGE;

	if (fs_set_blk_dev(argv[1], argv[2], fstype))
		return 1;

483
	if (fs_size(argv[3], &size) < 0)
484 485
		return CMD_RET_FAILURE;

486
	env_set_hex("filesize", size);
487 488 489 490

	return 0;
}

S
Stephen Warren 已提交
491
int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
492
		int fstype)
493 494 495 496
{
	unsigned long addr;
	const char *addr_str;
	const char *filename;
497 498 499 500
	loff_t bytes;
	loff_t pos;
	loff_t len_read;
	int ret;
501
	unsigned long time;
502
	char *ep;
503

504 505 506
	if (argc < 2)
		return CMD_RET_USAGE;
	if (argc > 7)
507 508
		return CMD_RET_USAGE;

509
	if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
510 511 512
		return 1;

	if (argc >= 4) {
513 514 515
		addr = simple_strtoul(argv[3], &ep, 16);
		if (ep == argv[3] || *ep != '\0')
			return CMD_RET_USAGE;
516
	} else {
517
		addr_str = env_get("loadaddr");
518 519 520 521 522 523 524 525
		if (addr_str != NULL)
			addr = simple_strtoul(addr_str, NULL, 16);
		else
			addr = CONFIG_SYS_LOAD_ADDR;
	}
	if (argc >= 5) {
		filename = argv[4];
	} else {
526
		filename = env_get("bootfile");
527 528 529 530 531 532
		if (!filename) {
			puts("** No boot file defined **\n");
			return 1;
		}
	}
	if (argc >= 6)
533
		bytes = simple_strtoul(argv[5], NULL, 16);
534 535 536
	else
		bytes = 0;
	if (argc >= 7)
537
		pos = simple_strtoul(argv[6], NULL, 16);
538 539 540
	else
		pos = 0;

541
	time = get_timer(0);
542
	ret = fs_read(filename, addr, pos, bytes, &len_read);
543
	time = get_timer(time);
544
	if (ret < 0)
545 546
		return 1;

547
	printf("%llu bytes read in %lu ms", len_read, time);
548 549
	if (time > 0) {
		puts(" (");
550
		print_size(div_u64(len_read, time) * 1000, "/s");
551 552 553
		puts(")");
	}
	puts("\n");
554

555 556
	env_set_hex("fileaddr", addr);
	env_set_hex("filesize", len_read);
557 558 559 560 561 562 563 564 565

	return 0;
}

int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
	int fstype)
{
	if (argc < 2)
		return CMD_RET_USAGE;
566 567
	if (argc > 4)
		return CMD_RET_USAGE;
568 569 570 571

	if (fs_set_blk_dev(argv[1], (argc >= 3) ? argv[2] : NULL, fstype))
		return 1;

572
	if (fs_ls(argc >= 4 ? argv[3] : "/"))
573 574 575 576
		return 1;

	return 0;
}
577

578 579 580 581 582 583 584 585 586
int file_exists(const char *dev_type, const char *dev_part, const char *file,
		int fstype)
{
	if (fs_set_blk_dev(dev_type, dev_part, fstype))
		return 0;

	return fs_exists(file);
}

587
int do_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
588
		int fstype)
589 590 591
{
	unsigned long addr;
	const char *filename;
592 593 594 595
	loff_t bytes;
	loff_t pos;
	loff_t len;
	int ret;
596 597 598 599 600 601 602 603
	unsigned long time;

	if (argc < 6 || argc > 7)
		return CMD_RET_USAGE;

	if (fs_set_blk_dev(argv[1], argv[2], fstype))
		return 1;

604 605
	addr = simple_strtoul(argv[3], NULL, 16);
	filename = argv[4];
606
	bytes = simple_strtoul(argv[5], NULL, 16);
607
	if (argc >= 7)
608
		pos = simple_strtoul(argv[6], NULL, 16);
609 610 611 612
	else
		pos = 0;

	time = get_timer(0);
613
	ret = fs_write(filename, addr, pos, bytes, &len);
614
	time = get_timer(time);
615
	if (ret < 0)
616 617
		return 1;

618
	printf("%llu bytes written in %lu ms", len, time);
619 620
	if (time > 0) {
		puts(" (");
621
		print_size(div_u64(len, time) * 1000, "/s");
622 623 624 625 626 627
		puts(")");
	}
	puts("\n");

	return 0;
}
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646

int do_fs_uuid(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
		int fstype)
{
	int ret;
	char uuid[37];
	memset(uuid, 0, sizeof(uuid));

	if (argc < 3 || argc > 4)
		return CMD_RET_USAGE;

	if (fs_set_blk_dev(argv[1], argv[2], fstype))
		return 1;

	ret = fs_uuid(uuid);
	if (ret)
		return CMD_RET_FAILURE;

	if (argc == 4)
S
Simon Glass 已提交
647
		env_set(argv[3], uuid);
648 649 650 651 652
	else
		printf("%s\n", uuid);

	return CMD_RET_SUCCESS;
}
653 654 655 656 657 658 659 660 661 662 663 664 665 666

int do_fs_type(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	struct fstype_info *info;

	if (argc < 3 || argc > 4)
		return CMD_RET_USAGE;

	if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY))
		return 1;

	info = fs_get_info(fs_type);

	if (argc == 4)
S
Simon Glass 已提交
667
		env_set(argv[3], info->name);
668 669 670 671 672 673
	else
		printf("%s\n", info->name);

	return CMD_RET_SUCCESS;
}