* [ptxdist] [PATCH v2 0/5] libubootenv: New package to replace target u-boot-tools
@ 2025-09-19 10:06 Alexander Dahl via ptxdist
2025-09-19 10:06 ` [ptxdist] [PATCH v2 1/5] libubootenv: Introduce new package Alexander Dahl via ptxdist
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Alexander Dahl via ptxdist @ 2025-09-19 10:06 UTC (permalink / raw)
To: ptxdist; +Cc: Alexander Dahl
Hello,
some months ago I sent an RFC series for swupdate which contained a new
libubootenv package as dependency. No need for swupdate from my side,
but I have two more use cases for libubootenv now. This is why I took
the two patches from the old series and made a v2 without swupdate.
I just added three new patches now, but if needed those can be squashed
in any way the maintainer thinks would fit better. For now makes it
better comparable to v1 from March 2025.
I'm not sure how to realize the dependency from the rauc package,
though. Currently RAUC_U_BOOT selects (target) U_BOOT_TOOLS. Now we
have two compatible alternatives for the fw_printenv/fw_setenv tools
from two differente packages. Either of these packages would be
sufficient to provide the dependency for RAUC, but how should this be
expressed in the Kconfig menu? (This is a runtime dependency only, it
just works by disabling RAUC_U_BOOT and selecting LIBUBOOTENV_TOOLS
here. As far as I understood, something like this is not possible?
select LIBUBOOTENV_TOOLS || U_BOOT_TOOLS
Background: when porting a new board using the u-boot bootloader to use
rauc, I noticed the kernel does not allow to write to mmcblockXbootX
devices per default. When rauc writes to such devices by itself it
disables the readonly mode through sysfs, and enables the write
protection again after writing. For reading/writing the u-boot
environment however it calls fw_printenv/fw_setenv, which were coming
from u-boot itself and are built in ptxdist by the u-boot-tools package.
Those tools can not write to these mmc boot partitions, but the drop in
replacements from libubootenv can.
The other reason is libubootenv variants of fw_printenv/fw_setenv might
be able to use _one_ fw_env.config for multiple boards, if bootloader
puts same information about u-boot into devicetree's 'chosen' node.
Did not test that yet, though.
v2:
- Reworded some commit messages
- Added three new patches
RFC:
- Link: https://lore.ptxdist.org/ptxdist/mailman.2955.1742547175.466.ptxdist@pengutronix.de/
Alexander Dahl (5):
libubootenv: Introduce new package
libubootenv: Add option for installing tools
libubootenv: Install config file to target
libubootenv: Import some fixes from master
libubootenv: Make yaml support optional
.../0001-Make-libyaml-optional.patch | 1507 +++++++++++++++++
..._config-fix-segfault-on-empty-config.patch | 40 +
...-Fix-warning-when-copying-UBI-volume.patch | 35 +
...segfault-due-to-uninitialized-pointe.patch | 32 +
...nfig.c-Catch-NULL-pointer-for-calloc.patch | 30 +
patches/libubootenv-0.3.6/series | 8 +
rules/libubootenv.in | 35 +
rules/libubootenv.make | 67 +
8 files changed, 1754 insertions(+)
create mode 100644 patches/libubootenv-0.3.6/0001-Make-libyaml-optional.patch
create mode 100644 patches/libubootenv-0.3.6/0002-extended_config-fix-segfault-on-empty-config.patch
create mode 100644 patches/libubootenv-0.3.6/0003-BUG-Fix-warning-when-copying-UBI-volume.patch
create mode 100644 patches/libubootenv-0.3.6/0004-libubootenv-fix-segfault-due-to-uninitialized-pointe.patch
create mode 100644 patches/libubootenv-0.3.6/0005-extended_config.c-Catch-NULL-pointer-for-calloc.patch
create mode 100644 patches/libubootenv-0.3.6/series
create mode 100644 rules/libubootenv.in
create mode 100644 rules/libubootenv.make
base-commit: db4766231f67a702335986c510727f76459f53fc
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ptxdist] [PATCH v2 1/5] libubootenv: Introduce new package
2025-09-19 10:06 [ptxdist] [PATCH v2 0/5] libubootenv: New package to replace target u-boot-tools Alexander Dahl via ptxdist
@ 2025-09-19 10:06 ` Alexander Dahl via ptxdist
2025-09-22 11:04 ` [ptxdist] [APPLIED] " Michael Olbrich
2025-09-19 10:06 ` [ptxdist] [PATCH v2 2/5] libubootenv: Add option for installing tools Alexander Dahl via ptxdist
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Alexander Dahl via ptxdist @ 2025-09-19 10:06 UTC (permalink / raw)
To: ptxdist; +Cc: Alexander Dahl
Useful to replace target u-boot-tools.
Dependency for a potential new swupdate package.
Link: https://github.com/sbabic/libubootenv
Signed-off-by: Alexander Dahl <ada@thorsis.com>
---
Notes:
v2:
- reworded commit message
v1:
- Link: https://lore.ptxdist.org/ptxdist/mailman.2959.1742547175.466.ptxdist@pengutronix.de/
rules/libubootenv.in | 15 +++++++++++
rules/libubootenv.make | 58 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 73 insertions(+)
create mode 100644 rules/libubootenv.in
create mode 100644 rules/libubootenv.make
diff --git a/rules/libubootenv.in b/rules/libubootenv.in
new file mode 100644
index 000000000..70f6d9863
--- /dev/null
+++ b/rules/libubootenv.in
@@ -0,0 +1,15 @@
+## SECTION=system_libraries
+
+config LIBUBOOTENV
+ tristate
+ select HOST_CMAKE
+ select LIBYAML
+ select ZLIB
+ prompt "libubootenv"
+ help
+ Generic library and tools to access and modify U-Boot
+ environment from User Space.
+
+ https://github.com/sbabic/libubootenv
+
+# vim: ft=kconfig noet tw=72 ts=8 sw=8
diff --git a/rules/libubootenv.make b/rules/libubootenv.make
new file mode 100644
index 000000000..d4e678152
--- /dev/null
+++ b/rules/libubootenv.make
@@ -0,0 +1,58 @@
+# -*-makefile-*-
+#
+# Copyright (C) 2025 by Alexander Dahl <ada@thorsis.com>
+#
+# For further information about the PTXdist project and license conditions
+# see the README file.
+#
+
+#
+# We provide this package
+#
+PACKAGES-$(PTXCONF_LIBUBOOTENV) += libubootenv
+
+#
+# Paths and names
+#
+LIBUBOOTENV_VERSION := 0.3.6
+LIBUBOOTENV_MD5 := 7d6b623e8da435cf36e7fcd419a03e43
+LIBUBOOTENV := libubootenv-$(LIBUBOOTENV_VERSION)
+LIBUBOOTENV_SUFFIX := tar.gz
+LIBUBOOTENV_URL := https://github.com/sbabic/libubootenv/archive/refs/tags/v$(LIBUBOOTENV_VERSION).$(LIBUBOOTENV_SUFFIX)
+LIBUBOOTENV_SOURCE := $(SRCDIR)/$(LIBUBOOTENV).$(LIBUBOOTENV_SUFFIX)
+LIBUBOOTENV_DIR := $(BUILDDIR)/$(LIBUBOOTENV)
+LIBUBOOTENV_LICENSE := MIT AND CC0-1.0 AND LGPL-2.1-or-later
+LIBUBOOTENV_LICENSE_FILES := \
+ file://LICENSES/MIT.txt;md5=838c366f69b72c5df05c96dff79b35f2 \
+ file://LICENSES/CC0-1.0.txt;md5=0ceb3372c9595f0a8067e55da801e4a1 \
+ file://LICENSES/LGPL-2.1-or-later.txt;md5=4fbd65380cdd255951079008b364516c
+
+# ----------------------------------------------------------------------------
+# Prepare
+# ----------------------------------------------------------------------------
+
+LIBUBOOTENV_CONF_TOOL := cmake
+LIBUBOOTENV_CONF_OPT := \
+ $(CROSS_CMAKE_USR) \
+ -DBUILD_DOC=OFF
+
+# ----------------------------------------------------------------------------
+# Target-Install
+# ----------------------------------------------------------------------------
+
+$(STATEDIR)/libubootenv.targetinstall:
+ @$(call targetinfo)
+
+ @$(call install_init, libubootenv)
+ @$(call install_fixup, libubootenv,PRIORITY,optional)
+ @$(call install_fixup, libubootenv,SECTION,base)
+ @$(call install_fixup, libubootenv,AUTHOR,"Alexander Dahl <ada@thorsis.com>")
+ @$(call install_fixup, libubootenv,DESCRIPTION,missing)
+
+ @$(call install_lib, libubootenv, 0, 0, 0644, libubootenv)
+
+ @$(call install_finish, libubootenv)
+
+ @$(call touch)
+
+# vim: ft=make noet tw=72 ts=8 sw=8
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ptxdist] [PATCH v2 2/5] libubootenv: Add option for installing tools
2025-09-19 10:06 [ptxdist] [PATCH v2 0/5] libubootenv: New package to replace target u-boot-tools Alexander Dahl via ptxdist
2025-09-19 10:06 ` [ptxdist] [PATCH v2 1/5] libubootenv: Introduce new package Alexander Dahl via ptxdist
@ 2025-09-19 10:06 ` Alexander Dahl via ptxdist
2025-09-22 11:04 ` [ptxdist] [APPLIED] " Michael Olbrich
2025-09-19 10:06 ` [ptxdist] [PATCH v2 3/5] libubootenv: Install config file to target Alexander Dahl via ptxdist
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Alexander Dahl via ptxdist @ 2025-09-19 10:06 UTC (permalink / raw)
To: ptxdist; +Cc: Alexander Dahl
These tools can also be provided by the u-boot-tools package.
This variant promises to be board independent, and comes with a library,
and has more features.
Can be used as drop-in replacement for fw_printenv and fw_setenv.
Helpful for writing env to mmcblockXbootX which is ro by default, and
u-boot-tools does not handle that.
Signed-off-by: Alexander Dahl <ada@thorsis.com>
---
Notes:
v2:
- reworded commit message
v1:
- Link: https://lore.ptxdist.org/ptxdist/mailman.2960.1742547175.466.ptxdist@pengutronix.de/
rules/libubootenv.in | 18 ++++++++++++++++--
rules/libubootenv.make | 7 +++++++
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/rules/libubootenv.in b/rules/libubootenv.in
index 70f6d9863..317a440fc 100644
--- a/rules/libubootenv.in
+++ b/rules/libubootenv.in
@@ -1,15 +1,29 @@
## SECTION=system_libraries
-config LIBUBOOTENV
+menuconfig LIBUBOOTENV
tristate
select HOST_CMAKE
select LIBYAML
select ZLIB
- prompt "libubootenv"
+ prompt "libubootenv "
help
Generic library and tools to access and modify U-Boot
environment from User Space.
https://github.com/sbabic/libubootenv
+if LIBUBOOTENV
+
+config LIBUBOOTENV_TOOLS
+ bool
+ prompt "fw_printenv / fw_setenv"
+ depends on !U_BOOT_TOOLS || ALLYES
+ help
+ Install fw_printenv and fw_setenv to target.
+
+comment "u-boot-tools is selected!"
+ depends on U_BOOT_TOOLS
+
+endif
+
# vim: ft=kconfig noet tw=72 ts=8 sw=8
diff --git a/rules/libubootenv.make b/rules/libubootenv.make
index d4e678152..769961dfa 100644
--- a/rules/libubootenv.make
+++ b/rules/libubootenv.make
@@ -51,6 +51,13 @@ $(STATEDIR)/libubootenv.targetinstall:
@$(call install_lib, libubootenv, 0, 0, 0644, libubootenv)
+ifdef PTXCONF_LIBUBOOTENV_TOOLS
+# # same path as the equivalents from u-boot-tools package
+ @$(call install_copy, libubootenv, 0, 0, 0755, \
+ $(LIBUBOOTENV_PKGDIR)/usr/bin/fw_printenv, /usr/sbin/fw_printenv)
+ @$(call install_link, libubootenv, fw_printenv, /usr/sbin/fw_setenv)
+endif
+
@$(call install_finish, libubootenv)
@$(call touch)
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ptxdist] [PATCH v2 3/5] libubootenv: Install config file to target
2025-09-19 10:06 [ptxdist] [PATCH v2 0/5] libubootenv: New package to replace target u-boot-tools Alexander Dahl via ptxdist
2025-09-19 10:06 ` [ptxdist] [PATCH v2 1/5] libubootenv: Introduce new package Alexander Dahl via ptxdist
2025-09-19 10:06 ` [ptxdist] [PATCH v2 2/5] libubootenv: Add option for installing tools Alexander Dahl via ptxdist
@ 2025-09-19 10:06 ` Alexander Dahl via ptxdist
2025-09-22 11:04 ` [ptxdist] [APPLIED] " Michael Olbrich
2025-09-19 10:06 ` [ptxdist] [PATCH v2 4/5] libubootenv: Import some fixes from master Alexander Dahl via ptxdist
2025-09-19 10:06 ` [ptxdist] [PATCH v2 5/5] libubootenv: Make yaml support optional Alexander Dahl via ptxdist
4 siblings, 1 reply; 11+ messages in thread
From: Alexander Dahl via ptxdist @ 2025-09-19 10:06 UTC (permalink / raw)
To: ptxdist; +Cc: Alexander Dahl
Same as with u-boot-tools.
Signed-off-by: Alexander Dahl <ada@thorsis.com>
---
Notes:
v2:
- new patch, not present in v1
rules/libubootenv.make | 1 +
1 file changed, 1 insertion(+)
diff --git a/rules/libubootenv.make b/rules/libubootenv.make
index 769961dfa..74bfaaf19 100644
--- a/rules/libubootenv.make
+++ b/rules/libubootenv.make
@@ -56,6 +56,7 @@ ifdef PTXCONF_LIBUBOOTENV_TOOLS
@$(call install_copy, libubootenv, 0, 0, 0755, \
$(LIBUBOOTENV_PKGDIR)/usr/bin/fw_printenv, /usr/sbin/fw_printenv)
@$(call install_link, libubootenv, fw_printenv, /usr/sbin/fw_setenv)
+ @$(call install_alternative, libubootenv, 0, 0, 0644, /etc/fw_env.config)
endif
@$(call install_finish, libubootenv)
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ptxdist] [PATCH v2 4/5] libubootenv: Import some fixes from master
2025-09-19 10:06 [ptxdist] [PATCH v2 0/5] libubootenv: New package to replace target u-boot-tools Alexander Dahl via ptxdist
` (2 preceding siblings ...)
2025-09-19 10:06 ` [ptxdist] [PATCH v2 3/5] libubootenv: Install config file to target Alexander Dahl via ptxdist
@ 2025-09-19 10:06 ` Alexander Dahl via ptxdist
2025-09-22 11:04 ` [ptxdist] [APPLIED] " Michael Olbrich
2025-09-19 10:06 ` [ptxdist] [PATCH v2 5/5] libubootenv: Make yaml support optional Alexander Dahl via ptxdist
4 siblings, 1 reply; 11+ messages in thread
From: Alexander Dahl via ptxdist @ 2025-09-19 10:06 UTC (permalink / raw)
To: ptxdist; +Cc: Alexander Dahl
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 <ada@thorsis.com>
---
Notes:
v2:
- new patch, not present in v1
.../0001-Make-libyaml-optional.patch | 1507 +++++++++++++++++
..._config-fix-segfault-on-empty-config.patch | 40 +
...-Fix-warning-when-copying-UBI-volume.patch | 35 +
...segfault-due-to-uninitialized-pointe.patch | 32 +
...nfig.c-Catch-NULL-pointer-for-calloc.patch | 30 +
patches/libubootenv-0.3.6/series | 8 +
rules/libubootenv.make | 3 +-
7 files changed, 1654 insertions(+), 1 deletion(-)
create mode 100644 patches/libubootenv-0.3.6/0001-Make-libyaml-optional.patch
create mode 100644 patches/libubootenv-0.3.6/0002-extended_config-fix-segfault-on-empty-config.patch
create mode 100644 patches/libubootenv-0.3.6/0003-BUG-Fix-warning-when-copying-UBI-volume.patch
create mode 100644 patches/libubootenv-0.3.6/0004-libubootenv-fix-segfault-due-to-uninitialized-pointe.patch
create mode 100644 patches/libubootenv-0.3.6/0005-extended_config.c-Catch-NULL-pointer-for-calloc.patch
create mode 100644 patches/libubootenv-0.3.6/series
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 000000000..631d0f1ef
--- /dev/null
+++ b/patches/libubootenv-0.3.6/0001-Make-libyaml-optional.patch
@@ -0,0 +1,1507 @@
+From: Stefano Babic <stefano.babic@swupdate.org>
+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 <stefano.babic@swupdate.org>
+---
+ 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, <stefano.babic@swupdate.org>
++ *
++ * SPDX-License-Identifier: LGPL-2.1-or-later
++ */
++
++#define _GNU_SOURCE
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <stdint.h>
++#include <stddef.h>
++#include <dirent.h>
++#include <unistd.h>
++#include <limits.h>
++#include <string.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <sys/stat.h>
++#include <sys/ioctl.h>
++#ifdef __FreeBSD__
++#include <sys/disk.h>
++#define BLKGETSIZE64 DIOCGMEDIASIZE
++#else
++#include <linux/fs.h>
++#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, <stefano.babic@swupdate.org>
++ *
++ * 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, <stefano.babic@swupdate.org>
++ *
++ * 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 <stdio.h>
++#include <stdlib.h>
++#include <stdint.h>
++#include <stddef.h>
++#include <unistd.h>
++#include <errno.h>
++#include <yaml.h>
++
++#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 <dirent.h>
+ #include <unistd.h>
+ #include <limits.h>
+-#ifdef __FreeBSD__
+-#include <sys/disk.h>
+-#define BLKGETSIZE64 DIOCGMEDIASIZE
+-#else
+-#include <linux/fs.h>
+-#endif
+ #include <string.h>
+ #include <fcntl.h>
+ #include <errno.h>
+@@ -38,60 +32,15 @@
+ #include <sys/wait.h>
+ #include <sys/ioctl.h>
+ #include <zlib.h>
+-#include <yaml.h>
+
+ #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 000000000..a4574ff5f
--- /dev/null
+++ b/patches/libubootenv-0.3.6/0002-extended_config-fix-segfault-on-empty-config.patch
@@ -0,0 +1,40 @@
+From: James Hilliard <james.hilliard1@gmail.com>
+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 <james.hilliard1@gmail.com>
+---
+ 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 000000000..1ead89548
--- /dev/null
+++ b/patches/libubootenv-0.3.6/0003-BUG-Fix-warning-when-copying-UBI-volume.patch
@@ -0,0 +1,35 @@
+From: Stefano Babic <stefano.babic@swupdate.org>
+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 <stefano.babic@swupdate.org>
+---
+ 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 000000000..e7ae92fb8
--- /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 <mohamed.toumi_ext@softathome.com>
+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 <mohamed.toumi_ext@softathome.com>
+---
+ 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 000000000..9c89141c7
--- /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 <steffen.kothe@skothe.net>
+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 <steffen.kothe@skothe.net>
+Acked-by: Stefano Babic <stefano.babic@swupdate.org>
+---
+ 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 000000000..b9356ee68
--- /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 74bfaaf19..d6753cd87 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
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* [ptxdist] [PATCH v2 5/5] libubootenv: Make yaml support optional
2025-09-19 10:06 [ptxdist] [PATCH v2 0/5] libubootenv: New package to replace target u-boot-tools Alexander Dahl via ptxdist
` (3 preceding siblings ...)
2025-09-19 10:06 ` [ptxdist] [PATCH v2 4/5] libubootenv: Import some fixes from master Alexander Dahl via ptxdist
@ 2025-09-19 10:06 ` Alexander Dahl via ptxdist
2025-09-22 11:04 ` [ptxdist] [APPLIED] " Michael Olbrich
4 siblings, 1 reply; 11+ messages in thread
From: Alexander Dahl via ptxdist @ 2025-09-19 10:06 UTC (permalink / raw)
To: ptxdist; +Cc: Alexander Dahl
If just used as drop in replacement with the old configuration format,
libyaml dependency is not required. You only need to enable it, if you
intend to use the new, extended configuration format.
Link: https://github.com/sbabic/libubootenv/blob/master/docs/fw_env_config.md#configuration-file-in-yaml
Signed-off-by: Alexander Dahl <ada@thorsis.com>
---
Notes:
v2:
- new patch, not in v1
rules/libubootenv.in | 8 +++++++-
rules/libubootenv.make | 2 +-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/rules/libubootenv.in b/rules/libubootenv.in
index 317a440fc..3bee47481 100644
--- a/rules/libubootenv.in
+++ b/rules/libubootenv.in
@@ -3,7 +3,7 @@
menuconfig LIBUBOOTENV
tristate
select HOST_CMAKE
- select LIBYAML
+ select LIBYAML if LIBUBOOTENV_YAML_CONFIG
select ZLIB
prompt "libubootenv "
help
@@ -21,6 +21,12 @@ config LIBUBOOTENV_TOOLS
help
Install fw_printenv and fw_setenv to target.
+config LIBUBOOTENV_YAML_CONFIG
+ bool
+ prompt "extended conf file format"
+ help
+ Support the new, extended, yaml based configuration file format.
+
comment "u-boot-tools is selected!"
depends on U_BOOT_TOOLS
diff --git a/rules/libubootenv.make b/rules/libubootenv.make
index d6753cd87..1ec86693a 100644
--- a/rules/libubootenv.make
+++ b/rules/libubootenv.make
@@ -35,7 +35,7 @@ LIBUBOOTENV_CONF_TOOL := cmake
LIBUBOOTENV_CONF_OPT := \
$(CROSS_CMAKE_USR) \
-DBUILD_DOC=OFF \
- -DNO_YML_SUPPORT=OFF
+ -DNO_YML_SUPPORT=$(call ptx/ifdef, PTXCONF_LIBUBOOTENV_YAML_CONFIG, OFF, ON)
# ----------------------------------------------------------------------------
# Target-Install
--
2.39.5
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [ptxdist] [APPLIED] libubootenv: Introduce new package
2025-09-19 10:06 ` [ptxdist] [PATCH v2 1/5] libubootenv: Introduce new package Alexander Dahl via ptxdist
@ 2025-09-22 11:04 ` Michael Olbrich
0 siblings, 0 replies; 11+ messages in thread
From: Michael Olbrich @ 2025-09-22 11:04 UTC (permalink / raw)
To: ptxdist; +Cc: Alexander Dahl
Thanks, applied as 50cde354492d493d649b38f5a5381ded59293482.
Michael
[sent from post-receive hook]
On Mon, 22 Sep 2025 13:04:24 +0200, Alexander Dahl <ada@thorsis.com> wrote:
> Useful to replace target u-boot-tools.
> Dependency for a potential new swupdate package.
>
> Link: https://github.com/sbabic/libubootenv
> Signed-off-by: Alexander Dahl <ada@thorsis.com>
> Message-Id: <20250919100643.199174-2-ada@thorsis.com>
> Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
>
> diff --git a/rules/libubootenv.in b/rules/libubootenv.in
> new file mode 100644
> index 000000000000..70f6d9863885
> --- /dev/null
> +++ b/rules/libubootenv.in
> @@ -0,0 +1,15 @@
> +## SECTION=system_libraries
> +
> +config LIBUBOOTENV
> + tristate
> + select HOST_CMAKE
> + select LIBYAML
> + select ZLIB
> + prompt "libubootenv"
> + help
> + Generic library and tools to access and modify U-Boot
> + environment from User Space.
> +
> + https://github.com/sbabic/libubootenv
> +
> +# vim: ft=kconfig noet tw=72 ts=8 sw=8
> diff --git a/rules/libubootenv.make b/rules/libubootenv.make
> new file mode 100644
> index 000000000000..d4e67815266b
> --- /dev/null
> +++ b/rules/libubootenv.make
> @@ -0,0 +1,58 @@
> +# -*-makefile-*-
> +#
> +# Copyright (C) 2025 by Alexander Dahl <ada@thorsis.com>
> +#
> +# For further information about the PTXdist project and license conditions
> +# see the README file.
> +#
> +
> +#
> +# We provide this package
> +#
> +PACKAGES-$(PTXCONF_LIBUBOOTENV) += libubootenv
> +
> +#
> +# Paths and names
> +#
> +LIBUBOOTENV_VERSION := 0.3.6
> +LIBUBOOTENV_MD5 := 7d6b623e8da435cf36e7fcd419a03e43
> +LIBUBOOTENV := libubootenv-$(LIBUBOOTENV_VERSION)
> +LIBUBOOTENV_SUFFIX := tar.gz
> +LIBUBOOTENV_URL := https://github.com/sbabic/libubootenv/archive/refs/tags/v$(LIBUBOOTENV_VERSION).$(LIBUBOOTENV_SUFFIX)
> +LIBUBOOTENV_SOURCE := $(SRCDIR)/$(LIBUBOOTENV).$(LIBUBOOTENV_SUFFIX)
> +LIBUBOOTENV_DIR := $(BUILDDIR)/$(LIBUBOOTENV)
> +LIBUBOOTENV_LICENSE := MIT AND CC0-1.0 AND LGPL-2.1-or-later
> +LIBUBOOTENV_LICENSE_FILES := \
> + file://LICENSES/MIT.txt;md5=838c366f69b72c5df05c96dff79b35f2 \
> + file://LICENSES/CC0-1.0.txt;md5=0ceb3372c9595f0a8067e55da801e4a1 \
> + file://LICENSES/LGPL-2.1-or-later.txt;md5=4fbd65380cdd255951079008b364516c
> +
> +# ----------------------------------------------------------------------------
> +# Prepare
> +# ----------------------------------------------------------------------------
> +
> +LIBUBOOTENV_CONF_TOOL := cmake
> +LIBUBOOTENV_CONF_OPT := \
> + $(CROSS_CMAKE_USR) \
> + -DBUILD_DOC=OFF
> +
> +# ----------------------------------------------------------------------------
> +# Target-Install
> +# ----------------------------------------------------------------------------
> +
> +$(STATEDIR)/libubootenv.targetinstall:
> + @$(call targetinfo)
> +
> + @$(call install_init, libubootenv)
> + @$(call install_fixup, libubootenv,PRIORITY,optional)
> + @$(call install_fixup, libubootenv,SECTION,base)
> + @$(call install_fixup, libubootenv,AUTHOR,"Alexander Dahl <ada@thorsis.com>")
> + @$(call install_fixup, libubootenv,DESCRIPTION,missing)
> +
> + @$(call install_lib, libubootenv, 0, 0, 0644, libubootenv)
> +
> + @$(call install_finish, libubootenv)
> +
> + @$(call touch)
> +
> +# vim: ft=make noet tw=72 ts=8 sw=8
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [ptxdist] [APPLIED] libubootenv: Add option for installing tools
2025-09-19 10:06 ` [ptxdist] [PATCH v2 2/5] libubootenv: Add option for installing tools Alexander Dahl via ptxdist
@ 2025-09-22 11:04 ` Michael Olbrich
0 siblings, 0 replies; 11+ messages in thread
From: Michael Olbrich @ 2025-09-22 11:04 UTC (permalink / raw)
To: ptxdist; +Cc: Alexander Dahl
Thanks, applied as 2468a48e9295b206a233c739e9b099824f9158e8.
Michael
[sent from post-receive hook]
On Mon, 22 Sep 2025 13:04:25 +0200, Alexander Dahl <ada@thorsis.com> wrote:
> These tools can also be provided by the u-boot-tools package.
>
> This variant promises to be board independent, and comes with a library,
> and has more features.
>
> Can be used as drop-in replacement for fw_printenv and fw_setenv.
> Helpful for writing env to mmcblockXbootX which is ro by default, and
> u-boot-tools does not handle that.
>
> Signed-off-by: Alexander Dahl <ada@thorsis.com>
> Message-Id: <20250919100643.199174-3-ada@thorsis.com>
> Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
>
> diff --git a/rules/libubootenv.in b/rules/libubootenv.in
> index 70f6d9863885..317a440fc0de 100644
> --- a/rules/libubootenv.in
> +++ b/rules/libubootenv.in
> @@ -1,15 +1,29 @@
> ## SECTION=system_libraries
>
> -config LIBUBOOTENV
> +menuconfig LIBUBOOTENV
> tristate
> select HOST_CMAKE
> select LIBYAML
> select ZLIB
> - prompt "libubootenv"
> + prompt "libubootenv "
> help
> Generic library and tools to access and modify U-Boot
> environment from User Space.
>
> https://github.com/sbabic/libubootenv
>
> +if LIBUBOOTENV
> +
> +config LIBUBOOTENV_TOOLS
> + bool
> + prompt "fw_printenv / fw_setenv"
> + depends on !U_BOOT_TOOLS || ALLYES
> + help
> + Install fw_printenv and fw_setenv to target.
> +
> +comment "u-boot-tools is selected!"
> + depends on U_BOOT_TOOLS
> +
> +endif
> +
> # vim: ft=kconfig noet tw=72 ts=8 sw=8
> diff --git a/rules/libubootenv.make b/rules/libubootenv.make
> index d4e67815266b..769961dfa068 100644
> --- a/rules/libubootenv.make
> +++ b/rules/libubootenv.make
> @@ -51,6 +51,13 @@ $(STATEDIR)/libubootenv.targetinstall:
>
> @$(call install_lib, libubootenv, 0, 0, 0644, libubootenv)
>
> +ifdef PTXCONF_LIBUBOOTENV_TOOLS
> +# # same path as the equivalents from u-boot-tools package
> + @$(call install_copy, libubootenv, 0, 0, 0755, \
> + $(LIBUBOOTENV_PKGDIR)/usr/bin/fw_printenv, /usr/sbin/fw_printenv)
> + @$(call install_link, libubootenv, fw_printenv, /usr/sbin/fw_setenv)
> +endif
> +
> @$(call install_finish, libubootenv)
>
> @$(call touch)
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [ptxdist] [APPLIED] libubootenv: Install config file to target
2025-09-19 10:06 ` [ptxdist] [PATCH v2 3/5] libubootenv: Install config file to target Alexander Dahl via ptxdist
@ 2025-09-22 11:04 ` Michael Olbrich
0 siblings, 0 replies; 11+ messages in thread
From: Michael Olbrich @ 2025-09-22 11:04 UTC (permalink / raw)
To: ptxdist; +Cc: Alexander Dahl
Thanks, applied as e006fc66f9b283bbd024a23a8adb2a43e91de3cd.
Michael
[sent from post-receive hook]
On Mon, 22 Sep 2025 13:04:26 +0200, Alexander Dahl <ada@thorsis.com> wrote:
> Same as with u-boot-tools.
>
> Signed-off-by: Alexander Dahl <ada@thorsis.com>
> Message-Id: <20250919100643.199174-4-ada@thorsis.com>
> Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
>
> diff --git a/rules/libubootenv.make b/rules/libubootenv.make
> index 769961dfa068..74bfaaf1965d 100644
> --- a/rules/libubootenv.make
> +++ b/rules/libubootenv.make
> @@ -56,6 +56,7 @@ ifdef PTXCONF_LIBUBOOTENV_TOOLS
> @$(call install_copy, libubootenv, 0, 0, 0755, \
> $(LIBUBOOTENV_PKGDIR)/usr/bin/fw_printenv, /usr/sbin/fw_printenv)
> @$(call install_link, libubootenv, fw_printenv, /usr/sbin/fw_setenv)
> + @$(call install_alternative, libubootenv, 0, 0, 0644, /etc/fw_env.config)
> endif
>
> @$(call install_finish, libubootenv)
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [ptxdist] [APPLIED] libubootenv: Import some fixes from master
2025-09-19 10:06 ` [ptxdist] [PATCH v2 4/5] libubootenv: Import some fixes from master Alexander Dahl via ptxdist
@ 2025-09-22 11:04 ` Michael Olbrich
0 siblings, 0 replies; 11+ messages in thread
From: Michael Olbrich @ 2025-09-22 11:04 UTC (permalink / raw)
To: ptxdist; +Cc: Alexander Dahl
Thanks, applied as 670e4f1259e813706d2f0d73b8431f69b708a9c7.
Michael
[sent from post-receive hook]
On Mon, 22 Sep 2025 13:04:27 +0200, Alexander Dahl <ada@thorsis.com> 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 <ada@thorsis.com>
> Message-Id: <20250919100643.199174-5-ada@thorsis.com>
> Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
>
> 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 <stefano.babic@swupdate.org>
> +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 <stefano.babic@swupdate.org>
> +---
> + 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, <stefano.babic@swupdate.org>
> ++ *
> ++ * SPDX-License-Identifier: LGPL-2.1-or-later
> ++ */
> ++
> ++#define _GNU_SOURCE
> ++
> ++#include <stdio.h>
> ++#include <stdlib.h>
> ++#include <stdint.h>
> ++#include <stddef.h>
> ++#include <dirent.h>
> ++#include <unistd.h>
> ++#include <limits.h>
> ++#include <string.h>
> ++#include <errno.h>
> ++#include <fcntl.h>
> ++#include <sys/stat.h>
> ++#include <sys/ioctl.h>
> ++#ifdef __FreeBSD__
> ++#include <sys/disk.h>
> ++#define BLKGETSIZE64 DIOCGMEDIASIZE
> ++#else
> ++#include <linux/fs.h>
> ++#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, <stefano.babic@swupdate.org>
> ++ *
> ++ * 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, <stefano.babic@swupdate.org>
> ++ *
> ++ * 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 <stdio.h>
> ++#include <stdlib.h>
> ++#include <stdint.h>
> ++#include <stddef.h>
> ++#include <unistd.h>
> ++#include <errno.h>
> ++#include <yaml.h>
> ++
> ++#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 <dirent.h>
> + #include <unistd.h>
> + #include <limits.h>
> +-#ifdef __FreeBSD__
> +-#include <sys/disk.h>
> +-#define BLKGETSIZE64 DIOCGMEDIASIZE
> +-#else
> +-#include <linux/fs.h>
> +-#endif
> + #include <string.h>
> + #include <fcntl.h>
> + #include <errno.h>
> +@@ -38,60 +32,15 @@
> + #include <sys/wait.h>
> + #include <sys/ioctl.h>
> + #include <zlib.h>
> +-#include <yaml.h>
> +
> + #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 <james.hilliard1@gmail.com>
> +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 <james.hilliard1@gmail.com>
> +---
> + 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 <stefano.babic@swupdate.org>
> +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 <stefano.babic@swupdate.org>
> +---
> + 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 <mohamed.toumi_ext@softathome.com>
> +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 <mohamed.toumi_ext@softathome.com>
> +---
> + 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 <steffen.kothe@skothe.net>
> +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 <steffen.kothe@skothe.net>
> +Acked-by: Stefano Babic <stefano.babic@swupdate.org>
> +---
> + 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
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [ptxdist] [APPLIED] libubootenv: Make yaml support optional
2025-09-19 10:06 ` [ptxdist] [PATCH v2 5/5] libubootenv: Make yaml support optional Alexander Dahl via ptxdist
@ 2025-09-22 11:04 ` Michael Olbrich
0 siblings, 0 replies; 11+ messages in thread
From: Michael Olbrich @ 2025-09-22 11:04 UTC (permalink / raw)
To: ptxdist; +Cc: Alexander Dahl
Thanks, applied as 1a4e37486db0139a3fcffbd6676b26fce2d51afd.
Michael
[sent from post-receive hook]
On Mon, 22 Sep 2025 13:04:28 +0200, Alexander Dahl <ada@thorsis.com> wrote:
> If just used as drop in replacement with the old configuration format,
> libyaml dependency is not required. You only need to enable it, if you
> intend to use the new, extended configuration format.
>
> Link: https://github.com/sbabic/libubootenv/blob/master/docs/fw_env_config.md#configuration-file-in-yaml
> Signed-off-by: Alexander Dahl <ada@thorsis.com>
> Message-Id: <20250919100643.199174-6-ada@thorsis.com>
> Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
>
> diff --git a/rules/libubootenv.in b/rules/libubootenv.in
> index 317a440fc0de..3bee47481cb0 100644
> --- a/rules/libubootenv.in
> +++ b/rules/libubootenv.in
> @@ -3,7 +3,7 @@
> menuconfig LIBUBOOTENV
> tristate
> select HOST_CMAKE
> - select LIBYAML
> + select LIBYAML if LIBUBOOTENV_YAML_CONFIG
> select ZLIB
> prompt "libubootenv "
> help
> @@ -21,6 +21,12 @@ config LIBUBOOTENV_TOOLS
> help
> Install fw_printenv and fw_setenv to target.
>
> +config LIBUBOOTENV_YAML_CONFIG
> + bool
> + prompt "extended conf file format"
> + help
> + Support the new, extended, yaml based configuration file format.
> +
> comment "u-boot-tools is selected!"
> depends on U_BOOT_TOOLS
>
> diff --git a/rules/libubootenv.make b/rules/libubootenv.make
> index d6753cd87081..1ec86693a4e3 100644
> --- a/rules/libubootenv.make
> +++ b/rules/libubootenv.make
> @@ -35,7 +35,7 @@ LIBUBOOTENV_CONF_TOOL := cmake
> LIBUBOOTENV_CONF_OPT := \
> $(CROSS_CMAKE_USR) \
> -DBUILD_DOC=OFF \
> - -DNO_YML_SUPPORT=OFF
> + -DNO_YML_SUPPORT=$(call ptx/ifdef, PTXCONF_LIBUBOOTENV_YAML_CONFIG, OFF, ON)
>
> # ----------------------------------------------------------------------------
> # Target-Install
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-09-22 11:04 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-19 10:06 [ptxdist] [PATCH v2 0/5] libubootenv: New package to replace target u-boot-tools Alexander Dahl via ptxdist
2025-09-19 10:06 ` [ptxdist] [PATCH v2 1/5] libubootenv: Introduce new package Alexander Dahl via ptxdist
2025-09-22 11:04 ` [ptxdist] [APPLIED] " Michael Olbrich
2025-09-19 10:06 ` [ptxdist] [PATCH v2 2/5] libubootenv: Add option for installing tools Alexander Dahl via ptxdist
2025-09-22 11:04 ` [ptxdist] [APPLIED] " Michael Olbrich
2025-09-19 10:06 ` [ptxdist] [PATCH v2 3/5] libubootenv: Install config file to target Alexander Dahl via ptxdist
2025-09-22 11:04 ` [ptxdist] [APPLIED] " Michael Olbrich
2025-09-19 10:06 ` [ptxdist] [PATCH v2 4/5] libubootenv: Import some fixes from master Alexander Dahl via ptxdist
2025-09-22 11:04 ` [ptxdist] [APPLIED] " Michael Olbrich
2025-09-19 10:06 ` [ptxdist] [PATCH v2 5/5] libubootenv: Make yaml support optional Alexander Dahl via ptxdist
2025-09-22 11:04 ` [ptxdist] [APPLIED] " Michael Olbrich
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox