zfs: add an option to the bootloader to rewind the ZFS checkpoint

The checkpoints are another way of keeping the state of ZFS.
During the rewind, the pool has to be exported.
This makes checkpoints unusable when using ZFS as root.
Add the option to rewind the ZFS checkpoint at the boot time.
If checkpoint exists, a new option for rewinding a checkpoint will appear in
the bootloader menu.
We fully support boot environments.
If the rewind option is selected, the boot loader will show a list of
boot environments that existed before the checkpoint.

Reviewed by:	tsoome, allanjude, kevans (ok with high-level overview)
Differential Revision:	https://reviews.freebsd.org/D24920
This commit is contained in:
Mariusz Zaborski 2020-08-18 19:48:04 +00:00
parent ad7a0eb189
commit 277f38abff
11 changed files with 240 additions and 86 deletions

View File

@ -269,7 +269,7 @@ probe_zfs_currdev(uint64_t guid)
currdev.root_guid = 0;
set_currdev_devdesc((struct devdesc *)&currdev);
devname = efi_fmtdev(&currdev);
init_zfs_bootenv(devname);
init_zfs_boot_options(devname);
rv = sanity_check_currdev();
if (rv) {

View File

@ -363,7 +363,7 @@ extract_currdev(void)
#ifdef LOADER_ZFS_SUPPORT
if (new_currdev.dd.d_dev->dv_type == DEVT_ZFS)
init_zfs_bootenv(zfs_fmtdev(&new_currdev));
init_zfs_boot_options(zfs_fmtdev(&new_currdev));
#endif
env_setenv("currdev", EV_VOLATILE, i386_fmtdev(&new_currdev),

View File

@ -123,7 +123,7 @@ int zfs_nextboot(void *vdev, char *buf, size_t size);
int zfs_probe_dev(const char *devname, uint64_t *pool_guid);
int zfs_list(const char *name);
uint64_t ldi_get_size(void *);
void init_zfs_bootenv(const char *currdev);
void init_zfs_boot_options(const char *currdev);
int zfs_bootenv(const char *name);
int zfs_belist_add(const char *name, uint64_t __unused);
int zfs_set_env(void);

View File

@ -60,7 +60,10 @@ static off_t zfs_seek(struct open_file *f, off_t offset, int where);
static int zfs_stat(struct open_file *f, struct stat *sb);
static int zfs_readdir(struct open_file *f, struct dirent *d);
static void zfs_bootenv_initial(const char *);
static void zfs_bootenv_initial(const char *envname, spa_t *spa,
const char *name, const char *dsname, int checkpoint);
static void zfs_checkpoints_initial(spa_t *spa, const char *name,
const char *dsname);
struct devsw zfs_dev;
@ -1077,15 +1080,15 @@ zfs_fmtdev(void *vdev)
return (buf);
}
int
zfs_list(const char *name)
static int
split_devname(const char *name, char *poolname, size_t size,
const char **dsnamep)
{
static char poolname[ZFS_MAXNAMELEN];
uint64_t objid;
spa_t *spa;
const char *dsname;
int len;
int rv;
const char *dsname;
size_t len;
ASSERT(name != NULL);
ASSERT(poolname != NULL);
len = strlen(name);
dsname = strchr(name, '/');
@ -1094,8 +1097,29 @@ zfs_list(const char *name)
dsname++;
} else
dsname = "";
memcpy(poolname, name, len);
poolname[len] = '\0';
if (len + 1 > size)
return (EINVAL);
strlcpy(poolname, name, len + 1);
if (dsnamep != NULL)
*dsnamep = dsname;
return (0);
}
int
zfs_list(const char *name)
{
static char poolname[ZFS_MAXNAMELEN];
uint64_t objid;
spa_t *spa;
const char *dsname;
int rv;
if (split_devname(name, poolname, sizeof(poolname), &dsname) != 0)
return (EINVAL);
spa = spa_find_by_name(poolname);
if (!spa)
@ -1108,10 +1132,13 @@ zfs_list(const char *name)
}
void
init_zfs_bootenv(const char *currdev_in)
init_zfs_boot_options(const char *currdev_in)
{
char poolname[ZFS_MAXNAMELEN];
char *beroot, *currdev;
spa_t *spa;
int currdev_len;
const char *dsname;
currdev = NULL;
currdev_len = strlen(currdev_in);
@ -1124,6 +1151,7 @@ init_zfs_bootenv(const char *currdev_in)
return;
/* Remove the trailing : */
currdev[currdev_len - 1] = '\0';
setenv("zfs_be_active", currdev, 1);
setenv("zfs_be_currpage", "1", 1);
/* Remove the last element (current bootenv) */
@ -1132,49 +1160,71 @@ init_zfs_bootenv(const char *currdev_in)
beroot[0] = '\0';
beroot = strchr(currdev, ':') + 1;
setenv("zfs_be_root", beroot, 1);
zfs_bootenv_initial(beroot);
if (split_devname(beroot, poolname, sizeof(poolname), &dsname) != 0)
return;
spa = spa_find_by_name(poolname);
if (spa == NULL)
return;
zfs_bootenv_initial("bootenvs", spa, beroot, dsname, 0);
zfs_checkpoints_initial(spa, beroot, dsname);
free(currdev);
}
static void
zfs_bootenv_initial(const char *name)
zfs_checkpoints_initial(spa_t *spa, const char *name, const char *dsname)
{
char poolname[ZFS_MAXNAMELEN], *dsname;
char envname[32], envval[256];
char envname[32];
if (spa->spa_uberblock_checkpoint.ub_checkpoint_txg != 0) {
snprintf(envname, sizeof(envname), "zpool_checkpoint");
setenv(envname, name, 1);
spa->spa_uberblock = &spa->spa_uberblock_checkpoint;
spa->spa_mos = &spa->spa_mos_checkpoint;
zfs_bootenv_initial("bootenvs_check", spa, name, dsname, 1);
spa->spa_uberblock = &spa->spa_uberblock_master;
spa->spa_mos = &spa->spa_mos_master;
}
}
static void
zfs_bootenv_initial(const char *envprefix, spa_t *spa, const char *rootname,
const char *dsname, int checkpoint)
{
char envname[32], envval[256];
uint64_t objid;
spa_t *spa;
int bootenvs_idx, len, rv;
int bootenvs_idx, rv;
SLIST_INIT(&zfs_be_head);
zfs_env_count = 0;
len = strlen(name);
dsname = strchr(name, '/');
if (dsname != NULL) {
len = dsname - name;
dsname++;
} else
dsname = "";
strlcpy(poolname, name, len + 1);
spa = spa_find_by_name(poolname);
if (spa == NULL)
return;
rv = zfs_lookup_dataset(spa, dsname, &objid);
if (rv != 0)
return;
rv = zfs_callback_dataset(spa, objid, zfs_belist_add);
bootenvs_idx = 0;
/* Populate the initial environment variables */
SLIST_FOREACH_SAFE(zfs_be, &zfs_be_head, entries, zfs_be_tmp) {
/* Enumerate all bootenvs for general usage */
snprintf(envname, sizeof(envname), "bootenvs[%d]", bootenvs_idx);
snprintf(envval, sizeof(envval), "zfs:%s/%s", name, zfs_be->name);
snprintf(envname, sizeof(envname), "%s[%d]",
envprefix, bootenvs_idx);
snprintf(envval, sizeof(envval), "zfs:%s%s/%s",
checkpoint ? "!" : "", rootname, zfs_be->name);
rv = setenv(envname, envval, 1);
if (rv != 0)
break;
bootenvs_idx++;
}
snprintf(envname, sizeof(envname), "%s_count", envprefix);
snprintf(envval, sizeof(envval), "%d", bootenvs_idx);
setenv("bootenvs_count", envval, 1);
setenv(envname, envval, 1);
/* Clean up the SLIST of ZFS BEs */
while (!SLIST_EMPTY(&zfs_be_head)) {
@ -1183,19 +1233,17 @@ zfs_bootenv_initial(const char *name)
free(zfs_be->name);
free(zfs_be);
}
return;
}
int
zfs_bootenv(const char *name)
{
static char poolname[ZFS_MAXNAMELEN], *dsname, *root;
char poolname[ZFS_MAXNAMELEN], *root;
const char *dsname;
char becount[4];
uint64_t objid;
spa_t *spa;
int len, rv, pages, perpage, currpage;
int rv, pages, perpage, currpage;
if (name == NULL)
return (EINVAL);
@ -1209,15 +1257,9 @@ zfs_bootenv(const char *name)
SLIST_INIT(&zfs_be_head);
zfs_env_count = 0;
len = strlen(name);
dsname = strchr(name, '/');
if (dsname != NULL) {
len = dsname - name;
dsname++;
} else
dsname = "";
memcpy(poolname, name, len);
poolname[len] = '\0';
if (split_devname(name, poolname, sizeof(poolname), &dsname) != 0)
return (EINVAL);
spa = spa_find_by_name(poolname);
if (!spa)
@ -1307,7 +1349,7 @@ zfs_set_env(void)
ctr++;
continue;
}
snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index);
snprintf(envval, sizeof(envval), "%s", zfs_be->name);
rv = setenv(envname, envval, 1);
@ -1340,7 +1382,7 @@ zfs_set_env(void)
}
}
for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) {
snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index);
(void)unsetenv(envname);

