fs: implement size/fatsize/ext4size

These commands may be used to determine the size of a file without
actually reading the whole file content into memory. This may be used
to determine if the file will fit into the memory buffer that will
contain it. In particular, the DFU code will use it for this purpose
in the next commit.

Signed-off-by: Stephen Warren <swarren@nvidia.com>
diff --git a/common/cmd_ext4.c b/common/cmd_ext4.c
index 68b047b..6d75dd2 100644
--- a/common/cmd_ext4.c
+++ b/common/cmd_ext4.c
@@ -42,6 +42,12 @@
 #include <usb.h>
 #endif
 
+int do_ext4_size(cmd_tbl_t *cmdtp, int flag, int argc,
+						char *const argv[])
+{
+	return do_size(cmdtp, flag, argc, argv, FS_TYPE_EXT);
+}
+
 int do_ext4_load(cmd_tbl_t *cmdtp, int flag, int argc,
 						char *const argv[])
 {
@@ -113,6 +119,14 @@
 
 #endif
 
+U_BOOT_CMD(
+	ext4size,	4,	0,	do_ext4_size,
+	"determine a file's size",
+	"<interface> <dev[:part]> <filename>\n"
+	"    - Find file 'filename' from 'dev' on 'interface'\n"
+	"      and determine its size."
+);
+
 U_BOOT_CMD(ext4ls, 4, 1, do_ext4_ls,
 	   "list files in a directory (default /)",
 	   "<interface> <dev[:part]> [directory]\n"
diff --git a/common/cmd_fat.c b/common/cmd_fat.c
index a478017..633fbf1 100644
--- a/common/cmd_fat.c
+++ b/common/cmd_fat.c
@@ -18,6 +18,19 @@
 #include <fat.h>
 #include <fs.h>
 
+int do_fat_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	return do_size(cmdtp, flag, argc, argv, FS_TYPE_FAT);
+}
+
+U_BOOT_CMD(
+	fatsize,	4,	0,	do_fat_size,
+	"determine a file's size",
+	"<interface> <dev[:part]> <filename>\n"
+	"    - Find file 'filename' from 'dev' on 'interface'\n"
+	"      and determine its size."
+);
+
 int do_fat_fsload (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
 	return do_load(cmdtp, flag, argc, argv, FS_TYPE_FAT);
diff --git a/common/cmd_fs.c b/common/cmd_fs.c
index 78590d2..6754340 100644
--- a/common/cmd_fs.c
+++ b/common/cmd_fs.c
@@ -20,6 +20,19 @@
 #include <command.h>
 #include <fs.h>
 
+static int do_size_wrapper(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	return do_size(cmdtp, flag, argc, argv, FS_TYPE_ANY);
+}
+
+U_BOOT_CMD(
+	size,	4,	0,	do_size_wrapper,
+	"determine a file's size",
+	"<interface> <dev[:part]> <filename>\n"
+	"    - Find file 'filename' from 'dev' on 'interface'\n"
+	"      and determine its size."
+);
+
 static int do_load_wrapper(cmd_tbl_t *cmdtp, int flag, int argc,
 				char * const argv[])
 {
diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c
index 417ce7b..cbdc220 100644
--- a/fs/ext4/ext4fs.c
+++ b/fs/ext4/ext4fs.c
@@ -182,6 +182,11 @@
 	return file_len >= 0;
 }
 
+int ext4fs_size(const char *filename)
+{
+	return ext4fs_open(filename);
+}
+
 int ext4fs_read(char *buf, unsigned len)
 {
 	if (ext4fs_root == NULL || ext4fs_file == NULL)
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index 54f42ea..561921f 100644
--- a/fs/fat/fat.c
+++ b/fs/fat/fat.c
@@ -1243,6 +1243,11 @@
 	return sz >= 0;
 }
 
+int fat_size(const char *filename)
+{
+	return do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1);
+}
+
 long file_fat_read_at(const char *filename, unsigned long pos, void *buffer,
 		      unsigned long maxsize)
 {
diff --git a/fs/fs.c b/fs/fs.c
index ea15c5f..dd680f3 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -46,6 +46,11 @@
 	return 0;
 }
 
+static inline int fs_size_unsupported(const char *filename)
+{
+	return -1;
+}
+
 static inline int fs_read_unsupported(const char *filename, void *buf,
 				      int offset, int len)
 {
@@ -77,6 +82,7 @@
 		     disk_partition_t *fs_partition);
 	int (*ls)(const char *dirname);
 	int (*exists)(const char *filename);
+	int (*size)(const char *filename);
 	int (*read)(const char *filename, void *buf, int offset, int len);
 	int (*write)(const char *filename, void *buf, int offset, int len);
 	void (*close)(void);
@@ -91,6 +97,7 @@
 		.close = fat_close,
 		.ls = file_fat_ls,
 		.exists = fat_exists,
+		.size = fat_size,
 		.read = fat_read_file,
 		.write = fs_write_unsupported,
 	},
