zfs label bootenv should store data as nvlist
nvlist does allow us to support different data types and systems. To encapsulate user data to/from nvlist, the libzfsbootenv library is provided. Reviewed-by: Arvind Sankar <nivedita@alum.mit.edu> Reviewed-by: Allan Jude <allan@klarasystems.com> Reviewed-by: Paul Dagnelie <pcd@delphix.com> Reviewed-by: Igor Kozhukhov <igor@dilos.org> Signed-off-by: Toomas Soome <tsoome@me.com> Closes #10774
This commit is contained in:
parent
37325e4749
commit
1db9e6e4e4
|
@ -161,6 +161,8 @@ AC_CONFIG_FILES([
|
||||||
lib/libuutil/Makefile
|
lib/libuutil/Makefile
|
||||||
lib/libzfs/Makefile
|
lib/libzfs/Makefile
|
||||||
lib/libzfs/libzfs.pc
|
lib/libzfs/libzfs.pc
|
||||||
|
lib/libzfsbootenv/Makefile
|
||||||
|
lib/libzfsbootenv/libzfsbootenv.pc
|
||||||
lib/libzfs_core/Makefile
|
lib/libzfs_core/Makefile
|
||||||
lib/libzfs_core/libzfs_core.pc
|
lib/libzfs_core/libzfs_core.pc
|
||||||
lib/libzpool/Makefile
|
lib/libzpool/Makefile
|
||||||
|
|
|
@ -15,6 +15,7 @@ USER_H = \
|
||||||
libuutil.h \
|
libuutil.h \
|
||||||
libuutil_impl.h \
|
libuutil_impl.h \
|
||||||
libzfs.h \
|
libzfs.h \
|
||||||
|
libzfsbootenv.h \
|
||||||
libzfs_core.h \
|
libzfs_core.h \
|
||||||
libzfs_impl.h \
|
libzfs_impl.h \
|
||||||
libzutil.h \
|
libzutil.h \
|
||||||
|
|
|
@ -892,8 +892,8 @@ extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **,
|
||||||
* Label manipulation.
|
* Label manipulation.
|
||||||
*/
|
*/
|
||||||
extern int zpool_clear_label(int);
|
extern int zpool_clear_label(int);
|
||||||
extern int zpool_set_bootenv(zpool_handle_t *, const char *);
|
extern int zpool_set_bootenv(zpool_handle_t *, const nvlist_t *);
|
||||||
extern int zpool_get_bootenv(zpool_handle_t *, char *, size_t, off_t);
|
extern int zpool_get_bootenv(zpool_handle_t *, nvlist_t **);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Management interfaces for SMB ACL files
|
* Management interfaces for SMB ACL files
|
||||||
|
|
|
@ -135,7 +135,7 @@ int lzc_wait(const char *, zpool_wait_activity_t, boolean_t *);
|
||||||
int lzc_wait_tag(const char *, zpool_wait_activity_t, uint64_t, boolean_t *);
|
int lzc_wait_tag(const char *, zpool_wait_activity_t, uint64_t, boolean_t *);
|
||||||
int lzc_wait_fs(const char *, zfs_wait_activity_t, boolean_t *);
|
int lzc_wait_fs(const char *, zfs_wait_activity_t, boolean_t *);
|
||||||
|
|
||||||
int lzc_set_bootenv(const char *, const char *);
|
int lzc_set_bootenv(const char *, const nvlist_t *);
|
||||||
int lzc_get_bootenv(const char *, nvlist_t **);
|
int lzc_get_bootenv(const char *, nvlist_t **);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* This file and its contents are supplied under the terms of the
|
||||||
|
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||||
|
* You may only use this file in accordance with the terms of version
|
||||||
|
* 1.0 of the CDDL.
|
||||||
|
*
|
||||||
|
* A full copy of the text of the CDDL should have accompanied this
|
||||||
|
* source. A copy of the CDDL is also available via the Internet at
|
||||||
|
* http://www.illumos.org/license/CDDL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2020 Toomas Soome <tsoome@me.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LIBZFSBOOTENV_H
|
||||||
|
#define _LIBZFSBOOTENV_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum lzbe_flags {
|
||||||
|
lzbe_add, /* add data to existing nvlist */
|
||||||
|
lzbe_replace /* replace current nvlist */
|
||||||
|
} lzbe_flags_t;
|
||||||
|
|
||||||
|
extern int lzbe_nvlist_get(const char *, const char *, void **);
|
||||||
|
extern int lzbe_nvlist_set(const char *, const char *, void *);
|
||||||
|
extern void lzbe_nvlist_free(void *);
|
||||||
|
extern int lzbe_add_pair(void *, const char *, const char *, void *, size_t);
|
||||||
|
extern int lzbe_remove_pair(void *, const char *);
|
||||||
|
extern int lzbe_set_boot_device(const char *, lzbe_flags_t, const char *);
|
||||||
|
extern int lzbe_get_boot_device(const char *, char **);
|
||||||
|
extern int lzbe_bootenv_print(const char *, const char *, FILE *);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _LIBZFSBOOTENV_H */
|
|
@ -2,6 +2,7 @@ KERNEL_H = \
|
||||||
freebsd_crypto.h \
|
freebsd_crypto.h \
|
||||||
sha2.h \
|
sha2.h \
|
||||||
vdev_os.h \
|
vdev_os.h \
|
||||||
|
zfs_bootenv_os.h \
|
||||||
zfs_context_os.h \
|
zfs_context_os.h \
|
||||||
zfs_ctldir.h \
|
zfs_ctldir.h \
|
||||||
zfs_dir.h \
|
zfs_dir.h \
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* This file and its contents are supplied under the terms of the
|
||||||
|
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||||
|
* You may only use this file in accordance with the terms of version
|
||||||
|
* 1.0 of the CDDL.
|
||||||
|
*
|
||||||
|
* A full copy of the text of the CDDL should have accompanied this
|
||||||
|
* source. A copy of the CDDL is also available via the Internet at
|
||||||
|
* http://www.illumos.org/license/CDDL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2020 Toomas Soome <tsoome@me.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ZFS_BOOTENV_OS_H
|
||||||
|
#define _ZFS_BOOTENV_OS_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BOOTENV_OS BE_FREEBSD_VENDOR
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ZFS_BOOTENV_OS_H */
|
|
@ -16,6 +16,7 @@ KERNEL_H = \
|
||||||
trace_zil.h \
|
trace_zil.h \
|
||||||
trace_zio.h \
|
trace_zio.h \
|
||||||
trace_zrlock.h \
|
trace_zrlock.h \
|
||||||
|
zfs_bootenv_os.h \
|
||||||
zfs_context_os.h \
|
zfs_context_os.h \
|
||||||
zfs_ctldir.h \
|
zfs_ctldir.h \
|
||||||
zfs_dir.h \
|
zfs_dir.h \
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* This file and its contents are supplied under the terms of the
|
||||||
|
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||||
|
* You may only use this file in accordance with the terms of version
|
||||||
|
* 1.0 of the CDDL.
|
||||||
|
*
|
||||||
|
* A full copy of the text of the CDDL should have accompanied this
|
||||||
|
* source. A copy of the CDDL is also available via the Internet at
|
||||||
|
* http://www.illumos.org/license/CDDL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2020 Toomas Soome <tsoome@me.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ZFS_BOOTENV_OS_H
|
||||||
|
#define _ZFS_BOOTENV_OS_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BOOTENV_OS BE_LINUX_VENDOR
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ZFS_BOOTENV_OS_H */
|
|
@ -102,6 +102,7 @@ COMMON_H = \
|
||||||
zcp_set.h \
|
zcp_set.h \
|
||||||
zfeature.h \
|
zfeature.h \
|
||||||
zfs_acl.h \
|
zfs_acl.h \
|
||||||
|
zfs_bootenv.h \
|
||||||
zfs_context.h \
|
zfs_context.h \
|
||||||
zfs_debug.h \
|
zfs_debug.h \
|
||||||
zfs_delay.h \
|
zfs_delay.h \
|
||||||
|
|
|
@ -1336,8 +1336,8 @@ typedef enum zfs_ioc {
|
||||||
ZFS_IOC_NEXTBOOT, /* 0x84 (FreeBSD) */
|
ZFS_IOC_NEXTBOOT, /* 0x84 (FreeBSD) */
|
||||||
ZFS_IOC_JAIL, /* 0x85 (FreeBSD) */
|
ZFS_IOC_JAIL, /* 0x85 (FreeBSD) */
|
||||||
ZFS_IOC_UNJAIL, /* 0x86 (FreeBSD) */
|
ZFS_IOC_UNJAIL, /* 0x86 (FreeBSD) */
|
||||||
ZFS_IOC_SET_BOOTENV, /* 0x87 (Linux) */
|
ZFS_IOC_SET_BOOTENV, /* 0x87 */
|
||||||
ZFS_IOC_GET_BOOTENV, /* 0x88 (Linux) */
|
ZFS_IOC_GET_BOOTENV, /* 0x88 */
|
||||||
ZFS_IOC_LAST
|
ZFS_IOC_LAST
|
||||||
} zfs_ioc_t;
|
} zfs_ioc_t;
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,7 @@ extern void vdev_config_generate_stats(vdev_t *vd, nvlist_t *nv);
|
||||||
extern void vdev_label_write(zio_t *zio, vdev_t *vd, int l, abd_t *buf, uint64_t
|
extern void vdev_label_write(zio_t *zio, vdev_t *vd, int l, abd_t *buf, uint64_t
|
||||||
offset, uint64_t size, zio_done_func_t *done, void *priv, int flags);
|
offset, uint64_t size, zio_done_func_t *done, void *priv, int flags);
|
||||||
extern int vdev_label_read_bootenv(vdev_t *, nvlist_t *);
|
extern int vdev_label_read_bootenv(vdev_t *, nvlist_t *);
|
||||||
extern int vdev_label_write_bootenv(vdev_t *, char *);
|
extern int vdev_label_write_bootenv(vdev_t *, nvlist_t *);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VDEV_LABEL_CREATE, /* create/add a new device */
|
VDEV_LABEL_CREATE, /* create/add a new device */
|
||||||
|
|
|
@ -476,7 +476,16 @@ typedef struct vdev_phys {
|
||||||
} vdev_phys_t;
|
} vdev_phys_t;
|
||||||
|
|
||||||
typedef enum vbe_vers {
|
typedef enum vbe_vers {
|
||||||
/* The bootenv file is stored as ascii text in the envblock */
|
/*
|
||||||
|
* The bootenv file is stored as ascii text in the envblock.
|
||||||
|
* It is used by the GRUB bootloader used on Linux to store the
|
||||||
|
* contents of the grubenv file. The file is stored as raw ASCII,
|
||||||
|
* and is protected by an embedded checksum. By default, GRUB will
|
||||||
|
* check if the boot filesystem supports storing the environment data
|
||||||
|
* in a special location, and if so, will invoke filesystem specific
|
||||||
|
* logic to retrieve it. This can be overriden by a variable, should
|
||||||
|
* the user so desire.
|
||||||
|
*/
|
||||||
VB_RAW = 0,
|
VB_RAW = 0,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* This file and its contents are supplied under the terms of the
|
||||||
|
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||||
|
* You may only use this file in accordance with the terms of version
|
||||||
|
* 1.0 of the CDDL.
|
||||||
|
*
|
||||||
|
* A full copy of the text of the CDDL should have accompanied this
|
||||||
|
* source. A copy of the CDDL is also available via the Internet at
|
||||||
|
* http://www.illumos.org/license/CDDL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2020 Toomas Soome <tsoome@me.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ZFS_BOOTENV_H
|
||||||
|
#define _ZFS_BOOTENV_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define macros for label bootenv nvlist pair keys.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BOOTENV_VERSION "version"
|
||||||
|
|
||||||
|
#define BE_ILLUMOS_VENDOR "illumos"
|
||||||
|
#define BE_FREEBSD_VENDOR "freebsd"
|
||||||
|
#define BE_GRUB_VENDOR "grub"
|
||||||
|
#define BE_LINUX_VENDOR "linux"
|
||||||
|
|
||||||
|
#include <sys/zfs_bootenv_os.h>
|
||||||
|
|
||||||
|
#define GRUB_ENVMAP BE_GRUB_VENDOR ":" "envmap"
|
||||||
|
|
||||||
|
#define FREEBSD_BOOTONCE BE_FREEBSD_VENDOR ":" "bootonce"
|
||||||
|
#define FREEBSD_BOOTONCE_USED BE_FREEBSD_VENDOR ":" "bootonce-used"
|
||||||
|
#define FREEBSD_NVSTORE BE_FREEBSD_VENDOR ":" "nvstore"
|
||||||
|
#define ILLUMOS_BOOTONCE BE_ILLUMOS_VENDOR ":" "bootonce"
|
||||||
|
#define ILLUMOS_BOOTONCE_USED BE_ILLUMOS_VENDOR ":" "bootonce-used"
|
||||||
|
#define ILLUMOS_NVSTORE BE_ILLUMOS_VENDOR ":" "nvstore"
|
||||||
|
|
||||||
|
#define OS_BOOTONCE BOOTENV_OS ":" "bootonce"
|
||||||
|
#define OS_BOOTONCE_USED BOOTENV_OS ":" "bootonce-used"
|
||||||
|
#define OS_NVSTORE BOOTENV_OS ":" "nvstore"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ZFS_BOOTENV_H */
|
|
@ -13,6 +13,6 @@ SUBDIRS += libnvpair
|
||||||
# libzutil depends on libefi if present
|
# libzutil depends on libefi if present
|
||||||
SUBDIRS += libzutil libunicode
|
SUBDIRS += libzutil libunicode
|
||||||
|
|
||||||
# These four libraries, which are installed as the final build product,
|
# These five libraries, which are installed as the final build product,
|
||||||
# incorporate the eight convenience libraries given above.
|
# incorporate the eight convenience libraries given above.
|
||||||
SUBDIRS += libuutil libzfs_core libzfs libzpool
|
SUBDIRS += libuutil libzfs_core libzfs libzpool libzfsbootenv
|
||||||
|
|
|
@ -4495,7 +4495,7 @@ zpool_wait_status(zpool_handle_t *zhp, zpool_wait_activity_t activity,
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
zpool_set_bootenv(zpool_handle_t *zhp, const char *envmap)
|
zpool_set_bootenv(zpool_handle_t *zhp, const nvlist_t *envmap)
|
||||||
{
|
{
|
||||||
int error = lzc_set_bootenv(zhp->zpool_name, envmap);
|
int error = lzc_set_bootenv(zhp->zpool_name, envmap);
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
|
@ -4508,24 +4508,20 @@ zpool_set_bootenv(zpool_handle_t *zhp, const char *envmap)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
zpool_get_bootenv(zpool_handle_t *zhp, char *outbuf, size_t size, off_t offset)
|
zpool_get_bootenv(zpool_handle_t *zhp, nvlist_t **nvlp)
|
||||||
{
|
{
|
||||||
nvlist_t *nvl = NULL;
|
nvlist_t *nvl;
|
||||||
int error = lzc_get_bootenv(zhp->zpool_name, &nvl);
|
int error;
|
||||||
|
|
||||||
|
nvl = NULL;
|
||||||
|
error = lzc_get_bootenv(zhp->zpool_name, &nvl);
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
(void) zpool_standard_error_fmt(zhp->zpool_hdl, error,
|
(void) zpool_standard_error_fmt(zhp->zpool_hdl, error,
|
||||||
dgettext(TEXT_DOMAIN,
|
dgettext(TEXT_DOMAIN,
|
||||||
"error getting bootenv in pool '%s'"), zhp->zpool_name);
|
"error getting bootenv in pool '%s'"), zhp->zpool_name);
|
||||||
return (-1);
|
} else {
|
||||||
}
|
*nvlp = nvl;
|
||||||
char *envmap = fnvlist_lookup_string(nvl, "envmap");
|
|
||||||
if (offset >= strlen(envmap)) {
|
|
||||||
fnvlist_free(nvl);
|
|
||||||
return (0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(outbuf, envmap + offset, size);
|
return (error);
|
||||||
int bytes = MIN(strlen(envmap + offset), size);
|
|
||||||
fnvlist_free(nvl);
|
|
||||||
return (bytes);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1625,13 +1625,9 @@ lzc_wait_fs(const char *fs, zfs_wait_activity_t activity, boolean_t *waited)
|
||||||
* Set the bootenv contents for the given pool.
|
* Set the bootenv contents for the given pool.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
lzc_set_bootenv(const char *pool, const char *env)
|
lzc_set_bootenv(const char *pool, const nvlist_t *env)
|
||||||
{
|
{
|
||||||
nvlist_t *args = fnvlist_alloc();
|
return (lzc_ioctl(ZFS_IOC_SET_BOOTENV, pool, (nvlist_t *)env, NULL));
|
||||||
fnvlist_add_string(args, "envmap", env);
|
|
||||||
int error = lzc_ioctl(ZFS_IOC_SET_BOOTENV, pool, args, NULL);
|
|
||||||
fnvlist_free(args);
|
|
||||||
return (error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
/libzfsbootenv.pc
|
|
@ -0,0 +1,32 @@
|
||||||
|
include $(top_srcdir)/config/Rules.am
|
||||||
|
|
||||||
|
pkgconfig_DATA = libzfsbootenv.pc
|
||||||
|
|
||||||
|
lib_LTLIBRARIES = libzfsbootenv.la
|
||||||
|
|
||||||
|
if BUILD_FREEBSD
|
||||||
|
DEFAULT_INCLUDES += -I$(top_srcdir)/include/os/freebsd/zfs
|
||||||
|
endif
|
||||||
|
if BUILD_LINUX
|
||||||
|
DEFAULT_INCLUDES += -I$(top_srcdir)/include/os/linux/zfs
|
||||||
|
endif
|
||||||
|
|
||||||
|
USER_C = \
|
||||||
|
lzbe_device.c \
|
||||||
|
lzbe_pair.c \
|
||||||
|
lzbe_util.c
|
||||||
|
|
||||||
|
dist_libzfsbootenv_la_SOURCES = \
|
||||||
|
$(USER_C)
|
||||||
|
|
||||||
|
libzfsbootenv_la_LIBADD = \
|
||||||
|
$(abs_top_builddir)/lib/libzfs/libzfs.la \
|
||||||
|
$(abs_top_builddir)/lib/libnvpair/libnvpair.la
|
||||||
|
|
||||||
|
libzfsbootenv_la_LDFLAGS =
|
||||||
|
|
||||||
|
if !ASAN_ENABLED
|
||||||
|
libzfsbootenv_la_LDFLAGS += -Wl,-z,defs
|
||||||
|
endif
|
||||||
|
|
||||||
|
libzfsbootenv_la_LDFLAGS += -version-info 1:0:0
|
|
@ -0,0 +1,12 @@
|
||||||
|
prefix=@prefix@
|
||||||
|
exec_prefix=@exec_prefix@
|
||||||
|
libdir=@libdir@
|
||||||
|
includedir=@includedir@
|
||||||
|
|
||||||
|
Name: libzfsbootenv
|
||||||
|
Description: LibZFSBootENV library
|
||||||
|
Version: @VERSION@
|
||||||
|
URL: https://zfsonlinux.org
|
||||||
|
Requires: libzfs libnvpair
|
||||||
|
Cflags: -I${includedir}
|
||||||
|
Libs: -L${libdir} -lzfsbootenv
|
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
* This file and its contents are supplied under the terms of the
|
||||||
|
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||||
|
* You may only use this file in accordance with the terms of version
|
||||||
|
* 1.0 of the CDDL.
|
||||||
|
*
|
||||||
|
* A full copy of the text of the CDDL should have accompanied this
|
||||||
|
* source. A copy of the CDDL is also available via the Internet at
|
||||||
|
* http://www.illumos.org/license/CDDL.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright 2020 Toomas Soome <tsoome@me.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <libzfs.h>
|
||||||
|
#include <libzfsbootenv.h>
|
||||||
|
#include <sys/zfs_bootenv.h>
|
||||||
|
#include <sys/vdev_impl.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store device name to zpool label bootenv area.
|
||||||
|
* This call will set bootenv version to VB_NVLIST, if bootenv currently
|
||||||
|
* does contain other version, then old data will be replaced.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lzbe_set_boot_device(const char *pool, lzbe_flags_t flag, const char *device)
|
||||||
|
{
|
||||||
|
libzfs_handle_t *hdl;
|
||||||
|
zpool_handle_t *zphdl;
|
||||||
|
nvlist_t *nv;
|
||||||
|
char *descriptor;
|
||||||
|
uint64_t version;
|
||||||
|
int rv = -1;
|
||||||
|
|
||||||
|
if (pool == NULL || *pool == '\0')
|
||||||
|
return (rv);
|
||||||
|
|
||||||
|
if ((hdl = libzfs_init()) == NULL)
|
||||||
|
return (rv);
|
||||||
|
|
||||||
|
zphdl = zpool_open(hdl, pool);
|
||||||
|
if (zphdl == NULL) {
|
||||||
|
libzfs_fini(hdl);
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (flag) {
|
||||||
|
case lzbe_add:
|
||||||
|
rv = zpool_get_bootenv(zphdl, &nv);
|
||||||
|
if (rv == 0) {
|
||||||
|
/*
|
||||||
|
* We got the nvlist, check for version.
|
||||||
|
* if version is missing or is not VB_NVLIST,
|
||||||
|
* create new list.
|
||||||
|
*/
|
||||||
|
rv = nvlist_lookup_uint64(nv, BOOTENV_VERSION,
|
||||||
|
&version);
|
||||||
|
if (rv == 0 && version == VB_NVLIST)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Drop this nvlist */
|
||||||
|
fnvlist_free(nv);
|
||||||
|
}
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case lzbe_replace:
|
||||||
|
nv = fnvlist_alloc();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* version is mandatory */
|
||||||
|
fnvlist_add_uint64(nv, BOOTENV_VERSION, VB_NVLIST);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If device name is empty, remove boot device configuration.
|
||||||
|
*/
|
||||||
|
if ((device == NULL || *device == '\0')) {
|
||||||
|
if (nvlist_exists(nv, OS_BOOTONCE))
|
||||||
|
fnvlist_remove(nv, OS_BOOTONCE);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Use device name directly if it does start with
|
||||||
|
* prefix "zfs:". Otherwise, add prefix and sufix.
|
||||||
|
*/
|
||||||
|
if (strncmp(device, "zfs:", 4) == 0) {
|
||||||
|
fnvlist_add_string(nv, OS_BOOTONCE, device);
|
||||||
|
} else {
|
||||||
|
descriptor = NULL;
|
||||||
|
if (asprintf(&descriptor, "zfs:%s:", device) > 0)
|
||||||
|
fnvlist_add_string(nv, OS_BOOTONCE, descriptor);
|
||||||
|
else
|
||||||
|
rv = ENOMEM;
|
||||||
|
free(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = zpool_set_bootenv(zphdl, nv);
|
||||||
|
if (rv != 0)
|
||||||
|
fprintf(stderr, "%s\n", libzfs_error_description(hdl));
|
||||||
|
|
||||||
|
fnvlist_free(nv);
|
||||||
|
zpool_close(zphdl);
|
||||||
|
libzfs_fini(hdl);
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return boot device name from bootenv, if set.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lzbe_get_boot_device(const char *pool, char **device)
|
||||||
|
{
|
||||||
|
libzfs_handle_t *hdl;
|
||||||
|
zpool_handle_t *zphdl;
|
||||||
|
nvlist_t *nv;
|
||||||
|
char *val;
|
||||||
|
int rv = -1;
|
||||||
|
|
||||||
|
if (pool == NULL || *pool == '\0' || device == NULL)
|
||||||
|
return (rv);
|
||||||
|
|
||||||
|
if ((hdl = libzfs_init()) == NULL)
|
||||||
|
return (rv);
|
||||||
|
|
||||||
|
zphdl = zpool_open(hdl, pool);
|
||||||
|
if (zphdl == NULL) {
|
||||||
|
libzfs_fini(hdl);
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = zpool_get_bootenv(zphdl, &nv);
|
||||||
|
if (rv == 0) {
|
||||||
|
rv = nvlist_lookup_string(nv, OS_BOOTONCE, &val);
|
||||||
|
if (rv == 0) {
|
||||||
|
/*
|
||||||
|
* zfs device descriptor is in form of "zfs:dataset:",
|
||||||
|
* we only do need dataset name.
|
||||||
|
*/
|
||||||
|
if (strncmp(val, "zfs:", 4) == 0) {
|
||||||
|
val += 4;
|
||||||
|
val = strdup(val);
|
||||||
|
if (val != NULL) {
|
||||||
|
size_t len = strlen(val);
|
||||||
|
|
||||||
|
if (val[len - 1] == ':')
|
||||||
|
val[len - 1] = '\0';
|
||||||
|
*device = val;
|
||||||
|
} else {
|
||||||
|
rv = ENOMEM;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rv = EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nvlist_free(nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
zpool_close(zphdl);
|
||||||
|
libzfs_fini(hdl);
|
||||||
|
return (rv);
|
||||||
|
}
|
|
@ -0,0 +1,329 @@
|
||||||
|
/*
|
||||||
|
* This file and its contents are supplied under the terms of the
|
||||||
|
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||||
|
* You may only use this file in accordance with the terms of version
|
||||||
|
* 1.0 of the CDDL.
|
||||||
|
*
|
||||||
|
* A full copy of the text of the CDDL should have accompanied this
|
||||||
|
* source. A copy of the CDDL is also available via the Internet at
|
||||||
|
* http://www.illumos.org/license/CDDL.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright 2020 Toomas Soome <tsoome@me.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <libzfs.h>
|
||||||
|
#include <libzfsbootenv.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get or create nvlist. If key is not NULL, get nvlist from bootenv,
|
||||||
|
* otherwise return bootenv.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lzbe_nvlist_get(const char *pool, const char *key, void **ptr)
|
||||||
|
{
|
||||||
|
libzfs_handle_t *hdl;
|
||||||
|
zpool_handle_t *zphdl;
|
||||||
|
nvlist_t *nv;
|
||||||
|
int rv = -1;
|
||||||
|
|
||||||
|
if (pool == NULL || *pool == '\0')
|
||||||
|
return (rv);
|
||||||
|
|
||||||
|
if ((hdl = libzfs_init()) == NULL) {
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
zphdl = zpool_open(hdl, pool);
|
||||||
|
if (zphdl == NULL) {
|
||||||
|
libzfs_fini(hdl);
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = zpool_get_bootenv(zphdl, &nv);
|
||||||
|
if (rv == 0) {
|
||||||
|
nvlist_t *nvl, *dup;
|
||||||
|
|
||||||
|
if (key != NULL) {
|
||||||
|
rv = nvlist_lookup_nvlist(nv, key, &nvl);
|
||||||
|
if (rv == 0) {
|
||||||
|
rv = nvlist_dup(nvl, &dup, 0);
|
||||||
|
nvlist_free(nv);
|
||||||
|
if (rv == 0)
|
||||||
|
nv = dup;
|
||||||
|
else
|
||||||
|
nv = NULL;
|
||||||
|
} else {
|
||||||
|
nvlist_free(nv);
|
||||||
|
rv = nvlist_alloc(&nv, NV_UNIQUE_NAME, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*ptr = nv;
|
||||||
|
}
|
||||||
|
|
||||||
|
zpool_close(zphdl);
|
||||||
|
libzfs_fini(hdl);
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
lzbe_nvlist_set(const char *pool, const char *key, void *ptr)
|
||||||
|
{
|
||||||
|
libzfs_handle_t *hdl;
|
||||||
|
zpool_handle_t *zphdl;
|
||||||
|
nvlist_t *nv;
|
||||||
|
int rv = -1;
|
||||||
|
|
||||||
|
if (pool == NULL || *pool == '\0')
|
||||||
|
return (rv);
|
||||||
|
|
||||||
|
if ((hdl = libzfs_init()) == NULL) {
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
zphdl = zpool_open(hdl, pool);
|
||||||
|
if (zphdl == NULL) {
|
||||||
|
libzfs_fini(hdl);
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key != NULL) {
|
||||||
|
rv = zpool_get_bootenv(zphdl, &nv);
|
||||||
|
if (rv == 0) {
|
||||||
|
rv = nvlist_add_nvlist(nv, key, ptr);
|
||||||
|
if (rv == 0)
|
||||||
|
rv = zpool_set_bootenv(zphdl, nv);
|
||||||
|
nvlist_free(nv);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rv = zpool_set_bootenv(zphdl, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
zpool_close(zphdl);
|
||||||
|
libzfs_fini(hdl);
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free nvlist we got via lzbe_nvlist_get()
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
lzbe_nvlist_free(void *ptr)
|
||||||
|
{
|
||||||
|
nvlist_free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *typenames[] = {
|
||||||
|
"DATA_TYPE_UNKNOWN",
|
||||||
|
"DATA_TYPE_BOOLEAN",
|
||||||
|
"DATA_TYPE_BYTE",
|
||||||
|
"DATA_TYPE_INT16",
|
||||||
|
"DATA_TYPE_UINT16",
|
||||||
|
"DATA_TYPE_INT32",
|
||||||
|
"DATA_TYPE_UINT32",
|
||||||
|
"DATA_TYPE_INT64",
|
||||||
|
"DATA_TYPE_UINT64",
|
||||||
|
"DATA_TYPE_STRING",
|
||||||
|
"DATA_TYPE_BYTE_ARRAY",
|
||||||
|
"DATA_TYPE_INT16_ARRAY",
|
||||||
|
"DATA_TYPE_UINT16_ARRAY",
|
||||||
|
"DATA_TYPE_INT32_ARRAY",
|
||||||
|
"DATA_TYPE_UINT32_ARRAY",
|
||||||
|
"DATA_TYPE_INT64_ARRAY",
|
||||||
|
"DATA_TYPE_UINT64_ARRAY",
|
||||||
|
"DATA_TYPE_STRING_ARRAY",
|
||||||
|
"DATA_TYPE_HRTIME",
|
||||||
|
"DATA_TYPE_NVLIST",
|
||||||
|
"DATA_TYPE_NVLIST_ARRAY",
|
||||||
|
"DATA_TYPE_BOOLEAN_VALUE",
|
||||||
|
"DATA_TYPE_INT8",
|
||||||
|
"DATA_TYPE_UINT8",
|
||||||
|
"DATA_TYPE_BOOLEAN_ARRAY",
|
||||||
|
"DATA_TYPE_INT8_ARRAY",
|
||||||
|
"DATA_TYPE_UINT8_ARRAY"
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
nvpair_type_from_name(const char *name)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(typenames); i++) {
|
||||||
|
if (strcmp(name, typenames[i]) == 0)
|
||||||
|
return (i);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add pair defined by key, type and value into nvlist.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lzbe_add_pair(void *ptr, const char *key, const char *type, void *value,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
nvlist_t *nv = ptr;
|
||||||
|
data_type_t dt;
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
if (ptr == NULL || key == NULL || value == NULL)
|
||||||
|
return (rv);
|
||||||
|
|
||||||
|
if (type == NULL)
|
||||||
|
type = "DATA_TYPE_STRING";
|
||||||
|
dt = nvpair_type_from_name(type);
|
||||||
|
if (dt == DATA_TYPE_UNKNOWN)
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
switch (dt) {
|
||||||
|
case DATA_TYPE_BYTE:
|
||||||
|
if (size != sizeof (uint8_t)) {
|
||||||
|
rv = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rv = nvlist_add_byte(nv, key, *(uint8_t *)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_INT16:
|
||||||
|
if (size != sizeof (int16_t)) {
|
||||||
|
rv = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rv = nvlist_add_int16(nv, key, *(int16_t *)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT16:
|
||||||
|
if (size != sizeof (uint16_t)) {
|
||||||
|
rv = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rv = nvlist_add_uint16(nv, key, *(uint16_t *)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_INT32:
|
||||||
|
if (size != sizeof (int32_t)) {
|
||||||
|
rv = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rv = nvlist_add_int32(nv, key, *(int32_t *)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT32:
|
||||||
|
if (size != sizeof (uint32_t)) {
|
||||||
|
rv = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rv = nvlist_add_uint32(nv, key, *(uint32_t *)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_INT64:
|
||||||
|
if (size != sizeof (int64_t)) {
|
||||||
|
rv = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rv = nvlist_add_int64(nv, key, *(int64_t *)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT64:
|
||||||
|
if (size != sizeof (uint64_t)) {
|
||||||
|
rv = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rv = nvlist_add_uint64(nv, key, *(uint64_t *)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_STRING:
|
||||||
|
rv = nvlist_add_string(nv, key, value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_BYTE_ARRAY:
|
||||||
|
rv = nvlist_add_byte_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_INT16_ARRAY:
|
||||||
|
rv = nvlist_add_int16_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT16_ARRAY:
|
||||||
|
rv = nvlist_add_uint16_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_INT32_ARRAY:
|
||||||
|
rv = nvlist_add_int32_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT32_ARRAY:
|
||||||
|
rv = nvlist_add_uint32_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_INT64_ARRAY:
|
||||||
|
rv = nvlist_add_int64_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT64_ARRAY:
|
||||||
|
rv = nvlist_add_uint64_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_STRING_ARRAY:
|
||||||
|
rv = nvlist_add_string_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_NVLIST:
|
||||||
|
rv = nvlist_add_nvlist(nv, key, (nvlist_t *)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_NVLIST_ARRAY:
|
||||||
|
rv = nvlist_add_nvlist_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_BOOLEAN_VALUE:
|
||||||
|
if (size != sizeof (boolean_t)) {
|
||||||
|
rv = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rv = nvlist_add_boolean_value(nv, key, *(boolean_t *)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_INT8:
|
||||||
|
if (size != sizeof (int8_t)) {
|
||||||
|
rv = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rv = nvlist_add_int8(nv, key, *(int8_t *)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT8:
|
||||||
|
if (size != sizeof (uint8_t)) {
|
||||||
|
rv = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rv = nvlist_add_uint8(nv, key, *(uint8_t *)value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_BOOLEAN_ARRAY:
|
||||||
|
rv = nvlist_add_boolean_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_INT8_ARRAY:
|
||||||
|
rv = nvlist_add_int8_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DATA_TYPE_UINT8_ARRAY:
|
||||||
|
rv = nvlist_add_uint8_array(nv, key, value, size);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
lzbe_remove_pair(void *ptr, const char *key)
|
||||||
|
{
|
||||||
|
|
||||||
|
return (nvlist_remove_all(ptr, key));
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* This file and its contents are supplied under the terms of the
|
||||||
|
* Common Development and Distribution License ("CDDL"), version 1.0.
|
||||||
|
* You may only use this file in accordance with the terms of version
|
||||||
|
* 1.0 of the CDDL.
|
||||||
|
*
|
||||||
|
* A full copy of the text of the CDDL should have accompanied this
|
||||||
|
* source. A copy of the CDDL is also available via the Internet at
|
||||||
|
* http://www.illumos.org/license/CDDL.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright 2020 Toomas Soome <tsoome@me.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <libzfs.h>
|
||||||
|
#include <libzfsbootenv.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Output bootenv information.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
lzbe_bootenv_print(const char *pool, const char *nvlist, FILE *of)
|
||||||
|
{
|
||||||
|
nvlist_t *nv;
|
||||||
|
int rv = -1;
|
||||||
|
|
||||||
|
if (pool == NULL || *pool == '\0' || of == NULL)
|
||||||
|
return (rv);
|
||||||
|
|
||||||
|
rv = lzbe_nvlist_get(pool, nvlist, (void **)&nv);
|
||||||
|
if (rv == 0) {
|
||||||
|
nvlist_print(of, nv);
|
||||||
|
nvlist_free(nv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (rv);
|
||||||
|
}
|
|
@ -7,6 +7,13 @@ VPATH = \
|
||||||
$(top_srcdir)/module/os/linux/zfs \
|
$(top_srcdir)/module/os/linux/zfs \
|
||||||
$(top_srcdir)/lib/libzpool
|
$(top_srcdir)/lib/libzpool
|
||||||
|
|
||||||
|
if BUILD_FREEBSD
|
||||||
|
DEFAULT_INCLUDES += -I$(top_srcdir)/include/os/freebsd/zfs
|
||||||
|
endif
|
||||||
|
if BUILD_LINUX
|
||||||
|
DEFAULT_INCLUDES += -I$(top_srcdir)/include/os/linux/zfs
|
||||||
|
endif
|
||||||
|
|
||||||
# Unconditionally enable debugging for libzpool
|
# Unconditionally enable debugging for libzpool
|
||||||
AM_CPPFLAGS += -DDEBUG -UNDEBUG -DZFS_DEBUG
|
AM_CPPFLAGS += -DDEBUG -UNDEBUG -DZFS_DEBUG
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,8 @@
|
||||||
#include <sys/dsl_scan.h>
|
#include <sys/dsl_scan.h>
|
||||||
#include <sys/abd.h>
|
#include <sys/abd.h>
|
||||||
#include <sys/fs/zfs.h>
|
#include <sys/fs/zfs.h>
|
||||||
|
#include <sys/byteorder.h>
|
||||||
|
#include <sys/zfs_bootenv.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Basic routines to read and write from a vdev label.
|
* Basic routines to read and write from a vdev label.
|
||||||
|
@ -1233,13 +1235,9 @@ vdev_label_read_bootenv_impl(zio_t *zio, vdev_t *vd, int flags)
|
||||||
* bootloader should have rewritten them all to be the same on boot,
|
* bootloader should have rewritten them all to be the same on boot,
|
||||||
* and any changes we made since boot have been the same across all
|
* and any changes we made since boot have been the same across all
|
||||||
* labels.
|
* labels.
|
||||||
*
|
|
||||||
* While grub supports writing to all four labels, other bootloaders
|
|
||||||
* don't, so we only use the first two labels to store boot
|
|
||||||
* information.
|
|
||||||
*/
|
*/
|
||||||
if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd)) {
|
if (vd->vdev_ops->vdev_op_leaf && vdev_readable(vd)) {
|
||||||
for (int l = 0; l < VDEV_LABELS / 2; l++) {
|
for (int l = 0; l < VDEV_LABELS; l++) {
|
||||||
vdev_label_read(zio, vd, l,
|
vdev_label_read(zio, vd, l,
|
||||||
abd_alloc_linear(VDEV_PAD_SIZE, B_FALSE),
|
abd_alloc_linear(VDEV_PAD_SIZE, B_FALSE),
|
||||||
offsetof(vdev_label_t, vl_be), VDEV_PAD_SIZE,
|
offsetof(vdev_label_t, vl_be), VDEV_PAD_SIZE,
|
||||||
|
@ -1249,14 +1247,15 @@ vdev_label_read_bootenv_impl(zio_t *zio, vdev_t *vd, int flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vdev_label_read_bootenv(vdev_t *rvd, nvlist_t *command)
|
vdev_label_read_bootenv(vdev_t *rvd, nvlist_t *bootenv)
|
||||||
{
|
{
|
||||||
|
nvlist_t *config;
|
||||||
spa_t *spa = rvd->vdev_spa;
|
spa_t *spa = rvd->vdev_spa;
|
||||||
abd_t *abd = NULL;
|
abd_t *abd = NULL;
|
||||||
int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL |
|
int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL |
|
||||||
ZIO_FLAG_SPECULATIVE | ZIO_FLAG_TRYHARD;
|
ZIO_FLAG_SPECULATIVE | ZIO_FLAG_TRYHARD;
|
||||||
|
|
||||||
ASSERT(command);
|
ASSERT(bootenv);
|
||||||
ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
|
ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
|
||||||
|
|
||||||
zio_t *zio = zio_root(spa, NULL, &abd, flags);
|
zio_t *zio = zio_root(spa, NULL, &abd, flags);
|
||||||
|
@ -1264,39 +1263,81 @@ vdev_label_read_bootenv(vdev_t *rvd, nvlist_t *command)
|
||||||
int err = zio_wait(zio);
|
int err = zio_wait(zio);
|
||||||
|
|
||||||
if (abd != NULL) {
|
if (abd != NULL) {
|
||||||
|
char *buf;
|
||||||
vdev_boot_envblock_t *vbe = abd_to_buf(abd);
|
vdev_boot_envblock_t *vbe = abd_to_buf(abd);
|
||||||
if (vbe->vbe_version != VB_RAW) {
|
|
||||||
abd_free(abd);
|
vbe->vbe_version = ntohll(vbe->vbe_version);
|
||||||
return (SET_ERROR(ENOTSUP));
|
switch (vbe->vbe_version) {
|
||||||
|
case VB_RAW:
|
||||||
|
/*
|
||||||
|
* if we have textual data in vbe_bootenv, create nvlist
|
||||||
|
* with key "envmap".
|
||||||
|
*/
|
||||||
|
fnvlist_add_uint64(bootenv, BOOTENV_VERSION, VB_RAW);
|
||||||
|
vbe->vbe_bootenv[sizeof (vbe->vbe_bootenv) - 1] = '\0';
|
||||||
|
fnvlist_add_string(bootenv, GRUB_ENVMAP,
|
||||||
|
vbe->vbe_bootenv);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VB_NVLIST:
|
||||||
|
err = nvlist_unpack(vbe->vbe_bootenv,
|
||||||
|
sizeof (vbe->vbe_bootenv), &config, 0);
|
||||||
|
if (err == 0) {
|
||||||
|
fnvlist_merge(bootenv, config);
|
||||||
|
nvlist_free(config);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
default:
|
||||||
|
/* Check for FreeBSD zfs bootonce command string */
|
||||||
|
buf = abd_to_buf(abd);
|
||||||
|
if (*buf == '\0') {
|
||||||
|
fnvlist_add_uint64(bootenv, BOOTENV_VERSION,
|
||||||
|
VB_NVLIST);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fnvlist_add_string(bootenv, FREEBSD_BOOTONCE, buf);
|
||||||
}
|
}
|
||||||
vbe->vbe_bootenv[sizeof (vbe->vbe_bootenv) - 1] = '\0';
|
|
||||||
fnvlist_add_string(command, "envmap", vbe->vbe_bootenv);
|
/*
|
||||||
/* abd was allocated in vdev_label_read_bootenv_impl() */
|
* abd was allocated in vdev_label_read_bootenv_impl()
|
||||||
|
*/
|
||||||
abd_free(abd);
|
abd_free(abd);
|
||||||
/* If we managed to read any successfully, return success. */
|
/*
|
||||||
|
* If we managed to read any successfully,
|
||||||
|
* return success.
|
||||||
|
*/
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
return (err);
|
return (err);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vdev_label_write_bootenv(vdev_t *vd, char *envmap)
|
vdev_label_write_bootenv(vdev_t *vd, nvlist_t *env)
|
||||||
{
|
{
|
||||||
zio_t *zio;
|
zio_t *zio;
|
||||||
spa_t *spa = vd->vdev_spa;
|
spa_t *spa = vd->vdev_spa;
|
||||||
vdev_boot_envblock_t *bootenv;
|
vdev_boot_envblock_t *bootenv;
|
||||||
int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL;
|
int flags = ZIO_FLAG_CONFIG_WRITER | ZIO_FLAG_CANFAIL;
|
||||||
int error = ENXIO;
|
int error;
|
||||||
|
size_t nvsize;
|
||||||
|
char *nvbuf;
|
||||||
|
|
||||||
if (strlen(envmap) >= sizeof (bootenv->vbe_bootenv)) {
|
error = nvlist_size(env, &nvsize, NV_ENCODE_XDR);
|
||||||
|
if (error != 0)
|
||||||
|
return (SET_ERROR(error));
|
||||||
|
|
||||||
|
if (nvsize >= sizeof (bootenv->vbe_bootenv)) {
|
||||||
return (SET_ERROR(E2BIG));
|
return (SET_ERROR(E2BIG));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
|
ASSERT(spa_config_held(spa, SCL_ALL, RW_WRITER) == SCL_ALL);
|
||||||
|
|
||||||
|
error = ENXIO;
|
||||||
for (int c = 0; c < vd->vdev_children; c++) {
|
for (int c = 0; c < vd->vdev_children; c++) {
|
||||||
int child_err = vdev_label_write_bootenv(vd->vdev_child[c],
|
int child_err;
|
||||||
envmap);
|
|
||||||
|
child_err = vdev_label_write_bootenv(vd->vdev_child[c], env);
|
||||||
/*
|
/*
|
||||||
* As long as any of the disks managed to write all of their
|
* As long as any of the disks managed to write all of their
|
||||||
* labels successfully, return success.
|
* labels successfully, return success.
|
||||||
|
@ -1312,16 +1353,41 @@ vdev_label_write_bootenv(vdev_t *vd, char *envmap)
|
||||||
ASSERT3U(sizeof (*bootenv), ==, VDEV_PAD_SIZE);
|
ASSERT3U(sizeof (*bootenv), ==, VDEV_PAD_SIZE);
|
||||||
abd_t *abd = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE);
|
abd_t *abd = abd_alloc_for_io(VDEV_PAD_SIZE, B_TRUE);
|
||||||
abd_zero(abd, VDEV_PAD_SIZE);
|
abd_zero(abd, VDEV_PAD_SIZE);
|
||||||
bootenv = abd_borrow_buf_copy(abd, VDEV_PAD_SIZE);
|
|
||||||
|
|
||||||
char *buf = bootenv->vbe_bootenv;
|
bootenv = abd_borrow_buf_copy(abd, VDEV_PAD_SIZE);
|
||||||
(void) strlcpy(buf, envmap, sizeof (bootenv->vbe_bootenv));
|
nvbuf = bootenv->vbe_bootenv;
|
||||||
bootenv->vbe_version = VB_RAW;
|
nvsize = sizeof (bootenv->vbe_bootenv);
|
||||||
abd_return_buf_copy(abd, bootenv, VDEV_PAD_SIZE);
|
|
||||||
|
bootenv->vbe_version = fnvlist_lookup_uint64(env, BOOTENV_VERSION);
|
||||||
|
switch (bootenv->vbe_version) {
|
||||||
|
case VB_RAW:
|
||||||
|
if (nvlist_lookup_string(env, GRUB_ENVMAP, &nvbuf) == 0) {
|
||||||
|
(void) strlcpy(bootenv->vbe_bootenv, nvbuf, nvsize);
|
||||||
|
}
|
||||||
|
error = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VB_NVLIST:
|
||||||
|
error = nvlist_pack(env, &nvbuf, &nvsize, NV_ENCODE_XDR,
|
||||||
|
KM_SLEEP);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error == 0) {
|
||||||
|
bootenv->vbe_version = htonll(bootenv->vbe_version);
|
||||||
|
abd_return_buf_copy(abd, bootenv, VDEV_PAD_SIZE);
|
||||||
|
} else {
|
||||||
|
abd_free(abd);
|
||||||
|
return (SET_ERROR(error));
|
||||||
|
}
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
zio = zio_root(spa, NULL, NULL, flags);
|
zio = zio_root(spa, NULL, NULL, flags);
|
||||||
for (int l = 0; l < VDEV_LABELS / 2; l++) {
|
for (int l = 0; l < VDEV_LABELS; l++) {
|
||||||
vdev_label_write(zio, vd, l, abd,
|
vdev_label_write(zio, vd, l, abd,
|
||||||
offsetof(vdev_label_t, vl_be),
|
offsetof(vdev_label_t, vl_be),
|
||||||
VDEV_PAD_SIZE, NULL, NULL, flags);
|
VDEV_PAD_SIZE, NULL, NULL, flags);
|
||||||
|
|
|
@ -3511,30 +3511,29 @@ zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl)
|
||||||
/*
|
/*
|
||||||
* This ioctl is used to set the bootenv configuration on the current
|
* This ioctl is used to set the bootenv configuration on the current
|
||||||
* pool. This configuration is stored in the second padding area of the label,
|
* pool. This configuration is stored in the second padding area of the label,
|
||||||
* and it is used by the GRUB bootloader used on Linux to store the contents
|
* and it is used by the bootloader(s) to store the bootloader and/or system
|
||||||
* of the grubenv file. The file is stored as raw ASCII, and is protected by
|
* specific data.
|
||||||
* an embedded checksum. By default, GRUB will check if the boot filesystem
|
* The data is stored as nvlist data stream, and is protected by
|
||||||
* supports storing the environment data in a special location, and if so,
|
* an embedded checksum.
|
||||||
* will invoke filesystem specific logic to retrieve it. This can be overridden
|
* The version can have two possible values:
|
||||||
* by a variable, should the user so desire.
|
* VB_RAW: nvlist should have key GRUB_ENVMAP, value DATA_TYPE_STRING.
|
||||||
|
* VB_NVLIST: nvlist with arbitrary <key, value> pairs.
|
||||||
*/
|
*/
|
||||||
/* ARGSUSED */
|
|
||||||
static const zfs_ioc_key_t zfs_keys_set_bootenv[] = {
|
static const zfs_ioc_key_t zfs_keys_set_bootenv[] = {
|
||||||
{"envmap", DATA_TYPE_STRING, 0},
|
{"version", DATA_TYPE_UINT64, 0},
|
||||||
|
{"<keys>", DATA_TYPE_ANY, ZK_OPTIONAL | ZK_WILDCARDLIST},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
zfs_ioc_set_bootenv(const char *name, nvlist_t *innvl, nvlist_t *outnvl)
|
zfs_ioc_set_bootenv(const char *name, nvlist_t *innvl, nvlist_t *outnvl)
|
||||||
{
|
{
|
||||||
char *envmap;
|
|
||||||
int error;
|
int error;
|
||||||
spa_t *spa;
|
spa_t *spa;
|
||||||
|
|
||||||
envmap = fnvlist_lookup_string(innvl, "envmap");
|
|
||||||
if ((error = spa_open(name, &spa, FTAG)) != 0)
|
if ((error = spa_open(name, &spa, FTAG)) != 0)
|
||||||
return (error);
|
return (error);
|
||||||
spa_vdev_state_enter(spa, SCL_ALL);
|
spa_vdev_state_enter(spa, SCL_ALL);
|
||||||
error = vdev_label_write_bootenv(spa->spa_root_vdev, envmap);
|
error = vdev_label_write_bootenv(spa->spa_root_vdev, innvl);
|
||||||
(void) spa_vdev_state_exit(spa, NULL, 0);
|
(void) spa_vdev_state_exit(spa, NULL, 0);
|
||||||
spa_close(spa, FTAG);
|
spa_close(spa, FTAG);
|
||||||
return (error);
|
return (error);
|
||||||
|
@ -3544,7 +3543,6 @@ static const zfs_ioc_key_t zfs_keys_get_bootenv[] = {
|
||||||
/* no nvl keys */
|
/* no nvl keys */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ARGSUSED */
|
|
||||||
static int
|
static int
|
||||||
zfs_ioc_get_bootenv(const char *name, nvlist_t *innvl, nvlist_t *outnvl)
|
zfs_ioc_get_bootenv(const char *name, nvlist_t *innvl, nvlist_t *outnvl)
|
||||||
{
|
{
|
||||||
|
|
|
@ -491,6 +491,7 @@ systemctl --system daemon-reload >/dev/null || true
|
||||||
|
|
||||||
%files -n libzfs2-devel
|
%files -n libzfs2-devel
|
||||||
%{_pkgconfigdir}/libzfs.pc
|
%{_pkgconfigdir}/libzfs.pc
|
||||||
|
%{_pkgconfigdir}/libzfsbootenv.pc
|
||||||
%{_pkgconfigdir}/libzfs_core.pc
|
%{_pkgconfigdir}/libzfs_core.pc
|
||||||
%{_libdir}/*.so
|
%{_libdir}/*.so
|
||||||
%{_includedir}/*
|
%{_includedir}/*
|
||||||
|
|
|
@ -4,6 +4,13 @@ pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
|
||||||
|
|
||||||
pkgexec_PROGRAMS = libzfs_input_check
|
pkgexec_PROGRAMS = libzfs_input_check
|
||||||
|
|
||||||
|
if BUILD_FREEBSD
|
||||||
|
DEFAULT_INCLUDES += -I$(top_srcdir)/include/os/freebsd/zfs
|
||||||
|
endif
|
||||||
|
if BUILD_LINUX
|
||||||
|
DEFAULT_INCLUDES += -I$(top_srcdir)/include/os/linux/zfs
|
||||||
|
endif
|
||||||
|
|
||||||
libzfs_input_check_SOURCES = libzfs_input_check.c
|
libzfs_input_check_SOURCES = libzfs_input_check.c
|
||||||
libzfs_input_check_LDADD = \
|
libzfs_input_check_LDADD = \
|
||||||
$(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
|
$(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
|
||||||
|
|
|
@ -25,7 +25,9 @@
|
||||||
#include <libzutil.h>
|
#include <libzutil.h>
|
||||||
|
|
||||||
#include <sys/nvpair.h>
|
#include <sys/nvpair.h>
|
||||||
|
#include <sys/vdev_impl.h>
|
||||||
#include <sys/zfs_ioctl.h>
|
#include <sys/zfs_ioctl.h>
|
||||||
|
#include <sys/zfs_bootenv.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Test the nvpair inputs for the non-legacy zfs ioctl commands.
|
* Test the nvpair inputs for the non-legacy zfs ioctl commands.
|
||||||
|
@ -762,9 +764,10 @@ test_set_bootenv(const char *pool)
|
||||||
{
|
{
|
||||||
nvlist_t *required = fnvlist_alloc();
|
nvlist_t *required = fnvlist_alloc();
|
||||||
|
|
||||||
fnvlist_add_string(required, "envmap", "test");
|
fnvlist_add_uint64(required, "version", VB_RAW);
|
||||||
|
fnvlist_add_string(required, GRUB_ENVMAP, "test");
|
||||||
|
|
||||||
IOC_INPUT_TEST(ZFS_IOC_SET_BOOTENV, pool, required, NULL, 0);
|
IOC_INPUT_TEST_WILD(ZFS_IOC_SET_BOOTENV, pool, required, NULL, 0);
|
||||||
|
|
||||||
nvlist_free(required);
|
nvlist_free(required);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue