This patch changes set_variables so that it can use an external environment block when one is present. The variable next_entry is written into the external block, env_block is treated as read only, and all other variables are written into the normal file based envblk.
A cleanup step is added to handle cases where GRUB at runtime writes variables into the external block because file based updates are not safe on a copy on write filesystem such as Btrfs. For example, the savedefault command can update saved_entry, and on Btrfs GRUB will place that update in the external block instead of the file envblk. If an older copy remains in the external block, it would override the newer value from the file envblk when GRUB first loads the file and then applies the external block on top of it. To avoid this, whenever a variable is updated in the file envblk, any same named key in the external block is deleted. Signed-off-by: Michael Chang <[email protected]> Reviewed-by: Neal Gompa <[email protected]> --- util/grub-editenv.c | 55 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/util/grub-editenv.c b/util/grub-editenv.c index 450bb00a5..efc6ec5f2 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -394,12 +394,33 @@ fs_envblk_write (grub_envblk_t envblk) fclose (fp); } +struct var_lookup_ctx { + const char *varname; + bool found; +}; + +static int +var_lookup_iter (const char *varname, const char *value __attribute__ ((unused)), void *hook_data) +{ + struct var_lookup_ctx *ctx = (struct var_lookup_ctx *) hook_data; + if (grub_strcmp (ctx->varname, varname) == 0) + { + ctx->found = true; + return 1; + } + return 0; +} + static void set_variables (const char *name, int argc, char *argv[]) { grub_envblk_t envblk; + grub_envblk_t envblk_fs = NULL; envblk = open_envblk_file (name); + if (fs_envblk != NULL) + envblk_fs = fs_envblk->ops->open (envblk); + while (argc) { char *p; @@ -410,8 +431,32 @@ set_variables (const char *name, int argc, char *argv[]) *(p++) = 0; - if (! grub_envblk_set (envblk, argv[0], p)) - grub_util_error ("%s", _("environment block too small")); + if ((strcmp (argv[0], "next_entry") == 0) && envblk_fs) + { + if (! grub_envblk_set (envblk_fs, argv[0], p)) + grub_util_error ("%s", _("environment block too small")); + } + else if (strcmp (argv[0], "env_block") == 0) + { + grub_util_warn (_("can't set env_block as it's read-only")); + } + else + { + if (! grub_envblk_set (envblk, argv[0], p)) + grub_util_error ("%s", _("environment block too small")); + + if (envblk_fs) + { + struct var_lookup_ctx ctx = { + .varname = argv[0], + .found = false + }; + + grub_envblk_iterate (envblk_fs, &ctx, var_lookup_iter); + if (ctx.found == true) + grub_envblk_delete (envblk_fs, argv[0]); + } + } argc--; argv++; @@ -419,6 +464,12 @@ set_variables (const char *name, int argc, char *argv[]) write_envblk (name, envblk); grub_envblk_close (envblk); + + if (envblk_fs != NULL) + { + fs_envblk->ops->write (envblk_fs); + grub_envblk_close (envblk_fs); + } } static void -- 2.51.0 _______________________________________________ Grub-devel mailing list [email protected] https://lists.gnu.org/mailman/listinfo/grub-devel
