mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-30 21:09:38 +03:00
9f0a21e641
Add the FreeBSD platform code to the OpenZFS repository. As of this commit the source can be compiled and tested on FreeBSD 11 and 12. Subsequent commits are now required to compile on FreeBSD and Linux. Additionally, they must pass the ZFS Test Suite on FreeBSD which is being run by the CI. As of this commit 1230 tests pass on FreeBSD and there are no unexpected failures. Reviewed-by: Sean Eric Fagan <sef@ixsystems.com> Reviewed-by: Jorgen Lundman <lundman@lundman.net> Reviewed-by: Richard Laager <rlaager@wiktel.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Co-authored-by: Ryan Moeller <ryan@iXsystems.com> Signed-off-by: Matt Macy <mmacy@FreeBSD.org> Signed-off-by: Ryan Moeller <ryan@iXsystems.com> Closes #898 Closes #8987
433 lines
10 KiB
C
433 lines
10 KiB
C
/*
|
|
* 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 <delphij@FreeBSD.org>. All rights reserved.
|
|
* Copyright 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
|
|
* Portions Copyright 2005, 2010, Oracle and/or its affiliates.
|
|
* All rights reserved.
|
|
* Use is subject to license terms.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/cred.h>
|
|
#include <sys/dmu.h>
|
|
#include <sys/zio.h>
|
|
#include <sys/nvpair.h>
|
|
#include <sys/dsl_deleg.h>
|
|
#include <sys/zfs_ioctl.h>
|
|
#include "zfs_namecheck.h"
|
|
#include <os/freebsd/zfs/sys/zfs_ioctl_compat.h>
|
|
|
|
/*
|
|
* 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 */
|