summaryrefslogtreecommitdiffstats
path: root/sys/cddl/compat/opensolaris/kern
diff options
context:
space:
mode:
authorpjd <pjd@FreeBSD.org>2007-04-06 01:09:06 +0000
committerpjd <pjd@FreeBSD.org>2007-04-06 01:09:06 +0000
commit3b005d330261f33318ca1ee3fef1940237fd788b (patch)
tree3061c8734d9ce560165e672836837a0f411a83c9 /sys/cddl/compat/opensolaris/kern
parent3be454b8211f48e634e6587f53807d3b5013e973 (diff)
downloadFreeBSD-src-3b005d330261f33318ca1ee3fef1940237fd788b.zip
FreeBSD-src-3b005d330261f33318ca1ee3fef1940237fd788b.tar.gz
Please welcome ZFS - The last word in file systems.
ZFS file system was ported from OpenSolaris operating system. The code in under CDDL license. I'd like to thank all SUN developers that created this great piece of software. Supported by: Wheel LTD (http://www.wheel.pl/) Supported by: The FreeBSD Foundation (http://www.freebsdfoundation.org/) Supported by: Sentex (http://www.sentex.net/)
Diffstat (limited to 'sys/cddl/compat/opensolaris/kern')
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c245
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_kobj.c142
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_kstat.c131
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_policy.c266
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_string.c102
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c270
-rw-r--r--sys/cddl/compat/opensolaris/kern/opensolaris_zone.c237
7 files changed, 1393 insertions, 0 deletions
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c b/sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c
new file mode 100644
index 0000000..2d6f3e5
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_kmem.c
@@ -0,0 +1,245 @@
+/*-
+ * Copyright (c) 2006-2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <vm/uma.h>
+#include <sys/kmem.h>
+#include <sys/debug.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/stack.h>
+
+#ifdef _KERNEL
+static MALLOC_DEFINE(M_SOLARIS, "solaris", "Solaris");
+#else
+#define malloc(size, type, flags) malloc(size)
+#define free(addr, type) free(addr)
+#endif
+
+#ifdef KMEM_DEBUG
+struct kmem_item {
+ struct stack stack;
+ LIST_ENTRY(kmem_item) next;
+};
+static LIST_HEAD(, kmem_item) kmem_items;
+static struct mtx kmem_items_mtx;
+MTX_SYSINIT(kmem_items_mtx, &kmem_items_mtx, "kmem_items", MTX_DEF);
+#endif /* KMEM_DEBUG */
+
+void *
+zfs_kmem_alloc(size_t size, int kmflags)
+{
+ void *p;
+#ifdef KMEM_DEBUG
+ struct kmem_item *i;
+
+ size += sizeof(struct kmem_item);
+#endif
+ p = malloc(size, M_SOLARIS, kmflags);
+#ifndef _KERNEL
+ if (kmflags & KM_SLEEP)
+ assert(p != NULL);
+#endif
+#ifdef KMEM_DEBUG
+ if (p != NULL) {
+ i = p;
+ p = (u_char *)p + sizeof(struct kmem_item);
+ stack_save(&i->stack);
+ mtx_lock(&kmem_items_mtx);
+ LIST_INSERT_HEAD(&kmem_items, i, next);
+ mtx_unlock(&kmem_items_mtx);
+ }
+#endif
+ return (p);
+}
+
+void *
+kmem_zalloc(size_t size, int kmflags)
+{
+ return (kmem_alloc(size, kmflags | M_ZERO));
+}
+
+void
+zfs_kmem_free(void *buf, size_t size __unused)
+{
+#ifdef KMEM_DEBUG
+ struct kmem_item *i;
+
+ buf = (u_char *)buf - sizeof(struct kmem_item);
+ mtx_lock(&kmem_items_mtx);
+ LIST_FOREACH(i, &kmem_items, next) {
+ if (i == buf)
+ break;
+ }
+ ASSERT(i != NULL);
+ LIST_REMOVE(i, next);
+ mtx_unlock(&kmem_items_mtx);
+#endif
+ free(buf, M_SOLARIS);
+}
+
+static int
+kmem_std_constructor(void *mem, int size __unused, void *private, int flags)
+{
+ struct kmem_cache *cache = private;
+
+ return (cache->kc_constructor(mem, cache->kc_private, flags));
+}
+
+static void
+kmem_std_destructor(void *mem, int size __unused, void *private)
+{
+ struct kmem_cache *cache = private;
+
+ cache->kc_destructor(mem, cache->kc_private);
+}
+
+kmem_cache_t *
+kmem_cache_create(char *name, size_t bufsize, size_t align,
+ int (*constructor)(void *, void *, int), void (*destructor)(void *, void *),
+ void (*reclaim)(void *) __unused, void *private, vmem_t *vmp, int cflags)
+{
+ kmem_cache_t *cache;
+
+ ASSERT(vmp == NULL);
+
+ cache = kmem_alloc(sizeof(*cache), KM_SLEEP);
+ strlcpy(cache->kc_name, name, sizeof(cache->kc_name));
+ cache->kc_constructor = constructor;
+ cache->kc_destructor = destructor;
+ cache->kc_private = private;
+#ifdef _KERNEL
+ cache->kc_zone = uma_zcreate(cache->kc_name, bufsize,
+ constructor != NULL ? kmem_std_constructor : NULL,
+ destructor != NULL ? kmem_std_destructor : NULL,
+ NULL, NULL, align > 0 ? align - 1 : 0, cflags);
+#else
+ cache->kc_size = bufsize;
+#endif
+
+ return (cache);
+}
+
+void
+kmem_cache_destroy(kmem_cache_t *cache)
+{
+ uma_zdestroy(cache->kc_zone);
+ kmem_free(cache, sizeof(*cache));
+}
+
+void *
+kmem_cache_alloc(kmem_cache_t *cache, int flags)
+{
+#ifdef _KERNEL
+ return (uma_zalloc_arg(cache->kc_zone, cache, flags));
+#else
+ void *p;
+
+ p = kmem_alloc(cache->kc_size, flags);
+ if (p != NULL) {
+ kmem_std_constructor(p, cache->kc_size, cache->kc_private,
+ flags);
+ }
+ return (p);
+#endif
+}
+
+void
+kmem_cache_free(kmem_cache_t *cache, void *buf)
+{
+#ifdef _KERNEL
+ uma_zfree_arg(cache->kc_zone, buf, cache);
+#else
+ kmem_std_destructor(buf, cache->kc_size, cache->kc_private);
+ kmem_free(buf, cache->kc_size);
+#endif
+}
+
+#ifdef _KERNEL
+extern void zone_drain(uma_zone_t zone);
+void
+kmem_cache_reap_now(kmem_cache_t *cache)
+{
+ zone_drain(cache->kc_zone);
+}
+
+void
+kmem_reap(void)
+{
+ uma_reclaim();
+}
+#else
+void
+kmem_cache_reap_now(kmem_cache_t *cache __unused)
+{
+}
+
+void
+kmem_reap(void)
+{
+}
+#endif
+
+int
+kmem_debugging(void)
+{
+ return (0);
+}
+
+void *
+calloc(size_t n, size_t s)
+{
+ return (kmem_zalloc(n * s, KM_NOSLEEP));
+}
+
+#ifdef KMEM_DEBUG
+static void
+kmem_show(void *dummy __unused)
+{
+ struct kmem_item *i;
+
+ mtx_lock(&kmem_items_mtx);
+ if (LIST_EMPTY(&kmem_items))
+ printf("KMEM_DEBUG: No leaked elements.\n");
+ else {
+ printf("KMEM_DEBUG: Leaked elements:\n\n");
+ LIST_FOREACH(i, &kmem_items, next) {
+ printf("address=%p\n", i);
+ stack_print(&i->stack);
+ printf("\n");
+ }
+ }
+ mtx_unlock(&kmem_items_mtx);
+}
+
+SYSUNINIT(sol_kmem, SI_SUB_DRIVERS, SI_ORDER_FIRST, kmem_show, NULL);
+#endif /* KMEM_DEBUG */
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_kobj.c b/sys/cddl/compat/opensolaris/kern/opensolaris_kobj.c
new file mode 100644
index 0000000..b205238
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_kobj.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <sys/fcntl.h>
+#include <sys/kobj.h>
+
+void
+kobj_free(void *address, size_t size)
+{
+
+ kmem_free(address, size);
+}
+
+void *
+kobj_alloc(size_t size, int flag)
+{
+
+ return (kmem_alloc(size, (flag & KM_NOWAIT) ? KM_NOSLEEP : KM_SLEEP));
+}
+
+void *
+kobj_zalloc(size_t size, int flag)
+{
+ void *p;
+
+ if ((p = kobj_alloc(size, flag)) != NULL)
+ bzero(p, size);
+ return (p);
+}
+
+struct _buf *
+kobj_open_file(const char *file)
+{
+ struct thread *td = curthread;
+ struct nameidata nd;
+ int error, flags;
+
+ if (td->td_proc->p_fd->fd_rdir == NULL)
+ td->td_proc->p_fd->fd_rdir = rootvnode;
+ if (td->td_proc->p_fd->fd_cdir == NULL)
+ td->td_proc->p_fd->fd_cdir = rootvnode;
+
+ flags = FREAD;
+ NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, file, td);
+ error = vn_open_cred(&nd, &flags, 0, td->td_ucred, -1);
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ if (error != 0)
+ return ((struct _buf *)-1);
+ /* We just unlock so we hold a reference. */
+ VOP_UNLOCK(nd.ni_vp, 0, td);
+ return ((struct _buf *)nd.ni_vp);
+}
+
+int
+kobj_get_filesize(struct _buf *file, uint64_t *size)
+{
+ struct vnode *vp = (struct vnode *)file;
+ struct thread *td = curthread;
+ struct vattr va;
+ int error;
+
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ error = VOP_GETATTR(vp, &va, td->td_ucred, td);
+ VOP_UNLOCK(vp, 0, td);
+ if (error == 0)
+ *size = (uint64_t)va.va_size;
+ return (error);
+}
+
+int
+kobj_read_file(struct _buf *file, char *buf, unsigned size, unsigned off)
+{
+ struct vnode *vp = (struct vnode *)file;
+ struct thread *td = curthread;
+ struct uio auio;
+ struct iovec aiov;
+ int error;
+
+ bzero(&aiov, sizeof(aiov));
+ bzero(&auio, sizeof(auio));
+
+ aiov.iov_base = buf;
+ aiov.iov_len = size;
+
+ auio.uio_iov = &aiov;
+ auio.uio_offset = (off_t)off;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = size;
+ auio.uio_td = td;
+
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ error = VOP_READ(vp, &auio, IO_UNIT | IO_SYNC, td->td_ucred);
+ VOP_UNLOCK(vp, 0, td);
+ return (error != 0 ? -1 : size - auio.uio_resid);
+}
+
+void
+kobj_close_file(struct _buf *file)
+{
+ struct vnode *vp = (struct vnode *)file;
+ struct thread *td = curthread;
+ int flags;
+
+ flags = FREAD;
+ vn_close(vp, flags, td->td_ucred, td);
+}
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_kstat.c b/sys/cddl/compat/opensolaris/kern/opensolaris_kstat.c
new file mode 100644
index 0000000..673de14
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_kstat.c
@@ -0,0 +1,131 @@
+/*-
+ * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/sysctl.h>
+#include <sys/kstat.h>
+
+static MALLOC_DEFINE(M_KSTAT, "kstat_data", "Kernel statistics");
+
+SYSCTL_NODE(, OID_AUTO, kstat, CTLFLAG_RW, 0, "Kernel statistics");
+
+kstat_t *
+kstat_create(char *module, int instance, char *name, char *class, uchar_t type,
+ ulong_t ndata, uchar_t flags)
+{
+ struct sysctl_oid *root;
+ kstat_t *ksp;
+
+ KASSERT(instance == 0, ("instance=%d", instance));
+ KASSERT(type == KSTAT_TYPE_NAMED, ("type=%hhu", type));
+ KASSERT(flags == KSTAT_FLAG_VIRTUAL, ("flags=%02hhx", flags));
+
+ /*
+ * Allocate the main structure. We don't need to copy module/class/name
+ * stuff in here, because it is only used for sysctl node creation
+ * done in this function.
+ */
+ ksp = malloc(sizeof(*ksp), M_KSTAT, M_WAITOK);
+ ksp->ks_ndata = ndata;
+
+ /*
+ * Create sysctl tree for those statistics:
+ *
+ * kstat.<module>.<class>.<name>.
+ */
+ sysctl_ctx_init(&ksp->ks_sysctl_ctx);
+ root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx,
+ SYSCTL_STATIC_CHILDREN(_kstat), OID_AUTO, module, CTLFLAG_RW, 0,
+ "");
+ if (root == NULL) {
+ printf("%s: Cannot create kstat.%s tree!\n", __func__, module);
+ sysctl_ctx_free(&ksp->ks_sysctl_ctx);
+ free(ksp, M_KSTAT);
+ return (NULL);
+ }
+ root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(root),
+ OID_AUTO, class, CTLFLAG_RW, 0, "");
+ if (root == NULL) {
+ printf("%s: Cannot create kstat.%s.%s tree!\n", __func__,
+ module, class);
+ sysctl_ctx_free(&ksp->ks_sysctl_ctx);
+ free(ksp, M_KSTAT);
+ return (NULL);
+ }
+ root = SYSCTL_ADD_NODE(&ksp->ks_sysctl_ctx, SYSCTL_CHILDREN(root),
+ OID_AUTO, name, CTLFLAG_RW, 0, "");
+ if (root == NULL) {
+ printf("%s: Cannot create kstat.%s.%s.%s tree!\n", __func__,
+ module, class, name);
+ sysctl_ctx_free(&ksp->ks_sysctl_ctx);
+ free(ksp, M_KSTAT);
+ return (NULL);
+ }
+ ksp->ks_sysctl_root = root;
+
+ return (ksp);
+}
+
+static int
+kstat_sysctl(SYSCTL_HANDLER_ARGS)
+{
+ kstat_named_t *ksent = arg1;
+ uint64_t val;
+
+ val = ksent->value.ui64;
+ return sysctl_handle_int(oidp, &val, sizeof(val), req);
+}
+
+void
+kstat_install(kstat_t *ksp)
+{
+ kstat_named_t *ksent;
+ u_int i;
+
+ ksent = ksp->ks_data;
+ for (i = 0; i < ksp->ks_ndata; i++, ksent++) {
+ KASSERT(ksent->data_type == KSTAT_DATA_UINT64,
+ ("data_type=%d", ksent->data_type));
+ SYSCTL_ADD_PROC(&ksp->ks_sysctl_ctx,
+ SYSCTL_CHILDREN(ksp->ks_sysctl_root), OID_AUTO, ksent->name,
+ CTLTYPE_QUAD | CTLFLAG_RD, ksent, sizeof(*ksent),
+ kstat_sysctl, "IU", "");
+ }
+}
+
+void
+kstat_delete(kstat_t *ksp)
+{
+
+ sysctl_ctx_free(&ksp->ks_sysctl_ctx);
+ free(ksp, M_KSTAT);
+}
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_policy.c b/sys/cddl/compat/opensolaris/kern/opensolaris_policy.c
new file mode 100644
index 0000000..c109a4c
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_policy.c
@@ -0,0 +1,266 @@
+/*-
+ * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/policy.h>
+
+int
+secpolicy_zfs(struct ucred *cred)
+{
+
+ return (priv_check_cred(cred, PRIV_VFS_MOUNT, 0));
+}
+
+int
+secpolicy_sys_config(struct ucred *cred, int checkonly __unused)
+{
+
+ return (priv_check_cred(cred, PRIV_ZFS_POOL_CONFIG, 0));
+}
+
+int
+secpolicy_zinject(struct ucred *cred)
+{
+
+ return (priv_check_cred(cred, PRIV_ZFS_INJECT, 0));
+}
+
+int
+secpolicy_fs_unmount(struct ucred *cred, struct mount *vfsp __unused)
+{
+
+ return (priv_check_cred(cred, PRIV_VFS_UNMOUNT, 0));
+}
+
+/*
+ * This check is done in kern_link(), so we could just return 0 here.
+ */
+extern int hardlink_check_uid;
+int
+secpolicy_basic_link(struct ucred *cred)
+{
+
+ if (!hardlink_check_uid)
+ return (0);
+ return (priv_check_cred(cred, PRIV_VFS_LINK, SUSER_ALLOWJAIL));
+}
+
+int
+secpolicy_vnode_stky_modify(struct ucred *cred)
+{
+
+ return (EPERM);
+}
+
+int
+secpolicy_vnode_remove(struct ucred *cred)
+{
+
+ return (priv_check_cred(cred, PRIV_VFS_ADMIN, SUSER_ALLOWJAIL));
+}
+
+int
+secpolicy_vnode_access(struct ucred *cred, struct vnode *vp, uint64_t owner,
+ int mode)
+{
+
+ if ((mode & VREAD) &&
+ priv_check_cred(cred, PRIV_VFS_READ, SUSER_ALLOWJAIL) != 0) {
+ return (EACCES);
+ }
+ if ((mode & VWRITE) &&
+ priv_check_cred(cred, PRIV_VFS_WRITE, SUSER_ALLOWJAIL) != 0) {
+ return (EACCES);
+ }
+ if (mode & VEXEC) {
+ if (vp->v_type == VDIR) {
+ if (priv_check_cred(cred, PRIV_VFS_LOOKUP,
+ SUSER_ALLOWJAIL) != 0) {
+ return (EACCES);
+ }
+ } else {
+ if (priv_check_cred(cred, PRIV_VFS_EXEC,
+ SUSER_ALLOWJAIL) != 0) {
+ return (EACCES);
+ }
+ }
+ }
+ return (0);
+}
+
+int
+secpolicy_vnode_setdac(struct ucred *cred, uid_t owner)
+{
+
+ if (owner == cred->cr_uid)
+ return (0);
+ return (priv_check_cred(cred, PRIV_VFS_ADMIN, SUSER_ALLOWJAIL));
+}
+
+int
+secpolicy_vnode_setattr(struct ucred *cred, struct vnode *vp, struct vattr *vap,
+ const struct vattr *ovap, int flags,
+ int unlocked_access(void *, int, struct ucred *), void *node)
+{
+ int mask = vap->va_mask;
+ int error;
+
+ if (mask & AT_SIZE) {
+ if (vp->v_type == VDIR)
+ return (EISDIR);
+ error = unlocked_access(node, VWRITE, cred);
+ if (error)
+ return (error);
+ }
+ if (mask & AT_MODE) {
+ /*
+ * If not the owner of the file then check privilege
+ * for two things: the privilege to set the mode at all
+ * and, if we're setting setuid, we also need permissions
+ * to add the set-uid bit, if we're not the owner.
+ * In the specific case of creating a set-uid root
+ * file, we need even more permissions.
+ */
+ error = secpolicy_vnode_setdac(cred, ovap->va_uid);
+ if (error)
+ return (error);
+ error = secpolicy_setid_setsticky_clear(vp, vap, ovap, cred);
+ if (error)
+ return (error);
+ } else {
+ vap->va_mode = ovap->va_mode;
+ }
+ if (mask & (AT_UID | AT_GID)) {
+ error = secpolicy_vnode_setdac(cred, ovap->va_uid);
+ if (error)
+ return (error);
+
+ /*
+ * To change the owner of a file, or change the group of a file to a
+ * group of which we are not a member, the caller must have
+ * privilege.
+ */
+ if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
+ ((mask & AT_GID) && vap->va_gid != ovap->va_gid &&
+ !groupmember(vap->va_gid, cred))) {
+ error = priv_check_cred(cred, PRIV_VFS_CHOWN,
+ SUSER_ALLOWJAIL);
+ if (error)
+ return (error);
+ }
+
+ if (((mask & AT_UID) && vap->va_uid != ovap->va_uid) ||
+ ((mask & AT_GID) && vap->va_gid != ovap->va_gid)) {
+ secpolicy_setid_clear(vap, cred);
+ }
+ }
+ if (mask & (AT_ATIME | AT_MTIME)) {
+ /*
+ * From utimes(2):
+ * If times is NULL, ... The caller must be the owner of
+ * the file, have permission to write the file, or be the
+ * super-user.
+ * If times is non-NULL, ... The caller must be the owner of
+ * the file or be the super-user.
+ */
+ error = secpolicy_vnode_setdac(cred, ovap->va_uid);
+ if (error && (vap->va_vaflags & VA_UTIMES_NULL))
+ error = unlocked_access(node, VWRITE, cred);
+ if (error)
+ return (error);
+ }
+ return (0);
+}
+
+int
+secpolicy_vnode_create_gid(struct ucred *cred)
+{
+
+ return (EPERM);
+}
+
+int
+secpolicy_vnode_setids_setgids(struct ucred *cred, gid_t gid)
+{
+
+ if (!groupmember(gid, cred))
+ return (priv_check_cred(cred, PRIV_VFS_SETGID, SUSER_ALLOWJAIL));
+ return (0);
+}
+
+int
+secpolicy_vnode_setid_retain(struct ucred *cred, boolean_t issuidroot __unused)
+{
+
+ return (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, SUSER_ALLOWJAIL));
+}
+
+void
+secpolicy_setid_clear(struct vattr *vap, struct ucred *cred)
+{
+
+ if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0) {
+ if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID,
+ SUSER_ALLOWJAIL)) {
+ vap->va_mask |= AT_MODE;
+ vap->va_mode &= ~(S_ISUID|S_ISGID);
+ }
+ }
+}
+
+int
+secpolicy_setid_setsticky_clear(struct vnode *vp, struct vattr *vap,
+ const struct vattr *ovap, struct ucred *cred)
+{
+ int error;
+
+ /*
+ * Privileged processes may set the sticky bit on non-directories,
+ * as well as set the setgid bit on a file with a group that the process
+ * is not a member of. Both of these are allowed in jail(8).
+ */
+ if (vp->v_type != VDIR && (vap->va_mode & S_ISTXT)) {
+ if (priv_check_cred(cred, PRIV_VFS_STICKYFILE, SUSER_ALLOWJAIL))
+ return (EFTYPE);
+ }
+ /*
+ * Check for privilege if attempting to set the
+ * group-id bit.
+ */
+ if ((vap->va_mode & S_ISGID) != 0) {
+ error = secpolicy_vnode_setids_setgids(cred, ovap->va_gid);
+ if (error)
+ return (error);
+ }
+ return (0);
+}
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_string.c b/sys/cddl/compat/opensolaris/kern/opensolaris_string.c
new file mode 100644
index 0000000..cae984f
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_string.c
@@ -0,0 +1,102 @@
+/*
+ * 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 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/param.h>
+#include <sys/string.h>
+
+#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
+
+#define IS_ALPHA(c) \
+ (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
+
+char *
+strchr(const char *s, int c)
+{
+ char ch;
+
+ ch = c;
+ for (;; ++s) {
+ if (*s == ch)
+ return ((char *)s);
+ if (*s == '\0')
+ return (NULL);
+ }
+ /* NOTREACHED */
+}
+
+char *
+strrchr(const char *s, int c)
+{
+ char *save;
+ char ch;
+
+ ch = c;
+ for (save = NULL;; ++s) {
+ if (*s == ch)
+ save = (char *)s;
+ if (*s == '\0')
+ return (save);
+ }
+ /* NOTREACHED */
+}
+
+char *
+strpbrk(const char *s, const char *b)
+{
+ const char *p;
+
+ do {
+ for (p = b; *p != '\0' && *p != *s; ++p)
+ ;
+ if (*p != '\0')
+ return ((char *)s);
+ } while (*s++);
+
+ return (NULL);
+}
+
+/*
+ * Convert a string into a valid C identifier by replacing invalid
+ * characters with '_'. Also makes sure the string is nul-terminated
+ * and takes up at most n bytes.
+ */
+void
+strident_canon(char *s, size_t n)
+{
+ char c;
+ char *end = s + n - 1;
+
+ if ((c = *s) == 0)
+ return;
+
+ if (!IS_ALPHA(c) && c != '_')
+ *s = '_';
+
+ while (s < end && ((c = *(++s)) != 0)) {
+ if (!IS_ALPHA(c) && !IS_DIGIT(c) && c != '_')
+ *s = '_';
+ }
+ *s = 0;
+}
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c b/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c
new file mode 100644
index 0000000..074a24b
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_vfs.c
@@ -0,0 +1,270 @@
+/*-
+ * Copyright (c) 2006-2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/cred.h>
+#include <sys/vfs.h>
+#include <sys/priv.h>
+#include <sys/libkern.h>
+
+MALLOC_DECLARE(M_MOUNT);
+
+TAILQ_HEAD(vfsoptlist, vfsopt);
+struct vfsopt {
+ TAILQ_ENTRY(vfsopt) link;
+ char *name;
+ void *value;
+ int len;
+};
+
+void
+vfs_setmntopt(vfs_t *vfsp, const char *name, const char *arg,
+ int flags __unused)
+{
+ struct vfsopt *opt;
+ size_t namesize;
+
+ if (vfsp->mnt_opt == NULL) {
+ vfsp->mnt_opt = malloc(sizeof(*vfsp->mnt_opt), M_MOUNT, M_WAITOK);
+ TAILQ_INIT(vfsp->mnt_opt);
+ }
+
+ opt = malloc(sizeof(*opt), M_MOUNT, M_WAITOK);
+
+ namesize = strlen(name) + 1;
+ opt->name = malloc(namesize, M_MOUNT, M_WAITOK);
+ strlcpy(opt->name, name, namesize);
+
+ if (arg == NULL) {
+ opt->value = NULL;
+ opt->len = 0;
+ } else {
+ opt->len = strlen(arg) + 1;
+ opt->value = malloc(opt->len, M_MOUNT, M_WAITOK);
+ bcopy(arg, opt->value, opt->len);
+ }
+ /* TODO: Locking. */
+ TAILQ_INSERT_TAIL(vfsp->mnt_opt, opt, link);
+}
+
+void
+vfs_clearmntopt(vfs_t *vfsp, const char *name)
+{
+ struct vfsopt *opt;
+
+ if (vfsp->mnt_opt == NULL)
+ return;
+ /* TODO: Locking. */
+ TAILQ_FOREACH(opt, vfsp->mnt_opt, link) {
+ if (strcmp(opt->name, name) == 0)
+ break;
+ }
+ if (opt != NULL) {
+ TAILQ_REMOVE(vfsp->mnt_opt, opt, link);
+ free(opt->name, M_MOUNT);
+ if (opt->value != NULL)
+ free(opt->value, M_MOUNT);
+ free(opt, M_MOUNT);
+ }
+}
+
+int
+vfs_optionisset(const vfs_t *vfsp, const char *opt, char **argp)
+{
+ struct vfsoptlist *opts = vfsp->mnt_opt;
+ int error;
+
+ if (opts == NULL)
+ return (0);
+ error = vfs_getopt(opts, opt, (void **)argp, NULL);
+ return (error != 0 ? 0 : 1);
+}
+
+int
+traverse(vnode_t **cvpp)
+{
+ kthread_t *td = curthread;
+ vnode_t *cvp;
+ vnode_t *tvp;
+ vfs_t *vfsp;
+ int error;
+
+ cvp = *cvpp;
+ error = 0;
+
+ /*
+ * If this vnode is mounted on, then we transparently indirect
+ * to the vnode which is the root of the mounted file system.
+ * Before we do this we must check that an unmount is not in
+ * progress on this vnode.
+ */
+
+ for (;;) {
+ /*
+ * Reached the end of the mount chain?
+ */
+ vfsp = vn_mountedvfs(cvp);
+ if (vfsp == NULL)
+ break;
+ VN_RELE(cvp);
+
+ /*
+ * The read lock must be held across the call to VFS_ROOT() to
+ * prevent a concurrent unmount from destroying the vfs.
+ */
+ error = VFS_ROOT(vfsp, 0, &tvp, td);
+ if (error)
+ break;
+ VOP_UNLOCK(tvp, 0, td);
+
+ cvp = tvp;
+ }
+
+ *cvpp = cvp;
+ return (error);
+}
+
+extern struct mount *vfs_mount_alloc(struct vnode *vp, struct vfsconf *vfsp,
+ const char *fspath, struct thread *td);
+
+int
+domount(kthread_t *td, vnode_t *vp, const char *fstype, char *fspath,
+ char *fspec, int fsflags)
+{
+ struct mount *mp;
+ struct vfsconf *vfsp;
+ int error;
+
+ /*
+ * Be ultra-paranoid about making sure the type and fspath
+ * variables will fit in our mp buffers, including the
+ * terminating NUL.
+ */
+ if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
+ return (ENAMETOOLONG);
+
+ if ((error = priv_check(td, PRIV_VFS_MOUNT)) != 0)
+ return (error);
+
+ vfsp = vfs_byname_kld(fstype, td, &error);
+ if (vfsp == NULL)
+ return (ENODEV);
+
+ if (vp->v_type != VDIR)
+ return (ENOTDIR);
+ VI_LOCK(vp);
+ if ((vp->v_iflag & VI_MOUNT) != 0 ||
+ vp->v_mountedhere != NULL) {
+ VI_UNLOCK(vp);
+ return (EBUSY);
+ }
+ vp->v_iflag |= VI_MOUNT;
+ VI_UNLOCK(vp);
+
+ /*
+ * Allocate and initialize the filesystem.
+ */
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ mp = vfs_mount_alloc(vp, vfsp, fspath, td);
+ VOP_UNLOCK(vp, 0, td);
+
+ mp->mnt_optnew = NULL;
+ vfs_setmntopt(mp, "from", fspec, 0);
+ mp->mnt_optnew = mp->mnt_opt;
+ mp->mnt_opt = NULL;
+
+ /*
+ * Set the mount level flags.
+ */
+ if (fsflags & MNT_RDONLY)
+ mp->mnt_flag |= MNT_RDONLY;
+ mp->mnt_flag &=~ MNT_UPDATEMASK;
+ mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE | MNT_ROOTFS);
+ /*
+ * Mount the filesystem.
+ * XXX The final recipients of VFS_MOUNT just overwrite the ndp they
+ * get. No freeing of cn_pnbuf.
+ */
+ error = VFS_MOUNT(mp, td);
+
+ if (!error) {
+ if (mp->mnt_opt != NULL)
+ vfs_freeopts(mp->mnt_opt);
+ mp->mnt_opt = mp->mnt_optnew;
+ (void)VFS_STATFS(mp, &mp->mnt_stat, td);
+ }
+ /*
+ * Prevent external consumers of mount options from reading
+ * mnt_optnew.
+ */
+ mp->mnt_optnew = NULL;
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ /*
+ * Put the new filesystem on the mount list after root.
+ */
+// cache_purge(vp);
+ if (!error) {
+ vnode_t *mvp;
+
+ VI_LOCK(vp);
+ vp->v_iflag &= ~VI_MOUNT;
+ VI_UNLOCK(vp);
+ vp->v_mountedhere = mp;
+ mtx_lock(&mountlist_mtx);
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ mtx_unlock(&mountlist_mtx);
+ vfs_event_signal(NULL, VQ_MOUNT, 0);
+ if (VFS_ROOT(mp, LK_EXCLUSIVE, &mvp, td))
+ panic("mount: lost mount");
+ mountcheckdirs(vp, mvp);
+ vput(mvp);
+ VOP_UNLOCK(vp, 0, td);
+ if ((mp->mnt_flag & MNT_RDONLY) == 0)
+ error = vfs_allocate_syncvnode(mp);
+ vfs_unbusy(mp, td);
+ if (error)
+ vrele(vp);
+ else
+ vfs_mountedfrom(mp, fspec);
+ } else {
+ VI_LOCK(vp);
+ vp->v_iflag &= ~VI_MOUNT;
+ VI_UNLOCK(vp);
+ vfs_unbusy(mp, td);
+ vfs_mount_destroy(mp);
+ if (VOP_ISLOCKED(vp, td) != LK_EXCLUSIVE) {
+ printf("%s:%u: vnode vp=%p not locked\n", __func__, __LINE__, vp);
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ }
+ }
+ return (error);
+}
diff --git a/sys/cddl/compat/opensolaris/kern/opensolaris_zone.c b/sys/cddl/compat/opensolaris/kern/opensolaris_zone.c
new file mode 100644
index 0000000..3059a78
--- /dev/null
+++ b/sys/cddl/compat/opensolaris/kern/opensolaris_zone.c
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sx.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/jail.h>
+#include <sys/priv.h>
+#include <sys/zone.h>
+
+static MALLOC_DEFINE(M_ZONES, "zones_data", "Zones data");
+
+/*
+ * Structure to record list of ZFS datasets exported to a zone.
+ */
+typedef struct zone_dataset {
+ LIST_ENTRY(zone_dataset) zd_next;
+ char zd_dataset[0];
+} zone_dataset_t;
+
+LIST_HEAD(zone_dataset_head, zone_dataset);
+
+static struct prison_service *zone_prison_service = NULL;
+
+int
+zone_dataset_attach(struct ucred *cred, const char *dataset, int jailid)
+{
+ struct zone_dataset_head *head;
+ zone_dataset_t *zd, *zd2;
+ struct prison *pr;
+ int error;
+
+ if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0)
+ return (error);
+
+ /* Allocate memory before we grab prison's mutex. */
+ zd = malloc(sizeof(*zd) + strlen(dataset) + 1, M_ZONES, M_WAITOK);
+
+ sx_slock(&allprison_lock);
+ pr = prison_find(jailid); /* Locks &pr->pr_mtx. */
+ sx_sunlock(&allprison_lock);
+ if (pr == NULL) {
+ free(zd, M_ZONES);
+ return (ENOENT);
+ }
+
+ head = prison_service_data_get(zone_prison_service, pr);
+ LIST_FOREACH(zd2, head, zd_next) {
+ if (strcmp(dataset, zd2->zd_dataset) == 0) {
+ free(zd, M_ZONES);
+ error = EEXIST;
+ goto failure;
+ }
+ }
+ strcpy(zd->zd_dataset, dataset);
+ LIST_INSERT_HEAD(head, zd, zd_next);
+failure:
+ mtx_unlock(&pr->pr_mtx);
+ return (error);
+}
+
+int
+zone_dataset_detach(struct ucred *cred, const char *dataset, int jailid)
+{
+ struct zone_dataset_head *head;
+ zone_dataset_t *zd;
+ struct prison *pr;
+ int error;
+
+ if ((error = priv_check_cred(cred, PRIV_ZFS_JAIL, 0)) != 0)
+ return (error);
+
+ sx_slock(&allprison_lock);
+ pr = prison_find(jailid);
+ sx_sunlock(&allprison_lock);
+ if (pr == NULL)
+ return (ENOENT);
+ head = prison_service_data_get(zone_prison_service, pr);
+ LIST_FOREACH(zd, head, zd_next) {
+ if (strcmp(dataset, zd->zd_dataset) == 0) {
+ LIST_REMOVE(zd, zd_next);
+ free(zd, M_ZONES);
+ goto success;
+ }
+ }
+ error = ENOENT;
+success:
+ mtx_unlock(&pr->pr_mtx);
+ return (error);
+}
+
+/*
+ * Returns true if the named dataset is visible in the current zone.
+ * The 'write' parameter is set to 1 if the dataset is also writable.
+ */
+int
+zone_dataset_visible(const char *dataset, int *write)
+{
+ struct zone_dataset_head *head;
+ zone_dataset_t *zd;
+ struct prison *pr;
+ size_t len;
+ int ret = 0;
+
+ if (dataset[0] == '\0')
+ return (0);
+ if (INGLOBALZONE(curproc)) {
+ if (write != NULL)
+ *write = 1;
+ return (1);
+ }
+ pr = curthread->td_ucred->cr_prison;
+ mtx_lock(&pr->pr_mtx);
+ head = prison_service_data_get(zone_prison_service, pr);
+
+ /*
+ * Walk the list once, looking for datasets which match exactly, or
+ * specify a dataset underneath an exported dataset. If found, return
+ * true and note that it is writable.
+ */
+ LIST_FOREACH(zd, head, zd_next) {
+ len = strlen(zd->zd_dataset);
+ if (strlen(dataset) >= len &&
+ bcmp(dataset, zd->zd_dataset, len) == 0 &&
+ (dataset[len] == '\0' || dataset[len] == '/' ||
+ dataset[len] == '@')) {
+ if (write)
+ *write = 1;
+ ret = 1;
+ goto end;
+ }
+ }
+
+ /*
+ * Walk the list a second time, searching for datasets which are parents
+ * of exported datasets. These should be visible, but read-only.
+ *
+ * Note that we also have to support forms such as 'pool/dataset/', with
+ * a trailing slash.
+ */
+ LIST_FOREACH(zd, head, zd_next) {
+ len = strlen(dataset);
+ if (dataset[len - 1] == '/')
+ len--; /* Ignore trailing slash */
+ if (len < strlen(zd->zd_dataset) &&
+ bcmp(dataset, zd->zd_dataset, len) == 0 &&
+ zd->zd_dataset[len] == '/') {
+ if (write)
+ *write = 0;
+ ret = 1;
+ goto end;
+ }
+ }
+end:
+ mtx_unlock(&pr->pr_mtx);
+ return (ret);
+}
+
+static int
+zone_create(struct prison_service *psrv, struct prison *pr)
+{
+ struct zone_dataset_head *head;
+
+ head = malloc(sizeof(*head), M_ZONES, M_WAITOK);
+ LIST_INIT(head);
+ mtx_lock(&pr->pr_mtx);
+ prison_service_data_set(psrv, pr, head);
+ mtx_unlock(&pr->pr_mtx);
+ return (0);
+}
+
+static int
+zone_destroy(struct prison_service *psrv, struct prison *pr)
+{
+ struct zone_dataset_head *head;
+ zone_dataset_t *zd;
+
+ mtx_lock(&pr->pr_mtx);
+ head = prison_service_data_del(psrv, pr);
+ mtx_unlock(&pr->pr_mtx);
+ while ((zd = LIST_FIRST(head)) != NULL) {
+ LIST_REMOVE(zd, zd_next);
+ free(zd, M_ZONES);
+ }
+ free(head, M_ZONES);
+ return (0);
+}
+
+static void
+zone_sysinit(void *arg __unused)
+{
+
+ zone_prison_service = prison_service_register("zfs", zone_create,
+ zone_destroy);
+}
+
+static void
+zone_sysuninit(void *arg __unused)
+{
+
+ prison_service_deregister(zone_prison_service);
+}
+
+SYSINIT(zone_sysinit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysinit, NULL);
+SYSUNINIT(zone_sysuninit, SI_SUB_DRIVERS, SI_ORDER_ANY, zone_sysuninit, NULL);
OpenPOWER on IntegriCloud