libzfs_pool.c revision c58b352673e88983cd2b8a388a8c7625f35e2f18
/*
* 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
* 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 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
*/
#include <ctype.h>
#include <errno.h>
#include <devid.h>
#include <fcntl.h>
#include <libintl.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/efi_partition.h>
#include <sys/zfs_ioctl.h>
#include <dlfcn.h>
#include "zfs_namecheck.h"
#include "zfs_prop.h"
#include "libzfs_impl.h"
#include "zfs_comutil.h"
#include "zfeature_common.h"
#define RDISK_ROOT "/dev/rdsk"
#define BACKUP_SLICE "s2"
typedef struct prop_flags {
} prop_flags_t;
/*
* ====================================================================
* zpool property functions
* ====================================================================
*/
static int
{
return (-1);
return (-1);
}
} else {
return (-1);
}
}
return (-1);
}
return (0);
}
static int
{
if (zpool_get_all_props(zhp) != 0)
return (-1);
return (0);
}
static char *
{
char *value;
} else {
value = "-";
}
if (src)
return (value);
}
{
/*
* 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) &&
ZPOOL_CONFIG_VDEV_TREE, &nv) == 0) &&
== 0)) {
return (value);
}
return (zpool_prop_default_numeric(prop));
}
} else {
}
if (src)
return (value);
}
/*
* Map VDEV STATE to printed strings.
*/
char *
{
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:
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"));
}
return (gettext("UNKNOWN"));
}
/*
* Get a zpool property value for 'prop' and return the value in
* a pre-allocated buffer.
*/
int
{
const char *strval;
switch (prop) {
case ZPOOL_PROP_NAME:
break;
case ZPOOL_PROP_HEALTH:
break;
case ZPOOL_PROP_GUID:
break;
case ZPOOL_PROP_ALTROOT:
case ZPOOL_PROP_CACHEFILE:
case ZPOOL_PROP_COMMENT:
zpool_get_all_props(zhp) == 0) {
len);
break;
}
/* FALLTHROUGH */
default:
break;
}
return (0);
}
prop != ZPOOL_PROP_NAME)
return (-1);
switch (zpool_prop_get_type(prop)) {
case PROP_TYPE_STRING:
len);
break;
case PROP_TYPE_NUMBER:
switch (prop) {
case ZPOOL_PROP_SIZE:
case ZPOOL_PROP_ALLOCATED:
case ZPOOL_PROP_FREE:
case ZPOOL_PROP_FREEING:
case ZPOOL_PROP_EXPANDSZ:
if (literal) {
} else {
}
break;
case ZPOOL_PROP_CAPACITY:
if (literal) {
} else {
}
break;
case ZPOOL_PROP_DEDUPRATIO:
break;
case ZPOOL_PROP_HEALTH:
ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
== 0);
break;
case ZPOOL_PROP_VERSION:
if (intval >= SPA_VERSION_FEATURES) {
break;
}
/* FALLTHROUGH */
default:
}
break;
case PROP_TYPE_INDEX:
!= 0)
return (-1);
break;
default:
abort();
}
if (srctype)
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
{
return (B_FALSE);
return (B_TRUE);
return (B_FALSE);
}
/*
* Inspect the configuration to determine if any of the devices contain
* an EFI label.
*/
static boolean_t
{
for (c = 0; c < children; c++) {
if (pool_uses_efi(child[c]))
return (B_TRUE);
}
return (B_FALSE);
}
{
char bootfs[ZPOOL_MAXNAMELEN];
sizeof (bootfs)) != 0);
}
/*
* 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 *
{
char *strval;
return (NULL);
}
int err;
if (err != 0) {
"invalid feature '%s'"), fname);
goto error;
}
"'%s' must be a string"), propname);
goto error;
}
"property '%s' can only be set to "
"'enabled'"), propname);
goto error;
}
goto error;
}
continue;
}
/*
* Make sure this property is valid and applies to this type.
*/
if (prop == ZPROP_INVAL) {
"invalid property '%s'"), propname);
goto error;
}
if (zpool_prop_readonly(prop)) {
"is readonly"), propname);
goto error;
}
goto error;
/*
* Perform additional checking for specific properties.
*/
switch (prop) {
case ZPOOL_PROP_VERSION:
"property '%s' number %d is invalid."),
goto error;
}
break;
case ZPOOL_PROP_BOOTFS:
"property '%s' cannot be set at creation "
"or import time"), propname);
goto error;
}
if (version < SPA_VERSION_BOOTFS) {
"pool must be upgraded to support "
"'%s' property"), propname);
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.
*/
strval)) {
"is an invalid name"), strval);
goto error;
}
"could not open pool '%s'"), poolname);
goto error;
}
ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
/*
* bootfs property cannot be set on a disk which has
* been EFI labeled.
*/
if (pool_uses_efi(nvroot)) {
"property '%s' not supported on "
"EFI labeled devices"), propname);
goto error;
}
break;
case ZPOOL_PROP_ALTROOT:
"property '%s' can only be set during pool "
"creation or import"), propname);
goto error;
}
if (strval[0] != '/') {
"bad alternate root '%s'"), strval);
goto error;
}
break;
case ZPOOL_PROP_CACHEFILE:
if (strval[0] == '\0')
break;
break;
if (strval[0] != '/') {
"property '%s' must be empty, an "
"absolute path, or 'none'"), propname);
goto error;
}
"'%s' is not a valid file"), strval);
goto error;
}
*slash = '\0';
if (strval[0] != '\0' &&
"'%s' is not a valid directory"),
strval);
goto error;
}
*slash = '/';
break;
case ZPOOL_PROP_COMMENT:
"comment may only have printable "
"characters"));
errbuf);
goto error;
}
}
"comment must not exceed %d characters"),
goto error;
}
break;
case ZPOOL_PROP_READONLY:
"property '%s' can only be set at "
"import time"), propname);
goto error;
}
break;
}
}
return (retprops);
return (NULL);
}
/*
* Set zpool property : propname=propval.
*/
int
{
int ret = -1;
char errbuf[1024];
prop_flags_t flags = { 0 };
zhp->zpool_name);
}
return (-1);
}
/*
* Execute the corresponding ioctl() to set this property.
*/
return (-1);
}
if (ret)
else
(void) zpool_props_refresh(zhp);
return (ret);
}
int
{
char buf[ZFS_MAXPROPLEN];
zprop_list_t **last;
return (-1);
for (int i = 0; i < SPA_FEATURES; i++) {
sizeof (zprop_list_t));
}
}
/* add any unsupported features */
char *propname;
continue;
nvpair_name(nvp));
/*
* Before adding the property to the list make sure that no
* other pool already added the same property.
*/
break;
}
}
if (found) {
continue;
}
}
continue;
}
}
return (0);
}
/*
* Get the state for the given feature on the given ZFS pool.
*/
int
{
/*
* Convert from feature name to feature guid. This conversion is
* unecessary for unsupported@... properties because they already
* use guids.
*/
if (supported) {
int ret;
if (ret != 0) {
return (ENOTSUP);
}
}
if (supported) {
if (!found) {
} else {
if (refcount == 0)
else
}
} else {
if (found) {
if (refcount == 0) {
} else {
}
} else {
return (ENOTSUP);
}
}
return (0);
}
/*
* Don't start the slice at the default block of 34; many storage
* devices will use a stripe width of 128k, so start there instead.
*/
#define NEW_START_BLOCK 256
/*
* Validate the given pool name, optionally putting an extended error message in
* 'buf'.
*/
{
char what;
int ret;
/*
* 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.
*/
return (B_FALSE);
}
if (ret != 0) {
switch (why) {
case NAME_ERR_TOOLONG:
break;
case NAME_ERR_INVALCHAR:
"'%c' in pool name"), what);
break;
case NAME_ERR_NOLETTER:
"name must begin with a letter"));
break;
case NAME_ERR_RESERVED:
"name is reserved"));
break;
case NAME_ERR_DISKLIKE:
"pool name is reserved"));
break;
case NAME_ERR_LEADING_SLASH:
"leading slash in name"));
break;
case NAME_ERR_EMPTY_COMPONENT:
"empty component in name"));
break;
case NAME_ERR_TRAILING_SLASH:
"trailing slash in name"));
break;
case NAME_ERR_MULTIPLE_AT:
"multiple '@' delimiters in name"));
break;
}
}
return (B_FALSE);
}
return (B_TRUE);
}
/*
* Open a handle to the given pool, even if the pool is currently in the FAULTED
* state.
*/
{
/*
* Make sure the pool name is valid.
*/
pool);
return (NULL);
}
return (NULL);
return (NULL);
}
if (missing) {
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
{
return (-1);
return (-1);
}
if (missing) {
return (0);
}
return (0);
}
/*
* Similar to zpool_open_canfail(), but refuses to open pools in the faulted
* state.
*/
{
return (NULL);
return (NULL);
}
return (zhp);
}
/*
* Close the handle. Simply frees the memory associated with the handle.
*/
void
{
if (zhp->zpool_config)
if (zhp->zpool_old_config)
if (zhp->zpool_props)
}
/*
* Return the name of the pool.
*/
const char *
{
return (zhp->zpool_name);
}
/*
* Return the state of the pool (ACTIVE or UNAVAILABLE)
*/
int
{
return (zhp->zpool_state);
}
/*
* 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
{
char msg[1024];
int ret = -1;
"cannot create '%s'"), pool);
return (-1);
if (props) {
goto create_failed;
}
}
if (fsprops) {
char *zonestr;
goto create_failed;
}
if (!zc_props &&
goto create_failed;
}
ZPOOL_ROOTFS_PROPS, zc_fsprops) != 0) {
goto create_failed;
}
}
goto create_failed;
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.
*/
"one or more vdevs refer to the same device"));
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];
"one or more devices is less than the "
"minimum size (%s)"), buf);
}
case ENOSPC:
"one or more devices is out of space"));
case ENOTBLK:
"cache device must be a disk or disk slice"));
default:
}
}
return (ret);
}
/*
* Destroy the given pool. It is up to the caller to ensure that there are no
* datasets left in the pool.
*/
int
{
char msg[1024];
return (-1);
"one or more devices is read only"));
} else {
}
if (zfp)
return (-1);
}
if (zfp) {
}
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
{
int ret;
char msg[1024];
"upgraded to add hot spares"));
}
uint64_t s;
for (s = 0; s < nspares; s++) {
char *path;
"device '%s' contains an EFI label and "
"cannot be used on root pools."),
B_FALSE));
}
}
}
"upgraded to add cache devices"));
}
return (-1);
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.
*/
"one or more vdevs refer to the same device"));
break;
case EOVERFLOW:
/*
* This occurrs 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];
"device is less than the minimum "
"size (%s)"), buf);
}
break;
case ENOTSUP:
"pool must be upgraded to add these vdevs"));
break;
case EDOM:
"root pool can not have multiple vdevs"
" or separate logs"));
break;
case ENOTBLK:
"cache device must be a disk or disk slice"));
break;
default:
}
ret = -1;
} else {
ret = 0;
}
return (ret);
}
/*
* Exports the pool from the system. The caller must ensure that there are no
* mounted datasets in the pool.
*/
static int
const char *log_str)
{
char msg[1024];
switch (errno) {
case EXDEV:
"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."),
msg));
default:
msg));
}
}
return (0);
}
int
{
}
int
{
}
static void
{
struct tm t;
char timestr[128];
return;
return;
}
return;
if (dryrun) {
"Would be able to return %s "
"to its state as of %s.\n"),
} else {
"Pool %s returned to its state as of %s.\n"),
}
if (loss > 120) {
"%s approximately %lld "),
"minutes of transactions.\n"));
} else if (loss > 0) {
"%s approximately %lld "),
"seconds of transactions.\n"));
}
}
}
void
{
struct tm t;
char timestr[128];
if (!hdl->libzfs_printerr)
return;
if (reason >= 0)
else
/* All attempted rewinds failed if ZPOOL_CONFIG_LOAD_TIME missing */
goto no_info;
&edata);
"Recovery is possible, but will result in some data loss.\n"));
"\tReturning the pool to its state as of %s\n"
"\tshould correct the problem. "),
timestr);
} else {
"\tReverting the pool to an earlier state "
"should correct the problem.\n\t"));
}
if (loss > 120) {
"Approximately %lld minutes of data\n"
} else if (loss > 0) {
"Approximately %lld seconds of data\n"
"\tmust be discarded, irreversibly. "), loss);
}
if (edata == 1) {
"After rewind, at least\n"
"\tone persistent user-data error will remain. "));
} else {
"After rewind, several\n"
"\tpersistent user-data errors will remain. "));
}
}
"Recovery can be attempted\n\tby executing 'zpool %s -F %s'. "),
"A scrub of the pool\n"
"\tis strongly recommended after recovery.\n"));
return;
"Destroy and re-create the pool from\n\ta backup source.\n"));
}
/*
* 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
char *altroot)
{
int ret;
newname));
}
if (nvlist_add_string(props,
newname));
}
}
if (props)
return (ret);
}
static void
int indent)
{
char *vname;
&is_log);
return;
for (c = 0; c < children; c++) {
}
}
void
{
0);
&unsup_feat) == 0);
char *desc;
else
}
}
/*
* 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
{
char *thename;
char *origname;
int ret;
int error = 0;
char errbuf[1024];
&origname) == 0);
"cannot import pool '%s'"), origname);
newname));
} else {
}
if (props) {
&version) == 0);
return (-1);
return (-1);
}
}
return (-1);
}
return (-1);
}
return (-1);
}
}
if (ret != 0)
if (error) {
char desc[1024];
/*
* Dry-run failed, but we print out what success
* looks like if we found a best txg
*/
return (-1);
}
thename);
else
switch (error) {
case ENOTSUP:
ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 &&
"pool uses the following feature(s) not "
"supported by this system:\n"));
if (nvlist_exists(nvinfo,
"All unsupported features are only "
"required for writing to the pool."
"\nThe pool can be imported using "
"'-o readonly=on'.\n"));
}
}
/*
* Unsupported version.
*/
break;
case EINVAL:
break;
case EROFS:
"one or more devices is read only"));
break;
case ENXIO:
ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 &&
ZPOOL_CONFIG_MISSING_DEVICES, &missing) == 0) {
"The devices below are missing, use "
"'-m' to import the pool anyway:\n"));
(void) printf("\n");
}
break;
case EEXIST:
break;
default:
break;
}
ret = -1;
} else {
/*
* This should never fail, but play it safe anyway.
*/
ret = -1;
if (policy.zrp_request &
(ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) {
}
return (0);
}
return (ret);
}
/*
* Scan the pool.
*/
int
{
char msg[1024];
return (0);
if (func == POOL_SCAN_SCRUB) {
} else if (func == POOL_SCAN_NONE) {
} else {
assert(!"unexpected result");
}
ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
(void) nvlist_lookup_uint64_array(nvroot,
else
} else {
}
}
/*
* This provides a very minimal check whether a given string is likely a
* c#t#d# style string. Users of this are expected to do their own
* verification of the s# part.
*/
/*
* and the like.
*/
static int
ctd_check_path(char *str) {
/*
* If it starts with a slash, check the last component.
*/
/*
* If it ends in "/old", check the second-to-last
* component of the string instead.
*/
;
}
}
}
/*
* 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.
*/
static nvlist_t *
{
char *srchkey;
/* Nothing to look for */
return (NULL);
/* Obtain the key we will use to search */
switch (nvpair_type(pair)) {
case DATA_TYPE_UINT64:
&theguid) == 0);
return (nv);
}
break;
case DATA_TYPE_STRING: {
break;
/*
* Search for the requested value. Special cases:
*
* - ZPOOL_CONFIG_PATH for whole disk entries. These end in
* but included in the string, so this matches around it.
* - looking for a top-level vdev name (i.e. ZPOOL_CONFIG_TYPE).
*
* Otherwise, all other searches are simple string compares.
*/
ctd_check_path(val)) {
&wholedisk);
if (wholedisk) {
break;
/*
* make_leaf_vdev() should only set
* wholedisk for ZPOOL_CONFIG_PATHs which
* room for the indices used next.
*/
/*
* strings identical except trailing "s0"
*/
return (nv);
/*
*/
return (nv);
break;
}
/*
* Determine our vdev type, keeping in mind
* that the srchval is composed of a type and
* vdev id pair (i.e. mirror-4).
*/
return (NULL);
break;
}
idx = p + 1;
*p = '\0';
/*
* If the types don't match then keep looking.
*/
break;
}
strlen(VDEV_TYPE_RAIDZ)) == 0 ||
strlen(VDEV_TYPE_MIRROR)) == 0);
&id) == 0);
errno = 0;
if (errno != 0)
return (NULL);
/*
* Now verify that we have the correct vdev id.
*/
return (nv);
}
/*
* Common case
*/
return (nv);
break;
}
default:
break;
}
return (NULL);
for (c = 0; c < children; c++) {
/*
* 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).
*/
ZPOOL_CONFIG_IS_LOG, &is_log) == 0 &&
is_log) {
}
return (ret);
}
}
for (c = 0; c < children; c++) {
*avail_spare = B_TRUE;
return (ret);
}
}
}
for (c = 0; c < children; c++) {
return (ret);
}
}
}
return (NULL);
}
/*
* Given a physical path (minus the "/devices" prefix), find the
* associated vdev.
*/
nvlist_t *
{
&nvroot) == 0);
*avail_spare = B_FALSE;
return (ret);
}
/*
*/
zpool_vdev_is_interior(const char *name)
{
return (B_TRUE);
return (B_FALSE);
}
nvlist_t *
{
char buf[MAXPATHLEN];
char *end;
} else if (zpool_vdev_is_interior(path)) {
} else if (path[0] != '/') {
} else {
}
&nvroot) == 0);
*avail_spare = B_FALSE;
return (ret);
}
static int
{
return (0);
return (1);
}
/*
* Helper function for zpool_get_physpaths().
*/
static int
{
char *tmppath;
const char *format;
&tmppath) != 0)
return (EZFS_NODEVICE);
pos = *bytes_written;
*bytes_written += rsz;
if (rsz >= bytes_left) {
/* if physpath was not copied properly, clear it */
if (bytes_left != 0) {
}
return (EZFS_NOSPC);
}
return (0);
}
static int
{
char *type;
int ret;
return (EZFS_INVALCONFIG);
/*
* An active spare device has ZPOOL_CONFIG_IS_SPARE set.
* For a spare vdev, we only want to boot from the active
* spare device.
*/
if (is_spare) {
&spare);
if (!spare)
return (EZFS_INVALCONFIG);
}
if (vdev_online(nv)) {
phypath_size, rsz)) != 0)
return (ret);
}
int i, ret;
return (EZFS_INVALCONFIG);
for (i = 0; i < count; i++) {
if (ret == EZFS_NOSPC)
return (ret);
}
}
return (EZFS_POOL_INVALARG);
}
/*
* Get phys_path for a root pool config.
* Return 0 on success; non-zero on failure.
*/
static int
{
char *type;
rsz = 0;
&vdev_root) != 0)
return (EZFS_INVALCONFIG);
return (EZFS_INVALCONFIG);
/*
* root pool can not have EFI labeled disks and can only have
* a single top-level vdev.
*/
return (EZFS_POOL_INVALARG);
B_FALSE);
/* No online devices */
if (rsz == 0)
return (EZFS_NODEVICE);
return (0);
}
/*
* Get phys_path for a root pool
* Return 0 on success; non-zero on failure.
*/
int
{
phypath_size));
}
/*
* If the device has being dynamically expanded then we need to relabel
* the disk to use the new unallocated space.
*/
static int
{
char path[MAXPATHLEN];
char errbuf[1024];
int (*_efi_use_whole_disk)(int);
"efi_use_whole_disk")) == NULL)
return (-1);
"relabel '%s': unable to open device"), name);
}
/*
* It's possible that we might encounter an error if the device
* does not have any unallocated space left. If so, we simply
* ignore that error and continue on.
*/
"relabel '%s': unable to read disk capacity"), name);
}
return (0);
}
/*
* Bring the specified vdev online. The 'flags' parameter is a set of the
* ZFS_ONLINE_* flags.
*/
int
{
char msg[1024];
if (flags & ZFS_ONLINE_EXPAND) {
} else {
}
if (avail_spare)
if (flags & ZFS_ONLINE_EXPAND ||
&wholedisk);
&pathname) == 0);
/*
* XXX - L2ARC 1.0 devices can't support expansion.
*/
if (l2cache) {
"cannot expand cache devices"));
}
if (wholedisk) {
}
}
"from this pool into a new one. Use '%s' "
"instead"), "zpool detach");
}
}
return (0);
}
/*
* Take the specified vdev offline
*/
int
{
char msg[1024];
if (avail_spare)
return (0);
switch (errno) {
case EBUSY:
/*
* There are no other replicas of this device.
*/
case EEXIST:
/*
* The log device has unplayed logs
*/
default:
}
}
/*
* Mark the given vdev faulted.
*/
int
{
char msg[1024];
return (0);
switch (errno) {
case EBUSY:
/*
* There are no other replicas of this device.
*/
default:
}
}
/*
* Mark the given vdev degraded.
*/
int
{
char msg[1024];
return (0);
}
/*
* Returns TRUE if the given nvlist is a vdev that was originally swapped in as
* a hot spare.
*/
static boolean_t
{
char *type;
&children) == 0) {
&type) == 0);
return (B_TRUE);
for (c = 0; c < children; c++)
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
{
char msg[1024];
int ret;
char *newname;
if (replacing)
else
/*
* If this is a root pool, make sure that we're not attaching an
* EFI labeled device.
*/
"EFI labeled devices are not supported on root pools."));
}
&islog)) == 0)
if (avail_spare)
if (l2cache)
"new device must be a single disk"));
}
ZPOOL_CONFIG_VDEV_TREE, &config_root) == 0);
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 &&
"can only be replaced by another hot spare"));
}
return (-1);
if (ret == 0) {
if (rootpool) {
/*
* XXX need a better way to prevent user from
* booting up a half-baked vdev.
*/
"sure to wait until resilver is done "
"before rebooting.\n"));
}
return (0);
}
switch (errno) {
case ENOTSUP:
/*
* Can't attach to or replace this type of vdev.
*/
if (replacing) {
if (islog)
"cannot replace a log with a spare"));
else if (version >= SPA_VERSION_MULTI_REPLACE)
"for completion or use 'zpool detach'"));
else
"cannot replace a replacing device"));
} else {
"can only attach to mirrors and top-level "
"disks"));
}
break;
case EINVAL:
/*
* The new device must be a single disk.
*/
"new device must be a single disk"));
break;
case EBUSY:
new_disk);
break;
case EOVERFLOW:
/*
* The new device is too small.
*/
"device is too small"));
break;
case EDOM:
/*
* The new device has a different alignment requirement.
*/
"devices have different sector alignment"));
break;
case ENAMETOOLONG:
/*
* The resulting top-level vdev spec won't fit in the label.
*/
break;
default:
}
return (-1);
}
/*
* Detach the specified device.
*/
int
{
char msg[1024];
NULL)) == 0)
if (avail_spare)
if (l2cache)
return (0);
switch (errno) {
case ENOTSUP:
/*
* Can't detach from this type of vdev.
*/
"applicable to mirror and replacing vdevs"));
break;
case EBUSY:
/*
* There are no other replicas of this device.
*/
break;
default:
}
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
{
if (result) {
return (mc);
}
}
}
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
{
char msg[1024];
int retval = 0;
"retrieve pool configuration\n"));
return (-1);
}
== 0);
if (props) {
return (-1);
}
&children) != 0) {
"Source pool is missing vdev tree"));
if (zc_props)
return (-1);
}
vcount = 0;
&newchild, &newchildren) != 0)
newchildren = 0;
for (c = 0; c < children; c++) {
char *type;
int entry;
/*
* Unlike cache & spares, slogs are stored in the
* ZPOOL_CONFIG_CHILDREN array. We filter them out here.
*/
&is_log);
&is_hole);
/*
* Create a hole vdev and put it in the config.
*/
goto out;
VDEV_TYPE_HOLE) != 0)
goto out;
1) != 0)
goto out;
if (lastlog == 0)
continue;
}
lastlog = 0;
== 0);
"Source pool must be composed only of mirrors\n"));
goto out;
}
/* find or add an entry for this top-level vdev */
if (newchildren > 0 &&
newchild, newchildren)) >= 0) {
/* We found a disk that the user specified. */
++found;
} else {
/* User didn't specify a disk for this vdev. */
}
goto out;
}
/* did we find every disk the user specified? */
if (found != newchildren) {
"include at most one disk from each mirror"));
goto out;
}
/* Prepare the nvlist for populating. */
goto out;
VDEV_TYPE_ROOT) != 0)
goto out;
} else {
}
/* Add all the children we found */
goto out;
/*
* If we're just doing a dry run, exit now with success.
*/
goto out;
}
/* now build up the config list & call the ioctl */
goto out;
ZPOOL_CONFIG_VDEV_TREE, *newroot) != 0 ||
ZPOOL_CONFIG_POOL_NAME, newname) != 0 ||
goto out;
/*
* The new pool is automatically part of the namespace unless we
* explicitly export it.
*/
goto out;
goto out;
goto out;
}
out:
int v;
for (v = 0; v < vcount; v++)
nvlist_free(varray[v]);
}
if (zc_props)
if (newconfig)
if (freelist) {
}
if (retval != 0)
return (retval);
if (memory_err)
return (0);
}
/*
* Remove the given device. Currently, this is supported only for hot spares
* and level 2 cache devices.
*/
int
{
char msg[1024];
&islog)) == 0)
/*
* XXX - this should just go away.
*/
"only inactive hot spares, cache, top-level, "
"or log devices can be removed"));
}
"pool must be upgrade to support log removal"));
}
return (0);
}
/*
* Clear the errors for the pool, or the particular device if specified.
*/
int
{
char msg[1024];
int error;
if (path)
path);
else
zhp->zpool_name);
if (path) {
/*
* Don't allow error clearing for hot spares. Do allow
* error clearing for l2cache devices.
*/
if (avail_spare)
}
return (-1);
return (-1);
return (-1);
}
}
if (policy.zrp_request &
(ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) {
nvi);
}
return (0);
}
}
/*
* Similar to zpool_clear(), but takes a GUID (used by fmd).
*/
int
{
char msg[1024];
guid);
return (0);
}
/*
* Change the GUID for a pool.
*/
int
{
char msg[1024];
return (0);
}
/*
* Reopen the pool.
*/
int
{
char msg[1024];
zhp->zpool_name);
return (0);
}
/*
* Convert from a devid string to a path.
*/
static char *
devid_to_path(char *devid_str)
{
char *minor;
char *path;
int ret;
return (NULL);
if (ret != 0)
return (NULL);
return (NULL);
return (path);
}
/*
* Convert from a path to a devid string.
*/
static char *
path_to_devid(const char *path)
{
int fd;
return (NULL);
}
return (ret);
}
/*
* Issue the necessary ioctl() to update the stored path value for the vdev. We
* ignore any failure here, since a common case is for an unprivileged user to
* type 'zpool status', and we'll display the correct information anyway.
*/
static void
{
}
/*
* Given a vdev, return the name to display in iostat. If the vdev has a path,
* 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 *
{
char buf[64];
&value) == 0) {
&value) == 0);
/*
* If the device is dead (faulted, offline, etc) then don't
* bother opening it. Otherwise we may be forcing the user to
* open a misbehaving device, which can have undesirable
* effects.
*/
/*
* Determine if the current path is correct.
*/
char *newpath;
/*
* Update the path appropriately.
*/
if (nvlist_add_string(nv,
ZPOOL_CONFIG_PATH, newpath) == 0)
&path) == 0);
}
}
if (newdevid)
}
path += 9;
/*
* If it starts with c#, and ends with "s0", chop
* the "s0" from the middle.
*/
} else if (pathlen > 6 &&
"/old");
}
}
return (tmp);
}
} else {
/*
* If it's a raidz device, we need to stick in the parity level.
*/
&value) == 0);
}
/*
* We identify each top-level vdev by using a <type-id>
* naming convention.
*/
if (verbose) {
&id) == 0);
(u_longlong_t)id);
}
}
}
static int
zbookmark_compare(const void *a, const void *b)
{
return (memcmp(a, b, sizeof (zbookmark_t)));
}
/*
* Retrieve the persistent error log, uniquify the members, and return to the
* caller.
*/
int
{
int i;
/*
* Retrieve the raw error list from the kernel. If the number of errors
* has increased, allocate more space and continue until we get the
* entire list.
*/
&count) == 0);
if (count == 0)
return (0);
return (-1);
for (;;) {
&zc) != 0) {
return (-1);
} else {
return (-1);
}
} 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 boomarks
* _not_ copied as part of the process. So we point the start of our
* array appropriate and decrement the total number of elements.
*/
/*
* Fill in the nverrlistp with nvlist's of dataset and object numbers.
*/
for (i = 0; i < count; i++) {
/* ignoring zb_blkid and zb_level for now */
continue;
goto nomem;
goto nomem;
}
goto nomem;
}
goto nomem;
}
}
return (0);
}
/*
* Upgrade a ZFS pool to the latest on-disk version.
*/
int
{
zhp->zpool_name));
return (0);
}
void
{
for (int i = 1; i < argc; i++) {
}
}
int
{
int err;
args = fnvlist_alloc();
if (err == 0)
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
{
switch (errno) {
case EPERM:
"cannot show history for pool '%s'"),
zhp->zpool_name));
case ENOENT:
case ENOTSUP:
default:
}
}
return (0);
}
/*
* Process the buffer of nvlists, unpacking and storing each nvlist record
* into 'records'. 'leftover' is set to the number of bytes that weren't
* processed as there wasn't a complete record.
*/
int
{
int i;
while (bytes_read > sizeof (reclen)) {
/* get length of packed record (stored as little endian) */
break;
/* unpack record */
return (ENOMEM);
/* add record to nvlist array */
(*numrecords)++;
}
}
*leftover = bytes_read;
return (0);
}
/*
* Retrieve the command history of a pool.
*/
int
{
char buf[HIS_BUF_LEN];
uint_t numrecords = 0;
int err, i;
do {
break;
/* if nothing else was read in, we're at EOF, just return */
if (!bytes_read)
break;
break;
/* CONSTCOND */
} while (1);
if (!err) {
records, numrecords) == 0);
}
for (i = 0; i < numrecords; i++)
nvlist_free(records[i]);
return (err);
}
void
{
char dsname[MAXNAMELEN];
if (dsobj == 0) {
/* special case for the MOS */
return;
}
/* get the dataset's name */
ZFS_IOC_DSOBJ_TO_DSNAME, &zc) != 0) {
/* just write out a path of two object numbers */
return;
}
/* find out if the dataset is mounted */
/* get the corrupted object's path */
&zc) == 0) {
if (mounted) {
} else {
}
} else {
}
}
/*
* Read the EFI label from the config, if a label does not exist then
* pass back the error to the caller. If the caller has passed a non-NULL
* diskaddr argument then we set it to the starting address of the EFI
* partition.
*/
static int
{
char *path;
int fd;
char diskname[MAXPATHLEN];
int err = -1;
return (err);
}
}
return (err);
}
/*
* determine where a partition starts on a disk in the current
* configuration
*/
static diskaddr_t
{
return (MAXOFFSET_T);
}
sb = MAXOFFSET_T;
return (sb);
}
for (c = 0; c < children; c++) {
if (sb != MAXOFFSET_T) {
return (sb);
}
}
return (MAXOFFSET_T);
}
/*
* Label an individual disk. The name provided is the short name,
* stripped of any leading /dev path.
*/
int
{
char path[MAXPATHLEN];
int fd;
char errbuf[1024];
/* prepare an error message just in case */
if (zhp) {
if (zpool_is_bootable(zhp)) {
"EFI labeled devices are not supported on root "
"pools."));
}
ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
if (zhp->zpool_start_block == 0)
else
} else {
/* new pool */
}
/*
* This shouldn't happen. We've long since verified that this
* is a valid device.
*/
}
/*
* The only way this can fail is if we run out of memory, or we
* were unable to read the disk's capacity
*/
"unable to read disk capacity"), name);
}
if (start_block == MAXOFFSET_T)
/*
* Why we use V_USR: V_BACKUP confuses users, and is considered
* disposable by some EFI utilities (since EFI doesn't have a backup
* slice). V_UNASSIGNED is supposed to be used only for zero size
* partitions, and efi_write() will fail if we use it. V_ROOT, V_BOOT,
* etc. were all pretty specific. V_USR is as close to reality as we
* can get, in the absence of V_OTHER.
*/
/*
* Some block drivers (like pcata) may not support EFI
* GPT labels. Print out a helpful error message dir-
* ecting the user to manually label the disk and give
* a specific slice.
*/
"try using fdisk(1M) and then provide a specific slice"));
}
return (0);
}
static boolean_t
{
char *type;
"vdev type '%s' is not supported"), type);
return (B_FALSE);
}
for (c = 0; c < children; c++) {
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* Check if this zvol is allowable for use as a dump device; zero if
* it is, > 0 if it isn't, < 0 if it isn't a zvol.
*
* Allowable storage configurations include mirrors, all raidz variants, and
* pools with log, cache, and spare devices. Pools which are backed by files or
*/
int
zvol_check_dump_config(char *arg)
{
char *p, *volname;
char errbuf[1024];
char poolname[ZPOOL_MAXNAMELEN];
int ret = 1;
return (-1);
}
"dump is not supported on device '%s'"), arg);
return (1);
/* check the configuration of the pool */
"malformed dataset name"));
return (1);
} else if (p - volname >= ZFS_MAXNAMELEN) {
"dataset name is too long"));
return (1);
} else {
}
"could not open pool '%s'"), poolname);
goto out;
}
&nvroot) != 0) {
"could not obtain vdev configuration for '%s'"), poolname);
goto out;
}
goto out;
}
ret = 0;
out:
if (zhp)
return (ret);
}