View File

@ -699,7 +699,7 @@ vdev_indirect_read(vdev_t *vdev, const blkptr_t *bp, void *buf,
vic = &vdev->vdev_indirect_config;
vdev->v_mapping = vdev_indirect_mapping_open(spa,
&spa->spa_mos, vic->vic_mapping_object);
spa->spa_mos, vic->vic_mapping_object);
}
vdev_indirect_remap(vdev, offset, bytes, &zio);
@ -1347,6 +1347,8 @@ spa_create(uint64_t guid, const char *name)
free(spa);
return (NULL);
}
spa->spa_uberblock = &spa->spa_uberblock_master;
spa->spa_mos = &spa->spa_mos_master;
spa->spa_guid = guid;
spa->spa_root_vdev = vdev_create(guid, NULL);
if (spa->spa_root_vdev == NULL) {
@ -1883,7 +1885,7 @@ vdev_probe(vdev_phys_read_t *_read, void *read_priv, spa_t **spap)
* the best uberblock and then we can actually access
* the contents of the pool.
*/
vdev_uberblock_load(vdev, &spa->spa_uberblock);
vdev_uberblock_load(vdev, spa->spa_uberblock);
if (spap != NULL)
*spap = spa;
@ -2409,8 +2411,9 @@ fzap_lookup(const spa_t *spa, const dnode_phys_t *dnode, zap_phys_t *zh,
if (zh->zap_magic != ZAP_MAGIC)
return (EIO);
if ((rc = fzap_check_size(integer_size, num_integers)) != 0)
if ((rc = fzap_check_size(integer_size, num_integers)) != 0) {
return (rc);
}
z.zap_block_shift = ilog2(bsize);
z.zap_phys = zh;
@ -2766,7 +2769,7 @@ zfs_rlookup(const spa_t *spa, uint64_t objnum, char *result)
p = &name[sizeof(name) - 1];
*p = '\0';
if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
if (objset_get_dnode(spa, spa->spa_mos, objnum, &dataset)) {
printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
return (EIO);
}
@ -2774,7 +2777,7 @@ zfs_rlookup(const spa_t *spa, uint64_t objnum, char *result)
dir_obj = ds->ds_dir_obj;
for (;;) {
if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir) != 0)
if (objset_get_dnode(spa, spa->spa_mos, dir_obj, &dir) != 0)
return (EIO);
dd = (dsl_dir_phys_t *)&dir.dn_bonus;
@ -2783,12 +2786,12 @@ zfs_rlookup(const spa_t *spa, uint64_t objnum, char *result)
if (parent_obj == 0)
break;
if (objset_get_dnode(spa, &spa->spa_mos, parent_obj,
if (objset_get_dnode(spa, spa->spa_mos, parent_obj,
&parent) != 0)
return (EIO);
dd = (dsl_dir_phys_t *)&parent.dn_bonus;
child_dir_zapobj = dd->dd_child_dir_zapobj;
if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
if (objset_get_dnode(spa, spa->spa_mos, child_dir_zapobj,
&child_dir_zap) != 0)
return (EIO);
if (zap_rlookup(spa, &child_dir_zap, component, dir_obj) != 0)
@ -2820,7 +2823,7 @@ zfs_lookup_dataset(const spa_t *spa, const char *name, uint64_t *objnum)
dsl_dir_phys_t *dd;
const char *p, *q;
if (objset_get_dnode(spa, &spa->spa_mos,
if (objset_get_dnode(spa, spa->spa_mos,
DMU_POOL_DIRECTORY_OBJECT, &dir))
return (EIO);
if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET, sizeof (dir_obj),
@ -2829,7 +2832,7 @@ zfs_lookup_dataset(const spa_t *spa, const char *name, uint64_t *objnum)
p = name;
for (;;) {
if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir))
if (objset_get_dnode(spa, spa->spa_mos, dir_obj, &dir))
return (EIO);
dd = (dsl_dir_phys_t *)&dir.dn_bonus;
@ -2850,7 +2853,7 @@ zfs_lookup_dataset(const spa_t *spa, const char *name, uint64_t *objnum)
}
child_dir_zapobj = dd->dd_child_dir_zapobj;
if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
if (objset_get_dnode(spa, spa->spa_mos, child_dir_zapobj,
&child_dir_zap) != 0)
return (EIO);
@ -2873,21 +2876,21 @@ zfs_list_dataset(const spa_t *spa, uint64_t objnum/*, int pos, char *entry*/)
dsl_dataset_phys_t *ds;
dsl_dir_phys_t *dd;
if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
if (objset_get_dnode(spa, spa->spa_mos, objnum, &dataset)) {
printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
return (EIO);
}
ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
dir_obj = ds->ds_dir_obj;
if (objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir)) {
if (objset_get_dnode(spa, spa->spa_mos, dir_obj, &dir)) {
printf("ZFS: can't find dirobj %ju\n", (uintmax_t)dir_obj);
return (EIO);
}
dd = (dsl_dir_phys_t *)&dir.dn_bonus;
child_dir_zapobj = dd->dd_child_dir_zapobj;
if (objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
if (objset_get_dnode(spa, spa->spa_mos, child_dir_zapobj,
&child_dir_zap) != 0) {
printf("ZFS: can't find child zap %ju\n", (uintmax_t)dir_obj);
return (EIO);
@ -2908,7 +2911,7 @@ zfs_callback_dataset(const spa_t *spa, uint64_t objnum,
size_t size;
int err;
err = objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset);
err = objset_get_dnode(spa, spa->spa_mos, objnum, &dataset);
if (err != 0) {
printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
return (err);
@ -2916,7 +2919,7 @@ zfs_callback_dataset(const spa_t *spa, uint64_t objnum,
ds = (dsl_dataset_phys_t *)&dataset.dn_bonus;
dir_obj = ds->ds_dir_obj;
err = objset_get_dnode(spa, &spa->spa_mos, dir_obj, &dir);
err = objset_get_dnode(spa, spa->spa_mos, dir_obj, &dir);
if (err != 0) {
printf("ZFS: can't find dirobj %ju\n", (uintmax_t)dir_obj);
return (err);
@ -2924,7 +2927,7 @@ zfs_callback_dataset(const spa_t *spa, uint64_t objnum,
dd = (dsl_dir_phys_t *)&dir.dn_bonus;
child_dir_zapobj = dd->dd_child_dir_zapobj;
err = objset_get_dnode(spa, &spa->spa_mos, child_dir_zapobj,
err = objset_get_dnode(spa, spa->spa_mos, child_dir_zapobj,
&child_dir_zap);
if (err != 0) {
printf("ZFS: can't find child zap %ju\n", (uintmax_t)dir_obj);
@ -2962,7 +2965,7 @@ zfs_mount_dataset(const spa_t *spa, uint64_t objnum, objset_phys_t *objset)
dnode_phys_t dataset;
dsl_dataset_phys_t *ds;
if (objset_get_dnode(spa, &spa->spa_mos, objnum, &dataset)) {
if (objset_get_dnode(spa, spa->spa_mos, objnum, &dataset)) {
printf("ZFS: can't find dataset %ju\n", (uintmax_t)objnum);
return (EIO);
}
@ -2992,7 +2995,7 @@ zfs_get_root(const spa_t *spa, uint64_t *objid)
/*
* Start with the MOS directory object.
*/
if (objset_get_dnode(spa, &spa->spa_mos,
if (objset_get_dnode(spa, spa->spa_mos,
DMU_POOL_DIRECTORY_OBJECT, &dir)) {
printf("ZFS: can't read MOS object directory\n");
return (EIO);
@ -3003,7 +3006,7 @@ zfs_get_root(const spa_t *spa, uint64_t *objid)
*/
if (zap_lookup(spa, &dir, DMU_POOL_PROPS,
sizeof(props), 1, &props) == 0 &&
objset_get_dnode(spa, &spa->spa_mos, props, &propdir) == 0 &&
objset_get_dnode(spa, spa->spa_mos, props, &propdir) == 0 &&
zap_lookup(spa, &propdir, "bootfs",
sizeof(bootfs), 1, &bootfs) == 0 && bootfs != 0) {
*objid = bootfs;
@ -3014,7 +3017,7 @@ zfs_get_root(const spa_t *spa, uint64_t *objid)
*/
if (zap_lookup(spa, &dir, DMU_POOL_ROOT_DATASET,
sizeof(root), 1, &root) ||
objset_get_dnode(spa, &spa->spa_mos, root, &dir)) {
objset_get_dnode(spa, spa->spa_mos, root, &dir)) {
printf("ZFS: can't find root dsl_dir\n");
return (EIO);
}
@ -3085,7 +3088,7 @@ check_mos_features(const spa_t *spa)
size_t size;
int rc;
if ((rc = objset_get_dnode(spa, &spa->spa_mos, DMU_OT_OBJECT_DIRECTORY,
if ((rc = objset_get_dnode(spa, spa->spa_mos, DMU_OT_OBJECT_DIRECTORY,
&dir)) != 0)
return (rc);
if ((rc = zap_lookup(spa, &dir, DMU_POOL_FEATURES_FOR_READ,
@ -3097,7 +3100,7 @@ check_mos_features(const spa_t *spa)
return (0);
}
if ((rc = objset_get_dnode(spa, &spa->spa_mos, objnum, &dir)) != 0)
if ((rc = objset_get_dnode(spa, spa->spa_mos, objnum, &dir)) != 0)
return (rc);
if (dir.dn_type != DMU_OTN_ZAP_METADATA)
@ -3131,7 +3134,7 @@ load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value)
unsigned char *nv;
*value = NULL;
if ((rc = objset_get_dnode(spa, &spa->spa_mos, obj, &dir)) != 0)
if ((rc = objset_get_dnode(spa, spa->spa_mos, obj, &dir)) != 0)
return (rc);
if (dir.dn_type != DMU_OT_PACKED_NVLIST &&
dir.dn_bonustype != DMU_OT_PACKED_NVLIST_SIZE) {
@ -3160,22 +3163,23 @@ load_nvlist(spa_t *spa, uint64_t obj, nvlist_t **value)
static int
zfs_spa_init(spa_t *spa)
{
struct uberblock checkpoint;
dnode_phys_t dir;
uint64_t config_object;
nvlist_t *nvlist;
int rc;
if (zio_read(spa, &spa->spa_uberblock.ub_rootbp, &spa->spa_mos)) {
if (zio_read(spa, &spa->spa_uberblock->ub_rootbp, spa->spa_mos)) {
printf("ZFS: can't read MOS of pool %s\n", spa->spa_name);
return (EIO);
}
if (spa->spa_mos.os_type != DMU_OST_META) {
if (spa->spa_mos->os_type != DMU_OST_META) {
printf("ZFS: corrupted MOS of pool %s\n", spa->spa_name);
return (EIO);
}
if (objset_get_dnode(spa, &spa->spa_mos, DMU_POOL_DIRECTORY_OBJECT,
&dir)) {
if (objset_get_dnode(spa, &spa->spa_mos_master,
DMU_POOL_DIRECTORY_OBJECT, &dir)) {
printf("ZFS: failed to read pool %s directory object\n",
spa->spa_name);
return (EIO);
@ -3200,6 +3204,20 @@ zfs_spa_init(spa_t *spa)
rc = load_nvlist(spa, config_object, &nvlist);
if (rc != 0)
return (rc);
rc = zap_lookup(spa, &dir, DMU_POOL_ZPOOL_CHECKPOINT,
sizeof(uint64_t), sizeof(checkpoint) / sizeof(uint64_t),
&checkpoint);
if (rc == 0 && checkpoint.ub_checkpoint_txg != 0) {
memcpy(&spa->spa_uberblock_checkpoint, &checkpoint,
sizeof(checkpoint));
if (zio_read(spa, &spa->spa_uberblock_checkpoint.ub_rootbp,
&spa->spa_mos_checkpoint)) {
printf("ZFS: can not read checkpoint data.\n");
return (EIO);
}
}
/*
* Update vdevs from MOS config. Note, we do skip encoding bytes
* here. See also vdev_label_read_config().

View File

@ -38,6 +38,8 @@ local default_safe_mode = false
local default_single_user = false
local default_verbose = false
local bootenv_list = "bootenvs"
local function composeLoaderCmd(cmd_name, argstr)
if argstr ~= nil then
cmd_name = cmd_name .. " " .. argstr
@ -270,7 +272,7 @@ function core.bootenvDefault()
end
function core.bootenvList()
local bootenv_count = tonumber(loader.getenv("bootenvs_count"))
local bootenv_count = tonumber(loader.getenv(bootenv_list .. "_count"))
local bootenvs = {}
local curenv
local envcount = 0
@ -281,7 +283,12 @@ function core.bootenvList()
end
-- Currently selected bootenv is always first/default
curenv = core.bootenvDefault()
-- On the rewinded list the bootenv may not exists
if core.isRewinded() then
curenv = core.bootenvDefaultRewinded()
else
curenv = core.bootenvDefault()
end
if curenv ~= nil then
envcount = envcount + 1
bootenvs[envcount] = curenv
@ -289,7 +296,7 @@ function core.bootenvList()
end
for curenv_idx = 0, bootenv_count - 1 do
curenv = loader.getenv("bootenvs[" .. curenv_idx .. "]")
curenv = loader.getenv(bootenv_list .. "[" .. curenv_idx .. "]")
if curenv ~= nil and unique[curenv] == nil then
envcount = envcount + 1
bootenvs[envcount] = curenv
@ -299,6 +306,40 @@ function core.bootenvList()
return bootenvs
end
function core.isCheckpointed()
return loader.getenv("zpool_checkpoint") ~= nil
end
function core.bootenvDefaultRewinded()
local defname = "zfs:!" .. string.sub(core.bootenvDefault(), 5)
local bootenv_count = tonumber("bootenvs_check_count")
if bootenv_count == nil or bootenv_count <= 0 then
return defname
end
for curenv_idx = 0, bootenv_count - 1 do
curenv = loader.getenv("bootenvs_check[" .. curenv_idx .. "]")
if curenv == defname then
return defname
end
end
return loader.getenv("bootenvs_check[0]")
end
function core.isRewinded()
return bootenv_list == "bootenvs_check"
end
function core.changeRewindCheckpoint()
if core.isRewinded() then
bootenv_list = "bootenvs"
else
bootenv_list = "bootenvs_check"
end
end
function core.setDefaults()
core.setACPI(core.getACPIPresent(true))
core.setSafeMode(default_safe_mode)

View File

@ -132,6 +132,9 @@ menu.boot_environments = {
},
{
entry_type = core.MENU_ENTRY,
visible = function()
return core.isRewinded() == false
end,
name = function()
return color.highlight("b") .. "ootfs: " ..
core.bootenvDefault()
@ -250,6 +253,7 @@ menu.welcome = {
},
menu_entries.kernel_options,
menu_entries.boot_options,
menu_entries.zpool_checkpoints,
menu_entries.boot_envs,
menu_entries.chainload,
}
@ -334,6 +338,32 @@ menu.welcome = {
submenu = menu.boot_options,
alias = {"o", "O"},
},
zpool_checkpoints = {
entry_type = core.MENU_ENTRY,
name = function()
rewind = "No"
if core.isRewinded() then
rewind = "Yes"
end
return "Rewind ZFS " .. color.highlight("C") ..
"heckpoint: " .. rewind
end,
func = function()
core.changeRewindCheckpoint()
if core.isRewinded() then
bootenvSet(
core.bootenvDefaultRewinded())
else
bootenvSet(core.bootenvDefault())
end
config.setCarouselIndex("be_active", 1)
end,
visible = function()
return core.isZFSBoot() and
core.isCheckpointed()
end,
alias = {"c", "C"},
},
boot_envs = {
entry_type = core.MENU_SUBMENU,
visible = function()

View File

@ -1351,6 +1351,7 @@ typedef struct dsl_dataset_phys {
#define DMU_POOL_REMOVING "com.delphix:removing"
#define DMU_POOL_OBSOLETE_BPOBJ "com.delphix:obsolete_bpobj"
#define DMU_POOL_CONDENSING_INDIRECT "com.delphix:condensing_indirect"
#define DMU_POOL_ZPOOL_CHECKPOINT "com.delphix:zpool_checkpoint"
#define ZAP_MAGIC 0x2F52AB2ABULL
@ -1814,12 +1815,17 @@ typedef struct spa {
char *spa_name; /* pool name */
uint64_t spa_guid; /* pool guid */
uint64_t spa_txg; /* most recent transaction */
struct uberblock spa_uberblock; /* best uberblock so far */
struct uberblock *spa_uberblock; /* best uberblock so far */
vdev_t *spa_root_vdev; /* toplevel vdev container */
objset_phys_t spa_mos; /* MOS for this pool */
objset_phys_t *spa_mos; /* MOS for this pool */
zio_cksum_salt_t spa_cksum_salt; /* secret salt for cksum */
void *spa_cksum_tmpls[ZIO_CHECKSUM_FUNCTIONS];
boolean_t spa_with_log; /* this pool has log */
struct uberblock spa_uberblock_master; /* best uberblock so far */
objset_phys_t spa_mos_master; /* MOS for this pool */
struct uberblock spa_uberblock_checkpoint; /* checkpoint uberblock */
objset_phys_t spa_mos_checkpoint; /* Checkpoint MOS */
} spa_t;
/* IO related arguments. */

View File

@ -5719,7 +5719,7 @@ spa_generate_rootconf(const char *name)
}
int
spa_import_rootpool(const char *name)
spa_import_rootpool(const char *name, bool checkpointrewind)
{
spa_t *spa;
vdev_t *rvd, *bvd, *avd = NULL;
@ -5778,6 +5778,9 @@ spa_import_rootpool(const char *name)
}
spa->spa_is_root = B_TRUE;
spa->spa_import_flags = ZFS_IMPORT_VERBATIM;
if (checkpointrewind) {
spa->spa_import_flags |= ZFS_IMPORT_CHECKPOINT;
}
/*
* Build up a vdev tree based on the boot device's label config.

View File

@ -643,7 +643,7 @@ extern int spa_create(const char *pool, nvlist_t *config, nvlist_t *props,
#ifdef illumos
extern int spa_import_rootpool(char *devpath, char *devid);
#else
extern int spa_import_rootpool(const char *name);
extern int spa_import_rootpool(const char *name, bool checkpointrewind);
#endif
extern int spa_import(const char *pool, nvlist_t *config, nvlist_t *props,
uint64_t flags);

View File

@ -1783,6 +1783,18 @@ getpoolname(const char *osname, char *poolname)
return (0);
}
static void
fetch_osname_options(char *name, bool *checkpointrewind)
{
if (name[0] == '!') {
*checkpointrewind = true;
memmove(name, name + 1, strlen(name));
} else {
*checkpointrewind = false;
}
}
/*ARGSUSED*/
static int
zfs_mount(vfs_t *vfsp)
@ -1793,6 +1805,7 @@ zfs_mount(vfs_t *vfsp)
char *osname;
int error = 0;
int canwrite;
bool checkpointrewind;
#ifdef illumos
if (mvp->v_type != VDIR)
@ -1836,6 +1849,7 @@ zfs_mount(vfs_t *vfsp)
secpolicy_fs_mount_clearopts(cr, vfsp);
}
#endif /* illumos */
fetch_osname_options(osname, &checkpointrewind);
/*
* Check for mount privilege?
@ -1921,7 +1935,7 @@ zfs_mount(vfs_t *vfsp)
error = getpoolname(osname, pname);
if (error == 0)
error = spa_import_rootpool(pname);
error = spa_import_rootpool(pname, checkpointrewind);
if (error)
goto out;
}