@@ -103,6 +110,7 @@
 		.close = ext4fs_close,
 		.ls = ext4fs_ls,
 		.exists = ext4fs_exists,
+		.size = ext4fs_size,
 		.read = ext4_read_file,
 		.write = fs_write_unsupported,
 	},
@@ -115,6 +123,7 @@
 		.close = sandbox_fs_close,
 		.ls = sandbox_fs_ls,
 		.exists = sandbox_fs_exists,
+		.size = sandbox_fs_size,
 		.read = fs_read_sandbox,
 		.write = fs_write_sandbox,
 	},
@@ -126,6 +135,7 @@
 		.close = fs_close_unsupported,
 		.ls = fs_ls_unsupported,
 		.exists = fs_exists_unsupported,
+		.size = fs_size_unsupported,
 		.read = fs_read_unsupported,
 		.write = fs_write_unsupported,
 	},
@@ -223,6 +233,19 @@
 	return ret;
 }
 
+int fs_size(const char *filename)
+{
+	int ret;
+
+	struct fstype_info *info = fs_get_info(fs_type);
+
+	ret = info->size(filename);
+
+	fs_close();
+
+	return ret;
+}
+
 int fs_read(const char *filename, ulong addr, int offset, int len)
 {
 	struct fstype_info *info = fs_get_info(fs_type);
@@ -266,6 +289,26 @@
 	return ret;
 }
 
+int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
+		int fstype)
+{
+	int size;
+
+	if (argc != 4)
+		return CMD_RET_USAGE;
+
+	if (fs_set_blk_dev(argv[1], argv[2], fstype))
+		return 1;
+
+	size = fs_size(argv[3]);
+	if (size < 0)
+		return CMD_RET_FAILURE;
+
+	setenv_hex("filesize", size);
+
+	return 0;
+}
+
 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
 		int fstype)
 {
diff --git a/fs/sandbox/sandboxfs.c b/fs/sandbox/sandboxfs.c
index 8507978..ba6402c 100644
--- a/fs/sandbox/sandboxfs.c
+++ b/fs/sandbox/sandboxfs.c
@@ -80,6 +80,11 @@
 	return sz >= 0;
 }
 
+int sandbox_fs_size(const char *filename)
+{
+	return os_get_filesize(filename);
+}
+
 void sandbox_fs_close(void)
 {
 }
diff --git a/include/ext4fs.h b/include/ext4fs.h
index fbbb002..6c419f3 100644
--- a/include/ext4fs.h
+++ b/include/ext4fs.h
@@ -136,6 +136,7 @@
 void ext4fs_reinit_global(void);
 int ext4fs_ls(const char *dirname);
 int ext4fs_exists(const char *filename);
+int ext4fs_size(const char *filename);
 void ext4fs_free_node(struct ext2fs_node *node, struct ext2fs_node *currroot);
 int ext4fs_devread(lbaint_t sector, int byte_offset, int byte_len, char *buf);
 void ext4fs_set_blk_dev(block_dev_desc_t *rbdd, disk_partition_t *info);
diff --git a/include/fat.h b/include/fat.h
index 63cf787..20ca3f3 100644
--- a/include/fat.h
+++ b/include/fat.h
@@ -198,6 +198,7 @@
 int file_fat_detectfs(void);
 int file_fat_ls(const char *dir);
 int fat_exists(const char *filename);
+int fat_size(const char *filename);
 long file_fat_read_at(const char *filename, unsigned long pos, void *buffer,
 		      unsigned long maxsize);
 long file_fat_read(const char *filename, void *buffer, unsigned long maxsize);
diff --git a/include/fs.h b/include/fs.h
index 26de053..06a45f2 100644
--- a/include/fs.h
+++ b/include/fs.h
@@ -51,6 +51,13 @@
 int fs_exists(const char *filename);
 
 /*
+ * Determine a file's size
+ *
+ * Returns the file's size in bytes, or a negative value if it doesn't exist.
+ */
+int fs_size(const char *filename);
+
+/*
  * Read file "filename" from the partition previously set by fs_set_blk_dev(),
  * to address "addr", starting at byte offset "offset", and reading "len"
  * bytes. "offset" may be 0 to read from the start of the file. "len" may be
@@ -75,6 +82,8 @@
  * Common implementation for various filesystem commands, optionally limited
  * to a specific filesystem type via the fstype parameter.
  */
+int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
+		int fstype);
 int do_load(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
 		int fstype);
 int do_ls(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
diff --git a/include/sandboxfs.h b/include/sandboxfs.h
index a51ad13..e7c3262 100644
--- a/include/sandboxfs.h
+++ b/include/sandboxfs.h
@@ -26,6 +26,7 @@
 void sandbox_fs_close(void);
 int sandbox_fs_ls(const char *dirname);
 int sandbox_fs_exists(const char *filename);
+int sandbox_fs_size(const char *filename);
 int fs_read_sandbox(const char *filename, void *buf, int offset, int len);
 int fs_write_sandbox(const char *filename, void *buf, int offset, int len);