summaryrefslogtreecommitdiff
path: root/usr/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib')
-rw-r--r--usr/src/lib/Makefile10
-rw-r--r--usr/src/lib/libavl/spec/avl.spec6
-rw-r--r--usr/src/lib/libc/port/sys/zone.c12
-rw-r--r--usr/src/lib/libdevinfo/Makefile.com4
-rw-r--r--usr/src/lib/libdevinfo/devinfo_devperm.c40
-rw-r--r--usr/src/lib/libproc/common/Pcontrol.h9
-rw-r--r--usr/src/lib/libproc/common/Putil.c10
-rw-r--r--usr/src/lib/libproc/common/Putil.h8
-rw-r--r--usr/src/lib/libsec/Makefile57
-rw-r--r--usr/src/lib/libsec/Makefile.com50
-rw-r--r--usr/src/lib/libsec/common/aclcheck.c182
-rw-r--r--usr/src/lib/libsec/common/acltext.c801
-rw-r--r--usr/src/lib/libsec/common/aclutils.c1436
-rw-r--r--usr/src/lib/libsec/common/aclutils.h85
-rw-r--r--usr/src/lib/libsec/common/llib-lsec12
-rw-r--r--usr/src/lib/libsec/inc.flg29
-rw-r--r--usr/src/lib/libsec/spec/acl.spec141
-rw-r--r--usr/src/lib/libsec/spec/versions8
-rw-r--r--usr/src/lib/libsecdb/exec_attr.txt2
-rw-r--r--usr/src/lib/libsecdb/help/profiles/Makefile4
-rw-r--r--usr/src/lib/libsecdb/help/profiles/RtZFSFileSysMngmnt.html43
-rw-r--r--usr/src/lib/libsecdb/help/profiles/RtZFSStorageMngmnt.html43
-rw-r--r--usr/src/lib/libsecdb/prof_attr.txt2
-rw-r--r--usr/src/lib/libzfs/Makefile67
-rw-r--r--usr/src/lib/libzfs/Makefile.com68
-rw-r--r--usr/src/lib/libzfs/amd64/Makefile31
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h298
-rw-r--r--usr/src/lib/libzfs/common/libzfs_changelist.c416
-rw-r--r--usr/src/lib/libzfs/common/libzfs_config.c309
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c2939
-rw-r--r--usr/src/lib/libzfs/common/libzfs_graph.c527
-rw-r--r--usr/src/lib/libzfs/common/libzfs_impl.h103
-rw-r--r--usr/src/lib/libzfs/common/libzfs_import.c753
-rw-r--r--usr/src/lib/libzfs/common/libzfs_mount.c558
-rw-r--r--usr/src/lib/libzfs/common/libzfs_pool.c1154
-rw-r--r--usr/src/lib/libzfs/common/libzfs_status.c248
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c204
-rw-r--r--usr/src/lib/libzfs/common/llib-lzfs32
-rw-r--r--usr/src/lib/libzfs/i386/Makefile30
-rw-r--r--usr/src/lib/libzfs/inc.flg31
-rw-r--r--usr/src/lib/libzfs/sparc/Makefile30
-rw-r--r--usr/src/lib/libzfs/sparcv9/Makefile31
-rw-r--r--usr/src/lib/libzfs/spec/Makefile28
-rw-r--r--usr/src/lib/libzfs/spec/Makefile.targ31
-rw-r--r--usr/src/lib/libzfs/spec/amd64/Makefile35
-rw-r--r--usr/src/lib/libzfs/spec/i386/Makefile34
-rw-r--r--usr/src/lib/libzfs/spec/libzfs.spec341
-rw-r--r--usr/src/lib/libzfs/spec/sparc/Makefile34
-rw-r--r--usr/src/lib/libzfs/spec/sparcv9/Makefile35
-rw-r--r--usr/src/lib/libzfs/spec/versions40
-rw-r--r--usr/src/lib/libzfs_jni/Makefile73
-rw-r--r--usr/src/lib/libzfs_jni/Makefile.com69
-rw-r--r--usr/src/lib/libzfs_jni/amd64/Makefile31
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c710
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.h62
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_disk.c198
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_disk.h49
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_diskmgt.c764
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_diskmgt.h80
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_main.c485
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_main.h215
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c575
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_pool.h55
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_property.c1163
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_property.h52
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_util.c310
-rw-r--r--usr/src/lib/libzfs_jni/common/libzfs_jni_util.h100
-rw-r--r--usr/src/lib/libzfs_jni/common/llib-lzfs_jni33
-rw-r--r--usr/src/lib/libzfs_jni/i386/Makefile30
-rw-r--r--usr/src/lib/libzfs_jni/sparc/Makefile30
-rw-r--r--usr/src/lib/libzfs_jni/sparcv9/Makefile31
-rw-r--r--usr/src/lib/libzfs_jni/spec/Makefile28
-rw-r--r--usr/src/lib/libzfs_jni/spec/Makefile.targ31
-rw-r--r--usr/src/lib/libzfs_jni/spec/amd64/Makefile35
-rw-r--r--usr/src/lib/libzfs_jni/spec/i386/Makefile34
-rw-r--r--usr/src/lib/libzfs_jni/spec/libzfs_jni.spec113
-rw-r--r--usr/src/lib/libzfs_jni/spec/sparc/Makefile34
-rw-r--r--usr/src/lib/libzfs_jni/spec/sparcv9/Makefile35
-rw-r--r--usr/src/lib/libzfs_jni/spec/versions43
-rw-r--r--usr/src/lib/libzonecfg/common/libzonecfg.c175
-rw-r--r--usr/src/lib/libzonecfg/dtd/zonecfg.dtd.112
-rw-r--r--usr/src/lib/libzonecfg/spec/libzonecfg.spec42
-rw-r--r--usr/src/lib/libzpool/Makefile51
-rw-r--r--usr/src/lib/libzpool/Makefile.com83
-rw-r--r--usr/src/lib/libzpool/amd64/Makefile32
-rw-r--r--usr/src/lib/libzpool/common/kernel.c675
-rw-r--r--usr/src/lib/libzpool/common/llib-lzpool51
-rw-r--r--usr/src/lib/libzpool/common/sys/zfs_context.h411
-rw-r--r--usr/src/lib/libzpool/common/taskq.c250
-rw-r--r--usr/src/lib/libzpool/common/util.c135
-rw-r--r--usr/src/lib/libzpool/i386/Makefile31
-rw-r--r--usr/src/lib/libzpool/inc.flg31
-rw-r--r--usr/src/lib/libzpool/sparcv9/Makefile34
93 files changed, 18674 insertions, 208 deletions
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 8e93cef77c..4ae89dfeaa 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -190,6 +190,9 @@ SUBDIRS= \
../cmd/sendmail/libmilter \
sasl_plugins \
udapl \
+ libzpool \
+ libzfs \
+ libzfs_jni \
$($(MACH)_SUBDIRS)
sparc_SUBDIRS= .WAIT \
@@ -314,6 +317,7 @@ HDRSUBDIRS= libaio \
librpcsvc \
librsm \
libsasl \
+ libsec \
libslp \
libsmedia \
libsysevent \
@@ -326,6 +330,7 @@ HDRSUBDIRS= libaio \
libuutil \
libwrap \
libxcurses2 \
+ libzfs \
libzoneinfo \
lvm \
openssl \
@@ -391,7 +396,7 @@ libc: libc_i18n
libcmdutils: libavl
libcontract: libnvpair
libdevid: libdevinfo
-libdevinfo: libnvpair
+libdevinfo: libnvpair libsec
libdhcpagent: libdhcputil libnsl libsocket
libdhcpsvc: libinetutil
libdhcputil: libinetutil
@@ -427,6 +432,9 @@ librestart: libuutil libscf
pkcs11: libcryptoutil
print: libldap5
udapl/udapl_tavor: udapl/libdat
+libzfs: libdevinfo libdevid libgen libnvpair libuutil
+libzfs_jni: libdiskmgt libnvpair libzfs
+libzpool: libavl libumem libnvpair
#
# The reason this rule checks for the existence of the
diff --git a/usr/src/lib/libavl/spec/avl.spec b/usr/src/lib/libavl/spec/avl.spec
index b981b60711..67a4782034 100644
--- a/usr/src/lib/libavl/spec/avl.spec
+++ b/usr/src/lib/libavl/spec/avl.spec
@@ -92,6 +92,12 @@ declaration ulong_t avl_numnodes(avl_tree_t *tree)
version SUNWprivate_1.1
end
+function avl_add
+include <sys/avl.h>
+declaration void avl_remove(avl_tree_t *tree, void *data)
+version SUNWprivate_1.1
+end
+
function avl_remove
include <sys/avl.h>
declaration void avl_remove(avl_tree_t *tree, void *data)
diff --git a/usr/src/lib/libc/port/sys/zone.c b/usr/src/lib/libc/port/sys/zone.c
index 416313941f..c3ef0fab84 100644
--- a/usr/src/lib/libc/port/sys/zone.c
+++ b/usr/src/lib/libc/port/sys/zone.c
@@ -40,9 +40,10 @@
#include <stdlib.h>
#include <errno.h>
-zoneid_t
-zone_create(const char *name, const char *root, const priv_set_t *privs,
- const char *rctls, size_t rctlsz, int *extended_error)
+extern zoneid_t
+zone_create(const char *name, const char *root, const struct priv_set *privs,
+ const char *rctls, size_t rctlsz, const char *zfs, size_t zfssz,
+ int *extended_error)
{
zone_def zd;
@@ -51,10 +52,11 @@ zone_create(const char *name, const char *root, const priv_set_t *privs,
zd.zone_privs = privs;
zd.rctlbuf = rctls;
zd.rctlbufsz = rctlsz;
+ zd.zfsbuf = zfs;
+ zd.zfsbufsz = zfssz;
zd.extended_error = extended_error;
- return ((zoneid_t)syscall(SYS_zone,
- ZONE_CREATE, &zd));
+ return ((zoneid_t)syscall(SYS_zone, ZONE_CREATE, &zd));
}
int
diff --git a/usr/src/lib/libdevinfo/Makefile.com b/usr/src/lib/libdevinfo/Makefile.com
index 96ebbf9a7c..cc05e1bad6 100644
--- a/usr/src/lib/libdevinfo/Makefile.com
+++ b/usr/src/lib/libdevinfo/Makefile.com
@@ -20,7 +20,7 @@
# CDDL HEADER END
#
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -36,7 +36,7 @@ include ../../Makefile.lib
include ../../Makefile.rootfs
LIBS = $(DYNLIB) $(LINTLIB)
-LDLIBS += -lnvpair -lc
+LDLIBS += -lnvpair -lsec -lc
$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
SRCDIR = ..
diff --git a/usr/src/lib/libdevinfo/devinfo_devperm.c b/usr/src/lib/libdevinfo/devinfo_devperm.c
index df1f5dff3c..ef5ca55c48 100644
--- a/usr/src/lib/libdevinfo/devinfo_devperm.c
+++ b/usr/src/lib/libdevinfo/devinfo_devperm.c
@@ -84,7 +84,6 @@ setdevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode,
void (*errmsg)(char *))
{
int err = 0, local_errno;
- aclent_t acls[4];
char errstring[MAX_LINELEN];
if (chown(dev, uid, gid) == -1) {
@@ -106,24 +105,12 @@ setdevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode,
(*errmsg)(errstring);
}
- acls[0].a_type = USER_OBJ;
- acls[0].a_id = uid;
- acls[0].a_perm = ((mode & 0700) >> 6);
-
- acls[1].a_type = GROUP_OBJ;
- acls[1].a_id = gid;
- acls[1].a_perm = ((mode & 0070) >> 3);
-
- acls[2].a_type = CLASS_OBJ;
- acls[2].a_id = (uid_t)-1;
- acls[2].a_perm = ((mode & 0070) >> 3);
-
- acls[3].a_type = OTHER_OBJ;
- acls[3].a_id = (uid_t)-1;
- acls[3].a_perm = (mode & 0007);
+ /*
+ * strip_acl sets an acl and changes the files owner/group
+ */
+ err = acl_strip(dev, uid, gid, mode);
- /* Remove ACLs */
- if (acl(dev, SETACL, 4, acls) < 0) {
+ if (err != 0) {
/*
* If the file system returned ENOSYS, we know that it
* doesn't support ACLs, therefore, we must assume that
@@ -139,15 +126,14 @@ setdevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode,
(*errmsg)(errstring);
}
}
- }
-
- if (chmod(dev, mode) == -1) {
- err = -1;
- if (errmsg) {
- (void) snprintf(errstring, MAX_LINELEN,
- "failed to chmod device %s: %s\n",
- dev, strerror(errno));
- (*errmsg)(errstring);
+ if (chmod(dev, mode) == -1) {
+ err = -1;
+ if (errmsg) {
+ (void) snprintf(errstring, MAX_LINELEN,
+ "failed to chmod device %s: %s\n",
+ dev, strerror(errno));
+ (*errmsg)(errstring);
+ }
}
}
diff --git a/usr/src/lib/libproc/common/Pcontrol.h b/usr/src/lib/libproc/common/Pcontrol.h
index 5d3797d16f..44d9e43904 100644
--- a/usr/src/lib/libproc/common/Pcontrol.h
+++ b/usr/src/lib/libproc/common/Pcontrol.h
@@ -68,7 +68,7 @@ typedef struct { /* symbol table */
} sym_tbl_t;
typedef struct file_info { /* symbol information for a mapped file */
- list_t file_list; /* linked list */
+ plist_t file_list; /* linked list */
char file_pname[PRMAPSZ]; /* name from prmap_t */
struct map_info *file_map; /* primary (text) mapping */
int file_ref; /* references from map_info_t structures */
@@ -102,7 +102,7 @@ typedef struct map_info { /* description of an address space mapping */
} map_info_t;
typedef struct lwp_info { /* per-lwp information from core file */
- list_t lwp_list; /* linked list */
+ plist_t lwp_list; /* linked list */
lwpid_t lwp_id; /* lwp identifier */
lwpsinfo_t lwp_psinfo; /* /proc/<pid>/lwp/<lwpid>/lwpsinfo data */
lwpstatus_t lwp_status; /* /proc/<pid>/lwp/<lwpid>/lwpstatus data */
@@ -116,7 +116,7 @@ typedef struct lwp_info { /* per-lwp information from core file */
typedef struct core_info { /* information specific to core files */
char core_dmodel; /* data model for core file */
int core_errno; /* error during initialization if != 0 */
- list_t core_lwp_head; /* head of list of lwp info */
+ plist_t core_lwp_head; /* head of list of lwp info */
lwp_info_t *core_lwp; /* current lwp information */
uint_t core_nlwp; /* number of lwp's in list */
off64_t core_size; /* size of core file in bytes */
@@ -171,7 +171,7 @@ struct ps_prochandle {
size_t map_count; /* number of mappings */
size_t map_alloc; /* number of mappings allocated */
uint_t num_files; /* number of file elements in file_info */
- list_t file_head; /* head of mapped files w/ symbol table info */
+ plist_t file_head; /* head of mapped files w/ symbol table info */
char *execname; /* name of the executable file */
auxv_t *auxv; /* the process's aux vector */
int nauxv; /* number of aux vector entries */
@@ -228,6 +228,7 @@ extern int Padd_mapping(struct ps_prochandle *, off64_t, file_info_t *,
prmap_t *);
extern void Psort_mappings(struct ps_prochandle *);
+
/*
* Architecture-dependent definition of the breakpoint instruction.
*/
diff --git a/usr/src/lib/libproc/common/Putil.c b/usr/src/lib/libproc/common/Putil.c
index 7e06c14f67..791ec668cb 100644
--- a/usr/src/lib/libproc/common/Putil.c
+++ b/usr/src/lib/libproc/common/Putil.c
@@ -20,8 +20,8 @@
* CDDL HEADER END
*/
/*
- * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
@@ -41,8 +41,8 @@
void
list_link(void *new, void *existing)
{
- list_t *p = new;
- list_t *q = existing;
+ plist_t *p = new;
+ plist_t *q = existing;
if (q) {
p->list_forw = q;
@@ -60,7 +60,7 @@ list_link(void *new, void *existing)
void
list_unlink(void *old)
{
- list_t *p = old;
+ plist_t *p = old;
if (p->list_forw != p) {
p->list_back->list_forw = p->list_forw;
diff --git a/usr/src/lib/libproc/common/Putil.h b/usr/src/lib/libproc/common/Putil.h
index 328440fc81..55ea45dba2 100644
--- a/usr/src/lib/libproc/common/Putil.h
+++ b/usr/src/lib/libproc/common/Putil.h
@@ -20,7 +20,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -39,7 +39,7 @@ extern "C" {
typedef struct P_list {
struct P_list *list_forw;
struct P_list *list_back;
-} list_t;
+} plist_t;
/*
* Routines to manipulate linked lists:
@@ -47,8 +47,8 @@ typedef struct P_list {
extern void list_link(void *, void *);
extern void list_unlink(void *);
-#define list_next(elem) (void *)(((list_t *)(elem))->list_forw)
-#define list_prev(elem) (void *)(((list_t *)(elem))->list_back)
+#define list_next(elem) (void *)(((plist_t *)(elem))->list_forw)
+#define list_prev(elem) (void *)(((plist_t *)(elem))->list_back)
/*
* Routines to manipulate sigset_t, fltset_t, or sysset_t.
diff --git a/usr/src/lib/libsec/Makefile b/usr/src/lib/libsec/Makefile
index a698fe0251..2a61f47756 100644
--- a/usr/src/lib/libsec/Makefile
+++ b/usr/src/lib/libsec/Makefile
@@ -20,70 +20,49 @@
# CDDL HEADER END
#
#
-# Copyright (c) 1993-1999 by Sun Microsystems, Inc.
-# All rights reserved.
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
#
# lib/libsec/Makefile
-include ../../Makefile.master
include ../Makefile.lib
-SUBDIRS= spec .WAIT $(MACH) $(BUILD64) $(MACH64)
+HDRDIR= common
+HDRS= aclutils.h
+SUBDIRS= $(MACH) $(BUILD64) $(MACH64)
all := TARGET= all
clean := TARGET= clean
clobber := TARGET= clobber
-delete := TARGET= delete
install := TARGET= install
lint := TARGET= lint
-_msg := TARGET= _msg
-package := TARGET= package
-LIBRARY= libsec.a
-TEXT_DOMAIN= SUNW_OST_OSLIB
-XGETFLAGS= -a
-POFILE= $(LIBRARY:.a=.po)
-POFILES= generic.po
-
-SED= sed
-GREP= grep
-CP= cp
+MSGFILES= common/acltext.c common/aclutils.c common/aclmode.c \
+ common/aclsort.c common/aclcheck.c
+POFILE= libsec.po
.KEEP_STATE:
-all clean clobber delete install lint package: $(SUBDIRS)
+all clean clobber install: spec .WAIT $(SUBDIRS)
+
+$(POFILE): pofile_MSGFILES
-# definitions for install_h target
-HDRS=
-ROOTHDRDIR= $(ROOT)/usr/include
-ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%)
-CHECKHDRS= $(HDRS:%.h=%.check)
+lint: $(SUBDIRS)
# install rule for install_h target
-$(ROOTHDRDIR)/%: common/%
- $(INS.file)
install_h: $(ROOTHDRS)
check: $(CHECKHDRS)
-$(MACH) $(MACH64) spec: FRC
- @cd $@; pwd; $(MAKE) $(TARGET)
-
-_msg: $(MSGDOMAIN) $(POFILE)
- $(RM) $(MSGDOMAIN)/$(POFILE)
- $(CP) $(POFILE) $(MSGDOMAIN)
+_msg: $(MSGDOMAINPOFILE)
-$(POFILE): $(POFILES)
- $(RM) $@
- $(CAT) $(POFILES) > $@
-
-$(POFILES):
- $(RM) messages.po
- $(XGETTEXT) $(XGETFLAGS) *.[ch]* */*.[ch]*
- $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@
- $(RM) messages.po
+$(SUBDIRS) spec: FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
FRC:
+
+include ../Makefile.targ
+include ../../Makefile.msg.targ
diff --git a/usr/src/lib/libsec/Makefile.com b/usr/src/lib/libsec/Makefile.com
index b619caa0b7..0cf7541cd0 100644
--- a/usr/src/lib/libsec/Makefile.com
+++ b/usr/src/lib/libsec/Makefile.com
@@ -20,7 +20,7 @@
# CDDL HEADER END
#
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
@@ -31,48 +31,40 @@
LIBRARY= libsec.a
VERS= .1
-OBJECTS= \
- aclcheck.o \
- aclmode.o \
- aclsort.o \
- acltext.o
+OBJS_SHARED= acl_common.o
+OBJS_COMMON= aclcheck.o aclmode.o aclsort.o acltext.o aclutils.o
+OBJECTS= $(OBJS_COMMON) $(OBJS_SHARED)
# include library definitions
include ../../Makefile.lib
-# install this library in the root filesystem
-include ../../Makefile.rootfs
-
-MAPFILE= $(MAPDIR)/mapfile
-MAPOPTS= $(MAPFILE:%=-M %)
-SRCS= $(OBJECTS:%.o=../common/%.c)
-
LIBS = $(DYNLIB) $(LINTLIB)
-$(LINTLIB):= SRCS=../common/llib-lsec
-
-LINTSRC= $(LINTLIB:%.ln=%)
-
CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -I$(SRCDIR) -I../../../common/acl
DYNFLAGS += $(MAPOPTS)
-LDLIBS += -lc
+LDLIBS += -lc
-.KEEP_STATE:
+# install this library in the root filesystem
+include ../../Makefile.rootfs
-lint: lintcheck
+SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \
+ $(OBJS_SHARED:%.o=$(SRC)/common/acl/%.c)
-$(DYNLIB): $(MAPFILE)
+$(LINTLIB):= SRCS= $(SRCDIR)/$(LINTSRC)
-$(MAPFILE):
- @cd $(MAPDIR); $(MAKE) mapfile
+SRCDIR= ../common
+MAPDIR= ../spec/$(TRANSMACH)
+SPECMAPFILE= $(MAPDIR)/mapfile
-# include library targets
-include ../../Makefile.targ
+.KEEP_STATE:
-pics/%.o: ../common/%.c
+all: $(LIBS)
+
+lint: lintcheck
+
+pics/%.o: ../../../common/acl/%.c
$(COMPILE.c) -o $@ $<
$(POST_PROCESS_O)
-# install rule for lint library target
-$(ROOTLINTDIR)/%: ../common/%
- $(INS.file)
+include ../../Makefile.targ
diff --git a/usr/src/lib/libsec/common/aclcheck.c b/usr/src/lib/libsec/common/aclcheck.c
index 75c1a6cf56..6b1a12d6d9 100644
--- a/usr/src/lib/libsec/common/aclcheck.c
+++ b/usr/src/lib/libsec/common/aclcheck.c
@@ -20,7 +20,8 @@
* CDDL HEADER END
*/
/*
- * Copyright (c) 1993-1997 by Sun Microsystems, Inc.
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
@@ -42,6 +43,7 @@
#include <string.h>
#include <sys/types.h>
#include <sys/acl.h>
+#include <aclutils.h>
struct entry {
int count;
@@ -66,8 +68,8 @@ struct entry_stat {
static void free_mem(struct entry_stat *);
static int check_dup(int, uid_t *, uid_t, struct entry_stat *);
-int
-aclcheck(aclent_t *aclbufp, int nentries, int *which)
+static int
+aclent_aclcheck(aclent_t *aclbufp, int nentries, int *which, int isdir)
{
struct entry_stat tally;
aclent_t *aclentp;
@@ -82,10 +84,10 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
case USER_OBJ:
/* check uniqueness */
if (tally.user_obj.count > 0) {
- *which = (int) (aclentp - aclbufp);
+ *which = (int)(aclentp - aclbufp);
(void) free_mem(&tally);
errno = EINVAL;
- return (USER_ERROR);
+ return (EACL_USER_ERROR);
}
tally.user_obj.count = 1;
break;
@@ -93,10 +95,10 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
case GROUP_OBJ:
/* check uniqueness */
if (tally.group_obj.count > 0) {
- *which = (int) (aclentp - aclbufp);
+ *which = (int)(aclentp - aclbufp);
(void) free_mem(&tally);
errno = EINVAL;
- return (GRP_ERROR);
+ return (EACL_GRP_ERROR);
}
tally.group_obj.count = 1;
break;
@@ -104,10 +106,10 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
case OTHER_OBJ:
/* check uniqueness */
if (tally.other_obj.count > 0) {
- *which = (int) (aclentp - aclbufp);
+ *which = (int)(aclentp - aclbufp);
(void) free_mem(&tally);
errno = EINVAL;
- return (OTHER_ERROR);
+ return (EACL_OTHER_ERROR);
}
tally.other_obj.count = 1;
break;
@@ -115,10 +117,10 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
case CLASS_OBJ:
/* check uniqueness */
if (tally.class_obj.count > 0) {
- *which = (int) (aclentp - aclbufp);
+ *which = (int)(aclentp - aclbufp);
(void) free_mem(&tally);
errno = EINVAL;
- return (CLASS_ERROR);
+ return (EACL_CLASS_ERROR);
}
tally.class_obj.count = 1;
break;
@@ -145,12 +147,12 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
if (cnt == 0) {
*idp = calloc(nentries, sizeof (uid_t));
if (*idp == NULL)
- return (MEM_ERROR);
+ return (EACL_MEM_ERROR);
} else {
if (check_dup(cnt, *idp, aclentp->a_id,
&tally) == -1) {
- *which = (int) (aclentp - aclbufp);
- return (DUPLICATE_ERROR);
+ *which = (int)(aclentp - aclbufp);
+ return (EACL_DUPLICATE_ERROR);
}
}
(*idp)[cnt] = aclentp->a_id;
@@ -159,10 +161,10 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
case DEF_USER_OBJ:
/* check uniqueness */
if (tally.def_user_obj.count > 0) {
- *which = (int) (aclentp - aclbufp);
+ *which = (int)(aclentp - aclbufp);
(void) free_mem(&tally);
errno = EINVAL;
- return (USER_ERROR);
+ return (EACL_USER_ERROR);
}
tally.def_user_obj.count = 1;
break;
@@ -170,10 +172,10 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
case DEF_GROUP_OBJ:
/* check uniqueness */
if (tally.def_group_obj.count > 0) {
- *which = (int) (aclentp - aclbufp);
+ *which = (int)(aclentp - aclbufp);
(void) free_mem(&tally);
errno = EINVAL;
- return (GRP_ERROR);
+ return (EACL_GRP_ERROR);
}
tally.def_group_obj.count = 1;
break;
@@ -181,10 +183,10 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
case DEF_OTHER_OBJ:
/* check uniqueness */
if (tally.def_other_obj.count > 0) {
- *which = (int) (aclentp - aclbufp);
+ *which = (int)(aclentp - aclbufp);
(void) free_mem(&tally);
errno = EINVAL;
- return (OTHER_ERROR);
+ return (EACL_OTHER_ERROR);
}
tally.def_other_obj.count = 1;
break;
@@ -192,10 +194,10 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
case DEF_CLASS_OBJ:
/* check uniqueness */
if (tally.def_class_obj.count > 0) {
- *which = (int) (aclentp - aclbufp);
+ *which = (int)(aclentp - aclbufp);
(void) free_mem(&tally);
errno = EINVAL;
- return (CLASS_ERROR);
+ return (EACL_CLASS_ERROR);
}
tally.def_class_obj.count = 1;
break;
@@ -203,8 +205,8 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
default:
(void) free_mem(&tally);
errno = EINVAL;
- *which = (int) (aclentp - aclbufp);
- return (ENTRY_ERROR);
+ *which = (int)(aclentp - aclbufp);
+ return (EACL_ENTRY_ERROR);
}
}
/* If there are group or user entries, there must be one class entry */
@@ -212,14 +214,14 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
if (tally.class_obj.count != 1) {
(void) free_mem(&tally);
errno = EINVAL;
- return (MISS_ERROR);
+ return (EACL_MISS_ERROR);
}
/* same is true for default entries */
if (tally.def_user.count > 0 || tally.def_group.count > 0)
if (tally.def_class_obj.count != 1) {
(void) free_mem(&tally);
errno = EINVAL;
- return (MISS_ERROR);
+ return (EACL_MISS_ERROR);
}
/* there must be exactly one user_obj, group_obj, and other_obj entry */
@@ -228,27 +230,43 @@ aclcheck(aclent_t *aclbufp, int nentries, int *which)
tally.other_obj.count != 1) {
(void) free_mem(&tally);
errno = EINVAL;
- return (MISS_ERROR);
+ return (EACL_MISS_ERROR);
}
/* has default? same rules apply to default entries */
- if (tally.def_user.count > 0 ||
- tally.def_user_obj.count > 0 ||
- tally.def_group.count > 0 ||
- tally.def_group_obj.count > 0 ||
- tally.def_class_obj.count > 0 ||
- tally.def_other_obj.count > 0)
+ if (tally.def_user.count > 0 || tally.def_user_obj.count > 0 ||
+ tally.def_group.count > 0 || tally.def_group_obj.count > 0 ||
+ tally.def_class_obj.count > 0 || tally.def_other_obj.count > 0) {
+
+ /*
+ * Can't have default ACL's on non-directories
+ */
+ if (isdir == 0) {
+ (void) free_mem(&tally);
+ errno = EINVAL;
+ return (EACL_INHERIT_NOTDIR);
+ }
+
if (tally.def_user_obj.count != 1 ||
tally.def_group_obj.count != 1 ||
tally.def_other_obj.count != 1) {
(void) free_mem(&tally);
errno = EINVAL;
- return (MISS_ERROR);
+ return (EACL_MISS_ERROR);
}
+ }
+
(void) free_mem(&tally);
return (0);
}
+int
+aclcheck(aclent_t *aclbufp, int nentries, int *which)
+{
+ return (aclent_aclcheck(aclbufp, nentries, which, 1));
+}
+
+
static void
free_mem(struct entry_stat *tallyp)
{
@@ -276,3 +294,99 @@ check_dup(int count, uid_t *ids, uid_t newid, struct entry_stat *tallyp)
}
return (0);
}
+
+#define IFLAGS (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE| \
+ ACE_NO_PROPAGATE_INHERIT_ACE|ACE_INHERIT_ONLY_ACE)
+
+static int
+ace_aclcheck(acl_t *aclp, int isdir)
+{
+ ace_t *acep;
+ int i;
+ int error = 0;
+
+ /*
+ * step through all valid flags.
+ */
+
+ if (aclp->acl_cnt <= 0 || aclp->acl_cnt > MAX_ACL_ENTRIES)
+ return (EACL_COUNT_ERROR);
+
+ for (i = 0, acep = aclp->acl_aclp;
+ i != aclp->acl_cnt && error == 0; i++, acep++) {
+ switch (acep->a_flags & 0xf040) {
+ case 0:
+ case ACE_OWNER:
+ case ACE_EVERYONE:
+ case ACE_IDENTIFIER_GROUP:
+ case ACE_GROUP|ACE_IDENTIFIER_GROUP:
+ break;
+ default:
+ errno = EINVAL;
+ return (EACL_FLAGS_ERROR);
+ }
+
+ /*
+ * Can't have inheritance on files.
+ */
+ if ((acep->a_flags &
+ (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE|
+ ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) &&
+ isdir == 0) {
+ errno = EINVAL;
+ return (EACL_INHERIT_NOTDIR);
+ }
+
+ /*
+ * INHERIT_ONLY/NO_PROPAGATE need a to INHERIT_FILE
+ * or INHERIT_DIR also
+ */
+ if (acep->a_flags &
+ (ACE_INHERIT_ONLY_ACE|ACE_NO_PROPAGATE_INHERIT_ACE)) {
+ if ((acep->a_flags & (ACE_FILE_INHERIT_ACE|
+ ACE_DIRECTORY_INHERIT_ACE)) == 0) {
+ errno = EINVAL;
+ return (EACL_INHERIT_ERROR);
+ }
+ break;
+ }
+
+ switch (acep->a_type) {
+ case ACE_ACCESS_ALLOWED_ACE_TYPE:
+ case ACE_ACCESS_DENIED_ACE_TYPE:
+ case ACE_SYSTEM_AUDIT_ACE_TYPE:
+ case ACE_SYSTEM_ALARM_ACE_TYPE:
+ break;
+ default:
+ errno = EINVAL;
+ return (EACL_ENTRY_ERROR);
+ }
+ if (acep->a_access_mask > ACE_ALL_PERMS) {
+ errno = EINVAL;
+ return (EACL_PERM_MASK_ERROR);
+ }
+ }
+
+ return (0);
+}
+
+int
+acl_check(acl_t *aclp, int flag)
+{
+ int error;
+ int where;
+
+ switch (aclp->acl_type) {
+ case ACLENT_T:
+ error = aclent_aclcheck(aclp->acl_aclp, aclp->acl_cnt,
+ &where, flag);
+ break;
+ case ACE_T:
+ error = ace_aclcheck(aclp, flag);
+ break;
+ default:
+ errno = EINVAL;
+ error = EACL_ENTRY_ERROR;
+ }
+ return (error);
+}
diff --git a/usr/src/lib/libsec/common/acltext.c b/usr/src/lib/libsec/common/acltext.c
index da3195379c..75b0dc7857 100644
--- a/usr/src/lib/libsec/common/acltext.c
+++ b/usr/src/lib/libsec/common/acltext.c
@@ -32,9 +32,15 @@
#include <string.h>
#include <limits.h>
#include <stdlib.h>
+#include <errno.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/acl.h>
+#include <aclutils.h>
+#include <libintl.h>
+
+
+extern acl_t *acl_alloc(enum acl_type);
/*
* acltotext() converts each ACL entry to look like this:
@@ -64,8 +70,21 @@ static char *strappend(char *, char *);
static char *convert_perm(char *, o_mode_t);
static int increase_length(struct dynaclstr *, size_t);
-#define FREE free(aclp);\
- free(allocp)
+static int
+acl_str_to_id(char *str, int *id)
+{
+ char *end;
+ uid_t value;
+
+ value = strtol(str, &end, 10);
+
+ if (errno != 0 || *end != '\0')
+ return (EACL_INVALID_USER_GROUP);
+
+ *id = value;
+
+ return (0);
+}
/*
* Convert internal acl representation to external representation.
@@ -213,8 +232,8 @@ acltotext(aclent_t *aclp, int aclcnt)
* The comma at the end is not prescribed by the man pages.
* But it is needed not to break the old programs.
*/
-aclent_t *
-aclfromtext(char *aclstr, int *aclcnt)
+static int
+aclent_aclfromtext(char *aclstr, acl_t **ret_aclp)
{
char *fieldp;
char *tp;
@@ -224,23 +243,29 @@ aclfromtext(char *aclstr, int *aclcnt)
int entry_type;
int id;
int len;
+ int error;
o_mode_t perm;
aclent_t *tmpaclp;
- aclent_t *aclp;
+ acl_t *aclp;
struct group *groupp;
struct passwd *passwdp;
- *aclcnt = 0;
aclp = NULL;
if (! aclstr)
return (NULL);
+ aclp = acl_alloc(ACLENT_T);
+ if (aclp == NULL) {
+ return (EACL_MEM_ERROR);
+ }
+
+ *ret_aclp = NULL;
+
len = strlen(aclstr);
if ((aclimport = allocp = strdup(aclstr)) == NULL) {
- fprintf(stderr, "malloc() failed\n");
- return (NULL);
+ return (EACL_MEM_ERROR);
}
if (aclimport[len - 1] == ',')
@@ -256,32 +281,33 @@ aclfromtext(char *aclstr, int *aclcnt)
nextp = tp + 1;
}
- *aclcnt += 1;
+ aclp->acl_cnt += 1;
/*
* get additional memory:
* can be more efficient by allocating a bigger block
* each time.
*/
- if (*aclcnt > 1)
- tmpaclp = (aclent_t *)realloc(aclp,
- sizeof (aclent_t) * (*aclcnt));
+ if (aclp->acl_cnt > 1)
+ tmpaclp = (aclent_t *)realloc(aclp->acl_aclp,
+ sizeof (aclent_t) * (aclp->acl_cnt));
else
tmpaclp = (aclent_t *)malloc(sizeof (aclent_t));
if (tmpaclp == NULL) {
free(allocp);
- if (aclp)
- free(aclp);
- return (NULL);
+ acl_free(aclp);
+ return (EACL_MEM_ERROR);
}
- aclp = tmpaclp;
- tmpaclp = aclp + (*aclcnt - 1);
+ aclp->acl_aclp = tmpaclp;
+ tmpaclp = (aclent_t *)aclp->acl_aclp + (aclp->acl_cnt - 1);
/* look for entry type field */
tp = strchr(aclimport, ':');
if (tp == NULL) {
- FREE;
- return (NULL);
+ free(allocp);
+ if (aclp)
+ acl_free(aclp);
+ return (EACL_ENTRY_ERROR);
} else
*tp = '\0';
if (strcmp(aclimport, "user") == 0) {
@@ -313,8 +339,9 @@ aclfromtext(char *aclstr, int *aclcnt)
else if (strcmp(aclimport, "defaultother") == 0)
entry_type = DEF_OTHER_OBJ;
else {
- FREE;
- return (NULL);
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_ENTRY_ERROR);
}
/* look for user/group name */
@@ -324,8 +351,9 @@ aclfromtext(char *aclstr, int *aclcnt)
fieldp = tp + 1;
tp = strchr(fieldp, ':');
if (tp == NULL) {
- FREE;
- return (NULL);
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_INVALID_USER_GROUP);
} else
*tp = '\0';
if (fieldp != tp) {
@@ -341,32 +369,37 @@ aclfromtext(char *aclstr, int *aclcnt)
* change. Use the friendlier interface
* getpwnam().
*/
+ error = 0;
passwdp = getpwnam(fieldp);
if (passwdp == NULL) {
- (void) fprintf(stderr,
- "user %s not found\n", fieldp);
- id = UID_NOBODY; /* nobody */
- }
- else
+ error = acl_str_to_id(fieldp,
+ &id);
+ } else {
id = passwdp->pw_uid;
+ }
+
+ if (error) {
+ free(allocp);
+ acl_free(aclp);
+ return (error);
+ }
+
} else {
+ error = 0;
if (entry_type == GROUP ||
entry_type == DEF_GROUP) {
groupp = getgrnam(fieldp);
if (groupp == NULL) {
- (void) fprintf(stderr,
- "group %s not found\n",
- fieldp);
- /* no group? */
- id = GID_NOBODY;
+ error = acl_str_to_id(
+ fieldp, &id);
}
- else
+ if (error == 0)
id = groupp->gr_gid;
- } else {
- (void) fprintf(stderr,
- "acl import errors\n");
- FREE;
- return (NULL);
+ }
+ if (error) {
+ free(allocp);
+ acl_free(aclp);
+ return (error);
}
}
} else {
@@ -390,8 +423,9 @@ aclfromtext(char *aclstr, int *aclcnt)
fieldp = tp + 1;
if (strlen(fieldp) != 3) {
/* not "rwx" format */
- FREE;
- return (NULL);
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_PERM_MASK_ERROR);
} else {
char s[] = "rwx";
int mask = 0x04;
@@ -402,8 +436,9 @@ aclfromtext(char *aclstr, int *aclcnt)
if (fieldp[i] == s[i])
perm |= mask;
else if (fieldp[i] != '-') {
- FREE;
- return (NULL);
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_PERM_MASK_ERROR);
}
}
}
@@ -414,9 +449,30 @@ aclfromtext(char *aclstr, int *aclcnt)
aclimport = nextp;
}
free(allocp);
- return (aclp);
+ *ret_aclp = aclp;
+ return (0);
+}
+
+aclent_t *
+aclfromtext(char *aclstr, int *aclcnt)
+{
+ acl_t *aclp;
+ aclent_t *aclentp;
+ int error;
+
+ error = aclent_aclfromtext(aclstr, &aclp);
+ if (error)
+ return (NULL);
+
+ aclentp = aclp->acl_aclp;
+ aclp->acl_aclp = NULL;
+ acl_free(aclp);
+
+ *aclcnt = aclp->acl_cnt;
+ return (aclentp);
}
+
static char *
strappend(char *where, char *newstr)
{
@@ -443,6 +499,129 @@ convert_perm(char *where, o_mode_t perm)
return (where);
}
+static char *
+ace_convert_perm(char *where, mode_t perm, int isdir, int iflags)
+{
+ char *start = where;
+
+ /*
+ * The following mneumonics all have the
+ * same value. The only difference is the
+ * first value is for files and second for directories
+ * ACE_READ_DATA/ACE_LIST_DIRECTORY
+ * ACE_WRITE_DATA/ACE_ADD_FILE
+ * ACE_APPEND_DATA/ACE_ADD_SUBDIRECTORY
+ */
+
+ /*
+ * If ACE is a directory, but inheritance indicates its
+ * for a file then print permissions for file rather than
+ * dir.
+ */
+ if (isdir) {
+ if (perm & ACE_LIST_DIRECTORY) {
+ if (iflags == ACE_FILE_INHERIT_ACE)
+ where = strappend(where, "read_data/");
+ else
+ where = strappend(where,
+ "list_directory/read_data/");
+ }
+ if (perm & ACE_ADD_FILE) {
+ if (iflags == ACE_FILE_INHERIT_ACE)
+ where = strappend(where, "write_data/");
+ else
+ where = strappend(where,
+ "add_file/write_data/");
+ }
+ if (perm & ACE_ADD_SUBDIRECTORY) {
+ if (iflags == ACE_FILE_INHERIT_ACE)
+ where = strappend(where, "append_data/");
+ else
+ where = strappend(where,
+ "add_subdirectory/append_data/");
+ }
+ } else {
+ if (perm & ACE_READ_DATA)
+ where = strappend(where, "read_data/");
+ if (perm & ACE_WRITE_DATA)
+ where = strappend(where, "write_data/");
+ if (perm & ACE_APPEND_DATA)
+ where = strappend(where, "append_data/");
+ }
+ if (perm & ACE_READ_NAMED_ATTRS)
+ where = strappend(where, "read_xattr/");
+ if (perm & ACE_WRITE_NAMED_ATTRS)
+ where = strappend(where, "write_xattr/");
+ if (perm & ACE_EXECUTE)
+ where = strappend(where, "execute/");
+ if (perm & ACE_DELETE_CHILD)
+ where = strappend(where, "delete_child/");
+ if (perm & ACE_READ_ATTRIBUTES)
+ where = strappend(where, "read_attributes/");
+ if (perm & ACE_WRITE_ATTRIBUTES)
+ where = strappend(where, "write_attributes/");
+ if (perm & ACE_DELETE)
+ where = strappend(where, "delete/");
+ if (perm & ACE_READ_ACL)
+ where = strappend(where, "read_acl/");
+ if (perm & ACE_WRITE_ACL)
+ where = strappend(where, "write_acl/");
+ if (perm & ACE_WRITE_OWNER)
+ where = strappend(where, "write_owner/");
+ if (perm & ACE_SYNCHRONIZE)
+ where = strappend(where, "synchronize");
+
+ if (start[strlen(start) - 1] == '/') {
+ start[strlen(start) - 1] = '\0';
+ where = start + strlen(start);
+ }
+ return (where);
+}
+
+int
+ace_permask(char *perm_tok, int *perm)
+{
+ if (strcmp(perm_tok, "read_data") == 0)
+ *perm |= ACE_READ_DATA;
+ else if (strcmp(perm_tok, "list_directory") == 0)
+ *perm |= ACE_LIST_DIRECTORY;
+ else if (strcmp(perm_tok, "write_data") == 0)
+ *perm |= ACE_WRITE_DATA;
+ else if (strcmp(perm_tok, "add_file") == 0)
+ *perm |= ACE_ADD_FILE;
+ else if (strcmp(perm_tok, "append_data") == 0)
+ *perm |= ACE_APPEND_DATA;
+ else if (strcmp(perm_tok, "add_subdirectory") == 0)
+ *perm |= ACE_ADD_SUBDIRECTORY;
+ else if (strcmp(perm_tok, "read_xattr") == 0)
+ *perm |= ACE_READ_NAMED_ATTRS;
+ else if (strcmp(perm_tok, "write_xattr") == 0)
+ *perm |= ACE_WRITE_NAMED_ATTRS;
+ else if (strcmp(perm_tok, "execute") == 0)
+ *perm |= ACE_EXECUTE;
+ else if (strcmp(perm_tok, "delete_child") == 0)
+ *perm |= ACE_DELETE_CHILD;
+ else if (strcmp(perm_tok, "read_attributes") == 0)
+ *perm |= ACE_READ_ATTRIBUTES;
+ else if (strcmp(perm_tok, "write_attributes") == 0)
+ *perm |= ACE_WRITE_ATTRIBUTES;
+ else if (strcmp(perm_tok, "delete") == 0)
+ *perm |= ACE_DELETE;
+ else if (strcmp(perm_tok, "read_acl") == 0)
+ *perm |= ACE_READ_ACL;
+ else if (strcmp(perm_tok, "write_acl") == 0)
+ *perm |= ACE_WRITE_ACL;
+ else if (strcmp(perm_tok, "write_owner") == 0)
+ *perm |= ACE_WRITE_OWNER;
+ else if (strcmp(perm_tok, "synchronize") == 0)
+ *perm |= ACE_SYNCHRONIZE;
+ else {
+ return (1);
+ }
+
+ return (0);
+}
+
/*
* Callers should check the return code as this routine may change the string
* pointer in dynaclstr.
@@ -462,3 +641,537 @@ increase_length(struct dynaclstr *dacl, size_t increase)
} else
return (0);
}
+
+/*
+ * ace_acltotext() conver each ace formatted acl to look like this:
+ *
+ * entry_type:uid^gid^name:perms:allow^deny[:flags][,]
+ *
+ * The maximum length of entry_type is 5 ("group")
+ *
+ * The max length of a uid^gid^name entry (in theory) is 8, hence we use
+ * LOGNAME_MAX.
+ *
+ * The length of a perms entry is 144 i.e read_data/write_data...
+ * to each acl entry.
+ *
+ * iflags: file_inherit/dir_inherit/inherit_only/no_propagate
+ *
+ */
+
+#define ACE_ENTRYTYPLEN 6
+#define IFLAGS_SIZE 51
+#define ACCESS_TYPE_SIZE 5
+#define COLON_CNT 3
+#define PERMS_LEN 216
+#define ACE_ENTRY_SIZE (ACE_ENTRYTYPLEN + LOGNAME_MAX + PERMS_LEN +\
+ ACCESS_TYPE_SIZE + IFLAGS_SIZE + COLON_CNT)
+
+static char *
+ace_acltotext(acl_t *aceaclp)
+{
+ ace_t *aclp = aceaclp->acl_aclp;
+ int aclcnt = aceaclp->acl_cnt;
+ char *aclexport;
+ char *where;
+ char *start;
+ struct group *groupp;
+ struct passwd *passwdp;
+ struct dynaclstr *dstr;
+ int i, rtn;
+ int isdir = (aceaclp->acl_flags & ACL_IS_DIR);
+ size_t excess = 0;
+
+ if (aclp == NULL)
+ return (NULL);
+ if ((dstr = malloc(sizeof (struct dynaclstr))) == NULL)
+ return (NULL);
+ dstr->bufsize = aclcnt * ACE_ENTRY_SIZE;
+ if ((dstr->aclexport = malloc(dstr->bufsize)) == NULL)
+ return (NULL);
+ *dstr->aclexport = '\0';
+ where = dstr->aclexport;
+
+ for (i = 0; i < aclcnt; i++, aclp++) {
+ switch (aclp->a_flags & 0xf040) {
+ case ACE_OWNER:
+ case 0:
+ if ((aclp->a_flags & 0xf040) == ACE_OWNER)
+ where = strappend(where, "owner@");
+ else
+ where = strappend(where, "user:");
+ if ((aclp->a_flags & 0xf040) == 0) {
+ passwdp = getpwuid(aclp->a_who);
+ if (passwdp == (struct passwd *)NULL) {
+ /* put in uid instead */
+ (void) sprintf(where, "%d",
+ aclp->a_who);
+ } else {
+ excess = strlen(passwdp->pw_name) -
+ LOGNAME_MAX;
+ if (excess > 0) {
+ rtn = increase_length(dstr,
+ excess);
+ if (rtn == 1)
+ /* reset where */
+ where =
+ dstr->aclexport +
+ strlen(
+ dstr->aclexport);
+ else
+ return (NULL);
+ }
+ where = strappend(where,
+ passwdp->pw_name);
+ }
+ } else {
+ where = strappend(where, "");
+ }
+ where = strappend(where, ":");
+ break;
+ case ACE_GROUP|ACE_IDENTIFIER_GROUP:
+ case ACE_IDENTIFIER_GROUP:
+ if ((aclp->a_flags & 0xf040) ==
+ (ACE_GROUP | ACE_IDENTIFIER_GROUP))
+ where = strappend(where, "group@");
+ else
+ where = strappend(where, "group:");
+ if (!(aclp->a_flags & ACE_GROUP)) {
+ groupp = getgrgid(aclp->a_who);
+ if (groupp == (struct group *)NULL) {
+ /* put in gid instead */
+ (void) sprintf(where,
+ "%d", aclp->a_who);
+ } else {
+ excess = strlen(groupp->gr_name) -
+ LOGNAME_MAX;
+ if (excess > 0) {
+ rtn = increase_length(dstr,
+ excess);
+ if (rtn == 1)
+ /* reset where */
+ where =
+ dstr->aclexport +
+ strlen(
+ dstr->aclexport);
+ else
+ return (NULL);
+ }
+ where = strappend(where,
+ groupp->gr_name);
+ }
+ } else {
+ where = strappend(where, "");
+ }
+ where = strappend(where, ":");
+ break;
+ case ACE_EVERYONE:
+ where = strappend(where, "everyone@:");
+ break;
+ default:
+ free(dstr->aclexport);
+ free(dstr);
+ return (NULL);
+
+ }
+ where = ace_convert_perm(where, aclp->a_access_mask,
+ isdir, (aclp->a_flags &
+ (ACE_FILE_INHERIT_ACE|ACE_DIRECTORY_INHERIT_ACE)));
+ where = strappend(where,
+ (aclp->a_type == ACE_ACCESS_ALLOWED_ACE_TYPE) ?
+ ":allow" : ":deny");
+
+ /*
+ * slap on inheritance flags if we have any
+ */
+
+ if (aclp->a_flags & 0xf) {
+ where = strappend(where, ":");
+ start = where;
+ if (aclp->a_flags & ACE_FILE_INHERIT_ACE)
+ where = strappend(where, "file_inherit/");
+ if (aclp->a_flags & ACE_DIRECTORY_INHERIT_ACE)
+ where = strappend(where, "dir_inherit/");
+ if (aclp->a_flags & ACE_NO_PROPAGATE_INHERIT_ACE)
+ where = strappend(where, "no_propagate/");
+ if (aclp->a_flags & ACE_INHERIT_ONLY_ACE)
+ where = strappend(where, "inherit_only");
+
+ /*
+ * chop off trailing slash, if present
+ */
+ if (start[strlen(start) - 1] == '/') {
+ start[strlen(start) - 1] = '\0';
+ where = start + strlen(start);
+ }
+ }
+ if (i < aclcnt - 1)
+ where = strappend(where, ",");
+ }
+ aclexport = dstr->aclexport;
+ free(dstr);
+ return (aclexport);
+}
+
+static int
+build_iflags(char *str, int *iflags)
+{
+
+ char *tok;
+ *iflags = 0;
+
+ tok = strtok(str, "/");
+
+ if (tok == NULL)
+ return (1);
+
+ do {
+ if (strcmp(tok, "file_inherit") == 0)
+ *iflags |= ACE_FILE_INHERIT_ACE;
+ else if (strcmp(tok, "dir_inherit") == 0)
+ *iflags |= ACE_DIRECTORY_INHERIT_ACE;
+ else if (strcmp(tok, "inherit_only") == 0)
+ *iflags |= ACE_INHERIT_ONLY_ACE;
+ else if (strcmp(tok, "no_propagate") == 0)
+ *iflags |= ACE_NO_PROPAGATE_INHERIT_ACE;
+ else
+ return (1);
+ } while (tok = strtok(NULL, "/"));
+ return (0);
+}
+
+/*
+ * Convert external acl representation to internal representation.
+ * The accepted syntax is: <acl_entry>[,<acl_entry>]*[,]
+ * The comma at the end is not prescribed by the man pages.
+ * But it is needed not to break the old programs.
+ */
+
+int
+ace_aclfromtext(char *aclstr, acl_t **ret_aclp)
+{
+ char *fieldp;
+ char *tp;
+ char *nextp;
+ char *allocp;
+ char *aclimport;
+ char *str;
+ char *perm_tok;
+ int entry_type;
+ int id;
+ int type;
+ int iflags;
+ int len;
+ int error;
+ int32_t perm;
+ ace_t *tmpaclp;
+ acl_t *aclp;
+ struct group *groupp;
+ struct passwd *passwdp;
+
+ if (! aclstr)
+ return (EACL_INVALID_STR);
+
+ len = strlen(aclstr);
+
+ aclp = acl_alloc(ACE_T);
+ if (aclp == NULL) {
+ return (EACL_MEM_ERROR);
+ }
+
+ *ret_aclp = NULL;
+
+ if ((aclimport = allocp = strdup(aclstr)) == NULL) {
+ return (EACL_MEM_ERROR);
+ }
+
+
+ if (aclimport[len - 1] == ',')
+ aclimport[len - 1] = '\0';
+
+ for (; aclimport; ) {
+ /* look for an ACL entry */
+ tp = strchr(aclimport, ',');
+ if (tp == NULL) {
+ nextp = NULL;
+ } else {
+ *tp = '\0';
+ nextp = tp + 1;
+ }
+
+ aclp->acl_cnt += 1;
+
+ /*
+ * get additional memory:
+ * can be more efficient by allocating a bigger block
+ * each time.
+ */
+ if (aclp->acl_cnt > 1)
+ tmpaclp = (ace_t *)realloc(aclp->acl_aclp,
+ sizeof (ace_t) * (aclp->acl_cnt));
+ else
+ tmpaclp = (ace_t *)malloc(sizeof (ace_t));
+ if (tmpaclp == NULL) {
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_MEM_ERROR);
+ }
+ aclp->acl_aclp = tmpaclp;
+ tmpaclp = (ace_t *)aclp->acl_aclp + (aclp->acl_cnt - 1);
+
+ /* look for entry type field */
+ tp = strchr(aclimport, ':');
+ if (tp == NULL) {
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_ENTRY_ERROR);
+ } else
+ *tp = '\0';
+ if (strcmp(aclimport, "owner@") == 0) {
+ entry_type = ACE_OWNER;
+ } else if (strcmp(aclimport, "group@") == 0) {
+ entry_type = ACE_GROUP | ACE_IDENTIFIER_GROUP;
+ } else if (strcmp(aclimport, "everyone@") == 0) {
+ entry_type = ACE_EVERYONE;
+ } else if (strcmp(aclimport, "group") == 0) {
+ entry_type = ACE_IDENTIFIER_GROUP;
+ } else if (strcmp(aclimport, "user") == 0) {
+ entry_type = 0;
+ } else {
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_ENTRY_ERROR);
+ }
+
+ /*
+ * If not an abstraction owner@, group@ or everyone@
+ * then we must have a user/group name next
+ */
+
+ if (entry_type == 0 || entry_type == ACE_IDENTIFIER_GROUP) {
+ fieldp = tp + 1;
+ tp = strchr(fieldp, ':');
+ if (tp == NULL) {
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_INVALID_USER_GROUP);
+ } else
+ *tp = '\0';
+ if (fieldp != tp) {
+ /*
+ * The second field could be empty. We only care
+ * when the field has user/group name.
+ */
+ if (entry_type == 0) {
+ /*
+ * The reentrant interface getpwnam_r()
+ * is uncommitted and subject to
+ * change. Use the friendlier interface
+ * getpwnam().
+ */
+ error = 0;
+ passwdp = getpwnam(fieldp);
+ if (passwdp == NULL) {
+ error = acl_str_to_id(
+ fieldp, &id);
+ } else {
+ id = passwdp->pw_uid;
+ }
+
+ if (error) {
+ free(allocp);
+ acl_free(aclp);
+ return (error);
+ }
+ } else {
+ error = 0;
+ if (entry_type ==
+ ACE_IDENTIFIER_GROUP) {
+ groupp = getgrnam(fieldp);
+ if (groupp == NULL) {
+ /* no group? */
+ error = acl_str_to_id(
+ fieldp, &id);
+ } else
+ id = groupp->gr_gid;
+
+ } else if ((entry_type == ACE_OWNER) ||
+ (entry_type ==
+ (ACE_IDENTIFIER_GROUP|ACE_GROUP)) ||
+ (entry_type != ACE_EVERYONE)) {
+ error = EACL_FIELD_NOT_BLANK;
+ } else {
+ error = EACL_ENTRY_ERROR;
+ }
+
+ if (error) {
+ free(allocp);
+ acl_free(aclp);
+ return (error);
+ }
+ }
+ }
+ } else {
+ id = -1;
+ }
+
+ /* next field: permission */
+ fieldp = tp + 1;
+ tp = strchr(fieldp, ':');
+ if (tp == NULL) {
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_PERM_MASK_ERROR);
+ } else
+ *tp = '\0';
+
+ perm = 0;
+
+ perm_tok = strtok(fieldp, "/");
+ if (perm_tok == NULL) {
+ perm = 0;
+ } else {
+ do {
+ if (ace_permask(perm_tok, &perm) != 0) {
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_PERM_MASK_ERROR);
+ }
+ } while (perm_tok = strtok(NULL, "/"));
+ }
+
+ /* grab allow/deny */
+ fieldp = tp + 1;
+ tp = strchr(fieldp, ':');
+ if (tp != NULL)
+ *tp = '\0';
+
+ if (strcmp(fieldp, "allow") == 0)
+ type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+ else if (strcmp(fieldp, "deny") == 0)
+ type = ACE_ACCESS_DENIED_ACE_TYPE;
+ else {
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_INVALID_ACCESS_TYPE);
+ }
+
+ /* grab option inherit flags */
+
+ iflags = 0;
+ if (tp != NULL) {
+ fieldp = tp + 1;
+ if (fieldp != NULL) {
+ *tp = '\0';
+ str = fieldp;
+ if (build_iflags(str, &iflags) != 0) {
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_INHERIT_ERROR);
+ }
+ } else {
+ free(allocp);
+ acl_free(aclp);
+ return (EACL_UNKNOWN_DATA);
+ }
+ }
+ /* slap fields into ace_t structure */
+
+ tmpaclp->a_flags = entry_type;
+ tmpaclp->a_flags |= iflags;
+ tmpaclp->a_who = id;
+ tmpaclp->a_access_mask = perm;
+ tmpaclp->a_type = type;
+ aclimport = nextp;
+ }
+ free(allocp);
+ *ret_aclp = aclp;
+ return (0);
+}
+
+char
+*acl_totext(acl_t *aclp)
+{
+ if (aclp == NULL)
+ return (NULL);
+
+ switch (aclp->acl_type) {
+ case ACE_T:
+ return (ace_acltotext(aclp));
+ case ACLENT_T:
+ return (acltotext(aclp->acl_aclp, aclp->acl_cnt));
+ }
+ return (NULL);
+}
+
+int
+acl_fromtext(const char *acltextp, acl_t **ret_aclp)
+{
+ acl_t *aclp;
+ char *token;
+ char *ptr;
+ char *textp;
+ enum acl_type flavor;
+ int colon_cnt = 0;
+ int error;
+
+ /*
+ * first try and detect what type of acl entries we have
+ *
+ * aclent_t can have 1, 2 or 3 colons
+ * if 3 then must have word default:
+ *
+ * ace_t can have 2, 3 or 4
+ * for 2 then must be owner@, group@ or everyone@
+ */
+
+ textp = strdup(acltextp);
+ if (textp == NULL)
+ return (-1);
+
+ token = strtok(textp, ",");
+ if (token == NULL) {
+ free(textp);
+ return (-1);
+ }
+
+ for (ptr = token; *ptr; ptr++) {
+ if (*ptr == ':')
+ colon_cnt++;
+ }
+
+ if (colon_cnt == 1 || colon_cnt == 2) {
+ if ((strncmp(acltextp, "owner@", 6) == 0) ||
+ (strncmp(acltextp, "group@", 6) == 0) ||
+ (strncmp(acltextp, "everyone@", 9) == 0))
+ flavor = ACE_T;
+ else
+ flavor = ACLENT_T;
+ } else if (colon_cnt == 3) {
+ ptr = strtok(token, ":");
+ if (ptr == NULL) {
+ free(textp);
+ return (EACL_MISSING_FIELDS);
+ } else if (strcmp(ptr, "default") == 0) {
+ flavor = ACLENT_T;
+ } else {
+ flavor = ACE_T;
+ }
+ } else if (colon_cnt == 4) {
+ flavor = ACE_T;
+ } else {
+ free(textp);
+ return (EACL_MISSING_FIELDS);
+ }
+
+
+ free(textp);
+
+ if (flavor == ACLENT_T)
+ error = aclent_aclfromtext((char *)acltextp, &aclp);
+ else
+ error = ace_aclfromtext((char *)acltextp, &aclp);
+
+ *ret_aclp = aclp;
+ return (error);
+}
diff --git a/usr/src/lib/libsec/common/aclutils.c b/usr/src/lib/libsec/common/aclutils.c
new file mode 100644
index 0000000000..f3c8856054
--- /dev/null
+++ b/usr/src/lib/libsec/common/aclutils.c
@@ -0,0 +1,1436 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/acl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <aclutils.h>
+#include <acl_common.h>
+
+#define ACL_PATH 0
+#define ACL_FD 1
+
+#define ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \
+ ACE_WRITE_DATA | ACE_APPEND_DATA | ACE_EXECUTE | \
+ ACE_READ_ATTRIBUTES | ACE_READ_ACL | ACE_WRITE_ACL)
+
+
+#define ACL_SYNCHRONIZE_SET_ALLOW 0x0000002
+#define ACL_SYNCHRONIZE_SET_DENY 0x0000001
+
+#define ACL_WRITE_OWNER_SET_ALLOW 0x0000020
+#define ACL_WRITE_OWNER_SET_DENY 0x0000010
+
+#define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000
+#define ACL_WRITE_ATTRS_OWNER_SET_DENY 0x0001000
+
+#define ACL_WRITE_ATTRS_WRITER_SET_DENY 0x0010000
+
+#define ACL_DELETE_SET_ALLOW 0x0000200
+#define ACL_DELETE_SET_DENY 0x0000100
+
+#define ACL_READ_NAMED_READER_SET_ALLOW 0x2000000
+
+#define ACL_WRITE_NAMED_WRITER_SET_ALLOW 0x0200000
+#define ACL_WRITE_NAMED_WRITER_SET_DENY 0x0100000
+
+#define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000
+#define ACL_WRITE_ATTRS_WRITER_SET_ALLOW 0x0020000
+
+#define ACL_WRITE_OWNER_ERR_DENY 0x0000040
+#define ACL_READ_NAMED_READER_SET_DENY 0x1000000
+#define ACL_WRITE_NAMED_WRITER_SET_ALLO W0x0200000
+typedef union {
+ const char *file;
+ int fd;
+} acl_inp;
+
+acl_t *
+acl_alloc(enum acl_type type)
+{
+ acl_t *aclp;
+
+ aclp = malloc(sizeof (acl_t));
+
+ if (aclp == NULL)
+ return (NULL);
+
+ aclp->acl_aclp = NULL;
+ aclp->acl_cnt = 0;
+
+ switch (type) {
+ case ACE_T:
+ aclp->acl_type = ACE_T;
+ aclp->acl_entry_size = sizeof (ace_t);
+ break;
+ case ACLENT_T:
+ aclp->acl_type = ACLENT_T;
+ aclp->acl_entry_size = sizeof (aclent_t);
+ break;
+ default:
+ acl_free(aclp);
+ aclp = NULL;
+ }
+ return (aclp);
+}
+
+/*
+ * Free acl_t structure
+ */
+void
+acl_free(acl_t *aclp)
+{
+ if (aclp == NULL)
+ return;
+
+ if (aclp->acl_aclp)
+ free(aclp->acl_aclp);
+ free(aclp);
+}
+
+/*
+ * Determine whether a file has a trivial ACL
+ * returns: 0 = trivial
+ * 1 = nontrivial
+ * <0 some other system failure, such as ENOENT or EPERM
+ */
+int
+acl_trivial(const char *filename)
+{
+ int acl_flavor;
+ int aclcnt;
+ int cntcmd;
+ int val = 0;
+ ace_t *acep;
+
+ acl_flavor = pathconf(filename, _PC_ACL_ENABLED);
+ if (acl_flavor == -1)
+ return (-1);
+
+ if (acl_flavor == _ACL_ACE_ENABLED)
+ cntcmd = ACE_GETACLCNT;
+ else
+ cntcmd = GETACLCNT;
+
+ aclcnt = acl(filename, cntcmd, 0, NULL);
+ if (aclcnt > 0) {
+ if (acl_flavor == _ACL_ACE_ENABLED) {
+ if (aclcnt != 6)
+ val = 1;
+ else {
+ acep = malloc(sizeof (ace_t) * aclcnt);
+ if (acep == NULL)
+ return (-1);
+ if (acl(filename, ACE_GETACL,
+ aclcnt, acep) < 0) {
+ free(acep);
+ return (-1);
+ }
+
+ val = ace_trivial(acep, aclcnt);
+ free(acep);
+ }
+ } else if (aclcnt > MIN_ACL_ENTRIES)
+ val = 1;
+ }
+ return (val);
+}
+
+static uint32_t
+access_mask_set(int haswriteperm, int hasreadperm, int isowner, int isallow)
+{
+ uint32_t access_mask = 0;
+ int acl_produce;
+ int synchronize_set = 0, write_owner_set = 0;
+ int delete_set = 0, write_attrs_set = 0;
+ int read_named_set = 0, write_named_set = 0;
+
+ acl_produce = (ACL_SYNCHRONIZE_SET_ALLOW |
+ ACL_WRITE_ATTRS_OWNER_SET_ALLOW |
+ ACL_WRITE_ATTRS_WRITER_SET_DENY);
+
+ if (isallow) {
+ synchronize_set = ACL_SYNCHRONIZE_SET_ALLOW;
+ write_owner_set = ACL_WRITE_OWNER_SET_ALLOW;
+ delete_set = ACL_DELETE_SET_ALLOW;
+ if (hasreadperm)
+ read_named_set = ACL_READ_NAMED_READER_SET_ALLOW;
+ if (haswriteperm)
+ write_named_set = ACL_WRITE_NAMED_WRITER_SET_ALLOW;
+ if (isowner)
+ write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_ALLOW;
+ else if (haswriteperm)
+ write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_ALLOW;
+ } else {
+
+ synchronize_set = ACL_SYNCHRONIZE_SET_DENY;
+ write_owner_set = ACL_WRITE_OWNER_SET_DENY;
+ delete_set = ACL_DELETE_SET_DENY;
+ if (hasreadperm)
+ read_named_set = ACL_READ_NAMED_READER_SET_DENY;
+ if (haswriteperm)
+ write_named_set = ACL_WRITE_NAMED_WRITER_SET_DENY;
+ if (isowner)
+ write_attrs_set = ACL_WRITE_ATTRS_OWNER_SET_DENY;
+ else if (haswriteperm)
+ write_attrs_set = ACL_WRITE_ATTRS_WRITER_SET_DENY;
+ else
+ /*
+ * If the entity is not the owner and does not
+ * have write permissions ACE_WRITE_ATTRIBUTES will
+ * always go in the DENY ACE.
+ */
+ access_mask |= ACE_WRITE_ATTRIBUTES;
+ }
+
+ if (acl_produce & synchronize_set)
+ access_mask |= ACE_SYNCHRONIZE;
+ if (acl_produce & write_owner_set)
+ access_mask |= ACE_WRITE_OWNER;
+ if (acl_produce & delete_set)
+ access_mask |= ACE_DELETE;
+ if (acl_produce & write_attrs_set)
+ access_mask |= ACE_WRITE_ATTRIBUTES;
+ if (acl_produce & read_named_set)
+ access_mask |= ACE_READ_NAMED_ATTRS;
+ if (acl_produce & write_named_set)
+ access_mask |= ACE_WRITE_NAMED_ATTRS;
+
+ return (access_mask);
+}
+
+/*
+ * Given an mode_t, convert it into an access_mask as used
+ * by nfsace, assuming aclent_t -> nfsace semantics.
+ */
+static uint32_t
+mode_to_ace_access(mode_t mode, int isdir, int isowner, int isallow)
+{
+ uint32_t access = 0;
+ int haswriteperm = 0;
+ int hasreadperm = 0;
+
+ if (isallow) {
+ haswriteperm = (mode & 02);
+ hasreadperm = (mode & 04);
+ } else {
+ haswriteperm = !(mode & 02);
+ hasreadperm = !(mode & 04);
+ }
+
+ /*
+ * The following call takes care of correctly setting the following
+ * mask bits in the access_mask:
+ * ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
+ * ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
+ */
+ access = access_mask_set(haswriteperm, hasreadperm, isowner, isallow);
+
+ if (isallow) {
+ access |= ACE_READ_ACL | ACE_READ_ATTRIBUTES;
+ if (isowner)
+ access |= ACE_WRITE_ACL;
+ } else {
+ if (! isowner)
+ access |= ACE_WRITE_ACL;
+ }
+
+ /* read */
+ if (mode & 04) {
+ access |= ACE_READ_DATA;
+ }
+ /* write */
+ if (mode & 02) {
+ access |= ACE_WRITE_DATA |
+ ACE_APPEND_DATA;
+ if (isdir)
+ access |= ACE_DELETE_CHILD;
+ }
+ /* exec */
+ if (mode & 01) {
+ access |= ACE_EXECUTE;
+ }
+
+ return (access);
+}
+
+/*
+ * Given an nfsace (presumably an ALLOW entry), make a
+ * corresponding DENY entry at the address given.
+ */
+static void
+ace_make_deny(ace_t *allow, ace_t *deny, int isdir, int isowner)
+{
+ (void) memcpy(deny, allow, sizeof (ace_t));
+
+ deny->a_who = allow->a_who;
+
+ deny->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+ deny->a_access_mask ^= ACE_POSIX_SUPPORTED_BITS;
+ if (isdir)
+ deny->a_access_mask ^= ACE_DELETE_CHILD;
+
+ deny->a_access_mask &= ~(ACE_SYNCHRONIZE | ACE_WRITE_OWNER |
+ ACE_DELETE | ACE_WRITE_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
+ ACE_WRITE_NAMED_ATTRS);
+ deny->a_access_mask |= access_mask_set((allow->a_access_mask &
+ ACE_WRITE_DATA), (allow->a_access_mask & ACE_READ_DATA), isowner,
+ B_FALSE);
+}
+/*
+ * Make an initial pass over an array of aclent_t's. Gather
+ * information such as an ACL_MASK (if any), number of users,
+ * number of groups, and whether the array needs to be sorted.
+ */
+static int
+ln_aent_preprocess(aclent_t *aclent, int n,
+ int *hasmask, mode_t *mask,
+ int *numuser, int *numgroup, int *needsort)
+{
+ int error = 0;
+ int i;
+ int curtype = 0;
+
+ *hasmask = 0;
+ *mask = 07;
+ *needsort = 0;
+ *numuser = 0;
+ *numgroup = 0;
+
+ for (i = 0; i < n; i++) {
+ if (aclent[i].a_type < curtype)
+ *needsort = 1;
+ else if (aclent[i].a_type > curtype)
+ curtype = aclent[i].a_type;
+ if (aclent[i].a_type & USER)
+ (*numuser)++;
+ if (aclent[i].a_type & (GROUP | GROUP_OBJ))
+ (*numgroup)++;
+ if (aclent[i].a_type & CLASS_OBJ) {
+ if (*hasmask) {
+ error = EINVAL;
+ goto out;
+ } else {
+ *hasmask = 1;
+ *mask = aclent[i].a_perm;
+ }
+ }
+ }
+
+ if ((! *hasmask) && (*numuser + *numgroup > 1)) {
+ error = EINVAL;
+ goto out;
+ }
+
+out:
+ return (error);
+}
+
+/*
+ * Convert an array of aclent_t into an array of nfsace entries,
+ * following POSIX draft -> nfsv4 conversion semantics as outlined in
+ * the IETF draft.
+ */
+static int
+ln_aent_to_ace(aclent_t *aclent, int n, ace_t **acepp, int *rescount, int isdir)
+{
+ int error = 0;
+ mode_t mask;
+ int numuser, numgroup, needsort;
+ int resultsize = 0;
+ int i, groupi = 0, skip;
+ ace_t *acep, *result = NULL;
+ int hasmask;
+
+ error = ln_aent_preprocess(aclent, n, &hasmask, &mask,
+ &numuser, &numgroup, &needsort);
+ if (error != 0)
+ goto out;
+
+ /* allow + deny for each aclent */
+ resultsize = n * 2;
+ if (hasmask) {
+ /*
+ * stick extra deny on the group_obj and on each
+ * user|group for the mask (the group_obj was added
+ * into the count for numgroup)
+ */
+ resultsize += numuser + numgroup;
+ /* ... and don't count the mask itself */
+ resultsize -= 2;
+ }
+
+ /* sort the source if necessary */
+ if (needsort)
+ ksort((caddr_t)aclent, n, sizeof (aclent_t), cmp2acls);
+
+ result = acep = calloc(1, resultsize * sizeof (ace_t));
+ if (result == NULL)
+ goto out;
+
+ for (i = 0; i < n; i++) {
+ /*
+ * don't process CLASS_OBJ (mask); mask was grabbed in
+ * ln_aent_preprocess()
+ */
+ if (aclent[i].a_type & CLASS_OBJ)
+ continue;
+
+ /* If we need an ACL_MASK emulator, prepend it now */
+ if ((hasmask) &&
+ (aclent[i].a_type & (USER | GROUP | GROUP_OBJ))) {
+ acep->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+ acep->a_flags = 0;
+ if (aclent[i].a_type & GROUP_OBJ) {
+ acep->a_who = -1;
+ acep->a_flags |=
+ (ACE_IDENTIFIER_GROUP|ACE_GROUP);
+ } else if (aclent[i].a_type & USER) {
+ acep->a_who = aclent[i].a_id;
+ } else {
+ acep->a_who = aclent[i].a_id;
+ acep->a_flags |= ACE_IDENTIFIER_GROUP;
+ }
+ if (aclent[i].a_type & ACL_DEFAULT) {
+ acep->a_flags |= ACE_INHERIT_ONLY_ACE |
+ ACE_FILE_INHERIT_ACE |
+ ACE_DIRECTORY_INHERIT_ACE;
+ }
+ /*
+ * Set the access mask for the prepended deny
+ * ace. To do this, we invert the mask (found
+ * in ln_aent_preprocess()) then convert it to an
+ * DENY ace access_mask.
+ */
+ acep->a_access_mask = mode_to_ace_access((mask ^ 07),
+ isdir, 0, 0);
+ acep += 1;
+ }
+
+ /* handle a_perm -> access_mask */
+ acep->a_access_mask = mode_to_ace_access(aclent[i].a_perm,
+ isdir, aclent[i].a_type & USER_OBJ, 1);
+
+ /* emulate a default aclent */
+ if (aclent[i].a_type & ACL_DEFAULT) {
+ acep->a_flags |= ACE_INHERIT_ONLY_ACE |
+ ACE_FILE_INHERIT_ACE |
+ ACE_DIRECTORY_INHERIT_ACE;
+ }
+
+ /*
+ * handle a_perm and a_id
+ *
+ * this must be done last, since it involves the
+ * corresponding deny aces, which are handled
+ * differently for each different a_type.
+ */
+ if (aclent[i].a_type & USER_OBJ) {
+ acep->a_who = -1;
+ acep->a_flags |= ACE_OWNER;
+ ace_make_deny(acep, acep + 1, isdir, B_TRUE);
+ acep += 2;
+ } else if (aclent[i].a_type & USER) {
+ acep->a_who = aclent[i].a_id;
+ ace_make_deny(acep, acep + 1, isdir, B_FALSE);
+ acep += 2;
+ } else if (aclent[i].a_type & (GROUP_OBJ | GROUP)) {
+ if (aclent[i].a_type & GROUP_OBJ) {
+ acep->a_who = -1;
+ acep->a_flags |= ACE_GROUP;
+ } else {
+ acep->a_who = aclent[i].a_id;
+ }
+ acep->a_flags |= ACE_IDENTIFIER_GROUP;
+ /*
+ * Set the corresponding deny for the group ace.
+ *
+ * The deny aces go after all of the groups, unlike
+ * everything else, where they immediately follow
+ * the allow ace.
+ *
+ * We calculate "skip", the number of slots to
+ * skip ahead for the deny ace, here.
+ *
+ * The pattern is:
+ * MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
+ * thus, skip is
+ * (2 * numgroup) - 1 - groupi
+ * (2 * numgroup) to account for MD + A
+ * - 1 to account for the fact that we're on the
+ * access (A), not the mask (MD)
+ * - groupi to account for the fact that we have
+ * passed up groupi number of MD's.
+ */
+ skip = (2 * numgroup) - 1 - groupi;
+ ace_make_deny(acep, acep + skip, isdir, B_FALSE);
+ /*
+ * If we just did the last group, skip acep past
+ * all of the denies; else, just move ahead one.
+ */
+ if (++groupi >= numgroup)
+ acep += numgroup + 1;
+ else
+ acep += 1;
+ } else if (aclent[i].a_type & OTHER_OBJ) {
+ acep->a_who = -1;
+ acep->a_flags |= ACE_EVERYONE;
+ ace_make_deny(acep, acep + 1, isdir, B_FALSE);
+ acep += 2;
+ } else {
+ error = EINVAL;
+ goto out;
+ }
+ }
+
+ *acepp = result;
+ *rescount = resultsize;
+
+out:
+ if (error != 0) {
+ if ((result != NULL) && (resultsize > 0)) {
+ free(result);
+ }
+ }
+
+ return (error);
+}
+
+static int
+convert_aent_to_ace(aclent_t *aclentp, int aclcnt, int isdir,
+ ace_t **retacep, int *retacecnt)
+{
+ ace_t *acep;
+ ace_t *dfacep;
+ ace_t *newacep;
+ int acecnt = 0;
+ int dfacecnt = 0;
+ int dfaclstart = 0;
+ int dfaclcnt = 0;
+ aclent_t *aclp;
+ int i;
+ int error;
+
+ ksort((caddr_t)aclentp, aclcnt, sizeof (aclent_t), cmp2acls);
+
+ for (i = 0, aclp = aclentp; i < aclcnt; aclp++, i++) {
+ if (aclp->a_type & ACL_DEFAULT)
+ break;
+ }
+
+ if (i < aclcnt) {
+ dfaclstart = aclcnt - i;
+ dfaclcnt = i;
+ }
+
+ if (dfaclcnt && isdir == 0) {
+ return (-1);
+ }
+
+ error = ln_aent_to_ace(aclentp, i, &acep, &acecnt, isdir);
+ if (error)
+ return (-1);
+
+ if (dfaclcnt) {
+ error = ln_aent_to_ace(&aclentp[dfaclstart], dfaclcnt,
+ &dfacep, &dfacecnt, isdir);
+ if (error) {
+ if (acep) {
+ free(acep);
+ }
+ return (-1);
+ }
+ }
+
+ newacep = malloc(sizeof (ace_t) * (acecnt + dfacecnt));
+ if (newacep == NULL)
+ return (-1);
+
+ (void) memcpy(newacep, acep, sizeof (ace_t) * acecnt);
+ if (dfaclcnt) {
+ (void) memcpy(newacep + acecnt, dfacep,
+ sizeof (ace_t) * dfacecnt);
+ }
+ free(acep);
+ if (dfaclcnt)
+ free(dfacep);
+
+ *retacecnt = acecnt + dfacecnt;
+ *retacep = newacep;
+ return (0);
+}
+
+
+static int
+cacl_get(acl_inp inp, int get_flag, int type, acl_t **aclp)
+{
+ const char *fname;
+ int fd;
+ int ace_acl = 0;
+ int error;
+ int getcmd, cntcmd;
+ acl_t *acl_info;
+ int save_errno;
+ int stat_error;
+ struct stat64 statbuf;
+
+ *aclp = NULL;
+ if (type == ACL_PATH) {
+ fname = inp.file;
+ ace_acl = pathconf(fname, _PC_ACL_ENABLED);
+ } else {
+ fd = inp.fd;
+ ace_acl = fpathconf(fd, _PC_ACL_ENABLED);
+ }
+
+ if (ace_acl == -1)
+ return (-1);
+
+ /*
+ * if acl's aren't supported then
+ * send it through the old GETACL interface
+ */
+ if (ace_acl == 0) {
+ ace_acl = _ACL_ACLENT_ENABLED;
+ }
+
+ if (ace_acl & _ACL_ACE_ENABLED) {
+ cntcmd = ACE_GETACLCNT;
+ getcmd = ACE_GETACL;
+ acl_info = acl_alloc(ACE_T);
+ } else {
+ cntcmd = GETACLCNT;
+ getcmd = GETACL;
+ acl_info = acl_alloc(ACLENT_T);
+ }
+
+ if (acl_info == NULL)
+ return (-1);
+
+ if (type == ACL_PATH) {
+ acl_info->acl_cnt = acl(fname, cntcmd, 0, NULL);
+ } else {
+ acl_info->acl_cnt = facl(fd, cntcmd, 0, NULL);
+ }
+
+ save_errno = errno;
+ if (acl_info->acl_cnt < 0) {
+ acl_free(acl_info);
+ errno = save_errno;
+ return (-1);
+ }
+
+ if (acl_info->acl_cnt == 0) {
+ acl_free(acl_info);
+ errno = save_errno;
+ return (0);
+ }
+
+ acl_info->acl_aclp =
+ malloc(acl_info->acl_cnt * acl_info->acl_entry_size);
+ save_errno = errno;
+
+ if (acl_info->acl_aclp == NULL) {
+ acl_free(acl_info);
+ errno = save_errno;
+ return (-1);
+ }
+
+ if (type == ACL_PATH) {
+ stat_error = stat64(fname, &statbuf);
+ error = acl(fname, getcmd, acl_info->acl_cnt,
+ acl_info->acl_aclp);
+ } else {
+ stat_error = fstat64(fd, &statbuf);
+ error = facl(fd, getcmd, acl_info->acl_cnt,
+ acl_info->acl_aclp);
+ }
+
+ save_errno = errno;
+ if (error == -1) {
+ acl_free(acl_info);
+ errno = save_errno;
+ return (-1);
+ }
+
+
+ if (stat_error == 0) {
+ acl_info->acl_flags =
+ (S_ISDIR(statbuf.st_mode) ? ACL_IS_DIR : 0);
+ } else
+ acl_info->acl_flags = 0;
+
+ switch (acl_info->acl_type) {
+ case ACLENT_T:
+ if (acl_info->acl_cnt <= MIN_ACL_ENTRIES)
+ acl_info->acl_flags |= ACL_IS_TRIVIAL;
+ break;
+ case ACE_T:
+ if (ace_trivial(acl_info->acl_aclp, acl_info->acl_cnt) == 0)
+ acl_info->acl_flags |= ACL_IS_TRIVIAL;
+ break;
+ default:
+ errno = EINVAL;
+ acl_free(acl_info);
+ return (-1);
+ }
+
+ if ((acl_info->acl_flags & ACL_IS_TRIVIAL) &&
+ (get_flag & ACL_NO_TRIVIAL)) {
+ acl_free(acl_info);
+ errno = 0;
+ return (0);
+ }
+
+ *aclp = acl_info;
+ return (0);
+}
+
+/*
+ * return -1 on failure, otherwise the number of acl
+ * entries is returned
+ */
+int
+acl_get(const char *path, int get_flag, acl_t **aclp)
+{
+ acl_inp acl_inp;
+ acl_inp.file = path;
+
+ return (cacl_get(acl_inp, get_flag, ACL_PATH, aclp));
+}
+
+int
+facl_get(int fd, int get_flag, acl_t **aclp)
+{
+
+ acl_inp acl_inp;
+ acl_inp.fd = fd;
+
+ return (cacl_get(acl_inp, get_flag, ACL_FD, aclp));
+}
+
+/*
+ * Set an ACL, translates acl to ace_t when appropriate.
+ */
+static int
+cacl_set(acl_inp *acl_inp, acl_t *aclp, int type)
+{
+ int error = 0;
+ int acl_flavor_target;
+ ace_t *acep = NULL;
+ int acecnt;
+ struct stat64 statbuf;
+ int stat_error;
+ int isdir;
+
+
+ if (type == ACL_PATH) {
+ stat_error = stat64(acl_inp->file, &statbuf);
+ if (stat_error)
+ return (-1);
+ acl_flavor_target = pathconf(acl_inp->file, _PC_ACL_ENABLED);
+ } else {
+ stat_error = fstat64(acl_inp->fd, &statbuf);
+ if (stat_error)
+ return (-1);
+ acl_flavor_target = fpathconf(acl_inp->fd, _PC_ACL_ENABLED);
+ }
+
+ isdir = S_ISDIR(statbuf.st_mode);
+
+ if (acl_flavor_target == -1)
+ return (-1);
+
+ /*
+ * Translate aclent_t ACL's to ACE ACL's.
+ */
+ if (acl_flavor_target == _ACL_ACE_ENABLED &&
+ aclp->acl_type == ACLENT_T) {
+ error = convert_aent_to_ace(aclp->acl_aclp,
+ aclp->acl_cnt, isdir, &acep, &acecnt);
+ if (error) {
+ errno = ENOTSUP;
+ return (-1);
+ }
+ /*
+ * replace old acl with newly translated acl
+ */
+ free(aclp->acl_aclp);
+ aclp->acl_aclp = acep;
+ aclp->acl_cnt = acecnt;
+ aclp->acl_type = ACE_T;
+ }
+
+ if (type == ACL_PATH) {
+ error = acl(acl_inp->file,
+ (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
+ aclp->acl_cnt, aclp->acl_aclp);
+ } else {
+ error = facl(acl_inp->fd,
+ (aclp->acl_type == ACE_T) ? ACE_SETACL : SETACL,
+ aclp->acl_cnt, aclp->acl_aclp);
+ }
+
+ return (error);
+}
+
+int
+acl_set(const char *path, acl_t *aclp)
+{
+ acl_inp acl_inp;
+
+ acl_inp.file = path;
+
+ return (cacl_set(&acl_inp, aclp, ACL_PATH));
+}
+
+int
+facl_set(int fd, acl_t *aclp)
+{
+ acl_inp acl_inp;
+
+ acl_inp.fd = fd;
+
+ return (cacl_set(&acl_inp, aclp, ACL_FD));
+}
+
+int
+acl_cnt(acl_t *aclp)
+{
+ return (aclp->acl_cnt);
+}
+
+int
+acl_type(acl_t *aclp)
+{
+ return (aclp->acl_type);
+}
+
+acl_t *
+acl_dup(acl_t *aclp)
+{
+ acl_t *newaclp;
+
+ newaclp = acl_alloc(aclp->acl_type);
+ if (newaclp == NULL)
+ return (NULL);
+
+ newaclp->acl_aclp = malloc(aclp->acl_entry_size * aclp->acl_cnt);
+ if (newaclp->acl_aclp == NULL) {
+ acl_free(newaclp);
+ return (NULL);
+ }
+
+ (void) memcpy(newaclp->acl_aclp,
+ aclp->acl_aclp, aclp->acl_entry_size * aclp->acl_cnt);
+ newaclp->acl_cnt = aclp->acl_cnt;
+
+ return (newaclp);
+}
+
+int
+acl_flags(acl_t *aclp)
+{
+ return (aclp->acl_flags);
+}
+
+void *
+acl_data(acl_t *aclp)
+{
+ return (aclp->acl_aclp);
+}
+
+/*
+ * Remove an ACL from a file and create a trivial ACL based
+ * off of the mode argument. After acl has been set owner/group
+ * are updated to match owner,group arguments
+ */
+int
+acl_strip(const char *file, uid_t owner, gid_t group, mode_t mode)
+{
+ int error = 0;
+ aclent_t min_acl[MIN_ACL_ENTRIES];
+ ace_t min_ace_acl[6]; /* owner, group, everyone + complement denies */
+ int acl_flavor;
+ int aclcnt;
+
+ acl_flavor = pathconf(file, _PC_ACL_ENABLED);
+
+ if (acl_flavor == -1)
+ return (-1);
+ /*
+ * force it through aclent flavor when file system doesn't
+ * understand question
+ */
+ if (acl_flavor == 0)
+ acl_flavor = _ACL_ACLENT_ENABLED;
+
+ if (acl_flavor & _ACL_ACLENT_ENABLED) {
+ min_acl[0].a_type = USER_OBJ;
+ min_acl[0].a_id = owner;
+ min_acl[0].a_perm = ((mode & 0700) >> 6);
+ min_acl[1].a_type = GROUP_OBJ;
+ min_acl[1].a_id = group;
+ min_acl[1].a_perm = ((mode & 0070) >> 3);
+ min_acl[2].a_type = CLASS_OBJ;
+ min_acl[2].a_id = (uid_t)-1;
+ min_acl[2].a_perm = ((mode & 0070) >> 3);
+ min_acl[3].a_type = OTHER_OBJ;
+ min_acl[3].a_id = (uid_t)-1;
+ min_acl[3].a_perm = (mode & 0007);
+ aclcnt = 4;
+ error = acl(file, SETACL, aclcnt, min_acl);
+ } else if (acl_flavor & _ACL_ACE_ENABLED) {
+ (void) memcpy(min_ace_acl, trivial_acl, sizeof (ace_t) * 6);
+
+ /*
+ * Make aces match request mode
+ */
+ adjust_ace_pair(&min_ace_acl[0], (mode & 0700) >> 6);
+ adjust_ace_pair(&min_ace_acl[2], (mode & 0070) >> 3);
+ adjust_ace_pair(&min_ace_acl[4], mode & 0007);
+
+ error = acl(file, ACE_SETACL, 6, min_ace_acl);
+ } else {
+ errno = EINVAL;
+ error = 1;
+ }
+
+ if (error == 0)
+ error = chown(file, owner, group);
+ return (error);
+}
+
+static int
+ace_match(void *entry1, void *entry2)
+{
+ ace_t *p1 = (ace_t *)entry1;
+ ace_t *p2 = (ace_t *)entry2;
+ ace_t ace1, ace2;
+
+ ace1 = *p1;
+ ace2 = *p2;
+
+ /*
+ * Need to fixup who field for abstrations for
+ * accurate comparison, since field is undefined.
+ */
+ if (ace1.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
+ ace1.a_who = -1;
+ if (ace2.a_flags & (ACE_OWNER|ACE_GROUP|ACE_EVERYONE))
+ ace2.a_who = -1;
+ return (memcmp(&ace1, &ace2, sizeof (ace_t)));
+}
+
+static int
+aclent_match(void *entry1, void *entry2)
+{
+ aclent_t *aclent1 = (aclent_t *)entry1;
+ aclent_t *aclent2 = (aclent_t *)entry2;
+
+ return (memcmp(aclent1, aclent2, sizeof (aclent_t)));
+}
+
+/*
+ * Find acl entries in acl that correspond to removeacl. Search
+ * is started from slot. The flag argument indicates whether to
+ * remove all matches or just the first match.
+ */
+int
+acl_removeentries(acl_t *acl, acl_t *removeacl, int start_slot, int flag)
+{
+ int i, j;
+ int match;
+ int (*acl_match)(void *acl1, void *acl2);
+ void *acl_entry, *remove_entry;
+ void *start;
+ int found = 0;
+
+ if (flag != ACL_REMOVE_ALL && flag != ACL_REMOVE_FIRST)
+ flag = ACL_REMOVE_FIRST;
+
+ if (acl == NULL || removeacl == NULL)
+ return (EACL_NO_ACL_ENTRY);
+
+ if (acl->acl_type != removeacl->acl_type)
+ return (EACL_DIFF_TYPE);
+
+ if (acl->acl_type == ACLENT_T)
+ acl_match = aclent_match;
+ else
+ acl_match = ace_match;
+
+ for (i = 0, remove_entry = removeacl->acl_aclp;
+ i != removeacl->acl_cnt; i++) {
+
+ j = 0;
+ acl_entry = (char *)acl->acl_aclp +
+ (acl->acl_entry_size * start_slot);
+ for (;;) {
+ match = acl_match(acl_entry, remove_entry);
+ if (match == 0) {
+ found++;
+ start = (char *)acl_entry +
+ acl->acl_entry_size;
+ (void) memmove(acl_entry, start,
+ acl->acl_entry_size *
+ acl->acl_cnt-- - (j + 1));
+
+ if (flag == ACL_REMOVE_FIRST)
+ break;
+ /*
+ * List has changed, restart search from
+ * beginning.
+ */
+ acl_entry = acl->acl_aclp;
+ j = 0;
+ continue;
+ }
+ acl_entry = ((char *)acl_entry + acl->acl_entry_size);
+ if (++j >= acl->acl_cnt) {
+ break;
+ }
+ }
+ }
+
+ return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
+}
+
+/*
+ * Replace entires entries in acl1 with the corresponding entries
+ * in newentries. The where argument specifies where to begin
+ * the replacement. If the where argument is 1 greater than the
+ * number of acl entries in acl1 then they are appended. If the
+ * where argument is 2+ greater than the number of acl entries then
+ * EACL_INVALID_SLOT is returned.
+ */
+int
+acl_modifyentries(acl_t *acl1, acl_t *newentries, int where)
+{
+
+ int slot;
+ int slots_needed;
+ int slots_left;
+ int newsize;
+
+ if (acl1 == NULL || newentries == NULL)
+ return (EACL_NO_ACL_ENTRY);
+
+ if (where < 0 || where >= acl1->acl_cnt)
+ return (EACL_INVALID_SLOT);
+
+ if (acl1->acl_type != newentries->acl_type)
+ return (EACL_DIFF_TYPE);
+
+ slot = where;
+
+ slots_left = acl1->acl_cnt - slot + 1;
+ if (slots_left < newentries->acl_cnt) {
+ slots_needed = newentries->acl_cnt - slots_left;
+ newsize = (acl1->acl_entry_size * acl1->acl_cnt) +
+ (acl1->acl_entry_size * slots_needed);
+ acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
+ if (acl1->acl_aclp == NULL)
+ return (-1);
+ }
+ (void) memcpy((char *)acl1->acl_aclp + (acl1->acl_entry_size * slot),
+ newentries->acl_aclp,
+ newentries->acl_entry_size * newentries->acl_cnt);
+
+ /*
+ * Did ACL grow?
+ */
+
+ if ((slot + newentries->acl_cnt) > acl1->acl_cnt) {
+ acl1->acl_cnt = slot + newentries->acl_cnt;
+ }
+
+ return (0);
+}
+
+/*
+ * Add acl2 entries into acl1. The where argument specifies where
+ * to add the entries.
+ */
+int
+acl_addentries(acl_t *acl1, acl_t *acl2, int where)
+{
+
+ int newsize;
+ int len;
+ void *start;
+ void *to;
+
+ if (acl1 == NULL || acl2 == NULL)
+ return (EACL_NO_ACL_ENTRY);
+
+ if (acl1->acl_type != acl2->acl_type)
+ return (EACL_DIFF_TYPE);
+
+ /*
+ * allow where to specify 1 past last slot for an append operation
+ * but anything greater is an error.
+ */
+ if (where < 0 || where > acl1->acl_cnt)
+ return (EACL_INVALID_SLOT);
+
+ newsize = (acl2->acl_entry_size * acl2->acl_cnt) +
+ (acl1->acl_entry_size * acl1->acl_cnt);
+ acl1->acl_aclp = realloc(acl1->acl_aclp, newsize);
+ if (acl1->acl_aclp == NULL)
+ return (-1);
+
+ /*
+ * first push down entries where new ones will be inserted
+ */
+
+ to = (void *)((char *)acl1->acl_aclp +
+ ((where + acl2->acl_cnt) * acl1->acl_entry_size));
+
+ start = (void *)((char *)acl1->acl_aclp +
+ where * acl1->acl_entry_size);
+
+ if (where < acl1->acl_cnt) {
+ len = (acl1->acl_cnt - where) * acl1->acl_entry_size;
+ (void) memmove(to, start, len);
+ }
+
+ /*
+ * now stick in new entries.
+ */
+
+ (void) memmove(start, acl2->acl_aclp,
+ acl2->acl_cnt * acl2->acl_entry_size);
+
+ acl1->acl_cnt += acl2->acl_cnt;
+ return (0);
+}
+
+static void
+aclent_perms(int perm, char *txt_perms)
+{
+ if (perm & S_IROTH)
+ txt_perms[0] = 'r';
+ else
+ txt_perms[0] = '-';
+ if (perm & S_IWOTH)
+ txt_perms[1] = 'w';
+ else
+ txt_perms[1] = '-';
+ if (perm & S_IXOTH)
+ txt_perms[2] = 'x';
+ else
+ txt_perms[2] = '-';
+ txt_perms[3] = '\0';
+}
+
+static char *
+pruname(uid_t uid)
+{
+ struct passwd *passwdp;
+ static char uidp[10]; /* big enough */
+
+ passwdp = getpwuid(uid);
+ if (passwdp == (struct passwd *)NULL) {
+ /* could not get passwd information: display uid instead */
+ (void) sprintf(uidp, "%ld", (long)uid);
+ return (uidp);
+ } else
+ return (passwdp->pw_name);
+}
+
+static char *
+prgname(gid_t gid)
+{
+ struct group *groupp;
+ static char gidp[10]; /* big enough */
+
+ groupp = getgrgid(gid);
+ if (groupp == (struct group *)NULL) {
+ /* could not get group information: display gid instead */
+ (void) sprintf(gidp, "%ld", (long)gid);
+ return (gidp);
+ } else
+ return (groupp->gr_name);
+}
+static void
+aclent_printacl(acl_t *aclp)
+{
+ aclent_t *tp;
+ int aclcnt;
+ int mask;
+ int slot = 0;
+ char perm[4];
+
+ /* display ACL: assume it is sorted. */
+ aclcnt = aclp->acl_cnt;
+ for (tp = aclp->acl_aclp; aclcnt--; tp++) {
+ if (tp->a_type == CLASS_OBJ)
+ mask = tp->a_perm;
+ }
+ aclcnt = aclp->acl_cnt;
+ for (tp = aclp->acl_aclp; aclcnt--; tp++) {
+ (void) printf(" %d:", slot++);
+ switch (tp->a_type) {
+ case USER:
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("user:%s:%s\t\t",
+ pruname(tp->a_id), perm);
+ aclent_perms((tp->a_perm & mask), perm);
+ (void) printf("#effective:%s\n", perm);
+ break;
+ case USER_OBJ:
+ /* no need to display uid */
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("user::%s\n", perm);
+ break;
+ case GROUP:
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("group:%s:%s\t\t",
+ prgname(tp->a_id), perm);
+ aclent_perms(tp->a_perm & mask, perm);
+ (void) printf("#effective:%s\n", perm);
+ break;
+ case GROUP_OBJ:
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("group::%s\t\t", perm);
+ aclent_perms(tp->a_perm & mask, perm);
+ (void) printf("#effective:%s\n", perm);
+ break;
+ case CLASS_OBJ:
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("mask:%s\n", perm);
+ break;
+ case OTHER_OBJ:
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("other:%s\n", perm);
+ break;
+ case DEF_USER:
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("default:user:%s:%s\n",
+ pruname(tp->a_id), perm);
+ break;
+ case DEF_USER_OBJ:
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("default:user::%s\n", perm);
+ break;
+ case DEF_GROUP:
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("default:group:%s:%s\n",
+ prgname(tp->a_id), perm);
+ break;
+ case DEF_GROUP_OBJ:
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("default:group::%s\n", perm);
+ break;
+ case DEF_CLASS_OBJ:
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("default:mask:%s\n", perm);
+ break;
+ case DEF_OTHER_OBJ:
+ aclent_perms(tp->a_perm, perm);
+ (void) printf("default:other:%s\n", perm);
+ break;
+ default:
+ (void) fprintf(stderr,
+ gettext("unrecognized entry\n"));
+ break;
+ }
+ }
+}
+
+static void
+split_line(char *str, int cols)
+{
+ char *ptr;
+ int len;
+ int i;
+ int last_split;
+ char pad[11];
+ int pad_len;
+
+ len = strlen(str);
+ ptr = str;
+ (void) strcpy(pad, "");
+ pad_len = 0;
+
+ ptr = str;
+ last_split = 0;
+ for (i = 0; i != len; i++) {
+ if ((i + pad_len + 4) >= cols) {
+ (void) printf("%s%.*s\n", pad, last_split, ptr);
+ ptr = &ptr[last_split];
+ len = strlen(ptr);
+ i = 0;
+ pad_len = 4;
+ (void) strcpy(pad, " ");
+ } else {
+ if (ptr[i] == '/' || ptr[i] == ':') {
+ last_split = i;
+ }
+ }
+ }
+ if (i == len) {
+ (void) printf("%s%s\n", pad, ptr);
+ }
+}
+
+static void
+ace_printacl(acl_t *aclp, int cols)
+{
+ int slot = 0;
+ char *token;
+ char *acltext;
+
+ acltext = acl_totext(aclp);
+
+ if (acltext == NULL)
+ return;
+
+ token = strtok(acltext, ",");
+ if (token == NULL) {
+ free(acltext);
+ return;
+ }
+
+ do {
+ (void) printf(" %d:", slot++);
+ split_line(token, cols - 5);
+ } while (token = strtok(NULL, ","));
+ free(acltext);
+}
+
+/*
+ * pretty print an ACL.
+ * For aclent_t ACL's the format is
+ * similar to the old format used by getfacl,
+ * with the addition of adding a "slot" number
+ * before each entry.
+ *
+ * for ace_t ACL's the cols variable will break up
+ * the long lines into multiple lines and will also
+ * print a "slot" number.
+ */
+void
+acl_printacl(acl_t *aclp, int cols)
+{
+
+ switch (aclp->acl_type) {
+ case ACLENT_T:
+ aclent_printacl(aclp);
+ break;
+ case ACE_T:
+ ace_printacl(aclp, cols);
+ break;
+ }
+}
+
+
+/*
+ * return text for an ACL error.
+ */
+char *
+acl_strerror(int errnum)
+{
+ switch (errnum) {
+ case EACL_GRP_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "There is more than one user group owner entry"));
+ case EACL_USER_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "There is more than one user owner entry"));
+ case EACL_OTHER_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "There is more than one other entry"));
+ case EACL_CLASS_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "There is more than one mask entry"));
+ case EACL_DUPLICATE_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "Duplicate user or group entries"));
+ case EACL_MISS_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "Missing user/group owner, other, mask entry"));
+ case EACL_MEM_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "Memory error"));
+ case EACL_ENTRY_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "Unrecognized entry type"));
+ case EACL_INHERIT_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "Invalid inheritance flags"));
+ case EACL_FLAGS_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "Unrecognized entry flags"));
+ case EACL_PERM_MASK_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "Invalid ACL permissions"));
+ case EACL_COUNT_ERROR:
+ return (dgettext(TEXT_DOMAIN,
+ "Invalid ACL count"));
+ case EACL_INVALID_SLOT:
+ return (dgettext(TEXT_DOMAIN,
+ "Invalid ACL entry number specified"));
+ case EACL_NO_ACL_ENTRY:
+ return (dgettext(TEXT_DOMAIN,
+ "ACL entry doesn't exist"));
+ case EACL_DIFF_TYPE:
+ return (dgettext(TEXT_DOMAIN,
+ "ACL type's are different"));
+ case EACL_INVALID_USER_GROUP:
+ return (dgettext(TEXT_DOMAIN, "Invalid user or group"));
+ case EACL_INVALID_STR:
+ return (dgettext(TEXT_DOMAIN, "ACL string is invalid"));
+ case EACL_FIELD_NOT_BLANK:
+ return (dgettext(TEXT_DOMAIN, "Field expected to be blank"));
+ case EACL_INVALID_ACCESS_TYPE:
+ return (dgettext(TEXT_DOMAIN, "Invalid access type"));
+ case EACL_UNKNOWN_DATA:
+ return (dgettext(TEXT_DOMAIN, "Unrecognized entry"));
+ case EACL_MISSING_FIELDS:
+ return (dgettext(TEXT_DOMAIN,
+ "ACL specification missing required fields"));
+ case EACL_INHERIT_NOTDIR:
+ return (dgettext(TEXT_DOMAIN,
+ "Inheritance flags are only allowed on directories"));
+ case -1:
+ return (strerror(errno));
+ default:
+ errno = EINVAL;
+ return (dgettext(TEXT_DOMAIN, "Unknown error"));
+ }
+}
diff --git a/usr/src/lib/libsec/common/aclutils.h b/usr/src/lib/libsec/common/aclutils.h
new file mode 100644
index 0000000000..b8e95dfe80
--- /dev/null
+++ b/usr/src/lib/libsec/common/aclutils.h
@@ -0,0 +1,85 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ACLUTILS_H
+#define _ACLUTILS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ACL_REMOVE_ALL 0x0
+#define ACL_REMOVE_FIRST 0x1
+
+/*
+ * Hint for whether acl_totext() should use
+ * mneumonics:
+ * read_data/list_directory
+ * write_data/add_file or
+ * append_data/add_subdirectory
+ * when object of ACL is known.
+ */
+#define ACL_IS_DIR 0x2
+
+typedef enum acl_type {
+ ACLENT_T = 0,
+ ACE_T = 1
+} acl_type_t;
+
+/*
+ * acl flags
+ */
+#define ACL_IS_TRIVIAL 0x1
+
+struct acl_info {
+ acl_type_t acl_type; /* style of acl */
+ int acl_cnt; /* number of acl entries */
+ int acl_entry_size; /* sizeof acl entry */
+ int acl_flags; /* special flags about acl */
+ void *acl_aclp; /* the acl */
+};
+
+
+extern int acl_addentries(acl_t *, acl_t *, int);
+extern int acl_removeentries(acl_t *, acl_t *, int, int);
+extern int acl_modifyentries(acl_t *, acl_t *, int);
+extern void acl_printacl(acl_t *, int);
+extern char *acl_strerror(int);
+extern acl_t *acl_dup(acl_t *);
+extern int acl_type(acl_t *);
+extern int acl_cnt(acl_t *);
+extern int acl_flags(acl_t *);
+extern void *acl_data(acl_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ACLUTILS_H */
diff --git a/usr/src/lib/libsec/common/llib-lsec b/usr/src/lib/libsec/common/llib-lsec
index 8db1f3875f..36d04ec197 100644
--- a/usr/src/lib/libsec/common/llib-lsec
+++ b/usr/src/lib/libsec/common/llib-lsec
@@ -23,8 +23,8 @@
/* PROTOLIB1 */
/*
- * Copyright (c) 1997 by Sun Microsystems, Inc.
- * All rights reserved.
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
@@ -37,10 +37,4 @@
#include <sys/param.h>
#include <sys/types.h>
#include <sys/acl.h>
-
-int aclcheck(aclent_t *aclbufp, int nentries, int *which);
-int aclfrommode(aclent_t *aclbufp, int nentries, mode_t *modep);
-aclent_t *aclfromtext(char *aclimport, int *aclcnt);
-int aclsort(int nentries, int calcmask, aclent_t *aclbufp);
-int acltomode(aclent_t *aclbufp, int nentries, mode_t *modep);
-char *acltotext(aclent_t *aclp, int aclcnt);
+#include <aclutils.h>
diff --git a/usr/src/lib/libsec/inc.flg b/usr/src/lib/libsec/inc.flg
new file mode 100644
index 0000000000..46ff3fd658
--- /dev/null
+++ b/usr/src/lib/libsec/inc.flg
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2005 Sun Microsystems, Inc.
+# All rights reserved. Use is subject to license terms.
+
+find_files "s.*" usr/src/common/acl
diff --git a/usr/src/lib/libsec/spec/acl.spec b/usr/src/lib/libsec/spec/acl.spec
index b69d7109e1..afedf74f34 100644
--- a/usr/src/lib/libsec/spec/acl.spec
+++ b/usr/src/lib/libsec/spec/acl.spec
@@ -41,6 +41,26 @@ exception ($return == GRP_ERROR || \
$return == MEM_ERROR)
end
+function acl_check
+include <sys/acl.h>
+declaration int acl_check(acl_t *aclp, int flag);
+version SUNW_1.2
+errno EINVAL
+exception ($return == EACL_GRP_ERROR || \
+ $return == EACL_USER_ERROR || \
+ $return == EACL_OTHER_ERROR || \
+ $return == EACL_CLASS_ERROR || \
+ $return == EACL_DUPLICATE_ERROR || \
+ $return == EACL_MISS_ERROR || \
+ $return == EACL_MEM_ERROR || \
+ $return == EACL_ENTRY_ERROR) || \
+ $return == EACL_INHERIT_ERROR || \
+ $return == EACL_FLAGS_ERROR || \
+ $return == EACL_PERM_MASK_ERROR || \
+ $return == EACL_COUNT_ERROR
+end
+
+
function aclsort
include <sys/acl.h>
declaration int aclsort(int nentries, int calclass, aclent_t *aclbufp)
@@ -78,3 +98,124 @@ version SUNW_0.9
exception $return == 0
end
+function acl_get
+include <sys/acl.h>
+declaration int acl_get(char *, int, acl_t **);
+version SUNW_1.2
+end
+
+function facl_get
+include <aclutils.h>
+declaration int facl_get(int, int, acl_t **);
+version SUNW_1.2
+end
+
+function acl_set
+include <sys/acl.h>
+declaration int acl_set(char *, acl_t *);
+version SUNW_1.2
+end
+
+function facl_set
+include <sys/acl.h>
+declaration int facl_set(int, acl_t *);
+version SUNW_1.2
+end
+
+function acl_strip
+include <sys/acl.h>
+declaration int acl_strip(char *, uid_t, gid_t, mode_t);
+version SUNW_1.2
+end
+
+function acl_trivial
+include <sys/acl.h>
+declaration int acl_trivial(char *file)
+version SUNW_1.2
+end
+
+function acl_totext
+include <sys/acl.h>
+declaration char *acl_totext(acl_t *acl);
+version SUNW_1.2
+exception $return == 0
+end
+
+function acl_fromtext
+include <sys/acl.h>
+declaration int acl_fromtext(char *textp, acl_t **);
+version SUNW_1.2
+end
+
+function acl_free
+include <sys/acl.h>
+declaration void acl_free(acl_t *aclp);
+version SUNW_1.2
+end
+
+function acl_addentries
+include <sys/acl.h>
+declaration int acl_addentries(acl_t *acl1, aclt_t *acl2, int slot);
+version SUNWprivate_1.1
+end
+
+function acl_removeentries
+include <sys/acl.h>
+declaration int acl_removeentries(acl_t *acl1, aclt_t *acl2, int, int);
+version SUNWprivate_1.1
+end
+
+function acl_printacl
+include <sys/acl.h>
+declaration void acl_printacl(acl_t *aclp, int cols);
+version SUNWprivate_1.1
+end
+
+function acl_strerror
+include <sys/acl.h>
+declaration char *acl_strerror(int errnum);
+version SUNWprivate_1.1
+end
+
+function acl_modifyentries
+include <sys/acl.h>
+declaration int acl_modifyentries(acl_t *acl1, acl_t *newentries,
+ int where);
+version SUNWprivate_1.1
+end
+
+function acl_alloc
+include <sys/acl.h>
+declaration int acl_alloc(enum acl_type);
+version SUNWprivate_1.1
+end
+
+function acl_dup
+include <aclutils.h>
+declaration acl_t acl_dup(acl_t *);
+version SUNWprivate_1.1
+end
+
+function acl_cnt
+include <aclutils.h>
+declaration int acl_cnt(acl_t *);
+version SUNWprivate_1.1
+end
+
+function acl_type
+include <aclutils.h>
+declaration int acl_type(acl_t *);
+version SUNWprivate_1.1
+end
+
+function acl_flags
+include <aclutils.h>
+declaration int acl_flags(acl_t *);
+version SUNWprivate_1.1
+end
+
+function acl_data
+include <aclutils.h>
+declaration void *acl_data(acl_t *);
+version SUNWprivate_1.1
+end
diff --git a/usr/src/lib/libsec/spec/versions b/usr/src/lib/libsec/spec/versions
index 710b438c72..c8e4665f4d 100644
--- a/usr/src/lib/libsec/spec/versions
+++ b/usr/src/lib/libsec/spec/versions
@@ -30,18 +30,26 @@
# (when it did contain symbols explicitly) may depend on it.
#
i386 {
+ SUNW_1.2: {SUNW_1.1};
SUNW_1.1: {SUNW_0.9};
SUNW_0.9;
+ SUNWprivate_1.1;
}
sparcv9 {
+ SUNW_1.2: {SUNW_1.1};
SUNW_1.1: {SUNW_0.9};
SUNW_0.9;
+ SUNWprivate_1.1;
}
sparc {
+ SUNW_1.2: {SUNW_1.1};
SUNW_1.1: {SUNW_0.9};
SUNW_0.9;
+ SUNWprivate_1.1;
}
amd64 {
+ SUNW_1.2: {SUNW_1.1};
SUNW_1.1: {SUNW_0.9};
SUNW_0.9;
+ SUNWprivate_1.1;
}
diff --git a/usr/src/lib/libsecdb/exec_attr.txt b/usr/src/lib/libsecdb/exec_attr.txt
index 36eaaabd01..8ae0023213 100644
--- a/usr/src/lib/libsecdb/exec_attr.txt
+++ b/usr/src/lib/libsecdb/exec_attr.txt
@@ -291,6 +291,8 @@ User Security:solaris:cmd:::/usr/sbin/passmgmt:uid=0
User Security:suser:cmd:::/usr/sbin/pwck:euid=0
User Security:suser:cmd:::/usr/sbin/pwconv:euid=0
DAT Administration:solaris:cmd:::/usr/sbin/datadm:euid=0
+ZFS File System Management:solaris:cmd:::/usr/sbin/zfs:euid=0
+ZFS Storage Management:solaris:cmd:::/usr/sbin/zpool:euid=0
Zone Management:solaris:cmd:::/usr/sbin/zonecfg:uid=0
Zone Management:solaris:cmd:::/usr/sbin/zoneadm:uid=0
Zone Management:solaris:cmd:::/usr/sbin/zlogin:uid=0
diff --git a/usr/src/lib/libsecdb/help/profiles/Makefile b/usr/src/lib/libsecdb/help/profiles/Makefile
index c6891cf9c4..1368939ed9 100644
--- a/usr/src/lib/libsecdb/help/profiles/Makefile
+++ b/usr/src/lib/libsecdb/help/profiles/Makefile
@@ -19,7 +19,7 @@
#
# CDDL HEADER END
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -63,6 +63,8 @@ HTMLENTS = \
RtUserMngmnt.html \
RtUserSecurity.html \
RtDatAdmin.html \
+ RtZFSFileSysMngmnt.html \
+ RtZFSStorageMngmnt.html \
RtZoneMngmnt.html \
RtDefault.html
diff --git a/usr/src/lib/libsecdb/help/profiles/RtZFSFileSysMngmnt.html b/usr/src/lib/libsecdb/help/profiles/RtZFSFileSysMngmnt.html
new file mode 100644
index 0000000000..d33cbab990
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/profiles/RtZFSFileSysMngmnt.html
@@ -0,0 +1,43 @@
+<HTML>
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (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 2005 Sun Microsystems, Inc. All rights reserved.
+-- Use is subject to license terms.
+-->
+<HEAD>
+ <TITLE> </TITLE>
+
+
+</HEAD>
+<BODY>
+<!-- ident "%Z%%M% %I% %E% SMI" -->
+
+When ZFS File System Management is in the Rights Included column, it grants the
+right to use commands needed to manage ZFS filesystems. This includes creating
+and destroying filesystems, taking snapshots and clones, getting and setting
+properties, and mounting and unmounting filesystesm.
+<p>
+If ZFS File System Management is grayed, then you are not entitled to Add or
+Remove this right.
+<p>
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/help/profiles/RtZFSStorageMngmnt.html b/usr/src/lib/libsecdb/help/profiles/RtZFSStorageMngmnt.html
new file mode 100644
index 0000000000..6317b6202a
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/profiles/RtZFSStorageMngmnt.html
@@ -0,0 +1,43 @@
+<HTML>
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License, Version 1.0 only
+ (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 2005 Sun Microsystems, Inc. All rights reserved.
+-- Use is subject to license terms.
+-->
+<HEAD>
+ <TITLE> </TITLE>
+
+
+</HEAD>
+<BODY>
+<!-- ident "%Z%%M% %I% %E% SMI" -->
+
+When ZFS Storage Management is in the Rights Included column, it grants the
+right to use commands needed to manage ZFS Storage Pools. This includes
+creating and destroying pools, adding and removing devices, replacing devices,
+an importing and exporting storage pools.
+<p>
+If ZFS Storage Management is grayed, then you are not entitled to Add or Remove
+this right.
+<p>
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt
index 131184f618..913d2b94dd 100644
--- a/usr/src/lib/libsecdb/prof_attr.txt
+++ b/usr/src/lib/libsecdb/prof_attr.txt
@@ -66,6 +66,8 @@ Crypto Management:::Cryptographic Framework Administration:help=RtCryptoMngmnt.h
Kerberos Client Management:::Maintain and Administer Kerberos excluding the servers:help=RtKerberosClntMngmnt.html
Kerberos Server Management:::Maintain and Administer Kerberos Servers:profiles=Kerberos Client Management;help=RtKerberosSrvrMngmnt.html
DAT Administration:::Manage the DAT configuration:help=RtDatAdmin.html
+ZFS File System Management:::Create and Manage ZFS File Systems:help=RtZFSFileSysMngmnt.html
+ZFS Storage Management:::Create and Manage ZFS Storage Pools:help=RtZFSStorageMngmnt.html
Zone Management:::Zones Virtual Application Environment Administration:help=RtZoneMngmnt.html
IP Filter Management:::IP Filter Administration:help=RtIPFilterMngmnt.html
Project Management:::Add/Modify/Remove projects:help=RtProjManagement.html
diff --git a/usr/src/lib/libzfs/Makefile b/usr/src/lib/libzfs/Makefile
new file mode 100644
index 0000000000..5a5e6abd84
--- /dev/null
+++ b/usr/src/lib/libzfs/Makefile
@@ -0,0 +1,67 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.lib
+
+HDRS= libzfs.h
+
+HDRDIR= common
+
+SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+
+MSGFILES = common/libzfs_dataset.c common/libzfs_mount.c \
+ common/libzfs_util.c
+POFILE = libzfs.po
+
+.KEEP_STATE:
+
+all clean clobber install: spec .WAIT $(SUBDIRS)
+
+$(POFILE): pofile_MSGFILES
+
+lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+_msg: $(MSGDOMAINPOFILE)
+
+$(SUBDIRS) spec: FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
+include ../../Makefile.msg.targ
diff --git a/usr/src/lib/libzfs/Makefile.com b/usr/src/lib/libzfs/Makefile.com
new file mode 100644
index 0000000000..f8d17fbf1a
--- /dev/null
+++ b/usr/src/lib/libzfs/Makefile.com
@@ -0,0 +1,68 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+LIBRARY= libzfs.a
+VERS= .1
+
+OBJS_SHARED= zfs_namecheck.o zfs_prop.o
+OBJS_COMMON= libzfs_dataset.o libzfs_util.o libzfs_graph.o libzfs_mount.o \
+ libzfs_pool.o libzfs_changelist.o libzfs_config.o libzfs_import.o \
+ libzfs_status.o
+OBJECTS= $(OBJS_COMMON) $(OBJS_SHARED)
+
+include ../../Makefile.lib
+
+LIBS= $(DYNLIB) $(LINTLIB)
+
+INCS += -I$(SRCDIR)
+INCS += -I../../../uts/common/fs/zfs
+INCS += -I../../../common/zfs
+
+C99MODE= -xc99=%all
+C99LMODE= -Xc99=%all
+LDLIBS += -lc -lm -ldevinfo -ldevid -lgen -lnvpair -luutil
+CPPFLAGS += $(INCS) -D_REENTRANT
+
+SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c) \
+ $(OBJS_SHARED:%.o=$(SRC)/common/zfs/%.c)
+$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
+
+SRCDIR= ../common
+MAPDIR= ../spec/$(TRANSMACH)
+SPECMAPFILE= $(MAPDIR)/mapfile
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+pics/%.o: ../../../common/zfs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libzfs/amd64/Makefile b/usr/src/lib/libzfs/amd64/Makefile
new file mode 100644
index 0000000000..44075ed1bd
--- /dev/null
+++ b/usr/src/lib/libzfs/amd64/Makefile
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h
new file mode 100644
index 0000000000..a9caff662c
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -0,0 +1,298 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBZFS_H
+#define _LIBZFS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <libnvpair.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/varargs.h>
+#include <sys/fs/zfs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Miscellaneous ZFS constants
+ */
+#define ZFS_MAXNAMELEN MAXNAMELEN
+#define ZPOOL_MAXNAMELEN MAXNAMELEN
+#define ZFS_MAXPROPLEN MAXPATHLEN
+
+/*
+ * Basic handle types
+ */
+typedef struct zfs_handle zfs_handle_t;
+typedef struct zpool_handle zpool_handle_t;
+
+/*
+ * Basic handle functions
+ */
+extern zpool_handle_t *zpool_open(const char *);
+extern zpool_handle_t *zpool_open_canfail(const char *);
+extern void zpool_close(zpool_handle_t *);
+extern const char *zpool_get_name(zpool_handle_t *);
+extern uint64_t zpool_get_guid(zpool_handle_t *);
+extern uint64_t zpool_get_space_used(zpool_handle_t *);
+extern uint64_t zpool_get_space_total(zpool_handle_t *);
+extern int zpool_get_root(zpool_handle_t *, char *, size_t);
+extern int zpool_get_state(zpool_handle_t *);
+
+/*
+ * Iterate over all active pools in the system.
+ */
+typedef int (*zpool_iter_f)(zpool_handle_t *, void *);
+extern int zpool_iter(zpool_iter_f, void *);
+
+/*
+ * Functions to create and destroy pools
+ */
+extern int zpool_create(const char *, nvlist_t *, const char *);
+extern int zpool_destroy(zpool_handle_t *);
+extern int zpool_add(zpool_handle_t *, nvlist_t *);
+
+/*
+ * Functions to manipulate pool and vdev state
+ */
+extern int zpool_scrub(zpool_handle_t *, pool_scrub_type_t);
+
+extern int zpool_vdev_online(zpool_handle_t *, const char *);
+extern int zpool_vdev_offline(zpool_handle_t *, const char *);
+extern int zpool_vdev_attach(zpool_handle_t *, const char *, const char *,
+ nvlist_t *, int);
+extern int zpool_vdev_detach(zpool_handle_t *, const char *);
+
+/*
+ * Pool health statistics.
+ */
+typedef enum {
+ /*
+ * The following correspond to faults as defined in the (fault.fs.zfs.*)
+ * event namespace. Each is associated with a correponding message ID.
+ */
+ ZPOOL_STATUS_CORRUPT_CACHE, /* corrupt /kernel/drv/zpool.cache */
+ ZPOOL_STATUS_MISSING_DEV_R, /* missing device with replicas */
+ ZPOOL_STATUS_MISSING_DEV_NR, /* missing device with no replicas */
+ ZPOOL_STATUS_CORRUPT_LABEL_R, /* bad device label with replicas */
+ ZPOOL_STATUS_CORRUPT_LABEL_NR, /* bad device lable with no replicas */
+ ZPOOL_STATUS_BAD_GUID_SUM, /* sum of device guids didn't match */
+ ZPOOL_STATUS_CORRUPT_POOL, /* pool metadata is corrupted */
+ ZPOOL_STATUS_CORRUPT_DATA, /* data errors in user (meta)data */
+ ZPOOL_STATUS_FAILING_DEV, /* device experiencing errors */
+ ZPOOL_STATUS_VERSION_MISMATCH, /* bad on-disk version */
+
+ /*
+ * The following are not faults per se, but still an error possibly
+ * requiring adminsitrative attention. There is no corresponding
+ * message ID.
+ */
+ ZPOOL_STATUS_RESILVERING, /* device being resilvered */
+ ZPOOL_STATUS_OFFLINE_DEV, /* device online */
+
+ /*
+ * Finally, the following indicates a healthy pool.
+ */
+ ZPOOL_STATUS_OK
+} zpool_status_t;
+
+extern zpool_status_t zpool_get_status(zpool_handle_t *, char **msgid);
+extern zpool_status_t zpool_import_status(nvlist_t *, char **msgid);
+
+/*
+ * Statistics and configuration functions.
+ */
+extern nvlist_t *zpool_get_config(zpool_handle_t *);
+extern int zpool_refresh_stats(zpool_handle_t *,
+ nvlist_t **oldconfig, nvlist_t **newconfig);
+
+/*
+ * Import and export functions
+ */
+extern int zpool_export(zpool_handle_t *);
+extern int zpool_import(nvlist_t *, const char *, const char *);
+
+/*
+ * Search for pools to import
+ */
+extern nvlist_t *zpool_find_import(int argc, char **argv);
+
+/*
+ * Basic handle manipulations. These functions do not create or destroy the
+ * underlying datasets, only the references to them.
+ */
+extern zfs_handle_t *zfs_open(const char *, int);
+extern void zfs_close(zfs_handle_t *);
+extern zfs_type_t zfs_get_type(const zfs_handle_t *);
+extern const char *zfs_get_name(const zfs_handle_t *);
+
+typedef enum {
+ ZFS_SRC_NONE = 0x1,
+ ZFS_SRC_DEFAULT = 0x2,
+ ZFS_SRC_TEMPORARY = 0x4,
+ ZFS_SRC_LOCAL = 0x8,
+ ZFS_SRC_INHERITED = 0x10
+} zfs_source_t;
+
+#define ZFS_SRC_ALL 0x1f
+
+/*
+ * Property management functions. Some functions are shared with the kernel,
+ * and are found in fs/zfs.h.
+ */
+const char *zfs_prop_to_name(zfs_prop_t);
+int zfs_prop_set(zfs_handle_t *, zfs_prop_t, const char *);
+int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t, zfs_source_t *,
+ char *, size_t, int);
+int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *, zfs_source_t *,
+ char *, size_t);
+uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
+int zfs_prop_validate(zfs_prop_t, const char *, uint64_t *);
+int zfs_prop_inheritable(zfs_prop_t);
+int zfs_prop_inherit(zfs_handle_t *, zfs_prop_t);
+const char *zfs_prop_values(zfs_prop_t);
+int zfs_prop_valid_for_type(zfs_prop_t, int);
+void zfs_prop_default_string(zfs_prop_t prop, char *buf, size_t buflen);
+uint64_t zfs_prop_default_numeric(zfs_prop_t);
+int zfs_prop_is_string(zfs_prop_t prop);
+const char *zfs_prop_column_name(zfs_prop_t);
+const char *zfs_prop_column_format(zfs_prop_t);
+char ** zfs_prop_column_subopts(void);
+char ** zfs_prop_column_short_subopts(void);
+
+#define ZFS_MOUNTPOINT_NONE "none"
+#define ZFS_MOUNTPOINT_LEGACY "legacy"
+
+/*
+ * Iterator functions.
+ */
+typedef int (*zfs_iter_f)(zfs_handle_t *, void *);
+extern int zfs_iter_root(zfs_iter_f, void *);
+extern int zfs_iter_children(zfs_handle_t *, zfs_iter_f, void *);
+extern int zfs_iter_dependents(zfs_handle_t *, zfs_iter_f, void *);
+
+/*
+ * Functions to create and destroy datasets.
+ */
+extern int zfs_create(const char *, zfs_type_t, const char *, const char *);
+extern int zfs_destroy(zfs_handle_t *);
+extern int zfs_clone(zfs_handle_t *, const char *);
+extern int zfs_snapshot(const char *);
+extern int zfs_rollback(zfs_handle_t *);
+extern int zfs_rename(zfs_handle_t *, const char *);
+extern int zfs_backup(zfs_handle_t *, zfs_handle_t *);
+extern int zfs_restore(const char *, int, int, int);
+
+/*
+ * Miscellaneous functions.
+ */
+extern const char *zfs_type_to_name(zfs_type_t);
+extern void zfs_refresh_properties(zfs_handle_t *);
+extern int zfs_name_valid(const char *, zfs_type_t);
+
+/*
+ * Mount support functions.
+ */
+extern int zfs_is_mounted(zfs_handle_t *, char **);
+extern int zfs_mount(zfs_handle_t *, const char *, int);
+extern int zfs_unmount(zfs_handle_t *, const char *, int);
+extern int zfs_unmountall(zfs_handle_t *, int);
+
+/*
+ * Share support functions.
+ */
+extern int zfs_is_shared(zfs_handle_t *, char **);
+extern int zfs_share(zfs_handle_t *);
+extern int zfs_unshare(zfs_handle_t *, const char *);
+extern int zfs_unshareall(zfs_handle_t *);
+
+/*
+ * For clients that need to capture error output.
+ */
+extern void zfs_set_error_handler(void (*)(const char *, va_list));
+
+/*
+ * When dealing with nvlists, verify() is extremely useful
+ */
+#ifdef NDEBUG
+#define verify(EX) ((void)(EX))
+#else
+#define verify(EX) assert(EX)
+#endif
+
+/*
+ * Utility function to convert a number to a human-readable form.
+ */
+extern void zfs_nicenum(uint64_t, char *, size_t);
+extern int zfs_nicestrtonum(const char *, uint64_t *);
+
+/*
+ * Pool destroy special. Remove the device information without destroying
+ * the underlying dataset.
+ */
+extern int zfs_remove_link(zfs_handle_t *);
+
+/*
+ * Given a device or file, determine if it is part of a pool.
+ */
+extern int zpool_in_use(int fd, char **state,
+ char **name);
+
+/*
+ * ftyp special. Read the label from a given device.
+ */
+extern nvlist_t *zpool_read_label(int fd);
+
+/*
+ * Create and remove zvol /dev links
+ */
+extern int zpool_create_zvol_links(zpool_handle_t *);
+extern int zpool_remove_zvol_links(zpool_handle_t *);
+
+/*
+ * zoneadmd hack
+ */
+extern void zfs_init(void);
+
+/*
+ * Useful defines
+ */
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBZFS_H */
diff --git a/usr/src/lib/libzfs/common/libzfs_changelist.c b/usr/src/lib/libzfs/common/libzfs_changelist.c
new file mode 100644
index 0000000000..497461e19f
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_changelist.c
@@ -0,0 +1,416 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libintl.h>
+#include <libuutil.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <zone.h>
+
+#include <libzfs.h>
+
+#include "libzfs_impl.h"
+
+/*
+ * Structure to keep track of dataset state. Before changing the 'sharenfs' or
+ * 'mountpoint' property, we record whether the filesystem was previously
+ * mounted/shared. This prior state dictates whether we remount/reshare the
+ * dataset after the property has been changed.
+ *
+ * The interface consists of the following sequence of functions:
+ *
+ * changelist_gather()
+ * changelist_prefix()
+ * < change property >
+ * changelist_postfix()
+ * changelist_free()
+ *
+ * Other interfaces:
+ *
+ * changelist_rename() - renames all datasets appropriately when doing a rename
+ * changelist_unshare() - unshares all the nodes in a given changelist
+ * changelist_haszonedchild() - check if there is any child exported to
+ * a local zone
+ */
+typedef struct prop_changenode {
+ zfs_handle_t *cn_handle;
+ int cn_shared;
+ int cn_mounted;
+ int cn_zoned;
+ uu_list_node_t cn_listnode;
+} prop_changenode_t;
+
+struct prop_changelist {
+ zfs_prop_t cl_prop;
+ zfs_prop_t cl_realprop;
+ uu_list_pool_t *cl_pool;
+ uu_list_t *cl_list;
+ int cl_waslegacy;
+ int cl_allchildren;
+ int cl_flags;
+ int cl_haszonedchild;
+};
+
+/*
+ * If the property is 'mountpoint', go through and unmount filesystems as
+ * necessary. We don't do the same for 'sharenfs', because we can just re-share
+ * with different options without interrupting service.
+ */
+int
+changelist_prefix(prop_changelist_t *clp)
+{
+ prop_changenode_t *cn;
+ int ret = 0;
+
+ if (clp->cl_prop != ZFS_PROP_MOUNTPOINT)
+ return (0);
+
+ for (cn = uu_list_first(clp->cl_list); cn != NULL;
+ cn = uu_list_next(clp->cl_list, cn)) {
+ /*
+ * if we are in a global zone, but this dataset is exported to
+ * a local zone, do nothing.
+ */
+ if ((getzoneid() == GLOBAL_ZONEID) && cn->cn_zoned)
+ continue;
+
+ /*
+ * If we have a volume and this was a rename, remove the
+ * /dev/zvol links
+ */
+ if (cn->cn_handle->zfs_volblocksize &&
+ clp->cl_realprop == ZFS_PROP_NAME) {
+ if (zvol_remove_link(cn->cn_handle->zfs_name) != 0)
+ ret = -1;
+ } else if (zfs_unmount(cn->cn_handle, NULL, clp->cl_flags) != 0)
+ ret = -1;
+ }
+
+ return (ret);
+}
+
+/*
+ * If the proeprty is 'mountpoint' or 'sharenfs', go through and remount and/or
+ * reshare the filesystems as necessary. In changelist_gather() we recorded
+ * whether the filesystem was previously shared or mounted. The action we take
+ * depends on the previous state, and whether the value was previously 'legacy'.
+ * For non-legacy properties, we only remount/reshare the filesystem if it was
+ * previously mounted/shared. Otherwise, we always remount/reshare the
+ * filesystem.
+ */
+int
+changelist_postfix(prop_changelist_t *clp)
+{
+ prop_changenode_t *cn;
+ int ret = 0;
+
+ /*
+ * If we're changing the mountpoint, attempt to destroy the underlying
+ * mountpoint. All other datasets will have inherited from this dataset
+ * (in which case their mountpoints exist in the filesystem in the new
+ * location), or have explicit mountpoints set (in which case they won't
+ * be in the changelist).
+ */
+ if ((cn = uu_list_last(clp->cl_list)) == NULL)
+ return (0);
+
+ if (clp->cl_prop == ZFS_PROP_MOUNTPOINT)
+ remove_mountpoint(cn->cn_handle);
+
+ /*
+ * We walk the datasets in reverse, because we want to mount any parent
+ * datasets before mounting the children.
+ */
+ for (cn = uu_list_last(clp->cl_list); cn != NULL;
+ cn = uu_list_prev(clp->cl_list, cn)) {
+ /*
+ * if we are in a global zone, but this dataset is exported to
+ * a local zone, do nothing.
+ */
+ if ((getzoneid() == GLOBAL_ZONEID) && cn->cn_zoned)
+ continue;
+
+ zfs_refresh_properties(cn->cn_handle);
+
+ /*
+ * If this is a volume and we're doing a rename, recreate the
+ * /dev/zvol links.
+ */
+ if (cn->cn_handle->zfs_volblocksize &&
+ clp->cl_realprop == ZFS_PROP_NAME) {
+ if (zvol_create_link(cn->cn_handle->zfs_name) != 0)
+ ret = -1;
+ continue;
+ }
+
+ if ((clp->cl_waslegacy || cn->cn_mounted) &&
+ !zfs_is_mounted(cn->cn_handle, NULL) &&
+ zfs_mount(cn->cn_handle, NULL, 0) != 0)
+ ret = -1;
+
+ /*
+ * We always re-share even if the filesystem is currently
+ * shared, so that we can adopt any new options.
+ */
+ if ((cn->cn_shared ||
+ (clp->cl_prop == ZFS_PROP_SHARENFS && clp->cl_waslegacy))) {
+ char shareopts[ZFS_MAXPROPLEN];
+ if (zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
+ shareopts, sizeof (shareopts), NULL, NULL, 0,
+ FALSE) == 0 && strcmp(shareopts, "off") == 0)
+ ret = zfs_unshare(cn->cn_handle, NULL);
+ else
+ ret = zfs_share(cn->cn_handle);
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * If we rename a filesystem, and child filesystem handles are no longer valid,
+ * since we identify datasets by their name in the ZFS namespace. So, we have
+ * to go through and fix up all the names appropriately. We could do this
+ * automatically if libzfs kept track of all open handles, but this is a lot
+ * less work.
+ */
+void
+changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
+{
+ prop_changenode_t *cn;
+ char newname[ZFS_MAXNAMELEN];
+
+ for (cn = uu_list_first(clp->cl_list); cn != NULL;
+ cn = uu_list_next(clp->cl_list, cn)) {
+ /*
+ * Destroy the previous mountpoint if needed.
+ */
+ remove_mountpoint(cn->cn_handle);
+
+ (void) strlcpy(newname, dst, sizeof (newname));
+ (void) strcat(newname, cn->cn_handle->zfs_name + strlen(src));
+
+ (void) strlcpy(cn->cn_handle->zfs_name, newname,
+ sizeof (cn->cn_handle->zfs_name));
+ }
+}
+
+/*
+ * Given a gathered changelist for the "sharenfs" property,
+ * unshare all the nodes in the list.
+ */
+int
+changelist_unshare(prop_changelist_t *clp)
+{
+ prop_changenode_t *cn;
+ int ret = 0;
+
+ if (clp->cl_prop != ZFS_PROP_SHARENFS)
+ return (0);
+
+ for (cn = uu_list_first(clp->cl_list); cn != NULL;
+ cn = uu_list_next(clp->cl_list, cn)) {
+
+ if (zfs_unshare(cn->cn_handle, NULL) != 0)
+ ret = -1;
+ }
+
+ return (ret);
+}
+
+/*
+ * Check if there is any child exported to a local zone in a
+ * given changelist. This information has already been recorded
+ * while gathering the changelist via changelist_gather().
+ */
+int
+changelist_haszonedchild(prop_changelist_t *clp)
+{
+ return (clp->cl_haszonedchild);
+}
+
+/*
+ * Release any memory associated with a changelist.
+ */
+void
+changelist_free(prop_changelist_t *clp)
+{
+ prop_changenode_t *cn;
+ uu_list_walk_t *walk;
+
+ verify((walk = uu_list_walk_start(clp->cl_list,
+ UU_WALK_ROBUST)) != NULL);
+
+ while ((cn = uu_list_walk_next(walk)) != NULL) {
+
+ uu_list_remove(clp->cl_list, cn);
+
+ zfs_close(cn->cn_handle);
+ free(cn);
+ }
+
+ uu_list_pool_destroy(clp->cl_pool);
+
+ free(clp);
+}
+
+static int
+change_one(zfs_handle_t *zhp, void *data)
+{
+ prop_changelist_t *clp = data;
+ char property[ZFS_MAXPROPLEN];
+ char where[64];
+ prop_changenode_t *cn;
+ zfs_source_t sourcetype;
+
+ /*
+ * We only want to unmount/unshare those filesystems which may
+ * inherit from the target filesystem. If we find any filesystem
+ * with a locally set mountpoint, we ignore any children since changing
+ * the property will not affect them. If this is a rename, we iterate
+ * over all children regardless, since we need them unmounted in order
+ * to do the rename. Also, if this is a volume and we're doing a
+ * rename, then always add it to the changelist.
+ */
+
+ if (!(zhp->zfs_volblocksize && clp->cl_realprop == ZFS_PROP_NAME) &&
+ zfs_prop_get(zhp, clp->cl_prop, property,
+ sizeof (property), &sourcetype, where, sizeof (where),
+ FALSE) != 0)
+ return (0);
+
+ if (clp->cl_allchildren || sourcetype == ZFS_SRC_DEFAULT ||
+ sourcetype == ZFS_SRC_INHERITED) {
+ cn = zfs_malloc(sizeof (prop_changenode_t));
+
+ cn->cn_handle = zhp;
+ cn->cn_mounted = zfs_is_mounted(zhp, NULL);
+ cn->cn_shared = zfs_is_shared(zhp, NULL);
+ cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
+
+ /* indicate if any child is exported to a local zone */
+ if ((getzoneid() == GLOBAL_ZONEID) && cn->cn_zoned)
+ clp->cl_haszonedchild = TRUE;
+
+ uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
+ verify(uu_list_insert_before(clp->cl_list,
+ uu_list_first(clp->cl_list), cn) == 0);
+
+ return (zfs_iter_children(zhp, change_one, data));
+ } else {
+ zfs_close(zhp);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Given a ZFS handle and a property, construct a complete list of datasets that
+ * need to be modified as part of this process. For anything but the
+ * 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
+ * Otherwise, we iterate over all children and look for any datasets which
+ * inherit this property. For each such dataset, we add it to the list and mark
+ * whether it was shared beforehand.
+ */
+prop_changelist_t *
+changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
+{
+ prop_changelist_t *clp = zfs_malloc(sizeof (prop_changelist_t));
+ prop_changenode_t *cn;
+ zfs_handle_t *temp;
+ char property[ZFS_MAXPROPLEN];
+
+ clp->cl_pool = uu_list_pool_create("changelist_pool",
+ sizeof (prop_changenode_t),
+ offsetof(prop_changenode_t, cn_listnode),
+ NULL, 0);
+ assert(clp->cl_pool != NULL);
+
+ clp->cl_list = uu_list_create(clp->cl_pool, NULL, 0);
+ clp->cl_flags = flags;
+
+ /*
+ * If this is a rename or the 'zoned' property, we pretend we're
+ * changing the mountpoint and flag it so we can catch all children in
+ * change_one().
+ */
+ if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED) {
+ clp->cl_prop = ZFS_PROP_MOUNTPOINT;
+ clp->cl_allchildren = TRUE;
+ } else {
+ clp->cl_prop = prop;
+ }
+ clp->cl_realprop = prop;
+
+ if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
+ clp->cl_prop != ZFS_PROP_SHARENFS)
+ return (clp);
+
+ if (zfs_iter_children(zhp, change_one, clp) != 0) {
+ changelist_free(clp);
+ return (NULL);
+ }
+
+ /*
+ * We have to re-open ourselves because we auto-close all the handles
+ * and can't tell the difference.
+ */
+ if ((temp = zfs_open(zfs_get_name(zhp), ZFS_TYPE_ANY)) == NULL) {
+ free(clp);
+ return (NULL);
+ }
+
+ /*
+ * Always add ourself to the list. We add ourselves to the end so that
+ * we're the last to be unmounted.
+ */
+ cn = zfs_malloc(sizeof (prop_changenode_t));
+ cn->cn_handle = temp;
+ cn->cn_mounted = zfs_is_mounted(temp, NULL);
+ cn->cn_shared = zfs_is_shared(temp, NULL);
+ cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
+
+ uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
+ verify(uu_list_insert_after(clp->cl_list,
+ uu_list_last(clp->cl_list), cn) == 0);
+
+ /*
+ * If the property was previously 'legacy' or 'none', record this fact,
+ * as the behavior of changelist_postfix() will be different.
+ */
+ if (zfs_prop_get(zhp, prop, property, sizeof (property),
+ NULL, NULL, 0, FALSE) == 0 &&
+ (strcmp(property, "legacy") == 0 || strcmp(property, "none") == 0 ||
+ strcmp(property, "off") == 0))
+ clp->cl_waslegacy = TRUE;
+
+ return (clp);
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_config.c b/usr/src/lib/libzfs/common/libzfs_config.c
new file mode 100644
index 0000000000..4c5a22a459
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_config.c
@@ -0,0 +1,309 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * The pool configuration repository is stored in /etc/zfs/zpool.cache as a
+ * single packed nvlist. While it would be nice to just read in this
+ * file from userland, this wouldn't work from a local zone. So we have to have
+ * a zpool ioctl to return the complete configuration for all pools. In the
+ * global zone, this will be identical to reading the file and unpacking it in
+ * userland.
+ */
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <libuutil.h>
+
+#include "libzfs_impl.h"
+
+static uu_avl_t *namespace_avl;
+static uint64_t namespace_generation;
+
+typedef struct config_node {
+ char *cn_name;
+ nvlist_t *cn_config;
+ uu_avl_node_t cn_avl;
+} config_node_t;
+
+/* ARGSUSED */
+static int
+config_node_compare(const void *a, const void *b, void *unused)
+{
+ int ret;
+
+ const config_node_t *ca = (config_node_t *)a;
+ const config_node_t *cb = (config_node_t *)b;
+
+ ret = strcmp(ca->cn_name, cb->cn_name);
+
+ if (ret < 0)
+ return (-1);
+ else if (ret > 0)
+ return (1);
+ else
+ return (0);
+}
+
+/*
+ * Loads the pool namespace, or re-loads it if the cache has changed.
+ */
+static void
+namespace_reload()
+{
+ nvlist_t *config;
+ config_node_t *cn;
+ nvpair_t *elem;
+ zfs_cmd_t zc = { 0 };
+ uu_avl_walk_t *walk;
+
+ if (namespace_generation == 0) {
+ /*
+ * This is the first time we've accessed the configuration
+ * cache. Initialize the AVL tree and then fall through to the
+ * common code.
+ */
+ uu_avl_pool_t *pool;
+
+ if ((pool = uu_avl_pool_create("config_pool",
+ sizeof (config_node_t),
+ offsetof(config_node_t, cn_avl),
+ config_node_compare, UU_DEFAULT)) == NULL)
+ no_memory();
+
+ if ((namespace_avl = uu_avl_create(pool, NULL,
+ UU_DEFAULT)) == NULL)
+ no_memory();
+ }
+
+ /*
+ * Issue the ZFS_IOC_POOL_CONFIGS ioctl.
+ * This can fail for one of two reasons:
+ *
+ * EEXIST The generation counts match, nothing to do.
+ * ENOMEM The zc_config_dst buffer isn't large enough to
+ * hold the config; zc_config_dst_size will have
+ * been modified to tell us how much to allocate.
+ */
+ zc.zc_config_dst_size = 1024;
+ zc.zc_config_dst = (uint64_t)(uintptr_t)
+ zfs_malloc(zc.zc_config_dst_size);
+ for (;;) {
+ zc.zc_cookie = namespace_generation;
+ if (ioctl(zfs_fd, ZFS_IOC_POOL_CONFIGS, &zc) != 0) {
+ switch (errno) {
+ case EEXIST:
+ /*
+ * The namespace hasn't changed.
+ */
+ free((void *)(uintptr_t)zc.zc_config_dst);
+ return;
+
+ case ENOMEM:
+ free((void *)(uintptr_t)zc.zc_config_dst);
+ zc.zc_config_dst = (uint64_t)(uintptr_t)
+ zfs_malloc(zc.zc_config_dst_size);
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+ } else {
+ namespace_generation = zc.zc_cookie;
+ break;
+ }
+ }
+
+ verify(nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst,
+ zc.zc_config_dst_size, &config, 0) == 0);
+
+ free((void *)(uintptr_t)zc.zc_config_dst);
+
+ /*
+ * Clear out any existing configuration information.
+ */
+ if ((walk = uu_avl_walk_start(namespace_avl, UU_WALK_ROBUST)) == NULL)
+ no_memory();
+
+ while ((cn = uu_avl_walk_next(walk)) != NULL) {
+ uu_avl_remove(namespace_avl, cn);
+ nvlist_free(cn->cn_config);
+ free(cn->cn_name);
+ free(cn);
+ }
+
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(config, elem)) != NULL) {
+ nvlist_t *child;
+ uu_avl_index_t where;
+
+ cn = zfs_malloc(sizeof (config_node_t));
+ cn->cn_name = zfs_strdup(nvpair_name(elem));
+
+ verify(nvpair_value_nvlist(elem, &child) == 0);
+ verify(nvlist_dup(child, &cn->cn_config, 0) == 0);
+ verify(uu_avl_find(namespace_avl, cn, NULL, &where) == NULL);
+
+ uu_avl_insert(namespace_avl, cn, where);
+ }
+
+ nvlist_free(config);
+}
+
+/*
+ * Retrive the configuration for the given pool. The configuration is a nvlist
+ * describing the vdevs, as well as the statistics associated with each one.
+ */
+nvlist_t *
+zpool_get_config(zpool_handle_t *zhp)
+{
+ return (zhp->zpool_config);
+}
+
+/*
+ * Refresh the vdev statistics associated with the given pool. This is used in
+ * iostat to show configuration changes and determine the delta from the last
+ * time the function was called. This function can fail, in case the pool has
+ * been destroyed.
+ */
+int
+zpool_refresh_stats(zpool_handle_t *zhp, nvlist_t **oldconfig,
+ nvlist_t **newconfig)
+{
+ zfs_cmd_t zc = { 0 };
+ int error;
+
+ (void) strcpy(zc.zc_name, zhp->zpool_name);
+
+ if (zhp->zpool_config_size == 0)
+ zhp->zpool_config_size = 1 << 16;
+
+ zc.zc_config_dst_size = zhp->zpool_config_size;
+ zc.zc_config_dst = (uint64_t)(uintptr_t)
+ zfs_malloc(zc.zc_config_dst_size);
+
+ while ((error = ioctl(zfs_fd, ZFS_IOC_POOL_STATS, &zc)) != 0) {
+ error = errno;
+
+ if (error == ENXIO) {
+ /*
+ * We can't open one or more top-level vdevs,
+ * but we have the config.
+ */
+ break;
+ }
+
+ free((void *)(uintptr_t)zc.zc_config_dst);
+
+ if (error == ENOENT || error == EINVAL) {
+ /*
+ * There's no such pool (ENOENT)
+ * or the config is bogus (EINVAL).
+ */
+ return (error);
+ }
+
+ if (error != ENOMEM)
+ zfs_baderror(error);
+
+ zc.zc_config_dst =
+ (uint64_t)(uintptr_t)zfs_malloc(zc.zc_config_dst_size);
+ }
+
+ verify(nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst,
+ zc.zc_config_dst_size, newconfig, 0) == 0);
+
+ zhp->zpool_config_size = zc.zc_config_dst_size;
+ free((void *)(uintptr_t)zc.zc_config_dst);
+
+ set_pool_health(*newconfig);
+
+ if (oldconfig != NULL)
+ *oldconfig = zhp->zpool_config;
+ else
+ nvlist_free(zhp->zpool_config);
+
+ zhp->zpool_config = *newconfig;
+
+ return (error);
+}
+
+/*
+ * Iterate over all pools in the system.
+ */
+int
+zpool_iter(zpool_iter_f func, void *data)
+{
+ config_node_t *cn;
+ zpool_handle_t *zhp;
+ int ret;
+
+ namespace_reload();
+
+ for (cn = uu_avl_first(namespace_avl); cn != NULL;
+ cn = uu_avl_next(namespace_avl, cn)) {
+
+ if ((zhp = zpool_open_silent(cn->cn_name)) == NULL)
+ continue;
+
+ if ((ret = func(zhp, data)) != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+/*
+ * Iterate over root datasets, calling the given function for each. The zfs
+ * handle passed each time must be explicitly closed by the callback.
+ */
+int
+zfs_iter_root(zfs_iter_f func, void *data)
+{
+ config_node_t *cn;
+ zfs_handle_t *zhp;
+ int ret;
+
+ namespace_reload();
+
+ for (cn = uu_avl_first(namespace_avl); cn != NULL;
+ cn = uu_avl_next(namespace_avl, cn)) {
+
+ if ((zhp = make_dataset_handle(cn->cn_name)) == NULL)
+ continue;
+
+ if ((ret = func(zhp, data)) != 0)
+ return (ret);
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
new file mode 100644
index 0000000000..5a4b1d92be
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -0,0 +1,2939 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <libdevinfo.h>
+#include <libintl.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <zone.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+
+#include <sys/spa.h>
+#include <sys/zio.h>
+#include <libzfs.h>
+
+#include "zfs_namecheck.h"
+#include "zfs_prop.h"
+#include "libzfs_impl.h"
+
+/*
+ * Given a single type (not a mask of types), return the type in a human
+ * readable form.
+ */
+const char *
+zfs_type_to_name(zfs_type_t type)
+{
+ switch (type) {
+ case ZFS_TYPE_FILESYSTEM:
+ return (dgettext(TEXT_DOMAIN, "filesystem"));
+ case ZFS_TYPE_SNAPSHOT:
+ return (dgettext(TEXT_DOMAIN, "snapshot"));
+ case ZFS_TYPE_VOLUME:
+ return (dgettext(TEXT_DOMAIN, "volume"));
+ }
+
+ zfs_baderror(type);
+ return (NULL);
+}
+
+/*
+ * Given a path and mask of ZFS types, return a string describing this dataset.
+ * This is used when we fail to open a dataset and we cannot get an exact type.
+ * We guess what the type would have been based on the path and the mask of
+ * acceptable types.
+ */
+static const char *
+path_to_str(const char *path, int types)
+{
+ /*
+ * When given a single type, always report the exact type.
+ */
+ if (types == ZFS_TYPE_SNAPSHOT)
+ return (dgettext(TEXT_DOMAIN, "snapshot"));
+ if (types == ZFS_TYPE_FILESYSTEM)
+ return (dgettext(TEXT_DOMAIN, "filesystem"));
+ if (types == ZFS_TYPE_VOLUME)
+ return (dgettext(TEXT_DOMAIN, "volume"));
+
+ /*
+ * The user is requesting more than one type of dataset. If this is the
+ * case, consult the path itself. If we're looking for a snapshot, and
+ * a '@' is found, then report it as "snapshot". Otherwise, remove the
+ * snapshot attribute and try again.
+ */
+ if (types & ZFS_TYPE_SNAPSHOT) {
+ if (strchr(path, '@') != NULL)
+ return (dgettext(TEXT_DOMAIN, "snapshot"));
+ return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT));
+ }
+
+
+ /*
+ * The user has requested either filesystems or volumes.
+ * We have no way of knowing a priori what type this would be, so always
+ * report it as "filesystem" or "volume", our two primitive types.
+ */
+ if (types & ZFS_TYPE_FILESYSTEM)
+ return (dgettext(TEXT_DOMAIN, "filesystem"));
+
+ assert(types & ZFS_TYPE_VOLUME);
+ return (dgettext(TEXT_DOMAIN, "volume"));
+}
+
+/*
+ * Validate a ZFS path. This is used even before trying to open the dataset, to
+ * provide a more meaningful error message. We place a more useful message in
+ * 'buf' detailing exactly why the name was not valid.
+ */
+static int
+zfs_validate_name(const char *path, int type, char *buf, size_t buflen)
+{
+ namecheck_err_t why;
+ char what;
+
+ if (dataset_namecheck(path, &why, &what) != 0) {
+ if (buf != NULL) {
+ switch (why) {
+ case NAME_ERR_LEADING_SLASH:
+ (void) strlcpy(buf, dgettext(TEXT_DOMAIN,
+ "leading slash"), buflen);
+ break;
+
+ case NAME_ERR_EMPTY_COMPONENT:
+ (void) strlcpy(buf, dgettext(TEXT_DOMAIN,
+ "empty component"), buflen);
+ break;
+
+ case NAME_ERR_TRAILING_SLASH:
+ (void) strlcpy(buf, dgettext(TEXT_DOMAIN,
+ "trailing slash"), buflen);
+ break;
+
+ case NAME_ERR_INVALCHAR:
+ (void) snprintf(buf, buflen,
+ dgettext(TEXT_DOMAIN, "invalid character "
+ "'%c'"), what);
+ break;
+
+ case NAME_ERR_MULTIPLE_AT:
+ (void) strlcpy(buf, dgettext(TEXT_DOMAIN,
+ "multiple '@' delimiters"), buflen);
+ break;
+ }
+ }
+
+ return (0);
+ }
+
+ if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
+ if (buf != NULL)
+ (void) strlcpy(buf,
+ dgettext(TEXT_DOMAIN,
+ "snapshot delimiter '@'"), buflen);
+ return (0);
+ }
+
+ return (1);
+}
+
+int
+zfs_name_valid(const char *name, zfs_type_t type)
+{
+ return (zfs_validate_name(name, type, NULL, NULL));
+}
+
+/*
+ * Utility function to gather stats (objset and zpl) for the given object.
+ */
+static int
+get_stats(zfs_handle_t *zhp)
+{
+ zfs_cmd_t zc = { 0 };
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ /*
+ * get the generic DMU stats and per-type (zfs, zvol) stats
+ */
+ if (ioctl(zfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
+ return (-1);
+
+ bcopy(&zc.zc_objset_stats, &zhp->zfs_dmustats,
+ sizeof (zc.zc_objset_stats));
+
+ bcopy(&zc.zc_zfs_stats, &zhp->zfs_zplstats, sizeof (zc.zc_zfs_stats));
+
+ zhp->zfs_volsize = zc.zc_volsize;
+ zhp->zfs_volblocksize = zc.zc_volblocksize;
+
+ return (0);
+}
+
+/*
+ * Refresh the properties currently stored in the handle.
+ */
+void
+zfs_refresh_properties(zfs_handle_t *zhp)
+{
+ (void) get_stats(zhp);
+}
+
+/*
+ * Makes a handle from the given dataset name. Used by zfs_open() and
+ * zfs_iter_* to create child handles on the fly.
+ */
+zfs_handle_t *
+make_dataset_handle(const char *path)
+{
+ zfs_handle_t *zhp = zfs_malloc(sizeof (zfs_handle_t));
+
+ (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
+
+ if (get_stats(zhp) != 0) {
+ free(zhp);
+ return (NULL);
+ }
+
+ /*
+ * We've managed to open the dataset and gather statistics. Determine
+ * the high-level type.
+ */
+ if (zhp->zfs_dmustats.dds_is_snapshot)
+ zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
+ else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
+ zhp->zfs_type = ZFS_TYPE_VOLUME;
+ else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
+ zhp->zfs_type = ZFS_TYPE_FILESYSTEM;
+ else
+ /* we should never see any other dataset types */
+ zfs_baderror(zhp->zfs_dmustats.dds_type);
+
+ return (zhp);
+}
+
+/*
+ * Opens the given snapshot, filesystem, or volume. The 'types'
+ * argument is a mask of acceptable types. The function will print an
+ * appropriate error message and return NULL if it can't be opened.
+ */
+zfs_handle_t *
+zfs_open(const char *path, int types)
+{
+ zfs_handle_t *zhp;
+
+ /*
+ * If the path is longer than the maximum dataset length, treat it as
+ * ENOENT because we know there can't be any dataset with that path.
+ */
+ if (strlen(path) >= ZFS_MAXNAMELEN) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot open '%s': no such %s"), path,
+ path_to_str(path, types));
+ return (NULL);
+ }
+
+ /*
+ * Validate the name before we even try to open it. We don't care about
+ * the verbose invalid messages here; just report a generic error.
+ */
+ if (!zfs_validate_name(path, types, NULL, 0)) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot open '%s': invalid %s name"), path,
+ path_to_str(path, types));
+ return (NULL);
+ }
+
+ /*
+ * Try to get stats for the dataset, which will tell us if it exists.
+ */
+ errno = 0;
+ if ((zhp = make_dataset_handle(path)) == NULL) {
+ switch (errno) {
+ case ENOENT:
+ /*
+ * The dataset doesn't exist.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot open '%s': no such %s"), path,
+ path_to_str(path, types));
+ break;
+
+ case EBUSY:
+ /*
+ * We were able to open the dataset but couldn't
+ * get the stats.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot open '%s': %s is busy"), path,
+ path_to_str(path, types));
+ break;
+
+ default:
+ zfs_baderror(errno);
+
+ }
+ return (NULL);
+ }
+
+ if (!(types & zhp->zfs_type)) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot open '%s': operation "
+ "not supported for %ss"), path,
+ zfs_type_to_name(zhp->zfs_type));
+ free(zhp);
+ return (NULL);
+ }
+
+ return (zhp);
+}
+
+/*
+ * Release a ZFS handle. Nothing to do but free the associated memory.
+ */
+void
+zfs_close(zfs_handle_t *zhp)
+{
+ if (zhp->zfs_mntopts)
+ free(zhp->zfs_mntopts);
+ free(zhp);
+}
+
+struct {
+ const char *name;
+ uint64_t value;
+} checksum_table[] = {
+ { "on", ZIO_CHECKSUM_ON },
+ { "off", ZIO_CHECKSUM_OFF },
+ { "fletcher2", ZIO_CHECKSUM_FLETCHER_2 },
+ { "fletcher4", ZIO_CHECKSUM_FLETCHER_4 },
+ { "sha256", ZIO_CHECKSUM_SHA256 },
+ { NULL }
+};
+
+struct {
+ const char *name;
+ uint64_t value;
+} compress_table[] = {
+ { "on", ZIO_COMPRESS_ON },
+ { "off", ZIO_COMPRESS_OFF },
+ { "lzjb", ZIO_COMPRESS_LZJB },
+ { NULL }
+};
+
+struct {
+ const char *name;
+ uint64_t value;
+} snapdir_table[] = {
+ { "hidden", HIDDEN },
+ { "visible", VISIBLE },
+ { NULL }
+};
+
+struct {
+ const char *name;
+ uint64_t value;
+} acl_mode_table[] = {
+ { "discard", DISCARD },
+ { "groupmask", GROUPMASK },
+ { "passthrough", PASSTHROUGH },
+ { NULL }
+};
+
+struct {
+ const char *name;
+ uint64_t value;
+} acl_inherit_table[] = {
+ { "discard", DISCARD },
+ { "noallow", NOALLOW },
+ { "secure", SECURE },
+ { "passthrough", PASSTHROUGH },
+ { NULL }
+};
+
+
+/*
+ * Given a numeric suffix, convert the value into a number of bits that the
+ * resulting value must be shifted.
+ */
+static int
+str2shift(const char *buf, char *reason, size_t len)
+{
+ const char *ends = "BKMGTPEZ";
+ int i;
+
+ if (buf[0] == '\0')
+ return (0);
+ for (i = 0; i < strlen(ends); i++) {
+ if (toupper(buf[0]) == ends[i])
+ break;
+ }
+ if (i == strlen(ends)) {
+ (void) snprintf(reason, len, dgettext(TEXT_DOMAIN, "invalid "
+ "numeric suffix '%s'"), buf);
+ return (-1);
+ }
+
+ /*
+ * We want to allow trailing 'b' characters for 'GB' or 'Mb'. But don't
+ * allow 'BB' - that's just weird.
+ */
+ if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' &&
+ toupper(buf[0]) != 'B')) {
+ return (10*i);
+ }
+
+ (void) snprintf(reason, len, dgettext(TEXT_DOMAIN, "invalid numeric "
+ "suffix '%s'"), buf);
+ return (-1);
+}
+
+/*
+ * Convert a string of the form '100G' into a real number. Used when setting
+ * properties or creating a volume. 'buf' is used to place an extended error
+ * message for the caller to use.
+ */
+static int
+nicestrtonum(const char *value, uint64_t *num, char *buf, size_t buflen)
+{
+ char *end;
+ int shift;
+
+ *num = 0;
+
+ /* Check to see if this looks like a number. */
+ if ((value[0] < '0' || value[0] > '9') && value[0] != '.') {
+ (void) strlcpy(buf, dgettext(TEXT_DOMAIN,
+ "must be a numeric value"), buflen);
+ return (-1);
+ }
+
+ /* Rely on stroll() to process the numeric portion. */
+ errno = 0;
+ *num = strtoll(value, &end, 10);
+
+ /*
+ * Check for ERANGE, which indicates that the value is too large to fit
+ * in a 64-bit value.
+ */
+ if (errno == ERANGE) {
+ (void) strlcpy(buf, dgettext(TEXT_DOMAIN,
+ "value is too large"), buflen);
+ return (-1);
+ }
+
+ /*
+ * If we have a decimal value, then do the computation with floating
+ * point arithmetic. Otherwise, use standard arithmetic.
+ */
+ if (*end == '.') {
+ double fval = strtod(value, &end);
+
+ if ((shift = str2shift(end, buf, buflen)) == -1)
+ return (-1);
+
+ fval *= pow(2, shift);
+
+ if (fval > UINT64_MAX) {
+ (void) strlcpy(buf, dgettext(TEXT_DOMAIN,
+ "value is too large"), buflen);
+ return (-1);
+ }
+
+ *num = (uint64_t)fval;
+ } else {
+ if ((shift = str2shift(end, buf, buflen)) == -1)
+ return (-1);
+
+ /* Check for overflow */
+ if (shift >= 64 || (*num << shift) >> shift != *num) {
+ (void) strlcpy(buf, dgettext(TEXT_DOMAIN,
+ "value is too large"), buflen);
+ return (-1);
+ }
+
+ *num <<= shift;
+ }
+
+ return (0);
+}
+
+int
+zfs_nicestrtonum(const char *str, uint64_t *val)
+{
+ char buf[1];
+
+ return (nicestrtonum(str, val, buf, sizeof (buf)));
+}
+
+/*
+ * Given a property type and value, verify that the value is appropriate. Used
+ * by zfs_prop_set() and some libzfs consumers.
+ */
+int
+zfs_prop_validate(zfs_prop_t prop, const char *value, uint64_t *intval)
+{
+ const char *propname = zfs_prop_to_name(prop);
+ uint64_t number;
+ char reason[64];
+ int i;
+
+ /*
+ * Check to see if this a read-only property.
+ */
+ if (zfs_prop_readonly(prop)) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot set %s property: read-only property"), propname);
+ return (-1);
+ }
+
+ /* See if the property value is too long */
+ if (strlen(value) >= ZFS_MAXPROPLEN) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad %s value '%s': value is too long"), propname,
+ value);
+ return (-1);
+ }
+
+ /* Perform basic checking based on property type */
+ switch (zfs_prop_get_type(prop)) {
+ case prop_type_boolean:
+ if (strcmp(value, "on") == 0) {
+ number = 1;
+ } else if (strcmp(value, "off") == 0) {
+ number = 0;
+ } else {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad %s value '%s': must be 'on' or 'off'"),
+ propname, value);
+ return (-1);
+ }
+ break;
+
+ case prop_type_number:
+ /* treat 'none' as 0 */
+ if (strcmp(value, "none") == 0) {
+ number = 0;
+ break;
+ }
+
+ if (nicestrtonum(value, &number, reason,
+ sizeof (reason)) != 0) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad %s value '%s': %s"), propname, value,
+ reason);
+ return (-1);
+ }
+
+ /* don't allow 0 for quota, use 'none' instead */
+ if (prop == ZFS_PROP_QUOTA && number == 0 &&
+ strcmp(value, "none") != 0) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad %s value '%s': use '%s=none' to disable"),
+ propname, value, propname);
+ return (-1);
+ }
+
+ /* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
+ if (prop == ZFS_PROP_RECORDSIZE ||
+ prop == ZFS_PROP_VOLBLOCKSIZE) {
+ if (number < SPA_MINBLOCKSIZE ||
+ number > SPA_MAXBLOCKSIZE || !ISP2(number)) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad %s value '%s': "
+ "must be power of 2 from %u to %uk"),
+ propname, value,
+ (uint_t)SPA_MINBLOCKSIZE,
+ (uint_t)SPA_MAXBLOCKSIZE >> 10);
+ return (-1);
+ }
+ }
+
+ break;
+
+ case prop_type_string:
+ case prop_type_index:
+ /*
+ * The two writable string values, 'mountpoint' and
+ * 'checksum' need special consideration. The 'index' types are
+ * specified as strings by the user, but passed to the kernel as
+ * integers.
+ */
+ switch (prop) {
+ case ZFS_PROP_MOUNTPOINT:
+ if (strcmp(value, ZFS_MOUNTPOINT_NONE) == 0 ||
+ strcmp(value, ZFS_MOUNTPOINT_LEGACY) == 0)
+ break;
+
+ if (value[0] != '/') {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad %s value '%s': must be an absolute "
+ "path, 'none', or 'legacy'"),
+ propname, value);
+ return (-1);
+ }
+ break;
+
+ case ZFS_PROP_CHECKSUM:
+ for (i = 0; checksum_table[i].name != NULL; i++) {
+ if (strcmp(value, checksum_table[i].name)
+ == 0) {
+ number = checksum_table[i].value;
+ break;
+ }
+ }
+
+ if (checksum_table[i].name == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad %s value '%s': must be 'on', 'off', "
+ "'fletcher2', 'fletcher4', or 'sha256'"),
+ propname, value);
+ return (-1);
+ }
+ break;
+
+ case ZFS_PROP_COMPRESSION:
+ for (i = 0; compress_table[i].name != NULL; i++) {
+ if (strcmp(value, compress_table[i].name)
+ == 0) {
+ number = compress_table[i].value;
+ break;
+ }
+ }
+
+ if (compress_table[i].name == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad %s value '%s': must be 'on', 'off', "
+ "or 'lzjb'"),
+ propname, value);
+ return (-1);
+ }
+ break;
+
+ case ZFS_PROP_SNAPDIR:
+ for (i = 0; snapdir_table[i].name != NULL; i++) {
+ if (strcmp(value, snapdir_table[i].name) == 0) {
+ number = snapdir_table[i].value;
+ break;
+ }
+ }
+
+ if (snapdir_table[i].name == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad %s value '%s': must be 'hidden' "
+ "or 'visible'"),
+ propname, value);
+ return (-1);
+ }
+ break;
+
+ case ZFS_PROP_ACLMODE:
+ for (i = 0; acl_mode_table[i].name != NULL; i++) {
+ if (strcmp(value, acl_mode_table[i].name)
+ == 0) {
+ number = acl_mode_table[i].value;
+ break;
+ }
+ }
+
+ if (acl_mode_table[i].name == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad %s value '%s': must be 'discard', "
+ "'groupmask' or 'passthrough'"),
+ propname, value);
+ return (-1);
+ }
+ break;
+
+ case ZFS_PROP_ACLINHERIT:
+ for (i = 0; acl_inherit_table[i].name != NULL; i++) {
+ if (strcmp(value, acl_inherit_table[i].name)
+ == 0) {
+ number = acl_inherit_table[i].value;
+ break;
+ }
+ }
+
+ if (acl_inherit_table[i].name == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad %s value '%s': must be 'discard', "
+ "'noallow', 'groupmask' or 'passthrough'"),
+ propname, value);
+ return (-1);
+ }
+ break;
+
+ case ZFS_PROP_SHARENFS:
+ /*
+ * Nothing to do for 'sharenfs', this gets passed on to
+ * share(1M) verbatim.
+ */
+ break;
+ }
+ }
+
+ if (intval != NULL)
+ *intval = number;
+
+ return (0);
+}
+
+/*
+ * Given a property name and value, set the property for the given dataset.
+ */
+int
+zfs_prop_set(zfs_handle_t *zhp, zfs_prop_t prop, const char *propval)
+{
+ const char *propname = zfs_prop_to_name(prop);
+ uint64_t number;
+ zfs_cmd_t zc = { 0 };
+ int ret;
+ prop_changelist_t *cl;
+
+ if (zfs_prop_validate(prop, propval, &number) != 0)
+ return (-1);
+
+ /*
+ * Check to see if the value applies to this type
+ */
+ if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot set %s for '%s': property does not apply to %ss"),
+ propname, zhp->zfs_name, zfs_type_to_name(zhp->zfs_type));
+ return (-1);
+ }
+
+ /*
+ * For the mountpoint and sharenfs properties, check if it can be set
+ * in a global/non-global zone based on the zoned property value:
+ *
+ * global zone non-global zone
+ * -----------------------------------------------------
+ * zoned=on mountpoint (no) mountpoint (yes)
+ * sharenfs (no) sharenfs (no)
+ *
+ * zoned=off mountpoint (yes) N/A
+ * sharenfs (yes)
+ */
+ if (prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) {
+ if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
+ if (getzoneid() == GLOBAL_ZONEID) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot set %s for '%s', "
+ "dataset is used in a non-global zone"),
+ propname, zhp->zfs_name);
+ return (-1);
+ } else if (prop == ZFS_PROP_SHARENFS) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot set %s for '%s', filesystems "
+ "cannot be shared in a non-global zone"),
+ propname, zhp->zfs_name);
+ return (-1);
+ }
+ } else if (getzoneid() != GLOBAL_ZONEID) {
+ /*
+ * If zoned property is 'off', this must be in
+ * a globle zone. If not, something is wrong.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot set %s for '%s', dataset is "
+ "used in a non-global zone, but 'zoned' "
+ "property is not set"),
+ propname, zhp->zfs_name);
+ return (-1);
+ }
+ }
+
+ if ((cl = changelist_gather(zhp, prop, 0)) == NULL)
+ return (-1);
+
+ if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot set %s for '%s', "
+ "child dataset with inherited mountpoint is used "
+ "in a non-global zone"),
+ propname, zhp->zfs_name);
+ ret = -1;
+ goto error;
+ }
+
+ if ((ret = changelist_prefix(cl)) != 0)
+ goto error;
+
+ /*
+ * Execute the corresponding ioctl() to set this property.
+ */
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ switch (prop) {
+ case ZFS_PROP_QUOTA:
+ zc.zc_cookie = number;
+ ret = ioctl(zfs_fd, ZFS_IOC_SET_QUOTA, &zc);
+ break;
+ case ZFS_PROP_RESERVATION:
+ zc.zc_cookie = number;
+ ret = ioctl(zfs_fd, ZFS_IOC_SET_RESERVATION, &zc);
+ break;
+ case ZFS_PROP_MOUNTPOINT:
+ case ZFS_PROP_SHARENFS:
+ /*
+ * These properties are passed down as real strings.
+ */
+ (void) strlcpy(zc.zc_prop_name, propname,
+ sizeof (zc.zc_prop_name));
+ (void) strlcpy(zc.zc_prop_value, propval,
+ sizeof (zc.zc_prop_value));
+ zc.zc_intsz = 1;
+ zc.zc_numints = strlen(propval) + 1;
+ ret = ioctl(zfs_fd, ZFS_IOC_SET_PROP, &zc);
+ break;
+ case ZFS_PROP_VOLSIZE:
+ zc.zc_volsize = number;
+ ret = ioctl(zfs_fd, ZFS_IOC_SET_VOLSIZE, &zc);
+ break;
+ case ZFS_PROP_VOLBLOCKSIZE:
+ zc.zc_volblocksize = number;
+ ret = ioctl(zfs_fd, ZFS_IOC_SET_VOLBLOCKSIZE, &zc);
+ break;
+ default:
+ (void) strlcpy(zc.zc_prop_name, propname,
+ sizeof (zc.zc_prop_name));
+ /* LINTED - alignment */
+ *(uint64_t *)zc.zc_prop_value = number;
+ zc.zc_intsz = 8;
+ zc.zc_numints = 1;
+ ret = ioctl(zfs_fd, ZFS_IOC_SET_PROP, &zc);
+ break;
+ }
+
+ if (ret != 0) {
+ switch (errno) {
+
+ case EPERM:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot set %s for '%s': permission "
+ "denied"), propname, zhp->zfs_name);
+ break;
+
+ case ENOENT:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot open '%s': no such %s"), zhp->zfs_name,
+ zfs_type_to_name(zhp->zfs_type));
+ break;
+
+ case ENOSPC:
+ /*
+ * For quotas and reservations, ENOSPC indicates
+ * something different; setting a quota or reservation
+ * doesn't use any disk space.
+ */
+ switch (prop) {
+ case ZFS_PROP_QUOTA:
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot set %s "
+ "for '%s': size is less than current "
+ "used or reserved space"), propname,
+ zhp->zfs_name);
+ break;
+
+ case ZFS_PROP_RESERVATION:
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot set %s "
+ "for '%s': size is greater than available "
+ "space"), propname, zhp->zfs_name);
+ break;
+
+ default:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot set %s for '%s': out of space"),
+ propname, zhp->zfs_name);
+ break;
+ }
+ break;
+
+ case EBUSY:
+ if (prop == ZFS_PROP_VOLBLOCKSIZE) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot set %s for '%s': "
+ "volume already contains data"),
+ propname, zhp->zfs_name);
+ } else {
+ zfs_baderror(errno);
+ }
+ break;
+
+ case EOVERFLOW:
+ /*
+ * This platform can't address a volume this big.
+ */
+#ifdef _ILP32
+ if (prop == ZFS_PROP_VOLSIZE) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot set %s for '%s': "
+ "max volume size is 1TB on 32-bit systems"),
+ propname, zhp->zfs_name);
+ break;
+ }
+#endif
+ zfs_baderror(errno);
+ default:
+ zfs_baderror(errno);
+ }
+ } else {
+ /*
+ * Refresh the statistics so the new property value
+ * is reflected.
+ */
+ if ((ret = changelist_postfix(cl)) != 0)
+ goto error;
+
+ (void) get_stats(zhp);
+ }
+
+error:
+ changelist_free(cl);
+ return (ret);
+}
+
+/*
+ * Given a property, inherit the value from the parent dataset.
+ */
+int
+zfs_prop_inherit(zfs_handle_t *zhp, zfs_prop_t prop)
+{
+ const char *propname = zfs_prop_to_name(prop);
+ zfs_cmd_t zc = { 0 };
+ int ret;
+ prop_changelist_t *cl;
+
+ /*
+ * Verify that this property is inheritable.
+ */
+ if (zfs_prop_readonly(prop)) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot inherit %s for '%s': property is read-only"),
+ propname, zhp->zfs_name);
+ return (-1);
+ }
+
+ if (!zfs_prop_inheritable(prop)) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot inherit %s for '%s': property is not inheritable"),
+ propname, zhp->zfs_name);
+ return (-1);
+ }
+
+ /*
+ * Check to see if the value applies to this type
+ */
+ if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot inherit %s for '%s': property does "
+ "not apply to %ss"), propname, zhp->zfs_name,
+ zfs_type_to_name(zhp->zfs_type));
+ return (-1);
+ }
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_prop_name, propname, sizeof (zc.zc_prop_name));
+
+ if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
+ zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot inherit %s for '%s', "
+ "dataset is used in a non-global zone"), propname,
+ zhp->zfs_name);
+ return (-1);
+ }
+
+ /*
+ * Determine datasets which will be affected by this change, if any.
+ */
+ if ((cl = changelist_gather(zhp, prop, 0)) == NULL)
+ return (-1);
+
+ if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot inherit %s for '%s', "
+ "child dataset with inherited mountpoint is "
+ "used in a non-global zone"),
+ propname, zhp->zfs_name);
+ ret = -1;
+ goto error;
+ }
+
+ if ((ret = changelist_prefix(cl)) != 0)
+ goto error;
+
+ zc.zc_numints = 0;
+
+ if ((ret = ioctl(zfs_fd, ZFS_IOC_SET_PROP, &zc)) != 0) {
+ switch (errno) {
+ case EPERM:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot inherit %s for '%s': permission "
+ "denied"), propname, zhp->zfs_name);
+ break;
+ case ENOENT:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot open '%s': no such %s"), zhp->zfs_name,
+ zfs_type_to_name(zhp->zfs_type));
+ break;
+ case ENOSPC:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot inherit %s for '%s': "
+ "out of space"), propname, zhp->zfs_name);
+ break;
+ default:
+ zfs_baderror(errno);
+ }
+
+ } else {
+
+ if ((ret = changelist_postfix(cl)) != 0)
+ goto error;
+
+ /*
+ * Refresh the statistics so the new property is reflected.
+ */
+ (void) get_stats(zhp);
+ }
+
+
+error:
+ changelist_free(cl);
+ return (ret);
+}
+
+static void
+nicebool(int value, char *buf, size_t buflen)
+{
+ if (value)
+ (void) strlcpy(buf, "on", buflen);
+ else
+ (void) strlcpy(buf, "off", buflen);
+}
+
+/*
+ * Internal function for getting a numeric property. Both zfs_prop_get() and
+ * zfs_prop_get_int() are built using this interface.
+ *
+ * Certain properties can be overridden using 'mount -o'. In this case, scan
+ * the contents of the /etc/mnttab entry, searching for the appropriate options.
+ * If they differ from the on-disk values, report the current values and mark
+ * the source "temporary".
+ */
+static uint64_t
+get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src,
+ char **source)
+{
+ uint64_t val;
+ struct mnttab mnt;
+
+ *source = NULL;
+
+ if (zhp->zfs_mntopts == NULL)
+ mnt.mnt_mntopts = "";
+ else
+ mnt.mnt_mntopts = zhp->zfs_mntopts;
+
+ switch (prop) {
+ case ZFS_PROP_ATIME:
+ *source = zhp->zfs_zplstats.zs_atime_setpoint;
+ val = zhp->zfs_zplstats.zs_devices;
+
+ if (hasmntopt(&mnt, MNTOPT_ATIME) && !val) {
+ val = TRUE;
+ if (src)
+ *src = ZFS_SRC_TEMPORARY;
+ } else if (hasmntopt(&mnt, MNTOPT_NOATIME) && val) {
+ val = FALSE;
+ if (src)
+ *src = ZFS_SRC_TEMPORARY;
+ }
+ return (zhp->zfs_zplstats.zs_atime);
+
+ case ZFS_PROP_AVAILABLE:
+ return (zhp->zfs_dmustats.dds_available);
+
+ case ZFS_PROP_DEVICES:
+ *source = zhp->zfs_zplstats.zs_devices_setpoint;
+ val = zhp->zfs_zplstats.zs_devices;
+
+ if (hasmntopt(&mnt, MNTOPT_DEVICES) && !val) {
+ val = TRUE;
+ if (src)
+ *src = ZFS_SRC_TEMPORARY;
+ } else if (hasmntopt(&mnt, MNTOPT_NODEVICES) && val) {
+ val = FALSE;
+ if (src)
+ *src = ZFS_SRC_TEMPORARY;
+ }
+ return (val);
+
+ case ZFS_PROP_EXEC:
+ *source = zhp->zfs_zplstats.zs_exec_setpoint;
+ val = zhp->zfs_zplstats.zs_exec;
+
+ if (hasmntopt(&mnt, MNTOPT_EXEC) && !val) {
+ val = TRUE;
+ if (src)
+ *src = ZFS_SRC_TEMPORARY;
+ } else if (hasmntopt(&mnt, MNTOPT_NOEXEC) && val) {
+ val = FALSE;
+ if (src)
+ *src = ZFS_SRC_TEMPORARY;
+ }
+ return (val);
+
+ case ZFS_PROP_RECORDSIZE:
+ *source = zhp->zfs_zplstats.zs_recordsize_setpoint;
+ return (zhp->zfs_zplstats.zs_recordsize);
+
+ case ZFS_PROP_COMPRESSION:
+ *source = zhp->zfs_dmustats.dds_compression_setpoint;
+ return (zhp->zfs_dmustats.dds_compression);
+
+ case ZFS_PROP_READONLY:
+ *source = zhp->zfs_zplstats.zs_readonly_setpoint;
+ val = zhp->zfs_zplstats.zs_readonly;
+
+ if (hasmntopt(&mnt, MNTOPT_RO) && !val) {
+ val = TRUE;
+ if (src)
+ *src = ZFS_SRC_TEMPORARY;
+ } else if (hasmntopt(&mnt, MNTOPT_RW) && val) {
+ val = FALSE;
+ if (src)
+ *src = ZFS_SRC_TEMPORARY;
+ }
+ return (val);
+
+ case ZFS_PROP_QUOTA:
+ if (zhp->zfs_dmustats.dds_quota == 0)
+ *source = ""; /* default */
+ else
+ *source = zhp->zfs_name;
+ return (zhp->zfs_dmustats.dds_quota);
+
+ case ZFS_PROP_RESERVATION:
+ if (zhp->zfs_dmustats.dds_reserved == 0)
+ *source = ""; /* default */
+ else
+ *source = zhp->zfs_name;
+ return (zhp->zfs_dmustats.dds_reserved);
+
+ case ZFS_PROP_COMPRESSRATIO:
+ /*
+ * Using physical space and logical space, calculate the
+ * compression ratio. We return the number as a multiple of
+ * 100, so '2.5x' would be returned as 250.
+ */
+ if (zhp->zfs_dmustats.dds_compressed_bytes == 0)
+ return (100ULL);
+ else
+ return (zhp->zfs_dmustats.dds_uncompressed_bytes * 100 /
+ zhp->zfs_dmustats.dds_compressed_bytes);
+
+ case ZFS_PROP_REFERENCED:
+ /*
+ * 'referenced' refers to the amount of physical space
+ * referenced (possibly shared) by this object.
+ */
+ return (zhp->zfs_dmustats.dds_space_refd);
+
+ case ZFS_PROP_SETUID:
+ *source = zhp->zfs_zplstats.zs_setuid_setpoint;
+ val = zhp->zfs_zplstats.zs_setuid;
+
+ if (hasmntopt(&mnt, MNTOPT_SETUID) && !val) {
+ val = TRUE;
+ if (src)
+ *src = ZFS_SRC_TEMPORARY;
+ } else if (hasmntopt(&mnt, MNTOPT_NOSETUID) && val) {
+ val = FALSE;
+ if (src)
+ *src = ZFS_SRC_TEMPORARY;
+ }
+ return (val);
+
+ case ZFS_PROP_VOLSIZE:
+ return (zhp->zfs_volsize);
+
+ case ZFS_PROP_VOLBLOCKSIZE:
+ return (zhp->zfs_volblocksize);
+
+ case ZFS_PROP_ZONED:
+ *source = zhp->zfs_dmustats.dds_zoned_setpoint;
+ return (zhp->zfs_dmustats.dds_zoned);
+
+ case ZFS_PROP_USED:
+ return (zhp->zfs_dmustats.dds_space_used);
+
+ case ZFS_PROP_CREATETXG:
+ return (zhp->zfs_dmustats.dds_creation_txg);
+
+ case ZFS_PROP_MOUNTED:
+ /*
+ * Unlike other properties, we defer calculation of 'MOUNTED'
+ * until actually requested. This is because the getmntany()
+ * call can be extremely expensive on systems with a large
+ * number of filesystems, and the property isn't needed in
+ * normal use cases.
+ */
+ if (zhp->zfs_mntopts == NULL) {
+ struct mnttab search = { 0 }, entry;
+
+ search.mnt_special = (char *)zhp->zfs_name;
+ rewind(mnttab_file);
+
+ if (getmntany(mnttab_file, &entry, &search) == 0)
+ zhp->zfs_mntopts =
+ zfs_strdup(entry.mnt_mntopts);
+ }
+ return (zhp->zfs_mntopts != NULL);
+
+ default:
+ zfs_baderror(EINVAL);
+ }
+
+ return (0);
+}
+
+/*
+ * Calculate the source type, given the raw source string.
+ */
+static void
+get_source(zfs_handle_t *zhp, zfs_source_t *srctype, char *source,
+ char *statbuf, size_t statlen)
+{
+ if (statbuf == NULL || *srctype == ZFS_SRC_TEMPORARY)
+ return;
+
+ if (source == NULL) {
+ *srctype = ZFS_SRC_NONE;
+ } else if (source[0] == '\0') {
+ *srctype = ZFS_SRC_DEFAULT;
+ } else {
+ if (strcmp(source, zhp->zfs_name) == 0) {
+ *srctype = ZFS_SRC_LOCAL;
+ } else {
+ (void) strlcpy(statbuf, source, statlen);
+ *srctype = ZFS_SRC_INHERITED;
+ }
+ }
+
+}
+
+/*
+ * Retrieve a property from the given object. If 'literal' is specified, then
+ * numbers are left as exact values. Otherwise, numbers are converted to a
+ * human-readable form.
+ *
+ * Returns 0 on success, or -1 on error.
+ */
+int
+zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
+ zfs_source_t *src, char *statbuf, size_t statlen, int literal)
+{
+ char *source = NULL;
+ uint64_t val;
+ char *str;
+ int i;
+ const char *root;
+
+ /*
+ * Check to see if this property applies to our object
+ */
+ if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
+ return (-1);
+
+ if (src)
+ *src = ZFS_SRC_NONE;
+
+ switch (prop) {
+ case ZFS_PROP_ATIME:
+ case ZFS_PROP_READONLY:
+ case ZFS_PROP_SETUID:
+ case ZFS_PROP_ZONED:
+ case ZFS_PROP_DEVICES:
+ case ZFS_PROP_EXEC:
+ /*
+ * Basic boolean values are built on top of
+ * get_numeric_property().
+ */
+ nicebool(get_numeric_property(zhp, prop, src, &source),
+ propbuf, proplen);
+
+ break;
+
+ case ZFS_PROP_AVAILABLE:
+ case ZFS_PROP_RECORDSIZE:
+ case ZFS_PROP_CREATETXG:
+ case ZFS_PROP_REFERENCED:
+ case ZFS_PROP_USED:
+ case ZFS_PROP_VOLSIZE:
+ case ZFS_PROP_VOLBLOCKSIZE:
+ /*
+ * Basic numeric values are built on top of
+ * get_numeric_property().
+ */
+ val = get_numeric_property(zhp, prop, src, &source);
+ if (literal)
+ (void) snprintf(propbuf, proplen, "%llu", val);
+ else
+ zfs_nicenum(val, propbuf, proplen);
+ break;
+
+ case ZFS_PROP_COMPRESSION:
+ for (i = 0; compress_table[i].name != NULL; i++) {
+ if (compress_table[i].value ==
+ zhp->zfs_dmustats.dds_compression)
+ break;
+ }
+ assert(compress_table[i].name != NULL);
+ (void) strlcpy(propbuf, compress_table[i].name, proplen);
+ source = zhp->zfs_dmustats.dds_compression_setpoint;
+ break;
+
+ case ZFS_PROP_CHECKSUM:
+ for (i = 0; checksum_table[i].name != NULL; i++) {
+ if (checksum_table[i].value ==
+ zhp->zfs_dmustats.dds_checksum)
+ break;
+ }
+ assert(checksum_table[i].name != NULL);
+ (void) strlcpy(propbuf, checksum_table[i].name, proplen);
+ source = zhp->zfs_dmustats.dds_checksum_setpoint;
+ break;
+
+ case ZFS_PROP_SNAPDIR:
+ for (i = 0; snapdir_table[i].name != NULL; i++) {
+ if (snapdir_table[i].value ==
+ zhp->zfs_zplstats.zs_snapdir)
+ break;
+ }
+ assert(snapdir_table[i].name != NULL);
+ (void) strlcpy(propbuf, snapdir_table[i].name, proplen);
+ source = zhp->zfs_zplstats.zs_snapdir_setpoint;
+ break;
+
+ case ZFS_PROP_ACLMODE:
+ for (i = 0; acl_mode_table[i].name != NULL; i++) {
+ if (acl_mode_table[i].value ==
+ zhp->zfs_zplstats.zs_acl_mode)
+ break;
+ }
+ assert(acl_mode_table[i].name != NULL);
+ (void) strlcpy(propbuf, acl_mode_table[i].name, proplen);
+ source = zhp->zfs_zplstats.zs_acl_mode_setpoint;
+ break;
+
+ case ZFS_PROP_ACLINHERIT:
+ for (i = 0; acl_inherit_table[i].name != NULL; i++) {
+ if (acl_inherit_table[i].value ==
+ zhp->zfs_zplstats.zs_acl_inherit)
+ break;
+ }
+ assert(acl_inherit_table[i].name != NULL);
+ (void) strlcpy(propbuf, acl_inherit_table[i].name, proplen);
+ source = zhp->zfs_zplstats.zs_acl_inherit_setpoint;
+ break;
+
+ case ZFS_PROP_CREATION:
+ /*
+ * 'creation' is a time_t stored in the statistics. We convert
+ * this into a string unless 'literal' is specified.
+ */
+ {
+ time_t time = (time_t)
+ zhp->zfs_dmustats.dds_creation_time;
+ struct tm t;
+
+ if (literal ||
+ localtime_r(&time, &t) == NULL ||
+ strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
+ &t) == 0)
+ (void) snprintf(propbuf, proplen, "%llu",
+ zhp->zfs_dmustats.dds_creation_time);
+ }
+ break;
+
+ case ZFS_PROP_MOUNTPOINT:
+ /*
+ * Getting the precise mountpoint can be tricky.
+ *
+ * - for 'none' or 'legacy', return those values.
+ * - for default mountpoints, construct it as /zfs/<dataset>
+ * - for inherited mountpoints, we want to take everything
+ * after our ancestor and append it to the inherited value.
+ *
+ * If the pool has an alternate root, we want to prepend that
+ * root to any values we return.
+ */
+ root = zhp->zfs_dmustats.dds_altroot;
+
+ if (zhp->zfs_zplstats.zs_mountpoint[0] == '\0') {
+ (void) snprintf(propbuf, proplen, "%s/zfs/%s",
+ root, zhp->zfs_name);
+ } else if (zhp->zfs_zplstats.zs_mountpoint[0] == '/') {
+ const char *relpath = zhp->zfs_name +
+ strlen(zhp->zfs_zplstats.zs_mountpoint_setpoint);
+ const char *mntpoint = zhp->zfs_zplstats.zs_mountpoint;
+
+ if (relpath[0] == '/')
+ relpath++;
+ if (mntpoint[1] == '\0')
+ mntpoint++;
+
+ if (relpath[0] == '\0')
+ (void) snprintf(propbuf, proplen, "%s%s",
+ root, mntpoint);
+ else
+ (void) snprintf(propbuf, proplen, "%s%s%s%s",
+ root, mntpoint,
+ relpath[0] == '@' ? "" : "/",
+ relpath);
+ } else {
+ /* 'legacy' or 'none' */
+ (void) strlcpy(propbuf, zhp->zfs_zplstats.zs_mountpoint,
+ proplen);
+ }
+
+ source = zhp->zfs_zplstats.zs_mountpoint_setpoint;
+ break;
+
+ case ZFS_PROP_SHARENFS:
+ (void) strlcpy(propbuf, zhp->zfs_zplstats.zs_sharenfs, proplen);
+ source = zhp->zfs_zplstats.zs_sharenfs_setpoint;
+ break;
+
+ case ZFS_PROP_ORIGIN:
+ (void) strlcpy(propbuf, zhp->zfs_dmustats.dds_clone_of,
+ proplen);
+ /*
+ * If there is no parent at all, return failure to indicate that
+ * it doesn't apply to this dataset.
+ */
+ if (propbuf[0] == '\0')
+ return (-1);
+ break;
+
+ case ZFS_PROP_QUOTA:
+ case ZFS_PROP_RESERVATION:
+ val = get_numeric_property(zhp, prop, src, &source);
+
+ /*
+ * If quota or reservation is 0, we translate this into 'none'
+ * (unless literal is set), and indicate that it's the default
+ * value. Otherwise, we print the number nicely and indicate
+ * that its set locally.
+ */
+ if (val == 0) {
+ if (literal)
+ (void) strlcpy(propbuf, "0", proplen);
+ else
+ (void) strlcpy(propbuf, "none", proplen);
+ } else {
+ if (literal)
+ (void) snprintf(propbuf, proplen, "%llu", val);
+ else
+ zfs_nicenum(val, propbuf, proplen);
+ }
+ break;
+
+ case ZFS_PROP_COMPRESSRATIO:
+ val = get_numeric_property(zhp, prop, src, &source);
+ (void) snprintf(propbuf, proplen, "%lld.%02lldx", val / 100,
+ val % 100);
+ break;
+
+ case ZFS_PROP_TYPE:
+ switch (zhp->zfs_type) {
+ case ZFS_TYPE_FILESYSTEM:
+ str = "filesystem";
+ break;
+ case ZFS_TYPE_VOLUME:
+ str = "volume";
+ break;
+ case ZFS_TYPE_SNAPSHOT:
+ str = "snapshot";
+ break;
+ default:
+ zfs_baderror(zhp->zfs_type);
+ }
+ (void) snprintf(propbuf, proplen, "%s", str);
+ break;
+
+ case ZFS_PROP_MOUNTED:
+ /*
+ * The 'mounted' property is a pseudo-property that described
+ * whether the filesystem is currently mounted. Even though
+ * it's a boolean value, the typical values of "on" and "off"
+ * don't make sense, so we translate to "yes" and "no".
+ */
+ if (get_numeric_property(zhp, ZFS_PROP_MOUNTED, src, &source))
+ (void) strlcpy(propbuf, "yes", proplen);
+ else
+ (void) strlcpy(propbuf, "no", proplen);
+ break;
+
+ case ZFS_PROP_NAME:
+ /*
+ * The 'name' property is a pseudo-property derived from the
+ * dataset name. It is presented as a real property to simplify
+ * consumers.
+ */
+ (void) strlcpy(propbuf, zhp->zfs_name, proplen);
+ break;
+
+ default:
+ zfs_baderror(EINVAL);
+ }
+
+ get_source(zhp, src, source, statbuf, statlen);
+
+ return (0);
+}
+
+/*
+ * Utility function to get the given numeric property. Does no validation that
+ * the given property is the appropriate type; should only be used with
+ * hard-coded property types.
+ */
+uint64_t
+zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
+{
+ char *source;
+ zfs_source_t sourcetype = ZFS_SRC_NONE;
+
+ return (get_numeric_property(zhp, prop, &sourcetype, &source));
+}
+
+/*
+ * Similar to zfs_prop_get(), but returns the value as an integer.
+ */
+int
+zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
+ zfs_source_t *src, char *statbuf, size_t statlen)
+{
+ char *source;
+
+ /*
+ * Check to see if this property applies to our object
+ */
+ if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
+ return (-1);
+
+ if (src)
+ *src = ZFS_SRC_NONE;
+
+ *value = get_numeric_property(zhp, prop, src, &source);
+
+ get_source(zhp, src, source, statbuf, statlen);
+
+ return (0);
+}
+
+/*
+ * Returns the name of the given zfs handle.
+ */
+const char *
+zfs_get_name(const zfs_handle_t *zhp)
+{
+ return (zhp->zfs_name);
+}
+
+/*
+ * Returns the type of the given zfs handle.
+ */
+zfs_type_t
+zfs_get_type(const zfs_handle_t *zhp)
+{
+ return (zhp->zfs_type);
+}
+
+/*
+ * Iterate over all children, datasets and snapshots.
+ */
+int
+zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+{
+ zfs_cmd_t zc = { 0 };
+ zfs_handle_t *nzhp;
+ int ret;
+
+ for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ ioctl(zfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) {
+ /*
+ * Ignore private dataset names.
+ */
+ if (dataset_name_hidden(zc.zc_name))
+ continue;
+
+ /*
+ * Silently ignore errors, as the only plausible explanation is
+ * that the pool has since been removed.
+ */
+ if ((nzhp = make_dataset_handle(zc.zc_name)) == NULL)
+ continue;
+
+ if ((ret = func(nzhp, data)) != 0)
+ return (ret);
+ }
+
+ /*
+ * An errno value of ESRCH indicates normal completion. If ENOENT is
+ * returned, then the underlying dataset has been removed since we
+ * obtained the handle.
+ */
+ if (errno != ESRCH && errno != ENOENT)
+ zfs_baderror(errno);
+
+ bzero(&zc, sizeof (zc));
+
+ for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ ioctl(zfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0;
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) {
+
+ if ((nzhp = make_dataset_handle(zc.zc_name)) == NULL)
+ continue;
+
+ if ((ret = func(nzhp, data)) != 0)
+ return (ret);
+ }
+
+ /*
+ * An errno value of ESRCH indicates normal completion. If ENOENT is
+ * returned, then the underlying dataset has been removed since we
+ * obtained the handle. Silently ignore this case, and return success.
+ */
+ if (errno != ESRCH && errno != ENOENT)
+ zfs_baderror(errno);
+
+ return (0);
+}
+
+/*
+ * Given a complete name, return just the portion that refers to the parent.
+ * Can return NULL if this is a pool.
+ */
+static int
+parent_name(const char *path, char *buf, size_t buflen)
+{
+ char *loc;
+
+ if ((loc = strrchr(path, '/')) == NULL)
+ return (-1);
+
+ (void) strncpy(buf, path, MIN(buflen, loc - path));
+ buf[loc - path] = '\0';
+
+ return (0);
+}
+
+/*
+ * Checks to make sure that the given path has a parent, and that it exists.
+ */
+static int
+check_parents(const char *path, zfs_type_t type)
+{
+ zfs_cmd_t zc = { 0 };
+ char parent[ZFS_MAXNAMELEN];
+ char *slash;
+
+ /* get parent, and check to see if this is just a pool */
+ if (parent_name(path, parent, sizeof (parent)) != 0) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': missing dataset name"),
+ path, zfs_type_to_name(type));
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "use 'zpool create' to create a storage pool"));
+ return (-1);
+ }
+
+ /* check to see if the pool exists */
+ if ((slash = strchr(parent, '/')) == NULL)
+ slash = parent + strlen(parent);
+ (void) strncpy(zc.zc_name, parent, slash - parent);
+ zc.zc_name[slash - parent] = '\0';
+ if (ioctl(zfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
+ errno == ENOENT) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': no such pool '%s'"), path, zc.zc_name);
+ return (-1);
+ }
+
+ /* check to see if the parent dataset exists */
+ (void) strlcpy(zc.zc_name, parent, sizeof (zc.zc_name));
+ if (ioctl(zfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
+ switch (errno) {
+ case ENOENT:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': parent does not exist"), path);
+ return (-1);
+
+ default:
+ zfs_baderror(errno);
+ }
+ }
+
+ /* we are in a non-global zone, but parent is in the global zone */
+ if (getzoneid() != GLOBAL_ZONEID && !zc.zc_objset_stats.dds_zoned) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': permission denied"), path);
+ return (-1);
+ }
+
+ /* make sure parent is a filesystem */
+ if (zc.zc_objset_stats.dds_type != DMU_OST_ZFS) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': parent is not a filesystem"),
+ path);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Create a new filesystem or volume. 'sizestr' and 'blocksizestr' are used
+ * only for volumes, and indicate the size and blocksize of the volume.
+ */
+int
+zfs_create(const char *path, zfs_type_t type,
+ const char *sizestr, const char *blocksizestr)
+{
+ char reason[64];
+ zfs_cmd_t zc = { 0 };
+ int ret;
+ uint64_t size = 0;
+ uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
+
+ /* convert sizestr into integer size */
+ if (sizestr != NULL && nicestrtonum(sizestr, &size,
+ reason, sizeof (reason)) != 0) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad volume size '%s': %s"), sizestr, reason);
+ return (-1);
+ }
+
+ /* convert blocksizestr into integer blocksize */
+ if (blocksizestr != NULL && nicestrtonum(blocksizestr, &blocksize,
+ reason, sizeof (reason)) != 0) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad volume blocksize '%s': %s"), blocksizestr, reason);
+ return (-1);
+ }
+
+ /* make sure the name is not too long */
+ if (strlen(path) >= ZFS_MAXNAMELEN) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': %s name is too long"),
+ path, zfs_type_to_name(type));
+ return (-1);
+ }
+
+ /* validate the path, taking care to note the extended error message */
+ if (!zfs_validate_name(path, type, reason, sizeof (reason))) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': %s in %s name"), path, reason,
+ zfs_type_to_name(type));
+ if (strstr(reason, "snapshot") != NULL)
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "use 'zfs snapshot' to create a snapshot"));
+ return (-1);
+ }
+
+ /* validate parents exist */
+ if (check_parents(path, type) != 0)
+ return (-1);
+
+ /*
+ * The failure modes when creating a dataset of a different type over
+ * one that already exists is a little strange. In particular, if you
+ * try to create a dataset on top of an existing dataset, the ioctl()
+ * will return ENOENT, not EEXIST. To prevent this from happening, we
+ * first try to see if the dataset exists.
+ */
+ (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
+ if (ioctl(zfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': dataset exists"), path);
+ return (-1);
+ }
+
+ if (type == ZFS_TYPE_VOLUME)
+ zc.zc_objset_type = DMU_OST_ZVOL;
+ else
+ zc.zc_objset_type = DMU_OST_ZFS;
+
+ if (type == ZFS_TYPE_VOLUME) {
+ if (size == 0) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "bad volume size '%s': cannot be zero"), sizestr);
+ return (-1);
+ }
+
+ zc.zc_volsize = size;
+ zc.zc_volblocksize = blocksize;
+ }
+
+ /* create the dataset */
+
+ ret = ioctl(zfs_fd, ZFS_IOC_CREATE, &zc);
+
+ if (ret == 0 && type == ZFS_TYPE_VOLUME)
+ ret = zvol_create_link(path);
+
+ /* check for failure */
+ if (ret != 0) {
+ char parent[ZFS_MAXNAMELEN];
+ (void) parent_name(path, parent, sizeof (parent));
+
+ switch (errno) {
+ case ENOENT:
+ /*
+ * The parent dataset has been deleted since our
+ * previous check.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': no such parent '%s'"),
+ path, parent);
+ break;
+
+ case EPERM:
+ /*
+ * The user doesn't have permission to create a new
+ * dataset here.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': permission denied"), path);
+ break;
+
+ case EDQUOT:
+ case ENOSPC:
+ /*
+ * The parent dataset does not have enough free space
+ * to create a new dataset.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': not enough space in '%s'"),
+ path, parent);
+ break;
+
+ case EEXIST:
+ /*
+ * The target dataset already exists. We should have
+ * caught this above, but there may be some unexplained
+ * race condition.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': dataset exists"), path);
+ break;
+
+ case EINVAL:
+ /*
+ * The target dataset does not support children.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': children unsupported in '%s'"),
+ path, parent);
+ break;
+
+ case EDOM:
+ zfs_error(dgettext(TEXT_DOMAIN, "bad %s value '%s': "
+ "must be power of 2 from %u to %uk"),
+ zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
+ blocksizestr ? blocksizestr : "<unknown>",
+ (uint_t)SPA_MINBLOCKSIZE,
+ (uint_t)SPA_MAXBLOCKSIZE >> 10);
+ break;
+#ifdef _ILP32
+ case EOVERFLOW:
+ /*
+ * This platform can't address a volume this big.
+ */
+ if (type == ZFS_TYPE_VOLUME) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': "
+ "max volume size is 1TB on 32-bit systems"),
+ path);
+ break;
+ }
+#endif
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Destroys the given dataset. The caller must make sure that the filesystem
+ * isn't mounted, and that there are no active dependents.
+ */
+int
+zfs_destroy(zfs_handle_t *zhp)
+{
+ zfs_cmd_t zc = { 0 };
+ int ret;
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ /*
+ * We use the check for 'zfs_volblocksize' instead of ZFS_TYPE_VOLUME
+ * so that we do the right thing for snapshots of volumes.
+ */
+ if (zhp->zfs_volblocksize != 0) {
+ if (zvol_remove_link(zhp->zfs_name) != 0)
+ return (-1);
+
+ zc.zc_objset_type = DMU_OST_ZVOL;
+ } else {
+ zc.zc_objset_type = DMU_OST_ZFS;
+ }
+
+ ret = ioctl(zfs_fd, ZFS_IOC_DESTROY, &zc);
+
+ if (ret != 0) {
+ switch (errno) {
+
+ case EPERM:
+ /*
+ * We don't have permission to destroy this dataset.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot destroy '%s': permission denied"),
+ zhp->zfs_name);
+ break;
+
+ case ENOENT:
+ /*
+ * We've hit a race condition where the dataset has been
+ * destroyed since we opened it.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot destroy '%s': no such %s"),
+ zhp->zfs_name, zfs_type_to_name(zhp->zfs_type));
+ break;
+
+ case EBUSY:
+ /*
+ * Even if we destroy all children, there is a chance we
+ * can hit this case if:
+ *
+ * - A child dataset has since been created
+ * - A filesystem is mounted
+ *
+ * This error message is awful, but hopefully we've
+ * already caught the common cases (and aborted more
+ * appropriately) before calling this function. There's
+ * nothing else we can do at this point.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot destroy '%s': %s is busy"),
+ zhp->zfs_name, zfs_type_to_name(zhp->zfs_type));
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ return (-1);
+ }
+
+ remove_mountpoint(zhp);
+
+ return (0);
+}
+
+/*
+ * Clones the given dataset. The target must be of the same type as the source.
+ */
+int
+zfs_clone(zfs_handle_t *zhp, const char *target)
+{
+ char reason[64];
+ zfs_cmd_t zc = { 0 };
+ char parent[ZFS_MAXNAMELEN];
+ int ret;
+
+ assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
+
+ /* validate the target name */
+ if (!zfs_validate_name(target, ZFS_TYPE_FILESYSTEM, reason,
+ sizeof (reason))) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': %s in filesystem name"), target,
+ reason, zfs_type_to_name(ZFS_TYPE_FILESYSTEM));
+ return (-1);
+ }
+
+ /* validate parents exist */
+ if (check_parents(target, zhp->zfs_type) != 0)
+ return (-1);
+
+ (void) parent_name(target, parent, sizeof (parent));
+
+ /* do the clone */
+ if (zhp->zfs_volblocksize != 0)
+ zc.zc_objset_type = DMU_OST_ZVOL;
+ else
+ zc.zc_objset_type = DMU_OST_ZFS;
+
+ (void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_filename, zhp->zfs_name, sizeof (zc.zc_filename));
+ ret = ioctl(zfs_fd, ZFS_IOC_CREATE, &zc);
+
+ if (ret != 0) {
+ switch (errno) {
+ case EPERM:
+ /*
+ * The user doesn't have permission to create the clone.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': permission denied"),
+ target);
+ break;
+
+ case ENOENT:
+ /*
+ * The parent doesn't exist. We should have caught this
+ * above, but there may a race condition that has since
+ * destroyed the parent.
+ *
+ * At this point, we don't know whether it's the source
+ * that doesn't exist anymore, or whether the target
+ * dataset doesn't exist.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': no such parent '%s'"),
+ target, parent);
+ break;
+
+ case EDQUOT:
+ case ENOSPC:
+ /*
+ * There is not enough space in the target dataset
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': not enough space in '%s'"),
+ target, parent);
+ break;
+
+ case EEXIST:
+ /*
+ * The target already exists.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': dataset exists"), target);
+ break;
+
+ case EXDEV:
+ /*
+ * The source and target pools differ.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': "
+ "source and target pools differ"), target);
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+ } else if (zhp->zfs_volblocksize != 0) {
+ ret = zvol_create_link(target);
+ }
+
+ return (ret);
+}
+
+/*
+ * Takes a snapshot of the given dataset
+ */
+int
+zfs_snapshot(const char *path)
+{
+ char reason[64];
+ const char *delim;
+ char *parent;
+ zfs_handle_t *zhp;
+ zfs_cmd_t zc = { 0 };
+ int ret;
+
+ /* validate the snapshot name */
+ if (!zfs_validate_name(path, ZFS_TYPE_SNAPSHOT, reason,
+ sizeof (reason))) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot snapshot '%s': %s in snapshot name"), path,
+ reason);
+ return (-1);
+ }
+
+ /* make sure we have a snapshot */
+ if ((delim = strchr(path, '@')) == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot snapshot '%s': missing '@' delim in snapshot "
+ "name"), path);
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "use 'zfs create' to create a filesystem"));
+ return (-1);
+ }
+
+ /* make sure the parent exists and is of the appropriate type */
+ parent = zfs_malloc(delim - path + 1);
+ (void) strncpy(parent, path, delim - path);
+ parent[delim - path] = '\0';
+
+ if ((zhp = zfs_open(parent, ZFS_TYPE_FILESYSTEM |
+ ZFS_TYPE_VOLUME)) == NULL) {
+ free(parent);
+ return (-1);
+ }
+
+ (void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
+
+ if (zhp->zfs_type == ZFS_TYPE_VOLUME)
+ zc.zc_objset_type = DMU_OST_ZVOL;
+ else
+ zc.zc_objset_type = DMU_OST_ZFS;
+
+ ret = ioctl(zfs_fd, ZFS_IOC_CREATE, &zc);
+
+ if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) {
+ ret = zvol_create_link(path);
+ if (ret != 0)
+ (void) ioctl(zfs_fd, ZFS_IOC_DESTROY, &zc);
+ }
+
+ if (ret != 0) {
+ switch (errno) {
+ case EPERM:
+ /*
+ * User doesn't have permission to create a snapshot
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': "
+ "permission denied"), path);
+ break;
+
+ case EDQUOT:
+ case ENOSPC:
+ /*
+ * Out of space in parent.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': "
+ "not enough space in '%s'"), path, parent);
+ break;
+
+ case EEXIST:
+ /*
+ * Snapshot already exists.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': "
+ "snapshot exists"), path);
+ break;
+
+ case ENOENT:
+ /*
+ * Shouldn't happen because we verified the parent
+ * above. But there may be a race condition where it
+ * has since been removed.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot open '%s': "
+ "no such %s"), parent,
+ zfs_type_to_name(zhp->zfs_type));
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+ }
+
+ free(parent);
+ zfs_close(zhp);
+
+ return (ret);
+}
+
+/*
+ * Dumps a backup of tosnap, incremental from fromsnap if it isn't NULL.
+ */
+int
+zfs_backup(zfs_handle_t *zhp_to, zfs_handle_t *zhp_from)
+{
+ zfs_cmd_t zc = { 0 };
+ int ret;
+
+ /* do the ioctl() */
+ (void) strlcpy(zc.zc_name, zhp_to->zfs_name, sizeof (zc.zc_name));
+ if (zhp_from) {
+ (void) strlcpy(zc.zc_prop_value, zhp_from->zfs_name,
+ sizeof (zc.zc_name));
+ } else {
+ zc.zc_prop_value[0] = '\0';
+ }
+ zc.zc_cookie = STDOUT_FILENO;
+
+ ret = ioctl(zfs_fd, ZFS_IOC_SENDBACKUP, &zc);
+ if (ret != 0) {
+ switch (errno) {
+ case EPERM:
+ /*
+ * User doesn't have permission to do a backup
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot backup '%s': "
+ "permission denied"), zhp_to->zfs_name);
+ break;
+
+ case EXDEV:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot do incremental backup from %s:\n"
+ "it is not an earlier snapshot from the "
+ "same fs as %s"),
+ zhp_from->zfs_name, zhp_to->zfs_name);
+ break;
+
+ case ENOENT:
+ /*
+ * Shouldn't happen because we verified the parent
+ * above. But there may be a race condition where it
+ * has since been removed.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot open: "
+ "no such snapshot"));
+ break;
+
+ case EDQUOT:
+ case EFBIG:
+ case EIO:
+ case ENOLINK:
+ case ENOSPC:
+ case ENOSTR:
+ case ENXIO:
+ case EPIPE:
+ case ERANGE:
+ case EFAULT:
+ case EROFS:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot write backup stream: %s"),
+ strerror(errno));
+ break;
+
+ case EINTR:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "backup failed: signal recieved"));
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * Restores a backup of tosnap from stdin.
+ */
+int
+zfs_restore(const char *tosnap, int isprefix, int verbose, int dryrun)
+{
+ zfs_cmd_t zc = { 0 };
+ time_t begin_time;
+ int err, bytes;
+ char *cp;
+ dmu_replay_record_t drr;
+ struct drr_begin *drrb = &zc.zc_begin_record;
+
+ begin_time = time(NULL);
+
+ /* trim off snapname, if any */
+ (void) strcpy(zc.zc_name, tosnap);
+ cp = strchr(zc.zc_name, '@');
+ if (cp)
+ *cp = '\0';
+
+ /* read in the BEGIN record */
+ cp = (char *)&drr;
+ bytes = 0;
+ do {
+ err = read(STDIN_FILENO, cp, sizeof (drr) - bytes);
+ cp += err;
+ bytes += err;
+ } while (err > 0);
+
+ if (err < 0 || bytes != sizeof (drr)) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: invalid backup stream "
+ "(couldn't read first record)"));
+ return (-1);
+ }
+
+ zc.zc_begin_record = drr.drr_u.drr_begin;
+
+ if (drrb->drr_magic != DMU_BACKUP_MAGIC &&
+ drrb->drr_magic != BSWAP_64(DMU_BACKUP_MAGIC)) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: invalid backup stream "
+ "(invalid magic number)"));
+ return (-1);
+ }
+
+ if (drrb->drr_version != DMU_BACKUP_VERSION &&
+ drrb->drr_version != BSWAP_64(DMU_BACKUP_VERSION)) {
+ if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC))
+ drrb->drr_version = BSWAP_64(drrb->drr_version);
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: only backup version 0x%llx is supported, "
+ "stream is version %llx."),
+ DMU_BACKUP_VERSION, drrb->drr_version);
+ return (-1);
+ }
+
+ /*
+ * Determine name of destination snapshot.
+ */
+ (void) strcpy(drrb->drr_toname, tosnap);
+ if (isprefix) {
+ if (strchr(tosnap, '@') != NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: "
+ "argument to -d must be a filesystem"));
+ return (-1);
+ }
+
+ cp = strchr(drr.drr_u.drr_begin.drr_toname, '/');
+ if (cp == NULL)
+ cp = drr.drr_u.drr_begin.drr_toname;
+ else
+ cp++;
+
+ (void) strcat(drrb->drr_toname, "/");
+ (void) strcat(drrb->drr_toname, cp);
+ } else if (strchr(tosnap, '@') == NULL) {
+ /*
+ * they specified just a filesystem; tack on the
+ * snapname from the backup.
+ */
+ cp = strchr(drr.drr_u.drr_begin.drr_toname, '@');
+ if (cp == NULL || strlen(tosnap) + strlen(cp) >= MAXNAMELEN) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: invalid backup stream "
+ "(invalid snapshot name)"));
+ return (-1);
+ }
+ (void) strcat(drrb->drr_toname, cp);
+ }
+
+ if (drrb->drr_fromguid) {
+ zfs_handle_t *h;
+ /* incremental backup stream */
+
+ /* do the ioctl to the containing fs */
+ (void) strcpy(zc.zc_name, drrb->drr_toname);
+ cp = strchr(zc.zc_name, '@');
+ *cp = '\0';
+
+ /* make sure destination fs exists */
+ h = zfs_open(zc.zc_name, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
+ if (h == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore incrememtal backup: destination\n"
+ "filesystem %s does not exist"),
+ zc.zc_name);
+ return (-1);
+ }
+ /* unmount destination fs */
+ if (!dryrun)
+ (void) zfs_unmount(h, NULL, 0);
+ zfs_close(h);
+
+
+ } else {
+ /* full backup stream */
+
+ /* do the ioctl to the containing fs's parent */
+ (void) strcpy(zc.zc_name, drrb->drr_toname);
+ cp = strrchr(zc.zc_name, '/');
+ if (cp == NULL) {
+ cp = strchr(zc.zc_name, '@');
+ if (cp)
+ *cp = '\0';
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: destination fs %s already exists"),
+ zc.zc_name);
+ return (-1);
+ }
+ *cp = '\0';
+
+ /* make sure destination fs exists */
+
+ if (isprefix) {
+ /* make sure prefix exists */
+ zfs_handle_t *h;
+
+ /* make sure destination fs exists */
+ h = zfs_open(tosnap, ZFS_TYPE_FILESYSTEM |
+ ZFS_TYPE_VOLUME);
+ if (h == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore:"
+ "filesystem %s does not exist"),
+ tosnap);
+ return (-1);
+ }
+
+ /* create any necessary ancestors up to prefix */
+ cp = zc.zc_name + strlen(tosnap) + 1;
+ while (cp = strchr(cp, '/')) {
+ *cp = '\0';
+ err = ioctl(zfs_fd, ZFS_IOC_CREATE, &zc);
+ if (err && err != ENOENT && err != EEXIST) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore:"
+ "couldn't create ancestor %s"),
+ zc.zc_name);
+ return (-1);
+ }
+ }
+ }
+ }
+
+ (void) strcpy(zc.zc_prop_value, tosnap);
+ zc.zc_cookie = STDIN_FILENO;
+ zc.zc_intsz = isprefix;
+ if (verbose) {
+ (void) printf("%s %s backup of %s into %s\n",
+ dryrun ? "would restore" : "restoring",
+ drrb->drr_fromguid ? "incremental" : "full",
+ drr.drr_u.drr_begin.drr_toname,
+ zc.zc_begin_record.drr_toname);
+ (void) fflush(stdout);
+ }
+ if (dryrun)
+ return (0);
+ err = ioctl(zfs_fd, ZFS_IOC_RECVBACKUP, &zc);
+ if (err != 0) {
+ switch (errno) {
+ case ENODEV:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: "
+ "Most recent snapshot does not "
+ "match incremental backup source"));
+ break;
+ case ETXTBSY:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: "
+ "Destination has been modified since "
+ "most recent snapshot.\n"
+ "Use 'zfs rollback' to discard changes."));
+ break;
+ case EEXIST:
+ if (drrb->drr_fromguid == 0) {
+ /* it's the containing fs that exists */
+ cp = strchr(drrb->drr_toname, '@');
+ *cp = '\0';
+ }
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore to %s: Destination already exists"),
+ drrb->drr_toname);
+ break;
+ case ENOENT:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: "
+ "Destination does not exist"));
+ break;
+ case EBUSY:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: "
+ "Destination is in use"));
+ break;
+ case ENOSPC:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: "
+ "Out of space"));
+ break;
+ case EDQUOT:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: "
+ "Quota exceeded"));
+ break;
+ case EINTR:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Restore failed: signal recieved"));
+ break;
+ case EINVAL:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "Can't restore: "
+ "invalid backup stream"));
+ break;
+ default:
+ zfs_baderror(errno);
+ }
+ }
+
+ /*
+ * Mount or recreate the /dev links for the target filesystem.
+ */
+ cp = strchr(drrb->drr_toname, '@');
+ if (cp && (err == 0 || drrb->drr_fromguid)) {
+ zfs_handle_t *h;
+
+ *cp = '\0';
+ h = zfs_open(drrb->drr_toname,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
+ if (h) {
+ if (h->zfs_type == ZFS_TYPE_FILESYSTEM)
+ err = zfs_mount(h, NULL, 0);
+ else
+ err = zvol_create_link(h->zfs_name);
+ zfs_close(h);
+ }
+ }
+
+ /*
+ * If the destination snapshot was also specified, and it was a volume,
+ * make sure that the appropriate /dev link was created as well.
+ */
+ if (err == 0) {
+ zfs_handle_t *h;
+
+ if (cp)
+ *cp = '@';
+
+ h = zfs_open(drrb->drr_toname, ZFS_TYPE_SNAPSHOT |
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
+ if (h) {
+ if (h->zfs_volblocksize)
+ err = zvol_create_link(h->zfs_name);
+ zfs_close(h);
+ }
+ }
+
+ if (err)
+ return (err);
+
+ if (verbose) {
+ char buf1[64];
+ char buf2[64];
+ uint64_t bytes = zc.zc_cookie;
+ time_t delta = time(NULL) - begin_time;
+ if (delta == 0)
+ delta = 1;
+ zfs_nicenum(bytes, buf1, sizeof (buf1));
+ zfs_nicenum(bytes/delta, buf2, sizeof (buf1));
+
+ (void) printf("restored %sb backup in %lu seconds (%sb/sec)\n",
+ buf1, delta, buf2);
+ }
+ return (0);
+}
+
+/*
+ * Rollback the given dataset to the previous snapshot. It is up to the caller
+ * to verify that there is a previous snapshot available.
+ */
+int
+zfs_rollback(zfs_handle_t *zhp)
+{
+ int ret;
+ zfs_cmd_t zc = { 0 };
+
+ assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
+ zhp->zfs_type == ZFS_TYPE_VOLUME);
+
+ if (zhp->zfs_type == ZFS_TYPE_VOLUME &&
+ zvol_remove_link(zhp->zfs_name) != 0)
+ return (-1);
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+
+ if (zhp->zfs_volblocksize != 0)
+ zc.zc_objset_type = DMU_OST_ZVOL;
+ else
+ zc.zc_objset_type = DMU_OST_ZFS;
+
+ /*
+ * We rely on the consumer to verify that there are no newer snapshots
+ * for the given dataset. Given these constraints, we can simply pass
+ * the name on to the ioctl() call. There is still an unlikely race
+ * condition where the user has taken a snapshot since we verified that
+ * this was the most recent.
+ */
+ if ((ret = ioctl(zfs_fd, ZFS_IOC_ROLLBACK, &zc)) != 0) {
+ switch (errno) {
+ case EPERM:
+ /*
+ * The user doesn't have permission to rollback the
+ * given dataset.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot rollback '%s': "
+ "permission denied"), zhp->zfs_name);
+ break;
+
+ case EDQUOT:
+ case ENOSPC:
+ /*
+ * The parent dataset doesn't have enough space to
+ * rollback to the last snapshot.
+ */
+ {
+ char parent[ZFS_MAXNAMELEN];
+ (void) parent_name(zhp->zfs_name, parent,
+ sizeof (parent));
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot "
+ "rollback '%s': out of space"), parent);
+ }
+ break;
+
+ case ENOENT:
+ /*
+ * The dataset doesn't exist. This shouldn't happen
+ * except in race conditions.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot rollback '%s': "
+ "no such %s"), zhp->zfs_name,
+ zfs_type_to_name(zhp->zfs_type));
+ break;
+
+ case EBUSY:
+ /*
+ * The filesystem is busy. This should have been caught
+ * by the caller before getting here, but there may be
+ * an unexpected problem.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot rollback '%s': "
+ "%s is busy"), zhp->zfs_name,
+ zfs_type_to_name(zhp->zfs_type));
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+ } else if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
+ ret = zvol_create_link(zhp->zfs_name);
+ }
+
+ return (ret);
+}
+
+/*
+ * Iterate over all dependents for a given dataset. This includes both
+ * hierarchical dependents (children) and data dependents (snapshots and
+ * clones). The bulk of the processing occurs in get_dependents() in
+ * libzfs_graph.c.
+ */
+int
+zfs_iter_dependents(zfs_handle_t *zhp, zfs_iter_f func, void *data)
+{
+ char **dependents;
+ size_t count;
+ int i;
+ zfs_handle_t *child;
+ int ret = 0;
+
+ dependents = get_dependents(zhp->zfs_name, &count);
+ for (i = 0; i < count; i++) {
+ if ((child = make_dataset_handle(dependents[i])) == NULL)
+ continue;
+
+ if ((ret = func(child, data)) != 0)
+ break;
+ }
+
+ for (i = 0; i < count; i++)
+ free(dependents[i]);
+ free(dependents);
+
+ return (ret);
+}
+
+/*
+ * Renames the given dataset.
+ */
+int
+zfs_rename(zfs_handle_t *zhp, const char *target)
+{
+ int ret;
+ zfs_cmd_t zc = { 0 };
+ char reason[64];
+ char *delim;
+ prop_changelist_t *cl;
+ char parent[ZFS_MAXNAMELEN];
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ (void) strlcpy(zc.zc_prop_value, target, sizeof (zc.zc_prop_value));
+
+ /* if we have the same exact name, just return success */
+ if (strcmp(zhp->zfs_name, target) == 0)
+ return (0);
+
+ /*
+ * Make sure the target name is valid
+ */
+ if (!zfs_validate_name(target, zhp->zfs_type, reason,
+ sizeof (reason))) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create '%s': %s in %s name"), target, reason,
+ zfs_type_to_name(zhp->zfs_type));
+ return (-1);
+ }
+
+ if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
+ if ((delim = strchr(target, '@')) == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot rename to '%s': not a snapshot"), target);
+ return (-1);
+ }
+
+ /*
+ * Make sure we're renaming within the same dataset.
+ */
+ if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
+ zhp->zfs_name[delim - target] != '@') {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot rename to '%s': snapshots must be part "
+ "of same dataset"), target);
+ return (-1);
+ }
+
+ (void) strncpy(parent, target, delim - target);
+ parent[delim - target] = '\0';
+ } else {
+ /* validate parents */
+ if (check_parents(target, zhp->zfs_type) != 0)
+ return (-1);
+
+ (void) parent_name(target, parent, sizeof (parent));
+
+ /* make sure we're in the same pool */
+ verify((delim = strchr(target, '/')) != NULL);
+ if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
+ zhp->zfs_name[delim - target] != '/') {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot rename to '%s': "
+ "datasets must be within same pool"), target);
+ return (-1);
+ }
+ }
+
+ if (getzoneid() == GLOBAL_ZONEID &&
+ zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot rename %s, "
+ "dataset is used in a non-global zone"), zhp->zfs_name);
+ return (-1);
+ }
+
+ if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0)) == NULL)
+ return (1);
+
+ if (changelist_haszonedchild(cl)) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot rename '%s': child dataset with inherited "
+ "mountpoint is used in a non-global zone"), zhp->zfs_name);
+ ret = -1;
+ goto error;
+ }
+
+ if ((ret = changelist_prefix(cl)) != 0)
+ goto error;
+
+ if (zhp->zfs_volblocksize != 0)
+ zc.zc_objset_type = DMU_OST_ZVOL;
+ else
+ zc.zc_objset_type = DMU_OST_ZFS;
+
+ if ((ret = ioctl(zfs_fd, ZFS_IOC_RENAME, &zc)) != 0) {
+ switch (errno) {
+ case EPERM:
+ /*
+ * The user doesn't have permission to rename the
+ * given dataset.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot rename '%s': "
+ "permission denied"), zhp->zfs_name);
+ break;
+
+ case EDQUOT:
+ case ENOSPC:
+ /*
+ * Not enough space in the parent dataset.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot "
+ "rename '%s': not enough space in '%s'"),
+ zhp->zfs_name, parent);
+ break;
+
+ case ENOENT:
+ /*
+ * The destination doesn't exist.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot rename '%s' "
+ "to '%s': destination doesn't exist"),
+ zhp->zfs_name, target);
+ break;
+
+ case EEXIST:
+ /*
+ * The destination already exists.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot rename '%s' "
+ "to '%s': destination already exists"),
+ zhp->zfs_name, target);
+ break;
+
+ case EBUSY:
+ /*
+ * The filesystem is busy. This should have been caught
+ * by the caller before getting here, but there may be
+ * an unexpected problem.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot rename '%s': "
+ "%s is busy"), zhp->zfs_name,
+ zfs_type_to_name(zhp->zfs_type));
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ /*
+ * On failure, we still want to remount any filesystems that
+ * were previously mounted, so we don't alter the system state.
+ */
+ (void) changelist_postfix(cl);
+ } else {
+ changelist_rename(cl, zfs_get_name(zhp), target);
+
+ ret = changelist_postfix(cl);
+ }
+
+error:
+ changelist_free(cl);
+ return (ret);
+}
+
+/*
+ * Given a zvol dataset, issue the ioctl to create the appropriate minor node,
+ * poke devfsadm to create the /dev link, and then wait for the link to appear.
+ */
+int
+zvol_create_link(const char *dataset)
+{
+ zfs_cmd_t zc = { 0 };
+ di_devlink_handle_t hdl;
+
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+
+ /*
+ * Issue the appropriate ioctl.
+ */
+ if (ioctl(zfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) {
+ switch (errno) {
+ case EPERM:
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create "
+ "device links for '%s': permission denied"),
+ dataset);
+ break;
+
+ case EEXIST:
+ /*
+ * Silently ignore the case where the link already
+ * exists. This allows 'zfs volinit' to be run multiple
+ * times without errors.
+ */
+ return (0);
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ return (-1);
+ }
+
+ /*
+ * Call devfsadm and wait for the links to magically appear.
+ */
+ if ((hdl = di_devlink_init(ZFS_DRIVER, DI_MAKE_LINK)) == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot create device links for '%s'"), dataset);
+ (void) ioctl(zfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
+ return (-1);
+ } else {
+ (void) di_devlink_fini(&hdl);
+ }
+
+ return (0);
+}
+
+/*
+ * Remove a minor node for the given zvol and the associated /dev links.
+ */
+int
+zvol_remove_link(const char *dataset)
+{
+ zfs_cmd_t zc = { 0 };
+
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+
+ if (ioctl(zfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) {
+ switch (errno) {
+ case EPERM:
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot remove "
+ "device links for '%s': permission denied"),
+ dataset);
+ break;
+
+ case EBUSY:
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot remove "
+ "device links for '%s': volume is in use"),
+ dataset);
+ break;
+
+ case ENXIO:
+ /*
+ * Silently ignore the case where the link no longer
+ * exists, so that 'zfs volfini' can be run multiple
+ * times without errors.
+ */
+ return (0);
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_graph.c b/usr/src/lib/libzfs/common/libzfs_graph.c
new file mode 100644
index 0000000000..65b115879b
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_graph.c
@@ -0,0 +1,527 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Iterate over all children of the current object. This includes the normal
+ * dataset hierarchy, but also arbitrary hierarchies due to clones. We want to
+ * walk all datasets in the pool, and construct a directed graph of the form:
+ *
+ * home
+ * |
+ * +----+----+
+ * | |
+ * v v ws
+ * bar baz |
+ * | |
+ * v v
+ * @yesterday ----> foo
+ *
+ * In order to construct this graph, we have to walk every dataset in the pool,
+ * because the clone parent is stored as a property of the child, not the
+ * parent. The parent only keeps track of the number of clones.
+ *
+ * In the normal case (without clones) this would be rather expensive. To avoid
+ * unnecessary computation, we first try a walk of the subtree hierarchy
+ * starting from the initial node. At each dataset, we construct a node in the
+ * graph and an edge leading from its parent. If we don't see any snapshots
+ * with a non-zero clone count, then we are finished.
+ *
+ * If we do find a cloned snapshot, then we finish the walk of the current
+ * subtree, but indicate that we need to do a complete walk. We then perform a
+ * global walk of all datasets, avoiding the subtree we already processed.
+ *
+ * At the end of this, we'll end up with a directed graph of all relevant (and
+ * possible some irrelevant) datasets in the system. We need to both find our
+ * limiting subgraph and determine a safe ordering in which to destroy the
+ * datasets. We do a topological ordering of our graph starting at our target
+ * dataset, and then walk the results in reverse.
+ *
+ * When removing datasets, we want to destroy the snapshots in chronological
+ * order (because this is the most efficient method). In order to accomplish
+ * this, we store the creation transaction group with each vertex and keep each
+ * vertex's edges sorted according to this value. The topological sort will
+ * automatically walk the snapshots in the correct order.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <libzfs.h>
+
+#include "libzfs_impl.h"
+#include "zfs_namecheck.h"
+
+#define MIN_EDGECOUNT 4
+
+/*
+ * Vertex structure. Indexed by dataset name, this structure maintains a list
+ * of edges to other vertices.
+ */
+struct zfs_edge;
+typedef struct zfs_vertex {
+ char zv_dataset[ZFS_MAXNAMELEN];
+ struct zfs_vertex *zv_next;
+ int zv_visited;
+ uint64_t zv_txg;
+ struct zfs_edge **zv_edges;
+ int zv_edgecount;
+ int zv_edgealloc;
+} zfs_vertex_t;
+
+/*
+ * Edge structure. Simply maintains a pointer to the destination vertex. There
+ * is no need to store the source vertex, since we only use edges in the context
+ * of the source vertex.
+ */
+typedef struct zfs_edge {
+ zfs_vertex_t *ze_dest;
+ struct zfs_edge *ze_next;
+} zfs_edge_t;
+
+#define ZFS_GRAPH_SIZE 1027 /* this could be dynamic some day */
+
+/*
+ * Graph structure. Vertices are maintained in a hash indexed by dataset name.
+ */
+typedef struct zfs_graph {
+ zfs_vertex_t **zg_hash;
+ size_t zg_size;
+ size_t zg_nvertex;
+} zfs_graph_t;
+
+/*
+ * Allocate a new edge pointing to the target vertex.
+ */
+static zfs_edge_t *
+zfs_edge_create(zfs_vertex_t *dest)
+{
+ zfs_edge_t *zep = zfs_malloc(sizeof (zfs_edge_t));
+
+ zep->ze_dest = dest;
+
+ return (zep);
+}
+
+/*
+ * Destroy an edge.
+ */
+static void
+zfs_edge_destroy(zfs_edge_t *zep)
+{
+ free(zep);
+}
+
+/*
+ * Allocate a new vertex with the given name.
+ */
+static zfs_vertex_t *
+zfs_vertex_create(const char *dataset)
+{
+ zfs_vertex_t *zvp = zfs_malloc(sizeof (zfs_vertex_t));
+
+ assert(strlen(dataset) < ZFS_MAXNAMELEN);
+
+ (void) strlcpy(zvp->zv_dataset, dataset, sizeof (zvp->zv_dataset));
+
+ zvp->zv_edges = zfs_malloc(MIN_EDGECOUNT * sizeof (void *));
+ zvp->zv_edgealloc = MIN_EDGECOUNT;
+
+ return (zvp);
+}
+
+/*
+ * Destroy a vertex. Frees up any associated edges.
+ */
+static void
+zfs_vertex_destroy(zfs_vertex_t *zvp)
+{
+ int i;
+
+ for (i = 0; i < zvp->zv_edgecount; i++)
+ zfs_edge_destroy(zvp->zv_edges[i]);
+
+ free(zvp->zv_edges);
+ free(zvp);
+}
+
+/*
+ * Given a vertex, add an edge to the destination vertex.
+ */
+static void
+zfs_vertex_add_edge(zfs_vertex_t *zvp, zfs_vertex_t *dest)
+{
+ zfs_edge_t *zep = zfs_edge_create(dest);
+
+ if (zvp->zv_edgecount == zvp->zv_edgealloc) {
+ zfs_edge_t **newedges = zfs_malloc(zvp->zv_edgealloc * 2 *
+ sizeof (void *));
+
+ bcopy(zvp->zv_edges, newedges,
+ zvp->zv_edgealloc * sizeof (void *));
+
+ zvp->zv_edgealloc *= 2;
+ free(zvp->zv_edges);
+ zvp->zv_edges = newedges;
+ }
+
+ zvp->zv_edges[zvp->zv_edgecount++] = zep;
+}
+
+static int
+zfs_edge_compare(const void *a, const void *b)
+{
+ const zfs_edge_t *ea = *((zfs_edge_t **)a);
+ const zfs_edge_t *eb = *((zfs_edge_t **)b);
+
+ if (ea->ze_dest->zv_txg < eb->ze_dest->zv_txg)
+ return (-1);
+ if (ea->ze_dest->zv_txg > eb->ze_dest->zv_txg)
+ return (1);
+ return (0);
+}
+
+/*
+ * Sort the given vertex edges according to the creation txg of each vertex.
+ */
+static void
+zfs_vertex_sort_edges(zfs_vertex_t *zvp)
+{
+ if (zvp->zv_edgecount == 0)
+ return;
+
+ qsort(zvp->zv_edges, zvp->zv_edgecount, sizeof (void *),
+ zfs_edge_compare);
+}
+
+/*
+ * Construct a new graph object. We allow the size to be specified as a
+ * parameter so in the future we can size the hash according to the number of
+ * datasets in the pool.
+ */
+static zfs_graph_t *
+zfs_graph_create(size_t size)
+{
+ zfs_graph_t *zgp = zfs_malloc(sizeof (zfs_graph_t));
+
+ zgp->zg_size = size;
+ zgp->zg_hash = zfs_malloc(size * sizeof (zfs_vertex_t *));
+
+ return (zgp);
+}
+
+/*
+ * Destroy a graph object. We have to iterate over all the hash chains,
+ * destroying each vertex in the process.
+ */
+static void
+zfs_graph_destroy(zfs_graph_t *zgp)
+{
+ int i;
+ zfs_vertex_t *current, *next;
+
+ for (i = 0; i < zgp->zg_size; i++) {
+ current = zgp->zg_hash[i];
+ while (current != NULL) {
+ next = current->zv_next;
+ zfs_vertex_destroy(current);
+ current = next;
+ }
+ }
+
+ free(zgp->zg_hash);
+ free(zgp);
+}
+
+/*
+ * Graph hash function. Classic bernstein k=33 hash function, taken from
+ * usr/src/cmd/sgs/tools/common/strhash.c
+ */
+static size_t
+zfs_graph_hash(zfs_graph_t *zgp, const char *str)
+{
+ size_t hash = 5381;
+ int c;
+
+ while ((c = *str++) != 0)
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+
+ return (hash % zgp->zg_size);
+}
+
+/*
+ * Given a dataset name, finds the associated vertex, creating it if necessary.
+ */
+static zfs_vertex_t *
+zfs_graph_lookup(zfs_graph_t *zgp, const char *dataset, uint64_t txg)
+{
+ size_t idx = zfs_graph_hash(zgp, dataset);
+ zfs_vertex_t *zvp;
+
+ for (zvp = zgp->zg_hash[idx]; zvp != NULL; zvp = zvp->zv_next) {
+ if (strcmp(zvp->zv_dataset, dataset) == 0) {
+ if (zvp->zv_txg == 0)
+ zvp->zv_txg = txg;
+ return (zvp);
+ }
+ }
+
+ zvp = zfs_vertex_create(dataset);
+ zvp->zv_next = zgp->zg_hash[idx];
+ zvp->zv_txg = txg;
+ zgp->zg_hash[idx] = zvp;
+ zgp->zg_nvertex++;
+
+ return (zvp);
+}
+
+/*
+ * Given two dataset names, create an edge between them. For the source vertex,
+ * mark 'zv_visited' to indicate that we have seen this vertex, and not simply
+ * created it as a destination of another edge. If 'dest' is NULL, then this
+ * is an individual vertex (i.e. the starting vertex), so don't add an edge.
+ */
+static void
+zfs_graph_add(zfs_graph_t *zgp, const char *source, const char *dest,
+ uint64_t txg)
+{
+ zfs_vertex_t *svp, *dvp;
+
+ svp = zfs_graph_lookup(zgp, source, 0);
+ svp->zv_visited = 1;
+ if (dest != NULL) {
+ dvp = zfs_graph_lookup(zgp, dest, txg);
+ zfs_vertex_add_edge(svp, dvp);
+ }
+}
+
+/*
+ * Iterate over all children of the given dataset, adding any vertices as
+ * necessary. Returns 0 if no cloned snapshots were seen, 1 otherwise. This is
+ * a simple recursive algorithm - the ZFS namespace typically is very flat. We
+ * manually invoke the necessary ioctl() calls to avoid the overhead and
+ * additional semantics of zfs_open().
+ */
+static int
+iterate_children(zfs_graph_t *zgp, const char *dataset)
+{
+ zfs_cmd_t zc = { 0 };
+ int ret = 0;
+ zfs_vertex_t *zvp;
+
+ /*
+ * Look up the source vertex, and avoid it if we've seen it before.
+ */
+ zvp = zfs_graph_lookup(zgp, dataset, 0);
+ if (zvp->zv_visited)
+ return (0);
+
+ for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+ ioctl(zfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
+
+ /*
+ * Ignore private dataset names.
+ */
+ if (dataset_name_hidden(zc.zc_name))
+ continue;
+
+ /*
+ * Get statistics for this dataset, to determine the type of the
+ * dataset and clone statistics. If this fails, the dataset has
+ * since been removed, and we're pretty much screwed anyway.
+ */
+ if (ioctl(zfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
+ continue;
+
+ /*
+ * Add an edge between the parent and the child.
+ */
+ zfs_graph_add(zgp, dataset, zc.zc_name,
+ zc.zc_objset_stats.dds_creation_txg);
+
+ /*
+ * If this dataset has a clone parent, add an appropriate edge.
+ */
+ if (zc.zc_objset_stats.dds_clone_of[0] != '\0')
+ zfs_graph_add(zgp, zc.zc_objset_stats.dds_clone_of,
+ zc.zc_name, zc.zc_objset_stats.dds_creation_txg);
+
+ /*
+ * Iterate over all children
+ */
+ ret |= iterate_children(zgp, zc.zc_name);
+
+ /*
+ * Indicate if we found a dataset with a non-zero clone count.
+ */
+ if (zc.zc_objset_stats.dds_num_clones != 0)
+ ret |= 1;
+ }
+
+ /*
+ * Now iterate over all snapshots.
+ */
+ bzero(&zc, sizeof (zc));
+
+ for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+ ioctl(zfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0;
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
+
+ /*
+ * Get statistics for this dataset, to determine the type of the
+ * dataset and clone statistics. If this fails, the dataset has
+ * since been removed, and we're pretty much screwed anyway.
+ */
+ if (ioctl(zfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
+ continue;
+
+ /*
+ * Add an edge between the parent and the child.
+ */
+ zfs_graph_add(zgp, dataset, zc.zc_name,
+ zc.zc_objset_stats.dds_creation_txg);
+
+ /*
+ * Indicate if we found a dataset with a non-zero clone count.
+ */
+ if (zc.zc_objset_stats.dds_num_clones != 0)
+ ret |= 1;
+ }
+
+ zvp->zv_visited = 1;
+
+ return (ret);
+}
+
+/*
+ * Construct a complete graph of all necessary vertices. First, we iterate over
+ * only our object's children. If we don't find any cloned snapshots, then we
+ * simple return that. Otherwise, we have to start at the pool root and iterate
+ * over all datasets.
+ */
+static zfs_graph_t *
+construct_graph(const char *dataset)
+{
+ zfs_graph_t *zgp = zfs_graph_create(ZFS_GRAPH_SIZE);
+ zfs_cmd_t zc = { 0 };
+
+ /*
+ * We need to explicitly check whether this dataset has clones or not,
+ * since iterate_children() only checks the children.
+ */
+ (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
+ (void) ioctl(zfs_fd, ZFS_IOC_OBJSET_STATS, &zc);
+
+ if (zc.zc_objset_stats.dds_num_clones != 0 ||
+ iterate_children(zgp, dataset) != 0) {
+ /*
+ * Determine pool name and try again.
+ */
+ char *pool, *slash;
+
+ if ((slash = strchr(dataset, '/')) != NULL ||
+ (slash = strchr(dataset, '@')) != NULL) {
+ pool = zfs_malloc(slash - dataset + 1);
+ (void) strncpy(pool, dataset, slash - dataset);
+ pool[slash - dataset] = '\0';
+
+ (void) iterate_children(zgp, pool);
+ zfs_graph_add(zgp, pool, NULL, 0);
+
+ free(pool);
+ }
+ }
+ zfs_graph_add(zgp, dataset, NULL, 0);
+
+ return (zgp);
+}
+
+/*
+ * Given a graph, do a recursive topological sort into the given array. This is
+ * really just a depth first search, so that the deepest nodes appear first.
+ * hijack the 'zv_visited' marker to avoid visiting the same vertex twice.
+ */
+static void
+topo_sort(char **result, size_t *idx, zfs_vertex_t *zgv)
+{
+ int i;
+
+ /* avoid doing a search if we don't have to */
+ if (zgv->zv_visited == 2)
+ return;
+
+ zfs_vertex_sort_edges(zgv);
+ for (i = 0; i < zgv->zv_edgecount; i++)
+ topo_sort(result, idx, zgv->zv_edges[i]->ze_dest);
+
+ /* we may have visited this in the course of the above */
+ if (zgv->zv_visited == 2)
+ return;
+
+ result[*idx] = zfs_malloc(strlen(zgv->zv_dataset) + 1);
+ (void) strcpy(result[*idx], zgv->zv_dataset);
+ *idx += 1;
+ zgv->zv_visited = 2;
+}
+
+/*
+ * The only public interface for this file. Do the dirty work of constructing a
+ * child list for the given object. Construct the graph, do the toplogical
+ * sort, and then return the array of strings to the caller.
+ */
+char **
+get_dependents(const char *dataset, size_t *count)
+{
+ char **result;
+ zfs_graph_t *zgp;
+ zfs_vertex_t *zvp;
+
+ zgp = construct_graph(dataset);
+ result = zfs_malloc(zgp->zg_nvertex * sizeof (char *));
+
+ zvp = zfs_graph_lookup(zgp, dataset, 0);
+
+ *count = 0;
+ topo_sort(result, count, zvp);
+
+ /*
+ * Get rid of the last entry, which is our starting vertex and not
+ * strictly a dependent.
+ */
+ assert(*count > 0);
+ free(result[*count - 1]);
+ (*count)--;
+
+ zfs_graph_destroy(zgp);
+
+ return (result);
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_impl.h b/usr/src/lib/libzfs/common/libzfs_impl.h
new file mode 100644
index 0000000000..3fdd98c997
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_impl.h
@@ -0,0 +1,103 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBFS_IMPL_H
+#define _LIBFS_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/dmu.h>
+#include <sys/fs/zfs.h>
+#include <sys/zfs_ioctl.h>
+#include <sys/zfs_acl.h>
+#include <sys/nvpair.h>
+
+#include <libzfs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct zfs_handle {
+ char zfs_name[ZFS_MAXNAMELEN];
+ zfs_type_t zfs_type;
+ dmu_objset_stats_t zfs_dmustats;
+ zfs_stats_t zfs_zplstats;
+ uint64_t zfs_volsize;
+ uint64_t zfs_volblocksize;
+ char *zfs_mntopts;
+};
+
+struct zpool_handle {
+ char zpool_name[ZPOOL_MAXNAMELEN];
+ int zpool_state;
+ size_t zpool_config_size;
+ nvlist_t *zpool_config;
+};
+
+void zfs_error(const char *, ...);
+void zfs_fatal(const char *, ...);
+void *zfs_malloc(size_t);
+char *zfs_strdup(const char *);
+void no_memory(void);
+
+#define zfs_baderror(err) \
+ (zfs_fatal(dgettext(TEXT_DOMAIN, \
+ "internal error: unexpected error %d at line %d of %s"), \
+ (err), (__LINE__), (__FILE__)))
+
+int zfs_fd;
+
+char **get_dependents(const char *, size_t *);
+
+FILE *mnttab_file;
+FILE *sharetab_file;
+
+typedef struct prop_changelist prop_changelist_t;
+
+int changelist_prefix(prop_changelist_t *);
+int changelist_postfix(prop_changelist_t *);
+void changelist_rename(prop_changelist_t *, const char *, const char *);
+void changelist_free(prop_changelist_t *);
+prop_changelist_t *changelist_gather(zfs_handle_t *, zfs_prop_t, int);
+int changelist_unshare(prop_changelist_t *);
+int changelist_haszonedchild(prop_changelist_t *);
+
+void remove_mountpoint(zfs_handle_t *);
+
+zfs_handle_t *make_dataset_handle(const char *);
+void set_pool_health(nvlist_t *config);
+
+zpool_handle_t *zpool_open_silent(const char *pool);
+
+int zvol_create_link(const char *dataset);
+int zvol_remove_link(const char *dataset);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBFS_IMPL_H */
diff --git a/usr/src/lib/libzfs/common/libzfs_import.c b/usr/src/lib/libzfs/common/libzfs_import.c
new file mode 100644
index 0000000000..c71bc437f5
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_import.c
@@ -0,0 +1,753 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Pool import support functions.
+ *
+ * To import a pool, we rely on reading the configuration information from the
+ * ZFS label of each device. If we successfully read the label, then we
+ * organize the configuration information in the following hierarchy:
+ *
+ * pool guid -> toplevel vdev guid -> label txg
+ *
+ * Duplicate entries matching this same tuple will be discarded. Once we have
+ * examined every device, we pick the best label txg config for each toplevel
+ * vdev. We then arrange these toplevel vdevs into a complete pool config, and
+ * update any paths that have changed. Finally, we attempt to import the pool
+ * using our derived config, and record the results.
+ */
+
+#include <devid.h>
+#include <dirent.h>
+#include <errno.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/vdev_impl.h>
+
+#include "libzfs.h"
+#include "libzfs_impl.h"
+
+/*
+ * Intermediate structures used to gather configuration information.
+ */
+typedef struct config_entry {
+ uint64_t ce_txg;
+ nvlist_t *ce_config;
+ struct config_entry *ce_next;
+} config_entry_t;
+
+typedef struct vdev_entry {
+ uint64_t ve_guid;
+ config_entry_t *ve_configs;
+ struct vdev_entry *ve_next;
+} vdev_entry_t;
+
+typedef struct pool_entry {
+ uint64_t pe_guid;
+ vdev_entry_t *pe_vdevs;
+ struct pool_entry *pe_next;
+} pool_entry_t;
+
+typedef struct name_entry {
+ const char *ne_name;
+ uint64_t ne_guid;
+ struct name_entry *ne_next;
+} name_entry_t;
+
+typedef struct pool_list {
+ pool_entry_t *pools;
+ name_entry_t *names;
+} pool_list_t;
+
+static char *
+get_devid(const char *path)
+{
+ int fd;
+ ddi_devid_t devid;
+ char *minor, *ret;
+
+ if ((fd = open(path, O_RDONLY)) < 0)
+ return (NULL);
+
+ minor = NULL;
+ ret = NULL;
+ if (devid_get(fd, &devid) == 0) {
+ if (devid_get_minor_name(fd, &minor) == 0)
+ ret = devid_str_encode(devid, minor);
+ if (minor != NULL)
+ devid_str_free(minor);
+ devid_free(devid);
+ }
+
+ return (ret);
+}
+
+
+/*
+ * Go through and fix up any path and/or devid information for the given vdev
+ * configuration.
+ */
+static void
+fix_paths(nvlist_t *nv, name_entry_t *names)
+{
+ nvlist_t **child;
+ uint_t c, children;
+ uint64_t guid;
+ name_entry_t *ne;
+ char *devid;
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) == 0) {
+ for (c = 0; c < children; c++)
+ fix_paths(child[c], names);
+ return;
+ }
+
+ /*
+ * This is a leaf (file or disk) vdev. In either case, go through
+ * the name list and see if we find a matching guid. If so, replace
+ * the path and see if we can calculate a new devid.
+ */
+ verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0);
+
+ for (ne = names; ne != NULL; ne = ne->ne_next)
+ if (ne->ne_guid == guid)
+ break;
+
+ if (ne == NULL)
+ return;
+
+ verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, ne->ne_name) == 0);
+
+ if ((devid = get_devid(ne->ne_name)) == NULL) {
+ (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID);
+ } else {
+ verify(nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) == 0);
+ devid_str_free(devid);
+ }
+}
+
+/*
+ * Add the given configuration to the list of known devices.
+ */
+static void
+add_config(pool_list_t *pl, const char *path, nvlist_t *config)
+{
+ uint64_t pool_guid, vdev_guid, top_guid, txg;
+ pool_entry_t *pe;
+ vdev_entry_t *ve;
+ config_entry_t *ce;
+ name_entry_t *ne;
+
+ /*
+ * If we have a valid config but cannot read any of these fields, then
+ * it means we have a half-initialized label. In vdev_label_init()
+ * we write a label with txg == 0 so that we can identify the device
+ * in case the user refers to the same disk later on. If we fail to
+ * create the pool, we'll be left with a label in this state
+ * which should not be considered part of a valid pool.
+ */
+ if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
+ &pool_guid) != 0 ||
+ nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
+ &vdev_guid) != 0 ||
+ nvlist_lookup_uint64(config, ZPOOL_CONFIG_TOP_GUID,
+ &top_guid) != 0 ||
+ nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG,
+ &txg) != 0 || txg == 0) {
+ nvlist_free(config);
+ return;
+ }
+
+ /*
+ * First, see if we know about this pool. If not, then add it to the
+ * list of known pools.
+ */
+ for (pe = pl->pools; pe != NULL; pe = pe->pe_next) {
+ if (pe->pe_guid == pool_guid)
+ break;
+ }
+
+ if (pe == NULL) {
+ pe = zfs_malloc(sizeof (pool_entry_t));
+ pe->pe_guid = pool_guid;
+ pe->pe_next = pl->pools;
+ pl->pools = pe;
+ }
+
+ /*
+ * Second, see if we know about this toplevel vdev. Add it if its
+ * missing.
+ */
+ for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) {
+ if (ve->ve_guid == top_guid)
+ break;
+ }
+
+ if (ve == NULL) {
+ ve = zfs_malloc(sizeof (vdev_entry_t));
+ ve->ve_guid = top_guid;
+ ve->ve_next = pe->pe_vdevs;
+ pe->pe_vdevs = ve;
+ }
+
+ /*
+ * Third, see if we have a config with a matching transaction group. If
+ * so, then we do nothing. Otherwise, add it to the list of known
+ * configs.
+ */
+ for (ce = ve->ve_configs; ce != NULL; ce = ce->ce_next) {
+ if (ce->ce_txg == txg)
+ break;
+ }
+
+ if (ce == NULL) {
+ ce = zfs_malloc(sizeof (config_entry_t));
+ ce->ce_txg = txg;
+ ce->ce_config = config;
+ ce->ce_next = ve->ve_configs;
+ ve->ve_configs = ce;
+ } else {
+ nvlist_free(config);
+ }
+
+ /*
+ * At this point we've successfully added our config to the list of
+ * known configs. The last thing to do is add the vdev guid -> path
+ * mappings so that we can fix up the configuration as necessary before
+ * doing the import.
+ */
+ ne = zfs_malloc(sizeof (name_entry_t));
+
+ ne->ne_name = zfs_strdup(path);
+ ne->ne_guid = vdev_guid;
+ ne->ne_next = pl->names;
+ pl->names = ne;
+}
+
+/*
+ * Convert our list of pools into the definitive set of configurations. We
+ * start by picking the best config for each toplevel vdev. Once that's done,
+ * we assemble the toplevel vdevs into a full config for the pool. We make a
+ * pass to fix up any incorrect paths, and then add it to the main list to
+ * return to the user.
+ */
+static nvlist_t *
+get_configs(pool_list_t *pl)
+{
+ pool_entry_t *pe, *penext;
+ vdev_entry_t *ve, *venext;
+ config_entry_t *ce, *cenext;
+ nvlist_t *ret, *config, *tmp, *nvtop, *nvroot;
+ int config_seen;
+ uint64_t best_txg;
+ char *name;
+ zfs_cmd_t zc = { 0 };
+ uint64_t guid;
+ char *packed;
+ size_t len;
+ int err;
+
+ verify(nvlist_alloc(&ret, 0, 0) == 0);
+
+ for (pe = pl->pools; pe != NULL; pe = penext) {
+ uint_t c;
+ uint_t children = 0;
+ uint64_t id;
+ nvlist_t **child = NULL;
+
+ penext = pe->pe_next;
+
+ verify(nvlist_alloc(&config, NV_UNIQUE_NAME, 0) == 0);
+ config_seen = FALSE;
+
+ /*
+ * Iterate over all toplevel vdevs. Grab the pool configuration
+ * from the first one we find, and then go through the rest and
+ * add them as necessary to the 'vdevs' member of the config.
+ */
+ for (ve = pe->pe_vdevs; ve != NULL; ve = venext) {
+ venext = ve->ve_next;
+
+ /*
+ * Determine the best configuration for this vdev by
+ * selecting the config with the latest transaction
+ * group.
+ */
+ best_txg = 0;
+ for (ce = ve->ve_configs; ce != NULL;
+ ce = ce->ce_next) {
+
+ if (ce->ce_txg > best_txg)
+ tmp = ce->ce_config;
+ }
+
+ if (!config_seen) {
+ /*
+ * Copy the relevant pieces of data to the pool
+ * configuration:
+ *
+ * pool guid
+ * name
+ * pool state
+ */
+ uint64_t state;
+
+ verify(nvlist_lookup_uint64(tmp,
+ ZPOOL_CONFIG_POOL_GUID, &guid) == 0);
+ verify(nvlist_add_uint64(config,
+ ZPOOL_CONFIG_POOL_GUID, guid) == 0);
+ verify(nvlist_lookup_string(tmp,
+ ZPOOL_CONFIG_POOL_NAME, &name) == 0);
+ verify(nvlist_add_string(config,
+ ZPOOL_CONFIG_POOL_NAME, name) == 0);
+ verify(nvlist_lookup_uint64(tmp,
+ ZPOOL_CONFIG_POOL_STATE, &state) == 0);
+ verify(nvlist_add_uint64(config,
+ ZPOOL_CONFIG_POOL_STATE, state) == 0);
+
+ config_seen = TRUE;
+ }
+
+ /*
+ * Add this top-level vdev to the child array.
+ */
+ verify(nvlist_lookup_nvlist(tmp,
+ ZPOOL_CONFIG_VDEV_TREE, &nvtop) == 0);
+ verify(nvlist_lookup_uint64(nvtop, ZPOOL_CONFIG_ID,
+ &id) == 0);
+ if (id >= children) {
+ nvlist_t **newchild;
+
+ newchild = zfs_malloc((id + 1) *
+ sizeof (nvlist_t *));
+
+ for (c = 0; c < children; c++)
+ newchild[c] = child[c];
+
+ free(child);
+ child = newchild;
+ children = id + 1;
+ }
+ verify(nvlist_dup(nvtop, &child[id], 0) == 0);
+
+ /*
+ * Go through and free all config information.
+ */
+ for (ce = ve->ve_configs; ce != NULL; ce = cenext) {
+ cenext = ce->ce_next;
+
+ nvlist_free(ce->ce_config);
+ free(ce);
+ }
+
+ /*
+ * Free this vdev entry, since it has now been merged
+ * into the main config.
+ */
+ free(ve);
+ }
+
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
+ &guid) == 0);
+
+ /*
+ * Look for any missing top-level vdevs. If this is the case,
+ * create a faked up 'missing' vdev as a placeholder. We cannot
+ * simply compress the child array, because the kernel performs
+ * certain checks to make sure the vdev IDs match their location
+ * in the configuration.
+ */
+ for (c = 0; c < children; c++)
+ if (child[c] == NULL) {
+ nvlist_t *missing;
+ verify(nvlist_alloc(&missing, NV_UNIQUE_NAME,
+ 0) == 0);
+ verify(nvlist_add_string(missing,
+ ZPOOL_CONFIG_TYPE, VDEV_TYPE_MISSING) == 0);
+ verify(nvlist_add_uint64(missing,
+ ZPOOL_CONFIG_ID, c) == 0);
+ verify(nvlist_add_uint64(missing,
+ ZPOOL_CONFIG_GUID, 0ULL) == 0);
+ child[c] = missing;
+ }
+
+ /*
+ * Put all of this pool's top-level vdevs into a root vdev.
+ */
+ verify(nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) == 0);
+ verify(nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE,
+ VDEV_TYPE_ROOT) == 0);
+ verify(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) == 0);
+ verify(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, guid) == 0);
+ verify(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
+ child, children) == 0);
+
+ for (c = 0; c < children; c++)
+ nvlist_free(child[c]);
+ free(child);
+
+ /*
+ * Go through and fix up any paths and/or devids based on our
+ * known list of vdev GUID -> path mappings.
+ */
+ fix_paths(nvroot, pl->names);
+
+ /*
+ * Add the root vdev to this pool's configuration.
+ */
+ verify(nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ nvroot) == 0);
+ nvlist_free(nvroot);
+
+ /*
+ * Free this pool entry.
+ */
+ free(pe);
+
+ /*
+ * Determine if this pool is currently active, in which case we
+ * can't actually import it.
+ */
+ verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
+ &name) == 0);
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
+ &guid) == 0);
+
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+ if (ioctl(zfs_fd, ZFS_IOC_POOL_GUID, &zc) == 0 &&
+ guid == zc.zc_pool_guid) {
+ nvlist_free(config);
+ continue;
+ }
+
+ /*
+ * Try to do the import in order to get vdev state.
+ */
+ if ((err = nvlist_size(config, &len, NV_ENCODE_NATIVE)) != 0)
+ zfs_baderror(err);
+
+ packed = zfs_malloc(len);
+
+ if ((err = nvlist_pack(config, &packed, &len,
+ NV_ENCODE_NATIVE, 0)) != 0)
+ zfs_baderror(err);
+
+ nvlist_free(config);
+ config = NULL;
+
+ zc.zc_config_src_size = len;
+ zc.zc_config_src = (uint64_t)(uintptr_t)packed;
+
+ zc.zc_config_dst_size = 2 * len;
+ zc.zc_config_dst = (uint64_t)(uintptr_t)
+ zfs_malloc(zc.zc_config_dst_size);
+
+ while ((err = ioctl(zfs_fd, ZFS_IOC_POOL_TRYIMPORT,
+ &zc)) != 0 && errno == ENOMEM) {
+ free((void *)(uintptr_t)zc.zc_config_dst);
+ zc.zc_config_dst = (uint64_t)(uintptr_t)
+ zfs_malloc(zc.zc_config_dst_size);
+ }
+
+ free(packed);
+
+ if (err)
+ zfs_baderror(errno);
+
+ verify(nvlist_unpack((void *)(uintptr_t)zc.zc_config_dst,
+ zc.zc_config_dst_size, &config, 0) == 0);
+
+ set_pool_health(config);
+
+ /*
+ * Add this pool to the list of configs.
+ */
+ verify(nvlist_add_nvlist(ret, name, config) == 0);
+
+ nvlist_free(config);
+
+ free((void *)(uintptr_t)zc.zc_config_dst);
+ }
+
+ return (ret);
+}
+
+/*
+ * Return the offset of the given label.
+ */
+static uint64_t
+label_offset(size_t size, int l)
+{
+ return (l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ?
+ 0 : size - VDEV_LABELS * sizeof (vdev_label_t)));
+}
+
+/*
+ * Given a file descriptor, read the label information and return an nvlist
+ * describing the configuration, if there is one.
+ */
+nvlist_t *
+zpool_read_label(int fd)
+{
+ struct stat64 statbuf;
+ int l;
+ vdev_label_t *label;
+ nvlist_t *config;
+ uint64_t version, state, txg;
+
+ if (fstat64(fd, &statbuf) == -1)
+ return (NULL);
+
+ label = zfs_malloc(sizeof (vdev_label_t));
+
+ for (l = 0; l < VDEV_LABELS; l++) {
+ if (pread(fd, label, sizeof (vdev_label_t),
+ label_offset(statbuf.st_size, l)) != sizeof (vdev_label_t))
+ continue;
+
+ if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist,
+ sizeof (label->vl_vdev_phys.vp_nvlist), &config, 0) != 0)
+ continue;
+
+ if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
+ &version) != 0 || version != UBERBLOCK_VERSION) {
+ nvlist_free(config);
+ continue;
+ }
+
+ if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
+ &state) != 0 || state > POOL_STATE_EXPORTED) {
+ nvlist_free(config);
+ continue;
+ }
+
+ if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG,
+ &txg) != 0 || txg == 0) {
+ nvlist_free(config);
+ continue;
+ }
+
+ free(label);
+ return (config);
+ }
+
+ free(label);
+ return (NULL);
+}
+
+/*
+ * Given a list of directories to search, find all pools stored on disk. This
+ * includes partial pools which are not available to import. If no args are
+ * given (argc is 0), then the default directory (/dev/dsk) is searched.
+ */
+nvlist_t *
+zpool_find_import(int argc, char **argv)
+{
+ int i;
+ DIR *dirp;
+ struct dirent64 *dp;
+ char path[MAXPATHLEN];
+ struct stat64 statbuf;
+ nvlist_t *ret, *config;
+ static char *default_dir = "/dev/dsk";
+ int fd;
+ pool_list_t pools = { 0 };
+
+ if (argc == 0) {
+ argc = 1;
+ argv = &default_dir;
+ }
+
+ /*
+ * Go through and read the label configuration information from every
+ * possible device, organizing the information according to pool GUID
+ * and toplevel GUID.
+ */
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] != '/') {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot open '%s': must be an absolute path"),
+ argv[i]);
+ return (NULL);
+ }
+
+ if ((dirp = opendir(argv[i])) == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot open '%s': %s"), argv[i],
+ strerror(errno));
+ return (NULL);
+ }
+
+ /*
+ * This is not MT-safe, but we have no MT consumers of libzfs
+ */
+ while ((dp = readdir64(dirp)) != NULL) {
+
+ (void) snprintf(path, sizeof (path), "%s/%s",
+ argv[i], dp->d_name);
+
+ if (stat64(path, &statbuf) != 0)
+ continue;
+
+ /*
+ * Ignore directories (which includes "." and "..").
+ */
+ if (S_ISDIR(statbuf.st_mode))
+ continue;
+
+ if ((fd = open64(path, O_RDONLY)) < 0)
+ continue;
+
+ config = zpool_read_label(fd);
+
+ (void) close(fd);
+
+ if (config != NULL)
+ add_config(&pools, path, config);
+ }
+ }
+
+ ret = get_configs(&pools);
+
+ return (ret);
+}
+
+int
+find_guid(nvlist_t *nv, uint64_t guid)
+{
+ uint64_t tmp;
+ nvlist_t **child;
+ uint_t c, children;
+
+ verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &tmp) == 0);
+ if (tmp == guid)
+ return (TRUE);
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) == 0) {
+ for (c = 0; c < children; c++)
+ if (find_guid(child[c], guid))
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+/*
+ * Determines if the pool is in use. If so, it returns TRUE and the state of
+ * the pool as well as the name of the pool. Both strings are allocated and
+ * must be freed by the caller.
+ */
+int
+zpool_in_use(int fd, char **statestr, char **namestr)
+{
+ nvlist_t *config;
+ uint64_t state;
+ char *name;
+ int ret;
+ zfs_cmd_t zc = { 0 };
+ uint64_t guid, vdev_guid;
+ zpool_handle_t *zhp;
+ nvlist_t *pool_config;
+
+ if ((config = zpool_read_label(fd)) == NULL)
+ return (FALSE);
+
+ verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
+ &name) == 0);
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
+ &state) == 0);
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
+ &guid) == 0);
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
+ &vdev_guid) == 0);
+
+ switch (state) {
+ case POOL_STATE_EXPORTED:
+ *statestr = zfs_strdup(dgettext(TEXT_DOMAIN, "exported"));
+ *namestr = zfs_strdup(name);
+ ret = TRUE;
+ break;
+
+ case POOL_STATE_ACTIVE:
+ /*
+ * For an active pool, we have to determine if it's really part
+ * of an active pool (in which case the pool will exist and the
+ * guid will be the same), or whether it's part of an active
+ * pool that was disconnected without being explicitly exported.
+ *
+ * We use the direct ioctl() first to avoid triggering an error
+ * message if the pool cannot be opened.
+ */
+ (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
+ if (ioctl(zfs_fd, ZFS_IOC_POOL_GUID, &zc) == 0 &&
+ guid == zc.zc_pool_guid) {
+ /*
+ * Because the device may have been removed while
+ * offlined, we only report it as active if the vdev is
+ * still present in the config. Otherwise, pretend like
+ * it's not in use.
+ */
+ if ((zhp = zpool_open_canfail(name)) != NULL &&
+ (pool_config = zpool_get_config(zhp)) != NULL) {
+ nvlist_t *nvroot;
+
+ verify(nvlist_lookup_nvlist(pool_config,
+ ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
+ if (find_guid(nvroot, vdev_guid)) {
+ *statestr = zfs_strdup(
+ dgettext(TEXT_DOMAIN, "active"));
+ *namestr = zfs_strdup(name);
+ ret = TRUE;
+ } else {
+ ret = FALSE;
+ }
+ } else {
+ ret = FALSE;
+ }
+ } else {
+ *statestr = zfs_strdup(dgettext(TEXT_DOMAIN,
+ "potentially active"));
+ *namestr = zfs_strdup(name);
+ ret = TRUE;
+ }
+ break;
+
+ default:
+ ret = FALSE;
+ }
+
+ nvlist_free(config);
+ return (ret);
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_mount.c b/usr/src/lib/libzfs/common/libzfs_mount.c
new file mode 100644
index 0000000000..1f4bec2499
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_mount.c
@@ -0,0 +1,558 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines to manage ZFS mounts. We separate all the nasty routines that have
+ * to deal with the OS. The main entry points are:
+ *
+ * zfs_is_mounted()
+ * zfs_mount()
+ * zfs_unmount()
+ * zfs_unmountall()
+ *
+ * These functions are used by mount and unmount, and when changing a
+ * filesystem's mountpoint. This file also contains the functions used to
+ * manage sharing filesystems via NFS:
+ *
+ * zfs_is_shared()
+ * zfs_share()
+ * zfs_unshare()
+ * zfs_unshareall()
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <libgen.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <zone.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <libzfs.h>
+
+#include "libzfs_impl.h"
+
+
+/*
+ * The following two files are opened as part of zfs_init(). It's OK to for
+ * the sharetab to be NULL, but mnttab must always be non-NULL;
+ */
+FILE *mnttab_file;
+FILE *sharetab_file;
+
+/*
+ * Search the sharetab for the given mountpoint, returning TRUE if it is found.
+ */
+static int
+is_shared(const char *mountpoint)
+{
+ char buf[MAXPATHLEN], *tab;
+
+ if (sharetab_file == NULL)
+ return (0);
+
+ (void) fseek(sharetab_file, 0, SEEK_SET);
+
+ while (fgets(buf, sizeof (buf), sharetab_file) != NULL) {
+
+ /* the mountpoint is the first entry on each line */
+ if ((tab = strchr(buf, '\t')) != NULL) {
+ *tab = '\0';
+ if (strcmp(buf, mountpoint) == 0)
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Returns TRUE if the specified directory is empty. If we can't open the
+ * directory at all, return TRUE so that the mount can fail with a more
+ * informative error message.
+ */
+static int
+dir_is_empty(const char *dirname)
+{
+ DIR *dirp;
+ struct dirent64 *dp;
+
+ if ((dirp = opendir(dirname)) == NULL)
+ return (TRUE);
+
+ while ((dp = readdir64(dirp)) != NULL) {
+
+ if (strcmp(dp->d_name, ".") == 0 ||
+ strcmp(dp->d_name, "..") == 0)
+ continue;
+
+ (void) closedir(dirp);
+ return (FALSE);
+ }
+
+ (void) closedir(dirp);
+ return (TRUE);
+}
+
+/*
+ * Checks to see if the mount is active. If the filesystem is mounted, we fill
+ * in 'where' with the current mountpoint, and return 1. Otherwise, we return
+ * 0.
+ */
+int
+zfs_is_mounted(zfs_handle_t *zhp, char **where)
+{
+ struct mnttab search = { 0 }, entry;
+
+ /*
+ * Search for the entry in /etc/mnttab. We don't bother getting the
+ * mountpoint, as we can just search for the special device. This will
+ * also let us find mounts when the mountpoint is 'legacy'.
+ */
+ search.mnt_special = (char *)zfs_get_name(zhp);
+
+ rewind(mnttab_file);
+ if (getmntany(mnttab_file, &entry, &search) != 0)
+ return (FALSE);
+
+ if (where != NULL)
+ *where = zfs_strdup(entry.mnt_mountp);
+
+ return (TRUE);
+}
+
+/*
+ * Mount the given filesystem.
+ */
+int
+zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
+{
+ struct stat buf;
+ char mountpoint[ZFS_MAXPROPLEN];
+ char mntopts[MNT_LINE_MAX];
+
+ if (options == NULL)
+ mntopts[0] = '\0';
+ else
+ (void) strlcpy(mntopts, options, sizeof (mntopts));
+
+ /* ignore non-filesystems */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), NULL, NULL, 0, FALSE) != 0)
+ return (0);
+
+ /* return success if there is no mountpoint set */
+ if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
+ strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
+ return (0);
+
+ /*
+ * If the 'zoned' property is set, and we're in the global zone, simply
+ * return success.
+ */
+ if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
+ char zonename[ZONENAME_MAX];
+ if (getzonenamebyid(getzoneid(), zonename,
+ sizeof (zonename)) < 0) {
+ zfs_error(dgettext(TEXT_DOMAIN, "internal error: "
+ "cannot determine current zone"));
+ return (1);
+ }
+
+ if (strcmp(zonename, "global") == 0)
+ return (0);
+ }
+
+ /* Create the directory if it doesn't already exist */
+ if (lstat(mountpoint, &buf) != 0) {
+ if (mkdirp(mountpoint, 0755) != 0) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': "
+ "unable to create mountpoint"), mountpoint);
+ return (1);
+ }
+ }
+
+ /*
+ * Determine if the mountpoint is empty. If so, refuse to perform the
+ * mount. We don't perform this check if MS_OVERLAY is specified, which
+ * would defeat the point. We also avoid this check if 'remount' is
+ * specified.
+ */
+ if ((flags & MS_OVERLAY) == 0 &&
+ strstr(mntopts, MNTOPT_REMOUNT) == NULL &&
+ !dir_is_empty(mountpoint)) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': "
+ "directory is not empty"), mountpoint);
+ zfs_error(dgettext(TEXT_DOMAIN, "use legacy mountpoint to "
+ "allow this behavior, or use the -O flag"));
+ return (1);
+ }
+
+ /* perform the mount */
+ if (mount(zfs_get_name(zhp), mountpoint, MS_OPTIONSTR | flags,
+ MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) {
+ /*
+ * Generic errors are nasty, but there are just way too many
+ * from mount(), and they're well-understood. We pick a few
+ * common ones to improve upon.
+ */
+ switch (errno) {
+ case EBUSY:
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': "
+ "mountpoint '%s' is busy"), zhp->zfs_name,
+ mountpoint);
+ break;
+ case EPERM:
+ case EACCES:
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot mount '%s': "
+ "permission denied"), zhp->zfs_name,
+ mountpoint);
+ break;
+ default:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot mount '%s': %s"),
+ mountpoint, strerror(errno));
+ break;
+ }
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Unmount the given filesystem.
+ */
+int
+zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
+{
+ struct mnttab search = { 0 }, entry;
+
+ /* check to see if need to unmount the filesystem */
+ search.mnt_special = (char *)zfs_get_name(zhp);
+ rewind(mnttab_file);
+ if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
+ getmntany(mnttab_file, &entry, &search) == 0)) {
+
+ if (mountpoint == NULL)
+ mountpoint = entry.mnt_mountp;
+
+ /*
+ * Always unshare the filesystem first.
+ */
+ if (zfs_unshare(zhp, mountpoint) != 0)
+ return (-1);
+
+ /*
+ * Try to unmount the filesystem. There is no reason to try a
+ * forced unmount because the vnodes will still carry a
+ * reference to the underlying dataset, so we can't destroy it
+ * anyway.
+ *
+ * In the unmount case, we print out a slightly more informative
+ * error message, though we'll be relying on the poor error
+ * semantics from the kernel.
+ */
+ if (umount2(mountpoint, flags) != 0) {
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot unmount '%s': %s"),
+ mountpoint, strerror(errno));
+ return (-1);
+ }
+
+ /*
+ * Don't actually destroy the underlying directory
+ */
+ }
+
+ return (0);
+}
+
+/*
+ * Unmount this filesystem and any children inheriting the mountpoint property.
+ * To do this, just act like we're changing the mountpoint property, but don't
+ * remount the filesystems afterwards.
+ */
+int
+zfs_unmountall(zfs_handle_t *zhp, int flags)
+{
+ prop_changelist_t *clp;
+ int ret;
+
+ clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, flags);
+ if (clp == NULL)
+ return (-1);
+
+ ret = changelist_prefix(clp);
+ changelist_free(clp);
+
+ return (ret);
+}
+
+/*
+ * Check to see if the filesystem is currently shared.
+ */
+int
+zfs_is_shared(zfs_handle_t *zhp, char **where)
+{
+ char *mountpoint;
+
+ if (!zfs_is_mounted(zhp, &mountpoint))
+ return (FALSE);
+
+ if (is_shared(mountpoint)) {
+ if (where != NULL)
+ *where = mountpoint;
+ else
+ free(mountpoint);
+ return (TRUE);
+ } else {
+ free(mountpoint);
+ return (FALSE);
+ }
+}
+
+/*
+ * Share the given filesystem according to the options in 'sharenfs'. We rely
+ * on share(1M) to the dirty work for us.
+ */
+int
+zfs_share(zfs_handle_t *zhp)
+{
+ char mountpoint[ZFS_MAXPROPLEN];
+ char shareopts[ZFS_MAXPROPLEN];
+ char buf[MAXPATHLEN];
+ FILE *fp;
+
+ /* ignore non-filesystems */
+ if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM)
+ return (0);
+
+ /* return success if there is no mountpoint set */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
+ mountpoint, sizeof (mountpoint), NULL, NULL, 0, FALSE) != 0 ||
+ strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
+ strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
+ return (0);
+
+ /* return success if there are no share options */
+ if (zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, sizeof (shareopts),
+ NULL, NULL, 0, FALSE) != 0 ||
+ strcmp(shareopts, "off") == 0)
+ return (0);
+
+ /*
+ * If the 'zoned' property is set, simply return success since:
+ * 1. in a global zone, a dataset should not be shared if it's
+ * managed in a local zone.
+ * 2. in a local zone, NFS server is not available.
+ */
+ if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
+ return (0);
+ }
+
+ /*
+ * Invoke the share(1M) command. We always do this, even if it's
+ * currently shared, as the options may have changed.
+ */
+ if (strcmp(shareopts, "on") == 0)
+ (void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
+ "-F nfs \"%s\" 2>&1", mountpoint);
+ else
+ (void) snprintf(buf, sizeof (buf), "/usr/sbin/share "
+ "-F nfs -o \"%s\" \"%s\" 2>&1", shareopts,
+ mountpoint);
+
+ if ((fp = popen(buf, "r")) == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot share '%s': "
+ "share(1M) failed"), zfs_get_name(zhp));
+ return (-1);
+ }
+
+ /*
+ * share(1M) should only produce output if there is some kind
+ * of error. All output begins with "share_nfs: ", so we trim
+ * this off to get to the real error.
+ */
+ if (fgets(buf, sizeof (buf), fp) != NULL) {
+ char *colon = strchr(buf, ':');
+
+ while (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = '\0';
+
+ if (colon == NULL)
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot share "
+ "'%s': share(1M) failed"),
+ zfs_get_name(zhp));
+ else
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot share "
+ "'%s': %s"), zfs_get_name(zhp),
+ colon + 2);
+
+ verify(pclose(fp) != 0);
+ return (-1);
+ }
+
+ verify(pclose(fp) == 0);
+
+ return (0);
+}
+
+/*
+ * Unshare the given filesystem.
+ */
+int
+zfs_unshare(zfs_handle_t *zhp, const char *mountpoint)
+{
+ char buf[MAXPATHLEN];
+ struct mnttab search = { 0 }, entry;
+
+ /* check to see if need to unmount the filesystem */
+ search.mnt_special = (char *)zfs_get_name(zhp);
+ rewind(mnttab_file);
+ if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
+ getmntany(mnttab_file, &entry, &search) == 0)) {
+
+ if (mountpoint == NULL)
+ mountpoint = entry.mnt_mountp;
+
+ if (is_shared(mountpoint)) {
+ FILE *fp;
+
+ (void) snprintf(buf, sizeof (buf),
+ "/usr/sbin/unshare \"%s\" 2>&1",
+ mountpoint);
+
+ if ((fp = popen(buf, "r")) == NULL) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot "
+ "unshare '%s': unshare(1M) failed"),
+ zfs_get_name(zhp));
+ return (-1);
+ }
+
+ /*
+ * unshare(1M) should only produce output if there is
+ * some kind of error. All output begins with "unshare
+ * nfs: ", so we trim this off to get to the real error.
+ */
+ if (fgets(buf, sizeof (buf), fp) != NULL) {
+ char *colon = strchr(buf, ':');
+
+ while (buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = '\0';
+
+ if (colon == NULL)
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot unshare '%s': unshare(1M) "
+ "failed"), zfs_get_name(zhp));
+ else
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot unshare '%s': %s"),
+ zfs_get_name(zhp), colon + 2);
+
+ verify(pclose(fp) != 0);
+ return (-1);
+ }
+
+ verify(pclose(fp) == 0);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Same as zfs_unmountall(), but for unshares.
+ */
+int
+zfs_unshareall(zfs_handle_t *zhp)
+{
+ prop_changelist_t *clp;
+ int ret;
+
+ clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0);
+ if (clp == NULL)
+ return (-1);
+
+ ret = changelist_unshare(clp);
+ changelist_free(clp);
+
+ return (ret);
+}
+
+/*
+ * Remove the mountpoint associated with the current dataset, if necessary.
+ * We only remove the underlying directory if:
+ *
+ * - The mountpoint is not 'none' or 'legacy'
+ * - The mountpoint is non-empty
+ * - The mountpoint is the default or inherited
+ * - The 'zoned' property is set, or we're in a local zone
+ *
+ * Any other directories we leave alone.
+ */
+void
+remove_mountpoint(zfs_handle_t *zhp)
+{
+ char mountpoint[ZFS_MAXPROPLEN];
+ char source[ZFS_MAXNAMELEN];
+ zfs_source_t sourcetype;
+ char zonename[ZONENAME_MAX];
+
+ /* ignore non-filesystems */
+ if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+ sizeof (mountpoint), &sourcetype, source, sizeof (source),
+ FALSE) != 0)
+ return;
+
+ if (getzonenamebyid(getzoneid(), zonename, sizeof (zonename)) < 0)
+ zfs_fatal(dgettext(TEXT_DOMAIN, "internal error: "
+ "cannot determine current zone"));
+
+ if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0 &&
+ strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 &&
+ (sourcetype == ZFS_SRC_DEFAULT ||
+ sourcetype == ZFS_SRC_INHERITED) &&
+ (!zfs_prop_get_int(zhp, ZFS_PROP_ZONED) ||
+ strcmp(zonename, "global") != 0)) {
+
+ /*
+ * Try to remove the directory, silently ignoring any errors.
+ * The filesystem may have since been removed or moved around,
+ * and this isn't really useful to the administrator in any
+ * way.
+ */
+ (void) rmdir(mountpoint);
+ }
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c
new file mode 100644
index 0000000000..6b6f381bb1
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c
@@ -0,0 +1,1154 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <devid.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/zfs_ioctl.h>
+
+#include "zfs_namecheck.h"
+#include "libzfs_impl.h"
+
+/*
+ * Validate the given pool name, optionally putting an extended error message in
+ * 'buf'.
+ */
+static int
+zpool_name_valid(const char *pool, char *buf, size_t buflen)
+{
+ namecheck_err_t why;
+ char what;
+
+ if (strlen(pool) >= ZPOOL_MAXNAMELEN) {
+ if (buf)
+ (void) snprintf(buf, buflen,
+ dgettext(TEXT_DOMAIN, "name is too long"));
+ return (FALSE);
+ }
+
+ if (pool_namecheck(pool, &why, &what) != 0) {
+ if (buf != NULL) {
+ switch (why) {
+ case NAME_ERR_INVALCHAR:
+ (void) snprintf(buf, buflen,
+ dgettext(TEXT_DOMAIN, "invalid character "
+ "'%c' in pool name"), what);
+ break;
+
+ case NAME_ERR_NOLETTER:
+ (void) strlcpy(buf, dgettext(TEXT_DOMAIN,
+ "name must begin with a letter"), buflen);
+ break;
+
+ case NAME_ERR_RESERVED:
+ (void) strlcpy(buf, dgettext(TEXT_DOMAIN,
+ "name is reserved\n"
+ "pool name may have been omitted"), buflen);
+ break;
+
+ case NAME_ERR_DISKLIKE:
+ (void) strlcpy(buf, dgettext(TEXT_DOMAIN,
+ "pool name is reserved\n"
+ "pool name may have been omitted"), buflen);
+ break;
+ }
+ }
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+/*
+ * Set the pool-wide health based on the vdev state of the root vdev.
+ */
+void
+set_pool_health(nvlist_t *config)
+{
+ nvlist_t *nvroot;
+ vdev_stat_t *vs;
+ uint_t vsc;
+ char *health;
+
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+ verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &vsc) == 0);
+
+ switch (vs->vs_state) {
+
+ case VDEV_STATE_CLOSED:
+ case VDEV_STATE_CANT_OPEN:
+ case VDEV_STATE_OFFLINE:
+ health = dgettext(TEXT_DOMAIN, "FAULTED");
+ break;
+
+ case VDEV_STATE_DEGRADED:
+ health = dgettext(TEXT_DOMAIN, "DEGRADED");
+ break;
+
+ case VDEV_STATE_HEALTHY:
+ health = dgettext(TEXT_DOMAIN, "ONLINE");
+ break;
+
+ default:
+ zfs_baderror(vs->vs_state);
+ }
+
+ verify(nvlist_add_string(config, ZPOOL_CONFIG_POOL_HEALTH,
+ health) == 0);
+}
+
+/*
+ * Open a handle to the given pool, even if the pool is currently in the FAULTED
+ * state.
+ */
+zpool_handle_t *
+zpool_open_canfail(const char *pool)
+{
+ zpool_handle_t *zhp;
+ nvlist_t *newconfig;
+ int error;
+
+ /*
+ * Make sure the pool name is valid.
+ */
+ if (!zpool_name_valid(pool, NULL, 0)) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot open '%s': invalid "
+ "pool name"), pool);
+ return (NULL);
+ }
+
+ zhp = zfs_malloc(sizeof (zpool_handle_t));
+
+ (void) strlcpy(zhp->zpool_name, pool, sizeof (zhp->zpool_name));
+
+ if ((error = zpool_refresh_stats(zhp, NULL, &newconfig)) != 0) {
+ if (error == ENOENT || error == EINVAL) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot open '%s': no "
+ "such pool"), pool);
+ free(zhp);
+ return (NULL);
+ } else {
+ zhp->zpool_state = POOL_STATE_UNAVAIL;
+ }
+ } else {
+ zhp->zpool_state = POOL_STATE_ACTIVE;
+ }
+
+ return (zhp);
+}
+
+/*
+ * Like the above, but silent on error. Used when iterating over pools (because
+ * the configuration cache may be out of date).
+ */
+zpool_handle_t *
+zpool_open_silent(const char *pool)
+{
+ zpool_handle_t *zhp;
+ nvlist_t *newconfig;
+ int error;
+
+ zhp = zfs_malloc(sizeof (zpool_handle_t));
+
+ (void) strlcpy(zhp->zpool_name, pool, sizeof (zhp->zpool_name));
+
+ if ((error = zpool_refresh_stats(zhp, NULL, &newconfig)) != 0) {
+ if (error == ENOENT || error == EINVAL) {
+ free(zhp);
+ return (NULL);
+ } else {
+ zhp->zpool_state = POOL_STATE_UNAVAIL;
+ }
+ } else {
+ zhp->zpool_state = POOL_STATE_ACTIVE;
+ }
+
+ return (zhp);
+}
+
+/*
+ * Similar to zpool_open_canfail(), but refuses to open pools in the faulted
+ * state.
+ */
+zpool_handle_t *
+zpool_open(const char *pool)
+{
+ zpool_handle_t *zhp;
+
+ if ((zhp = zpool_open_canfail(pool)) == NULL)
+ return (NULL);
+
+ if (zhp->zpool_state == POOL_STATE_UNAVAIL) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot open ' %s': pool is "
+ "currently unavailable\n"), zhp->zpool_name);
+ zfs_error(dgettext(TEXT_DOMAIN, "run 'zpool status -v %s' for "
+ "detailed information\n"), zhp->zpool_name);
+ zpool_close(zhp);
+ return (NULL);
+ }
+
+ return (zhp);
+}
+
+/*
+ * Close the handle. Simply frees the memory associated with the handle.
+ */
+void
+zpool_close(zpool_handle_t *zhp)
+{
+ if (zhp->zpool_config)
+ nvlist_free(zhp->zpool_config);
+ free(zhp);
+}
+
+/*
+ * Return the name of the pool.
+ */
+const char *
+zpool_get_name(zpool_handle_t *zhp)
+{
+ return (zhp->zpool_name);
+}
+
+/*
+ * Return the GUID of the pool.
+ */
+uint64_t
+zpool_get_guid(zpool_handle_t *zhp)
+{
+ uint64_t guid;
+
+ verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_POOL_GUID,
+ &guid) == 0);
+ return (guid);
+}
+
+/*
+ * Return the amount of space currently consumed by the pool.
+ */
+uint64_t
+zpool_get_space_used(zpool_handle_t *zhp)
+{
+ nvlist_t *nvroot;
+ vdev_stat_t *vs;
+ uint_t vsc;
+
+ verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+ verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &vsc) == 0);
+
+ return (vs->vs_alloc);
+}
+
+/*
+ * Return the total space in the pool.
+ */
+uint64_t
+zpool_get_space_total(zpool_handle_t *zhp)
+{
+ nvlist_t *nvroot;
+ vdev_stat_t *vs;
+ uint_t vsc;
+
+ verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+ verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &vsc) == 0);
+
+ return (vs->vs_space);
+}
+
+/*
+ * Return the alternate root for this pool, if any.
+ */
+int
+zpool_get_root(zpool_handle_t *zhp, char *buf, size_t buflen)
+{
+ zfs_cmd_t zc = { 0 };
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ if (ioctl(zfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 ||
+ zc.zc_objset_stats.dds_altroot[0] == '\0')
+ return (-1);
+
+ (void) strlcpy(buf, zc.zc_objset_stats.dds_altroot, buflen);
+
+ return (0);
+}
+
+/*
+ * Return the state of the pool (ACTIVE or UNAVAILABLE)
+ */
+int
+zpool_get_state(zpool_handle_t *zhp)
+{
+ return (zhp->zpool_state);
+}
+
+/*
+ * Create the named pool, using the provided vdev list. It is assumed
+ * that the consumer has already validated the contents of the nvlist, so we
+ * don't have to worry about error semantics.
+ */
+int
+zpool_create(const char *pool, nvlist_t *nvroot, const char *altroot)
+{
+ zfs_cmd_t zc = { 0 };
+ char *packed;
+ size_t len;
+ int err;
+ char reason[64];
+
+ if (!zpool_name_valid(pool, reason, sizeof (reason))) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': %s"),
+ pool, reason);
+ return (-1);
+ }
+
+ if (altroot != NULL && altroot[0] != '/') {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': alternate "
+ "root '%s' must be a complete path"), pool, altroot);
+ return (-1);
+ }
+
+ if ((err = nvlist_size(nvroot, &len, NV_ENCODE_NATIVE)) != 0)
+ zfs_baderror(err);
+
+ packed = zfs_malloc(len);
+
+ if ((err = nvlist_pack(nvroot, &packed, &len,
+ NV_ENCODE_NATIVE, 0)) != 0)
+ zfs_baderror(err);
+
+ (void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name));
+ zc.zc_config_src = (uint64_t)(uintptr_t)packed;
+ zc.zc_config_src_size = len;
+
+ if (altroot != NULL)
+ (void) strlcpy(zc.zc_root, altroot, sizeof (zc.zc_root));
+
+ if (ioctl(zfs_fd, ZFS_IOC_POOL_CREATE, &zc) != 0) {
+ switch (errno) {
+ case EEXIST:
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': "
+ "pool exists"), pool);
+ break;
+
+ case EPERM:
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': "
+ "permission denied"), pool);
+ break;
+
+ case EBUSY:
+ /*
+ * This can happen if the user has specified the same
+ * device multiple times. We can't reliably detect this
+ * until we try to add it and see we already have a
+ * label.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': "
+ "one or more vdevs refer to the same device"),
+ pool);
+ break;
+
+ case EOVERFLOW:
+ /*
+ * This occurrs when one of the devices is below
+ * SPA_MINDEVSIZE. Unfortunately, we can't detect which
+ * device was the problem device since there's no
+ * reliable way to determine device size from userland.
+ */
+ {
+ char buf[64];
+
+ zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf));
+
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot "
+ "create '%s': one or more devices is less "
+ "than the minimum size (%s)"), pool,
+ buf);
+ }
+ break;
+
+ case ENAMETOOLONG:
+ /*
+ * One of the vdevs has exceeded VDEV_SPEC_MAX length in
+ * its plaintext representation.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': "
+ "too many devices in a single vdev"), pool);
+ break;
+
+ case EIO:
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': "
+ "I/O error on one or more devices"), pool);
+ break;
+
+ case ENXIO:
+ /*
+ * This is unlikely to happen since we've verified that
+ * all the devices can be opened from userland, but it's
+ * still possible in some circumstances.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': "
+ "one or more devices is unavailable"), pool);
+ break;
+
+ case ENOSPC:
+ /*
+ * This can occur if we were incapable of writing to a
+ * file vdev because the underlying filesystem is out of
+ * space. This is very similar to EOVERFLOW, but we'll
+ * produce a slightly different message.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot create '%s': "
+ "one or more devices is out of space"), pool);
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ return (-1);
+ }
+
+ free(packed);
+
+ /*
+ * If this is an alternate root pool, then we automatically set the
+ * moutnpoint of the root dataset to be '/'.
+ */
+ if (altroot != NULL) {
+ zfs_handle_t *zhp;
+
+ verify((zhp = zfs_open(pool, ZFS_TYPE_ANY)) != NULL);
+ verify(zfs_prop_set(zhp, ZFS_PROP_MOUNTPOINT, "/") == 0);
+
+ zfs_close(zhp);
+ }
+
+ return (0);
+}
+
+/*
+ * Destroy the given pool. It is up to the caller to ensure that there are no
+ * datasets left in the pool.
+ */
+int
+zpool_destroy(zpool_handle_t *zhp)
+{
+ zfs_cmd_t zc = { 0 };
+ zfs_handle_t *zfp = NULL;
+
+ if (zhp->zpool_state == POOL_STATE_ACTIVE &&
+ (zfp = zfs_open(zhp->zpool_name, ZFS_TYPE_FILESYSTEM)) == NULL)
+ return (-1);
+
+ if (zpool_remove_zvol_links(zhp) != NULL)
+ return (-1);
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+
+ if (ioctl(zfs_fd, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
+ switch (errno) {
+ case EPERM:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot destroy '%s': permission denied"),
+ zhp->zpool_name);
+ break;
+
+ case EBUSY:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot destroy '%s': pool busy"),
+ zhp->zpool_name);
+ break;
+
+ case ENOENT:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot destroy '%s': no such pool"),
+ zhp->zpool_name);
+ break;
+
+ case EROFS:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot destroy '%s': one or more devices is "
+ "read only, or '/' is mounted read only"),
+ zhp->zpool_name);
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ if (zfp)
+ zfs_close(zfp);
+ return (-1);
+ }
+
+ if (zfp) {
+ remove_mountpoint(zfp);
+ zfs_close(zfp);
+ }
+
+ return (0);
+}
+
+/*
+ * Add the given vdevs to the pool. The caller must have already performed the
+ * necessary verification to ensure that the vdev specification is well-formed.
+ */
+int
+zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
+{
+ char *packed;
+ size_t len;
+ zfs_cmd_t zc;
+
+ verify(nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) == 0);
+
+ packed = zfs_malloc(len);
+
+ verify(nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ zc.zc_config_src = (uint64_t)(uintptr_t)packed;
+ zc.zc_config_src_size = len;
+
+ if (ioctl(zfs_fd, ZFS_IOC_VDEV_ADD, &zc) != 0) {
+ switch (errno) {
+ case EPERM:
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot add to '%s': "
+ "permission denied"), zhp->zpool_name);
+ break;
+
+ case EBUSY:
+ /*
+ * This can happen if the user has specified the same
+ * device multiple times. We can't reliably detect this
+ * until we try to add it and see we already have a
+ * label.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot add to '%s': "
+ "one or more vdevs refer to the same device"),
+ zhp->zpool_name);
+ break;
+
+ case ENAMETOOLONG:
+ /*
+ * One of the vdevs has exceeded VDEV_SPEC_MAX length in
+ * its plaintext representation.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot add to '%s': "
+ "too many devices in a single vdev"),
+ zhp->zpool_name);
+ break;
+
+ case ENXIO:
+ /*
+ * This is unlikely to happen since we've verified that
+ * all the devices can be opened from userland, but it's
+ * still possible in some circumstances.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot add to '%s': "
+ "one or more devices is unavailable"),
+ zhp->zpool_name);
+ break;
+
+ case EOVERFLOW:
+ /*
+ * This occurrs when one of the devices is below
+ * SPA_MINDEVSIZE. Unfortunately, we can't detect which
+ * device was the problem device since there's no
+ * reliable way to determine device size from userland.
+ */
+ {
+ char buf[64];
+
+ zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf));
+
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot "
+ "add to '%s': one or more devices is less "
+ "than the minimum size (%s)"),
+ zhp->zpool_name, buf);
+ }
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ return (-1);
+ }
+
+ free(packed);
+
+ return (0);
+}
+
+/*
+ * Exports the pool from the system. The caller must ensure that there are no
+ * mounted datasets in the pool.
+ */
+int
+zpool_export(zpool_handle_t *zhp)
+{
+ zfs_cmd_t zc = { 0 };
+
+ if (zpool_remove_zvol_links(zhp) != 0)
+ return (-1);
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+
+ if (ioctl(zfs_fd, ZFS_IOC_POOL_EXPORT, &zc) != 0) {
+ switch (errno) {
+ case EPERM:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot export '%s': permission denied"),
+ zhp->zpool_name);
+ break;
+
+ case EBUSY:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot export '%s': pool is in use"),
+ zhp->zpool_name);
+ break;
+
+ case ENOENT:
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "cannot export '%s': no such pool"),
+ zhp->zpool_name);
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Import the given pool using the known configuration. The configuration
+ * should have come from zpool_find_import(). The 'newname' and 'altroot'
+ * parameters control whether the pool is imported with a different name or with
+ * an alternate root, respectively.
+ */
+int
+zpool_import(nvlist_t *config, const char *newname, const char *altroot)
+{
+ zfs_cmd_t zc;
+ char *packed;
+ size_t len;
+ char *thename;
+ char *origname;
+ int ret;
+
+ verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
+ &origname) == 0);
+
+ if (newname != NULL) {
+ if (!zpool_name_valid(newname, NULL, 0)) {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot import '%s': "
+ "invalid pool name"), newname);
+ return (-1);
+ }
+ thename = (char *)newname;
+ } else {
+ thename = origname;
+ }
+
+ if (altroot != NULL && altroot[0] != '/') {
+ zfs_error(dgettext(TEXT_DOMAIN, "cannot import '%s': alternate "
+ "root '%s' must be a complete path"), thename,
+ altroot);
+ return (-1);
+ }
+
+ (void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name));
+
+ if (altroot != NULL)
+ (void) strlcpy(zc.zc_root, altroot, sizeof (zc.zc_root));
+ else
+ zc.zc_root[0] = '\0';
+
+ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
+ &zc.zc_pool_guid) == 0);
+
+ verify(nvlist_size(config, &len, NV_ENCODE_NATIVE) == 0);
+
+ packed = zfs_malloc(len);
+
+ verify(nvlist_pack(config, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);
+
+ zc.zc_config_src = (uint64_t)(uintptr_t)packed;
+ zc.zc_config_src_size = len;
+
+ ret = 0;
+ if (ioctl(zfs_fd, ZFS_IOC_POOL_IMPORT, &zc) != 0) {
+ char desc[1024];
+ if (newname == NULL)
+ (void) snprintf(desc, sizeof (desc),
+ dgettext(TEXT_DOMAIN, "cannot import '%s'"),
+ thename);
+ else
+ (void) snprintf(desc, sizeof (desc),
+ dgettext(TEXT_DOMAIN, "cannot import '%s' as '%s'"),
+ origname, thename);
+
+ switch (errno) {
+ case EEXIST:
+ /*
+ * A pool with that name already exists.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: pool exists"),
+ desc);
+ break;
+
+ case EPERM:
+ /*
+ * The user doesn't have permission to create pools.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: permission "
+ "denied"), desc);
+ break;
+
+ case ENXIO:
+ case EDOM:
+ /*
+ * Device is unavailable, or vdev sum didn't match.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: one or more "
+ "devices is unavailable"),
+ desc);
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ ret = -1;
+ } else {
+ zpool_handle_t *zhp;
+ /*
+ * This should never fail, but play it safe anyway.
+ */
+ if ((zhp = zpool_open_silent(thename)) != NULL) {
+ ret = zpool_create_zvol_links(zhp);
+ zpool_close(zhp);
+ }
+ }
+
+ free(packed);
+ return (ret);
+}
+
+/*
+ * Scrub the pool.
+ */
+int
+zpool_scrub(zpool_handle_t *zhp, pool_scrub_type_t type)
+{
+ zfs_cmd_t zc = { 0 };
+ char msg[1024];
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ zc.zc_cookie = type;
+
+ if (ioctl(zfs_fd, ZFS_IOC_POOL_SCRUB, &zc) == 0)
+ return (0);
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot scrub %s"), zc.zc_name);
+
+ switch (errno) {
+ case EPERM:
+ /*
+ * No permission to scrub this pool.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: permission denied"), msg);
+ break;
+
+ case EBUSY:
+ /*
+ * Resilver in progress.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: currently resilvering"),
+ msg);
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+ return (-1);
+}
+
+/*
+ * Bring the specified vdev online
+ */
+int
+zpool_vdev_online(zpool_handle_t *zhp, const char *path)
+{
+ zfs_cmd_t zc = { 0 };
+ char msg[1024];
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ (void) snprintf(zc.zc_prop_value, sizeof (zc.zc_prop_value),
+ "%s%s", path[0] == '/' ? "" : "/dev/dsk/", path);
+
+ if (ioctl(zfs_fd, ZFS_IOC_VDEV_ONLINE, &zc) == 0)
+ return (0);
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot online %s"), zc.zc_prop_value);
+
+ switch (errno) {
+ case ENODEV:
+ /*
+ * Device doesn't exist
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: device not in pool"), msg);
+ break;
+
+ case EPERM:
+ /*
+ * No permission to bring this vdev online.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: permission denied"), msg);
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+ return (-1);
+}
+
+/*
+ * Take the specified vdev offline
+ */
+int
+zpool_vdev_offline(zpool_handle_t *zhp, const char *path)
+{
+ zfs_cmd_t zc = { 0 };
+ char msg[1024];
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ (void) snprintf(zc.zc_prop_value, sizeof (zc.zc_prop_value),
+ "%s%s", path[0] == '/' ? "" : "/dev/dsk/", path);
+
+ if (ioctl(zfs_fd, ZFS_IOC_VDEV_OFFLINE, &zc) == 0)
+ return (0);
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot offline %s"), zc.zc_prop_value);
+
+ switch (errno) {
+ case ENODEV:
+ /*
+ * Device doesn't exist
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: device not in pool"), msg);
+ break;
+
+ case EPERM:
+ /*
+ * No permission to take this vdev offline.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: permission denied"), msg);
+ break;
+
+ case EBUSY:
+ /*
+ * There are no other replicas of this device.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: no valid replicas"), msg);
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+ return (-1);
+}
+
+/*
+ * Attach new_disk (fully described by nvroot) to old_disk.
+ * If 'replacing' is specified, tne new disk will replace the old one.
+ */
+int
+zpool_vdev_attach(zpool_handle_t *zhp,
+ const char *old_disk, const char *new_disk, nvlist_t *nvroot, int replacing)
+{
+ zfs_cmd_t zc = { 0 };
+ char msg[1024];
+ char *packed;
+ int ret;
+ size_t len;
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ (void) snprintf(zc.zc_prop_value, sizeof (zc.zc_prop_value),
+ "%s%s", old_disk[0] == '/' ? "" : "/dev/dsk/", old_disk);
+ zc.zc_cookie = replacing;
+
+ verify(nvlist_size(nvroot, &len, NV_ENCODE_NATIVE) == 0);
+
+ packed = zfs_malloc(len);
+
+ verify(nvlist_pack(nvroot, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);
+
+ zc.zc_config_src = (uint64_t)(uintptr_t)packed;
+ zc.zc_config_src_size = len;
+
+ ret = ioctl(zfs_fd, ZFS_IOC_VDEV_ATTACH, &zc);
+
+ free(packed);
+
+ if (ret == 0)
+ return (0);
+
+ if (replacing)
+ (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
+ "cannot replace %s with %s"), old_disk, new_disk);
+ else
+ (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
+ "cannot attach %s to %s"), new_disk, old_disk);
+
+ switch (errno) {
+ case EPERM:
+ /*
+ * No permission to mess with the config.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: permission denied"), msg);
+ break;
+
+ case ENODEV:
+ /*
+ * Device doesn't exist.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: %s not in pool"),
+ msg, old_disk);
+ break;
+
+ case ENOTSUP:
+ /*
+ * Can't attach to or replace this type of vdev.
+ */
+ if (replacing)
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "%s: cannot replace a replacing device"), msg);
+ else
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "%s: attach is only applicable to mirrors"), msg);
+ break;
+
+ case EINVAL:
+ /*
+ * The new device must be a single disk.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "%s: <new_device> must be a single disk"), msg);
+ break;
+
+ case ENXIO:
+ /*
+ * This is unlikely to happen since we've verified that
+ * all the devices can be opened from userland, but it's
+ * still possible in some circumstances.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: %s is unavailable"),
+ msg, new_disk);
+ break;
+
+ case EBUSY:
+ /*
+ * The new device is is use.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: %s busy"), msg, new_disk);
+ break;
+
+ case EOVERFLOW:
+ /*
+ * The new device is too small.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: %s is too small"),
+ msg, new_disk);
+ break;
+
+ case EDOM:
+ /*
+ * The new device has a different alignment requirement.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "%s: devices have different sector alignment"), msg);
+ break;
+
+ case ENAMETOOLONG:
+ /*
+ * The resulting top-level vdev spec won't fit in the label.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "%s: too many devices in a single vdev"), msg);
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ return (1);
+}
+
+/*
+ * Detach the specified device.
+ */
+int
+zpool_vdev_detach(zpool_handle_t *zhp, const char *path)
+{
+ zfs_cmd_t zc = { 0 };
+ char msg[1024];
+
+ (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
+ (void) snprintf(zc.zc_prop_value, sizeof (zc.zc_prop_value),
+ "%s%s", path[0] == '/' ? "" : "/dev/dsk/", path);
+
+ if (ioctl(zfs_fd, ZFS_IOC_VDEV_DETACH, &zc) == 0)
+ return (0);
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "cannot detach %s"), zc.zc_prop_value);
+
+ switch (errno) {
+ case EPERM:
+ /*
+ * No permission to mess with the config.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: permission denied"), msg);
+ break;
+
+ case ENODEV:
+ /*
+ * Device doesn't exist.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: device not in pool"), msg);
+ break;
+
+ case ENOTSUP:
+ /*
+ * Can't detach from this type of vdev.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN,
+ "%s: only applicable to mirror and replacing vdevs"), msg);
+ break;
+
+ case EBUSY:
+ /*
+ * There are no other replicas of this device.
+ */
+ zfs_error(dgettext(TEXT_DOMAIN, "%s: no valid replicas"), msg);
+ break;
+
+ default:
+ zfs_baderror(errno);
+ }
+
+ return (1);
+}
+
+static int
+do_zvol(zfs_handle_t *zhp, void *data)
+{
+ int linktype = (int)(uintptr_t)data;
+ int ret;
+
+ /*
+ * We check for volblocksize intead of ZFS_TYPE_VOLUME so that we
+ * correctly handle snapshots of volumes.
+ */
+ if (zhp->zfs_volblocksize != 0) {
+ if (linktype)
+ ret = zvol_create_link(zhp->zfs_name);
+ else
+ ret = zvol_remove_link(zhp->zfs_name);
+ }
+
+ ret = zfs_iter_children(zhp, do_zvol, data);
+
+ zfs_close(zhp);
+ return (ret);
+}
+
+/*
+ * Iterate over all zvols in the pool and make any necessary minor nodes.
+ */
+int
+zpool_create_zvol_links(zpool_handle_t *zhp)
+{
+ zfs_handle_t *zfp;
+ int ret;
+
+ /*
+ * If the pool is unavailable, just return success.
+ */
+ if ((zfp = make_dataset_handle(zhp->zpool_name)) == NULL)
+ return (0);
+
+ ret = zfs_iter_children(zfp, do_zvol, (void *)TRUE);
+
+ zfs_close(zfp);
+ return (ret);
+}
+
+/*
+ * Iterate over all zvols in the poool and remove any minor nodes.
+ */
+int
+zpool_remove_zvol_links(zpool_handle_t *zhp)
+{
+ zfs_handle_t *zfp;
+ int ret;
+
+ /*
+ * If the pool is unavailable, just return success.
+ */
+ if ((zfp = make_dataset_handle(zhp->zpool_name)) == NULL)
+ return (0);
+
+ ret = zfs_iter_children(zfp, do_zvol, (void *)FALSE);
+
+ zfs_close(zfp);
+ return (ret);
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_status.c b/usr/src/lib/libzfs/common/libzfs_status.c
new file mode 100644
index 0000000000..27a86d0c3c
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_status.c
@@ -0,0 +1,248 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file contains the functions which analyze the status of a pool. This
+ * include both the status of an active pool, as well as the status exported
+ * pools. Returns one of the ZPOOL_STATUS_* defines describing the status of
+ * the pool. This status is independent (to a certain degree) from the state of
+ * the pool. A pool's state descsribes only whether or not it is capable of
+ * providing the necessary fault tolerance for data. The status describes the
+ * overall status of devices. A pool that is online can still have a device
+ * that is experiencing errors.
+ *
+ * Only a subset of the possible faults can be detected using 'zpool status',
+ * and not all possible errors correspond to a FMA message ID. The explanation
+ * is left up to the caller, depending on whether it is a live pool or an
+ * import.
+ */
+
+#include <libzfs.h>
+#include <string.h>
+#include "libzfs_impl.h"
+
+/*
+ * Message ID table. This must be kep in sync with the ZPOOL_STATUS_* defines
+ * in libzfs.h. Note that there are some status results which go past the end
+ * of this table, and hence have no associated message ID.
+ */
+static char *msgid_table[] = {
+ "ZFS-8000-14",
+ "ZFS-8000-2Q",
+ "ZFS-8000-3C",
+ "ZFS-8000-4J",
+ "ZFS-8000-5E",
+ "ZFS-8000-6X",
+ "ZFS-8000-72",
+ "ZFS-8000-8A",
+ "ZFS-8000-9P",
+ "ZFS-8000-A5"
+};
+
+#define NMSGID (sizeof (msgid_table) / sizeof (msgid_table[0]))
+
+/* ARGSUSED */
+static int
+vdev_missing(uint64_t state, uint64_t aux, uint64_t errs)
+{
+ return (state == VDEV_STATE_CANT_OPEN &&
+ aux == VDEV_AUX_OPEN_FAILED);
+}
+
+/* ARGSUSED */
+static int
+vdev_errors(uint64_t state, uint64_t aux, uint64_t errs)
+{
+ return (errs != 0);
+}
+
+/* ARGSUSED */
+static int
+vdev_broken(uint64_t state, uint64_t aux, uint64_t errs)
+{
+ return (state == VDEV_STATE_CANT_OPEN);
+}
+
+/* ARGSUSED */
+static int
+vdev_offlined(uint64_t state, uint64_t aux, uint64_t errs)
+{
+ return (state == VDEV_STATE_OFFLINE);
+}
+
+/*
+ * Detect if any leaf devices that have seen errors or could not be opened.
+ */
+static int
+find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t))
+{
+ nvlist_t **child;
+ vdev_stat_t *vs;
+ uint_t c, children;
+ char *type;
+
+ /*
+ * Ignore problems within a 'replacing' vdev, since we're presumably in
+ * the process of repairing any such errors, and don't want to call them
+ * out again. We'll pick up the fact that a resilver is happening
+ * later.
+ */
+ verify(nvlist_lookup_string(vdev, ZPOOL_CONFIG_TYPE, &type) == 0);
+ if (strcmp(type, VDEV_TYPE_REPLACING) == 0)
+ return (FALSE);
+
+ if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, &child,
+ &children) == 0) {
+ for (c = 0; c < children; c++)
+ if (find_vdev_problem(child[c], func))
+ return (TRUE);
+ } else {
+ verify(nvlist_lookup_uint64_array(vdev, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &c) == 0);
+
+ if (func(vs->vs_state, vs->vs_aux,
+ vs->vs_read_errors +
+ vs->vs_write_errors +
+ vs->vs_checksum_errors))
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+/*
+ * Active pool health status.
+ *
+ * To determine the status for a pool, we make several passes over the config,
+ * picking the most egregious error we find. In order of importance, we do the
+ * following:
+ *
+ * - Check for a complete and valid configuration
+ * - Look for any missing devices
+ * - Look for any devices showing errors
+ * - Check for any data errors
+ * - Check for any resilvering devices
+ *
+ * There can obviously be multiple errors within a single pool, so this routine
+ * only picks the most damaging of all the current errors to report.
+ */
+static zpool_status_t
+check_status(nvlist_t *config, int isimport)
+{
+ nvlist_t *nvroot;
+ vdev_stat_t *vs;
+ uint_t vsc;
+
+ verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+ verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &vsc) == 0);
+
+ /*
+ * Check that the config is complete.
+ */
+ if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
+ vs->vs_aux == VDEV_AUX_BAD_GUID_SUM) {
+ return (ZPOOL_STATUS_BAD_GUID_SUM);
+ }
+
+ /*
+ * Missing devices
+ */
+ if (find_vdev_problem(nvroot, vdev_missing)) {
+ if (vs->vs_state == VDEV_STATE_CANT_OPEN)
+ return (ZPOOL_STATUS_MISSING_DEV_NR);
+ else
+ return (ZPOOL_STATUS_MISSING_DEV_R);
+ }
+
+ /*
+ * Devices with corrupted labels.
+ */
+ if (find_vdev_problem(nvroot, vdev_broken)) {
+ if (vs->vs_state == VDEV_STATE_CANT_OPEN)
+ return (ZPOOL_STATUS_CORRUPT_LABEL_NR);
+ else
+ return (ZPOOL_STATUS_CORRUPT_LABEL_R);
+ }
+
+ /*
+ * Devices with errors
+ */
+ if (!isimport && find_vdev_problem(nvroot, vdev_errors))
+ return (ZPOOL_STATUS_FAILING_DEV);
+
+ /*
+ * Offlined devices
+ */
+ if (find_vdev_problem(nvroot, vdev_offlined))
+ return (ZPOOL_STATUS_OFFLINE_DEV);
+
+ /*
+ * Currently resilvering
+ */
+ if (!vs->vs_scrub_complete && vs->vs_scrub_type == POOL_SCRUB_RESILVER)
+ return (ZPOOL_STATUS_RESILVERING);
+
+ /*
+ * We currently have no way to detect the following errors:
+ *
+ * CORRUPT_CACHE
+ * VERSION_MISMATCH
+ * CORRUPT_POOL
+ * CORRUPT_DATA
+ */
+
+ return (ZPOOL_STATUS_OK);
+}
+
+zpool_status_t
+zpool_get_status(zpool_handle_t *zhp, char **msgid)
+{
+ zpool_status_t ret = check_status(zhp->zpool_config, FALSE);
+
+ if (ret >= NMSGID)
+ *msgid = NULL;
+ else
+ *msgid = msgid_table[ret];
+
+ return (ret);
+}
+
+zpool_status_t
+zpool_import_status(nvlist_t *config, char **msgid)
+{
+ zpool_status_t ret = check_status(config, TRUE);
+
+ if (ret >= NMSGID)
+ *msgid = NULL;
+ else
+ *msgid = msgid_table[ret];
+
+ return (ret);
+}
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
new file mode 100644
index 0000000000..2f5c538212
--- /dev/null
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -0,0 +1,204 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Internal utility routines for the ZFS library.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/mnttab.h>
+
+#include <libzfs.h>
+
+#include "libzfs_impl.h"
+
+int zfs_fd;
+
+void (*error_func)(const char *, va_list);
+
+/*
+ * All error handling is kept within libzfs where we have the most information
+ * immediately available. While this may not be suitable for a general purpose
+ * library, it greatly simplifies our commands. This command name is used to
+ * prefix all error messages appropriately.
+ */
+void
+zfs_error(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if (error_func != NULL) {
+ error_func(fmt, ap);
+ } else {
+ (void) vfprintf(stderr, fmt, ap);
+ (void) fprintf(stderr, "\n");
+ }
+
+ va_end(ap);
+}
+
+/*
+ * An internal error is something that we cannot recover from, and should never
+ * happen (such as running out of memory). It should only be used in
+ * exceptional circumstances.
+ */
+void
+zfs_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if (error_func != NULL) {
+ error_func(fmt, ap);
+ } else {
+ (void) vfprintf(stderr, fmt, ap);
+ (void) fprintf(stderr, "\n");
+ }
+
+ va_end(ap);
+
+ exit(1);
+}
+
+/*
+ * Consumers (such as the JNI interface) that need to capture error output can
+ * override the default error handler using this function.
+ */
+void
+zfs_set_error_handler(void (*func)(const char *, va_list))
+{
+ error_func = func;
+}
+
+/*
+ * Display an out of memory error message and abort the current program.
+ */
+void
+no_memory(void)
+{
+ assert(errno == ENOMEM);
+ zfs_fatal(dgettext(TEXT_DOMAIN, "internal error: out of memory\n"));
+}
+
+/*
+ * A safe form of malloc() which will die if the allocation fails.
+ */
+void *
+zfs_malloc(size_t size)
+{
+ void *data;
+
+ if ((data = calloc(1, size)) == NULL)
+ no_memory();
+
+ return (data);
+}
+
+/*
+ * A safe form of strdup() which will die if the allocation fails.
+ */
+char *
+zfs_strdup(const char *str)
+{
+ char *ret;
+
+ if ((ret = strdup(str)) == NULL)
+ no_memory();
+
+ return (ret);
+}
+
+/*
+ * Initialize the library. Sets the command name used when reporting errors.
+ * This command name is used to prefix all error messages appropriately.
+ * Also opens /dev/zfs and dies if it cannot be opened.
+ */
+#pragma init(zfs_init)
+void
+zfs_init(void)
+{
+ if ((zfs_fd = open(ZFS_DEV, O_RDWR)) < 0)
+ zfs_fatal(dgettext(TEXT_DOMAIN,
+ "internal error: cannot open zfs device"));
+
+ if ((mnttab_file = fopen(MNTTAB, "r")) == NULL)
+ zfs_fatal(dgettext(TEXT_DOMAIN, "internal error: unable to "
+ "open %s\n"), MNTTAB);
+
+ sharetab_file = fopen("/etc/dfs/sharetab", "r");
+}
+
+/*
+ * Cleanup function for library. Simply close the file descriptors that we
+ * opened as part of libzfs_init().
+ */
+#pragma fini(zfs_fini)
+void
+zfs_fini(void)
+{
+ (void) close(zfs_fd);
+}
+
+/*
+ * Convert a number to an appropriately human-readable output.
+ */
+void
+zfs_nicenum(uint64_t num, char *buf, size_t buflen)
+{
+ uint64_t n = num;
+ int index = 0;
+ char u;
+
+ while (n >= 1024) {
+ n = (n + (1024 / 2)) / 1024; /* Round up or down */
+ index++;
+ }
+
+ u = " KMGTPE"[index];
+
+ if (index == 0)
+ (void) snprintf(buf, buflen, "%llu", n);
+ else if (n < 10 && (num & (num - 1)) != 0)
+ (void) snprintf(buf, buflen, "%.2f%c",
+ (double)num / (1ULL << 10 * index), u);
+ else if (n < 100 && (num & (num - 1)) != 0)
+ (void) snprintf(buf, buflen, "%.1f%c",
+ (double)num / (1ULL << 10 * index), u);
+ else
+ (void) snprintf(buf, buflen, "%llu%c", n, u);
+}
diff --git a/usr/src/lib/libzfs/common/llib-lzfs b/usr/src/lib/libzfs/common/llib-lzfs
new file mode 100644
index 0000000000..83ac1841a8
--- /dev/null
+++ b/usr/src/lib/libzfs/common/llib-lzfs
@@ -0,0 +1,32 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <libzfs.h>
diff --git a/usr/src/lib/libzfs/i386/Makefile b/usr/src/lib/libzfs/i386/Makefile
new file mode 100644
index 0000000000..cd02883abf
--- /dev/null
+++ b/usr/src/lib/libzfs/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libzfs/inc.flg b/usr/src/lib/libzfs/inc.flg
new file mode 100644
index 0000000000..94a1191086
--- /dev/null
+++ b/usr/src/lib/libzfs/inc.flg
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+find_files "s.*" usr/src/common/zfs
+find_files "s.*" usr/src/uts/common/fs/zfs/sys
+echo_file usr/src/uts/common/sys/fs/zfs.h
diff --git a/usr/src/lib/libzfs/sparc/Makefile b/usr/src/lib/libzfs/sparc/Makefile
new file mode 100644
index 0000000000..cd02883abf
--- /dev/null
+++ b/usr/src/lib/libzfs/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libzfs/sparcv9/Makefile b/usr/src/lib/libzfs/sparcv9/Makefile
new file mode 100644
index 0000000000..44075ed1bd
--- /dev/null
+++ b/usr/src/lib/libzfs/sparcv9/Makefile
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libzfs/spec/Makefile b/usr/src/lib/libzfs/spec/Makefile
new file mode 100644
index 0000000000..2cb984bfc9
--- /dev/null
+++ b/usr/src/lib/libzfs/spec/Makefile
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include $(SRC)/lib/Makefile.spec.arch
diff --git a/usr/src/lib/libzfs/spec/Makefile.targ b/usr/src/lib/libzfs/spec/Makefile.targ
new file mode 100644
index 0000000000..5af8faa767
--- /dev/null
+++ b/usr/src/lib/libzfs/spec/Makefile.targ
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+LIBRARY= libzfs.a
+VERS= .1
+
+OBJECTS= libzfs.o
diff --git a/usr/src/lib/libzfs/spec/amd64/Makefile b/usr/src/lib/libzfs/spec/amd64/Makefile
new file mode 100644
index 0000000000..98db1f9271
--- /dev/null
+++ b/usr/src/lib/libzfs/spec/amd64/Makefile
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.lib.64
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB64)
diff --git a/usr/src/lib/libzfs/spec/i386/Makefile b/usr/src/lib/libzfs/spec/i386/Makefile
new file mode 100644
index 0000000000..6256c68c81
--- /dev/null
+++ b/usr/src/lib/libzfs/spec/i386/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libzfs/spec/libzfs.spec b/usr/src/lib/libzfs/spec/libzfs.spec
new file mode 100644
index 0000000000..a8949c9636
--- /dev/null
+++ b/usr/src/lib/libzfs/spec/libzfs.spec
@@ -0,0 +1,341 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+function zfs_backup
+version SUNWprivate_1.1
+end
+
+function zfs_clone
+version SUNWprivate_1.1
+end
+
+function zfs_close
+version SUNWprivate_1.1
+end
+
+function zfs_create
+version SUNWprivate_1.1
+end
+
+function zfs_destroy
+version SUNWprivate_1.1
+end
+
+function zfs_get_name
+version SUNWprivate_1.1
+end
+
+function zfs_get_type
+version SUNWprivate_1.1
+end
+
+function zfs_init
+version SUNWprivate_1.1
+end
+
+function zfs_is_mounted
+version SUNWprivate_1.1
+end
+
+function zfs_is_shared
+version SUNWprivate_1.1
+end
+
+function zfs_iter_children
+version SUNWprivate_1.1
+end
+
+function zfs_iter_dependents
+version SUNWprivate_1.1
+end
+
+function zfs_iter_root
+version SUNWprivate_1.1
+end
+
+function zfs_mount
+version SUNWprivate_1.1
+end
+
+function zfs_name_to_prop
+version SUNWprivate_1.1
+end
+
+function zfs_name_valid
+version SUNWprivate_1.1
+end
+
+function zfs_nicenum
+version SUNWprivate_1.1
+end
+
+function zfs_nicestrtonum
+version SUNWprivate_1.1
+end
+
+function zfs_open
+version SUNWprivate_1.1
+end
+
+function zfs_prop_column_name
+version SUNWprivate_1.1
+end
+
+function zfs_prop_column_format
+version SUNWprivate_1.1
+end
+
+function zfs_prop_column_subopts
+version SUNWprivate_1.1
+end
+
+function zfs_prop_column_short_subopts
+version SUNWprivate_1.1
+end
+
+function zfs_prop_default_numeric
+version SUNWprivate_1.1
+end
+
+function zfs_prop_default_string
+version SUNWprivate_1.1
+end
+
+function zfs_prop_get
+version SUNWprivate_1.1
+end
+
+function zfs_prop_get_int
+version SUNWprivate_1.1
+end
+
+function zfs_prop_get_numeric
+version SUNWprivate_1.1
+end
+
+function zfs_prop_inherit
+version SUNWprivate_1.1
+end
+
+function zfs_prop_inheritable
+version SUNWprivate_1.1
+end
+
+function zfs_prop_is_string
+version SUNWprivate_1.1
+end
+
+function zfs_prop_readonly
+version SUNWprivate_1.1
+end
+
+function zfs_prop_set
+version SUNWprivate_1.1
+end
+
+function zfs_prop_valid_for_type
+version SUNWprivate_1.1
+end
+
+function zfs_prop_validate
+version SUNWprivate_1.1
+end
+
+function zfs_prop_values
+version SUNWprivate_1.1
+end
+
+function zfs_prop_to_name
+version SUNWprivate_1.1
+end
+
+function zfs_refresh_properties
+version SUNWprivate_1.1
+end
+
+function zfs_rename
+version SUNWprivate_1.1
+end
+
+function zfs_restore
+version SUNWprivate_1.1
+end
+
+function zfs_rollback
+version SUNWprivate_1.1
+end
+
+function zfs_set_error_handler
+version SUNWprivate_1.1
+end
+
+function zfs_share
+version SUNWprivate_1.1
+end
+
+function zfs_snapshot
+version SUNWprivate_1.1
+end
+
+function zfs_type_to_name
+version SUNWprivate_1.1
+end
+
+function zfs_unmount
+version SUNWprivate_1.1
+end
+
+function zfs_unmountall
+version SUNWprivate_1.1
+end
+
+function zfs_unshare
+version SUNWprivate_1.1
+end
+
+function zfs_unshareall
+version SUNWprivate_1.1
+end
+
+function zpool_add
+version SUNWprivate_1.1
+end
+
+function zpool_close
+version SUNWprivate_1.1
+end
+
+function zpool_create
+version SUNWprivate_1.1
+end
+
+function zpool_create_zvol_links
+version SUNWprivate_1.1
+end
+
+function zpool_destroy
+version SUNWprivate_1.1
+end
+
+function zpool_export
+version SUNWprivate_1.1
+end
+
+function zpool_find_import
+version SUNWprivate_1.1
+end
+
+function zpool_get_config
+version SUNWprivate_1.1
+end
+
+function zpool_get_guid
+version SUNWprivate_1.1
+end
+
+function zpool_get_name
+version SUNWprivate_1.1
+end
+
+function zpool_get_root
+version SUNWprivate_1.1
+end
+
+function zpool_get_space_total
+version SUNWprivate_1.1
+end
+
+function zpool_get_space_used
+version SUNWprivate_1.1
+end
+
+function zpool_get_state
+version SUNWprivate_1.1
+end
+
+function zpool_get_status
+version SUNWprivate_1.1
+end
+
+function zpool_import
+version SUNWprivate_1.1
+end
+
+function zpool_import_status
+version SUNWprivate_1.1
+end
+
+function zpool_scrub
+version SUNWprivate_1.1
+end
+
+function zpool_in_use
+version SUNWprivate_1.1
+end
+
+function zpool_iter
+version SUNWprivate_1.1
+end
+
+function zpool_open
+version SUNWprivate_1.1
+end
+
+function zpool_open_canfail
+version SUNWprivate_1.1
+end
+
+function zpool_read_label
+version SUNWprivate_1.1
+end
+
+function zpool_refresh_stats
+version SUNWprivate_1.1
+end
+
+function zpool_remove_zvol_links
+version SUNWprivate_1.1
+end
+
+function zpool_vdev_online
+version SUNWprivate_1.1
+end
+
+function zpool_vdev_offline
+version SUNWprivate_1.1
+end
+
+function zpool_vdev_attach
+version SUNWprivate_1.1
+end
+
+function zpool_vdev_detach
+version SUNWprivate_1.1
+end
diff --git a/usr/src/lib/libzfs/spec/sparc/Makefile b/usr/src/lib/libzfs/spec/sparc/Makefile
new file mode 100644
index 0000000000..6256c68c81
--- /dev/null
+++ b/usr/src/lib/libzfs/spec/sparc/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libzfs/spec/sparcv9/Makefile b/usr/src/lib/libzfs/spec/sparcv9/Makefile
new file mode 100644
index 0000000000..98db1f9271
--- /dev/null
+++ b/usr/src/lib/libzfs/spec/sparcv9/Makefile
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.lib.64
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB64)
diff --git a/usr/src/lib/libzfs/spec/versions b/usr/src/lib/libzfs/spec/versions
new file mode 100644
index 0000000000..0cbdf7e792
--- /dev/null
+++ b/usr/src/lib/libzfs/spec/versions
@@ -0,0 +1,40 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+sparc {
+ SUNWprivate_1.1;
+}
+sparcv9 {
+ SUNWprivate_1.1;
+}
+i386 {
+ SUNWprivate_1.1;
+}
+amd64 {
+ SUNWprivate_1.1;
+}
diff --git a/usr/src/lib/libzfs_jni/Makefile b/usr/src/lib/libzfs_jni/Makefile
new file mode 100644
index 0000000000..2e46af841e
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/Makefile
@@ -0,0 +1,73 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.lib
+
+HDRS= libzfs_jni_dataset.h \
+ libzfs_jni_disk.h \
+ libzfs_jni_diskmgt.h \
+ libzfs_jni_main.h \
+ libzfs_jni_pool.h \
+ libzfs_jni_property.h \
+ libzfs_jni_util.h
+
+HDRDIR= common
+
+SUBDIRS= $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+
+MSGFILES =
+
+POFILE =
+
+.KEEP_STATE:
+
+all clean clobber install: spec .WAIT $(SUBDIRS)
+
+$(POFILE): pofile_MSGFILES
+
+lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+_msg: $(MSGDOMAINPOFILE)
+
+$(SUBDIRS) spec: FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
+include ../../Makefile.msg.targ
diff --git a/usr/src/lib/libzfs_jni/Makefile.com b/usr/src/lib/libzfs_jni/Makefile.com
new file mode 100644
index 0000000000..b9282caa84
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/Makefile.com
@@ -0,0 +1,69 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+LIBRARY= libzfs_jni.a
+VERS= .1
+
+OBJS_COMMON= libzfs_jni_dataset.o \
+ libzfs_jni_disk.o \
+ libzfs_jni_diskmgt.o \
+ libzfs_jni_main.o \
+ libzfs_jni_pool.o \
+ libzfs_jni_property.o \
+ libzfs_jni_util.o
+OBJECTS= $(OBJS_COMMON)
+
+include ../../Makefile.lib
+
+LIBS= $(DYNLIB) $(LINTLIB)
+
+INCS += -I$(SRCDIR) \
+ -I../../../common/zfsj \
+ -I$(JAVA_ROOT)/include \
+ -I$(JAVA_ROOT)/include/solaris
+
+LDLIBS += -lc -lnvpair -ldiskmgt -lzfs
+CPPFLAGS += $(INCS)
+
+SRCS= $(OBJS_COMMON:%.o=$(SRCDIR)/%.c)
+$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
+
+SRCDIR= ../common
+MAPDIR= ../spec/$(TRANSMACH)
+SPECMAPFILE= $(MAPDIR)/mapfile
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+pics/%.o: ../../../common/zfsj/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libzfs_jni/amd64/Makefile b/usr/src/lib/libzfs_jni/amd64/Makefile
new file mode 100644
index 0000000000..44075ed1bd
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/amd64/Makefile
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c
new file mode 100644
index 0000000000..124e52d8e1
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.c
@@ -0,0 +1,710 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libzfs_jni_dataset.h"
+#include "libzfs_jni_property.h"
+#include <strings.h>
+
+#define REGEX_ZFS_NAME "^((([^/]*)(/.+)?)[/@])?([^/]+)/*"
+#define REGEX_ZFS_NAME_NGROUPS 6
+#define REGEX_ZFS_NAME_POOL_GROUP 3
+#define REGEX_ZFS_NAME_PARENT_GROUP 2
+#define REGEX_ZFS_NAME_BASE_GROUP 5
+
+/*
+ * Types
+ */
+
+typedef struct DatasetBean {
+ zjni_Object_t super;
+
+ jmethodID method_setPoolName;
+ jmethodID method_setParentName;
+ jmethodID method_setBaseName;
+ jmethodID method_setProperties;
+ jmethodID method_addProperty;
+} DatasetBean_t;
+
+typedef struct FileSystemBean {
+ DatasetBean_t super;
+} FileSystemBean_t;
+
+typedef struct PoolBean {
+ FileSystemBean_t super;
+} PoolBean_t;
+
+typedef struct VolumeBean {
+ DatasetBean_t super;
+} VolumeBean_t;
+
+typedef struct SnapshotBean {
+ DatasetBean_t super;
+} SnapshotBean_t;
+
+typedef struct FileSystemSnapshotBean {
+ DatasetBean_t super;
+} FileSystemSnapshotBean_t;
+
+typedef struct VolumeSnapshotBean {
+ DatasetBean_t super;
+} VolumeSnapshotBean_t;
+
+/*
+ * Function prototypes
+ */
+
+static void new_DatasetBean(JNIEnv *, DatasetBean_t *);
+static void new_PoolBean(JNIEnv *, PoolBean_t *);
+static void new_FileSystemBean(JNIEnv *, FileSystemBean_t *);
+static void new_VolumeBean(JNIEnv *, VolumeBean_t *);
+static void new_SnapshotBean(JNIEnv *, SnapshotBean_t *);
+static void new_FileSystemSnapshotBean(JNIEnv *, FileSystemSnapshotBean_t *);
+static void new_VolumeSnapshotBean(JNIEnv *, VolumeSnapshotBean_t *);
+static int populate_DatasetBean(JNIEnv *, zfs_handle_t *, DatasetBean_t *);
+static int populate_PoolBean(JNIEnv *, zfs_handle_t *, PoolBean_t *);
+static int populate_FileSystemBean(
+ JNIEnv *, zfs_handle_t *, FileSystemBean_t *);
+static int populate_VolumeBean(
+ JNIEnv *, zfs_handle_t *, VolumeBean_t *);
+static int populate_SnapshotBean(JNIEnv *, zfs_handle_t *, SnapshotBean_t *);
+static int populate_FileSystemSnapshotBean(
+ JNIEnv *, zfs_handle_t *, FileSystemSnapshotBean_t *);
+static int populate_VolumeSnapshotBean(
+ JNIEnv *, zfs_handle_t *, VolumeSnapshotBean_t *);
+static jobject create_PoolBean(JNIEnv *, zfs_handle_t *);
+static jobject create_FileSystemBean(JNIEnv *, zfs_handle_t *);
+static jobject create_VolumeBean(JNIEnv *, zfs_handle_t *);
+static jobject create_FileSystemSnapshotBean(JNIEnv *, zfs_handle_t *);
+static jobject create_VolumeSnapshotBean(JNIEnv *, zfs_handle_t *);
+static jobject create_DatasetBean(JNIEnv *, zfs_handle_t *);
+static int is_fs_snapshot(zfs_handle_t *);
+static int is_pool(zfs_handle_t *);
+static zfs_handle_t *open_device(JNIEnv *, jstring, zfs_type_t);
+
+/*
+ * Static functions
+ */
+
+/* Create a DatasetBean */
+static void
+new_DatasetBean(JNIEnv *env, DatasetBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+ object->class =
+ (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA "DatasetBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ bean->method_setPoolName = (*env)->GetMethodID(
+ env, object->class, "setPoolName", "(Ljava/lang/String;)V");
+
+ bean->method_setParentName = (*env)->GetMethodID(
+ env, object->class, "setParentName", "(Ljava/lang/String;)V");
+
+ bean->method_setBaseName = (*env)->GetMethodID(
+ env, object->class, "setBaseName", "(Ljava/lang/String;)V");
+
+ bean->method_setProperties = (*env)->GetMethodID(
+ env, object->class, "setProperties",
+ "([L" ZFSJNI_PACKAGE_DATA "Property;)V");
+
+ bean->method_addProperty = (*env)->GetMethodID(
+ env, object->class, "addProperty",
+ "(L" ZFSJNI_PACKAGE_DATA "Property;)V");
+}
+
+/* Create a PoolBean */
+static void
+new_PoolBean(JNIEnv *env, PoolBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+
+ object->class =
+ (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA "PoolBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ new_FileSystemBean(env, (FileSystemBean_t *)bean);
+}
+
+/* Create a FileSystemBean */
+static void
+new_FileSystemBean(JNIEnv *env, FileSystemBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+ object->class =
+ (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "FileSystemBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ new_DatasetBean(env, (DatasetBean_t *)bean);
+}
+
+/* Create a VolumeBean */
+static void
+new_VolumeBean(JNIEnv *env, VolumeBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+ object->class =
+ (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "VolumeBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ new_DatasetBean(env, (DatasetBean_t *)bean);
+}
+
+/* Create a SnapshotBean */
+static void
+new_SnapshotBean(JNIEnv *env, SnapshotBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+ object->class =
+ (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "SnapshotBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ new_DatasetBean(env, (DatasetBean_t *)bean);
+}
+
+/* Create a FileSystemSnapshotBean */
+static void
+new_FileSystemSnapshotBean(JNIEnv *env, FileSystemSnapshotBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+ object->class =
+ (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "FileSystemSnapshotBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ new_SnapshotBean(env, (SnapshotBean_t *)bean);
+}
+
+/* Create a VolumeSnapshotBean */
+static void
+new_VolumeSnapshotBean(JNIEnv *env, VolumeSnapshotBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+ object->class =
+ (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "VolumeSnapshotBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ new_SnapshotBean(env, (SnapshotBean_t *)bean);
+}
+
+static int
+populate_DatasetBean(JNIEnv *env, zfs_handle_t *zhp, DatasetBean_t *bean)
+{
+ jstring poolUTF;
+ jstring parentUTF;
+ jstring baseUTF;
+ jobjectArray properties;
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ /*
+ * zhp->zfs_name has the format
+ * <pool>[[/<container...>]/<dataset>[@<snapshot>]]
+ */
+
+ regex_t re;
+ regmatch_t matches[REGEX_ZFS_NAME_NGROUPS];
+
+ char *name = (char *)zfs_get_name(zhp);
+ if (regcomp(&re, REGEX_ZFS_NAME, REG_EXTENDED) != 0 ||
+ regexec(&re, name, REGEX_ZFS_NAME_NGROUPS, matches, 0) != 0) {
+ regfree(&re);
+ zjni_throw_exception(env, "invalid name: %s", name);
+ return (-1);
+ }
+
+ regfree(&re);
+
+ /* Set names */
+ poolUTF = zjni_get_matched_string(
+ env, name, matches + REGEX_ZFS_NAME_POOL_GROUP);
+ parentUTF = zjni_get_matched_string(
+ env, name, matches + REGEX_ZFS_NAME_PARENT_GROUP);
+ baseUTF = zjni_get_matched_string(
+ env, name, matches + REGEX_ZFS_NAME_BASE_GROUP);
+
+ if (poolUTF == NULL) {
+ poolUTF = baseUTF;
+ }
+
+ (*env)->CallVoidMethod(
+ env, object->object, bean->method_setPoolName, poolUTF);
+ (*env)->CallVoidMethod(
+ env, object->object, bean->method_setBaseName, baseUTF);
+
+ if (parentUTF != NULL) {
+ (*env)->CallVoidMethod(
+ env, object->object, bean->method_setParentName, parentUTF);
+ }
+
+ properties = zjni_get_Dataset_properties(env, zhp);
+ if (properties == NULL) {
+ /* Must not call any more Java methods to preserve exception */
+ return (-1);
+ }
+
+ (*env)->CallVoidMethod(
+ env, object->object, bean->method_setProperties, properties);
+
+ return (0);
+}
+
+static int
+populate_PoolBean(JNIEnv *env, zfs_handle_t *zhp, PoolBean_t *bean)
+{
+ return (populate_FileSystemBean(env, zhp, (FileSystemBean_t *)bean));
+}
+
+static int
+populate_FileSystemBean(JNIEnv *env, zfs_handle_t *zhp, FileSystemBean_t *bean)
+{
+ return (populate_DatasetBean(env, zhp, (DatasetBean_t *)bean));
+}
+
+static int
+populate_VolumeBean(JNIEnv *env, zfs_handle_t *zhp, VolumeBean_t *bean)
+{
+ return (populate_DatasetBean(env, zhp, (DatasetBean_t *)bean));
+}
+
+static int
+populate_SnapshotBean(JNIEnv *env, zfs_handle_t *zhp, SnapshotBean_t *bean)
+{
+ return (populate_DatasetBean(env, zhp, (DatasetBean_t *)bean));
+}
+
+static int
+populate_FileSystemSnapshotBean(JNIEnv *env, zfs_handle_t *zhp,
+ FileSystemSnapshotBean_t *bean)
+{
+ return (populate_SnapshotBean(env, zhp, (SnapshotBean_t *)bean));
+}
+
+static int
+populate_VolumeSnapshotBean(JNIEnv *env, zfs_handle_t *zhp,
+ VolumeSnapshotBean_t *bean)
+{
+ return (populate_SnapshotBean(env, zhp, (SnapshotBean_t *)bean));
+}
+
+static jobject
+create_PoolBean(JNIEnv *env, zfs_handle_t *zhp)
+{
+ int result;
+ PoolBean_t bean_obj = {0};
+ PoolBean_t *bean = &bean_obj;
+
+ /* Construct PoolBean */
+ new_PoolBean(env, bean);
+
+ result = populate_PoolBean(env, zhp, bean);
+ if (result) {
+ /* Must not call any more Java methods to preserve exception */
+ return (NULL);
+ }
+
+ return (((zjni_Object_t *)bean)->object);
+}
+
+static jobject
+create_FileSystemBean(JNIEnv *env, zfs_handle_t *zhp)
+{
+ int result;
+ FileSystemBean_t bean_obj = {0};
+ FileSystemBean_t *bean = &bean_obj;
+
+ /* Construct FileSystemBean */
+ new_FileSystemBean(env, bean);
+
+ result = populate_FileSystemBean(env, zhp, bean);
+ if (result) {
+ /* Must not call any more Java methods to preserve exception */
+ return (NULL);
+ }
+
+ return (((zjni_Object_t *)bean)->object);
+}
+
+static jobject
+create_VolumeBean(JNIEnv *env, zfs_handle_t *zhp)
+{
+ int result;
+ VolumeBean_t bean_obj = {0};
+ VolumeBean_t *bean = &bean_obj;
+
+ /* Construct VolumeBean */
+ new_VolumeBean(env, bean);
+
+ result = populate_VolumeBean(env, zhp, bean);
+ if (result) {
+ /* Must not call any more Java methods to preserve exception */
+ return (NULL);
+ }
+
+ return (((zjni_Object_t *)bean)->object);
+}
+
+static jobject
+create_FileSystemSnapshotBean(JNIEnv *env, zfs_handle_t *zhp)
+{
+ int result;
+ FileSystemSnapshotBean_t bean_obj = {0};
+ FileSystemSnapshotBean_t *bean = &bean_obj;
+
+ /* Construct FileSystemSnapshotBean */
+ new_FileSystemSnapshotBean(env, bean);
+
+ result = populate_FileSystemSnapshotBean(env, zhp, bean);
+ if (result) {
+ /* Must not call any more Java methods to preserve exception */
+ return (NULL);
+ }
+
+ return (((zjni_Object_t *)bean)->object);
+}
+
+static jobject
+create_VolumeSnapshotBean(JNIEnv *env, zfs_handle_t *zhp)
+{
+ int result;
+ VolumeSnapshotBean_t bean_obj = {0};
+ VolumeSnapshotBean_t *bean = &bean_obj;
+
+ /* Construct VolumeSnapshotBean */
+ new_VolumeSnapshotBean(env, bean);
+
+ result = populate_VolumeSnapshotBean(env, zhp, bean);
+ if (result) {
+ /* Must not call any more Java methods to preserve exception */
+ return (NULL);
+ }
+
+ return (((zjni_Object_t *)bean)->object);
+}
+
+static jobject
+create_DatasetBean(JNIEnv *env, zfs_handle_t *zhp)
+{
+ jobject object = NULL;
+
+ switch (zfs_get_type(zhp)) {
+ case ZFS_TYPE_FILESYSTEM:
+ object = is_pool(zhp) ?
+ create_PoolBean(env, zhp) :
+ create_FileSystemBean(env, zhp);
+ break;
+
+ case ZFS_TYPE_VOLUME:
+ object = create_VolumeBean(env, zhp);
+ break;
+
+ case ZFS_TYPE_SNAPSHOT:
+ object = is_fs_snapshot(zhp) ?
+ create_FileSystemSnapshotBean(env, zhp) :
+ create_VolumeSnapshotBean(env, zhp);
+ break;
+ }
+
+ return (object);
+}
+
+/*
+ * Determines whether the given snapshot is a snapshot of a file
+ * system or of a volume.
+ *
+ * Returns:
+ *
+ * 0 if it is a volume snapshot
+ * 1 if it is a file system snapshot
+ * -1 on error
+ */
+static int
+is_fs_snapshot(zfs_handle_t *zhp)
+{
+ char parent[ZFS_MAXNAMELEN];
+ zfs_handle_t *parent_zhp;
+ int isfs;
+
+ if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
+ return (-1);
+ }
+
+ zjni_get_dataset_from_snapshot(
+ zfs_get_name(zhp), parent, sizeof (parent));
+
+ parent_zhp = zfs_open(parent, ZFS_TYPE_ANY);
+ if (parent_zhp == NULL) {
+ return (-1);
+ }
+
+ isfs = zfs_get_type(parent_zhp) == ZFS_TYPE_FILESYSTEM;
+ zfs_close(parent_zhp);
+
+ return (isfs);
+}
+
+static int
+is_pool(zfs_handle_t *zhp)
+{
+ return (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM &&
+ strchr(zfs_get_name(zhp), '/') == NULL);
+}
+
+static zfs_handle_t *
+open_device(JNIEnv *env, jstring nameUTF, zfs_type_t typemask)
+{
+ zfs_handle_t *zhp = NULL;
+
+ if (nameUTF != NULL) {
+ const char *name =
+ (*env)->GetStringUTFChars(env, nameUTF, NULL);
+
+ zhp = zfs_open(name, typemask);
+ if (zhp == NULL) {
+ zjni_throw_exception(env, "invalid device name: %s",
+ name);
+ }
+
+ (*env)->ReleaseStringUTFChars(env, nameUTF, name);
+ }
+
+ return (zhp);
+}
+
+/*
+ * Package-private functions
+ */
+
+/*
+ * Callback function for zfs_iter_children(). Creates the appropriate
+ * Dataset and adds it to the given zjni_ArrayList. Per the contract
+ * with zfs_iter_children(), calls zfs_close() on the given
+ * zfs_handle_t.
+ */
+int
+zjni_create_add_Dataset(zfs_handle_t *zhp, void *data)
+{
+ JNIEnv *env = ((zjni_ArrayCallbackData_t *)data)->env;
+ zjni_Collection_t *list = ((zjni_ArrayCallbackData_t *)data)->list;
+ zfs_type_t typemask =
+ ((zjni_DatasetArrayCallbackData_t *)data)->typemask;
+
+ /* Only add allowed types */
+ if (zfs_get_type(zhp) & typemask) {
+
+ jobject bean = create_DatasetBean(env, zhp);
+ zfs_close(zhp);
+
+ if (bean == NULL) {
+ /*
+ * Must not call any more Java methods to preserve
+ * exception
+ */
+ return (-1);
+ }
+
+ /* Add pool to zjni_ArrayList */
+ (*env)->CallBooleanMethod(env, ((zjni_Object_t *)list)->object,
+ ((zjni_Collection_t *)list)->method_add, bean);
+ }
+
+ return (0);
+}
+
+jobjectArray
+zjni_get_Datasets_below(JNIEnv *env, jstring parentUTF,
+ zfs_type_t parent_typemask, zfs_type_t child_typemask, char *arrayClass)
+{
+ jobjectArray array = NULL;
+ zfs_handle_t *zhp;
+
+ /* Create an array list to hold the children */
+ zjni_DatasetSet_t list_obj = {0};
+ zjni_DatasetSet_t *list = &list_obj;
+ zjni_new_DatasetSet(env, list);
+
+ /* Retrieve parent */
+ zhp = open_device(env, parentUTF, parent_typemask);
+ if (zhp != NULL) {
+
+ if (!(zfs_get_type(zhp) & parent_typemask)) {
+ zjni_throw_exception(env, "wrong type: %s",
+ zfs_get_name(zhp));
+ } else {
+
+ zjni_DatasetArrayCallbackData_t data = {0};
+ data.data.env = env;
+ data.data.list = (zjni_Collection_t *)list;
+ data.typemask = child_typemask;
+
+ (void) zfs_iter_children(zhp, zjni_create_add_Dataset,
+ &data);
+ }
+
+ zfs_close(zhp);
+ }
+
+ if ((*env)->ExceptionOccurred(env) == NULL) {
+ array = zjni_Collection_to_array(
+ env, (zjni_Collection_t *)list, arrayClass);
+ }
+
+ return (array);
+}
+
+jobjectArray
+zjni_get_Datasets_dependents(JNIEnv *env, jobjectArray paths)
+{
+ jint i;
+ jint npaths;
+ zjni_DatasetArrayCallbackData_t data = {0};
+ jobjectArray array = NULL;
+
+ /* Create a list to hold the children */
+ zjni_DatasetSet_t list_obj = {0};
+ zjni_DatasetSet_t *list = &list_obj;
+ zjni_new_DatasetSet(env, list);
+
+ data.data.env = env;
+ data.data.list = (zjni_Collection_t *)list;
+ data.typemask = ZFS_TYPE_ANY;
+
+ npaths = (*env)->GetArrayLength(env, paths);
+ for (i = 0; i < npaths; i++) {
+
+ jstring pathUTF = (jstring)
+ ((*env)->GetObjectArrayElement(env, paths, i));
+
+ zfs_handle_t *zhp = open_device(env, pathUTF, ZFS_TYPE_ANY);
+ if (zhp == NULL) {
+ /* Clear the exception */
+ (*env)->ExceptionClear(env);
+ } else {
+
+ /* Add all dependents of this Dataset to the list */
+ (void) zfs_iter_dependents(zhp,
+ zjni_create_add_Dataset, &data);
+
+ /* Add this Dataset to the list (and close zhp) */
+ (void) zjni_create_add_Dataset(zhp, &data);
+ }
+ }
+
+ if ((*env)->ExceptionOccurred(env) == NULL) {
+ array = zjni_Collection_to_array(env, (zjni_Collection_t *)list,
+ ZFSJNI_PACKAGE_DATA "Dataset");
+ }
+
+ return (array);
+}
+
+/*
+ * Gets a Dataset of the given name and type, or NULL if no such
+ * Dataset exists.
+ */
+jobject
+zjni_get_Dataset(JNIEnv *env, jstring nameUTF, zfs_type_t typemask)
+{
+ jobject device = NULL;
+ zfs_handle_t *zhp = open_device(env, nameUTF, typemask);
+ if (zhp == NULL) {
+ /*
+ * Clear the exception -- this function returns NULL
+ * on invalid device
+ */
+ (*env)->ExceptionClear(env);
+ } else {
+
+ /* Is this device the expected type? */
+ if (zfs_get_type(zhp) & typemask) {
+ /* Creates an object of the appropriate class */
+ device = create_DatasetBean(env, zhp);
+ }
+ zfs_close(zhp);
+ }
+
+ return (device);
+}
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.h b/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.h
new file mode 100644
index 0000000000..4476e0ed1a
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_dataset.h
@@ -0,0 +1,62 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBZFS_JNI_DATASET_H
+#define _LIBZFS_JNI_DATASET_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libzfs_jni_util.h>
+#include <libzfs.h>
+
+/*
+ * Types
+ */
+
+typedef struct zjni_DatasetArrayCallbackData {
+ zjni_ArrayCallbackData_t data;
+ zfs_type_t typemask;
+} zjni_DatasetArrayCallbackData_t;
+
+/*
+ * Function prototypes
+ */
+
+jobjectArray zjni_get_Datasets_below(JNIEnv *, jstring,
+ zfs_type_t, zfs_type_t, char *);
+jobjectArray zjni_get_Datasets_dependents(JNIEnv *, jobjectArray);
+jobject zjni_get_Dataset(JNIEnv *, jstring, zfs_type_t);
+int zjni_create_add_Dataset(zfs_handle_t *, void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBZFS_JNI_DATASET_H */
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_disk.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_disk.c
new file mode 100644
index 0000000000..218f3d8226
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_disk.c
@@ -0,0 +1,198 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libzfs_jni_disk.h"
+
+/*
+ * Function prototypes
+ */
+
+static jobject create_DiskDeviceBean(JNIEnv *, dmgt_disk_t *);
+static jobject get_SliceUsage_Use(JNIEnv *, char *);
+static jobject create_SliceUsage(JNIEnv *env, dmgt_slice_t *sp);
+static jobject create_SliceDeviceBean(JNIEnv *env, dmgt_slice_t *sp);
+static jobjectArray create_SliceDeviceBean_array(JNIEnv *, dmgt_slice_t **);
+
+/*
+ * Static functions
+ */
+
+static jobject
+create_DiskDeviceBean(JNIEnv *env, dmgt_disk_t *dp)
+{
+ jobject disk = NULL;
+
+ int naliases = zjni_count_elements((void **)dp->aliases);
+ jobjectArray aliases = zjni_string_array_to_String_array(
+ env, dp->aliases, naliases);
+ if (aliases != NULL) {
+ jobjectArray slices = create_SliceDeviceBean_array(env,
+ dp->slices);
+ if (slices != NULL) {
+ jstring nameUTF = (*env)->NewStringUTF(env, dp->name);
+
+ jclass class_DiskDeviceBean = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "DiskDeviceBean");
+
+ jmethodID constructor =
+ (*env)->GetMethodID(env, class_DiskDeviceBean,
+ "<init>",
+ "(JLjava/lang/String;[Ljava/lang/String;[L"
+ ZFSJNI_PACKAGE_DATA "SliceDeviceBean;)V");
+
+ disk = (*env)->NewObject(env, class_DiskDeviceBean,
+ constructor, dp->size, nameUTF, aliases, slices);
+ }
+ }
+
+ return (disk);
+}
+
+static jobject
+get_SliceUsage_Use(JNIEnv *env, char *dm_usage)
+{
+ jobject enumVal = NULL;
+
+ if (dm_usage != NULL) {
+ jclass class_SliceUsage_Use = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "SliceUsage$Use");
+
+ jfieldID id = (*env)->GetStaticFieldID(env,
+ class_SliceUsage_Use,
+ dm_usage, "L" ZFSJNI_PACKAGE_DATA "SliceUsage$Use;");
+
+ if (id != NULL) {
+ /* Retrieve the proper SliceUsage$Use enum value */
+ enumVal = (*env)->GetStaticObjectField(
+ env, class_SliceUsage_Use, id);
+#ifdef DEBUG
+ } else {
+ (void) fprintf(stderr, "Unknown slice usage: %s\n",
+ dm_usage);
+#endif /* DEBUG */
+ }
+ }
+
+ return (enumVal);
+}
+
+static jobject
+create_SliceUsage(JNIEnv *env, dmgt_slice_t *sp)
+{
+ jobject usage = NULL;
+ if (sp->used_name != NULL) {
+ jobject use = get_SliceUsage_Use(env, sp->used_name);
+
+ if (use != NULL) {
+ jstring usedByUTF =
+ (*env)->NewStringUTF(env, sp->used_by);
+
+ jclass class_SliceUsage = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "SliceUsage");
+
+ jmethodID constructor =
+ (*env)->GetMethodID(env, class_SliceUsage, "<init>",
+ "(L" ZFSJNI_PACKAGE_DATA
+ "SliceUsage$Use;Ljava/lang/String;)V");
+
+ usage = (*env)->NewObject(env,
+ class_SliceUsage, constructor, use, usedByUTF);
+ }
+ }
+
+ return (usage);
+}
+
+static jobject
+create_SliceDeviceBean(JNIEnv *env, dmgt_slice_t *sp)
+{
+ jobject slice = NULL;
+
+ /* May be NULL if unused */
+ jobject usage = create_SliceUsage(env, sp);
+
+ jstring nameUTF = (*env)->NewStringUTF(env, sp->name);
+
+ jclass class_SliceDeviceBean = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "SliceDeviceBean");
+
+ jmethodID constructor =
+ (*env)->GetMethodID(env, class_SliceDeviceBean, "<init>",
+ "(JLjava/lang/String;JL" ZFSJNI_PACKAGE_DATA "SliceUsage;)V");
+
+ slice = (*env)->NewObject(env, class_SliceDeviceBean,
+ constructor, sp->size, nameUTF, sp->start, usage);
+
+ return (slice);
+}
+
+static jobjectArray
+create_SliceDeviceBean_array(JNIEnv *env, dmgt_slice_t **slices)
+{
+ /* Create an array list */
+ zjni_ArrayList_t list_class = {0};
+ zjni_ArrayList_t *list_class_p = &list_class;
+ zjni_new_ArrayList(env, list_class_p);
+
+ if (slices != NULL) {
+ int i;
+ for (i = 0; slices[i] != NULL; i++) {
+ dmgt_slice_t *slice = slices[i];
+ jobject obj;
+ obj = create_SliceDeviceBean(env, slice);
+ if (obj != NULL) {
+ (*env)->CallBooleanMethod(env,
+ ((zjni_Object_t *)list_class_p)->object,
+ ((zjni_Collection_t *)list_class_p)->
+ method_add, obj);
+ }
+ }
+ }
+
+ return (zjni_Collection_to_array(
+ env, (zjni_Collection_t *)list_class_p,
+ ZFSJNI_PACKAGE_DATA "SliceDeviceBean"));
+}
+
+/*
+ * Package-private functions
+ */
+
+int
+zjni_create_add_DiskDevice(dmgt_disk_t *dp, void *data)
+{
+ JNIEnv *env = ((zjni_ArrayCallbackData_t *)data)->env;
+ zjni_Collection_t *list = ((zjni_ArrayCallbackData_t *)data)->list;
+ jobject disk = create_DiskDeviceBean(env, dp);
+
+ /* Add disk to zjni_ArrayList */
+ (*env)->CallBooleanMethod(env, ((zjni_Object_t *)list)->object,
+ ((zjni_Collection_t *)list)->method_add, disk);
+
+ return (0);
+}
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_disk.h b/usr/src/lib/libzfs_jni/common/libzfs_jni_disk.h
new file mode 100644
index 0000000000..a05efc13b8
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_disk.h
@@ -0,0 +1,49 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBZFS_JNI_DISK_H
+#define _LIBZFS_JNI_DISK_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libzfs_jni_util.h>
+#include <libzfs_jni_diskmgt.h>
+
+/*
+ * Function prototypes
+ */
+
+int zjni_create_add_DiskDevice(dmgt_disk_t *, void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBZFS_JNI_DISK_H */
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_diskmgt.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_diskmgt.c
new file mode 100644
index 0000000000..ded27aaf39
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_diskmgt.c
@@ -0,0 +1,764 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libzfs_jni_diskmgt.h"
+#include "libzfs_jni_util.h"
+#include <strings.h>
+#include <libzfs.h>
+#include <sys/mnttab.h>
+
+/*
+ * Constants
+ */
+
+#define DISK_IN_USE 12345
+
+/*
+ * Function prototypes
+ */
+
+static void free_slice_array(dmgt_slice_t **slices);
+static char *get_device_name(dm_descriptor_t device, int *error);
+static dmgt_disk_t *get_disk(dm_descriptor_t disk, int *error);
+static char **get_disk_aliases(dm_descriptor_t disk, char *name, int *error);
+static int get_disk_online(dm_descriptor_t disk, int *error);
+static void remove_slice_from_list(dmgt_slice_t **slices, int index);
+static dmgt_slice_t **get_disk_slices(dm_descriptor_t media,
+ const char *name, uint32_t blocksize, int *error);
+static dmgt_slice_t **get_disk_usable_slices(dm_descriptor_t media,
+ const char *name, uint32_t blocksize, int *in_use, int *error);
+static void get_disk_size(dm_descriptor_t media, char *name,
+ uint64_t *size, uint32_t *blocksize, int *error);
+static void get_slice_use(dm_descriptor_t slice, char *name,
+ char **used_name, char **used_by, int *error);
+static dmgt_slice_t *get_slice(
+ dm_descriptor_t slice, uint32_t blocksize, int *error);
+static void handle_error(const char *format, ...);
+static int slice_in_use(dmgt_slice_t *slice);
+static int slice_too_small(dmgt_slice_t *slice);
+
+/*
+ * Static data
+ */
+
+static void (*error_func)(const char *, va_list);
+
+/*
+ * Static functions
+ */
+
+static void
+free_slice_array(dmgt_slice_t **slices)
+{
+ if (slices != NULL) {
+ int i;
+ for (i = 0; slices[i] != NULL; i++) {
+ dmgt_free_slice(slices[i]);
+ }
+ free(slices);
+ }
+}
+
+static char *
+get_device_name(dm_descriptor_t device, int *error)
+{
+ char *dup;
+ char *name;
+
+ *error = 0;
+ name = dm_get_name(device, error);
+ if (*error) {
+ handle_error("could not determine name of device");
+ } else {
+ dup = strdup(name);
+ if (dup == NULL) {
+ handle_error("out of memory");
+ *error = -1;
+ }
+
+ dm_free_name(name);
+ }
+
+ return (dup);
+}
+
+/*
+ * Gets a dmgt_disk_t for the given disk dm_descriptor_t.
+ *
+ * Results:
+ *
+ * 1. Success: error is set to 0 and a dmgt_disk_t is returned
+ *
+ * 2. Failure: error is set to -1 and NULL is returned
+ *
+ * 3. In use: error is set to DISK_IN_USE and NULL is returned if all
+ * of the slices have an existing use that precludes use in ZFS
+ */
+static dmgt_disk_t *
+get_disk(dm_descriptor_t disk, int *error)
+{
+ dmgt_disk_t *dp;
+ *error = 0;
+
+ dp = (dmgt_disk_t *)calloc(1, sizeof (dmgt_disk_t));
+ if (dp == NULL) {
+ handle_error("out of memory");
+ *error = -1;
+ } else {
+
+ /* Get name */
+ dp->name = get_device_name(disk, error);
+ if (!*error) {
+
+ /* Get aliases */
+ dp->aliases = get_disk_aliases(disk, dp->name, error);
+ if (!*error) {
+
+ /* Get media */
+ dm_descriptor_t *media =
+ dm_get_associated_descriptors(disk,
+ DM_MEDIA, error);
+ if (*error != 0 || media == NULL ||
+ *media == NULL) {
+ handle_error(
+ "could not get media from disk %s",
+ dp->name);
+ *error = -1;
+ } else {
+ /* Get size */
+ get_disk_size(media[0], dp->name,
+ &(dp->size), &(dp->blocksize),
+ error);
+ if (!*error) {
+ /* Get free slices */
+ dp->slices =
+ get_disk_usable_slices(
+ media[0], dp->name,
+ dp->blocksize,
+ &(dp->in_use), error);
+
+ /*
+ * If this disk has no usable
+ * slices...
+ */
+ if (dp->in_use) {
+ *error = DISK_IN_USE;
+ }
+ }
+ dm_free_descriptors(media);
+ }
+ }
+ }
+ }
+
+ if (*error) {
+ if (*error != DISK_IN_USE) {
+ /* Normalize error */
+ *error = -1;
+ }
+
+ if (dp != NULL) {
+ dmgt_free_disk(dp);
+ dp = NULL;
+ }
+ }
+
+ return (dp);
+}
+
+static char **
+get_disk_aliases(dm_descriptor_t disk, char *name, int *error)
+{
+ char **names = NULL;
+ dm_descriptor_t *aliases;
+
+ *error = 0;
+ aliases = dm_get_associated_descriptors(disk, DM_ALIAS, error);
+ if (*error || aliases == NULL) {
+ *error = -1;
+ handle_error("could not get aliases for disk %s", name);
+ } else {
+
+ int j;
+
+ /* Count aliases */
+ for (j = 0; aliases[j] != NULL; j++);
+
+ names = (char **)calloc(j + 1, sizeof (char *));
+ if (names == NULL) {
+ *error = -1;
+ handle_error("out of memory");
+ } else {
+
+ /* For each alias... */
+ for (j = 0; *error == 0 && aliases[j] != NULL; j++) {
+
+ dm_descriptor_t alias = aliases[j];
+ char *aname = dm_get_name(alias, error);
+ if (*error) {
+ handle_error("could not get alias %d "
+ "for disk %s", (j + 1), name);
+ } else {
+ names[j] = strdup(aname);
+ if (names[j] == NULL) {
+ *error = -1;
+ handle_error("out of memory");
+ }
+
+ dm_free_name(aname);
+ }
+ }
+ }
+
+ dm_free_descriptors(aliases);
+ }
+
+ if (*error && names != NULL) {
+ int i;
+ /* Free previously-allocated names */
+ for (i = 0; names[i] != NULL; i++) {
+ free(names[i]);
+ }
+ free(names);
+ }
+
+ return (names);
+}
+
+static int
+get_disk_online(dm_descriptor_t disk, int *error)
+{
+ uint32_t status = 0;
+
+ nvlist_t *attrs;
+ *error = 0;
+ attrs = dm_get_attributes(disk, error);
+ if (*error) {
+ handle_error("could not get disk attributes for disk");
+ } else {
+
+ /* Try to get the status */
+ nvpair_t *match = zjni_nvlist_walk_nvpair(
+ attrs, DM_STATUS, DATA_TYPE_UINT32, NULL);
+
+ if (match == NULL || nvpair_value_uint32(match, &status)) {
+
+ handle_error("could not get status of disk");
+ *error = 1;
+ }
+
+ nvlist_free(attrs);
+ }
+
+ return (status != 0);
+}
+
+/*
+ * Gets the slices for the given disk.
+ *
+ * Results:
+ *
+ * 1. Success: error is set to 0 and slices are returned
+ *
+ * 2. Failure: error is set to -1 and NULL is returned
+ */
+static dmgt_slice_t **
+get_disk_slices(dm_descriptor_t media, const char *name, uint32_t blocksize,
+ int *error)
+{
+ dm_descriptor_t *slices;
+ dmgt_slice_t **sap = NULL;
+
+ *error = 0;
+ slices = dm_get_associated_descriptors(media, DM_SLICE, error);
+ if (*error != 0) {
+ handle_error("could not get slices of disk %s", name);
+ } else {
+ int j;
+ int nslices = 0;
+
+ /* For each slice... */
+ for (j = 0; *error == 0 &&
+ slices != NULL && slices[j] != NULL; j++) {
+
+ /* Get slice */
+ dmgt_slice_t *slice =
+ get_slice(slices[j], blocksize, error);
+ if (!*error) {
+
+ sap = (dmgt_slice_t **)realloc(sap,
+ (nslices + 2) * sizeof (dmgt_slice_t *));
+ if (sap == NULL) {
+ handle_error("out of memory");
+ *error = -1;
+ } else {
+
+ /* NULL-terminated array */
+ sap[nslices] = slice;
+ sap[nslices + 1] = NULL;
+
+ nslices++;
+ }
+ }
+ }
+
+ dm_free_descriptors(slices);
+ }
+
+ if (*error) {
+ /* Normalize error */
+ *error = -1;
+ }
+
+ if (*error && sap != NULL) {
+ free_slice_array(sap);
+ sap = NULL;
+ }
+
+ return (sap);
+}
+
+static void
+remove_slice_from_list(dmgt_slice_t **slices, int index)
+{
+ int i;
+ for (i = index; slices[i] != NULL; i++) {
+ slices[i] = slices[i + 1];
+ }
+}
+
+static int
+slices_overlap(dmgt_slice_t *slice1, dmgt_slice_t *slice2)
+{
+
+ uint64_t start1 = slice1->start;
+ uint64_t end1 = start1 + slice1->size - 1;
+ uint64_t start2 = slice2->start;
+ uint64_t end2 = start2 + slice2->size - 2;
+
+ int overlap = (start2 <= end1 && start1 <= end2);
+
+#ifdef DEBUG
+ if (overlap) {
+ (void) fprintf(stderr, "can't use %s: overlaps with %s\n",
+ slice2->name, slice1->name);
+ (void) fprintf(stderr, " 1: start: %llu - %llu\n",
+ (unsigned long long)start1, (unsigned long long)end1);
+ (void) fprintf(stderr, " 2: start: %llu - %llu\n",
+ (unsigned long long)start2, (unsigned long long)end2);
+ }
+#endif
+
+ return (overlap);
+}
+
+/*
+ * Gets the slices for the given disk.
+ *
+ * Results:
+ *
+ * 1. Success: error is set to 0 and slices are returned
+ *
+ * 2. Failure: error is set to -1 and NULL is returned
+ */
+static dmgt_slice_t **
+get_disk_usable_slices(dm_descriptor_t media, const char *name,
+ uint32_t blocksize, int *in_use, int *error)
+{
+ dmgt_slice_t **slices = get_disk_slices(media, name, blocksize, error);
+
+ *in_use = 0;
+
+ if (!*error && slices != NULL) {
+ int i, nslices;
+
+ for (nslices = 0; slices[nslices] != NULL; nslices++);
+
+ /* Prune slices based on use */
+ for (i = nslices - 1; i >= 0; i--) {
+ dmgt_slice_t *slice = slices[i];
+ if (slice == NULL) {
+ continue;
+ }
+
+ if (slice_in_use(slice)) {
+ int j;
+ remove_slice_from_list(slices, i);
+
+ *in_use = 1;
+
+ /*
+ * Remove any slice that overlaps with this
+ * in-use slice
+ */
+ for (j = nslices - 1; j >= 0; j--) {
+ if (slices[j] == NULL) {
+ continue;
+ }
+ if (slices_overlap(slice, slices[j])) {
+ remove_slice_from_list(slices,
+ j);
+ }
+ }
+ } else
+ if (slice_too_small(slice)) {
+ remove_slice_from_list(slices, i);
+ }
+ }
+ }
+
+ return (slices);
+}
+
+static void
+get_disk_size(dm_descriptor_t media, char *name, uint64_t *size,
+ uint32_t *blocksize, int *error)
+{
+ nvlist_t *attrs;
+
+ *size = 0;
+ *error = 0;
+
+ attrs = dm_get_attributes(media, error);
+
+ if (*error) {
+ handle_error("could not get media attributes from disk: %s",
+ name);
+ } else {
+ /* Try to get the number of accessible blocks */
+ nvpair_t *match = zjni_nvlist_walk_nvpair(
+ attrs, DM_NACCESSIBLE, DATA_TYPE_UINT64, NULL);
+ if (match == NULL || nvpair_value_uint64(match, size)) {
+
+ /* Disk is probably not labeled, get raw size instead */
+ match = zjni_nvlist_walk_nvpair(
+ attrs, DM_SIZE, DATA_TYPE_UINT64, NULL);
+ if (match == NULL || nvpair_value_uint64(match, size)) {
+ handle_error("could not get size of disk: %s",
+ name);
+ *error = 1;
+ }
+ }
+
+ if (*error == 0) {
+ match = zjni_nvlist_walk_nvpair(
+ attrs, DM_BLOCKSIZE, DATA_TYPE_UINT32, NULL);
+ if (match == NULL ||
+ nvpair_value_uint32(match, blocksize)) {
+ handle_error("could not get "
+ "block size of disk: %s", name);
+ *error = 1;
+ } else {
+ *size *= *blocksize;
+ }
+ }
+
+ nvlist_free(attrs);
+ }
+}
+
+static void
+get_slice_use(dm_descriptor_t slice, char *name, char **used_name,
+ char **used_by, int *error)
+{
+ /* Get slice use statistics */
+ nvlist_t *stats = dm_get_stats(slice, DM_SLICE_STAT_USE, error);
+ if (*error != 0) {
+ handle_error("could not get stats of slice %s", name);
+ } else {
+
+ *used_name = NULL;
+ *used_by = NULL;
+
+ if (stats != NULL) {
+ char *tmp;
+ nvpair_t *match;
+
+ /* Get the type of usage for this slice */
+ match = zjni_nvlist_walk_nvpair(
+ stats, DM_USED_BY, DATA_TYPE_STRING, NULL);
+
+ if (match != NULL &&
+ nvpair_value_string(match, &tmp) == 0) {
+
+ *used_name = strdup(tmp);
+ if (*used_name == NULL) {
+ *error = -1;
+ handle_error("out of memory");
+ } else {
+
+ /* Get the object using this slice */
+ match =
+ zjni_nvlist_walk_nvpair(stats,
+ DM_USED_NAME, DATA_TYPE_STRING,
+ NULL);
+
+ if (match != NULL &&
+ nvpair_value_string(match, &tmp) ==
+ 0) {
+ *used_by = strdup(tmp);
+ if (*used_by == NULL) {
+ *error = -1;
+ handle_error(
+ "out of memory");
+ }
+ }
+ }
+ }
+ nvlist_free(stats);
+ }
+ }
+}
+
+static dmgt_slice_t *
+get_slice(dm_descriptor_t slice, uint32_t blocksize, int *error)
+{
+ dmgt_slice_t *sp;
+ *error = 0;
+ sp = (dmgt_slice_t *)calloc(1, sizeof (dmgt_slice_t));
+ if (sp == NULL) {
+ *error = -1;
+ handle_error("out of memory");
+ } else {
+
+ /* Get name */
+ sp->name = get_device_name(slice, error);
+ if (!*error) {
+
+ nvlist_t *attrs = dm_get_attributes(slice, error);
+ if (*error) {
+ handle_error("could not get "
+ "attributes from slice: %s", sp->name);
+ } else {
+ /* Get the size in blocks */
+ nvpair_t *match = zjni_nvlist_walk_nvpair(
+ attrs, DM_SIZE, DATA_TYPE_UINT64, NULL);
+ uint64_t size_blocks;
+
+ sp->size = 0;
+
+ if (match == NULL ||
+ nvpair_value_uint64(match, &size_blocks)) {
+ handle_error("could not get "
+ "size of slice: %s", sp->name);
+ *error = 1;
+ } else {
+ uint64_t start_blocks;
+
+ /* Convert to bytes */
+ sp->size = blocksize * size_blocks;
+
+ /* Get the starting block */
+ match = zjni_nvlist_walk_nvpair(
+ attrs, DM_START, DATA_TYPE_UINT64,
+ NULL);
+
+ if (match == NULL ||
+ nvpair_value_uint64(match,
+ &start_blocks)) {
+ handle_error(
+ "could not get "
+ "start block of slice: %s",
+ sp->name);
+ *error = 1;
+ } else {
+ /* Convert to bytes */
+ sp->start =
+ blocksize * start_blocks;
+
+ /* Set slice use */
+ get_slice_use(slice, sp->name,
+ &(sp->used_name),
+ &(sp->used_by), error);
+ }
+ }
+ }
+ }
+ }
+
+ if (*error && sp != NULL) {
+ dmgt_free_slice(sp);
+ }
+
+ return (sp);
+}
+
+static void
+handle_error(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+
+ if (error_func != NULL) {
+ error_func(format, ap);
+ }
+
+ va_end(ap);
+}
+
+/* Should go away once 6285992 is fixed */
+static int
+slice_too_small(dmgt_slice_t *slice)
+{
+ /* Check size */
+ if (slice->size < SPA_MINDEVSIZE) {
+#ifdef DEBUG
+ (void) fprintf(stderr, "can't use %s: slice too small: %llu\n",
+ slice->name, (unsigned long long)slice->size);
+#endif
+ return (1);
+ }
+
+ return (0);
+}
+
+/* Should go away once 6285992 is fixed */
+static int
+slice_in_use(dmgt_slice_t *slice)
+{
+ int in_use = 0;
+
+ /* Check use */
+ if (slice->used_name != NULL) {
+
+ in_use = 1;
+
+ /* If the slice contains an unmounted file system... */
+ if (strcmp(DM_USE_FS, slice->used_name) == 0) {
+
+ /* Allow only if file system is not ZFS */
+ if (strcmp(slice->used_by, "zfs") != 0) {
+ in_use = 0;
+ }
+ } else
+
+ /* Uses that don't preclude slice from use by ZFS */
+ if (strcmp(DM_USE_SVM, slice->used_name) == 0 ||
+ strcmp(DM_USE_VXVM, slice->used_name) == 0 ||
+ strcmp(DM_USE_LU, slice->used_name) == 0) {
+ in_use = 0;
+ }
+ }
+
+#ifdef DEBUG
+ if (in_use) {
+ (void) fprintf(stderr,
+ "can't use %s: used name: %s: used by: %s\n",
+ slice->name, slice->used_name, slice->used_by);
+ }
+#endif
+
+ return (in_use);
+}
+
+/*
+ * Extern functions
+ */
+
+/*
+ * Iterates through each available disk on the system. For each free
+ * dmgt_disk_t *, runs the given function with the dmgt_disk_t * as
+ * the first arg and the given void * as the second arg.
+ */
+int
+dmgt_avail_disk_iter(dmgt_disk_iter_f func, void *data)
+{
+ int error = 0;
+ int filter[] = { DM_DT_FIXED, -1 };
+
+ /* Search for fixed disks */
+ dm_descriptor_t *disks = dm_get_descriptors(DM_DRIVE, filter, &error);
+
+ if (error) {
+ handle_error("unable to communicate with libdiskmgt");
+ } else {
+ int i;
+
+ /* For each disk... */
+ for (i = 0; disks != NULL && error == 0 && disks[i] != NULL;
+ i++) {
+ /* Is this disk online? */
+ dm_descriptor_t disk = (dm_descriptor_t)disks[i];
+ int online = get_disk_online(disk, &error);
+ if (!error && online) {
+ dmgt_disk_t *dp = get_disk(disk, &error);
+ if (error == DISK_IN_USE) {
+ error = 0;
+ } else
+ if (!error) {
+ /* Run the given function */
+ if (func(dp, data)) {
+ error = -1;
+ }
+ dmgt_free_disk(dp);
+ }
+ }
+ }
+ dm_free_descriptors(disks);
+ }
+ return (error);
+}
+
+void
+dmgt_free_disk(dmgt_disk_t *disk)
+{
+ if (disk != NULL) {
+ int i;
+ free(disk->name);
+
+ if (disk->aliases != NULL) {
+ for (i = 0; disk->aliases[i] != NULL; i++) {
+ free(disk->aliases[i]);
+ }
+ free(disk->aliases);
+ }
+
+ free_slice_array(disk->slices);
+ free(disk);
+ }
+}
+
+void
+dmgt_free_slice(dmgt_slice_t *slice)
+{
+ if (slice != NULL) {
+ free(slice->name);
+ free(slice->used_name);
+ free(slice->used_by);
+ free(slice);
+ }
+}
+
+/*
+ * For clients that need to capture error output.
+ */
+void
+dmgt_set_error_handler(void (*func)(const char *, va_list))
+{
+ error_func = func;
+}
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_diskmgt.h b/usr/src/lib/libzfs_jni/common/libzfs_jni_diskmgt.h
new file mode 100644
index 0000000000..0d10909eaa
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_diskmgt.h
@@ -0,0 +1,80 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBZFS_JNI_DISKMGT_H
+#define _LIBZFS_JNI_DISKMGT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libdiskmgt.h>
+#include <sys/varargs.h>
+
+/*
+ * Types
+ */
+
+typedef struct dmgt_slice {
+ char *name;
+ uint64_t start;
+ uint64_t size;
+ char *used_name;
+ char *used_by;
+} dmgt_slice_t;
+
+typedef struct dmgt_disk {
+ char *name;
+ uint64_t size;
+ uint32_t blocksize;
+ int in_use;
+
+ /* NULL-terminated array */
+ char **aliases;
+
+ /* NULL-terminated array */
+ dmgt_slice_t **slices;
+} dmgt_disk_t;
+
+/* Callback function for available disk iteration */
+typedef int (*dmgt_disk_iter_f)(dmgt_disk_t *, void *);
+
+/*
+ * Function prototypes
+ */
+
+extern int dmgt_avail_disk_iter(dmgt_disk_iter_f func, void *data);
+extern void dmgt_free_disk(dmgt_disk_t *);
+extern void dmgt_free_slice(dmgt_slice_t *);
+extern void dmgt_set_error_handler(void (*)(const char *, va_list));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBZFS_JNI_DISKMGT_H */
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_main.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_main.c
new file mode 100644
index 0000000000..b6c78644a2
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_main.c
@@ -0,0 +1,485 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libzfs_jni_main.h"
+#include "libzfs_jni_util.h"
+#include "libzfs_jni_dataset.h"
+#include "libzfs_jni_property.h"
+#include "libzfs_jni_pool.h"
+#include "libzfs_jni_diskmgt.h"
+#include "libzfs_jni_disk.h"
+
+/*
+ * Function prototypes
+ */
+
+static void handle_error(const char *, va_list);
+static void init();
+
+/*
+ * Static functions
+ */
+
+char libzfs_err[1024];
+static void
+handle_error(const char *fmt, va_list ap)
+{
+ /* Save the error message in case it's needed */
+ (void) vsnprintf(libzfs_err, sizeof (libzfs_err), fmt, ap);
+#ifdef DEBUG
+ (void) fprintf(stderr, "caught error: %s\n", libzfs_err);
+#endif
+}
+
+/*
+ * Initialize the library. Sets the error handler.
+ */
+#pragma init(init)
+static void
+init()
+{
+ libzfs_err[0] = '\0';
+
+ /* libzfs error handler */
+ zfs_set_error_handler(handle_error);
+
+ /* diskmgt.o error handler */
+ dmgt_set_error_handler(handle_error);
+}
+
+/*
+ * JNI functions
+ */
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getPools
+ * Signature: ()[Lcom/sun/zfs/common/model/Pool;
+ */
+/* ARGSUSED */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getPools(JNIEnv *env, jobject obj)
+{
+ zjni_DatasetArrayCallbackData_t data = {0};
+ int result;
+
+ /* Create an array list */
+ zjni_ArrayList_t list_obj = {0};
+ zjni_ArrayList_t *list = &list_obj;
+ zjni_new_ArrayList(env, list);
+
+ data.data.env = env;
+ data.data.list = (zjni_Collection_t *)list;
+ data.typemask = ZFS_TYPE_FILESYSTEM;
+
+ result = zfs_iter_root(zjni_create_add_Dataset, &data);
+ if (result && (*env)->ExceptionOccurred(env) != NULL) {
+ /* Must not call any more Java methods to preserve exception */
+ return (NULL);
+ }
+
+ return (zjni_Collection_to_array(env, (zjni_Collection_t *)list,
+ ZFSJNI_PACKAGE_DATA "Pool"));
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getPool
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/Pool;
+ */
+/* ARGSUSED */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getPool(JNIEnv *env,
+ jobject obj, jstring poolUTF)
+{
+ return (zjni_get_Dataset(env, poolUTF, ZFS_TYPE_FILESYSTEM));
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getFileSystems
+ * Signature: (Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/FileSystem;
+ */
+/* ARGSUSED */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getFileSystems(JNIEnv *env,
+ jobject obj, jstring containerUTF)
+{
+ return (zjni_get_Datasets_below(env, containerUTF,
+ ZFS_TYPE_FILESYSTEM, ZFS_TYPE_FILESYSTEM,
+ ZFSJNI_PACKAGE_DATA "FileSystem"));
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getFileSystem
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/FileSystem;
+ */
+/* ARGSUSED */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getFileSystem(JNIEnv *env,
+ jobject obj, jstring nameUTF)
+{
+ return (zjni_get_Dataset(env, nameUTF, ZFS_TYPE_FILESYSTEM));
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getVolumes
+ * Signature: (Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/Volume;
+ */
+/* ARGSUSED */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getVolumes(JNIEnv *env,
+ jobject obj, jstring containerUTF)
+{
+ return (zjni_get_Datasets_below(env, containerUTF,
+ ZFS_TYPE_FILESYSTEM, ZFS_TYPE_VOLUME,
+ ZFSJNI_PACKAGE_DATA "Volume"));
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getVolume
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/Volume;
+ */
+/* ARGSUSED */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getVolume(JNIEnv *env,
+ jobject obj, jstring nameUTF)
+{
+ return (zjni_get_Dataset(env, nameUTF, ZFS_TYPE_VOLUME));
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getSnapshots
+ * Signature: (Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/Snapshot;
+ */
+/* ARGSUSED */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getSnapshots(JNIEnv *env,
+ jobject obj, jstring datasetUTF)
+{
+ return (zjni_get_Datasets_below(env, datasetUTF,
+ ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, ZFS_TYPE_SNAPSHOT,
+ ZFSJNI_PACKAGE_DATA "Snapshot"));
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getSnapshot
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/Snapshot;
+ */
+/* ARGSUSED */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getSnapshot(JNIEnv *env,
+ jobject obj, jstring nameUTF)
+{
+ return (zjni_get_Dataset(env, nameUTF, ZFS_TYPE_SNAPSHOT));
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getDatasets
+ * Signature: (Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/Dataset;
+ */
+/* ARGSUSED */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getDatasets(JNIEnv *env,
+ jobject obj, jstring containerUTF)
+{
+ if (containerUTF == NULL) {
+ return (Java_com_sun_zfs_common_model_SystemDataModel_getPools(
+ env, obj));
+ }
+
+ return (zjni_get_Datasets_below(env, containerUTF,
+ ZFS_TYPE_FILESYSTEM, ZFS_TYPE_ANY,
+ ZFSJNI_PACKAGE_DATA "Dataset"));
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getDataset
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/Dataset;
+ */
+/* ARGSUSED */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getDataset(JNIEnv *env,
+ jobject obj, jstring nameUTF)
+{
+ return (zjni_get_Dataset(env, nameUTF, ZFS_TYPE_ANY));
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getVirtualDevice
+ * Signature: (Ljava/lang/String;J)Lcom/sun/zfs/common/model/VirtualDevice;
+ */
+/* ARGSUSED */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevice(JNIEnv *env,
+ jobject obj, jstring poolUTF, jlong index)
+{
+ jobject vdev = NULL;
+
+ if (poolUTF != NULL) {
+ const char *pool = (*env)->GetStringUTFChars(env, poolUTF,
+ NULL);
+ zpool_handle_t *zhp = zpool_open(pool);
+ (*env)->ReleaseStringUTFChars(env, poolUTF, pool);
+
+ if (zhp != NULL) {
+ nvlist_t *vdev_cfg = zjni_get_vdev(zhp, NULL, index);
+ if (vdev_cfg != NULL) {
+ vdev = zjni_get_VirtualDevice_from_vdev(env,
+ zhp, vdev_cfg);
+ }
+ zpool_close(zhp);
+ }
+ }
+
+ return (vdev);
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getVirtualDevices
+ * Signature: (Ljava/lang/String;J)
+ * [Lcom/sun/zfs/common/model/VirtualDevice;
+ */
+/* ARGSUSED */
+JNIEXPORT jobjectArray JNICALL
+/* CSTYLED */
+Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevices__Ljava_lang_String_2J(
+ JNIEnv *env, jobject obj, jstring poolUTF, jlong index)
+{
+ jobjectArray vdevs = NULL;
+
+ if (poolUTF != NULL) {
+ const char *pool = (*env)->GetStringUTFChars(env, poolUTF,
+ NULL);
+ zpool_handle_t *zhp = zpool_open(pool);
+ (*env)->ReleaseStringUTFChars(env, poolUTF, pool);
+
+ /* Is the pool valid? */
+ if (zhp != NULL) {
+ nvlist_t *vdev_cfg = zjni_get_vdev(zhp, NULL, index);
+ if (vdev_cfg != NULL) {
+ vdevs = zjni_get_VirtualDevices_from_vdev(
+ env, zhp, vdev_cfg);
+ }
+ zpool_close(zhp);
+ }
+ }
+
+ return (vdevs);
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getVirtualDevices
+ * Signature: (Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/VirtualDevice;
+ */
+/* ARGSUSED */
+JNIEXPORT jobjectArray JNICALL
+/* CSTYLED */
+Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevices__Ljava_lang_String_2(
+ JNIEnv *env, jobject obj, jstring poolUTF)
+{
+ jobjectArray vdevs = NULL;
+
+ if (poolUTF != NULL) {
+ const char *pool = (*env)->GetStringUTFChars(env,
+ poolUTF, NULL);
+ zpool_handle_t *zhp = zpool_open(pool);
+ (*env)->ReleaseStringUTFChars(env, poolUTF, pool);
+
+ /* Is the pool valid? */
+ if (zhp != NULL) {
+ vdevs = zjni_get_VirtualDevices_from_vdev(env,
+ zhp, NULL);
+ zpool_close(zhp);
+ }
+ }
+
+ return (vdevs);
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getAvailableDisks
+ * Signature: ()[Lcom/sun/zfs/common/model/DiskDevice;
+ */
+/* ARGSUSED */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getAvailableDisks(JNIEnv *env,
+ jobject obj)
+{
+ int error;
+ zjni_ArrayCallbackData_t data = {0};
+ jobjectArray array = NULL;
+
+ /* Create an array list */
+ zjni_ArrayList_t list_obj = {0};
+ zjni_ArrayList_t *list = &list_obj;
+ zjni_new_ArrayList(env, list);
+
+ data.env = env;
+ data.list = (zjni_Collection_t *)list;
+ error = dmgt_avail_disk_iter(zjni_create_add_DiskDevice, &data);
+
+ if (error) {
+ zjni_throw_exception(env, "%s", libzfs_err);
+ } else {
+ array = zjni_Collection_to_array(
+ env, (zjni_Collection_t *)list,
+ ZFSJNI_PACKAGE_DATA "DiskDevice");
+ }
+
+ return (array);
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getDependents
+ * Signature: ([Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/Dataset;
+ */
+/* ARGSUSED */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getDependents(JNIEnv *env,
+ jobject obj, jobjectArray paths)
+{
+ return (zjni_get_Datasets_dependents(env, paths));
+}
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getPropertyDefault
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/Property;
+ */
+/* ARGSUSED */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getPropertyDefault(JNIEnv *env,
+ jobject obj, jstring nameUTF)
+{
+ jobject defProperty = NULL;
+
+ const char *name = (*env)->GetStringUTFChars(env, nameUTF, NULL);
+ zfs_prop_t prop = zjni_get_property_from_name(name);
+ (*env)->ReleaseStringUTFChars(env, nameUTF, name);
+
+ if (prop != ZFS_PROP_INVAL) {
+ defProperty = zjni_get_default_property(env, prop);
+ }
+
+ return (defProperty);
+}
+
+typedef struct zjni_class_type_map {
+ char *class;
+ zfs_type_t type;
+} zjni_class_type_map_t;
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getValidPropertyNames
+ * Signature: (Ljava/lang/Class;)
+ * [Ljava/lang/String;
+ */
+/* ARGSUSED */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getValidPropertyNames(JNIEnv *env,
+ jobject obj, jclass class)
+{
+ int i;
+
+ /* Mappings of class names to zfs_type_t */
+ static zjni_class_type_map_t mappings[] = {
+ { ZFSJNI_PACKAGE_DATA "FileSystem", ZFS_TYPE_FILESYSTEM },
+ { ZFSJNI_PACKAGE_DATA "Volume", ZFS_TYPE_VOLUME },
+ { ZFSJNI_PACKAGE_DATA "Snapshot", ZFS_TYPE_SNAPSHOT },
+ };
+ int nmappings = sizeof (mappings) / sizeof (zjni_class_type_map_t);
+
+ jclass class_Class = (*env)->FindClass(env, "java/lang/Class");
+
+ jmethodID isAssignableFrom = (*env)->GetMethodID(
+ env, class_Class, "isAssignableFrom", "(Ljava/lang/Class;)Z");
+
+ /* Create an array list for the property names */
+ zjni_ArrayList_t list_obj = {0};
+ zjni_ArrayList_t *list = &list_obj;
+ zjni_new_ArrayList(env, list);
+
+ /* For each mapping... */
+ for (i = 0; i < nmappings; i++) {
+ /*
+ * Is the given class an instance of the class in the mapping?
+ */
+ jclass typeClass = (*env)->FindClass(env, mappings[i].class);
+
+ jboolean isInstance = (*env)->CallBooleanMethod(
+ env, typeClass, isAssignableFrom, class);
+
+ if (isInstance == JNI_TRUE) {
+ zfs_prop_t prop;
+ for (prop = 0; prop < ZFS_NPROP_VISIBLE; prop++) {
+ if (zfs_prop_valid_for_type(prop,
+ mappings[i].type)) {
+ /* Add name of property to list */
+ jstring propName =
+ (*env)->NewStringUTF(env,
+ zfs_prop_to_name(prop));
+ (*env)->CallBooleanMethod(
+ env,
+ ((zjni_Object_t *)list)->object,
+ ((zjni_Collection_t *)list)->
+ method_add, propName);
+ }
+ }
+ break;
+ }
+ }
+
+ return (zjni_Collection_to_array(
+ env, (zjni_Collection_t *)list, "java/lang/String"));
+}
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_main.h b/usr/src/lib/libzfs_jni/common/libzfs_jni_main.h
new file mode 100644
index 0000000000..c64d49953d
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_main.h
@@ -0,0 +1,215 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ZLIBZFS_JNI_MAIN_H
+#define _ZLIBZFS_JNI_MAIN_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <jni.h>
+/* Header for class com_sun_zfs_common_model_SystemDataModel */
+
+#ifndef _Included_com_sun_zfs_common_model_SystemDataModel
+#define _Included_com_sun_zfs_common_model_SystemDataModel
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getPools
+ * Signature: ()[Lcom/sun/zfs/common/model/Pool;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getPools(
+ JNIEnv *, jobject);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getPool
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/Pool;
+ */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getPool(
+ JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getFileSystems
+ * Signature: (Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/FileSystem;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getFileSystems(
+ JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getFileSystem
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/FileSystem;
+ */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getFileSystem(
+ JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getVolumes
+ * Signature: (Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/Volume;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getVolumes(
+ JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getVolume
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/Volume;
+ */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getVolume(
+ JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getSnapshots
+ * Signature: (Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/Snapshot;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getSnapshots(
+ JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getSnapshot
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/Snapshot;
+ */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getSnapshot(
+ JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getDatasets
+ * Signature: (Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/Dataset;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getDatasets(
+ JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getDataset
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/Dataset;
+ */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getDataset(
+ JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getVirtualDevice
+ * Signature: (Ljava/lang/String;J)Lcom/sun/zfs/common/model/VirtualDevice;
+ */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevice(
+ JNIEnv *, jobject, jstring, jlong);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getVirtualDevices
+ * Signature: (Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/VirtualDevice;
+ */
+JNIEXPORT jobjectArray JNICALL
+/* CSTYLED */
+Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevices__Ljava_lang_String_2(
+ JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getVirtualDevices
+ * Signature: (Ljava/lang/String;J)[Lcom/sun/zfs/common/model/VirtualDevice;
+ */
+JNIEXPORT jobjectArray JNICALL
+/* CSTYLED */
+Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevices__Ljava_lang_String_2J(
+ JNIEnv *, jobject, jstring, jlong);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getAvailableDisks
+ * Signature: ()[Lcom/sun/zfs/common/model/DiskDevice;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getAvailableDisks(
+ JNIEnv *, jobject);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getDependents
+ * Signature: ([Ljava/lang/String;)
+ * [Lcom/sun/zfs/common/model/Dataset;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getDependents(
+ JNIEnv *, jobject, jobjectArray);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getPropertyDefault
+ * Signature: (Ljava/lang/String;)
+ * Lcom/sun/zfs/common/model/Property;
+ */
+JNIEXPORT jobject JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getPropertyDefault(
+ JNIEnv *, jobject, jstring);
+
+/*
+ * Class: com_sun_zfs_common_model_SystemDataModel
+ * Method: getValidPropertyNames
+ * Signature: (Ljava/lang/Class;)
+ * [Ljava/lang/String;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_com_sun_zfs_common_model_SystemDataModel_getValidPropertyNames(
+ JNIEnv *, jobject, jclass);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
+
+#endif /* _ZLIBZFS_JNI_MAIN_H */
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c
new file mode 100644
index 0000000000..5e50313ebd
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.c
@@ -0,0 +1,575 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libzfs_jni_pool.h"
+#include "libzfs_jni_util.h"
+#include <strings.h>
+
+/*
+ * Types
+ */
+
+typedef struct VirtualDeviceBean {
+ zjni_Object_t super;
+
+ jmethodID method_setPoolName;
+ jmethodID method_setIndex;
+ jmethodID method_setSize;
+ jmethodID method_setUsed;
+} VirtualDeviceBean_t;
+
+typedef struct DiskVirtualDeviceBean {
+ VirtualDeviceBean_t super;
+
+ jmethodID method_setDiskName;
+} DiskVirtualDeviceBean_t;
+
+typedef struct FileVirtualDeviceBean {
+ VirtualDeviceBean_t super;
+
+ jmethodID method_setFileName;
+} FileVirtualDeviceBean_t;
+
+typedef struct RAIDVirtualDeviceBean {
+ VirtualDeviceBean_t super;
+} RAIDVirtualDeviceBean_t;
+
+typedef struct MirrorVirtualDeviceBean {
+ VirtualDeviceBean_t super;
+} MirrorVirtualDeviceBean_t;
+
+/*
+ * Function prototypes
+ */
+
+static void new_VirtualDevice(JNIEnv *, VirtualDeviceBean_t *);
+static void new_DiskVirtualDeviceBean(JNIEnv *, DiskVirtualDeviceBean_t *);
+static void new_FileVirtualDeviceBean(JNIEnv *, FileVirtualDeviceBean_t *);
+static void new_RAIDVirtualDeviceBean(JNIEnv *, RAIDVirtualDeviceBean_t *);
+static void new_MirrorVirtualDeviceBean(JNIEnv *, MirrorVirtualDeviceBean_t *);
+static int populate_VirtualDeviceBean(
+ JNIEnv *, zpool_handle_t *, nvlist_t *, VirtualDeviceBean_t *);
+static int populate_DiskVirtualDeviceBean(
+ JNIEnv *, zpool_handle_t *, nvlist_t *, DiskVirtualDeviceBean_t *);
+static int populate_FileVirtualDeviceBean(
+ JNIEnv *, zpool_handle_t *, nvlist_t *, FileVirtualDeviceBean_t *);
+static int populate_RAIDVirtualDeviceBean(
+ JNIEnv *, zpool_handle_t *, nvlist_t *, RAIDVirtualDeviceBean_t *);
+static int populate_MirrorVirtualDeviceBean(
+ JNIEnv *, zpool_handle_t *, nvlist_t *, MirrorVirtualDeviceBean_t *);
+static jobject create_DiskVirtualDeviceBean(
+ JNIEnv *, zpool_handle_t *, nvlist_t *);
+static jobject create_FileVirtualDeviceBean(
+ JNIEnv *, zpool_handle_t *, nvlist_t *);
+static jobject create_RAIDVirtualDeviceBean(
+ JNIEnv *, zpool_handle_t *, nvlist_t *);
+static jobject create_MirrorVirtualDeviceBean(
+ JNIEnv *, zpool_handle_t *, nvlist_t *);
+
+/*
+ * Static functions
+ */
+
+/* Create a VirtualDeviceBean */
+static void
+new_VirtualDevice(JNIEnv *env, VirtualDeviceBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+ object->class =
+ (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "VirtualDeviceBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ bean->method_setPoolName = (*env)->GetMethodID(
+ env, object->class, "setPoolName", "(Ljava/lang/String;)V");
+
+ bean->method_setIndex = (*env)->GetMethodID(
+ env, object->class, "setIndex", "(J)V");
+
+ bean->method_setSize = (*env)->GetMethodID(
+ env, object->class, "setSize", "(J)V");
+
+ bean->method_setUsed = (*env)->GetMethodID(
+ env, object->class, "setUsed", "(J)V");
+}
+
+/* Create a DiskVirtualDeviceBean */
+static void
+new_DiskVirtualDeviceBean(JNIEnv *env, DiskVirtualDeviceBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+ object->class = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "DiskVirtualDeviceBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ new_VirtualDevice(env, (VirtualDeviceBean_t *)bean);
+
+ bean->method_setDiskName = (*env)->GetMethodID(
+ env, object->class, "setDiskName", "(Ljava/lang/String;)V");
+
+}
+
+/* Create a FileVirtualDeviceBean */
+static void
+new_FileVirtualDeviceBean(JNIEnv *env, FileVirtualDeviceBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+ object->class = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "FileVirtualDeviceBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ new_VirtualDevice(env, (VirtualDeviceBean_t *)bean);
+
+ bean->method_setFileName = (*env)->GetMethodID(
+ env, object->class, "setFileName", "(Ljava/lang/String;)V");
+}
+
+/* Create a RAIDVirtualDeviceBean */
+static void
+new_RAIDVirtualDeviceBean(JNIEnv *env, RAIDVirtualDeviceBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+
+ object->class = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "RAIDVirtualDeviceBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ new_VirtualDevice(env, (VirtualDeviceBean_t *)bean);
+}
+
+/* Create a MirrorVirtualDeviceBean */
+static void
+new_MirrorVirtualDeviceBean(JNIEnv *env, MirrorVirtualDeviceBean_t *bean)
+{
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ if (object->object == NULL) {
+ object->class = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "MirrorVirtualDeviceBean");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object =
+ (*env)->NewObject(env, object->class, object->constructor);
+ }
+
+ new_VirtualDevice(env, (VirtualDeviceBean_t *)bean);
+}
+
+static int
+populate_VirtualDeviceBean(JNIEnv *env, zpool_handle_t *zhp,
+ nvlist_t *vdev, VirtualDeviceBean_t *bean)
+{
+ int result;
+ uint64_t vdev_id;
+ zjni_Object_t *object = (zjni_Object_t *)bean;
+
+ /* Set pool name */
+ jstring poolUTF = (*env)->NewStringUTF(env, zpool_get_name(zhp));
+ (*env)->CallVoidMethod(
+ env, object->object, bean->method_setPoolName, poolUTF);
+
+ /* Get index */
+ result = nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_GUID, &vdev_id);
+ if (result != 0) {
+ zjni_throw_exception(env,
+ "could not retrieve virtual device ID (pool %s)",
+ zpool_get_name(zhp));
+ } else {
+
+ uint64_t used;
+ uint64_t total;
+
+ (*env)->CallVoidMethod(
+ env, object->object, bean->method_setIndex, (jlong)vdev_id);
+
+ /* Set used space */
+ used = zpool_get_space_used(zhp);
+
+ (*env)->CallVoidMethod(
+ env, object->object, bean->method_setUsed, (jlong)used);
+
+ /* Set available space */
+ total = zpool_get_space_total(zhp);
+
+ (*env)->CallVoidMethod(
+ env, object->object, bean->method_setSize, (jlong)total);
+ }
+
+ return (result != 0);
+}
+
+static int
+populate_DiskVirtualDeviceBean(JNIEnv *env, zpool_handle_t *zhp,
+ nvlist_t *vdev, DiskVirtualDeviceBean_t *bean)
+{
+ char *path;
+ int result = populate_VirtualDeviceBean(
+ env, zhp, vdev, (VirtualDeviceBean_t *)bean);
+
+ if (result) {
+ /* Must not call any more Java methods to preserve exception */
+ return (-1);
+ }
+
+ /* Set path */
+ result = nvlist_lookup_string(vdev, ZPOOL_CONFIG_PATH, &path);
+ if (result != 0) {
+ zjni_throw_exception(env,
+ "could not retrive path from disk virtual device (pool %s)",
+ zpool_get_name(zhp));
+ } else {
+
+ jstring pathUTF = (*env)->NewStringUTF(env, path);
+ (*env)->CallVoidMethod(env, ((zjni_Object_t *)bean)->object,
+ bean->method_setDiskName, pathUTF);
+ }
+
+ return (result != 0);
+}
+
+static int
+populate_FileVirtualDeviceBean(JNIEnv *env, zpool_handle_t *zhp,
+ nvlist_t *vdev, FileVirtualDeviceBean_t *bean)
+{
+ char *path;
+ int result = populate_VirtualDeviceBean(
+ env, zhp, vdev, (VirtualDeviceBean_t *)bean);
+
+ if (result) {
+ /* Must not call any more Java methods to preserve exception */
+ return (-1);
+ }
+
+ /* Set path */
+ result = nvlist_lookup_string(vdev, ZPOOL_CONFIG_PATH, &path);
+ if (result != 0) {
+ zjni_throw_exception(env,
+ "could not retrive path from disk virtual device (pool %s)",
+ zpool_get_name(zhp));
+ } else {
+
+ jstring pathUTF = (*env)->NewStringUTF(env, path);
+ (*env)->CallVoidMethod(env, ((zjni_Object_t *)bean)->object,
+ bean->method_setFileName, pathUTF);
+ }
+
+ return (result != 0);
+}
+
+static int
+populate_RAIDVirtualDeviceBean(JNIEnv *env, zpool_handle_t *zhp,
+ nvlist_t *vdev, RAIDVirtualDeviceBean_t *bean)
+{
+ return (populate_VirtualDeviceBean(env, zhp, vdev,
+ (VirtualDeviceBean_t *)bean));
+}
+
+static int
+populate_MirrorVirtualDeviceBean(JNIEnv *env, zpool_handle_t *zhp,
+ nvlist_t *vdev, MirrorVirtualDeviceBean_t *bean)
+{
+ return (populate_VirtualDeviceBean(env, zhp, vdev,
+ (VirtualDeviceBean_t *)bean));
+}
+
+static jobject
+create_DiskVirtualDeviceBean(JNIEnv *env, zpool_handle_t *zhp, nvlist_t *vdev)
+{
+ int result;
+ DiskVirtualDeviceBean_t bean_obj = {0};
+ DiskVirtualDeviceBean_t *bean = &bean_obj;
+
+ /* Construct DiskVirtualDeviceBean */
+ new_DiskVirtualDeviceBean(env, bean);
+
+ result = populate_DiskVirtualDeviceBean(env, zhp, vdev, bean);
+ if (result) {
+ /* Must not call any more Java methods to preserve exception */
+ return (NULL);
+ }
+
+ return (((zjni_Object_t *)bean)->object);
+}
+
+static jobject
+create_FileVirtualDeviceBean(JNIEnv *env, zpool_handle_t *zhp, nvlist_t *vdev)
+{
+ int result;
+ FileVirtualDeviceBean_t bean_obj = {0};
+ FileVirtualDeviceBean_t *bean = &bean_obj;
+
+ /* Construct FileVirtualDeviceBean */
+ new_FileVirtualDeviceBean(env, bean);
+
+ result = populate_FileVirtualDeviceBean(env, zhp, vdev, bean);
+ if (result) {
+ /* Must not call any more Java methods to preserve exception */
+ return (NULL);
+ }
+
+ return (((zjni_Object_t *)bean)->object);
+}
+
+static jobject
+create_RAIDVirtualDeviceBean(JNIEnv *env, zpool_handle_t *zhp, nvlist_t *vdev)
+{
+ int result;
+ RAIDVirtualDeviceBean_t bean_obj = {0};
+ RAIDVirtualDeviceBean_t *bean = &bean_obj;
+
+ ((zjni_Object_t *)bean)->object = NULL;
+
+ /* Construct RAIDVirtualDeviceBean */
+ new_RAIDVirtualDeviceBean(env, bean);
+
+ result = populate_RAIDVirtualDeviceBean(env, zhp, vdev, bean);
+ if (result) {
+ /* Must not call any more Java methods to preserve exception */
+ return (NULL);
+ }
+
+ return (((zjni_Object_t *)bean)->object);
+}
+
+static jobject
+create_MirrorVirtualDeviceBean(JNIEnv *env, zpool_handle_t *zhp, nvlist_t *vdev)
+{
+ int result;
+ MirrorVirtualDeviceBean_t bean_obj = {0};
+ MirrorVirtualDeviceBean_t *bean = &bean_obj;
+
+ /* Construct MirrorVirtualDeviceBean */
+ new_MirrorVirtualDeviceBean(env, bean);
+
+ result = populate_MirrorVirtualDeviceBean(env, zhp, vdev, bean);
+ if (result) {
+ /* Must not call any more Java methods to preserve exception */
+ return (NULL);
+ }
+
+ return (((zjni_Object_t *)bean)->object);
+}
+
+/*
+ * Package-private functions
+ */
+
+/*
+ * Gets the root vdev (an nvlist_t *) for the given pool.
+ */
+nvlist_t *
+zjni_get_root_vdev(zpool_handle_t *zhp)
+{
+ nvlist_t *root = NULL;
+
+ if (zhp != NULL) {
+ nvlist_t *attrs = zpool_get_config(zhp);
+
+ if (attrs != NULL) {
+ int result = nvlist_lookup_nvlist(
+ attrs, ZPOOL_CONFIG_VDEV_TREE, &root);
+ if (result != 0) {
+ root = NULL;
+ }
+ /* nvlist_print(stderr, vdev_parent); */
+ }
+ }
+
+ return (root);
+}
+
+/*
+ * Gets the vdev (an nvlist_t *) with the given vdev_id, below the
+ * given vdev. If the given vdev is NULL, all vdevs within the given
+ * pool are searched.
+ */
+nvlist_t *
+zjni_get_vdev(zpool_handle_t *zhp, nvlist_t *vdev_parent,
+ uint64_t vdev_id_to_find)
+{
+ int result;
+
+ /* Was a vdev specified? */
+ if (vdev_parent == NULL) {
+ /* No -- retrieve the top-level pool vdev */
+ vdev_parent = zjni_get_root_vdev(zhp);
+ } else {
+ /* Get index of this vdev and compare with vdev_id_to_find */
+ uint64_t id;
+ result = nvlist_lookup_uint64(
+ vdev_parent, ZPOOL_CONFIG_GUID, &id);
+ if (result == 0 && id == vdev_id_to_find) {
+ return (vdev_parent);
+ }
+ }
+
+ if (vdev_parent != NULL) {
+
+ nvlist_t **children;
+ uint_t nelem = 0;
+
+ /* Get the vdevs under this vdev */
+ result = nvlist_lookup_nvlist_array(
+ vdev_parent, ZPOOL_CONFIG_CHILDREN, &children, &nelem);
+
+ if (result == 0) {
+
+ int i;
+ nvlist_t *child;
+
+ /* For each vdev child... */
+ for (i = 0; i < nelem; i++) {
+ child = zjni_get_vdev(zhp, children[i],
+ vdev_id_to_find);
+ if (child != NULL) {
+ return (child);
+ }
+ }
+ }
+ }
+
+ return (NULL);
+}
+
+jobject
+zjni_get_VirtualDevice_from_vdev(JNIEnv *env, zpool_handle_t *zhp,
+ nvlist_t *vdev)
+{
+ jobject obj = NULL;
+ char *type = NULL;
+ int result = nvlist_lookup_string(vdev, ZPOOL_CONFIG_TYPE, &type);
+
+ if (result == 0) {
+ if (strcmp(type, VDEV_TYPE_DISK) == 0) {
+ obj = create_DiskVirtualDeviceBean(env, zhp, vdev);
+ } else if (strcmp(type, VDEV_TYPE_FILE) == 0) {
+ obj = create_FileVirtualDeviceBean(env, zhp,
+ vdev);
+ } else if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {
+ obj = create_RAIDVirtualDeviceBean(env,
+ zhp, vdev);
+ } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0) {
+ obj = create_MirrorVirtualDeviceBean(env, zhp, vdev);
+ }
+ }
+
+ return (obj);
+}
+
+jobject
+zjni_get_VirtualDevices_from_vdev(JNIEnv *env, zpool_handle_t *zhp,
+ nvlist_t *vdev_parent)
+{
+ /* Create an array list for the vdevs */
+ zjni_ArrayList_t list_class = {0};
+ zjni_ArrayList_t *list_class_p = &list_class;
+ zjni_new_ArrayList(env, list_class_p);
+
+ /* Was a vdev specified? */
+ if (vdev_parent == NULL) {
+ /* No -- retrieve the top-level pool vdev */
+ vdev_parent = zjni_get_root_vdev(zhp);
+ }
+
+ /* nvlist_print(stderr, vdev_parent); */
+
+ if (vdev_parent != NULL) {
+
+ /* Get the vdevs under this vdev */
+ nvlist_t **children;
+ uint_t nelem = 0;
+ int result = nvlist_lookup_nvlist_array(
+ vdev_parent, ZPOOL_CONFIG_CHILDREN, &children, &nelem);
+
+ if (result == 0) {
+
+ /* For each vdev child... */
+ int i;
+ for (i = 0; i < nelem; i++) {
+ nvlist_t *child = children[i];
+
+ /* Create a Java object from this vdev */
+ jobject obj =
+ zjni_get_VirtualDevice_from_vdev(env,
+ zhp, child);
+
+ if (obj == NULL) {
+ /*
+ * Must not call any more Java methods
+ * to preserve exception
+ */
+ return (NULL);
+ }
+
+ /* Add child to child vdev list */
+ (*env)->CallBooleanMethod(env,
+ ((zjni_Object_t *)list_class_p)->object,
+ ((zjni_Collection_t *)list_class_p)->
+ method_add, obj);
+ }
+ }
+ }
+
+ return (zjni_Collection_to_array(
+ env, (zjni_Collection_t *)list_class_p,
+ ZFSJNI_PACKAGE_DATA "VirtualDevice"));
+}
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.h b/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.h
new file mode 100644
index 0000000000..e7c08cea36
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_pool.h
@@ -0,0 +1,55 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBZFS_JNI_POOL_H
+#define _LIBZFS_JNI_POOL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <jni.h>
+#include <libnvpair.h>
+#include <libzfs.h>
+
+/*
+ * Function prototypes
+ */
+
+nvlist_t *zjni_get_root_vdev(zpool_handle_t *);
+nvlist_t *zjni_get_vdev(zpool_handle_t *, nvlist_t *, uint64_t);
+jobject zjni_get_VirtualDevice_from_vdev(
+ JNIEnv *, zpool_handle_t *, nvlist_t *);
+jobject zjni_get_VirtualDevices_from_vdev(
+ JNIEnv *, zpool_handle_t *, nvlist_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBZFS_JNI_POOL_H */
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_property.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_property.c
new file mode 100644
index 0000000000..d396da4e53
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_property.c
@@ -0,0 +1,1163 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libzfs_jni_property.h"
+#include "libzfs_jni_util.h"
+#include <strings.h>
+
+/*
+ * Function prototypes
+ */
+
+static jobject create_BooleanProperty(JNIEnv *, zfs_handle_t *, zfs_prop_t);
+static jobject create_ChecksumProperty(JNIEnv *, zfs_handle_t *);
+static jobject create_CompressionProperty(JNIEnv *, zfs_handle_t *);
+static jobject create_DateProperty(JNIEnv *, zfs_handle_t *, zfs_prop_t);
+static jobject create_LongProperty(JNIEnv *, zfs_handle_t *, zfs_prop_t);
+static jobject create_RecordSizeProperty(JNIEnv *, zfs_handle_t *);
+static jobject create_StringProperty(JNIEnv *, zfs_handle_t *, zfs_prop_t);
+static jobject str_to_checksum(JNIEnv *, char *);
+static jobject str_to_compression(JNIEnv *, char *);
+static jobject create_default_BooleanProperty(JNIEnv *, zfs_prop_t);
+static jobject create_default_LongProperty(JNIEnv *, zfs_prop_t);
+static jobject create_default_StringProperty(JNIEnv *, zfs_prop_t);
+static jobject create_default_MountPointProperty(JNIEnv *);
+static jobject create_default_ShareNFSProperty(JNIEnv *);
+static jobject create_default_ChecksumProperty(JNIEnv *);
+static jobject create_default_CompressionProperty(JNIEnv *);
+static jobject create_default_RecordSizeProperty(JNIEnv *);
+
+/*
+ * Static data
+ */
+
+zfs_prop_t props_boolean[] = {
+ ZFS_PROP_ATIME,
+ ZFS_PROP_DEVICES,
+ ZFS_PROP_EXEC,
+ ZFS_PROP_MOUNTED,
+ ZFS_PROP_READONLY,
+ ZFS_PROP_SETUID,
+ ZFS_PROP_ZONED,
+ ZFS_PROP_INVAL
+};
+
+zfs_prop_t props_long[] = {
+ ZFS_PROP_AVAILABLE,
+ ZFS_PROP_QUOTA,
+ /* ZFS_PROP_RATIO, */
+ ZFS_PROP_REFERENCED,
+ ZFS_PROP_RESERVATION,
+ ZFS_PROP_USED,
+ ZFS_PROP_VOLSIZE,
+ ZFS_PROP_INVAL
+};
+
+zfs_prop_t props_string[] = {
+ ZFS_PROP_ORIGIN,
+ /* ZFS_PROP_TYPE, */
+ ZFS_PROP_INVAL
+};
+
+/*
+ * Static functions
+ */
+
+static jobject
+create_BooleanProperty(JNIEnv *env, zfs_handle_t *zhp, zfs_prop_t prop)
+{
+ jobject propertyObject = NULL;
+ char source[ZFS_MAXNAMELEN];
+ uint64_t value;
+ zfs_source_t srctype;
+
+ int result = zfs_prop_get_numeric(zhp, prop, &value,
+ &srctype, source, sizeof (source));
+
+ if (result == 0) {
+ jclass class_BooleanProperty = (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "BooleanProperty");
+
+ jstring propName = (*env)->NewStringUTF(
+ env, zfs_prop_to_name(prop));
+ jobject propValue = zjni_int_to_boolean(env, value);
+ jboolean readOnly = zfs_prop_readonly(prop) ?
+ JNI_TRUE : JNI_FALSE;
+
+ jmethodID constructor_BooleanProperty;
+
+ if (srctype == ZFS_SRC_INHERITED) {
+
+ jstring propSource = (*env)->NewStringUTF(env, source);
+ constructor_BooleanProperty = (*env)->GetMethodID(
+ env, class_BooleanProperty, "<init>",
+ "(Ljava/lang/String;Ljava/lang/Boolean;ZL"
+ "java/lang/String;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_BooleanProperty,
+ constructor_BooleanProperty,
+ propName, propValue, readOnly, propSource);
+ } else {
+ jobject lineage = zjni_get_lineage(env, srctype);
+
+ constructor_BooleanProperty = (*env)->GetMethodID(
+ env, class_BooleanProperty, "<init>",
+ "(Ljava/lang/String;Ljava/lang/Boolean;ZL"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_BooleanProperty,
+ constructor_BooleanProperty,
+ propName, propValue, readOnly, lineage);
+ }
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+create_ChecksumProperty(JNIEnv *env, zfs_handle_t *zhp)
+{
+ jobject propertyObject = NULL;
+ char propbuf[ZFS_MAXPROPLEN];
+ char source[ZFS_MAXNAMELEN];
+ zfs_source_t srctype;
+
+ int result = zfs_prop_get(zhp, ZFS_PROP_CHECKSUM,
+ propbuf, sizeof (propbuf), &srctype, source, sizeof (source), 1);
+
+ if (result == 0) {
+ jobject propValue = str_to_checksum(env, propbuf);
+
+ if (propValue != NULL) {
+
+ jclass class_ChecksumProperty = (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "ChecksumProperty");
+
+ jmethodID constructor_ChecksumProperty;
+
+ if (srctype == ZFS_SRC_INHERITED) {
+
+ jstring propSource = (*env)->NewStringUTF(env,
+ source);
+ constructor_ChecksumProperty =
+ (*env)->GetMethodID(
+ env, class_ChecksumProperty, "<init>",
+ "(L" ZFSJNI_PACKAGE_DATA
+ "ChecksumProperty$Checksum;Ljava/lang/"
+ "String;)V");
+
+ propertyObject = (*env)->NewObject(env,
+ class_ChecksumProperty,
+ constructor_ChecksumProperty,
+ propValue, propSource);
+
+ } else {
+ jobject lineage =
+ zjni_get_lineage(env, srctype);
+ constructor_ChecksumProperty =
+ (*env)->GetMethodID(
+ env, class_ChecksumProperty, "<init>",
+ "(L" ZFSJNI_PACKAGE_DATA
+ "ChecksumProperty$Checksum;L"
+ ZFSJNI_PACKAGE_DATA
+ "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(env,
+ class_ChecksumProperty,
+ constructor_ChecksumProperty,
+ propValue, lineage);
+ }
+ }
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+create_CompressionProperty(JNIEnv *env, zfs_handle_t *zhp)
+{
+ jobject propertyObject = NULL;
+ char propbuf[ZFS_MAXPROPLEN];
+ char source[ZFS_MAXNAMELEN];
+ zfs_source_t srctype;
+
+ int result = zfs_prop_get(zhp, ZFS_PROP_COMPRESSION,
+ propbuf, sizeof (propbuf), &srctype, source, sizeof (source), 1);
+
+ if (result == 0) {
+ jobject propValue = str_to_compression(env, propbuf);
+
+ if (propValue != NULL) {
+
+ jclass class_CompressionProperty =
+ (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "CompressionProperty");
+
+ jmethodID constructor_CompressionProperty;
+
+ if (srctype == ZFS_SRC_INHERITED) {
+
+ jstring propSource = (*env)->NewStringUTF(env,
+ source);
+ constructor_CompressionProperty =
+ (*env)->GetMethodID(
+ env, class_CompressionProperty,
+ "<init>",
+ "(L" ZFSJNI_PACKAGE_DATA
+ "CompressionProperty$Compression;Ljava/"
+ "lang/String;)V");
+
+ propertyObject = (*env)->NewObject(env,
+ class_CompressionProperty,
+ constructor_CompressionProperty,
+ propValue, propSource);
+ } else {
+ jobject lineage = zjni_get_lineage(env,
+ srctype);
+
+ constructor_CompressionProperty =
+ (*env)->GetMethodID(
+ env, class_CompressionProperty,
+ "<init>",
+ "(L" ZFSJNI_PACKAGE_DATA
+ "CompressionProperty$Compression;L"
+ ZFSJNI_PACKAGE_DATA
+ "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(env,
+ class_CompressionProperty,
+ constructor_CompressionProperty,
+ propValue, lineage);
+ }
+ }
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+create_DateProperty(JNIEnv *env, zfs_handle_t *zhp, zfs_prop_t prop)
+{
+ jobject propertyObject = NULL;
+ char propbuf[ZFS_MAXPROPLEN];
+ char source[ZFS_MAXNAMELEN];
+ zfs_source_t srctype;
+
+ int result = zfs_prop_get(zhp, prop, propbuf, sizeof (propbuf),
+ &srctype, source, sizeof (source), 1);
+
+ if (result == 0) {
+
+ jobject propValue = zjni_str_to_date(env, propbuf);
+ if (propValue != NULL) {
+
+ jclass class_DateProperty = (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "DateProperty");
+
+ jstring propName = (*env)->NewStringUTF(
+ env, zfs_prop_to_name(prop));
+ jboolean readOnly =
+ zfs_prop_readonly(prop) ? JNI_TRUE : JNI_FALSE;
+
+ jmethodID constructor_DateProperty;
+
+ if (srctype == ZFS_SRC_INHERITED) {
+
+ jstring propSource = (*env)->NewStringUTF(env,
+ source);
+ constructor_DateProperty = (*env)->GetMethodID(
+ env, class_DateProperty, "<init>",
+ "(Ljava/lang/String;Ljava/util/Date;ZL"
+ "java/lang/String;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_DateProperty,
+ constructor_DateProperty,
+ propName, propValue, readOnly, propSource);
+ } else {
+ jobject lineage = zjni_get_lineage(env,
+ srctype);
+
+ constructor_DateProperty = (*env)->GetMethodID(
+ env, class_DateProperty, "<init>",
+ "(Ljava/lang/String;Ljava/util/Date;ZL"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_DateProperty,
+ constructor_DateProperty,
+ propName, propValue, readOnly, lineage);
+ }
+ }
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+create_LongProperty(JNIEnv *env, zfs_handle_t *zhp, zfs_prop_t prop)
+{
+ jobject propertyObject = NULL;
+ char propbuf[ZFS_MAXPROPLEN];
+ char source[ZFS_MAXNAMELEN];
+ zfs_source_t srctype;
+
+ int result = zfs_prop_get(zhp, prop, propbuf, sizeof (propbuf),
+ &srctype, source, sizeof (source), 1);
+
+ if (result == 0) {
+
+ jobject propValue = zjni_str_to_long(env, propbuf);
+ if (propValue != NULL) {
+
+ jclass class_LongProperty = (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "LongProperty");
+
+ jstring propName = (*env)->NewStringUTF(
+ env, zfs_prop_to_name(prop));
+ jboolean readOnly =
+ zfs_prop_readonly(prop) ? JNI_TRUE : JNI_FALSE;
+
+ jmethodID constructor_LongProperty;
+
+ if (srctype == ZFS_SRC_INHERITED) {
+
+ jstring propSource =
+ (*env)->NewStringUTF(env, source);
+ constructor_LongProperty = (*env)->GetMethodID(
+ env, class_LongProperty, "<init>",
+ "(Ljava/lang/String;Ljava/lang/Long;ZL"
+ "java/lang/String;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_LongProperty,
+ constructor_LongProperty,
+ propName, propValue, readOnly, propSource);
+ } else {
+ jobject lineage = zjni_get_lineage(env,
+ srctype);
+
+ constructor_LongProperty = (*env)->GetMethodID(
+ env, class_LongProperty, "<init>",
+ "(Ljava/lang/String;Ljava/lang/Long;ZL"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_LongProperty,
+ constructor_LongProperty,
+ propName, propValue, readOnly, lineage);
+ }
+ }
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+create_RecordSizeProperty(JNIEnv *env, zfs_handle_t *zhp)
+{
+ jobject propertyObject = NULL;
+ char propbuf[ZFS_MAXPROPLEN];
+ char source[ZFS_MAXNAMELEN];
+ zfs_source_t srctype;
+
+ int result = zfs_prop_get(zhp, ZFS_PROP_RECORDSIZE,
+ propbuf, sizeof (propbuf), &srctype, source, sizeof (source), 1);
+
+ if (result == 0) {
+
+ jobject propValue = zjni_str_to_long(env, propbuf);
+ if (propValue != NULL) {
+
+ jclass class_RecordSizeProperty = (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "RecordSizeProperty");
+
+ jmethodID constructor_RecordSizeProperty;
+
+ if (srctype == ZFS_SRC_INHERITED) {
+
+ jstring propSource =
+ (*env)->NewStringUTF(env, source);
+ constructor_RecordSizeProperty =
+ (*env)->GetMethodID(
+ env, class_RecordSizeProperty, "<init>",
+ "(Ljava/lang/Long;Ljava/lang/"
+ "String;)V");
+
+ propertyObject = (*env)->NewObject(env,
+ class_RecordSizeProperty,
+ constructor_RecordSizeProperty,
+ propValue, propSource);
+ } else {
+ jobject lineage =
+ zjni_get_lineage(env, srctype);
+
+ constructor_RecordSizeProperty =
+ (*env)->GetMethodID(
+ env, class_RecordSizeProperty, "<init>",
+ "(Ljava/lang/Long;L"
+ ZFSJNI_PACKAGE_DATA
+ "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(env,
+ class_RecordSizeProperty,
+ constructor_RecordSizeProperty,
+ propValue, lineage);
+ }
+ }
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+create_StringProperty(JNIEnv *env, zfs_handle_t *zhp, zfs_prop_t prop)
+{
+ jobject propertyObject = NULL;
+ char propbuf[ZFS_MAXPROPLEN];
+ char source[ZFS_MAXNAMELEN];
+ zfs_source_t srctype;
+
+ int result = zfs_prop_get(zhp, prop, propbuf, sizeof (propbuf),
+ &srctype, source, sizeof (source), 1);
+
+ if (result == 0) {
+ jmethodID constructor_StringProperty;
+
+ jclass class_StringProperty =
+ (*env)->FindClass(env, ZFSJNI_PACKAGE_DATA
+ "StringProperty");
+
+ jstring propName =
+ (*env)->NewStringUTF(env, zfs_prop_to_name(prop));
+
+ jobject propValue = (*env)->NewStringUTF(env, propbuf);
+ jboolean readOnly = zfs_prop_readonly(prop) ?
+ JNI_TRUE : JNI_FALSE;
+
+ if (srctype == ZFS_SRC_INHERITED) {
+
+ jstring propSource = (*env)->NewStringUTF(env, source);
+ constructor_StringProperty = (*env)->GetMethodID(
+ env, class_StringProperty, "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;ZL"
+ "java/lang/String;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_StringProperty,
+ constructor_StringProperty,
+ propName, propValue, readOnly, propSource);
+ } else {
+ jobject lineage = zjni_get_lineage(env, srctype);
+
+ constructor_StringProperty = (*env)->GetMethodID(
+ env, class_StringProperty, "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;ZL"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_StringProperty,
+ constructor_StringProperty,
+ propName, propValue, readOnly, lineage);
+ }
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+create_MountPointProperty(JNIEnv *env, zfs_handle_t *zhp)
+{
+ jobject propertyObject = NULL;
+ char propbuf[ZFS_MAXPROPLEN];
+ char source[ZFS_MAXNAMELEN];
+ zfs_source_t srctype;
+
+ int result = zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
+ propbuf, sizeof (propbuf), &srctype, source, sizeof (source), 1);
+
+ if (result == 0) {
+ jmethodID constructor_MountPointProperty;
+
+ jclass class_MountPointProperty = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "MountPointProperty");
+
+ jobject propValue = (*env)->NewStringUTF(env, propbuf);
+
+ if (srctype == ZFS_SRC_INHERITED) {
+
+ jstring propSource = (*env)->NewStringUTF(env, source);
+ constructor_MountPointProperty = (*env)->GetMethodID(
+ env, class_MountPointProperty, "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;)V");
+
+ propertyObject = (*env)->NewObject(env,
+ class_MountPointProperty,
+ constructor_MountPointProperty,
+ propValue, propSource);
+ } else {
+ jobject lineage = zjni_get_lineage(env, srctype);
+
+ constructor_MountPointProperty = (*env)->GetMethodID(
+ env, class_MountPointProperty, "<init>",
+ "(Ljava/lang/String;L"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(env,
+ class_MountPointProperty,
+ constructor_MountPointProperty,
+ propValue, lineage);
+ }
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+create_ShareNFSProperty(JNIEnv *env, zfs_handle_t *zhp)
+{
+ jobject propertyObject = NULL;
+ char propbuf[ZFS_MAXPROPLEN];
+ char source[ZFS_MAXNAMELEN];
+ zfs_source_t srctype;
+
+ int result = zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
+ propbuf, sizeof (propbuf), &srctype, source, sizeof (source), 1);
+
+ if (result == 0) {
+ jmethodID constructor_ShareNFSProperty;
+
+ jclass class_ShareNFSProperty = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "ShareNFSProperty");
+
+ jobject propValue = (*env)->NewStringUTF(env, propbuf);
+
+ if (srctype == ZFS_SRC_INHERITED) {
+
+ jstring propSource = (*env)->NewStringUTF(env, source);
+ constructor_ShareNFSProperty = (*env)->GetMethodID(
+ env, class_ShareNFSProperty, "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_ShareNFSProperty,
+ constructor_ShareNFSProperty,
+ propValue, propSource);
+ } else {
+ jobject lineage = zjni_get_lineage(env, srctype);
+
+ constructor_ShareNFSProperty = (*env)->GetMethodID(
+ env, class_ShareNFSProperty, "<init>",
+ "(Ljava/lang/String;L"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_ShareNFSProperty,
+ constructor_ShareNFSProperty,
+ propValue, lineage);
+ }
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+str_to_checksum(JNIEnv *env, char *str)
+{
+ jclass class_Checksum = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "ChecksumProperty$Checksum");
+
+ jmethodID method_valueOf = (*env)->GetStaticMethodID(
+ env, class_Checksum, "valueOf",
+ "(Ljava/lang/String;)L"
+ ZFSJNI_PACKAGE_DATA "ChecksumProperty$Checksum;");
+
+ jstring utf = (*env)->NewStringUTF(env, str);
+
+ return (*env)->CallStaticObjectMethod(
+ env, class_Checksum, method_valueOf, utf);
+}
+
+static jobject
+str_to_compression(JNIEnv *env, char *str)
+{
+ jclass class_Compression = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "CompressionProperty$Compression");
+
+ jmethodID method_valueOf = (*env)->GetStaticMethodID(
+ env, class_Compression, "valueOf",
+ "(Ljava/lang/String;)L"
+ ZFSJNI_PACKAGE_DATA "CompressionProperty$Compression;");
+
+ jstring utf = (*env)->NewStringUTF(env, str);
+
+ return (*env)->CallStaticObjectMethod(
+ env, class_Compression, method_valueOf, utf);
+}
+
+/*
+ * Package-private functions
+ */
+jobject
+zjni_get_default_property(JNIEnv *env, zfs_prop_t prop)
+{
+ int i;
+ for (i = 0; props_boolean[i] != ZFS_PROP_INVAL; i++) {
+ if (prop == props_boolean[i]) {
+ return (create_default_BooleanProperty(env, prop));
+ }
+ }
+
+ for (i = 0; props_long[i] != ZFS_PROP_INVAL; i++) {
+ if (prop == props_long[i]) {
+ return (create_default_LongProperty(env, prop));
+ }
+ }
+
+ for (i = 0; props_string[i] != ZFS_PROP_INVAL; i++) {
+ if (prop == props_string[i]) {
+ return (create_default_StringProperty(env, prop));
+ }
+ }
+
+ if (prop == ZFS_PROP_MOUNTPOINT) {
+ return (create_default_MountPointProperty(env));
+ }
+
+ if (prop == ZFS_PROP_SHARENFS) {
+ return (create_default_ShareNFSProperty(env));
+ }
+
+ if (prop == ZFS_PROP_CHECKSUM) {
+ return (create_default_ChecksumProperty(env));
+ }
+
+ if (prop == ZFS_PROP_COMPRESSION) {
+ return (create_default_CompressionProperty(env));
+ }
+
+ if (prop == ZFS_PROP_RECORDSIZE) {
+ return (create_default_RecordSizeProperty(env));
+ }
+
+ return (NULL);
+}
+
+zfs_prop_t
+zjni_get_property_from_name(const char *name)
+{
+ zfs_prop_t prop;
+ for (prop = 0; prop < ZFS_NPROP_VISIBLE; prop++) {
+ if (strcasecmp(name, zfs_prop_to_name(prop)) == 0) {
+ return (prop);
+ }
+ }
+
+ return (ZFS_PROP_INVAL);
+}
+
+jobject
+zjni_get_lineage(JNIEnv *env, zfs_source_t srctype)
+{
+ char *field;
+ jclass class_Lineage;
+ jfieldID id;
+
+ switch (srctype) {
+ case ZFS_SRC_NONE:
+ field = "ZFS_PROP_LINEAGE_NOTINHERITABLE";
+ break;
+
+ case ZFS_SRC_DEFAULT:
+ field = "ZFS_PROP_LINEAGE_DEFAULT";
+ break;
+
+ case ZFS_SRC_LOCAL:
+ field = "ZFS_PROP_LINEAGE_LOCAL";
+ break;
+
+ case ZFS_SRC_TEMPORARY:
+ field = "ZFS_PROP_LINEAGE_TEMPORARY";
+ break;
+
+ default:
+ case ZFS_SRC_INHERITED:
+ field = "ZFS_PROP_LINEAGE_INHERITED";
+ break;
+ }
+
+ class_Lineage = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "Property$Lineage");
+
+ id = (*env)->GetStaticFieldID(env, class_Lineage,
+ field, "L" ZFSJNI_PACKAGE_DATA "Property$Lineage;");
+
+ return (*env)->GetStaticObjectField(env, class_Lineage, id);
+}
+
+jobjectArray
+zjni_get_Dataset_properties(JNIEnv *env, zfs_handle_t *zhp)
+{
+ jobject prop;
+ int i;
+
+ /* Create an array list for the properties */
+ zjni_ArrayList_t proplist_obj = {0};
+ zjni_ArrayList_t *proplist = &proplist_obj;
+ zjni_new_ArrayList(env, proplist);
+
+ for (i = 0; props_boolean[i] != ZFS_PROP_INVAL; i++) {
+ /* Create property and add to list */
+ prop = create_BooleanProperty(env, zhp, props_boolean[i]);
+
+ /* Does this property apply to this object? */
+ if (prop != NULL) {
+
+ (*env)->CallBooleanMethod(
+ env, ((zjni_Object_t *)proplist)->object,
+ ((zjni_Collection_t *)proplist)->method_add, prop);
+ } else {
+
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return (NULL);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr, "Property %s is not appropriate "
+ "for %s\n", zfs_prop_to_name(props_boolean[i]),
+ zfs_get_name(zhp));
+#endif
+ }
+ }
+
+ for (i = 0; props_long[i] != ZFS_PROP_INVAL; i++) {
+ /* Create property and add to list */
+ prop = create_LongProperty(env, zhp, props_long[i]);
+
+ /* Does this property apply to this object? */
+ if (prop != NULL) {
+
+ (*env)->CallBooleanMethod(
+ env, ((zjni_Object_t *)proplist)->object,
+ ((zjni_Collection_t *)proplist)->method_add, prop);
+ } else {
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return (NULL);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr, "Property %s is not appropriate "
+ "for %s\n", zfs_prop_to_name(props_long[i]),
+ zfs_get_name(zhp));
+#endif
+ }
+ }
+
+ for (i = 0; props_string[i] != ZFS_PROP_INVAL; i++) {
+ /* Create property and add to list */
+ prop = create_StringProperty(env, zhp, props_string[i]);
+
+ /* Does this property apply to this object? */
+ if (prop != NULL) {
+
+ (*env)->CallBooleanMethod(
+ env, ((zjni_Object_t *)proplist)->object,
+ ((zjni_Collection_t *)proplist)->method_add, prop);
+ } else {
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return (NULL);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr, "Property %s is not appropriate "
+ "for %s\n", zfs_prop_to_name(props_string[i]),
+ zfs_get_name(zhp));
+#endif
+ }
+ }
+
+ prop = create_MountPointProperty(env, zhp);
+ /* Does this property apply to this object? */
+ if (prop != NULL) {
+
+ (*env)->CallBooleanMethod(env,
+ ((zjni_Object_t *)proplist)->object,
+ ((zjni_Collection_t *)proplist)->method_add, prop);
+ } else {
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return (NULL);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr, "Property %s is not appropriate "
+ "for %s\n", zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
+ zfs_get_name(zhp));
+#endif
+ }
+
+ prop = create_ShareNFSProperty(env, zhp);
+ /* Does this property apply to this object? */
+ if (prop != NULL) {
+
+ (*env)->CallBooleanMethod(env,
+ ((zjni_Object_t *)proplist)->object,
+ ((zjni_Collection_t *)proplist)->method_add, prop);
+ } else {
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return (NULL);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr, "Property %s is not appropriate "
+ "for %s\n", zfs_prop_to_name(ZFS_PROP_SHARENFS),
+ zfs_get_name(zhp));
+#endif
+ }
+
+ prop = create_ChecksumProperty(env, zhp);
+ /* Does this property apply to this object? */
+ if (prop != NULL) {
+
+ (*env)->CallBooleanMethod(env,
+ ((zjni_Object_t *)proplist)->object,
+ ((zjni_Collection_t *)proplist)->method_add, prop);
+ } else {
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return (NULL);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr, "Property %s is not appropriate "
+ "for %s\n", zfs_prop_to_name(ZFS_PROP_CHECKSUM),
+ zfs_get_name(zhp));
+#endif
+ }
+
+ prop = create_CompressionProperty(env, zhp);
+ /* Does this property apply to this object? */
+ if (prop != NULL) {
+
+ (*env)->CallBooleanMethod(env,
+ ((zjni_Object_t *)proplist)->object,
+ ((zjni_Collection_t *)proplist)->method_add, prop);
+ } else {
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return (NULL);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr, "Property %s is not appropriate "
+ "for %s\n", zfs_prop_to_name(ZFS_PROP_COMPRESSION),
+ zfs_get_name(zhp));
+#endif
+ }
+
+ prop = create_RecordSizeProperty(env, zhp);
+ /* Does this property apply to this object? */
+ if (prop != NULL) {
+
+ (*env)->CallBooleanMethod(env,
+ ((zjni_Object_t *)proplist)->object,
+ ((zjni_Collection_t *)proplist)->method_add, prop);
+ } else {
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return (NULL);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr, "Property %s is not appropriate "
+ "for %s\n", zfs_prop_to_name(ZFS_PROP_RECORDSIZE),
+ zfs_get_name(zhp));
+#endif
+ }
+
+ prop = create_DateProperty(env, zhp, ZFS_PROP_CREATION);
+ /* Does this property apply to this object? */
+ if (prop != NULL) {
+
+ (*env)->CallBooleanMethod(env,
+ ((zjni_Object_t *)proplist)->object,
+ ((zjni_Collection_t *)proplist)->method_add, prop);
+ } else {
+ if ((*env)->ExceptionOccurred(env) != NULL) {
+ return (NULL);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr, "Property %s is not appropriate "
+ "for %s\n", zfs_prop_to_name(ZFS_PROP_CREATION),
+ zfs_get_name(zhp));
+#endif
+ }
+
+ return (zjni_Collection_to_array(
+ env, (zjni_Collection_t *)proplist,
+ ZFSJNI_PACKAGE_DATA "Property"));
+}
+
+static jobject
+create_default_BooleanProperty(JNIEnv *env, zfs_prop_t prop)
+{
+ jobject propertyObject = NULL;
+
+ if (!zfs_prop_readonly(prop)) {
+
+ jclass class_BooleanProperty = (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "BooleanProperty");
+
+ jmethodID constructor_BooleanProperty = (*env)->GetMethodID(
+ env, class_BooleanProperty, "<init>",
+ "(Ljava/lang/String;Ljava/lang/Boolean;ZL"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ jstring propName =
+ (*env)->NewStringUTF(env, zfs_prop_to_name(prop));
+ jobject propValue =
+ zjni_int_to_boolean(env, zfs_prop_default_numeric(prop));
+ jboolean readOnly = zfs_prop_readonly(prop) ?
+ JNI_TRUE : JNI_FALSE;
+ jobject lineage = zjni_get_lineage(env, ZFS_SRC_DEFAULT);
+
+ propertyObject = (*env)->NewObject(
+ env, class_BooleanProperty, constructor_BooleanProperty,
+ propName, propValue, readOnly, lineage);
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+create_default_LongProperty(JNIEnv *env, zfs_prop_t prop)
+{
+ jobject propertyObject = NULL;
+
+ if (!zfs_prop_readonly(prop)) {
+
+ jclass class_LongProperty = (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "LongProperty");
+
+ jmethodID constructor_LongProperty = (*env)->GetMethodID(
+ env, class_LongProperty, "<init>",
+ "(Ljava/lang/String;Ljava/lang/Long;ZL"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ jstring propName =
+ (*env)->NewStringUTF(env, zfs_prop_to_name(prop));
+ jobject propValue =
+ zjni_long_to_Long(env, zfs_prop_default_numeric(prop));
+ jboolean readOnly = zfs_prop_readonly(prop)
+ ? JNI_TRUE : JNI_FALSE;
+ jobject lineage = zjni_get_lineage(env, ZFS_SRC_DEFAULT);
+
+ propertyObject = (*env)->NewObject(
+ env, class_LongProperty, constructor_LongProperty,
+ propName, propValue, readOnly, lineage);
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+create_default_StringProperty(JNIEnv *env, zfs_prop_t prop)
+{
+ jobject propertyObject = NULL;
+
+ if (zfs_prop_is_string(prop) && !zfs_prop_readonly(prop)) {
+
+ char propbuf[ZFS_MAXPROPLEN];
+ jclass class_StringProperty;
+ jmethodID constructor_StringProperty;
+ jstring propName;
+ jobject propValue;
+ jboolean readOnly;
+ jobject lineage;
+
+ zfs_prop_default_string(prop, propbuf, sizeof (propbuf));
+
+ class_StringProperty =
+ (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "StringProperty");
+
+ constructor_StringProperty = (*env)->GetMethodID(
+ env, class_StringProperty, "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;ZL"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ propName = (*env)->NewStringUTF(env, zfs_prop_to_name(prop));
+ propValue = (*env)->NewStringUTF(env, propbuf);
+ readOnly = zfs_prop_readonly(prop) ? JNI_TRUE : JNI_FALSE;
+ lineage = zjni_get_lineage(env, ZFS_SRC_DEFAULT);
+
+ propertyObject = (*env)->NewObject(
+ env, class_StringProperty, constructor_StringProperty,
+ propName, propValue, readOnly, lineage);
+ }
+
+ return (propertyObject);
+}
+
+static jobject
+create_default_MountPointProperty(JNIEnv *env)
+{
+ jobject propertyObject = NULL;
+
+ char propbuf[ZFS_MAXPROPLEN];
+ jclass class_MountPointProperty;
+ jmethodID constructor_MountPointProperty;
+ jobject propValue;
+ jobject lineage;
+
+ zfs_prop_default_string(ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf));
+
+ class_MountPointProperty = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "MountPointProperty");
+
+ propValue = (*env)->NewStringUTF(env, propbuf);
+ lineage = zjni_get_lineage(env, ZFS_SRC_DEFAULT);
+
+ constructor_MountPointProperty = (*env)->GetMethodID(
+ env, class_MountPointProperty, "<init>",
+ "(Ljava/lang/String;L"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_MountPointProperty, constructor_MountPointProperty,
+ propValue, lineage);
+
+ return (propertyObject);
+}
+
+static jobject
+create_default_ShareNFSProperty(JNIEnv *env)
+{
+ jobject propertyObject = NULL;
+
+ char propbuf[ZFS_MAXPROPLEN];
+ jclass class_ShareNFSProperty;
+ jmethodID constructor_ShareNFSProperty;
+ jobject propValue;
+ jobject lineage;
+
+ zfs_prop_default_string(ZFS_PROP_SHARENFS, propbuf, sizeof (propbuf));
+
+ class_ShareNFSProperty = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "ShareNFSProperty");
+
+ propValue = (*env)->NewStringUTF(env, propbuf);
+ lineage = zjni_get_lineage(env, ZFS_SRC_DEFAULT);
+
+ constructor_ShareNFSProperty = (*env)->GetMethodID(
+ env, class_ShareNFSProperty, "<init>",
+ "(Ljava/lang/String;L"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(
+ env, class_ShareNFSProperty, constructor_ShareNFSProperty,
+ propValue, lineage);
+
+ return (propertyObject);
+}
+
+static jobject
+create_default_ChecksumProperty(JNIEnv *env)
+{
+ jobject propertyObject = NULL;
+
+ char propbuf[ZFS_MAXPROPLEN];
+ jclass class_ChecksumProperty;
+ jmethodID constructor_ChecksumProperty;
+ jobject propValue;
+ jobject lineage;
+
+ zfs_prop_default_string(ZFS_PROP_CHECKSUM, propbuf, sizeof (propbuf));
+ propValue = str_to_checksum(env, propbuf);
+
+ class_ChecksumProperty = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "ChecksumProperty");
+
+ lineage = zjni_get_lineage(env, ZFS_SRC_DEFAULT);
+
+ constructor_ChecksumProperty = (*env)->GetMethodID(
+ env, class_ChecksumProperty, "<init>",
+ "(L" ZFSJNI_PACKAGE_DATA "ChecksumProperty$Checksum;L"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(env,
+ class_ChecksumProperty, constructor_ChecksumProperty,
+ propValue, lineage);
+
+ return (propertyObject);
+}
+
+static jobject
+create_default_CompressionProperty(JNIEnv *env)
+{
+ jobject propertyObject = NULL;
+
+ char propbuf[ZFS_MAXPROPLEN];
+ jclass class_CompressionProperty;
+ jmethodID constructor_CompressionProperty;
+ jobject propValue;
+ jobject lineage;
+
+ zfs_prop_default_string(
+ ZFS_PROP_COMPRESSION, propbuf, sizeof (propbuf));
+ propValue = str_to_compression(env, propbuf);
+
+ class_CompressionProperty = (*env)->FindClass(
+ env, ZFSJNI_PACKAGE_DATA "CompressionProperty");
+
+ lineage = zjni_get_lineage(env, ZFS_SRC_DEFAULT);
+
+ constructor_CompressionProperty = (*env)->GetMethodID(
+ env, class_CompressionProperty, "<init>",
+ "(L" ZFSJNI_PACKAGE_DATA "CompressionProperty$Compression;L"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ propertyObject = (*env)->NewObject(env,
+ class_CompressionProperty, constructor_CompressionProperty,
+ propValue, lineage);
+
+ return (propertyObject);
+}
+
+static jobject
+create_default_RecordSizeProperty(JNIEnv *env)
+{
+ jclass class_RecordSizeProperty = (*env)->FindClass(env,
+ ZFSJNI_PACKAGE_DATA "RecordSizeProperty");
+
+ jmethodID constructor_RecordSizeProperty = (*env)->GetMethodID(
+ env, class_RecordSizeProperty, "<init>",
+ "(Ljava/lang/Long;L"
+ ZFSJNI_PACKAGE_DATA "Property$Lineage;)V");
+
+ jobject propValue = zjni_long_to_Long(
+ env, zfs_prop_default_numeric(ZFS_PROP_RECORDSIZE));
+
+ jobject lineage = zjni_get_lineage(env, ZFS_SRC_DEFAULT);
+
+ jobject propertyObject = (*env)->NewObject(
+ env, class_RecordSizeProperty, constructor_RecordSizeProperty,
+ propValue, lineage);
+
+ return (propertyObject);
+}
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_property.h b/usr/src/lib/libzfs_jni/common/libzfs_jni_property.h
new file mode 100644
index 0000000000..8ab1c41b6a
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_property.h
@@ -0,0 +1,52 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBZFS_JNI_PROPERTY_H
+#define _LIBZFS_JNI_PROPERTY_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Function prototypes
+ */
+
+#include <jni.h>
+#include <libzfs.h>
+
+jobject zjni_get_default_property(JNIEnv *, zfs_prop_t);
+jobject zjni_get_lineage(JNIEnv *, zfs_source_t);
+jobjectArray zjni_get_Dataset_properties(JNIEnv *, zfs_handle_t *);
+zfs_prop_t zjni_get_property_from_name(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBZFS_JNI_PROPERTY_H */
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_util.c b/usr/src/lib/libzfs_jni/common/libzfs_jni_util.c
new file mode 100644
index 0000000000..4bbf63668e
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_util.c
@@ -0,0 +1,310 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libzfs_jni_util.h"
+#include <strings.h>
+
+/*
+ * Package-private functions
+ */
+
+/*PRINTFLIKE2*/
+void
+zjni_throw_exception(JNIEnv *env, const char *fmt, ...)
+{
+ char error[1024];
+ va_list ap;
+ jclass class_UnsupportedOperationException;
+
+ va_start(ap, fmt);
+ (void) vsnprintf(error, sizeof (error), fmt, ap);
+ va_end(ap);
+
+ class_UnsupportedOperationException =
+ (*env)->FindClass(env, "java/lang/UnsupportedOperationException");
+
+ (*env)->ThrowNew(env, class_UnsupportedOperationException, error);
+}
+
+jstring
+zjni_get_matched_string(JNIEnv *env, char *name, regmatch_t *match)
+{
+ jstring stringUTF = NULL;
+ if (match->rm_so != -1 && match->rm_eo != -1) {
+ char *end = name + match->rm_eo;
+ char tmp = *end;
+ *end = '\0';
+ stringUTF = (*env)->NewStringUTF(env, name + match->rm_so);
+ *end = tmp;
+ }
+ return (stringUTF);
+}
+
+void
+zjni_get_dataset_from_snapshot(const char *snapshot, char *dataset,
+ size_t len)
+{
+ char *at;
+ (void) strncpy(dataset, snapshot, len);
+ at = strchr(dataset, '@');
+ if (at != NULL) {
+ *at = '\0';
+ }
+}
+
+/* Convert a zjni_Collection to a (Java) array */
+jobjectArray
+zjni_Collection_to_array(JNIEnv *env, zjni_Collection_t *list, char *class)
+{
+ /* Get size of zjni_Collection */
+ jint length = (*env)->CallIntMethod(
+ env, ((zjni_Object_t *)list)->object,
+ ((zjni_Collection_t *)list)->method_size);
+
+ /* Create array to hold elements of list */
+ jobjectArray array = (*env)->NewObjectArray(
+ env, length, (*env)->FindClass(env, class), NULL);
+
+ /* Copy list elements to array */
+ return (*env)->CallObjectMethod(env, ((zjni_Object_t *)list)->object,
+ ((zjni_Collection_t *)list)->method_toArray, array);
+}
+
+/* Create a zjni_Collection */
+void
+new_Collection(JNIEnv *env, zjni_Collection_t *collection)
+{
+ zjni_Object_t *object = (zjni_Object_t *)collection;
+
+ collection->method_add = (*env)->GetMethodID(
+ env, object->class, "add", "(Ljava/lang/Object;)Z");
+
+ collection->method_size =
+ (*env)->GetMethodID(env, object->class, "size", "()I");
+
+ collection->method_toArray =
+ (*env)->GetMethodID(env, object->class, "toArray",
+ "([Ljava/lang/Object;)[Ljava/lang/Object;");
+}
+
+/* Create an zjni_ArrayList */
+void
+zjni_new_ArrayList(JNIEnv *env, zjni_ArrayList_t *list)
+{
+ zjni_Object_t *object = (zjni_Object_t *)list;
+
+ if (object->object == NULL) {
+ object->class = (*env)->FindClass(env, "java/util/ArrayList");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object = (*env)->NewObject(
+ env, object->class, object->constructor);
+ }
+
+ new_Collection(env, (zjni_Collection_t *)list);
+}
+
+/* Create an zjni_DatasetSet */
+void
+zjni_new_DatasetSet(JNIEnv *env, zjni_DatasetSet_t *list)
+{
+ zjni_Object_t *object = (zjni_Object_t *)list;
+
+ if (object->object == NULL) {
+ object->class = (*env)->FindClass(
+ env, "com/sun/zfs/common/util/DatasetSet");
+
+ object->constructor =
+ (*env)->GetMethodID(env, object->class, "<init>", "()V");
+
+ object->object = (*env)->NewObject(
+ env, object->class, object->constructor);
+ }
+
+ new_Collection(env, (zjni_Collection_t *)list);
+}
+
+jobject
+zjni_int_to_boolean(JNIEnv *env, uint64_t value)
+{
+ jclass class_Boolean = (*env)->FindClass(
+ env, "java/lang/Boolean");
+
+ jfieldID id = (*env)->GetStaticFieldID(env, class_Boolean,
+ value ? "TRUE" : "FALSE", "Ljava/lang/Boolean;");
+
+ return (*env)->GetStaticObjectField(env, class_Boolean, id);
+}
+
+jobject
+zjni_str_to_long(JNIEnv *env, char *str)
+{
+ jobject value = NULL;
+ jclass class_Long = (*env)->FindClass(env, "java/lang/Long");
+
+ jmethodID method_valueOf = (*env)->GetStaticMethodID(env,
+ class_Long, "valueOf", "(Ljava/lang/String;)Ljava/lang/Long;");
+
+ jstring utf = (*env)->NewStringUTF(env, str);
+
+ /* May throw a NumberFormatException */
+ value = (*env)->CallStaticObjectMethod(
+ env, class_Long, method_valueOf, utf);
+
+ return (value);
+}
+
+jobject
+zjni_long_to_Long(JNIEnv *env, uint64_t value)
+{
+ jclass class_Long = (*env)->FindClass(env, "java/lang/Long");
+
+ jmethodID constructor_Long = (*env)->GetMethodID(
+ env, class_Long, "<init>", "(J)V");
+
+ jobject obj = (*env)->NewObject(
+ env, class_Long, constructor_Long, value);
+
+ return (obj);
+}
+
+jobject
+zjni_str_to_date(JNIEnv *env, char *str)
+{
+ jobject date = NULL;
+ jclass class_Long = (*env)->FindClass(env, "java/lang/Long");
+
+ jmethodID method_parseLong = (*env)->GetStaticMethodID(env,
+ class_Long, "parseLong", "(Ljava/lang/String;)J");
+
+ jstring utf = (*env)->NewStringUTF(env, str);
+ if (utf != NULL) {
+
+ /* May throw a NumberFormatException */
+ jlong time = (*env)->CallStaticLongMethod(
+ env, class_Long, method_parseLong, utf);
+
+ if ((*env)->ExceptionOccurred(env) == NULL) {
+
+ jclass class_Date = (*env)->FindClass(env,
+ "java/util/Date");
+
+ jmethodID constructor_Date = (*env)->GetMethodID(
+ env, class_Date, "<init>", "(J)V");
+
+ /* Date constructor takes epoch milliseconds */
+ time *= 1000;
+
+ date = (*env)->NewObject(
+ env, class_Date, constructor_Date, time);
+ }
+ }
+
+ return (date);
+}
+
+jobjectArray
+zjni_string_array_to_String_array(JNIEnv *env, char **array, int n)
+{
+ int i;
+ jclass class_String = (*env)->FindClass(env, "java/lang/String");
+ jobjectArray jarray =
+ (*env)->NewObjectArray(env, n, class_String, NULL);
+
+ for (i = 0; i < n; i++) {
+ jstring elementUTF = (*env)->NewStringUTF(env, array[i]);
+ (void) (*env)->SetObjectArrayElement(env, jarray, i,
+ elementUTF);
+ }
+
+ return (jarray);
+}
+
+/*
+ * Counts the number of elements in the given NULL-terminated array.
+ * Does not include the terminating NULL in the count.
+ */
+int
+zjni_count_elements(void **array)
+{
+ int i = 0;
+ if (array != NULL) {
+ for (; array[i] != NULL; i++);
+ }
+ return (i);
+}
+
+/*
+ * Get a handle to the next nvpair with the specified name and data
+ * type in the list following the given nvpair.
+ *
+ * This function is needed because the nvlist_lookup_* routines can
+ * only be used with nvlists allocated with NV_UNIQUE_NAME or
+ * NV_UNIQUE_NAME_TYPE, ie. lists of unique name/value pairs.
+ *
+ * Some variation of this function will likely appear in the libnvpair
+ * library per 4981923.
+ *
+ * @param nvl
+ * the nvlist_t to search
+ *
+ * @param name
+ * the string key for the pair to find in the list, or
+ * NULL to match any name
+ *
+ * @param type
+ * the data type for the pair to find in the list, or
+ * DATA_TYPE_UNKNOWN to match any type
+ *
+ * @param nvp
+ * the pair to search from in the list, or NULL to search
+ * from the beginning of the list
+ *
+ * @return the next nvpair in the list matching the given
+ * criteria, or NULL if no matching nvpair is found
+ */
+nvpair_t *
+zjni_nvlist_walk_nvpair(nvlist_t *nvl, const char *name, data_type_t type,
+ nvpair_t *nvp)
+{
+ /* For each nvpair in the list following nvp... */
+ while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+
+ /* Does this pair's name match the given name? */
+ if ((name == NULL || strcmp(nvpair_name(nvp), name) == 0) &&
+
+ /* Does this pair's type match the given type? */
+ (type == DATA_TYPE_UNKNOWN || type == nvpair_type(nvp))) {
+ return (nvp);
+ }
+ }
+
+ return (NULL);
+}
diff --git a/usr/src/lib/libzfs_jni/common/libzfs_jni_util.h b/usr/src/lib/libzfs_jni/common/libzfs_jni_util.h
new file mode 100644
index 0000000000..48c60f34e5
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/libzfs_jni_util.h
@@ -0,0 +1,100 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBZFS_JNI_UTIL_H
+#define _LIBZFS_JNI_UTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <jni.h>
+#include <regex.h>
+#include <libnvpair.h>
+
+/*
+ * Constants
+ */
+
+#define ZFSJNI_PACKAGE_DATA "com/sun/zfs/common/model/"
+
+/*
+ * Types
+ */
+
+typedef struct zjni_Object {
+ jclass class;
+ jobject object;
+ jmethodID constructor;
+} zjni_Object_t;
+
+typedef struct zjni_Collection {
+ zjni_Object_t super;
+
+ jmethodID method_add;
+ jmethodID method_size;
+ jmethodID method_toArray;
+} zjni_Collection_t;
+
+typedef struct zjni_ArrayList {
+ zjni_Collection_t super;
+} zjni_ArrayList_t;
+
+typedef struct zjni_DatasetSet {
+ zjni_Collection_t super;
+} zjni_DatasetSet_t;
+
+typedef struct zjni_ArrayCallbackData {
+ JNIEnv *env;
+ zjni_Collection_t *list;
+} zjni_ArrayCallbackData_t;
+
+/*
+ * Function prototypes
+ */
+
+void zjni_throw_exception(JNIEnv *, const char *, ...);
+jstring zjni_get_matched_string(JNIEnv *, char *, regmatch_t *);
+void zjni_get_dataset_from_snapshot(const char *, char *, size_t);
+jobjectArray zjni_Collection_to_array(JNIEnv *, zjni_Collection_t *, char *);
+void zjni_new_ArrayList(JNIEnv *, zjni_ArrayList_t *);
+void zjni_new_DatasetSet(JNIEnv *, zjni_DatasetSet_t *);
+jobject zjni_int_to_boolean(JNIEnv *, uint64_t);
+jobject zjni_str_to_long(JNIEnv *, char *);
+jobject zjni_long_to_Long(JNIEnv *, uint64_t);
+jobject zjni_str_to_date(JNIEnv *, char *);
+jobjectArray zjni_string_array_to_String_array(JNIEnv *, char **, int);
+int zjni_count_elements(void **);
+nvpair_t *zjni_nvlist_walk_nvpair(
+ nvlist_t *, const char *, data_type_t, nvpair_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBZFS_JNI_UTIL_H */
diff --git a/usr/src/lib/libzfs_jni/common/llib-lzfs_jni b/usr/src/lib/libzfs_jni/common/llib-lzfs_jni
new file mode 100644
index 0000000000..3e7aa11d57
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/common/llib-lzfs_jni
@@ -0,0 +1,33 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+
+#include <libzfs_jni_main.h>
+#include <libzfs_jni_diskmgt.h>
diff --git a/usr/src/lib/libzfs_jni/i386/Makefile b/usr/src/lib/libzfs_jni/i386/Makefile
new file mode 100644
index 0000000000..cd02883abf
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libzfs_jni/sparc/Makefile b/usr/src/lib/libzfs_jni/sparc/Makefile
new file mode 100644
index 0000000000..cd02883abf
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/sparc/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libzfs_jni/sparcv9/Makefile b/usr/src/lib/libzfs_jni/sparcv9/Makefile
new file mode 100644
index 0000000000..44075ed1bd
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/sparcv9/Makefile
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libzfs_jni/spec/Makefile b/usr/src/lib/libzfs_jni/spec/Makefile
new file mode 100644
index 0000000000..2cb984bfc9
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/spec/Makefile
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include $(SRC)/lib/Makefile.spec.arch
diff --git a/usr/src/lib/libzfs_jni/spec/Makefile.targ b/usr/src/lib/libzfs_jni/spec/Makefile.targ
new file mode 100644
index 0000000000..0a844e13a9
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/spec/Makefile.targ
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+LIBRARY= libzfs_jni.a
+VERS= .1
+
+OBJECTS= libzfs_jni.o
diff --git a/usr/src/lib/libzfs_jni/spec/amd64/Makefile b/usr/src/lib/libzfs_jni/spec/amd64/Makefile
new file mode 100644
index 0000000000..98db1f9271
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/spec/amd64/Makefile
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.lib.64
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB64)
diff --git a/usr/src/lib/libzfs_jni/spec/i386/Makefile b/usr/src/lib/libzfs_jni/spec/i386/Makefile
new file mode 100644
index 0000000000..6256c68c81
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/spec/i386/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libzfs_jni/spec/libzfs_jni.spec b/usr/src/lib/libzfs_jni/spec/libzfs_jni.spec
new file mode 100644
index 0000000000..8531b0f407
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/spec/libzfs_jni.spec
@@ -0,0 +1,113 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getPools
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getPool
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getFileSystems
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getFileSystem
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getVolumes
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getVolume
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getSnapshots
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getSnapshot
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getDatasets
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getDataset
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevice
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevices__Ljava_lang_String_2
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getVirtualDevices__Ljava_lang_String_2J
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getAvailableDisks
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getDependents
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getPropertyDefault
+version SUNWprivate_1.1
+end
+
+function Java_com_sun_zfs_common_model_SystemDataModel_getValidPropertyNames
+version SUNWprivate_1.1
+end
+
+function dmgt_avail_disk_iter
+version SUNWprivate_1.1
+end
+
+function dmgt_free_disk
+version SUNWprivate_1.1
+end
+
+function dmgt_free_slice
+version SUNWprivate_1.1
+end
+
+function dmgt_set_error_handler
+version SUNWprivate_1.1
+end
diff --git a/usr/src/lib/libzfs_jni/spec/sparc/Makefile b/usr/src/lib/libzfs_jni/spec/sparc/Makefile
new file mode 100644
index 0000000000..6256c68c81
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/spec/sparc/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libzfs_jni/spec/sparcv9/Makefile b/usr/src/lib/libzfs_jni/spec/sparcv9/Makefile
new file mode 100644
index 0000000000..98db1f9271
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/spec/sparcv9/Makefile
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.lib.64
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB64)
diff --git a/usr/src/lib/libzfs_jni/spec/versions b/usr/src/lib/libzfs_jni/spec/versions
new file mode 100644
index 0000000000..5b067849f5
--- /dev/null
+++ b/usr/src/lib/libzfs_jni/spec/versions
@@ -0,0 +1,43 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+sparc {
+ SUNWprivate_1.1;
+}
+sparcv9 {
+ SUNWprivate_1.1;
+}
+i386 {
+ SUNWprivate_1.1;
+}
+amd64 {
+ SUNWprivate_1.1;
+}
diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c
index b6a6a59cee..416be740e3 100644
--- a/usr/src/lib/libzonecfg/common/libzonecfg.c
+++ b/usr/src/lib/libzonecfg/common/libzonecfg.c
@@ -67,6 +67,7 @@
#define DTD_ELEM_RCTL (const xmlChar *) "rctl"
#define DTD_ELEM_RCTLVALUE (const xmlChar *) "rctl-value"
#define DTD_ELEM_ZONE (const xmlChar *) "zone"
+#define DTD_ELEM_DATASET (const xmlChar *) "dataset"
#define DTD_ATTR_ACTION (const xmlChar *) "action"
#define DTD_ATTR_ADDRESS (const xmlChar *) "address"
@@ -1907,6 +1908,7 @@ static const char *standard_devs[] = {
#endif
"cpu/self/cpuid",
"dtrace/helper",
+ "zfs",
NULL
};
@@ -3518,3 +3520,176 @@ zonecfg_valid_rctl(const char *name, const rctlblk_t *rctlblk)
return (B_TRUE);
}
+
+static int
+zonecfg_add_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr)
+{
+ xmlNodePtr newnode, cur = handle->zone_dh_cur;
+ int err;
+
+ newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DATASET, NULL);
+ if ((err = newprop(newnode, DTD_ATTR_NAME,
+ tabptr->zone_dataset_name)) != Z_OK)
+ return (err);
+ return (Z_OK);
+}
+
+int
+zonecfg_add_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
+{
+ int err;
+
+ if (tabptr == NULL)
+ return (Z_INVAL);
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ if ((err = zonecfg_add_ds_core(handle, tabptr)) != Z_OK)
+ return (err);
+
+ return (Z_OK);
+}
+
+static int
+zonecfg_delete_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr)
+{
+ xmlNodePtr cur = handle->zone_dh_cur;
+
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ if (xmlStrcmp(cur->name, DTD_ELEM_DATASET))
+ continue;
+
+ if (match_prop(cur, DTD_ATTR_NAME,
+ tabptr->zone_dataset_name)) {
+ xmlUnlinkNode(cur);
+ xmlFreeNode(cur);
+ return (Z_OK);
+ }
+ }
+ return (Z_NO_RESOURCE_ID);
+}
+
+int
+zonecfg_delete_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
+{
+ int err;
+
+ if (tabptr == NULL)
+ return (Z_INVAL);
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ if ((err = zonecfg_delete_ds_core(handle, tabptr)) != Z_OK)
+ return (err);
+
+ return (Z_OK);
+}
+
+int
+zonecfg_modify_ds(
+ zone_dochandle_t handle,
+ struct zone_dstab *oldtabptr,
+ struct zone_dstab *newtabptr)
+{
+ int err;
+
+ if (oldtabptr == NULL || newtabptr == NULL)
+ return (Z_INVAL);
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ if ((err = zonecfg_delete_ds_core(handle, oldtabptr)) != Z_OK)
+ return (err);
+
+ if ((err = zonecfg_add_ds_core(handle, newtabptr)) != Z_OK)
+ return (err);
+
+ return (Z_OK);
+}
+
+int
+zonecfg_lookup_ds(zone_dochandle_t handle, struct zone_dstab *tabptr)
+{
+ xmlNodePtr cur, firstmatch;
+ int err;
+ char dataset[MAXNAMELEN];
+
+ if (tabptr == NULL)
+ return (Z_INVAL);
+
+ if ((err = operation_prep(handle)) != Z_OK)
+ return (err);
+
+ cur = handle->zone_dh_cur;
+ firstmatch = NULL;
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ if (xmlStrcmp(cur->name, DTD_ELEM_DATASET))
+ continue;
+ if (strlen(tabptr->zone_dataset_name) > 0) {
+ if ((fetchprop(cur, DTD_ATTR_NAME, dataset,
+ sizeof (dataset)) == Z_OK) &&
+ (strcmp(tabptr->zone_dataset_name,
+ dataset) == 0)) {
+ if (firstmatch == NULL)
+ firstmatch = cur;
+ else
+ return (Z_INSUFFICIENT_SPEC);
+ }
+ }
+ }
+ if (firstmatch == NULL)
+ return (Z_NO_RESOURCE_ID);
+
+ cur = firstmatch;
+
+ if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_dataset_name,
+ sizeof (tabptr->zone_dataset_name))) != Z_OK)
+ return (err);
+
+ return (Z_OK);
+}
+
+int
+zonecfg_setdsent(zone_dochandle_t handle)
+{
+ return (zonecfg_setent(handle));
+}
+
+int
+zonecfg_getdsent(zone_dochandle_t handle, struct zone_dstab *tabptr)
+{
+ xmlNodePtr cur;
+ int err;
+
+ if (handle == NULL)
+ return (Z_INVAL);
+
+ if ((cur = handle->zone_dh_cur) == NULL)
+ return (Z_NO_ENTRY);
+
+ for (; cur != NULL; cur = cur->next)
+ if (!xmlStrcmp(cur->name, DTD_ELEM_DATASET))
+ break;
+ if (cur == NULL) {
+ handle->zone_dh_cur = handle->zone_dh_top;
+ return (Z_NO_ENTRY);
+ }
+
+ if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_dataset_name,
+ sizeof (tabptr->zone_dataset_name))) != Z_OK) {
+ handle->zone_dh_cur = handle->zone_dh_top;
+ return (err);
+ }
+
+ handle->zone_dh_cur = cur->next;
+ return (Z_OK);
+}
+
+int
+zonecfg_enddsent(zone_dochandle_t handle)
+{
+ return (zonecfg_endent(handle));
+}
diff --git a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1
index d10bb5283d..d15fe65fcd 100644
--- a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1
+++ b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1
@@ -1,9 +1,6 @@
<?xml version='1.0' encoding='UTF-8' ?>
<!--
- Copyright 2005 Sun Microsystems, Inc. All rights reserved.
- Use is subject to license terms.
-
CDDL HEADER START
The contents of this file are subject to the terms of the
@@ -24,6 +21,9 @@
CDDL HEADER END
+ Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
ident "%Z%%M% %I% %E% SMI"
-->
@@ -69,7 +69,11 @@
#REQUIRED
value CDATA #REQUIRED>
-<!ELEMENT zone (filesystem | inherited-pkg-dir | network | device | rctl | attr)*>
+<!ELEMENT dataset EMPTY>
+
+<!ATTLIST dataset name CDATA #REQUIRED>
+
+<!ELEMENT zone (filesystem | inherited-pkg-dir | network | device | rctl | attr | dataset)*>
<!ATTLIST zone name CDATA #REQUIRED
zonepath CDATA #REQUIRED
diff --git a/usr/src/lib/libzonecfg/spec/libzonecfg.spec b/usr/src/lib/libzonecfg/spec/libzonecfg.spec
index dbc9f5da9a..a0cdb3db98 100644
--- a/usr/src/lib/libzonecfg/spec/libzonecfg.spec
+++ b/usr/src/lib/libzonecfg/spec/libzonecfg.spec
@@ -279,6 +279,30 @@ declaration int zonecfg_lookup_attr(zone_dochandle_t, struct zone_attrtab *)
version SUNWprivate_1.1
end
+function zonecfg_add_ds
+include <libzonecfg.h>
+declaration int zonecfg_add_ds(zone_dochandle_t, struct zone_dstab *)
+version SUNWprivate_1.1
+end
+
+function zonecfg_delete_ds
+include <libzonecfg.h>
+declaration int zonecfg_delete_ds(zone_dochandle_t, struct zone_dstab *)
+version SUNWprivate_1.1
+end
+
+function zonecfg_modify_ds
+include <libzonecfg.h>
+declaration int zonecfg_modify_ds(zone_dochandle_t, struct zone_dstab *, struct zone_dstab *)
+version SUNWprivate_1.1
+end
+
+function zonecfg_lookup_ds
+include <libzonecfg.h>
+declaration int zonecfg_lookup_ds(zone_dochandle_t, struct zone_dstab *)
+version SUNWprivate_1.1
+end
+
function zonecfg_get_attr_boolean
include <libzonecfg.h>
declaration int zonecfg_get_attr_boolean(const struct zone_attrtab *, boolean_t *)
@@ -459,6 +483,24 @@ declaration int zonecfg_endrctlent(zone_dochandle_t)
version SUNWprivate_1.1
end
+function zonecfg_setdsent
+include <libzonecfg.h>
+declaration int zonecfg_setdsent(zone_dochandle_t);
+version SUNWprivate_1.1
+end
+
+function zonecfg_getdsent
+include <libzonecfg.h>
+declaration int zonecfg_getdsent(zone_dochandle_t, struct zone_dstab *)
+version SUNWprivate_1.1
+end
+
+function zonecfg_enddsent
+include <libzonecfg.h>
+declaration int zonecfg_enddsent(zone_dochandle_t)
+version SUNWprivate_1.1
+end
+
function zonecfg_destroy
include <libzonecfg.h>
declaration int zonecfg_destroy(const char *, boolean_t)
diff --git a/usr/src/lib/libzpool/Makefile b/usr/src/lib/libzpool/Makefile
new file mode 100644
index 0000000000..f77568c08a
--- /dev/null
+++ b/usr/src/lib/libzpool/Makefile
@@ -0,0 +1,51 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.lib
+
+$(INTEL_BLD)SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(MACH) $(MACH64): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/libzpool/Makefile.com b/usr/src/lib/libzpool/Makefile.com
new file mode 100644
index 0000000000..c39f5f08ed
--- /dev/null
+++ b/usr/src/lib/libzpool/Makefile.com
@@ -0,0 +1,83 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+
+LIBRARY= libzpool.a
+VERS= .1
+
+include ../../../uts/common/Makefile.files
+KERNEL_OBJS = kernel.o taskq.o util.o
+LIST_OBJS = list.o
+
+OBJECTS=$(ZFS_COMMON_OBJS) $(ZFS_SHARED_OBJS) $(KERNEL_OBJS) $(LIST_OBJS)
+
+# include library definitions
+include ../../Makefile.lib
+
+ZFS_COMMON_SRCS= $(ZFS_COMMON_OBJS:%.o=../../../uts/common/fs/zfs/%.c)
+SHARED_SRCS= $(ZFS_SHARED_OBJS:%.o=../../../common/zfs/%.c)
+KERNEL_SRCS= $(KERNEL_OBJS:%.o=../common/%.c)
+LIST_SRCS= $(LIST_OBJS:%.o=../../../uts/common/os/%.c)
+
+SRCS=$(ZFS_COMMON_SRCS) $(KERNEL_SRCS) $(LIST_SRCS)
+SRCDIR= ../common
+
+LIBS += $(LINTLIB)
+
+INCS += -I../common
+INCS += -I../../../uts/common/fs/zfs
+INCS += -I../../../common/zfs
+
+$(LINTLIB) := SRCS= $(SRCDIR)/$(LINTSRC)
+
+C99MODE= -xc99=%all
+C99LMODE= -Xc99=%all
+
+CFLAGS += -g $(CCVERBOSE) $(CNOGLOBAL)
+CFLAGS64 += -g $(CCVERBOSE) $(CNOGLOBAL)
+LDLIBS += -lumem -lavl -lnvpair -lc
+CPPFLAGS += $(INCS)
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: $(LINTLIB)
+
+include ../../Makefile.targ
+
+objs/%.o pics/%.o: ../../../uts/common/fs/zfs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+objs/%.o pics/%.o: ../../../common/zfs/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+objs/%.o pics/%.o: ../../../uts/common/os/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
diff --git a/usr/src/lib/libzpool/amd64/Makefile b/usr/src/lib/libzpool/amd64/Makefile
new file mode 100644
index 0000000000..f484bb496d
--- /dev/null
+++ b/usr/src/lib/libzpool/amd64/Makefile
@@ -0,0 +1,32 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT)
diff --git a/usr/src/lib/libzpool/common/kernel.c b/usr/src/lib/libzpool/common/kernel.c
new file mode 100644
index 0000000000..83155b480f
--- /dev/null
+++ b/usr/src/lib/libzpool/common/kernel.c
@@ -0,0 +1,675 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <sys/zfs_context.h>
+#include <poll.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/spa.h>
+#include <sys/processor.h>
+
+/*
+ * Emulation of kernel services in userland.
+ */
+
+uint64_t physmem;
+vnode_t *rootdir = (vnode_t *)0xabcd1234;
+
+/*
+ * =========================================================================
+ * threads
+ * =========================================================================
+ */
+/*ARGSUSED*/
+kthread_t *
+zk_thread_create(void (*func)(), void *arg)
+{
+ thread_t tid;
+
+ VERIFY(thr_create(0, 0, (void *(*)(void *))func, arg, THR_DETACHED,
+ &tid) == 0);
+
+ return ((void *)(uintptr_t)tid);
+}
+
+/*
+ * =========================================================================
+ * mutexes
+ * =========================================================================
+ */
+void
+zmutex_init(kmutex_t *mp)
+{
+ mp->m_owner = NULL;
+ (void) _mutex_init(&mp->m_lock, USYNC_THREAD, NULL);
+}
+
+void
+zmutex_destroy(kmutex_t *mp)
+{
+ ASSERT(mp->m_owner == NULL);
+ (void) _mutex_destroy(&(mp)->m_lock);
+ mp->m_owner = (void *)-1UL;
+}
+
+void
+mutex_enter(kmutex_t *mp)
+{
+ ASSERT(mp->m_owner != (void *)-1UL);
+ ASSERT(mp->m_owner != curthread);
+ (void) mutex_lock(&mp->m_lock);
+ ASSERT(mp->m_owner == NULL);
+ mp->m_owner = curthread;
+}
+
+int
+mutex_tryenter(kmutex_t *mp)
+{
+ ASSERT(mp->m_owner != (void *)-1UL);
+ if (0 == mutex_trylock(&mp->m_lock)) {
+ ASSERT(mp->m_owner == NULL);
+ mp->m_owner = curthread;
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
+void
+mutex_exit(kmutex_t *mp)
+{
+ ASSERT(mutex_owner(mp) == curthread);
+ mp->m_owner = NULL;
+ (void) mutex_unlock(&mp->m_lock);
+}
+
+void *
+mutex_owner(kmutex_t *mp)
+{
+ return (mp->m_owner);
+}
+
+/*
+ * =========================================================================
+ * rwlocks
+ * =========================================================================
+ */
+/*ARGSUSED*/
+void
+rw_init(krwlock_t *rwlp, char *name, int type, void *arg)
+{
+ rwlock_init(&rwlp->rw_lock, USYNC_THREAD, NULL);
+ rwlp->rw_owner = NULL;
+}
+
+void
+rw_destroy(krwlock_t *rwlp)
+{
+ rwlock_destroy(&rwlp->rw_lock);
+ rwlp->rw_owner = (void *)-1UL;
+}
+
+void
+rw_enter(krwlock_t *rwlp, krw_t rw)
+{
+ ASSERT(!RW_LOCK_HELD(rwlp));
+ ASSERT(rwlp->rw_owner != (void *)-1UL);
+ ASSERT(rwlp->rw_owner != curthread);
+
+ if (rw == RW_READER)
+ (void) rw_rdlock(&rwlp->rw_lock);
+ else
+ (void) rw_wrlock(&rwlp->rw_lock);
+
+ rwlp->rw_owner = curthread;
+}
+
+void
+rw_exit(krwlock_t *rwlp)
+{
+ ASSERT(rwlp->rw_owner != (void *)-1UL);
+
+ rwlp->rw_owner = NULL;
+ (void) rw_unlock(&rwlp->rw_lock);
+}
+
+int
+rw_tryenter(krwlock_t *rwlp, krw_t rw)
+{
+ int rv;
+
+ ASSERT(rwlp->rw_owner != (void *)-1UL);
+
+ if (rw == RW_READER)
+ rv = rw_tryrdlock(&rwlp->rw_lock);
+ else
+ rv = rw_trywrlock(&rwlp->rw_lock);
+
+ if (rv == 0) {
+ rwlp->rw_owner = curthread;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+rw_tryupgrade(krwlock_t *rwlp)
+{
+ ASSERT(rwlp->rw_owner != (void *)-1UL);
+
+ return (0);
+}
+
+/*
+ * =========================================================================
+ * condition variables
+ * =========================================================================
+ */
+/*ARGSUSED*/
+void
+cv_init(kcondvar_t *cv, char *name, int type, void *arg)
+{
+ (void) cond_init(cv, type, NULL);
+}
+
+void
+cv_destroy(kcondvar_t *cv)
+{
+ (void) cond_destroy(cv);
+}
+
+void
+cv_wait(kcondvar_t *cv, kmutex_t *mp)
+{
+ ASSERT(mutex_owner(mp) == curthread);
+ mp->m_owner = NULL;
+ (void) cond_wait(cv, &mp->m_lock);
+ mp->m_owner = curthread;
+}
+
+clock_t
+cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime)
+{
+ int error;
+ timestruc_t ts;
+ clock_t delta;
+
+top:
+ delta = abstime - lbolt;
+ if (delta <= 0)
+ return (-1);
+
+ ts.tv_sec = delta / hz;
+ ts.tv_nsec = (delta % hz) * (NANOSEC / hz);
+
+ ASSERT(mutex_owner(mp) == curthread);
+ mp->m_owner = NULL;
+ error = cond_reltimedwait(cv, &mp->m_lock, &ts);
+ mp->m_owner = curthread;
+
+ if (error == ETIME)
+ return (-1);
+
+ if (error == EINTR)
+ goto top;
+
+ ASSERT(error == 0);
+
+ return (1);
+}
+
+void
+cv_signal(kcondvar_t *cv)
+{
+ (void) cond_signal(cv);
+}
+
+void
+cv_broadcast(kcondvar_t *cv)
+{
+ (void) cond_broadcast(cv);
+}
+
+/*
+ * =========================================================================
+ * vnode operations
+ * =========================================================================
+ */
+/*
+ * Note: for the xxxat() versions of these functions, we assume that the
+ * starting vp is always rootdir (which is true for spa_directory.c, the only
+ * ZFS consumer of these interfaces). We assert this is true, and then emulate
+ * them by adding '/' in front of the path.
+ */
+
+/*ARGSUSED*/
+int
+vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3)
+{
+ int fd;
+ vnode_t *vp;
+ int old_umask;
+ char realpath[MAXPATHLEN];
+ struct stat64 st;
+
+ /*
+ * If we're accessing a real disk from userland, we need to use
+ * the character interface to avoid caching. This is particularly
+ * important if we're trying to look at a real in-kernel storage
+ * pool from userland, e.g. via zdb, because otherwise we won't
+ * see the changes occurring under the segmap cache.
+ * On the other hand, the stupid character device returns zero
+ * for its size. So -- gag -- we open the block device to get
+ * its size, and remember it for subsequent VOP_GETATTR().
+ */
+ if (strncmp(path, "/dev/", 5) == 0) {
+ char *dsk;
+ fd = open64(path, O_RDONLY);
+ if (fd == -1)
+ return (errno);
+ if (fstat64(fd, &st) == -1) {
+ close(fd);
+ return (errno);
+ }
+ close(fd);
+ (void) sprintf(realpath, "%s", path);
+ dsk = strstr(path, "/dsk/");
+ if (dsk != NULL)
+ (void) sprintf(realpath + (dsk - path) + 1, "r%s",
+ dsk + 1);
+ } else {
+ (void) sprintf(realpath, "%s", path);
+ if (!(flags & FCREAT) && stat64(realpath, &st) == -1)
+ return (errno);
+ }
+
+ if (flags & FCREAT)
+ old_umask = umask(0);
+
+ /*
+ * The construct 'flags - FREAD' conveniently maps combinations of
+ * FREAD and FWRITE to the corresponding O_RDONLY, O_WRONLY, and O_RDWR.
+ */
+ fd = open64(realpath, flags - FREAD, mode);
+
+ if (flags & FCREAT)
+ (void) umask(old_umask);
+
+ if (fd == -1)
+ return (errno);
+
+ if (fstat64(fd, &st) == -1) {
+ close(fd);
+ return (errno);
+ }
+
+ (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ *vpp = vp = umem_zalloc(sizeof (vnode_t), UMEM_NOFAIL);
+
+ vp->v_fd = fd;
+ vp->v_size = st.st_size;
+ vp->v_path = spa_strdup(path);
+
+ return (0);
+}
+
+int
+vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2,
+ int x3, vnode_t *startvp)
+{
+ char *realpath = umem_alloc(strlen(path) + 2, UMEM_NOFAIL);
+ int ret;
+
+ ASSERT(startvp == rootdir);
+ (void) sprintf(realpath, "/%s", path);
+
+ ret = vn_open(realpath, x1, flags, mode, vpp, x2, x3);
+
+ umem_free(realpath, strlen(path) + 2);
+
+ return (ret);
+}
+
+/*ARGSUSED*/
+int
+vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset,
+ int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp)
+{
+ ssize_t iolen, split;
+
+ if (uio == UIO_READ) {
+ iolen = pread64(vp->v_fd, addr, len, offset);
+ } else {
+ /*
+ * To simulate partial disk writes, we split writes into two
+ * system calls so that the process can be killed in between.
+ */
+ split = (len > 0 ? rand() % len : 0);
+ iolen = pwrite64(vp->v_fd, addr, split, offset);
+ iolen += pwrite64(vp->v_fd, (char *)addr + split,
+ len - split, offset + split);
+ }
+
+ if (iolen == -1)
+ return (errno);
+ if (residp)
+ *residp = len - iolen;
+ else if (iolen != len)
+ return (EIO);
+ return (0);
+}
+
+void
+vn_close(vnode_t *vp)
+{
+ close(vp->v_fd);
+ spa_strfree(vp->v_path);
+ umem_free(vp, sizeof (vnode_t));
+}
+
+#ifdef ZFS_DEBUG
+
+/*
+ * =========================================================================
+ * Figure out which debugging statements to print
+ * =========================================================================
+ */
+
+static char *dprintf_string;
+static int dprintf_print_all;
+
+int
+dprintf_find_string(const char *string)
+{
+ char *tmp_str = dprintf_string;
+ int len = strlen(string);
+
+ /*
+ * Find out if this is a string we want to print.
+ * String format: file1.c,function_name1,file2.c,file3.c
+ */
+
+ while (tmp_str != NULL) {
+ if (strncmp(tmp_str, string, len) == 0 &&
+ (tmp_str[len] == ',' || tmp_str[len] == '\0'))
+ return (1);
+ tmp_str = strchr(tmp_str, ',');
+ if (tmp_str != NULL)
+ tmp_str++; /* Get rid of , */
+ }
+ return (0);
+}
+
+void
+dprintf_setup(int *argc, char **argv)
+{
+ int i, j;
+
+ /*
+ * Debugging can be specified two ways: by setting the
+ * environment variable ZFS_DEBUG, or by including a
+ * "debug=..." argument on the command line. The command
+ * line setting overrides the environment variable.
+ */
+
+ for (i = 1; i < *argc; i++) {
+ int len = strlen("debug=");
+ /* First look for a command line argument */
+ if (strncmp("debug=", argv[i], len) == 0) {
+ dprintf_string = argv[i] + len;
+ /* Remove from args */
+ for (j = i; j < *argc; j++)
+ argv[j] = argv[j+1];
+ argv[j] = NULL;
+ (*argc)--;
+ }
+ }
+
+ if (dprintf_string == NULL) {
+ /* Look for ZFS_DEBUG environment variable */
+ dprintf_string = getenv("ZFS_DEBUG");
+ }
+
+ /*
+ * Are we just turning on all debugging?
+ */
+ if (dprintf_find_string("on"))
+ dprintf_print_all = 1;
+}
+
+/*
+ * =========================================================================
+ * debug printfs
+ * =========================================================================
+ */
+void
+__dprintf(const char *file, const char *func, int line, const char *fmt, ...)
+{
+ const char *newfile;
+ va_list adx;
+
+ /*
+ * Get rid of annoying "../common/" prefix to filename.
+ */
+ newfile = strrchr(file, '/');
+ if (newfile != NULL) {
+ newfile = newfile + 1; /* Get rid of leading / */
+ } else {
+ newfile = file;
+ }
+
+ if (dprintf_print_all ||
+ dprintf_find_string(newfile) ||
+ dprintf_find_string(func)) {
+ /* Print out just the function name if requested */
+ flockfile(stdout);
+ if (dprintf_find_string("pid"))
+ (void) printf("%d ", getpid());
+ if (dprintf_find_string("tid"))
+ (void) printf("%u ", thr_self());
+ if (dprintf_find_string("cpu"))
+ (void) printf("%u ", getcpuid());
+ if (dprintf_find_string("time"))
+ (void) printf("%llu ", gethrtime());
+ if (dprintf_find_string("long"))
+ (void) printf("%s, line %d: ", newfile, line);
+ (void) printf("%s: ", func);
+ va_start(adx, fmt);
+ (void) vprintf(fmt, adx);
+ va_end(adx);
+ funlockfile(stdout);
+ }
+}
+
+#endif /* ZFS_DEBUG */
+
+/*
+ * =========================================================================
+ * cmn_err() and panic()
+ * =========================================================================
+ */
+static char ce_prefix[CE_IGNORE][10] = { "", "NOTICE: ", "WARNING: ", "" };
+static char ce_suffix[CE_IGNORE][2] = { "", "\n", "\n", "" };
+
+void
+vpanic(const char *fmt, va_list adx)
+{
+ (void) fprintf(stderr, "error: ");
+ (void) vfprintf(stderr, fmt, adx);
+ (void) fprintf(stderr, "\n");
+
+ abort(); /* think of it as a "user-level crash dump" */
+}
+
+void
+panic(const char *fmt, ...)
+{
+ va_list adx;
+
+ va_start(adx, fmt);
+ vpanic(fmt, adx);
+ va_end(adx);
+}
+
+/*PRINTFLIKE2*/
+void
+cmn_err(int ce, const char *fmt, ...)
+{
+ va_list adx;
+
+ va_start(adx, fmt);
+ if (ce == CE_PANIC)
+ vpanic(fmt, adx);
+ if (ce != CE_NOTE) { /* suppress noise in userland stress testing */
+ (void) fprintf(stderr, "%s", ce_prefix[ce]);
+ (void) vfprintf(stderr, fmt, adx);
+ (void) fprintf(stderr, "%s", ce_suffix[ce]);
+ }
+ va_end(adx);
+}
+
+/*
+ * =========================================================================
+ * misc routines
+ * =========================================================================
+ */
+
+void
+delay(clock_t ticks)
+{
+ poll(0, 0, ticks * (1000 / hz));
+}
+
+/*
+ * Find highest one bit set.
+ * Returns bit number + 1 of highest bit that is set, otherwise returns 0.
+ * High order bit is 31 (or 63 in _LP64 kernel).
+ */
+int
+highbit(ulong_t i)
+{
+ register int h = 1;
+
+ if (i == 0)
+ return (0);
+#ifdef _LP64
+ if (i & 0xffffffff00000000ul) {
+ h += 32; i >>= 32;
+ }
+#endif
+ if (i & 0xffff0000) {
+ h += 16; i >>= 16;
+ }
+ if (i & 0xff00) {
+ h += 8; i >>= 8;
+ }
+ if (i & 0xf0) {
+ h += 4; i >>= 4;
+ }
+ if (i & 0xc) {
+ h += 2; i >>= 2;
+ }
+ if (i & 0x2) {
+ h += 1;
+ }
+ return (h);
+}
+
+static int
+random_get_bytes_common(uint8_t *ptr, size_t len, char *devname)
+{
+ int fd = open(devname, O_RDONLY);
+ size_t resid = len;
+ ssize_t bytes;
+
+ ASSERT(fd != -1);
+
+ while (resid != 0) {
+ bytes = read(fd, ptr, resid);
+ ASSERT(bytes >= 0);
+ ptr += bytes;
+ resid -= bytes;
+ }
+
+ close(fd);
+
+ return (0);
+}
+
+int
+random_get_bytes(uint8_t *ptr, size_t len)
+{
+ return (random_get_bytes_common(ptr, len, "/dev/random"));
+}
+
+int
+random_get_pseudo_bytes(uint8_t *ptr, size_t len)
+{
+ return (random_get_bytes_common(ptr, len, "/dev/urandom"));
+}
+
+/*
+ * =========================================================================
+ * kernel emulation setup & teardown
+ * =========================================================================
+ */
+static int
+umem_out_of_memory(void)
+{
+ char errmsg[] = "out of memory -- generating core dump\n";
+
+ write(fileno(stderr), errmsg, sizeof (errmsg));
+ abort();
+ return (0);
+}
+
+void
+kernel_init(int mode)
+{
+ umem_nofail_callback(umem_out_of_memory);
+
+ physmem = sysconf(_SC_PHYS_PAGES);
+
+ dprintf("physmem = %llu pages (%.2f GB)\n", physmem,
+ (double)physmem * sysconf(_SC_PAGE_SIZE) / (1ULL << 30));
+
+ spa_init(mode);
+}
+
+void
+kernel_fini(void)
+{
+ spa_fini();
+}
diff --git a/usr/src/lib/libzpool/common/llib-lzpool b/usr/src/lib/libzpool/common/llib-lzpool
new file mode 100644
index 0000000000..90c2d6c4fe
--- /dev/null
+++ b/usr/src/lib/libzpool/common/llib-lzpool
@@ -0,0 +1,51 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <sys/zfs_context.h>
+#include <sys/list.h>
+#include <sys/list_impl.h>
+#include <sys/sysmacros.h>
+#include <sys/debug.h>
+#include <sys/dmu_traverse.h>
+#include <sys/dnode.h>
+#include <sys/dsl_prop.h>
+#include <sys/spa.h>
+#include <sys/spa_impl.h>
+#include <sys/space_map.h>
+#include <sys/vdev.h>
+#include <sys/vdev_impl.h>
+#include <sys/zap.h>
+#include <sys/zio.h>
+#include <sys/zio_compress.h>
+#include <sys/zil.h>
+#include <sys/bplist.h>
+
+extern uint64_t zio_gang_bang;
diff --git a/usr/src/lib/libzpool/common/sys/zfs_context.h b/usr/src/lib/libzpool/common/sys/zfs_context.h
new file mode 100644
index 0000000000..243258be98
--- /dev/null
+++ b/usr/src/lib/libzpool/common/sys/zfs_context.h
@@ -0,0 +1,411 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_ZFS_CONTEXT_H
+#define _SYS_ZFS_CONTEXT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _SYS_MUTEX_H
+#define _SYS_RWLOCK_H
+#define _SYS_CONDVAR_H
+#define _SYS_SYSTM_H
+#define _SYS_DEBUG_H
+#define _SYS_T_LOCK_H
+#define _SYS_VNODE_H
+#define _SYS_VFS_H
+#define _SYS_SUNDDI_H
+#define _SYS_CALLB_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <synch.h>
+#include <thread.h>
+#include <assert.h>
+#include <alloca.h>
+#include <umem.h>
+#include <limits.h>
+#include <atomic.h>
+#include <dirent.h>
+#include <time.h>
+#include <sys/note.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/bitmap.h>
+#include <sys/resource.h>
+#include <sys/byteorder.h>
+#include <sys/list.h>
+#include <sys/uio.h>
+#include <sys/zfs_debug.h>
+#include <sys/sdt.h>
+
+/*
+ * Debugging
+ */
+
+/*
+ * Note that we are not using the debugging levels.
+ */
+
+#define CE_CONT 0 /* continuation */
+#define CE_NOTE 1 /* notice */
+#define CE_WARN 2 /* warning */
+#define CE_PANIC 3 /* panic */
+#define CE_IGNORE 4 /* print nothing */
+
+/*
+ * ZFS debugging
+ */
+
+#ifdef ZFS_DEBUG
+extern void dprintf_setup(int *argc, char **argv);
+#endif /* ZFS_DEBUG */
+
+extern void cmn_err(int, const char *, ...);
+extern void panic(const char *, ...);
+extern void vpanic(const char *, __va_list);
+
+/* This definition is copied from assert.h. */
+#if defined(__STDC__)
+#if __STDC_VERSION__ - 0 >= 199901L
+#define verify(EX) (void)((EX) || \
+ (__assert_c99(#EX, __FILE__, __LINE__, __func__), 0))
+#else
+#define verify(EX) (void)((EX) || (__assert(#EX, __FILE__, __LINE__), 0))
+#endif /* __STDC_VERSION__ - 0 >= 199901L */
+#else
+#define verify(EX) (void)((EX) || (_assert("EX", __FILE__, __LINE__), 0))
+#endif /* __STDC__ */
+
+
+#define VERIFY verify
+#define ASSERT assert
+
+extern void __assert(const char *, const char *, int);
+
+#ifdef lint
+#define VERIFY3_IMPL(x, y, z, t) if (x == z) ((void)0)
+#else
+/* BEGIN CSTYLED */
+#define VERIFY3_IMPL(LEFT, OP, RIGHT, TYPE) do { \
+ const TYPE __left = (TYPE)(LEFT); \
+ const TYPE __right = (TYPE)(RIGHT); \
+ if (!(__left OP __right)) { \
+ char *__buf = alloca(256); \
+ (void) snprintf(__buf, 256, "%s %s %s (0x%llx %s 0x%llx)", \
+ #LEFT, #OP, #RIGHT, \
+ (u_longlong_t)__left, #OP, (u_longlong_t)__right); \
+ __assert(__buf, __FILE__, __LINE__); \
+ } \
+_NOTE(CONSTCOND) } while (0)
+/* END CSTYLED */
+#endif /* lint */
+
+#define VERIFY3S(x, y, z) VERIFY3_IMPL(x, y, z, int64_t)
+#define VERIFY3U(x, y, z) VERIFY3_IMPL(x, y, z, uint64_t)
+#define VERIFY3P(x, y, z) VERIFY3_IMPL(x, y, z, uintptr_t)
+
+#ifdef NDEBUG
+#define ASSERT3S(x, y, z) ((void)0)
+#define ASSERT3U(x, y, z) ((void)0)
+#define ASSERT3P(x, y, z) ((void)0)
+#else
+#define ASSERT3S(x, y, z) VERIFY3S(x, y, z)
+#define ASSERT3U(x, y, z) VERIFY3U(x, y, z)
+#define ASSERT3P(x, y, z) VERIFY3P(x, y, z)
+#endif
+
+/*
+ * Dtrace SDT probes have different signatures in userland than they do in
+ * kernel. If they're being used in kernel code, re-define them out of
+ * existence for their counterparts in libzpool.
+ */
+
+#ifdef DTRACE_PROBE1
+#undef DTRACE_PROBE1
+#define DTRACE_PROBE1(a, b, c) ((void)0)
+#endif /* DTRACE_PROBE1 */
+
+#ifdef DTRACE_PROBE2
+#undef DTRACE_PROBE2
+#define DTRACE_PROBE2(a, b, c, d, e) ((void)0)
+#endif /* DTRACE_PROBE2 */
+
+/*
+ * Threads
+ */
+#define curthread ((void *)(uintptr_t)thr_self())
+
+typedef struct kthread kthread_t;
+
+#define thread_create(stk, stksize, func, arg, len, pp, state, pri) \
+ zk_thread_create(func, arg)
+#define thread_exit() thr_exit(0)
+
+extern kthread_t *zk_thread_create(void (*func)(), void *arg);
+
+#define issig(why) (FALSE)
+#define ISSIG(thr, why) (FALSE)
+
+/*
+ * Mutexes
+ */
+typedef struct kmutex {
+ void *m_owner;
+ mutex_t m_lock;
+} kmutex_t;
+
+#define MUTEX_DEFAULT USYNC_THREAD
+#undef MUTEX_HELD
+#define MUTEX_HELD(m) _mutex_held(&(m)->m_lock)
+
+/*
+ * Argh -- we have to get cheesy here because the kernel and userland
+ * have different signatures for the same routine.
+ */
+extern int _mutex_init(mutex_t *mp, int type, void *arg);
+extern int _mutex_destroy(mutex_t *mp);
+
+#define mutex_init(mp, b, c, d) zmutex_init((kmutex_t *)(mp))
+#define mutex_destroy(mp) zmutex_destroy((kmutex_t *)(mp))
+
+extern void zmutex_init(kmutex_t *mp);
+extern void zmutex_destroy(kmutex_t *mp);
+extern void mutex_enter(kmutex_t *mp);
+extern void mutex_exit(kmutex_t *mp);
+extern int mutex_tryenter(kmutex_t *mp);
+extern void *mutex_owner(kmutex_t *mp);
+
+/*
+ * RW locks
+ */
+typedef struct krwlock {
+ void *rw_owner;
+ rwlock_t rw_lock;
+} krwlock_t;
+
+typedef int krw_t;
+
+#define RW_READER 0
+#define RW_WRITER 1
+#define RW_DEFAULT USYNC_THREAD
+
+#undef RW_READ_HELD
+#define RW_READ_HELD(x) _rw_read_held(&(x)->rw_lock)
+
+#undef RW_WRITE_HELD
+#define RW_WRITE_HELD(x) _rw_write_held(&(x)->rw_lock)
+
+extern void rw_init(krwlock_t *rwlp, char *name, int type, void *arg);
+extern void rw_destroy(krwlock_t *rwlp);
+extern void rw_enter(krwlock_t *rwlp, krw_t rw);
+extern int rw_tryenter(krwlock_t *rwlp, krw_t rw);
+extern int rw_tryupgrade(krwlock_t *rwlp);
+extern void rw_exit(krwlock_t *rwlp);
+#define rw_downgrade(rwlp) do { } while (0)
+
+/*
+ * Condition variables
+ */
+typedef cond_t kcondvar_t;
+
+#define CV_DEFAULT USYNC_THREAD
+
+extern void cv_init(kcondvar_t *cv, char *name, int type, void *arg);
+extern void cv_destroy(kcondvar_t *cv);
+extern void cv_wait(kcondvar_t *cv, kmutex_t *mp);
+extern clock_t cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime);
+extern void cv_signal(kcondvar_t *cv);
+extern void cv_broadcast(kcondvar_t *cv);
+
+/*
+ * Kernel memory
+ */
+#define KM_SLEEP UMEM_NOFAIL
+#define KM_NOSLEEP UMEM_DEFAULT
+#define kmem_alloc(_s, _f) umem_alloc(_s, _f)
+#define kmem_zalloc(_s, _f) umem_zalloc(_s, _f)
+#define kmem_free(_b, _s) umem_free(_b, _s)
+#define kmem_cache_create(_a, _b, _c, _d, _e, _f, _g, _h, _i) \
+ umem_cache_create(_a, _b, _c, _d, _e, _f, _g, _h, _i)
+#define kmem_cache_destroy(_c) umem_cache_destroy(_c)
+#define kmem_cache_alloc(_c, _f) umem_cache_alloc(_c, _f)
+#define kmem_cache_free(_c, _b) umem_cache_free(_c, _b)
+#define kmem_debugging() 0
+#define kmem_cache_reap_now(c)
+
+typedef umem_cache_t kmem_cache_t;
+
+/*
+ * Task queues
+ */
+typedef struct taskq taskq_t;
+typedef uintptr_t taskqid_t;
+typedef void (task_func_t)(void *);
+
+#define TASKQ_PREPOPULATE 0x0001
+#define TASKQ_CPR_SAFE 0x0002 /* Use CPR safe protocol */
+#define TASKQ_DYNAMIC 0x0004 /* Use dynamic thread scheduling */
+
+#define TQ_SLEEP KM_SLEEP /* Can block for memory */
+#define TQ_NOSLEEP KM_NOSLEEP /* cannot block for memory; may fail */
+#define TQ_NOQUEUE 0x02 /* Do not enqueue if can't dispatch */
+
+extern taskq_t *taskq_create(const char *, int, pri_t, int, int, uint_t);
+extern taskqid_t taskq_dispatch(taskq_t *, task_func_t, void *, uint_t);
+extern void taskq_destroy(taskq_t *);
+extern void taskq_wait(taskq_t *);
+extern int taskq_member(taskq_t *, void *);
+
+/*
+ * vnodes
+ */
+typedef struct vnode {
+ uint64_t v_size;
+ int v_fd;
+ char *v_path;
+} vnode_t;
+
+typedef struct vattr {
+ uint_t va_mask; /* bit-mask of attributes */
+ u_offset_t va_size; /* file size in bytes */
+} vattr_t;
+
+#define AT_TYPE 0x0001
+#define AT_MODE 0x0002
+#define AT_UID 0x0004
+#define AT_GID 0x0008
+#define AT_FSID 0x0010
+#define AT_NODEID 0x0020
+#define AT_NLINK 0x0040
+#define AT_SIZE 0x0080
+#define AT_ATIME 0x0100
+#define AT_MTIME 0x0200
+#define AT_CTIME 0x0400
+#define AT_RDEV 0x0800
+#define AT_BLKSIZE 0x1000
+#define AT_NBLOCKS 0x2000
+#define AT_SEQ 0x8000
+
+#define CRCREAT 0
+
+#define VOP_CLOSE(vp, f, c, o, cr) 0
+#define VOP_PUTPAGE(vp, of, sz, fl, cr) 0
+#define VOP_GETATTR(vp, vap, fl, cr) ((vap)->va_size = (vp)->v_size, 0)
+
+#define VOP_FSYNC(vp, f, cr) fsync((vp)->v_fd)
+
+#define VN_RELE(vp) vn_close(vp)
+
+extern int vn_open(char *path, int x1, int oflags, int mode, vnode_t **vpp,
+ int x2, int x3);
+extern int vn_openat(char *path, int x1, int oflags, int mode, vnode_t **vpp,
+ int x2, int x3, vnode_t *vp);
+extern int vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len,
+ offset_t offset, int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp);
+extern void vn_close(vnode_t *vp);
+
+#define vn_remove(path, x1, x2) remove(path)
+#define vn_rename(from, to, seg) rename((from), (to))
+
+extern vnode_t *rootdir;
+
+#include <sys/file.h> /* for FREAD, FWRITE, etc */
+
+/*
+ * Random stuff
+ */
+#define lbolt (gethrtime() >> 23)
+#define lbolt64 (gethrtime() >> 23)
+#define hz 119 /* frequency when using gethrtime() >> 23 for lbolt */
+
+extern void delay(clock_t ticks);
+
+#define gethrestime_sec() time(NULL)
+
+#define max_ncpus 64
+
+#define minclsyspri 60
+#define maxclsyspri 99
+
+#define CPU_SEQID (thr_self() & (max_ncpus - 1))
+
+#define kcred NULL
+#define CRED() NULL
+
+extern uint64_t physmem;
+
+extern int highbit(ulong_t i);
+extern int random_get_bytes(uint8_t *ptr, size_t len);
+extern int random_get_pseudo_bytes(uint8_t *ptr, size_t len);
+
+extern void kernel_init(int);
+extern void kernel_fini(void);
+
+struct spa;
+extern void nicenum(uint64_t num, char *buf);
+extern void show_pool_stats(struct spa *);
+
+typedef struct callb_cpr {
+ kmutex_t *cc_lockp;
+} callb_cpr_t;
+
+#define CALLB_CPR_INIT(cp, lockp, func, name) { \
+ (cp)->cc_lockp = lockp; \
+}
+
+#define CALLB_CPR_SAFE_BEGIN(cp) { \
+ ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
+}
+
+#define CALLB_CPR_SAFE_END(cp, lockp) { \
+ ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
+}
+
+#define CALLB_CPR_EXIT(cp) { \
+ ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
+ mutex_exit((cp)->cc_lockp); \
+}
+
+#define zone_dataset_visible(x, y) (1)
+#define INGLOBALZONE(z) (1)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_ZFS_CONTEXT_H */
diff --git a/usr/src/lib/libzpool/common/taskq.c b/usr/src/lib/libzpool/common/taskq.c
new file mode 100644
index 0000000000..f7b65718c3
--- /dev/null
+++ b/usr/src/lib/libzpool/common/taskq.c
@@ -0,0 +1,250 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/zfs_context.h>
+
+int taskq_now;
+
+typedef struct task {
+ struct task *task_next;
+ struct task *task_prev;
+ task_func_t *task_func;
+ void *task_arg;
+} task_t;
+
+#define TASKQ_ACTIVE 0x00010000
+
+struct taskq {
+ kmutex_t tq_lock;
+ krwlock_t tq_threadlock;
+ kcondvar_t tq_dispatch_cv;
+ kcondvar_t tq_wait_cv;
+ thread_t *tq_threadlist;
+ int tq_flags;
+ int tq_active;
+ int tq_nthreads;
+ int tq_nalloc;
+ int tq_minalloc;
+ int tq_maxalloc;
+ task_t *tq_freelist;
+ task_t tq_task;
+};
+
+static task_t *
+task_alloc(taskq_t *tq, int tqflags)
+{
+ task_t *t;
+
+ if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
+ tq->tq_freelist = t->task_next;
+ } else {
+ mutex_exit(&tq->tq_lock);
+ if (tq->tq_nalloc >= tq->tq_maxalloc) {
+ if (!(tqflags & KM_SLEEP)) {
+ mutex_enter(&tq->tq_lock);
+ return (NULL);
+ }
+ /*
+ * We don't want to exceed tq_maxalloc, but we can't
+ * wait for other tasks to complete (and thus free up
+ * task structures) without risking deadlock with
+ * the caller. So, we just delay for one second
+ * to throttle the allocation rate.
+ */
+ delay(hz);
+ }
+ t = kmem_alloc(sizeof (task_t), tqflags);
+ mutex_enter(&tq->tq_lock);
+ if (t != NULL)
+ tq->tq_nalloc++;
+ }
+ return (t);
+}
+
+static void
+task_free(taskq_t *tq, task_t *t)
+{
+ if (tq->tq_nalloc <= tq->tq_minalloc) {
+ t->task_next = tq->tq_freelist;
+ tq->tq_freelist = t;
+ } else {
+ tq->tq_nalloc--;
+ mutex_exit(&tq->tq_lock);
+ kmem_free(t, sizeof (task_t));
+ mutex_enter(&tq->tq_lock);
+ }
+}
+
+taskqid_t
+taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
+{
+ task_t *t;
+
+ if (taskq_now) {
+ func(arg);
+ return (1);
+ }
+
+ mutex_enter(&tq->tq_lock);
+ ASSERT(tq->tq_flags & TASKQ_ACTIVE);
+ if ((t = task_alloc(tq, tqflags)) == NULL) {
+ mutex_exit(&tq->tq_lock);
+ return (0);
+ }
+ t->task_next = &tq->tq_task;
+ t->task_prev = tq->tq_task.task_prev;
+ t->task_next->task_prev = t;
+ t->task_prev->task_next = t;
+ t->task_func = func;
+ t->task_arg = arg;
+ cv_signal(&tq->tq_dispatch_cv);
+ mutex_exit(&tq->tq_lock);
+ return (1);
+}
+
+void
+taskq_wait(taskq_t *tq)
+{
+ mutex_enter(&tq->tq_lock);
+ while (tq->tq_task.task_next != &tq->tq_task || tq->tq_active != 0)
+ cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
+ mutex_exit(&tq->tq_lock);
+}
+
+static void *
+taskq_thread(void *arg)
+{
+ taskq_t *tq = arg;
+ task_t *t;
+
+ mutex_enter(&tq->tq_lock);
+ while (tq->tq_flags & TASKQ_ACTIVE) {
+ if ((t = tq->tq_task.task_next) == &tq->tq_task) {
+ if (--tq->tq_active == 0)
+ cv_broadcast(&tq->tq_wait_cv);
+ cv_wait(&tq->tq_dispatch_cv, &tq->tq_lock);
+ tq->tq_active++;
+ continue;
+ }
+ t->task_prev->task_next = t->task_next;
+ t->task_next->task_prev = t->task_prev;
+ mutex_exit(&tq->tq_lock);
+
+ rw_enter(&tq->tq_threadlock, RW_READER);
+ t->task_func(t->task_arg);
+ rw_exit(&tq->tq_threadlock);
+
+ mutex_enter(&tq->tq_lock);
+ task_free(tq, t);
+ }
+ tq->tq_nthreads--;
+ cv_broadcast(&tq->tq_wait_cv);
+ mutex_exit(&tq->tq_lock);
+ return (NULL);
+}
+
+/*ARGSUSED*/
+taskq_t *
+taskq_create(const char *name, int nthreads, pri_t pri,
+ int minalloc, int maxalloc, uint_t flags)
+{
+ taskq_t *tq = kmem_zalloc(sizeof (taskq_t), KM_SLEEP);
+ int t;
+
+ rw_init(&tq->tq_threadlock, NULL, RW_DEFAULT, NULL);
+ tq->tq_flags = flags | TASKQ_ACTIVE;
+ tq->tq_active = nthreads;
+ tq->tq_nthreads = nthreads;
+ tq->tq_minalloc = minalloc;
+ tq->tq_maxalloc = maxalloc;
+ tq->tq_task.task_next = &tq->tq_task;
+ tq->tq_task.task_prev = &tq->tq_task;
+ tq->tq_threadlist = kmem_alloc(nthreads * sizeof (thread_t), KM_SLEEP);
+
+ if (flags & TASKQ_PREPOPULATE) {
+ mutex_enter(&tq->tq_lock);
+ while (minalloc-- > 0)
+ task_free(tq, task_alloc(tq, KM_SLEEP));
+ mutex_exit(&tq->tq_lock);
+ }
+
+ for (t = 0; t < nthreads; t++)
+ (void) thr_create(0, 0, taskq_thread,
+ tq, THR_BOUND, &tq->tq_threadlist[t]);
+
+ return (tq);
+}
+
+void
+taskq_destroy(taskq_t *tq)
+{
+ int t;
+ int nthreads = tq->tq_nthreads;
+
+ taskq_wait(tq);
+
+ mutex_enter(&tq->tq_lock);
+
+ tq->tq_flags &= ~TASKQ_ACTIVE;
+ cv_broadcast(&tq->tq_dispatch_cv);
+
+ while (tq->tq_nthreads != 0)
+ cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
+
+ tq->tq_minalloc = 0;
+ while (tq->tq_nalloc != 0) {
+ ASSERT(tq->tq_freelist != NULL);
+ task_free(tq, task_alloc(tq, KM_SLEEP));
+ }
+
+ mutex_exit(&tq->tq_lock);
+
+ for (t = 0; t < nthreads; t++)
+ (void) thr_join(tq->tq_threadlist[t], NULL, NULL);
+
+ kmem_free(tq->tq_threadlist, nthreads * sizeof (thread_t));
+
+ rw_destroy(&tq->tq_threadlock);
+
+ kmem_free(tq, sizeof (taskq_t));
+}
+
+int
+taskq_member(taskq_t *tq, void *t)
+{
+ int i;
+
+ if (taskq_now)
+ return (1);
+
+ for (i = 0; i < tq->tq_nthreads; i++)
+ if (tq->tq_threadlist[i] == (thread_t)(uintptr_t)t)
+ return (1);
+
+ return (0);
+}
diff --git a/usr/src/lib/libzpool/common/util.c b/usr/src/lib/libzpool/common/util.c
new file mode 100644
index 0000000000..28a6704702
--- /dev/null
+++ b/usr/src/lib/libzpool/common/util.c
@@ -0,0 +1,135 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <assert.h>
+#include <sys/zfs_context.h>
+#include <sys/avl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/spa.h>
+#include <sys/fs/zfs.h>
+
+/*
+ * Routines needed by more than one client of libzpool.
+ */
+
+void
+nicenum(uint64_t num, char *buf)
+{
+ uint64_t n = num;
+ int index = 0;
+ char u;
+
+ while (n >= 1024) {
+ n = (n + (1024 / 2)) / 1024; /* Round up or down */
+ index++;
+ }
+
+ u = " KMGTPE"[index];
+
+ if (index == 0) {
+ (void) sprintf(buf, "%llu", (u_longlong_t)n);
+ } else if (n < 10 && (num & (num - 1)) != 0) {
+ (void) sprintf(buf, "%.2f%c",
+ (double)num / (1ULL << 10 * index), u);
+ } else if (n < 100 && (num & (num - 1)) != 0) {
+ (void) sprintf(buf, "%.1f%c",
+ (double)num / (1ULL << 10 * index), u);
+ } else {
+ (void) sprintf(buf, "%llu%c", (u_longlong_t)n, u);
+ }
+}
+
+static void
+show_vdev_stats(const char *desc, nvlist_t *nv, int indent)
+{
+ nvlist_t **child;
+ uint_t c, children;
+ vdev_stat_t *vs;
+ uint64_t sec;
+ char used[6], avail[6];
+ char rops[6], wops[6], rbytes[6], wbytes[6], rerr[6], werr[6], cerr[6];
+
+ if (indent == 0) {
+ (void) printf(" "
+ " capacity operations bandwidth ---- errors ----\n");
+ (void) printf("description "
+ "used avail read write read write read write cksum\n");
+ }
+
+ VERIFY(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_STATS,
+ (uint64_t **)&vs, &c) == 0);
+
+ sec = MAX(1, vs->vs_timestamp / NANOSEC);
+
+ nicenum(vs->vs_alloc, used);
+ nicenum(vs->vs_space - vs->vs_alloc, avail);
+ nicenum(vs->vs_ops[ZIO_TYPE_READ] / sec, rops);
+ nicenum(vs->vs_ops[ZIO_TYPE_WRITE] / sec, wops);
+ nicenum(vs->vs_bytes[ZIO_TYPE_READ] / sec, rbytes);
+ nicenum(vs->vs_bytes[ZIO_TYPE_WRITE] / sec, wbytes);
+ nicenum(vs->vs_read_errors, rerr);
+ nicenum(vs->vs_write_errors, werr);
+ nicenum(vs->vs_checksum_errors, cerr);
+
+ (void) printf("%*s%*s%*s%*s %5s %5s %5s %5s %5s %5s %5s\n",
+ indent, "",
+ indent - 19 - (vs->vs_space ? 0 : 12), desc,
+ vs->vs_space ? 6 : 0, vs->vs_space ? used : "",
+ vs->vs_space ? 6 : 0, vs->vs_space ? avail : "",
+ rops, wops, rbytes, wbytes, rerr, werr, cerr);
+
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) != 0)
+ return;
+
+ for (c = 0; c < children; c++) {
+ nvlist_t *cnv = child[c];
+ char *cname;
+ if (nvlist_lookup_string(cnv, ZPOOL_CONFIG_PATH, &cname) &&
+ nvlist_lookup_string(cnv, ZPOOL_CONFIG_TYPE, &cname))
+ cname = "<unknown>";
+ show_vdev_stats(cname, cnv, indent + 2);
+ }
+}
+
+void
+show_pool_stats(spa_t *spa)
+{
+ nvlist_t *config = NULL;
+ nvlist_t *nvroot = NULL;
+
+ spa_config_enter(spa, RW_READER);
+ VERIFY(spa_get_stats(spa_name(spa), &config) == 0);
+ VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0);
+
+ show_vdev_stats(spa_name(spa), nvroot, 0);
+ spa_config_exit(spa);
+}
diff --git a/usr/src/lib/libzpool/i386/Makefile b/usr/src/lib/libzpool/i386/Makefile
new file mode 100644
index 0000000000..3ae822d920
--- /dev/null
+++ b/usr/src/lib/libzpool/i386/Makefile
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libzpool/inc.flg b/usr/src/lib/libzpool/inc.flg
new file mode 100644
index 0000000000..94a1191086
--- /dev/null
+++ b/usr/src/lib/libzpool/inc.flg
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+find_files "s.*" usr/src/common/zfs
+find_files "s.*" usr/src/uts/common/fs/zfs/sys
+echo_file usr/src/uts/common/sys/fs/zfs.h
diff --git a/usr/src/lib/libzpool/sparcv9/Makefile b/usr/src/lib/libzpool/sparcv9/Makefile
new file mode 100644
index 0000000000..49cb413342
--- /dev/null
+++ b/usr/src/lib/libzpool/sparcv9/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+sparcv9_C_PICFLAGS = $(sparcv9_C_BIGPICFLAGS)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT)