diff mbox series

[2/4] Introduce generic copy handler

Message ID 20221011111706.455706-2-sbabic@denx.de
State Accepted
Headers show
Series [1/4] raw_handler: drop duplicate #include | expand

Commit Message

Stefano Babic Oct. 11, 2022, 11:17 a.m. UTC
The current "rawcopy" handler is quite limited. It just works as "raw"
handler. One common use case is to orvide a copy from a MTD device, and
then it is useless. Add a copy handler that runs in a chain another
handler, like "flash", so that any handler can be reused.

Signed-off-by: Stefano Babic <sbabic@denx.de>
---
 handlers/Config.in        |   8 ++
 handlers/Makefile         |   3 +-
 handlers/chain_handler.c  |  48 +++++++++
 handlers/copy_handler.c   | 204 ++++++++++++++++++++++++++++++++++++++
 include/chained_handler.h |  13 +++
 5 files changed, 275 insertions(+), 1 deletion(-)
 create mode 100644 handlers/chain_handler.c
 create mode 100644 handlers/copy_handler.c
 create mode 100644 include/chained_handler.h
diff mbox series

Patch

diff --git a/handlers/Config.in b/handlers/Config.in
index 11c0b2d..744968e 100644
--- a/handlers/Config.in
+++ b/handlers/Config.in
@@ -60,6 +60,14 @@  config CFIHAMMING1
 
 	  You do not need this if you do not have an OMAP SoC.
 
+config COPY
+	bool "copy"
+	default n
+	help
+	  This works as script and copies files / images to a
+	  different destination. It requires a chained handler
+	  so that it works even with flash or special devices.
+
 config DELTA
 	bool "delta"
 	depends on HAVE_LIBCURL
diff --git a/handlers/Makefile b/handlers/Makefile
index 2b6faee..847d59f 100644
--- a/handlers/Makefile
+++ b/handlers/Makefile
@@ -7,9 +7,10 @@ 
 # not drop the handler if it is not called.
 # Handler can be called dynamically based
 # on the received image type.
-obj-y	+= dummy_handler.o
+obj-y	+= dummy_handler.o chain_handler.o
 obj-$(CONFIG_ARCHIVE) += archive_handler.o
 obj-$(CONFIG_BOOTLOADERHANDLER) += boot_handler.o
+obj-$(CONFIG_COPY) += copy_handler.o
 obj-$(CONFIG_CFI)	+= flash_handler.o
 obj-$(CONFIG_DELTA)	+= delta_handler.o delta_downloader.o zchunk_range.o
 obj-$(CONFIG_DISKFORMAT_HANDLER)	+= diskformat_handler.o
