From: Andreas Reichel <[email protected]>

From: Reichel Andreas <[email protected]>

Add possibility for user to store arbitrary variables into
environment.

Signed-off-by: Andreas Reichel <[email protected]>
---
 configure.ac           |  17 ++++
 docs/TODO.md           |  16 ++--
 docs/TOOLS.md          |  10 +++
 env/env_api.c          |  24 ++++-
 env/env_api_fat.c      | 239 ++++++++++++++++++++++++++++++++++++++++++++++---
 include/ebgenv.h       |  24 +++++
 include/env_api.h      |  16 +++-
 include/envdata.h      |   1 +
 tools/bg_setenv.c      |  79 +++++++++++++++-
 tools/tests/test_api.c | 123 ++++++++++++++++++++++++-
 10 files changed, 524 insertions(+), 25 deletions(-)

diff --git a/configure.ac b/configure.ac
index 1974fd8..4fae275 100644
--- a/configure.ac
+++ b/configure.ac
@@ -188,6 +188,22 @@ AC_ARG_WITH([num-config-parts],
            ])
 
 AC_DEFINE_UNQUOTED([ENV_NUM_CONFIG_PARTS], [${ENV_NUM_CONFIG_PARTS}], [Number 
of config partitions])
+
+AC_ARG_WITH([mem-uservars],
+           AS_HELP_STRING([--with-mem-uservars=INT],
+                          [specify amount of memory reserved for user 
variables in bytes]),
+           [
+               ENV_MEM_USERVARS=${withval:-0}
+               AS_IF([test "${ENV_MEM_USERVARS}" -lt "96"],
+                     [
+                       AC_MSG_ERROR([Minimum space allowed for uservars is 96 
bytes])
+                     ])
+           ],
+           [
+               ENV_MEM_USERVARS=131072
+           ])
+
+AC_DEFINE_UNQUOTED([ENV_MEM_USERVARS], [${ENV_MEM_USERVARS}], [Reserved memory 
for user variables])
 # 
------------------------------------------------------------------------------
 AC_CONFIG_FILES([
         Makefile
@@ -210,6 +226,7 @@ AC_MSG_RESULT([
 
        environment storage:     $ENV_STORAGE_TYPE
        number of config parts:  $ENV_NUM_CONFIG_PARTS
+       reserved for uservars:   $ENV_MEM_USERVARS bytes
 
        build and run tests:     $enable_tests
 ])
diff --git a/docs/TODO.md b/docs/TODO.md
index 29b7fe0..6f104ab 100644
--- a/docs/TODO.md
+++ b/docs/TODO.md
@@ -6,12 +6,16 @@
          the current working environment to the (latest-1) environment, so
          that if the current environment breaks, there is a backup with the
          latest values.
-
-* Application specific variables
-       * applications may need to store their own variables into the
-         bootloader environment. Currently this is not possible. A generic
-         method must be defined and implemented to account for generic
-         key-value pairs.
+       * Currently, `bg_setenv` generates a virtual environment copy while
+         parsing arguments and later uses an algorithm to find out, how to
+         merge this with the actual environment being modified. This led to
+         the introduction of a special marker byte which tells the algorithm
+         to not touch the original content. Deletion of user variables led to
+         another special case, where *negative* variables had to be defined by
+         a special `DELETED` type to tell the algorithm that the specific user
+         variable has to be deleted. This is rather complicated and a better
+         aproach has already been discussed using a journal with actions
+         instead of a prebuilt state.
 
 * API refactoring
        * Function / Datatype / Variable names remind of Parted and should be
diff --git a/docs/TOOLS.md b/docs/TOOLS.md
index ff0179f..4cf157d 100644
--- a/docs/TOOLS.md
+++ b/docs/TOOLS.md
@@ -80,3 +80,13 @@ issue:
 ```
 bg_setenv --partition=1 --ustate=2
 ```
+
+### Setting user variables ###
+
+`bg_setenv` has support for default user variables, meaning of type "String". 
To set a user variable, specify the `-x` flag:
+
+```
+bg_setenv -x key=value
+```
+
+This will set the variable named `key` to `value` in the current environment.
diff --git a/env/env_api.c b/env/env_api.c
index 1f073cf..d2d97ed 100644
--- a/env/env_api.c
+++ b/env/env_api.c
@@ -94,9 +94,31 @@ int ebg_env_get(char *key, char *buffer)
        return bgenv_get(env_current, key, NULL, buffer, ENV_STRING_LENGTH);
 }
 
+int ebg_env_get_ex(char *key, char *usertype, uint8_t *buffer, size_t maxlen)
+{
+       return bgenv_get(env_current, key, usertype, buffer, maxlen);
+}
+
 int ebg_env_set(char *key, char *value)
 {
-       return bgenv_set(env_current, key, "String", value, strlen(value));
+       return bgenv_set(env_current, key, USERVAR_TYPE_DEFAULT, value,
+                        strlen(value) + 1);
+}
+
+int ebg_env_set_ex(char *key, char *usertype, uint8_t *value, size_t datalen)
+{
+       return bgenv_set(env_current, key, usertype, value, datalen);
+}
+
+size_t ebg_env_user_free()
+{
+       if (!env_current) {
+               return 0;
+       }
+       if (!env_current->data) {
+               return 0;
+       }
+       return bgenv_user_free(env_current->data->userdata);
 }
 
 uint16_t ebg_env_getglobalstate()
diff --git a/env/env_api_fat.c b/env/env_api_fat.c
index 2047906..e609090 100644
--- a/env/env_api_fat.c
+++ b/env/env_api_fat.c
@@ -438,7 +438,79 @@ bool bgenv_close(BGENV *env)
        return false;
 }
 
