meta-digi-dey: swupdate: add 'UBIVOL_RDIFFHANDLER' support to MTD based systems
Writing directly into UBI volumes is not allowed, so a special 'rdiff' handler capable of write data in UBI volumes is required. This commits adds the new handler and enables it in MTD based systems. https://onedigi.atlassian.net/browse/DEL-8624 Signed-off-by: David Escalona <david.escalona@digi.com>
This commit is contained in:
parent
a203487d8f
commit
3bd1541f09
|
|
@ -0,0 +1,694 @@
|
|||
From: Sergey Nazaryev <sergey@coolautomation.com>
|
||||
Date: Thu, 27 Jan 2022 19:41:23 +0200
|
||||
Subject: [PATCH 1/1] handlers: rdiff handler for applying librsync's rdiff
|
||||
patches to UBI volumes
|
||||
|
||||
The ubivol rdiff handler adds support for applying binary
|
||||
delta patches generated by librsync's rdiff tool to UBI volumes,
|
||||
see http://librsync.sourcefrog.net
|
||||
|
||||
Signed-off-by: Sergey Nazaryev <sergey@coolautomation.com>
|
||||
Signed-off-by: David Escalona <david.escalona@digi.com>
|
||||
---
|
||||
Makefile.flags | 4 +
|
||||
configs/all_handlers_defconfig | 1 +
|
||||
handlers/Config.in | 13 +
|
||||
handlers/Makefile | 1 +
|
||||
handlers/ubivol_rdiff_handler.c | 606 ++++++++++++++++++++++++++++++++
|
||||
5 files changed, 625 insertions(+)
|
||||
create mode 100644 handlers/ubivol_rdiff_handler.c
|
||||
|
||||
diff --git a/Makefile.flags b/Makefile.flags
|
||||
index 2a021c8..b542358 100644
|
||||
--- a/Makefile.flags
|
||||
+++ b/Makefile.flags
|
||||
@@ -209,6 +209,10 @@ ifeq ($(CONFIG_RDIFFHANDLER),y)
|
||||
LDLIBS += rsync
|
||||
endif
|
||||
|
||||
+ifeq ($(CONFIG_UBIVOL_RDIFFHANDLER),y)
|
||||
+LDLIBS += rsync
|
||||
+endif
|
||||
+
|
||||
ifeq ($(CONFIG_REMOTE_HANDLER),y)
|
||||
LDLIBS += zmq
|
||||
endif
|
||||
diff --git a/configs/all_handlers_defconfig b/configs/all_handlers_defconfig
|
||||
index 8f6f8e0..9077220 100644
|
||||
--- a/configs/all_handlers_defconfig
|
||||
+++ b/configs/all_handlers_defconfig
|
||||
@@ -31,5 +31,6 @@ CONFIG_SHELLSCRIPTHANDLER=y
|
||||
CONFIG_SWUFORWARDER_HANDLER=y
|
||||
CONFIG_SSBLSWITCH=y
|
||||
CONFIG_UBIVOL=y
|
||||
+CONFIG_UBIVOL_RDIFFHANDLER=y
|
||||
CONFIG_UCFWHANDLER=y
|
||||
CONFIG_UNIQUEUUID=y
|
||||
diff --git a/handlers/Config.in b/handlers/Config.in
|
||||
index 107ffeb..62d54bd 100644
|
||||
--- a/handlers/Config.in
|
||||
+++ b/handlers/Config.in
|
||||
@@ -166,6 +166,19 @@ config RDIFFHANDLER
|
||||
comment "rdiff support needs librsync"
|
||||
depends on !HAVE_LIBRSYNC
|
||||
|
||||
+config UBIVOL_RDIFFHANDLER
|
||||
+ bool "ubirdiff"
|
||||
+ depends on HAVE_LIBRSYNC
|
||||
+ depends on HAVE_LIBUBI
|
||||
+ depends on MTD
|
||||
+ default n
|
||||
+ help
|
||||
+ Add support for applying librsync's rdiff patches to UBI volumes,
|
||||
+ see http://librsync.sourcefrog.net/
|
||||
+
|
||||
+comment "ubirdiff support needs libubi and librsync"
|
||||
+ depends on !HAVE_LIBRSYNC || !HAVE_LIBUBI
|
||||
+
|
||||
config READBACKHANDLER
|
||||
bool "readback"
|
||||
depends on HASH_VERIFY
|
||||
diff --git a/handlers/Makefile b/handlers/Makefile
|
||||
index b5203f9..bd47d3b 100644
|
||||
--- a/handlers/Makefile
|
||||
+++ b/handlers/Makefile
|
||||
@@ -21,6 +21,7 @@ obj-$(CONFIG_CFIHAMMING1)+= flash_hamming1_handler.o
|
||||
obj-$(CONFIG_LUASCRIPTHANDLER) += lua_scripthandler.o
|
||||
obj-$(CONFIG_RAW) += raw_handler.o
|
||||
obj-$(CONFIG_RDIFFHANDLER) += rdiff_handler.o
|
||||
+obj-$(CONFIG_UBIVOL_RDIFFHANDLER) += ubivol_rdiff_handler.o
|
||||
obj-$(CONFIG_READBACKHANDLER) += readback_handler.o
|
||||
obj-$(CONFIG_REMOTE_HANDLER) += remote_handler.o
|
||||
obj-$(CONFIG_SHELLSCRIPTHANDLER) += shell_scripthandler.o
|
||||
diff --git a/handlers/ubivol_rdiff_handler.c b/handlers/ubivol_rdiff_handler.c
|
||||
new file mode 100644
|
||||
index 0000000..3b6073d
|
||||
--- /dev/null
|
||||
+++ b/handlers/ubivol_rdiff_handler.c
|
||||
@@ -0,0 +1,606 @@
|
||||
+/*
|
||||
+ * Author: Christian Storm
|
||||
+ * Copyright (C) 2018, Siemens AG
|
||||
+ *
|
||||
+ * SPDX-License-Identifier: GPL-2.0-only
|
||||
+ */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <unistd.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <libgen.h>
|
||||
+#include <errno.h>
|
||||
+#include <sys/stat.h>
|
||||
+#include <stdbool.h>
|
||||
+#include <librsync.h>
|
||||
+#include <sys/types.h>
|
||||
+#include <sys/stat.h>
|
||||
+#include <fcntl.h>
|
||||
+
|
||||
+#include <mtd/mtd-user.h>
|
||||
+#include "swupdate.h"
|
||||
+#include "handler.h"
|
||||
+#include "flash.h"
|
||||
+#include "util.h"
|
||||
+
|
||||
+/* Use rdiff's default inbuf and outbuf size of 64K */
|
||||
+#define RDIFF_BUFFER_SIZE 64 * 1024
|
||||
+
|
||||
+#define TEST_OR_FAIL(expr, failret) \
|
||||
+ if (expr) { \
|
||||
+ } else { \
|
||||
+ ERROR("Assertion violated: %s.", #expr); \
|
||||
+ return failret; \
|
||||
+ }
|
||||
+
|
||||
+void ubivol_rdiff_image_handler(void);
|
||||
+
|
||||
+struct rdiff_t
|
||||
+{
|
||||
+ rs_job_t *job;
|
||||
+ rs_buffers_t buffers;
|
||||
+
|
||||
+ int dest_file_fd;
|
||||
+ FILE *base_file;
|
||||
+
|
||||
+ char *inbuf;
|
||||
+ char *outbuf;
|
||||
+
|
||||
+ uint8_t type;
|
||||
+};
|
||||
+
|
||||
+static void rdiff_log(rs_loglevel level, char const *msg)
|
||||
+{
|
||||
+ int loglevelmap[] =
|
||||
+ {
|
||||
+ [RS_LOG_EMERG] = ERRORLEVEL,
|
||||
+ [RS_LOG_ALERT] = ERRORLEVEL,
|
||||
+ [RS_LOG_CRIT] = ERRORLEVEL,
|
||||
+ [RS_LOG_ERR] = ERRORLEVEL,
|
||||
+ [RS_LOG_WARNING] = WARNLEVEL,
|
||||
+ [RS_LOG_NOTICE] = INFOLEVEL,
|
||||
+ [RS_LOG_INFO] = INFOLEVEL,
|
||||
+ [RS_LOG_DEBUG] = TRACELEVEL
|
||||
+ };
|
||||
+ *strchrnul(msg, '\n') = '\0';
|
||||
+ swupdate_notify(RUN, "%s", loglevelmap[level], msg);
|
||||
+}
|
||||
+
|
||||
+static rs_result base_file_read_cb(void *fp, rs_long_t pos, size_t *len, void **buf)
|
||||
+{
|
||||
+ FILE *f = (FILE *)fp;
|
||||
+
|
||||
+ if (fseek(f, pos, SEEK_SET) != 0) {
|
||||
+ ERROR("Error seeking rdiff base file: %s", strerror(errno));
|
||||
+ return RS_IO_ERROR;
|
||||
+ }
|
||||
+
|
||||
+ int ret = fread(*buf, 1, *len, f);
|
||||
+ if (ret == -1) {
|
||||
+ ERROR("Error reading rdiff base file: %s", strerror(errno));
|
||||
+ return RS_IO_ERROR;
|
||||
+ }
|
||||
+ if (ret == 0) {
|
||||
+ ERROR("Unexpected EOF on rdiff base file.");
|
||||
+ return RS_INPUT_ENDED;
|
||||
+ }
|
||||
+ *len = ret;
|
||||
+
|
||||
+ return RS_DONE;
|
||||
+}
|
||||
+
|
||||
+static rs_result fill_inbuffer(struct rdiff_t *rdiff_state, const void *buf, unsigned int *len)
|
||||
+{
|
||||
+ rs_buffers_t *buffers = &rdiff_state->buffers;
|
||||
+
|
||||
+ if (buffers->eof_in == true) {
|
||||
+ TRACE("EOF on rdiff chunk input, not reading more data.");
|
||||
+ return RS_DONE;
|
||||
+ }
|
||||
+
|
||||
+ if (*len == 0) {
|
||||
+ TRACE("No rdiff chunk input to consume.");
|
||||
+ return RS_DONE;
|
||||
+ }
|
||||
+
|
||||
+ if (buffers->avail_in == 0) {
|
||||
+ /* No more buffered input data pending, get some... */
|
||||
+ TEST_OR_FAIL(*len <= RDIFF_BUFFER_SIZE, RS_IO_ERROR);
|
||||
+ buffers->next_in = rdiff_state->inbuf;
|
||||
+ buffers->avail_in = *len;
|
||||
+ TRACE("Writing %d bytes to rdiff input buffer.", *len);
|
||||
+ (void)memcpy(rdiff_state->inbuf, buf, *len);
|
||||
+ *len = 0;
|
||||
+ } else {
|
||||
+ /* There's more input, try to append it to input buffer. */
|
||||
+ char *target = buffers->next_in + buffers->avail_in;
|
||||
+ unsigned int buflen = rdiff_state->inbuf + RDIFF_BUFFER_SIZE - target;
|
||||
+ buflen = buflen > *len ? *len : buflen;
|
||||
+ TEST_OR_FAIL(target + buflen <= rdiff_state->inbuf + RDIFF_BUFFER_SIZE, RS_IO_ERROR);
|
||||
+
|
||||
+ if (buflen == 0) {
|
||||
+ TRACE("Not consuming rdiff chunk input, buffer already filled.");
|
||||
+ return RS_BLOCKED;
|
||||
+ }
|
||||
+ TRACE("Appending %d bytes to rdiff input buffer.", buflen);
|
||||
+ buffers->avail_in += buflen;
|
||||
+ (void)memcpy(target, buf, buflen);
|
||||
+ *len -= buflen;
|
||||
+ }
|
||||
+ return RS_DONE;
|
||||
+}
|
||||
+
|
||||
+static rs_result drain_outbuffer(struct rdiff_t *rdiff_state)
|
||||
+{
|
||||
+ rs_buffers_t *buffers = &rdiff_state->buffers;
|
||||
+
|
||||
+ int len = buffers->next_out - rdiff_state->outbuf;
|
||||
+ TEST_OR_FAIL(len <= RDIFF_BUFFER_SIZE, RS_IO_ERROR);
|
||||
+ TEST_OR_FAIL(buffers->next_out >= rdiff_state->outbuf, RS_IO_ERROR);
|
||||
+ TEST_OR_FAIL(buffers->next_out <= rdiff_state->outbuf + RDIFF_BUFFER_SIZE, RS_IO_ERROR);
|
||||
+
|
||||
+ if (len > 0) {
|
||||
+ TRACE("Draining %d bytes from rdiff output buffer", len);
|
||||
+ buffers->next_out = rdiff_state->outbuf;
|
||||
+ buffers->avail_out = RDIFF_BUFFER_SIZE;
|
||||
+ if (copy_write(&rdiff_state->dest_file_fd, buffers->next_out, len) != 0) {
|
||||
+ ERROR("Cannot drain rdiff output buffer.");
|
||||
+ return RS_IO_ERROR;
|
||||
+ }
|
||||
+ } else {
|
||||
+ TRACE("No output rdiff buffer data to drain.");
|
||||
+ }
|
||||
+ return RS_DONE;
|
||||
+}
|
||||
+
|
||||
+static inline void rdiff_stats(const char* msg, struct rdiff_t *rdiff_state, rs_result result) {
|
||||
+ rs_buffers_t *buffers = &rdiff_state->buffers;
|
||||
+ char *strresult = (char*)"ERROR";
|
||||
+ switch (result) {
|
||||
+ case RS_DONE: strresult = (char*)"DONE"; break;
|
||||
+ case RS_BLOCKED: strresult = (char*)"BLOCKED"; break;
|
||||
+ case RS_RUNNING: strresult = (char*)"RUNNING"; break;
|
||||
+ default: break;
|
||||
+ }
|
||||
+ TRACE("%s avail_in=%ld avail_out=%ld result=%s",
|
||||
+ msg, buffers->avail_in, buffers->avail_out, strresult);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * check_ubi_alwaysremove - check the property always-remove for this image
|
||||
+ * @img: image information
|
||||
+ *
|
||||
+ * Return: 1 if the property always-remove is true, otherwise 0.
|
||||
+ */
|
||||
+static bool check_ubi_alwaysremove(struct img_type *img)
|
||||
+{
|
||||
+ return strtobool(dict_get_value(&img->properties, "always-remove"));
|
||||
+}
|
||||
+
|
||||
+static struct ubi_part *search_volume(const char *str, struct ubilist *list)
|
||||
+{
|
||||
+ struct ubi_part *vol;
|
||||
+
|
||||
+ LIST_FOREACH(vol, list, next) {
|
||||
+ if (strcmp(vol->vol_info.name, str) == 0)
|
||||
+ return vol;
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+/* search a UBI volume by name across all mtd partitions */
|
||||
+static struct ubi_part *search_volume_global(const char *str)
|
||||
+{
|
||||
+ struct flash_description *flash = get_flash_info();
|
||||
+ struct mtd_info *mtd_info = &flash->mtd;
|
||||
+ struct mtd_ubi_info *mtd_ubi_info;
|
||||
+ struct ubi_part *ubivol;
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = mtd_info->lowest_mtd_num; i <= mtd_info->highest_mtd_num; i++) {
|
||||
+ mtd_ubi_info = &flash->mtd_info[i];
|
||||
+ ubivol = search_volume(str, &mtd_ubi_info->ubi_partitions);
|
||||
+ if (ubivol)
|
||||
+ return ubivol;
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static int resize_volume(struct img_type *cfg, long long size)
|
||||
+{
|
||||
+ struct flash_description *nandubi = get_flash_info();
|
||||
+ struct ubi_part *ubivol;
|
||||
+ struct ubi_mkvol_request req;
|
||||
+ struct mtd_ubi_info *mtd_info;
|
||||
+ int mtdnum, req_vol_type;
|
||||
+ char node[64];
|
||||
+ int err;
|
||||
+ struct flash_description *flash = get_flash_info();
|
||||
+
|
||||
+ /* determine the requested volume type */
|
||||
+ if (!strcmp(cfg->type_data, "static"))
|
||||
+ req_vol_type = UBI_STATIC_VOLUME;
|
||||
+ else
|
||||
+ req_vol_type = UBI_DYNAMIC_VOLUME;
|
||||
+
|
||||
+ /*
|
||||
+ * Partition are adjusted only in one MTD device
|
||||
+ * Other MTD are not touched
|
||||
+ */
|
||||
+ mtdnum = get_mtd_from_device(cfg->device);
|
||||
+ if (mtdnum < 0) {
|
||||
+ /* Allow device to be specified by name OR number */
|
||||
+ mtdnum = get_mtd_from_name(cfg->device);
|
||||
+ }
|
||||
+ if (mtdnum < 0 || !mtd_dev_present(flash->libmtd, mtdnum)) {
|
||||
+ ERROR("%s does not exist: partitioning not possible",
|
||||
+ cfg->device);
|
||||
+ return -ENODEV;
|
||||
+ }
|
||||
+
|
||||
+ mtd_info = &nandubi->mtd_info[mtdnum];
|
||||
+
|
||||
+ /*
|
||||
+ * Search for volume with the same name
|
||||
+ */
|
||||
+ ubivol = mtd_info->ubi_partitions.lh_first;
|
||||
+ for(ubivol = mtd_info->ubi_partitions.lh_first;
|
||||
+ ubivol != NULL;
|
||||
+ ubivol = ubivol->next.le_next) {
|
||||
+ if (strcmp(ubivol->vol_info.name, cfg->volname) == 0) {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (ubivol) {
|
||||
+ unsigned int requested_lebs, allocated_lebs;
|
||||
+
|
||||
+ /* This should never happen, the fields are filled by scan_ubi */
|
||||
+ if (!mtd_info->dev_info.leb_size) {
|
||||
+ return -EFAULT;
|
||||
+ }
|
||||
+
|
||||
+ /* Check if size is changed */
|
||||
+ requested_lebs = size / mtd_info->dev_info.leb_size +
|
||||
+ ((size % mtd_info->dev_info.leb_size) ? 1 : 0);
|
||||
+ allocated_lebs = ubivol->vol_info.rsvd_bytes / mtd_info->dev_info.leb_size;
|
||||
+
|
||||
+ if (requested_lebs == allocated_lebs &&
|
||||
+ req_vol_type == ubivol->vol_info.type &&
|
||||
+ !check_ubi_alwaysremove(cfg)) {
|
||||
+ TRACE("skipping volume %s (same size and type)",
|
||||
+ ubivol->vol_info.name);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ snprintf(node, sizeof(node), "/dev/ubi%d", ubivol->vol_info.dev_num);
|
||||
+ err = ubi_rmvol(nandubi->libubi, node, ubivol->vol_info.vol_id);
|
||||
+ if (err) {
|
||||
+ ERROR("Volume %s cannot be dropped", ubivol->vol_info.name);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ TRACE("Removed UBI Volume %s", ubivol->vol_info.name);
|
||||
+
|
||||
+ LIST_REMOVE(ubivol, next);
|
||||
+ free(ubivol);
|
||||
+ }
|
||||
+
|
||||
+ if (size) {
|
||||
+ /* We do not need a volume to get the right node */
|
||||
+ snprintf(node, sizeof(node), "/dev/ubi%d", mtd_info->dev_info.dev_num);
|
||||
+
|
||||
+ /*
|
||||
+ * Creates all other partitions as specified in the description file
|
||||
+ * Volumes are empty, and they are filled later by the update procedure
|
||||
+ */
|
||||
+ memset(&req, 0, sizeof(req));
|
||||
+ req.vol_type = req_vol_type;
|
||||
+ req.vol_id = UBI_VOL_NUM_AUTO;
|
||||
+ req.alignment = 1;
|
||||
+ req.bytes = size;
|
||||
+ req.name = cfg->volname;
|
||||
+ err = ubi_mkvol(nandubi->libubi, node, &req);
|
||||
+ if (err < 0) {
|
||||
+ ERROR("cannot create %s UBI volume %s of %lld bytes",
|
||||
+ (req_vol_type == UBI_DYNAMIC_VOLUME) ? "dynamic" : "static",
|
||||
+ req.name, req.bytes);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ ubivol = (struct ubi_part *)calloc(1, sizeof(struct ubi_part));
|
||||
+ if (!ubivol) {
|
||||
+ ERROR("No memory: malloc failed");
|
||||
+ return -ENOMEM;
|
||||
+ }
|
||||
+ err = ubi_get_vol_info1(nandubi->libubi,
|
||||
+ mtd_info->dev_info.dev_num, req.vol_id,
|
||||
+ &ubivol->vol_info);
|
||||
+ if (err) {
|
||||
+ ERROR("cannot get information about "
|
||||
+ "newly created UBI volume");
|
||||
+ return err;
|
||||
+ }
|
||||
+ LIST_INSERT_HEAD(&mtd_info->ubi_partitions, ubivol, next);
|
||||
+ TRACE("Created %s UBI volume %s of %lld bytes (old size %lld)",
|
||||
+ (req_vol_type == UBI_DYNAMIC_VOLUME) ? "dynamic" : "static",
|
||||
+ req.name, req.bytes, ubivol->vol_info.rsvd_bytes);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * check_auto_resize - check the property auto-resize for this image
|
||||
+ * @img: image information
|
||||
+ *
|
||||
+ * Return: 1 if the property auto-resize is true, otherwise 0.
|
||||
+ */
|
||||
+static bool check_ubi_autoresize(struct img_type *img)
|
||||
+{
|
||||
+ return strtobool(dict_get_value(&img->properties, "auto-resize"));
|
||||
+}
|
||||
+
|
||||
+static int wait_volume(struct img_type *img)
|
||||
+{
|
||||
+ int ret = -1, num = 0, dev_num, vol_id;
|
||||
+ struct ubi_part *ubivol;
|
||||
+ struct stat buf;
|
||||
+ char node[64];
|
||||
+
|
||||
+ ubivol = search_volume_global(img->volname);
|
||||
+ if (!ubivol) {
|
||||
+ ERROR("can't found volume %s", img->volname);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ dev_num = ubivol->vol_info.dev_num;
|
||||
+ vol_id = ubivol->vol_info.vol_id;
|
||||
+
|
||||
+ snprintf(node, sizeof(node), "/dev/ubi%d_%d",
|
||||
+ dev_num,
|
||||
+ vol_id);
|
||||
+
|
||||
+ while (num++ < 5)
|
||||
+ {
|
||||
+ ret = stat(node, &buf);
|
||||
+ if (!ret)
|
||||
+ break;
|
||||
+
|
||||
+ sleep(1);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int apply_rdiff_chunk_cb(void *out, const void *buf, unsigned int len)
|
||||
+{
|
||||
+ struct rdiff_t *rdiff_state = (struct rdiff_t *)out;
|
||||
+ rs_buffers_t *buffers = &rdiff_state->buffers;
|
||||
+ unsigned int inbytesleft = len;
|
||||
+ rs_result result = RS_RUNNING;
|
||||
+ rs_result drain_run_result = RS_RUNNING;
|
||||
+
|
||||
+ if (buffers->next_out == NULL) {
|
||||
+ TEST_OR_FAIL(buffers->avail_out == 0, -1);
|
||||
+ buffers->next_out = rdiff_state->outbuf;
|
||||
+ buffers->avail_out = RDIFF_BUFFER_SIZE;
|
||||
+ }
|
||||
+
|
||||
+ while (inbytesleft > 0 || buffers->avail_in > 0) {
|
||||
+ rdiff_stats("[pre] ", rdiff_state, result);
|
||||
+ result = fill_inbuffer(rdiff_state, buf, &inbytesleft);
|
||||
+ if (result != RS_DONE && result != RS_BLOCKED) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+ result = rs_job_iter(rdiff_state->job, buffers);
|
||||
+ if (result != RS_DONE && result != RS_BLOCKED) {
|
||||
+ ERROR("Error processing rdiff chunk: %s", rs_strerror(result));
|
||||
+ return -1;
|
||||
+ }
|
||||
+ drain_run_result = drain_outbuffer(rdiff_state);
|
||||
+ if (drain_run_result != RS_DONE) {
|
||||
+ ERROR("drain_outbuffer return error");
|
||||
+ return -1;
|
||||
+ }
|
||||
+ rdiff_stats("[post]", rdiff_state, result);
|
||||
+
|
||||
+ if (result == RS_DONE) {
|
||||
+ TRACE("rdiff processing done.");
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ rdiff_stats("[ret] ", rdiff_state, result);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int apply_rdiff_patch(struct img_type *img,
|
||||
+ void __attribute__((__unused__)) * data)
|
||||
+{
|
||||
+ int ret = 0;
|
||||
+ long long bytes;
|
||||
+ char node[64];
|
||||
+ int err;
|
||||
+ char sbuf[128];
|
||||
+
|
||||
+ struct rdiff_t rdiff_state = {};
|
||||
+ rdiff_state.type = IMAGE_HANDLER;
|
||||
+
|
||||
+ struct flash_description *flash = get_flash_info();
|
||||
+ libubi_t libubi = flash->libubi;
|
||||
+
|
||||
+ char *base_file_filename = NULL;
|
||||
+ char *output_size_str = NULL;
|
||||
+ struct ubi_part *ubivol;
|
||||
+ struct ubi_vol_info *vol;
|
||||
+
|
||||
+ if (img->seek) {
|
||||
+ /*
|
||||
+ * img->seek mandates copyfile()'s out parameter to be a fd, it
|
||||
+ * isn't. So, the seek option is invalid for the rdiff handler.
|
||||
+ * */
|
||||
+ ERROR("Option 'seek' is not supported for rdiff.");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ base_file_filename = dict_get_value(&img->properties, "rdiffbase");
|
||||
+ if (base_file_filename == NULL) {
|
||||
+ ERROR("Property 'rdiffbase' is missing in sw-description.");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ output_size_str = dict_get_value(&img->properties, "rdiffnewsize");
|
||||
+ if (!output_size_str) {
|
||||
+ ERROR("Property 'rdiffnewsize' is missing in sw-description.");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ bytes = ustrtoull(output_size_str, NULL, 0);
|
||||
+ if (errno || bytes <= 0) {
|
||||
+ ERROR("rdiffnewsize argument %s: ustrtoull failed",
|
||||
+ output_size_str);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ TRACE("The size after applying patch would be %lld bytes", bytes);
|
||||
+
|
||||
+ if (check_ubi_autoresize(img)) {
|
||||
+ ret = resize_volume(img, bytes);
|
||||
+ if (ret < 0) {
|
||||
+ ERROR("Can't resize ubi volume %s", img->volname);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ ret = wait_volume(img);
|
||||
+ if (ret < 0) {
|
||||
+ ERROR("can't found ubi volume %s", img->volname);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!libubi) {
|
||||
+ ERROR("Request to write into UBI, but no UBI on system");
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ /* find the volume to be updated */
|
||||
+ ubivol = search_volume_global(img->volname);
|
||||
+
|
||||
+ if (!ubivol) {
|
||||
+ ERROR("Image %s should be stored in volume "
|
||||
+ "%s, but no volume found",
|
||||
+ img->fname,
|
||||
+ img->volname);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ vol = &ubivol->vol_info;
|
||||
+
|
||||
+ if (bytes > vol->rsvd_bytes) {
|
||||
+ ERROR("\"%s\" (size %lld) will not fit volume \"%s\" (size %lld)",
|
||||
+ img->fname, bytes, img->volname, vol->rsvd_bytes);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ snprintf(node, sizeof(node), "/dev/ubi%d_%d",
|
||||
+ vol->dev_num,
|
||||
+ vol->vol_id);
|
||||
+
|
||||
+ err = ubi_probe_node(libubi, node);
|
||||
+
|
||||
+ if (err == 1) {
|
||||
+ ERROR("\"%s\" is an UBI device node, not an UBI volume node",
|
||||
+ node);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ if (err < 0) {
|
||||
+ if (errno == ENODEV)
|
||||
+ ERROR("%s is not an UBI volume node", node);
|
||||
+ else
|
||||
+ ERROR("error while probing %s", node);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ rdiff_state.dest_file_fd = open(node, O_RDWR);
|
||||
+ if (rdiff_state.dest_file_fd < 0) {
|
||||
+ ERROR("cannot open UBI volume \"%s\"", node);
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ if ((rdiff_state.base_file = fopen(base_file_filename, "rb+")) == NULL) {
|
||||
+ ERROR("%s cannot be opened for reading: %s", base_file_filename, strerror(errno));
|
||||
+ ret = -1;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ if (!(rdiff_state.inbuf = malloc(RDIFF_BUFFER_SIZE))) {
|
||||
+ ERROR("Cannot allocate memory for rdiff input buffer.");
|
||||
+ ret = -1;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ if (!(rdiff_state.outbuf = malloc(RDIFF_BUFFER_SIZE))) {
|
||||
+ ERROR("Cannot allocate memory for rdiff output buffer.");
|
||||
+ ret = -1;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ err = ubi_update_start(libubi, rdiff_state.dest_file_fd, bytes);
|
||||
+ if (err) {
|
||||
+ ERROR("cannot start volume \"%s\" update", node);
|
||||
+ ret = -1;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+ snprintf(sbuf, sizeof(sbuf), "Installing image %s into volume %s(%s)",
|
||||
+ img->fname, node, img->volname);
|
||||
+ notify(RUN, RECOVERY_NO_ERROR, INFOLEVEL, sbuf);
|
||||
+
|
||||
+ int loglevelmap[] =
|
||||
+ {
|
||||
+ [OFF] = RS_LOG_ERR,
|
||||
+ [ERRORLEVEL] = RS_LOG_ERR,
|
||||
+ [WARNLEVEL] = RS_LOG_WARNING,
|
||||
+ [INFOLEVEL] = RS_LOG_INFO,
|
||||
+ [DEBUGLEVEL] = RS_LOG_DEBUG,
|
||||
+ [TRACELEVEL] = RS_LOG_DEBUG,
|
||||
+ };
|
||||
+ rs_trace_set_level(loglevelmap[loglevel]);
|
||||
+ rs_trace_to(rdiff_log);
|
||||
+
|
||||
+ rdiff_state.job = rs_patch_begin(base_file_read_cb, rdiff_state.base_file);
|
||||
+ ret = copyfile(img->fdin,
|
||||
+ &rdiff_state,
|
||||
+ img->size,
|
||||
+ (unsigned long *)&img->offset,
|
||||
+ img->seek,
|
||||
+ 0, /* no skip */
|
||||
+ img->compressed,
|
||||
+ &img->checksum,
|
||||
+ img->sha256,
|
||||
+ img->is_encrypted,
|
||||
+ img->ivt_ascii,
|
||||
+ apply_rdiff_chunk_cb);
|
||||
+ if (ret != 0) {
|
||||
+ ERROR("Error %d running rdiff job, aborting.", ret);
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
+cleanup:
|
||||
+ free(rdiff_state.inbuf);
|
||||
+ free(rdiff_state.outbuf);
|
||||
+ if (rdiff_state.job != NULL) {
|
||||
+ (void)rs_job_free(rdiff_state.job);
|
||||
+ }
|
||||
+ if (rdiff_state.base_file != NULL) {
|
||||
+ if (fclose(rdiff_state.base_file) == EOF) {
|
||||
+ ERROR("Error while closing rdiff base: %s", strerror(errno));
|
||||
+ }
|
||||
+ }
|
||||
+ close(rdiff_state.dest_file_fd);
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+__attribute__((constructor))
|
||||
+void ubivol_rdiff_image_handler(void)
|
||||
+{
|
||||
+ register_handler("ubivol_rdiff_image", apply_rdiff_patch, IMAGE_HANDLER, NULL);
|
||||
+}
|
||||
|
|
@ -6,3 +6,5 @@ CONFIG_UBIATTACH=y
|
|||
CONFIG_UBIBLACKLIST=""
|
||||
CONFIG_UBIWHITELIST=""
|
||||
CONFIG_UBIVIDOFFSET=0
|
||||
# Add UBIVOL_RDIFFHANDLER support
|
||||
CONFIG_UBIVOL_RDIFFHANDLER=y
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ RDEPENDS:${PN} += "libgcc"
|
|||
SRC_URI += " \
|
||||
file://0001-Makefile-change-Makefile-to-build-swupdate-library-s.patch \
|
||||
file://0002-config-add-on-the-fly-build-configuration-variable.patch \
|
||||
file://0003-handlers-rdiff-handler-for-applying-librsync-s-rdiff.patch \
|
||||
${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'file://systemd.cfg', '', d)} \
|
||||
${@bb.utils.contains('STORAGE_MEDIA', 'mtd', 'file://mtd.cfg', '', d)} \
|
||||
${@oe.utils.conditional('TRUSTFENCE_SIGN', '1', 'file://signed_images.cfg', '', d)} \
|
||||
|
|
|
|||
Loading…
Reference in New Issue