allow ZFS pool to have temporary name for duration of current import

The change adds -t <name> 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.

The changes come from ZoL with some small tweaks.
The porting has been done by julian.

The change is being submitted to OpenZFS:
https://github.com/openzfs/openzfs/pull/600

Submitted by:	julian
Reviewed by:	smh
MFC after:	2 weeks
Sponsored by:	Panzura (porting)
Differential Revision: https://reviews.freebsd.org/D14972
This commit is contained in:
Andriy Gapon 2018-04-12 10:37:26 +00:00
parent 6825c3a0c9
commit 81f187e576
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=332426
7 changed files with 156 additions and 29 deletions

View File

@ -62,6 +62,7 @@
.Ar ... .Ar ...
.Op Fl m Ar mountpoint .Op Fl m Ar mountpoint
.Op Fl R Ar root .Op Fl R Ar root
.Op Fl t Ar tempname
.Ar pool vdev ... .Ar pool vdev ...
.Nm .Nm
.Cm destroy .Cm destroy
@ -115,6 +116,7 @@
.Op Fl m .Op Fl m
.Op Fl N .Op Fl N
.Op Fl R Ar root .Op Fl R Ar root
.Op Fl t
.Op Fl F Op Fl n .Op Fl F Op Fl n
.Ar pool | id .Ar pool | id
.Op Ar newpool .Op Ar newpool
@ -961,6 +963,7 @@ do not actually discard any transactions.
.Ar ... .Ar ...
.Op Fl m Ar mountpoint .Op Fl m Ar mountpoint
.Op Fl R Ar root .Op Fl R Ar root
.Op Fl t Ar tempname
.Ar pool vdev ... .Ar pool vdev ...
.Xc .Xc
.Pp .Pp
@ -1062,6 +1065,18 @@ or
.Qq Cm none . .Qq Cm none .
For more information on dataset mount points, see For more information on dataset mount points, see
.Xr zfs 8 . .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 .El
.It Xo .It Xo
.Nm .Nm
@ -1316,6 +1331,7 @@ Searches for and imports all pools found.
.Op Fl m .Op Fl m
.Op Fl N .Op Fl N
.Op Fl R Ar root .Op Fl R Ar root
.Op Fl t
.Op Fl F Op Fl n .Op Fl F Op Fl n
.Ar pool | id .Ar pool | id
.Op Ar newpool .Op Ar newpool
@ -1375,6 +1391,20 @@ Import the pool without mounting any file systems.
.It Fl R Ar root .It Fl R Ar root
Equivalent to Equivalent to
.Qq Fl o Cm cachefile=none,altroot= Ns Pa root .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 .It Fl F
Recovery mode for a non-importable pool. Attempt to return the pool to an 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 importable state by discarding the last few transactions. Not all damaged pools

View File

