summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormm <mm@FreeBSD.org>2013-04-09 22:27:44 +0000
committermm <mm@FreeBSD.org>2013-04-09 22:27:44 +0000
commit6cbe43292efc65801d4567c37e87a006c8850340 (patch)
treee9111837bcbde6a67f9789b2333ecdb28085ef46
parent306fddaf7801d7fae1206025486e9d9a97f52ad4 (diff)
downloadFreeBSD-src-6cbe43292efc65801d4567c37e87a006c8850340.zip
FreeBSD-src-6cbe43292efc65801d4567c37e87a006c8850340.tar.gz
ZFS expects a copyout of zfs_cmd_t on an ioctl error. Our sys_ioctl()
doesn't copyout in this case. To solve this issue a new struct zfs_iocparm_t is introduced consisting of: - zfs_ioctl_version (future backwards compatibility purposes) - user space pointer to zfs_cmd_t (copyin and copyout) - size of zfs_cmd_t (verification purposes) The copyin and copyout of zfs_cmd_t is now done the illumos (vendor) way what makes porting of new changes easier and ensures correct behavior if returning an error. MFC after: 10 days
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c4
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c14
-rw-r--r--sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h10
-rw-r--r--sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c87
4 files changed, 88 insertions, 27 deletions
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
index 2a2ae76..3c8119d 100644
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
+++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
@@ -72,7 +72,9 @@ 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_DEADMAN)
+ 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;
/*
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 df91b2e..cfa4f44 100644
--- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c
@@ -577,12 +577,18 @@ zcmd_ioctl_compat(int fd, int request, zfs_cmd_t *zc, const int cflag)
int nc, ret;
void *zc_c;
unsigned long ncmd;
+ zfs_iocparm_t zp;
switch (cflag) {
case ZFS_CMD_COMPAT_NONE:
+ ncmd = _IOWR('Z', request, struct zfs_iocparm);
+ zp.zfs_cmd = (uint64_t)zc;
+ zp.zfs_cmd_size = sizeof(zfs_cmd_t);
+ zp.zfs_ioctl_version = ZFS_IOCVER_CURRENT;
+ return (ioctl(fd, ncmd, &zp));
+ case ZFS_CMD_COMPAT_LZC:
ncmd = _IOWR('Z', request, struct zfs_cmd);
- ret = ioctl(fd, ncmd, zc);
- return (ret);
+ return (ioctl(fd, ncmd, zc));
case ZFS_CMD_COMPAT_DEADMAN:
zc_c = malloc(sizeof(zfs_cmd_deadman_t));
ncmd = _IOWR('Z', request, struct zfs_cmd_deadman);
@@ -677,7 +683,7 @@ 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)
+ if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC)
goto out;
switch (vec) {
@@ -828,7 +834,7 @@ zfs_ioctl_compat_outnvl(zfs_cmd_t *zc, nvlist_t * outnvl, const int vec,
{
nvlist_t *tmpnvl;
- if (cflag == ZFS_CMD_COMPAT_NONE)
+ if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC)
return (outnvl);
switch (vec) {
diff --git a/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h b/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h
index 0e7130d..6e8d51d 100644
--- a/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h
+++ b/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h
@@ -49,19 +49,27 @@ extern "C" {
#define ZFS_IOCVER_NONE 0
#define ZFS_IOCVER_DEADMAN 1
#define ZFS_IOCVER_LZC 2
-#define ZFS_IOCVER_CURRENT ZFS_IOCVER_LZC
+#define ZFS_IOCVER_ZCMD 3
+#define ZFS_IOCVER_CURRENT ZFS_IOCVER_ZCMD
/* compatibility conversion flag */
#define ZFS_CMD_COMPAT_NONE 0
#define ZFS_CMD_COMPAT_V15 1
#define ZFS_CMD_COMPAT_V28 2
#define ZFS_CMD_COMPAT_DEADMAN 3
+#define ZFS_CMD_COMPAT_LZC 4
#define ZFS_IOC_COMPAT_PASS 254
#define ZFS_IOC_COMPAT_FAIL 255
#define ZFS_IOCREQ(ioreq) ((ioreq) & 0xff)
+typedef struct zfs_iocparm {
+ uint32_t zfs_ioctl_version;
+ uint64_t zfs_cmd;
+ uint64_t zfs_cmd_size;
+} zfs_iocparm_t;
+
typedef struct zinject_record_v15 {
uint64_t zi_objset;
uint64_t zi_object;
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 5b9defa..0dfa6ec 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
@@ -5713,11 +5713,13 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
{
zfs_cmd_t *zc;
uint_t vecnum;
-#ifdef illumos
int error, rc, len;
+#ifdef illumos
minor_t minor = getminor(dev);
#else
- int error, len, cflag, cmd, oldvecnum;
+ zfs_iocparm_t *zc_iocparm;
+ int cflag, cmd, oldvecnum;
+ boolean_t newioc, compat;
cred_t *cr = td->td_ucred;
#endif
const zfs_ioc_vec_t *vec;
@@ -5725,6 +5727,9 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
nvlist_t *innvl = NULL;
cflag = ZFS_CMD_COMPAT_NONE;
+ compat = B_FALSE;
+ newioc = B_TRUE;
+
len = IOCPARM_LEN(zcmd);
cmd = zcmd & 0xff;
@@ -5732,19 +5737,26 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
* Check if we are talking to supported older binaries
* and translate zfs_cmd if necessary
*/
- if (len != sizeof(zfs_cmd_t))
- if (len == sizeof(zfs_cmd_deadman_t)) {
+ if (len != sizeof(zfs_iocparm_t)) {
+ newioc = B_FALSE;
+ if (len == sizeof(zfs_cmd_t)) {
+ cflag = ZFS_CMD_COMPAT_LZC;
+ vecnum = cmd;
+ } else if (len == sizeof(zfs_cmd_deadman_t)) {
cflag = ZFS_CMD_COMPAT_DEADMAN;
+ compat = B_TRUE;
vecnum = cmd;
} else if (len == sizeof(zfs_cmd_v28_t)) {
cflag = ZFS_CMD_COMPAT_V28;
+ compat = B_TRUE;
vecnum = cmd;
} else if (len == sizeof(zfs_cmd_v15_t)) {
cflag = ZFS_CMD_COMPAT_V15;
+ compat = B_TRUE;
vecnum = zfs_ioctl_v15_to_v28[cmd];
} else
return (EINVAL);
- else
+ } else
vecnum = cmd;
#ifdef illumos
@@ -5752,7 +5764,7 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
#endif
- if (cflag != ZFS_CMD_COMPAT_NONE) {
+ if (compat) {
if (vecnum == ZFS_IOC_COMPAT_PASS)
return (0);
else if (vecnum == ZFS_IOC_COMPAT_FAIL)
@@ -5777,13 +5789,33 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
error = SET_ERROR(EFAULT);
goto out;
}
-#else
- error = 0;
-#endif
-
- if (cflag != ZFS_CMD_COMPAT_NONE) {
+#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;
+ }
+
+ 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 *)zc_iocparm->zfs_cmd, zc,
+ sizeof(zfs_cmd_t), flag);
+ if (error != 0) {
+ error = SET_ERROR(EFAULT);
+ goto out;
+ }
+ }
+
+ if (compat) {
zfs_cmd_compat_get(zc, arg, cflag);
oldvecnum = vecnum;
error = zfs_ioctl_compat_pre(zc, &vecnum, cflag);
@@ -5791,8 +5823,8 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
goto out;
if (oldvecnum != vecnum)
vec = &zfs_ioc_vec[vecnum];
- } else
- zc = (void *)arg;
+ }
+#endif /* !illumos */
zc->zc_iflags = flag & FKIOCTL;
if (zc->zc_nvlist_src_size != 0) {
@@ -5803,7 +5835,7 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
}
/* rewrite innvl for backwards compatibility */
- if (cflag != ZFS_CMD_COMPAT_NONE)
+ if (compat)
innvl = zfs_ioctl_compat_innvl(zc, innvl, vecnum, cflag);
/*
@@ -5880,7 +5912,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)
+ if (cflag != ZFS_CMD_COMPAT_NONE && cflag != ZFS_CMD_COMPAT_LZC)
outnvl = zfs_ioctl_compat_outnvl(zc, outnvl, vecnum,
cflag);
@@ -5904,10 +5936,23 @@ 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) {
+ rc = ddi_copyout(zc, (void *)zc_iocparm->zfs_cmd,
+ sizeof (zfs_cmd_t), flag);
+ if (error == 0 && rc != 0)
+ error = SET_ERROR(EFAULT);
+ }
#endif
if (error == 0 && vec->zvec_allow_log) {
char *s = tsd_get(zfs_allow_log_key);
@@ -5919,14 +5964,14 @@ out:
strfree(saved_poolname);
}
- if (cflag != ZFS_CMD_COMPAT_NONE) {
- zfs_ioctl_compat_post(zc, cmd, cflag);
- zfs_cmd_compat_put(zc, arg, vecnum, cflag);
- kmem_free(zc, sizeof (zfs_cmd_t));
- }
-
#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