diff options
Diffstat (limited to 'usr/src/lib')
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) |