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
This commit is contained in:
parent
e8b3186b6a
commit
83b4af1142
@ -72,7 +72,9 @@ zcmd_ioctl(int fd, int request, zfs_cmd_t *zc)
|
|||||||
if (zfs_ioctl_version == ZFS_IOCVER_UNDEF)
|
if (zfs_ioctl_version == ZFS_IOCVER_UNDEF)
|
||||||
zfs_ioctl_version = get_zfs_ioctl_version();
|
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;
|
cflag = ZFS_CMD_COMPAT_DEADMAN;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -577,12 +577,18 @@ zcmd_ioctl_compat(int fd, int request, zfs_cmd_t *zc, const int cflag)
|
|||||||
int nc, ret;
|
int nc, ret;
|
||||||
void *zc_c;
|
void *zc_c;
|
||||||
unsigned long ncmd;
|
unsigned long ncmd;
|
||||||
|
zfs_iocparm_t zp;
|
||||||
|
|
||||||
switch (cflag) {
|
switch (cflag) {
|
||||||
case ZFS_CMD_COMPAT_NONE:
|
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);
|
ncmd = _IOWR('Z', request, struct zfs_cmd);
|
||||||
ret = ioctl(fd, ncmd, zc);
|
return (ioctl(fd, ncmd, zc));
|
||||||
return (ret);
|
|
||||||
case ZFS_CMD_COMPAT_DEADMAN:
|
case ZFS_CMD_COMPAT_DEADMAN:
|
||||||
zc_c = malloc(sizeof(zfs_cmd_deadman_t));
|
zc_c = malloc(sizeof(zfs_cmd_deadman_t));
|
||||||
ncmd = _IOWR('Z', request, struct zfs_cmd_deadman);
|
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;
|
char *poolname, *snapname;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (cflag == ZFS_CMD_COMPAT_NONE)
|
if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
switch (vec) {
|
switch (vec) {
|
||||||
@ -828,7 +834,7 @@ zfs_ioctl_compat_outnvl(zfs_cmd_t *zc, nvlist_t * outnvl, const int vec,
|
|||||||
{
|
{
|
||||||
nvlist_t *tmpnvl;
|
nvlist_t *tmpnvl;
|
||||||
|
|
||||||
if (cflag == ZFS_CMD_COMPAT_NONE)
|
if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC)
|
||||||
return (outnvl);
|
return (outnvl);
|
||||||
|
|
||||||
switch (vec) {
|
switch (vec) {
|
||||||
|
@ -49,19 +49,27 @@ extern "C" {
|
|||||||
#define ZFS_IOCVER_NONE 0
|
#define ZFS_IOCVER_NONE 0
|
||||||
#define ZFS_IOCVER_DEADMAN 1
|
#define ZFS_IOCVER_DEADMAN 1
|
||||||
#define ZFS_IOCVER_LZC 2
|
#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 */
|
/* compatibility conversion flag */
|
||||||
#define ZFS_CMD_COMPAT_NONE 0
|
#define ZFS_CMD_COMPAT_NONE 0
|
||||||
#define ZFS_CMD_COMPAT_V15 1
|
#define ZFS_CMD_COMPAT_V15 1
|
||||||
#define ZFS_CMD_COMPAT_V28 2
|
#define ZFS_CMD_COMPAT_V28 2
|
||||||
#define ZFS_CMD_COMPAT_DEADMAN 3
|
#define ZFS_CMD_COMPAT_DEADMAN 3
|
||||||
|
#define ZFS_CMD_COMPAT_LZC 4
|
||||||
|
|
||||||
#define ZFS_IOC_COMPAT_PASS 254
|
#define ZFS_IOC_COMPAT_PASS 254
|
||||||
#define ZFS_IOC_COMPAT_FAIL 255
|
#define ZFS_IOC_COMPAT_FAIL 255
|
||||||
|
|
||||||
#define ZFS_IOCREQ(ioreq) ((ioreq) & 0xff)
|
#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 {
|
typedef struct zinject_record_v15 {
|
||||||
uint64_t zi_objset;
|
uint64_t zi_objset;
|
||||||
uint64_t zi_object;
|
uint64_t zi_object;
|
||||||
|
@ -5713,11 +5713,13 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
|
|||||||
{
|
{
|
||||||
zfs_cmd_t *zc;
|
zfs_cmd_t *zc;
|
||||||
uint_t vecnum;
|
uint_t vecnum;
|
||||||
#ifdef illumos
|
|
||||||
int error, rc, len;
|
int error, rc, len;
|
||||||
|
#ifdef illumos
|
||||||
minor_t minor = getminor(dev);
|
minor_t minor = getminor(dev);
|
||||||
#else
|
#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;
|
cred_t *cr = td->td_ucred;
|
||||||
#endif
|
#endif
|
||||||
const zfs_ioc_vec_t *vec;
|
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;
|
nvlist_t *innvl = NULL;
|
||||||
|
|
||||||
cflag = ZFS_CMD_COMPAT_NONE;
|
cflag = ZFS_CMD_COMPAT_NONE;
|
||||||
|
compat = B_FALSE;
|
||||||
|
newioc = B_TRUE;
|
||||||
|
|
||||||
len = IOCPARM_LEN(zcmd);
|
len = IOCPARM_LEN(zcmd);
|
||||||
cmd = zcmd & 0xff;
|
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
|
* Check if we are talking to supported older binaries
|
||||||
* and translate zfs_cmd if necessary
|
* and translate zfs_cmd if necessary
|
||||||
*/
|
*/
|
||||||
if (len != sizeof(zfs_cmd_t))
|
if (len != sizeof(zfs_iocparm_t)) {
|
||||||
if (len == sizeof(zfs_cmd_deadman_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;
|
cflag = ZFS_CMD_COMPAT_DEADMAN;
|
||||||
|
compat = B_TRUE;
|
||||||
vecnum = cmd;
|
vecnum = cmd;
|
||||||
} else if (len == sizeof(zfs_cmd_v28_t)) {
|
} else if (len == sizeof(zfs_cmd_v28_t)) {
|
||||||
cflag = ZFS_CMD_COMPAT_V28;
|
cflag = ZFS_CMD_COMPAT_V28;
|
||||||
|
compat = B_TRUE;
|
||||||
vecnum = cmd;
|
vecnum = cmd;
|
||||||
} else if (len == sizeof(zfs_cmd_v15_t)) {
|
} else if (len == sizeof(zfs_cmd_v15_t)) {
|
||||||
cflag = ZFS_CMD_COMPAT_V15;
|
cflag = ZFS_CMD_COMPAT_V15;
|
||||||
|
compat = B_TRUE;
|
||||||
vecnum = zfs_ioctl_v15_to_v28[cmd];
|
vecnum = zfs_ioctl_v15_to_v28[cmd];
|
||||||
} else
|
} else
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
else
|
} else
|
||||||
vecnum = cmd;
|
vecnum = cmd;
|
||||||
|
|
||||||
#ifdef illumos
|
#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));
|
ASSERT3U(getmajor(dev), ==, ddi_driver_major(zfs_dip));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (cflag != ZFS_CMD_COMPAT_NONE) {
|
if (compat) {
|
||||||
if (vecnum == ZFS_IOC_COMPAT_PASS)
|
if (vecnum == ZFS_IOC_COMPAT_PASS)
|
||||||
return (0);
|
return (0);
|
||||||
else if (vecnum == ZFS_IOC_COMPAT_FAIL)
|
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);
|
error = SET_ERROR(EFAULT);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
#else
|
#else /* !illumos */
|
||||||
error = 0;
|
/*
|
||||||
#endif
|
* We don't alloc/free zc only if talking to library ioctl version 2
|
||||||
|
*/
|
||||||
if (cflag != ZFS_CMD_COMPAT_NONE) {
|
if (cflag != ZFS_CMD_COMPAT_LZC) {
|
||||||
zc = kmem_zalloc(sizeof(zfs_cmd_t), KM_SLEEP);
|
zc = kmem_zalloc(sizeof(zfs_cmd_t), KM_SLEEP);
|
||||||
bzero(zc, sizeof(zfs_cmd_t));
|
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);
|
zfs_cmd_compat_get(zc, arg, cflag);
|
||||||
oldvecnum = vecnum;
|
oldvecnum = vecnum;
|
||||||
error = zfs_ioctl_compat_pre(zc, &vecnum, cflag);
|
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;
|
goto out;
|
||||||
if (oldvecnum != vecnum)
|
if (oldvecnum != vecnum)
|
||||||
vec = &zfs_ioc_vec[vecnum];
|
vec = &zfs_ioc_vec[vecnum];
|
||||||
} else
|
}
|
||||||
zc = (void *)arg;
|
#endif /* !illumos */
|
||||||
|
|
||||||
zc->zc_iflags = flag & FKIOCTL;
|
zc->zc_iflags = flag & FKIOCTL;
|
||||||
if (zc->zc_nvlist_src_size != 0) {
|
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 */
|
/* rewrite innvl for backwards compatibility */
|
||||||
if (cflag != ZFS_CMD_COMPAT_NONE)
|
if (compat)
|
||||||
innvl = zfs_ioctl_compat_innvl(zc, innvl, vecnum, cflag);
|
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);
|
fnvlist_free(lognv);
|
||||||
|
|
||||||
/* rewrite outnvl for backwards compatibility */
|
/* 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,
|
outnvl = zfs_ioctl_compat_outnvl(zc, outnvl, vecnum,
|
||||||
cflag);
|
cflag);
|
||||||
|
|
||||||
@ -5904,10 +5936,23 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
nvlist_free(innvl);
|
nvlist_free(innvl);
|
||||||
|
|
||||||
|
if (compat) {
|
||||||
|
zfs_ioctl_compat_post(zc, cmd, cflag);
|
||||||
|
zfs_cmd_compat_put(zc, arg, vecnum, cflag);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef illumos
|
#ifdef illumos
|
||||||
rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
|
rc = ddi_copyout(zc, (void *)arg, sizeof (zfs_cmd_t), flag);
|
||||||
if (error == 0 && rc != 0)
|
if (error == 0 && rc != 0)
|
||||||
error = SET_ERROR(EFAULT);
|
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
|
#endif
|
||||||
if (error == 0 && vec->zvec_allow_log) {
|
if (error == 0 && vec->zvec_allow_log) {
|
||||||
char *s = tsd_get(zfs_allow_log_key);
|
char *s = tsd_get(zfs_allow_log_key);
|
||||||
@ -5919,14 +5964,14 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
|
|||||||
strfree(saved_poolname);
|
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
|
#ifdef illumos
|
||||||
kmem_free(zc, sizeof (zfs_cmd_t));
|
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
|
#endif
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user