diff mbox series

[RFC] tools: fitmount: fuse mount fit images

Message ID 20200615174509.12557-1-selva.muthukumar@vvdntech.com
State New
Headers show
Series [RFC] tools: fitmount: fuse mount fit images | expand

Commit Message

selvamuthukumar v June 15, 2020, 5:45 p.m. UTC
Allow mounting of FIT images. If FIT images are used for firmware upgrade
from linux, mouting can save space in comparison to using dumpimage.

Signed-off-by: Selva Muthukumar <selva.muthukumar at vvdntech.com>
---
 tools/Makefile   |   5 +-
 tools/fitmount.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 258 insertions(+), 1 deletion(-)
 create mode 100644 tools/fitmount.c

Comments

selvamuthukumar v June 23, 2020, 12:31 p.m. UTC | #1
On Mon, Jun 15, 2020 at 11:15 PM Selva Muthukumar
<v.selvamuthukumar at gmail.com> wrote:
>
> Allow mounting of FIT images. If FIT images are used for firmware upgrade
> from linux, mouting can save space in comparison to using dumpimage.
>

Any comments on this? Is there any other way to get FIT image
contents, without extracting it?

Thanks,
Selva
Tom Rini June 23, 2020, 6:16 p.m. UTC | #2
On Tue, Jun 23, 2020 at 06:01:38PM +0530, selvamuthukumar v wrote:
> On Mon, Jun 15, 2020 at 11:15 PM Selva Muthukumar
> <v.selvamuthukumar at gmail.com> wrote:
> >
> > Allow mounting of FIT images. If FIT images are used for firmware upgrade
> > from linux, mouting can save space in comparison to using dumpimage.
> >
> 
> Any comments on this? Is there any other way to get FIT image
> contents, without extracting it?

Sorry for the delay.  For your program, there's a few things such as
missing license header and being able to opt-in as otherwise we add a
new build dependency for everyone.

For getting FIT image contents, the existing dumpimage tool handles
this.

What is missing and would be greatly appreciated is updating some
documentation to include what's described in:
commit 39931f966adaeadd66dc7a905f7dddb93f66bac3
Author: Guilherme Maciel Ferreira <guilherme.maciel.ferreira at gmail.com>
Date:   Thu Jan 15 02:54:42 2015 -0200

    dumpimage: fit: extract FIT images

to be much more visible.  What's documented there works today but I had
to double check how myself as it's not as visible as it should be.
Thanks!
selvamuthukumar v June 24, 2020, 2:50 a.m. UTC | #3
On Tue, Jun 23, 2020 at 11:47 PM Tom Rini <trini at konsulko.com> wrote:
>
> On Tue, Jun 23, 2020 at 06:01:38PM +0530, selvamuthukumar v wrote:
> > On Mon, Jun 15, 2020 at 11:15 PM Selva Muthukumar
> > <v.selvamuthukumar at gmail.com> wrote:
> > >
> > > Allow mounting of FIT images. If FIT images are used for firmware upgrade
> > > from linux, mouting can save space in comparison to using dumpimage.
> > >
> >
> > Any comments on this? Is there any other way to get FIT image
> > contents, without extracting it?
>
> Sorry for the delay.  For your program, there's a few things such as
> missing license header and being able to opt-in as otherwise we add a
> new build dependency for everyone.
>
> For getting FIT image contents, the existing dumpimage tool handles
> this.
>
dumpimage extracts image contents into new files. Current firmware
upgrade process in our product is:

1. FIT image has kernel and rootfs. Get new FIT image in tmpfs.
2. dumpimage to extract the FIT image in tmpfs.
3. Write kernel and rootfs in partitions.

many times step2 fails because there is not enough space in tmpfs. FIT
image and individual components need to be present in tmpfs. When we
mount the FIT image, it does not take extra space tmpfs.

Thanks.
Selva
Rasmus Villemoes June 24, 2020, 11:55 a.m. UTC | #4
On 24/06/2020 04.50, selvamuthukumar v wrote:
> On Tue, Jun 23, 2020 at 11:47 PM Tom Rini <trini at konsulko.com> wrote:
>>

>> For getting FIT image contents, the existing dumpimage tool handles
>> this.
>>
> dumpimage extracts image contents into new files. Current firmware
> upgrade process in our product is:
> 
> 1. FIT image has kernel and rootfs. Get new FIT image in tmpfs.
> 2. dumpimage to extract the FIT image in tmpfs.
> 3. Write kernel and rootfs in partitions.
>
> many times step2 fails because there is not enough space in tmpfs. FIT
> image and individual components need to be present in tmpfs. When we
> mount the FIT image, it does not take extra space tmpfs.

