mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	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: Matthew Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Don Brady <don.brady@delphix.com>
Closes #7780
			
			
This commit is contained in:
		
							parent
							
								
									e8bcb693d6
								
							
						
					
					
						commit
						b83a0e2dc1
					
				@ -7238,7 +7238,7 @@ zfs_do_bookmark(int argc, char **argv)
 | 
			
		||||
	fnvlist_free(nvl);
 | 
			
		||||
 | 
			
		||||
	if (ret != 0) {
 | 
			
		||||
		const char *err_msg;
 | 
			
		||||
		const char *err_msg = NULL;
 | 
			
		||||
		char errbuf[1024];
 | 
			
		||||
 | 
			
		||||
		(void) snprintf(errbuf, sizeof (errbuf),
 | 
			
		||||
@ -7265,11 +7265,13 @@ zfs_do_bookmark(int argc, char **argv)
 | 
			
		||||
			err_msg = "dataset does not exist";
 | 
			
		||||
			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);
 | 
			
		||||
@ -7285,7 +7287,7 @@ zfs_do_channel_program(int argc, char **argv)
 | 
			
		||||
	int ret, fd, 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;
 | 
			
		||||
@ -7412,8 +7414,9 @@ zfs_do_channel_program(int argc, char **argv)
 | 
			
		||||
		 * falling back on strerror() for an unexpected return code.
 | 
			
		||||
		 */
 | 
			
		||||
		char *errstring = NULL;
 | 
			
		||||
		const char *msg = gettext("Channel program execution failed");
 | 
			
		||||
		uint64_t instructions = 0;
 | 
			
		||||
		if (nvlist_exists(outnvl, ZCP_RET_ERROR)) {
 | 
			
		||||
		if (outnvl != NULL && nvlist_exists(outnvl, ZCP_RET_ERROR)) {
 | 
			
		||||
			(void) nvlist_lookup_string(outnvl,
 | 
			
		||||
			    ZCP_RET_ERROR, &errstring);
 | 
			
		||||
			if (errstring == NULL)
 | 
			
		||||
@ -7442,12 +7445,12 @@ zfs_do_channel_program(int argc, char **argv)
 | 
			
		||||
				    "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);
 | 
			
		||||
 | 
			
		||||
		if (ret == ETIME && instructions != 0)
 | 
			
		||||
			(void) fprintf(stderr,
 | 
			
		||||
			    gettext("%llu Lua instructions\n"),
 | 
			
		||||
 | 
			
		||||
@ -169,6 +169,7 @@ AC_CONFIG_FILES([
 | 
			
		||||
	tests/zfs-tests/cmd/file_trunc/Makefile
 | 
			
		||||
	tests/zfs-tests/cmd/file_write/Makefile
 | 
			
		||||
	tests/zfs-tests/cmd/largest_file/Makefile
 | 
			
		||||
	tests/zfs-tests/cmd/libzfs_input_check/Makefile
 | 
			
		||||
	tests/zfs-tests/cmd/mkbusy/Makefile
 | 
			
		||||
	tests/zfs-tests/cmd/mkfile/Makefile
 | 
			
		||||
	tests/zfs-tests/cmd/mkfiles/Makefile
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 | 
			
		||||
 * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
 | 
			
		||||
 * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
 | 
			
		||||
 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (c) 2013 Steven Hartland. All rights reserved.
 | 
			
		||||
 * Copyright (c) 2016, Intel Corporation.
 | 
			
		||||
@ -157,6 +157,7 @@ typedef enum zfs_error {
 | 
			
		||||
	EZFS_NO_CHECKPOINT,	/* pool has no checkpoint */
 | 
			
		||||
	EZFS_DEVRM_IN_PROGRESS,	/* a device is currently being removed */
 | 
			
		||||
	EZFS_VDEV_TOO_BIG,	/* a device is too big to be used */
 | 
			
		||||
	EZFS_IOC_NOTSUPPORTED,	/* operation not supported by zfs module */
 | 
			
		||||
	EZFS_UNKNOWN
 | 
			
		||||
} zfs_error_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 | 
			
		||||
 * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
 | 
			
		||||
 * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
 | 
			
		||||
 * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
 | 
			
		||||
 * Copyright (c) 2013, 2017 Joyent, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (c) 2014 Integros [integros.com]
 | 
			
		||||
@ -1078,6 +1078,8 @@ typedef struct ddt_histogram {
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * /dev/zfs ioctl numbers.
 | 
			
		||||
 *
 | 
			
		||||
 * These numbers cannot change over time. New ioctl numbers must be appended.
 | 
			
		||||
 */
 | 
			
		||||
typedef enum zfs_ioc {
 | 
			
		||||
	/*
 | 
			
		||||
@ -1085,93 +1087,93 @@ typedef enum zfs_ioc {
 | 
			
		||||
	 */
 | 
			
		||||
	ZFS_IOC_FIRST =	('Z' << 8),
 | 
			
		||||
	ZFS_IOC = ZFS_IOC_FIRST,
 | 
			
		||||
	ZFS_IOC_POOL_CREATE = ZFS_IOC_FIRST,
 | 
			
		||||
	ZFS_IOC_POOL_DESTROY,
 | 
			
		||||
	ZFS_IOC_POOL_IMPORT,
 | 
			
		||||
	ZFS_IOC_POOL_EXPORT,
 | 
			
		||||
	ZFS_IOC_POOL_CONFIGS,
 | 
			
		||||
	ZFS_IOC_POOL_STATS,
 | 
			
		||||
	ZFS_IOC_POOL_TRYIMPORT,
 | 
			
		||||
	ZFS_IOC_POOL_SCAN,
 | 
			
		||||
	ZFS_IOC_POOL_FREEZE,
 | 
			
		||||
	ZFS_IOC_POOL_UPGRADE,
 | 
			
		||||
	ZFS_IOC_POOL_GET_HISTORY,
 | 
			
		||||
	ZFS_IOC_VDEV_ADD,
 | 
			
		||||
	ZFS_IOC_VDEV_REMOVE,
 | 
			
		||||
	ZFS_IOC_VDEV_SET_STATE,
 | 
			
		||||
	ZFS_IOC_VDEV_ATTACH,
 | 
			
		||||
	ZFS_IOC_VDEV_DETACH,
 | 
			
		||||
	ZFS_IOC_VDEV_SETPATH,
 | 
			
		||||
	ZFS_IOC_VDEV_SETFRU,
 | 
			
		||||
	ZFS_IOC_OBJSET_STATS,
 | 
			
		||||
	ZFS_IOC_OBJSET_ZPLPROPS,
 | 
			
		||||
	ZFS_IOC_DATASET_LIST_NEXT,
 | 
			
		||||
	ZFS_IOC_SNAPSHOT_LIST_NEXT,
 | 
			
		||||
	ZFS_IOC_SET_PROP,
 | 
			
		||||
	ZFS_IOC_CREATE,
 | 
			
		||||
	ZFS_IOC_DESTROY,
 | 
			
		||||
	ZFS_IOC_ROLLBACK,
 | 
			
		||||
	ZFS_IOC_RENAME,
 | 
			
		||||
	ZFS_IOC_RECV,
 | 
			
		||||
	ZFS_IOC_SEND,
 | 
			
		||||
	ZFS_IOC_INJECT_FAULT,
 | 
			
		||||
	ZFS_IOC_CLEAR_FAULT,
 | 
			
		||||
	ZFS_IOC_INJECT_LIST_NEXT,
 | 
			
		||||
	ZFS_IOC_ERROR_LOG,
 | 
			
		||||
	ZFS_IOC_CLEAR,
 | 
			
		||||
	ZFS_IOC_PROMOTE,
 | 
			
		||||
	ZFS_IOC_SNAPSHOT,
 | 
			
		||||
	ZFS_IOC_DSOBJ_TO_DSNAME,
 | 
			
		||||
	ZFS_IOC_OBJ_TO_PATH,
 | 
			
		||||
	ZFS_IOC_POOL_SET_PROPS,
 | 
			
		||||
	ZFS_IOC_POOL_GET_PROPS,
 | 
			
		||||
	ZFS_IOC_SET_FSACL,
 | 
			
		||||
	ZFS_IOC_GET_FSACL,
 | 
			
		||||
	ZFS_IOC_SHARE,
 | 
			
		||||
	ZFS_IOC_INHERIT_PROP,
 | 
			
		||||
	ZFS_IOC_SMB_ACL,
 | 
			
		||||
	ZFS_IOC_USERSPACE_ONE,
 | 
			
		||||
	ZFS_IOC_USERSPACE_MANY,
 | 
			
		||||
	ZFS_IOC_USERSPACE_UPGRADE,
 | 
			
		||||
	ZFS_IOC_HOLD,
 | 
			
		||||
	ZFS_IOC_RELEASE,
 | 
			
		||||
	ZFS_IOC_GET_HOLDS,
 | 
			
		||||
	ZFS_IOC_OBJSET_RECVD_PROPS,
 | 
			
		||||
	ZFS_IOC_VDEV_SPLIT,
 | 
			
		||||
	ZFS_IOC_NEXT_OBJ,
 | 
			
		||||
	ZFS_IOC_DIFF,
 | 
			
		||||
	ZFS_IOC_TMP_SNAPSHOT,
 | 
			
		||||
	ZFS_IOC_OBJ_TO_STATS,
 | 
			
		||||
	ZFS_IOC_SPACE_WRITTEN,
 | 
			
		||||
	ZFS_IOC_SPACE_SNAPS,
 | 
			
		||||
	ZFS_IOC_DESTROY_SNAPS,
 | 
			
		||||
	ZFS_IOC_POOL_REGUID,
 | 
			
		||||
	ZFS_IOC_POOL_REOPEN,
 | 
			
		||||
	ZFS_IOC_SEND_PROGRESS,
 | 
			
		||||
	ZFS_IOC_LOG_HISTORY,
 | 
			
		||||
	ZFS_IOC_SEND_NEW,
 | 
			
		||||
	ZFS_IOC_SEND_SPACE,
 | 
			
		||||
	ZFS_IOC_CLONE,
 | 
			
		||||
	ZFS_IOC_BOOKMARK,
 | 
			
		||||
	ZFS_IOC_GET_BOOKMARKS,
 | 
			
		||||
	ZFS_IOC_DESTROY_BOOKMARKS,
 | 
			
		||||
	ZFS_IOC_CHANNEL_PROGRAM,
 | 
			
		||||
	ZFS_IOC_RECV_NEW,
 | 
			
		||||
	ZFS_IOC_POOL_SYNC,
 | 
			
		||||
	ZFS_IOC_LOAD_KEY,
 | 
			
		||||
	ZFS_IOC_UNLOAD_KEY,
 | 
			
		||||
	ZFS_IOC_CHANGE_KEY,
 | 
			
		||||
	ZFS_IOC_REMAP,
 | 
			
		||||
	ZFS_IOC_POOL_CHECKPOINT,
 | 
			
		||||
	ZFS_IOC_POOL_DISCARD_CHECKPOINT,
 | 
			
		||||
	ZFS_IOC_POOL_CREATE = ZFS_IOC_FIRST,	/* 0x5a00 */
 | 
			
		||||
	ZFS_IOC_POOL_DESTROY,			/* 0x5a01 */
 | 
			
		||||
	ZFS_IOC_POOL_IMPORT,			/* 0x5a02 */
 | 
			
		||||
	ZFS_IOC_POOL_EXPORT,			/* 0x5a03 */
 | 
			
		||||
	ZFS_IOC_POOL_CONFIGS,			/* 0x5a04 */
 | 
			
		||||
	ZFS_IOC_POOL_STATS,			/* 0x5a05 */
 | 
			
		||||
	ZFS_IOC_POOL_TRYIMPORT,			/* 0x5a06 */
 | 
			
		||||
	ZFS_IOC_POOL_SCAN,			/* 0x5a07 */
 | 
			
		||||
	ZFS_IOC_POOL_FREEZE,			/* 0x5a08 */
 | 
			
		||||
	ZFS_IOC_POOL_UPGRADE,			/* 0x5a09 */
 | 
			
		||||
	ZFS_IOC_POOL_GET_HISTORY,		/* 0x5a0a */
 | 
			
		||||
	ZFS_IOC_VDEV_ADD,			/* 0x5a0b */
 | 
			
		||||
	ZFS_IOC_VDEV_REMOVE,			/* 0x5a0c */
 | 
			
		||||
	ZFS_IOC_VDEV_SET_STATE,			/* 0x5a0d */
 | 
			
		||||
	ZFS_IOC_VDEV_ATTACH,			/* 0x5a0e */
 | 
			
		||||
	ZFS_IOC_VDEV_DETACH,			/* 0x5a0f */
 | 
			
		||||
	ZFS_IOC_VDEV_SETPATH,			/* 0x5a10 */
 | 
			
		||||
	ZFS_IOC_VDEV_SETFRU,			/* 0x5a11 */
 | 
			
		||||
	ZFS_IOC_OBJSET_STATS,			/* 0x5a12 */
 | 
			
		||||
	ZFS_IOC_OBJSET_ZPLPROPS,		/* 0x5a13 */
 | 
			
		||||
	ZFS_IOC_DATASET_LIST_NEXT,		/* 0x5a14 */
 | 
			
		||||
	ZFS_IOC_SNAPSHOT_LIST_NEXT,		/* 0x5a15 */
 | 
			
		||||
	ZFS_IOC_SET_PROP,			/* 0x5a16 */
 | 
			
		||||
	ZFS_IOC_CREATE,				/* 0x5a17 */
 | 
			
		||||
	ZFS_IOC_DESTROY,			/* 0x5a18 */
 | 
			
		||||
	ZFS_IOC_ROLLBACK,			/* 0x5a19 */
 | 
			
		||||
	ZFS_IOC_RENAME,				/* 0x5a1a */
 | 
			
		||||
	ZFS_IOC_RECV,				/* 0x5a1b */
 | 
			
		||||
	ZFS_IOC_SEND,				/* 0x5a1c */
 | 
			
		||||
	ZFS_IOC_INJECT_FAULT,			/* 0x5a1d */
 | 
			
		||||
	ZFS_IOC_CLEAR_FAULT,			/* 0x5a1e */
 | 
			
		||||
	ZFS_IOC_INJECT_LIST_NEXT,		/* 0x5a1f */
 | 
			
		||||
	ZFS_IOC_ERROR_LOG,			/* 0x5a20 */
 | 
			
		||||
	ZFS_IOC_CLEAR,				/* 0x5a21 */
 | 
			
		||||
	ZFS_IOC_PROMOTE,			/* 0x5a22 */
 | 
			
		||||
	ZFS_IOC_SNAPSHOT,			/* 0x5a23 */
 | 
			
		||||
	ZFS_IOC_DSOBJ_TO_DSNAME,		/* 0x5a24 */
 | 
			
		||||
	ZFS_IOC_OBJ_TO_PATH,			/* 0x5a25 */
 | 
			
		||||
	ZFS_IOC_POOL_SET_PROPS,			/* 0x5a26 */
 | 
			
		||||
	ZFS_IOC_POOL_GET_PROPS,			/* 0x5a27 */
 | 
			
		||||
	ZFS_IOC_SET_FSACL,			/* 0x5a28 */
 | 
			
		||||
	ZFS_IOC_GET_FSACL,			/* 0x5a29 */
 | 
			
		||||
	ZFS_IOC_SHARE,				/* 0x5a2a */
 | 
			
		||||
	ZFS_IOC_INHERIT_PROP,			/* 0x5a2b */
 | 
			
		||||
	ZFS_IOC_SMB_ACL,			/* 0x5a2c */
 | 
			
		||||
	ZFS_IOC_USERSPACE_ONE,			/* 0x5a2d */
 | 
			
		||||
	ZFS_IOC_USERSPACE_MANY,			/* 0x5a2e */
 | 
			
		||||
	ZFS_IOC_USERSPACE_UPGRADE,		/* 0x5a2f */
 | 
			
		||||
	ZFS_IOC_HOLD,				/* 0x5a30 */
 | 
			
		||||
	ZFS_IOC_RELEASE,			/* 0x5a31 */
 | 
			
		||||
	ZFS_IOC_GET_HOLDS,			/* 0x5a32 */
 | 
			
		||||
	ZFS_IOC_OBJSET_RECVD_PROPS,		/* 0x5a33 */
 | 
			
		||||
	ZFS_IOC_VDEV_SPLIT,			/* 0x5a34 */
 | 
			
		||||
	ZFS_IOC_NEXT_OBJ,			/* 0x5a35 */
 | 
			
		||||
	ZFS_IOC_DIFF,				/* 0x5a36 */
 | 
			
		||||
	ZFS_IOC_TMP_SNAPSHOT,			/* 0x5a37 */
 | 
			
		||||
	ZFS_IOC_OBJ_TO_STATS,			/* 0x5a38 */
 | 
			
		||||
	ZFS_IOC_SPACE_WRITTEN,			/* 0x5a39 */
 | 
			
		||||
	ZFS_IOC_SPACE_SNAPS,			/* 0x5a3a */
 | 
			
		||||
	ZFS_IOC_DESTROY_SNAPS,			/* 0x5a3b */
 | 
			
		||||
	ZFS_IOC_POOL_REGUID,			/* 0x5a3c */
 | 
			
		||||
	ZFS_IOC_POOL_REOPEN,			/* 0x5a3d */
 | 
			
		||||
	ZFS_IOC_SEND_PROGRESS,			/* 0x5a3e */
 | 
			
		||||
	ZFS_IOC_LOG_HISTORY,			/* 0x5a3f */
 | 
			
		||||
	ZFS_IOC_SEND_NEW,			/* 0x5a40 */
 | 
			
		||||
	ZFS_IOC_SEND_SPACE,			/* 0x5a41 */
 | 
			
		||||
	ZFS_IOC_CLONE,				/* 0x5a42 */
 | 
			
		||||
	ZFS_IOC_BOOKMARK,			/* 0x5a43 */
 | 
			
		||||
	ZFS_IOC_GET_BOOKMARKS,			/* 0x5a44 */
 | 
			
		||||
	ZFS_IOC_DESTROY_BOOKMARKS,		/* 0x5a45 */
 | 
			
		||||
	ZFS_IOC_CHANNEL_PROGRAM,		/* 0x5a46 */
 | 
			
		||||
	ZFS_IOC_RECV_NEW,			/* 0x5a47 */
 | 
			
		||||
	ZFS_IOC_POOL_SYNC,			/* 0x5a48 */
 | 
			
		||||
	ZFS_IOC_LOAD_KEY,			/* 0x5a49 */
 | 
			
		||||
	ZFS_IOC_UNLOAD_KEY,			/* 0x5a4a */
 | 
			
		||||
	ZFS_IOC_CHANGE_KEY,			/* 0x5a4b */
 | 
			
		||||
	ZFS_IOC_REMAP,				/* 0x5a4c */
 | 
			
		||||
	ZFS_IOC_POOL_CHECKPOINT,		/* 0x5a4d */
 | 
			
		||||
	ZFS_IOC_POOL_DISCARD_CHECKPOINT,	/* 0x5a4e */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Linux - 3/64 numbers reserved.
 | 
			
		||||
	 */
 | 
			
		||||
	ZFS_IOC_LINUX = ('Z' << 8) + 0x80,
 | 
			
		||||
	ZFS_IOC_EVENTS_NEXT,
 | 
			
		||||
	ZFS_IOC_EVENTS_CLEAR,
 | 
			
		||||
	ZFS_IOC_EVENTS_SEEK,
 | 
			
		||||
	ZFS_IOC_EVENTS_NEXT,			/* 0x5a81 */
 | 
			
		||||
	ZFS_IOC_EVENTS_CLEAR,			/* 0x5a82 */
 | 
			
		||||
	ZFS_IOC_EVENTS_SEEK,			/* 0x5a83 */
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * FreeBSD - 1/64 numbers reserved.
 | 
			
		||||
@ -1193,13 +1195,19 @@ 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,
 | 
			
		||||
	ZFS_ERR_DISCARDING_CHECKPOINT,
 | 
			
		||||
	ZFS_ERR_NO_CHECKPOINT,
 | 
			
		||||
	ZFS_ERR_DEVRM_IN_PROGRESS,
 | 
			
		||||
	ZFS_ERR_VDEV_TOO_BIG
 | 
			
		||||
	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_errno_t;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 | 
			
		||||
 * Copyright (c) 2018, Joyent, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
 | 
			
		||||
 * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
 | 
			
		||||
 * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
 | 
			
		||||
 * Copyright (c) 2017 Datto Inc.
 | 
			
		||||
 */
 | 
			
		||||
@ -227,6 +227,9 @@ libzfs_error_description(libzfs_handle_t *hdl)
 | 
			
		||||
	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"));
 | 
			
		||||
@ -445,6 +448,22 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
 | 
			
		||||
	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);
 | 
			
		||||
@ -556,6 +575,22 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
 | 
			
		||||
	case ZFS_ERR_VDEV_TOO_BIG:
 | 
			
		||||
		zfs_verror(hdl, EZFS_VDEV_TOO_BIG, 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);
 | 
			
		||||
 | 
			
		||||
@ -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) 2017 Datto Inc.
 | 
			
		||||
 * Copyright 2017 RackTop Systems.
 | 
			
		||||
@ -78,6 +78,9 @@
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#ifdef ZFS_DEBUG
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
@ -91,6 +94,42 @@ static int g_fd = -1;
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
@ -103,6 +142,10 @@ libzfs_core_init(void)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	g_refcount++;
 | 
			
		||||
 | 
			
		||||
#ifdef ZFS_DEBUG
 | 
			
		||||
	libzfs_core_debug_ioc();
 | 
			
		||||
#endif
 | 
			
		||||
	(void) pthread_mutex_unlock(&g_lock);
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
@ -135,6 +178,11 @@ lzc_ioctl(zfs_ioc_t ioc, const char *name,
 | 
			
		||||
	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));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@
 | 
			
		||||
 * Copyright (c) 2014, 2016 Joyent, Inc. All rights reserved.
 | 
			
		||||
 * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
 | 
			
		||||
 * Copyright (c) 2014, 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]
 | 
			
		||||
@ -63,8 +63,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
 | 
			
		||||
@ -90,6 +91,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
 | 
			
		||||
@ -138,6 +143,14 @@
 | 
			
		||||
 *         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
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
@ -220,6 +233,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,
 | 
			
		||||
@ -241,6 +285,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 */
 | 
			
		||||
@ -841,8 +887,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);
 | 
			
		||||
@ -993,8 +1039,8 @@ zfs_secpolicy_snapshot(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
 | 
			
		||||
	int error = 0;
 | 
			
		||||
	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);
 | 
			
		||||
@ -1014,7 +1060,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
 | 
			
		||||
@ -1249,9 +1295,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)) {
 | 
			
		||||
@ -1306,12 +1350,15 @@ 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3221,6 +3268,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)
 | 
			
		||||
{
 | 
			
		||||
@ -3229,14 +3283,11 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
	nvlist_t *nvprops = NULL;
 | 
			
		||||
	nvlist_t *hidden_args = 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;
 | 
			
		||||
	dsl_crypto_params_t *dcp = NULL;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	(void) nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args);
 | 
			
		||||
 | 
			
		||||
@ -3357,6 +3408,12 @@ zfs_ioc_create(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
 * outputs:
 | 
			
		||||
 * 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)
 | 
			
		||||
{
 | 
			
		||||
@ -3364,8 +3421,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, '@') ||
 | 
			
		||||
@ -3389,6 +3445,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)
 | 
			
		||||
