Author: delphij
Date: Thu Jul 17 05:20:18 2014
New Revision: 268786
URL: http://svnweb.freebsd.org/changeset/base/268786

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

Modified:
  stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
  stable/10/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
==============================================================================
--- stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c        
Thu Jul 17 02:43:53 2014        (r268785)
+++ stable/10/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c        
Thu Jul 17 05:20:18 2014        (r268786)
@@ -72,16 +72,23 @@ 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_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)

Modified: stable/10/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c
==============================================================================
--- stable/10/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c        
Thu Jul 17 02:43:53 2014        (r268785)
+++ stable/10/sys/cddl/contrib/opensolaris/common/zfs/zfs_ioctl_compat.c        
Thu Jul 17 05:20:18 2014        (r268786)
@@ -697,6 +697,12 @@ zcmd_ioctl_compat(int fd, int request, z
                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, nv
        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, n
 {
        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) {

Modified: stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
==============================================================================
--- stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c        
Thu Jul 17 02:43:53 2014        (r268785)
+++ stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c        
Thu Jul 17 05:20:18 2014        (r268786)
@@ -24,6 +24,7 @@
  * Copyright (c) 2011-2012 Pawel Jakub Dawidek <pa...@dawidek.net>.
  * All rights reserved.
  * Copyright 2013 Martin Matuska <m...@freebsd.org>. All rights reserved.
+ * Copyright 2014 Xin Li <delp...@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 zc
        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 zc
 
        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 zc
         */
        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 zc
                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 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) {
+       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);
 }
 
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to