zpool: Provide GUID to zpool-reguid(8) with -g (#16239)

This commit extends the zpool-reguid(8) command with a -g flag, which
allows the user to specify the GUID to set.

This change also adds some general tests for zpool-reguid(8).

Sponsored-by: Wasabi Technology, Inc.
Sponsored-by: Klara, Inc.

Signed-off-by: Mateusz Piotrowski <0mp@FreeBSD.org>
Reviewed-by: Rob Norris <rob.norris@klarasystems.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
This commit is contained in:
Mateusz Piotrowski 2024-08-26 18:27:24 +02:00 committed by GitHub
parent 2420ee6e12
commit 6be8bf5552
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 342 additions and 16 deletions

View File

@ -537,7 +537,7 @@ get_usage(zpool_help_t idx)
"\t [-o property=value] <pool> <newpool> "
"[<device> ...]\n"));
case HELP_REGUID:
return (gettext("\treguid <pool>\n"));
return (gettext("\treguid [-g guid] <pool>\n"));
case HELP_SYNC:
return (gettext("\tsync [pool] ...\n"));
case HELP_VERSION:
@ -2025,7 +2025,7 @@ zpool_do_create(int argc, char **argv)
char *end;
u_longlong_t ver;
ver = strtoull(propval, &end, 10);
ver = strtoull(propval, &end, 0);
if (*end == '\0' &&
ver < SPA_VERSION_FEATURES) {
enable_pool_features = B_FALSE;
@ -8232,19 +8232,32 @@ zpool_do_clear(int argc, char **argv)
}
/*
* zpool reguid <pool>
* zpool reguid [-g <guid>] <pool>
*/
int
zpool_do_reguid(int argc, char **argv)
{
uint64_t guid;
uint64_t *guidp = NULL;
int c;
char *endptr;
char *poolname;
zpool_handle_t *zhp;
int ret = 0;
/* check options */
while ((c = getopt(argc, argv, "")) != -1) {
while ((c = getopt(argc, argv, "g:")) != -1) {
switch (c) {
case 'g':
errno = 0;
guid = strtoull(optarg, &endptr, 10);
if (errno != 0 || *endptr != '\0') {
(void) fprintf(stderr,
gettext("invalid GUID: %s\n"), optarg);
usage(B_FALSE);
}
guidp = &guid;
break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@ -8270,7 +8283,7 @@ zpool_do_reguid(int argc, char **argv)
if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
return (1);
ret = zpool_reguid(zhp);
ret = zpool_set_guid(zhp, guidp);
zpool_close(zhp);
return (ret);

View File

@ -6746,7 +6746,7 @@ ztest_reguid(ztest_ds_t *zd, uint64_t id)
load = spa_load_guid(spa);
(void) pthread_rwlock_wrlock(&ztest_name_lock);
error = spa_change_guid(spa);
error = spa_change_guid(spa, NULL);
zs->zs_guid = spa_guid(spa);
(void) pthread_rwlock_unlock(&ztest_name_lock);

View File

@ -300,6 +300,7 @@ _LIBZFS_H int zpool_trim(zpool_handle_t *, pool_trim_func_t, nvlist_t *,
_LIBZFS_H int zpool_clear(zpool_handle_t *, const char *, nvlist_t *);
_LIBZFS_H int zpool_reguid(zpool_handle_t *);
_LIBZFS_H int zpool_set_guid(zpool_handle_t *, const uint64_t *);
_LIBZFS_H int zpool_reopen_one(zpool_handle_t *, void *);
_LIBZFS_H int zpool_sync_one(zpool_handle_t *, void *);

View File

@ -1710,6 +1710,11 @@ typedef enum {
#define ZPOOL_INITIALIZE_COMMAND "initialize_command"
#define ZPOOL_INITIALIZE_VDEVS "initialize_vdevs"
/*
* The following are names used when invoking ZFS_IOC_POOL_REGUID.
*/
#define ZPOOL_REGUID_GUID "guid"
/*
* The following are names used when invoking ZFS_IOC_POOL_TRIM.
*/

View File

@ -1092,7 +1092,7 @@ extern void spa_strfree(char *);
extern uint64_t spa_generate_guid(spa_t *spa);
extern void snprintf_blkptr(char *buf, size_t buflen, const blkptr_t *bp);
extern void spa_freeze(spa_t *spa);
extern int spa_change_guid(spa_t *spa);
extern int spa_change_guid(spa_t *spa, const uint64_t *guidp);
extern void spa_upgrade(spa_t *spa, uint64_t version);
extern void spa_evict_all(void);
extern vdev_t *spa_lookup_by_guid(spa_t *spa, uint64_t guid,

View File

@ -556,6 +556,7 @@
<elf-symbol name='zpool_scan' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zpool_search_import' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zpool_set_bootenv' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zpool_set_guid' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zpool_set_prop' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zpool_set_vdev_prop' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zpool_skip_pool' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
@ -6639,6 +6640,11 @@
<parameter type-id='9c313c2d' name='guid'/>
<return type-id='95e97e5e'/>
</function-decl>
<function-decl name='zpool_set_guid' mangled-name='zpool_set_guid' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zpool_set_guid'>
<parameter type-id='4c81de99' name='zhp'/>
<parameter type-id='713a56f5' name='guid'/>
<return type-id='95e97e5e'/>
</function-decl>
<function-decl name='zpool_reguid' mangled-name='zpool_reguid' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zpool_reguid'>
<parameter type-id='4c81de99' name='zhp'/>
<return type-id='95e97e5e'/>

View File

@ -4310,22 +4310,55 @@ zpool_vdev_clear(zpool_handle_t *zhp, uint64_t guid)
/*
* Change the GUID for a pool.
*
* Similar to zpool_reguid(), but may take a GUID.
*
* If the guid argument is NULL, then no GUID is passed in the nvlist to the
* ioctl().
*/
int
zpool_reguid(zpool_handle_t *zhp)
zpool_set_guid(zpool_handle_t *zhp, const uint64_t *guid)
{
char errbuf[ERRBUFLEN];
libzfs_handle_t *hdl = zhp->zpool_hdl;
nvlist_t *nvl = NULL;
zfs_cmd_t zc = {"\0"};
int error = -1;
if (guid != NULL) {
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
return (no_memory(hdl));
if (nvlist_add_uint64(nvl, ZPOOL_REGUID_GUID, *guid) != 0) {
nvlist_free(nvl);
return (no_memory(hdl));
}
zcmd_write_src_nvlist(hdl, &zc, nvl);
}
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "cannot reguid '%s'"), zhp->zpool_name);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
if (zfs_ioctl(hdl, ZFS_IOC_POOL_REGUID, &zc) == 0)
return (0);
error = zfs_ioctl(hdl, ZFS_IOC_POOL_REGUID, &zc);
if (error) {
return (zpool_standard_error(hdl, errno, errbuf));
}
if (guid != NULL) {
zcmd_free_nvlists(&zc);
nvlist_free(nvl);
}
return (0);
}
return (zpool_standard_error(hdl, errno, errbuf));
/*
* Change the GUID for a pool.
*/
int
zpool_reguid(zpool_handle_t *zhp)
{
return (zpool_set_guid(zhp, NULL));
}
/*

View File

@ -25,8 +25,10 @@
.\" Copyright (c) 2018 George Melikov. All Rights Reserved.
.\" Copyright 2017 Nexenta Systems, Inc.
.\" Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
.\" Copyright (c) 2024, Klara Inc.
.\" Copyright (c) 2024, Mateusz Piotrowski
.\"
.Dd May 31, 2021
.Dd June 21, 2023
.Dt ZPOOL-REGUID 8
.Os
.
@ -36,6 +38,7 @@
.Sh SYNOPSIS
.Nm zpool
.Cm reguid
.Op Fl g Ar guid
.Ar pool
.
.Sh DESCRIPTION
@ -43,6 +46,15 @@ Generates a new unique identifier for the pool.
You must ensure that all devices in this pool are online and healthy before
performing this action.
.
.Bl -tag -width Ds
.It Fl g Ar guid
Set the pool GUID to the provided value.
The GUID can be any 64-bit value accepted by
.Xr strtoull 3
in base 10.
.Nm
will return an error if the provided GUID is already in use.
.El
.Sh SEE ALSO
.Xr zpool-export 8 ,
.Xr zpool-import 8

View File

@ -1040,16 +1040,34 @@ spa_change_guid_sync(void *arg, dmu_tx_t *tx)
* online when we do this, or else any vdevs that weren't present
* would be orphaned from our pool. We are also going to issue a
* sysevent to update any watchers.
*
* The GUID of the pool will be changed to the value pointed to by guidp.
* The GUID may not be set to the reserverd value of 0.
* The new GUID will be generated if guidp is NULL.
*/
int
spa_change_guid(spa_t *spa)
spa_change_guid(spa_t *spa, const uint64_t *guidp)
{
int error;
uint64_t guid;
int error;
mutex_enter(&spa->spa_vdev_top_lock);
mutex_enter(&spa_namespace_lock);
guid = spa_generate_guid(NULL);
if (guidp != NULL) {
guid = *guidp;
if (guid == 0) {
error = SET_ERROR(EINVAL);
goto out;
}
if (spa_guid_exists(guid, 0)) {
error = SET_ERROR(EEXIST);
goto out;
}
} else {
guid = spa_generate_guid(NULL);
}
error = dsl_sync_task(spa->spa_name, spa_change_guid_check,
spa_change_guid_sync, &guid, 5, ZFS_SPACE_CHECK_RESERVED);
@ -1068,6 +1086,7 @@ spa_change_guid(spa_t *spa)
spa_event_notify(spa, NULL, NULL, ESC_ZFS_POOL_REGUID);
}
out:
mutex_exit(&spa_namespace_lock);
mutex_exit(&spa->spa_vdev_top_lock);

View File

@ -1794,17 +1794,45 @@ zfs_ioc_pool_get_history(zfs_cmd_t *zc)
return (error);
}
/*
* inputs:
* zc_nvlist_src nvlist optionally containing ZPOOL_REGUID_GUID
* zc_nvlist_src_size size of the nvlist
*/
static int
zfs_ioc_pool_reguid(zfs_cmd_t *zc)
{
uint64_t *guidp = NULL;
nvlist_t *props = NULL;
spa_t *spa;
uint64_t guid;
int error;
if (zc->zc_nvlist_src_size != 0) {
error = get_nvlist(zc->zc_nvlist_src, zc->zc_nvlist_src_size,
zc->zc_iflags, &props);
if (error != 0)
return (error);
error = nvlist_lookup_uint64(props, ZPOOL_REGUID_GUID, &guid);
if (error == 0)
guidp = &guid;
else if (error == ENOENT)
guidp = NULL;
else
goto out;
}
error = spa_open(zc->zc_name, &spa, FTAG);
if (error == 0) {
error = spa_change_guid(spa);
error = spa_change_guid(spa, guidp);
spa_close(spa, FTAG);
}
out:
if (props != NULL)
nvlist_free(props);
return (error);
}

View File

@ -514,6 +514,10 @@ tags = ['functional', 'cli_root', 'zpool_offline']
tests = ['zpool_online_001_pos', 'zpool_online_002_neg']
tags = ['functional', 'cli_root', 'zpool_online']
[tests/functional/cli_root/zpool_reguid]
tests = ['zpool_reguid_001_pos', 'zpool_reguid_002_neg']
tags = ['functional', 'cli_root', 'zpool_reguid']
[tests/functional/cli_root/zpool_remove]
tests = ['zpool_remove_001_neg', 'zpool_remove_002_pos',
'zpool_remove_003_pos']

View File

@ -0,0 +1,6 @@
pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/cli_root/zpool_reguid
dist_pkgdata_SCRIPTS = \
setup.ksh \
cleanup.ksh \
zpool_reguid_001_pos.ksh \
zpool_reguid_002_neg.ksh

View File

@ -0,0 +1,32 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or https://opensource.org/licenses/CDDL-1.0.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
. $STF_SUITE/include/libtest.shlib
verify_runnable "global"
default_cleanup

View File

@ -0,0 +1,34 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or https://opensource.org/licenses/CDDL-1.0.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
. $STF_SUITE/include/libtest.shlib
verify_runnable "global"
DISK=${DISKS%% *}
default_setup $DISK

View File

@ -0,0 +1,73 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
# CDDL HEADER END
#
# Copyright 2023 Mateusz Piotrowski
#
. $STF_SUITE/include/libtest.shlib
#
# DESCRIPTION:
# Verify 'zpool reguid' can change pool's GUID.
#
# STRATEGY:
# 1. Use zpool get to obtain the initial GUID of a pool.
# 2. Change pool's GUID with zpool reguid.
# 3. Verify the GUID has changed to a random GUID.
#
# 4. Change pool's GUID with zpool reguid -g.
# 5. Verify the GUID has changed to the specified GUID.
#
# set_guid guid [expected_guid]
set_guid() {
gflag_guid="$1"
expected_guid="${2:-"$gflag_guid"}"
initial_guid="$(zpool get -H -o value guid "$TESTPOOL")"
log_assert "Verify 'zpool reguid -g \"$gflag_guid\"' sets GUID as expected."
log_must zpool reguid -g "$gflag_guid" "$TESTPOOL"
retrieved_guid="$(zpool get -H -o value guid "$TESTPOOL")"
if [[ "$retrieved_guid" == "" ]]; then
log_fail "Unable to obtain the new GUID of pool $TESTPOOL"
fi
if [[ "$expected_guid" != "$retrieved_guid" ]]; then
log_fail "GUID set to '$retrieved_guid' instead of '$expected_guid'"
fi
}
log_assert "Verify 'zpool reguid' picks a new random GUID for the pool."
initial_guid="$(zpool get -H -o value guid "$TESTPOOL")"
if [[ $initial_guid == "" ]]; then
log_fail "Unable to obtain the initial GUID of pool $TESTPOOL"
fi
log_must zpool reguid "$TESTPOOL"
new_guid="$(zpool get -H -o value guid "$TESTPOOL")"
if [[ "$new_guid" == "" ]]; then
log_fail "Unable to obtain the new GUID of pool $TESTPOOL"
fi
if [[ "$initial_guid" == "$new_guid" ]]; then
log_fail "GUID change failed; GUID has not changed: $initial_guid"
fi
for g in "$(bc -e '2^64 - 1')" 0; do
set_guid "$g"
done
# zpool-reguid(8) will strip the leading 0.
set_guid 0123 "123"
# GUID "-1" is effectively 2^64 - 1 in value.
set_guid -1 "$(bc -e '2^64 - 1')"
log_pass "'zpool reguid' changes GUID as expected."

View File

@ -0,0 +1,60 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
# CDDL HEADER END
#
# Copyright 2023 Mateusz Piotrowski
#
. $STF_SUITE/include/libtest.shlib
#
# DESCRIPTION:
# Verify 'zpool reguid' does not accept invalid GUIDs.
#
# STRATEGY:
# 1. Call zpool reguid with an invalid GUID.
# 2. Verify that the call fails.
# 3. Verify that the pool GUID did not change.
#
# 4. Call zpool reguid with a GUID that is already in use.
# 5. Verify that the call fails.
#
check_guid() {
invalid_guid="$1"
initial_guid="$(zpool get -H -o value guid "$TESTPOOL")"
log_assert "'zpool reguid' will not accept invalid GUID '$invalid_guid'"
if zpool reguid -g "$invalid_guid" "$TESTPOOL"; then
log_fail "'zpool reguid' accepted invalid GUID: $invalid_guid"
fi
final_guid="$(zpool get -H -o value guid "$TESTPOOL")"
if [[ "$initial_guid" != "$final_guid" ]]; then
log_fail "Invalid GUID change from '$initial_guid' to '$final_guid'"
fi
}
log_assert "Verify 'zpool reguid' does not accept invalid GUIDs"
for ig in "$(bc -e '2^64')" 0xA 0xa; do
check_guid "$ig"
done
guid="42"
log_assert "Verify 'zpool reguid -g' does not accept GUID which are already in use"
log_must zpool reguid -g "$guid" "$TESTPOOL"
if zpool reguid -g "$guid" "$TESTPOOL"; then
log_fail "'zpool reguid' accepted GUID that was already in use: $invalid_guid"
fi
log_pass "'zpool reguid' does not accept invalid GUIDs."