@ -3408,6 +3468,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)
 | 
			
		||||
{
 | 
			
		||||
@ -3424,8 +3489,7 @@ zfs_ioc_snapshot(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
	    zfs_earlier_version(poolname, SPA_VERSION_SNAP_PROPS))
 | 
			
		||||
		return (SET_ERROR(ENOTSUP));
 | 
			
		||||
 | 
			
		||||
	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)) {
 | 
			
		||||
@ -3465,6 +3529,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)
 | 
			
		||||
@ -3490,10 +3558,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);
 | 
			
		||||
@ -3566,6 +3631,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)
 | 
			
		||||
@ -3574,8 +3644,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");
 | 
			
		||||
 | 
			
		||||
	for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
 | 
			
		||||
@ -3597,6 +3666,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)
 | 
			
		||||
@ -3634,6 +3707,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)
 | 
			
		||||
{
 | 
			
		||||
@ -3648,6 +3725,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)
 | 
			
		||||
@ -3680,6 +3761,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)
 | 
			
		||||
@ -3689,9 +3778,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;
 | 
			
		||||
	}
 | 
			
		||||
@ -3701,9 +3788,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);
 | 
			
		||||
@ -3718,6 +3803,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)
 | 
			
		||||
@ -3729,6 +3818,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,
 | 
			
		||||
