summaryrefslogtreecommitdiff
path: root/usr/src/lib/libshare
diff options
context:
space:
mode:
authordougm <none@none>2006-11-01 16:37:33 -0800
committerdougm <none@none>2006-11-01 16:37:33 -0800
commit6185db853e024a486ff8837e6784dd290d866112 (patch)
tree63111669df90312edea60a2931a4ab3fed34e952 /usr/src/lib/libshare
parentb4034a75a927c4d7d1850ef306cc6b1d49c48f20 (diff)
downloadillumos-joyent-6185db853e024a486ff8837e6784dd290d866112.tar.gz
PSARC 2005/374 Share management improvements
6281048 NFS needs simplified administration --HG-- rename : usr/src/cmd/dfs.cmds/share/Makefile => deleted_files/usr/src/cmd/dfs.cmds/share/Makefile rename : usr/src/cmd/dfs.cmds/share/share.c => deleted_files/usr/src/cmd/dfs.cmds/share/share.c rename : usr/src/cmd/fs.d/nfs/share/share.c => deleted_files/usr/src/cmd/fs.d/nfs/share/share.c rename : usr/src/cmd/fs.d/nfs/unshare/Makefile => deleted_files/usr/src/cmd/fs.d/nfs/unshare/Makefile rename : usr/src/cmd/fs.d/nfs/unshare/unshare.c => deleted_files/usr/src/cmd/fs.d/nfs/unshare/unshare.c rename : usr/src/cmd/fs.d/nfs/share/issubdir.c => usr/src/lib/libshare/common/issubdir.c
Diffstat (limited to 'usr/src/lib/libshare')
-rw-r--r--usr/src/lib/libshare/Makefile68
-rw-r--r--usr/src/lib/libshare/Makefile.com81
-rw-r--r--usr/src/lib/libshare/common/issubdir.c155
-rw-r--r--usr/src/lib/libshare/common/libshare.c2665
-rw-r--r--usr/src/lib/libshare/common/libshare.h217
-rw-r--r--usr/src/lib/libshare/common/libshare_impl.h143
-rw-r--r--usr/src/lib/libshare/common/libshare_zfs.c496
-rw-r--r--usr/src/lib/libshare/common/libsharecore.c1801
-rw-r--r--usr/src/lib/libshare/common/llib-lshare32
-rw-r--r--usr/src/lib/libshare/common/mapfile-vers110
-rw-r--r--usr/src/lib/libshare/common/parser.c121
-rw-r--r--usr/src/lib/libshare/common/plugin.c458
-rw-r--r--usr/src/lib/libshare/common/scfutil.c1443
-rw-r--r--usr/src/lib/libshare/common/scfutil.h81
-rw-r--r--usr/src/lib/libshare/i386/Makefile30
-rw-r--r--usr/src/lib/libshare/sparc/Makefile30
16 files changed, 7931 insertions, 0 deletions
diff --git a/usr/src/lib/libshare/Makefile b/usr/src/lib/libshare/Makefile
new file mode 100644
index 0000000000..afdcaee749
--- /dev/null
+++ b/usr/src/lib/libshare/Makefile
@@ -0,0 +1,68 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../../Makefile.master
+
+include ../Makefile.lib
+
+HDRS = libshare.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+ROOTHDRDIR= $(ROOT)/usr/include
+ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%)
+
+MSGFILES= common/libshare.c common/libsharecore.c common/scfutil.c \
+ common/parser.c common/libshare_zfs.c
+POFILE= libshare.po
+
+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)
+
+$(POFILE): $(MSGFILES)
+ $(BUILDPO.msgfiles)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+_msg: $(MSGDOMAINPOFILE)
+
+
+FRC:
+
+include ../Makefile.targ
+include ../../Makefile.msg.targ
diff --git a/usr/src/lib/libshare/Makefile.com b/usr/src/lib/libshare/Makefile.com
new file mode 100644
index 0000000000..d444041cf7
--- /dev/null
+++ b/usr/src/lib/libshare/Makefile.com
@@ -0,0 +1,81 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+LIBRARY = libshare.a
+VERS = .1
+NFSLIB_DIR = $(SRC)/cmd/fs.d/nfs/lib
+NFSSECSRC = $(NFSLIB_DIR)/nfs_sec.c
+NFSSHARETAB = $(NFSLIB_DIR)/sharetab.c
+
+LIBOBJS = libshare.o libsharecore.o scfutil.o libshare_zfs.o \
+ plugin.o parser.o issubdir.o
+OTHOBJS = sharetab.o nfs_sec.o
+OBJECTS = $(LIBOBJS) $(OTHOBJS)
+COMMON = ../common
+
+SRCS = $(OBJECTS:%.o=../%.c)
+LIBSRCS = $(LIBOBJS:%.o=$(COMMON)/%.c)
+POFILES = $(OBJECTS:%.o=%.po)
+POFILE = libshare.po
+
+include ../../Makefile.lib
+
+ROOTDIRS= $(ROOT)/usr/include
+
+ROOTHDRS= $(HDRS:%=$(ROOTDIRS)/%)
+
+CHECKHDRS= $(HDRS:%.h=%.check)
+
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -lc -lnsl -lscf -lxml2 -lzfs -luuid -lfsmgt
+$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
+
+SRCDIR = ../common
+MAPDIR = ../spec/$(TRANSMACH)
+SPECMAPFILE = $(MAPDIR)/mapfile
+
+#add nfs/lib directory as part of the include path
+CFLAGS += $(CCVERBOSE) -g
+CPPFLAGS += -D_REENTRANT -I$(SRC)/lib/libfsmgt/common \
+ -I$(SRC)/cmd/fs.d/nfs/lib -I/usr/include/libxml2 \
+ -I../common
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+# we don't want to lint the sharetab and nfs_sec files
+lint: $$(LIBSRCS)
+ $(LINT.c) $(LINTCHECKFLAGS) $(LIBSRCS) $(LDLIBS)
+
+pics/%.o: $(NFSLIB_DIR)/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+include ../../Makefile.targ
+
+$(POFILE): $(POFILES)
+ $(RM) $@; cat $(POFILES) > $@
diff --git a/usr/src/lib/libshare/common/issubdir.c b/usr/src/lib/libshare/common/issubdir.c
new file mode 100644
index 0000000000..d24a4b89e5
--- /dev/null
+++ b/usr/src/lib/libshare/common/issubdir.c
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Subdirectory detection: needed by exportfs and rpc.mountd.
+ * The above programs call issubdir() frequently, so we make
+ * it fast by caching the results of stat().
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#define MAXSTATS MAXPATHLEN/2 /* maximum number of stat()'s to save */
+
+#define inoeq(ino1, ino2) ((ino1) == (ino2))
+#define deveq(dev1, dev2) ((dev1) == (dev2))
+
+/*
+ * dir1 is a subdirectory of dir2 within the same filesystem if
+ * (a) dir1 is identical to dir2
+ * (b) dir1's parent is dir2
+ */
+int
+issubdir(dir1, dir2)
+ char *dir1;
+ char *dir2;
+{
+ struct stat st;
+ struct stat parent_st;
+ char *p;
+ int index;
+
+ static dev_t child_dev;
+ static dev_t child_rdev;
+ static ino_t child_ino[MAXSTATS];
+ static int valid;
+ static char childdir[MAXPATHLEN];
+ static char child_fstype[_ST_FSTYPSZ];
+
+ /*
+ * Get parent directory info
+ */
+ if (stat(dir2, &parent_st) < 0) {
+ return (0);
+ }
+
+ if (strcmp(childdir, dir1) != 0) {
+ /*
+ * Not in cache: get child directory info
+ */
+ p = strcpy(childdir, dir1) + strlen(dir1);
+ index = 0;
+ valid = 0;
+ for (;;) {
+ if (stat(childdir, &st) < 0) {
+ childdir[0] = 0; /* invalidate cache */
+ return (0);
+ }
+ if (index == 0) {
+ child_dev = st.st_dev;
+ child_rdev = st.st_rdev;
+ (void) strncpy(child_fstype, st.st_fstype,
+ sizeof (child_fstype));
+ }
+ if (index > 0 &&
+ (child_dev != st.st_dev ||
+ inoeq(child_ino[index - 1], st.st_ino))) {
+ /*
+ * Hit root: done
+ */
+ break;
+ }
+ child_ino[index++] = st.st_ino;
+ if (S_ISDIR(st.st_mode)) {
+ p = strcpy(p, "/..") + 3;
+ } else {
+ p = strrchr(childdir, '/');
+ if (p == NULL) {
+ p = strcpy(childdir, ".") + 1;
+ } else {
+ while (((p - 1) > childdir) &&
+ *(p - 1) == '/') {
+ p--;
+ }
+ *p = '\0';
+ }
+ }
+ }
+ valid = index;
+ (void) strcpy(childdir, dir1);
+ }
+
+ /*
+ * Perform the test
+ */
+ if (!deveq(parent_st.st_dev, child_dev)) {
+ return (0);
+ }
+
+ /*
+ * Check rdev also in case of lofs
+ */
+ if (((strcmp(parent_st.st_fstype, "lofs") == 0)) &&
+ (strcmp(child_fstype, "lofs") == 0)) {
+ if (!deveq(parent_st.st_rdev, child_rdev)) {
+ return (0);
+ }
+ }
+
+ for (index = 0; index < valid; index++) {
+ if (inoeq(child_ino[index], parent_st.st_ino)) {
+ return (1);
+ }
+ }
+ return (0);
+}
diff --git a/usr/src/lib/libshare/common/libshare.c b/usr/src/lib/libshare/common/libshare.c
new file mode 100644
index 0000000000..65ab1fc55f
--- /dev/null
+++ b/usr/src/lib/libshare/common/libshare.c
@@ -0,0 +1,2665 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Share control API
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include "libshare.h"
+#include "libshare_impl.h"
+#include <libscf.h>
+#include "scfutil.h"
+#include <ctype.h>
+#include <libintl.h>
+
+#if _NOT_SMF
+#define CONFIG_FILE "/var/tmp/share.cfg"
+#define CONFIG_FILE_TMP "/var/tmp/share.cfg.tmp"
+#endif
+#define TSTAMP(tm) (uint64_t)(((uint64_t)tm.tv_sec << 32) | \
+ (tm.tv_nsec & 0xffffffff))
+
+/*
+ * internal data structures
+ */
+
+static xmlNodePtr sa_config_tree; /* the current config */
+static xmlDocPtr sa_config_doc = NULL; /* current config document */
+extern struct sa_proto_plugin *sap_proto_list;
+
+/* current SMF/SVC repository handle */
+static scfutilhandle_t *scf_handle = NULL;
+extern void getlegacyconfig(char *, xmlNodePtr *);
+extern int gettransients(xmlNodePtr *);
+extern int sa_valid_property(void *, char *, sa_property_t);
+extern char *sa_fstype(char *);
+extern int sa_is_share(void *);
+extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */
+extern int sa_group_is_zfs(sa_group_t);
+extern int sa_path_is_zfs(char *);
+extern int sa_zfs_set_sharenfs(sa_group_t, char *, int);
+extern void update_legacy_config(void);
+extern int issubdir(char *, char *);
+
+static int sa_initialized = 0;
+
+/* helper functions */
+
+char *
+sa_errorstr(int err)
+{
+ static char errstr[32];
+ char *ret = NULL;
+
+ switch (err) {
+ case SA_OK:
+ ret = gettext("ok");
+ break;
+ case SA_NO_SUCH_PATH:
+ ret = gettext("path doesn't exist");
+ break;
+ case SA_NO_MEMORY:
+ ret = gettext("no memory");
+ break;
+ case SA_DUPLICATE_NAME:
+ ret = gettext("name in use");
+ break;
+ case SA_BAD_PATH:
+ ret = gettext("bad path");
+ break;
+ case SA_NO_SUCH_GROUP:
+ ret = gettext("no such group");
+ break;
+ case SA_CONFIG_ERR:
+ ret = gettext("configuration error");
+ break;
+ case SA_SYSTEM_ERR:
+ ret = gettext("system error");
+ break;
+ case SA_SYNTAX_ERR:
+ ret = gettext("syntax error");
+ break;
+ case SA_NO_PERMISSION:
+ ret = gettext("no permission");
+ break;
+ case SA_BUSY:
+ ret = gettext("busy");
+ break;
+ case SA_NO_SUCH_PROP:
+ ret = gettext("no such property");
+ break;
+ case SA_INVALID_NAME:
+ ret = gettext("invalid name");
+ break;
+ case SA_INVALID_PROTOCOL:
+ ret = gettext("invalid protocol");
+ break;
+ case SA_NOT_ALLOWED:
+ ret = gettext("operation not allowed");
+ break;
+ case SA_BAD_VALUE:
+ ret = gettext("bad property value");
+ break;
+ case SA_INVALID_SECURITY:
+ ret = gettext("invalid security type");
+ break;
+ case SA_NO_SUCH_SECURITY:
+ ret = gettext("security type not found");
+ break;
+ case SA_VALUE_CONFLICT:
+ ret = gettext("property value conflict");
+ break;
+ case SA_NOT_IMPLEMENTED:
+ ret = gettext("not implemented");
+ break;
+ case SA_INVALID_PATH:
+ ret = gettext("invalid path");
+ break;
+ case SA_NOT_SUPPORTED:
+ ret = gettext("operation not supported");
+ break;
+ case SA_PROP_SHARE_ONLY:
+ ret = gettext("property not valid for group");
+ break;
+ case SA_NOT_SHARED:
+ ret = gettext("not shared");
+ break;
+ default:
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("unknown %d"), err);
+ ret = errstr;
+ }
+ return (ret);
+}
+
+/*
+ * get_legacy_timestamp(root, path)
+ * gets the timestamp of the last time sharemgr updated the legacy
+ * files. This is used to determine if someone has modified them by
+ * hand.
+ */
+static uint64_t
+get_legacy_timestamp(xmlNodePtr root, char *path)
+{
+ uint64_t tval = 0;
+ xmlNodePtr node;
+ xmlChar *lpath = NULL;
+ xmlChar *timestamp = NULL;
+
+ for (node = root->xmlChildrenNode; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
+ /* a possible legacy node for this path */
+ lpath = xmlGetProp(node, (xmlChar *)"path");
+ if (lpath != NULL && xmlStrcmp(lpath, (xmlChar *)path) == 0) {
+ /* now have the node, extract the data */
+ timestamp = xmlGetProp(node, (xmlChar *)"timestamp");
+ if (timestamp != NULL) {
+ tval = strtoull((char *)timestamp, NULL, 0);
+ break;
+ }
+ }
+ if (lpath != NULL) {
+ xmlFree(lpath);
+ lpath = NULL;
+ }
+ }
+ }
+ if (lpath != NULL)
+ xmlFree(lpath);
+ if (timestamp != NULL)
+ xmlFree(timestamp);
+ return (tval);
+}
+
+/*
+ * set_legacy_timestamp(root, path, timevalue)
+ *
+ * add the current timestamp value to the configuration for use in
+ * determining when to update the legacy files. For SMF, this
+ * property is kept in default/operation/legacy_timestamp
+ */
+
+static void
+set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval)
+{
+ xmlNodePtr node;
+ xmlChar *lpath = NULL;
+
+ for (node = root->xmlChildrenNode; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
+ /* a possible legacy node for this path */
+ lpath = xmlGetProp(node, (xmlChar *)"path");
+ if (lpath != NULL && xmlStrcmp(lpath, (xmlChar *)path) == 0) {
+ xmlFree(lpath);
+ break;
+ }
+ if (lpath != NULL)
+ xmlFree(lpath);
+ }
+ }
+ if (node == NULL) {
+ /* need to create the first legacy timestamp node */
+ node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL);
+ }
+ if (node != NULL) {
+ char tstring[32];
+ int ret;
+
+ (void) snprintf(tstring, sizeof (tstring), "%lld", tval);
+ xmlSetProp(node, (xmlChar *)"timestamp", (xmlChar *)tstring);
+ xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path);
+ /* now commit to SMF */
+ ret = sa_get_instance(scf_handle, "default");
+ if (ret == SA_OK) {
+ ret = sa_start_transaction(scf_handle, "operation");
+ if (ret == SA_OK) {
+ ret = sa_set_property(scf_handle, "legacy-timestamp",
+ tstring);
+ if (ret == SA_OK) {
+ (void) sa_end_transaction(scf_handle);
+ } else {
+ sa_abort_transaction(scf_handle);
+ }
+ }
+ }
+ }
+}
+
+/*
+ * is_shared(share)
+ *
+ * determine if the specified share is currently shared or not.
+ */
+static int
+is_shared(sa_share_t share)
+{
+ char *shared;
+ int result = 0; /* assume not */
+
+ shared = sa_get_share_attr(share, "shared");
+ if (shared != NULL) {
+ if (strcmp(shared, "true") == 0)
+ result = 1;
+ sa_free_attr_string(shared);
+ }
+ return (result);
+}
+
+/*
+ * checksubdir determines if the specified path is a subdirectory of
+ * another share. It calls issubdir() from the old share
+ * implementation to do the complicated work.
+ */
+static int
+checksubdir(char *newpath)
+{
+ sa_group_t group;
+ sa_share_t share;
+ int issub;
+ char *path = NULL;
+
+ for (issub = 0, group = sa_get_group(NULL);
+ group != NULL && !issub;
+ group = sa_get_next_group(group)) {
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ /*
+ * The original behavior of share never checked
+ * against the permanent configuration
+ * (/etc/dfs/dfstab). PIT has a number of cases where
+ * it depends on this older behavior even though it
+ * could be considered incorrect. We may tighten this
+ * up in the future.
+ */
+ if (!is_shared(share))
+ continue;
+
+ path = sa_get_share_attr(share, "path");
+ if (newpath != NULL &&
+ (strcmp(path, newpath) == 0 || issubdir(newpath, path) ||
+ issubdir(path, newpath))) {
+ sa_free_attr_string(path);
+ path = NULL;
+ issub = SA_INVALID_PATH;
+ break;
+ }
+ sa_free_attr_string(path);
+ path = NULL;
+ }
+ }
+ if (path != NULL)
+ sa_free_attr_string(path);
+ return (issub);
+}
+
+/*
+ * validpath(path)
+ * determine if the provided path is valid for a share. It shouldn't
+ * be a sub-dir of an already shared path or the parent directory of a
+ * share path.
+ */
+static int
+validpath(char *path)
+{
+ int error = SA_OK;
+ struct stat st;
+ sa_share_t share;
+ char *fstype;
+
+ if (*path != '/') {
+ return (SA_BAD_PATH);
+ }
+ if (stat(path, &st) < 0) {
+ error = SA_NO_SUCH_PATH;
+ } else {
+ share = sa_find_share(path);
+ if (share != NULL) {
+ error = SA_DUPLICATE_NAME;
+ }
+ if (error == SA_OK) {
+ /*
+ * check for special case with file system that might
+ * have restrictions. For now, ZFS is the only case
+ * since it has its own idea of how to configure
+ * shares. We do this before subdir checking since
+ * things like ZFS will do that for us. This should
+ * also be done via plugin interface.
+ */
+ fstype = sa_fstype(path);
+ if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
+ if (sa_zfs_is_shared(path))
+ error = SA_DUPLICATE_NAME;
+ }
+ if (fstype != NULL)
+ sa_free_fstype(fstype);
+ }
+ if (error == SA_OK) {
+ error = checksubdir(path);
+ }
+ }
+ return (error);
+}
+
+/*
+ * check to see if group/share is persistent.
+ */
+static int
+is_persistent(sa_group_t group)
+{
+ char *type;
+ int persist = 1;
+
+ type = sa_get_group_attr(group, "type");
+ if (type != NULL && strcmp(type, "transient") == 0)
+ persist = 0;
+ if (type != NULL)
+ sa_free_attr_string(type);
+ return (persist);
+}
+
+/*
+ * sa_valid_group_name(name)
+ *
+ * check that the "name" contains only valid characters and otherwise
+ * fits the required naming conventions. Valid names must start with
+ * an alphabetic and the remainder may consist of only alphanumeric
+ * plus the '-' and '_' characters. This name limitation comes from
+ * inherent limitations in SMF.
+ */
+
+int
+sa_valid_group_name(char *name)
+{
+ int ret = 1;
+ ssize_t len;
+
+ if (name != NULL && isalpha(*name)) {
+ char c;
+ len = strlen(name);
+ if (len < (scf_max_name_len - sizeof ("group:"))) {
+ for (c = *name++; c != '\0' && ret != 0; c = *name++) {
+ if (!isalnum(c) && c != '-' && c != '_')
+ ret = 0;
+ }
+ } else {
+ ret = 0;
+ }
+ } else {
+ ret = 0;
+ }
+ return (ret);
+}
+
+
+/*
+ * is_zfs_group(group)
+ * Determine if the specified group is a ZFS sharenfs group
+ */
+static int
+is_zfs_group(sa_group_t group)
+{
+ int ret = 0;
+ xmlNodePtr parent;
+ xmlChar *zfs;
+
+ if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0) {
+ parent = (xmlNodePtr)sa_get_parent_group(group);
+ } else {
+ parent = (xmlNodePtr)group;
+ }
+ zfs = xmlGetProp(parent, (xmlChar *)"zfs");
+ if (zfs != NULL) {
+ xmlFree(zfs);
+ ret = 1;
+ }
+ return (ret);
+}
+
+/*
+ * sa_optionset_name(optionset, oname, len, id)
+ * return the SMF name for the optionset. If id is not NULL, it
+ * will have the GUID value for a share and should be used
+ * instead of the keyword "optionset" which is used for
+ * groups. If the optionset doesn't have a protocol type
+ * associated with it, "default" is used. This shouldn't happen
+ * at this point but may be desirable in the future if there are
+ * protocol independent properties added. The name is returned in
+ * oname.
+ */
+
+static int
+sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id)
+{
+ char *proto;
+
+ if (id == NULL)
+ id = "optionset";
+
+ proto = sa_get_optionset_attr(optionset, "type");
+ len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default");
+
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ return (len);
+}
+
+/*
+ * sa_security_name(optionset, oname, len, id)
+ *
+ * return the SMF name for the security. If id is not NULL, it will
+ * have the GUID value for a share and should be used instead of the
+ * keyword "optionset" which is used for groups. If the optionset
+ * doesn't have a protocol type associated with it, "default" is
+ * used. This shouldn't happen at this point but may be desirable in
+ * the future if there are protocol independent properties added. The
+ * name is returned in oname. The security type is also encoded into
+ * the name. In the future, this wil *be handled a bit differently.
+ */
+
+static int
+sa_security_name(sa_security_t security, char *oname, size_t len, char *id)
+{
+ char *proto;
+ char *sectype;
+
+ if (id == NULL)
+ id = "optionset";
+
+ proto = sa_get_security_attr(security, "type");
+ sectype = sa_get_security_attr(security, "sectype");
+ len = snprintf(oname, len, "%s_%s_%s", id,
+ proto ? proto : "default",
+ sectype ? sectype : "default");
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ if (sectype != NULL)
+ sa_free_attr_string(sectype);
+ return (len);
+}
+
+/*
+ * sa_init()
+ * Initialize the API
+ * find all the shared objects
+ * init the tables with all objects
+ * read in the current configuration
+ */
+
+void
+sa_init(int init_service)
+{
+ struct stat st;
+ int legacy = 0;
+ uint64_t tval = 0;
+
+ if (!sa_initialized) {
+ /* get protocol specific structures */
+ (void) proto_plugin_init();
+ if (init_service & SA_INIT_SHARE_API) {
+ /*
+ * since we want to use SMF, initialize an svc handle
+ * and find out what is there.
+ */
+ scf_handle = sa_scf_init();
+ if (scf_handle != NULL) {
+ (void) sa_get_config(scf_handle, &sa_config_tree,
+ &sa_config_doc);
+ tval = get_legacy_timestamp(sa_config_tree,
+ SA_LEGACY_DFSTAB);
+ if (tval == 0) {
+ /* first time so make sure default is setup */
+ sa_group_t defgrp;
+ sa_optionset_t opt;
+ defgrp = sa_get_group("default");
+ if (defgrp != NULL) {
+ opt = sa_get_optionset(defgrp, NULL);
+ if (opt == NULL)
+ /* NFS is the default for default */
+ opt = sa_create_optionset(defgrp, "nfs");
+ }
+ }
+ if (stat(SA_LEGACY_DFSTAB, &st) >= 0 &&
+ tval != TSTAMP(st.st_ctim)) {
+ getlegacyconfig(SA_LEGACY_DFSTAB, &sa_config_tree);
+ if (stat(SA_LEGACY_DFSTAB, &st) >= 0)
+ set_legacy_timestamp(sa_config_tree,
+ SA_LEGACY_DFSTAB,
+ TSTAMP(st.st_ctim));
+ }
+ legacy |= sa_get_zfs_shares("zfs");
+ legacy |= gettransients(&sa_config_tree);
+ }
+ }
+ }
+}
+
+/*
+ * sa_fini()
+ * Uninitialize the API structures including the configuration
+ * data structures
+ */
+
+void
+sa_fini()
+{
+ if (sa_initialized) {
+ /* free the config trees */
+ sa_initialized = 0;
+ if (sa_config_doc != NULL)
+ xmlFreeDoc(sa_config_doc);
+ sa_config_tree = NULL;
+ sa_config_doc = NULL;
+ sa_scf_fini(scf_handle);
+ (void) proto_plugin_init();
+ }
+}
+
+/*
+ * sa_get_protocols(char **protocol)
+ * Get array of protocols that are supported
+ * Returns pointer to an allocated and NULL terminated
+ * array of strings. Caller must free.
+ * This really should be determined dynamically.
+ * If there aren't any defined, return -1.
+ * Use free() to return memory.
+ */
+
+int
+sa_get_protocols(char ***protocols)
+{
+ int numproto = -1;
+
+ if (protocols != NULL) {
+ struct sa_proto_plugin *plug;
+ for (numproto = 0, plug = sap_proto_list; plug != NULL;
+ plug = plug->plugin_next) {
+ numproto++;
+ }
+
+ *protocols = calloc(numproto + 1, sizeof (char *));
+ if (*protocols != NULL) {
+ int ret = 0;
+ for (plug = sap_proto_list; plug != NULL;
+ plug = plug->plugin_next) {
+ /* faking for now */
+ (*protocols)[ret++] = plug->plugin_ops->sa_protocol;
+ }
+ } else {
+ numproto = -1;
+ }
+ }
+ return (numproto);
+}
+
+/*
+ * find_group_by_name(node, group)
+ *
+ * search the XML document subtree specified by node to find the group
+ * specified by group. Searching subtree allows subgroups to be
+ * searched for.
+ */
+
+static xmlNodePtr
+find_group_by_name(xmlNodePtr node, xmlChar *group)
+{
+ xmlChar *name = NULL;
+
+ for (node = node->xmlChildrenNode; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) {
+ /* if no groupname, return the first found */
+ if (group == NULL)
+ break;
+ name = xmlGetProp(node, (xmlChar *)"name");
+ if (name != NULL &&
+ xmlStrcmp(name, group) == 0) {
+ break;
+ }
+ if (name != NULL) {
+ xmlFree(name);
+ name = NULL;
+ }
+ }
+ }
+ if (name != NULL)
+ xmlFree(name);
+ return (node);
+}
+
+/*
+ * sa_get_group(groupname)
+ * Return the "group" specified. If groupname is NULL,
+ * return the first group of the list of groups.
+ */
+sa_group_t
+sa_get_group(char *groupname)
+{
+ xmlNodePtr node = NULL;
+ char *subgroup = NULL;
+ char *group = NULL;
+
+ if (sa_config_tree != NULL) {
+ if (groupname != NULL) {
+ group = strdup(groupname);
+ subgroup = strchr(group, '/');
+ if (subgroup != NULL)
+ *subgroup++ = '\0';
+ }
+ node = find_group_by_name(sa_config_tree, (xmlChar *)group);
+ /* if a subgroup, find it before returning */
+ if (subgroup != NULL && node != NULL) {
+ node = find_group_by_name(node, (xmlChar *)subgroup);
+ }
+ }
+ if (node != NULL && (char *)group != NULL)
+ (void) sa_get_instance(scf_handle, (char *)group);
+ if (group != NULL)
+ free(group);
+ return ((sa_group_t)(node));
+}
+
+/*
+ * sa_get_next_group(group)
+ * Return the "next" group after the specified group from
+ * the internal group list. NULL if there are no more.
+ */
+sa_group_t
+sa_get_next_group(sa_group_t group)
+{
+ xmlNodePtr ngroup = NULL;
+ if (group != NULL) {
+ for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
+ ngroup = ngroup->next) {
+ if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
+ break;
+ }
+ }
+ return ((sa_group_t)ngroup);
+}
+
+/*
+ * sa_get_share(group, sharepath)
+ * Return the share object for the share specified. The share
+ * must be in the specified group. Return NULL if not found.
+ */
+sa_share_t
+sa_get_share(sa_group_t group, char *sharepath)
+{
+ xmlNodePtr node = NULL;
+ xmlChar *path;
+
+ /*
+ * For future scalability, this should end up building a cache
+ * since it will get called regularly by the mountd and info
+ * services.
+ */
+ if (group != NULL) {
+ for (node = ((xmlNodePtr)group)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
+ if (sharepath == NULL) {
+ break;
+ } else {
+ /* is it the correct share? */
+ path = xmlGetProp(node, (xmlChar *)"path");
+ if (path != NULL &&
+ xmlStrcmp(path, (xmlChar *)sharepath) == 0) {
+ xmlFree(path);
+ break;
+ }
+ xmlFree(path);
+ }
+ }
+ }
+ }
+ return ((sa_share_t)node);
+}
+
+/*
+ * sa_get_next_share(share)
+ * Return the next share following the specified share
+ * from the internal list of shares. Returns NULL if there
+ * are no more shares. The list is relative to the same
+ * group.
+ */
+sa_share_t
+sa_get_next_share(sa_share_t share)
+{
+ xmlNodePtr node = NULL;
+
+ if (share != NULL) {
+ for (node = ((xmlNodePtr)share)->next; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
+ break;
+ }
+ }
+ }
+ return ((sa_share_t)node);
+}
+
+/*
+ * _sa_get_child_node(node, type)
+ *
+ * find the child node of the specified node that has "type". This is
+ * used to implement several internal functions.
+ */
+
+static xmlNodePtr
+_sa_get_child_node(xmlNodePtr node, xmlChar *type)
+{
+ xmlNodePtr child;
+ for (child = node->xmlChildrenNode; child != NULL;
+ child = child->next)
+ if (xmlStrcmp(child->name, type) == 0)
+ return (child);
+ return ((xmlNodePtr)NULL);
+}
+
+/*
+ * find_share(group, path)
+ *
+ * Search all the shares in the specified group for one that has the
+ * specified path.
+ */
+
+static sa_share_t
+find_share(sa_group_t group, char *sharepath)
+{
+ sa_share_t share;
+ char *path;
+
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ path = sa_get_share_attr(share, "path");
+ if (path != NULL && strcmp(path, sharepath) == 0) {
+ sa_free_attr_string(path);
+ break;
+ }
+ if (path != NULL)
+ sa_free_attr_string(path);
+ }
+ return (share);
+}
+
+/*
+ * sa_get_sub_group(group)
+ *
+ * Get the first sub-group of group. The sa_get_next_group() function
+ * can be used to get the rest. This is currently only used for ZFS
+ * sub-groups but could be used to implement a more general mechanism.
+ */
+
+sa_group_t
+sa_get_sub_group(sa_group_t group)
+{
+ return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
+ (xmlChar *)"group"));
+}
+
+/*
+ * sa_find_share(sharepath)
+ * Finds a share regardless of group. In the future, this
+ * function should utilize a cache and hash table of some kind.
+ * The current assumption is that a path will only be shared
+ * once. In the future, this may change as implementation of
+ * resource names comes into being.
+ */
+sa_share_t
+sa_find_share(char *sharepath)
+{
+ sa_group_t group;
+ sa_group_t zgroup;
+ sa_share_t share = NULL;
+ int done = 0;
+
+ for (group = sa_get_group(NULL); group != NULL && !done;
+ group = sa_get_next_group(group)) {
+ if (is_zfs_group(group)) {
+ for (zgroup = (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
+ (xmlChar *)"group");
+ zgroup != NULL; zgroup = sa_get_next_group(zgroup)) {
+ share = find_share(zgroup, sharepath);
+ if (share != NULL)
+ break;
+ }
+ } else {
+ share = find_share(group, sharepath);
+ }
+ if (share != NULL)
+ break;
+ }
+ return (share);
+}
+
+/*
+ * sa_check_path(group, path)
+ *
+ * check that path is a valid path relative to the group. Currently,
+ * we are ignoring the group and checking only the NFS rules. Later,
+ * we may want to use the group to then check against the protocols
+ * enabled on the group.
+ */
+
+int
+sa_check_path(sa_group_t group, char *path)
+{
+#ifdef lint
+ group = group;
+#endif
+ return (validpath(path));
+}
+
+/*
+ * _sa_add_share(group, sharepath, persist, *error)
+ *
+ * common code for all types of add_share. sa_add_share() is the
+ * public API, we also need to be able to do this when parsing legacy
+ * files and construction of the internal configuration while
+ * extracting config info from SMF.
+ */
+
+sa_share_t
+_sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
+{
+ xmlNodePtr node = NULL;
+ int err;
+
+ err = SA_OK; /* assume success */
+
+ node = xmlNewChild((xmlNodePtr)group, NULL,
+ (xmlChar *)"share", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
+ xmlSetProp(node, (xmlChar *)"type", persist ?
+ (xmlChar *)"persist" : (xmlChar *)"transient");
+ if (persist != SA_SHARE_TRANSIENT) {
+ /*
+ * persistent shares come in two flavors: SMF and
+ * ZFS. Sort this one out based on target group and
+ * path type. Currently, only NFS is supported in the
+ * ZFS group and it is always on.
+ */
+ if (sa_group_is_zfs(group) && sa_path_is_zfs(sharepath)) {
+ err = sa_zfs_set_sharenfs(group, sharepath, 1);
+ } else {
+ err = sa_commit_share(scf_handle, group,
+ (sa_share_t)node);
+ }
+ }
+ if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) {
+ /* called by the dfstab parser so could be a show */
+ err = SA_OK;
+ }
+ if (err != SA_OK) {
+ /*
+ * we couldn't commit to the repository so undo
+ * our internal state to reflect reality.
+ */
+ xmlUnlinkNode(node);
+ xmlFreeNode(node);
+ node = NULL;
+ }
+ } else {
+ err = SA_NO_MEMORY;
+ }
+ if (error != NULL)
+ *error = err;
+ return (node);
+}
+
+/*
+ * sa_add_share(group, sharepath, persist, *error)
+ *
+ * Add a new share object to the specified group. The share will
+ * have the specified sharepath and will only be constructed if
+ * it is a valid path to be shared. NULL is returned on error
+ * and a detailed error value will be returned via the error
+ * pointer.
+ */
+sa_share_t
+sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
+{
+ xmlNodePtr node = NULL;
+ sa_share_t dup;
+
+ if ((dup = sa_find_share(sharepath)) == NULL &&
+ (*error = sa_check_path(group, sharepath)) == SA_OK) {
+ node = _sa_add_share(group, sharepath, persist, error);
+ }
+ if (dup != NULL)
+ *error = SA_DUPLICATE_NAME;
+
+ return ((sa_share_t)node);
+}
+
+/*
+ * sa_enable_share(share, protocol)
+ * Enable the specified share to the specified protocol.
+ * If protocol is NULL, then all protocols.
+ */
+int
+sa_enable_share(sa_share_t share, char *protocol)
+{
+ char *sharepath;
+ struct stat st;
+ int err = 0;
+
+ sharepath = sa_get_share_attr(share, "path");
+ if (stat(sharepath, &st) < 0) {
+ err = SA_NO_SUCH_PATH;
+ } else {
+ /* tell the server about the share */
+ if (protocol != NULL) {
+ /* lookup protocol specific handler */
+ err = sa_proto_share(protocol, share);
+ if (err == SA_OK)
+ (void) sa_set_share_attr(share, "shared", "true");
+ } else {
+ /* tell all protocols */
+ err = sa_proto_share("nfs", share); /* only NFS for now */
+ (void) sa_set_share_attr(share, "shared", "true");
+ }
+ }
+ if (sharepath != NULL)
+ sa_free_attr_string(sharepath);
+ return (err);
+}
+
+/*
+ * sa_disable_share(share, protocol)
+ * Disable the specified share to the specified protocol.
+ * If protocol is NULL, then all protocols.
+ */
+int
+sa_disable_share(sa_share_t share, char *protocol)
+{
+ char *path;
+ char *shared;
+ int ret = SA_OK;
+
+ path = sa_get_share_attr(share, "path");
+ shared = sa_get_share_attr(share, "shared");
+
+ if (protocol != NULL) {
+ ret = sa_proto_unshare(protocol, path);
+ } else {
+ /* need to do all protocols */
+ ret = sa_proto_unshare("nfs", path);
+ }
+ if (ret == SA_OK)
+ (void) sa_set_share_attr(share, "shared", NULL);
+ if (path != NULL)
+ sa_free_attr_string(path);
+ if (shared != NULL)
+ sa_free_attr_string(shared);
+ return (ret);
+}
+
+/*
+ * sa_remove_share(share)
+ *
+ * remove the specified share from its containing group.
+ * Remove from the SMF or ZFS configuration space.
+ */
+
+int
+sa_remove_share(sa_share_t share)
+{
+ sa_group_t group;
+ int ret = SA_OK;
+ char *type;
+ int transient = 0;
+ char *groupname;
+ char *zfs;
+
+ type = sa_get_share_attr(share, "type");
+ group = sa_get_parent_group(share);
+ zfs = sa_get_group_attr(group, "zfs");
+ groupname = sa_get_group_attr(group, "name");
+ if (type != NULL && strcmp(type, "persist") != 0)
+ transient = 1;
+ if (type != NULL)
+ sa_free_attr_string(type);
+
+ /* remove the node from its group then free the memory */
+
+ /*
+ * need to test if "busy"
+ */
+ /* only do SMF action if permanent */
+ if (!transient || zfs != NULL) {
+ /* remove from legacy dfstab as well as possible SMF */
+ ret = sa_delete_legacy(share);
+ if (ret == SA_OK) {
+ if (!sa_group_is_zfs(group)) {
+ ret = sa_delete_share(scf_handle, group, share);
+ } else {
+ char *sharepath = sa_get_share_attr(share, "path");
+ if (sharepath != NULL) {
+ ret = sa_zfs_set_sharenfs(group, sharepath, 0);
+ sa_free_attr_string(sharepath);
+ }
+ }
+ }
+ }
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+ if (zfs != NULL)
+ sa_free_attr_string(zfs);
+
+ xmlUnlinkNode((xmlNodePtr)share);
+ xmlFreeNode((xmlNodePtr)share);
+ return (ret);
+}
+
+/*
+ * sa_move_share(group, share)
+ *
+ * move the specified share to the specified group. Update SMF
+ * appropriately.
+ */
+
+int
+sa_move_share(sa_group_t group, sa_share_t share)
+{
+ sa_group_t oldgroup;
+ int ret = SA_OK;
+
+ /* remove the node from its group then free the memory */
+
+ oldgroup = sa_get_parent_group(share);
+ if (oldgroup != group) {
+ xmlUnlinkNode((xmlNodePtr)share);
+ /* now that the share isn't in its old group, add to the new one */
+ xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
+ /* need to deal with SMF */
+ if (ret == SA_OK) {
+ /*
+ * need to remove from old group first and then add to
+ * new group. Ideally, we would do the other order but
+ * need to avoid having the share in two groups at the
+ * same time.
+ */
+ ret = sa_delete_share(scf_handle, oldgroup, share);
+ }
+ ret = sa_commit_share(scf_handle, group, share);
+ }
+ return (ret);
+}
+
+/*
+ * sa_get_parent_group(share)
+ *
+ * Return the containg group for the share. If a group was actually
+ * passed in, we don't want a parent so return NULL.
+ */
+
+sa_group_t
+sa_get_parent_group(sa_share_t share)
+{
+ xmlNodePtr node = NULL;
+ if (share != NULL) {
+ node = ((xmlNodePtr)share)->parent;
+ /*
+ * make sure parent is a group and not sharecfg since
+ * we may be cheating and passing in a group.
+ * Eventually, groups of groups might come into being.
+ */
+ if (node == NULL ||
+ xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
+ node = NULL;
+ }
+ return ((sa_group_t)node);
+}
+
+/*
+ * _sa_create_group(groupname)
+ *
+ * Create a group in the document. The caller will need to deal with
+ * configuration store and activation.
+ */
+
+sa_group_t
+_sa_create_group(char *groupname)
+{
+ xmlNodePtr node = NULL;
+
+ if (sa_valid_group_name(groupname)) {
+ node = xmlNewChild(sa_config_tree, NULL,
+ (xmlChar *)"group", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
+ xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
+ }
+ }
+ return ((sa_group_t)node);
+}
+
+/*
+ * _sa_create_zfs_group(group, groupname)
+ *
+ * Create a ZFS subgroup under the specified group. This may
+ * eventually form the basis of general sub-groups, but is currently
+ * restricted to ZFS.
+ */
+sa_group_t
+_sa_create_zfs_group(sa_group_t group, char *groupname)
+{
+ xmlNodePtr node = NULL;
+
+ node = xmlNewChild((xmlNodePtr)group, NULL,
+ (xmlChar *)"group", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
+ xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
+ }
+
+ return ((sa_group_t)node);
+}
+
+/*
+ * sa_create_group(groupname, *error)
+ *
+ * Create a new group with groupname. Need to validate that it is a
+ * legal name for SMF and the construct the SMF service instance of
+ * svc:/network/shares/group to implement the group. All necessary
+ * operational properties must be added to the group at this point
+ * (via the SMF transaction model).
+ */
+sa_group_t
+sa_create_group(char *groupname, int *error)
+{
+ xmlNodePtr node = NULL;
+ sa_group_t group;
+ int ret;
+ char rbacstr[256];
+
+ ret = SA_OK;
+
+ if (scf_handle == NULL) {
+ ret = SA_SYSTEM_ERR;
+ goto err;
+ }
+
+ group = sa_get_group(groupname);
+ if (group != NULL) {
+ ret = SA_DUPLICATE_NAME;
+ } else {
+ if (sa_valid_group_name(groupname)) {
+ node = xmlNewChild(sa_config_tree, NULL,
+ (xmlChar *)"group", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
+ /* default to the group being enabled */
+ xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
+ ret = sa_create_instance(scf_handle, groupname);
+ if (ret == SA_OK) {
+ ret = sa_start_transaction(scf_handle, "operation");
+ }
+ if (ret == SA_OK) {
+ ret = sa_set_property(scf_handle, "state", "enabled");
+ if (ret == SA_OK) {
+ ret = sa_end_transaction(scf_handle);
+ } else {
+ sa_abort_transaction(scf_handle);
+ }
+ }
+ if (ret == SA_OK) {
+ /* initialize the RBAC strings */
+ ret = sa_start_transaction(scf_handle, "general");
+ if (ret == SA_OK) {
+ (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s",
+ SA_RBAC_MANAGE, groupname);
+ ret = sa_set_property(scf_handle,
+ "action_authorization",
+ rbacstr);
+ }
+ if (ret == SA_OK) {
+ (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s",
+ SA_RBAC_VALUE, groupname);
+ ret = sa_set_property(scf_handle,
+ "value_authorization",
+ rbacstr);
+ }
+ if (ret == SA_OK) {
+ ret = sa_end_transaction(scf_handle);
+ } else {
+ sa_abort_transaction(scf_handle);
+ }
+ }
+ if (ret != SA_OK) {
+ /*
+ * Couldn't commit the group so we need to
+ * undo internally.
+ */
+ xmlUnlinkNode(node);
+ xmlFreeNode(node);
+ node = NULL;
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ } else {
+ ret = SA_INVALID_NAME;
+ }
+ }
+err:
+ if (error != NULL)
+ *error = ret;
+ return ((sa_group_t)node);
+}
+
+/*
+ * sa_remove_group(group)
+ *
+ * Remove the specified group. This deletes from the SMF repository.
+ * All property groups and properties are removed.
+ */
+
+int
+sa_remove_group(sa_group_t group)
+{
+ char *name;
+ int ret = SA_OK;
+
+ name = sa_get_group_attr(group, "name");
+ if (name != NULL) {
+ ret = sa_delete_instance(scf_handle, name);
+ sa_free_attr_string(name);
+ }
+ xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
+ xmlFreeNode((xmlNodePtr)group); /* now it is gone */
+ return (ret);
+}
+
+/*
+ * sa_update_config()
+ *
+ * Used to update legacy files that need to be updated in bulk
+ * Currently, this is a placeholder and will go away in a future
+ * release.
+ */
+
+int
+sa_update_config()
+{
+ struct stat st;
+
+ /*
+ * do legacy files first so we can tell when they change.
+ * This will go away when we start updating individual records
+ * rather than the whole file.
+ */
+ update_legacy_config();
+ /* update legacy timestamp */
+ if (stat(SA_LEGACY_DFSTAB, &st) >= 0) {
+ set_legacy_timestamp(sa_config_tree, SA_LEGACY_DFSTAB,
+ TSTAMP(st.st_ctim));
+ }
+ return (SA_OK);
+}
+
+/*
+ * get_node_attr(node, tag)
+ *
+ * Get the speficied tag(attribute) if it exists on the node. This is
+ * used internally by a number of attribute oriented functions.
+ */
+
+static char *
+get_node_attr(void *nodehdl, char *tag)
+{
+ xmlNodePtr node = (xmlNodePtr)nodehdl;
+ xmlChar *name = NULL;
+
+ if (node != NULL) {
+ name = xmlGetProp(node, (xmlChar *)tag);
+ }
+ return ((char *)name);
+}
+
+/*
+ * get_node_attr(node, tag)
+ *
+ * Set the speficied tag(attribute) to the specified value This is
+ * used internally by a number of attribute oriented functions. It
+ * doesn't update the repository, only the internal document state.
+ */
+
+void
+set_node_attr(void *nodehdl, char *tag, char *value)
+{
+ xmlNodePtr node = (xmlNodePtr)nodehdl;
+ if (node != NULL && tag != NULL) {
+ if (value != NULL) {
+ xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value);
+ } else {
+ xmlUnsetProp(node, (xmlChar *)tag);
+ }
+ }
+}
+
+/*
+ * sa_get_group_attr(group, tag)
+ *
+ * Get the specied attribute, if defined, for the group.
+ */
+
+char *
+sa_get_group_attr(sa_group_t group, char *tag)
+{
+ return (get_node_attr((void *)group, tag));
+}
+
+/*
+ * sa_set_group_attr(group, tag, value)
+ *
+ * set the specified tag/attribute on the group using value as its
+ * value.
+ *
+ * This will result in setting the property in the SMF repository as
+ * well as in the internal document.
+ */
+
+int
+sa_set_group_attr(sa_group_t group, char *tag, char *value)
+{
+ int ret;
+ char *groupname;
+
+ groupname = sa_get_group_attr(group, "name");
+ ret = sa_get_instance(scf_handle, groupname);
+ if (ret == SA_OK) {
+ set_node_attr((void *)group, tag, value);
+ ret = sa_start_transaction(scf_handle, "operation");
+ if (ret == SA_OK) {
+ ret = sa_set_property(scf_handle, tag, value);
+ if (ret == SA_OK)
+ (void) sa_end_transaction(scf_handle);
+ else {
+ sa_abort_transaction(scf_handle);
+ }
+ }
+ }
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+ return (ret);
+}
+
+/*
+ * sa_get_share_attr(share, tag)
+ *
+ * Return the value of the tag/attribute set on the specified
+ * share. Returns NULL if the tag doesn't exist.
+ */
+
+char *
+sa_get_share_attr(sa_share_t share, char *tag)
+{
+ return (get_node_attr((void *)share, tag));
+}
+
+/*
+ * sa_get_resource(group, resource)
+ *
+ * Search all the shares in the speified group for a share with a
+ * resource name matching the one specified.
+ *
+ * In the future, it may be advantageous to allow group to be NULL and
+ * search all groups but that isn't needed at present.
+ */
+
+sa_share_t
+sa_get_resource(sa_group_t group, char *resource)
+{
+ sa_share_t share = NULL;
+ char *name = NULL;
+
+ if (resource != NULL) {
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ name = sa_get_share_attr(share, "resource");
+ if (name != NULL) {
+ if (strcmp(name, resource) == 0)
+ break;
+ sa_free_attr_string(name);
+ name = NULL;
+ }
+ }
+ if (name != NULL)
+ sa_free_attr_string(name);
+ }
+ return ((sa_share_t)share);
+}
+
+/*
+ * _sa_set_share_description(share, description)
+ *
+ * Add a description tag with text contents to the specified share.
+ * A separate XML tag is used rather than a property.
+ */
+
+xmlNodePtr
+_sa_set_share_description(sa_share_t share, char *content)
+{
+ xmlNodePtr node;
+ node = xmlNewChild((xmlNodePtr)share,
+ NULL, (xmlChar *)"description", NULL);
+ xmlNodeSetContent(node, (xmlChar *)content);
+ return (node);
+}
+
+/*
+ * sa_set_share_attr(share, tag, value)
+ *
+ * Set the share attribute specified by tag to the specified value. In
+ * the case of "resource", enforce a no duplicates in a group rule. If
+ * the share is not transient, commit the changes to the repository
+ * else just update the share internally.
+ */
+
+int
+sa_set_share_attr(sa_share_t share, char *tag, char *value)
+{
+ sa_group_t group;
+ sa_share_t resource;
+ int ret = SA_OK;
+
+ group = sa_get_parent_group(share);
+
+ /*
+ * There are some attributes that may have specific
+ * restrictions on them. Initially, only "resource" has
+ * special meaning that needs to be checked. Only one instance
+ * of a resource name may exist within a group.
+ */
+
+ if (strcmp(tag, "resource") == 0) {
+ resource = sa_get_resource(group, value);
+ if (resource != share && resource != NULL)
+ ret = SA_DUPLICATE_NAME;
+ }
+ if (ret == SA_OK) {
+ set_node_attr((void *)share, tag, value);
+ if (group != NULL) {
+ char *type;
+ /* we can probably optimize this some */
+ type = sa_get_share_attr(share, "type");
+ if (type == NULL || strcmp(type, "transient") != 0)
+ ret = sa_commit_share(scf_handle, group, share);
+ if (type != NULL)
+ sa_free_attr_string(type);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_get_property_attr(prop, tag)
+ *
+ * Get the value of the specified property attribute. Standard
+ * attributes are "type" and "value".
+ */
+
+char *
+sa_get_property_attr(sa_property_t prop, char *tag)
+{
+ return (get_node_attr((void *)prop, tag));
+}
+
+/*
+ * sa_get_optionset_attr(prop, tag)
+ *
+ * Get the value of the specified property attribute. Standard
+ * attribute is "type".
+ */
+
+char *
+sa_get_optionset_attr(sa_property_t optionset, char *tag)
+{
+ return (get_node_attr((void *)optionset, tag));
+
+}
+
+/*
+ * sa_set_optionset_attr(optionset, tag, value)
+ *
+ * Set the specified attribute(tag) to the specified value on the
+ * optionset.
+ */
+
+void
+sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
+{
+ set_node_attr((void *)optionset, tag, value);
+}
+
+/*
+ * sa_free_attr_string(string)
+ *
+ * Free the string that was returned in one of the sa_get_*_attr()
+ * functions.
+ */
+
+void
+sa_free_attr_string(char *string)
+{
+ xmlFree((xmlChar *)string);
+}
+
+/*
+ * sa_get_optionset(group, proto)
+ *
+ * Return the optionset, if it exists, that is associated with the
+ * specified protocol.
+ */
+
+sa_optionset_t
+sa_get_optionset(void *group, char *proto)
+{
+ xmlNodePtr node;
+ xmlChar *value = NULL;
+
+ for (node = ((xmlNodePtr)group)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
+ value = xmlGetProp(node, (xmlChar *)"type");
+ if (proto != NULL) {
+ if (value != NULL &&
+ xmlStrcmp(value, (xmlChar *)proto) == 0) {
+ break;
+ }
+ if (value != NULL) {
+ xmlFree(value);
+ value = NULL;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ if (value != NULL)
+ xmlFree(value);
+ return ((sa_optionset_t)node);
+}
+
+/*
+ * sa_get_next_optionset(optionset)
+ *
+ * Return the next optionset in the group. NULL if this was the last.
+ */
+
+sa_optionset_t
+sa_get_next_optionset(sa_optionset_t optionset)
+{
+ xmlNodePtr node;
+
+ for (node = ((xmlNodePtr)optionset)->next; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
+ break;
+ }
+ }
+ return ((sa_optionset_t)node);
+}
+
+/*
+ * sa_get_security(group, sectype, proto)
+ *
+ * Return the security optionset. The internal name is a hold over
+ * from the implementation and will be changed before the API is
+ * finalized. This is really a named optionset that can be negotiated
+ * as a group of properties (like NFS security options).
+ */
+
+sa_security_t
+sa_get_security(sa_group_t group, char *sectype, char *proto)
+{
+ xmlNodePtr node;
+ xmlChar *value = NULL;
+
+ for (node = ((xmlNodePtr)group)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
+ if (proto != NULL) {
+ value = xmlGetProp(node, (xmlChar *)"type");
+ if (value == NULL ||
+ (value != NULL &&
+ xmlStrcmp(value, (xmlChar *)proto) != 0)) {
+ /* it doesn't match so continue */
+ xmlFree(value);
+ value = NULL;
+ continue;
+ }
+ }
+ if (value != NULL) {
+ xmlFree(value);
+ value = NULL;
+ }
+ /* potential match */
+ if (sectype != NULL) {
+ value = xmlGetProp(node, (xmlChar *)"sectype");
+ if (value != NULL &&
+ xmlStrcmp(value, (xmlChar *)sectype) == 0) {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ if (value != NULL) {
+ xmlFree(value);
+ value = NULL;
+ }
+ }
+ if (value != NULL)
+ xmlFree(value);
+ return ((sa_security_t)node);
+}
+
+/*
+ * sa_get_next_security(security)
+ *
+ * Get the next security optionset if one exists.
+ */
+
+sa_security_t
+sa_get_next_security(sa_security_t security)
+{
+ xmlNodePtr node;
+
+ for (node = ((xmlNodePtr)security)->next; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
+ break;
+ }
+ }
+ return ((sa_security_t)node);
+}
+
+/*
+ * sa_get_property(optionset, prop)
+ *
+ * Get the property object with the name specified in prop from the
+ * optionset.
+ */
+
+sa_property_t
+sa_get_property(sa_optionset_t optionset, char *prop)
+{
+ xmlNodePtr node = (xmlNodePtr)optionset;
+ xmlChar *value = NULL;
+
+ if (optionset == NULL)
+ return (NULL);
+
+ for (node = node->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
+ if (prop == NULL)
+ break;
+ value = xmlGetProp(node, (xmlChar *)"type");
+ if (value != NULL && xmlStrcmp(value, (xmlChar *)prop) == 0) {
+ break;
+ }
+ if (value != NULL) {
+ xmlFree(value);
+ value = NULL;
+ }
+ }
+ }
+ if (value != NULL)
+ xmlFree(value);
+ if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
+ /* avoid a non option node -- it is possible to be a text node */
+ node = NULL;
+ }
+ return ((sa_property_t)node);
+}
+
+/*
+ * sa_get_next_property(property)
+ *
+ * Get the next property following the specified property. NULL if
+ * this was the last.
+ */
+
+sa_property_t
+sa_get_next_property(sa_property_t property)
+{
+ xmlNodePtr node;
+
+ for (node = ((xmlNodePtr)property)->next; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
+ break;
+ }
+ }
+ return ((sa_property_t)node);
+}
+
+/*
+ * sa_set_share_description(share, content)
+ *
+ * Set the description of share to content.
+ */
+
+int
+sa_set_share_description(sa_share_t share, char *content)
+{
+ xmlNodePtr node;
+ sa_group_t group;
+ int ret = SA_OK;
+
+ for (node = ((xmlNodePtr)share)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
+ break;
+ }
+ }
+ group = sa_get_parent_group(share);
+ /* no existing description but want to add */
+ if (node == NULL && content != NULL) {
+ /* add a description */
+ node = _sa_set_share_description(share, content);
+ } else if (node != NULL && content != NULL) {
+ /* update a description */
+ xmlNodeSetContent(node, (xmlChar *)content);
+ } else if (node != NULL && content == NULL) {
+ /* remove an existing description */
+ xmlUnlinkNode(node);
+ xmlFreeNode(node);
+ }
+ if (group != NULL && is_persistent((sa_group_t)share))
+ ret = sa_commit_share(scf_handle, group, share);
+ return (ret);
+}
+
+/*
+ * fixproblemchars(string)
+ *
+ * don't want any newline or tab characters in the text since these
+ * could break display of data and legacy file formats.
+ */
+static void
+fixproblemchars(char *str)
+{
+ int c;
+ for (c = *str; c != '\0'; c = *++str) {
+ if (c == '\t' || c == '\n')
+ *str = ' ';
+ else if (c == '"')
+ *str = '\'';
+ }
+}
+
+/*
+ * sa_get_share_description(share)
+ *
+ * Return the description text for the specified share if it
+ * exists. NULL if no description exists.
+ */
+
+char *
+sa_get_share_description(sa_share_t share)
+{
+ xmlChar *description = NULL;
+ xmlNodePtr node;
+
+ for (node = ((xmlNodePtr)share)->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
+ break;
+ }
+ }
+ if (node != NULL) {
+ description = xmlNodeGetContent((xmlNodePtr)share);
+ fixproblemchars((char *)description);
+ }
+ return ((char *)description);
+}
+
+/*
+ * sa_free(share_description(description)
+ *
+ * Free the description string.
+ */
+
+void
+sa_free_share_description(char *description)
+{
+ xmlFree((xmlChar *)description);
+}
+
+/*
+ * sa_create_optionset(group, proto)
+ *
+ * Create an optionset for the specified protocol in the specied
+ * group. This is manifested as a property group within SMF.
+ */
+
+sa_optionset_t
+sa_create_optionset(sa_group_t group, char *proto)
+{
+ sa_optionset_t optionset;
+ sa_group_t parent = group;
+
+ optionset = sa_get_optionset(group, proto);
+ if (optionset != NULL) {
+ /* can't have a duplicate protocol */
+ optionset = NULL;
+ } else {
+ optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
+ NULL,
+ (xmlChar *)"optionset",
+ NULL);
+ /*
+ * only put to repository if on a group and we were
+ * able to create an optionset.
+ */
+ if (optionset != NULL) {
+ char oname[256];
+ char *groupname;
+ char *id = NULL;
+
+ if (sa_is_share(group))
+ parent = sa_get_parent_group((sa_share_t)group);
+
+ sa_set_optionset_attr(optionset, "type", proto);
+
+ if (sa_is_share(group)) {
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ }
+ (void) sa_optionset_name(optionset, oname,
+ sizeof (oname), id);
+ groupname = sa_get_group_attr(parent, "name");
+ if (groupname != NULL && is_persistent(group)) {
+ (void) sa_get_instance(scf_handle, groupname);
+ sa_free_attr_string(groupname);
+ (void) sa_create_pgroup(scf_handle, oname);
+ }
+ if (id != NULL)
+ sa_free_attr_string(id);
+ }
+ }
+ return (optionset);
+}
+
+/*
+ * sa_get_property_parent(property)
+ *
+ * Given a property, return the object it is a property of. This will
+ * be an optionset of some type.
+ */
+
+static sa_optionset_t
+sa_get_property_parent(sa_property_t property)
+{
+ xmlNodePtr node = NULL;
+
+ if (property != NULL) {
+ node = ((xmlNodePtr)property)->parent;
+ }
+ return ((sa_optionset_t)node);
+}
+
+/*
+ * sa_get_optionset_parent(optionset)
+ *
+ * Return the parent of the specified optionset. This could be a group
+ * or a share.
+ */
+
+static sa_group_t
+sa_get_optionset_parent(sa_optionset_t optionset)
+{
+ xmlNodePtr node = NULL;
+
+ if (optionset != NULL) {
+ node = ((xmlNodePtr)optionset)->parent;
+ }
+ return ((sa_group_t)node);
+}
+
+/*
+ * zfs_needs_update(share)
+ *
+ * In order to avoid making multiple updates to a ZFS share when
+ * setting properties, the share attribute "changed" will be set to
+ * true when a property is added or modifed. When done adding
+ * properties, we can then detect that an update is needed. We then
+ * clear the state here to detect additional changes.
+ */
+
+static int
+zfs_needs_update(sa_share_t share)
+{
+ char *attr;
+ int result = 0;
+
+ attr = sa_get_share_attr(share, "changed");
+ if (attr != NULL) {
+ sa_free_attr_string(attr);
+ result = 1;
+ }
+ set_node_attr((void *)share, "changed", NULL);
+ return (result);
+}
+
+/*
+ * zfs_set_update(share)
+ *
+ * Set the changed attribute of the share to true.
+ */
+
+static void
+zfs_set_update(sa_share_t share)
+{
+ set_node_attr((void *)share, "changed", "true");
+}
+
+/*
+ * sa_commit_properties(optionset, clear)
+ *
+ * Check if SMF or ZFS config and either update or abort the pending
+ * changes.
+ */
+
+int
+sa_commit_properties(sa_optionset_t optionset, int clear)
+{
+ sa_group_t group;
+ sa_group_t parent;
+ int zfs = 0;
+ int needsupdate = 0;
+ int ret = SA_OK;
+
+ group = sa_get_optionset_parent(optionset);
+ if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
+ /* only update ZFS if on a share */
+ parent = sa_get_parent_group(group);
+ zfs++;
+ if (parent != NULL && is_zfs_group(parent)) {
+ needsupdate = zfs_needs_update(group);
+ } else {
+ zfs = 0;
+ }
+ }
+ if (zfs) {
+ if (!clear && needsupdate)
+ ret = sa_zfs_update((sa_share_t)group);
+ } else {
+ if (clear)
+ (void) sa_abort_transaction(scf_handle);
+ else
+ ret = sa_end_transaction(scf_handle);
+ }
+ return (ret);
+}
+
+/*
+ * sa_destroy_optionset(optionset)
+ *
+ * Remove the optionset from its group. Update the repostory to
+ * reflect this change.
+ */
+
+int
+sa_destroy_optionset(sa_optionset_t optionset)
+{
+ char name[256];
+ int len;
+ int ret;
+ char *id = NULL;
+ sa_group_t group;
+ int ispersist = 1;
+
+ /* now delete the prop group */
+ group = sa_get_optionset_parent(optionset);
+ if (group != NULL && sa_is_share(group)) {
+ ispersist = is_persistent(group);
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ }
+ if (ispersist) {
+ len = sa_optionset_name(optionset, name, sizeof (name), id);
+ if (len > 0) {
+ ret = sa_delete_pgroup(scf_handle, name);
+ }
+ }
+ xmlUnlinkNode((xmlNodePtr)optionset);
+ xmlFreeNode((xmlNodePtr)optionset);
+ if (id != NULL)
+ sa_free_attr_string(id);
+ return (ret);
+}
+
+/* private to the implementation */
+int
+_sa_remove_optionset(sa_optionset_t optionset)
+{
+ int ret = SA_OK;
+
+ xmlUnlinkNode((xmlNodePtr)optionset);
+ xmlFreeNode((xmlNodePtr)optionset);
+ return (ret);
+}
+
+/*
+ * sa_create_security(group, sectype, proto)
+ *
+ * Create a security optionset (one that has a type name and a
+ * proto). Security is left over from a pure NFS implementation. The
+ * naming will change in the future when the API is released.
+ */
+sa_security_t
+sa_create_security(sa_group_t group, char *sectype, char *proto)
+{
+ sa_security_t security;
+ char *id = NULL;
+ sa_group_t parent;
+ char *groupname = NULL;
+
+ if (group != NULL && sa_is_share(group)) {
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ parent = sa_get_parent_group(group);
+ if (parent != NULL)
+ groupname = sa_get_group_attr(parent, "name");
+ } else if (group != NULL) {
+ groupname = sa_get_group_attr(group, "name");
+ }
+
+ security = sa_get_security(group, sectype, proto);
+ if (security != NULL) {
+ /* can't have a duplicate security option */
+ security = NULL;
+ } else {
+ security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
+ NULL,
+ (xmlChar *)"security",
+ NULL);
+ if (security != NULL) {
+ char oname[256];
+ sa_set_security_attr(security, "type", proto);
+
+ sa_set_security_attr(security, "sectype", sectype);
+ (void) sa_security_name(security, oname,
+ sizeof (oname), id);
+ if (groupname != NULL && is_persistent(group)) {
+ (void) sa_get_instance(scf_handle, groupname);
+ (void) sa_create_pgroup(scf_handle, oname);
+ }
+ }
+ }
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+ return (security);
+}
+
+/*
+ * sa_destroy_security(security)
+ *
+ * Remove the specified optionset from the document and the
+ * configuration.
+ */
+
+int
+sa_destroy_security(sa_security_t security)
+{
+ char name[256];
+ int len;
+ int ret = SA_OK;
+ char *id = NULL;
+ sa_group_t group;
+ int iszfs = 0;
+ int ispersist = 1;
+
+ group = sa_get_optionset_parent(security);
+
+ if (group != NULL)
+ iszfs = sa_group_is_zfs(group);
+
+ if (group != NULL && !iszfs) {
+ if (sa_is_share(group))
+ ispersist = is_persistent(group);
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ }
+ if (ispersist) {
+ len = sa_security_name(security, name, sizeof (name), id);
+ if (!iszfs && len > 0) {
+ ret = sa_delete_pgroup(scf_handle, name);
+ }
+ }
+ xmlUnlinkNode((xmlNodePtr)security);
+ xmlFreeNode((xmlNodePtr)security);
+ if (iszfs) {
+ ret = sa_zfs_update(group);
+ }
+ if (id != NULL)
+ sa_free_attr_string(id);
+ return (ret);
+}
+
+/*
+ * sa_get_security_attr(optionset, tag)
+ *
+ * Return the specified attribute value from the optionset.
+ */
+
+char *
+sa_get_security_attr(sa_property_t optionset, char *tag)
+{
+ return (get_node_attr((void *)optionset, tag));
+
+}
+
+/*
+ * sa_set_security_attr(optionset, tag, value)
+ *
+ * Set the optioset attribute specied by tag to the specified value.
+ */
+
+void
+sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
+{
+ set_node_attr((void *)optionset, tag, value);
+}
+
+/*
+ * is_nodetype(node, type)
+ *
+ * Check to see if node is of the type specified.
+ */
+
+static int
+is_nodetype(void *node, char *type)
+{
+ return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
+}
+
+/*
+ * sa_set_prop_by_prop(optionset, group, prop, type)
+ *
+ * Add/remove/update the specified property prop into the optionset or
+ * share. If a share, sort out which property group based on GUID. In
+ * all cases, the appropriate transaction is set (or ZFS share is
+ * marked as needing an update)
+ */
+
+#define SA_PROP_OP_REMOVE 1
+#define SA_PROP_OP_ADD 2
+#define SA_PROP_OP_UPDATE 3
+static int
+sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
+ sa_property_t prop, int type)
+{
+ char *name;
+ char *valstr;
+ int ret = SA_OK;
+ scf_transaction_entry_t *entry;
+ scf_value_t *value;
+ int opttype; /* 1 == optionset, 0 == security */
+ char *id = NULL;
+ int iszfs = 0;
+ int isshare = 0;
+ sa_group_t parent = NULL;
+
+ if (!is_persistent(group)) {
+ /*
+ * if the group/share is not persistent we don't need
+ * to do anything here
+ */
+ return (SA_OK);
+ }
+ name = sa_get_property_attr(prop, "type");
+ valstr = sa_get_property_attr(prop, "value");
+ entry = scf_entry_create(scf_handle->handle);
+ opttype = is_nodetype((void *)optionset, "optionset");
+
+ if (valstr != NULL && entry != NULL) {
+ if (sa_is_share(group)) {
+ isshare = 1;
+ parent = sa_get_parent_group(group);
+ if (parent != NULL) {
+ iszfs = is_zfs_group(parent);
+ }
+ } else {
+ iszfs = is_zfs_group(group);
+ }
+ if (!iszfs) {
+ if (scf_handle->trans == NULL) {
+ char oname[256];
+ char *groupname = NULL;
+ if (isshare) {
+ if (parent != NULL) {
+ groupname = sa_get_group_attr(parent, "name");
+ }
+ id = sa_get_share_attr((sa_share_t)group, "id");
+ } else {
+ groupname = sa_get_group_attr(group, "name");
+ }
+ if (groupname != NULL) {
+ ret = sa_get_instance(scf_handle, groupname);
+ sa_free_attr_string(groupname);
+ }
+ if (opttype)
+ (void) sa_optionset_name(optionset, oname,
+ sizeof (oname), id);
+ else
+ (void) sa_security_name(optionset, oname,
+ sizeof (oname), id);
+ ret = sa_start_transaction(scf_handle, oname);
+ }
+ if (ret == SA_OK) {
+ switch (type) {
+ case SA_PROP_OP_REMOVE:
+ ret = scf_transaction_property_delete(scf_handle->trans,
+ entry,
+ name);
+ break;
+ case SA_PROP_OP_ADD:
+ case SA_PROP_OP_UPDATE:
+ value = scf_value_create(scf_handle->handle);
+ if (value != NULL) {
+ if (type == SA_PROP_OP_ADD)
+ ret = scf_transaction_property_new(
+ scf_handle->trans,
+ entry,
+ name,
+ SCF_TYPE_ASTRING);
+ else
+ ret = scf_transaction_property_change(
+ scf_handle->trans,
+ entry,
+ name,
+ SCF_TYPE_ASTRING);
+ if (ret == 0) {
+ ret = scf_value_set_astring(value, valstr);
+ if (ret == 0)
+ ret = scf_entry_add_value(entry, value);
+ if (ret != 0) {
+ scf_value_destroy(value);
+ ret = SA_SYSTEM_ERR;
+ }
+ } else {
+ scf_entry_destroy(entry);
+ ret = SA_SYSTEM_ERR;
+ }
+ break;
+ }
+ }
+ }
+ } else {
+ /*
+ * ZFS update. The calling function would have updated
+ * the internal XML structure. Just need to flag it as
+ * changed for ZFS.
+ */
+ zfs_set_update((sa_share_t)group);
+ }
+ }
+
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (valstr != NULL)
+ sa_free_attr_string(valstr);
+ else if (entry != NULL)
+ scf_entry_destroy(entry);
+
+ if (ret == -1)
+ ret = SA_SYSTEM_ERR;
+
+ return (ret);
+}
+
+/*
+ * sa_create_property(name, value)
+ *
+ * Create a new property with the specified name and value.
+ */
+
+sa_property_t
+sa_create_property(char *name, char *value)
+{
+ xmlNodePtr node;
+
+ node = xmlNewNode(NULL, (xmlChar *)"option");
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
+ xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
+ }
+ return ((sa_property_t)node);
+}
+
+/*
+ * sa_add_property(object, property)
+ *
+ * Add the specified property to the object. Issue the appropriate
+ * transaction or mark a ZFS object as needing an update.
+ */
+
+int
+sa_add_property(void *object, sa_property_t property)
+{
+ int ret = SA_OK;
+ sa_group_t parent;
+ sa_group_t group;
+ char *proto;
+
+ proto = sa_get_optionset_attr(object, "type");
+ if (property != NULL) {
+ if ((ret = sa_valid_property(object, proto, property)) == SA_OK) {
+ property = (sa_property_t)xmlAddChild((xmlNodePtr)object,
+ (xmlNodePtr)property);
+ } else {
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ return (ret);
+ }
+ }
+
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+
+ parent = sa_get_parent_group(object);
+ if (!is_persistent(parent)) {
+ return (ret);
+ }
+
+ if (sa_is_share(parent))
+ group = sa_get_parent_group(parent);
+ else
+ group = parent;
+
+ if (property == NULL)
+ ret = SA_NO_MEMORY;
+ else {
+ char oname[256];
+
+ if (!is_zfs_group(group)) {
+ char *id = NULL;
+ if (sa_is_share((sa_group_t)parent)) {
+ id = sa_get_share_attr((sa_share_t)parent, "id");
+ }
+ if (scf_handle->trans == NULL) {
+ if (is_nodetype(object, "optionset"))
+ (void) sa_optionset_name((sa_optionset_t)object,
+ oname, sizeof (oname), id);
+ else
+ (void) sa_security_name((sa_optionset_t)object,
+ oname, sizeof (oname), id);
+ ret = sa_start_transaction(scf_handle, oname);
+ }
+ if (ret == SA_OK) {
+ char *name;
+ char *value;
+ name = sa_get_property_attr(property, "type");
+ value = sa_get_property_attr(property, "value");
+ if (name != NULL && value != NULL) {
+ if (scf_handle->scf_state == SCH_STATE_INIT)
+ ret = sa_set_property(scf_handle, name, value);
+ } else
+ ret = SA_CONFIG_ERR;
+ if (name != NULL)
+ sa_free_attr_string(name);
+ if (value != NULL)
+ sa_free_attr_string(value);
+ }
+ if (id != NULL)
+ sa_free_attr_string(id);
+ } else {
+ /*
+ * ZFS is a special case. We do want to allow editing
+ * property/security lists since we can have a better
+ * syntax and we also want to keep things consistent
+ * when possible.
+ *
+ * Right now, we defer until the sa_commit_properties
+ * so we can get them all at once. We do need to mark
+ * the share as "changed"
+ */
+ zfs_set_update((sa_share_t)parent);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_remove_property(property)
+ *
+ * Remove the specied property from its containing object. Update the
+ * repository as appropriate.
+ */
+
+int
+sa_remove_property(sa_property_t property)
+{
+ int ret = SA_OK;
+
+ if (property != NULL) {
+ sa_optionset_t optionset;
+ sa_group_t group;
+ optionset = sa_get_property_parent(property);
+ if (optionset != NULL) {
+ group = sa_get_optionset_parent(optionset);
+ if (group != NULL) {
+ ret = sa_set_prop_by_prop(optionset, group, property,
+ SA_PROP_OP_REMOVE);
+ }
+ }
+ xmlUnlinkNode((xmlNodePtr)property);
+ xmlFreeNode((xmlNodePtr)property);
+ } else {
+ ret = SA_NO_SUCH_PROP;
+ }
+ return (ret);
+}
+
+/*
+ * sa_update_property(property, value)
+ *
+ * Update the specified property to the new value. If value is NULL,
+ * we currently treat this as a remove.
+ */
+
+int
+sa_update_property(sa_property_t property, char *value)
+{
+ int ret = SA_OK;
+ if (value == NULL) {
+ return (sa_remove_property(property));
+ } else {
+ sa_optionset_t optionset;
+ sa_group_t group;
+ set_node_attr((void *)property, "value", value);
+ optionset = sa_get_property_parent(property);
+ if (optionset != NULL) {
+ group = sa_get_optionset_parent(optionset);
+ if (group != NULL) {
+ ret = sa_set_prop_by_prop(optionset, group, property,
+ SA_PROP_OP_UPDATE);
+ }
+ } else {
+ ret = SA_NO_SUCH_PROP;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * _sa_get_next_error(node)
+ *
+ * Get the next (first if node==NULL) error node in the
+ * document. "error" nodes are added if there were syntax errors
+ * during parsing of the /etc/dfs/dfstab file. They are preserved in
+ * comments and recreated in the doc on the next parse.
+ */
+
+xmlNodePtr
+_sa_get_next_error(xmlNodePtr node)
+{
+ if (node == NULL) {
+ for (node = sa_config_tree->xmlChildrenNode;
+ node != NULL; node = node->next)
+ if (xmlStrcmp(node->name, (xmlChar *)"error") == 0)
+ return (node);
+ } else {
+ for (node = node->next; node != NULL; node = node->next)
+ if (xmlStrcmp(node->name, (xmlChar *)"error") == 0)
+ return (node);
+ }
+ return (node);
+}
+
+/*
+ * sa_get_protocol_property(propset, prop)
+ *
+ * Get the specified protocol specific property. These are global to
+ * the protocol and not specific to a group or share.
+ */
+
+sa_property_t
+sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
+{
+ xmlNodePtr node = (xmlNodePtr)propset;
+ xmlChar *value = NULL;
+
+ for (node = node->children; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
+ if (prop == NULL)
+ break;
+ value = xmlGetProp(node, (xmlChar *)"type");
+ if (value != NULL &&
+ xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
+ break;
+ }
+ if (value != NULL) {
+ xmlFree(value);
+ value = NULL;
+ }
+ }
+ }
+ if (value != NULL)
+ xmlFree(value);
+ if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
+ /* avoid a non option node -- it is possible to be a text node */
+ node = NULL;
+ }
+ return ((sa_property_t)node);
+}
+
+/*
+ * sa_get_next_protocol_property(prop)
+ *
+ * Get the next protocol specific property in the list.
+ */
+
+sa_property_t
+sa_get_next_protocol_property(sa_property_t prop)
+{
+ xmlNodePtr node;
+
+ for (node = ((xmlNodePtr)prop)->next; node != NULL;
+ node = node->next) {
+ if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
+ break;
+ }
+ }
+ return ((sa_property_t)node);
+}
+
+/*
+ * sa_set_protocol_property(prop, value)
+ *
+ * Set the specified property to have the new value. The protocol
+ * specific plugin will then be called to update the property.
+ */
+
+int
+sa_set_protocol_property(sa_property_t prop, char *value)
+{
+ sa_protocol_properties_t propset;
+ char *proto;
+ int ret = SA_INVALID_PROTOCOL;
+
+ propset = ((xmlNodePtr)prop)->parent;
+ if (propset != NULL) {
+ proto = sa_get_optionset_attr(propset, "type");
+ if (proto != NULL) {
+ set_node_attr((xmlNodePtr)prop, "value", value);
+ ret = sa_proto_set_property(proto, prop);
+ sa_free_attr_string(prop);
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_add_protocol_property(propset, prop)
+ *
+ * Add a new property to the protocol sepcific property set.
+ */
+
+int
+sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
+{
+ xmlNodePtr node;
+
+ /* should check for legitimacy */
+ node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
+ if (node != NULL)
+ return (SA_OK);
+ return (SA_NO_MEMORY);
+}
+
+/*
+ * sa_create_protocol_properties(proto)
+ *
+ * Create a protocol specifity property set.
+ */
+
+sa_protocol_properties_t
+sa_create_protocol_properties(char *proto)
+{
+ xmlNodePtr node;
+ node = xmlNewNode(NULL, (xmlChar *)"propertyset");
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
+ }
+ return (node);
+}
diff --git a/usr/src/lib/libshare/common/libshare.h b/usr/src/lib/libshare/common/libshare.h
new file mode 100644
index 0000000000..1c172f6f2a
--- /dev/null
+++ b/usr/src/lib/libshare/common/libshare.h
@@ -0,0 +1,217 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * basic API declarations for share management
+ */
+
+#ifndef _LIBSHARE_H
+#define _LIBSHARE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Basic datatypes for most functions
+ */
+typedef void *sa_group_t;
+typedef void *sa_share_t;
+typedef void *sa_property_t;
+typedef void *sa_optionset_t;
+typedef void *sa_security_t;
+typedef void *sa_protocol_properties_t;
+
+typedef void *sa_handle_t; /* opaque handle to access core functions */
+
+/*
+ * defined error values
+ */
+
+#define SA_OK 0
+#define SA_NO_SUCH_PATH 1 /* provided path doesn't exist */
+#define SA_NO_MEMORY 2 /* no memory for data structures */
+#define SA_DUPLICATE_NAME 3 /* object name is already in use */
+#define SA_BAD_PATH 4 /* not a full path */
+#define SA_NO_SUCH_GROUP 5 /* group is not defined */
+#define SA_CONFIG_ERR 6 /* system configuration error */
+#define SA_SYSTEM_ERR 7 /* system error, use errno */
+#define SA_SYNTAX_ERR 8 /* syntax error on command line */
+#define SA_NO_PERMISSION 9 /* no permission for operation */
+#define SA_BUSY 10 /* resource is busy */
+#define SA_NO_SUCH_PROP 11 /* property doesn't exist */
+#define SA_INVALID_NAME 12 /* name of object is invalid */
+#define SA_INVALID_PROTOCOL 13 /* specified protocol not valid */
+#define SA_NOT_ALLOWED 14 /* operation not allowed */
+#define SA_BAD_VALUE 15 /* bad value for property */
+#define SA_INVALID_SECURITY 16 /* invalid security type */
+#define SA_NO_SUCH_SECURITY 17 /* security set not found */
+#define SA_VALUE_CONFLICT 18 /* property value conflict */
+#define SA_NOT_IMPLEMENTED 19 /* plugin interface not implemented */
+#define SA_INVALID_PATH 20 /* path is sub-dir of existing share */
+#define SA_NOT_SUPPORTED 21 /* operation not supported for proto */
+#define SA_PROP_SHARE_ONLY 22 /* property valid on share only */
+#define SA_NOT_SHARED 23 /* path is not shared */
+
+/* API Initialization */
+#define SA_INIT_SHARE_API 0x0001 /* init share specific interface */
+#define SA_INIT_CONTROL_API 0x0002 /* init control specific interface */
+
+/* not part of API returns */
+#define SA_LEGACY_ERR 32 /* share/unshare error return */
+
+/*
+ * other defined values
+ */
+
+#define SA_MAX_NAME_LEN 100 /* must fit service instance name */
+#define SA_SHARE_PERMANENT 2 /* share goes to repository */
+#define SA_SHARE_LEGACY 1 /* share is in dfstab only */
+#define SA_SHARE_TRANSIENT 0 /* shared but not across reboot */
+
+/* RBAC related */
+#define SA_RBAC_MANAGE "solaris.smf.manage.shares"
+#define SA_RBAC_VALUE "solaris.smf.value.shares"
+
+/*
+ * legacy files
+ */
+
+#define SA_LEGACY_DFSTAB "/etc/dfs/dfstab"
+#define SA_LEGACY_SHARETAB "/etc/dfs/sharetab"
+
+/*
+ * SMF related
+ */
+
+#define SA_SVC_FMRI_BASE "svc:/network/shares/group"
+
+/* initialization */
+extern void sa_init(int);
+extern void sa_fini(void);
+extern int sa_update_config(void);
+extern char *sa_errorstr(int);
+
+/* protocol names */
+extern int sa_get_protocols(char ***);
+extern int sa_valid_protocol(char *);
+
+/* group control (create, remove, etc) */
+extern sa_group_t sa_create_group(char *, int *);
+extern int sa_remove_group(sa_group_t);
+extern sa_group_t sa_get_group(char *);
+extern sa_group_t sa_get_next_group(sa_group_t);
+extern char *sa_get_group_attr(sa_group_t, char *);
+extern int sa_set_group_attr(sa_group_t, char *, char *);
+extern sa_group_t sa_get_sub_group(sa_group_t);
+extern int sa_valid_group_name(char *);
+
+/* share control */
+extern sa_share_t sa_add_share(sa_group_t, char *, int, int *);
+extern int sa_check_path(sa_group_t, char *);
+extern int sa_move_share(sa_group_t, sa_share_t);
+extern int sa_remove_share(sa_share_t);
+extern sa_share_t sa_get_share(sa_group_t, char *);
+extern sa_share_t sa_get_resource(sa_group_t, char *);
+extern sa_share_t sa_find_share(char *);
+extern sa_share_t sa_get_next_share(sa_share_t);
+extern char *sa_get_share_attr(sa_share_t, char *);
+extern char *sa_get_share_description(sa_share_t);
+extern sa_group_t sa_get_parent_group(sa_share_t);
+extern int sa_set_share_attr(sa_share_t, char *, char *);
+extern int sa_set_share_description(sa_share_t, char *);
+extern int sa_enable_share(sa_group_t, char *);
+extern int sa_disable_share(sa_group_t, char *);
+extern int sa_is_share(void *);
+
+/* data structure free calls */
+extern void sa_free_attr_string(char *);
+extern void sa_free_share_description(char *);
+
+/* optionset control */
+extern sa_optionset_t sa_get_optionset(sa_group_t, char *);
+extern sa_optionset_t sa_get_next_optionset(sa_group_t);
+extern char *sa_get_optionset_attr(sa_optionset_t, char *);
+extern void sa_set_optionset_attr(sa_optionset_t, char *, char *);
+extern sa_optionset_t sa_create_optionset(sa_group_t, char *);
+extern int sa_destroy_optionset(sa_optionset_t);
+extern sa_optionset_t sa_get_derived_optionset(void *, char *, int);
+extern void sa_free_derived_optionset(sa_optionset_t);
+
+/* property functions */
+extern sa_optionset_t sa_get_property(sa_optionset_t, char *);
+extern sa_optionset_t sa_get_next_property(sa_group_t);
+extern char *sa_get_property_attr(sa_property_t, char *);
+extern sa_property_t sa_create_property(char *, char *);
+extern int sa_add_property(void *, sa_property_t);
+extern int sa_update_property(sa_property_t, char *);
+extern int sa_remove_property(sa_property_t);
+extern int sa_commit_properties(sa_optionset_t, int);
+extern int sa_valid_property(void *, char *, sa_property_t);
+
+/* security control */
+extern sa_security_t sa_get_security(sa_group_t, char *, char *);
+extern sa_security_t sa_get_next_security(sa_security_t);
+extern char *sa_get_security_attr(sa_optionset_t, char *);
+extern sa_security_t sa_create_security(sa_group_t, char *, char *);
+extern int sa_destroy_security(sa_security_t);
+extern void sa_set_security_attr(sa_security_t, char *, char *);
+extern sa_optionset_t sa_get_all_security_types(void *, char *, int);
+extern sa_security_t sa_get_derived_security(void *, char *, char *, int);
+extern void sa_free_derived_security(sa_security_t);
+
+/* protocol specific interfaces */
+extern int sa_parse_legacy_options(sa_group_t, char *, char *);
+extern char *sa_proto_legacy_format(char *, sa_group_t, int);
+extern int sa_is_security(char *, char *);
+extern sa_protocol_properties_t sa_proto_get_properties(char *);
+extern sa_property_t sa_get_protocol_property(sa_protocol_properties_t, char *);
+extern sa_property_t sa_get_next_protocol_property(sa_property_t);
+extern int sa_set_protocol_property(sa_property_t, char *);
+extern char *sa_get_protocol_status(char *);
+extern void sa_format_free(char *);
+extern sa_protocol_properties_t sa_create_protocol_properties(char *);
+extern int sa_add_protocol_property(sa_protocol_properties_t, sa_property_t);
+extern int sa_proto_valid_prop(char *, sa_property_t, sa_optionset_t);
+extern int sa_proto_valid_space(char *, char *);
+extern char *sa_proto_space_alias(char *, char *);
+
+/* handle legacy (dfstab/sharetab) files */
+extern int sa_delete_legacy(sa_share_t);
+extern int sa_update_legacy(sa_share_t, char *);
+extern int sa_update_sharetab(sa_share_t, char *);
+extern int sa_delete_sharetab(char *, char *);
+
+/* ZFS functions */
+extern int sa_zfs_is_shared(char *);
+extern int sa_group_is_zfs(sa_group_t);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSHARE_H */
diff --git a/usr/src/lib/libshare/common/libshare_impl.h b/usr/src/lib/libshare/common/libshare_impl.h
new file mode 100644
index 0000000000..0bb846e863
--- /dev/null
+++ b/usr/src/lib/libshare/common/libshare_impl.h
@@ -0,0 +1,143 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * basic declarations for implementation of the share management
+ * libraries.
+ */
+
+#ifndef _LIBSHARE_IMPL_H
+#define _LIBSHARE_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libshare.h>
+#include <libscf.h>
+#include <scfutil.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* directory to find plugin modules in */
+#define SA_LIB_DIR "/usr/lib/fs"
+
+/* default group name for dfstab file */
+#define SA_DEFAULT_FILE_GRP "sys"
+
+typedef void *sa_phandle_t;
+
+#define SA_PLUGIN_VERSION 1
+struct sa_plugin_ops {
+ int sa_version;
+ char *sa_protocol; /* protocol name */
+ int (*sa_init)();
+ void (*sa_fini)();
+ int (*sa_share)(sa_share_t); /* start sharing */
+ int (*sa_unshare)(char *); /* stop sharing */
+ int (*sa_valid_prop)(sa_property_t, sa_optionset_t);
+ int (*sa_valid_space)(char *); /* is name valid optionspace? */
+ int (*sa_security_prop)(char *); /* property is security */
+ int (*sa_legacy_opts)(sa_group_t, char *); /* parse legacy opts */
+ char *(*sa_legacy_format)(sa_group_t, int);
+ int (*sa_set_proto_prop)(sa_property_t);
+ sa_protocol_properties_t (*sa_get_proto_set)();
+ char *(*sa_get_proto_status)();
+ char *(*sa_space_alias)(char *);
+ int (*sa_update_legacy)(sa_share_t);
+ int (*sa_delete_legacy)(sa_share_t);
+ int (*sa_run_command)(int, int, char **); /* proto specific */
+ int (*sa_command_help)();
+};
+
+struct sa_proto_handle {
+ int sa_num_proto;
+ char **sa_proto;
+ struct sa_plugin_ops **sa_ops;
+};
+
+typedef struct propertylist {
+ struct propertylist *pl_next;
+ int pl_type;
+ union propval {
+ sa_optionset_t pl_optionset;
+ sa_security_t pl_security;
+ void *pl_void;
+ } pl_value;
+} property_list_t;
+
+extern int sa_proto_share(char *, sa_share_t);
+extern int sa_proto_unshare(char *, char *);
+extern int sa_proto_valid_prop(char *, sa_property_t, sa_optionset_t);
+extern int sa_proto_security_prop(char *, char *);
+extern int sa_proto_legacy_opts(char *, sa_group_t, char *);
+
+/* internal utility functions */
+extern sa_optionset_t sa_get_derived_optionset(sa_group_t, char *, int);
+extern void sa_free_derived_optionset(sa_optionset_t);
+extern sa_optionset_t sa_get_all_security_types(void *, char *, int);
+extern sa_security_t sa_get_derived_security(void *, char *, char *, int);
+extern void sa_free_derived_security(sa_security_t);
+extern sa_protocol_properties_t sa_create_protocol_properties(char *);
+extern int sa_start_transaction(scfutilhandle_t *, char *);
+extern int sa_end_transaction(scfutilhandle_t *);
+extern void sa_abort_transaction(scfutilhandle_t *);
+extern int sa_commit_share(scfutilhandle_t *, sa_group_t, sa_share_t);
+extern int sa_set_property(scfutilhandle_t *, char *, char *);
+extern void sa_free_fstype(char *fstyp);
+extern int sa_delete_share(scfutilhandle_t *, sa_group_t, sa_share_t);
+extern int sa_delete_instance(scfutilhandle_t *, char *);
+extern int sa_create_pgroup(scfutilhandle_t *, char *);
+extern int sa_delete_pgroup(scfutilhandle_t *, char *);
+
+/* ZFS functions */
+extern int sa_get_zfs_shares(char *);
+extern int sa_zfs_update(sa_share_t);
+
+/* plugin specific functions */
+extern int proto_plugin_init();
+extern int sa_proto_set_property(char *, sa_property_t);
+extern int sa_proto_delete_legacy(char *, sa_share_t);
+extern int sa_proto_update_legacy(char *, sa_share_t);
+
+#define PL_TYPE_PROPERTY 0
+#define PL_TYPE_SECURITY 1
+
+/* values only used by the internal dfstab/sharetab parser */
+#define SA_SHARE_PARSER 0x1000
+
+/* plugin handler only */
+struct sa_proto_plugin {
+ struct sa_proto_plugin *plugin_next;
+ struct sa_plugin_ops *plugin_ops;
+ void *plugin_handle;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSHARE_IMPL_H */
diff --git a/usr/src/lib/libshare/common/libshare_zfs.c b/usr/src/lib/libshare/common/libshare_zfs.c
new file mode 100644
index 0000000000..0a8f327edd
--- /dev/null
+++ b/usr/src/lib/libshare/common/libshare_zfs.c
@@ -0,0 +1,496 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libfsmgt.h"
+#include <libzfs.h>
+#include <string.h>
+#include <libshare.h>
+#include "libshare_impl.h"
+
+extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
+extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
+extern char *sa_fstype(char *);
+extern void set_node_attr(void *, char *, char *);
+extern int sa_is_share(void *);
+/*
+ * File system specific code for ZFS
+ */
+
+/*
+ * get_zfs_dataset(path)
+ *
+ * get the name of the ZFS dataset the path is equivalent to. The
+ * dataset name is used for get/set of ZFS properties since libzfs
+ * requires a dataset to do a zfs_open().
+ */
+
+static char *
+get_zfs_dataset(char *path)
+{
+ fs_mntlist_t *list;
+ fs_mntlist_t *cur;
+ int err;
+ char *dataset = NULL;
+
+ list = fs_get_filtered_mount_list(NULL, NULL, "zfs", NULL,
+ NULL, 0, &err);
+ for (cur = list; cur != NULL; cur = cur->next) {
+ if (strcmp(path, cur->mountp) == 0 ||
+ strncmp(path, cur->mountp, strlen(cur->mountp)) == 0) {
+ /*
+ * we want the longest resource so keep trying. This
+ * check avoids dropping out on a partial match. ZFS
+ * resources are ordered when mounted in order to
+ * ensure inheritence of properties.
+ */
+ dataset = cur->resource;
+ }
+ }
+ if (dataset != NULL) {
+ dataset = strdup(dataset);
+ }
+ fs_free_mount_list(list);
+ return (dataset);
+}
+
+/*
+ * get_zfs_property(dataset, property)
+ *
+ * Get the file system property specified from the ZFS dataset.
+ */
+
+static char *
+get_zfs_property(char *dataset, zfs_prop_t property)
+{
+ zfs_handle_t *handle = NULL;
+ char shareopts[ZFS_MAXPROPLEN];
+ libzfs_handle_t *libhandle;
+
+ libhandle = libzfs_init();
+ if (libhandle != NULL) {
+ handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
+ if (handle != NULL) {
+ if (zfs_prop_get(handle, property, shareopts,
+ sizeof (shareopts), NULL, NULL, 0,
+ FALSE) == 0) {
+ zfs_close(handle);
+ libzfs_fini(libhandle);
+ return (strdup(shareopts));
+ }
+ zfs_close(handle);
+ }
+ libzfs_fini(libhandle);
+ }
+ return (NULL);
+}
+
+/*
+ * sa_zfs_is_shared(path)
+ *
+ * Check to see if the ZFS path provided has the sharenfs option set
+ * or not.
+ */
+
+int
+sa_zfs_is_shared(char *path)
+{
+ int ret = 0;
+ char *dataset;
+ zfs_handle_t *handle = NULL;
+ char shareopts[ZFS_MAXPROPLEN];
+ libzfs_handle_t *libhandle;
+
+ dataset = get_zfs_dataset(path);
+ if (dataset != NULL) {
+ libhandle = libzfs_init();
+ if (libhandle != NULL) {
+ handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
+ if (handle != NULL) {
+ if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
+ sizeof (shareopts), NULL, NULL, 0,
+ FALSE) == 0 &&
+ strcmp(shareopts, "off") != 0)
+ ret = 1; /* it is shared */
+ zfs_close(handle);
+ }
+ libzfs_fini(libhandle);
+ }
+ free(dataset);
+ }
+ return (ret);
+}
+
+/*
+ * find_or_create_group(groupname, proto, *err)
+ *
+ * While walking the ZFS tree, we need to add shares to a defined
+ * group. If the group doesn't exist, create it first, making sure it
+ * is marked as a ZFS group.
+ *
+ * Not that all ZFS shares are in a subgroup of the top level group
+ * "zfs".
+ */
+
+static sa_group_t
+find_or_create_group(char *groupname, char *proto, int *err)
+{
+ sa_group_t group;
+ sa_optionset_t optionset;
+ int ret = SA_OK;
+
+ /*
+ * we check to see if the "zfs" group exists. Since this
+ * should be the top level group, we don't want the
+ * parent. This is to make sure the zfs group has been created
+ * and to created if it hasn't been.
+ */
+ group = sa_get_group(groupname);
+ if (group == NULL) {
+ group = sa_create_group(groupname, &ret);
+ if (group != NULL)
+ ret = sa_set_group_attr(group, "zfs", "true");
+ }
+ if (group != NULL) {
+ if (proto != NULL) {
+ optionset = sa_get_optionset(group, proto);
+ if (optionset == NULL) {
+ optionset = sa_create_optionset(group, proto);
+ } else {
+ char **protolist;
+ int numprotos, i;
+ numprotos = sa_get_protocols(&protolist);
+ for (i = 0; i < numprotos; i++) {
+ optionset = sa_create_optionset(group, protolist[i]);
+ }
+ if (protolist != NULL)
+ free(protolist);
+ }
+ }
+ }
+ if (err != NULL)
+ *err = ret;
+ return (group);
+}
+
+/*
+ * sa_get_zfs_shares(groupname)
+ *
+ * Walk the mnttab for all zfs mounts and determine which are
+ * shared. Find or create the appropriate group/sub-group to contain
+ * the shares.
+ *
+ * All shares are in a sub-group that will hold the properties. This
+ * allows representing the inherited property model.
+ */
+
+int
+sa_get_zfs_shares(char *groupname)
+{
+ sa_group_t group;
+ sa_group_t zfsgroup;
+ int legacy = 0;
+ int err;
+ fs_mntlist_t *list;
+ fs_mntlist_t *cur;
+ zfs_handle_t *handle = NULL;
+ char shareopts[ZFS_MAXPROPLEN];
+ sa_share_t share;
+ zfs_source_t source;
+ char sourcestr[ZFS_MAXPROPLEN];
+ libzfs_handle_t *libhandle;
+
+ /*
+ * if we can't access libzfs, don't bother doing anything.
+ */
+ libhandle = libzfs_init();
+ if (libhandle == NULL)
+ return (SA_SYSTEM_ERR);
+
+ zfsgroup = find_or_create_group(groupname, "nfs", &err);
+ if (zfsgroup != NULL) {
+ /*
+ * need to walk the mounted ZFS pools and datasets to
+ * find shares that are possible.
+ */
+ list = fs_get_filtered_mount_list(NULL, NULL, "zfs", NULL,
+ NULL, 0, &err);
+ group = zfsgroup;
+ for (cur = list; cur != NULL; cur = cur->next) {
+ handle = zfs_open(libhandle, cur->resource,
+ ZFS_TYPE_FILESYSTEM);
+ if (handle != NULL) {
+ source = ZFS_SRC_ALL;
+ if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
+ sizeof (shareopts), &source, sourcestr,
+ ZFS_MAXPROPLEN,
+ FALSE) == 0 &&
+ strcmp(shareopts, "off") != 0) {
+ /* it is shared so add to list */
+ share = sa_find_share(cur->mountp);
+ err = SA_OK;
+ if (share != NULL) {
+ /*
+ * A zfs file system had been shared
+ * through tradiditional methods
+ * (share/dfstab or added to a non-zfs
+ * group. Now it has been added to a
+ * ZFS group via the zfs
+ * command. Remove from previous
+ * config and setup with current
+ * options.
+ */
+ err = sa_remove_share(share);
+ share = NULL;
+ }
+ if (err == SA_OK) {
+ if (source & ZFS_SRC_INHERITED) {
+ share = _sa_add_share(group, cur->mountp,
+ SA_SHARE_TRANSIENT,
+ &err);
+ } else {
+ group = _sa_create_zfs_group(zfsgroup,
+ cur->resource);
+ set_node_attr(group, "zfs", "true");
+ share = _sa_add_share(group, cur->mountp,
+ SA_SHARE_TRANSIENT,
+ &err);
+ if (err == SA_OK) {
+ char *options;
+ if (strcmp(shareopts, "on") != 0) {
+ options = strdup(shareopts);
+ if (options != NULL) {
+ err = sa_parse_legacy_options(group,
+ options,
+ "nfs");
+ free(options);
+ }
+ /* unmark the share's changed state */
+ set_node_attr(share, "changed", NULL);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (list != NULL)
+ fs_free_mount_list(list);
+ }
+ if (libhandle != NULL)
+ libzfs_fini(libhandle);
+ return (legacy);
+}
+
+#define COMMAND "/usr/sbin/zfs"
+
+/*
+ * sa_zfs_set_sharenfs(group, path, on)
+ *
+ * Update the "sharenfs" property on the path. If on is true, then set
+ * to the properties on the group or "on" if no properties are
+ * defined. Set to "off" if on is false.
+ */
+
+int
+sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
+{
+ int ret = SA_NOT_IMPLEMENTED;
+ char *command;
+
+ command = malloc(ZFS_MAXPROPLEN * 2);
+ if (command != NULL) {
+ char *opts = NULL;
+ char *dataset;
+ FILE *pfile;
+ /* for now, NFS is always available for "zfs" */
+ if (on) {
+ opts = sa_proto_legacy_format("nfs", group, 1);
+ if (opts != NULL && strlen(opts) == 0) {
+ free(opts);
+ opts = strdup("on");
+ }
+ }
+ dataset = get_zfs_dataset(path);
+ if (dataset != NULL) {
+ (void) snprintf(command, ZFS_MAXPROPLEN * 2,
+ "%s set sharenfs=\"%s\" %s", COMMAND,
+ opts != NULL ? opts : "off",
+ dataset);
+ pfile = popen(command, "r");
+ if (pfile != NULL) {
+ ret = pclose(pfile);
+ if (ret != 0)
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ if (opts != NULL)
+ free(opts);
+ if (dataset != NULL)
+ free(dataset);
+ free(command);
+ }
+ return (ret);
+}
+
+/*
+ * sa_zfs_update(group)
+ *
+ * call back to ZFS to update the share if necessary.
+ * Don't do it if it isn't a real change.
+ */
+int
+sa_zfs_update(sa_group_t group)
+{
+ sa_optionset_t protopt;
+ sa_group_t parent;
+ char *command;
+ char *optstring;
+ int ret = SA_OK;
+ int doupdate = 0;
+ FILE *pfile;
+
+ if (sa_is_share(group))
+ parent = sa_get_parent_group(group);
+ else
+ parent = group;
+
+ if (parent != NULL) {
+ command = malloc(ZFS_MAXPROPLEN * 2);
+ if (command == NULL)
+ return (SA_NO_MEMORY);
+
+ *command = '\0';
+ for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
+ protopt = sa_get_next_optionset(protopt)) {
+
+ char *proto = sa_get_optionset_attr(protopt, "type");
+ char *path;
+ char *dataset = NULL;
+ char *zfsopts = NULL;
+
+ if (sa_is_share(group)) {
+ path = sa_get_share_attr((sa_share_t)group, "path");
+ if (path != NULL) {
+ dataset = get_zfs_dataset(path);
+ sa_free_attr_string(path);
+ }
+ } else {
+ dataset = sa_get_group_attr(group, "name");
+ }
+ /* update only when there is an optstring found */
+ doupdate = 0;
+ if (proto != NULL && dataset != NULL) {
+ optstring = sa_proto_legacy_format(proto, group, 1);
+ zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS);
+
+ if (optstring != NULL && zfsopts != NULL) {
+ if (strcmp(optstring, zfsopts) != 0)
+ doupdate++;
+ }
+
+ if (doupdate) {
+ if (optstring != NULL && strlen(optstring) > 0) {
+ (void) snprintf(command, ZFS_MAXPROPLEN * 2,
+ "%s set sharenfs=%s %s", COMMAND,
+ optstring, dataset);
+ } else {
+ (void) snprintf(command, ZFS_MAXPROPLEN * 2,
+ "%s set sharenfs=on %s", COMMAND,
+ dataset);
+ }
+ pfile = popen(command, "r");
+ if (pfile != NULL)
+ ret = pclose(pfile);
+ switch (ret) {
+ default:
+ case 1:
+ ret = SA_SYSTEM_ERR;
+ break;
+ case 2:
+ ret = SA_SYNTAX_ERR;
+ break;
+ case 0:
+ break;
+ }
+ }
+ if (optstring != NULL) {
+ free(optstring);
+ }
+ if (zfsopts != NULL)
+ free(zfsopts);
+ }
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ if (dataset != NULL)
+ free(dataset);
+ }
+ free(command);
+ }
+ return (ret);
+}
+
+/*
+ * sa_group_is_zfs(group)
+ *
+ * Given the group, determine if the zfs attribute is set.
+ */
+
+int
+sa_group_is_zfs(sa_group_t group)
+{
+ char *zfs;
+ int ret = 0;
+
+ zfs = sa_get_group_attr(group, "zfs");
+ if (zfs != NULL) {
+ ret = 1;
+ sa_free_attr_string(zfs);
+ }
+ return (ret);
+}
+
+/*
+ * sa_path_is_zfs(path)
+ *
+ * Check to see if the file system path represents is of type "zfs".
+ */
+
+int
+sa_path_is_zfs(char *path)
+{
+ char *fstype;
+ int ret = 0;
+
+ fstype = sa_fstype(path);
+ if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
+ ret = 1;
+ }
+ if (fstype != NULL)
+ sa_free_fstype(fstype);
+ return (ret);
+}
diff --git a/usr/src/lib/libshare/common/libsharecore.c b/usr/src/lib/libshare/common/libsharecore.c
new file mode 100644
index 0000000000..9e1348feb6
--- /dev/null
+++ b/usr/src/lib/libshare/common/libsharecore.c
@@ -0,0 +1,1801 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * core library for common functions across all config store types
+ * and file systems to be exported. This includes legacy dfstab/sharetab
+ * parsing. Need to eliminate XML where possible.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include "libshare.h"
+#include "libshare_impl.h"
+#include "libfsmgt.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <grp.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <signal.h>
+#include <libintl.h>
+
+#include "sharetab.h"
+
+#define DFSTAB_NOTICE_LINES 5
+static char *notice[DFSTAB_NOTICE_LINES] = {
+ "# Do not modify this file directly.\n",
+ "# Use the sharemgr(1m) command for all share management\n",
+ "# This file is reconstructed and only maintained for backward\n",
+ "# compatibility. Configuration lines could be lost.\n",
+ "#\n"
+};
+
+#define STRNCAT(x, y, z) (xmlChar *)strncat((char *)x, (char *)y, z)
+
+/* will be much smaller, but this handles bad syntax in the file */
+#define MAXARGSFORSHARE 256
+
+/* used internally only */
+typedef
+struct sharelist {
+ struct sharelist *next;
+ int persist;
+ char *path;
+ char *resource;
+ char *fstype;
+ char *options;
+ char *description;
+ char *group;
+ char *origline;
+ int lineno;
+} xfs_sharelist_t;
+static void parse_dfstab(char *, xmlNodePtr);
+extern char *get_token(char *);
+static void dfs_free_list(xfs_sharelist_t *);
+/* prototypes */
+void getlegacyconfig(char *, xmlNodePtr *);
+extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
+extern xmlNodePtr _sa_get_next_error(xmlNodePtr);
+extern sa_group_t _sa_create_group(char *);
+static void outdfstab(FILE *, xfs_sharelist_t *);
+extern int _sa_remove_optionset(sa_optionset_t);
+extern int set_node_share(void *, char *, char *);
+extern void set_node_attr(void *, char *, char *);
+
+/*
+ * alloc_sharelist()
+ *
+ * allocator function to return an zfs_sharelist_t
+ */
+
+static xfs_sharelist_t *
+alloc_sharelist()
+{
+ xfs_sharelist_t *item;
+
+ item = (xfs_sharelist_t *)malloc(sizeof (xfs_sharelist_t));
+ if (item != NULL)
+ (void) memset(item, '\0', sizeof (xfs_sharelist_t));
+ return (item);
+}
+
+/*
+ * fix_notice(list)
+ *
+ * Look at the beginning of the current /etc/dfs/dfstab file and add
+ * the do not modify notice if it doesn't exist.
+ */
+
+static xfs_sharelist_t *
+fix_notice(xfs_sharelist_t *list)
+{
+ xfs_sharelist_t *item, *prev;
+ int i;
+
+ if (list->path == NULL && list->description != NULL &&
+ strcmp(list->description, notice[0]) != 0) {
+ for (prev = NULL, i = 0; i < DFSTAB_NOTICE_LINES; i++) {
+ item = alloc_sharelist();
+ if (item != NULL) {
+ item->description = strdup(notice[i]);
+ if (prev == NULL) {
+ item->next = list;
+ prev = item;
+ list = item;
+ } else {
+ item->next = prev->next;
+ prev->next = item;
+ prev = item;
+ }
+ }
+ }
+ }
+ return (list);
+}
+
+/*
+ * getdfstab(dfs)
+ *
+ * Returns an zfs_sharelist_t list of lines from the dfstab file
+ * pointed to by the FILE pointer dfs. Each entry is parsed and the
+ * original line is also preserved. Used in parsing and updating the
+ * dfstab file.
+ */
+
+static xfs_sharelist_t *
+getdfstab(FILE *dfs)
+{
+ char buff[_POSIX_ARG_MAX]; /* reasonable size given syntax of share */
+ char *bp;
+ char *token;
+ char *args[MAXARGSFORSHARE];
+ int argc;
+ int c;
+ static int line = 0;
+ xfs_sharelist_t *item, *first, *last;
+
+ if (dfs != NULL) {
+ first = NULL;
+ line = 0;
+ while (fgets(buff, sizeof (buff), dfs) != NULL) {
+ line++;
+ bp = buff;
+ if (buff[0] == '#') {
+ item = alloc_sharelist();
+ if (item != NULL) {
+ /* if no path, then comment */
+ item->lineno = line;
+ item->description = strdup(buff);
+ if (first == NULL) {
+ first = item;
+ last = item;
+ } else {
+ last->next = item;
+ last = item;
+ }
+ } else {
+ break;
+ }
+ continue;
+ } else if (buff[0] == '\n') {
+ continue;
+ }
+ optind = 1;
+ item = alloc_sharelist();
+ if (item == NULL) {
+ break;
+ } else if (first == NULL) {
+ first = item;
+ last = item;
+ } else {
+ last->next = item;
+ last = item;
+ }
+ item->lineno = line;
+ item->origline = strdup(buff);
+ (void) get_token(NULL); /* reset to new pointers */
+ argc = 0;
+ while ((token = get_token(bp)) != NULL) {
+ if (argc < MAXARGSFORSHARE) {
+ args[argc++] = token;
+ }
+ }
+ while ((c = getopt(argc, args, "F:o:d:pg:")) != -1) {
+ switch (c) {
+ case 'p':
+ item->persist = 1;
+ break;
+ case 'F':
+ item->fstype = strdup(optarg);
+ break;
+ case 'o':
+ item->options = strdup(optarg);
+ break;
+ case 'd':
+ item->description = strdup(optarg);
+ break;
+ case 'g':
+ item->group = strdup(optarg);
+ break;
+ default:
+ break;
+ }
+ }
+ if (optind < argc) {
+ item->path = strdup(args[optind]);
+ optind++;
+ if (optind < argc) {
+ char *resource;
+ char *optgroup;
+ /* resource and/or groupname */
+ resource = args[optind];
+ optgroup = strchr(resource, '@');
+ if (optgroup != NULL) {
+ *optgroup++ = '\0';
+ }
+ if (optgroup != NULL)
+ item->group = strdup(optgroup);
+ if (resource != NULL && strlen(resource) > 0)
+ item->resource = strdup(resource);
+ }
+ }
+ }
+ if (item->fstype == NULL)
+ item->fstype = strdup("nfs"); /* this is the default */
+ }
+ first = fix_notice(first);
+ return (first);
+}
+
+/*
+ * finddfsentry(list, path)
+ *
+ * Look for path in the zfs_sharelist_t list and return the tnry if it
+ * exists.
+ */
+
+static xfs_sharelist_t *
+finddfsentry(xfs_sharelist_t *list, char *path)
+{
+ xfs_sharelist_t *item;
+
+ for (item = list; item != NULL; item = item->next) {
+ if (item->path != NULL && strcmp(item->path, path) == 0)
+ return (item);
+ }
+ return (NULL);
+}
+
+/*
+ * remdfsentry(list, path, proto)
+ *
+ * Remove the specified path (with protocol) from the list. This will
+ * remove it from dfstab when the file is rewritten.
+ */
+
+static xfs_sharelist_t *
+remdfsentry(xfs_sharelist_t *list, char *path, char *proto)
+{
+ xfs_sharelist_t *item, *prev = NULL;
+
+
+ for (item = prev = list; item != NULL; item = item->next) {
+ /* skip comment entry but don't lose it */
+ if (item->path == NULL) {
+ prev = item;
+ continue;
+ }
+ /* if proto is NULL, remove all protocols */
+ if (proto == NULL || (strcmp(item->path, path) == 0 &&
+ (item->fstype != NULL && strcmp(item->fstype, proto) == 0)))
+ break;
+ if (item->fstype == NULL &&
+ (proto == NULL || strcmp(proto, "nfs") == 0))
+ break;
+ prev = item;
+ }
+ if (item != NULL) {
+ if (item == prev) {
+ list = item->next; /* this must be the first one */
+ } else {
+ prev->next = item->next;
+ }
+ item->next = NULL;
+ dfs_free_list(item);
+ }
+ return (list);
+}
+
+/*
+ * remdfsline(list, line)
+ *
+ * Remove the line specified from the list.
+ */
+
+static xfs_sharelist_t *
+remdfsline(xfs_sharelist_t *list, char *line)
+{
+ xfs_sharelist_t *item, *prev = NULL;
+
+ for (item = prev = list; item != NULL; item = item->next) {
+ /* skip comment entry but don't lose it */
+ if (item->path == NULL) {
+ prev = item;
+ continue;
+ }
+ if (strcmp(item->origline, line) == 0) {
+ break;
+ }
+ prev = item;
+ }
+ if (item != NULL) {
+ if (item == prev) {
+ list = item->next; /* this must be the first one */
+ } else {
+ prev->next = item->next;
+ }
+ item->next = NULL;
+ dfs_free_list(item);
+ }
+ return (list);
+}
+
+/*
+ * adddfsentry(list, share, proto)
+ *
+ * Add an entry to the dfstab list for share (relative to proto). This
+ * is used to update dfstab for legacy purposes.
+ */
+
+static xfs_sharelist_t *
+adddfsentry(xfs_sharelist_t *list, sa_share_t share, char *proto)
+{
+ xfs_sharelist_t *item, *tmp;
+ sa_group_t parent;
+ char *groupname;
+
+ item = alloc_sharelist();
+ if (item != NULL) {
+ parent = sa_get_parent_group(share);
+ groupname = sa_get_group_attr(parent, "name");
+ if (strcmp(groupname, "default") == 0) {
+ sa_free_attr_string(groupname);
+ groupname = NULL;
+ }
+ item->path = sa_get_share_attr(share, "path");
+ item->resource = sa_get_share_attr(share, "resource");
+ item->group = groupname;
+ item->fstype = strdup(proto);
+ item->options = sa_proto_legacy_format(proto, share, 1);
+ if (item->options != NULL && strlen(item->options) == 0) {
+ free(item->options);
+ item->options = NULL;
+ }
+ item->description = sa_get_share_description(share);
+ if (item->description != NULL && strlen(item->description) == 0) {
+ sa_free_share_description(item->description);
+ item->description = NULL;
+ }
+ if (list == NULL) {
+ list = item;
+ } else {
+ for (tmp = list; tmp->next != NULL; tmp = tmp->next)
+ /* do nothing */;
+ tmp->next = item;
+ }
+ }
+ return (list);
+}
+
+/*
+ * outdfstab(dfstab, list)
+ *
+ * Output the list to dfstab making sure the file is truncated.
+ * Comments and errors are preserved.
+ */
+
+static void
+outdfstab(FILE *dfstab, xfs_sharelist_t *list)
+{
+ xfs_sharelist_t *item;
+
+ (void) ftruncate(fileno(dfstab), 0);
+
+ for (item = list; item != NULL; item = item->next) {
+ if (item->path != NULL) {
+ if (*item->path == '/')
+ (void) fprintf(dfstab, "share %s%s%s%s%s%s%s %s%s%s%s%s\n",
+ (item->fstype != NULL) ? "-F " : "",
+ (item->fstype != NULL) ? item->fstype : "",
+ (item->options != NULL) ? " -o " : "",
+ (item->options != NULL) ? item->options : "",
+ (item->description != NULL) ? " -d \"" : "",
+ (item->description != NULL) ?
+ item->description : "",
+ (item->description != NULL) ? "\"" : "",
+ item->path,
+ ((item->resource != NULL) ||
+ (item->group != NULL)) ? " " : "",
+ (item->resource != NULL) ? item->resource : "",
+ item->group != NULL ? "@" : "",
+ item->group != NULL ? item->group : "");
+ else
+ (void) fprintf(dfstab, "%s", item->origline);
+ } else {
+ if (item->description != NULL) {
+ (void) fprintf(dfstab, "%s", item->description);
+ } else {
+ (void) fprintf(dfstab, "%s", item->origline);
+ }
+ }
+ }
+}
+
+/*
+ * open_dfstab(file)
+ *
+ * Open the specified dfstab file. If the owner/group/perms are wrong,
+ * fix them.
+ */
+
+static FILE *
+open_dfstab(char *file)
+{
+ struct group *grp;
+ struct group group;
+ char *buff;
+ int grsize;
+ FILE *dfstab;
+
+ dfstab = fopen(file, "r+");
+ if (dfstab == NULL) {
+ dfstab = fopen(file, "w+");
+ }
+ if (dfstab != NULL) {
+ grsize = sysconf(_SC_GETGR_R_SIZE_MAX);
+ buff = malloc(grsize);
+ if (buff != NULL)
+ grp = getgrnam_r(SA_DEFAULT_FILE_GRP, &group, buff, grsize);
+ else
+ grp = getgrnam(SA_DEFAULT_FILE_GRP); /* take the risk */
+ (void) fchmod(fileno(dfstab), 0644);
+ (void) fchown(fileno(dfstab), 0,
+ grp != NULL ? grp->gr_gid : 3);
+ if (buff != NULL)
+ free(buff);
+ rewind(dfstab);
+ }
+ return (dfstab);
+}
+
+/*
+ * sa_comment_line(line, err)
+ *
+ * Add a comment to the dfstab file with err as a prefix to the
+ * original line.
+ */
+
+static void
+sa_comment_line(char *line, char *err)
+{
+ FILE *dfstab;
+ xfs_sharelist_t *list;
+ sigset_t old, new;
+
+ dfstab = open_dfstab(SA_LEGACY_DFSTAB);
+ if (dfstab != NULL) {
+ (void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
+ (void) sigprocmask(SIG_BLOCK, NULL, &new);
+ (void) sigaddset(&new, SIGHUP);
+ (void) sigaddset(&new, SIGINT);
+ (void) sigaddset(&new, SIGQUIT);
+ (void) sigaddset(&new, SIGTSTP);
+ (void) sigprocmask(SIG_SETMASK, &new, &old);
+ (void) lockf(fileno(dfstab), F_LOCK, 0);
+ list = getdfstab(dfstab);
+ rewind(dfstab);
+ (void) remdfsline(list, line);
+ outdfstab(dfstab, list);
+ (void) fprintf(dfstab, "# Error: %s: %s", err, line);
+ (void) fsync(fileno(dfstab));
+ (void) lockf(fileno(dfstab), F_ULOCK, 0);
+ (void) fclose(dfstab);
+ (void) sigprocmask(SIG_SETMASK, &old, NULL);
+ if (list != NULL)
+ dfs_free_list(list);
+ }
+}
+
+/*
+ * sa_delete_legacy(share)
+ *
+ * Delete the specified share from the legacy config file.
+ */
+
+int
+sa_delete_legacy(sa_share_t share)
+{
+ FILE *dfstab;
+ int err;
+ int ret = SA_OK;
+ xfs_sharelist_t *list;
+ char *path;
+ sa_optionset_t optionset;
+ sa_group_t parent;
+ sigset_t old, new;
+
+ dfstab = open_dfstab(SA_LEGACY_DFSTAB);
+ if (dfstab != NULL) {
+ (void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
+ (void) sigprocmask(SIG_BLOCK, NULL, &new);
+ (void) sigaddset(&new, SIGHUP);
+ (void) sigaddset(&new, SIGINT);
+ (void) sigaddset(&new, SIGQUIT);
+ (void) sigaddset(&new, SIGTSTP);
+ (void) sigprocmask(SIG_SETMASK, &new, &old);
+ path = sa_get_share_attr(share, "path");
+ parent = sa_get_parent_group(share);
+ if (parent != NULL) {
+ (void) lockf(fileno(dfstab), F_LOCK, 0);
+ list = getdfstab(dfstab);
+ rewind(dfstab);
+ for (optionset = sa_get_optionset(parent, NULL);
+ optionset != NULL;
+ optionset = sa_get_next_optionset(optionset)) {
+ char *proto = sa_get_optionset_attr(optionset, "type");
+ if (list != NULL && proto != NULL)
+ (void) remdfsentry(list, path, proto);
+ if (proto == NULL)
+ ret = SA_NO_MEMORY;
+ /*
+ * may want to only do the dfstab if this call
+ * returns NOT IMPLEMENTED but it shouldn't
+ * hurt.
+ */
+ if (ret == SA_OK) {
+ err = sa_proto_delete_legacy(proto, share);
+ if (err != SA_NOT_IMPLEMENTED)
+ ret = err;
+ }
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ }
+ outdfstab(dfstab, list);
+ if (list != NULL)
+ dfs_free_list(list);
+ (void) fflush(dfstab);
+ (void) lockf(fileno(dfstab), F_ULOCK, 0);
+ }
+ (void) fsync(fileno(dfstab));
+ (void) sigprocmask(SIG_SETMASK, &old, NULL);
+ (void) fclose(dfstab);
+ sa_free_attr_string(path);
+ } else {
+ if (errno == EACCES || errno == EPERM) {
+ ret = SA_NO_PERMISSION;
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_update_legacy(share, proto)
+ *
+ * There is an assumption that dfstab will be the most common form of
+ * legacy configuration file for shares, but not the only one. Because
+ * of that, dfstab handling is done in the main code with calls to
+ * this function and protocol specific calls to deal with formating
+ * options into dfstab/share compatible syntax. Since not everything
+ * will be dfstab, there is a provision for calling a protocol
+ * specific plugin interface that allows the protocol plugin to do its
+ * own legacy files and skip the dfstab update.
+ */
+
+int
+sa_update_legacy(sa_share_t share, char *proto)
+{
+ FILE *dfstab;
+ int ret = SA_OK;
+ xfs_sharelist_t *list;
+ char *path;
+ sigset_t old, new;
+ char *persist;
+
+ ret = sa_proto_update_legacy(proto, share);
+ if (ret != SA_NOT_IMPLEMENTED)
+ return (ret);
+ /* do the dfstab format */
+ persist = sa_get_share_attr(share, "type");
+ /*
+ * only update if the share is not transient -- no share type
+ * set or the type is not "transient".
+ */
+ if (persist == NULL || strcmp(persist, "transient") != 0) {
+ dfstab = open_dfstab(SA_LEGACY_DFSTAB);
+ if (dfstab != NULL) {
+ (void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
+ (void) sigprocmask(SIG_BLOCK, NULL, &new);
+ (void) sigaddset(&new, SIGHUP);
+ (void) sigaddset(&new, SIGINT);
+ (void) sigaddset(&new, SIGQUIT);
+ (void) sigaddset(&new, SIGTSTP);
+ (void) sigprocmask(SIG_SETMASK, &new, &old);
+ path = sa_get_share_attr(share, "path");
+ (void) lockf(fileno(dfstab), F_LOCK, 0);
+ list = getdfstab(dfstab);
+ rewind(dfstab);
+ if (list != NULL)
+ list = remdfsentry(list, path, proto);
+ list = adddfsentry(list, share, proto);
+ outdfstab(dfstab, list);
+ (void) fflush(dfstab);
+ (void) lockf(fileno(dfstab), F_ULOCK, 0);
+ (void) fsync(fileno(dfstab));
+ (void) sigprocmask(SIG_SETMASK, &old, NULL);
+ (void) fclose(dfstab);
+ sa_free_attr_string(path);
+ if (list != NULL)
+ dfs_free_list(list);
+ } else {
+ if (errno == EACCES || errno == EPERM) {
+ ret = SA_NO_PERMISSION;
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ }
+ }
+ if (persist != NULL)
+ sa_free_attr_string(persist);
+ return (ret);
+}
+
+/*
+ * sa_is_security(optname, proto)
+ *
+ * Check to see if optname is a security (named optionset) specific
+ * property for the specified protocol.
+ */
+
+int
+sa_is_security(char *optname, char *proto)
+{
+ int ret = 0;
+ if (proto != NULL)
+ ret = sa_proto_security_prop(proto, optname);
+ return (ret);
+}
+
+/*
+ * add_syntax_comment(root, line, err, todfstab)
+ *
+ * add a comment to the document indicating a syntax error. If
+ * todfstab is set, write it back to the dfstab file as well.
+ */
+
+static void
+add_syntax_comment(xmlNodePtr root, char *line, char *err, int todfstab)
+{
+ xmlNodePtr node;
+
+ node = xmlNewChild(root, NULL, (xmlChar *)"error", (xmlChar *)line);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)err);
+ }
+ if (todfstab)
+ sa_comment_line(line, err);
+}
+
+/*
+ * sa_is_share(object)
+ *
+ * returns true of the object is of type "share".
+ */
+
+int
+sa_is_share(void *object)
+{
+ if (object != NULL) {
+ if (strcmp((char *)((xmlNodePtr)object)->name, "share") == 0)
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * _sa_remove_property(property)
+ *
+ * remove a property only from the document.
+ */
+
+static void
+_sa_remove_property(sa_property_t property)
+{
+ xmlUnlinkNode((xmlNodePtr)property);
+ xmlFreeNode((xmlNodePtr)property);
+}
+
+/*
+ * sa_parse_legacy_options(group, options, proto)
+ *
+ * In order to support legacy configurations, we allow the protocol
+ * specific plugin to parse legacy syntax options (like those in
+ * /etc/dfs/dfstab). This adds a new optionset to the group (or
+ * share).
+ *
+ * Once the optionset has been created, we then get the derived
+ * optionset of the parent (options from the optionset of the parent
+ * and any parent it might have) and remove those from the created
+ * optionset. This avoids duplication of options.
+ */
+
+int
+sa_parse_legacy_options(sa_group_t group, char *options, char *proto)
+{
+ int ret = SA_INVALID_PROTOCOL;
+ sa_group_t parent;
+ parent = sa_get_parent_group(group);
+
+ if (proto != NULL)
+ ret = sa_proto_legacy_opts(proto, group, options);
+ /*
+ * if in a group, remove the inherited options and security
+ */
+ if (ret == SA_OK) {
+ if (parent != NULL) {
+ sa_optionset_t optionset;
+ sa_property_t popt, prop;
+ sa_optionset_t localoptions;
+ /* find parent options to remove from child */
+ optionset = sa_get_derived_optionset(parent, proto, 1);
+ localoptions = sa_get_optionset(group, proto);
+ if (optionset != NULL) {
+ for (popt = sa_get_property(optionset, NULL);
+ popt != NULL;
+ popt = sa_get_next_property(popt)) {
+ char *tag;
+ char *value1;
+ char *value2;
+
+ tag = sa_get_property_attr(popt, "type");
+ if (tag != NULL) {
+ prop = sa_get_property(localoptions, tag);
+ if (prop != NULL) {
+ value1 = sa_get_property_attr(popt, "value");
+ value2 = sa_get_property_attr(prop, "value");
+ if (value1 != NULL && value2 != NULL &&
+ strcmp(value1, value2) == 0) {
+ /* remove the property from the child */
+ (void) _sa_remove_property(prop);
+ }
+ if (value1 != NULL)
+ sa_free_attr_string(value1);
+ if (value2 != NULL)
+ sa_free_attr_string(value2);
+ }
+ sa_free_attr_string(tag);
+ }
+ }
+ prop = sa_get_property(localoptions, NULL);
+ if (prop == NULL && sa_is_share(group)) {
+ /*
+ * all properties removed so remove the
+ * optionset if it is on a share
+ */
+ (void) _sa_remove_optionset(localoptions);
+ }
+ sa_free_derived_optionset(optionset);
+ }
+ /*
+ * need to remove security here. If there are no
+ * security options on the local group/share, don't
+ * bother since those are the only ones that would be
+ * affected.
+ */
+ localoptions = sa_get_all_security_types(group, proto, 0);
+ if (localoptions != NULL) {
+ for (prop = sa_get_property(localoptions, NULL);
+ prop != NULL; prop = sa_get_next_property(prop)) {
+ char *tag;
+ sa_security_t security;
+ tag = sa_get_property_attr(prop, "type");
+ if (tag != NULL) {
+ security = sa_get_security(group, tag, proto);
+ sa_free_attr_string(tag);
+ for (popt = sa_get_property(security, NULL);
+ popt != NULL;
+ popt = sa_get_next_property(popt)) {
+ char *value1;
+ char *value2;
+
+ /* remove duplicates from this level */
+ value1 = sa_get_property_attr(popt, "value");
+ value2 = sa_get_property_attr(prop, "value");
+ if (value1 != NULL && value2 != NULL &&
+ strcmp(value1, value2) == 0) {
+ /* remove the property from the child */
+ (void) _sa_remove_property(prop);
+ }
+ if (value1 != NULL)
+ sa_free_attr_string(value1);
+ if (value2 != NULL)
+ sa_free_attr_string(value2);
+ }
+ }
+ }
+ (void) sa_destroy_optionset(localoptions);
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * dfs_free_list(list)
+ *
+ * Free the data in each list entry of the list as well as freeing the
+ * entries themselves. We need to avoid memory leaks and don't want to
+ * dereference any NULL members.
+ */
+
+static void
+dfs_free_list(xfs_sharelist_t *list)
+{
+ xfs_sharelist_t *entry;
+ for (entry = list; entry != NULL; entry = list) {
+ if (entry->path != NULL)
+ free(entry->path);
+ if (entry->resource != NULL)
+ free(entry->resource);
+ if (entry->fstype != NULL)
+ free(entry->fstype);
+ if (entry->options != NULL)
+ free(entry->options);
+ if (entry->description != NULL)
+ free(entry->description);
+ if (entry->origline != NULL)
+ free(entry->origline);
+ if (entry->group != NULL)
+ free(entry->group);
+ list = list->next;
+ free(entry);
+ }
+}
+
+/*
+ * parse_dfstab(dfstab, root)
+ *
+ * Open and read the existing dfstab, parsing each line and adding it
+ * to the internal configuration. Make sure syntax errors, etc are
+ * preserved as comments.
+ */
+
+static void
+parse_dfstab(char *dfstab, xmlNodePtr root)
+{
+ sa_share_t share;
+ sa_group_t group;
+ sa_group_t sgroup = NULL;
+ sa_group_t defgroup;
+ xfs_sharelist_t *head, *list;
+ int err;
+ int defined_group;
+ FILE *dfs;
+ char *oldprops;
+
+ /* read the dfstab format file and fill in the doc tree */
+
+ dfs = fopen(dfstab, "r");
+ if (dfs == NULL) {
+ return;
+ }
+
+ defgroup = sa_get_group("default");
+
+ for (head = list = getdfstab(dfs);
+ list != NULL;
+ list = list->next) {
+ share = NULL;
+ group = NULL;
+ defined_group = 0;
+ err = 0;
+
+ if (list->origline == NULL) {
+ /*
+ * Comment line that we will likely skip.
+ * If the line has the syntax:
+ * # error: string: string
+ * It should be preserved until manually deleted.
+ */
+ if (list->description != NULL &&
+ strncmp(list->description, "# Error: ", 9) == 0) {
+ char *line;
+ char *error;
+ char *cmd;
+ line = strdup(list->description);
+ if (line != NULL) {
+ error = line + 9;
+ cmd = strchr(error, ':');
+ if (cmd != NULL) {
+ int len;
+ *cmd = '\0';
+ cmd += 2;
+ len = strlen(cmd);
+ cmd[len - 1] = '\0';
+ add_syntax_comment(root, cmd, error, 0);
+ }
+ free(line);
+ }
+ }
+ continue;
+ }
+ if (list->path != NULL && strlen(list->path) > 0 &&
+ *list->path == '/') {
+ share = sa_find_share(list->path);
+ if (share != NULL)
+ sgroup = sa_get_parent_group(share);
+ else
+ sgroup = NULL;
+ } else {
+ (void) printf(gettext("No share specified in dfstab: "
+ "line %d: %s\n"),
+ list->lineno, list->origline);
+ add_syntax_comment(root, list->origline,
+ gettext("No share specified"),
+ 1);
+ continue;
+ }
+ if (list->group != NULL && strlen(list->group) > 0) {
+ group = sa_get_group(list->group);
+ defined_group = 1;
+ } else {
+ group = defgroup;
+ }
+ if (defined_group && group == NULL) {
+ (void) printf(gettext("Unknown group used in dfstab: "
+ "line %d: %s\n"),
+ list->lineno, list->origline);
+ add_syntax_comment(root, list->origline,
+ gettext("Unknown group specified"), 1);
+ continue;
+ }
+ if (group != NULL) {
+ if (share == NULL) {
+ if (!defined_group && group == defgroup) {
+ /* this is an OK add for legacy */
+ share = sa_add_share(defgroup, list->path,
+ SA_SHARE_PERMANENT | SA_SHARE_PARSER,
+ &err);
+ if (share != NULL) {
+ if (list->description != NULL &&
+ strlen(list->description) > 0)
+ (void) sa_set_share_description(share,
+ list->description);
+ if (list->options != NULL &&
+ strlen(list->options) > 0) {
+ (void) sa_parse_legacy_options(share,
+ list->options,
+ list->fstype);
+ }
+ if (list->resource != NULL)
+ (void) sa_set_share_attr(share, "resource",
+ list->resource);
+ } else {
+ (void) printf(gettext("Error in dfstab: "
+ "line %d: %s\n"),
+ list->lineno, list->origline);
+ if (err != SA_BAD_PATH)
+ add_syntax_comment(root, list->origline,
+ gettext("Syntax"), 1);
+ else
+ add_syntax_comment(root, list->origline,
+ gettext("Path"), 1);
+ continue;
+ }
+ }
+ } else {
+ if (group != sgroup) {
+ (void) printf(gettext("Attempt to change"
+ "configuration in"
+ "dfstab: line %d: %s\n"),
+ list->lineno, list->origline);
+ add_syntax_comment(root, list->origline,
+ gettext("Attempt to change configuration"), 1);
+ continue;
+ }
+ /* its the same group but could have changed options */
+ oldprops = sa_proto_legacy_format(list->fstype, share, 0);
+ if (oldprops != NULL) {
+ if (list->options != NULL &&
+ strcmp(oldprops, list->options) != 0) {
+ sa_optionset_t opts;
+ sa_security_t secs;
+ /* possibly different values */
+ opts = sa_get_optionset((sa_group_t)share,
+ list->fstype);
+ (void) sa_destroy_optionset(opts);
+ for (secs = sa_get_security((sa_group_t)share,
+ NULL, list->fstype);
+ secs != NULL;
+ secs = sa_get_security((sa_group_t)share,
+ NULL, list->fstype)) {
+ (void) sa_destroy_security(secs);
+ }
+ (void) sa_parse_legacy_options(share,
+ list->options,
+ list->fstype);
+ }
+ }
+ }
+ } else {
+ /* shouldn't happen */
+ err = SA_CONFIG_ERR;
+ }
+
+ }
+ dfs_free_list(head);
+}
+
+/*
+ * legacy_removes(group, file)
+ *
+ * Find any shares that are "missing" from the legacy file. These
+ * should be removed from the configuration since they are likely from
+ * a legacy app or the admin modified the dfstab file directly. We
+ * have to support this even if it is not the recommended way to do
+ * things.
+ */
+
+static void
+legacy_removes(sa_group_t group, char *file)
+{
+ sa_share_t share;
+ char *path;
+ xfs_sharelist_t *list, *item;
+ FILE *dfstab;
+
+ dfstab = fopen(file, "r");
+ if (dfstab != NULL) {
+ list = getdfstab(dfstab);
+ (void) fclose(dfstab);
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ /* now see if the share is in the dfstab file */
+ path = sa_get_share_attr(share, "path");
+ if (path != NULL) {
+ item = finddfsentry(list, path);
+ if (item == NULL) {
+ /* the share was removed this way */
+ (void) sa_remove_share(share);
+ /* start over since the list was broken */
+ share = sa_get_share(group, NULL);
+ }
+ sa_free_attr_string(path);
+ }
+ }
+ if (list != NULL)
+ dfs_free_list(list);
+ }
+}
+
+/*
+ * getlegacyconfig(path, root)
+ *
+ * Parse dfstab and build the legacy configuration. This only gets
+ * called when a change was detected.
+ */
+
+void
+getlegacyconfig(char *path, xmlNodePtr *root)
+{
+ sa_group_t defgroup;
+
+ if (root != NULL) {
+ if (*root == NULL)
+ *root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
+ if (*root != NULL) {
+ if (strcmp(path, SA_LEGACY_DFSTAB) == 0) {
+ /*
+ * walk the default shares and find anything
+ * missing. we do this first to make sure it
+ * is cleaned up since there may be legacy
+ * code add/del via dfstab and we need to
+ * cleanup SMF.
+ */
+ defgroup = sa_get_group("default");
+ if (defgroup != NULL) {
+ legacy_removes(defgroup, path);
+ }
+ /* parse the dfstab and add anything new */
+ parse_dfstab(path, *root);
+ }
+ }
+ }
+}
+
+/*
+ * parse_sharetab(void)
+ *
+ * Read the /etc/dfs/sharetab file via libfsmgt and see which entries
+ * don't exist in the repository. These shares are marked transient.
+ * We also need to see if they are ZFS shares since ZFS bypasses the
+ * SMF repository.
+ */
+
+int
+parse_sharetab(void)
+{
+ fs_sharelist_t *list, *tmplist;
+ int err = 0;
+ sa_share_t share;
+ sa_group_t group;
+ sa_group_t lgroup;
+ char *groupname;
+ int legacy = 0;
+
+ list = fs_get_share_list(&err);
+ if (list == NULL)
+ return (legacy);
+
+ lgroup = sa_get_group("default");
+
+ for (tmplist = list; tmplist != NULL; tmplist = tmplist->next) {
+ group = NULL;
+ share = sa_find_share(tmplist->path);
+ if (share == NULL) {
+ /*
+ * this share is transient so needs to be
+ * added. Initially, this will be under
+ * default(legacy) unless it is a ZFS
+ * share. If zfs, we need a zfs group.
+ */
+ if (tmplist->resource != NULL &&
+ (groupname = strchr(tmplist->resource, '@')) != NULL) {
+ /* there is a defined group */
+ *groupname++ = '\0';
+ group = sa_get_group(groupname);
+ if (group != NULL) {
+ share = _sa_add_share(group, tmplist->path,
+ SA_SHARE_TRANSIENT, &err);
+ } else {
+ (void) printf(gettext("Group for temporary share"
+ "not found: %s\n"),
+ tmplist->path);
+ share = _sa_add_share(lgroup, tmplist->path,
+ SA_SHARE_TRANSIENT, &err);
+ }
+ } else {
+ if (sa_zfs_is_shared(tmplist->path)) {
+ group = sa_get_group("zfs");
+ if (group == NULL) {
+ group = sa_create_group("zfs", &err);
+ if (group == NULL && err == SA_NO_PERMISSION) {
+ group = _sa_create_group("zfs");
+ }
+ if (group != NULL) {
+ (void) sa_create_optionset(group,
+ tmplist->fstype);
+ (void) sa_set_group_attr(group, "zfs", "true");
+ }
+ }
+ if (group != NULL) {
+ share = _sa_add_share(group, tmplist->path,
+ SA_SHARE_TRANSIENT, &err);
+ }
+ } else {
+ share = _sa_add_share(lgroup, tmplist->path,
+ SA_SHARE_TRANSIENT, &err);
+ }
+ }
+ if (share == NULL)
+ (void) printf(gettext("Problem with transient: %s\n"),
+ sa_errorstr(err));
+ if (share != NULL)
+ set_node_attr(share, "shared", "true");
+
+ if (err == SA_OK) {
+ if (tmplist->options != NULL &&
+ strlen(tmplist->options) > 0) {
+ (void) sa_parse_legacy_options(share,
+ tmplist->options,
+ tmplist->fstype);
+ }
+ if (tmplist->resource != NULL &&
+ strcmp(tmplist->resource, "-") != 0)
+ set_node_attr(share, "resource", tmplist->resource);
+ if (tmplist->description != NULL) {
+ xmlNodePtr node;
+ node = xmlNewChild((xmlNodePtr)share, NULL,
+ (xmlChar *)"description", NULL);
+ xmlNodeSetContent(node,
+ (xmlChar *)tmplist->description);
+ }
+ legacy = 1;
+ }
+ } else {
+ /*
+ * if this is a legacy share, mark as shared so we
+ * only update sharetab appropriately.
+ */
+ set_node_attr(share, "shared", "true");
+ }
+ }
+ fs_free_share_list(list);
+ return (legacy);
+}
+
+/*
+ * get the transient shares from the sharetab (or other) file. since
+ * these are transient, they only appear in the working file and not
+ * in a repository.
+ */
+int
+gettransients(xmlNodePtr *root)
+{
+ int legacy = 0;
+
+ if (root != NULL) {
+ if (*root == NULL)
+ *root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
+ if (*root != NULL) {
+ legacy = parse_sharetab();
+ }
+ }
+ return (legacy);
+}
+
+/*
+ * sa_has_prop(optionset, prop)
+ *
+ * Is the specified property a member of the optionset?
+ */
+
+int
+sa_has_prop(sa_optionset_t optionset, sa_property_t prop)
+{
+ char *name;
+ sa_property_t otherprop;
+ int result = 0;
+
+ if (optionset != NULL) {
+ name = sa_get_property_attr(prop, "type");
+ if (name != NULL) {
+ otherprop = sa_get_property(optionset, name);
+ if (otherprop != NULL)
+ result = 1;
+ sa_free_attr_string(name);
+ }
+ }
+ return (result);
+}
+
+/*
+ * Update legacy files
+ *
+ * Provides functions to add/remove/modify individual entries
+ * in dfstab and sharetab
+ */
+
+void
+update_legacy_config(void)
+{
+ /*
+ * no longer used -- this is a placeholder in case we need to
+ * add it back later.
+ */
+}
+
+/*
+ * sa_valid_property(object, proto, property)
+ *
+ * check to see if the specified property is valid relative to the
+ * specified protocol. The protocol plugin is called to do the work.
+ */
+
+int
+sa_valid_property(void *object, char *proto, sa_property_t property)
+{
+ int ret = SA_OK;
+
+ if (proto != NULL && property != NULL) {
+ ret = sa_proto_valid_prop(proto, property, object);
+ }
+
+ return (ret);
+}
+
+/*
+ * sa_fstype(path)
+ *
+ * Given path, return the string representing the path's file system
+ * type. This is used to discover ZFS shares.
+ */
+
+char *
+sa_fstype(char *path)
+{
+ int err;
+ struct stat st;
+
+ err = stat(path, &st);
+ if (err < 0) {
+ err = SA_NO_SUCH_PATH;
+ } else {
+ err = SA_OK;
+ }
+ if (err == SA_OK) {
+ /* have a valid path at this point */
+ return (strdup(st.st_fstype));
+ }
+ return (NULL);
+}
+
+void
+sa_free_fstype(char *type)
+{
+ free(type);
+}
+
+/*
+ * sa_get_derived_optionset(object, proto, hier)
+ *
+ * Work backward to the top of the share object tree and start
+ * copying protocol specific optionsets into a newly created
+ * optionset that doesn't have a parent (it will be freed
+ * later). This provides for the property inheritence model. That
+ * is, properties closer to the share take precedence over group
+ * level. This also provides for groups of groups in the future.
+ */
+
+sa_optionset_t
+sa_get_derived_optionset(void *object, char *proto, int hier)
+{
+ sa_optionset_t newoptionset;
+ sa_optionset_t optionset;
+ sa_group_t group;
+
+ if (hier &&
+ (group = sa_get_parent_group((sa_share_t)object)) != NULL) {
+ newoptionset = sa_get_derived_optionset((void *)group, proto, hier);
+ } else {
+ newoptionset = (sa_optionset_t)xmlNewNode(NULL,
+ (xmlChar *)"optionset");
+ if (newoptionset != NULL) {
+ sa_set_optionset_attr(newoptionset, "type", proto);
+ }
+ }
+ /* dont' do anything if memory wasn't allocated */
+ if (newoptionset == NULL)
+ return (NULL);
+
+ /* found the top so working back down the stack */
+ optionset = sa_get_optionset((sa_optionset_t)object, proto);
+ if (optionset != NULL) {
+ sa_property_t prop;
+ /* add optionset to the newoptionset */
+ for (prop = sa_get_property(optionset, NULL);
+ prop != NULL; prop = sa_get_next_property(prop)) {
+ sa_property_t newprop;
+ char *name;
+ char *value;
+ name = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+ if (name != NULL) {
+ newprop = sa_get_property(newoptionset, name);
+ /* replace the value with the new value */
+ if (newprop != NULL) {
+ /*
+ * only set if value is non NULL, old value ok
+ * if it is NULL.
+ */
+ if (value != NULL)
+ set_node_attr(newprop, "value", value);
+ } else {
+ /* an entirely new property */
+ if (value != NULL) {
+ newprop = sa_create_property(name, value);
+ if (newprop != NULL) {
+ newprop = (sa_property_t)
+ xmlAddChild((xmlNodePtr)newoptionset,
+ (xmlNodePtr)newprop);
+ }
+ }
+ }
+ sa_free_attr_string(name);
+ }
+ if (value != NULL)
+ sa_free_attr_string(value);
+ }
+ }
+ return (newoptionset);
+}
+
+void
+sa_free_derived_optionset(sa_optionset_t optionset)
+{
+ /* while it shouldn't be linked, it doesn't hurt */
+ if (optionset != NULL) {
+ xmlUnlinkNode((xmlNodePtr) optionset);
+ xmlFreeNode((xmlNodePtr) optionset);
+ }
+}
+
+/*
+ * sa_get_all_security_types(object, proto, hier)
+ *
+ * find all the security types set for this object. This is
+ * preliminary to getting a derived security set. The return value is an
+ * optionset containg properties which are the sectype values found by
+ * walking up the XML document struture. The returned optionset
+ * is a derived optionset.
+ *
+ * If hier is 0, only look at object. If non-zero, walk up the tree.
+ */
+sa_optionset_t
+sa_get_all_security_types(void *object, char *proto, int hier)
+{
+ sa_optionset_t options;
+ sa_security_t security;
+ sa_group_t group;
+ sa_property_t prop;
+
+ options = NULL;
+
+ if (hier &&
+ (group = sa_get_parent_group((sa_share_t)object)) != NULL) {
+ options = sa_get_all_security_types((void *)group, proto, hier);
+ } else {
+ options = (sa_optionset_t)xmlNewNode(NULL,
+ (xmlChar *)"optionset");
+ }
+ /* hit the top so collect the security types working back */
+ if (options != NULL) {
+ for (security = sa_get_security((sa_group_t)object, NULL, NULL);
+ security != NULL; security = sa_get_next_security(security)) {
+ char *type;
+ char *sectype;
+
+ type = sa_get_security_attr(security, "type");
+ if (type != NULL) {
+ if (strcmp(type, proto) != 0) {
+ sa_free_attr_string(type);
+ continue;
+ }
+ sectype = sa_get_security_attr(security, "sectype");
+ if (sectype != NULL) {
+ /*
+ * have a security type, check to see if
+ * already present in optionset and add if it
+ * isn't.
+ */
+ if (sa_get_property(options, sectype) == NULL) {
+ prop = sa_create_property(sectype, "true");
+ if (prop != NULL)
+ prop = (sa_property_t)
+ xmlAddChild((xmlNodePtr)options,
+ (xmlNodePtr)prop);
+ }
+ sa_free_attr_string(sectype);
+ }
+ sa_free_attr_string(type);
+ }
+ }
+ }
+ return (options);
+}
+
+/*
+ * sa_get_derived_security(object, sectype, proto, hier)
+ *
+ * Get the derived security(named optionset) for the object given the
+ * sectype and proto. If hier is non-zero, walk up the tree to get all
+ * properties defined for this object, otherwise just those on the
+ * object.
+ */
+
+sa_security_t
+sa_get_derived_security(void *object, char *sectype, char *proto, int hier)
+{
+ sa_security_t newsecurity;
+ sa_security_t security;
+ sa_group_t group;
+
+ if (hier &&
+ (group = sa_get_parent_group((sa_share_t)object)) != NULL) {
+ newsecurity = sa_get_derived_security((void *)group,
+ sectype, proto, hier);
+ } else {
+ newsecurity = (sa_security_t)xmlNewNode(NULL,
+ (xmlChar *)"security");
+ if (newsecurity != NULL) {
+ sa_set_security_attr(newsecurity, "type", proto);
+ sa_set_security_attr(newsecurity, "sectype", sectype);
+ }
+ }
+ /* dont' do anything if memory wasn't allocated */
+ if (newsecurity == NULL)
+ return (NULL);
+
+ /* found the top so working back down the stack */
+ security = sa_get_security((sa_security_t)object, sectype, proto);
+ if (security != NULL) {
+ sa_property_t prop;
+ /* add security to the newsecurity */
+ for (prop = sa_get_property(security, NULL);
+ prop != NULL; prop = sa_get_next_property(prop)) {
+ sa_property_t newprop;
+ char *name;
+ char *value;
+ name = sa_get_property_attr(prop, "type");
+ value = sa_get_property_attr(prop, "value");
+ if (name != NULL) {
+ newprop = sa_get_property(newsecurity, name);
+ /* replace the value with the new value */
+ if (newprop != NULL) {
+ /*
+ * only set if value is non NULL, old value ok
+ * if it is NULL.
+ */
+ if (value != NULL)
+ set_node_attr(newprop, name, value);
+ } else {
+ /* an entirely new property */
+ if (value != NULL) {
+ newprop = sa_create_property(name, value);
+ newprop = (sa_property_t)
+ xmlAddChild((xmlNodePtr)newsecurity,
+ (xmlNodePtr)newprop);
+ }
+ }
+ sa_free_attr_string(name);
+ }
+ if (value != NULL)
+ sa_free_attr_string(value);
+ }
+ }
+ return (newsecurity);
+}
+
+void
+sa_free_derived_security(sa_security_t security)
+{
+ /* while it shouldn't be linked, it doesn't hurt */
+ if (security != NULL) {
+ xmlUnlinkNode((xmlNodePtr)security);
+ xmlFreeNode((xmlNodePtr)security);
+ }
+}
+
+/*
+ * sharetab utility functions
+ *
+ * makes use of the original sharetab.c from fs.d/nfs/lib
+ */
+
+/*
+ * fillshare(share, proto, sh)
+ *
+ * Fill the struct share with values obtained from the share object.
+ */
+static void
+fillshare(sa_share_t share, char *proto, struct share *sh)
+{
+ char *groupname = NULL;
+ char *value;
+ sa_group_t group;
+ char *buff;
+ char *zfs;
+
+ group = sa_get_parent_group(share);
+ if (group != NULL) {
+ zfs = sa_get_group_attr(group, "zfs");
+ groupname = sa_get_group_attr(group, "name");
+
+ if (groupname != NULL &&
+ (strcmp(groupname, "default") == 0 || zfs != NULL)) {
+ /*
+ * since the groupname is either "default" or the
+ * group is a ZFS group, we don't want to keep
+ * groupname. We do want it if it is any other type of
+ * group.
+ */
+ sa_free_attr_string(groupname);
+ groupname = NULL;
+ }
+ if (zfs != NULL)
+ sa_free_attr_string(zfs);
+ }
+
+ value = sa_get_share_attr(share, "path");
+ if (value != NULL) {
+ sh->sh_path = strdup(value);
+ sa_free_attr_string(value);
+ }
+
+ value = sa_get_share_attr(share, "resource");
+ if (value != NULL || groupname != NULL) {
+ int len = 0;
+
+ if (value != NULL)
+ len += strlen(value);
+ if (groupname != NULL)
+ len += strlen(groupname);
+ len += 3; /* worst case */
+ buff = malloc(len);
+ (void) snprintf(buff, len, "%s%s%s",
+ (value != NULL && strlen(value) > 0) ? value : "-",
+ groupname != NULL ? "@" : "",
+ groupname != NULL ? groupname : "");
+ sh->sh_res = buff;
+ if (value != NULL)
+ sa_free_attr_string(value);
+ if (groupname != NULL) {
+ sa_free_attr_string(groupname);
+ groupname = NULL;
+ }
+ } else {
+ sh->sh_res = strdup("-");
+ }
+
+ sh->sh_fstype = strdup(proto);
+ value = sa_proto_legacy_format(proto, share, 1);
+ if (value != NULL) {
+ if (strlen(value) > 0)
+ sh->sh_opts = strdup(value);
+ else
+ sh->sh_opts = strdup("rw");
+ free(value);
+ } else
+ sh->sh_opts = strdup("rw");
+
+ value = sa_get_share_description(share);
+ if (value != NULL) {
+ sh->sh_descr = strdup(value);
+ sa_free_share_description(value);
+ } else
+ sh->sh_descr = strdup("");
+}
+
+/*
+ * emptyshare(sh)
+ *
+ * Free the strings in the non-NULL members of sh.
+ */
+
+static void
+emptyshare(struct share *sh)
+{
+ if (sh->sh_path != NULL)
+ free(sh->sh_path);
+ sh->sh_path = NULL;
+ if (sh->sh_res != NULL)
+ free(sh->sh_res);
+ sh->sh_res = NULL;
+ if (sh->sh_fstype != NULL)
+ free(sh->sh_fstype);
+ sh->sh_fstype = NULL;
+ if (sh->sh_opts != NULL)
+ free(sh->sh_opts);
+ sh->sh_opts = NULL;
+ if (sh->sh_descr != NULL)
+ free(sh->sh_descr);
+ sh->sh_descr = NULL;
+}
+
+/*
+ * sa_update_sharetab(share, proto)
+ *
+ * Update the sharetab file with info from the specified share.
+ * This could be an update or add.
+ */
+
+int
+sa_update_sharetab(sa_share_t share, char *proto)
+{
+ int ret = SA_OK;
+ struct share shtab;
+ char *path;
+ int logging = 0;
+ FILE *sharetab;
+ sigset_t old, new;
+
+ path = sa_get_share_attr(share, "path");
+ if (path != NULL) {
+ (void) memset(&shtab, '\0', sizeof (shtab));
+ sharetab = fopen(SA_LEGACY_SHARETAB, "r+");
+ if (sharetab == NULL) {
+ sharetab = fopen(SA_LEGACY_SHARETAB, "w+");
+ }
+ if (sharetab != NULL) {
+ (void) setvbuf(sharetab, NULL, _IOLBF, BUFSIZ * 8);
+ (void) sigprocmask(SIG_BLOCK, NULL, &new);
+ (void) sigaddset(&new, SIGHUP);
+ (void) sigaddset(&new, SIGINT);
+ (void) sigaddset(&new, SIGQUIT);
+ (void) sigaddset(&new, SIGTSTP);
+ (void) sigprocmask(SIG_SETMASK, &new, &old);
+ (void) lockf(fileno(sharetab), F_LOCK, 0);
+ (void) remshare(sharetab, path, &logging);
+ /* fill in share structure and write it out */
+ (void) fillshare(share, proto, &shtab);
+ (void) putshare(sharetab, &shtab);
+ emptyshare(&shtab);
+ (void) fflush(sharetab);
+ (void) lockf(fileno(sharetab), F_ULOCK, 0);
+ (void) fsync(fileno(sharetab));
+ (void) sigprocmask(SIG_SETMASK, &old, NULL);
+ (void) fclose(sharetab);
+ } else {
+ if (errno == EACCES || errno == EPERM) {
+ ret = SA_NO_PERMISSION;
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ }
+ sa_free_attr_string(path);
+ }
+ return (ret);
+}
+
+/*
+ * sa_delete_sharetab(path, proto)
+ *
+ * remove the specified share from sharetab.
+ */
+
+int
+sa_delete_sharetab(char *path, char *proto)
+{
+ int ret = SA_OK;
+ int logging = 0;
+ FILE *sharetab;
+ sigset_t old, new;
+#ifdef lint
+ proto = proto;
+#endif
+
+ if (path != NULL) {
+ sharetab = fopen(SA_LEGACY_SHARETAB, "r+");
+ if (sharetab == NULL) {
+ sharetab = fopen(SA_LEGACY_SHARETAB, "w+");
+ }
+ if (sharetab != NULL) {
+ /* should block keyboard level signals around the lock */
+ (void) sigprocmask(SIG_BLOCK, NULL, &new);
+ (void) sigaddset(&new, SIGHUP);
+ (void) sigaddset(&new, SIGINT);
+ (void) sigaddset(&new, SIGQUIT);
+ (void) sigaddset(&new, SIGTSTP);
+ (void) sigprocmask(SIG_SETMASK, &new, &old);
+ (void) lockf(fileno(sharetab), F_LOCK, 0);
+ ret = remshare(sharetab, path, &logging);
+ (void) fflush(sharetab);
+ (void) lockf(fileno(sharetab), F_ULOCK, 0);
+ (void) fsync(fileno(sharetab));
+ (void) sigprocmask(SIG_SETMASK, &old, NULL);
+ (void) fclose(sharetab);
+ } else {
+ if (errno == EACCES || errno == EPERM) {
+ ret = SA_NO_PERMISSION;
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ }
+ }
+ return (ret);
+}
diff --git a/usr/src/lib/libshare/common/llib-lshare b/usr/src/lib/libshare/common/llib-lshare
new file mode 100644
index 0000000000..d426f1bf58
--- /dev/null
+++ b/usr/src/lib/libshare/common/llib-lshare
@@ -0,0 +1,32 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include "libshare.h"
diff --git a/usr/src/lib/libshare/common/mapfile-vers b/usr/src/lib/libshare/common/mapfile-vers
new file mode 100644
index 0000000000..9585f9a386
--- /dev/null
+++ b/usr/src/lib/libshare/common/mapfile-vers
@@ -0,0 +1,110 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+SUNWprivate_1.1 {
+ global:
+ sa_get_optionset;
+ sa_create_property;
+ sa_get_property;
+ sa_create_security;
+ sa_update_legacy;
+ sa_get_group_attr;
+ sa_free_share_description;
+ sa_remove_group;
+ sa_add_property;
+ sa_get_sub_group;
+ sa_get_next_optionset;
+ sa_update_property;
+ sa_destroy_security;
+ sa_update_sharetab;
+ sa_create_protocol_properties;
+ sa_get_group;
+ sa_get_security;
+ sa_get_protocol_property;
+ sa_add_share;
+ sa_valid_group_name;
+ sa_get_optionset_attr;
+ sa_remove_property;
+ sa_set_security_attr;
+ sa_delete_sharetab;
+ sa_format_free;
+ sa_add_protocol_property;
+ sa_check_path;
+ sa_free_derived_optionset;
+ sa_is_security;
+ sa_get_protocols;
+ sa_get_parent_group;
+ sa_set_optionset_attr;
+ sa_commit_properties;
+ sa_parse_legacy_options;
+ sa_zfs_is_shared;
+ sa_get_derived_optionset;
+ sa_move_share;
+ sa_group_is_zfs;
+ sa_update_config;
+ sa_get_share_attr;
+ sa_create_optionset;
+ sa_valid_property;
+ sa_proto_legacy_format;
+ sa_proto_valid_space;
+ sa_proto_space_alias;
+ sa_get_next_protocol_property;
+ sa_remove_share;
+ sa_is_share;
+ sa_get_share_description;
+ sa_get_share;
+ sa_get_next_group;
+ sa_set_share_attr;
+ sa_destroy_optionset;
+ sa_get_derived_security;
+ sa_proto_get_properties;
+ sa_get_all_security_types;
+ sa_get_resource;
+ sa_set_share_description;
+ sa_set_group_attr;
+ sa_disable_share;
+ sa_get_next_property;
+ sa_get_next_security;
+ sa_get_protocol_status;
+ sa_init;
+ sa_find_share;
+ sa_set_protocol_property;
+ sa_fini;
+ sa_errorstr;
+ sa_get_next_share;
+ sa_free_attr_string;
+ sa_get_property_attr;
+ sa_get_security_attr;
+ sa_delete_legacy;
+ sa_free_derived_security;
+ sa_enable_share;
+ sa_create_group;
+ sa_valid_protocol;
+ local:
+ *;
+};
+
diff --git a/usr/src/lib/libshare/common/parser.c b/usr/src/lib/libshare/common/parser.c
new file mode 100644
index 0000000000..3bd8f9f303
--- /dev/null
+++ b/usr/src/lib/libshare/common/parser.c
@@ -0,0 +1,121 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#define TK_INIT 0
+#define TK_TOKEN 1
+#define TK_SKIPWHITE 2
+#define TK_QUOTED 3
+
+/*
+ * assumes quoted strings are delimited by white space (i.e sp
+ * "string" sp). Backslash can be used to quote a quote mark.
+ * quoted strings will have the quotes stripped.
+ */
+
+char *
+get_token(char *string)
+{
+ static char *orig = NULL;
+ static char *curp;
+ char *ret;
+ int state = TK_INIT;
+ int c;
+ int quotechar;
+
+ if (string != orig || string == NULL) {
+ orig = string;
+ curp = string;
+ if (string == NULL) {
+ return (NULL);
+ }
+ }
+ ret = curp;
+ while ((c = *curp) != '\0') {
+ switch (state) {
+ case TK_SKIPWHITE:
+ case TK_INIT:
+ if (isspace(c)) {
+ while (*curp && isspace(*curp))
+ curp++;
+ ret = curp;
+ }
+ if (c == '"' || c == '\'') {
+ state = TK_QUOTED;
+ curp++;
+ ret = curp;
+ quotechar = c; /* want to match for close */
+ } else {
+ state = TK_TOKEN;
+ }
+ break;
+ case TK_TOKEN:
+ switch (c) {
+ case '\\':
+ curp++;
+ if (*curp) {
+ curp++;
+ break;
+ } else {
+ return (ret);
+ }
+ break;
+ default:
+ if (*curp == '\0' || isspace(c)) {
+ *curp++ = '\0';
+ return (ret);
+ }
+ curp++;
+ break;
+ }
+ break;
+ case TK_QUOTED:
+ switch (c) {
+ case '\\':
+ curp++;
+ if (*curp) {
+ curp++;
+ break;
+ }
+ curp++;
+ break;
+ default:
+ if (c == '\0' || c == quotechar) {
+ *curp++ = '\0';
+ return (ret);
+ }
+ curp++;
+ break;
+ }
+ break;
+ }
+ }
+ return (NULL);
+}
diff --git a/usr/src/lib/libshare/common/plugin.c b/usr/src/lib/libshare/common/plugin.c
new file mode 100644
index 0000000000..54b95d8fbb
--- /dev/null
+++ b/usr/src/lib/libshare/common/plugin.c
@@ -0,0 +1,458 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libshare.h>
+#include "libshare_impl.h"
+#include <dlfcn.h>
+#include <link.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <libintl.h>
+
+/*
+ * protocol plugin interface
+ *
+ * finds plugins and makes them accessible. This is only "used" by
+ * libshare.so.
+ */
+
+struct sa_proto_plugin *sap_proto_list;
+
+static struct sa_proto_handle sa_proto_handle;
+
+void proto_plugin_fini();
+
+/*
+ * proto_plugin_init()
+ *
+ * Initialize the protocol specific plugin modules.
+ *
+ * Walk /usr/lib/fs/\* for libshare_*.so modules. That is,
+ * /usr/lib/fs/nfs/libshare_nfs.so. The protocol specific directory
+ * would have a modules with name libshare_<proto>.so. If one is
+ * found, initialize it and add to the internal list of
+ * protocols. These are used for protocol specifici operations.
+ */
+
+int
+proto_plugin_init()
+{
+ struct sa_proto_plugin *proto;
+ int num_protos = 0;
+ int err;
+ struct sa_plugin_ops *plugin_ops;
+ void *dlhandle;
+ DIR *dir;
+ struct dirent *dent;
+ int ret = SA_OK;
+ struct stat st;
+
+ /*
+ * should walk "/usr/lib/fs/" for files of the form:
+ * libshare_*.so
+ */
+ dir = opendir(SA_LIB_DIR);
+ if (dir != NULL) {
+ while (ret == SA_OK && (dent = readdir(dir)) != NULL) {
+ char path[MAXPATHLEN];
+ (void) snprintf(path, MAXPATHLEN,
+ "%s/%s/libshare_%s.so", SA_LIB_DIR,
+ dent->d_name, dent->d_name);
+ if (stat(path, &st) < 0) {
+ /* file doesn't exist, so don't try to map it */
+ continue;
+ }
+ dlhandle = dlopen(path, RTLD_NOW|RTLD_GLOBAL|RTLD_WORLD);
+ if (dlhandle != NULL) {
+ plugin_ops = (struct sa_plugin_ops *)
+ dlsym(dlhandle, "sa_plugin_ops");
+ proto = (struct sa_proto_plugin *)
+ calloc(1, sizeof (struct sa_proto_plugin));
+ if (proto != NULL) {
+ proto->plugin_ops = plugin_ops;
+ proto->plugin_handle = dlhandle;
+ num_protos++;
+ proto->plugin_next = sap_proto_list;
+ sap_proto_list = proto;
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ } else {
+ (void) fprintf(stderr,
+ gettext("Error in plugin for protocol %s: %s\n"),
+ dent->d_name, dlerror());
+ }
+ }
+ (void) closedir(dir);
+ }
+ if (ret == SA_OK) {
+ sa_proto_handle.sa_proto =
+ (char **)calloc(num_protos, sizeof (char *));
+ sa_proto_handle.sa_ops =
+ (struct sa_plugin_ops **)calloc(num_protos,
+ sizeof (struct sa_plugin_ops *));
+ if (sa_proto_handle.sa_proto != NULL &&
+ sa_proto_handle.sa_ops != NULL) {
+ int i;
+ struct sa_proto_plugin *tmp;
+ for (i = 0, tmp = sap_proto_list; i < num_protos;
+ tmp = tmp->plugin_next) {
+ err = 0;
+ if (tmp->plugin_ops->sa_init != NULL)
+ err = tmp->plugin_ops->sa_init();
+ if (err == SA_OK) {
+ /* only include if the init succeeded or was NULL */
+ sa_proto_handle.sa_num_proto++;
+ sa_proto_handle.sa_ops[i] = tmp->plugin_ops;
+ sa_proto_handle.sa_proto[i] =
+ tmp->plugin_ops->sa_protocol;
+ i++;
+ }
+ }
+ }
+ } else {
+ /* there was an error, so cleanup prior to return of failure. */
+ proto_plugin_fini();
+ }
+ return (ret);
+}
+
+/*
+ * proto_plugin_fini()
+ *
+ * uninitialize all the plugin modules.
+ */
+
+void
+proto_plugin_fini()
+{
+ /*
+ * free up all the protocols, calling their fini, if there is
+ * one.
+ */
+ while (sap_proto_list != NULL) {
+ struct sa_proto_plugin *next;
+ next = sap_proto_list->plugin_next;
+ sap_proto_list->plugin_ops->sa_fini();
+ if (sap_proto_list->plugin_handle != NULL)
+ (void) dlclose(sap_proto_list->plugin_handle);
+ free(sap_proto_list);
+ sap_proto_list = next;
+ }
+ if (sa_proto_handle.sa_ops != NULL) {
+ free(sa_proto_handle.sa_ops);
+ sa_proto_handle.sa_ops = NULL;
+ }
+ if (sa_proto_handle.sa_proto != NULL) {
+ free(sa_proto_handle.sa_proto);
+ sa_proto_handle.sa_proto = NULL;
+ }
+ sa_proto_handle.sa_num_proto = 0;
+}
+
+/*
+ * find_protocol(proto)
+ *
+ * Search the plugin list for the specified protocol and return the
+ * ops vector. NULL if protocol is not defined.
+ */
+
+static struct sa_plugin_ops *
+find_protocol(char *proto)
+{
+ int i;
+
+ if (proto != NULL) {
+ for (i = 0; i < sa_proto_handle.sa_num_proto; i++) {
+ if (strcmp(proto, sa_proto_handle.sa_proto[i]) == 0)
+ return (sa_proto_handle.sa_ops[i]);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * sa_proto_share(proto, share)
+ *
+ * Activate a share for the specified protocol.
+ */
+
+int
+sa_proto_share(char *proto, sa_share_t share)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_INVALID_PROTOCOL;
+
+ if (ops != NULL && ops->sa_share != NULL)
+ ret = ops->sa_share(share);
+ return (ret);
+}
+
+/*
+ * sa_proto_unshare(proto, path)
+ *
+ * Deactivate (unshare) the path for this protocol.
+ */
+
+int
+sa_proto_unshare(char *proto, char *path)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_INVALID_PROTOCOL;
+
+ if (ops != NULL && ops->sa_unshare != NULL)
+ ret = ops->sa_unshare(path);
+ return (ret);
+}
+
+/*
+ * sa_proto_valid_prop(proto, prop, opt)
+ *
+ * check to see if the specified prop is valid for this protocol.
+ */
+
+int
+sa_proto_valid_prop(char *proto, sa_property_t prop, sa_optionset_t opt)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = 0;
+
+ if (ops != NULL && ops->sa_valid_prop != NULL)
+ ret = ops->sa_valid_prop(prop, opt);
+ return (ret);
+}
+
+/*
+ * sa_proto_valid_space(proto, space)
+ *
+ * check if space is valid optionspace for proto.
+ * Protocols that don't implement this don't support spaces.
+ */
+int
+sa_proto_valid_space(char *proto, char *token)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = 0;
+
+ if (ops != NULL && ops->sa_valid_space != NULL)
+ ret = ops->sa_valid_space(token);
+ return (ret);
+}
+
+/*
+ * sa_proto_space_alias(proto, space)
+ *
+ * if the name for space is an alias, return its proper name. This is
+ * used to translate "default" values into proper form.
+ */
+char *
+sa_proto_space_alias(char *proto, char *space)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ char *ret = space;
+
+ if (ops != NULL && ops->sa_space_alias != NULL)
+ ret = ops->sa_space_alias(space);
+ return (ret);
+}
+
+/*
+ * sa_proto_security_prop(proto, token)
+ *
+ * Check to see if the property name in token is a valid named
+ * optionset property.
+ */
+
+int
+sa_proto_security_prop(char *proto, char *token)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = 0;
+
+ if (ops != NULL && ops->sa_security_prop != NULL)
+ ret = ops->sa_security_prop(token);
+ return (ret);
+}
+
+/*
+ * sa_proto_legacy_opts(proto, grouup, options)
+ *
+ * Have the protocol specific parser parse the options string and add
+ * an appropriate optionset to group.
+ */
+
+int
+sa_proto_legacy_opts(char *proto, sa_group_t group, char *options)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_INVALID_PROTOCOL;
+
+ if (ops != NULL && ops->sa_legacy_opts != NULL)
+ ret = ops->sa_legacy_opts(group, options);
+ return (ret);
+}
+
+/*
+ * sa_proto_legacy_format(proto, group, hier)
+ *
+ * Return a legacy format string representing either the group's
+ * properties or the groups hierarchical properties.
+ */
+
+char *
+sa_proto_legacy_format(char *proto, sa_group_t group, int hier)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ char *ret = NULL;
+
+ if (ops != NULL && ops->sa_legacy_format != NULL)
+ ret = ops->sa_legacy_format(group, hier);
+ return (ret);
+}
+
+void
+sa_format_free(char *str)
+{
+ free(str);
+}
+
+/*
+ * sharectl related API functions
+ */
+
+/*
+ * sa_proto_get_properties(proto)
+ *
+ * Return the set of properties that are specific to the
+ * protocol. These are usually in /etc/dfs/<proto> and related files,
+ * but only the protocol module knows which ones for sure.
+ */
+
+sa_protocol_properties_t
+sa_proto_get_properties(char *proto)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ sa_protocol_properties_t props = NULL;
+
+ if (ops != NULL && ops->sa_get_proto_set != NULL)
+ props = ops->sa_get_proto_set();
+ return (props);
+}
+
+/*
+ * sa_proto_set_property(proto, prop)
+ *
+ * Update the protocol specifiec property.
+ */
+
+int
+sa_proto_set_property(char *proto, sa_property_t prop)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_OK;
+ if (ops != NULL && ops->sa_set_proto_prop != NULL)
+ ret = ops->sa_set_proto_prop(prop);
+ return (ret);
+}
+
+/*
+ * sa_valid_protocol(proto)
+ *
+ * check to see if the protocol specified is defined by a
+ * plugin. Returns true (1) or false (0)
+ */
+
+int
+sa_valid_protocol(char *proto)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ return (ops != NULL);
+}
+
+/*
+ * Return the current operational status of the protocol
+ */
+
+char *
+sa_get_protocol_status(char *proto)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ char *ret = NULL;
+ if (ops != NULL && ops->sa_get_proto_status != NULL)
+ ret = ops->sa_get_proto_status(proto);
+ return (ret);
+}
+
+/*
+ * sa_proto_update_legacy(proto, share)
+ *
+ * Update the protocol specific legacy files if necessary for the
+ * specified share.
+ */
+
+int
+sa_proto_update_legacy(char *proto, sa_share_t share)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_NOT_IMPLEMENTED;
+
+ if (ops != NULL) {
+ if (ops->sa_update_legacy != NULL)
+ ret = ops->sa_update_legacy(share);
+ }
+ return (ret);
+}
+
+/*
+ * sa_delete_legacy(proto, share)
+ *
+ * remove the specified share from the protocol specific legacy files.
+ */
+
+int
+sa_proto_delete_legacy(char *proto, sa_share_t share)
+{
+ struct sa_plugin_ops *ops = find_protocol(proto);
+ int ret = SA_OK;
+
+ if (ops != NULL) {
+ if (ops->sa_delete_legacy != NULL)
+ ret = ops->sa_delete_legacy(share);
+ } else {
+ if (proto != NULL)
+ ret = SA_NOT_IMPLEMENTED;
+ else
+ ret = SA_INVALID_PROTOCOL;
+ }
+ return (ret);
+}
diff --git a/usr/src/lib/libshare/common/scfutil.c b/usr/src/lib/libshare/common/scfutil.c
new file mode 100644
index 0000000000..88447471a6
--- /dev/null
+++ b/usr/src/lib/libshare/common/scfutil.c
@@ -0,0 +1,1443 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* helper functions for using libscf with sharemgr */
+
+#include <libscf.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include "libshare.h"
+#include "libshare_impl.h"
+#include "scfutil.h"
+#include <string.h>
+#include <errno.h>
+#include <uuid/uuid.h>
+#include <sys/param.h>
+
+ssize_t scf_max_name_len;
+extern struct sa_proto_plugin *sap_proto_list;
+
+/*
+ * The SMF facility uses some properties that must exist. We want to
+ * skip over these when processing protocol options.
+ */
+static char *skip_props[] = {
+ "modify_authorization",
+ "action_authorization",
+ "value_authorization",
+ NULL
+};
+
+/*
+ * sa_scf_fini(handle)
+ *
+ * must be called when done. Called with the handle allocated in
+ * sa_scf_init(), it cleans up the state and frees any SCF resources
+ * still in use. Called by sa_fini().
+ */
+
+void
+sa_scf_fini(scfutilhandle_t *handle)
+{
+ if (handle != NULL) {
+ int unbind = 0;
+ if (handle->scope != NULL) {
+ unbind = 1;
+ scf_scope_destroy(handle->scope);
+ }
+ if (handle->service != NULL)
+ scf_service_destroy(handle->service);
+ if (handle->pg != NULL)
+ scf_pg_destroy(handle->pg);
+ if (handle->handle != NULL) {
+ handle->scf_state = SCH_STATE_UNINIT;
+ if (unbind)
+ (void) scf_handle_unbind(handle->handle);
+ scf_handle_destroy(handle->handle);
+ }
+ free(handle);
+ }
+}
+
+/*
+ * sa_scf_init()
+ *
+ * must be called before using any of the SCF functions. Called by
+ * sa_init() during the API setup.
+ */
+
+scfutilhandle_t *
+sa_scf_init()
+{
+ scfutilhandle_t *handle;
+
+ scf_max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
+ if (scf_max_name_len <= 0)
+ scf_max_name_len = SA_MAX_NAME_LEN + 1;
+
+ handle = calloc(1, sizeof (scfutilhandle_t));
+ if (handle != NULL) {
+ handle->scf_state = SCH_STATE_INITIALIZING;
+ handle->handle = scf_handle_create(SCF_VERSION);
+ if (handle->handle != NULL) {
+ if (scf_handle_bind(handle->handle) == 0) {
+ handle->scope = scf_scope_create(handle->handle);
+ handle->service = scf_service_create(handle->handle);
+ handle->pg = scf_pg_create(handle->handle);
+ handle->instance = scf_instance_create(handle->handle);
+ if (scf_handle_get_scope(handle->handle,
+ SCF_SCOPE_LOCAL, handle->scope) == 0) {
+ if (scf_scope_get_service(handle->scope,
+ SA_GROUP_SVC_NAME,
+ handle->service) != 0) {
+ goto err;
+ }
+ handle->scf_state = SCH_STATE_INIT;
+ if (sa_get_instance(handle, "default") != SA_OK) {
+ char **protolist;
+ int numprotos, i;
+ sa_group_t defgrp;
+ defgrp = sa_create_group("default", NULL);
+ if (defgrp != NULL) {
+ numprotos = sa_get_protocols(&protolist);
+ for (i = 0; i < numprotos; i++) {
+ (void) sa_create_optionset(defgrp,
+ protolist[i]);
+ }
+ if (protolist != NULL)
+ free(protolist);
+ }
+ }
+ } else {
+ goto err;
+ }
+ } else {
+ goto err;
+ }
+ } else {
+ free(handle);
+ handle = NULL;
+ (void) printf("libshare could not access SMF repository: %s\n",
+ scf_strerror(scf_error()));
+ }
+ }
+ return (handle);
+
+ /* error handling/unwinding */
+err:
+ (void) sa_scf_fini(handle);
+ (void) printf("libshare SMF initialization problem: %s\n",
+ scf_strerror(scf_error()));
+ return (NULL);
+}
+
+/*
+ * get_scf_limit(name)
+ *
+ * Since we use scf_limit a lot and do the same check and return the
+ * same value if it fails, implement as a function for code
+ * simplification. Basically, if name isn't found, return MAXPATHLEN
+ * (1024) so we have a reasonable default buffer size.
+ */
+static ssize_t
+get_scf_limit(uint32_t name)
+{
+ ssize_t vallen;
+
+ vallen = scf_limit(name);
+ if (vallen == (ssize_t)-1)
+ vallen = MAXPATHLEN;
+ return (vallen);
+}
+
+/*
+ * skip_property(name)
+ *
+ * internal function to check to see if a property is an SMF magic
+ * property that needs to be skipped.
+ */
+static int
+skip_property(char *name)
+{
+ int i;
+
+ for (i = 0; skip_props[i] != NULL; i++)
+ if (strcmp(name, skip_props[i]) == 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * generate_unique_sharename(sharename)
+ *
+ * Shares are represented in SMF as property groups. Due to share
+ * paths containing characters that are not allowed in SMF names and
+ * the need to be unique, we use UUIDs to construct a unique name.
+ */
+
+static void
+generate_unique_sharename(char *sharename)
+{
+ uuid_t uuid;
+
+ uuid_generate(uuid);
+ (void) strcpy(sharename, "S-");
+ uuid_unparse(uuid, sharename + 2);
+}
+
+/*
+ * valid_protocol(proto)
+ *
+ * check to see if the specified protocol is a valid one for the
+ * general sharemgr facility. We determine this by checking which
+ * plugin protocols were found.
+ */
+
+static int
+valid_protocol(char *proto)
+{
+ struct sa_proto_plugin *plugin;
+ for (plugin = sap_proto_list; plugin != NULL;
+ plugin = plugin->plugin_next)
+ if (strcmp(proto, plugin->plugin_ops->sa_protocol) == 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * sa_extract_pgroup(root, handle, pg, nodetype, proto, sectype)
+ *
+ * extract the name property group and create the specified type of
+ * node on the provided group. type will be optionset or security.
+ */
+
+static int
+sa_extract_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_propertygroup_t *pg,
+ char *nodetype, char *proto, char *sectype)
+{
+ xmlNodePtr node;
+ scf_iter_t *iter;
+ scf_property_t *prop;
+ scf_value_t *value;
+ char *name;
+ char *valuestr;
+ ssize_t vallen;
+ int ret = SA_OK;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+
+ node = xmlNewChild(root, NULL, (xmlChar *)nodetype, NULL);
+ if (node != NULL) {
+ if (proto != NULL)
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
+ if (sectype != NULL)
+ xmlSetProp(node, (xmlChar *)"sectype", (xmlChar *)sectype);
+ /*
+ * have node to work with so iterate over the properties
+ * in the pg and create option sub nodes.
+ */
+ iter = scf_iter_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ prop = scf_property_create(handle->handle);
+ name = malloc(scf_max_name_len);
+ valuestr = malloc(vallen);
+ /*
+ * want to iterate through the properties and add them
+ * to the base optionset.
+ */
+ if (iter != NULL && value != NULL && prop != NULL &&
+ valuestr != NULL && name != NULL) {
+ if (scf_iter_pg_properties(iter, pg) == 0) {
+ /* now iterate the properties in the group */
+ while (scf_iter_next_property(iter, prop) > 0) {
+ /* have a property */
+ if (scf_property_get_name(prop, name,
+ scf_max_name_len) > 0) {
+ /* some properties are part of the framework */
+ if (skip_property(name))
+ continue;
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr,
+ vallen) >= 0) {
+ sa_property_t saprop;
+ saprop = sa_create_property(name,
+ valuestr);
+ if (saprop != NULL) {
+ /*
+ * since in SMF, don't
+ * recurse. Use xmlAddChild
+ * directly, instead.
+ */
+ xmlAddChild(node,
+ (xmlNodePtr) saprop);
+ }
+ }
+ }
+ }
+ }
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ /* cleanup to avoid memory leaks */
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+ if (name != NULL)
+ free(name);
+ if (valuestr != NULL)
+ free(valuestr);
+ }
+ return (ret);
+}
+
+/*
+ * sa_extract_attrs(root, handle, instance)
+ *
+ * local function to extract the actual attributes/properties from the
+ * property group of the service instance. These are the well known
+ * attributes of "state" and "zfs". If additional attributes are
+ * added, they should be added here.
+ */
+
+static void
+sa_extract_attrs(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_instance_t *instance)
+{
+ scf_property_t *prop;
+ scf_value_t *value;
+ char *valuestr;
+ ssize_t vallen;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ prop = scf_property_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ valuestr = malloc(vallen);
+ if (prop != NULL && value != NULL && valuestr != NULL &&
+ scf_instance_get_pg(instance, "operation",
+ handle->pg) == 0) {
+ /*
+ * have a property group with desired name so now get
+ * the known attributes.
+ */
+ if (scf_pg_get_property(handle->pg, "state", prop) == 0) {
+ /* found the property so get the value */
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr, vallen) >= 0) {
+ xmlSetProp(root, (xmlChar *)"state",
+ (xmlChar *)valuestr);
+ }
+ }
+ }
+ if (scf_pg_get_property(handle->pg, "zfs", prop) == 0) {
+ /* found the property so get the value */
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr, vallen) > 0) {
+ xmlSetProp(root, (xmlChar *)"zfs",
+ (xmlChar *)valuestr);
+ }
+ }
+ }
+ }
+ if (valuestr != NULL)
+ free(valuestr);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+}
+
+/*
+ * list of known share attributes.
+ */
+
+static char *share_attr[] = {
+ "path",
+ "id",
+ "resource",
+ NULL,
+};
+
+static int
+is_share_attr(char *name)
+{
+ int i;
+ for (i = 0; share_attr[i] != NULL; i++)
+ if (strcmp(name, share_attr[i]) == 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * sa_share_from_pgroup
+ *
+ * extract the share definition from the share property group. We do
+ * some sanity checking to avoid bad data.
+ *
+ * Since this is only constructing the internal data structures, we
+ * don't use the sa_* functions most of the time.
+ */
+void
+sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_propertygroup_t *pg, char *id)
+{
+ xmlNodePtr node;
+ char *name;
+ scf_iter_t *iter;
+ scf_property_t *prop;
+ scf_value_t *value;
+ ssize_t vallen;
+ char *valuestr;
+ int ret = SA_OK;
+
+ /*
+ * While preliminary check (starts with 'S') passed before
+ * getting here. Need to make sure it is in ID syntax
+ * (Snnnnnn). Note that shares with properties have similar
+ * pgroups.
+ */
+ vallen = strlen(id);
+ if (*id == SA_SHARE_PG_PREFIX[0] && vallen == SA_SHARE_PG_LEN) {
+ uuid_t uuid;
+ if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) != 0 ||
+ uuid_parse(id + 2, uuid) < 0)
+ return;
+ } else {
+ return;
+ }
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+
+ iter = scf_iter_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ prop = scf_property_create(handle->handle);
+ name = malloc(scf_max_name_len);
+ valuestr = malloc(vallen);
+
+ /*
+ * construct the share XML node. It is similar to sa_add_share
+ * but never changes the repository. Also, there won't be any
+ * ZFS or transient shares. Root will be the group it is
+ * associated with.
+ */
+ node = xmlNewChild(root, NULL, (xmlChar *)"share", NULL);
+ if (node != NULL) {
+ /*
+ * make sure the UUID part of the property group is
+ * stored in the share "id" property. We use this
+ * later.
+ */
+ xmlSetProp(node, (xmlChar *)"id", (xmlChar *)id);
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)"persist");
+ }
+
+ if (iter != NULL && value != NULL && prop != NULL && name != NULL) {
+ /* iterate over the share pg properties */
+ if (scf_iter_pg_properties(iter, pg) == 0) {
+ while (scf_iter_next_property(iter, prop) > 0) {
+ ret = SA_SYSTEM_ERR; /* assume the worst */
+ if (scf_property_get_name(prop, name,
+ scf_max_name_len) > 0) {
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr,
+ vallen) >= 0) {
+ ret = SA_OK;
+ }
+ }
+ }
+ if (ret == SA_OK) {
+ if (is_share_attr(name)) {
+ /*
+ * if a share attr, then simple -
+ * usually path and resource name
+ */
+ xmlSetProp(node, (xmlChar *)name,
+ (xmlChar *)valuestr);
+ } else {
+ if (strcmp(name, "description") == 0) {
+ /* we have a description node */
+ xmlNodePtr desc;
+ desc = xmlNewChild(node, NULL,
+ (xmlChar *)"description",
+ NULL);
+ if (desc != NULL)
+ xmlNodeSetContent(desc,
+ (xmlChar *)valuestr);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (name != NULL)
+ free(name);
+ if (valuestr != NULL)
+ free(valuestr);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+}
+
+/*
+ * find_share_by_id(shareid)
+ *
+ * Search all shares in all groups until we find the share represented
+ * by "id".
+ */
+
+static sa_share_t
+find_share_by_id(char *shareid)
+{
+ sa_group_t group;
+ sa_share_t share = NULL;
+ char *id = NULL;
+ int done = 0;
+
+ for (group = sa_get_group(NULL); group != NULL && !done;
+ group = sa_get_next_group(group)) {
+ for (share = sa_get_share(group, NULL); share != NULL;
+ share = sa_get_next_share(share)) {
+ id = sa_get_share_attr(share, "id");
+ if (id != NULL && strcmp(id, shareid) == 0) {
+ sa_free_attr_string(id);
+ id = NULL;
+ done++;
+ break;
+ }
+ if (id != NULL) {
+ sa_free_attr_string(id);
+ id = NULL;
+ }
+ }
+ }
+ return (share);
+}
+
+/*
+ * sa_share_props_from_pgroup(root, handle, pg, id)
+ *
+ * extract share properties from the SMF property group. More sanity
+ * checks are done and the share object is created. We ignore some
+ * errors that could exist in the repository and only worry about
+ * property groups that validate in naming.
+ */
+
+static int
+sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_propertygroup_t *pg, char *id)
+{
+ xmlNodePtr node;
+ char *name;
+ scf_iter_t *iter;
+ scf_property_t *prop;
+ scf_value_t *value;
+ ssize_t vallen;
+ char *valuestr;
+ int ret = SA_OK;
+ char *sectype = NULL;
+ char *proto;
+ sa_share_t share;
+
+ /*
+ * While preliminary check (starts with 'S') passed before
+ * getting here. Need to make sure it is in ID syntax
+ * (Snnnnnn). Note that shares with properties have similar
+ * pgroups. If the pg name is more than SA_SHARE_PG_LEN
+ * characters, it is likely one of the protocol/security
+ * versions.
+ */
+ vallen = strlen(id);
+ if (*id == SA_SHARE_PG_PREFIX[0] && vallen > SA_SHARE_PG_LEN) {
+ uuid_t uuid;
+ if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) == 0) {
+ proto = strchr(id, '_');
+ if (proto == NULL)
+ return (ret);
+ *proto++ = '\0';
+ if (uuid_parse(id + SA_SHARE_PG_PREFIXLEN, uuid) < 0)
+ return (ret);
+ /*
+ * probably a legal optionset so check a few more
+ * syntax points below.
+ */
+ if (*proto == '\0') {
+ /* not a valid proto (null) */
+ return (ret);
+ }
+ sectype = strchr(proto, '_');
+ if (sectype != NULL)
+ *sectype++ = '\0';
+ if (!valid_protocol(proto))
+ return (ret);
+ }
+ } else {
+ /*
+ * it is ok to not have what we thought since someone might
+ * have added a name via SMF.
+ */
+ return (ret);
+ }
+
+ /*
+ * to get here, we have a valid protocol and possibly a
+ * security. We now have to find the share that it is really
+ * associated with. The "id" portion of the pgroup name will
+ * match.
+ */
+
+ share = find_share_by_id(id);
+ if (share == NULL)
+ return (SA_BAD_PATH);
+
+ root = (xmlNodePtr)share;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+
+ iter = scf_iter_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ prop = scf_property_create(handle->handle);
+ name = malloc(scf_max_name_len);
+ valuestr = malloc(vallen);
+
+ if (sectype == NULL)
+ node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL);
+ else {
+ node = xmlNewChild(root, NULL, (xmlChar *)"security", NULL);
+ if (node != NULL)
+ xmlSetProp(node, (xmlChar *)"sectype", (xmlChar *)sectype);
+ }
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
+ /* now find the properties */
+ if (iter != NULL && value != NULL && prop != NULL && name != NULL) {
+ /* iterate over the share pg properties */
+ if (scf_iter_pg_properties(iter, pg) == 0) {
+ while (scf_iter_next_property(iter, prop) > 0) {
+ ret = SA_SYSTEM_ERR; /* assume the worst */
+ if (scf_property_get_name(prop, name,
+ scf_max_name_len) > 0) {
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr,
+ vallen) >= 0) {
+ ret = SA_OK;
+ }
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret == SA_OK) {
+ sa_property_t prop;
+ prop = sa_create_property(name, valuestr);
+ if (prop != NULL)
+ prop = (sa_property_t)xmlAddChild(node,
+ (xmlNodePtr)prop);
+ else
+ ret = SA_NO_MEMORY;
+ }
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+ if (name != NULL)
+ free(name);
+ if (valuestr != NULL)
+ free(valuestr);
+ return (ret);
+}
+
+/*
+ * sa_extract_group(root, handle, instance)
+ *
+ * get the config info for this instance of a group and create the XML
+ * subtree from it.
+ */
+
+static int
+sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_instance_t *instance)
+{
+ char *buff;
+ xmlNodePtr node;
+ scf_iter_t *iter;
+ char *proto;
+ char *sectype;
+ int have_shares = 0;
+ int has_proto = 0;
+ int is_default = 0;
+ int ret = SA_OK;
+ int err;
+
+ buff = malloc(scf_max_name_len);
+ iter = scf_iter_create(handle->handle);
+ if (buff != NULL) {
+ if (scf_instance_get_name(instance, buff,
+ scf_max_name_len) > 0) {
+ node = xmlNewChild(root, NULL, (xmlChar *)"group", NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"name", (xmlChar *)buff);
+ if (strcmp(buff, "default") == 0)
+ is_default++;
+ sa_extract_attrs(node, handle, instance);
+ /*
+ * Iterate through all the property groups
+ * looking for those with security or
+ * optionset prefixes. The names of the
+ * matching pgroups are parsed to get the
+ * protocol, and for security, the sectype.
+ * Syntax is as follows:
+ * optionset | optionset_<proto>
+ * security_default | security_<proto>_<sectype>
+ * "operation" is handled by
+ * sa_extract_attrs().
+ */
+ if (iter != NULL) {
+ if (scf_iter_instance_pgs(iter, instance) == 0) {
+ while (scf_iter_next_pg(iter, handle->pg) > 0) {
+ /* have a pgroup so sort it out */
+ ret = scf_pg_get_name(handle->pg, buff,
+ scf_max_name_len);
+ if (ret > 0) {
+ if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
+ sa_share_from_pgroup(node, handle,
+ handle->pg,
+ buff);
+ have_shares++;
+ } else if (strncmp(buff, "optionset", 9) ==
+ 0) {
+ char *nodetype = "optionset";
+ /* have an optionset */
+ sectype = NULL;
+ proto = strchr(buff, '_');
+ if (proto != NULL) {
+ *proto++ = '\0';
+ sectype = strchr(proto, '_');
+ if (sectype != NULL) {
+ *sectype++ = '\0';
+ nodetype = "security";
+ }
+ }
+ ret = sa_extract_pgroup(node, handle,
+ handle->pg,
+ nodetype,
+ proto, sectype);
+ has_proto++;
+ } else if (strncmp(buff,
+ "security", 8) == 0) {
+ /*
+ * have a security (note that
+ * this should change in the
+ * future)
+ */
+ proto = strchr(buff, '_');
+ sectype = NULL;
+ if (proto != NULL) {
+ *proto++ = '\0';
+ sectype = strchr(proto, '_');
+ if (sectype != NULL)
+ *sectype++ = '\0';
+ if (strcmp(proto, "default") == 0)
+ proto = NULL;
+ }
+ ret = sa_extract_pgroup(node, handle,
+ handle->pg,
+ "security", proto,
+ sectype);
+ has_proto++;
+ }
+ /* ignore everything else */
+ }
+ }
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ /*
+ * Make sure we have a valid default group.
+ * On first boot, default won't have any
+ * protocols defined and won't be enabled (but
+ * should be).
+ */
+ if (is_default) {
+ char *state = sa_get_group_attr((sa_group_t)node,
+ "state");
+ char **protos;
+ int numprotos;
+ int i;
+
+ if (state == NULL) {
+ /* set attribute to enabled */
+ (void) sa_set_group_attr((sa_group_t)node,
+ "state",
+ "enabled");
+ /* we can assume no protocols */
+ numprotos = sa_get_protocols(&protos);
+ for (i = 0; i < numprotos; i++)
+ (void) sa_create_optionset((sa_group_t)node,
+ protos[i]);
+ if (numprotos > 0)
+ free(protos);
+ } else {
+ sa_free_attr_string(state);
+ }
+ }
+ /* do a second pass if shares were found */
+ if (have_shares &&
+ scf_iter_instance_pgs(iter, instance) == 0) {
+ while (scf_iter_next_pg(iter, handle->pg) > 0) {
+ /*
+ * have a pgroup so see if it is a
+ * share optionset
+ */
+ err = scf_pg_get_name(handle->pg, buff,
+ scf_max_name_len);
+ if (err > 0) {
+ if (buff[0] == SA_SHARE_PG_PREFIX[0]) {
+ ret = sa_share_props_from_pgroup(node,
+ handle,
+ handle->pg,
+ buff);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ if (buff != NULL)
+ free(buff);
+ return (ret);
+}
+
+/*
+ * sa_extract_defaults(root, handle, instance)
+ *
+ * local function to find the default properties that live in the
+ * default instance's "operation" proprerty group.
+ */
+
+static void
+sa_extract_defaults(xmlNodePtr root, scfutilhandle_t *handle,
+ scf_instance_t *instance)
+{
+ xmlNodePtr node;
+ scf_property_t *prop;
+ scf_value_t *value;
+ char *valuestr;
+ ssize_t vallen;
+
+ vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ prop = scf_property_create(handle->handle);
+ value = scf_value_create(handle->handle);
+ valuestr = malloc(vallen);
+ if (prop != NULL && value != NULL && vallen != NULL &&
+ scf_instance_get_pg(instance, "operation",
+ handle->pg) == 0) {
+ if (scf_pg_get_property(handle->pg,
+ "legacy-timestamp", prop) == 0) {
+ /* found the property so get the value */
+ if (scf_property_get_value(prop, value) == 0) {
+ if (scf_value_get_astring(value, valuestr, vallen) > 0) {
+ node = xmlNewChild(root, NULL, (xmlChar *)"legacy",
+ NULL);
+ if (node != NULL) {
+ xmlSetProp(node, (xmlChar *)"timestamp",
+ (xmlChar *)valuestr);
+ xmlSetProp(node, (xmlChar *)"path",
+ (xmlChar *)SA_LEGACY_DFSTAB);
+ }
+ }
+ }
+ }
+ }
+ if (valuestr != NULL)
+ free(valuestr);
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (prop != NULL)
+ scf_property_destroy(prop);
+}
+
+
+/*
+ * sa_get_config(handle, root, doc)
+ *
+ * walk the SMF repository for /network/shares/group and find all the
+ * instances. These become group names. Then add the XML structure
+ * below the groups based on property groups and properties.
+ */
+int
+sa_get_config(scfutilhandle_t *handle, xmlNodePtr *root, xmlDocPtr *doc)
+{
+ int ret = SA_OK;
+ scf_instance_t *instance;
+ scf_iter_t *iter;
+ char buff[BUFSIZ * 2];
+
+ *doc = xmlNewDoc((xmlChar *)"1.0");
+ *root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
+ instance = scf_instance_create(handle->handle);
+ iter = scf_iter_create(handle->handle);
+ if (*doc != NULL && *root != NULL && instance != NULL && iter != NULL) {
+ xmlDocSetRootElement(*doc, *root);
+ if ((ret = scf_iter_service_instances(iter,
+ handle->service)) == 0) {
+ while ((ret = scf_iter_next_instance(iter,
+ instance)) > 0) {
+ if (scf_instance_get_name(instance, buff,
+ sizeof (buff)) > 0) {
+ if (strcmp(buff, "default") == 0)
+ sa_extract_defaults(*root, handle, instance);
+ ret = sa_extract_group(*root, handle, instance);
+ }
+ }
+ }
+ } else {
+ /* if we can't create the document, cleanup */
+ if (*doc != NULL)
+ xmlFreeDoc(*doc);
+ if (*root != NULL)
+ xmlFreeNode(*root);
+ *doc = NULL;
+ *root = NULL;
+ }
+ /* always cleanup these */
+ if (instance != NULL)
+ scf_instance_destroy(instance);
+ if (iter != NULL)
+ scf_iter_destroy(iter);
+ return (ret);
+}
+
+/*
+ * sa_get_instance(handle, instance)
+ *
+ * get the instance of the group service. This is actually the
+ * specific group name. The instance is needed for all property and
+ * control operations.
+ */
+
+int
+sa_get_instance(scfutilhandle_t *handle, char *instname)
+{
+ if (scf_service_get_instance(handle->service, instname,
+ handle->instance) != 0) {
+ return (SA_NO_SUCH_GROUP);
+ }
+ return (SA_OK);
+}
+
+/*
+ * sa_create_instance(handle, instname)
+ *
+ * Create a new SMF service instance. There can only be one with a
+ * given name.
+ */
+
+int
+sa_create_instance(scfutilhandle_t *handle, char *instname)
+{
+ int ret = SA_OK;
+ char instance[SA_GROUP_INST_LEN];
+ if (scf_service_add_instance(handle->service, instname,
+ handle->instance) != 0) {
+ /* better error returns need to be added based on real error */
+ if (scf_error() == SCF_ERROR_PERMISSION_DENIED)
+ ret = SA_NO_PERMISSION;
+ else
+ ret = SA_DUPLICATE_NAME;
+ } else {
+ /* have the service created, so enable it */
+ (void) snprintf(instance, sizeof (instance), "%s:%s",
+ SA_SVC_FMRI_BASE, instname);
+ (void) smf_enable_instance(instance, 0);
+ }
+ return (ret);
+}
+
+/*
+ * sa_delete_instance(handle, instname)
+ *
+ * When a group goes away, we also remove the service instance.
+ */
+
+int
+sa_delete_instance(scfutilhandle_t *handle, char *instname)
+{
+ int ret;
+
+ if (strcmp(instname, "default") == 0) {
+ ret = SA_NO_PERMISSION;
+ } else {
+ if ((ret = sa_get_instance(handle, instname)) == SA_OK) {
+ if (scf_instance_delete(handle->instance) != 0)
+ /* need better analysis */
+ ret = SA_NO_PERMISSION;
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_create_pgroup(handle, pgroup)
+ *
+ * create a new property group
+ */
+
+int
+sa_create_pgroup(scfutilhandle_t *handle, char *pgroup)
+{
+ int ret = SA_OK;
+ /*
+ * only create a handle if it doesn't exist. It is ok to exist
+ * since the pg handle will be set as a side effect.
+ */
+ if (handle->pg == NULL) {
+ handle->pg = scf_pg_create(handle->handle);
+ }
+ /*
+ * if the pgroup exists, we are done. If it doesn't, then we
+ * need to actually add one to the service instance.
+ */
+ if (scf_instance_get_pg(handle->instance,
+ pgroup, handle->pg) != 0) {
+ /* doesn't exist so create one */
+ if (scf_instance_add_pg(handle->instance, pgroup,
+ SCF_GROUP_APPLICATION, 0,
+ handle->pg) != 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SA_NO_PERMISSION;
+ break;
+ default:
+ ret = SA_SYSTEM_ERR;
+ break;
+ }
+ }
+ }
+ return (ret);
+}
+
+/*
+ * sa_delete_pgroup(handle, pgroup)
+ *
+ * remove the property group from the current instance of the service,
+ * but only if it actually exists.
+ */
+
+int
+sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup)
+{
+ int ret = SA_OK;
+ /*
+ * only delete if it does exist.
+ */
+ if (scf_instance_get_pg(handle->instance,
+ pgroup, handle->pg) == 0) {
+ /* does exist so delete it */
+ if (scf_pg_delete(handle->pg) != 0) {
+ ret = SA_SYSTEM_ERR;
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret == SA_SYSTEM_ERR &&
+ scf_error() == SCF_ERROR_PERMISSION_DENIED) {
+ ret = SA_NO_PERMISSION;
+ }
+ return (ret);
+}
+
+/*
+ * sa_start_transaction(handle, pgroup)
+ *
+ * Start an SMF transaction so we can deal with properties. it would
+ * be nice to not have to expose this, but we have to in order to
+ * optimize.
+ *
+ * Basic model is to hold the transaction in the handle and allow
+ * property adds/deletes/updates to be added then close the
+ * transaction (or abort). There may eventually be a need to handle
+ * other types of transaction mechanisms but we don't do that now.
+ *
+ * An sa_start_transaction must be followed by either an
+ * sa_end_transaction or sa_abort_transaction before another
+ * sa_start_transaction can be done.
+ */
+
+int
+sa_start_transaction(scfutilhandle_t *handle, char *propgroup)
+{
+ int ret = SA_OK;
+ /*
+ * lookup the property group and create it if it doesn't already
+ * exist.
+ */
+ if (handle->scf_state == SCH_STATE_INIT) {
+ ret = sa_create_pgroup(handle, propgroup);
+ if (ret == SA_OK) {
+ handle->trans = scf_transaction_create(handle->handle);
+ if (handle->trans != NULL) {
+ if (scf_transaction_start(handle->trans, handle->pg) != 0) {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret != SA_OK) {
+ scf_transaction_destroy(handle->trans);
+ handle->trans = NULL;
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ }
+ }
+ if (ret == SA_SYSTEM_ERR &&
+ scf_error() == SCF_ERROR_PERMISSION_DENIED) {
+ ret = SA_NO_PERMISSION;
+ }
+ return (ret);
+}
+
+/*
+ * sa_end_transaction(handle)
+ *
+ * Commit the changes that were added to the transaction in the
+ * handle. Do all necessary cleanup.
+ */
+
+int
+sa_end_transaction(scfutilhandle_t *handle)
+{
+ int ret = SA_OK;
+
+ if (handle->trans == NULL) {
+ ret = SA_SYSTEM_ERR;
+ } else {
+ if (scf_transaction_commit(handle->trans) < 0)
+ ret = SA_SYSTEM_ERR;
+ scf_transaction_destroy_children(handle->trans);
+ scf_transaction_destroy(handle->trans);
+ handle->trans = NULL;
+ }
+ return (ret);
+}
+
+/*
+ * sa_abort_transaction(handle)
+ *
+ * Abort the changes that were added to the transaction in the
+ * handle. Do all necessary cleanup.
+ */
+
+void
+sa_abort_transaction(scfutilhandle_t *handle)
+{
+ if (handle->trans != NULL) {
+ scf_transaction_reset_all(handle->trans);
+ scf_transaction_destroy_children(handle->trans);
+ scf_transaction_destroy(handle->trans);
+ handle->trans = NULL;
+ }
+}
+
+/*
+ * sa_set_property(handle, prop, value)
+ *
+ * set a property transaction entry into the pending SMF transaction.
+ */
+
+int
+sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr)
+{
+ int ret = SA_OK;
+ scf_value_t *value;
+ scf_transaction_entry_t *entry;
+ /*
+ * properties must be set in transactions and don't take
+ * effect until the transaction has been ended/committed.
+ */
+ value = scf_value_create(handle->handle);
+ entry = scf_entry_create(handle->handle);
+ if (value != NULL && entry != NULL) {
+ if (scf_transaction_property_change(handle->trans, entry,
+ propname,
+ SCF_TYPE_ASTRING) == 0 ||
+ scf_transaction_property_new(handle->trans, entry,
+ propname,
+ SCF_TYPE_ASTRING) == 0) {
+ if (scf_value_set_astring(value, valstr) == 0) {
+ if (scf_entry_add_value(entry, value) != 0) {
+ ret = SA_SYSTEM_ERR;
+ scf_value_destroy(value);
+ }
+ /* the value is in the transaction */
+ value = NULL;
+ } else {
+ /* value couldn't be constructed */
+ ret = SA_SYSTEM_ERR;
+ }
+ /* the entry is in the transaction */
+ entry = NULL;
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ } else {
+ ret = SA_SYSTEM_ERR;
+ }
+ if (ret == SA_SYSTEM_ERR) {
+ switch (scf_error()) {
+ case SCF_ERROR_PERMISSION_DENIED:
+ ret = SA_NO_PERMISSION;
+ break;
+ }
+ }
+ /*
+ * cleanup if there were any errors that didn't leave these
+ * values where they would be cleaned up later.
+ */
+ if (value != NULL)
+ scf_value_destroy(value);
+ if (entry != NULL)
+ scf_entry_destroy(entry);
+ return (ret);
+}
+
+/*
+ * sa_commit_share(handle, group, share)
+ *
+ * commit this share to the repository.
+ * properties are added if they exist but can be added later.
+ * Need to add to dfstab and sharetab, if appropriate.
+ */
+int
+sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
+{
+ int ret = SA_OK;
+ char *groupname;
+ char *name;
+ char *resource;
+ char *description;
+ char *sharename;
+ ssize_t proplen;
+ char *propstring;
+
+ /*
+ * don't commit in the zfs group. We do commit legacy
+ * (default) and all other groups/shares. ZFS is handled
+ * through the ZFS configuration rather than SMF.
+ */
+
+ groupname = sa_get_group_attr(group, "name");
+ if (groupname != NULL) {
+ if (strcmp(groupname, "zfs") == 0) {
+ /*
+ * adding to the ZFS group will result in the sharenfs
+ * property being set but we don't want to do anything
+ * SMF related at this point.
+ */
+ sa_free_attr_string(groupname);
+ return (ret);
+ }
+ }
+
+ proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ propstring = malloc(proplen);
+ if (propstring == NULL)
+ ret = SA_NO_MEMORY;
+
+ if (groupname != NULL && ret == SA_OK) {
+ ret = sa_get_instance(handle, groupname);
+ sa_free_attr_string(groupname);
+ groupname = NULL;
+ sharename = sa_get_share_attr(share, "id");
+ if (sharename == NULL) {
+ /* slipped by */
+ char shname[SA_SHARE_UUID_BUFLEN];
+ generate_unique_sharename(shname);
+ xmlSetProp((xmlNodePtr)share, (xmlChar *)"id",
+ (xmlChar *)shname);
+ sharename = strdup(shname);
+ }
+ if (sharename != NULL) {
+ /*
+ * have a share name allocated so create a pgroup
+ * for it. It may already exist, but that is OK.
+ */
+ ret = sa_create_pgroup(handle, sharename);
+ if (ret == SA_OK) {
+ /*
+ * now start the transaction for the
+ * properties that define this share. They may
+ * exist so attempt to update before create.
+ */
+ ret = sa_start_transaction(handle, sharename);
+ }
+ if (ret == SA_OK) {
+ name = sa_get_share_attr(share, "path");
+ if (name != NULL) {
+ /* there needs to be a path for a share to exist */
+ ret = sa_set_property(handle, "path", name);
+ sa_free_attr_string(name);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ }
+ if (ret == SA_OK) {
+ resource = sa_get_share_attr(share, "resource");
+ if (resource != NULL) {
+ ret = sa_set_property(handle, "resource", resource);
+ sa_free_attr_string(resource);
+ }
+ }
+ if (ret == SA_OK) {
+ description = sa_get_share_description(share);
+ if (description != NULL) {
+ ret = sa_set_property(handle, "description",
+ description);
+ sa_free_share_description(description);
+ }
+ }
+ /* make sure we cleanup the transaction */
+ if (ret == SA_OK) {
+ ret = sa_end_transaction(handle);
+ } else {
+ sa_abort_transaction(handle);
+ }
+ free(sharename);
+ }
+ }
+ if (ret == SA_SYSTEM_ERR) {
+ int err = scf_error();
+ if (err == SCF_ERROR_PERMISSION_DENIED)
+ ret = SA_NO_PERMISSION;
+ }
+ if (propstring != NULL)
+ free(propstring);
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+
+ return (ret);
+}
+
+/*
+ * sa_delete_share(handle, group, share)
+ *
+ * remove the specified share from the group (and service instance).
+ */
+
+int
+sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share)
+{
+ int ret = SA_OK;
+ char *groupname = NULL;
+ char *shareid = NULL;
+ sa_optionset_t opt;
+ sa_security_t sec;
+ ssize_t proplen;
+ char *propstring;
+
+ proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
+ propstring = malloc(proplen);
+ if (propstring == NULL)
+ ret = SA_NO_MEMORY;
+
+ if (ret == SA_OK) {
+ groupname = sa_get_group_attr(group, "name");
+ shareid = sa_get_share_attr(share, "id");
+ if (groupname != NULL && shareid != NULL) {
+ ret = sa_get_instance(handle, groupname);
+ if (ret == SA_OK) {
+ /* if a share has properties, remove them */
+ ret = sa_delete_pgroup(handle, shareid);
+ for (opt = sa_get_optionset(share, NULL); opt != NULL;
+ opt = sa_get_next_optionset(opt)) {
+ char *proto;
+ proto = sa_get_optionset_attr(opt, "type");
+ if (proto != NULL) {
+ (void) snprintf(propstring, proplen, "%s_%s",
+ shareid, proto);
+ ret = sa_delete_pgroup(handle, propstring);
+ sa_free_attr_string(proto);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ }
+ /*
+ * if a share has security/negotiable
+ * properties, remove them.
+ */
+ for (sec = sa_get_security(share, NULL, NULL); sec != NULL;
+ sec = sa_get_next_security(sec)) {
+ char *proto;
+ char *sectype;
+ proto = sa_get_security_attr(sec, "type");
+ sectype = sa_get_security_attr(sec, "sectype");
+ if (proto != NULL && sectype != NULL) {
+ (void) snprintf(propstring, proplen, "%s_%s_%s",
+ shareid,
+ proto, sectype);
+ ret = sa_delete_pgroup(handle, propstring);
+ } else {
+ ret = SA_NO_MEMORY;
+ }
+ if (proto != NULL)
+ sa_free_attr_string(proto);
+ if (sectype != NULL)
+ sa_free_attr_string(sectype);
+ }
+ }
+ } else {
+ ret = SA_CONFIG_ERR;
+ }
+ }
+ if (groupname != NULL)
+ sa_free_attr_string(groupname);
+ if (shareid != NULL)
+ sa_free_attr_string(shareid);
+ if (propstring != NULL)
+ free(propstring);
+
+ return (ret);
+}
diff --git a/usr/src/lib/libshare/common/scfutil.h b/usr/src/lib/libshare/common/scfutil.h
new file mode 100644
index 0000000000..da21d32785
--- /dev/null
+++ b/usr/src/lib/libshare/common/scfutil.h
@@ -0,0 +1,81 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * basic API declarations for share management
+ */
+
+#ifndef _SCFUTIL_H
+#define _SCFUTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <libxml/tree.h>
+
+typedef struct scfutilhandle {
+ scf_handle_t *handle;
+ int scf_state;
+ scf_service_t *service;
+ scf_scope_t *scope;
+ scf_transaction_t *trans;
+ scf_transaction_entry_t *entry;
+ scf_propertygroup_t *pg;
+ scf_instance_t *instance;
+} scfutilhandle_t;
+
+#define SCH_STATE_UNINIT 0
+#define SCH_STATE_INITIALIZING 1
+#define SCH_STATE_INIT 2
+
+extern void sa_scf_fini(scfutilhandle_t *);
+extern scfutilhandle_t *sa_scf_init();
+extern int sa_get_config(scfutilhandle_t *, xmlNodePtr *, xmlDocPtr *);
+extern int sa_get_instance(scfutilhandle_t *, char *);
+extern int sa_create_instance(scfutilhandle_t *, char *);
+
+/*
+ * Shares are held in a property group with name of the form
+ * S-<GUID>. The total length of the name is 38 characters.
+ */
+#define SA_SHARE_PG_PREFIX "S-"
+#define SA_SHARE_PG_PREFIXLEN 2
+#define SA_SHARE_PG_LEN 38
+#define SA_SHARE_UUID_BUFLEN 64
+
+/*
+ * service instance related defines
+ */
+#define SA_GROUP_SVC_NAME "network/shares/group"
+#define SA_GROUP_INST_LEN 256
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SCFUTIL_H */
diff --git a/usr/src/lib/libshare/i386/Makefile b/usr/src/lib/libshare/i386/Makefile
new file mode 100644
index 0000000000..729341c543
--- /dev/null
+++ b/usr/src/lib/libshare/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 (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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libshare/sparc/Makefile b/usr/src/lib/libshare/sparc/Makefile
new file mode 100644
index 0000000000..729341c543
--- /dev/null
+++ b/usr/src/lib/libshare/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 (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 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)