diff options
Diffstat (limited to 'lib/libzfs/libzfs_pool.c')
-rw-r--r-- | lib/libzfs/libzfs_pool.c | 5763 |
1 files changed, 0 insertions, 5763 deletions
diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c deleted file mode 100644 index ce154ae1a4cd..000000000000 --- a/lib/libzfs/libzfs_pool.c +++ /dev/null @@ -1,5763 +0,0 @@ -// SPDX-License-Identifier: CDDL-1.0 -/* - * 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 2015 Nexenta Systems, Inc. All rights reserved. - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2024 by Delphix. All rights reserved. - * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com> - * Copyright (c) 2018 Datto Inc. - * Copyright (c) 2017 Open-E, Inc. All Rights Reserved. - * Copyright (c) 2017, Intel Corporation. - * Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com> - * Copyright (c) 2021, Colm Buckley <colm@tuatha.org> - * Copyright (c) 2021, 2023, Klara Inc. - * Copyright (c) 2025 Hewlett Packard Enterprise Development LP. - */ - -#include <errno.h> -#include <libintl.h> -#include <stdio.h> -#include <stdlib.h> -#include <strings.h> -#include <unistd.h> -#include <libgen.h> -#include <zone.h> -#include <sys/stat.h> -#include <sys/efi_partition.h> -#include <sys/systeminfo.h> -#include <sys/zfs_ioctl.h> -#include <sys/zfs_sysfs.h> -#include <sys/vdev_disk.h> -#include <sys/types.h> -#include <dlfcn.h> -#include <libzutil.h> -#include <fcntl.h> - -#include "zfs_namecheck.h" -#include "zfs_prop.h" -#include "libzfs_impl.h" -#include "zfs_comutil.h" -#include "zfeature_common.h" - -static boolean_t zpool_vdev_is_interior(const char *name); - -typedef struct prop_flags { - unsigned int create:1; /* Validate property on creation */ - unsigned int import:1; /* Validate property on import */ - unsigned int vdevprop:1; /* Validate property as a VDEV property */ -} prop_flags_t; - -/* - * ==================================================================== - * zpool property functions - * ==================================================================== - */ - -static int -zpool_get_all_props(zpool_handle_t *zhp) -{ - zfs_cmd_t zc = {"\0"}; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - - if (zhp->zpool_n_propnames > 0) { - nvlist_t *innvl = fnvlist_alloc(); - fnvlist_add_string_array(innvl, ZPOOL_GET_PROPS_NAMES, - zhp->zpool_propnames, zhp->zpool_n_propnames); - zcmd_write_src_nvlist(hdl, &zc, innvl); - fnvlist_free(innvl); - } - - zcmd_alloc_dst_nvlist(hdl, &zc, 0); - - while (zfs_ioctl(hdl, ZFS_IOC_POOL_GET_PROPS, &zc) != 0) { - if (errno == ENOMEM) - zcmd_expand_dst_nvlist(hdl, &zc); - else { - zcmd_free_nvlists(&zc); - return (-1); - } - } - - if (zcmd_read_dst_nvlist(hdl, &zc, &zhp->zpool_props) != 0) { - zcmd_free_nvlists(&zc); - return (-1); - } - - zcmd_free_nvlists(&zc); - - return (0); -} - -int -zpool_props_refresh(zpool_handle_t *zhp) -{ - nvlist_t *old_props; - - old_props = zhp->zpool_props; - - if (zpool_get_all_props(zhp) != 0) - return (-1); - - nvlist_free(old_props); - return (0); -} - -static const char * -zpool_get_prop_string(zpool_handle_t *zhp, zpool_prop_t prop, - zprop_source_t *src) -{ - nvlist_t *nv, *nvl; - const char *value; - zprop_source_t source; - - nvl = zhp->zpool_props; - if (nvlist_lookup_nvlist(nvl, zpool_prop_to_name(prop), &nv) == 0) { - source = fnvlist_lookup_uint64(nv, ZPROP_SOURCE); - value = fnvlist_lookup_string(nv, ZPROP_VALUE); - } else { - source = ZPROP_SRC_DEFAULT; - if ((value = zpool_prop_default_string(prop)) == NULL) - value = "-"; - } - - if (src) - *src = source; - - return (value); -} - -uint64_t -zpool_get_prop_int(zpool_handle_t *zhp, zpool_prop_t prop, zprop_source_t *src) -{ - nvlist_t *nv, *nvl; - uint64_t value; - zprop_source_t source; - - if (zhp->zpool_props == NULL && zpool_get_all_props(zhp)) { - /* - * zpool_get_all_props() has most likely failed because - * the pool is faulted, but if all we need is the top level - * vdev's guid then get it from the zhp config nvlist. - */ - if ((prop == ZPOOL_PROP_GUID) && - (nvlist_lookup_nvlist(zhp->zpool_config, - ZPOOL_CONFIG_VDEV_TREE, &nv) == 0) && - (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &value) - == 0)) { - return (value); - } - return (zpool_prop_default_numeric(prop)); - } - - nvl = zhp->zpool_props; - if (nvlist_lookup_nvlist(nvl, zpool_prop_to_name(prop), &nv) == 0) { - source = fnvlist_lookup_uint64(nv, ZPROP_SOURCE); - value = fnvlist_lookup_uint64(nv, ZPROP_VALUE); - } else { - source = ZPROP_SRC_DEFAULT; - value = zpool_prop_default_numeric(prop); - } - - if (src) - *src = source; - - return (value); -} - -/* - * Map VDEV STATE to printed strings. - */ -const char * -zpool_state_to_name(vdev_state_t state, vdev_aux_t aux) -{ - switch (state) { - case VDEV_STATE_CLOSED: - case VDEV_STATE_OFFLINE: - return (gettext("OFFLINE")); - case VDEV_STATE_REMOVED: - return (gettext("REMOVED")); - case VDEV_STATE_CANT_OPEN: - if (aux == VDEV_AUX_CORRUPT_DATA || aux == VDEV_AUX_BAD_LOG) - return (gettext("FAULTED")); - else if (aux == VDEV_AUX_SPLIT_POOL) - return (gettext("SPLIT")); - else - return (gettext("UNAVAIL")); - case VDEV_STATE_FAULTED: - return (gettext("FAULTED")); - case VDEV_STATE_DEGRADED: - return (gettext("DEGRADED")); - case VDEV_STATE_HEALTHY: - return (gettext("ONLINE")); - - default: - break; - } - - return (gettext("UNKNOWN")); -} - -/* - * Map POOL STATE to printed strings. - */ -const char * -zpool_pool_state_to_name(pool_state_t state) -{ - switch (state) { - default: - break; - case POOL_STATE_ACTIVE: - return (gettext("ACTIVE")); - case POOL_STATE_EXPORTED: - return (gettext("EXPORTED")); - case POOL_STATE_DESTROYED: - return (gettext("DESTROYED")); - case POOL_STATE_SPARE: - return (gettext("SPARE")); - case POOL_STATE_L2CACHE: - return (gettext("L2CACHE")); - case POOL_STATE_UNINITIALIZED: - return (gettext("UNINITIALIZED")); - case POOL_STATE_UNAVAIL: - return (gettext("UNAVAIL")); - case POOL_STATE_POTENTIALLY_ACTIVE: - return (gettext("POTENTIALLY_ACTIVE")); - } - - return (gettext("UNKNOWN")); -} - -/* - * Given a pool handle, return the pool health string ("ONLINE", "DEGRADED", - * "SUSPENDED", etc). - */ -const char * -zpool_get_state_str(zpool_handle_t *zhp) -{ - zpool_errata_t errata; - zpool_status_t status; - const char *str; - - status = zpool_get_status(zhp, NULL, &errata); - - if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { - str = gettext("FAULTED"); - } else if (status == ZPOOL_STATUS_IO_FAILURE_WAIT || - status == ZPOOL_STATUS_IO_FAILURE_CONTINUE || - status == ZPOOL_STATUS_IO_FAILURE_MMP) { - str = gettext("SUSPENDED"); - } else { - nvlist_t *nvroot = fnvlist_lookup_nvlist( - zpool_get_config(zhp, NULL), ZPOOL_CONFIG_VDEV_TREE); - uint_t vsc; - vdev_stat_t *vs = (vdev_stat_t *)fnvlist_lookup_uint64_array( - nvroot, ZPOOL_CONFIG_VDEV_STATS, &vsc); - str = zpool_state_to_name(vs->vs_state, vs->vs_aux); - } - return (str); -} - -/* - * Get a zpool property value for 'prop' and return the value in - * a pre-allocated buffer. - */ -int -zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, - size_t len, zprop_source_t *srctype, boolean_t literal) -{ - uint64_t intval; - const char *strval; - zprop_source_t src = ZPROP_SRC_NONE; - - if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { - switch (prop) { - case ZPOOL_PROP_NAME: - (void) strlcpy(buf, zpool_get_name(zhp), len); - break; - - case ZPOOL_PROP_HEALTH: - (void) strlcpy(buf, zpool_get_state_str(zhp), len); - break; - - case ZPOOL_PROP_GUID: - intval = zpool_get_prop_int(zhp, prop, &src); - (void) snprintf(buf, len, "%llu", (u_longlong_t)intval); - break; - - case ZPOOL_PROP_ALTROOT: - case ZPOOL_PROP_CACHEFILE: - case ZPOOL_PROP_COMMENT: - case ZPOOL_PROP_COMPATIBILITY: - if (zhp->zpool_props != NULL || - zpool_get_all_props(zhp) == 0) { - (void) strlcpy(buf, - zpool_get_prop_string(zhp, prop, &src), - len); - break; - } - zfs_fallthrough; - default: - (void) strlcpy(buf, "-", len); - break; - } - - if (srctype != NULL) - *srctype = src; - return (0); - } - - /* - * ZPOOL_PROP_DEDUPCACHED can be fetched by name only using - * the ZPOOL_GET_PROPS_NAMES mechanism - */ - if (prop == ZPOOL_PROP_DEDUPCACHED) { - zpool_add_propname(zhp, ZPOOL_DEDUPCACHED_PROP_NAME); - (void) zpool_props_refresh(zhp); - } - - if (zhp->zpool_props == NULL && zpool_get_all_props(zhp) && - prop != ZPOOL_PROP_NAME) - return (-1); - - switch (zpool_prop_get_type(prop)) { - case PROP_TYPE_STRING: - (void) strlcpy(buf, zpool_get_prop_string(zhp, prop, &src), - len); - break; - - case PROP_TYPE_NUMBER: - intval = zpool_get_prop_int(zhp, prop, &src); - - switch (prop) { - case ZPOOL_PROP_DEDUP_TABLE_QUOTA: - /* - * If dedup quota is 0, we translate this into 'none' - * (unless literal is set). And if it is UINT64_MAX - * we translate that as 'automatic' (limit to size of - * the dedicated dedup VDEV. Otherwise, fall throught - * into the regular number formating. - */ - if (intval == 0) { - (void) strlcpy(buf, literal ? "0" : "none", - len); - break; - } else if (intval == UINT64_MAX) { - (void) strlcpy(buf, "auto", len); - break; - } - zfs_fallthrough; - - case ZPOOL_PROP_SIZE: - case ZPOOL_PROP_ALLOCATED: - case ZPOOL_PROP_FREE: - case ZPOOL_PROP_FREEING: - case ZPOOL_PROP_LEAKED: - case ZPOOL_PROP_ASHIFT: - case ZPOOL_PROP_MAXBLOCKSIZE: - case ZPOOL_PROP_MAXDNODESIZE: - case ZPOOL_PROP_BCLONESAVED: - case ZPOOL_PROP_BCLONEUSED: - case ZPOOL_PROP_DEDUP_TABLE_SIZE: - case ZPOOL_PROP_DEDUPCACHED: - if (literal) - (void) snprintf(buf, len, "%llu", - (u_longlong_t)intval); - else - (void) zfs_nicenum(intval, buf, len); - break; - - case ZPOOL_PROP_EXPANDSZ: - case ZPOOL_PROP_CHECKPOINT: - if (intval == 0) { - (void) strlcpy(buf, "-", len); - } else if (literal) { - (void) snprintf(buf, len, "%llu", - (u_longlong_t)intval); - } else { - (void) zfs_nicebytes(intval, buf, len); - } - break; - - case ZPOOL_PROP_CAPACITY: - if (literal) { - (void) snprintf(buf, len, "%llu", - (u_longlong_t)intval); - } else { - (void) snprintf(buf, len, "%llu%%", - (u_longlong_t)intval); - } - break; - - case ZPOOL_PROP_FRAGMENTATION: - if (intval == UINT64_MAX) { - (void) strlcpy(buf, "-", len); - } else if (literal) { - (void) snprintf(buf, len, "%llu", - (u_longlong_t)intval); - } else { - (void) snprintf(buf, len, "%llu%%", - (u_longlong_t)intval); - } - break; - - case ZPOOL_PROP_BCLONERATIO: - case ZPOOL_PROP_DEDUPRATIO: - if (literal) - (void) snprintf(buf, len, "%llu.%02llu", - (u_longlong_t)(intval / 100), - (u_longlong_t)(intval % 100)); - else - (void) snprintf(buf, len, "%llu.%02llux", - (u_longlong_t)(intval / 100), - (u_longlong_t)(intval % 100)); - break; - - case ZPOOL_PROP_HEALTH: - (void) strlcpy(buf, zpool_get_state_str(zhp), len); - break; - case ZPOOL_PROP_VERSION: - if (intval >= SPA_VERSION_FEATURES) { - (void) snprintf(buf, len, "-"); - break; - } - zfs_fallthrough; - default: - (void) snprintf(buf, len, "%llu", (u_longlong_t)intval); - } - break; - - case PROP_TYPE_INDEX: - intval = zpool_get_prop_int(zhp, prop, &src); - if (zpool_prop_index_to_string(prop, intval, &strval) - != 0) - return (-1); - (void) strlcpy(buf, strval, len); - break; - - default: - abort(); - } - - if (srctype) - *srctype = src; - - return (0); -} - -/* - * Get a zpool property value for 'propname' and return the value in - * a pre-allocated buffer. - */ -int -zpool_get_userprop(zpool_handle_t *zhp, const char *propname, char *buf, - size_t len, zprop_source_t *srctype) -{ - nvlist_t *nv; - uint64_t ival; - const char *value; - zprop_source_t source = ZPROP_SRC_LOCAL; - - if (zhp->zpool_props == NULL) - zpool_get_all_props(zhp); - - if (nvlist_lookup_nvlist(zhp->zpool_props, propname, &nv) == 0) { - if (nvlist_lookup_uint64(nv, ZPROP_SOURCE, &ival) == 0) - source = ival; - verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0); - } else { - source = ZPROP_SRC_DEFAULT; - value = "-"; - } - - if (srctype) - *srctype = source; - - (void) strlcpy(buf, value, len); - - return (0); -} - -/* - * Check if the bootfs name has the same pool name as it is set to. - * Assuming bootfs is a valid dataset name. - */ -static boolean_t -bootfs_name_valid(const char *pool, const char *bootfs) -{ - int len = strlen(pool); - if (bootfs[0] == '\0') - return (B_TRUE); - - if (!zfs_name_valid(bootfs, ZFS_TYPE_FILESYSTEM|ZFS_TYPE_SNAPSHOT)) - return (B_FALSE); - - if (strncmp(pool, bootfs, len) == 0 && - (bootfs[len] == '/' || bootfs[len] == '\0')) - return (B_TRUE); - - return (B_FALSE); -} - -/* - * Given an nvlist of zpool properties to be set, validate that they are - * correct, and parse any numeric properties (index, boolean, etc) if they are - * specified as strings. - */ -static nvlist_t * -zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname, - nvlist_t *props, uint64_t version, prop_flags_t flags, char *errbuf) -{ - nvpair_t *elem; - nvlist_t *retprops; - zpool_prop_t prop; - const char *strval; - uint64_t intval; - const char *check; - struct stat64 statbuf; - zpool_handle_t *zhp; - char *parent, *slash; - char report[1024]; - - if (nvlist_alloc(&retprops, NV_UNIQUE_NAME, 0) != 0) { - (void) no_memory(hdl); - return (NULL); - } - - elem = NULL; - while ((elem = nvlist_next_nvpair(props, elem)) != NULL) { - const char *propname = nvpair_name(elem); - - if (flags.vdevprop && zpool_prop_vdev(propname)) { - vdev_prop_t vprop = vdev_name_to_prop(propname); - - if (vdev_prop_readonly(vprop)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' " - "is readonly"), propname); - (void) zfs_error(hdl, EZFS_PROPREADONLY, - errbuf); - goto error; - } - - if (zprop_parse_value(hdl, elem, vprop, ZFS_TYPE_VDEV, - retprops, &strval, &intval, errbuf) != 0) - goto error; - - continue; - } else if (flags.vdevprop && vdev_prop_user(propname)) { - if (nvlist_add_nvpair(retprops, elem) != 0) { - (void) no_memory(hdl); - goto error; - } - continue; - } else if (flags.vdevprop) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "invalid property: '%s'"), propname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - - prop = zpool_name_to_prop(propname); - if (prop == ZPOOL_PROP_INVAL && zpool_prop_feature(propname)) { - int err; - char *fname = strchr(propname, '@') + 1; - - err = zfeature_lookup_name(fname, NULL); - if (err != 0) { - ASSERT3U(err, ==, ENOENT); - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "feature '%s' unsupported by kernel"), - fname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - - if (nvpair_type(elem) != DATA_TYPE_STRING) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "'%s' must be a string"), propname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - - (void) nvpair_value_string(elem, &strval); - if (strcmp(strval, ZFS_FEATURE_ENABLED) != 0 && - strcmp(strval, ZFS_FEATURE_DISABLED) != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property '%s' can only be set to " - "'enabled' or 'disabled'"), propname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - - if (!flags.create && - strcmp(strval, ZFS_FEATURE_DISABLED) == 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property '%s' can only be set to " - "'disabled' at creation time"), propname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - - if (nvlist_add_uint64(retprops, propname, 0) != 0) { - (void) no_memory(hdl); - goto error; - } - continue; - } else if (prop == ZPOOL_PROP_INVAL && - zfs_prop_user(propname)) { - /* - * This is a user property: make sure it's a - * string, and that it's less than ZAP_MAXNAMELEN. - */ - if (nvpair_type(elem) != DATA_TYPE_STRING) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "'%s' must be a string"), propname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - - if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property name '%s' is too long"), - propname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - - (void) nvpair_value_string(elem, &strval); - - if (strlen(strval) >= ZFS_MAXPROPLEN) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property value '%s' is too long"), - strval); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - - if (nvlist_add_string(retprops, propname, - strval) != 0) { - (void) no_memory(hdl); - goto error; - } - - continue; - } - - /* - * Make sure this property is valid and applies to this type. - */ - if (prop == ZPOOL_PROP_INVAL) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "invalid property '%s'"), propname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - - if (zpool_prop_readonly(prop)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' " - "is readonly"), propname); - (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf); - goto error; - } - - if (!flags.create && zpool_prop_setonce(prop)) { - 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; - } - - if (zprop_parse_value(hdl, elem, prop, ZFS_TYPE_POOL, retprops, - &strval, &intval, errbuf) != 0) - goto error; - - /* - * Perform additional checking for specific properties. - */ - switch (prop) { - case ZPOOL_PROP_VERSION: - if (intval < version || - !SPA_VERSION_IS_SUPPORTED(intval)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property '%s' number %llu is invalid."), - propname, (unsigned long long)intval); - (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); - goto error; - } - break; - - case ZPOOL_PROP_ASHIFT: - if (intval != 0 && - (intval < ASHIFT_MIN || intval > ASHIFT_MAX)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property '%s' number %llu is invalid, " - "only values between %" PRId32 " and %" - PRId32 " are allowed."), - propname, (unsigned long long)intval, - ASHIFT_MIN, ASHIFT_MAX); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - break; - - case ZPOOL_PROP_BOOTFS: - if (flags.create || flags.import) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property '%s' cannot be set at creation " - "or import time"), propname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - - if (version < SPA_VERSION_BOOTFS) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "pool must be upgraded to support " - "'%s' property"), propname); - (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); - goto error; - } - - /* - * bootfs property value has to be a dataset name and - * the dataset has to be in the same pool as it sets to. - */ - if (!bootfs_name_valid(poolname, strval)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' " - "is an invalid name"), strval); - (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf); - goto error; - } - - if ((zhp = zpool_open_canfail(hdl, poolname)) == NULL) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "could not open pool '%s'"), poolname); - (void) zfs_error(hdl, EZFS_OPENFAILED, errbuf); - goto error; - } - zpool_close(zhp); - break; - - case ZPOOL_PROP_ALTROOT: - if (!flags.create && !flags.import) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property '%s' can only be set during pool " - "creation or import"), propname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - - if (strval[0] != '/') { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "bad alternate root '%s'"), strval); - (void) zfs_error(hdl, EZFS_BADPATH, errbuf); - goto error; - } - break; - - case ZPOOL_PROP_CACHEFILE: - if (strval[0] == '\0') - break; - - if (strcmp(strval, "none") == 0) - break; - - if (strval[0] != '/') { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property '%s' must be empty, an " - "absolute path, or 'none'"), propname); - (void) zfs_error(hdl, EZFS_BADPATH, errbuf); - goto error; - } - - parent = strdup(strval); - if (parent == NULL) { - (void) zfs_error(hdl, EZFS_NOMEM, errbuf); - goto error; - } - slash = strrchr(parent, '/'); - - if (slash[1] == '\0' || strcmp(slash, "/.") == 0 || - strcmp(slash, "/..") == 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "'%s' is not a valid file"), parent); - (void) zfs_error(hdl, EZFS_BADPATH, errbuf); - free(parent); - goto error; - } - - *slash = '\0'; - - if (parent[0] != '\0' && - (stat64(parent, &statbuf) != 0 || - !S_ISDIR(statbuf.st_mode))) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "'%s' is not a valid directory"), - parent); - (void) zfs_error(hdl, EZFS_BADPATH, errbuf); - free(parent); - goto error; - } - free(parent); - - break; - - case ZPOOL_PROP_COMPATIBILITY: - switch (zpool_load_compat(strval, NULL, report, 1024)) { - case ZPOOL_COMPATIBILITY_OK: - case ZPOOL_COMPATIBILITY_WARNTOKEN: - break; - case ZPOOL_COMPATIBILITY_BADFILE: - case ZPOOL_COMPATIBILITY_BADTOKEN: - case ZPOOL_COMPATIBILITY_NOFILES: - zfs_error_aux(hdl, "%s", report); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - break; - - case ZPOOL_PROP_COMMENT: - for (check = strval; *check != '\0'; check++) { - if (!isprint(*check)) { - zfs_error_aux(hdl, - dgettext(TEXT_DOMAIN, - "comment may only have printable " - "characters")); - (void) zfs_error(hdl, EZFS_BADPROP, - errbuf); - goto error; - } - } - if (strlen(strval) > ZPROP_MAX_COMMENT) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "comment must not exceed %d characters"), - ZPROP_MAX_COMMENT); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - break; - case ZPOOL_PROP_READONLY: - if (!flags.import) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property '%s' can only be set at " - "import time"), propname); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - break; - case ZPOOL_PROP_MULTIHOST: - if (get_system_hostid() == 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "requires a non-zero system hostid")); - (void) zfs_error(hdl, EZFS_BADPROP, errbuf); - goto error; - } - break; - case ZPOOL_PROP_DEDUPDITTO: - printf("Note: property '%s' no longer has " - "any effect\n", propname); - break; - - default: - break; - } - } - - return (retprops); -error: - nvlist_free(retprops); - return (NULL); -} - -/* - * Set zpool property : propname=propval. - */ -int -zpool_set_prop(zpool_handle_t *zhp, const char *propname, const char *propval) -{ - zfs_cmd_t zc = {"\0"}; - int ret; - char errbuf[ERRBUFLEN]; - nvlist_t *nvl = NULL; - nvlist_t *realprops; - uint64_t version; - prop_flags_t flags = { 0 }; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot set property for '%s'"), - zhp->zpool_name); - - if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) - return (no_memory(zhp->zpool_hdl)); - - if (nvlist_add_string(nvl, propname, propval) != 0) { - nvlist_free(nvl); - return (no_memory(zhp->zpool_hdl)); - } - - version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); - if ((realprops = zpool_valid_proplist(zhp->zpool_hdl, - zhp->zpool_name, nvl, version, flags, errbuf)) == NULL) { - nvlist_free(nvl); - return (-1); - } - - nvlist_free(nvl); - nvl = realprops; - - /* - * Execute the corresponding ioctl() to set this property. - */ - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - - zcmd_write_src_nvlist(zhp->zpool_hdl, &zc, nvl); - - ret = zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_SET_PROPS, &zc); - - zcmd_free_nvlists(&zc); - nvlist_free(nvl); - - if (ret) - (void) zpool_standard_error(zhp->zpool_hdl, errno, errbuf); - else - (void) zpool_props_refresh(zhp); - - return (ret); -} - -int -zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp, - zfs_type_t type, boolean_t literal) -{ - libzfs_handle_t *hdl = zhp->zpool_hdl; - zprop_list_t *entry; - char buf[ZFS_MAXPROPLEN]; - nvlist_t *features = NULL; - nvpair_t *nvp; - zprop_list_t **last; - boolean_t firstexpand = (NULL == *plp); - int i; - - if (zprop_expand_list(hdl, plp, type) != 0) - return (-1); - - if (type == ZFS_TYPE_VDEV) - return (0); - - last = plp; - while (*last != NULL) - last = &(*last)->pl_next; - - if ((*plp)->pl_all) - features = zpool_get_features(zhp); - - if ((*plp)->pl_all && firstexpand) { - /* Handle userprops in the all properties case */ - if (zhp->zpool_props == NULL && zpool_props_refresh(zhp)) - return (-1); - - nvp = NULL; - while ((nvp = nvlist_next_nvpair(zhp->zpool_props, nvp)) != - NULL) { - const char *propname = nvpair_name(nvp); - - if (!zfs_prop_user(propname)) - continue; - - entry = zfs_alloc(hdl, sizeof (zprop_list_t)); - entry->pl_prop = ZPROP_USERPROP; - entry->pl_user_prop = zfs_strdup(hdl, propname); - entry->pl_width = strlen(entry->pl_user_prop); - entry->pl_all = B_TRUE; - - *last = entry; - last = &entry->pl_next; - } - - for (i = 0; i < SPA_FEATURES; i++) { - entry = zfs_alloc(hdl, sizeof (zprop_list_t)); - entry->pl_prop = ZPROP_USERPROP; - entry->pl_user_prop = zfs_asprintf(hdl, "feature@%s", - spa_feature_table[i].fi_uname); - entry->pl_width = strlen(entry->pl_user_prop); - entry->pl_all = B_TRUE; - - *last = entry; - last = &entry->pl_next; - } - } - - /* add any unsupported features */ - for (nvp = nvlist_next_nvpair(features, NULL); - nvp != NULL; nvp = nvlist_next_nvpair(features, nvp)) { - char *propname; - boolean_t found; - - if (zfeature_is_supported(nvpair_name(nvp))) - continue; - - propname = zfs_asprintf(hdl, "unsupported@%s", - nvpair_name(nvp)); - - /* - * Before adding the property to the list make sure that no - * other pool already added the same property. - */ - found = B_FALSE; - entry = *plp; - while (entry != NULL) { - if (entry->pl_user_prop != NULL && - strcmp(propname, entry->pl_user_prop) == 0) { - found = B_TRUE; - break; - } - entry = entry->pl_next; - } - if (found) { - free(propname); - continue; - } - - entry = zfs_alloc(hdl, sizeof (zprop_list_t)); - entry->pl_prop = ZPROP_USERPROP; - entry->pl_user_prop = propname; - entry->pl_width = strlen(entry->pl_user_prop); - entry->pl_all = B_TRUE; - - *last = entry; - last = &entry->pl_next; - } - - for (entry = *plp; entry != NULL; entry = entry->pl_next) { - if (entry->pl_fixed && !literal) - continue; - - if (entry->pl_prop != ZPROP_USERPROP && - zpool_get_prop(zhp, entry->pl_prop, buf, sizeof (buf), - NULL, literal) == 0) { - if (strlen(buf) > entry->pl_width) - entry->pl_width = strlen(buf); - } else if (entry->pl_prop == ZPROP_INVAL && - zfs_prop_user(entry->pl_user_prop) && - zpool_get_userprop(zhp, entry->pl_user_prop, buf, - sizeof (buf), NULL) == 0) { - if (strlen(buf) > entry->pl_width) - entry->pl_width = strlen(buf); - } - } - - return (0); -} - -int -vdev_expand_proplist(zpool_handle_t *zhp, const char *vdevname, - zprop_list_t **plp) -{ - zprop_list_t *entry; - char buf[ZFS_MAXPROPLEN]; - const char *strval = NULL; - int err = 0; - nvpair_t *elem = NULL; - nvlist_t *vprops = NULL; - nvlist_t *propval = NULL; - const char *propname; - vdev_prop_t prop; - zprop_list_t **last; - - for (entry = *plp; entry != NULL; entry = entry->pl_next) { - if (entry->pl_fixed) - continue; - - if (zpool_get_vdev_prop(zhp, vdevname, entry->pl_prop, - entry->pl_user_prop, buf, sizeof (buf), NULL, - B_FALSE) == 0) { - if (strlen(buf) > entry->pl_width) - entry->pl_width = strlen(buf); - } - if (entry->pl_prop == VDEV_PROP_NAME && - strlen(vdevname) > entry->pl_width) - entry->pl_width = strlen(vdevname); - } - - /* Handle the all properties case */ - last = plp; - if (*last != NULL && (*last)->pl_all == B_TRUE) { - while (*last != NULL) - last = &(*last)->pl_next; - - err = zpool_get_all_vdev_props(zhp, vdevname, &vprops); - if (err != 0) - return (err); - - while ((elem = nvlist_next_nvpair(vprops, elem)) != NULL) { - propname = nvpair_name(elem); - - /* Skip properties that are not user defined */ - if ((prop = vdev_name_to_prop(propname)) != - VDEV_PROP_USERPROP) - continue; - - if (nvpair_value_nvlist(elem, &propval) != 0) - continue; - - strval = fnvlist_lookup_string(propval, ZPROP_VALUE); - - entry = zfs_alloc(zhp->zpool_hdl, - sizeof (zprop_list_t)); - entry->pl_prop = prop; - entry->pl_user_prop = zfs_strdup(zhp->zpool_hdl, - propname); - entry->pl_width = strlen(strval); - entry->pl_all = B_TRUE; - *last = entry; - last = &entry->pl_next; - } - } - - return (0); -} - -/* - * Get the state for the given feature on the given ZFS pool. - */ -int -zpool_prop_get_feature(zpool_handle_t *zhp, const char *propname, char *buf, - size_t len) -{ - uint64_t refcount; - boolean_t found = B_FALSE; - nvlist_t *features = zpool_get_features(zhp); - boolean_t supported; - const char *feature = strchr(propname, '@') + 1; - - supported = zpool_prop_feature(propname); - ASSERT(supported || zpool_prop_unsupported(propname)); - - /* - * Convert from feature name to feature guid. This conversion is - * unnecessary for unsupported@... properties because they already - * use guids. - */ - if (supported) { - int ret; - spa_feature_t fid; - - ret = zfeature_lookup_name(feature, &fid); - if (ret != 0) { - (void) strlcpy(buf, "-", len); - return (ENOTSUP); - } - feature = spa_feature_table[fid].fi_guid; - } - - if (nvlist_lookup_uint64(features, feature, &refcount) == 0) - found = B_TRUE; - - if (supported) { - if (!found) { - (void) strlcpy(buf, ZFS_FEATURE_DISABLED, len); - } else { - if (refcount == 0) - (void) strlcpy(buf, ZFS_FEATURE_ENABLED, len); - else - (void) strlcpy(buf, ZFS_FEATURE_ACTIVE, len); - } - } else { - if (found) { - if (refcount == 0) { - (void) strcpy(buf, ZFS_UNSUPPORTED_INACTIVE); - } else { - (void) strcpy(buf, ZFS_UNSUPPORTED_READONLY); - } - } else { - (void) strlcpy(buf, "-", len); - return (ENOTSUP); - } - } - - return (0); -} - -/* - * Validate the given pool name, optionally putting an extended error message in - * 'buf'. - */ -boolean_t -zpool_name_valid(libzfs_handle_t *hdl, boolean_t isopen, const char *pool) -{ - namecheck_err_t why; - char what; - int ret; - - ret = pool_namecheck(pool, &why, &what); - - /* - * The rules for reserved pool names were extended at a later point. - * But we need to support users with existing pools that may now be - * invalid. So we only check for this expanded set of names during a - * create (or import), and only in userland. - */ - if (ret == 0 && !isopen && - (strncmp(pool, "mirror", 6) == 0 || - strncmp(pool, "raidz", 5) == 0 || - strncmp(pool, "draid", 5) == 0 || - strncmp(pool, "spare", 5) == 0 || - strcmp(pool, "log") == 0)) { - if (hdl != NULL) - zfs_error_aux(hdl, - dgettext(TEXT_DOMAIN, "name is reserved")); - return (B_FALSE); - } - - - if (ret != 0) { - if (hdl != NULL) { - switch (why) { - case NAME_ERR_TOOLONG: - zfs_error_aux(hdl, - dgettext(TEXT_DOMAIN, "name is too long")); - break; - - case NAME_ERR_INVALCHAR: - zfs_error_aux(hdl, - dgettext(TEXT_DOMAIN, "invalid character " - "'%c' in pool name"), what); - break; - - case NAME_ERR_NOLETTER: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "name must begin with a letter")); - break; - - case NAME_ERR_RESERVED: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "name is reserved")); - break; - - case NAME_ERR_DISKLIKE: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "pool name is reserved")); - break; - - case NAME_ERR_LEADING_SLASH: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "leading slash in name")); - break; - - case NAME_ERR_EMPTY_COMPONENT: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "empty component in name")); - break; - - case NAME_ERR_TRAILING_SLASH: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "trailing slash in name")); - break; - - case NAME_ERR_MULTIPLE_DELIMITERS: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "multiple '@' and/or '#' delimiters in " - "name")); - break; - - case NAME_ERR_NO_AT: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "permission set is missing '@'")); - break; - - default: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "(%d) not defined"), why); - break; - } - } - return (B_FALSE); - } - - return (B_TRUE); -} - -/* - * Open a handle to the given pool, even if the pool is currently in the FAULTED - * state. - */ -zpool_handle_t * -zpool_open_canfail(libzfs_handle_t *hdl, const char *pool) -{ - zpool_handle_t *zhp; - boolean_t missing; - - /* - * Make sure the pool name is valid. - */ - if (!zpool_name_valid(hdl, B_TRUE, pool)) { - (void) zfs_error_fmt(hdl, EZFS_INVALIDNAME, - dgettext(TEXT_DOMAIN, "cannot open '%s'"), - pool); - return (NULL); - } - - zhp = zfs_alloc(hdl, sizeof (zpool_handle_t)); - - zhp->zpool_hdl = hdl; - (void) strlcpy(zhp->zpool_name, pool, sizeof (zhp->zpool_name)); - - if (zpool_refresh_stats(zhp, &missing) != 0) { - zpool_close(zhp); - return (NULL); - } - - if (missing) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "no such pool")); - (void) zfs_error_fmt(hdl, EZFS_NOENT, - dgettext(TEXT_DOMAIN, "cannot open '%s'"), pool); - zpool_close(zhp); - return (NULL); - } - - return (zhp); -} - -/* - * Like the above, but silent on error. Used when iterating over pools (because - * the configuration cache may be out of date). - */ -int -zpool_open_silent(libzfs_handle_t *hdl, const char *pool, zpool_handle_t **ret) -{ - zpool_handle_t *zhp; - boolean_t missing; - - zhp = zfs_alloc(hdl, sizeof (zpool_handle_t)); - - zhp->zpool_hdl = hdl; - (void) strlcpy(zhp->zpool_name, pool, sizeof (zhp->zpool_name)); - - if (zpool_refresh_stats(zhp, &missing) != 0) { - zpool_close(zhp); - return (-1); - } - - if (missing) { - zpool_close(zhp); - *ret = NULL; - return (0); - } - - *ret = zhp; - return (0); -} - -/* - * Similar to zpool_open_canfail(), but refuses to open pools in the faulted - * state. - */ -zpool_handle_t * -zpool_open(libzfs_handle_t *hdl, const char *pool) -{ - zpool_handle_t *zhp; - - if ((zhp = zpool_open_canfail(hdl, pool)) == NULL) - return (NULL); - - if (zhp->zpool_state == POOL_STATE_UNAVAIL) { - (void) zfs_error_fmt(hdl, EZFS_POOLUNAVAIL, - dgettext(TEXT_DOMAIN, "cannot open '%s'"), zhp->zpool_name); - zpool_close(zhp); - return (NULL); - } - - return (zhp); -} - -/* - * Close the handle. Simply frees the memory associated with the handle. - */ -void -zpool_close(zpool_handle_t *zhp) -{ - nvlist_free(zhp->zpool_config); - nvlist_free(zhp->zpool_old_config); - nvlist_free(zhp->zpool_props); - free(zhp); -} - -/* - * Return the name of the pool. - */ -const char * -zpool_get_name(zpool_handle_t *zhp) -{ - return (zhp->zpool_name); -} - - -/* - * Return the state of the pool (ACTIVE or UNAVAILABLE) - */ -int -zpool_get_state(zpool_handle_t *zhp) -{ - return (zhp->zpool_state); -} - -/* - * Check if vdev list contains a dRAID vdev - */ -static boolean_t -zpool_has_draid_vdev(nvlist_t *nvroot) -{ - nvlist_t **child; - uint_t children; - - if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, - &child, &children) == 0) { - for (uint_t c = 0; c < children; c++) { - const char *type; - - if (nvlist_lookup_string(child[c], - ZPOOL_CONFIG_TYPE, &type) == 0 && - strcmp(type, VDEV_TYPE_DRAID) == 0) { - return (B_TRUE); - } - } - } - return (B_FALSE); -} - -/* - * Output a dRAID top-level vdev name in to the provided buffer. - */ -static char * -zpool_draid_name(char *name, int len, uint64_t data, uint64_t parity, - uint64_t spares, uint64_t children) -{ - snprintf(name, len, "%s%llu:%llud:%lluc:%llus", - VDEV_TYPE_DRAID, (u_longlong_t)parity, (u_longlong_t)data, - (u_longlong_t)children, (u_longlong_t)spares); - - return (name); -} - -/* - * Return B_TRUE if the provided name is a dRAID spare name. - */ -boolean_t -zpool_is_draid_spare(const char *name) -{ - uint64_t spare_id, parity, vdev_id; - - if (sscanf(name, VDEV_TYPE_DRAID "%llu-%llu-%llu", - (u_longlong_t *)&parity, (u_longlong_t *)&vdev_id, - (u_longlong_t *)&spare_id) == 3) { - return (B_TRUE); - } - - return (B_FALSE); -} - -/* - * Create the named pool, using the provided vdev list. It is assumed - * that the consumer has already validated the contents of the nvlist, so we - * don't have to worry about error semantics. - */ -int -zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot, - nvlist_t *props, nvlist_t *fsprops) -{ - zfs_cmd_t zc = {"\0"}; - nvlist_t *zc_fsprops = NULL; - nvlist_t *zc_props = NULL; - nvlist_t *hidden_args = NULL; - uint8_t *wkeydata = NULL; - uint_t wkeylen = 0; - char errbuf[ERRBUFLEN]; - int ret = -1; - - (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot create '%s'"), pool); - - if (!zpool_name_valid(hdl, B_FALSE, pool)) - return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); - - zcmd_write_conf_nvlist(hdl, &zc, nvroot); - - if (props) { - prop_flags_t flags = { .create = B_TRUE, .import = B_FALSE }; - - if ((zc_props = zpool_valid_proplist(hdl, pool, props, - SPA_VERSION_1, flags, errbuf)) == NULL) { - goto create_failed; - } - } - - if (fsprops) { - uint64_t zoned; - const char *zonestr; - - zoned = ((nvlist_lookup_string(fsprops, - zfs_prop_to_name(ZFS_PROP_ZONED), &zonestr) == 0) && - strcmp(zonestr, "on") == 0); - - if ((zc_fsprops = zfs_valid_proplist(hdl, ZFS_TYPE_FILESYSTEM, - fsprops, zoned, NULL, NULL, B_TRUE, errbuf)) == NULL) { - goto create_failed; - } - - if (!zc_props && - (nvlist_alloc(&zc_props, NV_UNIQUE_NAME, 0) != 0)) { - goto create_failed; - } - if (zfs_crypto_create(hdl, NULL, zc_fsprops, props, B_TRUE, - &wkeydata, &wkeylen) != 0) { - zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf); - goto create_failed; - } - if (nvlist_add_nvlist(zc_props, - ZPOOL_ROOTFS_PROPS, zc_fsprops) != 0) { - goto create_failed; - } - if (wkeydata != NULL) { - if (nvlist_alloc(&hidden_args, NV_UNIQUE_NAME, 0) != 0) - goto create_failed; - - if (nvlist_add_uint8_array(hidden_args, "wkeydata", - wkeydata, wkeylen) != 0) - goto create_failed; - - if (nvlist_add_nvlist(zc_props, ZPOOL_HIDDEN_ARGS, - hidden_args) != 0) - goto create_failed; - } - } - - if (zc_props) - zcmd_write_src_nvlist(hdl, &zc, zc_props); - - (void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name)); - - if ((ret = zfs_ioctl(hdl, ZFS_IOC_POOL_CREATE, &zc)) != 0) { - - zcmd_free_nvlists(&zc); - nvlist_free(zc_props); - nvlist_free(zc_fsprops); - nvlist_free(hidden_args); - if (wkeydata != NULL) - free(wkeydata); - - switch (errno) { - case EBUSY: - /* - * This can happen if the user has specified the same - * device multiple times. We can't reliably detect this - * until we try to add it and see we already have a - * label. This can also happen under if the device is - * part of an active md or lvm device. - */ - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "one or more vdevs refer to the same device, or " - "one of\nthe devices is part of an active md or " - "lvm device")); - return (zfs_error(hdl, EZFS_BADDEV, errbuf)); - - case ERANGE: - /* - * This happens if the record size is smaller or larger - * than the allowed size range, or not a power of 2. - * - * NOTE: although zfs_valid_proplist is called earlier, - * this case may have slipped through since the - * pool does not exist yet and it is therefore - * impossible to read properties e.g. max blocksize - * from the pool. - */ - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "record size invalid")); - return (zfs_error(hdl, EZFS_BADPROP, errbuf)); - - case EOVERFLOW: - /* - * This occurs when one of the devices is below - * SPA_MINDEVSIZE. Unfortunately, we can't detect which - * device was the problem device since there's no - * reliable way to determine device size from userland. - */ - { - char buf[64]; - - zfs_nicebytes(SPA_MINDEVSIZE, buf, - sizeof (buf)); - - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "one or more devices is less than the " - "minimum size (%s)"), buf); - } - return (zfs_error(hdl, EZFS_BADDEV, errbuf)); - - case ENOSPC: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "one or more devices is out of space")); - return (zfs_error(hdl, EZFS_BADDEV, errbuf)); - - case EINVAL: - if (zpool_has_draid_vdev(nvroot) && - zfeature_lookup_name("draid", NULL) != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "dRAID vdevs are unsupported by the " - "kernel")); - return (zfs_error(hdl, EZFS_BADDEV, errbuf)); - } else { - return (zpool_standard_error(hdl, errno, - errbuf)); - } - - default: - return (zpool_standard_error(hdl, errno, errbuf)); - } - } - -create_failed: - zcmd_free_nvlists(&zc); - nvlist_free(zc_props); - nvlist_free(zc_fsprops); - nvlist_free(hidden_args); - if (wkeydata != NULL) - free(wkeydata); - return (ret); -} - -/* - * Destroy the given pool. It is up to the caller to ensure that there are no - * datasets left in the pool. - */ -int -zpool_destroy(zpool_handle_t *zhp, const char *log_str) -{ - zfs_cmd_t zc = {"\0"}; - zfs_handle_t *zfp = NULL; - libzfs_handle_t *hdl = zhp->zpool_hdl; - char errbuf[ERRBUFLEN]; - - if (zhp->zpool_state == POOL_STATE_ACTIVE && - (zfp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_FILESYSTEM)) == NULL) - return (-1); - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - zc.zc_history = (uint64_t)(uintptr_t)log_str; - - if (zfs_ioctl(hdl, ZFS_IOC_POOL_DESTROY, &zc) != 0) { - (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot destroy '%s'"), zhp->zpool_name); - - if (errno == EROFS) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "one or more devices is read only")); - (void) zfs_error(hdl, EZFS_BADDEV, errbuf); - } else { - (void) zpool_standard_error(hdl, errno, errbuf); - } - - if (zfp) - zfs_close(zfp); - return (-1); - } - - if (zfp) { - remove_mountpoint(zfp); - zfs_close(zfp); - } - - return (0); -} - -/* - * Create a checkpoint in the given pool. - */ -int -zpool_checkpoint(zpool_handle_t *zhp) -{ - libzfs_handle_t *hdl = zhp->zpool_hdl; - char errbuf[ERRBUFLEN]; - int error; - - error = lzc_pool_checkpoint(zhp->zpool_name); - if (error != 0) { - (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot checkpoint '%s'"), zhp->zpool_name); - (void) zpool_standard_error(hdl, error, errbuf); - return (-1); - } - - return (0); -} - -/* - * Discard the checkpoint from the given pool. - */ -int -zpool_discard_checkpoint(zpool_handle_t *zhp) -{ - libzfs_handle_t *hdl = zhp->zpool_hdl; - char errbuf[ERRBUFLEN]; - int error; - - error = lzc_pool_checkpoint_discard(zhp->zpool_name); - if (error != 0) { - (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot discard checkpoint in '%s'"), zhp->zpool_name); - (void) zpool_standard_error(hdl, error, errbuf); - return (-1); - } - - return (0); -} - -/* - * Load data type for the given pool. - */ -int -zpool_prefetch(zpool_handle_t *zhp, zpool_prefetch_type_t type) -{ - libzfs_handle_t *hdl = zhp->zpool_hdl; - char msg[1024]; - int error; - - error = lzc_pool_prefetch(zhp->zpool_name, type); - if (error != 0) { - (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN, - "cannot prefetch %s in '%s'"), - type == ZPOOL_PREFETCH_DDT ? "ddt" : "", zhp->zpool_name); - (void) zpool_standard_error(hdl, error, msg); - return (-1); - } - - return (0); -} - -/* - * Add the given vdevs to the pool. The caller must have already performed the - * necessary verification to ensure that the vdev specification is well-formed. - */ -int -zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot, boolean_t check_ashift) -{ - zfs_cmd_t zc = {"\0"}; - int ret; - libzfs_handle_t *hdl = zhp->zpool_hdl; - char errbuf[ERRBUFLEN]; - nvlist_t **spares, **l2cache; - uint_t nspares, nl2cache; - - (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot add to '%s'"), zhp->zpool_name); - - if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) < - SPA_VERSION_SPARES && - nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, - &spares, &nspares) == 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be " - "upgraded to add hot spares")); - return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); - } - - if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) < - SPA_VERSION_L2CACHE && - nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, - &l2cache, &nl2cache) == 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be " - "upgraded to add cache devices")); - return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); - } - - zcmd_write_conf_nvlist(hdl, &zc, nvroot); - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - zc.zc_flags = check_ashift; - - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_ADD, &zc) != 0) { - switch (errno) { - case EBUSY: - /* - * This can happen if the user has specified the same - * device multiple times. We can't reliably detect this - * until we try to add it and see we already have a - * label. - */ - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "one or more vdevs refer to the same device")); - (void) zfs_error(hdl, EZFS_BADDEV, errbuf); - break; - - case EINVAL: - - if (zpool_has_draid_vdev(nvroot) && - zfeature_lookup_name("draid", NULL) != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "dRAID vdevs are unsupported by the " - "kernel")); - } else { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "invalid config; a pool with removing/" - "removed vdevs does not support adding " - "raidz or dRAID vdevs")); - } - - (void) zfs_error(hdl, EZFS_BADDEV, errbuf); - break; - - case EOVERFLOW: - /* - * This occurs when one of the devices is below - * SPA_MINDEVSIZE. Unfortunately, we can't detect which - * device was the problem device since there's no - * reliable way to determine device size from userland. - */ - { - char buf[64]; - - zfs_nicebytes(SPA_MINDEVSIZE, buf, - sizeof (buf)); - - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "device is less than the minimum " - "size (%s)"), buf); - } - (void) zfs_error(hdl, EZFS_BADDEV, errbuf); - break; - - case ENOTSUP: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "pool must be upgraded to add these vdevs")); - (void) zfs_error(hdl, EZFS_BADVERSION, errbuf); - break; - - default: - (void) zpool_standard_error(hdl, errno, errbuf); - } - - ret = -1; - } else { - ret = 0; - } - - zcmd_free_nvlists(&zc); - - return (ret); -} - -/* - * Exports the pool from the system. The caller must ensure that there are no - * mounted datasets in the pool. - */ -static int -zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce, - const char *log_str) -{ - zfs_cmd_t zc = {"\0"}; - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - zc.zc_cookie = force; - zc.zc_guid = hardforce; - zc.zc_history = (uint64_t)(uintptr_t)log_str; - - if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_EXPORT, &zc) != 0) { - switch (errno) { - case EXDEV: - zfs_error_aux(zhp->zpool_hdl, dgettext(TEXT_DOMAIN, - "use '-f' to override the following errors:\n" - "'%s' has an active shared spare which could be" - " used by other pools once '%s' is exported."), - zhp->zpool_name, zhp->zpool_name); - return (zfs_error_fmt(zhp->zpool_hdl, EZFS_ACTIVE_SPARE, - dgettext(TEXT_DOMAIN, "cannot export '%s'"), - zhp->zpool_name)); - default: - return (zpool_standard_error_fmt(zhp->zpool_hdl, errno, - dgettext(TEXT_DOMAIN, "cannot export '%s'"), - zhp->zpool_name)); - } - } - - return (0); -} - -int -zpool_export(zpool_handle_t *zhp, boolean_t force, const char *log_str) -{ - return (zpool_export_common(zhp, force, B_FALSE, log_str)); -} - -int -zpool_export_force(zpool_handle_t *zhp, const char *log_str) -{ - return (zpool_export_common(zhp, B_TRUE, B_TRUE, log_str)); -} - -static void -zpool_rewind_exclaim(libzfs_handle_t *hdl, const char *name, boolean_t dryrun, - nvlist_t *config) -{ - nvlist_t *nv = NULL; - uint64_t rewindto; - int64_t loss = -1; - struct tm t; - char timestr[128]; - - if (!hdl->libzfs_printerr || config == NULL) - return; - - if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 || - nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0) { - return; - } - - if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0) - return; - (void) nvlist_lookup_int64(nv, ZPOOL_CONFIG_REWIND_TIME, &loss); - - if (localtime_r((time_t *)&rewindto, &t) != NULL && - ctime_r((time_t *)&rewindto, timestr) != NULL) { - timestr[24] = 0; - if (dryrun) { - (void) printf(dgettext(TEXT_DOMAIN, - "Would be able to return %s " - "to its state as of %s.\n"), - name, timestr); - } else { - (void) printf(dgettext(TEXT_DOMAIN, - "Pool %s returned to its state as of %s.\n"), - name, timestr); - } - if (loss > 120) { - (void) printf(dgettext(TEXT_DOMAIN, - "%s approximately %lld "), - dryrun ? "Would discard" : "Discarded", - ((longlong_t)loss + 30) / 60); - (void) printf(dgettext(TEXT_DOMAIN, - "minutes of transactions.\n")); - } else if (loss > 0) { - (void) printf(dgettext(TEXT_DOMAIN, - "%s approximately %lld "), - dryrun ? "Would discard" : "Discarded", - (longlong_t)loss); - (void) printf(dgettext(TEXT_DOMAIN, - "seconds of transactions.\n")); - } - } -} - -void -zpool_explain_recover(libzfs_handle_t *hdl, const char *name, int reason, - nvlist_t *config, char *buf, size_t size) -{ - nvlist_t *nv = NULL; - int64_t loss = -1; - uint64_t edata = UINT64_MAX; - uint64_t rewindto; - struct tm t; - char timestr[128], temp[1024]; - - if (!hdl->libzfs_printerr) - return; - - /* All attempted rewinds failed if ZPOOL_CONFIG_LOAD_TIME missing */ - if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 || - nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0 || - nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0) - goto no_info; - - (void) nvlist_lookup_int64(nv, ZPOOL_CONFIG_REWIND_TIME, &loss); - (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_DATA_ERRORS, - &edata); - - (void) snprintf(buf, size, dgettext(TEXT_DOMAIN, - "Recovery is possible, but will result in some data loss.\n")); - - if (localtime_r((time_t *)&rewindto, &t) != NULL && - ctime_r((time_t *)&rewindto, timestr) != NULL) { - timestr[24] = 0; - (void) snprintf(temp, 1024, dgettext(TEXT_DOMAIN, - "\tReturning the pool to its state as of %s\n" - "\tshould correct the problem. "), timestr); - (void) strlcat(buf, temp, size); - } else { - (void) strlcat(buf, dgettext(TEXT_DOMAIN, - "\tReverting the pool to an earlier state " - "should correct the problem.\n\t"), size); - } - - if (loss > 120) { - (void) snprintf(temp, 1024, dgettext(TEXT_DOMAIN, - "Approximately %lld minutes of data\n" - "\tmust be discarded, irreversibly. "), - ((longlong_t)loss + 30) / 60); - (void) strlcat(buf, temp, size); - } else if (loss > 0) { - (void) snprintf(temp, 1024, dgettext(TEXT_DOMAIN, - "Approximately %lld seconds of data\n" - "\tmust be discarded, irreversibly. "), - (longlong_t)loss); - (void) strlcat(buf, temp, size); - } - if (edata != 0 && edata != UINT64_MAX) { - if (edata == 1) { - (void) strlcat(buf, dgettext(TEXT_DOMAIN, - "After rewind, at least\n" - "\tone persistent user-data error will remain. "), - size); - } else { - (void) strlcat(buf, dgettext(TEXT_DOMAIN, - "After rewind, several\n" - "\tpersistent user-data errors will remain. "), - size); - } - } - (void) snprintf(temp, 1024, dgettext(TEXT_DOMAIN, - "Recovery can be attempted\n\tby executing 'zpool %s -F %s'. "), - reason >= 0 ? "clear" : "import", name); - (void) strlcat(buf, temp, size); - - (void) strlcat(buf, dgettext(TEXT_DOMAIN, - "A scrub of the pool\n" - "\tis strongly recommended after recovery.\n"), size); - return; - -no_info: - (void) strlcat(buf, dgettext(TEXT_DOMAIN, - "Destroy and re-create the pool from\n\ta backup source.\n"), size); -} - -/* - * zpool_import() is a contracted interface. Should be kept the same - * if possible. - * - * Applications should use zpool_import_props() to import a pool with - * new properties value to be set. - */ -int -zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, - char *altroot) -{ - nvlist_t *props = NULL; - int ret; - - if (altroot != NULL) { - if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { - return (zfs_error_fmt(hdl, EZFS_NOMEM, - dgettext(TEXT_DOMAIN, "cannot import '%s'"), - newname)); - } - - if (nvlist_add_string(props, - zpool_prop_to_name(ZPOOL_PROP_ALTROOT), altroot) != 0 || - nvlist_add_string(props, - zpool_prop_to_name(ZPOOL_PROP_CACHEFILE), "none") != 0) { - nvlist_free(props); - return (zfs_error_fmt(hdl, EZFS_NOMEM, - dgettext(TEXT_DOMAIN, "cannot import '%s'"), - newname)); - } - } - - ret = zpool_import_props(hdl, config, newname, props, - ZFS_IMPORT_NORMAL); - nvlist_free(props); - return (ret); -} - -static void -print_vdev_tree(libzfs_handle_t *hdl, const char *name, nvlist_t *nv, - int indent) -{ - nvlist_t **child; - uint_t c, children; - char *vname; - uint64_t is_log = 0; - - (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG, - &is_log); - - if (name != NULL) - (void) printf("\t%*s%s%s\n", indent, "", name, - is_log ? " [log]" : ""); - - if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, - &child, &children) != 0) - return; - - for (c = 0; c < children; c++) { - vname = zpool_vdev_name(hdl, NULL, child[c], VDEV_NAME_TYPE_ID); - print_vdev_tree(hdl, vname, child[c], indent + 2); - free(vname); - } -} - -void -zpool_collect_unsup_feat(nvlist_t *config, char *buf, size_t size) -{ - nvlist_t *nvinfo, *unsup_feat; - char temp[512]; - - nvinfo = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO); - unsup_feat = fnvlist_lookup_nvlist(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT); - - for (nvpair_t *nvp = nvlist_next_nvpair(unsup_feat, NULL); - nvp != NULL; nvp = nvlist_next_nvpair(unsup_feat, nvp)) { - const char *desc = fnvpair_value_string(nvp); - if (strlen(desc) > 0) { - (void) snprintf(temp, 512, "\t%s (%s)\n", - nvpair_name(nvp), desc); - (void) strlcat(buf, temp, size); - } else { - (void) snprintf(temp, 512, "\t%s\n", nvpair_name(nvp)); - (void) strlcat(buf, temp, size); - } - } -} - -/* - * Import the given pool using the known configuration and a list of - * properties to be set. The configuration should have come from - * zpool_find_import(). The 'newname' parameters control whether the pool - * is imported with a different name. - */ -int -zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname, - nvlist_t *props, int flags) -{ - zfs_cmd_t zc = {"\0"}; - zpool_load_policy_t policy; - nvlist_t *nv = NULL; - nvlist_t *nvinfo = NULL; - nvlist_t *missing = NULL; - const char *thename; - const char *origname; - int ret; - int error = 0; - char buf[2048]; - char errbuf[ERRBUFLEN]; - - origname = fnvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME); - - (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot import pool '%s'"), origname); - - if (newname != NULL) { - if (!zpool_name_valid(hdl, B_FALSE, newname)) - return (zfs_error_fmt(hdl, EZFS_INVALIDNAME, - dgettext(TEXT_DOMAIN, "cannot import '%s'"), - newname)); - thename = newname; - } else { - thename = origname; - } - - if (props != NULL) { - uint64_t version; - prop_flags_t flags = { .create = B_FALSE, .import = B_TRUE }; - - version = fnvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION); - - if ((props = zpool_valid_proplist(hdl, origname, - props, version, flags, errbuf)) == NULL) - return (-1); - zcmd_write_src_nvlist(hdl, &zc, props); - nvlist_free(props); - } - - (void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name)); - - zc.zc_guid = fnvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID); - - zcmd_write_conf_nvlist(hdl, &zc, config); - zcmd_alloc_dst_nvlist(hdl, &zc, zc.zc_nvlist_conf_size * 2); - - zc.zc_cookie = flags; - while ((ret = zfs_ioctl(hdl, ZFS_IOC_POOL_IMPORT, &zc)) != 0 && - errno == ENOMEM) - zcmd_expand_dst_nvlist(hdl, &zc); - if (ret != 0) - error = errno; - - (void) zcmd_read_dst_nvlist(hdl, &zc, &nv); - - zcmd_free_nvlists(&zc); - - zpool_get_load_policy(config, &policy); - - if (error) { - char desc[1024]; - char aux[256]; - - /* - * Dry-run failed, but we print out what success - * looks like if we found a best txg - */ - if (policy.zlp_rewind & ZPOOL_TRY_REWIND) { - zpool_rewind_exclaim(hdl, newname ? origname : thename, - B_TRUE, nv); - nvlist_free(nv); - return (-1); - } - - if (newname == NULL) - (void) snprintf(desc, sizeof (desc), - dgettext(TEXT_DOMAIN, "cannot import '%s'"), - thename); - else - (void) snprintf(desc, sizeof (desc), - dgettext(TEXT_DOMAIN, "cannot import '%s' as '%s'"), - origname, thename); - - switch (error) { - case ENOTSUP: - if (nv != NULL && nvlist_lookup_nvlist(nv, - ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 && - nvlist_exists(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT)) { - (void) printf(dgettext(TEXT_DOMAIN, "This " - "pool uses the following feature(s) not " - "supported by this system:\n")); - memset(buf, 0, 2048); - zpool_collect_unsup_feat(nv, buf, 2048); - (void) printf("%s", buf); - if (nvlist_exists(nvinfo, - ZPOOL_CONFIG_CAN_RDONLY)) { - (void) printf(dgettext(TEXT_DOMAIN, - "All unsupported features are only " - "required for writing to the pool." - "\nThe pool can be imported using " - "'-o readonly=on'.\n")); - } - } - /* - * Unsupported version. - */ - (void) zfs_error(hdl, EZFS_BADVERSION, desc); - break; - - case EREMOTEIO: - if (nv != NULL && nvlist_lookup_nvlist(nv, - ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0) { - const char *hostname = "<unknown>"; - uint64_t hostid = 0; - mmp_state_t mmp_state; - - mmp_state = fnvlist_lookup_uint64(nvinfo, - ZPOOL_CONFIG_MMP_STATE); - - if (nvlist_exists(nvinfo, - ZPOOL_CONFIG_MMP_HOSTNAME)) - hostname = fnvlist_lookup_string(nvinfo, - ZPOOL_CONFIG_MMP_HOSTNAME); - - if (nvlist_exists(nvinfo, - ZPOOL_CONFIG_MMP_HOSTID)) - hostid = fnvlist_lookup_uint64(nvinfo, - ZPOOL_CONFIG_MMP_HOSTID); - - if (mmp_state == MMP_STATE_ACTIVE) { - (void) snprintf(aux, sizeof (aux), - dgettext(TEXT_DOMAIN, "pool is imp" - "orted on host '%s' (hostid=%lx).\n" - "Export the pool on the other " - "system, then run 'zpool import'."), - hostname, (unsigned long) hostid); - } else if (mmp_state == MMP_STATE_NO_HOSTID) { - (void) snprintf(aux, sizeof (aux), - dgettext(TEXT_DOMAIN, "pool has " - "the multihost property on and " - "the\nsystem's hostid is not set. " - "Set a unique system hostid with " - "the zgenhostid(8) command.\n")); - } - - (void) zfs_error_aux(hdl, "%s", aux); - } - (void) zfs_error(hdl, EZFS_ACTIVE_POOL, desc); - break; - - case EINVAL: - (void) zfs_error(hdl, EZFS_INVALCONFIG, desc); - break; - - case EROFS: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "one or more devices is read only")); - (void) zfs_error(hdl, EZFS_BADDEV, desc); - break; - - case ENXIO: - if (nv && nvlist_lookup_nvlist(nv, - ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 && - nvlist_lookup_nvlist(nvinfo, - ZPOOL_CONFIG_MISSING_DEVICES, &missing) == 0) { - (void) printf(dgettext(TEXT_DOMAIN, - "The devices below are missing or " - "corrupted, use '-m' to import the pool " - "anyway:\n")); - print_vdev_tree(hdl, NULL, missing, 2); - (void) printf("\n"); - } - (void) zpool_standard_error(hdl, error, desc); - break; - - case EEXIST: - (void) zpool_standard_error(hdl, error, desc); - break; - - case EBUSY: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "one or more devices are already in use\n")); - (void) zfs_error(hdl, EZFS_BADDEV, desc); - break; - case ENAMETOOLONG: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "new name of at least one dataset is longer than " - "the maximum allowable length")); - (void) zfs_error(hdl, EZFS_NAMETOOLONG, desc); - break; - default: - (void) zpool_standard_error(hdl, error, desc); - memset(buf, 0, 2048); - zpool_explain_recover(hdl, - newname ? origname : thename, -error, nv, - buf, 2048); - (void) printf("\t%s", buf); - break; - } - - nvlist_free(nv); - ret = -1; - } else { - zpool_handle_t *zhp; - - /* - * This should never fail, but play it safe anyway. - */ - if (zpool_open_silent(hdl, thename, &zhp) != 0) - ret = -1; - else if (zhp != NULL) - zpool_close(zhp); - if (policy.zlp_rewind & - (ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) { - zpool_rewind_exclaim(hdl, newname ? origname : thename, - ((policy.zlp_rewind & ZPOOL_TRY_REWIND) != 0), nv); - } - nvlist_free(nv); - } - - return (ret); -} - -/* - * Translate vdev names to guids. If a vdev_path is determined to be - * unsuitable then a vd_errlist is allocated and the vdev path and errno - * are added to it. - */ -static int -zpool_translate_vdev_guids(zpool_handle_t *zhp, nvlist_t *vds, - nvlist_t *vdev_guids, nvlist_t *guids_to_paths, nvlist_t **vd_errlist) -{ - nvlist_t *errlist = NULL; - int error = 0; - - for (nvpair_t *elem = nvlist_next_nvpair(vds, NULL); elem != NULL; - elem = nvlist_next_nvpair(vds, elem)) { - boolean_t spare, cache; - - const char *vd_path = nvpair_name(elem); - nvlist_t *tgt = zpool_find_vdev(zhp, vd_path, &spare, &cache, - NULL); - - if ((tgt == NULL) || cache || spare) { - if (errlist == NULL) { - errlist = fnvlist_alloc(); - error = EINVAL; - } - - uint64_t err = (tgt == NULL) ? EZFS_NODEVICE : - (spare ? EZFS_ISSPARE : EZFS_ISL2CACHE); - fnvlist_add_int64(errlist, vd_path, err); - continue; - } - - uint64_t guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID); - fnvlist_add_uint64(vdev_guids, vd_path, guid); - - char msg[MAXNAMELEN]; - (void) snprintf(msg, sizeof (msg), "%llu", (u_longlong_t)guid); - fnvlist_add_string(guids_to_paths, msg, vd_path); - } - - if (error != 0) { - verify(errlist != NULL); - if (vd_errlist != NULL) - *vd_errlist = errlist; - else - fnvlist_free(errlist); - } - - return (error); -} - -static int -xlate_init_err(int err) -{ - switch (err) { - case ENODEV: - return (EZFS_NODEVICE); - case EINVAL: - case EROFS: - return (EZFS_BADDEV); - case EBUSY: - return (EZFS_INITIALIZING); - case ESRCH: - return (EZFS_NO_INITIALIZE); - } - return (err); -} - -int -zpool_initialize_one(zpool_handle_t *zhp, void *data) -{ - int error; - libzfs_handle_t *hdl = zpool_get_handle(zhp); - const char *pool_name = zpool_get_name(zhp); - if (zpool_open_silent(hdl, pool_name, &zhp) != 0) - return (-1); - initialize_cbdata_t *cb = data; - nvlist_t *vdevs = fnvlist_alloc(); - - nvlist_t *config = zpool_get_config(zhp, NULL); - nvlist_t *nvroot = fnvlist_lookup_nvlist(config, - ZPOOL_CONFIG_VDEV_TREE); - zpool_collect_leaves(zhp, nvroot, vdevs); - if (cb->wait) - error = zpool_initialize_wait(zhp, cb->cmd_type, vdevs); - else - error = zpool_initialize(zhp, cb->cmd_type, vdevs); - fnvlist_free(vdevs); - - return (error); -} - -/* - * Begin, suspend, cancel, or uninit (clear) the initialization (initializing - * of all free blocks) for the given vdevs in the given pool. - */ -static int -zpool_initialize_impl(zpool_handle_t *zhp, pool_initialize_func_t cmd_type, - nvlist_t *vds, boolean_t wait) -{ - int err; - - nvlist_t *vdev_guids = fnvlist_alloc(); - nvlist_t *guids_to_paths = fnvlist_alloc(); - nvlist_t *vd_errlist = NULL; - nvlist_t *errlist; - nvpair_t *elem; - - err = zpool_translate_vdev_guids(zhp, vds, vdev_guids, - guids_to_paths, &vd_errlist); - - if (err != 0) { - verify(vd_errlist != NULL); - goto list_errors; - } - - err = lzc_initialize(zhp->zpool_name, cmd_type, - vdev_guids, &errlist); - - if (err != 0) { - if (errlist != NULL && nvlist_lookup_nvlist(errlist, - ZPOOL_INITIALIZE_VDEVS, &vd_errlist) == 0) { - goto list_errors; - } - - if (err == EINVAL && cmd_type == POOL_INITIALIZE_UNINIT) { - zfs_error_aux(zhp->zpool_hdl, dgettext(TEXT_DOMAIN, - "uninitialize is not supported by kernel")); - } - - (void) zpool_standard_error(zhp->zpool_hdl, err, - dgettext(TEXT_DOMAIN, "operation failed")); - goto out; - } - - if (wait) { - for (elem = nvlist_next_nvpair(vdev_guids, NULL); elem != NULL; - elem = nvlist_next_nvpair(vdev_guids, elem)) { - - uint64_t guid = fnvpair_value_uint64(elem); - - err = lzc_wait_tag(zhp->zpool_name, - ZPOOL_WAIT_INITIALIZE, guid, NULL); - if (err != 0) { - (void) zpool_standard_error_fmt(zhp->zpool_hdl, - err, dgettext(TEXT_DOMAIN, "error " - "waiting for '%s' to initialize"), - nvpair_name(elem)); - - goto out; - } - } - } - goto out; - -list_errors: - for (elem = nvlist_next_nvpair(vd_errlist, NULL); elem != NULL; - elem = nvlist_next_nvpair(vd_errlist, elem)) { - int64_t vd_error = xlate_init_err(fnvpair_value_int64(elem)); - const char *path; - - if (nvlist_lookup_string(guids_to_paths, nvpair_name(elem), - &path) != 0) - path = nvpair_name(elem); - - (void) zfs_error_fmt(zhp->zpool_hdl, vd_error, - "cannot initialize '%s'", path); - } - -out: - fnvlist_free(vdev_guids); - fnvlist_free(guids_to_paths); - - if (vd_errlist != NULL) - fnvlist_free(vd_errlist); - - return (err == 0 ? 0 : -1); -} - -int -zpool_initialize(zpool_handle_t *zhp, pool_initialize_func_t cmd_type, - nvlist_t *vds) -{ - return (zpool_initialize_impl(zhp, cmd_type, vds, B_FALSE)); -} - -int -zpool_initialize_wait(zpool_handle_t *zhp, pool_initialize_func_t cmd_type, - nvlist_t *vds) -{ - return (zpool_initialize_impl(zhp, cmd_type, vds, B_TRUE)); -} - -static int -xlate_trim_err(int err) -{ - switch (err) { - case ENODEV: - return (EZFS_NODEVICE); - case EINVAL: - case EROFS: - return (EZFS_BADDEV); - case EBUSY: - return (EZFS_TRIMMING); - case ESRCH: - return (EZFS_NO_TRIM); - case EOPNOTSUPP: - return (EZFS_TRIM_NOTSUP); - } - return (err); -} - -void -zpool_collect_leaves(zpool_handle_t *zhp, nvlist_t *nvroot, nvlist_t *res) -{ - libzfs_handle_t *hdl = zhp->zpool_hdl; - uint_t children = 0; - nvlist_t **child; - uint_t i; - - (void) nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, - &child, &children); - - if (children == 0) { - char *path = zpool_vdev_name(hdl, zhp, nvroot, - VDEV_NAME_PATH); - - if (strcmp(path, VDEV_TYPE_INDIRECT) != 0 && - strcmp(path, VDEV_TYPE_HOLE) != 0) - fnvlist_add_boolean(res, path); - - free(path); - return; - } - - for (i = 0; i < children; i++) { - zpool_collect_leaves(zhp, child[i], res); - } -} - -int -zpool_trim_one(zpool_handle_t *zhp, void *data) -{ - int error; - libzfs_handle_t *hdl = zpool_get_handle(zhp); - const char *pool_name = zpool_get_name(zhp); - if (zpool_open_silent(hdl, pool_name, &zhp) != 0) - return (-1); - - trim_cbdata_t *cb = data; - nvlist_t *vdevs = fnvlist_alloc(); - - /* no individual leaf vdevs specified, so add them all */ - nvlist_t *config = zpool_get_config(zhp, NULL); - nvlist_t *nvroot = fnvlist_lookup_nvlist(config, - ZPOOL_CONFIG_VDEV_TREE); - - zpool_collect_leaves(zhp, nvroot, vdevs); - error = zpool_trim(zhp, cb->cmd_type, vdevs, &cb->trim_flags); - fnvlist_free(vdevs); - - return (error); -} - -static int -zpool_trim_wait(zpool_handle_t *zhp, nvlist_t *vdev_guids) -{ - int err; - nvpair_t *elem; - - for (elem = nvlist_next_nvpair(vdev_guids, NULL); elem != NULL; - elem = nvlist_next_nvpair(vdev_guids, elem)) { - - uint64_t guid = fnvpair_value_uint64(elem); - - err = lzc_wait_tag(zhp->zpool_name, - ZPOOL_WAIT_TRIM, guid, NULL); - if (err != 0) { - (void) zpool_standard_error_fmt(zhp->zpool_hdl, - err, dgettext(TEXT_DOMAIN, "error " - "waiting to trim '%s'"), nvpair_name(elem)); - - return (err); - } - } - return (0); -} - -/* - * Check errlist and report any errors, omitting ones which should be - * suppressed. Returns B_TRUE if any errors were reported. - */ -static boolean_t -check_trim_errs(zpool_handle_t *zhp, trimflags_t *trim_flags, - nvlist_t *guids_to_paths, nvlist_t *vds, nvlist_t *errlist) -{ - nvpair_t *elem; - boolean_t reported_errs = B_FALSE; - int num_vds = 0; - int num_suppressed_errs = 0; - - for (elem = nvlist_next_nvpair(vds, NULL); - elem != NULL; elem = nvlist_next_nvpair(vds, elem)) { - num_vds++; - } - - for (elem = nvlist_next_nvpair(errlist, NULL); - elem != NULL; elem = nvlist_next_nvpair(errlist, elem)) { - int64_t vd_error = xlate_trim_err(fnvpair_value_int64(elem)); - const char *path; - - /* - * If only the pool was specified, and it was not a secure - * trim then suppress warnings for individual vdevs which - * do not support trimming. - */ - if (vd_error == EZFS_TRIM_NOTSUP && - trim_flags->fullpool && - !trim_flags->secure) { - num_suppressed_errs++; - continue; - } - - reported_errs = B_TRUE; - if (nvlist_lookup_string(guids_to_paths, nvpair_name(elem), - &path) != 0) - path = nvpair_name(elem); - - (void) zfs_error_fmt(zhp->zpool_hdl, vd_error, - "cannot trim '%s'", path); - } - - if (num_suppressed_errs == num_vds) { - (void) zfs_error_aux(zhp->zpool_hdl, dgettext(TEXT_DOMAIN, - "no devices in pool support trim operations")); - (void) (zfs_error(zhp->zpool_hdl, EZFS_TRIM_NOTSUP, - dgettext(TEXT_DOMAIN, "cannot trim"))); - reported_errs = B_TRUE; - } - - return (reported_errs); -} - -/* - * Begin, suspend, or cancel the TRIM (discarding of all free blocks) for - * the given vdevs in the given pool. - */ -int -zpool_trim(zpool_handle_t *zhp, pool_trim_func_t cmd_type, nvlist_t *vds, - trimflags_t *trim_flags) -{ - int err; - int retval = 0; - - nvlist_t *vdev_guids = fnvlist_alloc(); - nvlist_t *guids_to_paths = fnvlist_alloc(); - nvlist_t *errlist = NULL; - - err = zpool_translate_vdev_guids(zhp, vds, vdev_guids, - guids_to_paths, &errlist); - if (err != 0) { - check_trim_errs(zhp, trim_flags, guids_to_paths, vds, errlist); - retval = -1; - goto out; - } - - err = lzc_trim(zhp->zpool_name, cmd_type, trim_flags->rate, - trim_flags->secure, vdev_guids, &errlist); - if (err != 0) { - nvlist_t *vd_errlist; - if (errlist != NULL && nvlist_lookup_nvlist(errlist, - ZPOOL_TRIM_VDEVS, &vd_errlist) == 0) { - if (check_trim_errs(zhp, trim_flags, guids_to_paths, - vds, vd_errlist)) { - retval = -1; - goto out; - } - } else { - char errbuf[ERRBUFLEN]; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "operation failed")); - zpool_standard_error(zhp->zpool_hdl, err, errbuf); - retval = -1; - goto out; - } - } - - - if (trim_flags->wait) - retval = zpool_trim_wait(zhp, vdev_guids); - -out: - if (errlist != NULL) - fnvlist_free(errlist); - fnvlist_free(vdev_guids); - fnvlist_free(guids_to_paths); - return (retval); -} - -/* - * Scan the pool. - */ -int -zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func, pool_scrub_cmd_t cmd) { - return (zpool_scan_range(zhp, func, cmd, 0, 0)); -} - -int -zpool_scan_range(zpool_handle_t *zhp, pool_scan_func_t func, - pool_scrub_cmd_t cmd, time_t date_start, time_t date_end) -{ - char errbuf[ERRBUFLEN]; - int err; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - nvlist_t *args = fnvlist_alloc(); - fnvlist_add_uint64(args, "scan_type", (uint64_t)func); - fnvlist_add_uint64(args, "scan_command", (uint64_t)cmd); - fnvlist_add_uint64(args, "scan_date_start", (uint64_t)date_start); - fnvlist_add_uint64(args, "scan_date_end", (uint64_t)date_end); - - err = lzc_scrub(ZFS_IOC_POOL_SCRUB, zhp->zpool_name, args, NULL); - fnvlist_free(args); - - if (err == 0) { - return (0); - } else if (err == ZFS_ERR_IOC_CMD_UNAVAIL) { - zfs_cmd_t zc = {"\0"}; - (void) strlcpy(zc.zc_name, zhp->zpool_name, - sizeof (zc.zc_name)); - zc.zc_cookie = func; - zc.zc_flags = cmd; - - if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0) - return (0); - } - - /* - * An ECANCELED on a scrub means one of the following: - * 1. we resumed a paused scrub. - * 2. we resumed a paused error scrub. - * 3. Error scrub is not run because of no error log. - * - * Note that we no longer return ECANCELED in case 1 or 2. However, in - * order to prevent problems where we have a newer userland than - * kernel, we keep this check in place. That prevents erroneous - * failures when an older kernel returns ECANCELED in those cases. - */ - if (err == ECANCELED && (func == POOL_SCAN_SCRUB || - func == POOL_SCAN_ERRORSCRUB) && cmd == POOL_SCRUB_NORMAL) - return (0); - /* - * The following cases have been handled here: - * 1. Paused a scrub/error scrub if there is none in progress. - */ - if (err == ENOENT && func != POOL_SCAN_NONE && cmd == - POOL_SCRUB_PAUSE) { - return (0); - } - - ASSERT3U(func, >=, POOL_SCAN_NONE); - ASSERT3U(func, <, POOL_SCAN_FUNCS); - - if (func == POOL_SCAN_SCRUB || func == POOL_SCAN_ERRORSCRUB) { - if (cmd == POOL_SCRUB_PAUSE) { - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot pause scrubbing %s"), - zhp->zpool_name); - } else { - assert(cmd == POOL_SCRUB_NORMAL); - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot scrub %s"), - zhp->zpool_name); - } - } else if (func == POOL_SCAN_RESILVER) { - assert(cmd == POOL_SCRUB_NORMAL); - (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot restart resilver on %s"), zhp->zpool_name); - } else if (func == POOL_SCAN_NONE) { - (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot cancel scrubbing %s"), zhp->zpool_name); - } else { - assert(!"unexpected result"); - } - - /* - * With EBUSY, six cases are possible: - * - * Current state Requested - * 1. Normal Scrub Running Normal Scrub or Error Scrub - * 2. Normal Scrub Paused Error Scrub - * 3. Normal Scrub Paused Pause Normal Scrub - * 4. Error Scrub Running Normal Scrub or Error Scrub - * 5. Error Scrub Paused Pause Error Scrub - * 6. Resilvering Anything else - */ - if (err == EBUSY) { - nvlist_t *nvroot; - pool_scan_stat_t *ps = NULL; - uint_t psc; - - nvroot = fnvlist_lookup_nvlist(zhp->zpool_config, - ZPOOL_CONFIG_VDEV_TREE); - (void) nvlist_lookup_uint64_array(nvroot, - ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &psc); - if (ps && ps->pss_func == POOL_SCAN_SCRUB && - ps->pss_state == DSS_SCANNING) { - if (ps->pss_pass_scrub_pause == 0) { - /* handles case 1 */ - assert(cmd == POOL_SCRUB_NORMAL); - return (zfs_error(hdl, EZFS_SCRUBBING, - errbuf)); - } else { - if (func == POOL_SCAN_ERRORSCRUB) { - /* handles case 2 */ - ASSERT3U(cmd, ==, POOL_SCRUB_NORMAL); - return (zfs_error(hdl, - EZFS_SCRUB_PAUSED_TO_CANCEL, - errbuf)); - } else { - /* handles case 3 */ - ASSERT3U(func, ==, POOL_SCAN_SCRUB); - ASSERT3U(cmd, ==, POOL_SCRUB_PAUSE); - return (zfs_error(hdl, - EZFS_SCRUB_PAUSED, errbuf)); - } - } - } else if (ps && - ps->pss_error_scrub_func == POOL_SCAN_ERRORSCRUB && - ps->pss_error_scrub_state == DSS_ERRORSCRUBBING) { - if (ps->pss_pass_error_scrub_pause == 0) { - /* handles case 4 */ - ASSERT3U(cmd, ==, POOL_SCRUB_NORMAL); - return (zfs_error(hdl, EZFS_ERRORSCRUBBING, - errbuf)); - } else { - /* handles case 5 */ - ASSERT3U(func, ==, POOL_SCAN_ERRORSCRUB); - ASSERT3U(cmd, ==, POOL_SCRUB_PAUSE); - return (zfs_error(hdl, EZFS_ERRORSCRUB_PAUSED, - errbuf)); - } - } else { - /* handles case 6 */ - return (zfs_error(hdl, EZFS_RESILVERING, errbuf)); - } - } else if (err == ENOENT) { - return (zfs_error(hdl, EZFS_NO_SCRUB, errbuf)); - } else if (err == ENOTSUP && func == POOL_SCAN_RESILVER) { - return (zfs_error(hdl, EZFS_NO_RESILVER_DEFER, errbuf)); - } else { - return (zpool_standard_error(hdl, err, errbuf)); - } -} - -/* - * Find a vdev that matches the search criteria specified. We use the - * the nvpair name to determine how we should look for the device. - * 'avail_spare' is set to TRUE if the provided guid refers to an AVAIL - * spare; but FALSE if its an INUSE spare. - * - * If 'return_parent' is set, then return the *parent* of the vdev you're - * searching for rather than the vdev itself. - */ -static nvlist_t * -vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare, - boolean_t *l2cache, boolean_t *log, boolean_t return_parent) -{ - uint_t c, children; - nvlist_t **child; - nvlist_t *ret; - uint64_t is_log; - const char *srchkey; - nvpair_t *pair = nvlist_next_nvpair(search, NULL); - const char *tmp = NULL; - boolean_t is_root; - - /* Nothing to look for */ - if (search == NULL || pair == NULL) - return (NULL); - - /* Obtain the key we will use to search */ - srchkey = nvpair_name(pair); - - nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &tmp); - if (strcmp(tmp, "root") == 0) - is_root = B_TRUE; - else - is_root = B_FALSE; - - switch (nvpair_type(pair)) { - case DATA_TYPE_UINT64: - if (strcmp(srchkey, ZPOOL_CONFIG_GUID) == 0) { - uint64_t srchval = fnvpair_value_uint64(pair); - uint64_t theguid = fnvlist_lookup_uint64(nv, - ZPOOL_CONFIG_GUID); - if (theguid == srchval) - return (nv); - } - break; - - case DATA_TYPE_STRING: { - const char *srchval, *val; - - srchval = fnvpair_value_string(pair); - if (nvlist_lookup_string(nv, srchkey, &val) != 0) - break; - - /* - * Search for the requested value. Special cases: - * - * - ZPOOL_CONFIG_PATH for whole disk entries. These end in - * "-part1", or "p1". The suffix is hidden from the user, - * but included in the string, so this matches around it. - * - ZPOOL_CONFIG_PATH for short names zfs_strcmp_shortname() - * is used to check all possible expanded paths. - * - looking for a top-level vdev name (i.e. ZPOOL_CONFIG_TYPE). - * - * Otherwise, all other searches are simple string compares. - */ - if (strcmp(srchkey, ZPOOL_CONFIG_PATH) == 0) { - uint64_t wholedisk = 0; - - (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, - &wholedisk); - if (zfs_strcmp_pathname(srchval, val, wholedisk) == 0) - return (nv); - - } else if (strcmp(srchkey, ZPOOL_CONFIG_TYPE) == 0) { - char *type, *idx, *end, *p; - uint64_t id, vdev_id; - - /* - * Determine our vdev type, keeping in mind - * that the srchval is composed of a type and - * vdev id pair (i.e. mirror-4). - */ - if ((type = strdup(srchval)) == NULL) - return (NULL); - - if ((p = strrchr(type, '-')) == NULL) { - free(type); - break; - } - idx = p + 1; - *p = '\0'; - - /* - * draid names are presented like: draid2:4d:6c:0s - * We match them up to the first ':' so we can still - * do the parity check below, but the other params - * are ignored. - */ - if ((p = strchr(type, ':')) != NULL) { - if (strncmp(type, VDEV_TYPE_DRAID, - strlen(VDEV_TYPE_DRAID)) == 0) - *p = '\0'; - } - - /* - * If the types don't match then keep looking. - */ - if (strncmp(val, type, strlen(val)) != 0) { - free(type); - break; - } - - verify(zpool_vdev_is_interior(type)); - - id = fnvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID); - errno = 0; - vdev_id = strtoull(idx, &end, 10); - - /* - * If we are looking for a raidz and a parity is - * specified, make sure it matches. - */ - int rzlen = strlen(VDEV_TYPE_RAIDZ); - assert(rzlen == strlen(VDEV_TYPE_DRAID)); - int typlen = strlen(type); - if ((strncmp(type, VDEV_TYPE_RAIDZ, rzlen) == 0 || - strncmp(type, VDEV_TYPE_DRAID, rzlen) == 0) && - typlen != rzlen) { - uint64_t vdev_parity; - int parity = *(type + rzlen) - '0'; - - if (parity <= 0 || parity > 3 || - (typlen - rzlen) != 1) { - /* - * Nonsense parity specified, can - * never match - */ - free(type); - return (NULL); - } - vdev_parity = fnvlist_lookup_uint64(nv, - ZPOOL_CONFIG_NPARITY); - if ((int)vdev_parity != parity) { - free(type); - break; - } - } - - free(type); - if (errno != 0) - return (NULL); - - /* - * Now verify that we have the correct vdev id. - */ - if (vdev_id == id) - return (nv); - } - - /* - * Common case - */ - if (strcmp(srchval, val) == 0) - return (nv); - break; - } - - default: - break; - } - - if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, - &child, &children) != 0) - return (NULL); - - for (c = 0; c < children; c++) { - if ((ret = vdev_to_nvlist_iter(child[c], search, - avail_spare, l2cache, NULL, return_parent)) != NULL) { - /* - * The 'is_log' value is only set for the toplevel - * vdev, not the leaf vdevs. So we always lookup the - * log device from the root of the vdev tree (where - * 'log' is non-NULL). - */ - if (log != NULL && - nvlist_lookup_uint64(child[c], - ZPOOL_CONFIG_IS_LOG, &is_log) == 0 && - is_log) { - *log = B_TRUE; - } - return (ret && return_parent && !is_root ? nv : ret); - } - } - - if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, - &child, &children) == 0) { - for (c = 0; c < children; c++) { - if ((ret = vdev_to_nvlist_iter(child[c], search, - avail_spare, l2cache, NULL, return_parent)) - != NULL) { - *avail_spare = B_TRUE; - return (ret && return_parent && - !is_root ? nv : ret); - } - } - } - - if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, - &child, &children) == 0) { - for (c = 0; c < children; c++) { - if ((ret = vdev_to_nvlist_iter(child[c], search, - avail_spare, l2cache, NULL, return_parent)) - != NULL) { - *l2cache = B_TRUE; - return (ret && return_parent && - !is_root ? nv : ret); - } - } - } - - return (NULL); -} - -/* - * Given a physical path or guid, find the associated vdev. - */ -nvlist_t * -zpool_find_vdev_by_physpath(zpool_handle_t *zhp, const char *ppath, - boolean_t *avail_spare, boolean_t *l2cache, boolean_t *log) -{ - nvlist_t *search, *nvroot, *ret; - uint64_t guid; - char *end; - - search = fnvlist_alloc(); - - guid = strtoull(ppath, &end, 0); - if (guid != 0 && *end == '\0') { - fnvlist_add_uint64(search, ZPOOL_CONFIG_GUID, guid); - } else { - fnvlist_add_string(search, ZPOOL_CONFIG_PHYS_PATH, ppath); - } - - nvroot = fnvlist_lookup_nvlist(zhp->zpool_config, - ZPOOL_CONFIG_VDEV_TREE); - - *avail_spare = B_FALSE; - *l2cache = B_FALSE; - if (log != NULL) - *log = B_FALSE; - ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log, - B_FALSE); - fnvlist_free(search); - - return (ret); -} - -/* - * Determine if we have an "interior" top-level vdev (i.e mirror/raidz). - */ -static boolean_t -zpool_vdev_is_interior(const char *name) -{ - if (strncmp(name, VDEV_TYPE_RAIDZ, strlen(VDEV_TYPE_RAIDZ)) == 0 || - strncmp(name, VDEV_TYPE_SPARE, strlen(VDEV_TYPE_SPARE)) == 0 || - strncmp(name, - VDEV_TYPE_REPLACING, strlen(VDEV_TYPE_REPLACING)) == 0 || - strncmp(name, VDEV_TYPE_ROOT, strlen(VDEV_TYPE_ROOT)) == 0 || - strncmp(name, VDEV_TYPE_MIRROR, strlen(VDEV_TYPE_MIRROR)) == 0) - return (B_TRUE); - - if (strncmp(name, VDEV_TYPE_DRAID, strlen(VDEV_TYPE_DRAID)) == 0 && - !zpool_is_draid_spare(name)) - return (B_TRUE); - - return (B_FALSE); -} - -/* - * Lookup the nvlist for a given vdev or vdev's parent (depending on - * if 'return_parent' is set). - */ -static nvlist_t * -__zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare, - boolean_t *l2cache, boolean_t *log, boolean_t return_parent) -{ - char *end; - nvlist_t *nvroot, *search, *ret; - uint64_t guid; - boolean_t __avail_spare, __l2cache, __log; - - search = fnvlist_alloc(); - - guid = strtoull(path, &end, 0); - if (guid != 0 && *end == '\0') { - fnvlist_add_uint64(search, ZPOOL_CONFIG_GUID, guid); - } else if (zpool_vdev_is_interior(path)) { - fnvlist_add_string(search, ZPOOL_CONFIG_TYPE, path); - } else { - fnvlist_add_string(search, ZPOOL_CONFIG_PATH, path); - } - - nvroot = fnvlist_lookup_nvlist(zhp->zpool_config, - ZPOOL_CONFIG_VDEV_TREE); - - /* - * User can pass NULL for avail_spare, l2cache, and log, but - * we still need to provide variables to vdev_to_nvlist_iter(), so - * just point them to junk variables here. - */ - if (!avail_spare) - avail_spare = &__avail_spare; - if (!l2cache) - l2cache = &__l2cache; - if (!log) - log = &__log; - - *avail_spare = B_FALSE; - *l2cache = B_FALSE; - if (log != NULL) - *log = B_FALSE; - ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log, - return_parent); - fnvlist_free(search); - - return (ret); -} - -nvlist_t * -zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare, - boolean_t *l2cache, boolean_t *log) -{ - return (__zpool_find_vdev(zhp, path, avail_spare, l2cache, log, - B_FALSE)); -} - -/* Given a vdev path, return its parent's nvlist */ -nvlist_t * -zpool_find_parent_vdev(zpool_handle_t *zhp, const char *path, - boolean_t *avail_spare, boolean_t *l2cache, boolean_t *log) -{ - return (__zpool_find_vdev(zhp, path, avail_spare, l2cache, log, - B_TRUE)); -} - -/* - * Convert a vdev path to a GUID. Returns GUID or 0 on error. - * - * If is_spare, is_l2cache, or is_log is non-NULL, then store within it - * if the VDEV is a spare, l2cache, or log device. If they're NULL then - * ignore them. - */ -static uint64_t -zpool_vdev_path_to_guid_impl(zpool_handle_t *zhp, const char *path, - boolean_t *is_spare, boolean_t *is_l2cache, boolean_t *is_log) -{ - boolean_t spare = B_FALSE, l2cache = B_FALSE, log = B_FALSE; - nvlist_t *tgt; - - if ((tgt = zpool_find_vdev(zhp, path, &spare, &l2cache, - &log)) == NULL) - return (0); - - if (is_spare != NULL) - *is_spare = spare; - if (is_l2cache != NULL) - *is_l2cache = l2cache; - if (is_log != NULL) - *is_log = log; - - return (fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID)); -} - -/* Convert a vdev path to a GUID. Returns GUID or 0 on error. */ -uint64_t -zpool_vdev_path_to_guid(zpool_handle_t *zhp, const char *path) -{ - return (zpool_vdev_path_to_guid_impl(zhp, path, NULL, NULL, NULL)); -} - -/* - * Bring the specified vdev online. The 'flags' parameter is a set of the - * ZFS_ONLINE_* flags. - */ -int -zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags, - vdev_state_t *newstate) -{ - zfs_cmd_t zc = {"\0"}; - char errbuf[ERRBUFLEN]; - nvlist_t *tgt; - boolean_t avail_spare, l2cache, islog; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - if (flags & ZFS_ONLINE_EXPAND) { - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot expand %s"), path); - } else { - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot online %s"), path); - } - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, - &islog)) == NULL) - return (zfs_error(hdl, EZFS_NODEVICE, errbuf)); - - zc.zc_guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID); - - if (!(flags & ZFS_ONLINE_SPARE) && avail_spare) - return (zfs_error(hdl, EZFS_ISSPARE, errbuf)); - -#ifndef __FreeBSD__ - const char *pathname; - if ((flags & ZFS_ONLINE_EXPAND || - zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) && - nvlist_lookup_string(tgt, ZPOOL_CONFIG_PATH, &pathname) == 0) { - uint64_t wholedisk = 0; - - (void) nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_WHOLE_DISK, - &wholedisk); - - /* - * XXX - L2ARC 1.0 devices can't support expansion. - */ - if (l2cache) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "cannot expand cache devices")); - return (zfs_error(hdl, EZFS_VDEVNOTSUP, errbuf)); - } - - if (wholedisk) { - const char *fullpath = path; - char buf[MAXPATHLEN]; - int error; - - if (path[0] != '/') { - error = zfs_resolve_shortname(path, buf, - sizeof (buf)); - if (error != 0) - return (zfs_error(hdl, EZFS_NODEVICE, - errbuf)); - - fullpath = buf; - } - - error = zpool_relabel_disk(hdl, fullpath, errbuf); - if (error != 0) - return (error); - } - } -#endif - - zc.zc_cookie = VDEV_STATE_ONLINE; - zc.zc_obj = flags; - - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) != 0) { - if (errno == EINVAL) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "was split " - "from this pool into a new one. Use '%s' " - "instead"), "zpool detach"); - return (zfs_error(hdl, EZFS_POSTSPLIT_ONLINE, errbuf)); - } - return (zpool_standard_error(hdl, errno, errbuf)); - } - - *newstate = zc.zc_cookie; - return (0); -} - -/* - * Take the specified vdev offline - */ -int -zpool_vdev_offline(zpool_handle_t *zhp, const char *path, boolean_t istmp) -{ - zfs_cmd_t zc = {"\0"}; - char errbuf[ERRBUFLEN]; - nvlist_t *tgt; - boolean_t avail_spare, l2cache; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot offline %s"), path); - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, - NULL)) == NULL) - return (zfs_error(hdl, EZFS_NODEVICE, errbuf)); - - zc.zc_guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID); - - if (avail_spare) - return (zfs_error(hdl, EZFS_ISSPARE, errbuf)); - - zc.zc_cookie = VDEV_STATE_OFFLINE; - zc.zc_obj = istmp ? ZFS_OFFLINE_TEMPORARY : 0; - - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) == 0) - return (0); - - switch (errno) { - case EBUSY: - - /* - * There are no other replicas of this device. - */ - return (zfs_error(hdl, EZFS_NOREPLICAS, errbuf)); - - case EEXIST: - /* - * The log device has unplayed logs - */ - return (zfs_error(hdl, EZFS_UNPLAYED_LOGS, errbuf)); - - default: - return (zpool_standard_error(hdl, errno, errbuf)); - } -} - -/* - * Remove the specified vdev asynchronously from the configuration, so - * that it may come ONLINE if reinserted. This is called from zed on - * Udev remove event. - * Note: We also have a similar function zpool_vdev_remove() that - * removes the vdev from the pool. - */ -int -zpool_vdev_remove_wanted(zpool_handle_t *zhp, const char *path) -{ - zfs_cmd_t zc = {"\0"}; - char errbuf[ERRBUFLEN]; - nvlist_t *tgt; - boolean_t avail_spare, l2cache; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot remove %s"), path); - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, - NULL)) == NULL) - return (zfs_error(hdl, EZFS_NODEVICE, errbuf)); - - zc.zc_guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID); - - zc.zc_cookie = VDEV_STATE_REMOVED; - - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) == 0) - return (0); - - return (zpool_standard_error(hdl, errno, errbuf)); -} - -/* - * Mark the given vdev faulted. - */ -int -zpool_vdev_fault(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux) -{ - zfs_cmd_t zc = {"\0"}; - char errbuf[ERRBUFLEN]; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot fault %llu"), (u_longlong_t)guid); - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - zc.zc_guid = guid; - zc.zc_cookie = VDEV_STATE_FAULTED; - zc.zc_obj = aux; - - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) == 0) - return (0); - - switch (errno) { - case EBUSY: - - /* - * There are no other replicas of this device. - */ - return (zfs_error(hdl, EZFS_NOREPLICAS, errbuf)); - - default: - return (zpool_standard_error(hdl, errno, errbuf)); - } - -} - -/* - * Generic set vdev state function - */ -static int -zpool_vdev_set_state(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux, - vdev_state_t state) -{ - zfs_cmd_t zc = {"\0"}; - char errbuf[ERRBUFLEN]; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot set %s %llu"), - zpool_state_to_name(state, aux), (u_longlong_t)guid); - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - zc.zc_guid = guid; - zc.zc_cookie = state; - zc.zc_obj = aux; - - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) == 0) - return (0); - - return (zpool_standard_error(hdl, errno, errbuf)); -} - -/* - * Mark the given vdev degraded. - */ -int -zpool_vdev_degrade(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux) -{ - return (zpool_vdev_set_state(zhp, guid, aux, VDEV_STATE_DEGRADED)); -} - -/* - * Mark the given vdev as in a removed state (as if the device does not exist). - * - * This is different than zpool_vdev_remove() which does a removal of a device - * from the pool (but the device does exist). - */ -int -zpool_vdev_set_removed_state(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux) -{ - return (zpool_vdev_set_state(zhp, guid, aux, VDEV_STATE_REMOVED)); -} - -/* - * Returns TRUE if the given nvlist is a vdev that was originally swapped in as - * a hot spare. - */ -static boolean_t -is_replacing_spare(nvlist_t *search, nvlist_t *tgt, int which) -{ - nvlist_t **child; - uint_t c, children; - - if (nvlist_lookup_nvlist_array(search, ZPOOL_CONFIG_CHILDREN, &child, - &children) == 0) { - const char *type = fnvlist_lookup_string(search, - ZPOOL_CONFIG_TYPE); - if ((strcmp(type, VDEV_TYPE_SPARE) == 0 || - strcmp(type, VDEV_TYPE_DRAID_SPARE) == 0) && - children == 2 && child[which] == tgt) - return (B_TRUE); - - for (c = 0; c < children; c++) - if (is_replacing_spare(child[c], tgt, which)) - return (B_TRUE); - } - - return (B_FALSE); -} - -/* - * Attach new_disk (fully described by nvroot) to old_disk. - * If 'replacing' is specified, the new disk will replace the old one. - */ -int -zpool_vdev_attach(zpool_handle_t *zhp, const char *old_disk, - const char *new_disk, nvlist_t *nvroot, int replacing, boolean_t rebuild) -{ - zfs_cmd_t zc = {"\0"}; - char errbuf[ERRBUFLEN]; - int ret; - nvlist_t *tgt; - boolean_t avail_spare, l2cache, islog; - uint64_t val; - char *newname; - const char *type; - nvlist_t **child; - uint_t children; - nvlist_t *config_root; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - if (replacing) - (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot replace %s with %s"), old_disk, new_disk); - else - (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot attach %s to %s"), new_disk, old_disk); - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare, &l2cache, - &islog)) == NULL) - return (zfs_error(hdl, EZFS_NODEVICE, errbuf)); - - if (avail_spare) - return (zfs_error(hdl, EZFS_ISSPARE, errbuf)); - - if (l2cache) - return (zfs_error(hdl, EZFS_ISL2CACHE, errbuf)); - - zc.zc_guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID); - zc.zc_cookie = replacing; - zc.zc_simple = rebuild; - - if (rebuild && - zfeature_lookup_guid("org.openzfs:device_rebuild", NULL) != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "the loaded zfs module doesn't support device rebuilds")); - return (zfs_error(hdl, EZFS_POOL_NOTSUP, errbuf)); - } - - type = fnvlist_lookup_string(tgt, ZPOOL_CONFIG_TYPE); - if (strcmp(type, VDEV_TYPE_RAIDZ) == 0 && - zfeature_lookup_guid("org.openzfs:raidz_expansion", NULL) != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "the loaded zfs module doesn't support raidz expansion")); - return (zfs_error(hdl, EZFS_POOL_NOTSUP, errbuf)); - } - - if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, - &child, &children) != 0 || children != 1) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "new device must be a single disk")); - return (zfs_error(hdl, EZFS_INVALCONFIG, errbuf)); - } - - config_root = fnvlist_lookup_nvlist(zpool_get_config(zhp, NULL), - ZPOOL_CONFIG_VDEV_TREE); - - if ((newname = zpool_vdev_name(NULL, NULL, child[0], 0)) == NULL) - return (-1); - - /* - * If the target is a hot spare that has been swapped in, we can only - * replace it with another hot spare. - */ - if (replacing && - nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_IS_SPARE, &val) == 0 && - (zpool_find_vdev(zhp, newname, &avail_spare, &l2cache, - NULL) == NULL || !avail_spare) && - is_replacing_spare(config_root, tgt, 1)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "can only be replaced by another hot spare")); - free(newname); - return (zfs_error(hdl, EZFS_BADTARGET, errbuf)); - } - - free(newname); - - zcmd_write_conf_nvlist(hdl, &zc, nvroot); - - ret = zfs_ioctl(hdl, ZFS_IOC_VDEV_ATTACH, &zc); - - zcmd_free_nvlists(&zc); - - if (ret == 0) - return (0); - - switch (errno) { - case ENOTSUP: - /* - * Can't attach to or replace this type of vdev. - */ - if (replacing) { - uint64_t version = zpool_get_prop_int(zhp, - ZPOOL_PROP_VERSION, NULL); - - if (islog) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "cannot replace a log with a spare")); - } else if (rebuild) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "only mirror and dRAID vdevs support " - "sequential reconstruction")); - } else if (zpool_is_draid_spare(new_disk)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "dRAID spares can only replace child " - "devices in their parent's dRAID vdev")); - } else if (version >= SPA_VERSION_MULTI_REPLACE) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "already in replacing/spare config; wait " - "for completion or use 'zpool detach'")); - } else { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "cannot replace a replacing device")); - } - } else if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "raidz_expansion feature must be enabled " - "in order to attach a device to raidz")); - } else { - char status[64] = {0}; - zpool_prop_get_feature(zhp, - "feature@device_rebuild", status, 63); - if (rebuild && - strncmp(status, ZFS_FEATURE_DISABLED, 64) == 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "device_rebuild feature must be enabled " - "in order to use sequential " - "reconstruction")); - } else { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "can only attach to mirrors and top-level " - "disks")); - } - } - (void) zfs_error(hdl, EZFS_BADTARGET, errbuf); - break; - - case EINVAL: - /* - * The new device must be a single disk. - */ - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "new device must be a single disk")); - (void) zfs_error(hdl, EZFS_INVALCONFIG, errbuf); - break; - - case EBUSY: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s is busy"), - new_disk); - (void) zfs_error(hdl, EZFS_BADDEV, errbuf); - break; - - case EOVERFLOW: - /* - * The new device is too small. - */ - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "device is too small")); - (void) zfs_error(hdl, EZFS_BADDEV, errbuf); - break; - - case EDOM: - /* - * The new device has a different optimal sector size. - */ - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "new device has a different optimal sector size; use the " - "option '-o ashift=N' to override the optimal size")); - (void) zfs_error(hdl, EZFS_BADDEV, errbuf); - break; - - case ENAMETOOLONG: - /* - * The resulting top-level vdev spec won't fit in the label. - */ - (void) zfs_error(hdl, EZFS_DEVOVERFLOW, errbuf); - break; - - case ENXIO: - /* - * The existing raidz vdev has offline children - */ - if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "raidz vdev has devices that are are offline or " - "being replaced")); - (void) zfs_error(hdl, EZFS_BADDEV, errbuf); - break; - } else { - (void) zpool_standard_error(hdl, errno, errbuf); - } - break; - - case EADDRINUSE: - /* - * The boot reserved area is already being used (FreeBSD) - */ - if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "the reserved boot area needed for the expansion " - "is already being used by a boot loader")); - (void) zfs_error(hdl, EZFS_BADDEV, errbuf); - } else { - (void) zpool_standard_error(hdl, errno, errbuf); - } - break; - - case ZFS_ERR_ASHIFT_MISMATCH: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "The new device cannot have a higher alignment requirement " - "than the top-level vdev.")); - (void) zfs_error(hdl, EZFS_BADTARGET, errbuf); - break; - default: - (void) zpool_standard_error(hdl, errno, errbuf); - } - - return (-1); -} - -/* - * Detach the specified device. - */ -int -zpool_vdev_detach(zpool_handle_t *zhp, const char *path) -{ - zfs_cmd_t zc = {"\0"}; - char errbuf[ERRBUFLEN]; - nvlist_t *tgt; - boolean_t avail_spare, l2cache; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot detach %s"), path); - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, - NULL)) == NULL) - return (zfs_error(hdl, EZFS_NODEVICE, errbuf)); - - if (avail_spare) - return (zfs_error(hdl, EZFS_ISSPARE, errbuf)); - - if (l2cache) - return (zfs_error(hdl, EZFS_ISL2CACHE, errbuf)); - - zc.zc_guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID); - - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_DETACH, &zc) == 0) - return (0); - - switch (errno) { - - case ENOTSUP: - /* - * Can't detach from this type of vdev. - */ - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only " - "applicable to mirror and replacing vdevs")); - (void) zfs_error(hdl, EZFS_BADTARGET, errbuf); - break; - - case EBUSY: - /* - * There are no other replicas of this device. - */ - (void) zfs_error(hdl, EZFS_NOREPLICAS, errbuf); - break; - - default: - (void) zpool_standard_error(hdl, errno, errbuf); - } - - return (-1); -} - -/* - * Find a mirror vdev in the source nvlist. - * - * The mchild array contains a list of disks in one of the top-level mirrors - * of the source pool. The schild array contains a list of disks that the - * user specified on the command line. We loop over the mchild array to - * see if any entry in the schild array matches. - * - * If a disk in the mchild array is found in the schild array, we return - * the index of that entry. Otherwise we return -1. - */ -static int -find_vdev_entry(zpool_handle_t *zhp, nvlist_t **mchild, uint_t mchildren, - nvlist_t **schild, uint_t schildren) -{ - uint_t mc; - - for (mc = 0; mc < mchildren; mc++) { - uint_t sc; - char *mpath = zpool_vdev_name(zhp->zpool_hdl, zhp, - mchild[mc], 0); - - for (sc = 0; sc < schildren; sc++) { - char *spath = zpool_vdev_name(zhp->zpool_hdl, zhp, - schild[sc], 0); - boolean_t result = (strcmp(mpath, spath) == 0); - - free(spath); - if (result) { - free(mpath); - return (mc); - } - } - - free(mpath); - } - - return (-1); -} - -/* - * Split a mirror pool. If newroot points to null, then a new nvlist - * is generated and it is the responsibility of the caller to free it. - */ -int -zpool_vdev_split(zpool_handle_t *zhp, char *newname, nvlist_t **newroot, - nvlist_t *props, splitflags_t flags) -{ - zfs_cmd_t zc = {"\0"}; - char errbuf[ERRBUFLEN]; - const char *bias; - nvlist_t *tree, *config, **child, **newchild, *newconfig = NULL; - nvlist_t **varray = NULL, *zc_props = NULL; - uint_t c, children, newchildren, lastlog = 0, vcount, found = 0; - libzfs_handle_t *hdl = zhp->zpool_hdl; - uint64_t vers, readonly = B_FALSE; - boolean_t freelist = B_FALSE, memory_err = B_TRUE; - int retval = 0; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "Unable to split %s"), zhp->zpool_name); - - if (!zpool_name_valid(hdl, B_FALSE, newname)) - return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf)); - - if ((config = zpool_get_config(zhp, NULL)) == NULL) { - (void) fprintf(stderr, gettext("Internal error: unable to " - "retrieve pool configuration\n")); - return (-1); - } - - tree = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE); - vers = fnvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION); - - if (props) { - prop_flags_t flags = { .create = B_FALSE, .import = B_TRUE }; - if ((zc_props = zpool_valid_proplist(hdl, zhp->zpool_name, - props, vers, flags, errbuf)) == NULL) - return (-1); - (void) nvlist_lookup_uint64(zc_props, - zpool_prop_to_name(ZPOOL_PROP_READONLY), &readonly); - if (readonly) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "property %s can only be set at import time"), - zpool_prop_to_name(ZPOOL_PROP_READONLY)); - return (-1); - } - } - - if (nvlist_lookup_nvlist_array(tree, ZPOOL_CONFIG_CHILDREN, &child, - &children) != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Source pool is missing vdev tree")); - nvlist_free(zc_props); - return (-1); - } - - varray = zfs_alloc(hdl, children * sizeof (nvlist_t *)); - vcount = 0; - - if (*newroot == NULL || - nvlist_lookup_nvlist_array(*newroot, ZPOOL_CONFIG_CHILDREN, - &newchild, &newchildren) != 0) - newchildren = 0; - - for (c = 0; c < children; c++) { - uint64_t is_log = B_FALSE, is_hole = B_FALSE; - boolean_t is_special = B_FALSE, is_dedup = B_FALSE; - const char *type; - nvlist_t **mchild, *vdev; - uint_t mchildren; - int entry; - - /* - * Unlike cache & spares, slogs are stored in the - * ZPOOL_CONFIG_CHILDREN array. We filter them out here. - */ - (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, - &is_log); - (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_HOLE, - &is_hole); - if (is_log || is_hole) { - /* - * Create a hole vdev and put it in the config. - */ - if (nvlist_alloc(&vdev, NV_UNIQUE_NAME, 0) != 0) - goto out; - if (nvlist_add_string(vdev, ZPOOL_CONFIG_TYPE, - VDEV_TYPE_HOLE) != 0) - goto out; - if (nvlist_add_uint64(vdev, ZPOOL_CONFIG_IS_HOLE, - 1) != 0) - goto out; - if (lastlog == 0) - lastlog = vcount; - varray[vcount++] = vdev; - continue; - } - lastlog = 0; - type = fnvlist_lookup_string(child[c], ZPOOL_CONFIG_TYPE); - - if (strcmp(type, VDEV_TYPE_INDIRECT) == 0) { - vdev = child[c]; - if (nvlist_dup(vdev, &varray[vcount++], 0) != 0) - goto out; - continue; - } else if (strcmp(type, VDEV_TYPE_MIRROR) != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Source pool must be composed only of mirrors\n")); - retval = zfs_error(hdl, EZFS_INVALCONFIG, errbuf); - goto out; - } - - if (nvlist_lookup_string(child[c], - ZPOOL_CONFIG_ALLOCATION_BIAS, &bias) == 0) { - if (strcmp(bias, VDEV_ALLOC_BIAS_SPECIAL) == 0) - is_special = B_TRUE; - else if (strcmp(bias, VDEV_ALLOC_BIAS_DEDUP) == 0) - is_dedup = B_TRUE; - } - verify(nvlist_lookup_nvlist_array(child[c], - ZPOOL_CONFIG_CHILDREN, &mchild, &mchildren) == 0); - - /* find or add an entry for this top-level vdev */ - if (newchildren > 0 && - (entry = find_vdev_entry(zhp, mchild, mchildren, - newchild, newchildren)) >= 0) { - /* We found a disk that the user specified. */ - vdev = mchild[entry]; - ++found; - } else { - /* User didn't specify a disk for this vdev. */ - vdev = mchild[mchildren - 1]; - } - - if (nvlist_dup(vdev, &varray[vcount++], 0) != 0) - goto out; - - if (flags.dryrun != 0) { - if (is_dedup == B_TRUE) { - if (nvlist_add_string(varray[vcount - 1], - ZPOOL_CONFIG_ALLOCATION_BIAS, - VDEV_ALLOC_BIAS_DEDUP) != 0) - goto out; - } else if (is_special == B_TRUE) { - if (nvlist_add_string(varray[vcount - 1], - ZPOOL_CONFIG_ALLOCATION_BIAS, - VDEV_ALLOC_BIAS_SPECIAL) != 0) - goto out; - } - } - } - - /* did we find every disk the user specified? */ - if (found != newchildren) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Device list must " - "include at most one disk from each mirror")); - retval = zfs_error(hdl, EZFS_INVALCONFIG, errbuf); - goto out; - } - - /* Prepare the nvlist for populating. */ - if (*newroot == NULL) { - if (nvlist_alloc(newroot, NV_UNIQUE_NAME, 0) != 0) - goto out; - freelist = B_TRUE; - if (nvlist_add_string(*newroot, ZPOOL_CONFIG_TYPE, - VDEV_TYPE_ROOT) != 0) - goto out; - } else { - verify(nvlist_remove_all(*newroot, ZPOOL_CONFIG_CHILDREN) == 0); - } - - /* Add all the children we found */ - if (nvlist_add_nvlist_array(*newroot, ZPOOL_CONFIG_CHILDREN, - (const nvlist_t **)varray, lastlog == 0 ? vcount : lastlog) != 0) - goto out; - - /* - * If we're just doing a dry run, exit now with success. - */ - if (flags.dryrun) { - memory_err = B_FALSE; - freelist = B_FALSE; - goto out; - } - - /* now build up the config list & call the ioctl */ - if (nvlist_alloc(&newconfig, NV_UNIQUE_NAME, 0) != 0) - goto out; - - if (nvlist_add_nvlist(newconfig, - ZPOOL_CONFIG_VDEV_TREE, *newroot) != 0 || - nvlist_add_string(newconfig, - ZPOOL_CONFIG_POOL_NAME, newname) != 0 || - nvlist_add_uint64(newconfig, ZPOOL_CONFIG_VERSION, vers) != 0) - goto out; - - /* - * The new pool is automatically part of the namespace unless we - * explicitly export it. - */ - if (!flags.import) - zc.zc_cookie = ZPOOL_EXPORT_AFTER_SPLIT; - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - (void) strlcpy(zc.zc_string, newname, sizeof (zc.zc_string)); - zcmd_write_conf_nvlist(hdl, &zc, newconfig); - if (zc_props != NULL) - zcmd_write_src_nvlist(hdl, &zc, zc_props); - - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SPLIT, &zc) != 0) { - retval = zpool_standard_error(hdl, errno, errbuf); - goto out; - } - - freelist = B_FALSE; - memory_err = B_FALSE; - -out: - if (varray != NULL) { - int v; - - for (v = 0; v < vcount; v++) - nvlist_free(varray[v]); - free(varray); - } - zcmd_free_nvlists(&zc); - nvlist_free(zc_props); - nvlist_free(newconfig); - if (freelist) { - nvlist_free(*newroot); - *newroot = NULL; - } - - if (retval != 0) - return (retval); - - if (memory_err) - return (no_memory(hdl)); - - return (0); -} - -/* - * Remove the given device. - */ -int -zpool_vdev_remove(zpool_handle_t *zhp, const char *path) -{ - zfs_cmd_t zc = {"\0"}; - char errbuf[ERRBUFLEN]; - nvlist_t *tgt; - boolean_t avail_spare, l2cache, islog; - libzfs_handle_t *hdl = zhp->zpool_hdl; - uint64_t version; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot remove %s"), path); - - if (zpool_is_draid_spare(path)) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "dRAID spares cannot be removed")); - return (zfs_error(hdl, EZFS_NODEVICE, errbuf)); - } - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, - &islog)) == NULL) - return (zfs_error(hdl, EZFS_NODEVICE, errbuf)); - - version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); - if (islog && version < SPA_VERSION_HOLES) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "pool must be upgraded to support log removal")); - return (zfs_error(hdl, EZFS_BADVERSION, errbuf)); - } - - zc.zc_guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID); - - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_REMOVE, &zc) == 0) - return (0); - - switch (errno) { - - case EALREADY: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "removal for this vdev is already in progress.")); - (void) zfs_error(hdl, EZFS_BUSY, errbuf); - break; - - case EINVAL: - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "invalid config; all top-level vdevs must " - "have the same sector size and not be raidz.")); - (void) zfs_error(hdl, EZFS_INVALCONFIG, errbuf); - break; - - case EBUSY: - if (islog) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Mount encrypted datasets to replay logs.")); - } else { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Pool busy; removal may already be in progress")); - } - (void) zfs_error(hdl, EZFS_BUSY, errbuf); - break; - - case EACCES: - if (islog) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "Mount encrypted datasets to replay logs.")); - (void) zfs_error(hdl, EZFS_BUSY, errbuf); - } else { - (void) zpool_standard_error(hdl, errno, errbuf); - } - break; - - default: - (void) zpool_standard_error(hdl, errno, errbuf); - } - return (-1); -} - -int -zpool_vdev_remove_cancel(zpool_handle_t *zhp) -{ - zfs_cmd_t zc = {{0}}; - char errbuf[ERRBUFLEN]; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot cancel removal")); - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - zc.zc_cookie = 1; - - if (zfs_ioctl(hdl, ZFS_IOC_VDEV_REMOVE, &zc) == 0) - return (0); - - return (zpool_standard_error(hdl, errno, errbuf)); -} - -int -zpool_vdev_indirect_size(zpool_handle_t *zhp, const char *path, - uint64_t *sizep) -{ - char errbuf[ERRBUFLEN]; - nvlist_t *tgt; - boolean_t avail_spare, l2cache, islog; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot determine indirect size of %s"), - path); - - if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache, - &islog)) == NULL) - return (zfs_error(hdl, EZFS_NODEVICE, errbuf)); - - if (avail_spare || l2cache || islog) { - *sizep = 0; - return (0); - } - - if (nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_INDIRECT_SIZE, sizep) != 0) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "indirect size not available")); - return (zfs_error(hdl, EINVAL, errbuf)); - } - return (0); -} - -/* - * Clear the errors for the pool, or the particular device if specified. - */ -int -zpool_clear(zpool_handle_t *zhp, const char *path, nvlist_t *rewindnvl) -{ - zfs_cmd_t zc = {"\0"}; - char errbuf[ERRBUFLEN]; - nvlist_t *tgt; - zpool_load_policy_t policy; - boolean_t avail_spare, l2cache; - libzfs_handle_t *hdl = zhp->zpool_hdl; - nvlist_t *nvi = NULL; - int error; - - if (path) - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot clear errors for %s"), - path); - else - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot clear errors for %s"), - zhp->zpool_name); - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - if (path) { - if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, - &l2cache, NULL)) == NULL) - return (zfs_error(hdl, EZFS_NODEVICE, errbuf)); - - /* - * Don't allow error clearing for hot spares. Do allow - * error clearing for l2cache devices. - */ - if (avail_spare) - return (zfs_error(hdl, EZFS_ISSPARE, errbuf)); - - zc.zc_guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID); - } - - zpool_get_load_policy(rewindnvl, &policy); - zc.zc_cookie = policy.zlp_rewind; - - zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size * 2); - zcmd_write_src_nvlist(hdl, &zc, rewindnvl); - - while ((error = zfs_ioctl(hdl, ZFS_IOC_CLEAR, &zc)) != 0 && - errno == ENOMEM) - zcmd_expand_dst_nvlist(hdl, &zc); - - if (!error || ((policy.zlp_rewind & ZPOOL_TRY_REWIND) && - errno != EPERM && errno != EACCES)) { - if (policy.zlp_rewind & - (ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) { - (void) zcmd_read_dst_nvlist(hdl, &zc, &nvi); - zpool_rewind_exclaim(hdl, zc.zc_name, - ((policy.zlp_rewind & ZPOOL_TRY_REWIND) != 0), - nvi); - nvlist_free(nvi); - } - zcmd_free_nvlists(&zc); - return (0); - } - - zcmd_free_nvlists(&zc); - return (zpool_standard_error(hdl, errno, errbuf)); -} - -/* - * Similar to zpool_clear(), but takes a GUID (used by fmd). - */ -int -zpool_vdev_clear(zpool_handle_t *zhp, uint64_t guid) -{ - zfs_cmd_t zc = {"\0"}; - char errbuf[ERRBUFLEN]; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot clear errors for %llx"), - (u_longlong_t)guid); - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - zc.zc_guid = guid; - zc.zc_cookie = ZPOOL_NO_REWIND; - - if (zfs_ioctl(hdl, ZFS_IOC_CLEAR, &zc) == 0) - return (0); - - return (zpool_standard_error(hdl, errno, errbuf)); -} - -/* - * 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_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; - - 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)); - 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); -} - -/* - * Change the GUID for a pool. - */ -int -zpool_reguid(zpool_handle_t *zhp) -{ - return (zpool_set_guid(zhp, NULL)); -} - -/* - * Reopen the pool. - */ -int -zpool_reopen_one(zpool_handle_t *zhp, void *data) -{ - libzfs_handle_t *hdl = zpool_get_handle(zhp); - const char *pool_name = zpool_get_name(zhp); - boolean_t *scrub_restart = data; - int error; - - error = lzc_reopen(pool_name, *scrub_restart); - if (error) { - return (zpool_standard_error_fmt(hdl, error, - dgettext(TEXT_DOMAIN, "cannot reopen '%s'"), pool_name)); - } - - return (0); -} - -/* call into libzfs_core to execute the sync IOCTL per pool */ -int -zpool_sync_one(zpool_handle_t *zhp, void *data) -{ - int ret; - libzfs_handle_t *hdl = zpool_get_handle(zhp); - const char *pool_name = zpool_get_name(zhp); - boolean_t *force = data; - nvlist_t *innvl = fnvlist_alloc(); - - fnvlist_add_boolean_value(innvl, "force", *force); - if ((ret = lzc_sync(pool_name, innvl, NULL)) != 0) { - nvlist_free(innvl); - return (zpool_standard_error_fmt(hdl, ret, - dgettext(TEXT_DOMAIN, "sync '%s' failed"), pool_name)); - } - nvlist_free(innvl); - - return (0); -} - -#define PATH_BUF_LEN 64 - -/* - * Given a vdev, return the name to display in iostat. If the vdev has a path, - * we use that, stripping off any leading "/dev/dsk/"; if not, we use the type. - * We also check if this is a whole disk, in which case we strip off the - * trailing 's0' slice name. - * - * This routine is also responsible for identifying when disks have been - * reconfigured in a new location. The kernel will have opened the device by - * devid, but the path will still refer to the old location. To catch this, we - * first do a path -> devid translation (which is fast for the common case). If - * the devid matches, we're done. If not, we do a reverse devid -> path - * translation and issue the appropriate ioctl() to update the path of the vdev. - * If 'zhp' is NULL, then this is an exported pool, and we don't need to do any - * of these checks. - */ -char * -zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv, - int name_flags) -{ - const char *type, *tpath; - const char *path; - uint64_t value; - char buf[PATH_BUF_LEN]; - char tmpbuf[PATH_BUF_LEN * 2]; - - /* - * vdev_name will be "root"/"root-0" for the root vdev, but it is the - * zpool name that will be displayed to the user. - */ - type = fnvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE); - if (zhp != NULL && strcmp(type, "root") == 0) - return (zfs_strdup(hdl, zpool_get_name(zhp))); - - if (libzfs_envvar_is_set("ZPOOL_VDEV_NAME_PATH")) - name_flags |= VDEV_NAME_PATH; - if (libzfs_envvar_is_set("ZPOOL_VDEV_NAME_GUID")) - name_flags |= VDEV_NAME_GUID; - if (libzfs_envvar_is_set("ZPOOL_VDEV_NAME_FOLLOW_LINKS")) - name_flags |= VDEV_NAME_FOLLOW_LINKS; - - if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, &value) == 0 || - name_flags & VDEV_NAME_GUID) { - (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &value); - (void) snprintf(buf, sizeof (buf), "%llu", (u_longlong_t)value); - path = buf; - } else if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &tpath) == 0) { - path = tpath; - - if (name_flags & VDEV_NAME_FOLLOW_LINKS) { - char *rp = realpath(path, NULL); - if (rp) { - strlcpy(buf, rp, sizeof (buf)); - path = buf; - free(rp); - } - } - - /* - * For a block device only use the name. - */ - if ((strcmp(type, VDEV_TYPE_DISK) == 0) && - !(name_flags & VDEV_NAME_PATH)) { - path = zfs_strip_path(path); - } - - /* - * Remove the partition from the path if this is a whole disk. - */ - if (strcmp(type, VDEV_TYPE_DRAID_SPARE) != 0 && - nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, &value) - == 0 && value && !(name_flags & VDEV_NAME_PATH)) { - return (zfs_strip_partition(path)); - } - } else { - path = type; - - /* - * If it's a raidz device, we need to stick in the parity level. - */ - if (strcmp(path, VDEV_TYPE_RAIDZ) == 0) { - value = fnvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY); - (void) snprintf(buf, sizeof (buf), "%s%llu", path, - (u_longlong_t)value); - path = buf; - } - - /* - * If it's a dRAID device, we add parity, groups, and spares. - */ - if (strcmp(path, VDEV_TYPE_DRAID) == 0) { - uint64_t ndata, nparity, nspares; - nvlist_t **child; - uint_t children; - - verify(nvlist_lookup_nvlist_array(nv, - ZPOOL_CONFIG_CHILDREN, &child, &children) == 0); - nparity = fnvlist_lookup_uint64(nv, - ZPOOL_CONFIG_NPARITY); - ndata = fnvlist_lookup_uint64(nv, - ZPOOL_CONFIG_DRAID_NDATA); - nspares = fnvlist_lookup_uint64(nv, - ZPOOL_CONFIG_DRAID_NSPARES); - - path = zpool_draid_name(buf, sizeof (buf), ndata, - nparity, nspares, children); - } - - /* - * We identify each top-level vdev by using a <type-id> - * naming convention. - */ - if (name_flags & VDEV_NAME_TYPE_ID) { - uint64_t id = fnvlist_lookup_uint64(nv, - ZPOOL_CONFIG_ID); - (void) snprintf(tmpbuf, sizeof (tmpbuf), "%s-%llu", - path, (u_longlong_t)id); - path = tmpbuf; - } - } - - return (zfs_strdup(hdl, path)); -} - -static int -zbookmark_mem_compare(const void *a, const void *b) -{ - return (memcmp(a, b, sizeof (zbookmark_phys_t))); -} - -void -zpool_add_propname(zpool_handle_t *zhp, const char *propname) -{ - assert(zhp->zpool_n_propnames < ZHP_MAX_PROPNAMES); - zhp->zpool_propnames[zhp->zpool_n_propnames] = propname; - zhp->zpool_n_propnames++; -} - -/* - * Retrieve the persistent error log, uniquify the members, and return to the - * caller. - */ -int -zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp) -{ - zfs_cmd_t zc = {"\0"}; - libzfs_handle_t *hdl = zhp->zpool_hdl; - zbookmark_phys_t *buf; - uint64_t buflen = 10000; /* approx. 1MB of RAM */ - - if (fnvlist_lookup_uint64(zhp->zpool_config, - ZPOOL_CONFIG_ERRCOUNT) == 0) - return (0); - - /* - * Retrieve the raw error list from the kernel. If it doesn't fit, - * allocate a larger buffer and retry. - */ - (void) strcpy(zc.zc_name, zhp->zpool_name); - for (;;) { - buf = zfs_alloc(zhp->zpool_hdl, - buflen * sizeof (zbookmark_phys_t)); - zc.zc_nvlist_dst = (uintptr_t)buf; - zc.zc_nvlist_dst_size = buflen; - if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_ERROR_LOG, - &zc) != 0) { - free(buf); - if (errno == ENOMEM) { - buflen *= 2; - } else { - return (zpool_standard_error_fmt(hdl, errno, - dgettext(TEXT_DOMAIN, "errors: List of " - "errors unavailable"))); - } - } else { - break; - } - } - - /* - * Sort the resulting bookmarks. This is a little confusing due to the - * implementation of ZFS_IOC_ERROR_LOG. The bookmarks are copied last - * to first, and 'zc_nvlist_dst_size' indicates the number of bookmarks - * _not_ copied as part of the process. So we point the start of our - * array appropriate and decrement the total number of elements. - */ - zbookmark_phys_t *zb = buf + zc.zc_nvlist_dst_size; - uint64_t zblen = buflen - zc.zc_nvlist_dst_size; - - qsort(zb, zblen, sizeof (zbookmark_phys_t), zbookmark_mem_compare); - - verify(nvlist_alloc(nverrlistp, 0, KM_SLEEP) == 0); - - /* - * Fill in the nverrlistp with nvlist's of dataset and object numbers. - */ - for (uint64_t i = 0; i < zblen; i++) { - nvlist_t *nv; - - /* ignoring zb_blkid and zb_level for now */ - if (i > 0 && zb[i-1].zb_objset == zb[i].zb_objset && - zb[i-1].zb_object == zb[i].zb_object) - continue; - - if (nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) != 0) - goto nomem; - if (nvlist_add_uint64(nv, ZPOOL_ERR_DATASET, - zb[i].zb_objset) != 0) { - nvlist_free(nv); - goto nomem; - } - if (nvlist_add_uint64(nv, ZPOOL_ERR_OBJECT, - zb[i].zb_object) != 0) { - nvlist_free(nv); - goto nomem; - } - if (nvlist_add_nvlist(*nverrlistp, "ejk", nv) != 0) { - nvlist_free(nv); - goto nomem; - } - nvlist_free(nv); - } - - free(buf); - return (0); - -nomem: - free(buf); - return (no_memory(zhp->zpool_hdl)); -} - -/* - * Upgrade a ZFS pool to the latest on-disk version. - */ -int -zpool_upgrade(zpool_handle_t *zhp, uint64_t new_version) -{ - zfs_cmd_t zc = {"\0"}; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) strcpy(zc.zc_name, zhp->zpool_name); - zc.zc_cookie = new_version; - - if (zfs_ioctl(hdl, ZFS_IOC_POOL_UPGRADE, &zc) != 0) - return (zpool_standard_error_fmt(hdl, errno, - dgettext(TEXT_DOMAIN, "cannot upgrade '%s'"), - zhp->zpool_name)); - return (0); -} - -void -zfs_save_arguments(int argc, char **argv, char *string, int len) -{ - int i; - - (void) strlcpy(string, zfs_basename(argv[0]), len); - for (i = 1; i < argc; i++) { - (void) strlcat(string, " ", len); - (void) strlcat(string, argv[i], len); - } -} - -int -zpool_log_history(libzfs_handle_t *hdl, const char *message) -{ - zfs_cmd_t zc = {"\0"}; - nvlist_t *args; - - args = fnvlist_alloc(); - fnvlist_add_string(args, "message", message); - zcmd_write_src_nvlist(hdl, &zc, args); - int err = zfs_ioctl(hdl, ZFS_IOC_LOG_HISTORY, &zc); - nvlist_free(args); - zcmd_free_nvlists(&zc); - return (err); -} - -/* - * Perform ioctl to get some command history of a pool. - * - * 'buf' is the buffer to fill up to 'len' bytes. 'off' is the - * logical offset of the history buffer to start reading from. - * - * Upon return, 'off' is the next logical offset to read from and - * 'len' is the actual amount of bytes read into 'buf'. - */ -static int -get_history(zpool_handle_t *zhp, char *buf, uint64_t *off, uint64_t *len) -{ - zfs_cmd_t zc = {"\0"}; - libzfs_handle_t *hdl = zhp->zpool_hdl; - - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - - zc.zc_history = (uint64_t)(uintptr_t)buf; - zc.zc_history_len = *len; - zc.zc_history_offset = *off; - - if (zfs_ioctl(hdl, ZFS_IOC_POOL_GET_HISTORY, &zc) != 0) { - switch (errno) { - case EPERM: - return (zfs_error_fmt(hdl, EZFS_PERM, - dgettext(TEXT_DOMAIN, - "cannot show history for pool '%s'"), - zhp->zpool_name)); - case ENOENT: - return (zfs_error_fmt(hdl, EZFS_NOHISTORY, - dgettext(TEXT_DOMAIN, "cannot get history for pool " - "'%s'"), zhp->zpool_name)); - case ENOTSUP: - return (zfs_error_fmt(hdl, EZFS_BADVERSION, - dgettext(TEXT_DOMAIN, "cannot get history for pool " - "'%s', pool must be upgraded"), zhp->zpool_name)); - default: - return (zpool_standard_error_fmt(hdl, errno, - dgettext(TEXT_DOMAIN, - "cannot get history for '%s'"), zhp->zpool_name)); - } - } - - *len = zc.zc_history_len; - *off = zc.zc_history_offset; - - return (0); -} - -/* - * Retrieve the command history of a pool. - */ -int -zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp, uint64_t *off, - boolean_t *eof) -{ - libzfs_handle_t *hdl = zhp->zpool_hdl; - char *buf; - int buflen = 128 * 1024; - nvlist_t **records = NULL; - uint_t numrecords = 0; - int err = 0, i; - uint64_t start = *off; - - buf = zfs_alloc(hdl, buflen); - - /* process about 1MiB a time */ - while (*off - start < 1024 * 1024) { - uint64_t bytes_read = buflen; - uint64_t leftover; - - if ((err = get_history(zhp, buf, off, &bytes_read)) != 0) - break; - - /* if nothing else was read in, we're at EOF, just return */ - if (!bytes_read) { - *eof = B_TRUE; - break; - } - - if ((err = zpool_history_unpack(buf, bytes_read, - &leftover, &records, &numrecords)) != 0) { - zpool_standard_error_fmt(hdl, err, - dgettext(TEXT_DOMAIN, - "cannot get history for '%s'"), zhp->zpool_name); - break; - } - *off -= leftover; - if (leftover == bytes_read) { - /* - * no progress made, because buffer is not big enough - * to hold this record; resize and retry. - */ - buflen *= 2; - free(buf); - buf = zfs_alloc(hdl, buflen); - } - } - - free(buf); - - if (!err) { - *nvhisp = fnvlist_alloc(); - fnvlist_add_nvlist_array(*nvhisp, ZPOOL_HIST_RECORD, - (const nvlist_t **)records, numrecords); - } - for (i = 0; i < numrecords; i++) - nvlist_free(records[i]); - free(records); - - return (err); -} - -/* - * Retrieve the next event given the passed 'zevent_fd' file descriptor. - * If there is a new event available 'nvp' will contain a newly allocated - * nvlist and 'dropped' will be set to the number of missed events since - * the last call to this function. When 'nvp' is set to NULL it indicates - * no new events are available. In either case the function returns 0 and - * it is up to the caller to free 'nvp'. In the case of a fatal error the - * function will return a non-zero value. When the function is called in - * blocking mode (the default, unless the ZEVENT_NONBLOCK flag is passed), - * it will not return until a new event is available. - */ -int -zpool_events_next(libzfs_handle_t *hdl, nvlist_t **nvp, - int *dropped, unsigned flags, int zevent_fd) -{ - zfs_cmd_t zc = {"\0"}; - int error = 0; - - *nvp = NULL; - *dropped = 0; - zc.zc_cleanup_fd = zevent_fd; - - if (flags & ZEVENT_NONBLOCK) - zc.zc_guid = ZEVENT_NONBLOCK; - - zcmd_alloc_dst_nvlist(hdl, &zc, ZEVENT_SIZE); - -retry: - if (zfs_ioctl(hdl, ZFS_IOC_EVENTS_NEXT, &zc) != 0) { - switch (errno) { - case ESHUTDOWN: - error = zfs_error_fmt(hdl, EZFS_POOLUNAVAIL, - dgettext(TEXT_DOMAIN, "zfs shutdown")); - goto out; - case ENOENT: - /* Blocking error case should not occur */ - if (!(flags & ZEVENT_NONBLOCK)) - error = zpool_standard_error_fmt(hdl, errno, - dgettext(TEXT_DOMAIN, "cannot get event")); - - goto out; - case ENOMEM: - zcmd_expand_dst_nvlist(hdl, &zc); - goto retry; - default: - error = zpool_standard_error_fmt(hdl, errno, - dgettext(TEXT_DOMAIN, "cannot get event")); - goto out; - } - } - - error = zcmd_read_dst_nvlist(hdl, &zc, nvp); - if (error != 0) - goto out; - - *dropped = (int)zc.zc_cookie; -out: - zcmd_free_nvlists(&zc); - - return (error); -} - -/* - * Clear all events. - */ -int -zpool_events_clear(libzfs_handle_t *hdl, int *count) -{ - zfs_cmd_t zc = {"\0"}; - - if (zfs_ioctl(hdl, ZFS_IOC_EVENTS_CLEAR, &zc) != 0) - return (zpool_standard_error(hdl, errno, - dgettext(TEXT_DOMAIN, "cannot clear events"))); - - if (count != NULL) - *count = (int)zc.zc_cookie; /* # of events cleared */ - - return (0); -} - -/* - * Seek to a specific EID, ZEVENT_SEEK_START, or ZEVENT_SEEK_END for - * the passed zevent_fd file handle. On success zero is returned, - * otherwise -1 is returned and hdl->libzfs_error is set to the errno. - */ -int -zpool_events_seek(libzfs_handle_t *hdl, uint64_t eid, int zevent_fd) -{ - zfs_cmd_t zc = {"\0"}; - int error = 0; - - zc.zc_guid = eid; - zc.zc_cleanup_fd = zevent_fd; - - if (zfs_ioctl(hdl, ZFS_IOC_EVENTS_SEEK, &zc) != 0) { - switch (errno) { - case ENOENT: - error = zfs_error_fmt(hdl, EZFS_NOENT, - dgettext(TEXT_DOMAIN, "cannot get event")); - break; - - case ENOMEM: - error = zfs_error_fmt(hdl, EZFS_NOMEM, - dgettext(TEXT_DOMAIN, "cannot get event")); - break; - - default: - error = zpool_standard_error_fmt(hdl, errno, - dgettext(TEXT_DOMAIN, "cannot get event")); - break; - } - } - - return (error); -} - -static void -zpool_obj_to_path_impl(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, - char *pathname, size_t len, boolean_t always_unmounted) -{ - zfs_cmd_t zc = {"\0"}; - boolean_t mounted = B_FALSE; - char *mntpnt = NULL; - char dsname[ZFS_MAX_DATASET_NAME_LEN]; - - if (dsobj == 0) { - /* special case for the MOS */ - (void) snprintf(pathname, len, "<metadata>:<0x%llx>", - (longlong_t)obj); - return; - } - - /* get the dataset's name */ - (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name)); - zc.zc_obj = dsobj; - if (zfs_ioctl(zhp->zpool_hdl, - ZFS_IOC_DSOBJ_TO_DSNAME, &zc) != 0) { - /* just write out a path of two object numbers */ - (void) snprintf(pathname, len, "<0x%llx>:<0x%llx>", - (longlong_t)dsobj, (longlong_t)obj); - return; - } - (void) strlcpy(dsname, zc.zc_value, sizeof (dsname)); - - /* find out if the dataset is mounted */ - mounted = !always_unmounted && is_mounted(zhp->zpool_hdl, dsname, - &mntpnt); - - /* get the corrupted object's path */ - (void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name)); - zc.zc_obj = obj; - if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_OBJ_TO_PATH, - &zc) == 0) { - if (mounted) { - (void) snprintf(pathname, len, "%s%s", mntpnt, - zc.zc_value); - } else { - (void) snprintf(pathname, len, "%s:%s", - dsname, zc.zc_value); - } - } else { - (void) snprintf(pathname, len, "%s:<0x%llx>", dsname, - (longlong_t)obj); - } - free(mntpnt); -} - -void -zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, - char *pathname, size_t len) -{ - zpool_obj_to_path_impl(zhp, dsobj, obj, pathname, len, B_FALSE); -} - -void -zpool_obj_to_path_ds(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, - char *pathname, size_t len) -{ - zpool_obj_to_path_impl(zhp, dsobj, obj, pathname, len, B_TRUE); -} -/* - * Wait while the specified activity is in progress in the pool. - */ -int -zpool_wait(zpool_handle_t *zhp, zpool_wait_activity_t activity) -{ - boolean_t missing; - - int error = zpool_wait_status(zhp, activity, &missing, NULL); - - if (missing) { - (void) zpool_standard_error_fmt(zhp->zpool_hdl, ENOENT, - dgettext(TEXT_DOMAIN, "error waiting in pool '%s'"), - zhp->zpool_name); - return (ENOENT); - } else { - return (error); - } -} - -/* - * Wait for the given activity and return the status of the wait (whether or not - * any waiting was done) in the 'waited' parameter. Non-existent pools are - * reported via the 'missing' parameter, rather than by printing an error - * message. This is convenient when this function is called in a loop over a - * long period of time (as it is, for example, by zpool's wait cmd). In that - * scenario, a pool being exported or destroyed should be considered a normal - * event, so we don't want to print an error when we find that the pool doesn't - * exist. - */ -int -zpool_wait_status(zpool_handle_t *zhp, zpool_wait_activity_t activity, - boolean_t *missing, boolean_t *waited) -{ - int error = lzc_wait(zhp->zpool_name, activity, waited); - *missing = (error == ENOENT); - if (*missing) - return (0); - - if (error != 0) { - (void) zpool_standard_error_fmt(zhp->zpool_hdl, error, - dgettext(TEXT_DOMAIN, "error waiting in pool '%s'"), - zhp->zpool_name); - } - - return (error); -} - -int -zpool_set_bootenv(zpool_handle_t *zhp, const nvlist_t *envmap) -{ - int error = lzc_set_bootenv(zhp->zpool_name, envmap); - if (error != 0) { - (void) zpool_standard_error_fmt(zhp->zpool_hdl, error, - dgettext(TEXT_DOMAIN, - "error setting bootenv in pool '%s'"), zhp->zpool_name); - } - - return (error); -} - -int -zpool_get_bootenv(zpool_handle_t *zhp, nvlist_t **nvlp) -{ - nvlist_t *nvl; - int error; - - nvl = NULL; - error = lzc_get_bootenv(zhp->zpool_name, &nvl); - if (error != 0) { - (void) zpool_standard_error_fmt(zhp->zpool_hdl, error, - dgettext(TEXT_DOMAIN, - "error getting bootenv in pool '%s'"), zhp->zpool_name); - } else { - *nvlp = nvl; - } - - return (error); -} - -/* - * Attempt to read and parse feature file(s) (from "compatibility" property). - * Files contain zpool feature names, comma or whitespace-separated. - * Comments (# character to next newline) are discarded. - * - * Arguments: - * compatibility : string containing feature filenames - * features : either NULL or pointer to array of boolean - * report : either NULL or pointer to string buffer - * rlen : length of "report" buffer - * - * compatibility is NULL (unset), "", "off", "legacy", or list of - * comma-separated filenames. filenames should either be absolute, - * or relative to: - * 1) ZPOOL_SYSCONF_COMPAT_D (eg: /etc/zfs/compatibility.d) or - * 2) ZPOOL_DATA_COMPAT_D (eg: /usr/share/zfs/compatibility.d). - * (Unset), "" or "off" => enable all features - * "legacy" => disable all features - * - * Any feature names read from files which match unames in spa_feature_table - * will have the corresponding boolean set in the features array (if non-NULL). - * If more than one feature set specified, only features present in *all* of - * them will be set. - * - * "report" if not NULL will be populated with a suitable status message. - * - * Return values: - * ZPOOL_COMPATIBILITY_OK : files read and parsed ok - * ZPOOL_COMPATIBILITY_BADFILE : file too big or not a text file - * ZPOOL_COMPATIBILITY_BADTOKEN : SYSCONF file contains invalid feature name - * ZPOOL_COMPATIBILITY_WARNTOKEN : DATA file contains invalid feature name - * ZPOOL_COMPATIBILITY_NOFILES : no feature files found - */ -zpool_compat_status_t -zpool_load_compat(const char *compat, boolean_t *features, char *report, - size_t rlen) -{ - int sdirfd, ddirfd, featfd; - struct stat fs; - char *fc; - char *ps, *ls, *ws; - char *file, *line, *word; - - char l_compat[ZFS_MAXPROPLEN]; - - boolean_t ret_nofiles = B_TRUE; - boolean_t ret_badfile = B_FALSE; - boolean_t ret_badtoken = B_FALSE; - boolean_t ret_warntoken = B_FALSE; - - /* special cases (unset), "" and "off" => enable all features */ - if (compat == NULL || compat[0] == '\0' || - strcmp(compat, ZPOOL_COMPAT_OFF) == 0) { - if (features != NULL) { - for (uint_t i = 0; i < SPA_FEATURES; i++) - features[i] = B_TRUE; - } - if (report != NULL) - strlcpy(report, gettext("all features enabled"), rlen); - return (ZPOOL_COMPATIBILITY_OK); - } - - /* Final special case "legacy" => disable all features */ - if (strcmp(compat, ZPOOL_COMPAT_LEGACY) == 0) { - if (features != NULL) - for (uint_t i = 0; i < SPA_FEATURES; i++) - features[i] = B_FALSE; - if (report != NULL) - strlcpy(report, gettext("all features disabled"), rlen); - return (ZPOOL_COMPATIBILITY_OK); - } - - /* - * Start with all true; will be ANDed with results from each file - */ - if (features != NULL) - for (uint_t i = 0; i < SPA_FEATURES; i++) - features[i] = B_TRUE; - - char err_badfile[ZFS_MAXPROPLEN] = ""; - char err_badtoken[ZFS_MAXPROPLEN] = ""; - - /* - * We ignore errors from the directory open() - * as they're only needed if the filename is relative - * which will be checked during the openat(). - */ - -/* O_PATH safer than O_RDONLY if system allows it */ -#if defined(O_PATH) -#define ZC_DIR_FLAGS (O_DIRECTORY | O_CLOEXEC | O_PATH) -#else -#define ZC_DIR_FLAGS (O_DIRECTORY | O_CLOEXEC | O_RDONLY) -#endif - - sdirfd = open(ZPOOL_SYSCONF_COMPAT_D, ZC_DIR_FLAGS); - ddirfd = open(ZPOOL_DATA_COMPAT_D, ZC_DIR_FLAGS); - - (void) strlcpy(l_compat, compat, ZFS_MAXPROPLEN); - - for (file = strtok_r(l_compat, ",", &ps); - file != NULL; - file = strtok_r(NULL, ",", &ps)) { - - boolean_t l_features[SPA_FEATURES]; - - enum { Z_SYSCONF, Z_DATA } source; - - /* try sysconfdir first, then datadir */ - source = Z_SYSCONF; - if ((featfd = openat(sdirfd, file, O_RDONLY | O_CLOEXEC)) < 0) { - featfd = openat(ddirfd, file, O_RDONLY | O_CLOEXEC); - source = Z_DATA; - } - - /* File readable and correct size? */ - if (featfd < 0 || - fstat(featfd, &fs) < 0 || - fs.st_size < 1 || - fs.st_size > ZPOOL_COMPAT_MAXSIZE) { - (void) close(featfd); - strlcat(err_badfile, file, ZFS_MAXPROPLEN); - strlcat(err_badfile, " ", ZFS_MAXPROPLEN); - ret_badfile = B_TRUE; - continue; - } - -/* Prefault the file if system allows */ -#if defined(MAP_POPULATE) -#define ZC_MMAP_FLAGS (MAP_PRIVATE | MAP_POPULATE) -#elif defined(MAP_PREFAULT_READ) -#define ZC_MMAP_FLAGS (MAP_PRIVATE | MAP_PREFAULT_READ) -#else -#define ZC_MMAP_FLAGS (MAP_PRIVATE) -#endif - - /* private mmap() so we can strtok safely */ - fc = (char *)mmap(NULL, fs.st_size, PROT_READ | PROT_WRITE, - ZC_MMAP_FLAGS, featfd, 0); - (void) close(featfd); - - /* map ok, and last character == newline? */ - if (fc == MAP_FAILED || fc[fs.st_size - 1] != '\n') { - (void) munmap((void *) fc, fs.st_size); - strlcat(err_badfile, file, ZFS_MAXPROPLEN); - strlcat(err_badfile, " ", ZFS_MAXPROPLEN); - ret_badfile = B_TRUE; - continue; - } - - ret_nofiles = B_FALSE; - - for (uint_t i = 0; i < SPA_FEATURES; i++) - l_features[i] = B_FALSE; - - /* replace final newline with NULL to ensure string ends */ - fc[fs.st_size - 1] = '\0'; - - for (line = strtok_r(fc, "\n", &ls); - line != NULL; - line = strtok_r(NULL, "\n", &ls)) { - /* discard comments */ - char *r = strchr(line, '#'); - if (r != NULL) - *r = '\0'; - - for (word = strtok_r(line, ", \t", &ws); - word != NULL; - word = strtok_r(NULL, ", \t", &ws)) { - /* Find matching feature name */ - uint_t f; - for (f = 0; f < SPA_FEATURES; f++) { - zfeature_info_t *fi = - &spa_feature_table[f]; - if (strcmp(word, fi->fi_uname) == 0) { - l_features[f] = B_TRUE; - break; - } - } - if (f < SPA_FEATURES) - continue; - - /* found an unrecognized word */ - /* lightly sanitize it */ - if (strlen(word) > 32) - word[32] = '\0'; - for (char *c = word; *c != '\0'; c++) - if (!isprint(*c)) - *c = '?'; - - strlcat(err_badtoken, word, ZFS_MAXPROPLEN); - strlcat(err_badtoken, " ", ZFS_MAXPROPLEN); - if (source == Z_SYSCONF) - ret_badtoken = B_TRUE; - else - ret_warntoken = B_TRUE; - } - } - (void) munmap((void *) fc, fs.st_size); - - if (features != NULL) - for (uint_t i = 0; i < SPA_FEATURES; i++) - features[i] &= l_features[i]; - } - (void) close(sdirfd); - (void) close(ddirfd); - - /* Return the most serious error */ - if (ret_badfile) { - if (report != NULL) - snprintf(report, rlen, gettext("could not read/" - "parse feature file(s): %s"), err_badfile); - return (ZPOOL_COMPATIBILITY_BADFILE); - } - if (ret_nofiles) { - if (report != NULL) - strlcpy(report, - gettext("no valid compatibility files specified"), - rlen); - return (ZPOOL_COMPATIBILITY_NOFILES); - } - if (ret_badtoken) { - if (report != NULL) - snprintf(report, rlen, gettext("invalid feature " - "name(s) in local compatibility files: %s"), - err_badtoken); - return (ZPOOL_COMPATIBILITY_BADTOKEN); - } - if (ret_warntoken) { - if (report != NULL) - snprintf(report, rlen, gettext("unrecognized feature " - "name(s) in distribution compatibility files: %s"), - err_badtoken); - return (ZPOOL_COMPATIBILITY_WARNTOKEN); - } - if (report != NULL) - strlcpy(report, gettext("compatibility set ok"), rlen); - return (ZPOOL_COMPATIBILITY_OK); -} - -static int -zpool_vdev_guid(zpool_handle_t *zhp, const char *vdevname, uint64_t *vdev_guid) -{ - nvlist_t *tgt; - boolean_t avail_spare, l2cache; - - verify(zhp != NULL); - if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { - char errbuf[ERRBUFLEN]; - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "pool is in an unavailable state")); - return (zfs_error(zhp->zpool_hdl, EZFS_POOLUNAVAIL, errbuf)); - } - - if ((tgt = zpool_find_vdev(zhp, vdevname, &avail_spare, &l2cache, - NULL)) == NULL) { - char errbuf[ERRBUFLEN]; - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "can not find %s in %s"), - vdevname, zhp->zpool_name); - return (zfs_error(zhp->zpool_hdl, EZFS_NODEVICE, errbuf)); - } - - *vdev_guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID); - return (0); -} - -/* - * Get a vdev property value for 'prop' and return the value in - * a pre-allocated buffer. - */ -int -zpool_get_vdev_prop_value(nvlist_t *nvprop, vdev_prop_t prop, char *prop_name, - char *buf, size_t len, zprop_source_t *srctype, boolean_t literal) -{ - nvlist_t *nv; - const char *strval; - uint64_t intval; - zprop_source_t src = ZPROP_SRC_NONE; - - if (prop == VDEV_PROP_USERPROP) { - /* user property, prop_name must contain the property name */ - assert(prop_name != NULL); - if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) { - src = fnvlist_lookup_uint64(nv, ZPROP_SOURCE); - strval = fnvlist_lookup_string(nv, ZPROP_VALUE); - } else { - /* user prop not found */ - src = ZPROP_SRC_DEFAULT; - strval = "-"; - } - (void) strlcpy(buf, strval, len); - if (srctype) - *srctype = src; - return (0); - } - - if (prop_name == NULL) - prop_name = (char *)vdev_prop_to_name(prop); - - switch (vdev_prop_get_type(prop)) { - case PROP_TYPE_STRING: - if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) { - src = fnvlist_lookup_uint64(nv, ZPROP_SOURCE); - strval = fnvlist_lookup_string(nv, ZPROP_VALUE); - } else { - src = ZPROP_SRC_DEFAULT; - if ((strval = vdev_prop_default_string(prop)) == NULL) - strval = "-"; - } - (void) strlcpy(buf, strval, len); - break; - - case PROP_TYPE_NUMBER: - if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) { - src = fnvlist_lookup_uint64(nv, ZPROP_SOURCE); - intval = fnvlist_lookup_uint64(nv, ZPROP_VALUE); - } else { - src = ZPROP_SRC_DEFAULT; - intval = vdev_prop_default_numeric(prop); - } - - switch (prop) { - case VDEV_PROP_ASIZE: - case VDEV_PROP_PSIZE: - case VDEV_PROP_SIZE: - case VDEV_PROP_BOOTSIZE: - case VDEV_PROP_ALLOCATED: - case VDEV_PROP_FREE: - case VDEV_PROP_READ_ERRORS: - case VDEV_PROP_WRITE_ERRORS: - case VDEV_PROP_CHECKSUM_ERRORS: - case VDEV_PROP_INITIALIZE_ERRORS: - case VDEV_PROP_TRIM_ERRORS: - case VDEV_PROP_SLOW_IOS: - case VDEV_PROP_OPS_NULL: - case VDEV_PROP_OPS_READ: - case VDEV_PROP_OPS_WRITE: - case VDEV_PROP_OPS_FREE: - case VDEV_PROP_OPS_CLAIM: - case VDEV_PROP_OPS_TRIM: - case VDEV_PROP_BYTES_NULL: - case VDEV_PROP_BYTES_READ: - case VDEV_PROP_BYTES_WRITE: - case VDEV_PROP_BYTES_FREE: - case VDEV_PROP_BYTES_CLAIM: - case VDEV_PROP_BYTES_TRIM: - if (literal) { - (void) snprintf(buf, len, "%llu", - (u_longlong_t)intval); - } else { - (void) zfs_nicenum(intval, buf, len); - } - break; - case VDEV_PROP_EXPANDSZ: - if (intval == 0) { - (void) strlcpy(buf, "-", len); - } else if (literal) { - (void) snprintf(buf, len, "%llu", - (u_longlong_t)intval); - } else { - (void) zfs_nicenum(intval, buf, len); - } - break; - case VDEV_PROP_CAPACITY: - if (literal) { - (void) snprintf(buf, len, "%llu", - (u_longlong_t)intval); - } else { - (void) snprintf(buf, len, "%llu%%", - (u_longlong_t)intval); - } - break; - case VDEV_PROP_CHECKSUM_N: - case VDEV_PROP_CHECKSUM_T: - case VDEV_PROP_IO_N: - case VDEV_PROP_IO_T: - case VDEV_PROP_SLOW_IO_N: - case VDEV_PROP_SLOW_IO_T: - if (intval == UINT64_MAX) { - (void) strlcpy(buf, "-", len); - } else { - (void) snprintf(buf, len, "%llu", - (u_longlong_t)intval); - } - break; - case VDEV_PROP_FRAGMENTATION: - if (intval == UINT64_MAX) { - (void) strlcpy(buf, "-", len); - } else { - (void) snprintf(buf, len, "%llu%%", - (u_longlong_t)intval); - } - break; - case VDEV_PROP_STATE: - if (literal) { - (void) snprintf(buf, len, "%llu", - (u_longlong_t)intval); - } else { - (void) strlcpy(buf, zpool_state_to_name(intval, - VDEV_AUX_NONE), len); - } - break; - default: - (void) snprintf(buf, len, "%llu", - (u_longlong_t)intval); - } - break; - - case PROP_TYPE_INDEX: - if (nvlist_lookup_nvlist(nvprop, prop_name, &nv) == 0) { - src = fnvlist_lookup_uint64(nv, ZPROP_SOURCE); - intval = fnvlist_lookup_uint64(nv, ZPROP_VALUE); - } else { - /* 'trim_support' only valid for leaf vdevs */ - if (prop == VDEV_PROP_TRIM_SUPPORT) { - (void) strlcpy(buf, "-", len); - break; - } - src = ZPROP_SRC_DEFAULT; - intval = vdev_prop_default_numeric(prop); - /* Only use if provided by the RAIDZ VDEV above */ - if (prop == VDEV_PROP_RAIDZ_EXPANDING) - return (ENOENT); - if (prop == VDEV_PROP_SIT_OUT) - return (ENOENT); - } - if (vdev_prop_index_to_string(prop, intval, - (const char **)&strval) != 0) - return (-1); - (void) strlcpy(buf, strval, len); - break; - - default: - abort(); - } - - if (srctype) - *srctype = src; - - return (0); -} - -/* - * Get a vdev property value for 'prop_name' and return the value in - * a pre-allocated buffer. - */ -int -zpool_get_vdev_prop(zpool_handle_t *zhp, const char *vdevname, vdev_prop_t prop, - char *prop_name, char *buf, size_t len, zprop_source_t *srctype, - boolean_t literal) -{ - nvlist_t *reqnvl, *reqprops; - nvlist_t *retprops = NULL; - uint64_t vdev_guid = 0; - int ret; - - if ((ret = zpool_vdev_guid(zhp, vdevname, &vdev_guid)) != 0) - return (ret); - - if (nvlist_alloc(&reqnvl, NV_UNIQUE_NAME, 0) != 0) - return (no_memory(zhp->zpool_hdl)); - if (nvlist_alloc(&reqprops, NV_UNIQUE_NAME, 0) != 0) - return (no_memory(zhp->zpool_hdl)); - - fnvlist_add_uint64(reqnvl, ZPOOL_VDEV_PROPS_GET_VDEV, vdev_guid); - - if (prop != VDEV_PROP_USERPROP) { - /* prop_name overrides prop value */ - if (prop_name != NULL) - prop = vdev_name_to_prop(prop_name); - else - prop_name = (char *)vdev_prop_to_name(prop); - assert(prop < VDEV_NUM_PROPS); - } - - assert(prop_name != NULL); - if (nvlist_add_uint64(reqprops, prop_name, prop) != 0) { - nvlist_free(reqnvl); - nvlist_free(reqprops); - return (no_memory(zhp->zpool_hdl)); - } - - fnvlist_add_nvlist(reqnvl, ZPOOL_VDEV_PROPS_GET_PROPS, reqprops); - - ret = lzc_get_vdev_prop(zhp->zpool_name, reqnvl, &retprops); - - if (ret == 0) { - ret = zpool_get_vdev_prop_value(retprops, prop, prop_name, buf, - len, srctype, literal); - } else { - char errbuf[ERRBUFLEN]; - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot get vdev property %s from" - " %s in %s"), prop_name, vdevname, zhp->zpool_name); - (void) zpool_standard_error(zhp->zpool_hdl, ret, errbuf); - } - - nvlist_free(reqnvl); - nvlist_free(reqprops); - nvlist_free(retprops); - - return (ret); -} - -/* - * Get all vdev properties - */ -int -zpool_get_all_vdev_props(zpool_handle_t *zhp, const char *vdevname, - nvlist_t **outnvl) -{ - nvlist_t *nvl = NULL; - uint64_t vdev_guid = 0; - int ret; - - if ((ret = zpool_vdev_guid(zhp, vdevname, &vdev_guid)) != 0) - return (ret); - - if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) - return (no_memory(zhp->zpool_hdl)); - - fnvlist_add_uint64(nvl, ZPOOL_VDEV_PROPS_GET_VDEV, vdev_guid); - - ret = lzc_get_vdev_prop(zhp->zpool_name, nvl, outnvl); - - nvlist_free(nvl); - - if (ret) { - char errbuf[ERRBUFLEN]; - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot get vdev properties for" - " %s in %s"), vdevname, zhp->zpool_name); - (void) zpool_standard_error(zhp->zpool_hdl, errno, errbuf); - } - - return (ret); -} - -/* - * Set vdev property - */ -int -zpool_set_vdev_prop(zpool_handle_t *zhp, const char *vdevname, - const char *propname, const char *propval) -{ - int ret; - nvlist_t *nvl = NULL; - nvlist_t *outnvl = NULL; - nvlist_t *props; - nvlist_t *realprops; - prop_flags_t flags = { 0 }; - uint64_t version; - uint64_t vdev_guid; - - if ((ret = zpool_vdev_guid(zhp, vdevname, &vdev_guid)) != 0) - return (ret); - - if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) - return (no_memory(zhp->zpool_hdl)); - if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) - return (no_memory(zhp->zpool_hdl)); - - fnvlist_add_uint64(nvl, ZPOOL_VDEV_PROPS_SET_VDEV, vdev_guid); - - if (nvlist_add_string(props, propname, propval) != 0) { - nvlist_free(props); - return (no_memory(zhp->zpool_hdl)); - } - - char errbuf[ERRBUFLEN]; - (void) snprintf(errbuf, sizeof (errbuf), - dgettext(TEXT_DOMAIN, "cannot set property %s for %s on %s"), - propname, vdevname, zhp->zpool_name); - - flags.vdevprop = 1; - version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); - if ((realprops = zpool_valid_proplist(zhp->zpool_hdl, - zhp->zpool_name, props, version, flags, errbuf)) == NULL) { - nvlist_free(props); - nvlist_free(nvl); - return (-1); - } - - nvlist_free(props); - props = realprops; - - fnvlist_add_nvlist(nvl, ZPOOL_VDEV_PROPS_SET_PROPS, props); - - ret = lzc_set_vdev_prop(zhp->zpool_name, nvl, &outnvl); - - nvlist_free(props); - nvlist_free(nvl); - nvlist_free(outnvl); - - if (ret) { - if (errno == ENOTSUP) { - zfs_error_aux(zhp->zpool_hdl, dgettext(TEXT_DOMAIN, - "property not supported for this vdev")); - (void) zfs_error(zhp->zpool_hdl, EZFS_PROPTYPE, errbuf); - } else { - (void) zpool_standard_error(zhp->zpool_hdl, errno, - errbuf); - } - } - - return (ret); -} - -/* - * Prune older entries from the DDT to reclaim space under the quota - */ -int -zpool_ddt_prune(zpool_handle_t *zhp, zpool_ddt_prune_unit_t unit, - uint64_t amount) -{ - int error = lzc_ddt_prune(zhp->zpool_name, unit, amount); - if (error != 0) { - libzfs_handle_t *hdl = zhp->zpool_hdl; - char errbuf[ERRBUFLEN]; - - (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN, - "cannot prune dedup table on '%s'"), zhp->zpool_name); - - if (error == EALREADY) { - zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, - "a prune operation is already in progress")); - (void) zfs_error(hdl, EZFS_BUSY, errbuf); - } else { - (void) zpool_standard_error(hdl, errno, errbuf); - } - return (-1); - } - - return (0); -} |