/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2013 Xin Li . All rights reserved. * Copyright 2013 Martin Matuska . All rights reserved. * Portions Copyright 2005, 2010, Oracle and/or its affiliates. * All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include "zfs_namecheck.h" #include /* * FreeBSD zfs_cmd compatibility with older binaries * appropriately remap/extend the zfs_cmd_t structure */ void zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag) { } #if 0 static int zfs_ioctl_compat_get_nvlist(uint64_t nvl, size_t size, int iflag, nvlist_t **nvp) { char *packed; int error; nvlist_t *list = NULL; /* * Read in and unpack the user-supplied nvlist. */ if (size == 0) return (EINVAL); #ifdef _KERNEL packed = kmem_alloc(size, KM_SLEEP); if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size, iflag)) != 0) { kmem_free(packed, size); return (error); } #else packed = (void *)(uintptr_t)nvl; #endif error = nvlist_unpack(packed, size, &list, 0); #ifdef _KERNEL kmem_free(packed, size); #endif if (error != 0) return (error); *nvp = list; return (0); } static int zfs_ioctl_compat_put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl) { char *packed = NULL; int error = 0; size_t size; VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0); #ifdef _KERNEL packed = kmem_alloc(size, KM_SLEEP); VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE, KM_SLEEP) == 0); if (ddi_copyout(packed, (void *)(uintptr_t)zc->zc_nvlist_dst, size, zc->zc_iflags) != 0) error = EFAULT; kmem_free(packed, size); #else packed = (void *)(uintptr_t)zc->zc_nvlist_dst; VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE, 0) == 0); #endif zc->zc_nvlist_dst_size = size; return (error); } static void zfs_ioctl_compat_fix_stats_nvlist(nvlist_t *nvl) { nvlist_t **child; nvlist_t *nvroot = NULL; vdev_stat_t *vs; uint_t c, children, nelem; if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN, &child, &children) == 0) { for (c = 0; c < children; c++) { zfs_ioctl_compat_fix_stats_nvlist(child[c]); } } if (nvlist_lookup_nvlist(nvl, ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0) zfs_ioctl_compat_fix_stats_nvlist(nvroot); if ((nvlist_lookup_uint64_array(nvl, "stats", (uint64_t **)&vs, &nelem) == 0)) { nvlist_add_uint64_array(nvl, ZPOOL_CONFIG_VDEV_STATS, (uint64_t *)vs, nelem); nvlist_remove(nvl, "stats", DATA_TYPE_UINT64_ARRAY); } } static int zfs_ioctl_compat_fix_stats(zfs_cmd_t *zc, const int nc) { nvlist_t *nv, *nvp = NULL; nvpair_t *elem; int error; if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst, zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0) return (error); if (nc == 5) { /* ZFS_IOC_POOL_STATS */ elem = NULL; while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) { if (nvpair_value_nvlist(elem, &nvp) == 0) zfs_ioctl_compat_fix_stats_nvlist(nvp); } elem = NULL; } else zfs_ioctl_compat_fix_stats_nvlist(nv); error = zfs_ioctl_compat_put_nvlist(zc, nv); nvlist_free(nv); return (error); } static int zfs_ioctl_compat_pool_get_props(zfs_cmd_t *zc) { nvlist_t *nv, *nva = NULL; int error; if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst, zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0) return (error); if (nvlist_lookup_nvlist(nv, "used", &nva) == 0) { nvlist_add_nvlist(nv, "allocated", nva); nvlist_remove(nv, "used", DATA_TYPE_NVLIST); } if (nvlist_lookup_nvlist(nv, "available", &nva) == 0) { nvlist_add_nvlist(nv, "free", nva); nvlist_remove(nv, "available", DATA_TYPE_NVLIST); } error = zfs_ioctl_compat_put_nvlist(zc, nv); nvlist_free(nv); return (error); } #endif #ifdef _KERNEL int zfs_ioctl_compat_pre(zfs_cmd_t *zc, int *vec, const int cflag) { 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 zfs_ioctl_compat_post(zfs_cmd_t *zc, int vec, const int cflag) { if (cflag == ZFS_CMD_COMPAT_V15) { switch (vec) { 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) */ zfs_ioctl_compat_pool_get_props(zc); break; } } } nvlist_t * zfs_ioctl_compat_innvl(zfs_cmd_t *zc, nvlist_t *innvl, const int vec, const int cflag) { nvlist_t *nvl, *tmpnvl, *hnvl; nvpair_t *elem; char *poolname, *snapname; int err; if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC || cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP || cflag == ZFS_CMD_COMPAT_RESUME || cflag == ZFS_CMD_COMPAT_INLANES) 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 (zfs_component_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; case ZFS_IOC_HOLD: nvl = fnvlist_alloc(); tmpnvl = fnvlist_alloc(); if (zc->zc_cleanup_fd != -1) fnvlist_add_int32(nvl, "cleanup_fd", (int32_t)zc->zc_cleanup_fd); if (zc->zc_cookie) { hnvl = fnvlist_alloc(); if (dmu_get_recursive_snaps_nvl(zc->zc_name, zc->zc_value, hnvl) == 0) { elem = NULL; while ((elem = nvlist_next_nvpair(hnvl, elem)) != NULL) { nvlist_add_string(tmpnvl, nvpair_name(elem), zc->zc_string); } } nvlist_free(hnvl); } else { snapname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value); nvlist_add_string(tmpnvl, snapname, zc->zc_string); kmem_free(snapname, strlen(snapname + 1)); } fnvlist_add_nvlist(nvl, "holds", 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; case ZFS_IOC_RELEASE: nvl = fnvlist_alloc(); tmpnvl = fnvlist_alloc(); if (zc->zc_cookie) { hnvl = fnvlist_alloc(); if (dmu_get_recursive_snaps_nvl(zc->zc_name, zc->zc_value, hnvl) == 0) { elem = NULL; while ((elem = nvlist_next_nvpair(hnvl, elem)) != NULL) { fnvlist_add_boolean(tmpnvl, zc->zc_string); fnvlist_add_nvlist(nvl, nvpair_name(elem), tmpnvl); } } nvlist_free(hnvl); } else { snapname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value); fnvlist_add_boolean(tmpnvl, zc->zc_string); fnvlist_add_nvlist(nvl, snapname, tmpnvl); kmem_free(snapname, strlen(snapname + 1)); } 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 || cflag == ZFS_CMD_COMPAT_LZC || cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP || cflag == ZFS_CMD_COMPAT_RESUME || cflag == ZFS_CMD_COMPAT_INLANES) 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: case ZFS_IOC_HOLD: case ZFS_IOC_RELEASE: nvlist_free(outnvl); /* return empty outnvl */ tmpnvl = fnvlist_alloc(); return (tmpnvl); break; } return (outnvl); } #endif /* KERNEL */