-int bgenv_get(BGENV *env, char *key, char **type, void *data, size_t maxlen)
+void bgenv_map_uservar(uint8_t *udata, char **key, char **type, uint8_t **val,
+                      size_t *rsize, size_t *dsize)
+{
+       size_t *psize;
+
+       *key = (char *) udata;
+       psize = (size_t *) (*key + strlen(*key) + 1);
+       *rsize = *psize + strlen(*key) + 1;
+       *type = (char *) psize + sizeof(size_t);
+       *dsize = *psize - strlen(*type) - 1 - sizeof(size_t);
+       *val = (uint8_t *) (*type + strlen(*type) + 1);
+}
+
+uint8_t *bgenv_next_uservar(uint8_t *udata)
+{
+       char *key, *type;
+       uint8_t *val;
+       size_t rsize, dsize;
+
+       bgenv_map_uservar(udata, &key, &type, &val, &rsize, &dsize);
+
+       return udata + rsize;
+}
+
+uint8_t *bgenv_find_uservar(uint8_t *udata, char *key)
+{
+       char *varkey, *type;
+       uint8_t *val;
+       size_t rsize, dsize;
+
+       if (!udata) {
+               return NULL;
+       }
+       while (*udata) {
+               bgenv_map_uservar(udata, &varkey, &type, &val, &rsize, &dsize);
+
+               if (strncmp((char *) varkey, key, strlen(key) + 1) == 0) {
+                       return udata;
+               }
+               udata = bgenv_next_uservar(udata);
+       }
+       return NULL;
+}
+
+int bgenv_get_uservar(uint8_t *udata, char *key, char *type, void *data,
+                     size_t maxlen)
+{
+       uint8_t *uservar, *value;
+       char *lkey, *ltype;
+       size_t rsize, dsize;
+
+       uservar = bgenv_find_uservar(udata, key);
+
+       if (!uservar) {
+               return EINVAL;
+       }
+
+       bgenv_map_uservar(uservar, &lkey, &ltype, &value, &rsize, &dsize);
+
+       if (dsize > maxlen) {
+               dsize = maxlen;
+       }
+
+       memcpy(data, value, dsize);
+
+       if (type) {
+               memcpy(type, ltype, strlen(ltype) + 1);
+       }
+
+       return 0;
+}
+
+int bgenv_get(BGENV *env, char *key, char *type, void *data, size_t maxlen)
 {
        EBGENVKEY e;
 
@@ -446,41 +518,42 @@ int bgenv_get(BGENV *env, char *key, char **type, void 
*data, size_t maxlen)
                return EINVAL;
        }
        e = bgenv_str2enum(key);
-       if (e == EBGENV_UNKNOWN) {
-               return EINVAL;
-       }
        if (!env) {
                return EPERM;
        }
