From cfcb9052b13cb16cf02aecc262cdbe32147bb5ad Mon Sep 17 00:00:00 2001 From: avg Date: Thu, 3 May 2018 07:22:24 +0000 Subject: [PATCH] MFC r332426: allow ZFS pool to have temporary name for duration of current import The change adds -t option to zpool create and -t option to zpool import in its form with an old name and a new name. This allows to import (or create) a pool under a name that's different from its real, permanent name without affecting that name. This is useful when working with VM images or images of other physical systems if they happen to have a ZFS pool with the same name as the host system. Sponsored by: Panzura (porting) git-svn-id: svn://svn.freebsd.org/base/stable/10@333196 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- cddl/contrib/opensolaris/cmd/zpool/zpool.8 | 30 +++++++ .../opensolaris/cmd/zpool/zpool_main.c | 89 ++++++++++++++----- .../lib/libzfs/common/libzfs_pool.c | 11 +++ .../opensolaris/common/zfs/zpool_prop.c | 2 + .../opensolaris/uts/common/fs/zfs/spa.c | 19 +++- .../uts/common/fs/zfs/spa_config.c | 57 ++++++++---- .../opensolaris/uts/common/sys/fs/zfs.h | 2 + 7 files changed, 167 insertions(+), 43 deletions(-) diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool.8 b/cddl/contrib/opensolaris/cmd/zpool/zpool.8 index 61208cb4a..ee22e2d02 100644 --- a/cddl/contrib/opensolaris/cmd/zpool/zpool.8 +++ b/cddl/contrib/opensolaris/cmd/zpool/zpool.8 @@ -57,6 +57,7 @@ .Ar ... .Op Fl m Ar mountpoint .Op Fl R Ar root +.Op Fl t Ar tempname .Ar pool vdev ... .Nm .Cm destroy @@ -108,6 +109,7 @@ .Op Fl m .Op Fl N .Op Fl R Ar root +.Op Fl t .Op Fl F Op Fl n .Ar pool | id .Op Ar newpool @@ -868,6 +870,7 @@ do not actually discard any transactions. .Ar ... .Op Fl m Ar mountpoint .Op Fl R Ar root +.Op Fl t Ar tempname .Ar pool vdev ... .Xc .Pp @@ -969,6 +972,18 @@ or .Qq Cm none . For more information on dataset mount points, see .Xr zfs 8 . +.It Fl t Ar tempname +Sets the in-core pool name to +.Pa tempname +while the on-disk name will be the name specified as the pool name +.Pa pool . +This will set the default +.Sy cachefile +property to +.Sy none . +This is intended to handle name space collisions when creating pools +for other systems, such as virtual machines or physical machines +whose pools live on network block devices. .El .It Xo .Nm @@ -1223,6 +1238,7 @@ Searches for and imports all pools found. .Op Fl m .Op Fl N .Op Fl R Ar root +.Op Fl t .Op Fl F Op Fl n .Ar pool | id .Op Ar newpool @@ -1282,6 +1298,20 @@ Import the pool without mounting any file systems. .It Fl R Ar root Equivalent to .Qq Fl o Cm cachefile=none,altroot= Ns Pa root +.It Fl t +Used with +.Ar newpool . +Specifies that +.Ar newpool +is temporary. +Temporary pool names last until export. +Ensures that the original pool name will be used in all label updates and +therefore is retained upon export. +Will also set +.Sy cachefile +property to +.Sy none +when not explicitly specified. .It Fl F Recovery mode for a non-importable pool. Attempt to return the pool to an importable state by discarding the last few transactions. Not all damaged pools diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c index 5b8679827..933aad811 100644 --- a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c +++ b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c @@ -212,8 +212,9 @@ get_usage(zpool_help_t idx) return (gettext("\tclear [-nF] [device]\n")); case HELP_CREATE: return (gettext("\tcreate [-fnd] [-o property=value] ... \n" - "\t [-O file-system-property=value] ... \n" - "\t [-m mountpoint] [-R root] ...\n")); + "\t [-O file-system-property=value] ...\n" + "\t [-m mountpoint] [-R root] [-t tempname] " + " ...\n")); case HELP_DESTROY: return (gettext("\tdestroy [-f] \n")); case HELP_DETACH: @@ -230,7 +231,7 @@ get_usage(zpool_help_t idx) "[-R root] [-F [-n]] -a\n" "\timport [-o mntopts] [-o property=value] ... \n" "\t [-d dir | -c cachefile] [-D] [-f] [-m] [-N] " - "[-R root] [-F [-n]]\n" + "[-R root] [-F [-n]] [-t]\n" "\t [newpool]\n")); case HELP_IOSTAT: return (gettext("\tiostat [-v] [-T d|u] [pool] ... [interval " @@ -479,6 +480,21 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props, return (0); } +/* + * Set a default property pair (name, string-value) in a property nvlist + */ +static int +add_prop_list_default(const char *propname, char *propval, nvlist_t **props, + boolean_t poolprop) +{ + char *pval; + + if (nvlist_lookup_string(*props, propname, &pval) == 0) + return (0); + + return (add_prop_list(propname, propval, props, poolprop)); +} + /* * zpool add [-fn] ... * @@ -775,14 +791,15 @@ zpool_do_labelclear(int argc, char **argv) /* * zpool create [-fnd] [-o property=value] ... * [-O file-system-property=value] ... - * [-R root] [-m mountpoint] ... + * [-R root] [-m mountpoint] [-t tempname] ... * * -f Force creation, even if devices appear in use * -n Do not create the pool, but display the resulting layout if it * were to be created. - * -R Create a pool under an alternate root - * -m Set default mountpoint for the root dataset. By default it's + * -R Create a pool under an alternate root + * -m Set default mountpoint for the root dataset. By default it's * '/' + * -t Use the temporary name until the pool is exported. * -o Set property=value. * -d Don't automatically enable all supported pool features * (individual features can be enabled with -o). @@ -802,6 +819,7 @@ zpool_do_create(int argc, char **argv) int c; nvlist_t *nvroot = NULL; char *poolname; + char *tname = NULL; int ret = 1; char *altroot = NULL; char *mountpoint = NULL; @@ -810,7 +828,7 @@ zpool_do_create(int argc, char **argv) char *propval; /* check options */ - while ((c = getopt(argc, argv, ":fndR:m:o:O:")) != -1) { + while ((c = getopt(argc, argv, ":fndR:m:o:O:t:")) != -1) { switch (c) { case 'f': force = B_TRUE; @@ -826,11 +844,7 @@ zpool_do_create(int argc, char **argv) if (add_prop_list(zpool_prop_to_name( ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE)) goto errout; - if (nvlist_lookup_string(props, - zpool_prop_to_name(ZPOOL_PROP_CACHEFILE), - &propval) == 0) - break; - if (add_prop_list(zpool_prop_to_name( + if (add_prop_list_default(zpool_prop_to_name( ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) goto errout; break; @@ -889,6 +903,27 @@ zpool_do_create(int argc, char **argv) goto errout; } break; + case 't': + /* + * Sanity check temporary pool name. + */ + if (strchr(optarg, '/') != NULL) { + (void) fprintf(stderr, gettext("cannot create " + "'%s': invalid character '/' in temporary " + "name\n"), optarg); + (void) fprintf(stderr, gettext("use 'zfs " + "create' to create a dataset\n")); + goto errout; + } + + if (add_prop_list(zpool_prop_to_name( + ZPOOL_PROP_TNAME), optarg, &props, B_TRUE)) + goto errout; + if (add_prop_list_default(zpool_prop_to_name( + ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) + goto errout; + tname = optarg; + break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); @@ -1061,8 +1096,8 @@ zpool_do_create(int argc, char **argv) ret = 1; if (zpool_create(g_zfs, poolname, nvroot, props, fsprops) == 0) { - zfs_handle_t *pool = zfs_open(g_zfs, poolname, - ZFS_TYPE_FILESYSTEM); + zfs_handle_t *pool = zfs_open(g_zfs, + tname ? tname : poolname, ZFS_TYPE_FILESYSTEM); if (pool != NULL) { if (zfs_mount(pool, NULL, 0) == 0) ret = zfs_shareall(pool); @@ -1931,7 +1966,8 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, * import [-o mntopts] [-o prop=value] ... [-R root] [-D] * [-d dir | -c cachefile] [-f] -a * import [-o mntopts] [-o prop=value] ... [-R root] [-D] - * [-d dir | -c cachefile] [-f] [-n] [-F] [newpool] + * [-d dir | -c cachefile] [-f] [-n] [-F] [-t] + * [newpool] * * -c Read pool information from a cachefile instead of searching * devices. @@ -1960,6 +1996,9 @@ do_import(nvlist_t *config, const char *newname, const char *mntopts, * * -N Import the pool but don't mount datasets. * + * -t Use newpool as a temporary pool name instead of renaming + * the pool. + * * -T Specify a starting txg to use for import. This option is * intentionally undocumented option for testing purposes. * @@ -2001,7 +2040,7 @@ zpool_do_import(int argc, char **argv) char *endptr; /* check options */ - while ((c = getopt(argc, argv, ":aCc:d:DEfFmnNo:R:T:VX")) != -1) { + while ((c = getopt(argc, argv, ":aCc:d:DEfFmnNo:R:tT:VX")) != -1) { switch (c) { case 'a': do_all = B_TRUE; @@ -2055,11 +2094,13 @@ zpool_do_import(int argc, char **argv) if (add_prop_list(zpool_prop_to_name( ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE)) goto error; - if (nvlist_lookup_string(props, - zpool_prop_to_name(ZPOOL_PROP_CACHEFILE), - &propval) == 0) - break; - if (add_prop_list(zpool_prop_to_name( + if (add_prop_list_default(zpool_prop_to_name( + ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) + goto error; + break; + case 't': + flags |= ZFS_IMPORT_TEMP_NAME; + if (add_prop_list_default(zpool_prop_to_name( ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) goto error; break; @@ -2193,9 +2234,9 @@ zpool_do_import(int argc, char **argv) (void) fprintf(stderr, gettext("cannot import '%s': " "a pool with that name already exists\n"), argv[0]); - (void) fprintf(stderr, gettext("use the form '%s " - " ' to give it a new name\n"), - "zpool import"); + (void) fprintf(stderr, gettext("use the form 'zpool import " + "[-t] ' to give it a new temporary " + "or permanent name\n")); err = 1; } else if (pools == NULL && idata.exists) { (void) fprintf(stderr, gettext("cannot import '%s': " diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c index c0ad0437b..c8a5cbb44 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c @@ -630,6 +630,7 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname, goto error; } break; + case ZPOOL_PROP_READONLY: if (!flags.import) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, @@ -640,6 +641,16 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname, } break; + case ZPOOL_PROP_TNAME: + if (!flags.create) { + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "property '%s' can only be set at " + "creation time"), propname); + (void) zfs_error(hdl, EZFS_BADPROP, errbuf); + goto error; + } + break; + default: zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "property '%s'(%d) not defined"), propname, prop); diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zpool_prop.c b/sys/cddl/contrib/opensolaris/common/zfs/zpool_prop.c index 9c717442e..0a62519cf 100644 --- a/sys/cddl/contrib/opensolaris/common/zfs/zpool_prop.c +++ b/sys/cddl/contrib/opensolaris/common/zfs/zpool_prop.c @@ -130,6 +130,8 @@ zpool_prop_init(void) PROP_READONLY, ZFS_TYPE_POOL, "NAME"); zprop_register_hidden(ZPOOL_PROP_MAXBLOCKSIZE, "maxblocksize", PROP_TYPE_NUMBER, PROP_READONLY, ZFS_TYPE_POOL, "MAXBLOCKSIZE"); + zprop_register_hidden(ZPOOL_PROP_TNAME, "tname", PROP_TYPE_STRING, + PROP_ONETIME, ZFS_TYPE_POOL, "TNAME"); } /* diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c index 745e41c19..9e30aba41 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c @@ -3599,12 +3599,18 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, uint_t nspares, nl2cache; uint64_t version, obj; boolean_t has_features; + char *poolname; + nvlist_t *nvl; + + if (nvlist_lookup_string(props, + zpool_prop_to_name(ZPOOL_PROP_TNAME), &poolname) != 0) + poolname = (char *)pool; /* * If this pool already exists, return failure. */ mutex_enter(&spa_namespace_lock); - if (spa_lookup(pool) != NULL) { + if (spa_lookup(poolname) != NULL) { mutex_exit(&spa_namespace_lock); return (SET_ERROR(EEXIST)); } @@ -3612,9 +3618,12 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, /* * Allocate a new spa_t structure. */ + nvl = fnvlist_alloc(); + fnvlist_add_string(nvl, ZPOOL_CONFIG_POOL_NAME, pool); (void) nvlist_lookup_string(props, zpool_prop_to_name(ZPOOL_PROP_ALTROOT), &altroot); - spa = spa_add(pool, NULL, altroot); + spa = spa_add(poolname, nvl, altroot); + fnvlist_free(nvl); spa_activate(spa, spa_mode_global); if (props && (error = spa_prop_validate(spa, props))) { @@ -3624,6 +3633,12 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props, return (error); } + /* + * Temporary pool names should never be written to disk. + */ + if (poolname != pool) + spa->spa_import_flags |= ZFS_IMPORT_TEMP_NAME; + has_features = B_FALSE; for (nvpair_t *elem = nvlist_next_nvpair(props, NULL); elem != NULL; elem = nvlist_next_nvpair(props, elem)) { diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c index a5c17a564..bb021ce34 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_config.c @@ -230,6 +230,7 @@ spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent) nvlist_t *nvl; boolean_t ccw_failure; int error; + char *pool_name; ASSERT(MUTEX_HELD(&spa_namespace_lock)); @@ -279,11 +280,18 @@ spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent) VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) == 0); - VERIFY(nvlist_add_nvlist(nvl, spa->spa_name, - spa->spa_config) == 0); + if (spa->spa_import_flags & ZFS_IMPORT_TEMP_NAME) { + pool_name = fnvlist_lookup_string(spa->spa_config, + ZPOOL_CONFIG_POOL_NAME); + } else { + pool_name = spa_name(spa); + } + + fnvlist_add_nvlist(nvl, pool_name, + spa->spa_config); mutex_exit(&spa->spa_props_lock); - if (nvlist_lookup_nvlist(nvl, spa->spa_name, &nvroot) == 0) + if (nvlist_lookup_nvlist(nvl, pool_name, &nvroot) == 0) spa_config_clean(nvroot); } @@ -386,6 +394,7 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats) unsigned long hostid = 0; boolean_t locked = B_FALSE; uint64_t split_guid; + char *pool_name; if (vd == NULL) { vd = rvd; @@ -402,20 +411,34 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats) if (txg == -1ULL) txg = spa->spa_config_txg; - VERIFY(nvlist_alloc(&config, NV_UNIQUE_NAME, KM_SLEEP) == 0); - - VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_VERSION, - spa_version(spa)) == 0); - VERIFY(nvlist_add_string(config, ZPOOL_CONFIG_POOL_NAME, - spa_name(spa)) == 0); - VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_STATE, - spa_state(spa)) == 0); - VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_TXG, - txg) == 0); - VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_GUID, - spa_guid(spa)) == 0); - VERIFY(spa->spa_comment == NULL || nvlist_add_string(config, - ZPOOL_CONFIG_COMMENT, spa->spa_comment) == 0); + /* + * Originally, users had to handle spa namespace collisions by either + * exporting the already imported pool or by specifying a new name for + * the pool with a conflicting name. In the case of root pools from + * virtual guests, neither approach to collision resolution is + * reasonable. This is addressed by extending the new name syntax with + * an option to specify that the new name is temporary. When specified, + * ZFS_IMPORT_TEMP_NAME will be set in spa->spa_import_flags to tell us + * to use the previous name, which we do below. + */ + if (spa->spa_import_flags & ZFS_IMPORT_TEMP_NAME) { + pool_name = fnvlist_lookup_string(spa->spa_config, + ZPOOL_CONFIG_POOL_NAME); + } else { + pool_name = spa_name(spa); + } + + config = fnvlist_alloc(); + + fnvlist_add_uint64(config, ZPOOL_CONFIG_VERSION, spa_version(spa)); + fnvlist_add_string(config, ZPOOL_CONFIG_POOL_NAME, pool_name); + fnvlist_add_uint64(config, ZPOOL_CONFIG_POOL_STATE, spa_state(spa)); + fnvlist_add_uint64(config, ZPOOL_CONFIG_POOL_TXG, txg); + fnvlist_add_uint64(config, ZPOOL_CONFIG_POOL_GUID, spa_guid(spa)); + if (spa->spa_comment != NULL) { + fnvlist_add_string(config, ZPOOL_CONFIG_COMMENT, + spa->spa_comment); + } #ifdef _KERNEL diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h b/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h index 6d9a847f7..0cabebefc 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h +++ b/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h @@ -207,6 +207,7 @@ typedef enum { ZPOOL_PROP_FRAGMENTATION, ZPOOL_PROP_LEAKED, ZPOOL_PROP_MAXBLOCKSIZE, + ZPOOL_PROP_TNAME, ZPOOL_NUM_PROPS } zpool_prop_t; @@ -951,6 +952,7 @@ typedef enum { #define ZFS_IMPORT_ANY_HOST 0x2 #define ZFS_IMPORT_MISSING_LOG 0x4 #define ZFS_IMPORT_ONLY 0x8 +#define ZFS_IMPORT_TEMP_NAME 0x20 /* * Sysevent payload members. ZFS will generate the following sysevents with the -- 2.45.0