This patch adds the logic to locate and open an environment block that is stored in a reserved area on the device. It introduces the function fs_envblk_open() together with helper routines to read the block pointed to by the env_block variable, and to create the block on disk when it does not exist yet. When a block is created, the code records its location inside the file based envblk by setting env_block in block list syntax of offset plus size in sectors.
The env_block variable acts as a link from the file envblk to the raw disk region so that later runs of grub-editenv can follow it and access the external block. The helper is exposed through a small ops table attached to fs_envblk so that later patches can call fs_envblk->ops->open() without touching core code again. At this stage variables are still stored in the file envblk and no redirection has been applied. Signed-off-by: Michael Chang <[email protected]> Reviewed-by: Neal Gompa <[email protected]> Reviewed-by: Avnish Chouhan <[email protected]> --- util/grub-editenv.c | 126 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/util/grub-editenv.c b/util/grub-editenv.c index e650cbcd0..1bbd4b051 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -130,12 +130,24 @@ struct fs_envblk_spec { }; typedef struct fs_envblk_spec fs_envblk_spec_t; +static grub_envblk_t fs_envblk_open (grub_envblk_t envblk); + +struct fs_envblk_ops { + grub_envblk_t (*open) (grub_envblk_t); +}; +typedef struct fs_envblk_ops fs_envblk_ops_t; + struct fs_envblk { fs_envblk_spec_t *spec; + fs_envblk_ops_t *ops; const char *dev; }; typedef struct fs_envblk *fs_envblk_t; +static fs_envblk_ops_t fs_envblk_ops = { + .open = fs_envblk_open +}; + /* * fs_envblk_spec describes the file-system specific layout of reserved raw * blocks used as environment blocks. At present only Btrfs is supported. Other @@ -153,6 +165,119 @@ static fs_envblk_spec_t fs_envblk_spec[] = { static fs_envblk_t fs_envblk = NULL; +static int +read_env_block_var (const char *varname, const char *value, void *hook_data) +{ + grub_envblk_t *p_envblk = (grub_envblk_t *) hook_data; + off_t off; + size_t sz; + char *p, *buf; + FILE *fp; + + if (p_envblk == NULL || fs_envblk == NULL) + return 1; + + if (strcmp (varname, "env_block") != 0) + return 0; + + off = strtol (value, &p, 10); + if (*p == '+') + sz = strtol (p + 1, &p, 10); + else + return 0; + + if (*p != '\0' || sz == 0) + return 0; + + off <<= GRUB_DISK_SECTOR_BITS; + sz <<= GRUB_DISK_SECTOR_BITS; + + fp = grub_util_fopen (fs_envblk->dev, "rb"); + if (fp == NULL) + grub_util_error (_("cannot open `%s': %s"), fs_envblk->dev, strerror (errno)); + + if (fseek (fp, off, SEEK_SET) < 0) + grub_util_error (_("cannot seek `%s': %s"), fs_envblk->dev, strerror (errno)); + + buf = xmalloc (sz); + if ((fread (buf, 1, sz, fp)) != sz) + grub_util_error (_("cannot read `%s': %s"), fs_envblk->dev, strerror (errno)); + + fclose (fp); + + *p_envblk = grub_envblk_open (buf, sz); + + return 1; +} + +static void +create_env_on_block (void) +{ + FILE *fp; + char *buf; + const char *device; + off_t offset; + size_t size; + + if (fs_envblk == NULL) + return; + + device = fs_envblk->dev; + offset = fs_envblk->spec->offset; + size = fs_envblk->spec->size; + + fp = grub_util_fopen (device, "r+b"); + if (fp == NULL) + grub_util_error (_("cannot open `%s': %s"), device, strerror (errno)); + + buf = xmalloc (size); + memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1); + memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', size - sizeof (GRUB_ENVBLK_SIGNATURE) + 1); + + if (fseek (fp, offset, SEEK_SET) < 0) + grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno)); + + if (fwrite (buf, 1, size, fp) != size) + grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno)); + + grub_util_file_sync (fp); + free (buf); + fclose (fp); +} + +static grub_envblk_t +fs_envblk_open (grub_envblk_t envblk) +{ + grub_envblk_t envblk_on_block = NULL; + char *val; + off_t offset; + size_t size; + + if (envblk == NULL) + return NULL; + + offset = fs_envblk->spec->offset; + size = fs_envblk->spec->size; + + grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var); + + if (envblk_on_block != NULL && grub_envblk_size (envblk_on_block) == size) + return envblk_on_block; + + create_env_on_block (); + + offset = offset >> GRUB_DISK_SECTOR_BITS; + size = (size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS; + + val = xasprintf ("%lld+%zu", (long long) offset, size); + if (grub_envblk_set (envblk, "env_block", val) == 0) + grub_util_error ("%s", _("environment block too small")); + grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var); + free (val); + + return envblk_on_block; +} + static grub_envblk_t open_envblk_file (const char *name) { @@ -401,6 +526,7 @@ probe_fs_envblk (fs_envblk_spec_t *spec) fs_envblk = xmalloc (sizeof (fs_envblk_t)); fs_envblk->spec = p; fs_envblk->dev = devname; + fs_envblk->ops = &fs_envblk_ops; return fs_envblk; } } -- 2.51.0 _______________________________________________ Grub-devel mailing list [email protected] https://lists.gnu.org/mailman/listinfo/grub-devel