diff --git a/handlers/chain_handler.c b/handlers/chain_handler.c
new file mode 100644
index 0000000..75e79ee
--- /dev/null
+++ b/handlers/chain_handler.c
@@ -0,0 +1,48 @@ 
+/*
+ * (C) Copyright 2022
+ * Stefano Babic, DENX Software Engineering, sbabic@denx.de.
+ *
+ * SPDX-License-Identifier:     GPL-2.0-only
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include "chained_handler.h"
+#include "installer.h"
+#include "pctl.h"
+#include "util.h"
+
+/*
+ * Thread to start the chained handler.
+ * This received from FIFO the reassembled stream with
+ * the artifact and can pass it to the handler responsible for the install.
+ */
+void *chain_handler_thread(void *data)
+{
+	struct chain_handler_data *priv = (struct chain_handler_data *)data;
+	struct img_type *img = &priv->img;
+	unsigned long ret;
+
+	thread_ready();
+	if (img->fdin < 0) {
+		return (void *)1;
+	}
+
+	img->install_directly = true;
+	ret = install_single_image(img, false);
+
+	if (ret) {
+		ERROR("Chain handler return with Error");
+		close(img->fdin);
+	}
+
+	return (void *)ret;
+}
+
+
diff --git a/handlers/copy_handler.c b/handlers/copy_handler.c
new file mode 100644
index 0000000..6d1c577
--- /dev/null
+++ b/handlers/copy_handler.c
@@ -0,0 +1,204 @@ 
+/*
+ * (C) Copyright 2022
+ * Stefano Babic, DENX Software Engineering, sbabic@denx.de.
+ *
+ * SPDX-License-Identifier:     GPL-2.0-only
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <mtd/mtd-user.h>
+#ifdef __FreeBSD__
+#include <sys/disk.h>
+// the ioctls are almost identical except for the name, just alias it
+#define BLKGETSIZE64 DIOCGMEDIASIZE
+#else
+#include <linux/fs.h>
+#endif
+
+#include <pctl.h>
+#include "swupdate.h"
+#include "handler.h"
+#include "util.h"
+#include "chained_handler.h"
+#include "installer.h"
+
+#define PIPE_READ  0
+#define PIPE_WRITE 1
+
+static void copy_handler(void);
+
+static int copy_image_file(struct img_type *img, void *data)
+{
+	int ret;
+	int fdout, fdin;
+	struct dict_list *proplist;
+	struct dict_list_elem *entry;
+	uint32_t checksum;
+	unsigned long offset = 0;
+	size_t size;
+	struct stat statbuf;
+	struct script_handler_data *script_data;
+	struct chain_handler_data priv;
+	pthread_t chain_handler_thread_id;
+	int pipes[2];
+	char *path = NULL;
+	struct mtd_info_user	mtdinfo;
+
+	if (!data)
+		return -1;
+
+	script_data = data;
+
+	proplist = dict_get_list(&img->properties, "type");
+	if (proplist) {
+		entry = LIST_FIRST(proplist);
+		/* check if this should just run as pre or post install */
+		if (entry) {
+			if (strcmp(entry->value, "preinstall") && strcmp(entry->value, "postinstall")) {
+				ERROR("Type can be just preinstall or postinstall");
+				return -EINVAL;
+			}
+			script_fn type = !strcmp(entry->value, "preinstall") ? PREINSTALL : POSTINSTALL;
+			if (type != script_data->scriptfn) {
+				TRACE("Script set to %s, skipping", entry->value);
+				return 0;
+			}
+		}
+	}
+
+	proplist = dict_get_list(&img->properties, "copyfrom");
+	if (!proplist || !(entry = LIST_FIRST(proplist))) {
+		ERROR("Missing source device, no copyfrom property");
+		return -EINVAL;
+	}
+
+	path = realpath(entry->value, NULL);
+	if (!path) {
+		ERROR("%s cannot be resolved", entry->value);
+		return -EINVAL;
+	}
+
+	/*
+	 * Get information about source (size)
+	 */
+	fdin = open(path, O_RDONLY);
+	if (fdin < 0) {
+		ERROR("%s cannot be opened: %s", path, strerror(errno));
+		free(path);
+		return -EINVAL;
+	}
+
+	ret = fstat(fdin, &statbuf);
+	if (ret < 0) {
+		ERROR("Cannot be retrieved information on %s", path);
+		free(path);
+		close(fdin);
+		return -ENODEV;
+	}
+
+	/*
+	 * Detect the size if not set in sw-descriptiont
+	 */
+	size = dict_get_value(&img->properties, "size") ? ustrtoull(dict_get_value(&img->properties, "size"), NULL, 0) : 0;
+	if (!size) {
+		if (S_ISREG(statbuf.st_mode))
+			size = statbuf.st_size;
+		else if (S_ISBLK(statbuf.st_mode) && (ioctl(fdin, BLKGETSIZE64, &size) < 0)) {
+			ERROR("Cannot get size of Block Device %s", path);
+		} else if (S_ISCHR(statbuf.st_mode)) {
+			/* it is maybe a MTD device, just try */
+			ret = ioctl(fdin, MEMGETINFO, &mtdinfo);
+			if (!ret)
+				size = mtdinfo.size;
+		}
+	}
+
+	if (!size) {
+		ERROR("Size cannot be detected for %s", path);
+		free(path);
+		close(fdin);
+		return -ENODEV;
+	}
+
+	TRACE("Copying %lu from %s to %s", size, path, img->device);
+
+	free(path);
+
+	if (!size) {
+		ERROR("Size cannot be detected, please set it, exiting...");
+		close(fdin);
+		return -EFAULT;
+	}
+
+	if (pipe(pipes) < 0) {
+		ERROR("Could not create pipes for chained handler, existing...");
+		close(fdin);
+		return -EFAULT;
+	}
+
+	/* Overwrite some parameters for chained handler */
+	memcpy(&priv.img, img, sizeof(*img));
+	priv.img.compressed = COMPRESSED_FALSE;
+	memset(priv.img.sha256, 0, SHA256_HASH_LENGTH);
+	priv.img.fdin = pipes[PIPE_READ];
+	priv.img.size = size;
+
+	/*
+	 * No chain set, fallback to rawcopy
+	 */
+	proplist = dict_get_list(&img->properties, "chain");
+	if (!proplist || !(entry = LIST_FIRST(proplist))) {
+		WARN("No chained handler set, fallback to rawcopy");
+		strncpy(priv.img.type, "raw", sizeof(priv.img.type));
+	} else {
+		strlcpy(priv.img.type, entry->value, sizeof(priv.img.type));
+		TRACE("Set %s handler in the chain", priv.img.type);
+	}
+
+	fdout = pipes[PIPE_WRITE];
+	signal(SIGPIPE, SIG_IGN);
+
+	chain_handler_thread_id = start_thread(chain_handler_thread, &priv);
+	wait_threads_ready();
+
+	ret = copyfile(fdin,
+			&fdout,
+			size,
+			&offset,
+			0,
+			0, /* no skip */
+			0, /* no compressed */
+			&checksum,
+			0, /* no sha256 */
+			false, /* no encrypted */
+			NULL, /* no IVT */
+			NULL);
+
+	close(fdout);
+	void *status;
+	ret = pthread_join(chain_handler_thread_id, &status);
+	if (ret) {
+		ERROR("return code from pthread_join() is %d", ret);
+	} else
+		ret = (unsigned long)status;
+
+	DEBUG("Chained handler returned %d", ret);
+
+	close(fdin);
+	return ret;
+}
+
+__attribute__((constructor))
+void copy_handler(void)
+{
+	register_handler("copy", copy_image_file,
+				SCRIPT_HANDLER | NO_DATA_HANDLER, NULL);
+}
diff --git a/include/chained_handler.h b/include/chained_handler.h
new file mode 100644
index 0000000..0c74025
--- /dev/null
+++ b/include/chained_handler.h
@@ -0,0 +1,13 @@ 
+/*
+ * (C) Copyright 2022
+ * Stefano Babic, DENX Software Engineering, sbabic@denx.de.
+ *
+ * SPDX-License-Identifier:     GPL-2.0-only
+ */
+
+#include "swupdate.h"
+struct chain_handler_data {
+	struct img_type img;
+};
+
+extern void *chain_handler_thread(void *data);