summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordelphij <delphij@FreeBSD.org>2014-07-17 05:20:18 +0000
committerdelphij <delphij@FreeBSD.org>2014-07-17 05:20:18 +0000
commitadc65d02d14d973466e44875fad12c0421e04fc8 (patch)
treed2d5b5510972b713248a9f6f39e326dcdb96d589
parent4643efbd28426df43c1d3040e4eaab0e1d8b52aa (diff)
downloadFreeBSD-src-adc65d02d14d973466e44875fad12c0421e04fc8.zip
FreeBSD-src-adc65d02d14d973466e44875fad12c0421e04fc8.tar.gz
MFC r268116:
- Fix handling of "new" style of ioctl in compatiblity mode [1]; - Reorganize code and reduce diff from upstream; - Improve forward compatibility shims for previous kernel; Reported by: sbruno [1]
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c27
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c12
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c165
3 files changed, 121 insertions, 83 deletions
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
index 3c8119d..a3f6129 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
@@ -72,16 +72,23 @@ zcmd_ioctl(int fd, int request, zfs_cmd_t *zc)
if (zfs_ioctl_version == ZFS_IOCVER_UNDEF)
zfs_ioctl_version = get_zfs_ioctl_version();
- if (zfs_ioctl_version == ZFS_IOCVER_LZC)
- cflag = ZFS_CMD_COMPAT_LZC;
- else if (zfs_ioctl_version == ZFS_IOCVER_DEADMAN)
- cflag = ZFS_CMD_COMPAT_DEADMAN;
-
- /*
- * If vfs.zfs.version.ioctl is not defined, assume we have v28
- * compatible binaries and use vfs.zfs.version.spa to test for v15
- */
- if (zfs_ioctl_version < ZFS_IOCVER_DEADMAN) {
+ if (zfs_ioctl_version >= ZFS_IOCVER_DEADMAN) {
+ switch (zfs_ioctl_version) {
+ case ZFS_IOCVER_ZCMD:
+ cflag = ZFS_CMD_COMPAT_ZCMD;
+ break;
+ case ZFS_IOCVER_LZC:
+ cflag = ZFS_CMD_COMPAT_LZC;
+ break;
+ case ZFS_IOCVER_DEADMAN:
+ cflag = ZFS_CMD_COMPAT_DEADMAN;
+ break;
+ }
+ } else {
+ /*
+ * If vfs.zfs.version.ioctl is not defined, assume we have v28
+ * compatible binaries and use vfs.zfs.version.spa to test for v15
+ */
cflag = ZFS_CMD_COMPAT_V28;
if (zfs_spa_version < 0)
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c b/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c
index bdb6a99..62fed7b 100644
--- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c
@@ -697,6 +697,12 @@ zcmd_ioctl_compat(int fd, int request, zfs_cmd_t *zc, const int cflag)
zp.zfs_cmd_size = sizeof(zfs_cmd_t);
zp.zfs_ioctl_version = ZFS_IOCVER_CURRENT;
return (ioctl(fd, ncmd, &zp));
+ case ZFS_CMD_COMPAT_ZCMD:
+ ncmd = _IOWR('Z', request, struct zfs_iocparm);
+ zp.zfs_cmd = (uint64_t)zc;
+ zp.zfs_cmd_size = sizeof(zfs_cmd_zcmd_t);
+ zp.zfs_ioctl_version = ZFS_IOCVER_ZCMD;
+ return (ioctl(fd, ncmd, &zp));
case ZFS_CMD_COMPAT_LZC:
ncmd = _IOWR('Z', request, struct zfs_cmd);
return (ioctl(fd, ncmd, zc));
@@ -794,7 +800,8 @@ zfs_ioctl_compat_innvl(zfs_cmd_t *zc, nvlist_t * innvl, const int vec,
char *poolname, *snapname;
int err;
- if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC)
+ if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
+ cflag == ZFS_CMD_COMPAT_ZCMD)
goto out;
switch (vec) {
@@ -945,7 +952,8 @@ zfs_ioctl_compat_outnvl(zfs_cmd_t *zc, nvlist_t * outnvl, const int vec,
{
nvlist_t *tmpnvl;
- if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC)
+ if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
+ cflag == ZFS_CMD_COMPAT_ZCMD)
return (outnvl);
switch (vec) {
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
index 1e536e3..443a5b2 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
@@ -24,6 +24,7 @@
* Copyright (c) 2011-2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
* All rights reserved.
* Copyright 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
+ * Copyright 2014 Xin Li <delphij@FreeBSD.org>. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2014, Joyent, Inc. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
@@ -5893,6 +5894,7 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
zfs_iocparm_t *zc_iocparm;
int cflag, cmd, oldvecnum;
boolean_t newioc, compat;
+ void *compat_zc = NULL;
cred_t *cr = td->td_ucred;
#endif
const zfs_ioc_vec_t *vec;
@@ -5901,10 +5903,10 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
cflag = ZFS_CMD_COMPAT_NONE;
compat = B_FALSE;
- newioc = B_TRUE;
+ newioc = B_TRUE; /* "new" style (zfs_iocparm_t) ioctl */
len = IOCPARM_LEN(zcmd);
- cmd = zcmd & 0xff;
+ vecnum = cmd = zcmd & 0xff;
/*
* Check if we are talking to supported older binaries
@@ -5912,96 +5914,112 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
*/
if (len != sizeof(zfs_iocparm_t)) {
newioc = B_FALSE;
- if (len == sizeof(zfs_cmd_t)) {
+ compat = B_TRUE;
+
+ vecnum = cmd;
+
+ switch (len) {
+ case sizeof(zfs_cmd_zcmd_t):
cflag = ZFS_CMD_COMPAT_LZC;
- vecnum = cmd;
- } else if (len == sizeof(zfs_cmd_deadman_t)) {
+ break;
+ case sizeof(zfs_cmd_deadman_t):
cflag = ZFS_CMD_COMPAT_DEADMAN;
- compat = B_TRUE;
- vecnum = cmd;
- } else if (len == sizeof(zfs_cmd_v28_t)) {
+ break;
+ case sizeof(zfs_cmd_v28_t):
cflag = ZFS_CMD_COMPAT_V28;
- compat = B_TRUE;
- vecnum = cmd;
- } else if (len == sizeof(zfs_cmd_v15_t)) {
+ break;
+ case sizeof(zfs_cmd_v15_t):
cflag = ZFS_CMD_COMPAT_V15;
- compat = B_TRUE;
vecnum = zfs_ioctl_v15_to_v28[cmd];
- } else
+
+ /*
+ * Return without further handling
+ * if the command is blacklisted.
+ */
+ if (vecnum == ZFS_IOC_COMPAT_PASS)
+ return (0);
+ else if (vecnum == ZFS_IOC_COMPAT_FAIL)
+ return (ENOTSUP);
+ break;
+ default:
return (EINVAL);
- } else
- vecnum = cmd;
+ }
+ }
#ifdef illumos
vecnum = cmd - ZFS_IOC_FIRST;
ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
#endif
- if (compat) {
- if (vecnum == ZFS_IOC_COMPAT_PASS)
- return (0);
- else if (vecnum == ZFS_IOC_COMPAT_FAIL)
- return (ENOTSUP);
- }
-
- /*
- * Check if we have sufficient kernel memory allocated
- * for the zfs_cmd_t request. Bail out if not so we
- * will not access undefined memory region.
- */
if (vecnum >= sizeof (zfs_ioc_vec) / sizeof (zfs_ioc_vec[0]))
return (SET_ERROR(EINVAL));
vec = &zfs_ioc_vec[vecnum];
-#ifdef illumos
zc = kmem_zalloc(sizeof(zfs_cmd_t), KM_SLEEP);
- bzero(zc, sizeof(zfs_cmd_t));
+#ifdef illumos
error = ddi_copyin((void *)arg, zc, sizeof (zfs_cmd_t), flag);
if (error != 0) {
error = SET_ERROR(EFAULT);
goto out;
}
#else /* !illumos */
- /*
- * We don't alloc/free zc only if talking to library ioctl version 2
- */
- if (cflag != ZFS_CMD_COMPAT_LZC) {
- zc = kmem_zalloc(sizeof(zfs_cmd_t), KM_SLEEP);
- bzero(zc, sizeof(zfs_cmd_t));
- } else {
- zc = (void *)arg;
- error = 0;
- }
+ bzero(zc, sizeof(zfs_cmd_t));
if (newioc) {
zc_iocparm = (void *)arg;
- if (zc_iocparm->zfs_cmd_size != sizeof(zfs_cmd_t)) {
- error = SET_ERROR(EFAULT);
- goto out;
- }
- error = ddi_copyin((void *)(uintptr_t)zc_iocparm->zfs_cmd, zc,
- sizeof(zfs_cmd_t), flag);
- if (error != 0) {
- error = SET_ERROR(EFAULT);
+
+ switch (zc_iocparm->zfs_ioctl_version) {
+ case ZFS_IOCVER_CURRENT:
+ if (zc_iocparm->zfs_cmd_size != sizeof(zfs_cmd_t)) {
+ error = SET_ERROR(EINVAL);
+ goto out;
+ }
+ break;
+ case ZFS_IOCVER_ZCMD:
+ if (zc_iocparm->zfs_cmd_size > sizeof(zfs_cmd_t) ||
+ zc_iocparm->zfs_cmd_size < sizeof(zfs_cmd_zcmd_t)) {
+ error = SET_ERROR(EFAULT);
+ goto out;
+ }
+ compat = B_TRUE;
+ cflag = ZFS_CMD_COMPAT_ZCMD;
+ break;
+ default:
+ error = SET_ERROR(EINVAL);
goto out;
+ /* NOTREACHED */
}
- if (zc_iocparm->zfs_ioctl_version != ZFS_IOCVER_CURRENT) {
- compat = B_TRUE;
- switch (zc_iocparm->zfs_ioctl_version) {
- case ZFS_IOCVER_ZCMD:
- cflag = ZFS_CMD_COMPAT_ZCMD;
- break;
- default:
- error = SET_ERROR(EINVAL);
+ if (compat) {
+ ASSERT(sizeof(zfs_cmd_t) >= zc_iocparm->zfs_cmd_size);
+ compat_zc = kmem_zalloc(sizeof(zfs_cmd_t), KM_SLEEP);
+ bzero(compat_zc, sizeof(zfs_cmd_t));
+
+ error = ddi_copyin((void *)(uintptr_t)zc_iocparm->zfs_cmd,
+ compat_zc, zc_iocparm->zfs_cmd_size, flag);
+ if (error != 0) {
+ error = SET_ERROR(EFAULT);
+ goto out;
+ }
+ } else {
+ error = ddi_copyin((void *)(uintptr_t)zc_iocparm->zfs_cmd,
+ zc, zc_iocparm->zfs_cmd_size, flag);
+ if (error != 0) {
+ error = SET_ERROR(EFAULT);
goto out;
}
}
}
if (compat) {
- zfs_cmd_compat_get(zc, arg, cflag);
+ if (newioc) {
+ ASSERT(compat_zc != NULL);
+ zfs_cmd_compat_get(zc, compat_zc, cflag);
+ } else {
+ ASSERT(compat_zc == NULL);
+ zfs_cmd_compat_get(zc, arg, cflag);
+ }
oldvecnum = vecnum;
error = zfs_ioctl_compat_pre(zc, &vecnum, cflag);
if (error != 0)
@@ -6097,7 +6115,7 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
fnvlist_free(lognv);
/* rewrite outnvl for backwards compatibility */
- if (cflag != ZFS_CMD_COMPAT_NONE && cflag != ZFS_CMD_COMPAT_LZC)
+ if (compat)
outnvl = zfs_ioctl_compat_outnvl(zc, outnvl, vecnum,
cflag);
@@ -6122,17 +6140,30 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
out:
nvlist_free(innvl);
- if (compat) {
- zfs_ioctl_compat_post(zc, cmd, cflag);
- zfs_cmd_compat_put(zc, arg, vecnum, cflag);
- }
-
#ifdef illumos
rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
if (error == 0 && rc != 0)
error = SET_ERROR(EFAULT);
#else
- if (newioc) {
+ if (compat) {
+ zfs_ioctl_compat_post(zc, cmd, cflag);
+ if (newioc) {
+ ASSERT(compat_zc != NULL);
+ ASSERT(sizeof(zfs_cmd_t) >= zc_iocparm->zfs_cmd_size);
+
+ zfs_cmd_compat_put(zc, compat_zc, vecnum, cflag);
+ rc = ddi_copyout(compat_zc,
+ (void *)(uintptr_t)zc_iocparm->zfs_cmd,
+ zc_iocparm->zfs_cmd_size, flag);
+ if (error == 0 && rc != 0)
+ error = SET_ERROR(EFAULT);
+ kmem_free(compat_zc, sizeof (zfs_cmd_t));
+ } else {
+ zfs_cmd_compat_put(zc, arg, vecnum, cflag);
+ }
+ } else {
+ ASSERT(newioc);
+
rc = ddi_copyout(zc, (void *)(uintptr_t)zc_iocparm->zfs_cmd,
sizeof (zfs_cmd_t), flag);
if (error == 0 && rc != 0)
@@ -6149,15 +6180,7 @@ out:
strfree(saved_poolname);
}
-#ifdef illumos
kmem_free(zc, sizeof (zfs_cmd_t));
-#else
- /*
- * We don't alloc/free zc only if talking to library ioctl version 2
- */
- if (cflag != ZFS_CMD_COMPAT_LZC)
- kmem_free(zc, sizeof (zfs_cmd_t));
-#endif
return (error);
}
OpenPOWER on IntegriCloud