@ -3798,6 +3891,10 @@ zfs_ioc_destroy(zfs_cmd_t *zc)
 | 
			
		||||
 * 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)
 | 
			
		||||
@ -4738,6 +4835,7 @@ zfs_ioc_recv(zfs_cmd_t *zc)
 | 
			
		||||
 *     (optional) "resumable" -> resumable flag (value ignored)
 | 
			
		||||
 *     (optional) "cleanup_fd" -> cleanup-on-exit file descriptor
 | 
			
		||||
 *     (optional) "action_handle" -> handle for this guid/ds mapping
 | 
			
		||||
 *     (optional) "hidden_args" -> { "wkeydata" -> value }
 | 
			
		||||
 * }
 | 
			
		||||
 *
 | 
			
		||||
 * outnvl: {
 | 
			
		||||
@ -4747,6 +4845,20 @@ zfs_ioc_recv(zfs_cmd_t *zc)
 | 
			
		||||
 *     "errors" -> error for each unapplied received property (nvlist)
 | 
			
		||||
 * }
 | 
			
		||||
 */
 | 
			
		||||
static const zfs_ioc_key_t zfs_keys_recv_new[] = {
 | 
			
		||||
	{"snapname",		DATA_TYPE_STRING,	0},
 | 
			
		||||
	{"props",		DATA_TYPE_NVLIST,	ZK_OPTIONAL},
 | 
			
		||||
	{"localprops",		DATA_TYPE_NVLIST,	ZK_OPTIONAL},
 | 
			
		||||
	{"origin",		DATA_TYPE_STRING,	ZK_OPTIONAL},
 | 
			
		||||
	{"begin_record",	DATA_TYPE_BYTE_ARRAY,	0},
 | 
			
		||||
	{"input_fd",		DATA_TYPE_INT32,	0},
 | 
			
		||||
	{"force",		DATA_TYPE_BOOLEAN,	ZK_OPTIONAL},
 | 
			
		||||
	{"resumable",		DATA_TYPE_BOOLEAN,	ZK_OPTIONAL},
 | 
			
		||||
	{"cleanup_fd",		DATA_TYPE_INT32,	ZK_OPTIONAL},
 | 
			
		||||
	{"action_handle",	DATA_TYPE_UINT64,	ZK_OPTIONAL},
 | 
			
		||||
	{"hidden_args",		DATA_TYPE_NVLIST,	ZK_OPTIONAL},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
{
 | 
			
		||||
@ -4756,7 +4868,7 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
	nvlist_t *recvprops = NULL;
 | 
			
		||||
	nvlist_t *localprops = NULL;
 | 
			
		||||
	nvlist_t *hidden_args = NULL;
 | 
			
		||||
	char *snapname = NULL;
 | 
			
		||||
	char *snapname;
 | 
			
		||||
	char *origin = NULL;
 | 
			
		||||
	char *tosnap;
 | 
			
		||||
	char tofs[ZFS_MAX_DATASET_NAME_LEN];
 | 
			
		||||
@ -4769,9 +4881,7 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
	int cleanup_fd = -1;
 | 
			
		||||
	int error;
 | 
			
		||||
 | 
			
		||||
	error = nvlist_lookup_string(innvl, "snapname", &snapname);
 | 
			
		||||
	if (error != 0)
 | 
			
		||||
		return (SET_ERROR(EINVAL));
 | 
			
		||||
	snapname = fnvlist_lookup_string(innvl, "snapname");
 | 
			
		||||
 | 
			
		||||
	if (dataset_namecheck(snapname, NULL, NULL) != 0 ||
 | 
			
		||||
	    strchr(snapname, '@') == NULL ||
 | 
			
		||||
@ -4791,9 +4901,7 @@ zfs_ioc_recv_new(const char *fsname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
	if (error != 0 || begin_record_size != sizeof (*begin_record))
 | 
			
		||||
		return (SET_ERROR(EINVAL));
 | 
			
		||||
 | 
			
		||||
	error = nvlist_lookup_int32(innvl, "input_fd", &input_fd);
 | 
			
		||||
	if (error != 0)
 | 
			
		||||
		return (SET_ERROR(EINVAL));
 | 
			
		||||
	input_fd = fnvlist_lookup_int32(innvl, "input_fd");
 | 
			
		||||
 | 
			
		||||
	force = nvlist_exists(innvl, "force");
 | 
			
		||||
	resumable = nvlist_exists(innvl, "resumable");
 | 
			
		||||
@ -5132,6 +5240,10 @@ zfs_ioc_clear(zfs_cmd_t *zc)
 | 
			
		||||
 *
 | 
			
		||||
 * outnvl is unused
 | 
			
		||||
 */
 | 
			
		||||
static const zfs_ioc_key_t zfs_keys_pool_reopen[] = {
 | 
			
		||||
	{"scrub_restart",	DATA_TYPE_BOOLEAN_VALUE,	0},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* ARGSUSED */
 | 
			
		||||
static int
 | 
			
		||||
zfs_ioc_pool_reopen(const char *pool, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
@ -5141,10 +5253,8 @@ zfs_ioc_pool_reopen(const char *pool, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
	boolean_t scrub_restart = B_TRUE;
 | 
			
		||||
 | 
			
		||||
	if (innvl) {
 | 
			
		||||
		if (nvlist_lookup_boolean_value(innvl, "scrub_restart",
 | 
			
		||||
		    &scrub_restart) != 0) {
 | 
			
		||||
			return (SET_ERROR(EINVAL));
 | 
			
		||||
		}
 | 
			
		||||
		scrub_restart = fnvlist_lookup_boolean_value(innvl,
 | 
			
		||||
		    "scrub_restart");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	error = spa_open(pool, &spa, FTAG);
 | 
			
		||||
@ -5661,6 +5771,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)
 | 
			
		||||
@ -5671,9 +5786,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;
 | 
			
		||||
@ -5708,11 +5821,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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5727,6 +5843,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)
 | 
			
		||||
@ -5869,6 +5989,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)
 | 
			
		||||
{
 | 
			
		||||
@ -5878,8 +6002,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)
 | 
			
		||||
@ -5933,6 +6056,17 @@ zfs_ioc_space_snaps(const char *lastsnap, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
 *
 | 
			
		||||
 * 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)
 | 
			
		||||
@ -5949,9 +6083,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);
 | 
			
		||||
 | 
			
		||||
@ -5998,6 +6130,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)
 | 
			
		||||
{
 | 
			
		||||
@ -6090,6 +6231,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)
 | 
			
		||||
@ -6101,12 +6246,8 @@ zfs_ioc_pool_sync(const char *pool, nvlist_t *innvl, nvlist_t *onvl)
 | 
			
		||||
	if ((err = spa_open(pool, &spa, FTAG)) != 0)
 | 
			
		||||
		return (err);
 | 
			
		||||
 | 
			
		||||
	if (innvl) {
 | 
			
		||||
		if (nvlist_lookup_boolean_value(innvl, "force", &force) != 0) {
 | 
			
		||||
			err = SET_ERROR(EINVAL);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (innvl)
 | 
			
		||||
		force = fnvlist_lookup_boolean_value(innvl, "force");
 | 
			
		||||
 | 
			
		||||
	if (force) {
 | 
			
		||||
		spa_config_enter(spa, SCL_CONFIG, FTAG, RW_WRITER);
 | 
			
		||||
@ -6114,7 +6255,7 @@ zfs_ioc_pool_sync(const char *pool, nvlist_t *innvl, nvlist_t *onvl)
 | 
			
		||||
		spa_config_exit(spa, SCL_CONFIG, FTAG);
 | 
			
		||||
	}
 | 
			
		||||
	txg_wait_synced(spa_get_dsl(spa), 0);
 | 
			
		||||
out:
 | 
			
		||||
 | 
			
		||||
	spa_close(spa, FTAG);
 | 
			
		||||
 | 
			
		||||
	return (err);
 | 
			
		||||
@ -6129,6 +6270,11 @@ out:
 | 
			
		||||
 *         presence indicated key should only be verified, not loaded
 | 
			
		||||
 * }
 | 
			
		||||
 */
 | 
			
		||||
static const zfs_ioc_key_t zfs_keys_load_key[] = {
 | 
			
		||||
	{"hidden_args",	DATA_TYPE_NVLIST,	0},
 | 
			
		||||
	{"noop",	DATA_TYPE_BOOLEAN,	ZK_OPTIONAL},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* ARGSUSED */
 | 
			
		||||
static int
 | 
			
		||||
zfs_ioc_load_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
@ -6143,11 +6289,7 @@ zfs_ioc_load_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = nvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS, &hidden_args);
 | 
			
		||||
	if (ret != 0) {
 | 
			
		||||
		ret = SET_ERROR(EINVAL);
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
	hidden_args = fnvlist_lookup_nvlist(innvl, ZPOOL_HIDDEN_ARGS);
 | 
			
		||||
 | 
			
		||||
	ret = dsl_crypto_params_create_nvlist(DCP_CMD_NONE, NULL,
 | 
			
		||||
	    hidden_args, &dcp);
 | 
			
		||||
@ -6171,6 +6313,10 @@ error:
 | 
			
		||||
 * Unload a user's wrapping key from the kernel.
 | 
			
		||||
 * Both innvl and outnvl are unused.
 | 
			
		||||
 */
 | 
			
		||||
static const zfs_ioc_key_t zfs_keys_unload_key[] = {
 | 
			
		||||
	/* no nvl keys */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* ARGSUSED */
 | 
			
		||||
static int
 | 
			
		||||
zfs_ioc_unload_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
@ -6203,6 +6349,12 @@ out:
 | 
			
		||||
 *
 | 
			
		||||
 * outnvl is unused
 | 
			
		||||
 */
 | 
			
		||||
static const zfs_ioc_key_t zfs_keys_change_key[] = {
 | 
			
		||||
	{"crypt_cmd",	DATA_TYPE_UINT64,	ZK_OPTIONAL},
 | 
			
		||||
	{"hidden_args",	DATA_TYPE_NVLIST,	ZK_OPTIONAL},
 | 
			
		||||
	{"props",	DATA_TYPE_NVLIST,	ZK_OPTIONAL},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* ARGSUSED */
 | 
			
		||||
static int
 | 
			
		||||
zfs_ioc_change_key(const char *dsname, nvlist_t *innvl, nvlist_t *outnvl)
 | 
			
		||||
@ -6267,7 +6419,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];
 | 
			
		||||
 | 
			
		||||
@ -6286,6 +6438,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
 | 
			
		||||
@ -6348,102 +6502,128 @@ 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("receive", ZFS_IOC_RECV_NEW,
 | 
			
		||||
	    zfs_ioc_recv_new, zfs_secpolicy_recv_new, DATASET_NAME,
 | 
			
		||||
	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE);
 | 
			
		||||
	    POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE,
 | 
			
		||||
	    zfs_keys_recv_new, ARRAY_SIZE(zfs_keys_recv_new));
 | 
			
		||||
	zfs_ioctl_register("load-key", ZFS_IOC_LOAD_KEY,
 | 
			
		||||
	    zfs_ioc_load_key, zfs_secpolicy_load_key,
 | 
			
		||||
	    DATASET_NAME, POOL_CHECK_SUSPENDED, B_TRUE, B_TRUE);
 | 
			
		||||
	    DATASET_NAME, POOL_CHECK_SUSPENDED, B_TRUE, B_TRUE,
 | 
			
		||||
	    zfs_keys_load_key, ARRAY_SIZE(zfs_keys_load_key));
 | 
			
		||||
	zfs_ioctl_register("unload-key", ZFS_IOC_UNLOAD_KEY,
 | 
			
		||||
	    zfs_ioc_unload_key, zfs_secpolicy_load_key,
 | 
			
		||||
	    DATASET_NAME, POOL_CHECK_SUSPENDED, B_TRUE, B_TRUE);
 | 
			
		||||
	    DATASET_NAME, POOL_CHECK_SUSPENDED, B_TRUE, B_TRUE,
 | 
			
		||||
	    zfs_keys_unload_key, ARRAY_SIZE(zfs_keys_unload_key));
 | 
			
		||||
	zfs_ioctl_register("change-key", ZFS_IOC_CHANGE_KEY,
 | 
			
		||||
	    zfs_ioc_change_key, zfs_secpolicy_change_key,
 | 
			
		||||
	    DATASET_NAME, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY,
 | 
			
		||||
	    B_TRUE, B_TRUE);
 | 
			
		||||
	    B_TRUE, B_TRUE, zfs_keys_change_key,
 | 
			
		||||
	    ARRAY_SIZE(zfs_keys_change_key));
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	    B_TRUE, zfs_keys_pool_reopen, ARRAY_SIZE(zfs_keys_pool_reopen));
 | 
			
		||||
 | 
			
		||||
	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));
 | 
			
		||||
 | 
			
		||||
	/* IOCTLS that use the legacy function signature */
 | 
			
		||||
 | 
			
		||||
