1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or https://opensource.org/licenses/CDDL-1.0. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 #include <sys/types.h> 22 #include <sys/param.h> 23 #include <sys/sysctl.h> 24 #include <sys/zfs_ioctl.h> 25 #include <os/freebsd/zfs/sys/zfs_ioctl_compat.h> 26 #include <err.h> 27 #include <libzfs_core.h> 28 29 int zfs_ioctl_version = ZFS_IOCVER_UNDEF; 30 31 /* 32 * Get zfs_ioctl_version 33 */ 34 static int 35 get_zfs_ioctl_version(void) 36 { 37 size_t ver_size; 38 int ver = ZFS_IOCVER_NONE; 39 40 ver_size = sizeof (ver); 41 (void) sysctlbyname("vfs.zfs.version.ioctl", &ver, &ver_size, NULL, 0); 42 43 return (ver); 44 } 45 46 static int 47 zcmd_ioctl_compat(int fd, int request, zfs_cmd_t *zc, const int cflag) 48 { 49 int ret; 50 #ifdef ZFS_LEGACY_SUPPORT 51 int newrequest; 52 void *zc_c = NULL; 53 #endif 54 unsigned long ncmd; 55 zfs_iocparm_t zp; 56 57 switch (cflag) { 58 case ZFS_CMD_COMPAT_NONE: 59 ncmd = _IOWR('Z', request, zfs_iocparm_t); 60 zp.zfs_cmd = (uint64_t)(uintptr_t)zc; 61 zp.zfs_cmd_size = sizeof (zfs_cmd_t); 62 zp.zfs_ioctl_version = ZFS_IOCVER_OZFS; 63 break; 64 #ifdef ZFS_LEGACY_SUPPORT 65 case ZFS_CMD_COMPAT_LEGACY: 66 newrequest = zfs_ioctl_ozfs_to_legacy(request); 67 ncmd = _IOWR('Z', newrequest, zfs_iocparm_t); 68 zc_c = malloc(sizeof (zfs_cmd_legacy_t)); 69 zfs_cmd_ozfs_to_legacy(zc, zc_c); 70 zp.zfs_cmd = (uint64_t)(uintptr_t)zc_c; 71 zp.zfs_cmd_size = sizeof (zfs_cmd_legacy_t); 72 zp.zfs_ioctl_version = ZFS_IOCVER_LEGACY; 73 break; 74 #endif 75 default: 76 abort(); 77 return (EINVAL); 78 } 79 80 ret = ioctl(fd, ncmd, &zp); 81 if (ret) { 82 #ifdef ZFS_LEGACY_SUPPORT 83 if (zc_c) 84 free(zc_c); 85 #endif 86 return (ret); 87 } 88 #ifdef ZFS_LEGACY_SUPPORT 89 if (zc_c) { 90 zfs_cmd_legacy_to_ozfs(zc_c, zc); 91 free(zc_c); 92 } 93 #endif 94 return (ret); 95 } 96 97 /* 98 * This is FreeBSD version of ioctl, because Solaris' ioctl() updates 99 * zc_nvlist_dst_size even if an error is returned, on FreeBSD if an 100 * error is returned zc_nvlist_dst_size won't be updated. 101 */ 102 int 103 lzc_ioctl_fd(int fd, unsigned long request, zfs_cmd_t *zc) 104 { 105 size_t oldsize; 106 int ret, cflag = ZFS_CMD_COMPAT_NONE; 107 108 if (zfs_ioctl_version == ZFS_IOCVER_UNDEF) 109 zfs_ioctl_version = get_zfs_ioctl_version(); 110 111 switch (zfs_ioctl_version) { 112 #ifdef ZFS_LEGACY_SUPPORT 113 case ZFS_IOCVER_LEGACY: 114 cflag = ZFS_CMD_COMPAT_LEGACY; 115 break; 116 #endif 117 case ZFS_IOCVER_OZFS: 118 cflag = ZFS_CMD_COMPAT_NONE; 119 break; 120 default: 121 errx(1, "unrecognized zfs ioctl version %d", 122 zfs_ioctl_version); 123 } 124 125 oldsize = zc->zc_nvlist_dst_size; 126 ret = zcmd_ioctl_compat(fd, request, zc, cflag); 127 128 if (ret == 0 && oldsize < zc->zc_nvlist_dst_size) { 129 ret = -1; 130 errno = ENOMEM; 131 } 132 133 return (ret); 134 } 135