libzfs_core:

- provide complete backwards compatibility (old utility, new kernel)
  - add zfs_cmd_t compatibility mapping in both directions
  - determine ioctl address in zfs_ioctl_compat.c
This commit is contained in:
Martin Matuska 2013-03-17 10:57:04 +00:00
parent 4f33cfb284
commit e2b4467975
4 changed files with 314 additions and 27 deletions

View File

@ -43,18 +43,18 @@ static int zfs_ioctl_version = 0;
static __inline int
zcmd_ioctl(int fd, int request, zfs_cmd_t *zc)
{
unsigned long cmd;
size_t oldsize, zfs_kernel_version_size, zfs_ioctl_version_size;
int version, ret, cflag = ZFS_CMD_COMPAT_NONE;
cmd = _IOWR('Z', request, struct zfs_cmd);
zfs_ioctl_version_size = sizeof(zfs_ioctl_version);
if (zfs_ioctl_version == 0) {
sysctlbyname("vfs.zfs.version.ioctl", &zfs_ioctl_version,
&zfs_ioctl_version_size, NULL, 0);
}
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
@ -76,7 +76,7 @@ zcmd_ioctl(int fd, int request, zfs_cmd_t *zc)
}
oldsize = zc->zc_nvlist_dst_size;
ret = zcmd_ioctl_compat(fd, cmd, zc, cflag);
ret = zcmd_ioctl_compat(fd, request, zc, cflag);
if (ret == 0 && oldsize < zc->zc_nvlist_dst_size) {
ret = -1;
@ -85,7 +85,7 @@ zcmd_ioctl(int fd, int request, zfs_cmd_t *zc)
return (ret);
}
#define ioctl(fd, cmd, zc) zcmd_ioctl((fd), (cmd), (zc))
#define ioctl(fd, ioc, zc) zcmd_ioctl((fd), (ioc), (zc))
#ifdef __cplusplus
}

View File

