Index: head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c =================================================================== --- head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c +++ head/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c @@ -7235,7 +7235,7 @@ fnvlist_free(nvl); if (ret != 0) { - const char *err_msg; + const char *err_msg = NULL; char errbuf[1024]; (void) snprintf(errbuf, sizeof (errbuf), @@ -7259,11 +7259,13 @@ err_msg = "out of space"; break; default: - err_msg = "unknown error"; + (void) zfs_standard_error(g_zfs, ret, errbuf); break; } - (void) fprintf(stderr, "%s: %s\n", errbuf, - dgettext(TEXT_DOMAIN, err_msg)); + if (err_msg != NULL) { + (void) fprintf(stderr, "%s: %s\n", errbuf, + dgettext(TEXT_DOMAIN, err_msg)); + } } return (ret != 0); @@ -7280,7 +7282,7 @@ char c; char *progbuf, *filename, *poolname; size_t progsize, progread; - nvlist_t *outnvl; + nvlist_t *outnvl = NULL; uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT; uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT; boolean_t sync_flag = B_TRUE, json_output = B_FALSE; @@ -7420,7 +7422,8 @@ * falling back on strerror() for an unexpected return code. */ char *errstring = NULL; - if (nvlist_exists(outnvl, ZCP_RET_ERROR)) { + const char *msg = gettext("Channel program execution failed"); + if (outnvl != NULL && nvlist_exists(outnvl, ZCP_RET_ERROR)) { (void) nvlist_lookup_string(outnvl, ZCP_RET_ERROR, &errstring); if (errstring == NULL) @@ -7449,12 +7452,11 @@ "programs must be run as root."; break; default: - errstring = strerror(ret); + (void) zfs_standard_error(g_zfs, ret, msg); } } - (void) fprintf(stderr, - gettext("Channel program execution failed:\n%s\n"), - errstring); + if (errstring != NULL) + (void) fprintf(stderr, "%s:\n%s\n", msg, errstring); } else { if (json_output) { (void) nvlist_print_json(stdout, outnvl); Index: head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h =================================================================== --- head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h +++ head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h @@ -22,7 +22,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011 Pawel Jakub Dawidek. All rights reserved. - * Copyright (c) 2011, 2017 by Delphix. All rights reserved. + * Copyright (c) 2011, 2020 by Delphix. All rights reserved. * Copyright 2019 Joyent, Inc. * Copyright (c) 2012 Martin Matuska . All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. @@ -142,6 +142,7 @@ EZFS_INITIALIZING, /* currently initializing */ EZFS_NO_INITIALIZE, /* no active initialize */ EZFS_WRONG_PARENT, /* invalid parent dataset (e.g ZVOL) */ + EZFS_IOC_NOTSUPPORTED, /* operation not supported by zfs module */ EZFS_UNKNOWN } zfs_error_t; Index: head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c =================================================================== --- head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c +++ head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c @@ -22,7 +22,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2019 Joyent, Inc. - * Copyright (c) 2011, 2017 by Delphix. All rights reserved. + * Copyright (c) 2011, 2018 by Delphix. All rights reserved. * Copyright 2016 Igor Kozhukhov * Copyright (c) 2017 Datto Inc. */ @@ -207,6 +207,9 @@ case EZFS_NOTSUP: return (dgettext(TEXT_DOMAIN, "operation not supported " "on this dataset")); + case EZFS_IOC_NOTSUPPORTED: + return (dgettext(TEXT_DOMAIN, "operation not supported by " + "zfs kernel module")); case EZFS_ACTIVE_SPARE: return (dgettext(TEXT_DOMAIN, "pool has active shared spare " "device")); @@ -433,6 +436,22 @@ case EREMOTEIO: zfs_verror(hdl, EZFS_ACTIVE_POOL, 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 " + "be required to enable this operation.")); + zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap); + break; + case ZFS_ERR_IOC_ARG_UNAVAIL: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "the loaded zfs " + "module does not support an option for this operation. " + "A reboot may be required to enable this option.")); + zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap); + break; + case ZFS_ERR_IOC_ARG_REQUIRED: + case ZFS_ERR_IOC_ARG_BADTYPE: + zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap); + break; default: zfs_error_aux(hdl, strerror(error)); zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap); @@ -541,6 +560,22 @@ break; case ZFS_ERR_WRONG_PARENT: zfs_verror(hdl, EZFS_WRONG_PARENT, 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 " + "be required to enable this operation.")); + zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap); + break; + case ZFS_ERR_IOC_ARG_UNAVAIL: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "the loaded zfs " + "module does not support an option for this operation. " + "A reboot may be required to enable this option.")); + zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap); + break; + case ZFS_ERR_IOC_ARG_REQUIRED: + case ZFS_ERR_IOC_ARG_BADTYPE: + zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap); break; default: zfs_error_aux(hdl, strerror(error)); Index: head/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c =================================================================== --- head/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c +++ head/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c @@ -20,7 +20,7 @@ */ /* - * Copyright (c) 2012, 2017 by Delphix. All rights reserved. + * Copyright (c) 2012, 2018 by Delphix. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2014 Integros [integros.com] * Copyright 2017 RackTop Systems. @@ -80,6 +80,9 @@ #include #include #include +#ifdef ZFS_DEBUG +#include +#endif #include #include #include @@ -99,6 +102,42 @@ static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; static int g_refcount; +#ifdef ZFS_DEBUG +static zfs_ioc_t fail_ioc_cmd; +static zfs_errno_t fail_ioc_err; + +static void +libzfs_core_debug_ioc(void) +{ + /* + * To test running newer user space binaries with kernel's + * that don't yet support an ioctl or a new ioctl arg we + * provide an override to intentionally fail an ioctl. + * + * USAGE: + * The override variable, ZFS_IOC_TEST, is of the form "cmd:err" + * + * For example, to fail a ZFS_IOC_POOL_CHECKPOINT with a + * ZFS_ERR_IOC_CMD_UNAVAIL, the string would be "0x5a4d:1029" + * + * $ sudo sh -c "ZFS_IOC_TEST=0x5a4d:1029 zpool checkpoint tank" + * cannot checkpoint 'tank': the loaded zfs module does not support + * this operation. A reboot may be required to enable this operation. + */ + if (fail_ioc_cmd == 0) { + char *ioc_test = getenv("ZFS_IOC_TEST"); + unsigned int ioc_num = 0, ioc_err = 0; + + if (ioc_test != NULL && + sscanf(ioc_test, "%i:%i", &ioc_num, &ioc_err) == 2 && + ioc_num < ZFS_IOC_LAST) { + fail_ioc_cmd = ioc_num; + fail_ioc_err = ioc_err; + } + } +} +#endif + int libzfs_core_init(void) { @@ -111,6 +150,10 @@ } } g_refcount++; + +#ifdef ZFS_DEBUG + libzfs_core_debug_ioc(); +#endif (void) pthread_mutex_unlock(&g_lock); return (0); @@ -146,6 +189,11 @@ ASSERT3S(g_refcount, >, 0); VERIFY3S(g_fd, !=, -1); + +#ifdef ZFS_DEBUG + if (ioc == fail_ioc_cmd) + return (fail_ioc_err); +#endif if (name != NULL) (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name)); Index: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c =================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c +++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c @@ -27,7 +27,7 @@ * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2014, 2016 Joyent, Inc. All rights reserved. - * Copyright (c) 2011, 2017 by Delphix. All rights reserved. + * Copyright (c) 2011, 2018 by Delphix. All rights reserved. * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. * Copyright (c) 2013 Steven Hartland. All rights reserved. * Copyright (c) 2014 Integros [integros.com] @@ -61,8 +61,9 @@ * * zfs_ioc_t ioc * The ioctl request number, which userland will pass to ioctl(2). - * The ioctl numbers can change from release to release, because - * the caller (libzfs) must be matched to the kernel. + * We want newer versions of libzfs and libzfs_core to run against + * existing zfs kernel modules (i.e. a deferred reboot after an update). + * Therefore the ioctl numbers cannot change from release to release. * * zfs_secpolicy_func_t *secpolicy * This function will be called before the zfs_ioc_func_t, to @@ -88,6 +89,10 @@ * Multiple checks can be or-ed together (e.g. POOL_CHECK_SUSPENDED | * POOL_CHECK_READONLY). * + * zfs_ioc_key_t *nvl_keys + * The list of expected/allowable innvl input keys. This list is used + * to validate the nvlist input to the ioctl. + * * boolean_t smush_outnvlist * If smush_outnvlist is true, then the output is presumed to be a * list of errors, and it will be "smushed" down to fit into the @@ -136,7 +141,17 @@ * use the outnvl if they succeed, because the caller can not * distinguish between the operation failing, and * deserialization failing. + * + * + * IOCTL Interface Errors + * + * The following ioctl input errors can be returned: + * ZFS_ERR_IOC_CMD_UNAVAIL the ioctl number is not supported by kernel + * ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel + * ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing + * ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type */ + #ifdef __FreeBSD__ #include "opt_kstack_pages.h" #endif @@ -208,6 +223,10 @@ #include "lua.h" #include "lauxlib.h" +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) nitems(x) +#endif + static struct cdev *zfsdev; extern void zfs_init(void); @@ -222,7 +241,38 @@ typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_t *); typedef int zfs_secpolicy_func_t(zfs_cmd_t *, nvlist_t *, cred_t *); +/* + * IOC Keys are used to document and validate user->kernel interface inputs. + * See zfs_keys_recv_new for an example declaration. Any key name that is not + * listed will be rejected as input. + * + * The keyname 'optional' is always allowed, and must be an nvlist if present. + * Arguments which older kernels can safely ignore can be placed under the + * "optional" key. + * + * When adding new keys to an existing ioc for new functionality, consider: + * - adding an entry into zfs_sysfs.c zfs_features[] list + * - updating the libzfs_input_check.c test utility + * + * Note: in the ZK_WILDCARDLIST case, the name serves as documentation + * for the expected name (bookmark, snapshot, property, etc) but there + * is no validation in the preflight zfs_check_input_nvpairs() check. + */ typedef enum { + ZK_OPTIONAL = 1 << 0, /* pair is optional */ + ZK_WILDCARDLIST = 1 << 1, /* one or more unspecified key names */ +} ioc_key_flag_t; + +/* DATA_TYPE_ANY is used when zkey_type can vary. */ +#define DATA_TYPE_ANY DATA_TYPE_UNKNOWN + +typedef struct zfs_ioc_key { + const char *zkey_name; + data_type_t zkey_type; + ioc_key_flag_t zkey_flags; +} zfs_ioc_key_t; + +typedef enum { NO_NAME, POOL_NAME, DATASET_NAME, @@ -244,6 +294,8 @@ zfs_ioc_poolcheck_t zvec_pool_check; boolean_t zvec_smush_outnvlist; const char *zvec_name; + const zfs_ioc_key_t *zvec_nvl_keys; + size_t zvec_nvl_key_count; } zfs_ioc_vec_t; /* This array is indexed by zfs_userquota_prop_t */ @@ -865,8 +917,8 @@ nvpair_t *pair, *nextpair; int error = 0; - if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) - return (SET_ERROR(EINVAL)); + snaps = fnvlist_lookup_nvlist(innvl, "snaps"); + for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; pair = nextpair) { nextpair = nvlist_next_nvpair(snaps, pair); @@ -1042,8 +1094,8 @@ int error; nvpair_t *pair; - if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) - return (SET_ERROR(EINVAL)); + snaps = fnvlist_lookup_nvlist(innvl, "snaps"); + for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; pair = nvlist_next_nvpair(snaps, pair)) { char *name = nvpair_name(pair); @@ -1063,7 +1115,7 @@ } /* - * Check for permission to create each snapshot in the nvlist. + * Check for permission to create each bookmark in the nvlist. */ /* ARGSUSED */ static int @@ -1292,9 +1344,7 @@ nvlist_t *holds; int error; - error = nvlist_lookup_nvlist(innvl, "holds", &holds); - if (error != 0) - return (SET_ERROR(EINVAL)); + holds = fnvlist_lookup_nvlist(innvl, "holds"); for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; pair = nvlist_next_nvpair(holds, pair)) { @@ -1349,12 +1399,14 @@ return (0); error = zfs_secpolicy_snapshot_perms(zc->zc_name, cr); - if (error == 0) - error = zfs_secpolicy_hold(zc, innvl, cr); - if (error == 0) - error = zfs_secpolicy_release(zc, innvl, cr); - if (error == 0) - error = zfs_secpolicy_destroy(zc, innvl, cr); + if (innvl != NULL) { + if (error == 0) + error = zfs_secpolicy_hold(zc, innvl, cr); + if (error == 0) + error = zfs_secpolicy_release(zc, innvl, cr); + if (error == 0) + error = zfs_secpolicy_destroy(zc, innvl, cr); + } return (error); } @@ -3309,6 +3361,13 @@ * * outnvl: propname -> error code (int32) */ + +static const zfs_ioc_key_t zfs_keys_create[] = { + {"type", DATA_TYPE_INT32, 0}, + {"props", DATA_TYPE_NVLIST, ZK_OPTIONAL}, + {"hidden_args", DATA_TYPE_NVLIST, ZK_OPTIONAL}, +}; + static int zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) { @@ -3316,13 +3375,10 @@ zfs_creat_t zct = { 0 }; nvlist_t *nvprops = NULL; void (*cbfunc)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx); - int32_t type32; dmu_objset_type_t type; boolean_t is_insensitive = B_FALSE; - if (nvlist_lookup_int32(innvl, "type", &type32) != 0) - return (SET_ERROR(EINVAL)); - type = type32; + type = (dmu_objset_type_t)fnvlist_lookup_int32(innvl, "type"); (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); switch (type) { @@ -3427,6 +3483,12 @@ * * outnvl: propname -> error code (int32) */ +static const zfs_ioc_key_t zfs_keys_clone[] = { + {"origin", DATA_TYPE_STRING, 0}, + {"props", DATA_TYPE_NVLIST, ZK_OPTIONAL}, + {"hidden_args", DATA_TYPE_NVLIST, ZK_OPTIONAL}, +}; + static int zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) { @@ -3434,8 +3496,7 @@ nvlist_t *nvprops = NULL; char *origin_name; - if (nvlist_lookup_string(innvl, "origin", &origin_name) != 0) - return (SET_ERROR(EINVAL)); + origin_name = fnvlist_lookup_string(innvl, "origin"); (void) nvlist_lookup_nvlist(innvl, "props", &nvprops); if (strchr(fsname, '@') || @@ -3460,6 +3521,10 @@ return (error); } +static const zfs_ioc_key_t zfs_keys_remap[] = { + /* no nvl keys */ +}; + /* ARGSUSED */ static int zfs_ioc_remap(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) @@ -3479,6 +3544,11 @@ * * outnvl: snapshot -> error code (int32) */ +static const zfs_ioc_key_t zfs_keys_snapshot[] = { + {"snaps", DATA_TYPE_NVLIST, 0}, + {"props", DATA_TYPE_NVLIST, ZK_OPTIONAL}, +}; + static int zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) { @@ -3494,8 +3564,7 @@ if ((error = zfs_check_userprops(props)) != 0) return (error); - if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) - return (SET_ERROR(EINVAL)); + snaps = fnvlist_lookup_nvlist(innvl, "snaps"); poollen = strlen(poolname); for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL; pair = nvlist_next_nvpair(snaps, pair)) { @@ -3546,6 +3615,10 @@ /* * innvl: "message" -> string */ +static const zfs_ioc_key_t zfs_keys_log_history[] = { + {"message", DATA_TYPE_STRING, 0}, +}; + /* ARGSUSED */ static int zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) @@ -3569,10 +3642,7 @@ if (error != 0) return (error); - if (nvlist_lookup_string(innvl, "message", &message) != 0) { - spa_close(spa, FTAG); - return (SET_ERROR(EINVAL)); - } + message = fnvlist_lookup_string(innvl, "message"); if (spa_version(spa) < SPA_VERSION_ZPOOL_HISTORY) { spa_close(spa, FTAG); @@ -3585,6 +3655,10 @@ } #ifdef __FreeBSD__ +static const zfs_ioc_key_t zfs_keys_nextboot[] = { + {"command", DATA_TYPE_STRING, 0}, +}; + static int zfs_ioc_nextboot(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) { @@ -3602,9 +3676,7 @@ if (nvlist_lookup_uint64(innvl, ZPOOL_CONFIG_GUID, &vdev_guid) != 0) return (EINVAL); - if (nvlist_lookup_string(innvl, - "command", &command) != 0) - return (EINVAL); + command = fnvlist_lookup_string(innvl, "command"); mutex_enter(&spa_namespace_lock); spa = spa_by_guid(pool_guid, vdev_guid); @@ -3721,6 +3793,11 @@ * outnvl: snapshot -> error code (int32) * */ +static const zfs_ioc_key_t zfs_keys_destroy_snaps[] = { + {"snaps", DATA_TYPE_NVLIST, 0}, + {"defer", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, +}; + /* ARGSUSED */ static int zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) @@ -3730,8 +3807,7 @@ nvpair_t *pair; boolean_t defer; - if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) - return (SET_ERROR(EINVAL)); + snaps = fnvlist_lookup_nvlist(innvl, "snaps"); defer = nvlist_exists(innvl, "defer"); poollen = strlen(poolname); @@ -3764,6 +3840,10 @@ * outnvl: bookmark -> error code (int32) * */ +static const zfs_ioc_key_t zfs_keys_bookmark[] = { + {"...", DATA_TYPE_STRING, ZK_WILDCARDLIST}, +}; + /* ARGSUSED */ static int zfs_ioc_bookmark(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) @@ -3801,6 +3881,10 @@ * } * */ +static const zfs_ioc_key_t zfs_keys_get_bookmarks[] = { + {"...", DATA_TYPE_BOOLEAN, ZK_WILDCARDLIST | ZK_OPTIONAL}, +}; + static int zfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) { @@ -3815,6 +3899,10 @@ * outnvl: bookmark -> error code (int32) * */ +static const zfs_ioc_key_t zfs_keys_destroy_bookmarks[] = { + {"...", DATA_TYPE_BOOLEAN, ZK_WILDCARDLIST}, +}; + static int zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) @@ -3847,6 +3935,14 @@ return (error); } +static const zfs_ioc_key_t zfs_keys_channel_program[] = { + {"program", DATA_TYPE_STRING, 0}, + {"arg", DATA_TYPE_ANY, 0}, + {"sync", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL}, + {"instrlimit", DATA_TYPE_UINT64, ZK_OPTIONAL}, + {"memlimit", DATA_TYPE_UINT64, ZK_OPTIONAL}, +}; + static int zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) @@ -3856,9 +3952,7 @@ boolean_t sync_flag; nvpair_t *nvarg = NULL; - if (0 != nvlist_lookup_string(innvl, ZCP_ARG_PROGRAM, &program)) { - return (EINVAL); - } + program = fnvlist_lookup_string(innvl, ZCP_ARG_PROGRAM); if (0 != nvlist_lookup_boolean_value(innvl, ZCP_ARG_SYNC, &sync_flag)) { sync_flag = B_TRUE; } @@ -3868,9 +3962,7 @@ if (0 != nvlist_lookup_uint64(innvl, ZCP_ARG_MEMLIMIT, &memlimit)) { memlimit = ZCP_DEFAULT_MEMLIMIT; } - if (0 != nvlist_lookup_nvpair(innvl, ZCP_ARG_ARGLIST, &nvarg)) { - return (EINVAL); - } + nvarg = fnvlist_lookup_nvpair(innvl, ZCP_ARG_ARGLIST); if (instrlimit == 0 || instrlimit > zfs_lua_max_instrlimit) return (EINVAL); @@ -3885,6 +3977,10 @@ * innvl: unused * outnvl: empty */ +static const zfs_ioc_key_t zfs_keys_pool_checkpoint[] = { + /* no nvl keys */ +}; + /* ARGSUSED */ static int zfs_ioc_pool_checkpoint(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) @@ -3896,6 +3992,10 @@ * innvl: unused * outnvl: empty */ +static const zfs_ioc_key_t zfs_keys_pool_discard_checkpoint[] = { + /* no nvl keys */ +}; + /* ARGSUSED */ static int zfs_ioc_pool_discard_checkpoint(const char *poolname, nvlist_t *innvl, @@ -3955,6 +4055,11 @@ * } * */ +static const zfs_ioc_key_t zfs_keys_pool_initialize[] = { + {ZPOOL_INITIALIZE_COMMAND, DATA_TYPE_UINT64, 0}, + {ZPOOL_INITIALIZE_VDEVS, DATA_TYPE_NVLIST, 0} +}; + static int zfs_ioc_pool_initialize(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) { @@ -4020,6 +4125,10 @@ * outnvl: "target" -> name of most recent snapshot * } */ +static const zfs_ioc_key_t zfs_keys_rollback[] = { + {"target", DATA_TYPE_STRING, ZK_OPTIONAL}, +}; + /* ARGSUSED */ static int zfs_ioc_rollback(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) @@ -5167,13 +5276,33 @@ return (error); } +/* + * Reopen all the vdevs associated with the pool. + * + * innvl: { + * "scrub_restart" -> when true and scrub is running, allow to restart + * scrub as the side effect of the reopen (boolean). + * } + * + * outnvl is unused + */ +static const zfs_ioc_key_t zfs_keys_pool_reopen[] = { + {"scrub_restart", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL}, +}; + static int -zfs_ioc_pool_reopen(zfs_cmd_t *zc) +zfs_ioc_pool_reopen(const char *pool, nvlist_t *innvl, nvlist_t *outnvl) { spa_t *spa; int error; + boolean_t scrub_restart = B_TRUE; - error = spa_open(zc->zc_name, &spa, FTAG); + if (innvl) { + scrub_restart = fnvlist_lookup_boolean_value(innvl, + "scrub_restart"); + } + + error = spa_open(pool, &spa, FTAG); if (error != 0) return (error); @@ -5185,7 +5314,8 @@ * the scan as a side effect of the reopen. Otherwise, let * vdev_open() decided if a resilver is required. */ - spa->spa_scrub_reopen = dsl_scan_resilvering(spa->spa_dsl_pool); + spa->spa_scrub_reopen = (!scrub_restart && + dsl_scan_resilvering(spa->spa_dsl_pool)); vdev_reopen(spa->spa_root_vdev); spa->spa_scrub_reopen = B_FALSE; @@ -5193,6 +5323,7 @@ spa_close(spa, FTAG); return (0); } + /* * inputs: * zc_name name of filesystem @@ -5789,6 +5920,11 @@ * ... * } */ +static const zfs_ioc_key_t zfs_keys_hold[] = { + {"holds", DATA_TYPE_NVLIST, 0}, + {"cleanup_fd", DATA_TYPE_INT32, ZK_OPTIONAL}, +}; + /* ARGSUSED */ static int zfs_ioc_hold(const char *pool, nvlist_t *args, nvlist_t *errlist) @@ -5799,9 +5935,7 @@ int error; minor_t minor = 0; - error = nvlist_lookup_nvlist(args, "holds", &holds); - if (error != 0) - return (SET_ERROR(EINVAL)); + holds = fnvlist_lookup_nvlist(args, "holds"); /* make sure the user didn't pass us any invalid (empty) tags */ for (pair = nvlist_next_nvpair(holds, NULL); pair != NULL; @@ -5836,11 +5970,14 @@ * ... * } */ +static const zfs_ioc_key_t zfs_keys_get_holds[] = { + /* no nvl keys */ +}; + /* ARGSUSED */ static int zfs_ioc_get_holds(const char *snapname, nvlist_t *args, nvlist_t *outnvl) { - ASSERT3P(args, ==, NULL); return (dsl_dataset_get_holds(snapname, outnvl)); } @@ -5855,6 +5992,10 @@ * ... * } */ +static const zfs_ioc_key_t zfs_keys_release[] = { + {"...", DATA_TYPE_NVLIST, ZK_WILDCARDLIST}, +}; + /* ARGSUSED */ static int zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist) @@ -5913,6 +6054,10 @@ * "uncompressed" -> uncompressed space in bytes * } */ +static const zfs_ioc_key_t zfs_keys_space_snaps[] = { + {"firstsnap", DATA_TYPE_STRING, 0}, +}; + static int zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl) { @@ -5922,8 +6067,7 @@ char *firstsnap; uint64_t used, comp, uncomp; - if (nvlist_lookup_string(innvl, "firstsnap", &firstsnap) != 0) - return (SET_ERROR(EINVAL)); + firstsnap = fnvlist_lookup_string(innvl, "firstsnap"); error = dsl_pool_hold(lastsnap, FTAG, &dp); if (error != 0) @@ -5991,6 +6135,17 @@ * * outnvl is unused */ +static const zfs_ioc_key_t zfs_keys_send_new[] = { + {"fd", DATA_TYPE_INT32, 0}, + {"fromsnap", DATA_TYPE_STRING, ZK_OPTIONAL}, + {"largeblockok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, + {"embedok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, + {"compressok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, + {"rawok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, + {"resume_object", DATA_TYPE_UINT64, ZK_OPTIONAL}, + {"resume_offset", DATA_TYPE_UINT64, ZK_OPTIONAL}, +}; + /* ARGSUSED */ static int zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) @@ -6006,9 +6161,7 @@ uint64_t resumeobj = 0; uint64_t resumeoff = 0; - error = nvlist_lookup_int32(innvl, "fd", &fd); - if (error != 0) - return (SET_ERROR(EINVAL)); + fd = fnvlist_lookup_int32(innvl, "fd"); (void) nvlist_lookup_string(innvl, "fromsnap", &fromname); @@ -6065,6 +6218,15 @@ * "space" -> bytes of space (uint64) * } */ +static const zfs_ioc_key_t zfs_keys_send_space[] = { + {"from", DATA_TYPE_STRING, ZK_OPTIONAL}, + {"fromsnap", DATA_TYPE_STRING, ZK_OPTIONAL}, + {"largeblockok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, + {"embedok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, + {"compressok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, + {"rawok", DATA_TYPE_BOOLEAN, ZK_OPTIONAL}, +}; + static int zfs_ioc_send_space(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) { @@ -6153,6 +6315,10 @@ * * onvl is unused */ +static const zfs_ioc_key_t zfs_keys_pool_sync[] = { + {"force", DATA_TYPE_BOOLEAN_VALUE, 0}, +}; + /* ARGSUSED */ static int zfs_ioc_pool_sync(const char *pool, nvlist_t *innvl, nvlist_t *onvl) @@ -6206,7 +6372,7 @@ zfs_ioctl_register(const char *name, zfs_ioc_t ioc, zfs_ioc_func_t *func, zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, zfs_ioc_poolcheck_t pool_check, boolean_t smush_outnvlist, - boolean_t allow_log) + boolean_t allow_log, const zfs_ioc_key_t *nvl_keys, size_t num_keys) { zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; @@ -6225,6 +6391,8 @@ vec->zvec_pool_check = pool_check; vec->zvec_smush_outnvlist = smush_outnvlist; vec->zvec_allow_log = allow_log; + vec->zvec_nvl_keys = nvl_keys; + vec->zvec_nvl_key_count = num_keys; } static void @@ -6287,89 +6455,115 @@ { zfs_ioctl_register("snapshot", ZFS_IOC_SNAPSHOT, zfs_ioc_snapshot, zfs_secpolicy_snapshot, POOL_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, + zfs_keys_snapshot, ARRAY_SIZE(zfs_keys_snapshot)); zfs_ioctl_register("log_history", ZFS_IOC_LOG_HISTORY, zfs_ioc_log_history, zfs_secpolicy_log_history, NO_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE, + zfs_keys_log_history, ARRAY_SIZE(zfs_keys_log_history)); zfs_ioctl_register("space_snaps", ZFS_IOC_SPACE_SNAPS, zfs_ioc_space_snaps, zfs_secpolicy_read, DATASET_NAME, - POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); + POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE, + zfs_keys_space_snaps, ARRAY_SIZE(zfs_keys_space_snaps)); zfs_ioctl_register("send", ZFS_IOC_SEND_NEW, zfs_ioc_send_new, zfs_secpolicy_send_new, DATASET_NAME, - POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); + POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE, + zfs_keys_send_new, ARRAY_SIZE(zfs_keys_send_new)); zfs_ioctl_register("send_space", ZFS_IOC_SEND_SPACE, zfs_ioc_send_space, zfs_secpolicy_read, DATASET_NAME, - POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); + POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE, + zfs_keys_send_space, ARRAY_SIZE(zfs_keys_send_space)); zfs_ioctl_register("create", ZFS_IOC_CREATE, zfs_ioc_create, zfs_secpolicy_create_clone, DATASET_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, + zfs_keys_create, ARRAY_SIZE(zfs_keys_create)); zfs_ioctl_register("clone", ZFS_IOC_CLONE, zfs_ioc_clone, zfs_secpolicy_create_clone, DATASET_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, + zfs_keys_clone, ARRAY_SIZE(zfs_keys_clone)); zfs_ioctl_register("remap", ZFS_IOC_REMAP, zfs_ioc_remap, zfs_secpolicy_remap, DATASET_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE, + zfs_keys_remap, ARRAY_SIZE(zfs_keys_remap)); zfs_ioctl_register("destroy_snaps", ZFS_IOC_DESTROY_SNAPS, zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, POOL_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, + zfs_keys_destroy_snaps, ARRAY_SIZE(zfs_keys_destroy_snaps)); zfs_ioctl_register("hold", ZFS_IOC_HOLD, zfs_ioc_hold, zfs_secpolicy_hold, POOL_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, + zfs_keys_hold, ARRAY_SIZE(zfs_keys_hold)); zfs_ioctl_register("release", ZFS_IOC_RELEASE, zfs_ioc_release, zfs_secpolicy_release, POOL_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, + zfs_keys_release, ARRAY_SIZE(zfs_keys_release)); zfs_ioctl_register("get_holds", ZFS_IOC_GET_HOLDS, zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, - POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); + POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE, + zfs_keys_get_holds, ARRAY_SIZE(zfs_keys_get_holds)); zfs_ioctl_register("rollback", ZFS_IOC_ROLLBACK, zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE, + zfs_keys_rollback, ARRAY_SIZE(zfs_keys_rollback)); zfs_ioctl_register("bookmark", ZFS_IOC_BOOKMARK, zfs_ioc_bookmark, zfs_secpolicy_bookmark, POOL_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, + zfs_keys_bookmark, ARRAY_SIZE(zfs_keys_bookmark)); zfs_ioctl_register("get_bookmarks", ZFS_IOC_GET_BOOKMARKS, zfs_ioc_get_bookmarks, zfs_secpolicy_read, DATASET_NAME, - POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); + POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE, + zfs_keys_get_bookmarks, ARRAY_SIZE(zfs_keys_get_bookmarks)); zfs_ioctl_register("destroy_bookmarks", ZFS_IOC_DESTROY_BOOKMARKS, zfs_ioc_destroy_bookmarks, zfs_secpolicy_destroy_bookmarks, POOL_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, + zfs_keys_destroy_bookmarks, + ARRAY_SIZE(zfs_keys_destroy_bookmarks)); zfs_ioctl_register("channel_program", ZFS_IOC_CHANNEL_PROGRAM, zfs_ioc_channel_program, zfs_secpolicy_config, POOL_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, - B_TRUE); + B_TRUE, zfs_keys_channel_program, + ARRAY_SIZE(zfs_keys_channel_program)); zfs_ioctl_register("zpool_checkpoint", ZFS_IOC_POOL_CHECKPOINT, zfs_ioc_pool_checkpoint, zfs_secpolicy_config, POOL_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, + zfs_keys_pool_checkpoint, ARRAY_SIZE(zfs_keys_pool_checkpoint)); zfs_ioctl_register("zpool_discard_checkpoint", ZFS_IOC_POOL_DISCARD_CHECKPOINT, zfs_ioc_pool_discard_checkpoint, zfs_secpolicy_config, POOL_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, + zfs_keys_pool_discard_checkpoint, + ARRAY_SIZE(zfs_keys_pool_discard_checkpoint)); zfs_ioctl_register("initialize", ZFS_IOC_POOL_INITIALIZE, zfs_ioc_pool_initialize, zfs_secpolicy_config, POOL_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE, + zfs_keys_pool_initialize, ARRAY_SIZE(zfs_keys_pool_initialize)); zfs_ioctl_register("sync", ZFS_IOC_POOL_SYNC, zfs_ioc_pool_sync, zfs_secpolicy_none, POOL_NAME, - POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE); + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE, + zfs_keys_pool_sync, ARRAY_SIZE(zfs_keys_pool_sync)); + zfs_ioctl_register("reopen", ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen, + zfs_secpolicy_config, POOL_NAME, POOL_CHECK_SUSPENDED, B_TRUE, + B_TRUE, zfs_keys_pool_reopen, ARRAY_SIZE(zfs_keys_pool_reopen)); /* IOCTLS that use the legacy function signature */ @@ -6443,8 +6637,6 @@ zfs_ioctl_register_pool(ZFS_IOC_CLEAR, zfs_ioc_clear, zfs_secpolicy_config, B_TRUE, POOL_CHECK_READONLY); - zfs_ioctl_register_pool(ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen, - zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED); zfs_ioctl_register_dataset_read(ZFS_IOC_SPACE_WRITTEN, zfs_ioc_space_written); @@ -6517,10 +6709,85 @@ zfs_secpolicy_config, POOL_CHECK_NONE); zfs_ioctl_register("fbsd_nextboot", ZFS_IOC_NEXTBOOT, zfs_ioc_nextboot, zfs_secpolicy_config, NO_NAME, - POOL_CHECK_NONE, B_FALSE, B_FALSE); + POOL_CHECK_NONE, B_FALSE, B_FALSE, + zfs_keys_nextboot, ARRAY_SIZE(zfs_keys_nextboot)); #endif } +/* + * Verify that for non-legacy ioctls the input nvlist + * pairs match against the expected input. + * + * Possible errors are: + * ZFS_ERR_IOC_ARG_UNAVAIL An unrecognized nvpair was encountered + * ZFS_ERR_IOC_ARG_REQUIRED A required nvpair is missing + * ZFS_ERR_IOC_ARG_BADTYPE Invalid type for nvpair + */ +static int +zfs_check_input_nvpairs(nvlist_t *innvl, const zfs_ioc_vec_t *vec) +{ + const zfs_ioc_key_t *nvl_keys = vec->zvec_nvl_keys; + boolean_t required_keys_found = B_FALSE; + + /* + * examine each input pair + */ + for (nvpair_t *pair = nvlist_next_nvpair(innvl, NULL); + pair != NULL; pair = nvlist_next_nvpair(innvl, pair)) { + char *name = nvpair_name(pair); + data_type_t type = nvpair_type(pair); + boolean_t identified = B_FALSE; + + /* + * check pair against the documented names and type + */ + for (int k = 0; k < vec->zvec_nvl_key_count; k++) { + /* if not a wild card name, check for an exact match */ + if ((nvl_keys[k].zkey_flags & ZK_WILDCARDLIST) == 0 && + strcmp(nvl_keys[k].zkey_name, name) != 0) + continue; + + identified = B_TRUE; + + if (nvl_keys[k].zkey_type != DATA_TYPE_ANY && + nvl_keys[k].zkey_type != type) { + return (SET_ERROR(ZFS_ERR_IOC_ARG_BADTYPE)); + } + + if (nvl_keys[k].zkey_flags & ZK_OPTIONAL) + continue; + + required_keys_found = B_TRUE; + break; + } + + /* allow an 'optional' key, everything else is invalid */ + if (!identified && + (strcmp(name, "optional") != 0 || + type != DATA_TYPE_NVLIST)) { + return (SET_ERROR(ZFS_ERR_IOC_ARG_UNAVAIL)); + } + } + + /* verify that all required keys were found */ + for (int k = 0; k < vec->zvec_nvl_key_count; k++) { + if (nvl_keys[k].zkey_flags & ZK_OPTIONAL) + continue; + + if (nvl_keys[k].zkey_flags & ZK_WILDCARDLIST) { + /* at least one non-optionial key is expected here */ + if (!required_keys_found) + return (SET_ERROR(ZFS_ERR_IOC_ARG_REQUIRED)); + continue; + } + + if (!nvlist_exists(innvl, nvl_keys[k].zkey_name)) + return (SET_ERROR(ZFS_ERR_IOC_ARG_REQUIRED)); + } + + return (0); +} + int pool_status_check(const char *name, zfs_ioc_namecheck_t type, zfs_ioc_poolcheck_t check) @@ -6727,7 +6994,7 @@ #endif if (vecnum >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0])) - return (SET_ERROR(EINVAL)); + return (SET_ERROR(ZFS_ERR_IOC_CMD_UNAVAIL)); vec = &zfs_ioc_vec[vecnum]; zc = kmem_zalloc(sizeof(zfs_cmd_t), KM_SLEEP); @@ -6873,6 +7140,20 @@ case NO_NAME: break; + } + + /* + * Ensure that all input pairs are valid before we pass them down + * to the lower layers. + * + * The vectored functions can use fnvlist_lookup_{type} for any + * required pairs since zfs_check_input_nvpairs() confirmed that + * they exist and are of the correct type. + */ + if (error == 0 && vec->zvec_func != NULL) { + error = zfs_check_input_nvpairs(innvl, vec); + if (error != 0) + goto out; } if (error == 0) Index: head/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h =================================================================== --- head/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h +++ head/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2016 by Delphix. All rights reserved. + * Copyright (c) 2011, 2018 by Delphix. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2012, Martin Matuska . All rights reserved. * Copyright (c) 2014 Integros [integros.com] @@ -963,9 +963,19 @@ /* * /dev/zfs ioctl numbers. + * + * These numbers cannot change over time. New ioctl numbers must be appended. */ typedef enum zfs_ioc { + /* + * Core features - 81/128 numbers reserved. + */ +#ifdef __FreeBSD__ ZFS_IOC_FIRST = 0, +#else + ZFS_IOC_FIRST = ('Z' << 8), +#endif + ZFS_IOC = ZFS_IOC_FIRST, ZFS_IOC_POOL_CREATE = ZFS_IOC_FIRST, ZFS_IOC_POOL_DESTROY, ZFS_IOC_POOL_IMPORT, @@ -1057,6 +1067,8 @@ * The enum implicitly includes all the error codes from errno.h. * New code should use and extend this enum for errors that are * not described precisely by generic errno codes. + * + * These numbers should not change over time. New entries should be appended. */ typedef enum { ZFS_ERR_CHECKPOINT_EXISTS = 1024, @@ -1064,6 +1076,10 @@ ZFS_ERR_NO_CHECKPOINT, ZFS_ERR_DEVRM_IN_PROGRESS, ZFS_ERR_VDEV_TOO_BIG, + ZFS_ERR_IOC_CMD_UNAVAIL, + ZFS_ERR_IOC_ARG_UNAVAIL, + ZFS_ERR_IOC_ARG_REQUIRED, + ZFS_ERR_IOC_ARG_BADTYPE, ZFS_ERR_WRONG_PARENT, } zfs_errno_t;