But, why do you use a FIT to bundle the kernel and rootfs? Wouldn't a
squashfs image be a better container format? Depending on your rootfs
image type, that might even cost even less in tmpfs (and transfer to
target) than a FIT.

I don't have anything against your patch, except as Tom pointed out it
really needs to be opt-in so not everybody needs to have libfuse present
to build the host tools. Just curious why you've chosen FIT as the
container format.

Rasmus
Selva Muthukumar June 24, 2020, 12:47 p.m. UTC | #5
On Wed, Jun 24, 2020 at 5:25 PM Rasmus Villemoes
<rasmus.villemoes at prevas.dk> wrote:
>
> On 24/06/2020 04.50, selvamuthukumar v wrote:
> > On Tue, Jun 23, 2020 at 11:47 PM Tom Rini <trini at konsulko.com> wrote:
> >>
>
> >> For getting FIT image contents, the existing dumpimage tool handles
> >> this.
> >>
> > dumpimage extracts image contents into new files. Current firmware
> > upgrade process in our product is:
> >
> > 1. FIT image has kernel and rootfs. Get new FIT image in tmpfs.
> > 2. dumpimage to extract the FIT image in tmpfs.
> > 3. Write kernel and rootfs in partitions.
> >
> > many times step2 fails because there is not enough space in tmpfs. FIT
> > image and individual components need to be present in tmpfs. When we
> > mount the FIT image, it does not take extra space tmpfs.
>
> But, why do you use a FIT to bundle the kernel and rootfs? Wouldn't a
> squashfs image be a better container format? Depending on your rootfs
> image type, that might even cost even less in tmpfs (and transfer to
> target) than a FIT.
>

Platform is based on OpenWRT which already has the FIT image support.
Other than kernel and rootfs FIT image has some more contents, like
script to upgrade the image from u-boot

> I don't have anything against your patch, except as Tom pointed out it
> really needs to be opt-in so not everybody needs to have libfuse present
> to build the host tools. Just curious why you've chosen FIT as the
> container format.
>
I can make this tool as a configurable option.
Tom Rini June 24, 2020, 6:17 p.m. UTC | #6
On Wed, Jun 24, 2020 at 08:20:38AM +0530, selvamuthukumar v wrote:
> On Tue, Jun 23, 2020 at 11:47 PM Tom Rini <trini at konsulko.com> wrote:
> >
> > On Tue, Jun 23, 2020 at 06:01:38PM +0530, selvamuthukumar v wrote:
> > > On Mon, Jun 15, 2020 at 11:15 PM Selva Muthukumar
> > > <v.selvamuthukumar at gmail.com> wrote:
> > > >
> > > > Allow mounting of FIT images. If FIT images are used for firmware upgrade
> > > > from linux, mouting can save space in comparison to using dumpimage.
> > > >
> > >
> > > Any comments on this? Is there any other way to get FIT image
> > > contents, without extracting it?
> >
> > Sorry for the delay.  For your program, there's a few things such as
> > missing license header and being able to opt-in as otherwise we add a
> > new build dependency for everyone.
> >
> > For getting FIT image contents, the existing dumpimage tool handles
> > this.
> >
> dumpimage extracts image contents into new files. Current firmware
> upgrade process in our product is:
> 
> 1. FIT image has kernel and rootfs. Get new FIT image in tmpfs.
> 2. dumpimage to extract the FIT image in tmpfs.
> 3. Write kernel and rootfs in partitions.
> 
> many times step2 fails because there is not enough space in tmpfs. FIT
> image and individual components need to be present in tmpfs. When we
> mount the FIT image, it does not take extra space tmpfs.

