From mboxrd@z Thu Jan 1 00:00:00 1970 Delivery-date: Mon, 22 Sep 2025 13:04:58 +0200 Received: from metis.whiteo.stw.pengutronix.de ([2a0a:edc0:2:b01:1d::104]) by lore.white.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1v0eLq-005vgz-02 for lore@lore.pengutronix.de; Mon, 22 Sep 2025 13:04:58 +0200 Received: from localhost ([127.0.0.1] helo=metis.whiteo.stw.pengutronix.de) by metis.whiteo.stw.pengutronix.de with esmtp (Exim 4.92) (envelope-from ) id 1v0eLp-0000T4-GI; Mon, 22 Sep 2025 13:04:57 +0200 Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1v0eLL-0007Jp-Kk; Mon, 22 Sep 2025 13:04:27 +0200 Received: from dude05.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::54]) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1v0eLL-002aDI-1S; Mon, 22 Sep 2025 13:04:27 +0200 Received: from mol by dude05.red.stw.pengutronix.de with local (Exim 4.98.2) (envelope-from ) id 1v0eLL-00000008iJx-1cny; Mon, 22 Sep 2025 13:04:27 +0200 From: Michael Olbrich To: ptxdist@pengutronix.de Date: Mon, 22 Sep 2025 13:04:27 +0200 Message-ID: <20250922110427.2076983-1-m.olbrich@pengutronix.de> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20250919100643.199174-5-ada@thorsis.com> References: <20250919100643.199174-5-ada@thorsis.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Subject: Re: [ptxdist] [APPLIED] libubootenv: Import some fixes from master X-BeenThere: ptxdist@pengutronix.de X-Mailman-Version: 2.1.29 Precedence: list List-Id: PTXdist Development Mailing List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: ptxdist@pengutronix.de Cc: Alexander Dahl Sender: "ptxdist" X-SA-Exim-Connect-IP: 127.0.0.1 X-SA-Exim-Mail-From: ptxdist-bounces@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false Thanks, applied as 670e4f1259e813706d2f0d73b8431f69b708a9c7. Michael [sent from post-receive hook] On Mon, 22 Sep 2025 13:04:27 +0200, Alexander Dahl wrote: > Not part of a new libubootenv release yet, but at least the ubi volume > fix is necessary. Otherwise fw_printenv might fail on arm v7a storing > u-boot env in an ubi volume like this: > > root@DistroKit:/mnt/data/tmp fw_printenv > *** buffer overflow detected ***: terminated > Aborted (core dumped) > > Picked the other fixes along the way. > > Signed-off-by: Alexander Dahl > Message-Id: <20250919100643.199174-5-ada@thorsis.com> > Signed-off-by: Michael Olbrich > > diff --git a/patches/libubootenv-0.3.6/0001-Make-libyaml-optional.patch b/patches/libubootenv-0.3.6/0001-Make-libyaml-optional.patch > new file mode 100644 > index 000000000000..631d0f1ef7fb > --- /dev/null > +++ b/patches/libubootenv-0.3.6/0001-Make-libyaml-optional.patch > @@ -0,0 +1,1507 @@ > +From: Stefano Babic > +Date: Tue, 29 Oct 2024 09:23:20 +0100 > +Subject: [PATCH] Make libyaml optional > + > +NewYAML format is required for extended features because the format > +foreseen by U-Boot is very limited. However, some systems due to > +low resources don't want to link to libyaml. Add the option > +NO_YML_SUPPORT to disable YAML configuration file and just use > +fw_env.config in the U-Boot format. > + > +There are no functional changes in this patch - function depending on > +YML are moved in a separate file, and some functions are factorized. > + > +Signed-off-by: Stefano Babic > +--- > + CMakeLists.txt | 6 + > + src/CMakeLists.txt | 9 +- > + src/common.c | 248 +++++++++++++++++++ > + src/common.h | 15 ++ > + src/extended_config.c | 452 ++++++++++++++++++++++++++++++++++ > + src/uboot_env.c | 655 +------------------------------------------------- > + 6 files changed, 735 insertions(+), 650 deletions(-) > + create mode 100644 src/common.c > + create mode 100644 src/common.h > + create mode 100644 src/extended_config.c > + > +diff --git a/CMakeLists.txt b/CMakeLists.txt > +index 3bb93e16b42b..796d7bcffb29 100644 > +--- a/CMakeLists.txt > ++++ b/CMakeLists.txt > +@@ -12,6 +12,8 @@ set(VERSION "0.3.6") > + SET(SOVERSION "0") > + add_definitions(-DVERSION="${VERSION}") > + > ++option(NO_YML_SUPPORT "YML Support") > ++ > + if(DEFAULT_CFG_FILE) > + add_definitions(-DDEFAULT_CFG_FILE="${DEFAULT_CFG_FILE}") > + endif(DEFAULT_CFG_FILE) > +@@ -20,6 +22,10 @@ if(DEFAULT_ENV_FILE) > + add_definitions(-DDEFAULT_ENV_FILE="${DEFAULT_ENV_FILE}") > + endif(DEFAULT_ENV_FILE) > + > ++if(NO_YML_SUPPORT) > ++ add_definitions(-DNO_YAML_SUPPORT) > ++endif(NO_YML_SUPPORT) > ++ > + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") > + > + #set(CMAKE_C_FLAGS_DEBUG "-g") > +diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt > +index 63d48225f83b..c56d0c756528 100644 > +--- a/src/CMakeLists.txt > ++++ b/src/CMakeLists.txt > +@@ -6,6 +6,9 @@ cmake_minimum_required (VERSION 2.6) > + SET(libubootenv_SOURCES > + uboot_env.c > + uboot_mtd.c > ++ extended_config.c > ++ common.c > ++ common.h > + uboot_private.h > + ) > + > +@@ -22,7 +25,11 @@ SET_TARGET_PROPERTIES(ubootenv PROPERTIES VERSION ${VERSION} SOVERSION ${SOVERSI > + ADD_LIBRARY(ubootenv_static STATIC ${libubootenv_SOURCES} ${include_HEADERS}) > + SET_TARGET_PROPERTIES(ubootenv_static PROPERTIES OUTPUT_NAME ubootenv) > + add_executable(fw_printenv fw_printenv.c) > +-target_link_libraries(ubootenv z yaml) > ++target_link_libraries(ubootenv z) > ++if (NOT NO_YML_SUPPORT) > ++target_link_libraries(ubootenv yaml) > ++endif(NOT NO_YML_SUPPORT) > ++ > + target_link_libraries(fw_printenv ubootenv) > + add_custom_target(fw_setenv ALL ${CMAKE_COMMAND} -E create_symlink fw_printenv fw_setenv) > + > +diff --git a/src/common.c b/src/common.c > +new file mode 100644 > +index 000000000000..2cbde93a7140 > +--- /dev/null > ++++ b/src/common.c > +@@ -0,0 +1,248 @@ > ++/* > ++ * (C) Copyright 2024 > ++ * Stefano Babic, > ++ * > ++ * SPDX-License-Identifier: LGPL-2.1-or-later > ++ */ > ++ > ++#define _GNU_SOURCE > ++ > ++#include > ++#include > ++#include > ++#include > ++#include > ++#include > ++#include > ++#include > ++#include > ++#include > ++#include > ++#include > ++#ifdef __FreeBSD__ > ++#include > ++#define BLKGETSIZE64 DIOCGMEDIASIZE > ++#else > ++#include > ++#endif > ++ > ++#include "uboot_private.h" > ++#include "common.h" > ++ > ++static enum device_type get_device_type(char *device) > ++{ > ++ enum device_type type = DEVICE_NONE; > ++ > ++ if (!strncmp(device, DEVICE_MTD_NAME, strlen(DEVICE_MTD_NAME))) > ++ if (strchr(device, DEVNAME_SEPARATOR)) { > ++ type = DEVICE_UBI; > ++ } else { > ++ type = DEVICE_MTD; > ++ } > ++ else if (!strncmp(device, DEVICE_UBI_NAME, strlen(DEVICE_UBI_NAME))) > ++ type = DEVICE_UBI; > ++ else if (strlen(device) > 0) > ++ type = DEVICE_FILE; > ++ > ++ return type; > ++} > ++ > ++int normalize_device_path(char *path, struct uboot_flash_env *dev) > ++{ > ++ char *sep = NULL, *normalized = NULL; > ++ size_t normalized_len = 0, volume_len = 0, output_len = 0; > ++ > ++ /* > ++ * if volume name is present, split into device path and volume > ++ * since only the device path needs normalized > ++ */ > ++ sep = strchr(path, DEVNAME_SEPARATOR); > ++ if (sep) > ++ { > ++ volume_len = strlen(sep); > ++ *sep = '\0'; > ++ } > ++ > ++ if ((normalized = realpath(path, NULL)) == NULL) > ++ { > ++ /* device file didn't exist */ > ++ return -EINVAL; > ++ } > ++ > ++ normalized_len = strlen(normalized); > ++ output_len = sizeof(dev->devname) - 1; /* leave room for null */ > ++ if ((normalized_len + volume_len) > output_len) > ++ { > ++ /* full name is too long to fit */ > ++ free(normalized); > ++ return -EINVAL; > ++ } > ++ > ++ /* > ++ * save normalized path to device file, > ++ * and possibly append separator char & volume name > ++ */ > ++ memset(dev->devname, 0, sizeof(dev->devname)); > ++ strncpy(dev->devname, normalized, output_len); > ++ free(normalized); > ++ > ++ if (sep) > ++ { > ++ *sep = DEVNAME_SEPARATOR; > ++ strncpy(dev->devname + normalized_len, sep, output_len - normalized_len); > ++ } > ++ > ++ return 0; > ++} > ++ > ++void set_var_access_type(struct var_entry *entry, const char *pvarflags) > ++{ > ++ if (entry) { > ++ for (int i = 0; i < strlen(pvarflags); i++) { > ++ switch (pvarflags[i]) { > ++ case 's': > ++ entry->type = TYPE_ATTR_STRING; > ++ break; > ++ case 'd': > ++ entry->type = TYPE_ATTR_DECIMAL; > ++ break; > ++ case 'x': > ++ entry->type = TYPE_ATTR_HEX; > ++ break; > ++ case 'b': > ++ entry->type = TYPE_ATTR_BOOL; > ++ break; > ++ case 'i': > ++ entry->type = TYPE_ATTR_IP; > ++ break; > ++ case 'm': > ++ entry->type = TYPE_ATTR_MAC; > ++ break; > ++ case 'a': > ++ entry->access = ACCESS_ATTR_ANY; > ++ break; > ++ case 'r': > ++ entry->access = ACCESS_ATTR_READ_ONLY; > ++ break; > ++ case 'o': > ++ entry->access = ACCESS_ATTR_WRITE_ONCE; > ++ break; > ++ case 'c': > ++ entry->access = ACCESS_ATTR_CHANGE_DEFAULT; > ++ break; > ++ default: /* ignore it */ > ++ break; > ++ } > ++ } > ++ } > ++} > ++ > ++struct var_entry *create_var_entry(const char *name) > ++{ > ++ struct var_entry *entry; > ++ > ++ entry = (struct var_entry *)calloc(1, sizeof(*entry)); > ++ if (!entry) > ++ return NULL; > ++ entry->name = strdup(name); > ++ if (!entry->name) { > ++ free(entry); > ++ return NULL; > ++ } > ++ > ++ return entry; > ++} > ++ > ++bool check_compatible_devices(struct uboot_ctx *ctx) > ++{ > ++ if (!ctx->redundant) > ++ return true; > ++ > ++ if (ctx->envdevs[0].mtdinfo.type != ctx->envdevs[1].mtdinfo.type) > ++ return false; > ++ if (ctx->envdevs[0].flagstype != ctx->envdevs[1].flagstype) > ++ return false; > ++ if (ctx->envdevs[0].envsize != ctx->envdevs[1].envsize) > ++ return false; > ++ > ++ return true; > ++} > ++ > ++int check_env_device(struct uboot_flash_env *dev) > ++{ > ++ int fd, ret; > ++ struct stat st; > ++ > ++ dev->device_type = get_device_type(dev->devname); > ++ if (dev->device_type == DEVICE_NONE) > ++ return -EBADF; > ++ > ++ if (dev->device_type == DEVICE_UBI) { > ++ ret = libubootenv_ubi_update_name(dev); > ++ if (ret) > ++ return ret; > ++ } > ++ > ++ ret = stat(dev->devname, &st); > ++ if (ret < 0) > ++ return -EBADF; > ++ fd = open(dev->devname, O_RDONLY); > ++ if (fd < 0) > ++ return -EBADF; > ++ > ++ if (S_ISCHR(st.st_mode)) { > ++ if (dev->device_type == DEVICE_MTD) { > ++ ret = libubootenv_mtdgetinfo(fd, dev); > ++ if (ret < 0 || (dev->mtdinfo.type != MTD_NORFLASH && > ++ dev->mtdinfo.type != MTD_NANDFLASH)) { > ++ close(fd); > ++ return -EBADF; > ++ } > ++ if (dev->sectorsize == 0) { > ++ dev->sectorsize = dev->mtdinfo.erasesize; > ++ } > ++ } > ++ } > ++ > ++ switch (dev->device_type) { > ++ case DEVICE_FILE: > ++ dev->flagstype = FLAGS_INCREMENTAL; > ++ break; > ++ case DEVICE_MTD: > ++ switch (dev->mtdinfo.type) { > ++ case MTD_NORFLASH: > ++ dev->flagstype = FLAGS_BOOLEAN; > ++ break; > ++ case MTD_NANDFLASH: > ++ dev->flagstype = FLAGS_INCREMENTAL; > ++ }; > ++ break; > ++ case DEVICE_UBI: > ++ dev->flagstype = FLAGS_INCREMENTAL; > ++ break; > ++ default: > ++ close(fd); > ++ return -EBADF; > ++ }; > ++ > ++ /* > ++ * Check for negative offsets, treat it as backwards offset > ++ * from the end of the block device > ++ */ > ++ if (dev->offset < 0) { > ++ uint64_t blkdevsize; > ++ int rc; > ++ > ++ rc = ioctl(fd, BLKGETSIZE64, &blkdevsize); > ++ if (rc < 0) { > ++ close(fd); > ++ return -EINVAL; > ++ } > ++ > ++ dev->offset += blkdevsize; > ++ } > ++ > ++ close(fd); > ++ > ++ return 0; > ++} > +diff --git a/src/common.h b/src/common.h > +new file mode 100644 > +index 000000000000..adacd4896741 > +--- /dev/null > ++++ b/src/common.h > +@@ -0,0 +1,15 @@ > ++/* > ++ * (C) Copyright 2024 > ++ * Stefano Babic, > ++ * > ++ * SPDX-License-Identifier: LGPL-2.1-or-later > ++ */ > ++ > ++ > ++#include "uboot_private.h" > ++ > ++struct var_entry *create_var_entry(const char *name); > ++void set_var_access_type(struct var_entry *entry, const char *pvarflags); > ++int normalize_device_path(char *path, struct uboot_flash_env *dev); > ++int check_env_device(struct uboot_flash_env *dev); > ++bool check_compatible_devices(struct uboot_ctx *ctx); > +diff --git a/src/extended_config.c b/src/extended_config.c > +new file mode 100644 > +index 000000000000..ec814f484efd > +--- /dev/null > ++++ b/src/extended_config.c > +@@ -0,0 +1,452 @@ > ++/* > ++ * (C) Copyright 2024 > ++ * Stefano Babic, > ++ * > ++ * SPDX-License-Identifier: LGPL-2.1-or-later > ++ */ > ++ > ++/** > ++ * @file extended_config.c > ++ * > ++ * @brief Implement the extended config file YAML > ++ * > ++ */ > ++#define _GNU_SOURCE > ++ > ++#if !defined(NO_YAML_SUPPORT) > ++#include > ++#include > ++#include > ++#include > ++#include > ++#include > ++#include > ++ > ++#include "uboot_private.h" > ++#include "common.h" > ++ > ++/* yaml_* functions return 1 on success and 0 on failure. */ > ++enum yaml_status { > ++ SUCCESS = 0, > ++ FAILURE = 1 > ++}; > ++ > ++enum yaml_state { > ++ STATE_START, /* start state */ > ++ STATE_STREAM, /* start/end stream */ > ++ STATE_DOCUMENT, /* start/end document */ > ++ STATE_SECTION, /* top level */ > ++ > ++ STATE_NAMESPACE, /* Init Configuration Namespace */ > ++ STATE_NAMESPACE_FIELDS, /* namespace key list */ > ++ STATE_NKEY, /* Check key names */ > ++ STATE_NSIZE, /* Size key-value pair */ > ++ STATE_NLOCKFILE, /* Lockfile key-value pair */ > ++ STATE_DEVVALUES, /* Devices key names */ > ++ STATE_WRITELIST, /* List with vars that are accepted by write > ++ * if list is missing, all vars are accepted > ++ * var is in the format name:flags, see U-Boot > ++ * documentation > ++ */ > ++ > ++ STATE_NPATH, > ++ STATE_NOFFSET, > ++ STATE_NSECTORSIZE, > ++ STATE_NUNLOCK, > ++ STATE_STOP /* end state */ > ++}; > ++ > ++typedef enum yaml_parse_error_e { > ++ YAML_UNEXPECTED_STATE, > ++ YAML_UNEXPECTED_KEY, > ++ YAML_BAD_DEVICE, > ++ YAML_BAD_DEVNAME, > ++ YAML_BAD_VARLIST, > ++ YAML_DUPLICATE_VARLIST, > ++ YAML_OOM, > ++} yaml_parse_error_type_t; > ++ > ++struct parser_state { > ++ enum yaml_state state; /* The current parse state */ > ++ struct uboot_ctx *ctxsets; /* Array of vars set ctx */ > ++ struct uboot_ctx *ctx; /* Current ctx in parsing */ > ++ unsigned int nelem; /* Number of elemets in ctxsets */ > ++ unsigned int cdev; /* current device in parsing */ > ++ yaml_parse_error_type_t error; /* error causing parser to stop */ > ++ yaml_event_type_t event_type; /* event type causing error */ > ++}; > ++ > ++static int consume_event(struct parser_state *s, yaml_event_t *event) > ++{ > ++ char *value; > ++ struct uboot_flash_env *dev; > ++ struct uboot_ctx *newctx; > ++ int cdev; > ++ > ++ switch (s->state) { > ++ case STATE_START: > ++ switch (event->type) { > ++ case YAML_STREAM_START_EVENT: > ++ s->state = STATE_STREAM; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_STREAM: > ++ switch (event->type) { > ++ case YAML_DOCUMENT_START_EVENT: > ++ s->state = STATE_DOCUMENT; > ++ break; > ++ case YAML_STREAM_END_EVENT: > ++ s->state = STATE_STOP; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_DOCUMENT: > ++ switch (event->type) { > ++ case YAML_MAPPING_START_EVENT: > ++ s->state = STATE_SECTION; > ++ break; > ++ case YAML_DOCUMENT_END_EVENT: > ++ s->state = STATE_STREAM; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_SECTION: > ++ switch (event->type) { > ++ case YAML_SCALAR_EVENT: > ++ value = (char *)event->data.scalar.value; > ++ newctx = calloc (s->nelem + 1, sizeof(*newctx)); > ++ for (int i = 0; i < s->nelem; i++) { > ++ newctx[i] = s->ctxsets[i]; > ++ } > ++ if (s->ctxsets) free(s->ctxsets); > ++ s->ctxsets = newctx; > ++ s->ctx = &newctx[s->nelem]; > ++ s->ctx->name = strdup(value); > ++ s->nelem++; > ++ s->state = STATE_NAMESPACE; > ++ break; > ++ case YAML_MAPPING_END_EVENT: > ++ s->state = STATE_DOCUMENT; > ++ break; > ++ case YAML_DOCUMENT_END_EVENT: > ++ s->state = STATE_STREAM; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_NAMESPACE: > ++ switch (event->type) { > ++ case YAML_MAPPING_START_EVENT: > ++ s->state = STATE_NAMESPACE_FIELDS; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_NAMESPACE_FIELDS: > ++ switch (event->type) { > ++ case YAML_SCALAR_EVENT: > ++ value = (char *)event->data.scalar.value; > ++ if (!strcmp(value, "size")) { > ++ s->state = STATE_NSIZE; > ++ } else if (!strcmp(value, "lockfile")) { > ++ s->state = STATE_NLOCKFILE; > ++ } else if (!strcmp(value, "devices")) { > ++ s->state = STATE_DEVVALUES; > ++ s->cdev = 0; > ++ } else if (!strcmp(value, "writelist")) { > ++ s->state = STATE_WRITELIST; > ++ } else { > ++ s->error = YAML_UNEXPECTED_KEY; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ case YAML_MAPPING_END_EVENT: > ++ s->state = STATE_SECTION; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_NSIZE: > ++ switch (event->type) { > ++ case YAML_SCALAR_EVENT: > ++ value = (char *)event->data.scalar.value; > ++ errno = 0; > ++ s->ctx->size = strtoull(value, NULL, 0); > ++ s->state = STATE_NAMESPACE_FIELDS; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_NLOCKFILE: > ++ switch (event->type) { > ++ case YAML_SCALAR_EVENT: > ++ value = (char *)event->data.scalar.value; > ++ s->ctx->lockfile = strdup(value); > ++ s->state = STATE_NAMESPACE_FIELDS; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_DEVVALUES: > ++ switch (event->type) { > ++ case YAML_MAPPING_START_EVENT: > ++ case YAML_SEQUENCE_START_EVENT: > ++ break; > ++ case YAML_MAPPING_END_EVENT: > ++ dev = &s->ctx->envdevs[s->cdev]; > ++ if (check_env_device(dev) < 0) { > ++ s->error = YAML_BAD_DEVICE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ s->cdev++; > ++ break; > ++ case YAML_SEQUENCE_END_EVENT: > ++ s->state = STATE_NAMESPACE_FIELDS; > ++ break; > ++ case YAML_SCALAR_EVENT: > ++ value = (char *)event->data.scalar.value; > ++ if (s->cdev) > ++ s->ctx->redundant = true; > ++ if (!strcmp(value, "path")) { > ++ s->state = STATE_NPATH; > ++ } else if (!strcmp(value, "offset")) { > ++ s->state = STATE_NOFFSET; > ++ } else if (!strcmp(value, "sectorsize")) { > ++ s->state = STATE_NSECTORSIZE; > ++ } else if (!strcmp(value, "disablelock")) { > ++ s->state = STATE_NUNLOCK; > ++ } else { > ++ s->error = YAML_UNEXPECTED_KEY; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_WRITELIST: > ++ switch (event->type) { > ++ > ++ char *varflag, *name; > ++ struct var_entry *entry; > ++ > ++ case YAML_MAPPING_START_EVENT: > ++ case YAML_SEQUENCE_START_EVENT: > ++ break; > ++ case YAML_MAPPING_END_EVENT: > ++ break; > ++ case YAML_SEQUENCE_END_EVENT: > ++ s->state = STATE_NAMESPACE_FIELDS; > ++ break; > ++ case YAML_SCALAR_EVENT: > ++ value = (char *)event->data.scalar.value; > ++ > ++ /* > ++ * Format is name:flags, split it into two values > ++ */ > ++ varflag = strchr(value, ':'); > ++ if (!varflag || varflag > value + (strlen(value) - 1)) { > ++ s->error = YAML_BAD_VARLIST; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ *varflag++ = '\0'; > ++ > ++ /* > ++ * Check there is not yet an entry for this variable > ++ */ > ++ LIST_FOREACH(entry, &s->ctx->writevarlist, next) { > ++ if (strcmp(entry->name, value) == 0) { > ++ s->error = YAML_DUPLICATE_VARLIST; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ } > ++ > ++ /* > ++ * Insert variable with its configuration into the list > ++ * of modifiable vars > ++ */ > ++ entry = create_var_entry(value); > ++ if (!entry) { > ++ s->error = YAML_OOM; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ set_var_access_type(entry, varflag); > ++ LIST_INSERT_HEAD(&s->ctx->writevarlist, entry, next); > ++ > ++#if !defined(NDEBUG) > ++ fprintf(stdout, "Writelist: %s flags %s\n", value, varflag); > ++#endif > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_NPATH: > ++ switch (event->type) { > ++ case YAML_SCALAR_EVENT: > ++ dev = &s->ctx->envdevs[s->cdev]; > ++ value = (char *)event->data.scalar.value; > ++ if (normalize_device_path(value, dev) < 0) { > ++ s->error = YAML_BAD_DEVNAME; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ dev->envsize = s->ctx->size; > ++ s->state = STATE_DEVVALUES; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_NOFFSET: > ++ switch (event->type) { > ++ case YAML_SCALAR_EVENT: > ++ dev = &s->ctx->envdevs[s->cdev]; > ++ value = (char *)event->data.scalar.value; > ++ dev->offset = strtoull(value, NULL, 0); > ++ s->state = STATE_DEVVALUES; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_NSECTORSIZE: > ++ switch (event->type) { > ++ case YAML_SCALAR_EVENT: > ++ dev = &s->ctx->envdevs[s->cdev]; > ++ value = (char *)event->data.scalar.value; > ++ dev->sectorsize = strtoull(value, NULL, 0); > ++ s->state = STATE_DEVVALUES; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_NUNLOCK: > ++ switch (event->type) { > ++ case YAML_SCALAR_EVENT: > ++ dev = &s->ctx->envdevs[s->cdev]; > ++ value = (char *)event->data.scalar.value; > ++ if (!strcmp(value, "yes")) > ++ dev->disable_mtd_lock = 1; > ++ s->state = STATE_DEVVALUES; > ++ break; > ++ default: > ++ s->error = YAML_UNEXPECTED_STATE; > ++ s->event_type = event->type; > ++ return FAILURE; > ++ } > ++ break; > ++ > ++ case STATE_STOP: > ++ break; > ++ } > ++ return SUCCESS; > ++} > ++ > ++int parse_yaml_config(struct uboot_ctx **ctxlist, FILE *fp) > ++{ > ++ yaml_parser_t parser; > ++ yaml_event_t event; > ++ enum yaml_status status; > ++ struct parser_state state; > ++ struct uboot_ctx *ctx; > ++ > ++ if (!yaml_parser_initialize(&parser)) > ++ return -ENOMEM; > ++ > ++ /* Set input file */ > ++ yaml_parser_set_input_file(&parser, fp); > ++ memset(&state, 0, sizeof(state)); > ++ state.state = STATE_START; > ++ do { > ++ if (!yaml_parser_parse(&parser, &event)) { > ++ status = FAILURE; > ++ goto cleanup; > ++ } > ++ status = consume_event(&state, &event); > ++ yaml_event_delete(&event); > ++ if (status == FAILURE) { > ++ goto cleanup; > ++ } > ++ } while (state.state != STATE_STOP); > ++ > ++ state.ctxsets[0].nelem = state.nelem; > ++ > ++ for (int i = 0; i < state.nelem; i++) { > ++ ctx = &state.ctxsets[i]; > ++ ctx->ctxlist = &state.ctxsets[0]; > ++ if (ctx->redundant && !check_compatible_devices(ctx)) { > ++ status = FAILURE; > ++ break; > ++ } > ++ } > ++ > ++ > ++cleanup: > ++ yaml_parser_delete(&parser); > ++ if (status == FAILURE) { > ++ if (state.ctxsets) free (state.ctxsets); > ++ state.ctxsets = NULL; > ++ } > ++ *ctxlist = state.ctxsets; > ++ return status; > ++} > ++#endif > +diff --git a/src/uboot_env.c b/src/uboot_env.c > +index 0b4f9f4d0458..d8b93dac7479 100644 > +--- a/src/uboot_env.c > ++++ b/src/uboot_env.c > +@@ -21,12 +21,6 @@ > + #include > + #include > + #include > +-#ifdef __FreeBSD__ > +-#include > +-#define BLKGETSIZE64 DIOCGMEDIASIZE > +-#else > +-#include > +-#endif > + #include > + #include > + #include > +@@ -38,60 +32,15 @@ > + #include > + #include > + #include > +-#include > + > + #include "uboot_private.h" > ++#include "common.h" > + > +-/* yaml_* functions return 1 on success and 0 on failure. */ > +-enum yaml_status { > +- SUCCESS = 0, > +- FAILURE = 1 > +-}; > +- > +-enum yaml_state { > +- STATE_START, /* start state */ > +- STATE_STREAM, /* start/end stream */ > +- STATE_DOCUMENT, /* start/end document */ > +- STATE_SECTION, /* top level */ > +- > +- STATE_NAMESPACE, /* Init Configuration Namespace */ > +- STATE_NAMESPACE_FIELDS, /* namespace key list */ > +- STATE_NKEY, /* Check key names */ > +- STATE_NSIZE, /* Size key-value pair */ > +- STATE_NLOCKFILE, /* Lockfile key-value pair */ > +- STATE_DEVVALUES, /* Devices key names */ > +- STATE_WRITELIST, /* List with vars that are accepted by write > +- * if list is missing, all vars are accepted > +- * var is in the format name:flags, see U-Boot > +- * documentation > +- */ > +- > +- STATE_NPATH, > +- STATE_NOFFSET, > +- STATE_NSECTORSIZE, > +- STATE_NUNLOCK, > +- STATE_STOP /* end state */ > +-}; > +- > +-typedef enum yaml_parse_error_e { > +- YAML_UNEXPECTED_STATE, > +- YAML_UNEXPECTED_KEY, > +- YAML_BAD_DEVICE, > +- YAML_BAD_DEVNAME, > +- YAML_BAD_VARLIST, > +- YAML_DUPLICATE_VARLIST, > +- YAML_OOM, > +-} yaml_parse_error_type_t; > +- > +-struct parser_state { > +- enum yaml_state state; /* The current parse state */ > +- struct uboot_ctx *ctxsets; /* Array of vars set ctx */ > +- struct uboot_ctx *ctx; /* Current ctx in parsing */ > +- unsigned int nelem; /* Number of elemets in ctxsets */ > +- unsigned int cdev; /* current device in parsing */ > +- yaml_parse_error_type_t error; /* error causing parser to stop */ > +- yaml_event_type_t event_type; /* event type causing error */ > +-}; > ++#if defined(NO_YAML_SUPPORT) > ++#define parse_yaml_config(ctx,fp) -1 > ++#else > ++extern int parse_yaml_config(struct uboot_ctx **ctxlist, FILE *fp); > ++#endif > + > + #define FREE_ENTRY do { \ > + free(entry->name); \ > +@@ -152,64 +101,6 @@ static char attr_tostring(type_attribute a) > + return 's'; > + } > + > +-static void set_var_access_type(struct var_entry *entry, const char *pvarflags) > +-{ > +- if (entry) { > +- for (int i = 0; i < strlen(pvarflags); i++) { > +- switch (pvarflags[i]) { > +- case 's': > +- entry->type = TYPE_ATTR_STRING; > +- break; > +- case 'd': > +- entry->type = TYPE_ATTR_DECIMAL; > +- break; > +- case 'x': > +- entry->type = TYPE_ATTR_HEX; > +- break; > +- case 'b': > +- entry->type = TYPE_ATTR_BOOL; > +- break; > +- case 'i': > +- entry->type = TYPE_ATTR_IP; > +- break; > +- case 'm': > +- entry->type = TYPE_ATTR_MAC; > +- break; > +- case 'a': > +- entry->access = ACCESS_ATTR_ANY; > +- break; > +- case 'r': > +- entry->access = ACCESS_ATTR_READ_ONLY; > +- break; > +- case 'o': > +- entry->access = ACCESS_ATTR_WRITE_ONCE; > +- break; > +- case 'c': > +- entry->access = ACCESS_ATTR_CHANGE_DEFAULT; > +- break; > +- default: /* ignore it */ > +- break; > +- } > +- } > +- } > +-} > +- > +-static struct var_entry *create_var_entry(const char *name) > +-{ > +- struct var_entry *entry; > +- > +- entry = (struct var_entry *)calloc(1, sizeof(*entry)); > +- if (!entry) > +- return NULL; > +- entry->name = strdup(name); > +- if (!entry->name) { > +- free(entry); > +- return NULL; > +- } > +- > +- return entry; > +-} > +- > + static char access_tostring(access_attribute a) > + { > + switch(a) { > +@@ -389,166 +280,6 @@ static int __libuboot_set_env(struct uboot_ctx *ctx, const char *varname, const > + return 0; > + } > + > +-static enum device_type get_device_type(char *device) > +-{ > +- enum device_type type = DEVICE_NONE; > +- > +- if (!strncmp(device, DEVICE_MTD_NAME, strlen(DEVICE_MTD_NAME))) > +- if (strchr(device, DEVNAME_SEPARATOR)) { > +- type = DEVICE_UBI; > +- } else { > +- type = DEVICE_MTD; > +- } > +- else if (!strncmp(device, DEVICE_UBI_NAME, strlen(DEVICE_UBI_NAME))) > +- type = DEVICE_UBI; > +- else if (strlen(device) > 0) > +- type = DEVICE_FILE; > +- > +- return type; > +-} > +- > +-static int normalize_device_path(char *path, struct uboot_flash_env *dev) > +-{ > +- char *sep = NULL, *normalized = NULL; > +- size_t normalized_len = 0, volume_len = 0, output_len = 0; > +- > +- /* > +- * if volume name is present, split into device path and volume > +- * since only the device path needs normalized > +- */ > +- sep = strchr(path, DEVNAME_SEPARATOR); > +- if (sep) > +- { > +- volume_len = strlen(sep); > +- *sep = '\0'; > +- } > +- > +- if ((normalized = realpath(path, NULL)) == NULL) > +- { > +- /* device file didn't exist */ > +- return -EINVAL; > +- } > +- > +- normalized_len = strlen(normalized); > +- output_len = sizeof(dev->devname) - 1; /* leave room for null */ > +- if ((normalized_len + volume_len) > output_len) > +- { > +- /* full name is too long to fit */ > +- free(normalized); > +- return -EINVAL; > +- } > +- > +- /* > +- * save normalized path to device file, > +- * and possibly append separator char & volume name > +- */ > +- memset(dev->devname, 0, sizeof(dev->devname)); > +- strncpy(dev->devname, normalized, output_len); > +- free(normalized); > +- > +- if (sep) > +- { > +- *sep = DEVNAME_SEPARATOR; > +- strncpy(dev->devname + normalized_len, sep, output_len - normalized_len); > +- } > +- > +- return 0; > +-} > +- > +-static int check_env_device(struct uboot_flash_env *dev) > +-{ > +- int fd, ret; > +- struct stat st; > +- > +- dev->device_type = get_device_type(dev->devname); > +- if (dev->device_type == DEVICE_NONE) > +- return -EBADF; > +- > +- if (dev->device_type == DEVICE_UBI) { > +- ret = libubootenv_ubi_update_name(dev); > +- if (ret) > +- return ret; > +- } > +- > +- ret = stat(dev->devname, &st); > +- if (ret < 0) > +- return -EBADF; > +- fd = open(dev->devname, O_RDONLY); > +- if (fd < 0) > +- return -EBADF; > +- > +- if (S_ISCHR(st.st_mode)) { > +- if (dev->device_type == DEVICE_MTD) { > +- ret = libubootenv_mtdgetinfo(fd, dev); > +- if (ret < 0 || (dev->mtdinfo.type != MTD_NORFLASH && > +- dev->mtdinfo.type != MTD_NANDFLASH)) { > +- close(fd); > +- return -EBADF; > +- } > +- if (dev->sectorsize == 0) { > +- dev->sectorsize = dev->mtdinfo.erasesize; > +- } > +- } > +- } > +- > +- switch (dev->device_type) { > +- case DEVICE_FILE: > +- dev->flagstype = FLAGS_INCREMENTAL; > +- break; > +- case DEVICE_MTD: > +- switch (dev->mtdinfo.type) { > +- case MTD_NORFLASH: > +- dev->flagstype = FLAGS_BOOLEAN; > +- break; > +- case MTD_NANDFLASH: > +- dev->flagstype = FLAGS_INCREMENTAL; > +- }; > +- break; > +- case DEVICE_UBI: > +- dev->flagstype = FLAGS_INCREMENTAL; > +- break; > +- default: > +- close(fd); > +- return -EBADF; > +- }; > +- > +- /* > +- * Check for negative offsets, treat it as backwards offset > +- * from the end of the block device > +- */ > +- if (dev->offset < 0) { > +- uint64_t blkdevsize; > +- int rc; > +- > +- rc = ioctl(fd, BLKGETSIZE64, &blkdevsize); > +- if (rc < 0) { > +- close(fd); > +- return -EINVAL; > +- } > +- > +- dev->offset += blkdevsize; > +- } > +- > +- close(fd); > +- > +- return 0; > +-} > +- > +-static bool check_compatible_devices(struct uboot_ctx *ctx) > +-{ > +- if (!ctx->redundant) > +- return true; > +- > +- if (ctx->envdevs[0].mtdinfo.type != ctx->envdevs[1].mtdinfo.type) > +- return false; > +- if (ctx->envdevs[0].flagstype != ctx->envdevs[1].flagstype) > +- return false; > +- if (ctx->envdevs[0].envsize != ctx->envdevs[1].envsize) > +- return false; > +- > +- return true; > +-} > +- > + static int fileread(struct uboot_flash_env *dev, void *data) > + { > + int ret = 0; > +@@ -1035,380 +766,6 @@ static int libuboot_load(struct uboot_ctx *ctx) > + return ctx->valid ? 0 : -ENODATA; > + } > + > +-static int consume_event(struct parser_state *s, yaml_event_t *event) > +-{ > +- char *value; > +- struct uboot_flash_env *dev; > +- struct uboot_ctx *newctx; > +- int cdev; > +- > +- switch (s->state) { > +- case STATE_START: > +- switch (event->type) { > +- case YAML_STREAM_START_EVENT: > +- s->state = STATE_STREAM; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_STREAM: > +- switch (event->type) { > +- case YAML_DOCUMENT_START_EVENT: > +- s->state = STATE_DOCUMENT; > +- break; > +- case YAML_STREAM_END_EVENT: > +- s->state = STATE_STOP; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_DOCUMENT: > +- switch (event->type) { > +- case YAML_MAPPING_START_EVENT: > +- s->state = STATE_SECTION; > +- break; > +- case YAML_DOCUMENT_END_EVENT: > +- s->state = STATE_STREAM; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_SECTION: > +- switch (event->type) { > +- case YAML_SCALAR_EVENT: > +- value = (char *)event->data.scalar.value; > +- newctx = calloc (s->nelem + 1, sizeof(*newctx)); > +- for (int i = 0; i < s->nelem; i++) { > +- newctx[i] = s->ctxsets[i]; > +- } > +- if (s->ctxsets) free(s->ctxsets); > +- s->ctxsets = newctx; > +- s->ctx = &newctx[s->nelem]; > +- s->ctx->name = strdup(value); > +- s->nelem++; > +- s->state = STATE_NAMESPACE; > +- break; > +- case YAML_MAPPING_END_EVENT: > +- s->state = STATE_DOCUMENT; > +- break; > +- case YAML_DOCUMENT_END_EVENT: > +- s->state = STATE_STREAM; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_NAMESPACE: > +- switch (event->type) { > +- case YAML_MAPPING_START_EVENT: > +- s->state = STATE_NAMESPACE_FIELDS; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_NAMESPACE_FIELDS: > +- switch (event->type) { > +- case YAML_SCALAR_EVENT: > +- value = (char *)event->data.scalar.value; > +- if (!strcmp(value, "size")) { > +- s->state = STATE_NSIZE; > +- } else if (!strcmp(value, "lockfile")) { > +- s->state = STATE_NLOCKFILE; > +- } else if (!strcmp(value, "devices")) { > +- s->state = STATE_DEVVALUES; > +- s->cdev = 0; > +- } else if (!strcmp(value, "writelist")) { > +- s->state = STATE_WRITELIST; > +- } else { > +- s->error = YAML_UNEXPECTED_KEY; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- case YAML_MAPPING_END_EVENT: > +- s->state = STATE_SECTION; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_NSIZE: > +- switch (event->type) { > +- case YAML_SCALAR_EVENT: > +- value = (char *)event->data.scalar.value; > +- errno = 0; > +- s->ctx->size = strtoull(value, NULL, 0); > +- s->state = STATE_NAMESPACE_FIELDS; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_NLOCKFILE: > +- switch (event->type) { > +- case YAML_SCALAR_EVENT: > +- value = (char *)event->data.scalar.value; > +- s->ctx->lockfile = strdup(value); > +- s->state = STATE_NAMESPACE_FIELDS; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_DEVVALUES: > +- switch (event->type) { > +- case YAML_MAPPING_START_EVENT: > +- case YAML_SEQUENCE_START_EVENT: > +- break; > +- case YAML_MAPPING_END_EVENT: > +- dev = &s->ctx->envdevs[s->cdev]; > +- if (check_env_device(dev) < 0) { > +- s->error = YAML_BAD_DEVICE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- s->cdev++; > +- break; > +- case YAML_SEQUENCE_END_EVENT: > +- s->state = STATE_NAMESPACE_FIELDS; > +- break; > +- case YAML_SCALAR_EVENT: > +- value = (char *)event->data.scalar.value; > +- if (s->cdev) > +- s->ctx->redundant = true; > +- if (!strcmp(value, "path")) { > +- s->state = STATE_NPATH; > +- } else if (!strcmp(value, "offset")) { > +- s->state = STATE_NOFFSET; > +- } else if (!strcmp(value, "sectorsize")) { > +- s->state = STATE_NSECTORSIZE; > +- } else if (!strcmp(value, "disablelock")) { > +- s->state = STATE_NUNLOCK; > +- } else { > +- s->error = YAML_UNEXPECTED_KEY; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_WRITELIST: > +- switch (event->type) { > +- > +- char *varflag, *name; > +- struct var_entry *entry; > +- > +- case YAML_MAPPING_START_EVENT: > +- case YAML_SEQUENCE_START_EVENT: > +- break; > +- case YAML_MAPPING_END_EVENT: > +- break; > +- case YAML_SEQUENCE_END_EVENT: > +- s->state = STATE_NAMESPACE_FIELDS; > +- break; > +- case YAML_SCALAR_EVENT: > +- value = (char *)event->data.scalar.value; > +- > +- /* > +- * Format is name:flags, split it into two values > +- */ > +- varflag = strchr(value, ':'); > +- if (!varflag || varflag > value + (strlen(value) - 1)) { > +- s->error = YAML_BAD_VARLIST; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- *varflag++ = '\0'; > +- > +- /* > +- * Check there is not yet an entry for this variable > +- */ > +- LIST_FOREACH(entry, &s->ctx->writevarlist, next) { > +- if (strcmp(entry->name, value) == 0) { > +- s->error = YAML_DUPLICATE_VARLIST; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- } > +- > +- /* > +- * Insert variable with its configuration into the list > +- * of modifiable vars > +- */ > +- entry = create_var_entry(value); > +- if (!entry) { > +- s->error = YAML_OOM; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- set_var_access_type(entry, varflag); > +- LIST_INSERT_HEAD(&s->ctx->writevarlist, entry, next); > +- > +-#if !defined(NDEBUG) > +- fprintf(stdout, "Writelist: %s flags %s\n", value, varflag); > +-#endif > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_NPATH: > +- switch (event->type) { > +- case YAML_SCALAR_EVENT: > +- dev = &s->ctx->envdevs[s->cdev]; > +- value = (char *)event->data.scalar.value; > +- if (normalize_device_path(value, dev) < 0) { > +- s->error = YAML_BAD_DEVNAME; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- dev->envsize = s->ctx->size; > +- s->state = STATE_DEVVALUES; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_NOFFSET: > +- switch (event->type) { > +- case YAML_SCALAR_EVENT: > +- dev = &s->ctx->envdevs[s->cdev]; > +- value = (char *)event->data.scalar.value; > +- dev->offset = strtoull(value, NULL, 0); > +- s->state = STATE_DEVVALUES; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_NSECTORSIZE: > +- switch (event->type) { > +- case YAML_SCALAR_EVENT: > +- dev = &s->ctx->envdevs[s->cdev]; > +- value = (char *)event->data.scalar.value; > +- dev->sectorsize = strtoull(value, NULL, 0); > +- s->state = STATE_DEVVALUES; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_NUNLOCK: > +- switch (event->type) { > +- case YAML_SCALAR_EVENT: > +- dev = &s->ctx->envdevs[s->cdev]; > +- value = (char *)event->data.scalar.value; > +- if (!strcmp(value, "yes")) > +- dev->disable_mtd_lock = 1; > +- s->state = STATE_DEVVALUES; > +- break; > +- default: > +- s->error = YAML_UNEXPECTED_STATE; > +- s->event_type = event->type; > +- return FAILURE; > +- } > +- break; > +- > +- case STATE_STOP: > +- break; > +- } > +- return SUCCESS; > +-} > +- > +-int parse_yaml_config(struct uboot_ctx **ctxlist, FILE *fp) > +-{ > +- yaml_parser_t parser; > +- yaml_event_t event; > +- enum yaml_status status; > +- struct parser_state state; > +- struct uboot_ctx *ctx; > +- > +- if (!yaml_parser_initialize(&parser)) > +- return -ENOMEM; > +- > +- /* Set input file */ > +- yaml_parser_set_input_file(&parser, fp); > +- memset(&state, 0, sizeof(state)); > +- state.state = STATE_START; > +- do { > +- if (!yaml_parser_parse(&parser, &event)) { > +- status = FAILURE; > +- goto cleanup; > +- } > +- status = consume_event(&state, &event); > +- yaml_event_delete(&event); > +- if (status == FAILURE) { > +- goto cleanup; > +- } > +- } while (state.state != STATE_STOP); > +- > +- state.ctxsets[0].nelem = state.nelem; > +- > +- for (int i = 0; i < state.nelem; i++) { > +- ctx = &state.ctxsets[i]; > +- ctx->ctxlist = &state.ctxsets[0]; > +- if (ctx->redundant && !check_compatible_devices(ctx)) { > +- status = FAILURE; > +- break; > +- } > +- } > +- > +- > +-cleanup: > +- yaml_parser_delete(&parser); > +- if (status == FAILURE) { > +- if (state.ctxsets) free (state.ctxsets); > +- state.ctxsets = NULL; > +- } > +- *ctxlist = state.ctxsets; > +- return status; > +-} > +- > + #define LINE_LENGTH 2048 > + int libuboot_load_file(struct uboot_ctx *ctx, const char *filename) > + { > diff --git a/patches/libubootenv-0.3.6/0002-extended_config-fix-segfault-on-empty-config.patch b/patches/libubootenv-0.3.6/0002-extended_config-fix-segfault-on-empty-config.patch > new file mode 100644 > index 000000000000..a4574ff5fef6 > --- /dev/null > +++ b/patches/libubootenv-0.3.6/0002-extended_config-fix-segfault-on-empty-config.patch > @@ -0,0 +1,40 @@ > +From: James Hilliard > +Date: Thu, 24 Apr 2025 19:53:01 -0600 > +Subject: [PATCH] extended_config: fix segfault on empty config > + > +We need to validate that we have nelem and bail out if we don't. > + > +Fixes: > +==245== Invalid write of size 4 > +==245== at 0x4849E0C: parse_yaml_config (uboot_env.c:1390) > +==245== by 0x484A717: libuboot_read_config_ext (uboot_env.c:1484) > +==245== by 0x13F253: bootloader_initialize.constprop.0 (uboot.c:45) > +==245== by 0x13F457: do_env_set (uboot.c:72) > +==245== by 0x12B057: update_transaction_state (stream_interface.c:139) > +==245== by 0x12C367: extract_files (stream_interface.c:297) > +==245== by 0x12C367: network_initializer (stream_interface.c:658) > +==245== by 0x4EA89AF: ??? (in /usr/lib/libc.so.6) > +==245== by 0x4F0989B: ??? (in /usr/lib/libc.so.6) > +==245== Address 0x2e8 is not stack'd, malloc'd or (recently) free'd > + > +Signed-off-by: James Hilliard > +--- > + src/extended_config.c | 5 +++++ > + 1 file changed, 5 insertions(+) > + > +diff --git a/src/extended_config.c b/src/extended_config.c > +index ec814f484efd..45df3ec7a7ca 100644 > +--- a/src/extended_config.c > ++++ b/src/extended_config.c > +@@ -428,6 +428,11 @@ int parse_yaml_config(struct uboot_ctx **ctxlist, FILE *fp) > + } > + } while (state.state != STATE_STOP); > + > ++ if (state.nelem == 0) { > ++ status = FAILURE; > ++ goto cleanup; > ++ } > ++ > + state.ctxsets[0].nelem = state.nelem; > + > + for (int i = 0; i < state.nelem; i++) { > diff --git a/patches/libubootenv-0.3.6/0003-BUG-Fix-warning-when-copying-UBI-volume.patch b/patches/libubootenv-0.3.6/0003-BUG-Fix-warning-when-copying-UBI-volume.patch > new file mode 100644 > index 000000000000..1ead8954860b > --- /dev/null > +++ b/patches/libubootenv-0.3.6/0003-BUG-Fix-warning-when-copying-UBI-volume.patch > @@ -0,0 +1,35 @@ > +From: Stefano Babic > +Date: Tue, 29 Apr 2025 13:49:15 +0200 > +Subject: [PATCH] BUG: Fix warning when copying UBI volume > +MIME-Version: 1.0 > +Content-Type: text/plain; charset=UTF-8 > +Content-Transfer-Encoding: 8bit > + > +Fix: > + > +/home/stefano/SWUpdate/libubootenv/src/uboot_mtd.c: In function ‘libubootenv_ubi_update_name’: > +/home/stefano/SWUpdate/libubootenv/src/uboot_mtd.c:283:17: warning: ‘memset’ writing 256 bytes into a region of size 236 overflows the destination [-Wstringop-overflow=] > + 283 | memset(volume, 0, DEVNAME_MAX_LENGTH); > + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > +/home/stefano/SWUpdate/libubootenv/src/uboot_mtd.c:223:14: note: destination object ‘volume’ of size 236 > + > +This can lead to overwrite the buffer for the volumes. > + > +Signed-off-by: Stefano Babic > +--- > + src/uboot_mtd.c | 2 +- > + 1 file changed, 1 insertion(+), 1 deletion(-) > + > +diff --git a/src/uboot_mtd.c b/src/uboot_mtd.c > +index 81376e4ddc09..24e817d88944 100644 > +--- a/src/uboot_mtd.c > ++++ b/src/uboot_mtd.c > +@@ -280,7 +280,7 @@ int libubootenv_ubi_update_name(struct uboot_flash_env *dev) > + memset(device, 0, DEVNAME_MAX_LENGTH); > + memcpy(device, dev->devname, sep - dev->devname); > + > +- memset(volume, 0, DEVNAME_MAX_LENGTH); > ++ memset(volume, 0, VOLNAME_MAX_LENGTH); > + sscanf(sep + 1, "%s", &volume[0]); > + > + dev_id = ubi_get_dev_id(device); > diff --git a/patches/libubootenv-0.3.6/0004-libubootenv-fix-segfault-due-to-uninitialized-pointe.patch b/patches/libubootenv-0.3.6/0004-libubootenv-fix-segfault-due-to-uninitialized-pointe.patch > new file mode 100644 > index 000000000000..e7ae92fb8562 > --- /dev/null > +++ b/patches/libubootenv-0.3.6/0004-libubootenv-fix-segfault-due-to-uninitialized-pointe.patch > @@ -0,0 +1,32 @@ > +From: Mohamed-nour Toumi > +Date: Mon, 28 Jul 2025 22:25:35 +0200 > +Subject: [PATCH] libubootenv: fix segfault due to uninitialized pointer in > + config parser > + > +Issue: The issue was introduced in commit c478e8d9, which replaced the use of %ms in sscanf() with a calloc()-based workaround for platforms where %ms is not supported (e.g., FreeBSD). > +However, this change inadvertently introduced a logic flaw: it uses calloc to emulate %ms% in sscanf() but also added 2 free instructions which could lead to free non-allocated memory on tmp when sscanf() > +is used to perform the dynamic allocation. > + > +Fix: Ensure `tmp` is initialized to NULL before each call to sscanf with `%ms` > +in `libuboot_read_config_ext()`. This prevents `free(tmp)` from crashing > +when sscanf fails to allocate memory (e.g., due to malformed config lines). > + > +Fixes segmentation fault observed when running swupdate with a ubootenv config file. > + > +Signed-off-by: Mohamed-nour Toumi > +--- > + src/uboot_env.c | 1 + > + 1 file changed, 1 insertion(+) > + > +diff --git a/src/uboot_env.c b/src/uboot_env.c > +index d8b93dac7479..9641800f4bdc 100644 > +--- a/src/uboot_env.c > ++++ b/src/uboot_env.c > +@@ -871,6 +871,7 @@ int libuboot_read_config_ext(struct uboot_ctx **ctxlist, const char *config) > + tmp, > + #else > + (void)len; > ++ tmp = NULL; > + ret = sscanf(line, "%ms %lli %zx %zx %lx %d", > + &tmp, > + #endif > diff --git a/patches/libubootenv-0.3.6/0005-extended_config.c-Catch-NULL-pointer-for-calloc.patch b/patches/libubootenv-0.3.6/0005-extended_config.c-Catch-NULL-pointer-for-calloc.patch > new file mode 100644 > index 000000000000..9c89141c7a90 > --- /dev/null > +++ b/patches/libubootenv-0.3.6/0005-extended_config.c-Catch-NULL-pointer-for-calloc.patch > @@ -0,0 +1,30 @@ > +From: Steffen Kothe > +Date: Sun, 31 Aug 2025 19:07:56 +0000 > +Subject: [PATCH] extended_config.c: Catch NULL pointer for calloc > + > +calloc is not guaranteed to return a valid pointer to a free memory > +area. > + > +Hence check for possible NULL return and fail immediately. > + > +Addresses possible CWE-690. > + > +Signed-off-by: Steffen Kothe > +Acked-by: Stefano Babic > +--- > + src/extended_config.c | 2 ++ > + 1 file changed, 2 insertions(+) > + > +diff --git a/src/extended_config.c b/src/extended_config.c > +index 45df3ec7a7ca..c3782e1fba94 100644 > +--- a/src/extended_config.c > ++++ b/src/extended_config.c > +@@ -131,6 +131,8 @@ static int consume_event(struct parser_state *s, yaml_event_t *event) > + case YAML_SCALAR_EVENT: > + value = (char *)event->data.scalar.value; > + newctx = calloc (s->nelem + 1, sizeof(*newctx)); > ++ if (newctx == NULL) > ++ return FAILURE; > + for (int i = 0; i < s->nelem; i++) { > + newctx[i] = s->ctxsets[i]; > + } > diff --git a/patches/libubootenv-0.3.6/series b/patches/libubootenv-0.3.6/series > new file mode 100644 > index 000000000000..b9356ee68463 > --- /dev/null > +++ b/patches/libubootenv-0.3.6/series > @@ -0,0 +1,8 @@ > +# generated by git-ptx-patches > +#tag:base --start-number 1 > +0001-Make-libyaml-optional.patch > +0002-extended_config-fix-segfault-on-empty-config.patch > +0003-BUG-Fix-warning-when-copying-UBI-volume.patch > +0004-libubootenv-fix-segfault-due-to-uninitialized-pointe.patch > +0005-extended_config.c-Catch-NULL-pointer-for-calloc.patch > +# 8a759d09e861f42f9d568274b3aeb30b - git-ptx-patches magic > diff --git a/rules/libubootenv.make b/rules/libubootenv.make > index 74bfaaf1965d..d6753cd87081 100644 > --- a/rules/libubootenv.make > +++ b/rules/libubootenv.make > @@ -34,7 +34,8 @@ LIBUBOOTENV_LICENSE_FILES := \ > LIBUBOOTENV_CONF_TOOL := cmake > LIBUBOOTENV_CONF_OPT := \ > $(CROSS_CMAKE_USR) \ > - -DBUILD_DOC=OFF > + -DBUILD_DOC=OFF \ > + -DNO_YML_SUPPORT=OFF > > # ---------------------------------------------------------------------------- > # Target-Install