aboutsummaryrefslogtreecommitdiff
path: root/sys/contrib/openzfs/lib/libzfs/libzfs_util.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/openzfs/lib/libzfs/libzfs_util.c')
-rw-r--r--sys/contrib/openzfs/lib/libzfs/libzfs_util.c679
1 files changed, 529 insertions, 150 deletions
diff --git a/sys/contrib/openzfs/lib/libzfs/libzfs_util.c b/sys/contrib/openzfs/lib/libzfs/libzfs_util.c
index c3c009ae3a10..26f5135dff62 100644
--- a/sys/contrib/openzfs/lib/libzfs/libzfs_util.c
+++ b/sys/contrib/openzfs/lib/libzfs/libzfs_util.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
*
@@ -6,7 +7,7 @@
* 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 http://www.opensolaris.org/os/licensing.
+ * or https://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
@@ -22,7 +23,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2020 Joyent, Inc. All rights reserved.
- * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2024 by Delphix. All rights reserved.
* Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
* Copyright (c) 2017 Datto Inc.
* Copyright (c) 2020 The FreeBSD Foundation
@@ -68,6 +69,7 @@
* as necessary.
*/
#define URI_REGEX "^\\([A-Za-z][A-Za-z0-9+.\\-]*\\):"
+#define STR_NUMS "0123456789"
int
libzfs_errno(libzfs_handle_t *hdl)
@@ -170,6 +172,8 @@ libzfs_error_description(libzfs_handle_t *hdl)
return (dgettext(TEXT_DOMAIN, "I/O error"));
case EZFS_INTR:
return (dgettext(TEXT_DOMAIN, "signal received"));
+ case EZFS_CKSUM:
+ return (dgettext(TEXT_DOMAIN, "insufficient replicas"));
case EZFS_ISSPARE:
return (dgettext(TEXT_DOMAIN, "device is reserved as a hot "
"spare"));
@@ -241,10 +245,20 @@ libzfs_error_description(libzfs_handle_t *hdl)
"into a new one"));
case EZFS_SCRUB_PAUSED:
return (dgettext(TEXT_DOMAIN, "scrub is paused; "
- "use 'zpool scrub' to resume"));
+ "use 'zpool scrub' to resume scrub"));
+ case EZFS_SCRUB_PAUSED_TO_CANCEL:
+ return (dgettext(TEXT_DOMAIN, "scrub is paused; "
+ "use 'zpool scrub' to resume or 'zpool scrub -s' to "
+ "cancel scrub"));
case EZFS_SCRUBBING:
return (dgettext(TEXT_DOMAIN, "currently scrubbing; "
- "use 'zpool scrub -s' to cancel current scrub"));
+ "use 'zpool scrub -s' to cancel scrub"));
+ case EZFS_ERRORSCRUBBING:
+ return (dgettext(TEXT_DOMAIN, "currently error scrubbing; "
+ "use 'zpool scrub -s' to cancel error scrub"));
+ case EZFS_ERRORSCRUB_PAUSED:
+ return (dgettext(TEXT_DOMAIN, "error scrub is paused; "
+ "use 'zpool scrub -e' to resume error scrub"));
case EZFS_NO_SCRUB:
return (dgettext(TEXT_DOMAIN, "there is no active scrub"));
case EZFS_DIFF:
@@ -296,6 +310,20 @@ libzfs_error_description(libzfs_handle_t *hdl)
case EZFS_REBUILDING:
return (dgettext(TEXT_DOMAIN, "currently sequentially "
"resilvering"));
+ case EZFS_VDEV_NOTSUP:
+ return (dgettext(TEXT_DOMAIN, "operation not supported "
+ "on this type of vdev"));
+ case EZFS_NOT_USER_NAMESPACE:
+ return (dgettext(TEXT_DOMAIN, "the provided file "
+ "was not a user namespace file"));
+ case EZFS_RESUME_EXISTS:
+ return (dgettext(TEXT_DOMAIN, "Resuming recv on existing "
+ "dataset without force"));
+ case EZFS_RAIDZ_EXPAND_IN_PROGRESS:
+ return (dgettext(TEXT_DOMAIN, "raidz expansion in progress"));
+ case EZFS_ASHIFT_MISMATCH:
+ return (dgettext(TEXT_DOMAIN, "adding devices with "
+ "different physical sector sizes is not allowed"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default:
@@ -390,6 +418,10 @@ zfs_common_error(libzfs_handle_t *hdl, int error, const char *fmt,
case EINTR:
zfs_verror(hdl, EZFS_INTR, fmt, ap);
return (-1);
+
+ case ECKSUM:
+ zfs_verror(hdl, EZFS_CKSUM, fmt, ap);
+ return (-1);
}
return (0);
@@ -482,8 +514,11 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
case ZFS_ERR_BADPROP:
zfs_verror(hdl, EZFS_BADPROP, fmt, ap);
break;
+ case ZFS_ERR_NOT_USER_NAMESPACE:
+ zfs_verror(hdl, EZFS_NOT_USER_NAMESPACE, fmt, ap);
+ break;
default:
- zfs_error_aux(hdl, "%s", strerror(error));
+ zfs_error_aux(hdl, "%s", zfs_strerror(error));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
break;
}
@@ -597,8 +632,8 @@ zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
(void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
break;
}
+ zfs_fallthrough;
#endif
- fallthrough;
default:
(void) zfs_standard_error(hdl, err, errbuf);
}
@@ -670,7 +705,7 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
case ENOSPC:
case EDQUOT:
zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
- return (-1);
+ break;
case EAGAIN:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -716,6 +751,9 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
case ZFS_ERR_BADPROP:
zfs_verror(hdl, EZFS_BADPROP, fmt, ap);
break;
+ case ZFS_ERR_VDEV_NOTSUP:
+ zfs_verror(hdl, EZFS_VDEV_NOTSUP, fmt, ap);
+ break;
case ZFS_ERR_IOC_CMD_UNAVAIL:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "the loaded zfs "
"module does not support this operation. A reboot may "
@@ -732,8 +770,19 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
case ZFS_ERR_IOC_ARG_BADTYPE:
zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap);
break;
+ case ZFS_ERR_RAIDZ_EXPAND_IN_PROGRESS:
+ zfs_verror(hdl, EZFS_RAIDZ_EXPAND_IN_PROGRESS, fmt, ap);
+ break;
+ case ZFS_ERR_ASHIFT_MISMATCH:
+ zfs_verror(hdl, EZFS_ASHIFT_MISMATCH, fmt, ap);
+ break;
+ case ZFS_ERR_TOO_MANY_SITOUTS:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "too many disks "
+ "already sitting out"));
+ zfs_verror(hdl, EZFS_BUSY, fmt, ap);
+ break;
default:
- zfs_error_aux(hdl, "%s", strerror(error));
+ zfs_error_aux(hdl, "%s", zfs_strerror(error));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
}
@@ -741,6 +790,12 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
return (-1);
}
+int
+zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
+{
+ return (lzc_ioctl_fd(hdl->libzfs_fd, request, zc));
+}
+
/*
* Display an out of memory error message and abort the current program.
*/
@@ -801,7 +856,7 @@ zfs_realloc(libzfs_handle_t *hdl, void *ptr, size_t oldsize, size_t newsize)
return (NULL);
}
- bzero((char *)ret + oldsize, (newsize - oldsize));
+ memset((char *)ret + oldsize, 0, newsize - oldsize);
return (ret);
}
@@ -889,6 +944,7 @@ libzfs_run_process_impl(const char *path, char *argv[], char *env[], int flags,
pid = fork();
if (pid == 0) {
/* Child process */
+ setpgid(0, 0);
devnull_fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
if (devnull_fd < 0)
@@ -987,16 +1043,13 @@ libzfs_free_str_array(char **strs, int count)
*
* Returns 0 otherwise.
*/
-int
-libzfs_envvar_is_set(char *envvar)
+boolean_t
+libzfs_envvar_is_set(const char *envvar)
{
char *env = getenv(envvar);
- if (env && (strtoul(env, NULL, 0) > 0 ||
+ return (env && (strtoul(env, NULL, 0) > 0 ||
(!strncasecmp(env, "YES", 3) && strnlen(env, 4) == 3) ||
- (!strncasecmp(env, "ON", 2) && strnlen(env, 3) == 2)))
- return (1);
-
- return (0);
+ (!strncasecmp(env, "ON", 2) && strnlen(env, 3) == 2)));
}
libzfs_handle_t *
@@ -1034,6 +1087,7 @@ libzfs_init(void)
zfs_prop_init();
zpool_prop_init();
zpool_feature_init();
+ vdev_prop_init();
libzfs_mnttab_init(hdl);
fletcher_4_init();
@@ -1142,7 +1196,7 @@ zfs_path_to_zhandle(libzfs_handle_t *hdl, const char *path, zfs_type_t argtype)
* Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from
* an ioctl().
*/
-int
+void
zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len)
{
if (len == 0)
@@ -1150,10 +1204,6 @@ zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len)
zc->zc_nvlist_dst_size = len;
zc->zc_nvlist_dst =
(uint64_t)(uintptr_t)zfs_alloc(hdl, zc->zc_nvlist_dst_size);
- if (zc->zc_nvlist_dst == 0)
- return (-1);
-
- return (0);
}
/*
@@ -1161,16 +1211,12 @@ zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len)
* expand the nvlist to the size specified in 'zc_nvlist_dst_size', which was
* filled in by the kernel to indicate the actual required size.
*/
-int
+void
zcmd_expand_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc)
{
free((void *)(uintptr_t)zc->zc_nvlist_dst);
zc->zc_nvlist_dst =
(uint64_t)(uintptr_t)zfs_alloc(hdl, zc->zc_nvlist_dst_size);
- if (zc->zc_nvlist_dst == 0)
- return (-1);
-
- return (0);
}
/*
@@ -1187,38 +1233,33 @@ zcmd_free_nvlists(zfs_cmd_t *zc)
zc->zc_nvlist_dst = 0;
}
-static int
+static void
zcmd_write_nvlist_com(libzfs_handle_t *hdl, uint64_t *outnv, uint64_t *outlen,
nvlist_t *nvl)
{
char *packed;
- size_t len;
-
- verify(nvlist_size(nvl, &len, NV_ENCODE_NATIVE) == 0);
- if ((packed = zfs_alloc(hdl, len)) == NULL)
- return (-1);
+ size_t len = fnvlist_size(nvl);
+ packed = zfs_alloc(hdl, len);
verify(nvlist_pack(nvl, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);
*outnv = (uint64_t)(uintptr_t)packed;
*outlen = len;
-
- return (0);
}
-int
+void
zcmd_write_conf_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl)
{
- return (zcmd_write_nvlist_com(hdl, &zc->zc_nvlist_conf,
- &zc->zc_nvlist_conf_size, nvl));
+ zcmd_write_nvlist_com(hdl, &zc->zc_nvlist_conf,
+ &zc->zc_nvlist_conf_size, nvl);
}
-int
+void
zcmd_write_src_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl)
{
- return (zcmd_write_nvlist_com(hdl, &zc->zc_nvlist_src,
- &zc->zc_nvlist_src_size, nvl));
+ zcmd_write_nvlist_com(hdl, &zc->zc_nvlist_src,
+ &zc->zc_nvlist_src_size, nvl);
}
/*
@@ -1240,10 +1281,18 @@ zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
* ================================================================
*/
+void
+zcmd_print_json(nvlist_t *nvl)
+{
+ nvlist_print_json(stdout, nvl);
+ (void) putchar('\n');
+ nvlist_free(nvl);
+}
+
static void
zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
{
- zprop_list_t *pl = cbp->cb_proplist;
+ zprop_list_t *pl;
int i;
char *title;
size_t len;
@@ -1267,7 +1316,8 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
/* first property is always NAME */
assert(cbp->cb_proplist->pl_prop ==
- ((type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME : ZFS_PROP_NAME));
+ ((type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME :
+ ((type == ZFS_TYPE_VDEV) ? VDEV_PROP_NAME : ZFS_PROP_NAME)));
/*
* Go through and calculate the widths for each column. For the
@@ -1281,15 +1331,19 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
/*
* 'PROPERTY' column
*/
- if (pl->pl_prop != ZPROP_INVAL) {
+ if (pl->pl_prop != ZPROP_USERPROP) {
const char *propname = (type == ZFS_TYPE_POOL) ?
zpool_prop_to_name(pl->pl_prop) :
- zfs_prop_to_name(pl->pl_prop);
+ ((type == ZFS_TYPE_VDEV) ?
+ vdev_prop_to_name(pl->pl_prop) :
+ zfs_prop_to_name(pl->pl_prop));
+ assert(propname != NULL);
len = strlen(propname);
if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
cbp->cb_colwidths[GET_COL_PROPERTY] = len;
} else {
+ assert(pl->pl_user_prop != NULL);
len = strlen(pl->pl_user_prop);
if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
cbp->cb_colwidths[GET_COL_PROPERTY] = len;
@@ -1314,9 +1368,10 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
/*
* 'NAME' and 'SOURCE' columns
*/
- if (pl->pl_prop == (type == ZFS_TYPE_POOL ? ZPOOL_PROP_NAME :
- ZFS_PROP_NAME) &&
- pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) {
+ if (pl->pl_prop == ((type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME :
+ ((type == ZFS_TYPE_VDEV) ? VDEV_PROP_NAME :
+ ZFS_PROP_NAME)) && pl->pl_width >
+ cbp->cb_colwidths[GET_COL_NAME]) {
cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width;
cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width +
strlen(dgettext(TEXT_DOMAIN, "inherited from"));
@@ -1361,6 +1416,103 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
}
/*
+ * Add property value and source to provided nvlist, according to
+ * settings in cb structure. Later to be printed in JSON format.
+ */
+int
+zprop_nvlist_one_property(const char *propname,
+ const char *value, zprop_source_t sourcetype, const char *source,
+ const char *recvd_value, nvlist_t *nvl, boolean_t as_int)
+{
+ int ret = 0;
+ nvlist_t *src_nv, *prop;
+ boolean_t all_numeric = strspn(value, STR_NUMS) == strlen(value);
+ src_nv = prop = NULL;
+
+ if ((nvlist_alloc(&prop, NV_UNIQUE_NAME, 0) != 0) ||
+ (nvlist_alloc(&src_nv, NV_UNIQUE_NAME, 0) != 0)) {
+ ret = -1;
+ goto err;
+ }
+
+ if (as_int && all_numeric) {
+ uint64_t val;
+ sscanf(value, "%lld", (u_longlong_t *)&val);
+ if (nvlist_add_uint64(prop, "value", val) != 0) {
+ ret = -1;
+ goto err;
+ }
+ } else {
+ if (nvlist_add_string(prop, "value", value) != 0) {
+ ret = -1;
+ goto err;
+ }
+ }
+
+ switch (sourcetype) {
+ case ZPROP_SRC_NONE:
+ if (nvlist_add_string(src_nv, "type", "NONE") != 0 ||
+ (nvlist_add_string(src_nv, "data", "-") != 0)) {
+ ret = -1;
+ goto err;
+ }
+ break;
+ case ZPROP_SRC_DEFAULT:
+ if (nvlist_add_string(src_nv, "type", "DEFAULT") != 0 ||
+ (nvlist_add_string(src_nv, "data", "-") != 0)) {
+ ret = -1;
+ goto err;
+ }
+ break;
+ case ZPROP_SRC_LOCAL:
+ if (nvlist_add_string(src_nv, "type", "LOCAL") != 0 ||
+ (nvlist_add_string(src_nv, "data", "-") != 0)) {
+ ret = -1;
+ goto err;
+ }
+ break;
+ case ZPROP_SRC_TEMPORARY:
+ if (nvlist_add_string(src_nv, "type", "TEMPORARY") != 0 ||
+ (nvlist_add_string(src_nv, "data", "-") != 0)) {
+ ret = -1;
+ goto err;
+ }
+ break;
+ case ZPROP_SRC_INHERITED:
+ if (nvlist_add_string(src_nv, "type", "INHERITED") != 0 ||
+ (nvlist_add_string(src_nv, "data", source) != 0)) {
+ ret = -1;
+ goto err;
+ }
+ break;
+ case ZPROP_SRC_RECEIVED:
+ if (nvlist_add_string(src_nv, "type", "RECEIVED") != 0 ||
+ (nvlist_add_string(src_nv, "data",
+ (recvd_value == NULL ? "-" : recvd_value)) != 0)) {
+ ret = -1;
+ goto err;
+ }
+ break;
+ default:
+ assert(!"unhandled zprop_source_t");
+ if (nvlist_add_string(src_nv, "type",
+ "unhandled zprop_source_t") != 0) {
+ ret = -1;
+ goto err;
+ }
+ }
+ if ((nvlist_add_nvlist(prop, "source", src_nv) != 0) ||
+ (nvlist_add_nvlist(nvl, propname, prop)) != 0) {
+ ret = -1;
+ goto err;
+ }
+err:
+ nvlist_free(src_nv);
+ nvlist_free(prop);
+ return (ret);
+}
+
+/*
* Display a single line of output, according to the settings in the callback
* structure.
*/
@@ -1451,6 +1603,26 @@ zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
(void) printf("\n");
}
+int
+zprop_collect_property(const char *name, zprop_get_cbdata_t *cbp,
+ const char *propname, const char *value, zprop_source_t sourcetype,
+ const char *source, const char *recvd_value, nvlist_t *nvl)
+{
+ if (cbp->cb_json) {
+ if ((sourcetype & cbp->cb_sources) == 0)
+ return (0);
+ else {
+ return (zprop_nvlist_one_property(propname, value,
+ sourcetype, source, recvd_value, nvl,
+ cbp->cb_json_as_int));
+ }
+ } else {
+ zprop_print_one_property(name, cbp,
+ propname, value, sourcetype, source, recvd_value);
+ return (0);
+ }
+}
+
/*
* Given a numeric suffix, convert the value into a number of bits that the
* resulting value must be shifted.
@@ -1459,15 +1631,17 @@ static int
str2shift(libzfs_handle_t *hdl, const char *buf)
{
const char *ends = "BKMGTPEZ";
- int i;
+ int i, len;
if (buf[0] == '\0')
return (0);
- for (i = 0; i < strlen(ends); i++) {
+
+ len = strlen(ends);
+ for (i = 0; i < len; i++) {
if (toupper(buf[0]) == ends[i])
break;
}
- if (i == strlen(ends)) {
+ if (i == len) {
if (hdl)
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid numeric suffix '%s'"), buf);
@@ -1583,13 +1757,13 @@ zfs_nicestrtonum(libzfs_handle_t *hdl, const char *value, uint64_t *num)
*/
int
zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
- zfs_type_t type, nvlist_t *ret, char **svalp, uint64_t *ivalp,
+ zfs_type_t type, nvlist_t *ret, const char **svalp, uint64_t *ivalp,
const char *errbuf)
{
data_type_t datatype = nvpair_type(elem);
zprop_type_t proptype;
const char *propname;
- char *value;
+ const char *value;
boolean_t isnone = B_FALSE;
boolean_t isauto = B_FALSE;
int err = 0;
@@ -1597,6 +1771,9 @@ zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
if (type == ZFS_TYPE_POOL) {
proptype = zpool_prop_get_type(prop);
propname = zpool_prop_to_name(prop);
+ } else if (type == ZFS_TYPE_VDEV) {
+ proptype = vdev_prop_get_type(prop);
+ propname = vdev_prop_to_name(prop);
} else {
proptype = zfs_prop_get_type(prop);
propname = zfs_prop_to_name(prop);
@@ -1655,6 +1832,16 @@ zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
"use 'none' to disable quota/refquota"));
goto error;
}
+ /*
+ * Pool dedup table quota; force use of 'none' instead of 0
+ */
+ if ((type & ZFS_TYPE_POOL) && *ivalp == 0 &&
+ (!isnone && !isauto) &&
+ prop == ZPOOL_PROP_DEDUP_TABLE_QUOTA) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "use 'none' to disable ddt table quota"));
+ goto error;
+ }
/*
* Special handling for "*_limit=none". In this case it's not
@@ -1667,6 +1854,20 @@ zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
}
/*
+ * Special handling for "checksum_*=none". In this case it's not
+ * 0 but UINT64_MAX.
+ */
+ if ((type & ZFS_TYPE_VDEV) && isnone &&
+ (prop == VDEV_PROP_CHECKSUM_N ||
+ prop == VDEV_PROP_CHECKSUM_T ||
+ prop == VDEV_PROP_IO_N ||
+ prop == VDEV_PROP_IO_T ||
+ prop == VDEV_PROP_SLOW_IO_N ||
+ prop == VDEV_PROP_SLOW_IO_T)) {
+ *ivalp = UINT64_MAX;
+ }
+
+ /*
* Special handling for setting 'refreservation' to 'auto'. Use
* UINT64_MAX to tell the caller to use zfs_fix_auto_resv().
* 'auto' is only allowed on volumes.
@@ -1682,6 +1883,10 @@ zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
}
*ivalp = UINT64_MAX;
break;
+ case ZPOOL_PROP_DEDUP_TABLE_QUOTA:
+ ASSERT(type & ZFS_TYPE_POOL);
+ *ivalp = UINT64_MAX;
+ break;
default:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"'auto' is invalid value for '%s'"),
@@ -1735,43 +1940,35 @@ error:
}
static int
-addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
+addlist(libzfs_handle_t *hdl, const char *propname, zprop_list_t **listp,
zfs_type_t type)
{
- int prop;
- zprop_list_t *entry;
-
- prop = zprop_name_to_prop(propname, type);
-
+ int prop = zprop_name_to_prop(propname, type);
if (prop != ZPROP_INVAL && !zprop_valid_for_type(prop, type, B_FALSE))
prop = ZPROP_INVAL;
/*
- * When no property table entry can be found, return failure if
- * this is a pool property or if this isn't a user-defined
- * dataset property,
+ * Return failure if no property table entry was found and this isn't
+ * a user-defined property.
*/
- if (prop == ZPROP_INVAL && ((type == ZFS_TYPE_POOL &&
+ if (prop == ZPROP_USERPROP && ((type == ZFS_TYPE_POOL &&
+ !zfs_prop_user(propname) &&
!zpool_prop_feature(propname) &&
!zpool_prop_unsupported(propname)) ||
- (type == ZFS_TYPE_DATASET && !zfs_prop_user(propname) &&
- !zfs_prop_userquota(propname) && !zfs_prop_written(propname)))) {
+ ((type == ZFS_TYPE_DATASET) && !zfs_prop_user(propname) &&
+ !zfs_prop_userquota(propname) && !zfs_prop_written(propname)) ||
+ ((type == ZFS_TYPE_VDEV) && !vdev_prop_user(propname)))) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid property '%s'"), propname);
return (zfs_error(hdl, EZFS_BADPROP,
dgettext(TEXT_DOMAIN, "bad property list")));
}
- if ((entry = zfs_alloc(hdl, sizeof (zprop_list_t))) == NULL)
- return (-1);
+ zprop_list_t *entry = zfs_alloc(hdl, sizeof (*entry));
entry->pl_prop = prop;
- if (prop == ZPROP_INVAL) {
- if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) ==
- NULL) {
- free(entry);
- return (-1);
- }
+ if (prop == ZPROP_USERPROP) {
+ entry->pl_user_prop = zfs_strdup(hdl, propname);
entry->pl_width = strlen(propname);
} else {
entry->pl_width = zprop_width(prop, &entry->pl_fixed,
@@ -1811,62 +2008,25 @@ zprop_get_list(libzfs_handle_t *hdl, char *props, zprop_list_t **listp,
"bad property list")));
}
- /*
- * It would be nice to use getsubopt() here, but the inclusion of column
- * aliases makes this more effort than it's worth.
- */
- while (*props != '\0') {
- size_t len;
- char *p;
- char c;
-
- if ((p = strchr(props, ',')) == NULL) {
- len = strlen(props);
- p = props + len;
- } else {
- len = p - props;
- }
-
- /*
- * Check for empty options.
- */
- if (len == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "empty property name"));
- return (zfs_error(hdl, EZFS_BADPROP,
- dgettext(TEXT_DOMAIN, "bad property list")));
- }
-
- /*
- * Check all regular property names.
- */
- c = props[len];
- props[len] = '\0';
-
- if (strcmp(props, "space") == 0) {
- static char *spaceprops[] = {
+ for (char *p; (p = strsep(&props, ",")); )
+ if (strcmp(p, "space") == 0) {
+ static const char *const spaceprops[] = {
"name", "avail", "used", "usedbysnapshots",
"usedbydataset", "usedbyrefreservation",
- "usedbychildren", NULL
+ "usedbychildren"
};
- int i;
- for (i = 0; spaceprops[i]; i++) {
+ for (int i = 0; i < ARRAY_SIZE(spaceprops); i++) {
if (addlist(hdl, spaceprops[i], listp, type))
return (-1);
listp = &(*listp)->pl_next;
}
} else {
- if (addlist(hdl, props, listp, type))
+ if (addlist(hdl, p, listp, type))
return (-1);
listp = &(*listp)->pl_next;
}
- props = p;
- if (c == ',')
- props++;
- }
-
return (0);
}
@@ -1895,8 +2055,7 @@ zprop_expand_list_cb(int prop, void *cb)
zprop_list_t *entry;
expand_data_t *edp = cb;
- if ((entry = zfs_alloc(edp->hdl, sizeof (zprop_list_t))) == NULL)
- return (ZPROP_INVAL);
+ entry = zfs_alloc(edp->hdl, sizeof (zprop_list_t));
entry->pl_prop = prop;
entry->pl_width = zprop_width(prop, &entry->pl_fixed, edp->type);
@@ -1935,11 +2094,9 @@ zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp, zfs_type_t type)
* Add 'name' to the beginning of the list, which is handled
* specially.
*/
- if ((entry = zfs_alloc(hdl, sizeof (zprop_list_t))) == NULL)
- return (-1);
-
- entry->pl_prop = (type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME :
- ZFS_PROP_NAME;
+ entry = zfs_alloc(hdl, sizeof (zprop_list_t));
+ entry->pl_prop = ((type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME :
+ ((type == ZFS_TYPE_VDEV) ? VDEV_PROP_NAME : ZFS_PROP_NAME));
entry->pl_width = zprop_width(entry->pl_prop,
&entry->pl_fixed, type);
entry->pl_all = B_TRUE;
@@ -1956,45 +2113,66 @@ zprop_iter(zprop_func func, void *cb, boolean_t show_all, boolean_t ordered,
return (zprop_iter_common(func, cb, show_all, ordered, type));
}
-/*
- * Fill given version buffer with zfs userland version
- */
-void
-zfs_version_userland(char *version, int len)
+const char *
+zfs_version_userland(void)
{
- (void) strlcpy(version, ZFS_META_ALIAS, len);
+ return (ZFS_META_ALIAS);
}
/*
* Prints both zfs userland and kernel versions
- * Returns 0 on success, and -1 on error (with errno set)
+ * Returns 0 on success, and -1 on error
*/
int
zfs_version_print(void)
{
- char zver_userland[128];
- char zver_kernel[128];
-
- zfs_version_userland(zver_userland, sizeof (zver_userland));
+ (void) puts(ZFS_META_ALIAS);
- (void) printf("%s\n", zver_userland);
-
- if (zfs_version_kernel(zver_kernel, sizeof (zver_kernel)) == -1) {
+ char *kver = zfs_version_kernel();
+ if (kver == NULL) {
fprintf(stderr, "zfs_version_kernel() failed: %s\n",
- strerror(errno));
+ zfs_strerror(errno));
return (-1);
}
- (void) printf("zfs-kmod-%s\n", zver_kernel);
-
+ (void) printf("zfs-kmod-%s\n", kver);
+ free(kver);
return (0);
}
/*
+ * Returns an nvlist with both zfs userland and kernel versions.
+ * Returns NULL on error.
+ */
+nvlist_t *
+zfs_version_nvlist(void)
+{
+ nvlist_t *nvl;
+ char kmod_ver[64];
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
+ return (NULL);
+ if (nvlist_add_string(nvl, "userland", ZFS_META_ALIAS) != 0)
+ goto err;
+ char *kver = zfs_version_kernel();
+ if (kver == NULL) {
+ fprintf(stderr, "zfs_version_kernel() failed: %s\n",
+ zfs_strerror(errno));
+ goto err;
+ }
+ (void) snprintf(kmod_ver, 64, "zfs-kmod-%s", kver);
+ if (nvlist_add_string(nvl, "kernel", kmod_ver) != 0)
+ goto err;
+ return (nvl);
+err:
+ nvlist_free(nvl);
+ return (NULL);
+}
+
+/*
* Return 1 if the user requested ANSI color output, and our terminal supports
* it. Return 0 for no color.
*/
-static int
+int
use_color(void)
{
static int use_color = -1;
@@ -2040,31 +2218,39 @@ use_color(void)
}
/*
- * color_start() and color_end() are used for when you want to colorize a block
- * of text. For example:
+ * The functions color_start() and color_end() are used for when you want
+ * to colorize a block of text.
*
- * color_start(ANSI_RED_FG)
+ * For example:
+ * color_start(ANSI_RED)
* printf("hello");
* printf("world");
* color_end();
*/
void
-color_start(char *color)
+color_start(const char *color)
{
- if (use_color())
- printf("%s", color);
+ if (color && use_color()) {
+ fputs(color, stdout);
+ fflush(stdout);
+ }
}
void
color_end(void)
{
- if (use_color())
- printf(ANSI_RESET);
+ if (use_color()) {
+ fputs(ANSI_RESET, stdout);
+ fflush(stdout);
+ }
+
}
-/* printf() with a color. If color is NULL, then do a normal printf. */
+/*
+ * printf() with a color. If color is NULL, then do a normal printf.
+ */
int
-printf_color(char *color, char *format, ...)
+printf_color(const char *color, const char *format, ...)
{
va_list aptr;
int rc;
@@ -2081,3 +2267,196 @@ printf_color(char *color, char *format, ...)
return (rc);
}
+
+/* PATH + 5 env vars + a NULL entry = 7 */
+#define ZPOOL_VDEV_SCRIPT_ENV_COUNT 7
+
+/*
+ * There's a few places where ZFS will call external scripts (like the script
+ * in zpool.d/ and `zfs_prepare_disk`). These scripts are called with a
+ * reduced $PATH, and some vdev specific environment vars set. This function
+ * will allocate an populate the environment variable array that is passed to
+ * these scripts. The user must free the arrays with zpool_vdev_free_env() when
+ * they are done.
+ *
+ * The following env vars will be set (but value could be blank):
+ *
+ * POOL_NAME
+ * VDEV_PATH
+ * VDEV_UPATH
+ * VDEV_ENC_SYSFS_PATH
+ *
+ * In addition, you can set an optional environment variable named 'opt_key'
+ * to 'opt_val' if you want.
+ *
+ * Returns allocated env[] array on success, NULL otherwise.
+ */
+char **
+zpool_vdev_script_alloc_env(const char *pool_name,
+ const char *vdev_path, const char *vdev_upath,
+ const char *vdev_enc_sysfs_path, const char *opt_key, const char *opt_val)
+{
+ char **env = NULL;
+ int rc;
+
+ env = calloc(ZPOOL_VDEV_SCRIPT_ENV_COUNT, sizeof (*env));
+ if (!env)
+ return (NULL);
+
+ env[0] = strdup("PATH=/bin:/sbin:/usr/bin:/usr/sbin");
+ if (!env[0])
+ goto error;
+
+ /* Setup our custom environment variables */
+ rc = asprintf(&env[1], "POOL_NAME=%s", pool_name ? pool_name : "");
+ if (rc == -1) {
+ env[1] = NULL;
+ goto error;
+ }
+
+ rc = asprintf(&env[2], "VDEV_PATH=%s", vdev_path ? vdev_path : "");
+ if (rc == -1) {
+ env[2] = NULL;
+ goto error;
+ }
+
+ rc = asprintf(&env[3], "VDEV_UPATH=%s", vdev_upath ? vdev_upath : "");
+ if (rc == -1) {
+ env[3] = NULL;
+ goto error;
+ }
+
+ rc = asprintf(&env[4], "VDEV_ENC_SYSFS_PATH=%s",
+ vdev_enc_sysfs_path ? vdev_enc_sysfs_path : "");
+ if (rc == -1) {
+ env[4] = NULL;
+ goto error;
+ }
+
+ if (opt_key != NULL) {
+ rc = asprintf(&env[5], "%s=%s", opt_key,
+ opt_val ? opt_val : "");
+ if (rc == -1) {
+ env[5] = NULL;
+ goto error;
+ }
+ }
+
+ return (env);
+
+error:
+ for (int i = 0; i < ZPOOL_VDEV_SCRIPT_ENV_COUNT; i++)
+ free(env[i]);
+
+ free(env);
+
+ return (NULL);
+}
+
+/*
+ * Free the env[] array that was allocated by zpool_vdev_script_alloc_env().
+ */
+void
+zpool_vdev_script_free_env(char **env)
+{
+ for (int i = 0; i < ZPOOL_VDEV_SCRIPT_ENV_COUNT; i++)
+ free(env[i]);
+
+ free(env);
+}
+
+/*
+ * Prepare a disk by (optionally) running a program before labeling the disk.
+ * This can be useful for installing disk firmware or doing some pre-flight
+ * checks on the disk before it becomes part of the pool. The program run is
+ * located at ZFSEXECDIR/zfs_prepare_disk
+ * (E.x: /usr/local/libexec/zfs/zfs_prepare_disk).
+ *
+ * Return 0 on success, non-zero on failure.
+ */
+int
+zpool_prepare_disk(zpool_handle_t *zhp, nvlist_t *vdev_nv,
+ const char *prepare_str, char **lines[], int *lines_cnt)
+{
+ const char *script_path = ZFSEXECDIR "/zfs_prepare_disk";
+ const char *pool_name;
+ int rc = 0;
+
+ /* Path to script and a NULL entry */
+ char *argv[2] = {(char *)script_path};
+ char **env = NULL;
+ const char *path = NULL, *enc_sysfs_path = NULL;
+ char *upath;
+ *lines_cnt = 0;
+
+ if (access(script_path, X_OK) != 0) {
+ /* No script, nothing to do */
+ return (0);
+ }
+
+ (void) nvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_PATH, &path);
+ (void) nvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH,
+ &enc_sysfs_path);
+
+ upath = zfs_get_underlying_path(path);
+ pool_name = zhp ? zpool_get_name(zhp) : NULL;
+
+ env = zpool_vdev_script_alloc_env(pool_name, path, upath,
+ enc_sysfs_path, "VDEV_PREPARE", prepare_str);
+
+ free(upath);
+
+ if (env == NULL) {
+ return (ENOMEM);
+ }
+
+ rc = libzfs_run_process_get_stdout(script_path, argv, env, lines,
+ lines_cnt);
+
+ zpool_vdev_script_free_env(env);
+
+ return (rc);
+}
+
+/*
+ * Optionally run a script and then label a disk. The script can be used to
+ * prepare a disk for inclusion into the pool. For example, it might update
+ * the disk's firmware or check its health.
+ *
+ * The 'name' provided is the short name, stripped of any leading
+ * /dev path, and is passed to zpool_label_disk. vdev_nv is the nvlist for
+ * the vdev. prepare_str is a string that gets passed as the VDEV_PREPARE
+ * env variable to the script.
+ *
+ * The following env vars are passed to the script:
+ *
+ * POOL_NAME: The pool name (blank during zpool create)
+ * VDEV_PREPARE: Reason why the disk is being prepared for inclusion:
+ * "create", "add", "replace", or "autoreplace"
+ * VDEV_PATH: Path to the disk
+ * VDEV_UPATH: One of the 'underlying paths' to the disk. This is
+ * useful for DM devices.
+ * VDEV_ENC_SYSFS_PATH: Path to the disk's enclosure sysfs path, if available.
+ *
+ * Note, some of these values can be blank.
+ *
+ * Return 0 on success, non-zero otherwise.
+ */
+int
+zpool_prepare_and_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp,
+ const char *name, nvlist_t *vdev_nv, const char *prepare_str,
+ char **lines[], int *lines_cnt)
+{
+ int rc;
+ char vdev_path[MAXPATHLEN];
+ (void) snprintf(vdev_path, sizeof (vdev_path), "%s/%s", DISK_ROOT,
+ name);
+
+ /* zhp will be NULL when creating a pool */
+ rc = zpool_prepare_disk(zhp, vdev_nv, prepare_str, lines, lines_cnt);
+ if (rc != 0)
+ return (rc);
+
+ rc = zpool_label_disk(hdl, zhp, name);
+ return (rc);
+}