@ -33,6 +33,7 @@
#include <sys/nvpair.h>
#include <sys/dsl_deleg.h>
#include <sys/zfs_ioctl.h>
#include "zfs_namecheck.h"
#include "zfs_ioctl_compat.h"
static int zfs_version_ioctl = ZFS_IOCVER_CURRENT;
@ -49,8 +50,53 @@ zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag)
{
zfs_cmd_v15_t *zc_c;
zfs_cmd_v28_t *zc28_c;
zfs_cmd_deadman_t *zcdm_c;
switch (cflag) {
case ZFS_CMD_COMPAT_DEADMAN:
zcdm_c = (void *)addr;
/* zc */
strlcpy(zc->zc_name, zcdm_c->zc_name, MAXPATHLEN);
strlcpy(zc->zc_value, zcdm_c->zc_value, MAXPATHLEN * 2);
strlcpy(zc->zc_string, zcdm_c->zc_string, MAXPATHLEN);
strlcpy(zc->zc_top_ds, zcdm_c->zc_top_ds, MAXPATHLEN);
zc->zc_guid = zcdm_c->zc_guid;
zc->zc_nvlist_conf = zcdm_c->zc_nvlist_conf;
zc->zc_nvlist_conf_size = zcdm_c->zc_nvlist_conf_size;
zc->zc_nvlist_src = zcdm_c->zc_nvlist_src;
zc->zc_nvlist_src_size = zcdm_c->zc_nvlist_src_size;
zc->zc_nvlist_dst = zcdm_c->zc_nvlist_dst;
zc->zc_nvlist_dst_size = zcdm_c->zc_nvlist_dst_size;
zc->zc_cookie = zcdm_c->zc_cookie;
zc->zc_objset_type = zcdm_c->zc_objset_type;
zc->zc_perm_action = zcdm_c->zc_perm_action;
zc->zc_history = zcdm_c->zc_history;
zc->zc_history_len = zcdm_c->zc_history_len;
zc->zc_history_offset = zcdm_c->zc_history_offset;
zc->zc_obj = zcdm_c->zc_obj;
zc->zc_iflags = zcdm_c->zc_iflags;
zc->zc_share = zcdm_c->zc_share;
zc->zc_jailid = zcdm_c->zc_jailid;
zc->zc_objset_stats = zcdm_c->zc_objset_stats;
zc->zc_begin_record = zcdm_c->zc_begin_record;
zc->zc_defer_destroy = zcdm_c->zc_defer_destroy;
zc->zc_temphold = zcdm_c->zc_temphold;
zc->zc_action_handle = zcdm_c->zc_action_handle;
zc->zc_cleanup_fd = zcdm_c->zc_cleanup_fd;
zc->zc_simple = zcdm_c->zc_simple;
bcopy(zcdm_c->zc_pad, zc->zc_pad, sizeof(zc->zc_pad));
zc->zc_sendobj = zcdm_c->zc_sendobj;
zc->zc_fromobj = zcdm_c->zc_fromobj;
zc->zc_createtxg = zcdm_c->zc_createtxg;
zc->zc_stat = zcdm_c->zc_stat;
/* zc_inject_record doesn't change in libzfs_core */
zcdm_c->zc_inject_record = zc->zc_inject_record;
/* we always assume zc_nvlist_dst_filled is true */
zc->zc_nvlist_dst_filled = B_TRUE;
break;
case ZFS_CMD_COMPAT_V28:
zc28_c = (void *)addr;
@ -178,8 +224,51 @@ zfs_cmd_compat_put(zfs_cmd_t *zc, caddr_t addr, const int cflag)
{
zfs_cmd_v15_t *zc_c;
zfs_cmd_v28_t *zc28_c;
zfs_cmd_deadman_t *zcdm_c;
switch (cflag) {
case ZFS_CMD_COMPAT_DEADMAN:
zcdm_c = (void *)addr;
strlcpy(zcdm_c->zc_name, zc->zc_name, MAXPATHLEN);
strlcpy(zcdm_c->zc_value, zc->zc_value, MAXPATHLEN * 2);
strlcpy(zcdm_c->zc_string, zc->zc_string, MAXPATHLEN);
strlcpy(zcdm_c->zc_top_ds, zc->zc_top_ds, MAXPATHLEN);
zcdm_c->zc_guid = zc->zc_guid;
zcdm_c->zc_nvlist_conf = zc->zc_nvlist_conf;
zcdm_c->zc_nvlist_conf_size = zc->zc_nvlist_conf_size;
zcdm_c->zc_nvlist_src = zc->zc_nvlist_src;
zcdm_c->zc_nvlist_src_size = zc->zc_nvlist_src_size;
zcdm_c->zc_nvlist_dst = zc->zc_nvlist_dst;
zcdm_c->zc_nvlist_dst_size = zc->zc_nvlist_dst_size;
zcdm_c->zc_cookie = zc->zc_cookie;
zcdm_c->zc_objset_type = zc->zc_objset_type;
zcdm_c->zc_perm_action = zc->zc_perm_action;
zcdm_c->zc_history = zc->zc_history;
zcdm_c->zc_history_len = zc->zc_history_len;
zcdm_c->zc_history_offset = zc->zc_history_offset;
zcdm_c->zc_obj = zc->zc_obj;
zcdm_c->zc_iflags = zc->zc_iflags;
zcdm_c->zc_share = zc->zc_share;
zcdm_c->zc_jailid = zc->zc_jailid;
zcdm_c->zc_objset_stats = zc->zc_objset_stats;
zcdm_c->zc_begin_record = zc->zc_begin_record;
zcdm_c->zc_defer_destroy = zc->zc_defer_destroy;
zcdm_c->zc_temphold = zc->zc_temphold;
zcdm_c->zc_action_handle = zc->zc_action_handle;
zcdm_c->zc_cleanup_fd = zc->zc_cleanup_fd;
zcdm_c->zc_simple = zc->zc_simple;
bcopy(zc->zc_pad, zcdm_c->zc_pad, sizeof(zcdm_c->zc_pad));
zcdm_c->zc_sendobj = zc->zc_sendobj;
zcdm_c->zc_fromobj = zc->zc_fromobj;
zcdm_c->zc_createtxg = zc->zc_createtxg;
zcdm_c->zc_stat = zc->zc_stat;
/* zc_inject_record doesn't change in libzfs_core */
zc->zc_inject_record = zcdm_c->zc_inject_record;
break;
case ZFS_CMD_COMPAT_V28:
zc28_c = (void *)addr;
@ -476,7 +565,7 @@ zfs_ioctl_compat_pool_get_props(zfs_cmd_t *zc)
#ifndef _KERNEL
int
zcmd_ioctl_compat(int fd, unsigned long cmd, zfs_cmd_t *zc, const int cflag)
zcmd_ioctl_compat(int fd, int request, zfs_cmd_t *zc, const int cflag)
{
int nc, ret;
void *zc_c;
@ -484,16 +573,21 @@ zcmd_ioctl_compat(int fd, unsigned long cmd, zfs_cmd_t *zc, const int cflag)
switch (cflag) {
case ZFS_CMD_COMPAT_NONE:
ret = ioctl(fd, cmd, zc);
ncmd = _IOWR('Z', request, struct zfs_cmd);
ret = ioctl(fd, ncmd, zc);
return (ret);
case ZFS_CMD_COMPAT_DEADMAN:
zc_c = malloc(sizeof(zfs_cmd_deadman_t));
ncmd = _IOWR('Z', request, struct zfs_cmd_deadman);
break;
case ZFS_CMD_COMPAT_V28:
zc_c = malloc(sizeof(zfs_cmd_v28_t));
ncmd = _IOWR('Z', ZFS_IOCREQ(cmd), struct zfs_cmd_v28);
ncmd = _IOWR('Z', request, struct zfs_cmd_v28);
break;
case ZFS_CMD_COMPAT_V15:
nc = zfs_ioctl_v28_to_v15[ZFS_IOCREQ(cmd)];
nc = zfs_ioctl_v28_to_v15[request];
zc_c = malloc(sizeof(zfs_cmd_v15_t));
ncmd = _IOWR('Z', nc, struct zfs_cmd_v15);
ncmd = _IOWR('Z', request, struct zfs_cmd_v15);
break;
default:
return (EINVAL);
@ -505,18 +599,18 @@ zcmd_ioctl_compat(int fd, unsigned long cmd, zfs_cmd_t *zc, const int cflag)
zfs_cmd_compat_put(zc, (caddr_t)zc_c, cflag);
ret = ioctl(fd, ncmd, zc_c);
if (cflag == ZFS_CMD_COMPAT_V15 &&
nc == 2 /* ZFS_IOC_POOL_IMPORT */)
ret = ioctl(fd, _IOWR('Z', 4 /* ZFS_IOC_POOL_CONFIGS */,
nc == ZFS_IOC_POOL_IMPORT)
ret = ioctl(fd, _IOWR('Z', ZFS_IOC_POOL_CONFIGS,
struct zfs_cmd_v15), zc_c);
zfs_cmd_compat_get(zc, (caddr_t)zc_c, cflag);
free(zc_c);
if (cflag == ZFS_CMD_COMPAT_V15) {
switch (nc) {
case 2: /* ZFS_IOC_POOL_IMPORT */
case 4: /* ZFS_IOC_POOL_CONFIGS */
case 5: /* ZFS_IOC_POOL_STATS */
case 6: /* ZFS_IOC_POOL_TRYIMPORT */
case ZFS_IOC_POOL_IMPORT:
case ZFS_IOC_POOL_CONFIGS:
case ZFS_IOC_POOL_STATS:
case ZFS_IOC_POOL_TRYIMPORT:
zfs_ioctl_compat_fix_stats(zc, nc);
break;
case 41: /* ZFS_IOC_POOL_GET_PROPS (v15) */
@ -528,16 +622,25 @@ zcmd_ioctl_compat(int fd, unsigned long cmd, zfs_cmd_t *zc, const int cflag)
return (ret);
}
#else /* _KERNEL */
void
int
zfs_ioctl_compat_pre(zfs_cmd_t *zc, int *vec, const int cflag)
{
if (cflag == ZFS_CMD_COMPAT_V15)
int error = 0;
/* are we creating a clone? */
if (*vec == ZFS_IOC_CREATE && zc->zc_value[0] != '\0')
*vec = ZFS_IOC_CLONE;
if (cflag == ZFS_CMD_COMPAT_V15) {
switch (*vec) {
case 7: /* ZFS_IOC_POOL_SCRUB (v15) */
zc->zc_cookie = POOL_SCAN_SCRUB;
break;
}
}
return (error);
}
void
@ -545,9 +648,9 @@ zfs_ioctl_compat_post(zfs_cmd_t *zc, int vec, const int cflag)
{
if (cflag == ZFS_CMD_COMPAT_V15) {
switch (vec) {
case 4: /* ZFS_IOC_POOL_CONFIGS */
case 5: /* ZFS_IOC_POOL_STATS */
case 6: /* ZFS_IOC_POOL_TRYIMPORT */
case ZFS_IOC_POOL_CONFIGS:
case ZFS_IOC_POOL_STATS:
case ZFS_IOC_POOL_TRYIMPORT:
zfs_ioctl_compat_fix_stats(zc, vec);
break;
case 41: /* ZFS_IOC_POOL_GET_PROPS (v15) */
@ -556,4 +659,127 @@ zfs_ioctl_compat_post(zfs_cmd_t *zc, int vec, const int cflag)
}
}
}
nvlist_t *
zfs_ioctl_compat_innvl(zfs_cmd_t *zc, nvlist_t * innvl, const int vec,
const int cflag)
{
nvlist_t *nvl, *tmpnvl;
char *poolname, *snapname;
int err;
if (cflag == ZFS_CMD_COMPAT_NONE)
goto out;
switch (vec) {
case ZFS_IOC_CREATE:
nvl = fnvlist_alloc();
fnvlist_add_int32(nvl, "type", zc->zc_objset_type);
if (innvl != NULL) {
fnvlist_add_nvlist(nvl, "props", innvl);
nvlist_free(innvl);
}
return (nvl);
break;
case ZFS_IOC_CLONE:
nvl = fnvlist_alloc();
fnvlist_add_string(nvl, "origin", zc->zc_value);
if (innvl != NULL) {
fnvlist_add_nvlist(nvl, "props", innvl);
nvlist_free(innvl);
}
return (nvl);
break;
case ZFS_IOC_SNAPSHOT:
if (innvl == NULL)
goto out;
nvl = fnvlist_alloc();
fnvlist_add_nvlist(nvl, "props", innvl);
tmpnvl = fnvlist_alloc();
snapname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value);
fnvlist_add_boolean(tmpnvl, snapname);
kmem_free(snapname, strlen(snapname + 1));
/* check if we are doing a recursive snapshot */
if (zc->zc_cookie)
dmu_get_recursive_snaps_nvl(zc->zc_name, zc->zc_value,
tmpnvl);
fnvlist_add_nvlist(nvl, "snaps", tmpnvl);
fnvlist_free(tmpnvl);
nvlist_free(innvl);
/* strip dataset part from zc->zc_name */
zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
return (nvl);
break;
case ZFS_IOC_SPACE_SNAPS:
nvl = fnvlist_alloc();
fnvlist_add_string(nvl, "firstsnap", zc->zc_value);
if (innvl != NULL)
nvlist_free(innvl);
return (nvl);
break;
case ZFS_IOC_DESTROY_SNAPS:
if (innvl == NULL && cflag == ZFS_CMD_COMPAT_DEADMAN)
goto out;
nvl = fnvlist_alloc();
if (innvl != NULL) {
fnvlist_add_nvlist(nvl, "snaps", innvl);
} else {
/*
* We are probably called by even older binaries,
* allocate and populate nvlist with recursive
* snapshots
*/
if (snapshot_namecheck(zc->zc_value, NULL,
NULL) == 0) {
tmpnvl = fnvlist_alloc();
if (dmu_get_recursive_snaps_nvl(zc->zc_name,
zc->zc_value, tmpnvl) == 0)
fnvlist_add_nvlist(nvl, "snaps",
tmpnvl);
nvlist_free(tmpnvl);
}
}
if (innvl != NULL)
nvlist_free(innvl);
/* strip dataset part from zc->zc_name */
zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
return (nvl);
break;
}
out:
return (innvl);
}
nvlist_t *
zfs_ioctl_compat_outnvl(zfs_cmd_t *zc, nvlist_t * outnvl, const int vec,
const int cflag)
{
nvlist_t *tmpnvl;
if (cflag == ZFS_CMD_COMPAT_NONE)
return (outnvl);
switch (vec) {
case ZFS_IOC_SPACE_SNAPS:
(void) nvlist_lookup_uint64(outnvl, "used", &zc->zc_cookie);
(void) nvlist_lookup_uint64(outnvl, "compressed",
&zc->zc_objset_type);
(void) nvlist_lookup_uint64(outnvl, "uncompressed",
&zc->zc_perm_action);
nvlist_free(outnvl);
/* return empty outnvl */
tmpnvl = fnvlist_alloc();
return (tmpnvl);
break;
case ZFS_IOC_CREATE:
case ZFS_IOC_CLONE:
nvlist_free(outnvl);
/* return empty outnvl */
tmpnvl = fnvlist_alloc();
return (tmpnvl);
break;
}
return (outnvl);
}
#endif /* KERNEL */

View File

@ -45,13 +45,15 @@ extern "C" {
*/
/* ioctl versions for vfs.zfs.version.ioctl */
#define ZFS_IOCVER_LZC 2
#define ZFS_IOCVER_DEADMAN 1
#define ZFS_IOCVER_CURRENT ZFS_IOCVER_DEADMAN
#define ZFS_IOCVER_CURRENT ZFS_IOCVER_LZC
/* 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_IOC_COMPAT_PASS 254
#define ZFS_IOC_COMPAT_FAIL 255
@ -150,6 +152,44 @@ typedef struct zfs_cmd_v28 {
zfs_stat_t zc_stat;
} zfs_cmd_v28_t;
typedef struct zfs_cmd_deadman {
char zc_name[MAXPATHLEN];
char zc_value[MAXPATHLEN * 2];
char zc_string[MAXNAMELEN];
char zc_top_ds[MAXPATHLEN];
uint64_t zc_guid;
uint64_t zc_nvlist_conf; /* really (char *) */
uint64_t zc_nvlist_conf_size;
uint64_t zc_nvlist_src; /* really (char *) */
uint64_t zc_nvlist_src_size;
uint64_t zc_nvlist_dst; /* really (char *) */
uint64_t zc_nvlist_dst_size;
uint64_t zc_cookie;
uint64_t zc_objset_type;
uint64_t zc_perm_action;
uint64_t zc_history; /* really (char *) */
uint64_t zc_history_len;
uint64_t zc_history_offset;
uint64_t zc_obj;
uint64_t zc_iflags; /* internal to zfs(7fs) */
zfs_share_t zc_share;
uint64_t zc_jailid;
dmu_objset_stats_t zc_objset_stats;
struct drr_begin zc_begin_record;
/* zc_inject_record doesn't change in libzfs_core */
zinject_record_t zc_inject_record;
boolean_t zc_defer_destroy;
boolean_t zc_temphold;
uint64_t zc_action_handle;
int zc_cleanup_fd;
uint8_t zc_simple;
uint8_t zc_pad[3]; /* alignment */
uint64_t zc_sendobj;
uint64_t zc_fromobj;
uint64_t zc_createtxg;
zfs_stat_t zc_stat;
} zfs_cmd_deadman_t;
#ifdef _KERNEL
unsigned static long zfs_ioctl_v15_to_v28[] = {
0, /* 0 ZFS_IOC_POOL_CREATE */
@ -274,10 +314,14 @@ unsigned static long zfs_ioctl_v28_to_v15[] = {
#endif /* ! _KERNEL */
#ifdef _KERNEL
void zfs_ioctl_compat_pre(zfs_cmd_t *, int *, const int);
int zfs_ioctl_compat_pre(zfs_cmd_t *, int *, const int);
void zfs_ioctl_compat_post(zfs_cmd_t *, const int, const int);
nvlist_t *zfs_ioctl_compat_innvl(zfs_cmd_t *, nvlist_t *, const int,
const int);
nvlist_t *zfs_ioctl_compat_outnvl(zfs_cmd_t *, nvlist_t *, const int,
const int);
#else
int zcmd_ioctl_compat(int, unsigned long, zfs_cmd_t *, const int);
int zcmd_ioctl_compat(int, int, zfs_cmd_t *, const int);
#endif /* _KERNEL */
void zfs_cmd_compat_get(zfs_cmd_t *, caddr_t, const int);
void zfs_cmd_compat_put(zfs_cmd_t *, caddr_t, const int);

View File

@ -5805,7 +5805,7 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
#ifdef illumos
minor_t minor = getminor(dev);
#else
int cflag, cmd;
int cflag, cmd, oldvecnum;
cred_t *cr = td->td_ucred;
#endif
const zfs_ioc_vec_t *vec;
@ -5821,7 +5821,10 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
* and translate zfs_cmd if necessary
*/
if (len < sizeof(zfs_cmd_t))
if (len == sizeof(zfs_cmd_v28_t)) {
if (len == sizeof(zfs_cmd_deadman_t)) {
cflag = ZFS_CMD_COMPAT_DEADMAN;
vecnum = cmd;
} else if (len == sizeof(zfs_cmd_v28_t)) {
cflag = ZFS_CMD_COMPAT_V28;
vecnum = cmd;
} else if (len == sizeof(zfs_cmd_v15_t)) {
@ -5870,7 +5873,12 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
zc = kmem_zalloc(sizeof(zfs_cmd_t), KM_SLEEP);
bzero(zc, sizeof(zfs_cmd_t));
zfs_cmd_compat_get(zc, arg, cflag);
zfs_ioctl_compat_pre(zc, &vecnum, cflag);
oldvecnum = vecnum;
error = zfs_ioctl_compat_pre(zc, &vecnum, cflag);
if (error != 0)
goto out;
if (oldvecnum != vecnum)
vec = &zfs_ioc_vec[vecnum];
} else
zc = (void *)arg;
@ -5882,6 +5890,10 @@ zfsdev_ioctl(struct cdev *dev, u_long zcmd, caddr_t arg, int flag,
goto out;
}
/* rewrite innvl for backwards compatibility */
if (cflag != ZFS_CMD_COMPAT_NONE)
innvl = zfs_ioctl_compat_innvl(zc, innvl, vecnum, cflag);
/*
* Ensure that all pool/dataset names are valid before we pass down to
* the lower layers.
@ -5955,6 +5967,11 @@ 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)
outnvl = zfs_ioctl_compat_outnvl(zc, outnvl, vecnum,
cflag);
if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) {
int smusherror = 0;
if (vec->zvec_smush_outnvlist) {