diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 1c606e077ee2..d095b15a5864 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -2013,7 +2013,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; @@ -2075,6 +2075,10 @@ zpool_do_import(int argc, char **argv) ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) goto error; break; + case 't': + flags |= ZFS_IMPORT_TEMP_NAME; + break; + case 'T': errno = 0; txg = strtoull(optarg, &endptr, 10); diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index df5692d14b1c..882e9e84ab85 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -936,6 +936,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 0x10 /* * Sysevent payload members. ZFS will generate the following sysevents with the diff --git a/man/man8/zpool.8 b/man/man8/zpool.8 index 63037760a045..2cfa8558cd09 100644 --- a/man/man8/zpool.8 +++ b/man/man8/zpool.8 @@ -84,7 +84,7 @@ zpool \- configures ZFS storage pools .LP .nf \fBzpool import\fR [\fB-o \fImntopts\fR\fR] [\fB-o\fR \fIproperty=value\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR] - [\fB-D\fR] [\fB-f\fR] [\fB-m\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR]] \fIpool\fR |\fIid\fR [\fInewpool\fR] + [\fB-D\fR] [\fB-f\fR] [\fB-m\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR]] [\fB-t\fR]] \fIpool\fR |\fIid\fR [\fInewpool\fR] .fi .LP @@ -1300,7 +1300,7 @@ Used with the \fB-F\fR recovery option. Determines whether a non-importable pool .ne 2 .mk .na -\fB\fBzpool import\fR [\fB-o\fR \fImntopts\fR] [ \fB-o\fR \fIproperty\fR=\fIvalue\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR] [\fB-D\fR] [\fB-f\fR] [\fB-m\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR]] \fIpool\fR | \fIid\fR [\fInewpool\fR]\fR +\fB\fBzpool import\fR [\fB-o\fR \fImntopts\fR] [ \fB-o\fR \fIproperty\fR=\fIvalue\fR] ... [\fB-d\fR \fIdir\fR | \fB-c\fR \fIcachefile\fR] [\fB-D\fR] [\fB-f\fR] [\fB-m\fR] [\fB-R\fR \fIroot\fR] [\fB-F\fR [\fB-n\fR]] [\fB-t\fR]] \fIpool\fR | \fIid\fR [\fInewpool\fR]\fR .ad .sp .6 .RS 4n @@ -1406,6 +1406,17 @@ Sets the "\fBcachefile\fR" property to "\fBnone\fR" and the "\fIaltroot\fR" prop Used with the \fB-F\fR recovery option. Determines whether a non-importable pool can be made importable again, but does not actually perform the pool recovery. For more details about pool recovery mode, see the \fB-F\fR option, above. .RE +.sp +.ne 2 +.mk +.na +\fB\fB-t\fR\fR +.ad +.sp .6 +.RS 4n +Used with "\fBnewpool\fR". Specifies that "\fBnewpool\fR" 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. +.RE + .sp .ne 2 .mk diff --git a/module/zfs/spa_config.c b/module/zfs/spa_config.c index 5b95a8e811e5..c8fe79ed5278 100644 --- a/module/zfs/spa_config.c +++ b/module/zfs/spa_config.c @@ -208,6 +208,7 @@ spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent) { spa_config_dirent_t *dp, *tdp; nvlist_t *nvl; + char *pool_name; ASSERT(MUTEX_HELD(&spa_namespace_lock)); @@ -254,7 +255,13 @@ spa_config_sync(spa_t *target, boolean_t removing, boolean_t postsysevent) VERIFY(nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_PUSHPAGE) == 0); - VERIFY(nvlist_add_nvlist(nvl, spa->spa_name, + if (spa->spa_import_flags & ZFS_IMPORT_TEMP_NAME) { + VERIFY0(nvlist_lookup_string(spa->spa_config, + ZPOOL_CONFIG_POOL_NAME, &pool_name)); + } else + pool_name = spa_name(spa); + + VERIFY(nvlist_add_nvlist(nvl, pool_name, spa->spa_config) == 0); mutex_exit(&spa->spa_props_lock); } @@ -337,6 +344,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; @@ -353,12 +361,28 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats) if (txg == -1ULL) txg = spa->spa_config_txg; + /* + * 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) { + VERIFY0(nvlist_lookup_string(spa->spa_config, + ZPOOL_CONFIG_POOL_NAME, &pool_name)); + } else + pool_name = spa_name(spa); + VERIFY(nvlist_alloc(&config, NV_UNIQUE_NAME, KM_PUSHPAGE) == 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); + pool_name) == 0); VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_STATE, spa_state(spa)) == 0); VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_TXG,