@ -220,8 +220,9 @@ get_usage(zpool_help_t idx)
case HELP_CREATE: case HELP_CREATE:
return (gettext("\tcreate [-fnd] [-B] " return (gettext("\tcreate [-fnd] [-B] "
"[-o property=value] ... \n" "[-o property=value] ... \n"
"\t [-O file-system-property=value] ... \n" "\t [-O file-system-property=value] ...\n"
"\t [-m mountpoint] [-R root] <pool> <vdev> ...\n")); "\t [-m mountpoint] [-R root] [-t tempname] "
"<pool> <vdev> ...\n"));
case HELP_CHECKPOINT: case HELP_CHECKPOINT:
return (gettext("\tcheckpoint [--discard] <pool> ...\n")); return (gettext("\tcheckpoint [--discard] <pool> ...\n"));
case HELP_DESTROY: case HELP_DESTROY:
@ -239,7 +240,7 @@ get_usage(zpool_help_t idx)
"[-R root] [-F [-n]] -a\n" "[-R root] [-F [-n]] -a\n"
"\timport [-o mntopts] [-o property=value] ... \n" "\timport [-o mntopts] [-o property=value] ... \n"
"\t [-d dir | -c cachefile] [-D] [-f] [-m] [-N] " "\t [-d dir | -c cachefile] [-D] [-f] [-m] [-N] "
"[-R root] [-F [-n]]\n" "[-R root] [-F [-n]] [-t]\n"
"\t [--rewind-to-checkpoint] <pool | id> [newpool]\n")); "\t [--rewind-to-checkpoint] <pool | id> [newpool]\n"));
case HELP_IOSTAT: case HELP_IOSTAT:
return (gettext("\tiostat [-v] [-T d|u] [pool] ... [interval " return (gettext("\tiostat [-v] [-T d|u] [pool] ... [interval "
@ -488,6 +489,21 @@ add_prop_list(const char *propname, char *propval, nvlist_t **props,
return (0); 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] <pool> <vdev> ... * zpool add [-fn] <pool> <vdev> ...
* *
@ -850,15 +866,16 @@ zpool_do_labelclear(int argc, char **argv)
/* /*
* zpool create [-fnd] [-B] [-o property=value] ... * zpool create [-fnd] [-B] [-o property=value] ...
* [-O file-system-property=value] ... * [-O file-system-property=value] ...
* [-R root] [-m mountpoint] <pool> <dev> ... * [-R root] [-m mountpoint] [-t tempname] <pool> <dev> ...
* *
* -B Create boot partition. * -B Create boot partition.
* -f Force creation, even if devices appear in use * -f Force creation, even if devices appear in use
* -n Do not create the pool, but display the resulting layout if it * -n Do not create the pool, but display the resulting layout if it
* were to be created. * were to be created.
* -R Create a pool under an alternate root * -R Create a pool under an alternate root
* -m Set default mountpoint for the root dataset. By default it's * -m Set default mountpoint for the root dataset. By default it's
* '/<pool>' * '/<pool>'
* -t Use the temporary name until the pool is exported.
* -o Set property=value. * -o Set property=value.
* -d Don't automatically enable all supported pool features * -d Don't automatically enable all supported pool features
* (individual features can be enabled with -o). * (individual features can be enabled with -o).
@ -882,6 +899,7 @@ zpool_do_create(int argc, char **argv)
int c; int c;
nvlist_t *nvroot = NULL; nvlist_t *nvroot = NULL;
char *poolname; char *poolname;
char *tname = NULL;
int ret = 1; int ret = 1;
char *altroot = NULL; char *altroot = NULL;
char *mountpoint = NULL; char *mountpoint = NULL;
@ -890,7 +908,7 @@ zpool_do_create(int argc, char **argv)
char *propval; char *propval;
/* check options */ /* check options */
while ((c = getopt(argc, argv, ":fndBR:m:o:O:")) != -1) { while ((c = getopt(argc, argv, ":fndBR:m:o:O:t:")) != -1) {
switch (c) { switch (c) {
case 'f': case 'f':
force = B_TRUE; force = B_TRUE;
@ -922,11 +940,7 @@ zpool_do_create(int argc, char **argv)
if (add_prop_list(zpool_prop_to_name( if (add_prop_list(zpool_prop_to_name(
ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE)) ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE))
goto errout; goto errout;
if (nvlist_lookup_string(props, if (add_prop_list_default(zpool_prop_to_name(
zpool_prop_to_name(ZPOOL_PROP_CACHEFILE),
&propval) == 0)
break;
if (add_prop_list(zpool_prop_to_name(
ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
goto errout; goto errout;
break; break;
@ -999,6 +1013,27 @@ zpool_do_create(int argc, char **argv)
goto errout; goto errout;
} }
break; 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 ':': case ':':
(void) fprintf(stderr, gettext("missing argument for " (void) fprintf(stderr, gettext("missing argument for "
"'%c' option\n"), optopt); "'%c' option\n"), optopt);
@ -1205,8 +1240,8 @@ zpool_do_create(int argc, char **argv)
ret = 1; ret = 1;
if (zpool_create(g_zfs, poolname, if (zpool_create(g_zfs, poolname,
nvroot, props, fsprops) == 0) { nvroot, props, fsprops) == 0) {
zfs_handle_t *pool = zfs_open(g_zfs, poolname, zfs_handle_t *pool = zfs_open(g_zfs,
ZFS_TYPE_FILESYSTEM); tname ? tname : poolname, ZFS_TYPE_FILESYSTEM);
if (pool != NULL) { if (pool != NULL) {
if (zfs_mount(pool, NULL, 0) == 0) if (zfs_mount(pool, NULL, 0) == 0)
ret = zfs_shareall(pool); ret = zfs_shareall(pool);
@ -2162,7 +2197,8 @@ zpool_do_checkpoint(int argc, char **argv)
* import [-o mntopts] [-o prop=value] ... [-R root] [-D] * import [-o mntopts] [-o prop=value] ... [-R root] [-D]
* [-d dir | -c cachefile] [-f] -a * [-d dir | -c cachefile] [-f] -a
* import [-o mntopts] [-o prop=value] ... [-R root] [-D] * import [-o mntopts] [-o prop=value] ... [-R root] [-D]
* [-d dir | -c cachefile] [-f] [-n] [-F] <pool | id> [newpool] * [-d dir | -c cachefile] [-f] [-n] [-F] [-t]
* <pool | id> [newpool]
* *
* -c Read pool information from a cachefile instead of searching * -c Read pool information from a cachefile instead of searching
* devices. * devices.
@ -2191,6 +2227,9 @@ zpool_do_checkpoint(int argc, char **argv)
* *
* -N Import the pool but don't mount datasets. * -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 * -T Specify a starting txg to use for import. This option is
* intentionally undocumented option for testing purposes. * intentionally undocumented option for testing purposes.
* *
@ -2241,7 +2280,7 @@ zpool_do_import(int argc, char **argv)
}; };
/* check options */ /* check options */
while ((c = getopt_long(argc, argv, ":aCc:d:DEfFmnNo:rR:T:VX", while ((c = getopt_long(argc, argv, ":aCc:d:DEfFmnNo:rR:tT:VX",
long_options, NULL)) != -1) { long_options, NULL)) != -1) {
switch (c) { switch (c) {
case 'a': case 'a':
@ -2296,11 +2335,13 @@ zpool_do_import(int argc, char **argv)
if (add_prop_list(zpool_prop_to_name( if (add_prop_list(zpool_prop_to_name(
ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE)) ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE))
goto error; goto error;
if (nvlist_lookup_string(props, if (add_prop_list_default(zpool_prop_to_name(
zpool_prop_to_name(ZPOOL_PROP_CACHEFILE), ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
&propval) == 0) goto error;
break; break;
if (add_prop_list(zpool_prop_to_name( case 't':
flags |= ZFS_IMPORT_TEMP_NAME;
if (add_prop_list_default(zpool_prop_to_name(
ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
goto error; goto error;
break; break;
@ -2439,9 +2480,9 @@ zpool_do_import(int argc, char **argv)
(void) fprintf(stderr, gettext("cannot import '%s': " (void) fprintf(stderr, gettext("cannot import '%s': "
"a pool with that name already exists\n"), "a pool with that name already exists\n"),
argv[0]); argv[0]);
(void) fprintf(stderr, gettext("use the form '%s " (void) fprintf(stderr, gettext("use the form 'zpool import "
"<pool | id> <newpool>' to give it a new name\n"), "[-t] <pool | id> <newpool>' to give it a new temporary "
"zpool import"); "or permanent name\n"));
err = 1; err = 1;
} else if (pools == NULL && idata.exists) { } else if (pools == NULL && idata.exists) {
(void) fprintf(stderr, gettext("cannot import '%s': " (void) fprintf(stderr, gettext("cannot import '%s': "

View File

@ -644,6 +644,7 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
goto error; goto error;
} }
break; break;
case ZPOOL_PROP_READONLY: case ZPOOL_PROP_READONLY:
if (!flags.import) { if (!flags.import) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@ -654,6 +655,16 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
} }
break; 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: default:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"property '%s'(%d) not defined"), propname, prop); "property '%s'(%d) not defined"), propname, prop);

View File

@ -136,6 +136,8 @@ zpool_prop_init(void)
PROP_READONLY, ZFS_TYPE_POOL, "NAME"); PROP_READONLY, ZFS_TYPE_POOL, "NAME");
zprop_register_hidden(ZPOOL_PROP_MAXBLOCKSIZE, "maxblocksize", zprop_register_hidden(ZPOOL_PROP_MAXBLOCKSIZE, "maxblocksize",
PROP_TYPE_NUMBER, PROP_READONLY, ZFS_TYPE_POOL, "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");
} }
/* /*

View File

@ -4666,12 +4666,18 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
uint_t nspares, nl2cache; uint_t nspares, nl2cache;
uint64_t version, obj; uint64_t version, obj;
boolean_t has_features; 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. * If this pool already exists, return failure.
*/ */
mutex_enter(&spa_namespace_lock); mutex_enter(&spa_namespace_lock);
if (spa_lookup(pool) != NULL) { if (spa_lookup(poolname) != NULL) {
mutex_exit(&spa_namespace_lock); mutex_exit(&spa_namespace_lock);
return (SET_ERROR(EEXIST)); return (SET_ERROR(EEXIST));
} }
@ -4679,9 +4685,12 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
/* /*
* Allocate a new spa_t structure. * Allocate a new spa_t structure.
*/ */
nvl = fnvlist_alloc();
fnvlist_add_string(nvl, ZPOOL_CONFIG_POOL_NAME, pool);
(void) nvlist_lookup_string(props, (void) nvlist_lookup_string(props,
zpool_prop_to_name(ZPOOL_PROP_ALTROOT), &altroot); 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); spa_activate(spa, spa_mode_global);
if (props && (error = spa_prop_validate(spa, props))) { if (props && (error = spa_prop_validate(spa, props))) {
@ -4691,6 +4700,12 @@ spa_create(const char *pool, nvlist_t *nvroot, nvlist_t *props,
return (error); 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; has_features = B_FALSE;
for (nvpair_t *elem = nvlist_next_nvpair(props, NULL); for (nvpair_t *elem = nvlist_next_nvpair(props, NULL);
elem != NULL; elem = nvlist_next_nvpair(props, elem)) { elem != NULL; elem = nvlist_next_nvpair(props, elem)) {

View File

@ -226,6 +226,7 @@ spa_write_cachefile(spa_t *target, boolean_t removing, boolean_t postsysevent)
nvlist_t *nvl; nvlist_t *nvl;
boolean_t ccw_failure; boolean_t ccw_failure;
int error; int error;
char *pool_name;
ASSERT(MUTEX_HELD(&spa_namespace_lock)); ASSERT(MUTEX_HELD(&spa_namespace_lock));
@ -274,11 +275,18 @@ spa_write_cachefile(spa_t *target, boolean_t removing, boolean_t postsysevent)
if (nvl == NULL) if (nvl == NULL)
nvl = fnvlist_alloc(); nvl = fnvlist_alloc();
fnvlist_add_nvlist(nvl, spa->spa_name, 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); spa->spa_config);
mutex_exit(&spa->spa_props_lock); 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); spa_config_clean(nvroot);
} }
@ -382,6 +390,7 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats)
unsigned long hostid = 0; unsigned long hostid = 0;
boolean_t locked = B_FALSE; boolean_t locked = B_FALSE;
uint64_t split_guid; uint64_t split_guid;
char *pool_name;
if (vd == NULL) { if (vd == NULL) {
vd = rvd; vd = rvd;
@ -398,10 +407,27 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats)
if (txg == -1ULL) if (txg == -1ULL)
txg = spa->spa_config_txg; 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) {
pool_name = fnvlist_lookup_string(spa->spa_config,
ZPOOL_CONFIG_POOL_NAME);
} else {
pool_name = spa_name(spa);
}
config = fnvlist_alloc(); config = fnvlist_alloc();
fnvlist_add_uint64(config, ZPOOL_CONFIG_VERSION, spa_version(spa)); fnvlist_add_uint64(config, ZPOOL_CONFIG_VERSION, spa_version(spa));
fnvlist_add_string(config, ZPOOL_CONFIG_POOL_NAME, spa_name(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_STATE, spa_state(spa));
fnvlist_add_uint64(config, ZPOOL_CONFIG_POOL_TXG, txg); fnvlist_add_uint64(config, ZPOOL_CONFIG_POOL_TXG, txg);
fnvlist_add_uint64(config, ZPOOL_CONFIG_POOL_GUID, spa_guid(spa)); fnvlist_add_uint64(config, ZPOOL_CONFIG_POOL_GUID, spa_guid(spa));

View File

@ -214,6 +214,7 @@ typedef enum {
ZPOOL_PROP_MAXBLOCKSIZE, ZPOOL_PROP_MAXBLOCKSIZE,
ZPOOL_PROP_BOOTSIZE, ZPOOL_PROP_BOOTSIZE,
ZPOOL_PROP_CHECKPOINT, ZPOOL_PROP_CHECKPOINT,
ZPOOL_PROP_TNAME,
ZPOOL_NUM_PROPS ZPOOL_NUM_PROPS
} zpool_prop_t; } zpool_prop_t;
@ -1047,6 +1048,7 @@ typedef enum {
#define ZFS_IMPORT_MISSING_LOG 0x4 #define ZFS_IMPORT_MISSING_LOG 0x4
#define ZFS_IMPORT_ONLY 0x8 #define ZFS_IMPORT_ONLY 0x8
#define ZFS_IMPORT_CHECKPOINT 0x10 #define ZFS_IMPORT_CHECKPOINT 0x10
#define ZFS_IMPORT_TEMP_NAME 0x20
/* /*
* Channel program argument/return nvlist keys and defaults. * Channel program argument/return nvlist keys and defaults.