[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 5/7] Add ZFS envblock functions
From: |
Paul Dagnelie |
Subject: |
[PATCH 5/7] Add ZFS envblock functions |
Date: |
Wed, 11 Mar 2020 10:37:13 -0700 |
This patch adds a ZFS implementation of the fs_envblk_* functions. These
functions will be used to load the grubenv block from a padding area in the
label of ZFS. This padding area is protected by an embedded checksum, and
multiple copies are stored on each disk. This should provide sufficient
reliability for most use cases. Since this area is not part of the block tree,
it can be easily modified at boot time with a simple recalculation of the
embedded checksum, enabling save_env to work for ZFS boot filesystems.
Note that the open() function is doing the read; this is because the ZFS
envblk logic doesn't store the file size, so the only way to retrieve it is to
determine the length of the file using strlen. If this is considered too poor
of an approach to be acceptable, format changes can still be made to the
structure of the padding area.
Signed-off-by: Paul Dagnelie <address@hidden>
---
grub-core/fs/zfs/zfs.c | 134 ++++++++++++++++++++++++++++++++---
include/grub/zfs/vdev_impl.h | 33 +++++----
2 files changed, 139 insertions(+), 28 deletions(-)
diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
index 2f72e42bf..d741426cd 100644
--- a/grub-core/fs/zfs/zfs.c
+++ b/grub-core/fs/zfs/zfs.c
@@ -30,6 +30,7 @@
*
*/
+#include <stddef.h>
#include <grub/err.h>
#include <grub/file.h>
#include <grub/mm.h>
@@ -1146,7 +1147,6 @@ scan_disk (grub_device_t dev, struct grub_zfs_data *data,
{
int label = 0;
uberblock_phys_t *ub_array, *ubbest = NULL;
- vdev_boot_header_t *bh;
grub_err_t err;
int vdevnum;
struct grub_zfs_device_desc desc;
@@ -1155,13 +1155,6 @@ scan_disk (grub_device_t dev, struct grub_zfs_data *data,
if (!ub_array)
return grub_errno;
- bh = grub_malloc (VDEV_BOOT_HEADER_SIZE);
- if (!bh)
- {
- grub_free (ub_array);
- return grub_errno;
- }
-
vdevnum = VDEV_LABELS;
desc.dev = dev;
@@ -1175,7 +1168,7 @@ scan_disk (grub_device_t dev, struct grub_zfs_data *data,
{
desc.vdev_phys_sector
= label * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT)
- + ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SPA_MINBLOCKSHIFT)
+ + ((VDEV_PAD_SIZE * 2) >> SPA_MINBLOCKSHIFT)
+ (label < VDEV_LABELS / 2 ? 0 :
ALIGN_DOWN (grub_disk_get_size (dev->disk), sizeof (vdev_label_t))
- VDEV_LABELS * (sizeof (vdev_label_t) >> SPA_MINBLOCKSHIFT));
@@ -1184,6 +1177,7 @@ scan_disk (grub_device_t dev, struct grub_zfs_data *data,
err = grub_disk_read (dev->disk, desc.vdev_phys_sector
+ (VDEV_PHYS_SIZE >> SPA_MINBLOCKSHIFT),
0, VDEV_UBERBLOCK_RING, (char *) ub_array);
+
if (err)
{
grub_errno = GRUB_ERR_NONE;
@@ -1219,12 +1213,10 @@ scan_disk (grub_device_t dev, struct grub_zfs_data
*data,
continue;
#endif
grub_free (ub_array);
- grub_free (bh);
return GRUB_ERR_NONE;
}
grub_free (ub_array);
- grub_free (bh);
return grub_error (GRUB_ERR_BAD_FS, "couldn't find a valid label");
}
@@ -3765,6 +3757,60 @@ zfs_mtime (grub_device_t device, grub_int32_t *mt)
return GRUB_ERR_NONE;
}
+static grub_err_t
+zfs_devs_read_zbt (struct grub_zfs_data *data, grub_uint64_t offset, char
*buf, grub_size_t len)
+{
+ grub_err_t err = GRUB_ERR_NONE;
+ zio_cksum_t zc;
+ unsigned int i;
+ ZIO_SET_CHECKSUM(&zc, offset, 0, 0, 0);
+
+ for (i = 0; i < data->n_devices_attached; i++)
+ {
+ err = grub_disk_read (data->devices_attached[i].dev->disk,
+ offset >> SPA_MINBLOCKSHIFT,
+ offset & ((1 << SPA_MINBLOCKSHIFT) - 1),
+ len, buf);
+ if (err)
+ continue;
+ ZIO_SET_CHECKSUM(&zc, offset, 0, 0, 0);
+ err = zio_checksum_verify (zc, ZIO_CHECKSUM_LABEL,
GRUB_ZFS_LITTLE_ENDIAN,
+ buf, len);
+ if (!err)
+ return GRUB_ERR_NONE;
+ }
+ return err;
+}
+
+static grub_err_t
+grub_zfs_envblk_open (struct grub_file *file)
+{
+ grub_err_t err;
+ struct grub_zfs_data *data;
+ vdev_boot_envblock_t *vbe;
+ int l;
+
+ file->offset = 0;
+ data = zfs_mount (file->device);
+ file->data = data;
+ data->file_buf = grub_malloc (sizeof (vdev_boot_envblock_t));
+ for (l = 0; l < VDEV_LABELS / 2; l++)
+ {
+ grub_uint64_t offset = l * sizeof (vdev_label_t) + offsetof
(vdev_label_t, vl_be);
+
+ err = zfs_devs_read_zbt (data, offset, data->file_buf,
+ sizeof (vdev_boot_envblock_t));
+ if (err == GRUB_ERR_NONE)
+ {
+ vbe = (vdev_boot_envblock_t *)data->file_buf;
+ file->size = grub_strlen (vbe->vbe_bootenv);
+ return err;
+ }
+ }
+ zfs_unmount (data);
+ return err;
+}
+
/*
* zfs_open() locates a file in the rootpool by following the
* MOS and places the dnode of the file in the memory address DNODE.
@@ -3850,6 +3896,19 @@ grub_zfs_open (struct grub_file *file, const char
*fsfilename)
return GRUB_ERR_NONE;
}
+static grub_ssize_t
+grub_zfs_envblk_read (grub_file_t file, char *buf, grub_size_t len)
+{
+ struct grub_zfs_data *data = (struct grub_zfs_data *) file->data;
+ grub_ssize_t olen = len;
+ grub_uint64_t offset = file->offset + offsetof (vdev_boot_envblock_t,
vbe_bootenv);
+
+ if (len + file->offset > file->size)
+ olen = file->size - file->offset;
+ grub_memmove (buf, data->file_buf + offset, olen);
+ return olen;
+}
+
static grub_ssize_t
grub_zfs_read (grub_file_t file, char *buf, grub_size_t len)
{
@@ -3859,6 +3918,9 @@ grub_zfs_read (grub_file_t file, char *buf, grub_size_t
len)
grub_size_t read;
grub_err_t err;
+ if (grub_file_envblk (file))
+ return grub_zfs_envblk_read (file, buf, len);
+
/*
* If offset is in memory, move it into the buffer provided and return.
*/
@@ -3924,6 +3986,52 @@ grub_zfs_read (grub_file_t file, char *buf, grub_size_t
len)
return len;
}
+static grub_err_t
+grub_zfs_envblk_write (struct grub_file *file, char *buf, grub_size_t len)
+{
+ grub_uint64_t offset = file->offset + offsetof (vdev_boot_envblock_t,
vbe_bootenv);
+ grub_err_t err = GRUB_ERR_NONE;
+ struct grub_zfs_data *data = file->data;
+ unsigned int i, l;
+ zio_cksum_t cksum;
+ vdev_boot_envblock_t *vbe = (vdev_boot_envblock_t *)data->file_buf;
+
+ if (len + file->offset > file->size)
+ return GRUB_ERR_OUT_OF_RANGE;
+
+ grub_memmove (data->file_buf + offset, buf, len);
+
+ for (l = 0; l < VDEV_LABELS / 2; l++)
+ {
+ offset = l * sizeof (vdev_label_t) + offsetof (vdev_label_t, vl_be);
+ vbe->vbe_zbt.zec_magic = ZEC_MAGIC;
+ ZIO_SET_CHECKSUM(&vbe->vbe_zbt.zec_cksum, offset, 0, 0, 0);
+ vbe->vbe_zbt.zec_magic = ZEC_MAGIC;
+ zio_checksum_SHA256(vbe, VDEV_PAD_SIZE, GRUB_ZFS_LITTLE_ENDIAN, &cksum);
+ vbe->vbe_zbt.zec_cksum = cksum;
+
+ for (i = 0; i < data->n_devices_attached; i++)
+ {
+ struct grub_zfs_device_desc *desc = &data->devices_attached[i];
+ int num_sectors = VDEV_PAD_SIZE >> desc->ashift;
+ int label_seek = l * sizeof (vdev_label_t) >> desc->ashift;
+ int j;
+
+ for (j = 0; j < num_sectors; j++)
+ {
+ grub_err_t err2;
+
+ err2 = grub_disk_write (desc->dev->disk, label_seek + num_sectors
+ j, 0,
+ 1 << desc->ashift,
+ data->file_buf + (j << desc->ashift));
+ if (err2 != GRUB_ERR_NONE)
+ err = err2;
+ }
+ }
+ }
+ return err;
+}
+
static grub_err_t
grub_zfs_close (grub_file_t file)
{
@@ -4360,6 +4468,10 @@ static struct grub_fs grub_zfs_fs = {
.reserved_first_sector = 1,
.blocklist_install = 0,
#endif
+ .fs_envblk_open = grub_zfs_envblk_open,
+ .fs_envblk_read = grub_zfs_read,
+ .fs_envblk_write = grub_zfs_envblk_write,
+ .fs_envblk_close = grub_zfs_close,
.next = 0
};
diff --git a/include/grub/zfs/vdev_impl.h b/include/grub/zfs/vdev_impl.h
index 9b5f0a7a8..48ec59003 100644
--- a/include/grub/zfs/vdev_impl.h
+++ b/include/grub/zfs/vdev_impl.h
@@ -23,34 +23,33 @@
#ifndef _SYS_VDEV_IMPL_H
#define _SYS_VDEV_IMPL_H
-#define VDEV_SKIP_SIZE (8 << 10)
-#define VDEV_BOOT_HEADER_SIZE (8 << 10)
+#define VDEV_PAD_SIZE (8 << 10)
#define VDEV_PHYS_SIZE (112 << 10)
#define VDEV_UBERBLOCK_RING (128 << 10)
-/* ZFS boot block */
-#define VDEV_BOOT_MAGIC 0x2f5b007b10cULL
-#define VDEV_BOOT_VERSION 1 /* version number
*/
-
-typedef struct vdev_boot_header {
- grub_uint64_t vb_magic; /* VDEV_BOOT_MAGIC */
- grub_uint64_t vb_version; /* VDEV_BOOT_VERSION */
- grub_uint64_t vb_offset; /* start offset (bytes) */
- grub_uint64_t vb_size; /* size (bytes) */
- char vb_pad[VDEV_BOOT_HEADER_SIZE - 4 * sizeof
(grub_uint64_t)];
-} vdev_boot_header_t;
-
typedef struct vdev_phys {
char vp_nvlist[VDEV_PHYS_SIZE - sizeof (zio_eck_t)];
zio_eck_t vp_zbt;
} vdev_phys_t;
+typedef enum vbe_vers {
+ VB_RAW,
+ VB_NVLIST
+} vb_evers_t;
+
+typedef struct vdev_boot_envblock {
+ grub_uint64_t vbe_version;
+ char vbe_bootenv[VDEV_PAD_SIZE - sizeof (grub_uint64_t) -
+ sizeof (zio_eck_t)];
+ zio_eck_t vbe_zbt;
+} vdev_boot_envblock_t;
+
typedef struct vdev_label {
- char vl_pad[VDEV_SKIP_SIZE]; /* 8K */
- vdev_boot_header_t vl_boot_header; /* 8K */
+ char vl_pad1[VDEV_PAD_SIZE]; /* 8K */
+ vdev_boot_envblock_t vl_be; /* 8K */
vdev_phys_t vl_vdev_phys; /* 112K */
char vl_uberblock[VDEV_UBERBLOCK_RING]; /* 128K */
-} vdev_label_t; /* 256K
total */
+} vdev_label_t; /* 256K total */
/*
* Size and offset of embedded boot loader region on each label.
--
2.19.0
- [PATCH 0/7] ZFS/other CoW FS save_env support, Paul Dagnelie, 2020/03/11
- [PATCH 1/7] Factor out file filter function for reuse, Paul Dagnelie, 2020/03/11
- [PATCH 2/7] Add support for special envblk functions in GRUB, Paul Dagnelie, 2020/03/11
- [PATCH 3/7] Factor out envblk buffer creation for reuse, Paul Dagnelie, 2020/03/11
- [PATCH 4/7] Use envblk file and fs functions to implement reading the grubenv file from special FS regions, Paul Dagnelie, 2020/03/11
- [PATCH 5/7] Add ZFS envblock functions,
Paul Dagnelie <=
- [PATCH 6/7] Refactor default envblk size out for reuse, Paul Dagnelie, 2020/03/11
- [PATCH 7/7] Update editenv to support editing zfs envblock, fix whitespace, and autodetect bootenv support in libzfs, Paul Dagnelie, 2020/03/11
- Re: [PATCH 0/7] ZFS/other CoW FS save_env support, Daniel Kiper, 2020/03/25