+       if (e == EBGENV_UNKNOWN) {
+               return bgenv_get_uservar(env->data->userdata, key, type, data,
+                                        maxlen);
+       }
        switch (e) {
        case EBGENV_KERNELFILE:
                str16to8(data, env->data->kernelfile);
                if (type) {
-                       sprintf(*type, "char*");
+                       sprintf(type, "char*");
                }
                break;
        case EBGENV_KERNELPARAMS:
                str16to8(data, env->data->kernelparams);
                if (type) {
-                       sprintf(*type, "char*");
+                       sprintf(type, "char*");
                }
                break;
        case EBGENV_WATCHDOG_TIMEOUT_SEC:
                sprintf(data, "%lu", env->data->watchdog_timeout_sec);
                if (type) {
-                       sprintf(*type, "uint16_t");
+                       sprintf(type, "uint16_t");
                }
                break;
        case EBGENV_REVISION:
                sprintf(data, "%lu", env->data->revision);
                if (type) {
-                       sprintf(*type, "uint32_t");
+                       sprintf(type, "uint32_t");
                }
                break;
        case EBGENV_USTATE:
                sprintf(data, "%u", env->data->ustate);
                if (type) {
-                       sprintf(*type, "uint16_t");
+                       sprintf(type, "uint16_t");
                }
                break;
        default:
@@ -489,6 +562,147 @@ int bgenv_get(BGENV *env, char *key, char **type, void 
*data, size_t maxlen)
        return 0;
 }
 
+static uint8_t *bgenv_uservar_alloc(uint8_t *udata, size_t datalen)
+{
+       size_t spaceleft;
+
+       if (!udata) {
+               errno = EINVAL;
+               return NULL;
+       }
+       spaceleft = bgenv_user_free(udata);
+       VERBOSE(stdout, "uservar_alloc: free: %u  requested: %u \n", spaceleft,
+               datalen);
+
+       /* To find the end of user variables, a 2nd 0 must be there after the
+        * last variable content, thus, we need one extra byte if appending a
+        * new variable. */
+       if (spaceleft < datalen + 1) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       return udata + (ENV_MEM_USERVARS - spaceleft);
+}
+
+void bgenv_del_uservar(uint8_t *udata, uint8_t *var)
+{
+       size_t spaceleft;
+       char *key, *type;
+       uint8_t *value;
+       size_t rsize, dsize;
+
+       bgenv_map_uservar(var, &key, &type, &value, &rsize, &dsize);
+
+       /* Move variable out of place and close gap. */
+       spaceleft = bgenv_user_free(udata);
+
+       memmove(var,
+               var + rsize,
+               ENV_MEM_USERVARS - spaceleft - (var - udata) - rsize);
+
+       spaceleft = spaceleft + rsize;
+
+       memset(udata + ENV_MEM_USERVARS - spaceleft, 0, spaceleft);
+}
+
+static uint8_t *bgenv_uservar_realloc(uint8_t *udata, size_t new_rsize, 
uint8_t *p)
+{
+       size_t spaceleft;
+       char *key, *type;
+       uint8_t *value;
+       size_t rsize, dsize;
+
+       bgenv_map_uservar(p, &key, &type, &value, &rsize, &dsize);
+
+       /* Is the new record size equal to the old, so that we can
+        * keep the variable in place? */
+       if (new_rsize == rsize) {
+               return p;
+       }
+
+       /* Delete variable and return pointer to end of whole user vars */
+       bgenv_del_uservar(udata, p);
+
+       spaceleft = bgenv_user_free(udata);
+
+       if (spaceleft < new_rsize - 1) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       return udata + ENV_MEM_USERVARS - spaceleft;
+}
+
+int bgenv_set_uservar(uint8_t *udata, char *key, char *type, void *data,
+                     size_t datalen)
+{
+       size_t total_size;
+       uint8_t *p;
+
+       total_size = datalen + strlen(type) + 1 + sizeof(size_t) +
+                    strlen(key) + 1;
+
+       p = bgenv_find_uservar(udata, key);
+       if (p) {
+               if (strncmp(type, USERVAR_TYPE_DELETED,
+                           strlen(USERVAR_TYPE_DELETED) + 1) == 0) {
+                       bgenv_del_uservar(udata, p);
+                       return 0;
+               }
+
+               p = bgenv_uservar_realloc(udata, total_size, p);
+       } else {
+               p = bgenv_uservar_alloc(udata, total_size);
+       }
+       if (!p) {
+               return errno;
+       }
+
+       // store key
+       strncpy(p, key, strlen(key)+1);
+       p += strlen(key)+1;
+       // store size after key
+       total_size -= strlen(key) + 1;
+       memcpy(p, &total_size, sizeof(size_t));
+       p += sizeof(size_t);
+       // store datatype
+       strncpy(p, type, strlen(type)+1);
+       p += strlen(type)+1;
+       // store data
+       memcpy(p, data, datalen);
+
+       return 0;
+}
+
+size_t bgenv_user_free(uint8_t *udata)
+{
+       char *key, *type;
+       uint8_t *value;
+       size_t rsize, dsize;
+       size_t spaceleft;
+
+       spaceleft = ENV_MEM_USERVARS;
+
+       if (!udata) {
+               return 0;
+       }
+       if (!*udata) {
+               return spaceleft;
+       }
+
+       while (*udata) {
+               bgenv_map_uservar(udata, &key, &type, &value, &rsize,
+                                 &dsize);
+               spaceleft -= rsize;
+               if (spaceleft <= 0)
+                       break;
+               udata = bgenv_next_uservar(udata);
+       }
+
+       return spaceleft;
+}
+
 int bgenv_set(BGENV *env, char *key, char *type, void *data, size_t datalen)
 {
        EBGENVKEY e;
@@ -501,12 +715,13 @@ int bgenv_set(BGENV *env, char *key, char *type, void 
*data, size_t datalen)
        }
 
        e = bgenv_str2enum(key);