Ah, so that's your use-case.  But in this case, couldn't you use a named
pipe instead to stream from the image after step one?  Or add a -q
option to dumpimage (so it doesn't print anything) and use -o
/dev/stdout and pipe that directly to whatever is writing to flash?
diff mbox series

Patch

diff --git a/tools/Makefile b/tools/Makefile
index 081383d7a7..c2c6e952ed 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -53,7 +53,7 @@  HOSTCFLAGS_xway-swap-bytes.o := -pedantic
 hostprogs-y += mkenvimage
 mkenvimage-objs := mkenvimage.o os_support.o lib/crc32.o
 
-hostprogs-y += dumpimage mkimage
+hostprogs-y += dumpimage mkimage fitmount
 hostprogs-$(CONFIG_FIT_SIGNATURE) += fit_info fit_check_sign
 
 hostprogs-$(CONFIG_CMD_BOOTEFI_SELFTEST) += file2include
@@ -124,6 +124,8 @@  dumpimage-mkimage-objs := aisimage.o \
 dumpimage-objs := $(dumpimage-mkimage-objs) dumpimage.o
 mkimage-objs   := $(dumpimage-mkimage-objs) mkimage.o
 fit_info-objs   := $(dumpimage-mkimage-objs) fit_info.o
+fitmount-objs   := $(dumpimage-mkimage-objs) fitmount.o
+HOSTCFLAGS_fitmount.o += $(shell pkg-config fuse --cflags)
 fit_check_sign-objs   := $(dumpimage-mkimage-objs) fit_check_sign.o
 file2include-objs := file2include.o
 
@@ -167,6 +169,7 @@  HOSTCFLAGS_fit_image.o += -DMKIMAGE_DTC=\"$(CONFIG_MKIMAGE_DTC_PATH)\"
 HOSTLOADLIBES_dumpimage := $(HOSTLOADLIBES_mkimage)
 HOSTLOADLIBES_fit_info := $(HOSTLOADLIBES_mkimage)
 HOSTLOADLIBES_fit_check_sign := $(HOSTLOADLIBES_mkimage)
+HOSTLOADLIBES_fitmount := $(HOSTLOADLIBES_mkimage) $(shell pkg-config fuse --libs 2> /dev/null || echo "-lfuse")
 
 hostprogs-$(CONFIG_EXYNOS5250) += mkexynosspl
 hostprogs-$(CONFIG_EXYNOS5420) += mkexynosspl
diff --git a/tools/fitmount.c b/tools/fitmount.c
new file mode 100644
index 0000000000..0c52d275c6
--- /dev/null
+++ b/tools/fitmount.c
@@ -0,0 +1,254 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <libfdt.h>
+#include <errno.h>
+#include <image.h>
+#define FUSE_USE_VERSION 26
+#include <fuse/fuse.h>
+#include <fuse/fuse_opt.h>
+struct imginfo {
+	const char *name;
+	const void *data;
+	size_t len;
+};
+static int n_images = 0;
+static struct imginfo *g_imginfo = NULL;
+
+static char *imagefile = NULL;
+static char *mtpt = NULL;
+
+static struct fuse_operations fit_oper;
+enum
+{
+	KEY_HELP,
+};
+static struct fuse_opt fit_opts[] = {
+	FUSE_OPT_KEY("-h",             KEY_HELP),
+	FUSE_OPT_KEY("--help",         KEY_HELP),
+	FUSE_OPT_END
+};
+
+static void usage(const char *prog)
+{
+	fprintf(stderr, "usage: %s fitimage mountpoint\n", prog);
+}
+
+static struct imginfo* get_img_info(const char *path)
+{
+	int i;
+
+	for(i = 0; i < n_images; i++) {
+		if(strcmp(path, g_imginfo[i].name) == 0)
+			return &g_imginfo[i];
+	}
+	return NULL;
+}
+
+static int fit_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
+{
+	(void) data;
+
+	switch( key ) {
+	case FUSE_OPT_KEY_OPT:
+		return 1;
+
+	case FUSE_OPT_KEY_NONOPT:
+		if( !imagefile) {
+			imagefile = strdup(arg);
+			return 0;
+		} else if( !mtpt ) {
+			mtpt = strdup(arg);
+		}
+		return 1;
+
+	case KEY_HELP:
+		usage(outargs->argv[0]);
+		fuse_opt_add_arg(outargs, "-h");
+		fuse_main( outargs->argc, outargs->argv, &fit_oper, NULL);
+		exit(1);
+
+	default:
+		fprintf(stderr, "internal error\n");
+		abort();
+	}
+}
+
+static int fit_getattr(const char *path, struct stat *stbuf)
+{
+	int res = 0;
+	struct imginfo *in;
+
+	memset(stbuf, 0, sizeof(struct stat));
+	if (strcmp(path, "/") == 0) {
+		stbuf->st_mode = S_IFDIR | 0755;
+		stbuf->st_nlink = 2;
+	} else if ((in = get_img_info(path + 1))) {
+		stbuf->st_mode = S_IFREG | 0444;
+		stbuf->st_nlink = 1;
+		stbuf->st_size = in->len;
+	} else
+		res = -ENOENT;
+
+	return res;
+}
+
+static int fit_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+			 off_t offset, struct fuse_file_info *fi)
+{
+	(void) offset;
+	int i;
+
+	if (strcmp(path, "/") != 0)
+		return -ENOENT;
+
+	filler(buf, ".", NULL, 0);
+	filler(buf, "..", NULL, 0);
+	for(i = 0; i < n_images; i++)
+		filler(buf, g_imginfo[i].name, NULL, 0);
+
+	return 0;
+}
+
+static int fit_open(const char *path, struct fuse_file_info *fi)
+{
+	struct imginfo *in;
+
+	if ((in = get_img_info(path+1)) == NULL)
+		return -ENOENT;
+
+	if ((fi->flags & 3) != O_RDONLY)
+		return -EACCES;
+
+	return 0;
+}
+
+static int fit_read(const char *path, char *buf, size_t size, off_t offset,
+				struct fuse_file_info *fi)
+{
+	struct imginfo *in;
+
+	if ((in = get_img_info(path+1)) == NULL)
+		return -ENOENT;
+
+	if (offset < in->len) {
+		if (offset + size > in->len)
+			size = in->len - offset;
+		memcpy(buf, in->data + offset, size);
+	} else
+		size = 0;
+
+	return size;
+}
+
+static struct fuse_operations fit_oper = {
+	.getattr	= fit_getattr,
+	.readdir	= fit_readdir,
+	.open		= fit_open,
+	.read		= fit_read,
+};
+
+int main(int argc, char *argv[])
+{
+	int images_noffset;
+	int noffset;
+	int ndepth;
+	int count = 0;
+	int ifd = -1;
+	char *ptr;
+	void *fit;
+	struct stat sbuf;
+	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
+
+	if ( fuse_opt_parse(&args, NULL, fit_opts, fit_opt_proc) == -1)
+		return -1;
+
+	if (imagefile == NULL) {
+		fprintf(stderr, "fitimage missing\n");
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+	if (mtpt == NULL) {
+		fprintf(stderr, "mountpoint missing\n");
+		usage(argv[0]);
+		exit(EXIT_FAILURE);
+	}
+	/* check if mtpt is ok and writeable */
+	if( stat( mtpt, &sbuf ) != 0 ) {
+		perror( "Error stat'ing mountpoint" );
+		exit( EXIT_FAILURE );
+	}
+	if( ! S_ISDIR( sbuf.st_mode ) ) {
+		fprintf( stderr, "Problem with mountpoint: %s\n",
+				strerror( ENOTDIR ) );
+		exit( EXIT_FAILURE );
+	}
+
+	ifd = open(imagefile, O_RDONLY);
+	if (ifd < 0) {
+		fprintf(stderr, "%s: Can't open \"%s\": %s\n", argv[0],
+			imagefile, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	if (fstat(ifd, &sbuf) < 0) {
+		fprintf(stderr, "%s: Can't stat \"%s\": %s\n", argv[0],
+			imagefile, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, ifd, 0);
+	if (ptr == MAP_FAILED) {
+		fprintf(stderr, "%s: Can't read \"%s\": %s\n", argv[0],
+			imagefile, strerror(errno));
+		exit(EXIT_FAILURE);
+	}
+
+	if (fdt_check_header(ptr)) {
+		fprintf(stderr, "%s: FDT header check fails on \"%s\"\n", argv[0], imagefile);
+		exit(EXIT_FAILURE);
+	}
+
+	fit = ptr;
+
+	if (!fit_check_format(fit)) {
+		fprintf(stderr, "%s: Bad FIT image format \"%s\"\n", argv[0], imagefile);
+		exit(EXIT_FAILURE);
+	}
+
+	/* Find images parent node offset */
+	images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
+	if (images_noffset < 0) {
+		fprintf(stderr, "%s: Can't find images parent node '%s' (%s)\n",
+		       argv[0], FIT_IMAGES_PATH, fdt_strerror(images_noffset));
+		exit(EXIT_FAILURE);
+	}
+
+	/* Avoid any overrun */
+	count = fit_get_subimage_count(fit, images_noffset);
+
+	/* Process its subnodes, extract the desired component from image */
+	for (ndepth = 0, count = 0,
+		noffset = fdt_next_node(fit, images_noffset, &ndepth);
+		(noffset >= 0) && (ndepth > 0);
+		noffset = fdt_next_node(fit, noffset, &ndepth)) {
+		if (ndepth == 1) {
+			g_imginfo = realloc(g_imginfo, (count+1) * sizeof(*g_imginfo));
+
+			g_imginfo[count].name = fit_get_name(fit, noffset, NULL);
+			fit_image_get_data(fit, noffset, &g_imginfo[count].data, &g_imginfo[count].len);
+
+			count++;
+		}
+	}
+	n_images = count;
+
+	fuse_main(args.argc, args.argv, &fit_oper, NULL);
+	free(g_imginfo);
+	return 0;
+}