Message ID | 740689eb-7833-4114-86a7-cb850fc371deo@googlegroups.com |
---|---|
State | Changes Requested |
Headers | show |
Series | [meta-swupdate,libubootenv] Add correct handling of 'o' (write once) and any other access or type flags if the variable which has the flag is part of the .flags value string but does not exist as a valued variable yet (variable not created | expand |
Hi Uwe, On 05.06.20 16:08, Uwe Schuster wrote: > From 7766312aa1fbfa8d807ab3ded05cbd1db71440c9 Mon Sep 17 00:00:00 2001 > From: Uwe Schuster <uwe.schuster@woodward.com> > Date: Tue, 2 Jun 2020 15:32:10 +0200 > Subject: [PATCH] Add correct handling of 'o' (write once) and any other > access > or type flags if the variable which has the flag is part of the .flags > value > string but does not exist as a valued variable yet (variable not > created yet > and value assigned) (bugfix). > > With the fix you can specify variables in the .flags variable which > don't need to exist as a variable with a value assigned, but can be > created later and after creation will obey their flags given earlier. > Especially write once variables can now be created once and not changed > after. > The only issue which still exists even with this fix: when using > fw_printenv it will not include .flags variable in the printed list > (despite its there). Because of the chosen flag handling implementation > any solution for that would require much not easy changes in the > printenv code. > > Signed-off-by: Uwe Schuster <uwe.schuster@woodward.com> > --- > src/fw_printenv.c | 16 +++-- > src/uboot_env.c | 173 > +++++++++++++++++++++++++++++++++++------------------- > 2 files changed, 124 insertions(+), 65 deletions(-) > > diff --git a/src/fw_printenv.c b/src/fw_printenv.c > index 18887f9..8fd1f55 100644 > --- a/src/fw_printenv.c > +++ b/src/fw_printenv.c > @@ -137,17 +137,21 @@ int main (int argc, char **argv) { > if (!argc) { > tmp = NULL; > while ((tmp = libuboot_iterator(ctx, tmp)) != NULL) { > - name = libuboot_getname(tmp); > value = libuboot_getvalue(tmp); > - fprintf(stdout, "%s=%s\n", name, value); > + if (value) { > + name = libuboot_getname(tmp); > + fprintf(stdout, "%s=%s\n", name, value); > + } This has nothing to do with the flags, it should be addressed by a separate patch. But which is the issue you want to fix here ? If a variable is not set, it is removed by the list and it does not appear when printed. IMHO this above does not make sense. > } > } else { > for (i = 0; i < argc; i++) { > value = libuboot_get_env(ctx, argv[i]); > - if (noheader) > - fprintf(stdout, "%s\n", value ? value : ""); > - else > - fprintf(stdout, "%s=%s\n", argv[i], value ? value : > ""); > + if (value) { > + if (noheader) > + fprintf(stdout, "%s\n", value ? value : ""); > + else > + fprintf(stdout, "%s=%s\n", argv[i], value ? > value : ""); Where is the difference introduced by patch ? > + } > } > } > } else { /* setenv branch */ > diff --git a/src/uboot_env.c b/src/uboot_env.c > index 2f7f714..1d9b0f9 100644 > --- a/src/uboot_env.c > +++ b/src/uboot_env.c > @@ -765,6 +765,9 @@ int libuboot_env_store(struct uboot_ctx *ctx) > uint8_t offsetdata; > int ret; > int copy; > +#if !defined(NDEBUG) > + char * flagsbuf; > +#endif ??? > > /* > * Allocate the bigger of the case > @@ -782,21 +785,27 @@ int libuboot_env_store(struct uboot_ctx *ctx) > > buf = data; > LIST_FOREACH(entry, &ctx->varlist, next) { > - size = (ctx->size - offsetdata) - (buf - data); > - if ((strlen(entry->name) + strlen(entry->value) + 2) > size) > - return -ENOMEM; > + if (entry->value) { > + /* only save variables which have a value (do exist yet)*/ > + size = (ctx->size - offsetdata) - (buf - data); > + if ((strlen(entry->name) + strlen(entry->value) + 2) > size) > + return -ENOMEM; > > + buf += snprintf(buf, size, "%s=%s", entry->name, entry->value); > + buf++; > + } > + /* .. but include non yet existing vars into .flags string */ > if (entry->type || entry->access) > saveflags = true; > - > - buf += snprintf(buf, size, "%s=%s", entry->name, entry->value); > - buf++; > } > > /* > * Now save the .flags > */ > if (saveflags) { > +#if !defined(NDEBUG) > + flagsbuf = buf; > +#endif > bool first = true; > size = (ctx->size - offsetdata) - (buf - data); > buf += snprintf(buf, size, ".flags="); > @@ -809,11 +818,16 @@ int libuboot_env_store(struct uboot_ctx *ctx) > entry->name, > attr_tostring(entry->type), > access_tostring(entry->access)); > + first = false; > } > } > } > *buf++ = '\0'; > > +#if !defined(NDEBUG) > + fprintf(stdout, "Saved environment FLAGS %s\n", flagsbuf); > +#endif > + > if (ctx->redundant) { > unsigned char flags = ctx->envdevs[ctx->current].flags; > switch(ctx->envdevs[ctx->current].flagstype) { > @@ -847,18 +861,19 @@ int libuboot_env_store(struct uboot_ctx *ctx) > return ret; > } > > -static void libuboot_parseflags(struct uboot_ctx *ctx, char* flagsvar) > +static void libuboot_parseflags(struct uboot_ctx *ctx, const char* flags) > { > char *pvar; > char *pval; > struct var_entry *entry; > + char *flagsvar = strdup(flags); > > #if !defined(NDEBUG) > - fprintf(stdout, "Environment FLAGS %s\n", flagsvar); > + fprintf(stdout, "Parsed environment FLAGS %s\n", flags); > #endif > pvar = flagsvar; > > - while (*pvar && (pvar - flagsvar) < strlen(flagsvar)) { > + while (pvar && ((pvar - flagsvar) < strlen(flags))) { > char *pnext; > pval = strchr(pvar, ':'); > if (!pval) > @@ -871,47 +886,56 @@ static void libuboot_parseflags(struct uboot_ctx > *ctx, char* flagsvar) > else > *pnext++ = '\0'; > > +#if !defined(NDEBUG) > + fprintf(stdout, "Parsed var %s:%s\n", pvar, pval); > +#endif > entry = __libuboot_get_env(&ctx->varlist, pvar); > - if (entry) { > - for (int i = 0; i < strlen(pval); i++) { > - switch (pval[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; > - } > + if (!entry) { > + /* create non-valued entry to conserve its flags */ > + libuboot_set_env(ctx, pvar, NULL); > + entry = __libuboot_get_env(&ctx->varlist, pvar); > + } > + > + for (int i = 0; i < strlen(pval); i++) { > + switch (pval[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; > } Same problem reported before - same code, some issues with editor, it looks new code, it is not. > } > + > pvar = pnext; > } > + free(flagsvar); > } > > static int libuboot_load(struct uboot_ctx *ctx) > @@ -1095,7 +1119,7 @@ int libuboot_load_file(struct uboot_ctx *ctx, > const char *filename) > if (strlen(value)) > value = NULL; > > - libuboot_set_env(ctx, name, value); > + libuboot_set_env(ctx, name, value); > } > else /* process flags variable after while loop */ > flagsvar = strdup(value); > @@ -1205,12 +1229,19 @@ static bool libuboot_validate_flags(struct > var_entry *entry, const char *value) > ok_access = true; > break; > case ACCESS_ATTR_READ_ONLY: > - case ACCESS_ATTR_WRITE_ONCE: > ok_access = false; > break; > + case ACCESS_ATTR_WRITE_ONCE: > + if (entry->value) { > + ok_access = false; > + } > + break; > case ACCESS_ATTR_CHANGE_DEFAULT: > break; > } > +#if !defined(NDEBUG) > + fprintf(stdout, "Access flag validation %s\n", ok_access ? > "success." : "failed."); > +#endif > > if (!ok_access) > return false; > @@ -1243,6 +1274,10 @@ static bool libuboot_validate_flags(struct > var_entry *entry, const char *value) > case TYPE_ATTR_MAC: > break; > } > +#if !defined(NDEBUG) > + fprintf(stdout, "Type flag validation %s\n", ok_type ? "success." : > "failed."); > +#endif > + > return ok_type; > } > > @@ -1250,13 +1285,30 @@ int libuboot_set_env(struct uboot_ctx *ctx, > const char *varname, const char *val > { > struct var_entry *entry, *elm, *lastentry; > struct vars *envs = &ctx->varlist; > + /* update all .flags attributes in list with new value */ > + if (!strcmp(varname, ".flags")) { > + LIST_FOREACH(entry, &ctx->varlist, next) { > + entry->type = TYPE_ATTR_STRING; > + entry->access = ACCESS_ATTR_ANY; > + } > + if (value) > + libuboot_parseflags(ctx, strdup(value)); > + > + return 0; > + } > + > entry = __libuboot_get_env(envs, varname); > if (entry) { > if (libuboot_validate_flags(entry, value)) { > - if (!value) { > - free_var_entry(envs, entry); > - } else { > - free(entry->value); > + if (entry->value) { > + if (!value) { > + free_var_entry(envs, entry); > + } else { > + free(entry->value); > + entry->value = strdup(value); > + } > + } > + else if (value) { > entry->value = strdup(value); > } > return 0; > @@ -1265,9 +1317,6 @@ int libuboot_set_env(struct uboot_ctx *ctx, const > char *varname, const char *val > } > } > > - if (!value) > - return 0; > - > entry = (struct var_entry *)calloc(1, sizeof(*entry)); > if (!entry) > return -ENOMEM; > @@ -1276,12 +1325,18 @@ int libuboot_set_env(struct uboot_ctx *ctx, > const char *varname, const char *val > free(entry); > return -ENOMEM; > } > - entry->value = strdup(value); > - if (!entry->value) { > - free(entry->name); > - free(entry); > - return -ENOMEM; > + if (!value) { > + entry->value = NULL; > } > + else { > + entry->value = strdup(value); > + if (!entry->value) { > + free(entry->name); > + free(entry); > + return -ENOMEM; > + } > + } > + > lastentry = NULL; > LIST_FOREACH(elm, envs, next) { > if (strcmp(elm->name, varname) > 0) { > Best regards, Stefano Babic
Hi Stefano, Am Mittwoch, 10. Juni 2020 12:16:41 UTC+2 schrieb Stefano Babic: > > Hi Uwe, > > On 05.06.20 16:08, Uwe Schuster wrote: > > From 7766312aa1fbfa8d807ab3ded05cbd1db71440c9 Mon Sep 17 00:00:00 2001 > > > Subject: [PATCH] Add correct handling of 'o' (write once) and any other > > access > > or type flags if the variable which has the flag is part of the .flags > > value > > string but does not exist as a valued variable yet (variable not > > created yet > > and value assigned) (bugfix). > > > > With the fix you can specify variables in the .flags variable which > > don't need to exist as a variable with a value assigned, but can be > > created later and after creation will obey their flags given earlier. > > Especially write once variables can now be created once and not changed > > after. > > The only issue which still exists even with this fix: when using > > fw_printenv it will not include .flags variable in the printed list > > (despite its there). Because of the chosen flag handling implementation > > any solution for that would require much not easy changes in the > > printenv code. > > > > > diff --git a/src/fw_printenv.c b/src/fw_printenv.c > > index 18887f9..8fd1f55 100644 > > --- a/src/fw_printenv.c > > +++ b/src/fw_printenv.c > > @@ -137,17 +137,21 @@ int main (int argc, char **argv) { > > if (!argc) { > > tmp = NULL; > > while ((tmp = libuboot_iterator(ctx, tmp)) != NULL) { > > - name = libuboot_getname(tmp); > > value = libuboot_getvalue(tmp); > > - fprintf(stdout, "%s=%s\n", name, value); > > + if (value) { > > + name = libuboot_getname(tmp); > > + fprintf(stdout, "%s=%s\n", name, value); > > + } > > This has nothing to do with the flags, it should be addressed by a > separate patch. But which is the issue you want to fix here ? > > If a variable is not set, it is removed by the list and it does not > appear when printed. IMHO this above does not make sense. > It has to do with the patch and how I decided to implement the fix for it. The existing code does not anywhere deal with variables which do exist by name but without any value. There was no need yet. To implement a fix for the mentioned behavior I saw two different approaches for implementation. To change as little as possible in the existing implementation I decided against first approach by adding some more meta data to the variable struct to mark a variable as existing yet or not. Instead I used second approach - the value member of a variable is used as the indicator for such kind of variables. If value == NULL then the variable exists (as part of .flags) but has no value assigned yet. But this choosen implementation requires some small changes on places like the print_env function - it shall NOT print out variables which don't have a value assigned yet - they only exist in the internal database so that any storage of env will rebuild and store the .flags variable as it was before when read in. > > } > > } else { > > for (i = 0; i < argc; i++) { > > value = libuboot_get_env(ctx, argv[i]); > > - if (noheader) > > - fprintf(stdout, "%s\n", value ? value : ""); > > - else > > - fprintf(stdout, "%s=%s\n", argv[i], value ? value : > > ""); > > + if (value) { > > + if (noheader) > > + fprintf(stdout, "%s\n", value ? value : ""); > > + else > > + fprintf(stdout, "%s=%s\n", argv[i], value ? > > value : ""); > > Where is the difference introduced by patch ? > Difference is the if (value) clause - see my comment above. > > + } > > } > > } > > } else { /* setenv branch */ > > diff --git a/src/uboot_env.c b/src/uboot_env.c > > index 2f7f714..1d9b0f9 100644 > > --- a/src/uboot_env.c > > +++ b/src/uboot_env.c > > @@ -765,6 +765,9 @@ int libuboot_env_store(struct uboot_ctx *ctx) > > uint8_t offsetdata; > > int ret; > > int copy; > > +#if !defined(NDEBUG) > > + char * flagsbuf; > > +#endif > > This was added only for debug reasons the see whats going on with the .flags variable behind the scenes when read and written to env. > > > > > /* > > * Allocate the bigger of the case > > @@ -782,21 +785,27 @@ int libuboot_env_store(struct uboot_ctx *ctx) > > > > buf = data; > > LIST_FOREACH(entry, &ctx->varlist, next) { > > - size = (ctx->size - offsetdata) - (buf - data); > > - if ((strlen(entry->name) + strlen(entry->value) + 2) > size) > > - return -ENOMEM; > > + if (entry->value) { > > + /* only save variables which have a value (do exist yet)*/ > > + size = (ctx->size - offsetdata) - (buf - data); > > + if ((strlen(entry->name) + strlen(entry->value) + 2) > > size) > > + return -ENOMEM; > > > > + buf += snprintf(buf, size, "%s=%s", entry->name, > entry->value); > > + buf++; > > + } > > + /* .. but include non yet existing vars into .flags string */ > > if (entry->type || entry->access) > > saveflags = true; > > - > > - buf += snprintf(buf, size, "%s=%s", entry->name, entry->value); > > - buf++; > > } > > > > /* > > * Now save the .flags > > */ > > if (saveflags) { > > +#if !defined(NDEBUG) > > + flagsbuf = buf; > > +#endif > > bool first = true; > > size = (ctx->size - offsetdata) - (buf - data); > > buf += snprintf(buf, size, ".flags="); > > @@ -809,11 +818,16 @@ int libuboot_env_store(struct uboot_ctx *ctx) > > entry->name, > > attr_tostring(entry->type), > > access_tostring(entry->access)); > > + first = false; > > } > > } > > } > > *buf++ = '\0'; > > > > +#if !defined(NDEBUG) > > + fprintf(stdout, "Saved environment FLAGS %s\n", flagsbuf); > > +#endif > > + > > if (ctx->redundant) { > > unsigned char flags = ctx->envdevs[ctx->current].flags; > > switch(ctx->envdevs[ctx->current].flagstype) { > > @@ -847,18 +861,19 @@ int libuboot_env_store(struct uboot_ctx *ctx) > > return ret; > > } > > > > -static void libuboot_parseflags(struct uboot_ctx *ctx, char* flagsvar) > > +static void libuboot_parseflags(struct uboot_ctx *ctx, const char* > flags) > > { > > char *pvar; > > char *pval; > > struct var_entry *entry; > > + char *flagsvar = strdup(flags); > > > > #if !defined(NDEBUG) > > - fprintf(stdout, "Environment FLAGS %s\n", flagsvar); > > + fprintf(stdout, "Parsed environment FLAGS %s\n", flags); > > #endif > > pvar = flagsvar; > > > > - while (*pvar && (pvar - flagsvar) < strlen(flagsvar)) { > > + while (pvar && ((pvar - flagsvar) < strlen(flags))) { > > char *pnext; > > pval = strchr(pvar, ':'); > > if (!pval) > > @@ -871,47 +886,56 @@ static void libuboot_parseflags(struct uboot_ctx > > *ctx, char* flagsvar) > > else > > *pnext++ = '\0'; > > > > +#if !defined(NDEBUG) > > + fprintf(stdout, "Parsed var %s:%s\n", pvar, pval); > > +#endif > > entry = __libuboot_get_env(&ctx->varlist, pvar); > > - if (entry) { > > - for (int i = 0; i < strlen(pval); i++) { > > - switch (pval[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; > > - } > > + if (!entry) { > > + /* create non-valued entry to conserve its flags */ > > + libuboot_set_env(ctx, pvar, NULL); > > + entry = __libuboot_get_env(&ctx->varlist, pvar); > > + } > > + > > + for (int i = 0; i < strlen(pval); i++) { > > + switch (pval[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; > > } > > Same problem reported before - same code, some issues with editor, it > looks new code, it is not. > The difference here is the indentation level of the code - one less than before. However it can be made the same as before by not removing "if (entry) {" line. However I did remove it because with added if(!entry) clause, entry is always !=NULL now. Best Regards Uwe
Hi Uwe, On 10.06.20 14:33, Uwe Schuster wrote: > Hi Stefano, > > Am Mittwoch, 10. Juni 2020 12:16:41 UTC+2 schrieb Stefano Babic: > > Hi Uwe, > > On 05.06.20 16:08, Uwe Schuster wrote: > > From 7766312aa1fbfa8d807ab3ded05cbd1db71440c9 Mon Sep 17 00:00:00 > 2001 > > > Subject: [PATCH] Add correct handling of 'o' (write once) and any > other > > access > > or type flags if the variable which has the flag is part of the > .flags > > value > > string but does not exist as a valued variable yet (variable not > > created yet > > and value assigned) (bugfix). > > > > With the fix you can specify variables in the .flags variable which > > don't need to exist as a variable with a value assigned, but can be > > created later and after creation will obey their flags given earlier. > > Especially write once variables can now be created once and not > changed > > after. > > The only issue which still exists even with this fix: when using > > fw_printenv it will not include .flags variable in the printed list > > (despite its there). Because of the chosen flag handling > implementation > > any solution for that would require much not easy changes in the > > printenv code. > > > > > diff --git a/src/fw_printenv.c b/src/fw_printenv.c > > index 18887f9..8fd1f55 100644 > > --- a/src/fw_printenv.c > > +++ b/src/fw_printenv.c > > @@ -137,17 +137,21 @@ int main (int argc, char **argv) { > > if (!argc) { > > tmp = NULL; > > while ((tmp = libuboot_iterator(ctx, tmp)) != NULL) { > > - name = libuboot_getname(tmp); > > value = libuboot_getvalue(tmp); > > - fprintf(stdout, "%s=%s\n", name, value); > > + if (value) { > > + name = libuboot_getname(tmp); > > + fprintf(stdout, "%s=%s\n", name, value); > > + } > > This has nothing to do with the flags, it should be addressed by a > separate patch. But which is the issue you want to fix here ? > > If a variable is not set, it is removed by the list and it does not > appear when printed. IMHO this above does not make sense. > > > It has to do with the patch and how I decided to implement the fix for > it. Sorry, I have not understood this from commit message (write once flag). The existing code does not anywhere deal with variables which do > exist by name but without any value. There was no need yet. mmmh...ok, I have to think about. This is also the way used by U-Boot to drop variables. In U-Boot, I use: setenv dummy 1 ==> set variable setenv dummy ==> drop variable And the implementation in libubootenv corresponds to the behavior in U-Boot. Which is the use case to have a variable that points to a Null (not empty) value ? And cannot be this use case handled in U-Boot scripts (bootloader) or evaluating the result of fw_printenv in User Space (linux) ? In u-boot, scripts can use the "exists" clause, something like "env exists dummy && ". In user space, it is enough to check the return value of fw_printenv or evaluating the output. > To implement a fix for the mentioned behavior I saw two different > approaches for implementation. To change as little as possible in the > existing implementation I decided against first approach by adding some > more meta data to the variable struct to mark a variable as existing yet > or not. Let me know your use case, that is a variable without a value - I have not yet understood it. How do we distinguish between dropping variable and setting variable to Null (if second case makes sense, of course) ? > Instead I used second approach - the value member of a variable > is used as the indicator for such kind of variables. If value == NULL > then the variable exists (as part of .flags) but has no value assigned > yet. mmmhh...ok, I understand now the point: .flags contains the variable as write-once. I needed my time. Then I do not like the implementation, it looks like a trick. I would prefer that library adds an own structure for this "not-yet" elaborated flags, and this is parsed and evaluated when a set is done. If a write-once for the variable is found, then the first set is accepted and the entry is removed by the not yet-elaborated list. > But this choosen implementation requires some small changes on > places like the print_env function - it shall NOT print out variables > which don't have a value assigned yet - they only exist in the internal > database so that any storage of env will rebuild and store the .flags > variable as it was before when read in. > > > > } > > } else { > > for (i = 0; i < argc; i++) { > > value = libuboot_get_env(ctx, argv[i]); > > - if (noheader) > > - fprintf(stdout, "%s\n", value ? value : ""); > > - else > > - fprintf(stdout, "%s=%s\n", argv[i], value ? > value : > > ""); > > + if (value) { > > + if (noheader) > > + fprintf(stdout, "%s\n", value ? value : ""); > > + else > > + fprintf(stdout, "%s=%s\n", argv[i], value ? > > value : ""); > > Where is the difference introduced by patch ? > > > Difference is the if (value) clause - see my comment above. Ok, now it is clear to me, but it sounds like a hack. > > > > + } > > } > > } > > } else { /* setenv branch */ > > diff --git a/src/uboot_env.c b/src/uboot_env.c > > index 2f7f714..1d9b0f9 100644 > > --- a/src/uboot_env.c > > +++ b/src/uboot_env.c > > @@ -765,6 +765,9 @@ int libuboot_env_store(struct uboot_ctx *ctx) > > uint8_t offsetdata; > > int ret; > > int copy; > > +#if !defined(NDEBUG) > > + char * flagsbuf; > > +#endif > > This was added only for debug reasons the see whats going on with the > .flags variable behind the scenes when read and written to env. Ok > > > > > > /* > > * Allocate the bigger of the case > > @@ -782,21 +785,27 @@ int libuboot_env_store(struct uboot_ctx *ctx) > > > > buf = data; > > LIST_FOREACH(entry, &ctx->varlist, next) { > > - size = (ctx->size - offsetdata) - (buf - data); > > - if ((strlen(entry->name) + strlen(entry->value) + 2) > size) > > - return -ENOMEM; > > + if (entry->value) { > > + /* only save variables which have a value (do exist > yet)*/ > > + size = (ctx->size - offsetdata) - (buf - data); > > + if ((strlen(entry->name) + strlen(entry->value) + 2) > > size) > > + return -ENOMEM; > > > > + buf += snprintf(buf, size, "%s=%s", entry->name, > entry->value); > > + buf++; > > + } > > + /* .. but include non yet existing vars into .flags > string */ > > if (entry->type || entry->access) > > saveflags = true; > > - > > - buf += snprintf(buf, size, "%s=%s", entry->name, > entry->value); > > - buf++; > > } > > > > /* > > * Now save the .flags > > */ > > if (saveflags) { > > +#if !defined(NDEBUG) > > + flagsbuf = buf; > > +#endif > > bool first = true; > > size = (ctx->size - offsetdata) - (buf - data); > > buf += snprintf(buf, size, ".flags="); > > @@ -809,11 +818,16 @@ int libuboot_env_store(struct uboot_ctx *ctx) > > entry->name, > > attr_tostring(entry->type), > > access_tostring(entry->access)); > > + first = false; > > } > > } > > } > > *buf++ = '\0'; > > > > +#if !defined(NDEBUG) > > + fprintf(stdout, "Saved environment FLAGS %s\n", flagsbuf); > > +#endif > > + > > if (ctx->redundant) { > > unsigned char flags = ctx->envdevs[ctx->current].flags; > > switch(ctx->envdevs[ctx->current].flagstype) { > > @@ -847,18 +861,19 @@ int libuboot_env_store(struct uboot_ctx *ctx) > > return ret; > > } > > > > -static void libuboot_parseflags(struct uboot_ctx *ctx, char* > flagsvar) > > +static void libuboot_parseflags(struct uboot_ctx *ctx, const > char* flags) > > { > > char *pvar; > > char *pval; > > struct var_entry *entry; > > + char *flagsvar = strdup(flags); > > > > #if !defined(NDEBUG) > > - fprintf(stdout, "Environment FLAGS %s\n", flagsvar); > > + fprintf(stdout, "Parsed environment FLAGS %s\n", flags); > > #endif > > pvar = flagsvar; > > > > - while (*pvar && (pvar - flagsvar) < strlen(flagsvar)) { > > + while (pvar && ((pvar - flagsvar) < strlen(flags))) { > > char *pnext; > > pval = strchr(pvar, ':'); > > if (!pval) > > @@ -871,47 +886,56 @@ static void libuboot_parseflags(struct > uboot_ctx > > *ctx, char* flagsvar) > > else > > *pnext++ = '\0'; > > > > +#if !defined(NDEBUG) > > + fprintf(stdout, "Parsed var %s:%s\n", pvar, pval); > > +#endif > > entry = __libuboot_get_env(&ctx->varlist, pvar); > > - if (entry) { > > - for (int i = 0; i < strlen(pval); i++) { > > - switch (pval[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; > > - } > > + if (!entry) { > > + /* create non-valued entry to conserve its flags */ > > + libuboot_set_env(ctx, pvar, NULL); > > + entry = __libuboot_get_env(&ctx->varlist, pvar); > > + } > > + > > + for (int i = 0; i < strlen(pval); i++) { > > + switch (pval[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; > > } > > Same problem reported before - same code, some issues with editor, it > looks new code, it is not. > > > The difference here is the indentation level of the code - one less than > before. However it can be made the same as before by not removing "if > (entry) {" line. However I did remove it because with added if(!entry) > clause, entry is always !=NULL now. > Best regards, Stefano
Hi Stefano, sorry for the long delay, been busy other while. Am Mittwoch, 10. Juni 2020 15:35:19 UTC+2 schrieb Stefano Babic: > > Hi Uwe, > > On 10.06.20 14:33, Uwe Schuster wrote: > > Hi Stefano, > > > > Am Mittwoch, 10. Juni 2020 12:16:41 UTC+2 schrieb Stefano Babic: > > > > Hi Uwe, > > > > On 05.06.20 16:08, Uwe Schuster wrote: > > > From 7766312aa1fbfa8d807ab3ded05cbd1db71440c9 Mon Sep 17 00:00:00 > > 2001 > > > > > Subject: [PATCH] Add correct handling of 'o' (write once) and any > > other > > > access > > > or type flags if the variable which has the flag is part of the > > .flags > > > value > > > string but does not exist as a valued variable yet (variable not > > > created yet > > > and value assigned) (bugfix). > > > > > > With the fix you can specify variables in the .flags variable > which > > > don't need to exist as a variable with a value assigned, but can > be > > > created later and after creation will obey their flags given > earlier. > > > Especially write once variables can now be created once and not > > changed > > > after. > > > The only issue which still exists even with this fix: when using > > > fw_printenv it will not include .flags variable in the printed > list > > > (despite its there). Because of the chosen flag handling > > implementation > > > any solution for that would require much not easy changes in the > > > printenv code. > > > > > > > > diff --git a/src/fw_printenv.c b/src/fw_printenv.c > > > index 18887f9..8fd1f55 100644 > > > --- a/src/fw_printenv.c > > > +++ b/src/fw_printenv.c > > > @@ -137,17 +137,21 @@ int main (int argc, char **argv) { > > > if (!argc) { > > > tmp = NULL; > > > while ((tmp = libuboot_iterator(ctx, tmp)) != NULL) { > > > - name = libuboot_getname(tmp); > > > value = libuboot_getvalue(tmp); > > > - fprintf(stdout, "%s=%s\n", name, value); > > > + if (value) { > > > + name = libuboot_getname(tmp); > > > + fprintf(stdout, "%s=%s\n", name, value); > > > + } > > > > This has nothing to do with the flags, it should be addressed by a > > separate patch. But which is the issue you want to fix here ? > > > > If a variable is not set, it is removed by the list and it does not > > appear when printed. IMHO this above does not make sense. > > > > > > It has to do with the patch and how I decided to implement the fix for > > it. > > Sorry, I have not understood this from commit message (write once flag). > > The existing code does not anywhere deal with variables which do > > exist by name but without any value. There was no need yet. > > mmmh...ok, I have to think about. This is also the way used by U-Boot to > drop variables. In U-Boot, I use: > > setenv dummy 1 ==> set variable > setenv dummy ==> drop variable > > And the implementation in libubootenv corresponds to the behavior in > U-Boot. Which is the use case to have a variable that points to a Null > (not empty) value ? And cannot be this use case handled in U-Boot > scripts (bootloader) or evaluating the result of fw_printenv in User > Space (linux) ? > > In u-boot, scripts can use the "exists" clause, something like "env > exists dummy && ". In user space, it is enough to check the return value > of fw_printenv or evaluating the output. > > > To implement a fix for the mentioned behavior I saw two different > > approaches for implementation. To change as little as possible in the > > existing implementation I decided against first approach by adding some > > more meta data to the variable struct to mark a variable as existing yet > > or not. > > Let me know your use case, that is a variable without a value - I have > not yet understood it. > > How do we distinguish between dropping variable and setting variable to > Null (if second case makes sense, of course) ? > Our use case is as follows: in the production process the device will get its firmware deployed then it will be powered up into Linux and some remote running scripts will use swupdate/fw_setenv facilities to set some env variables in uboot to its final values - one of them is "ethaddr". To achieve this, the initial firmware is deployed with a default environment file, containing a .flags variable which itself contains "ethaddr" variable marked with "mo" flags but without a value. Then the scripts set the value of this variable to a specific MAC address. With existing libubootenv this fails because first the default env file is not parsed correctly for .flags variable (which is addressed by the other patch posted) and second (if first is fixed) the ethaddr is not stored in internal database because no value is assigned yet when read. This would be fixed by this patch. > > > Instead I used second approach - the value member of a variable > > is used as the indicator for such kind of variables. If value == NULL > > then the variable exists (as part of .flags) but has no value assigned > > yet. > > mmmhh...ok, I understand now the point: .flags contains the variable as > write-once. I needed my time. > > Then I do not like the implementation, it looks like a trick. I would > prefer that library adds an own structure for this "not-yet" elaborated > flags, and this is parsed and evaluated when a set is done. If a > write-once for the variable is found, then the first set is accepted and > the entry is removed by the not yet-elaborated list. > I'd not say its a real hack, since having a variable without a value could be imho really thought and implemented by having the value set to NULL. Of course there are other (nicer) implementations as you are mentioning one. I have considered those but decided against them, because they would require much more effort to be implemented and introduce more crucial changes in existing code. Which itself leads to the problem of testing. Since I could not spot any unit tests for this library I was concerned, that something else functionality gets broken because of any side effects etc. > > > But this choosen implementation requires some small changes on > > places like the print_env function - it shall NOT print out variables > > which don't have a value assigned yet - they only exist in the internal > > database so that any storage of env will rebuild and store the .flags > > variable as it was before when read in. > > > > Where is the difference introduced by patch ? > > > > > > Difference is the if (value) clause - see my comment above. > > Ok, now it is clear to me, but it sounds like a hack. > > I will not be able to rewrite the patch to implement your suggested approach because I'm already working for a different company. The old company just wanted to provide that fix for obviously wrong behavior and have it upstream, to make it easier for us to update to newer versions of swupdate in future. But it would be also appreciated and acceptable if you or someone else is going to fix the implementation as you suggest it. Important for us is only the correct behavior. Regards Uwe > >
Hallo Uwe, On 18.06.20 18:02, Uwe Schuster wrote: > Hi Stefano, > > sorry for the long delay, been busy other while. > > Am Mittwoch, 10. Juni 2020 15:35:19 UTC+2 schrieb Stefano Babic: [snip] > > Our use case is as follows: in the production process the device will > get its firmware deployed then it will be powered up into Linux and some > remote running scripts will use swupdate/fw_setenv facilities to set > some env variables in uboot to its final values - one of them is > "ethaddr". To achieve this, the initial firmware is deployed with a > default environment file, containing a .flags variable which itself > contains "ethaddr" variable marked with "mo" flags but without a value. > Then the scripts set the value of this variable to a specific MAC > address. With existing libubootenv this fails because first the default > env file is not parsed correctly for .flags variable (which is addressed > by the other patch posted) and second (if first is fixed) the ethaddr is > not stored in internal database because no value is assigned yet when > read. This would be fixed by this patch. Thanks for clarification - your use case is now clear to me. > > > > Instead I used second approach - the value member of a variable > > is used as the indicator for such kind of variables. If value == NULL > > then the variable exists (as part of .flags) but has no value > assigned > > yet. > > mmmhh...ok, I understand now the point: .flags contains the variable as > write-once. I needed my time. > > Then I do not like the implementation, it looks like a trick. I would > prefer that library adds an own structure for this "not-yet" elaborated > flags, and this is parsed and evaluated when a set is done. If a > write-once for the variable is found, then the first set is accepted > and > the entry is removed by the not yet-elaborated list. > > > I'd not say its a real hack, since having a variable without a value > could be imho really thought and implemented by having the value set to > NULL. This is against the logic supplied since from the beginning in U-Boot. A variable cannot be set to NULL, and when a variable is set to nothing, the variable is dropped (setenv behavior). I would like to maintain the same logic here. The other advantage about this implementation is that it is not necessary to check values for a variable. If a variable is not in the list, it does not exist. > Of course there are other (nicer) implementations as you are > mentioning one. I have considered those but decided against them, > because they would require much more effort to be implemented and > introduce more crucial changes in existing code. I agree this requires more effort, but I am quite sure this lets better and more maintainable code. > Which itself leads to > the problem of testing. Since I could not spot any unit tests for this > library I was concerned, that something else functionality gets broken > because of any side effects etc Right, test must be considered, too. . > > > > But this choosen implementation requires some small changes on > > places like the print_env function - it shall NOT print out variables > > which don't have a value assigned yet - they only exist in the > internal > > database so that any storage of env will rebuild and store the .flags > > variable as it was before when read in. > > > > Where is the difference introduced by patch ? > > > > > > Difference is the if (value) clause - see my comment above. > > Ok, now it is clear to me, but it sounds like a hack. > > > I will not be able to rewrite the patch to implement your suggested > approach because I'm already working for a different company. ;-) > The old > company just wanted to provide that fix for obviously wrong behavior Behavior with flags is wrong, fully agree at this point. > and > have it upstream, to make it easier for us to update to newer versions > of swupdate in future. ..but to make it upstreamable, it should be also done in a way that can be easier maintainable. > > But it would be also appreciated and acceptable if you or someone else > is going to fix the implementation as you suggest it. Important for us > is only the correct behavior. Fully agree about wrong behavior. In my case, I rework / fix when I have a running project asking for this, and it is not yet the case (flags is a feature not used very often). I would note this, or you can tell your old company to contact me for this work. Best regards, Stefano
diff --git a/src/fw_printenv.c b/src/fw_printenv.c index 18887f9..8fd1f55 100644 --- a/src/fw_printenv.c +++ b/src/fw_printenv.c @@ -137,17 +137,21 @@ int main (int argc, char **argv) { if (!argc) { tmp = NULL; while ((tmp = libuboot_iterator(ctx, tmp)) != NULL) { - name = libuboot_getname(tmp); value = libuboot_getvalue(tmp); - fprintf(stdout, "%s=%s\n", name, value); + if (value) { + name = libuboot_getname(tmp); + fprintf(stdout, "%s=%s\n", name, value); + } } } else { for (i = 0; i < argc; i++) { value = libuboot_get_env(ctx, argv[i]); - if (noheader) - fprintf(stdout, "%s\n", value ? value : ""); - else - fprintf(stdout, "%s=%s\n", argv[i], value ? value : ""); + if (value) { + if (noheader) + fprintf(stdout, "%s\n", value ? value : ""); + else + fprintf(stdout, "%s=%s\n", argv[i], value ? value : ""); + } } } } else { /* setenv branch */ diff --git a/src/uboot_env.c b/src/uboot_env.c index 2f7f714..1d9b0f9 100644 --- a/src/uboot_env.c +++ b/src/uboot_env.c @@ -765,6 +765,9 @@ int libuboot_env_store(struct uboot_ctx *ctx) uint8_t offsetdata; int ret; int copy; +#if !defined(NDEBUG) + char * flagsbuf; +#endif /* * Allocate the bigger of the case @@ -782,21 +785,27 @@ int libuboot_env_store(struct uboot_ctx *ctx) buf = data; LIST_FOREACH(entry, &ctx->varlist, next) { - size = (ctx->size - offsetdata) - (buf - data); - if ((strlen(entry->name) + strlen(entry->value) + 2) > size) - return -ENOMEM; + if (entry->value) { + /* only save variables which have a value (do exist yet)*/ + size = (ctx->size - offsetdata) - (buf - data); + if ((strlen(entry->name) + strlen(entry->value) + 2) > size) + return -ENOMEM; + buf += snprintf(buf, size, "%s=%s", entry->name, entry->value); + buf++; + } + /* .. but include non yet existing vars into .flags string */ if (entry->type || entry->access) saveflags = true; - - buf += snprintf(buf, size, "%s=%s", entry->name, entry->value); - buf++; } /* * Now save the .flags */ if (saveflags) { +#if !defined(NDEBUG) + flagsbuf = buf; +#endif bool first = true; size = (ctx->size - offsetdata) - (buf - data); buf += snprintf(buf, size, ".flags="); @@ -809,11 +818,16 @@ int libuboot_env_store(struct uboot_ctx *ctx) entry->name, attr_tostring(entry->type), access_tostring(entry->access)); + first = false; } } } *buf++ = '\0'; +#if !defined(NDEBUG) + fprintf(stdout, "Saved environment FLAGS %s\n", flagsbuf); +#endif + if (ctx->redundant) { unsigned char flags = ctx->envdevs[ctx->current].flags; switch(ctx->envdevs[ctx->current].flagstype) { @@ -847,18 +861,19 @@ int libuboot_env_store(struct uboot_ctx *ctx) return ret; } -static void libuboot_parseflags(struct uboot_ctx *ctx, char* flagsvar) +static void libuboot_parseflags(struct uboot_ctx *ctx, const char* flags) { char *pvar; char *pval; struct var_entry *entry; + char *flagsvar = strdup(flags); #if !defined(NDEBUG) - fprintf(stdout, "Environment FLAGS %s\n", flagsvar); + fprintf(stdout, "Parsed environment FLAGS %s\n", flags); #endif pvar = flagsvar; - while (*pvar && (pvar - flagsvar) < strlen(flagsvar)) { + while (pvar && ((pvar - flagsvar) < strlen(flags))) { char *pnext; pval = strchr(pvar, ':'); if (!pval) @@ -871,47 +886,56 @@ static void libuboot_parseflags(struct uboot_ctx *ctx, char* flagsvar) else *pnext++ = '\0'; +#if !defined(NDEBUG) + fprintf(stdout, "Parsed var %s:%s\n", pvar, pval); +#endif entry = __libuboot_get_env(&ctx->varlist, pvar); - if (entry) { - for (int i = 0; i < strlen(pval); i++) { - switch (pval[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; - } + if (!entry) { + /* create non-valued entry to conserve its flags */ + libuboot_set_env(ctx, pvar, NULL); + entry = __libuboot_get_env(&ctx->varlist, pvar); + } + + for (int i = 0; i < strlen(pval); i++) { + switch (pval[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; } } + pvar = pnext; } + free(flagsvar); }