Index: head/stand/efi/loader/main.c =================================================================== --- head/stand/efi/loader/main.c +++ head/stand/efi/loader/main.c @@ -269,7 +269,7 @@ 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) { Index: head/stand/i386/loader/main.c =================================================================== --- head/stand/i386/loader/main.c +++ head/stand/i386/loader/main.c @@ -363,7 +363,7 @@ #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), Index: head/stand/libsa/zfs/libzfs.h =================================================================== --- head/stand/libsa/zfs/libzfs.h +++ head/stand/libsa/zfs/libzfs.h @@ -123,7 +123,7 @@ 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); Index: head/stand/libsa/zfs/zfs.c =================================================================== --- head/stand/libsa/zfs/zfs.c +++ head/stand/libsa/zfs/zfs.c @@ -60,7 +60,10 @@ 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,16 +1080,16 @@ 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, '/'); if (dsname != NULL) { @@ -1094,9 +1097,30 @@ 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) return (ENXIO); @@ -1108,10 +1132,13 @@ } 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 @@ 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 @@ 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 @@ 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,16 +1257,10 @@ 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) return (ENXIO); @@ -1307,7 +1349,7 @@ 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 @@ } } - + for (; zfs_env_index <= ZFS_BE_LAST; zfs_env_index++) { snprintf(envname, sizeof(envname), "bootenvmenu_caption[%d]", zfs_env_index); (void)unsetenv(envname); Index: head/stand/libsa/zfs/zfsimpl.c =================================================================== --- head/stand/libsa/zfs/zfsimpl.c +++ head/stand/libsa/zfs/zfsimpl.c @@ -699,7 +699,7 @@ 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 @@ 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 @@ * 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* * 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 @@ */ 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 @@ */ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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(). Index: head/stand/lua/core.lua =================================================================== --- head/stand/lua/core.lua +++ head/stand/lua/core.lua @@ -38,6 +38,8 @@ 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 @@ 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 @@ 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 @@ 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 @@ -297,6 +304,40 @@ end end 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() Index: head/stand/lua/menu.lua =================================================================== --- head/stand/lua/menu.lua +++ head/stand/lua/menu.lua @@ -132,6 +132,9 @@ }, { 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_entries.kernel_options, menu_entries.boot_options, + menu_entries.zpool_checkpoints, menu_entries.boot_envs, menu_entries.chainload, } @@ -333,6 +337,32 @@ name = "Boot " .. color.highlight("O") .. "ptions", 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, Index: head/sys/cddl/boot/zfs/zfsimpl.h =================================================================== --- head/sys/cddl/boot/zfs/zfsimpl.h +++ head/sys/cddl/boot/zfs/zfsimpl.h @@ -1351,6 +1351,7 @@ #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 @@ 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. */ Index: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c =================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c @@ -5719,7 +5719,7 @@ } 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->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. Index: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h =================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h @@ -643,7 +643,7 @@ #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); Index: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c =================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c @@ -1783,6 +1783,18 @@ 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 @@ char *osname; int error = 0; int canwrite; + bool checkpointrewind; #ifdef illumos if (mvp->v_type != VDIR) @@ -1836,6 +1849,7 @@ secpolicy_fs_mount_clearopts(cr, vfsp); } #endif /* illumos */ + fetch_osname_options(osname, &checkpointrewind); /* * Check for mount privilege? @@ -1921,7 +1935,7 @@ error = getpoolname(osname, pname); if (error == 0) - error = spa_import_rootpool(pname); + error = spa_import_rootpool(pname, checkpointrewind); if (error) goto out; }