From 23c871671c7bc2c79ad6ec1804715f405058cdb6 Mon Sep 17 00:00:00 2001
From: Matthew Macy <mmacy@mattmacy.io>
Date: Wed, 15 Jul 2020 21:32:50 -0700
Subject: [PATCH] FreeBSD: zfs commands backward compatibility

Update the zfs commands such that they're backwards compatible with
the version of ZFS is the base FreeBSD.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Ryan Moeller <ryan@ixsystems.com>
Signed-off-by: Matt Macy <mmacy@FreeBSD.org>
Closes #10542
---
 include/os/freebsd/zfs/sys/zfs_ioctl_compat.h | 545 +-----------------
 lib/libzutil/Makefile.am                      |   6 +
 lib/libzutil/os/freebsd/zutil_compat.c        |  66 ++-
 module/Makefile.bsd                           |   1 +
 module/os/freebsd/zfs/kmod_core.c             |  40 +-
 module/os/freebsd/zfs/zfs_ioctl_compat.c      | 364 ++++++++++++
 6 files changed, 451 insertions(+), 571 deletions(-)
 create mode 100644 module/os/freebsd/zfs/zfs_ioctl_compat.c

diff --git a/include/os/freebsd/zfs/sys/zfs_ioctl_compat.h b/include/os/freebsd/zfs/sys/zfs_ioctl_compat.h
index a8db8bee52..91bc48effe 100644
--- a/include/os/freebsd/zfs/sys/zfs_ioctl_compat.h
+++ b/include/os/freebsd/zfs/sys/zfs_ioctl_compat.h
@@ -55,8 +55,8 @@ extern "C" {
 #define	ZFS_IOCVER_RESUME	5
 #define	ZFS_IOCVER_INLANES	6
 #define	ZFS_IOCVER_PAD		7
-#define	ZFS_IOCVER_FREEBSD	ZFS_IOCVER_PAD
-#define	ZFS_IOCVER_ZOF		15
+#define	ZFS_IOCVER_LEGACY	ZFS_IOCVER_PAD
+#define	ZFS_IOCVER_OZFS		15
 
 /* compatibility conversion flag */
 #define	ZFS_CMD_COMPAT_NONE	0
@@ -68,6 +68,7 @@ extern "C" {
 #define	ZFS_CMD_COMPAT_EDBP	6
 #define	ZFS_CMD_COMPAT_RESUME	7
 #define	ZFS_CMD_COMPAT_INLANES	8
+#define	ZFS_CMD_COMPAT_LEGACY	9
 
 #define	ZFS_IOC_COMPAT_PASS	254
 #define	ZFS_IOC_COMPAT_FAIL	255
@@ -80,328 +81,10 @@ typedef struct zfs_iocparm {
 	uint64_t	zfs_cmd_size;
 } zfs_iocparm_t;
 
-typedef struct zinject_record_v15 {
-	uint64_t	zi_objset;
-	uint64_t	zi_object;
-	uint64_t	zi_start;
-	uint64_t	zi_end;
-	uint64_t	zi_guid;
-	uint32_t	zi_level;
-	uint32_t	zi_error;
-	uint64_t	zi_type;
-	uint32_t	zi_freq;
-	uint32_t	zi_failfast;
-} zinject_record_v15_t;
 
-typedef struct zfs_cmd_v15 {
-	char		zc_name[MAXPATHLEN];
-	char		zc_value[MAXPATHLEN];
-	char		zc_string[MAXNAMELEN];
-	uint64_t	zc_guid;
-	uint64_t	zc_nvlist_conf;		/* really (char *) */
-	uint64_t	zc_nvlist_conf_size;
-	uint64_t	zc_nvlist_src;		/* really (char *) */
-	uint64_t	zc_nvlist_src_size;
-	uint64_t	zc_nvlist_dst;		/* really (char *) */
-	uint64_t	zc_nvlist_dst_size;
-	uint64_t	zc_cookie;
-	uint64_t	zc_objset_type;
-	uint64_t	zc_perm_action;
-	uint64_t 	zc_history;		/* really (char *) */
-	uint64_t 	zc_history_len;
-	uint64_t	zc_history_offset;
-	uint64_t	zc_obj;
-	zfs_share_t	zc_share;
-	uint64_t	zc_jailid;
-	dmu_objset_stats_t zc_objset_stats;
-	struct drr_begin zc_begin_record;
-	zinject_record_v15_t zc_inject_record;
-} zfs_cmd_v15_t;
+#define	LEGACY_MAXPATHLEN 1024
+#define	LEGACY_MAXNAMELEN 256
 
-typedef struct zinject_record_v28 {
-	uint64_t	zi_objset;
-	uint64_t	zi_object;
-	uint64_t	zi_start;
-	uint64_t	zi_end;
-	uint64_t	zi_guid;
-	uint32_t	zi_level;
-	uint32_t	zi_error;
-	uint64_t	zi_type;
-	uint32_t	zi_freq;
-	uint32_t	zi_failfast;
-	char		zi_func[MAXNAMELEN];
-	uint32_t	zi_iotype;
-	int32_t		zi_duration;
-	uint64_t	zi_timer;
-} zinject_record_v28_t;
-
-typedef struct zfs_cmd_v28 {
-	char		zc_name[MAXPATHLEN];
-	char		zc_value[MAXPATHLEN * 2];
-	char		zc_string[MAXNAMELEN];
-	char		zc_top_ds[MAXPATHLEN];
-	uint64_t	zc_guid;
-	uint64_t	zc_nvlist_conf;		/* really (char *) */
-	uint64_t	zc_nvlist_conf_size;
-	uint64_t	zc_nvlist_src;		/* really (char *) */
-	uint64_t	zc_nvlist_src_size;
-	uint64_t	zc_nvlist_dst;		/* really (char *) */
-	uint64_t	zc_nvlist_dst_size;
-	uint64_t	zc_cookie;
-	uint64_t	zc_objset_type;
-	uint64_t	zc_perm_action;
-	uint64_t 	zc_history;		/* really (char *) */
-	uint64_t 	zc_history_len;
-	uint64_t	zc_history_offset;
-	uint64_t	zc_obj;
-	uint64_t	zc_iflags;		/* internal to zfs(7fs) */
-	zfs_share_t	zc_share;
-	uint64_t	zc_jailid;
-	dmu_objset_stats_t zc_objset_stats;
-	struct drr_begin zc_begin_record;
-	zinject_record_v28_t zc_inject_record;
-	boolean_t	zc_defer_destroy;
-	boolean_t	zc_temphold;
-	uint64_t	zc_action_handle;
-	int		zc_cleanup_fd;
-	uint8_t		zc_simple;
-	uint8_t		zc_pad[3];		/* alignment */
-	uint64_t	zc_sendobj;
-	uint64_t	zc_fromobj;
-	uint64_t	zc_createtxg;
-	zfs_stat_t	zc_stat;
-} zfs_cmd_v28_t;
-
-typedef struct zinject_record_deadman {
-	uint64_t	zi_objset;
-	uint64_t	zi_object;
-	uint64_t	zi_start;
-	uint64_t	zi_end;
-	uint64_t	zi_guid;
-	uint32_t	zi_level;
-	uint32_t	zi_error;
-	uint64_t	zi_type;
-	uint32_t	zi_freq;
-	uint32_t	zi_failfast;
-	char		zi_func[MAXNAMELEN];
-	uint32_t	zi_iotype;
-	int32_t		zi_duration;
-	uint64_t	zi_timer;
-	uint32_t	zi_cmd;
-	uint32_t	zi_pad;
-} zinject_record_deadman_t;
-
-typedef struct zfs_cmd_deadman {
-	char		zc_name[MAXPATHLEN];
-	char		zc_value[MAXPATHLEN * 2];
-	char		zc_string[MAXNAMELEN];
-	char		zc_top_ds[MAXPATHLEN];
-	uint64_t	zc_guid;
-	uint64_t	zc_nvlist_conf;		/* really (char *) */
-	uint64_t	zc_nvlist_conf_size;
-	uint64_t	zc_nvlist_src;		/* really (char *) */
-	uint64_t	zc_nvlist_src_size;
-	uint64_t	zc_nvlist_dst;		/* really (char *) */
-	uint64_t	zc_nvlist_dst_size;
-	uint64_t	zc_cookie;
-	uint64_t	zc_objset_type;
-	uint64_t	zc_perm_action;
-	uint64_t 	zc_history;		/* really (char *) */
-	uint64_t 	zc_history_len;
-	uint64_t	zc_history_offset;
-	uint64_t	zc_obj;
-	uint64_t	zc_iflags;		/* internal to zfs(7fs) */
-	zfs_share_t	zc_share;
-	uint64_t	zc_jailid;
-	dmu_objset_stats_t zc_objset_stats;
-	struct drr_begin zc_begin_record;
-	/* zc_inject_record doesn't change in libzfs_core */
-	zinject_record_deadman_t zc_inject_record;
-	boolean_t	zc_defer_destroy;
-	boolean_t	zc_temphold;
-	uint64_t	zc_action_handle;
-	int		zc_cleanup_fd;
-	uint8_t		zc_simple;
-	uint8_t		zc_pad[3];		/* alignment */
-	uint64_t	zc_sendobj;
-	uint64_t	zc_fromobj;
-	uint64_t	zc_createtxg;
-	zfs_stat_t	zc_stat;
-} zfs_cmd_deadman_t;
-
-typedef struct zfs_cmd_zcmd {
-	char		zc_name[MAXPATHLEN];	/* name of pool or dataset */
-	uint64_t	zc_nvlist_src;		/* really (char *) */
-	uint64_t	zc_nvlist_src_size;
-	uint64_t	zc_nvlist_dst;		/* really (char *) */
-	uint64_t	zc_nvlist_dst_size;
-	boolean_t	zc_nvlist_dst_filled;	/* put an nvlist in dst? */
-	int		zc_pad2;
-
-	/*
-	 * The following members are for legacy ioctls which haven't been
-	 * converted to the new method.
-	 */
-	uint64_t	zc_history;		/* really (char *) */
-	char		zc_value[MAXPATHLEN * 2];
-	char		zc_string[MAXNAMELEN];
-	uint64_t	zc_guid;
-	uint64_t	zc_nvlist_conf;		/* really (char *) */
-	uint64_t	zc_nvlist_conf_size;
-	uint64_t	zc_cookie;
-	uint64_t	zc_objset_type;
-	uint64_t	zc_perm_action;
-	uint64_t	zc_history_len;
-	uint64_t	zc_history_offset;
-	uint64_t	zc_obj;
-	uint64_t	zc_iflags;		/* internal to zfs(7fs) */
-	zfs_share_t	zc_share;
-	uint64_t	zc_jailid;
-	dmu_objset_stats_t zc_objset_stats;
-	struct drr_begin zc_begin_record;
-	zinject_record_deadman_t zc_inject_record;
-	boolean_t	zc_defer_destroy;
-	boolean_t	zc_temphold;
-	uint64_t	zc_action_handle;
-	int		zc_cleanup_fd;
-	uint8_t		zc_simple;
-	uint8_t		zc_pad[3];		/* alignment */
-	uint64_t	zc_sendobj;
-	uint64_t	zc_fromobj;
-	uint64_t	zc_createtxg;
-	zfs_stat_t	zc_stat;
-} zfs_cmd_zcmd_t;
-
-typedef struct zfs_cmd_edbp {
-	char		zc_name[MAXPATHLEN];	/* name of pool or dataset */
-	uint64_t	zc_nvlist_src;		/* really (char *) */
-	uint64_t	zc_nvlist_src_size;
-	uint64_t	zc_nvlist_dst;		/* really (char *) */
-	uint64_t	zc_nvlist_dst_size;
-	boolean_t	zc_nvlist_dst_filled;	/* put an nvlist in dst? */
-	int		zc_pad2;
-
-	/*
-	 * The following members are for legacy ioctls which haven't been
-	 * converted to the new method.
-	 */
-	uint64_t	zc_history;		/* really (char *) */
-	char		zc_value[MAXPATHLEN * 2];
-	char		zc_string[MAXNAMELEN];
-	uint64_t	zc_guid;
-	uint64_t	zc_nvlist_conf;		/* really (char *) */
-	uint64_t	zc_nvlist_conf_size;
-	uint64_t	zc_cookie;
-	uint64_t	zc_objset_type;
-	uint64_t	zc_perm_action;
-	uint64_t	zc_history_len;
-	uint64_t	zc_history_offset;
-	uint64_t	zc_obj;
-	uint64_t	zc_iflags;		/* internal to zfs(7fs) */
-	zfs_share_t	zc_share;
-	uint64_t	zc_jailid;
-	dmu_objset_stats_t zc_objset_stats;
-	struct drr_begin zc_begin_record;
-	zinject_record_deadman_t zc_inject_record;
-	uint32_t	zc_defer_destroy;
-	uint32_t	zc_flags;
-	uint64_t	zc_action_handle;
-	int		zc_cleanup_fd;
-	uint8_t		zc_simple;
-	uint8_t		zc_pad[3];		/* alignment */
-	uint64_t	zc_sendobj;
-	uint64_t	zc_fromobj;
-	uint64_t	zc_createtxg;
-	zfs_stat_t	zc_stat;
-} zfs_cmd_edbp_t;
-
-typedef struct zfs_cmd_resume {
-	char		zc_name[MAXPATHLEN];	/* name of pool or dataset */
-	uint64_t	zc_nvlist_src;		/* really (char *) */
-	uint64_t	zc_nvlist_src_size;
-	uint64_t	zc_nvlist_dst;		/* really (char *) */
-	uint64_t	zc_nvlist_dst_size;
-	boolean_t	zc_nvlist_dst_filled;	/* put an nvlist in dst? */
-	int		zc_pad2;
-
-	/*
-	 * The following members are for legacy ioctls which haven't been
-	 * converted to the new method.
-	 */
-	uint64_t	zc_history;		/* really (char *) */
-	char		zc_value[MAXPATHLEN * 2];
-	char		zc_string[MAXNAMELEN];
-	uint64_t	zc_guid;
-	uint64_t	zc_nvlist_conf;		/* really (char *) */
-	uint64_t	zc_nvlist_conf_size;
-	uint64_t	zc_cookie;
-	uint64_t	zc_objset_type;
-	uint64_t	zc_perm_action;
-	uint64_t	zc_history_len;
-	uint64_t	zc_history_offset;
-	uint64_t	zc_obj;
-	uint64_t	zc_iflags;		/* internal to zfs(7fs) */
-	zfs_share_t	zc_share;
-	uint64_t	zc_jailid;
-	dmu_objset_stats_t zc_objset_stats;
-	dmu_replay_record_t zc_begin_record;
-	zinject_record_deadman_t zc_inject_record;
-	uint32_t	zc_defer_destroy;
-	uint32_t	zc_flags;
-	uint64_t	zc_action_handle;
-	int		zc_cleanup_fd;
-	uint8_t		zc_simple;
-	boolean_t	zc_resumable;
-	uint64_t	zc_sendobj;
-	uint64_t	zc_fromobj;
-	uint64_t	zc_createtxg;
-	zfs_stat_t	zc_stat;
-} zfs_cmd_resume_t;
-
-typedef struct zfs_cmd_inlanes {
-	char		zc_name[MAXPATHLEN];	/* name of pool or dataset */
-	uint64_t	zc_nvlist_src;		/* really (char *) */
-	uint64_t	zc_nvlist_src_size;
-	uint64_t	zc_nvlist_dst;		/* really (char *) */
-	uint64_t	zc_nvlist_dst_size;
-	boolean_t	zc_nvlist_dst_filled;	/* put an nvlist in dst? */
-	int		zc_pad2;
-
-	/*
-	 * The following members are for legacy ioctls which haven't been
-	 * converted to the new method.
-	 */
-	uint64_t	zc_history;		/* really (char *) */
-	char		zc_value[MAXPATHLEN * 2];
-	char		zc_string[MAXNAMELEN];
-	uint64_t	zc_guid;
-	uint64_t	zc_nvlist_conf;		/* really (char *) */
-	uint64_t	zc_nvlist_conf_size;
-	uint64_t	zc_cookie;
-	uint64_t	zc_objset_type;
-	uint64_t	zc_perm_action;
-	uint64_t	zc_history_len;
-	uint64_t	zc_history_offset;
-	uint64_t	zc_obj;
-	uint64_t	zc_iflags;		/* internal to zfs(7fs) */
-	zfs_share_t	zc_share;
-	uint64_t	zc_jailid;
-	dmu_objset_stats_t zc_objset_stats;
-	dmu_replay_record_t zc_begin_record;
-	zinject_record_t zc_inject_record;
-	uint32_t	zc_defer_destroy;
-	uint32_t	zc_flags;
-	uint64_t	zc_action_handle;
-	int		zc_cleanup_fd;
-	uint8_t		zc_simple;
-	boolean_t	zc_resumable;
-	uint64_t	zc_sendobj;
-	uint64_t	zc_fromobj;
-	uint64_t	zc_createtxg;
-	zfs_stat_t	zc_stat;
-} zfs_cmd_inlanes_t;
-
-#ifdef _KERNEL
 /*
  * Note: this struct must have the same layout in 32-bit and 64-bit, so
  * that 32-bit processes (like /sbin/zfs) can pass it to the 64-bit
@@ -409,7 +92,7 @@ typedef struct zfs_cmd_inlanes {
  * is automatically added on 64-bit (but not on 32-bit).
  */
 typedef struct zfs_cmd_legacy {
-	char		zc_name[MAXPATHLEN];	/* name of pool or dataset */
+	char		zc_name[LEGACY_MAXPATHLEN];	/* pool|dataset name */
 	uint64_t	zc_nvlist_src;		/* really (char *) */
 	uint64_t	zc_nvlist_src_size;
 	uint64_t	zc_nvlist_dst;		/* really (char *) */
@@ -422,8 +105,8 @@ typedef struct zfs_cmd_legacy {
 	 * converted to the new method.
 	 */
 	uint64_t	zc_history;		/* really (char *) */
-	char		zc_value[MAXPATHLEN * 2];
-	char		zc_string[MAXNAMELEN];
+	char		zc_value[LEGACY_MAXPATHLEN * 2];
+	char		zc_string[LEGACY_MAXNAMELEN];
 	uint64_t	zc_guid;
 	uint64_t	zc_nvlist_conf;		/* really (char *) */
 	uint64_t	zc_nvlist_conf_size;
@@ -436,10 +119,8 @@ typedef struct zfs_cmd_legacy {
 	uint64_t	zc_iflags;		/* internal to zfs(7fs) */
 	zfs_share_t	zc_share;
 	uint64_t	zc_jailid;
-
 	dmu_objset_stats_t zc_objset_stats;
-	uint64_t	zc_freebsd_drr_pad;
-	struct drr_begin zc_begin_record;
+	dmu_replay_record_t zc_begin_record;
 	zinject_record_t zc_inject_record;
 	uint32_t	zc_defer_destroy;
 	uint32_t	zc_flags;
@@ -455,209 +136,6 @@ typedef struct zfs_cmd_legacy {
 	zfs_stat_t	zc_stat;
 } zfs_cmd_legacy_t;
 
-unsigned static long zfs_ioctl_bsd12_to_zof[] = {
-	ZFS_IOC_POOL_CREATE,			/* 0x00 */
-	ZFS_IOC_POOL_DESTROY,			/* 0x01 */
-	ZFS_IOC_POOL_IMPORT,			/* 0x02 */
-	ZFS_IOC_POOL_EXPORT,			/* 0x03 */
-	ZFS_IOC_POOL_CONFIGS,			/* 0x04 */
-	ZFS_IOC_POOL_STATS,			/* 0x05 */
-	ZFS_IOC_POOL_TRYIMPORT,			/* 0x06 */
-	ZFS_IOC_POOL_SCAN,			/* 0x07 */
-	ZFS_IOC_POOL_FREEZE,			/* 0x08 */
-	ZFS_IOC_POOL_UPGRADE,			/* 0x09 */
-	ZFS_IOC_POOL_GET_HISTORY,		/* 0x0a */
-	ZFS_IOC_VDEV_ADD,			/* 0x0b */
-	ZFS_IOC_VDEV_REMOVE,			/* 0x0c */
-	ZFS_IOC_VDEV_SET_STATE,			/* 0x0d */
-	ZFS_IOC_VDEV_ATTACH,			/* 0x0e */
-	ZFS_IOC_VDEV_DETACH,			/* 0x0f */
-	ZFS_IOC_VDEV_SETPATH,			/* 0x10 */
-	ZFS_IOC_VDEV_SETFRU,			/* 0x11 */
-	ZFS_IOC_OBJSET_STATS,			/* 0x12 */
-	ZFS_IOC_OBJSET_ZPLPROPS,		/* 0x13 */
-	ZFS_IOC_DATASET_LIST_NEXT,		/* 0x14 */
-	ZFS_IOC_SNAPSHOT_LIST_NEXT,		/* 0x15 */
-	ZFS_IOC_SET_PROP,			/* 0x16 */
-	ZFS_IOC_CREATE,				/* 0x17 */
-	ZFS_IOC_DESTROY,			/* 0x18 */
-	ZFS_IOC_ROLLBACK,			/* 0x19 */
-	ZFS_IOC_RENAME,				/* 0x1a */
-	ZFS_IOC_RECV,				/* 0x1b */
-	ZFS_IOC_SEND,				/* 0x1c */
-	ZFS_IOC_INJECT_FAULT,			/* 0x1d */
-	ZFS_IOC_CLEAR_FAULT,			/* 0x1e */
-	ZFS_IOC_INJECT_LIST_NEXT,		/* 0x1f */
-	ZFS_IOC_ERROR_LOG,			/* 0x20 */
-	ZFS_IOC_CLEAR,				/* 0x21 */
-	ZFS_IOC_PROMOTE,			/* 0x22 */
-	/* start of mismatch */
-	ZFS_IOC_DESTROY_SNAPS,			/* 0x23:0x3b */
-	ZFS_IOC_SNAPSHOT,			/* 0x24:0x23 */
-	ZFS_IOC_DSOBJ_TO_DSNAME,		/* 0x25:0x24 */
-	ZFS_IOC_OBJ_TO_PATH,			/* 0x26:0x25 */
-	ZFS_IOC_POOL_SET_PROPS,			/* 0x27:0x26 */
-	ZFS_IOC_POOL_GET_PROPS,			/* 0x28:0x27 */
-	ZFS_IOC_SET_FSACL,			/* 0x29:0x28 */
-	ZFS_IOC_GET_FSACL,			/* 0x30:0x29 */
-	ZFS_IOC_SHARE,				/* 0x2b:0x2a */
-	ZFS_IOC_INHERIT_PROP,			/* 0x2c:0x2b */
-	ZFS_IOC_SMB_ACL,			/* 0x2d:0x2c */
-	ZFS_IOC_USERSPACE_ONE,			/* 0x2e:0x2d */
-	ZFS_IOC_USERSPACE_MANY,			/* 0x2f:0x2e */
-	ZFS_IOC_USERSPACE_UPGRADE,		/* 0x30:0x2f */
-	ZFS_IOC_HOLD,				/* 0x31:0x30 */
-	ZFS_IOC_RELEASE,			/* 0x32:0x31 */
-	ZFS_IOC_GET_HOLDS,			/* 0x33:0x32 */
-	ZFS_IOC_OBJSET_RECVD_PROPS,		/* 0x34:0x33 */
-	ZFS_IOC_VDEV_SPLIT,			/* 0x35:0x34 */
-	ZFS_IOC_NEXT_OBJ,			/* 0x36:0x35 */
-	ZFS_IOC_DIFF,				/* 0x37:0x36 */
-	ZFS_IOC_TMP_SNAPSHOT,			/* 0x38:0x37 */
-	ZFS_IOC_OBJ_TO_STATS,			/* 0x39:0x38 */
-	ZFS_IOC_JAIL,			/* 0x3a:0xc2 */
-	ZFS_IOC_UNJAIL,			/* 0x3b:0xc3 */
-	ZFS_IOC_POOL_REGUID,			/* 0x3c:0x3c */
-	ZFS_IOC_SPACE_WRITTEN,			/* 0x3d:0x39 */
-	ZFS_IOC_SPACE_SNAPS,			/* 0x3e:0x3a */
-	ZFS_IOC_SEND_PROGRESS,			/* 0x3f:0x3e */
-	ZFS_IOC_POOL_REOPEN,			/* 0x40:0x3d */
-	ZFS_IOC_LOG_HISTORY,			/* 0x41:0x3f */
-	ZFS_IOC_SEND_NEW,			/* 0x42:0x40 */
-	ZFS_IOC_SEND_SPACE,			/* 0x43:0x41 */
-	ZFS_IOC_CLONE,				/* 0x44:0x42 */
-	ZFS_IOC_BOOKMARK,			/* 0x45:0x43 */
-	ZFS_IOC_GET_BOOKMARKS,			/* 0x46:0x44 */
-	ZFS_IOC_DESTROY_BOOKMARKS,		/* 0x47:0x45 */
-	ZFS_IOC_NEXTBOOT,			/* 0x48:0xc1 */
-	ZFS_IOC_CHANNEL_PROGRAM,		/* 0x49:0x48 */
-	ZFS_IOC_REMAP,				/* 0x4a:0x4c */
-	ZFS_IOC_POOL_CHECKPOINT,		/* 0x4b:0x4d */
-	ZFS_IOC_POOL_DISCARD_CHECKPOINT,	/* 0x4c:0x4e */
-	ZFS_IOC_POOL_INITIALIZE,		/* 0x4d:0x4f */
-};
-
-unsigned static long zfs_ioctl_v15_to_v28[] = {
-	0,	/*  0 ZFS_IOC_POOL_CREATE */
-	1,	/*  1 ZFS_IOC_POOL_DESTROY */
-	2,	/*  2 ZFS_IOC_POOL_IMPORT */
-	3,	/*  3 ZFS_IOC_POOL_EXPORT */
-	4,	/*  4 ZFS_IOC_POOL_CONFIGS */
-	5,	/*  5 ZFS_IOC_POOL_STATS */
-	6,	/*  6 ZFS_IOC_POOL_TRYIMPORT */
-	7,	/*  7 ZFS_IOC_POOL_SCRUB */
-	8,	/*  8 ZFS_IOC_POOL_FREEZE */
-	9,	/*  9 ZFS_IOC_POOL_UPGRADE */
-	10,	/* 10 ZFS_IOC_POOL_GET_HISTORY */
-	11,	/* 11 ZFS_IOC_VDEV_ADD */
-	12,	/* 12 ZFS_IOC_VDEV_REMOVE */
-	13,	/* 13 ZFS_IOC_VDEV_SET_STATE */
-	14,	/* 14 ZFS_IOC_VDEV_ATTACH */
-	15,	/* 15 ZFS_IOC_VDEV_DETACH */
-	16,	/* 16 ZFS_IOC_VDEV_SETPATH */
-	18,	/* 17 ZFS_IOC_OBJSET_STATS */
-	19,	/* 18 ZFS_IOC_OBJSET_ZPLPROPS */
-	20, 	/* 19 ZFS_IOC_DATASET_LIST_NEXT */
-	21,	/* 20 ZFS_IOC_SNAPSHOT_LIST_NEXT */
-	22,	/* 21 ZFS_IOC_SET_PROP */
-	ZFS_IOC_COMPAT_PASS,	/* 22 ZFS_IOC_CREATE_MINOR */
-	ZFS_IOC_COMPAT_PASS,	/* 23 ZFS_IOC_REMOVE_MINOR */
-	23,	/* 24 ZFS_IOC_CREATE */
-	24,	/* 25 ZFS_IOC_DESTROY */
-	25,	/* 26 ZFS_IOC_ROLLBACK */
-	26,	/* 27 ZFS_IOC_RENAME */
-	27,	/* 28 ZFS_IOC_RECV */
-	28,	/* 29 ZFS_IOC_SEND */
-	29,	/* 30 ZFS_IOC_INJECT_FAULT */
-	30,	/* 31 ZFS_IOC_CLEAR_FAULT */
-	31,	/* 32 ZFS_IOC_INJECT_LIST_NEXT */
-	32,	/* 33 ZFS_IOC_ERROR_LOG */
-	33,	/* 34 ZFS_IOC_CLEAR */
-	34,	/* 35 ZFS_IOC_PROMOTE */
-	35,	/* 36 ZFS_IOC_DESTROY_SNAPS */
-	36,	/* 37 ZFS_IOC_SNAPSHOT */
-	37,	/* 38 ZFS_IOC_DSOBJ_TO_DSNAME */
-	38,	/* 39 ZFS_IOC_OBJ_TO_PATH */
-	39,	/* 40 ZFS_IOC_POOL_SET_PROPS */
-	40,	/* 41 ZFS_IOC_POOL_GET_PROPS */
-	41,	/* 42 ZFS_IOC_SET_FSACL */
-	42,	/* 43 ZFS_IOC_GET_FSACL */
-	ZFS_IOC_COMPAT_PASS,	/* 44 ZFS_IOC_ISCSI_PERM_CHECK */
-	43,	/* 45 ZFS_IOC_SHARE */
-	44,	/* 46 ZFS_IOC_IHNERIT_PROP */
-	58,	/* 47 ZFS_IOC_JAIL */
-	59,	/* 48 ZFS_IOC_UNJAIL */
-	45,	/* 49 ZFS_IOC_SMB_ACL */
-	46,	/* 50 ZFS_IOC_USERSPACE_ONE */
-	47,	/* 51 ZFS_IOC_USERSPACE_MANY */
-	48,	/* 52 ZFS_IOC_USERSPACE_UPGRADE */
-	17,	/* 53 ZFS_IOC_SETFRU */
-};
-
-#else	/* KERNEL */
-unsigned static long zfs_ioctl_v28_to_v15[] = {
-	0,	/*  0 ZFS_IOC_POOL_CREATE */
-	1,	/*  1 ZFS_IOC_POOL_DESTROY */
-	2,	/*  2 ZFS_IOC_POOL_IMPORT */
-	3,	/*  3 ZFS_IOC_POOL_EXPORT */
-	4,	/*  4 ZFS_IOC_POOL_CONFIGS */
-	5,	/*  5 ZFS_IOC_POOL_STATS */
-	6,	/*  6 ZFS_IOC_POOL_TRYIMPORT */
-	7,	/*  7 ZFS_IOC_POOL_SCAN */
-	8,	/*  8 ZFS_IOC_POOL_FREEZE */
-	9,	/*  9 ZFS_IOC_POOL_UPGRADE */
-	10,	/* 10 ZFS_IOC_POOL_GET_HISTORY */
-	11,	/* 11 ZFS_IOC_VDEV_ADD */
-	12,	/* 12 ZFS_IOC_VDEV_REMOVE */
-	13,	/* 13 ZFS_IOC_VDEV_SET_STATE */
-	14,	/* 14 ZFS_IOC_VDEV_ATTACH */
-	15,	/* 15 ZFS_IOC_VDEV_DETACH */
-	16,	/* 16 ZFS_IOC_VDEV_SETPATH */
-	53,	/* 17 ZFS_IOC_VDEV_SETFRU */
-	17,	/* 18 ZFS_IOC_OBJSET_STATS */
-	18,	/* 19 ZFS_IOC_OBJSET_ZPLPROPS */
-	19, 	/* 20 ZFS_IOC_DATASET_LIST_NEXT */
-	20,	/* 21 ZFS_IOC_SNAPSHOT_LIST_NEXT */
-	21,	/* 22 ZFS_IOC_SET_PROP */
-	24,	/* 23 ZFS_IOC_CREATE */
-	25,	/* 24 ZFS_IOC_DESTROY */
-	26,	/* 25 ZFS_IOC_ROLLBACK */
-	27,	/* 26 ZFS_IOC_RENAME */
-	28,	/* 27 ZFS_IOC_RECV */
-	29,	/* 28 ZFS_IOC_SEND */
-	30,	/* 39 ZFS_IOC_INJECT_FAULT */
-	31,	/* 30 ZFS_IOC_CLEAR_FAULT */
-	32,	/* 31 ZFS_IOC_INJECT_LIST_NEXT */
-	33,	/* 32 ZFS_IOC_ERROR_LOG */
-	34,	/* 33 ZFS_IOC_CLEAR */
-	35,	/* 34 ZFS_IOC_PROMOTE */
-	36,	/* 35 ZFS_IOC_DESTROY_SNAPS */
-	37,	/* 36 ZFS_IOC_SNAPSHOT */
-	38,	/* 37 ZFS_IOC_DSOBJ_TO_DSNAME */
-	39,	/* 38 ZFS_IOC_OBJ_TO_PATH */
-	40,	/* 39 ZFS_IOC_POOL_SET_PROPS */
-	41,	/* 40 ZFS_IOC_POOL_GET_PROPS */
-	42,	/* 41 ZFS_IOC_SET_FSACL */
-	43,	/* 42 ZFS_IOC_GET_FSACL */
-	45,	/* 43 ZFS_IOC_SHARE */
-	46,	/* 44 ZFS_IOC_IHNERIT_PROP */
-	49,	/* 45 ZFS_IOC_SMB_ACL */
-	50,	/* 46 ZFS_IOC_USERSPACE_ONE */
-	51,	/* 47 ZFS_IOC_USERSPACE_MANY */
-	52,	/* 48 ZFS_IOC_USERSPACE_UPGRADE */
-	ZFS_IOC_COMPAT_FAIL,	/* 49 ZFS_IOC_HOLD */
-	ZFS_IOC_COMPAT_FAIL,	/* 50 ZFS_IOC_RELEASE */
-	ZFS_IOC_COMPAT_FAIL,	/* 51 ZFS_IOC_GET_HOLDS */
-	ZFS_IOC_COMPAT_FAIL,	/* 52 ZFS_IOC_OBJSET_RECVD_PROPS */
-	ZFS_IOC_COMPAT_FAIL,	/* 53 ZFS_IOC_VDEV_SPLIT */
-	ZFS_IOC_COMPAT_FAIL,	/* 54 ZFS_IOC_NEXT_OBJ */
-	ZFS_IOC_COMPAT_FAIL,	/* 55 ZFS_IOC_DIFF */
-	ZFS_IOC_COMPAT_FAIL,	/* 56 ZFS_IOC_TMP_SNAPSHOT */
-	ZFS_IOC_COMPAT_FAIL,	/* 57 ZFS_IOC_OBJ_TO_STATS */
-	47,	/* 58 ZFS_IOC_JAIL */
-	48,	/* 59 ZFS_IOC_UNJAIL */
-};
-#endif	/* ! _KERNEL */
 
 #ifdef _KERNEL
 int zfs_ioctl_compat_pre(zfs_cmd_t *, int *, const int);
@@ -667,7 +145,12 @@ nvlist_t *zfs_ioctl_compat_innvl(zfs_cmd_t *, nvlist_t *, const int,
 nvlist_t *zfs_ioctl_compat_outnvl(zfs_cmd_t *, nvlist_t *, const int,
     const int);
 #endif	/* _KERNEL */
+int zfs_ioctl_legacy_to_ozfs(int request);
+int zfs_ioctl_ozfs_to_legacy(int request);
+void zfs_cmd_legacy_to_ozfs(zfs_cmd_legacy_t *src, zfs_cmd_t *dst);
 void zfs_cmd_compat_get(zfs_cmd_t *, caddr_t, const int);
+void zfs_cmd_ozfs_to_legacy(zfs_cmd_t *src, zfs_cmd_legacy_t *dst);
+
 void zfs_cmd_compat_put(zfs_cmd_t *, caddr_t, const int, const int);
 
 #ifdef	__cplusplus
diff --git a/lib/libzutil/Makefile.am b/lib/libzutil/Makefile.am
index bbd69b780f..cd03ba19d5 100644
--- a/lib/libzutil/Makefile.am
+++ b/lib/libzutil/Makefile.am
@@ -23,10 +23,16 @@ USER_C += \
 endif
 
 if BUILD_FREEBSD
+DEFAULT_INCLUDES += -I$(top_srcdir)/include/os/freebsd/zfs
+
 USER_C += \
 	os/freebsd/zutil_device_path_os.c \
 	os/freebsd/zutil_import_os.c \
 	os/freebsd/zutil_compat.c
+
+VPATH += $(top_srcdir)/module/os/freebsd/zfs
+
+nodist_libzutil_la_SOURCES = zfs_ioctl_compat.c
 endif
 
 libzutil_la_SOURCES = $(USER_C)
diff --git a/lib/libzutil/os/freebsd/zutil_compat.c b/lib/libzutil/os/freebsd/zutil_compat.c
index 5b0fe85485..3e70fef1ec 100644
--- a/lib/libzutil/os/freebsd/zutil_compat.c
+++ b/lib/libzutil/os/freebsd/zutil_compat.c
@@ -20,15 +20,35 @@
  */
 #include <sys/types.h>
 #include <sys/param.h>
+#include <sys/sysctl.h>
 #include <sys/zfs_ioctl.h>
 #include <os/freebsd/zfs/sys/zfs_ioctl_compat.h>
 #include <libzutil.h>
 
+#include <err.h>
+
+int zfs_ioctl_version = ZFS_IOCVER_UNDEF;
+
+/*
+ * Get zfs_ioctl_version
+ */
+static int
+get_zfs_ioctl_version(void)
+{
+	size_t ver_size;
+	int ver = ZFS_IOCVER_NONE;
+
+	ver_size = sizeof (ver);
+	sysctlbyname("vfs.zfs.version.ioctl", &ver, &ver_size, NULL, 0);
+
+	return (ver);
+}
+
 static int
 zcmd_ioctl_compat(int fd, int request, zfs_cmd_t *zc, const int cflag)
 {
-	int ret;
-	void *zc_c;
+	int newrequest, ret;
+	void *zc_c = NULL;
 	unsigned long ncmd;
 	zfs_iocparm_t zp;
 
@@ -37,17 +57,32 @@ zcmd_ioctl_compat(int fd, int request, zfs_cmd_t *zc, const int cflag)
 		ncmd = _IOWR('Z', request, zfs_iocparm_t);
 		zp.zfs_cmd = (uint64_t)zc;
 		zp.zfs_cmd_size = sizeof (zfs_cmd_t);
-		zp.zfs_ioctl_version = ZFS_IOCVER_ZOF;
-		return (ioctl(fd, ncmd, &zp));
+		zp.zfs_ioctl_version = ZFS_IOCVER_OZFS;
+		break;
+	case ZFS_CMD_COMPAT_LEGACY:
+		newrequest = zfs_ioctl_ozfs_to_legacy(request);
+		ncmd = _IOWR('Z', newrequest, zfs_iocparm_t);
+		zc_c = malloc(sizeof (zfs_cmd_legacy_t));
+		zfs_cmd_ozfs_to_legacy(zc, zc_c);
+		zp.zfs_cmd = (uint64_t)zc_c;
+		zp.zfs_cmd_size = sizeof (zfs_cmd_legacy_t);
+		zp.zfs_ioctl_version = ZFS_IOCVER_LEGACY;
+		break;
 	default:
 		abort();
 		return (EINVAL);
 	}
 
-	ret = ioctl(fd, ncmd, zc_c);
-	zfs_cmd_compat_get(zc, (caddr_t)zc_c, cflag);
-	free(zc_c);
-
+	ret = ioctl(fd, ncmd, &zp);
+	if (ret) {
+		if (zc_c)
+			free(zc_c);
+		return (ret);
+	}
+	if (zc_c) {
+		zfs_cmd_legacy_to_ozfs(zc_c, zc);
+		free(zc_c);
+	}
 	return (ret);
 }
 
@@ -62,6 +97,21 @@ zfs_ioctl_fd(int fd, unsigned long request, zfs_cmd_t *zc)
 	size_t oldsize;
 	int ret, cflag = ZFS_CMD_COMPAT_NONE;
 
+	if (zfs_ioctl_version == ZFS_IOCVER_UNDEF)
+		zfs_ioctl_version = get_zfs_ioctl_version();
+
+	switch (zfs_ioctl_version) {
+		case ZFS_IOCVER_LEGACY:
+			cflag = ZFS_CMD_COMPAT_LEGACY;
+			break;
+		case ZFS_IOCVER_OZFS:
+			cflag = ZFS_CMD_COMPAT_NONE;
+			break;
+		default:
+			errx(1, "unrecognized zfs ioctl version %d",
+			    zfs_ioctl_version);
+	}
+
 	oldsize = zc->zc_nvlist_dst_size;
 	ret = zcmd_ioctl_compat(fd, request, zc, cflag);
 
diff --git a/module/Makefile.bsd b/module/Makefile.bsd
index 1ac9d00e7b..6fd00f4746 100644
--- a/module/Makefile.bsd
+++ b/module/Makefile.bsd
@@ -141,6 +141,7 @@ SRCS+=	abd_os.c \
 	zfs_acl.c \
 	zfs_ctldir.c \
 	zfs_dir.c \
+	zfs_ioctl_compat.c \
 	zfs_ioctl_os.c \
 	zfs_log.c \
 	zfs_replay.c \
diff --git a/module/os/freebsd/zfs/kmod_core.c b/module/os/freebsd/zfs/kmod_core.c
index 4a4f55ea6d..e6eceacb22 100644
--- a/module/os/freebsd/zfs/kmod_core.c
+++ b/module/os/freebsd/zfs/kmod_core.c
@@ -99,7 +99,7 @@ SYSCTL_DECL(_vfs_zfs);
 SYSCTL_DECL(_vfs_zfs_vdev);
 
 
-static int zfs_version_ioctl = ZFS_IOCVER_ZOF;
+static int zfs_version_ioctl = ZFS_IOCVER_OZFS;
 SYSCTL_DECL(_vfs_zfs_version);
 SYSCTL_INT(_vfs_zfs_version, OID_AUTO, ioctl, CTLFLAG_RD, &zfs_version_ioctl,
     0, "ZFS_IOCTL_VERSION");
@@ -121,37 +121,13 @@ extern zfsdev_state_t *zfsdev_state_list;
 
 #define	ZFS_MIN_KSTACK_PAGES 4
 
-static void
-zfs_cmd_bsd12_to_zof(zfs_cmd_legacy_t *src, zfs_cmd_t *dst)
-{
-	memcpy(dst, src, offsetof(zfs_cmd_t, zc_objset_stats));
-	*&dst->zc_objset_stats = *&src->zc_objset_stats;
-	memcpy(&dst->zc_begin_record, &src->zc_begin_record,
-	    offsetof(zfs_cmd_t, zc_sendobj) -
-	    offsetof(zfs_cmd_t, zc_begin_record));
-	memcpy(&dst->zc_sendobj, &src->zc_sendobj,
-	    sizeof (zfs_cmd_t) - 8 - offsetof(zfs_cmd_t, zc_sendobj));
-	dst->zc_zoneid = src->zc_jailid;
-}
-
-static void
-zfs_cmd_zof_to_bsd12(zfs_cmd_t *src, zfs_cmd_legacy_t *dst)
-{
-	memcpy(dst, src, offsetof(zfs_cmd_t, zc_objset_stats));
-	*&dst->zc_objset_stats = *&src->zc_objset_stats;
-	memcpy(&dst->zc_begin_record, &src->zc_begin_record,
-	    offsetof(zfs_cmd_t, zc_sendobj) -
-	    offsetof(zfs_cmd_t, zc_begin_record));
-	memcpy(&dst->zc_sendobj, &src->zc_sendobj,
-	    sizeof (zfs_cmd_t) - 8 - offsetof(zfs_cmd_t, zc_sendobj));
-	dst->zc_jailid = src->zc_zoneid;
-}
 
 static int
 zfsdev_ioctl(struct cdev *dev, ulong_t zcmd, caddr_t arg, int flag,
     struct thread *td)
 {
-	uint_t len, vecnum;
+	uint_t len;
+	int vecnum;
 	zfs_iocparm_t *zp;
 	zfs_cmd_t *zc;
 	zfs_cmd_legacy_t *zcl;
@@ -175,25 +151,25 @@ zfsdev_ioctl(struct cdev *dev, ulong_t zcmd, caddr_t arg, int flag,
 	/*
 	 * Remap ioctl code for legacy user binaries
 	 */
-	if (zp->zfs_ioctl_version == ZFS_IOCVER_FREEBSD) {
-		if (vecnum >= sizeof (zfs_ioctl_bsd12_to_zof)/sizeof (long)) {
+	if (zp->zfs_ioctl_version == ZFS_IOCVER_LEGACY) {
+		vecnum = zfs_ioctl_legacy_to_ozfs(vecnum);
+		if (vecnum < 0) {
 			kmem_free(zc, sizeof (zfs_cmd_t));
 			return (ENOTSUP);
 		}
 		zcl = kmem_zalloc(sizeof (zfs_cmd_legacy_t), KM_SLEEP);
-		vecnum = zfs_ioctl_bsd12_to_zof[vecnum];
 		if (copyin(uaddr, zcl, sizeof (zfs_cmd_legacy_t))) {
 			error = SET_ERROR(EFAULT);
 			goto out;
 		}
-		zfs_cmd_bsd12_to_zof(zcl, zc);
+		zfs_cmd_legacy_to_ozfs(zcl, zc);
 	} else if (copyin(uaddr, zc, sizeof (zfs_cmd_t))) {
 		error = SET_ERROR(EFAULT);
 		goto out;
 	}
 	error = zfsdev_ioctl_common(vecnum, zc, 0);
 	if (zcl) {
-		zfs_cmd_zof_to_bsd12(zc, zcl);
+		zfs_cmd_ozfs_to_legacy(zc, zcl);
 		rc = copyout(zcl, uaddr, sizeof (*zcl));
 	} else {
 		rc = copyout(zc, uaddr, sizeof (*zc));
diff --git a/module/os/freebsd/zfs/zfs_ioctl_compat.c b/module/os/freebsd/zfs/zfs_ioctl_compat.c
new file mode 100644
index 0000000000..18ab587563
--- /dev/null
+++ b/module/os/freebsd/zfs/zfs_ioctl_compat.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2020 iXsystems, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/errno.h>
+#include <sys/uio.h>
+#include <sys/cmn_err.h>
+#include <sys/stat.h>
+#include <sys/zfs_ioctl_compat.h>
+
+enum zfs_ioc_legacy {
+	ZFS_IOC_LEGACY_NONE =	-1,
+	ZFS_IOC_LEGACY_FIRST =	0,
+	ZFS_LEGACY_IOC = ZFS_IOC_LEGACY_FIRST,
+	ZFS_IOC_LEGACY_POOL_CREATE = ZFS_IOC_LEGACY_FIRST,
+	ZFS_IOC_LEGACY_POOL_DESTROY,
+	ZFS_IOC_LEGACY_POOL_IMPORT,
+	ZFS_IOC_LEGACY_POOL_EXPORT,
+	ZFS_IOC_LEGACY_POOL_CONFIGS,
+	ZFS_IOC_LEGACY_POOL_STATS,
+	ZFS_IOC_LEGACY_POOL_TRYIMPORT,
+	ZFS_IOC_LEGACY_POOL_SCAN,
+	ZFS_IOC_LEGACY_POOL_FREEZE,
+	ZFS_IOC_LEGACY_POOL_UPGRADE,
+	ZFS_IOC_LEGACY_POOL_GET_HISTORY,
+	ZFS_IOC_LEGACY_VDEV_ADD,
+	ZFS_IOC_LEGACY_VDEV_REMOVE,
+	ZFS_IOC_LEGACY_VDEV_SET_STATE,
+	ZFS_IOC_LEGACY_VDEV_ATTACH,
+	ZFS_IOC_LEGACY_VDEV_DETACH,
+	ZFS_IOC_LEGACY_VDEV_SETPATH,
+	ZFS_IOC_LEGACY_VDEV_SETFRU,
+	ZFS_IOC_LEGACY_OBJSET_STATS,
+	ZFS_IOC_LEGACY_OBJSET_ZPLPROPS,
+	ZFS_IOC_LEGACY_DATASET_LIST_NEXT,
+	ZFS_IOC_LEGACY_SNAPSHOT_LIST_NEXT,
+	ZFS_IOC_LEGACY_SET_PROP,
+	ZFS_IOC_LEGACY_CREATE,
+	ZFS_IOC_LEGACY_DESTROY,
+	ZFS_IOC_LEGACY_ROLLBACK,
+	ZFS_IOC_LEGACY_RENAME,
+	ZFS_IOC_LEGACY_RECV,
+	ZFS_IOC_LEGACY_SEND,
+	ZFS_IOC_LEGACY_INJECT_FAULT,
+	ZFS_IOC_LEGACY_CLEAR_FAULT,
+	ZFS_IOC_LEGACY_INJECT_LIST_NEXT,
+	ZFS_IOC_LEGACY_ERROR_LOG,
+	ZFS_IOC_LEGACY_CLEAR,
+	ZFS_IOC_LEGACY_PROMOTE,
+	ZFS_IOC_LEGACY_DESTROY_SNAPS,
+	ZFS_IOC_LEGACY_SNAPSHOT,
+	ZFS_IOC_LEGACY_DSOBJ_TO_DSNAME,
+	ZFS_IOC_LEGACY_OBJ_TO_PATH,
+	ZFS_IOC_LEGACY_POOL_SET_PROPS,
+	ZFS_IOC_LEGACY_POOL_GET_PROPS,
+	ZFS_IOC_LEGACY_SET_FSACL,
+	ZFS_IOC_LEGACY_GET_FSACL,
+	ZFS_IOC_LEGACY_SHARE,
+	ZFS_IOC_LEGACY_INHERIT_PROP,
+	ZFS_IOC_LEGACY_SMB_ACL,
+	ZFS_IOC_LEGACY_USERSPACE_ONE,
+	ZFS_IOC_LEGACY_USERSPACE_MANY,
+	ZFS_IOC_LEGACY_USERSPACE_UPGRADE,
+	ZFS_IOC_LEGACY_HOLD,
+	ZFS_IOC_LEGACY_RELEASE,
+	ZFS_IOC_LEGACY_GET_HOLDS,
+	ZFS_IOC_LEGACY_OBJSET_RECVD_PROPS,
+	ZFS_IOC_LEGACY_VDEV_SPLIT,
+	ZFS_IOC_LEGACY_NEXT_OBJ,
+	ZFS_IOC_LEGACY_DIFF,
+	ZFS_IOC_LEGACY_TMP_SNAPSHOT,
+	ZFS_IOC_LEGACY_OBJ_TO_STATS,
+	ZFS_IOC_LEGACY_JAIL,
+	ZFS_IOC_LEGACY_UNJAIL,
+	ZFS_IOC_LEGACY_POOL_REGUID,
+	ZFS_IOC_LEGACY_SPACE_WRITTEN,
+	ZFS_IOC_LEGACY_SPACE_SNAPS,
+	ZFS_IOC_LEGACY_SEND_PROGRESS,
+	ZFS_IOC_LEGACY_POOL_REOPEN,
+	ZFS_IOC_LEGACY_LOG_HISTORY,
+	ZFS_IOC_LEGACY_SEND_NEW,
+	ZFS_IOC_LEGACY_SEND_SPACE,
+	ZFS_IOC_LEGACY_CLONE,
+	ZFS_IOC_LEGACY_BOOKMARK,
+	ZFS_IOC_LEGACY_GET_BOOKMARKS,
+	ZFS_IOC_LEGACY_DESTROY_BOOKMARKS,
+	ZFS_IOC_LEGACY_NEXTBOOT,
+	ZFS_IOC_LEGACY_CHANNEL_PROGRAM,
+	ZFS_IOC_LEGACY_REMAP,
+	ZFS_IOC_LEGACY_POOL_CHECKPOINT,
+	ZFS_IOC_LEGACY_POOL_DISCARD_CHECKPOINT,
+	ZFS_IOC_LEGACY_POOL_INITIALIZE,
+	ZFS_IOC_LEGACY_POOL_SYNC,
+	ZFS_IOC_LEGACY_LAST
+};
+
+unsigned static long zfs_ioctl_legacy_to_ozfs_[] = {
+	ZFS_IOC_POOL_CREATE,			/* 0x00 */
+	ZFS_IOC_POOL_DESTROY,			/* 0x01 */
+	ZFS_IOC_POOL_IMPORT,			/* 0x02 */
+	ZFS_IOC_POOL_EXPORT,			/* 0x03 */
+	ZFS_IOC_POOL_CONFIGS,			/* 0x04 */
+	ZFS_IOC_POOL_STATS,			/* 0x05 */
+	ZFS_IOC_POOL_TRYIMPORT,			/* 0x06 */
+	ZFS_IOC_POOL_SCAN,			/* 0x07 */
+	ZFS_IOC_POOL_FREEZE,			/* 0x08 */
+	ZFS_IOC_POOL_UPGRADE,			/* 0x09 */
+	ZFS_IOC_POOL_GET_HISTORY,		/* 0x0a */
+	ZFS_IOC_VDEV_ADD,			/* 0x0b */
+	ZFS_IOC_VDEV_REMOVE,			/* 0x0c */
+	ZFS_IOC_VDEV_SET_STATE,			/* 0x0d */
+	ZFS_IOC_VDEV_ATTACH,			/* 0x0e */
+	ZFS_IOC_VDEV_DETACH,			/* 0x0f */
+	ZFS_IOC_VDEV_SETPATH,			/* 0x10 */
+	ZFS_IOC_VDEV_SETFRU,			/* 0x11 */
+	ZFS_IOC_OBJSET_STATS,			/* 0x12 */
+	ZFS_IOC_OBJSET_ZPLPROPS,		/* 0x13 */
+	ZFS_IOC_DATASET_LIST_NEXT,		/* 0x14 */
+	ZFS_IOC_SNAPSHOT_LIST_NEXT,		/* 0x15 */
+	ZFS_IOC_SET_PROP,			/* 0x16 */
+	ZFS_IOC_CREATE,				/* 0x17 */
+	ZFS_IOC_DESTROY,			/* 0x18 */
+	ZFS_IOC_ROLLBACK,			/* 0x19 */
+	ZFS_IOC_RENAME,				/* 0x1a */
+	ZFS_IOC_RECV,				/* 0x1b */
+	ZFS_IOC_SEND,				/* 0x1c */
+	ZFS_IOC_INJECT_FAULT,			/* 0x1d */
+	ZFS_IOC_CLEAR_FAULT,			/* 0x1e */
+	ZFS_IOC_INJECT_LIST_NEXT,		/* 0x1f */
+	ZFS_IOC_ERROR_LOG,			/* 0x20 */
+	ZFS_IOC_CLEAR,				/* 0x21 */
+	ZFS_IOC_PROMOTE,			/* 0x22 */
+	/* start of mismatch */
+
+	ZFS_IOC_DESTROY_SNAPS,			/* 0x23:0x3b */
+	ZFS_IOC_SNAPSHOT,			/* 0x24:0x23 */
+	ZFS_IOC_DSOBJ_TO_DSNAME,		/* 0x25:0x24 */
+	ZFS_IOC_OBJ_TO_PATH,			/* 0x26:0x25 */
+	ZFS_IOC_POOL_SET_PROPS,			/* 0x27:0x26 */
+	ZFS_IOC_POOL_GET_PROPS,			/* 0x28:0x27 */
+	ZFS_IOC_SET_FSACL,			/* 0x29:0x28 */
+	ZFS_IOC_GET_FSACL,			/* 0x30:0x29 */
+	ZFS_IOC_SHARE,				/* 0x2b:0x2a */
+	ZFS_IOC_INHERIT_PROP,			/* 0x2c:0x2b */
+	ZFS_IOC_SMB_ACL,			/* 0x2d:0x2c */
+	ZFS_IOC_USERSPACE_ONE,			/* 0x2e:0x2d */
+	ZFS_IOC_USERSPACE_MANY,			/* 0x2f:0x2e */
+	ZFS_IOC_USERSPACE_UPGRADE,		/* 0x30:0x2f */
+	ZFS_IOC_HOLD,				/* 0x31:0x30 */
+	ZFS_IOC_RELEASE,			/* 0x32:0x31 */
+	ZFS_IOC_GET_HOLDS,			/* 0x33:0x32 */
+	ZFS_IOC_OBJSET_RECVD_PROPS,		/* 0x34:0x33 */
+	ZFS_IOC_VDEV_SPLIT,			/* 0x35:0x34 */
+	ZFS_IOC_NEXT_OBJ,			/* 0x36:0x35 */
+	ZFS_IOC_DIFF,				/* 0x37:0x36 */
+	ZFS_IOC_TMP_SNAPSHOT,			/* 0x38:0x37 */
+	ZFS_IOC_OBJ_TO_STATS,			/* 0x39:0x38 */
+	ZFS_IOC_JAIL,			/* 0x3a:0xc2 */
+	ZFS_IOC_UNJAIL,			/* 0x3b:0xc3 */
+	ZFS_IOC_POOL_REGUID,			/* 0x3c:0x3c */
+	ZFS_IOC_SPACE_WRITTEN,			/* 0x3d:0x39 */
+	ZFS_IOC_SPACE_SNAPS,			/* 0x3e:0x3a */
+	ZFS_IOC_SEND_PROGRESS,			/* 0x3f:0x3e */
+	ZFS_IOC_POOL_REOPEN,			/* 0x40:0x3d */
+	ZFS_IOC_LOG_HISTORY,			/* 0x41:0x3f */
+	ZFS_IOC_SEND_NEW,			/* 0x42:0x40 */
+	ZFS_IOC_SEND_SPACE,			/* 0x43:0x41 */
+	ZFS_IOC_CLONE,				/* 0x44:0x42 */
+	ZFS_IOC_BOOKMARK,			/* 0x45:0x43 */
+	ZFS_IOC_GET_BOOKMARKS,			/* 0x46:0x44 */
+	ZFS_IOC_DESTROY_BOOKMARKS,		/* 0x47:0x45 */
+	ZFS_IOC_NEXTBOOT,			/* 0x48:0xc1 */
+	ZFS_IOC_CHANNEL_PROGRAM,		/* 0x49:0x48 */
+	ZFS_IOC_REMAP,				/* 0x4a:0x4c */
+	ZFS_IOC_POOL_CHECKPOINT,		/* 0x4b:0x4d */
+	ZFS_IOC_POOL_DISCARD_CHECKPOINT,	/* 0x4c:0x4e */
+	ZFS_IOC_POOL_INITIALIZE,		/* 0x4d:0x4f */
+};
+
+unsigned static long zfs_ioctl_ozfs_to_legacy_common_[] = {
+	ZFS_IOC_POOL_CREATE,			/* 0x00 */
+	ZFS_IOC_POOL_DESTROY,			/* 0x01 */
+	ZFS_IOC_POOL_IMPORT,			/* 0x02 */
+	ZFS_IOC_POOL_EXPORT,			/* 0x03 */
+	ZFS_IOC_POOL_CONFIGS,			/* 0x04 */
+	ZFS_IOC_POOL_STATS,			/* 0x05 */
+	ZFS_IOC_POOL_TRYIMPORT,			/* 0x06 */
+	ZFS_IOC_POOL_SCAN,			/* 0x07 */
+	ZFS_IOC_POOL_FREEZE,			/* 0x08 */
+	ZFS_IOC_POOL_UPGRADE,			/* 0x09 */
+	ZFS_IOC_POOL_GET_HISTORY,		/* 0x0a */
+	ZFS_IOC_VDEV_ADD,			/* 0x0b */
+	ZFS_IOC_VDEV_REMOVE,			/* 0x0c */
+	ZFS_IOC_VDEV_SET_STATE,			/* 0x0d */
+	ZFS_IOC_VDEV_ATTACH,			/* 0x0e */
+	ZFS_IOC_VDEV_DETACH,			/* 0x0f */
+	ZFS_IOC_VDEV_SETPATH,			/* 0x10 */
+	ZFS_IOC_VDEV_SETFRU,			/* 0x11 */
+	ZFS_IOC_OBJSET_STATS,			/* 0x12 */
+	ZFS_IOC_OBJSET_ZPLPROPS,		/* 0x13 */
+	ZFS_IOC_DATASET_LIST_NEXT,		/* 0x14 */
+	ZFS_IOC_SNAPSHOT_LIST_NEXT,		/* 0x15 */
+	ZFS_IOC_SET_PROP,			/* 0x16 */
+	ZFS_IOC_CREATE,				/* 0x17 */
+	ZFS_IOC_DESTROY,			/* 0x18 */
+	ZFS_IOC_ROLLBACK,			/* 0x19 */
+	ZFS_IOC_RENAME,				/* 0x1a */
+	ZFS_IOC_RECV,				/* 0x1b */
+	ZFS_IOC_SEND,				/* 0x1c */
+	ZFS_IOC_INJECT_FAULT,			/* 0x1d */
+	ZFS_IOC_CLEAR_FAULT,			/* 0x1e */
+	ZFS_IOC_INJECT_LIST_NEXT,		/* 0x1f */
+	ZFS_IOC_ERROR_LOG,			/* 0x20 */
+	ZFS_IOC_CLEAR,				/* 0x21 */
+	ZFS_IOC_PROMOTE,			/* 0x22 */
+	/* start of mismatch */
+	ZFS_IOC_LEGACY_SNAPSHOT,		/* 0x23 */
+	ZFS_IOC_LEGACY_DSOBJ_TO_DSNAME,		/* 0x24 */
+	ZFS_IOC_LEGACY_OBJ_TO_PATH,		/* 0x25 */
+	ZFS_IOC_LEGACY_POOL_SET_PROPS,		/* 0x26 */
+	ZFS_IOC_LEGACY_POOL_GET_PROPS,		/* 0x27 */
+	ZFS_IOC_LEGACY_SET_FSACL,		/* 0x28 */
+	ZFS_IOC_LEGACY_GET_FSACL,		/* 0x29 */
+	ZFS_IOC_LEGACY_SHARE,			/* 0x2a */
+	ZFS_IOC_LEGACY_INHERIT_PROP,		/* 0x2b */
+	ZFS_IOC_LEGACY_SMB_ACL,			/* 0x2c */
+	ZFS_IOC_LEGACY_USERSPACE_ONE,		/* 0x2d */
+	ZFS_IOC_LEGACY_USERSPACE_MANY,		/* 0x2e */
+	ZFS_IOC_LEGACY_USERSPACE_UPGRADE,	/* 0x2f */
+	ZFS_IOC_LEGACY_HOLD,			/* 0x30 */
+	ZFS_IOC_LEGACY_RELEASE,			/* 0x31 */
+	ZFS_IOC_LEGACY_GET_HOLDS,		/* 0x32 */
+	ZFS_IOC_LEGACY_OBJSET_RECVD_PROPS,	/* 0x33 */
+	ZFS_IOC_LEGACY_VDEV_SPLIT,		/* 0x34 */
+	ZFS_IOC_LEGACY_NEXT_OBJ,		/* 0x35 */
+	ZFS_IOC_LEGACY_DIFF,			/* 0x36 */
+	ZFS_IOC_LEGACY_TMP_SNAPSHOT,		/* 0x37 */
+	ZFS_IOC_LEGACY_OBJ_TO_STATS,		/* 0x38 */
+	ZFS_IOC_LEGACY_SPACE_WRITTEN,		/* 0x39 */
+	ZFS_IOC_LEGACY_SPACE_SNAPS,		/* 0x3a */
+	ZFS_IOC_LEGACY_DESTROY_SNAPS,		/* 0x3b */
+	ZFS_IOC_LEGACY_POOL_REGUID,		/* 0x3c */
+	ZFS_IOC_LEGACY_POOL_REOPEN,		/* 0x3d */
+	ZFS_IOC_LEGACY_SEND_PROGRESS,		/* 0x3e */
+	ZFS_IOC_LEGACY_LOG_HISTORY,		/* 0x3f */
+	ZFS_IOC_LEGACY_SEND_NEW,		/* 0x40 */
+	ZFS_IOC_LEGACY_SEND_SPACE,		/* 0x41 */
+	ZFS_IOC_LEGACY_CLONE,			/* 0x42 */
+	ZFS_IOC_LEGACY_BOOKMARK,		/* 0x43 */
+	ZFS_IOC_LEGACY_GET_BOOKMARKS,		/* 0x44 */
+	ZFS_IOC_LEGACY_DESTROY_BOOKMARKS,	/* 0x45 */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_RECV_NEW */
+	ZFS_IOC_LEGACY_POOL_SYNC,		/* 0x47 */
+	ZFS_IOC_LEGACY_CHANNEL_PROGRAM,		/* 0x48 */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_LOAD_KEY */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_UNLOAD_KEY */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_CHANGE_KEY */
+	ZFS_IOC_LEGACY_REMAP,			/* 0x4c */
+	ZFS_IOC_LEGACY_POOL_CHECKPOINT,		/* 0x4d */
+	ZFS_IOC_LEGACY_POOL_DISCARD_CHECKPOINT,	/* 0x4e */
+	ZFS_IOC_LEGACY_POOL_INITIALIZE,		/* 0x4f  */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_POOL_TRIM */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_REDACT */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_GET_BOOKMARK_PROPS */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_WAIT */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_WAIT_FS */
+};
+
+unsigned static long zfs_ioctl_ozfs_to_legacy_platform_[] = {
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_EVENTS_NEXT */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_EVENTS_CLEAR */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_EVENTS_SEEK */
+	ZFS_IOC_LEGACY_NEXTBOOT,
+	ZFS_IOC_LEGACY_JAIL,
+	ZFS_IOC_LEGACY_UNJAIL,
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_SET_BOOTENV */
+	ZFS_IOC_LEGACY_NONE, /* ZFS_IOC_GET_BOOTENV */
+};
+
+int
+zfs_ioctl_legacy_to_ozfs(int request)
+{
+	if (request >= sizeof (zfs_ioctl_legacy_to_ozfs_)/sizeof (long))
+		return (-1);
+	return (zfs_ioctl_legacy_to_ozfs_[request]);
+}
+
+int
+zfs_ioctl_ozfs_to_legacy(int request)
+{
+	if (request > ZFS_IOC_LAST)
+		return (-1);
+
+	if (request > ZFS_IOC_PLATFORM)
+		return (zfs_ioctl_ozfs_to_legacy_platform_[request]);
+	if (request >= sizeof (zfs_ioctl_ozfs_to_legacy_common_)/sizeof (long))
+		return (-1);
+	return (zfs_ioctl_ozfs_to_legacy_common_[request]);
+}
+
+void
+zfs_cmd_legacy_to_ozfs(zfs_cmd_legacy_t *src, zfs_cmd_t *dst)
+{
+	memcpy(dst, src, offsetof(zfs_cmd_t, zc_objset_stats));
+	*&dst->zc_objset_stats = *&src->zc_objset_stats;
+	memcpy(&dst->zc_begin_record, &src->zc_begin_record,
+	    offsetof(zfs_cmd_t, zc_sendobj) -
+	    offsetof(zfs_cmd_t, zc_begin_record));
+	memcpy(&dst->zc_sendobj, &src->zc_sendobj,
+	    sizeof (zfs_cmd_t) - 8 - offsetof(zfs_cmd_t, zc_sendobj));
+	dst->zc_zoneid = src->zc_jailid;
+}
+
+void
+zfs_cmd_ozfs_to_legacy(zfs_cmd_t *src, zfs_cmd_legacy_t *dst)
+{
+	memcpy(dst, src, offsetof(zfs_cmd_t, zc_objset_stats));
+	*&dst->zc_objset_stats = *&src->zc_objset_stats;
+	*&dst->zc_begin_record.drr_u.drr_begin = *&src->zc_begin_record;
+	dst->zc_begin_record.drr_payloadlen = 0;
+	dst->zc_begin_record.drr_type = 0;
+
+	memcpy(&dst->zc_inject_record, &src->zc_inject_record,
+	    offsetof(zfs_cmd_t, zc_sendobj) -
+	    offsetof(zfs_cmd_t, zc_inject_record));
+	dst->zc_resumable = B_FALSE;
+	memcpy(&dst->zc_sendobj, &src->zc_sendobj,
+	    sizeof (zfs_cmd_t) - 8 - offsetof(zfs_cmd_t, zc_sendobj));
+	dst->zc_jailid = src->zc_zoneid;
+}