On 08/22/2017 06:15 PM, [ext] Reichel Andreas wrote:
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 this is the final API for efibootguard, I would like to point out
that it should be relatively future proof and usable in many unforeseen
scenarios. Having one global state to operate on, limits this.
From my experience library API should always take some kind of context
struct as the first parameter. This context struct would contain all the
now global variables. This way for instance two different environments
could be loaded and edited. For instance in a user space application
that displays the differences from multiple environments and allows to
edit them. This would not be possible if only one environment can be
loaded in one application thread at a time.
If swupdate wants to have a very simple API, then it should use a
wrapper around this API where is uses a global struct as the API context.
+{
+ 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);
I know I am demanding, and maybe a bit stupid, but I think this kind of
code could use some words of documentation, better var names and maybe
introduction of some temporary variables just to make reading and
understanding easier. Stuff like this is always a potential trove of
mistakes and bugs and it helps to make it very obvious what happens here.
+}
+
+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);
Maybe you should try to separate the bgenv struct, that you work with
from how it is represented in the file, and have serializing
deserializing methods that just handle this. This way you would have
only two functions that deal with the exact representation in the
environment file. If you do this, then you can store all additional data
(like all the different sizes) in there and don't have to 'parse' the
file representation again and again on every operation.
+ }
+ 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, <ype, &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;
Not sure about ENOMEM here. Since that normally happens if there is not
enough free main memory and not about not enough free space in a fixed
size file. Since its a range check ERANGE might be better suited for
this case. Maybe a error message there won't hurt.
+ 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;
+}
These functions could also become simpler when the environment file
format is separated from the internal representation.
Then we would have a central point for the environment file format and
some functions that only operate on the environment struct in memory.
Claudius
+
+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;
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-54 Fax: (+49)-8142-66989-80 Email: [email protected]
--
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/5e9f5c59-67b2-1358-15c0-4b276acaa4c3%40siemens.com.
For more options, visit https://groups.google.com/d/optout.