diff options
author | Toomas Soome <tsoome@FreeBSD.org> | 2020-06-23 06:42:39 +0000 |
---|---|---|
committer | Toomas Soome <tsoome@FreeBSD.org> | 2020-06-23 06:42:39 +0000 |
commit | a14844e0d67a69239e522de342c6690428edccd0 (patch) | |
tree | 6597aa42b3e2b571f966faa50d16d1c39e35c7c7 /sys/cddl/contrib/opensolaris/uts/common | |
parent | b40dd828bdc96959cf5fa90d02fc00d96c54fbe4 (diff) | |
download | src-a14844e0d67a69239e522de342c6690428edccd0.tar.gz src-a14844e0d67a69239e522de342c6690428edccd0.zip |
MFOpenZFS: Add basic zfs ioc input nvpair validation
We want newer versions of libzfs_core to run against an existing
zfs kernel module (i.e. a deferred reboot or module reload after
an update).
Programmatically document, via a zfs_ioc_key_t, the valid arguments
for the ioc commands that rely on nvpair input arguments (i.e. non
legacy commands from libzfs_core). Automatically verify the expected
pairs before dispatching a command.
This initial phase focuses on the non-legacy ioctls. A follow-on
change can address the legacy ioctl input from the zfs_cmd_t.
The zfs_ioc_key_t for zfs_keys_channel_program looks like:
static const zfs_ioc_key_t zfs_keys_channel_program[] = {
{"program", DATA_TYPE_STRING, 0},
{"arg", DATA_TYPE_UNKNOWN, 0},
{"sync", DATA_TYPE_BOOLEAN_VALUE, ZK_OPTIONAL},
{"instrlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
{"memlimit", DATA_TYPE_UINT64, ZK_OPTIONAL},
};
Introduce four input errors to identify specific input failures
(in addition to generic argument value errors like EINVAL, ERANGE,
EBADF, and E2BIG).
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
Reviewed by: allanjude
Obtained from: OpenZFS
Sponsored by: Netflix, Klara Inc.
Differential Revision: https://reviews.freebsd.org/D25393
Notes
Notes:
svn path=/head/; revision=362531
Diffstat (limited to 'sys/cddl/contrib/opensolaris/uts/common')
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c | 437 | ||||
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h | 18 |
2 files changed, 376 insertions, 79 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c index 9cdc77b72024..46dbb9f5cf1c 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c +++ b/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,6 +241,37 @@ typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *); 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, @@ -244,6 +294,8 @@ typedef struct zfs_ioc_vec { 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 @@ zfs_secpolicy_destroy_snaps(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 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 @@ zfs_secpolicy_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 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 @@ zfs_secpolicy_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) } /* - * 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 @@ zfs_secpolicy_hold(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 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 @@ zfs_secpolicy_tmp_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) 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 @@ zfs_fill_zplprops_root(uint64_t spa_vers, nvlist_t *createprops, * * 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_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 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 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) * * 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 @@ zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 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 @@ zfs_ioc_clone(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) 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 @@ zfs_ioc_remap(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) * * 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 @@ zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 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 @@ zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) /* * 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 @@ zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) 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 @@ zfs_ioc_log_history(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) } #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 @@ zfs_ioc_nextboot(const char *unused, nvlist_t *innvl, nvlist_t *outnvl) 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 @@ zfs_destroy_unmount_origin(const char *fsname) * 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 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) 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 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) * outnvl: bookmark -> error code (int32) * */ +static const zfs_ioc_key_t zfs_keys_bookmark[] = { + {"<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 @@ zfs_ioc_bookmark(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) * } * */ +static const zfs_ioc_key_t zfs_keys_get_bookmarks[] = { + {"<property>...", 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 @@ zfs_ioc_get_bookmarks(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl) * outnvl: bookmark -> error code (int32) * */ +static const zfs_ioc_key_t zfs_keys_destroy_bookmarks[] = { + {"<bookmark>...", DATA_TYPE_BOOLEAN, ZK_WILDCARDLIST}, +}; + static int zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) @@ -3847,6 +3935,14 @@ zfs_ioc_destroy_bookmarks(const char *poolname, nvlist_t *innvl, 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 @@ zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl, 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 @@ zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl, 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 @@ zfs_ioc_channel_program(const char *poolname, nvlist_t *innvl, * 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 @@ zfs_ioc_pool_checkpoint(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) * 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 @@ zfs_ioc_destroy(zfs_cmd_t *zc) * } * */ +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 @@ zfs_ioc_pool_initialize(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) * 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 @@ zfs_ioc_clear(zfs_cmd_t *zc) 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 @@ zfs_ioc_pool_reopen(zfs_cmd_t *zc) * 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 @@ zfs_ioc_pool_reopen(zfs_cmd_t *zc) spa_close(spa, FTAG); return (0); } + /* * inputs: * zc_name name of filesystem @@ -5789,6 +5920,11 @@ zfs_ioc_smb_acl(zfs_cmd_t *zc) * ... * } */ +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 @@ zfs_ioc_hold(const char *pool, nvlist_t *args, nvlist_t *errlist) 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 @@ zfs_ioc_hold(const char *pool, nvlist_t *args, nvlist_t *errlist) * ... * } */ +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 @@ zfs_ioc_get_holds(const char *snapname, nvlist_t *args, nvlist_t *outnvl) * ... * } */ +static const zfs_ioc_key_t zfs_keys_release[] = { + {"<snapname>...", DATA_TYPE_NVLIST, ZK_WILDCARDLIST}, +}; + /* ARGSUSED */ static int zfs_ioc_release(const char *pool, nvlist_t *holds, nvlist_t *errlist) @@ -5913,6 +6054,10 @@ zfs_ioc_space_written(zfs_cmd_t *zc) * "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 @@ zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl) 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 @@ zfs_ioc_unjail(zfs_cmd_t *zc) * * 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 @@ zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) 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 @@ zfs_ioc_send_new(const char *snapname, nvlist_t *innvl, nvlist_t *outnvl) * "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 @@ out: * * 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 @@ static void 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 @@ zfs_ioctl_register(const char *name, zfs_ioc_t ioc, zfs_ioc_func_t *func, 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_init(void) { 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_init(void) 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_ioctl_init(void) 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 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag, #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); @@ -6875,6 +7142,20 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag, 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) error = vec->zvec_secpolicy(zc, innvl, cr); diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h b/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h index 52de1a5f300b..8a6e534a421d 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h +++ b/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 <mm@FreeBSD.org>. All rights reserved. * Copyright (c) 2014 Integros [integros.com] @@ -963,9 +963,19 @@ typedef struct ddt_histogram { /* * /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 @@ typedef enum zfs_ioc { * 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 @@ typedef enum { 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; |