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 ...
.Op Fl m Ar mountpoint
.Op Fl R Ar root
.Op Fl t Ar tempname
.Ar pool vdev ...
.Nm
.Cm destroy
@ -115,6 +116,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
@ -961,6 +963,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
@ -1062,6 +1065,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
@ -1316,6 +1331,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
@ -1375,6 +1391,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

View File

@ -220,8 +220,9 @@ get_usage(zpool_help_t idx)
case HELP_CREATE:
return (gettext("\tcreate [-fnd] [-B] "
"[-o property=value] ... \n"
"\t [-O file-system-property=value] ... \n"
"\t [-m mountpoint] [-R root] <pool> <vdev> ...\n"));
"\t [-O file-system-property=value] ...\n"
"\t [-m mountpoint] [-R root] [-t tempname] "
"<pool> <vdev> ...\n"));
case HELP_CHECKPOINT:
return (gettext("\tcheckpoint [--discard] <pool> ...\n"));
case HELP_DESTROY:
@ -239,7 +240,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 [--rewind-to-checkpoint] <pool | id> [newpool]\n"));
case HELP_IOSTAT:
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);
}
/*
* 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> ...
*
@ -850,15 +866,16 @@ zpool_do_labelclear(int argc, char **argv)
/*
* zpool create [-fnd] [-B] [-o 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.
* -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
* '/<pool>'
* -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).
@ -882,6 +899,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;
@ -890,7 +908,7 @@ zpool_do_create(int argc, char **argv)
char *propval;
/* 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) {
case 'f':
force = B_TRUE;
@ -922,11 +940,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;
@ -999,6 +1013,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);
@ -1205,8 +1240,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);
@ -2162,7 +2197,8 @@ zpool_do_checkpoint(int argc, char **argv)
* 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] <pool | id> [newpool]
* [-d dir | -c cachefile] [-f] [-n] [-F] [-t]
* <pool | id> [newpool]
*
* -c Read pool information from a cachefile instead of searching
* devices.
@ -2191,6 +2227,9 @@ zpool_do_checkpoint(int argc, char **argv)
*
* -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.
*
@ -2241,7 +2280,7 @@ zpool_do_import(int argc, char **argv)
};
/* 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) {
switch (c) {
case 'a':
@ -2296,11 +2335,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;
@ -2439,9 +2480,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 "
"<pool | id> <newpool>' to give it a new name\n"),
"zpool import");
(void) fprintf(stderr, gettext("use the form 'zpool import "
"[-t] <pool | id> <newpool>' 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': "

View File

@ -644,6 +644,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,
@ -654,6 +655,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);

View File

@ -136,6 +136,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");
}
/*

View File

@ -4666,12 +4666,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));
}
@ -4679,9 +4685,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))) {
@ -4691,6 +4700,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)) {

View File

@ -226,6 +226,7 @@ spa_write_cachefile(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));
@ -274,11 +275,18 @@ spa_write_cachefile(spa_t *target, boolean_t removing, boolean_t postsysevent)
if (nvl == NULL)
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);
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);
}
@ -382,6 +390,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;
@ -398,10 +407,27 @@ 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) {
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, 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_TXG, txg);
fnvlist_add_uint64(config, ZPOOL_CONFIG_POOL_GUID, spa_guid(spa));

View File

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