- 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] X-MFC-With: r268075
This commit is contained in:
parent
8c4948bf02
commit
be1d315a04
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
@ -5922,6 +5923,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;
|
||||
@ -5930,10 +5932,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
|
||||
@ -5941,96 +5943,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)) {
|
||||
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
|
||||
compat = B_TRUE;
|
||||
|
||||
vecnum = cmd;
|
||||
|
||||
switch (len) {
|
||||
case sizeof(zfs_cmd_zcmd_t):
|
||||
cflag = ZFS_CMD_COMPAT_LZC;
|
||||
break;
|
||||
case sizeof(zfs_cmd_deadman_t):
|
||||
cflag = ZFS_CMD_COMPAT_DEADMAN;
|
||||
break;
|
||||
case sizeof(zfs_cmd_v28_t):
|
||||
cflag = ZFS_CMD_COMPAT_V28;
|
||||
break;
|
||||
case sizeof(zfs_cmd_v15_t):
|
||||
cflag = ZFS_CMD_COMPAT_V15;
|
||||
vecnum = zfs_ioctl_v15_to_v28[cmd];
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
#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);
|
||||
goto out;
|
||||
}
|
||||
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:
|
||||
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 (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)
|
||||
@ -6126,7 +6144,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);
|
||||
|
||||
@ -6151,17 +6169,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)
|
||||
@ -6178,15 +6209,7 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user