Author: mm
Date: Tue Apr  9 22:27:44 2013
New Revision: 249319
URL: http://svnweb.freebsd.org/changeset/base/249319

Log:
  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

Modified:
  head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
  head/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c
  head/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c

Modified: head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
==============================================================================
--- head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c     Tue Apr 
 9 21:02:20 2013        (r249318)
+++ head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c     Tue Apr 
 9 22:27:44 2013        (r249319)
@@ -72,7 +72,9 @@ zcmd_ioctl(int fd, int request, zfs_cmd_
        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;
 
        /*

Modified: head/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c     Tue Apr 
 9 21:02:20 2013        (r249318)
+++ head/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c     Tue Apr 
 9 22:27:44 2013        (r249319)
@@ -577,12 +577,18 @@ zcmd_ioctl_compat(int fd, int request, z
        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, nv
        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, n
 {
        nvlist_t *tmpnvl;
 
-       if (cflag == ZFS_CMD_COMPAT_NONE)
+       if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC)
                return (outnvl);
 
        switch (vec) {

Modified: head/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h
==============================================================================
--- head/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h     Tue Apr 
 9 21:02:20 2013        (r249318)
+++ head/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.h     Tue Apr 
 9 22:27:44 2013        (r249319)
@@ -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;

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c     Tue Apr 
 9 21:02:20 2013        (r249318)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c     Tue Apr 
 9 22:27:44 2013        (r249319)
@@ -5713,11 +5713,13 @@ zfsdev_ioctl(struct cdev *dev, u_long zc
 {
        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 zc
        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 zc
         * 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 zc
        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 zc
                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 zc
                        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 zc
        }
 
        /* 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 zc
                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 zc
 
 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);
 }
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to