-       if (e == EBGENV_UNKNOWN) {
-               return EINVAL;
-       }
        if (!env) {
                return EPERM;
        }
+       if (e == EBGENV_UNKNOWN) {
+               return bgenv_set_uservar(env->data->userdata, key, type, data,
+                                        datalen);
+       }
        switch (e) {
        case EBGENV_REVISION:
                errno = 0;
diff --git a/include/ebgenv.h b/include/ebgenv.h
index 0bcd363..e1cd0ac 100644
--- a/include/ebgenv.h
+++ b/include/ebgenv.h
@@ -48,6 +48,30 @@ int ebg_env_get(char *key, char *buffer);
  */
 int ebg_env_set(char *key, char *value);
 
+/** @brief Store new content into variable
+ *  @param key name of the environment variable to set
+ *  @param datatype user specific string to identify the datatype of the value
+ *  @param value arbitrary data to be stored into the variable
+ *  @param datalen length of the data to be stored into the variable
+ *  @return 0 on success, errno on failure
+ */
+int ebg_env_set_ex(char *key, char *datatype, uint8_t *value, size_t datalen);
+
+/** @brief Get content of user variable
+ *  @param key name of the environment variable to retrieve
+ *  @param datatype buffer for user specific string to identify the
+ *         datatype of the value
+ *  @param buffer destination for data to be stored into the variable
+ *  @param maxlen size of provided buffer
+ *  @return 0 on success, errno on failure
+ */
+int ebg_env_get_ex(char *key, char *datatype, uint8_t *buffer, size_t maxlen);
+
+/** @brief Get available space for user variables
+ *  @return Free space in bytes
+ */
+size_t ebg_env_user_free(void);
+
 /** @brief Get global ustate value, accounting for all environments
  *  @return ustate value
  */
diff --git a/include/env_api.h b/include/env_api.h
index 540a6f1..271c62b 100644
--- a/include/env_api.h
+++ b/include/env_api.h
@@ -47,6 +47,9 @@
        if (verbosity)                                                        \
        fprintf(o, __VA_ARGS__)
 
+#define USERVAR_TYPE_DEFAULT "String"
+#define USERVAR_TYPE_DELETED "\0xDE\0xAD"
+
 typedef enum {
        EBGENV_KERNELFILE,
        EBGENV_KERNELPARAMS,
@@ -81,9 +84,18 @@ extern BG_ENVDATA *bgenv_read(BGENV *env);
 extern bool bgenv_close(BGENV *env);
 
 extern BGENV *bgenv_create_new(void);
-extern int bgenv_get(BGENV *env, char *key, char **type, void *data,
+extern int bgenv_get(BGENV *env, char *key, char *type, void *data,
                     size_t maxlen);
 extern int bgenv_set(BGENV *env, char *key, char *type, void *data,
                     size_t datalen);
-
+extern size_t bgenv_user_free(uint8_t *udata);
+extern int bgenv_get_uservar(uint8_t *udata, char *key, char *type, void *data,
+                            size_t maxlen);
+extern int bgenv_set_uservar(uint8_t *udata, char *key, char *type, void *data,
+                            size_t datalen);
+extern void bgenv_map_uservar(uint8_t *udata, char **key, char **type,
+                             uint8_t **val, size_t *rsize, size_t *dsize);
+extern uint8_t *bgenv_next_uservar(uint8_t *udata);
+extern uint8_t *bgenv_find_uservar(uint8_t *udata, char *key);
+extern void bgenv_del_uservar(uint8_t *udata, uint8_t *var);
 #endif // __ENV_API_H__
diff --git a/include/envdata.h b/include/envdata.h
index 75e2b9f..573d976 100644
--- a/include/envdata.h
+++ b/include/envdata.h
@@ -31,6 +31,7 @@ struct _BG_ENVDATA {
        uint16_t ustate;
        uint16_t watchdog_timeout_sec;
        uint32_t revision;
+       uint8_t  userdata[ENV_MEM_USERVARS];
        uint32_t crc32;
 };
 #pragma pack(pop)
diff --git a/tools/bg_setenv.c b/tools/bg_setenv.c
index 761cb14..645a903 100644
--- a/tools/bg_setenv.c
+++ b/tools/bg_setenv.c
@@ -31,6 +31,9 @@ static struct argp_option options_setenv[] = {
     {"confirm", 'c', 0, 0, "Confirm working environment"},
     {"update", 'u', 0, 0, "Automatically update oldest revision"},
     {"verbose", 'v', 0, 0, "Be verbose"},
+    {"uservar", 'x', "KEY=VAL", 0, "Set user-defined string variable. For "
+                                  "setting multiple variables, use this "
+                                  "option multiple times."},
     {0}};
 
 static struct argp_option options_printenv[] = {
@@ -61,6 +64,26 @@ static bool verbosity = false;
 
 static char *envfilepath = NULL;
 
+static int set_uservars(uint8_t *uservars, char *arg)
+{
+       char *key, *value, *end_key;
+
+       key = strtok_r(arg, "=", &end_key);
+       if (key == NULL) {
+               return 0;
+       }
+
+       value = strtok_r(NULL, "=", &end_key);
+       if (value == NULL) {
+               bgenv_set_uservar(uservars, key, USERVAR_TYPE_DELETED, NULL, 0);
+               return 0;
+       }
+
+       bgenv_set_uservar(uservars, key, USERVAR_TYPE_DEFAULT, value, 
strlen(value)+1);
+
+       return 0;
+}
+
 static error_t parse_opt(int key, char *arg, struct argp_state *state)
 {
        struct arguments *arguments = state->input;
@@ -164,6 +187,10 @@ static error_t parse_opt(int key, char *arg, struct 
argp_state *state)
                /* Set verbosity in the library */
                bgenv_be_verbose(true);
                break;
+       case 'x':
+               /* Set user-defined variable(s) */
+               set_uservars(arguments->tmpdata.userdata, arg);
+               break;
        case ARGP_KEY_ARG:
                /* too many arguments - program terminates with call to
                 * argp_usage with non-zero return code */
@@ -175,6 +202,32 @@ static error_t parse_opt(int key, char *arg, struct 
argp_state *state)
        return 0;
 }
 
+static void merge_uservars(uint8_t *dest, uint8_t *src)
+{
+       char *key, *val, *type;
+       size_t rsize, dsize;
+       uint8_t *var;
+
+       if (!src || !dest) {
+               return;
+       }
+
+       while (*src) {
+               bgenv_map_uservar(src, &key, &type, (uint8_t **) &val, &rsize,
+                                 &dsize);
+               if (strncmp(type, USERVAR_TYPE_DELETED,
+                           strlen(USERVAR_TYPE_DELETED) - 1) == 0) {
+                       var = bgenv_find_uservar(dest, key);
+                       if (var) {
+                               bgenv_del_uservar(dest, var);
+                       }
+               } else {
+                       bgenv_set_uservar(dest, key, type, val, strlen(val) + 
1);
+               }
+               src = bgenv_next_uservar(src);
+       }
+}
+
 static void update_environment(BG_ENVDATA *dest, BG_ENVDATA *src)
 {
        if (!dest || !src) {
@@ -201,10 +254,30 @@ static void update_environment(BG_ENVDATA *dest, 
BG_ENVDATA *src)
                       (void *)&src->watchdog_timeout_sec,
                       sizeof(src->watchdog_timeout_sec));
        }
+       merge_uservars(dest->userdata, src->userdata);
+
        dest->crc32 =
            crc32(0, (Bytef *)dest, sizeof(BG_ENVDATA) - sizeof(dest->crc32));
 }
 
+static void dump_uservars(uint8_t *udata)
+{
+       char *key, *value, *type;
+       size_t rsize, dsize;
+
+       while (*udata) {
+               bgenv_map_uservar(udata, &key, &type, (uint8_t **) &value,
+                                 &rsize, &dsize);
+               printf("%s ", key);
+               if (strcmp(type, USERVAR_TYPE_DEFAULT) == 0) {
+                       printf("= %s\n", value);
+               } else {
+                       printf("( User defined type )");
+               }
+               udata = bgenv_next_uservar(udata);
+       }
+}
+
 static void dump_env(BG_ENVDATA *env)
 {
        char buffer[ENV_STRING_LENGTH];
@@ -214,6 +287,9 @@ static void dump_env(BG_ENVDATA *env)
        printf("kernelargs: %s\n", str16to8(buffer, env->kernelparams));
        printf("watchdog timeout: %u seconds\n", env->watchdog_timeout_sec);
        printf("ustate: %u\n", env->ustate);
+       printf("\n");
+       printf("user variables:\n");
+       dump_uservars(env->userdata);
        printf("\n\n");
 }
 
@@ -237,7 +313,8 @@ int main(int argc, char **argv)
        arguments.which_part = 0;
        memset((void *)&arguments.tmpdata, IGNORE_MARKER_BYTE,
               sizeof(BG_ENVDATA));
-
+       memset((void *)&arguments.tmpdata.userdata, 0,
+              sizeof(arguments.tmpdata.userdata));
        error_t e;
        if (e = argp_parse(argp, argc, argv, 0, 0, &arguments)) {
                return e;
diff --git a/tools/tests/test_api.c b/tools/tests/test_api.c
index dfabdac..707d9d2 100644
--- a/tools/tests/test_api.c
+++ b/tools/tests/test_api.c
@@ -102,8 +102,6 @@ static void test_api_accesscurrent(void **state)
        ret = ebg_env_close();
        assert_int_equal(ret, 0);
 
-       ret = ebg_env_set("NonsenseKey", "AnyValue");
-       assert_int_equal(ret, EINVAL);
 
        ret = ebg_env_set("kernelfile", "vmlinuz");
        assert_int_equal(ret, EPERM);
@@ -184,12 +182,131 @@ static void test_api_update(void **state)
        (void)state;
 }
 
+static void test_api_uservars(void **state)
+{
+       int ret;
+       char *test_key = "NonsenseKey";
+       char *test_key2 = "TestKey2";
+
+       char *test_val = "AnyValue";
+       char *test_val2 = "BnyVbluf";
+       char *test_val3 = "TESTTESTTESTTEST";
+       char *test_val4 = "abc";
+       char buffer[ENV_MEM_USERVARS];
+       size_t space_left;
+
+       will_return(bgenv_open_latest, &env);
+       ret = ebg_env_open_current();
+       assert_int_equal(ret, 0);
+
+       assert_int_equal(ebg_env_user_free(), ENV_MEM_USERVARS);
+
+       ret = ebg_env_set(test_key, test_val);
+       assert_int_equal(ret, 0);
+
+       space_left = ENV_MEM_USERVARS - strlen(test_key) - 1
+                       - strlen(test_val) - 1 - sizeof(size_t)
+                       - strlen(USERVAR_TYPE_DEFAULT) - 1;
+
+       assert_int_equal(ebg_env_user_free(), space_left);
+
+       ret = ebg_env_get(test_key, buffer);
+       assert_int_equal(ret, 0);
+       assert_string_equal(buffer, test_val);
+
+       // replace value with same length value
+       ret = ebg_env_set(test_key, test_val2);
+       assert_int_equal(ret, 0);
+       assert_int_equal(ebg_env_user_free(), space_left);
+
+       ret = ebg_env_get(test_key, buffer);
+       assert_int_equal(ret, 0);
+       assert_string_equal(buffer, test_val2);
+
+       // replace value with larger value
+       ret = ebg_env_set(test_key, test_val3);
+       assert_int_equal(ret, 0);
+
+       space_left = ENV_MEM_USERVARS - strlen(test_key) - 1
+                       - strlen(test_val3) - 1 - sizeof(size_t)
+                       - strlen(USERVAR_TYPE_DEFAULT) - 1;
+
+       assert_int_equal(ebg_env_user_free(), space_left);
+
+       // replace value with smaller value
+       ret = ebg_env_set(test_key, test_val4);
+       assert_int_equal(ret, 0);
+
+       space_left = ENV_MEM_USERVARS - strlen(test_key) - 1
+                       - strlen(test_val4) - 1 - sizeof(size_t)
+                       - strlen(USERVAR_TYPE_DEFAULT) - 1;
+
+       assert_int_equal(ebg_env_user_free(), space_left);
+
+       // add 2nd variable
+       ret = ebg_env_set(test_key2, test_val2);
+       assert_int_equal(ret, 0);
+
+       space_left = space_left - strlen(test_key2) - 1
+                       - strlen(test_val2) - 1 - sizeof(size_t)
+                       - strlen(USERVAR_TYPE_DEFAULT) - 1;
+
+       assert_int_equal(ebg_env_user_free(), space_left);
+
+       // retrieve both variables
+       ret = ebg_env_get(test_key2, buffer);
+       assert_int_equal(ret, 0);
+       assert_string_equal(buffer, test_val2);
+       ret = ebg_env_get(test_key, buffer);
+       assert_int_equal(ret, 0);
+       assert_string_equal(buffer, test_val4);
+
+       // overwrite first variable
+       ret = ebg_env_set(test_key, test_val3);
+       assert_int_equal(ret, 0);
+
+       space_left = space_left + strlen(test_val4)
+                               - strlen(test_val3);
+       assert_int_equal(ebg_env_user_free(), space_left);
+
+       // retrieve both variables
+       ret = ebg_env_get(test_key2, buffer);
+       assert_int_equal(ret, 0);
+       assert_string_equal(buffer, test_val2);
+       ret = ebg_env_get(test_key, buffer);
+       assert_int_equal(ret, 0);
+       assert_string_equal(buffer, test_val3);
+
+       void *dummymem = malloc(space_left);
+
+       // test out of memory
+       ret = ebg_env_set_ex("A", "B", dummymem, space_left);
+       free(dummymem);
+
+       assert_int_equal(ret, ENOMEM);
+
+       // test user data type
+       ret = ebg_env_set_ex("A", "B", "C", 2);
+       assert_int_equal(ret, 0);
+
+       char type[2];
+       char data[2];
+
+       ret = ebg_env_get_ex("A", type, data, sizeof(data));
+       assert_int_equal(ret, 0);
+       assert_string_equal("B", type);
+       assert_string_equal("C", data);
+
+       (void)state;
+}
+
 int main(void)
 {
        const struct CMUnitTest tests[] = {
            cmocka_unit_test(test_api_openclose),
            cmocka_unit_test(test_api_accesscurrent),
-           cmocka_unit_test(test_api_update)};
+           cmocka_unit_test(test_api_update),
+           cmocka_unit_test(test_api_uservars)};
 
        env.data = &data;
        data.revision = test_env_revision;
-- 
2.13.3

-- 
You received this message because you are subscribed to the Google Groups "EFI 
Boot Guard" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/efibootguard-dev/20170822161551.12662-12-andreas.reichel.ext%40siemens.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to