* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2014 by Delphix. All rights reserved.
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
+ * Copyright (c) 2017 Datto Inc.
+ * Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
*/
#include <ctype.h>
#include <zone.h>
#include <sys/stat.h>
#include <sys/efi_partition.h>
+#include <sys/systeminfo.h>
#include <sys/vtoc.h>
#include <sys/zfs_ioctl.h>
#include <dlfcn.h>
#include "zfeature_common.h"
static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
+static boolean_t zpool_vdev_is_interior(const char *name);
typedef struct prop_flags {
int create:1; /* Validate property on creation */
return (0);
}
-static char *
+static const char *
zpool_get_prop_string(zpool_handle_t *zhp, zpool_prop_t prop,
zprop_source_t *src)
{
/*
* Map VDEV STATE to printed strings.
*/
-char *
+const char *
zpool_state_to_name(vdev_state_t state, vdev_aux_t aux)
{
switch (state) {
bootfs_name_valid(const char *pool, 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);
const char *propname = nvpair_name(elem);
prop = zpool_name_to_prop(propname);
- if (prop == ZPROP_INVAL && zpool_prop_feature(propname)) {
+ if (prop == ZPOOL_PROP_INVAL && zpool_prop_feature(propname)) {
int err;
char *fname = strchr(propname, '@') + 1;
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;
/*
* Make sure this property is valid and applies to this type.
*/
- if (prop == ZPROP_INVAL) {
+ if (prop == ZPOOL_PROP_INVAL) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid property '%s'"), propname);
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
* bootfs property value has to be a dataset name and
* the dataset has to be in the same pool as it sets to.
*/
- if (strval[0] != '\0' && !bootfs_name_valid(poolname,
- strval)) {
+ 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;
}
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;
default:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"property '%s'(%d) not defined"), propname, prop);
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 msg[1024];
int ret = -1;
strcmp(zonestr, "on") == 0);
if ((zc_fsprops = zfs_valid_proplist(hdl, ZFS_TYPE_FILESYSTEM,
- fsprops, zoned, NULL, NULL, msg)) == NULL) {
+ fsprops, zoned, NULL, NULL, B_TRUE, msg)) == 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,
+ &wkeydata, &wkeylen) != 0) {
+ zfs_error(hdl, EZFS_CRYPTOFAILED, msg);
+ 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) != 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:
zcmd_free_nvlists(&zc);
nvlist_free(zc_props);
nvlist_free(zc_fsprops);
+ nvlist_free(hidden_args);
+ if (wkeydata != NULL)
+ free(wkeydata);
return (ret);
}
if (error) {
char desc[1024];
+ char aux[256];
/*
* Dry-run failed, but we print out what success
(void) zfs_error(hdl, EZFS_BADVERSION, desc);
break;
+ case EREMOTEIO:
+ if (nv != NULL && nvlist_lookup_nvlist(nv,
+ ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0) {
+ 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, aux);
+ }
+ (void) zfs_error(hdl, EZFS_ACTIVE_POOL, desc);
+ break;
+
case EINVAL:
(void) zfs_error(hdl, EZFS_INVALCONFIG, desc);
break;
* Scan the pool.
*/
int
-zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func)
+zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func, pool_scrub_cmd_t cmd)
{
zfs_cmd_t zc = {"\0"};
char msg[1024];
+ int err;
libzfs_handle_t *hdl = zhp->zpool_hdl;
(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);
+
+ err = errno;
+
+ /* ECANCELED on a scrub means we resumed a paused scrub */
+ if (err == ECANCELED && func == POOL_SCAN_SCRUB &&
+ cmd == POOL_SCRUB_NORMAL)
+ return (0);
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0 ||
- (errno == ENOENT && func != POOL_SCAN_NONE))
+ if (err == ENOENT && func != POOL_SCAN_NONE && cmd == POOL_SCRUB_NORMAL)
return (0);
if (func == POOL_SCAN_SCRUB) {
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot scrub %s"), zc.zc_name);
+ if (cmd == POOL_SCRUB_PAUSE) {
+ (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
+ "cannot pause scrubbing %s"), zc.zc_name);
+ } else {
+ assert(cmd == POOL_SCRUB_NORMAL);
+ (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
+ "cannot scrub %s"), zc.zc_name);
+ }
} else if (func == POOL_SCAN_NONE) {
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot cancel scrubbing %s"),
assert(!"unexpected result");
}
- if (errno == EBUSY) {
+ if (err == EBUSY) {
nvlist_t *nvroot;
pool_scan_stat_t *ps = NULL;
uint_t psc;
ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
(void) nvlist_lookup_uint64_array(nvroot,
ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &psc);
- if (ps && ps->pss_func == POOL_SCAN_SCRUB)
- return (zfs_error(hdl, EZFS_SCRUBBING, msg));
- else
+ if (ps && ps->pss_func == POOL_SCAN_SCRUB) {
+ if (cmd == POOL_SCRUB_PAUSE)
+ return (zfs_error(hdl, EZFS_SCRUB_PAUSED, msg));
+ else
+ return (zfs_error(hdl, EZFS_SCRUBBING, msg));
+ } else {
return (zfs_error(hdl, EZFS_RESILVERING, msg));
- } else if (errno == ENOENT) {
+ }
+ } else if (err == ENOENT) {
return (zfs_error(hdl, EZFS_NO_SCRUB, msg));
} else {
- return (zpool_standard_error(hdl, errno, msg));
+ return (zpool_standard_error(hdl, err, msg));
}
}
break;
}
- verify(strncmp(type, VDEV_TYPE_RAIDZ,
- strlen(VDEV_TYPE_RAIDZ)) == 0 ||
- strncmp(type, VDEV_TYPE_MIRROR,
- strlen(VDEV_TYPE_MIRROR)) == 0);
+ verify(zpool_vdev_is_interior(type));
verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID,
&id) == 0);
/*
* Determine if we have an "interior" top-level vdev (i.e mirror/raidz).
*/
-boolean_t
+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_MIRROR, strlen(VDEV_TYPE_MIRROR)) == 0)
return (B_TRUE);
return (B_FALSE);
}
static int
-vdev_online(nvlist_t *nv)
+vdev_is_online(nvlist_t *nv)
{
uint64_t ival;
return (EZFS_INVALCONFIG);
}
- if (vdev_online(nv)) {
+ if (vdev_is_online(nv)) {
if ((ret = vdev_get_one_physpath(nv, physpath,
phypath_size, rsz)) != 0)
return (ret);
{
zfs_cmd_t zc = {"\0"};
char msg[1024];
+ char *pathname;
nvlist_t *tgt;
boolean_t avail_spare, l2cache, islog;
libzfs_handle_t *hdl = zhp->zpool_hdl;
if (avail_spare)
return (zfs_error(hdl, EZFS_ISSPARE, msg));
- if (flags & ZFS_ONLINE_EXPAND ||
- zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) {
+ 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,
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;
+ uint64_t vers, readonly = B_FALSE;
boolean_t freelist = B_FALSE, memory_err = B_TRUE;
int retval = 0;
if ((zc_props = zpool_valid_proplist(hdl, zhp->zpool_name,
props, vers, flags, msg)) == 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,
* Reopen the pool.
*/
int
-zpool_reopen(zpool_handle_t *zhp)
+zpool_reopen_one(zpool_handle_t *zhp, void *data)
{
- zfs_cmd_t zc = {"\0"};
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
+ libzfs_handle_t *hdl = zpool_get_handle(zhp);
+ const char *pool_name = zpool_get_name(zhp);
+ boolean_t *scrub_restart = data;
+ int error;
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot reopen '%s'"),
- zhp->zpool_name);
+ error = lzc_reopen(pool_name, *scrub_restart);
+ if (error) {
+ return (zpool_standard_error_fmt(hdl, error,
+ dgettext(TEXT_DOMAIN, "cannot reopen '%s'"), pool_name));
+ }
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_REOPEN, &zc) == 0)
- return (0);
- return (zpool_standard_error(hdl, errno, msg));
+ 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);
}
#if defined(__sun__) || defined(__sun)
char buf[PATH_BUF_LEN];
char tmpbuf[PATH_BUF_LEN];
+ /*
+ * vdev_name will be "root"/"root-0" for the root vdev, but it is the
+ * zpool name that will be displayed to the user.
+ */
+ verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
+ if (zhp != NULL && strcmp(type, "root") == 0)
+ return (zfs_strdup(hdl, zpool_get_name(zhp)));
+
env = getenv("ZPOOL_VDEV_NAME_PATH");
if (env && (strtoul(env, NULL, 0) > 0 ||
!strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
/*
* For a block device only use the name.
*/
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
if ((strcmp(type, VDEV_TYPE_DISK) == 0) &&
!(name_flags & VDEV_NAME_PATH)) {
path = strrchr(path, '/');
return (zfs_strip_partition(path));
}
} else {
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &path) == 0);
+ path = type;
/*
* If it's a raidz device, we need to stick in the parity level.