@ -6587,6 +6767,80 @@ zfs_ioctl_init(void)
 | 
			
		||||
	    zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_NONE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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)
 | 
			
		||||
@ -6801,7 +7055,7 @@ zfsdev_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
 | 
			
		||||
 | 
			
		||||
	vecnum = cmd - ZFS_IOC_FIRST;
 | 
			
		||||
	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];
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
@ -6809,7 +7063,7 @@ zfsdev_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
 | 
			
		||||
	 * a normal or legacy handler are registered.
 | 
			
		||||
	 */
 | 
			
		||||
	if (vec->zvec_func == NULL && vec->zvec_legacy_func == NULL)
 | 
			
		||||
		return (-SET_ERROR(EINVAL));
 | 
			
		||||
		return (-SET_ERROR(ZFS_ERR_IOC_CMD_UNAVAIL));
 | 
			
		||||
 | 
			
		||||
	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
 | 
			
		||||
 | 
			
		||||
@ -6869,6 +7123,19 @@ zfsdev_ioctl(struct file *filp, unsigned cmd, unsigned long arg)
 | 
			
		||||
		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) {
 | 
			
		||||
		cookie = spl_fstrans_mark();
 | 
			
		||||
 | 
			
		||||
@ -858,5 +858,5 @@ tests = ['zvol_swap_001_pos', 'zvol_swap_002_pos', 'zvol_swap_003_pos',
 | 
			
		||||
tags = ['functional', 'zvol', 'zvol_swap']
 | 
			
		||||
 | 
			
		||||
[tests/functional/libzfs]
 | 
			
		||||
tests = ['many_fds']
 | 
			
		||||
tests = ['many_fds', 'libzfs_input']
 | 
			
		||||
tags = ['functional', 'libzfs']
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@ SUBDIRS = \
 | 
			
		||||
	file_trunc \
 | 
			
		||||
	file_write \
 | 
			
		||||
	largest_file \
 | 
			
		||||
	libzfs_input_check \
 | 
			
		||||
	mkbusy \
 | 
			
		||||
	mkfile \
 | 
			
		||||
	mkfiles \
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								tests/zfs-tests/cmd/libzfs_input_check/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/zfs-tests/cmd/libzfs_input_check/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
/libzfs_input_check
 | 
			
		||||
							
								
								
									
										14
									
								
								tests/zfs-tests/cmd/libzfs_input_check/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/zfs-tests/cmd/libzfs_input_check/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
include $(top_srcdir)/config/Rules.am
 | 
			
		||||
 | 
			
		||||
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/bin
 | 
			
		||||
 | 
			
		||||
DEFAULT_INCLUDES += \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_srcdir)/lib/libspl/include
 | 
			
		||||
 | 
			
		||||
pkgexec_PROGRAMS = libzfs_input_check
 | 
			
		||||
 | 
			
		||||
libzfs_input_check_SOURCES = libzfs_input_check.c
 | 
			
		||||
libzfs_input_check_LDADD = \
 | 
			
		||||
	$(top_builddir)/lib/libnvpair/libnvpair.la \
 | 
			
		||||
	$(top_builddir)/lib/libzfs_core/libzfs_core.la
 | 
			
		||||
							
								
								
									
										906
									
								
								tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										906
									
								
								tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,906 @@
 | 
			
		||||
/*
 | 
			
		||||
 * CDDL HEADER START
 | 
			
		||||
 *
 | 
			
		||||
 * This file and its contents are supplied under the terms of the
 | 
			
		||||
 * Common Development and Distribution License ("CDDL"), version 1.0.
 | 
			
		||||
 * You may only use this file in accordance with the terms of version
 | 
			
		||||
 * 1.0 of the CDDL.
 | 
			
		||||
 *
 | 
			
		||||
 * A full copy of the text of the CDDL should have accompanied this
 | 
			
		||||
 * source.  A copy of the CDDL is also available via the Internet at
 | 
			
		||||
 * http://www.illumos.org/license/CDDL.
 | 
			
		||||
 *
 | 
			
		||||
 * CDDL HEADER END
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2018 by Delphix. All rights reserved.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <strings.h>
 | 
			
		||||
#include <libzfs_core.h>
 | 
			
		||||
 | 
			
		||||
#include <sys/nvpair.h>
 | 
			
		||||
#include <sys/zfs_ioctl.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Test the nvpair inputs for the non-legacy zfs ioctl commands.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
boolean_t unexpected_failures;
 | 
			
		||||
int zfs_fd;
 | 
			
		||||
const char *active_test;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Tracks which zfs_ioc_t commands were tested
 | 
			
		||||
 */
 | 
			
		||||
boolean_t ioc_tested[256];
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Legacy ioctls that are skipped (for now)
 | 
			
		||||
 */
 | 
			
		||||
static unsigned ioc_skip[] = {
 | 
			
		||||
	ZFS_IOC_POOL_CREATE,
 | 
			
		||||
	ZFS_IOC_POOL_DESTROY,
 | 
			
		||||
	ZFS_IOC_POOL_IMPORT,
 | 
			
		||||
	ZFS_IOC_POOL_EXPORT,
 | 
			
		||||
	ZFS_IOC_POOL_CONFIGS,
 | 
			
		||||
	ZFS_IOC_POOL_STATS,
 | 
			
		||||
	ZFS_IOC_POOL_TRYIMPORT,
 | 
			
		||||
	ZFS_IOC_POOL_SCAN,
 | 
			
		||||
	ZFS_IOC_POOL_FREEZE,
 | 
			
		||||
	ZFS_IOC_POOL_UPGRADE,
 | 
			
		||||
	ZFS_IOC_POOL_GET_HISTORY,
 | 
			
		||||
 | 
			
		||||
	ZFS_IOC_VDEV_ADD,
 | 
			
		||||
	ZFS_IOC_VDEV_REMOVE,
 | 
			
		||||
	ZFS_IOC_VDEV_SET_STATE,
 | 
			
		||||
	ZFS_IOC_VDEV_ATTACH,
 | 
			
		||||
	ZFS_IOC_VDEV_DETACH,
 | 
			
		||||
	ZFS_IOC_VDEV_SETPATH,
 | 
			
		||||
	ZFS_IOC_VDEV_SETFRU,
 | 
			
		||||
 | 
			
		||||
	ZFS_IOC_OBJSET_STATS,
 | 
			
		||||
	ZFS_IOC_OBJSET_ZPLPROPS,
 | 
			
		||||
	ZFS_IOC_DATASET_LIST_NEXT,
 | 
			
		||||
	ZFS_IOC_SNAPSHOT_LIST_NEXT,
 | 
			
		||||
	ZFS_IOC_SET_PROP,
 | 
			
		||||
	ZFS_IOC_DESTROY,
 | 
			
		||||
	ZFS_IOC_RENAME,
 | 
			
		||||
	ZFS_IOC_RECV,
 | 
			
		||||
	ZFS_IOC_SEND,
 | 
			
		||||
	ZFS_IOC_INJECT_FAULT,
 | 
			
		||||
	ZFS_IOC_CLEAR_FAULT,
 | 
			
		||||
	ZFS_IOC_INJECT_LIST_NEXT,
 | 
			
		||||
	ZFS_IOC_ERROR_LOG,
 | 
			
		||||
	ZFS_IOC_CLEAR,
 | 
			
		||||
	ZFS_IOC_PROMOTE,
 | 
			
		||||
	ZFS_IOC_DSOBJ_TO_DSNAME,
 | 
			
		||||
	ZFS_IOC_OBJ_TO_PATH,
 | 
			
		||||
	ZFS_IOC_POOL_SET_PROPS,
 | 
			
		||||
	ZFS_IOC_POOL_GET_PROPS,
 | 
			
		||||
	ZFS_IOC_SET_FSACL,
 | 
			
		||||
	ZFS_IOC_GET_FSACL,
 | 
			
		||||
	ZFS_IOC_SHARE,
 | 
			
		||||
	ZFS_IOC_INHERIT_PROP,
 | 
			
		||||
	ZFS_IOC_SMB_ACL,
 | 
			
		||||
	ZFS_IOC_USERSPACE_ONE,
 | 
			
		||||
	ZFS_IOC_USERSPACE_MANY,
 | 
			
		||||
	ZFS_IOC_USERSPACE_UPGRADE,
 | 
			
		||||
	ZFS_IOC_OBJSET_RECVD_PROPS,
 | 
			
		||||
	ZFS_IOC_VDEV_SPLIT,
 | 
			
		||||
	ZFS_IOC_NEXT_OBJ,
 | 
			
		||||
	ZFS_IOC_DIFF,
 | 
			
		||||
	ZFS_IOC_TMP_SNAPSHOT,
 | 
			
		||||
	ZFS_IOC_OBJ_TO_STATS,
 | 
			
		||||
	ZFS_IOC_SPACE_WRITTEN,
 | 
			
		||||
	ZFS_IOC_POOL_REGUID,
 | 
			
		||||
	ZFS_IOC_SEND_PROGRESS,
 | 
			
		||||
 | 
			
		||||
	ZFS_IOC_EVENTS_NEXT,
 | 
			
		||||
	ZFS_IOC_EVENTS_CLEAR,
 | 
			
		||||
	ZFS_IOC_EVENTS_SEEK,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define	IOC_INPUT_TEST(ioc, name, req, opt, err)		\
 | 
			
		||||
	IOC_INPUT_TEST_IMPL(ioc, name, req, opt, err, B_FALSE)
 | 
			
		||||
 | 
			
		||||
#define	IOC_INPUT_TEST_WILD(ioc, name, req, opt, err)		\
 | 
			
		||||
	IOC_INPUT_TEST_IMPL(ioc, name, req, opt, err, B_TRUE)
 | 
			
		||||
 | 
			
		||||
#define	IOC_INPUT_TEST_IMPL(ioc, name, req, opt, err, wild)	\
 | 
			
		||||
	do {							\
 | 
			
		||||
		active_test = __func__ + 5;			\
 | 
			
		||||
		ioc_tested[ioc - ZFS_IOC_FIRST] = B_TRUE;	\
 | 
			
		||||
		lzc_ioctl_test(ioc, name, req, opt, err, wild);	\
 | 
			
		||||
	} while (0)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * run a zfs ioctl command, verify expected results and log failures
 | 
			
		||||
 */
 | 
			
		||||
static void
 | 
			
		||||
lzc_ioctl_run(zfs_ioc_t ioc, const char *name, nvlist_t *innvl, int expected)
 | 
			
		||||
{
 | 
			
		||||
	zfs_cmd_t zc = {"\0"};
 | 
			
		||||
	char *packed = NULL;
 | 
			
		||||
	const char *variant;
 | 
			
		||||
	size_t size = 0;
 | 
			
		||||
	int error = 0;
 | 
			
		||||
 | 
			
		||||
	switch (expected) {
 | 
			
		||||
	case ZFS_ERR_IOC_ARG_UNAVAIL:
 | 
			
		||||
		variant = "unsupported input";
 | 
			
		||||
		break;
 | 
			
		||||
	case ZFS_ERR_IOC_ARG_REQUIRED:
 | 
			
		||||
		variant = "missing input";
 | 
			
		||||
		break;
 | 
			
		||||
	case ZFS_ERR_IOC_ARG_BADTYPE:
 | 
			
		||||
		variant = "invalid input type";
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		variant = "valid input";
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	packed = fnvlist_pack(innvl, &size);
 | 
			
		||||
	(void) strncpy(zc.zc_name, name, sizeof (zc.zc_name));
 | 
			
		||||
	zc.zc_name[sizeof (zc.zc_name) - 1] = '\0';
 | 
			
		||||
	zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
 | 
			
		||||
	zc.zc_nvlist_src_size = size;
 | 
			
		||||
	zc.zc_nvlist_dst_size = MAX(size * 2, 128 * 1024);
 | 
			
		||||
	zc.zc_nvlist_dst = (uint64_t)(uintptr_t)malloc(zc.zc_nvlist_dst_size);
 | 
			
		||||
 | 
			
		||||
	if (ioctl(zfs_fd, ioc, &zc) != 0)
 | 
			
		||||
		error = errno;
 | 
			
		||||
 | 
			
		||||
	if (error != expected) {
 | 
			
		||||
		unexpected_failures = B_TRUE;
 | 
			
		||||
		(void) fprintf(stderr, "%s: Unexpected result with %s, "
 | 
			
		||||
		    "error %d (expecting %d)\n",
 | 
			
		||||
		    active_test, variant, error, expected);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fnvlist_pack_free(packed, size);
 | 
			
		||||
	free((void *)(uintptr_t)zc.zc_nvlist_dst);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Test each ioc for the folowing ioctl input errors:
 | 
			
		||||
 *   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
 | 
			
		||||
 */
 | 
			
		||||
static int
 | 
			
		||||
lzc_ioctl_test(zfs_ioc_t ioc, const char *name, nvlist_t *required,
 | 
			
		||||
    nvlist_t *optional, int expected_error, boolean_t wildcard)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *input = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *future = fnvlist_alloc();
 | 
			
		||||
	int error = 0;
 | 
			
		||||
 | 
			
		||||
	if (required != NULL) {
 | 
			
		||||
		for (nvpair_t *pair = nvlist_next_nvpair(required, NULL);
 | 
			
		||||
		    pair != NULL; pair = nvlist_next_nvpair(required, pair)) {
 | 
			
		||||
			fnvlist_add_nvpair(input, pair);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (optional != NULL) {
 | 
			
		||||
		for (nvpair_t *pair = nvlist_next_nvpair(optional, NULL);
 | 
			
		||||
		    pair != NULL; pair = nvlist_next_nvpair(optional, pair)) {
 | 
			
		||||
			fnvlist_add_nvpair(input, pair);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Generic input run with 'optional' nvlist pair
 | 
			
		||||
	 */
 | 
			
		||||
	if (!wildcard)
 | 
			
		||||
		fnvlist_add_nvlist(input, "optional", future);
 | 
			
		||||
	lzc_ioctl_run(ioc, name, input, expected_error);
 | 
			
		||||
	if (!wildcard)
 | 
			
		||||
		fnvlist_remove(input, "optional");
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Bogus input value
 | 
			
		||||
	 */
 | 
			
		||||
	if (!wildcard) {
 | 
			
		||||
		fnvlist_add_string(input, "bogus_input", "bogus");
 | 
			
		||||
		lzc_ioctl_run(ioc, name, input, ZFS_ERR_IOC_ARG_UNAVAIL);
 | 
			
		||||
		fnvlist_remove(input, "bogus_input");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Missing required inputs
 | 
			
		||||
	 */
 | 
			
		||||
	if (required != NULL) {
 | 
			
		||||
		nvlist_t *empty = fnvlist_alloc();
 | 
			
		||||
		lzc_ioctl_run(ioc, name, empty, ZFS_ERR_IOC_ARG_REQUIRED);
 | 
			
		||||
		nvlist_free(empty);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Wrong nvpair type
 | 
			
		||||
	 */
 | 
			
		||||
	if (required != NULL || optional != NULL) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * switch the type of one of the input pairs
 | 
			
		||||
		 */
 | 
			
		||||
		for (nvpair_t *pair = nvlist_next_nvpair(input, NULL);
 | 
			
		||||
		    pair != NULL; pair = nvlist_next_nvpair(input, pair)) {
 | 
			
		||||
			char pname[MAXNAMELEN];
 | 
			
		||||
			data_type_t ptype;
 | 
			
		||||
 | 
			
		||||
			strncpy(pname, nvpair_name(pair), sizeof (pname));
 | 
			
		||||
			pname[sizeof (pname) - 1] = '\0';
 | 
			
		||||
			ptype = nvpair_type(pair);
 | 
			
		||||
			fnvlist_remove_nvpair(input, pair);
 | 
			
		||||
 | 
			
		||||
			switch (ptype) {
 | 
			
		||||
			case DATA_TYPE_STRING:
 | 
			
		||||
				fnvlist_add_uint64(input, pname, 42);
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				fnvlist_add_string(input, pname, "bogus");
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		lzc_ioctl_run(ioc, name, input, ZFS_ERR_IOC_ARG_BADTYPE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nvlist_free(future);
 | 
			
		||||
	nvlist_free(input);
 | 
			
		||||
 | 
			
		||||
	return (error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_pool_sync(const char *pool)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_boolean_value(required, "force", B_TRUE);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_POOL_SYNC, pool, required, NULL, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_pool_reopen(const char *pool)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_boolean_value(required, "scrub_restart", B_FALSE);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_POOL_REOPEN, pool, required, NULL, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_pool_checkpoint(const char *pool)
 | 
			
		||||
{
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_POOL_CHECKPOINT, pool, NULL, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_pool_discard_checkpoint(const char *pool)
 | 
			
		||||
{
 | 
			
		||||
	int err = lzc_pool_checkpoint(pool);
 | 
			
		||||
	if (err == 0 || err == ZFS_ERR_CHECKPOINT_EXISTS)
 | 
			
		||||
		IOC_INPUT_TEST(ZFS_IOC_POOL_DISCARD_CHECKPOINT, pool, NULL,
 | 
			
		||||
		    NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_log_history(const char *pool)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_string(required, "message", "input check");
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_LOG_HISTORY, pool, required, NULL, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_create(const char *pool)
 | 
			
		||||
{
 | 
			
		||||
	char dataset[MAXNAMELEN + 32];
 | 
			
		||||
 | 
			
		||||
	(void) snprintf(dataset, sizeof (dataset), "%s/create-fs", pool);
 | 
			
		||||
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *optional = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *props = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_int32(required, "type", DMU_OST_ZFS);
 | 
			
		||||
	fnvlist_add_uint64(props, "recordsize", 8192);
 | 
			
		||||
	fnvlist_add_nvlist(optional, "props", props);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_CREATE, dataset, required, optional, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
	nvlist_free(optional);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_snapshot(const char *pool, const char *snapshot)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *optional = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *snaps = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *props = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_boolean(snaps, snapshot);
 | 
			
		||||
	fnvlist_add_nvlist(required, "snaps", snaps);
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_string(props, "org.openzfs:launch", "September 17th, 2013");
 | 
			
		||||
	fnvlist_add_nvlist(optional, "props", props);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_SNAPSHOT, pool, required, optional, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(props);
 | 
			
		||||
	nvlist_free(snaps);
 | 
			
		||||
	nvlist_free(optional);
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_space_snaps(const char *snapshot)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
	fnvlist_add_string(required, "firstsnap", snapshot);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_SPACE_SNAPS, snapshot, required, NULL, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_destroy_snaps(const char *pool, const char *snapshot)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *snaps = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_boolean(snaps, snapshot);
 | 
			
		||||
	fnvlist_add_nvlist(required, "snaps", snaps);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_DESTROY_SNAPS, pool, required, NULL, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(snaps);
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_bookmark(const char *pool, const char *snapshot, const char *bookmark)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_string(required, bookmark, snapshot);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST_WILD(ZFS_IOC_BOOKMARK, pool, required, NULL, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_get_bookmarks(const char *dataset)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *optional = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_boolean(optional, "guid");
 | 
			
		||||
	fnvlist_add_boolean(optional, "createtxg");
 | 
			
		||||
	fnvlist_add_boolean(optional, "creation");
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST_WILD(ZFS_IOC_GET_BOOKMARKS, dataset, NULL, optional, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(optional);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_destroy_bookmarks(const char *pool, const char *bookmark)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_boolean(required, bookmark);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST_WILD(ZFS_IOC_DESTROY_BOOKMARKS, pool, required, NULL, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_clone(const char *snapshot, const char *clone)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *optional = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *props = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_string(required, "origin", snapshot);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_CLONE, clone, required, NULL, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(props);
 | 
			
		||||
	nvlist_free(optional);
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_rollback(const char *dataset, const char *snapshot)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *optional = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_string(optional, "target", snapshot);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_ROLLBACK, dataset, NULL, optional, B_FALSE);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(optional);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_hold(const char *pool, const char *snapshot)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *optional = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *holds = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_string(holds, snapshot, "libzfs_check_hold");
 | 
			
		||||
	fnvlist_add_nvlist(required, "holds", holds);
 | 
			
		||||
	fnvlist_add_int32(optional, "cleanup_fd", zfs_fd);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_HOLD, pool, required, optional, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(holds);
 | 
			
		||||
	nvlist_free(optional);
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_get_holds(const char *snapshot)
 | 
			
		||||
{
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_GET_HOLDS, snapshot, NULL, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_release(const char *pool, const char *snapshot)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *release = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_boolean(release, "libzfs_check_hold");
 | 
			
		||||
	fnvlist_add_nvlist(required, snapshot, release);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST_WILD(ZFS_IOC_RELEASE, pool, required, NULL, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(release);
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_send_new(const char *snapshot, int fd)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *optional = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_int32(required, "fd", fd);
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_boolean(optional, "largeblockok");
 | 
			
		||||
	fnvlist_add_boolean(optional, "embedok");
 | 
			
		||||
	fnvlist_add_boolean(optional, "compressok");
 | 
			
		||||
	fnvlist_add_boolean(optional, "rawok");
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * TODO - Resumable send is harder to set up. So we currently
 | 
			
		||||
	 * ignore testing for that variant.
 | 
			
		||||
	 */
 | 
			
		||||
#if 0
 | 
			
		||||
	fnvlist_add_string(optional, "fromsnap", from);
 | 
			
		||||
	fnvlist_add_uint64(optional, "resume_object", resumeobj);
 | 
			
		||||
	fnvlist_add_uint64(optional, "resume_offset", offset);
 | 
			
		||||
#endif
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_SEND_NEW, snapshot, required, optional, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(optional);
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_recv_new(const char *dataset, int fd)
 | 
			
		||||
{
 | 
			
		||||
	dmu_replay_record_t drr = { 0 };
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *optional = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *props = fnvlist_alloc();
 | 
			
		||||
	char snapshot[MAXNAMELEN + 32];
 | 
			
		||||
	ssize_t count;
 | 
			
		||||
 | 
			
		||||
	int cleanup_fd = open(ZFS_DEV, O_RDWR);
 | 
			
		||||
 | 
			
		||||
	(void) snprintf(snapshot, sizeof (snapshot), "%s@replicant", dataset);
 | 
			
		||||
 | 
			
		||||
	count = pread(fd, &drr, sizeof (drr), 0);
 | 
			
		||||
	if (count != sizeof (drr)) {
 | 
			
		||||
		(void) fprintf(stderr, "could not read stream: %s\n",
 | 
			
		||||
		    strerror(errno));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_string(required, "snapname", snapshot);
 | 
			
		||||
	fnvlist_add_byte_array(required, "begin_record", (uchar_t *)&drr,
 | 
			
		||||
	    sizeof (drr));
 | 
			
		||||
	fnvlist_add_int32(required, "input_fd", fd);
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_string(props, "org.openzfs:launch", "September 17th, 2013");
 | 
			
		||||
	fnvlist_add_nvlist(optional, "localprops", props);
 | 
			
		||||
	fnvlist_add_boolean(optional, "force");
 | 
			
		||||
	fnvlist_add_int32(optional, "cleanup_fd", cleanup_fd);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * TODO - Resumable receive is harder to set up. So we currently
 | 
			
		||||
	 * ignore testing for one.
 | 
			
		||||
	 */
 | 
			
		||||
#if 0
 | 
			
		||||
	fnvlist_add_nvlist(optional, "props", recvdprops);
 | 
			
		||||
	fnvlist_add_string(optional, "origin", origin);
 | 
			
		||||
	fnvlist_add_boolean(optional, "resumable");
 | 
			
		||||
	fnvlist_add_uint64(optional, "action_handle", *action_handle);
 | 
			
		||||
#endif
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_RECV_NEW, dataset, required, optional, EBADE);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(props);
 | 
			
		||||
	nvlist_free(optional);
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
 | 
			
		||||
	(void) close(cleanup_fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_send_space(const char *snapshot1, const char *snapshot2)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *optional = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_string(optional, "from", snapshot1);
 | 
			
		||||
	fnvlist_add_boolean(optional, "largeblockok");
 | 
			
		||||
	fnvlist_add_boolean(optional, "embedok");
 | 
			
		||||
	fnvlist_add_boolean(optional, "compressok");
 | 
			
		||||
	fnvlist_add_boolean(optional, "rawok");
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_SEND_SPACE, snapshot2, NULL, optional, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(optional);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_remap(const char *dataset)
 | 
			
		||||
{
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_REMAP, dataset, NULL, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_channel_program(const char *pool)
 | 
			
		||||
{
 | 
			
		||||
	const char *program =
 | 
			
		||||
	    "arg = ...\n"
 | 
			
		||||
	    "argv = arg[\"argv\"]\n"
 | 
			
		||||
	    "return argv[1]";
 | 
			
		||||
	char *const argv[1] = { "Hello World!" };
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *optional = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *args = fnvlist_alloc();
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_string(required, "program", program);
 | 
			
		||||
	fnvlist_add_string_array(args, "argv", argv, 1);
 | 
			
		||||
	fnvlist_add_nvlist(required, "arg", args);
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_boolean_value(optional, "sync", B_TRUE);
 | 
			
		||||
	fnvlist_add_uint64(optional, "instrlimit", 1000 * 1000);
 | 
			
		||||
	fnvlist_add_uint64(optional, "memlimit", 8192 * 1024);
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_CHANNEL_PROGRAM, pool, required, optional, 0);
 | 
			
		||||
 | 
			
		||||
	nvlist_free(args);
 | 
			
		||||
	nvlist_free(optional);
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define	WRAPPING_KEY_LEN	32
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_load_key(const char *dataset)
 | 
			
		||||
{
 | 
			
		||||
	nvlist_t *required = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *optional = fnvlist_alloc();
 | 
			
		||||
	nvlist_t *hidden = fnvlist_alloc();
 | 
			
		||||
	uint8_t keydata[WRAPPING_KEY_LEN] = {0};
 | 
			
		||||
 | 
			
		||||
	fnvlist_add_uint8_array(hidden, "wkeydata", keydata, sizeof (keydata));
 | 
			
		||||
	fnvlist_add_nvlist(required, "hidden_args", hidden);
 | 
			
		||||
	fnvlist_add_boolean(optional, "noop");
 | 
			
		||||
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_LOAD_KEY, dataset, required, optional, EINVAL);
 | 
			
		||||
	nvlist_free(hidden);
 | 
			
		||||
	nvlist_free(optional);
 | 
			
		||||
	nvlist_free(required);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_change_key(const char *dataset)
 | 
			
		||||
{
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_CHANGE_KEY, dataset, NULL, NULL, EINVAL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
test_unload_key(const char *dataset)
 | 
			
		||||
{
 | 
			
		||||
	IOC_INPUT_TEST(ZFS_IOC_UNLOAD_KEY, dataset, NULL, NULL, EACCES);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
zfs_destroy(const char *dataset)
 | 
			
		||||
{
 | 
			
		||||
	zfs_cmd_t zc = {"\0"};
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	(void) strncpy(zc.zc_name, dataset, sizeof (zc.zc_name));
 | 
			
		||||
	zc.zc_name[sizeof (zc.zc_name) - 1] = '\0';
 | 
			
		||||
	zc.zc_objset_type = DMU_OST_ZFS;
 | 
			
		||||
	err = ioctl(zfs_fd, ZFS_IOC_DESTROY, &zc);
 | 
			
		||||
 | 
			
		||||
	return (err == 0 ? 0 : errno);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
zfs_ioc_input_tests(const char *pool)
 | 
			
		||||
{
 | 
			
		||||
	char filepath[] = "/tmp/ioc_test_file_XXXXXX";
 | 
			
		||||
	char dataset[ZFS_MAX_DATASET_NAME_LEN];
 | 
			
		||||
	char snapbase[ZFS_MAX_DATASET_NAME_LEN + 32];
 | 
			
		||||
	char snapshot[ZFS_MAX_DATASET_NAME_LEN + 32];
 | 
			
		||||
	char bookmark[ZFS_MAX_DATASET_NAME_LEN + 32];
 | 
			
		||||
	char backup[ZFS_MAX_DATASET_NAME_LEN];
 | 
			
		||||
	char clone[ZFS_MAX_DATASET_NAME_LEN];
 | 
			
		||||
	int tmpfd, err;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Setup names and create a working dataset
 | 
			
		||||
	 */
 | 
			
		||||
	(void) snprintf(dataset, sizeof (dataset), "%s/test-fs", pool);
 | 
			
		||||
	(void) snprintf(snapbase, sizeof (snapbase), "%s@snapbase", dataset);
 | 
			
		||||
	(void) snprintf(snapshot, sizeof (snapshot), "%s@snapshot", dataset);
 | 
			
		||||
	(void) snprintf(bookmark, sizeof (bookmark), "%s#bookmark", dataset);
 | 
			
		||||
	(void) snprintf(clone, sizeof (clone), "%s/test-fs-clone", pool);
 | 
			
		||||
	(void) snprintf(backup, sizeof (backup), "%s/backup", pool);
 | 
			
		||||
 | 
			
		||||
	err = lzc_create(dataset, DMU_OST_ZFS, NULL, NULL, 0);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		(void) fprintf(stderr, "could not create '%s': %s\n",
 | 
			
		||||
		    dataset, strerror(errno));
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmpfd = mkstemp(filepath);
 | 
			
		||||
	if (tmpfd < 0) {
 | 
			
		||||
		(void) fprintf(stderr, "could not create '%s': %s\n",
 | 
			
		||||
		    filepath, strerror(errno));
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * run a test for each ioctl
 | 
			
		||||
	 * Note that some test build on previous test operations
 | 
			
		||||
	 */
 | 
			
		||||
	test_pool_sync(pool);
 | 
			
		||||
	test_pool_reopen(pool);
 | 
			
		||||
	test_pool_checkpoint(pool);
 | 
			
		||||
	test_pool_discard_checkpoint(pool);
 | 
			
		||||
	test_log_history(pool);
 | 
			
		||||
 | 
			
		||||
	test_create(dataset);
 | 
			
		||||
	test_snapshot(pool, snapbase);
 | 
			
		||||
	test_snapshot(pool, snapshot);
 | 
			
		||||
 | 
			
		||||
	test_space_snaps(snapshot);
 | 
			
		||||
	test_send_space(snapbase, snapshot);
 | 
			
		||||
	test_send_new(snapshot, tmpfd);
 | 
			
		||||
	test_recv_new(backup, tmpfd);
 | 
			
		||||
 | 
			
		||||
	test_bookmark(pool, snapshot, bookmark);
 | 
			
		||||
	test_get_bookmarks(dataset);
 | 
			
		||||
	test_destroy_bookmarks(pool, bookmark);
 | 
			
		||||
 | 
			
		||||
	test_hold(pool, snapshot);
 | 
			
		||||
	test_get_holds(snapshot);
 | 
			
		||||
	test_release(pool, snapshot);
 | 
			
		||||
 | 
			
		||||
	test_clone(snapshot, clone);
 | 
			
		||||
	zfs_destroy(clone);
 | 
			
		||||
 | 
			
		||||
	test_rollback(dataset, snapshot);
 | 
			
		||||
	test_destroy_snaps(pool, snapshot);
 | 
			
		||||
	test_destroy_snaps(pool, snapbase);
 | 
			
		||||
 | 
			
		||||
	test_remap(dataset);
 | 
			
		||||
	test_channel_program(pool);
 | 
			
		||||
 | 
			
		||||
	test_load_key(dataset);
 | 
			
		||||
	test_change_key(dataset);
 | 
			
		||||
	test_unload_key(dataset);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * cleanup
 | 
			
		||||
	 */
 | 
			
		||||
	zfs_cmd_t zc = {"\0"};
 | 
			
		||||
 | 
			
		||||
	nvlist_t *snaps = fnvlist_alloc();
 | 
			
		||||
	fnvlist_add_boolean(snaps, snapshot);
 | 
			
		||||
	(void) lzc_destroy_snaps(snaps, B_FALSE, NULL);
 | 
			
		||||
	nvlist_free(snaps);
 | 
			
		||||
 | 
			
		||||
	(void) zfs_destroy(dataset);
 | 
			
		||||
	(void) zfs_destroy(backup);
 | 
			
		||||
 | 
			
		||||
	(void) close(tmpfd);
 | 
			
		||||
	(void) unlink(filepath);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * All the unused slots should yield ZFS_ERR_IOC_CMD_UNAVAIL
 | 
			
		||||
	 */
 | 
			
		||||
	for (int i = 0; i < ARRAY_SIZE(ioc_skip); i++) {
 | 
			
		||||
		if (ioc_tested[ioc_skip[i] - ZFS_IOC_FIRST])
 | 
			
		||||
			(void) fprintf(stderr, "cmd %d tested, not skipped!\n",
 | 
			
		||||
			    (int)(ioc_skip[i] - ZFS_IOC_FIRST));
 | 
			
		||||
 | 
			
		||||
		ioc_tested[ioc_skip[i] - ZFS_IOC_FIRST] = B_TRUE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	(void) strncpy(zc.zc_name, pool, sizeof (zc.zc_name));
 | 
			
		||||
	zc.zc_name[sizeof (zc.zc_name) - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
	for (unsigned ioc = ZFS_IOC_FIRST; ioc < ZFS_IOC_LAST; ioc++) {
 | 
			
		||||
		unsigned cmd = ioc - ZFS_IOC_FIRST;
 | 
			
		||||
 | 
			
		||||
		if (ioc_tested[cmd])
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (ioctl(zfs_fd, ioc, &zc) != 0 &&
 | 
			
		||||
		    errno != ZFS_ERR_IOC_CMD_UNAVAIL) {
 | 
			
		||||
			(void) fprintf(stderr, "cmd %d is missing a test case "
 | 
			
		||||
			    "(%d)\n", cmd, errno);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum zfs_ioc_ref {
 | 
			
		||||
	ZFS_IOC_BASE = ('Z' << 8),
 | 
			
		||||
	LINUX_IOC_BASE = ('Z' << 8) + 0x80,
 | 
			
		||||
	FREEBSD_IOC_BASE = ('Z' << 8) + 0xC0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Canonical reference check of /dev/zfs ioctl numbers.
 | 
			
		||||
 * These cannot change and new ioctl numbers must be appended.
 | 
			
		||||
 */
 | 
			
		||||
boolean_t
 | 
			
		||||
validate_ioc_values(void)
 | 
			
		||||
{
 | 
			
		||||
	return (
 | 
			
		||||
	    ZFS_IOC_BASE + 0 == ZFS_IOC_POOL_CREATE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 1 == ZFS_IOC_POOL_DESTROY &&
 | 
			
		||||
	    ZFS_IOC_BASE + 2 == ZFS_IOC_POOL_IMPORT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 3 == ZFS_IOC_POOL_EXPORT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 4 == ZFS_IOC_POOL_CONFIGS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 5 == ZFS_IOC_POOL_STATS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 6 == ZFS_IOC_POOL_TRYIMPORT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 7 == ZFS_IOC_POOL_SCAN &&
 | 
			
		||||
	    ZFS_IOC_BASE + 8 == ZFS_IOC_POOL_FREEZE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 9 == ZFS_IOC_POOL_UPGRADE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 10 == ZFS_IOC_POOL_GET_HISTORY &&
 | 
			
		||||
	    ZFS_IOC_BASE + 11 == ZFS_IOC_VDEV_ADD &&
 | 
			
		||||
	    ZFS_IOC_BASE + 12 == ZFS_IOC_VDEV_REMOVE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 13 == ZFS_IOC_VDEV_SET_STATE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 14 == ZFS_IOC_VDEV_ATTACH &&
 | 
			
		||||
	    ZFS_IOC_BASE + 15 == ZFS_IOC_VDEV_DETACH &&
 | 
			
		||||
	    ZFS_IOC_BASE + 16 == ZFS_IOC_VDEV_SETPATH &&
 | 
			
		||||
	    ZFS_IOC_BASE + 17 == ZFS_IOC_VDEV_SETFRU &&
 | 
			
		||||
	    ZFS_IOC_BASE + 18 == ZFS_IOC_OBJSET_STATS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 19 == ZFS_IOC_OBJSET_ZPLPROPS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 20 == ZFS_IOC_DATASET_LIST_NEXT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 21 == ZFS_IOC_SNAPSHOT_LIST_NEXT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 22 == ZFS_IOC_SET_PROP &&
 | 
			
		||||
	    ZFS_IOC_BASE + 23 == ZFS_IOC_CREATE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 24 == ZFS_IOC_DESTROY &&
 | 
			
		||||
	    ZFS_IOC_BASE + 25 == ZFS_IOC_ROLLBACK &&
 | 
			
		||||
	    ZFS_IOC_BASE + 26 == ZFS_IOC_RENAME &&
 | 
			
		||||
	    ZFS_IOC_BASE + 27 == ZFS_IOC_RECV &&
 | 
			
		||||
	    ZFS_IOC_BASE + 28 == ZFS_IOC_SEND &&
 | 
			
		||||
	    ZFS_IOC_BASE + 29 == ZFS_IOC_INJECT_FAULT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 30 == ZFS_IOC_CLEAR_FAULT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 31 == ZFS_IOC_INJECT_LIST_NEXT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 32 == ZFS_IOC_ERROR_LOG &&
 | 
			
		||||
	    ZFS_IOC_BASE + 33 == ZFS_IOC_CLEAR &&
 | 
			
		||||
	    ZFS_IOC_BASE + 34 == ZFS_IOC_PROMOTE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 35 == ZFS_IOC_SNAPSHOT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 36 == ZFS_IOC_DSOBJ_TO_DSNAME &&
 | 
			
		||||
	    ZFS_IOC_BASE + 37 == ZFS_IOC_OBJ_TO_PATH &&
 | 
			
		||||
	    ZFS_IOC_BASE + 38 == ZFS_IOC_POOL_SET_PROPS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 39 == ZFS_IOC_POOL_GET_PROPS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 40 == ZFS_IOC_SET_FSACL &&
 | 
			
		||||
	    ZFS_IOC_BASE + 41 == ZFS_IOC_GET_FSACL &&
 | 
			
		||||
	    ZFS_IOC_BASE + 42 == ZFS_IOC_SHARE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 43 == ZFS_IOC_INHERIT_PROP &&
 | 
			
		||||
	    ZFS_IOC_BASE + 44 == ZFS_IOC_SMB_ACL &&
 | 
			
		||||
	    ZFS_IOC_BASE + 45 == ZFS_IOC_USERSPACE_ONE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 46 == ZFS_IOC_USERSPACE_MANY &&
 | 
			
		||||
	    ZFS_IOC_BASE + 47 == ZFS_IOC_USERSPACE_UPGRADE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 48 == ZFS_IOC_HOLD &&
 | 
			
		||||
	    ZFS_IOC_BASE + 49 == ZFS_IOC_RELEASE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 50 == ZFS_IOC_GET_HOLDS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 51 == ZFS_IOC_OBJSET_RECVD_PROPS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 52 == ZFS_IOC_VDEV_SPLIT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 53 == ZFS_IOC_NEXT_OBJ &&
 | 
			
		||||
	    ZFS_IOC_BASE + 54 == ZFS_IOC_DIFF &&
 | 
			
		||||
	    ZFS_IOC_BASE + 55 == ZFS_IOC_TMP_SNAPSHOT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 56 == ZFS_IOC_OBJ_TO_STATS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 57 == ZFS_IOC_SPACE_WRITTEN &&
 | 
			
		||||
	    ZFS_IOC_BASE + 58 == ZFS_IOC_SPACE_SNAPS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 59 == ZFS_IOC_DESTROY_SNAPS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 60 == ZFS_IOC_POOL_REGUID &&
 | 
			
		||||
	    ZFS_IOC_BASE + 61 == ZFS_IOC_POOL_REOPEN &&
 | 
			
		||||
	    ZFS_IOC_BASE + 62 == ZFS_IOC_SEND_PROGRESS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 63 == ZFS_IOC_LOG_HISTORY &&
 | 
			
		||||
	    ZFS_IOC_BASE + 64 == ZFS_IOC_SEND_NEW &&
 | 
			
		||||
	    ZFS_IOC_BASE + 65 == ZFS_IOC_SEND_SPACE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 66 == ZFS_IOC_CLONE &&
 | 
			
		||||
	    ZFS_IOC_BASE + 67 == ZFS_IOC_BOOKMARK &&
 | 
			
		||||
	    ZFS_IOC_BASE + 68 == ZFS_IOC_GET_BOOKMARKS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 69 == ZFS_IOC_DESTROY_BOOKMARKS &&
 | 
			
		||||
	    ZFS_IOC_BASE + 70 == ZFS_IOC_CHANNEL_PROGRAM &&
 | 
			
		||||
	    ZFS_IOC_BASE + 71 == ZFS_IOC_RECV_NEW &&
 | 
			
		||||
	    ZFS_IOC_BASE + 72 == ZFS_IOC_POOL_SYNC &&
 | 
			
		||||
	    ZFS_IOC_BASE + 73 == ZFS_IOC_LOAD_KEY &&
 | 
			
		||||
	    ZFS_IOC_BASE + 74 == ZFS_IOC_UNLOAD_KEY &&
 | 
			
		||||
	    ZFS_IOC_BASE + 75 == ZFS_IOC_CHANGE_KEY &&
 | 
			
		||||
	    ZFS_IOC_BASE + 76 == ZFS_IOC_REMAP &&
 | 
			
		||||
	    ZFS_IOC_BASE + 77 == ZFS_IOC_POOL_CHECKPOINT &&
 | 
			
		||||
	    ZFS_IOC_BASE + 78 == ZFS_IOC_POOL_DISCARD_CHECKPOINT &&
 | 
			
		||||
	    LINUX_IOC_BASE + 1 == ZFS_IOC_EVENTS_NEXT &&
 | 
			
		||||
	    LINUX_IOC_BASE + 2 == ZFS_IOC_EVENTS_CLEAR &&
 | 
			
		||||
	    LINUX_IOC_BASE + 3 == ZFS_IOC_EVENTS_SEEK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main(int argc, const char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	if (argc != 2) {
 | 
			
		||||
		(void) fprintf(stderr, "usage: %s <pool>\n", argv[0]);
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!validate_ioc_values()) {
 | 
			
		||||
		(void) fprintf(stderr, "WARNING: zfs_ioc_t has binary "
 | 
			
		||||
		    "incompatible command values\n");
 | 
			
		||||
		exit(3);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	(void) libzfs_core_init();
 | 
			
		||||
	zfs_fd = open(ZFS_DEV, O_RDWR);
 | 
			
		||||
	if (zfs_fd < 0) {
 | 
			
		||||
		(void) fprintf(stderr, "open: %s\n", strerror(errno));
 | 
			
		||||
		libzfs_core_fini();
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	zfs_ioc_input_tests(argv[1]);
 | 
			
		||||
 | 
			
		||||
	(void) close(zfs_fd);
 | 
			
		||||
	libzfs_core_fini();
 | 
			
		||||
 | 
			
		||||
	return (unexpected_failures);
 | 
			
		||||
}
 | 
			
		||||
@ -160,6 +160,7 @@ export ZFSTEST_FILES='chg_usr_exec
 | 
			
		||||
    file_trunc
 | 
			
		||||
    file_write
 | 
			
		||||
    largest_file
 | 
			
		||||
    libzfs_input_check
 | 
			
		||||
    mkbusy
 | 
			
		||||
    mkfile
 | 
			
		||||
    mkfiles
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,8 @@ pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/libzfs
 | 
			
		||||
 | 
			
		||||
dist_pkgdata_SCRIPTS = \
 | 
			
		||||
	cleanup.ksh \
 | 
			
		||||
	setup.ksh
 | 
			
		||||
	setup.ksh \
 | 
			
		||||
	libzfs_input.ksh
 | 
			
		||||
 | 
			
		||||
DEFAULT_INCLUDES += \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								tests/zfs-tests/tests/functional/libzfs/libzfs_input.ksh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										30
									
								
								tests/zfs-tests/tests/functional/libzfs/libzfs_input.ksh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
#!/bin/ksh -p
 | 
			
		||||
#
 | 
			
		||||
# This file and its contents are supplied under the terms of the
 | 
			
		||||
# Common Development and Distribution License ("CDDL"), version 1.0.
 | 
			
		||||
# You may only use this file in accordance with the terms of version
 | 
			
		||||
# 1.0 of the CDDL.
 | 
			
		||||
#
 | 
			
		||||
# A full copy of the text of the CDDL should have accompanied this
 | 
			
		||||
# source.  A copy of the CDDL is also available via the Internet at
 | 
			
		||||
# http://www.illumos.org/license/CDDL.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2018 by Delphix. All rights reserved.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
. $STF_SUITE/include/libtest.shlib
 | 
			
		||||
 | 
			
		||||
verify_runnable "global"
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# DESCRIPTION:
 | 
			
		||||
#	run C program to test passing different input to libzfs ioctls
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
log_assert "libzfs ioctls handle invalid input arguments"
 | 
			
		||||
 | 
			
		||||
log_must libzfs_input_check $TESTPOOL
 | 
			
		||||
 | 
			
		||||
log_pass "libzfs ioctls handle invalid input arguments"
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user