@@ -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")
@@ -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)
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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);
new file mode 100644
@@ -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
@@ -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)
{
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 -- 2.34.1