changeset 3663:718327411bfa

6493853 sharemgr: inconsistent messages if parent is shared with UFS and ZFS 6499968 sharemgr: timing window can cause problems during system start if dfstab was modified 6502752 sharemgr: nfs_get_root_principal: bad host name 6510387 sharemgr: add-share prints error 6511920 sharemgr: set with -S krb5 optionset sets the group to nfs:sys 6513576 sharemgr: under some error conditions can't remove a share
author dougm
date Thu, 15 Feb 2007 15:33:40 -0800
parents d7848fb68fba
children ecc47116246b
files usr/src/cmd/dfs.cmds/sharemgr/commands.c usr/src/cmd/dfs.cmds/sharemgr/plugins/libshare_nfs.c usr/src/lib/libshare/common/libshare.c usr/src/lib/libshare/common/libsharecore.c
diffstat 4 files changed, 395 insertions(+), 162 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/dfs.cmds/sharemgr/commands.c	Thu Feb 15 13:46:37 2007 -0800
+++ b/usr/src/cmd/dfs.cmds/sharemgr/commands.c	Thu Feb 15 15:33:40 2007 -0800
@@ -1851,24 +1851,54 @@
 	    } else {
 		group = NULL;
 	    }
-	    if (ret == SA_OK) {
-		if (realpath(sharepath, dir) == NULL) {
-		    ret = SA_BAD_PATH;
-		    (void) printf(gettext("Path is not valid: %s\n"),
-					sharepath);
-		} else {
-		    sharepath = dir;
-		}
-	    }
+
+		/*
+		 * Lookup the path in the internal configuration. Care
+		 * must be taken to handle the case where the
+		 * underlying path has been removed since we need to
+		 * be able to deal with that as well.
+		 */
 	    if (ret == SA_OK) {
 		if (group != NULL)
 		    share = sa_get_share(group, sharepath);
 		else
 		    share = sa_find_share(sharepath);
+		/*
+		 * If we didn't find the share with the provided path,
+		 * it may be a symlink so attempt to resolve it using
+		 * realpath and try again. Realpath will resolve any
+		 * symlinks and place them in "dir". Note that
+		 * sharepath is only used for the lookup the first
+		 * time and later for error messages. dir will be used
+		 * on the second attempt. Once a share is found, all
+		 * operations are based off of the share variable.
+		 */
+		if (share == NULL) {
+		    if (realpath(sharepath, dir) == NULL) {
+			ret = SA_BAD_PATH;
+			(void) printf(gettext("Path is not valid: %s\n"),
+						sharepath);
+		    } else {
+			if (group != NULL)
+			    share = sa_get_share(group, dir);
+			else
+			    share = sa_find_share(dir);
+		    }
+		}
+	    }
+
+		/*
+		 * If there hasn't been an error, there was likely a
+		 * path found. If not, give the appropriate error
+		 * message and set the return error. If it was found,
+		 * then disable the share and then remove it from the
+		 * configuration.
+		 */
+	    if (ret == SA_OK) {
 		if (share == NULL) {
 		    if (group != NULL)
 			(void) printf(gettext("Share not found in group %s:"
-						"%s\n"),
+						" %s\n"),
 					argv[optind], sharepath);
 		    else
 			(void) printf(gettext("Share not found: %s\n"),
@@ -1882,10 +1912,14 @@
 			    ret = sa_disable_share(share, NULL);
 				/*
 				 * we don't care if it fails since it
-				 * could be disabled already.
+				 * could be disabled already. Some
+				 * unexpected errors could occur that
+				 * prevent removal, so also check for
+				 * force being set.
 				 */
 			    if (ret == SA_OK || ret == SA_NO_SUCH_PATH ||
-				ret == SA_NOT_SUPPORTED) {
+					ret == SA_NOT_SUPPORTED ||
+					ret == SA_SYSTEM_ERR || force) {
 				ret = sa_remove_share(share);
 			    }
 			    if (ret == SA_OK)
@@ -3859,22 +3893,37 @@
 		ret = run_legacy_command(cmd, argv);
 		return (ret);
 	    }
+		/*
+		 * Find the path in the internal configuration. If it
+		 * isn't found, attempt to resolve the path via
+		 * realpath() and try again.
+		 */
 	    sharepath = argv[optind++];
-	    if (realpath(sharepath, dir) == NULL) {
-		ret = SA_NO_SUCH_PATH;
+	    share = sa_find_share(sharepath);
+	    if (share == NULL) {
+		if (realpath(sharepath, dir) == NULL) {
+		    ret = SA_NO_SUCH_PATH;
+		} else {
+		    share = sa_find_share(dir);
+		}
+	    }
+	    if (share != NULL) {
+		ret = sa_disable_share(share, protocol);
+		/*
+		 * Errors are ok and removal should still occur. The
+		 * legacy unshare is more forgiving of errors than the
+		 * remove-share subcommand which may need the force
+		 * flag set for some error conditions. That is, the
+		 * "unshare" command will always unshare if it can
+		 * while "remove-share" might require the force option.
+		 */
+		if (persist == SA_SHARE_PERMANENT) {
+		    ret = sa_remove_share(share);
+		    if (ret == SA_OK)
+			ret = sa_update_config();
+		}
 	    } else {
-		sharepath = dir;
-		share = sa_find_share(sharepath);
-		if (share != NULL) {
-		    ret = sa_disable_share(share, protocol);
-		    if (ret == SA_OK) {
-			if (persist == SA_SHARE_PERMANENT)
-			    ret = sa_remove_share(share);
-			ret = sa_update_config();
-		    }
-		} else {
-		    ret = SA_NOT_SHARED;
-		}
+		ret = SA_NOT_SHARED;
 	    }
 	}
 	switch (ret) {
--- a/usr/src/cmd/dfs.cmds/sharemgr/plugins/libshare_nfs.c	Thu Feb 15 13:46:37 2007 -0800
+++ b/usr/src/cmd/dfs.cmds/sharemgr/plugins/libshare_nfs.c	Thu Feb 15 15:33:40 2007 -0800
@@ -151,7 +151,7 @@
 };
 
 /*
- * list of propertye that are related to security flavors.
+ * list of properties that are related to security flavors.
  */
 static char *seclist[] = {
 	SHOPT_RO,
@@ -163,8 +163,8 @@
 
 /* structure for list of securities */
 struct securities {
-    sa_security_t security;
-    struct securities *next;
+	sa_security_t security;
+	struct securities *next;
 };
 
 /*
@@ -318,6 +318,7 @@
 		    }
 		}
 	    }
+
 	    if (freetok) {
 		freetok = 0;
 		sa_free_attr_string(tok);
@@ -379,10 +380,22 @@
 		else
 		    value = "true";
 	    }
+
+		/*
+		 * Get the existing property, if it exists, so we can
+		 * determine what to do with it. The ro/rw/root
+		 * properties can be merged if multiple instances of
+		 * these properies are given. For example, if "rw"
+		 * exists with a value "host1" and a later token of
+		 * rw="host2" is seen, the values are merged into a
+		 * single rw="host1:host2".
+		 */
 	    prop = sa_get_property(sec->security, name);
+
 	    if (prop != NULL) {
 		char *oldvalue;
 		char *newvalue;
+
 		/*
 		 * The security options of ro/rw/root might appear
 		 * multiple times. If they do, the values need to be
@@ -391,14 +404,32 @@
 		 */
 		oldvalue = sa_get_property_attr(prop, "value");
 		if (oldvalue != NULL) {
-		    newvalue = nfs_alistcat(oldvalue, value, ':');
-		    if (newvalue != NULL)
-			value = newvalue;
-		    (void) sa_remove_property(prop);
-		    prop = sa_create_property(name, value);
-		    ret = sa_add_property(sec->security, prop);
-		    if (newvalue != NULL)
+			/*
+			 * The general case is to concatenate the new
+			 * value onto the old value for multiple
+			 * rw(ro/root) properties. A special case
+			 * exists when either the old or new is the
+			 * "all" case. In the special case, if both
+			 * are "all", then it is "all", else if one is
+			 * an access-list, that replaces the "all".
+			 */
+		    if (strcmp(oldvalue, "*") == 0) {
+			/* Replace old value with new value. */
+			newvalue = strdup(value);
+		    } else if (strcmp(value, "*") == 0) {
+			/* Keep old value and ignore the new value. */
+			newvalue = NULL;
+		    } else {
+			/* Make a new list of old plus new access-list. */
+			newvalue = nfs_alistcat(oldvalue, value, ':');
+		    }
+
+		    if (newvalue != NULL) {
+			(void) sa_remove_property(prop);
+			prop = sa_create_property(name, newvalue);
+			ret = sa_add_property(sec->security, prop);
 			free(newvalue);
+		    }
 		    if (oldvalue != NULL)
 			sa_free_attr_string(oldvalue);
 		}
@@ -548,13 +579,14 @@
 	 * we need to step through each option in the string and then
 	 * add either the option or the security option as needed. If
 	 * this is not a persistent share, don't commit to the
-	 * repository.
+	 * repository. If there is an error, we also want to abort the
+	 * processing and report it.
 	 */
 	persist = is_persistent(group);
 	base = dup;
 	token = dup;
 	lasts = NULL;
-	while (token != NULL) {
+	while (token != NULL && ret == SA_OK) {
 	    ret = SA_OK;
 	    token = strtok_r(base, ",", &lasts);
 	    base = NULL;
@@ -939,7 +971,7 @@
 	sa_property_t prop;
 	char *type;
 	int longform;
-	int err = 0;
+	int err = SC_NOERROR;
 
 	type = sa_get_security_attr(secopts, "sectype");
 	if (type != NULL) {
@@ -955,8 +987,10 @@
 		return (err);
 	}
 
+	err = SA_OK;
 	for (prop = sa_get_property(secopts, NULL);
-		prop != NULL; prop = sa_get_next_property(prop)) {
+		prop != NULL && err == SA_OK;
+		prop = sa_get_next_property(prop)) {
 	    char *name;
 	    char *value;
 
@@ -981,9 +1015,14 @@
 		if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX)
 		    continue;
 		/* not AUTH_UNIX */
-		if (value != NULL)
+		if (value != NULL) {
 		    sp->s_rootnames = get_rootnames(&sp->s_secinfo, value,
 							&sp->s_rootcnt);
+		    if (sp->s_rootnames == NULL) {
+			err = SA_BAD_VALUE;
+			(void) fprintf(stderr, gettext("Bad root list\n"));
+		    }
+		}
 		break;
 	    case OPT_WINDOW:
 		if (value != NULL) {
@@ -1833,6 +1872,15 @@
 		case OPT_TYPE_STRING:
 		    /* whatever is here should be ok */
 		    break;
+		case OPT_TYPE_SECURITY:
+			/*
+			 * The "sec" property isn't used in the
+			 * non-legacy parts of sharemgr. We need to
+			 * reject it here. For legacy, it is pulled
+			 * out well before we get here.
+			 */
+		    ret = SA_NO_SUCH_PROP;
+		    break;
 		default:
 		    break;
 		}
@@ -2632,7 +2680,15 @@
 {
 	char *name = space;
 	seconfig_t secconf;
-	if (nfs_getseconfig_default(&secconf) == 0) {
+
+	/*
+	 * Only the space named "default" is special. If it is used,
+	 * the default needs to be looked up and the real name used.
+	 * This is normally "sys" but could be changed.  We always
+	 * change defautl to the real name.
+	 */
+	if (strcmp(space, "default") == 0 &&
+	    nfs_getseconfig_default(&secconf) == 0) {
 	    if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0)
 		name = secconf.sc_name;
 	}
--- a/usr/src/lib/libshare/common/libshare.c	Thu Feb 15 13:46:37 2007 -0800
+++ b/usr/src/lib/libshare/common/libshare.c	Thu Feb 15 15:33:40 2007 -0800
@@ -34,6 +34,7 @@
 #include <ctype.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 #include <unistd.h>
 #include <libxml/parser.h>
 #include <libxml/tree.h>
@@ -51,6 +52,8 @@
 #define	TSTAMP(tm)	(uint64_t)(((uint64_t)tm.tv_sec << 32) | \
 					(tm.tv_nsec & 0xffffffff))
 
+#define	DFS_LOCK_FILE	"/etc/dfs/fstypes"
+
 /*
  * internal data structures
  */
@@ -74,6 +77,8 @@
 extern int issubdir(char *, char *);
 extern void sa_zfs_init(void);
 extern void sa_zfs_fini(void);
+extern void sablocksigs(sigset_t *);
+extern void saunblocksigs(sigset_t *);
 
 static int sa_initialized = 0;
 
@@ -167,46 +172,6 @@
 }
 
 /*
- * 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
@@ -282,30 +247,25 @@
 }
 
 /*
- * checksubdir(newpath, strictness)
+ * checksubdirgroup(group, newpath, strictness)
  *
- * checksubdir determines if the specified path (newpath) is a
- * subdirectory of another share. It calls issubdir() from the old
- * share implementation to do the complicated work. The strictness
- * parameter determines how strict a check to make against the
- * path. The strictness values mean:
+ * check all the specified newpath against all the paths in the
+ * group. This is a helper function for checksubdir to make it easier
+ * to also check ZFS subgroups.
+ * The strictness values mean:
  * SA_CHECK_NORMAL == only check newpath against shares that are active
  * SA_CHECK_STRICT == check newpath against both active shares and those
  *		      stored in the repository
  */
 static int
-checksubdir(char *newpath, int strictness)
+checksubdirgroup(sa_group_t group, char *newpath, int strictness)
 {
-	sa_group_t group;
 	sa_share_t share;
-	int issub;
-	char *path = NULL;
+	char *path;
+	int issub = SA_OK;
 
-	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)) {
+	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
@@ -314,27 +274,63 @@
 		 * could be considered incorrect.  We may tighten this
 		 * up in the future.
 		 */
-		if (strictness == SA_CHECK_NORMAL && !is_shared(share))
-		    continue;
+	    if (strictness == SA_CHECK_NORMAL && !is_shared(share))
+		continue;
 
-		path = sa_get_share_attr(share, "path");
+	    path = sa_get_share_attr(share, "path");
 		/*
 		 * If path is NULL, then a share is in the process of
 		 * construction or someone has modified the property
-		 * group inappropriately. It should be ignored.
+		 * group inappropriately. It should be
+		 * ignored. issubdir() comes from the original share
+		 * implementation and does the difficult part of
+		 * checking subdirectories.
 		 */
-		if (path == NULL)
-		    continue;
-		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;
-		}
+	    if (path == NULL)
+		continue;
+	    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;
+	}
+	return (issub);
+}
+
+/*
+ * checksubdir(newpath, strictness)
+ *
+ * checksubdir determines if the specified path (newpath) is a
+ * subdirectory of another share. It calls checksubdirgroup() to do
+ * the complicated work. The strictness parameter determines how
+ * strict a check to make against the path. The strictness values
+ * mean: SA_CHECK_NORMAL == only check newpath against shares that are
+ * active SA_CHECK_STRICT == check newpath against both active shares
+ * and those * stored in the repository
+ */
+static int
+checksubdir(char *newpath, int strictness)
+{
+	sa_group_t group;
+	int issub;
+	char *path = NULL;
+
+	for (issub = 0, group = sa_get_group(NULL);
+		group != NULL && !issub;
+		group = sa_get_next_group(group)) {
+	    if (sa_group_is_zfs(group)) {
+		sa_group_t subgroup;
+		for (subgroup = sa_get_sub_group(group);
+		    subgroup != NULL && !issub;
+		    subgroup = sa_get_next_group(subgroup))
+		    issub = checksubdirgroup(subgroup, newpath, strictness);
+	    } else {
+		issub = checksubdirgroup(group, newpath, strictness);
 	    }
 	}
 	if (path != NULL)
@@ -378,7 +374,7 @@
 		fstype = sa_fstype(path);
 		if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
 		    if (sa_zfs_is_shared(path))
-			error = SA_DUPLICATE_NAME;
+			error = SA_INVALID_NAME;
 		}
 		if (fstype != NULL)
 		    sa_free_fstype(fstype);
@@ -541,6 +537,10 @@
 	struct stat st;
 	int legacy = 0;
 	uint64_t tval = 0;
+	int lockfd;
+	sigset_t old;
+	int updatelegacy = B_FALSE;
+	scf_simple_prop_t *prop;
 
 	if (!sa_initialized) {
 	    /* get protocol specific structures */
@@ -557,10 +557,62 @@
 		 */
 		scf_handle = sa_scf_init();
 		if (scf_handle != NULL) {
+			/*
+			 * Need to lock the extraction of the
+			 * configuration if the dfstab file has
+			 * changed. Lock everything now and release if
+			 * not needed.  Use a file that isn't being
+			 * manipulated by other parts of the system in
+			 * order to not interfere with locking. Using
+			 * dfstab doesn't work.
+			 */
+		    sablocksigs(&old);
+		    lockfd = open(DFS_LOCK_FILE, O_RDWR);
+		    if (lockfd >= 0) {
+			extern int errno;
+			errno = 0;
+			(void) lockf(lockfd, F_LOCK, 0);
+			/*
+			 * Check whether we are going to need to merge
+			 * any dfstab changes. This is done by
+			 * comparing the value of legacy-timestamp
+			 * with the current st_ctim of the file. If
+			 * they are different, an update is needed and
+			 * the file must remain locked until the merge
+			 * is done in order to prevent multiple
+			 * startups from changing the SMF repository
+			 * at the same time.  The first to get the
+			 * lock will make any changes before the
+			 * others can read the repository.
+			 */
+			prop = scf_simple_prop_get(scf_handle->handle,
+						(const char *)
+						    SA_SVC_FMRI_BASE ":default",
+						"operation",
+						"legacy-timestamp");
+			if (prop != NULL) {
+			    char *i64;
+			    i64 = scf_simple_prop_next_astring(prop);
+			    if (i64 != NULL) {
+				tval = strtoull(i64, NULL, 0);
+			    }
+			    if (stat(SA_LEGACY_DFSTAB, &st) >= 0 &&
+				tval != TSTAMP(st.st_ctim)) {
+				updatelegacy = B_TRUE;
+			    }
+			} else {
+			    /* We haven't set the timestamp before so do it. */
+			    updatelegacy = B_TRUE;
+			}
+		    }
+		    if (updatelegacy == B_FALSE) {
+			/* Don't need the lock anymore */
+			(void) lockf(lockfd, F_ULOCK, 0);
+			(void) close(lockfd);
+		    }
 		    (void) sa_get_config(scf_handle, &sa_config_tree,
 				    &sa_config_doc);
-		    tval = get_legacy_timestamp(sa_config_tree,
-						SA_LEGACY_DFSTAB);
+		    saunblocksigs(&old);
 		    if (tval == 0) {
 			/* first time so make sure default is setup */
 			sa_group_t defgrp;
@@ -573,13 +625,17 @@
 				opt = sa_create_optionset(defgrp, "nfs");
 			}
 		    }
-		    if (stat(SA_LEGACY_DFSTAB, &st) >= 0 &&
-			tval != TSTAMP(st.st_ctim)) {
+		    if (updatelegacy == B_TRUE) {
+			sablocksigs(&old);
 			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));
+			saunblocksigs(&old);
+			/* Safe to unlock now to allow others to run */
+			(void) lockf(lockfd, F_ULOCK, 0);
+			(void) close(lockfd);
 		    }
 		    legacy |= sa_get_zfs_shares("zfs");
 		    legacy |= gettransients(&sa_config_tree);
--- a/usr/src/lib/libshare/common/libsharecore.c	Thu Feb 15 13:46:37 2007 -0800
+++ b/usr/src/lib/libshare/common/libsharecore.c	Thu Feb 15 15:33:40 2007 -0800
@@ -96,6 +96,39 @@
 extern void set_node_attr(void *, char *, char *);
 
 /*
+ * sablocksigs(*sigs)
+ *
+ * block important signals for a critical region. Arg is a pointer to
+ * a sigset_t that is used later for the unblock.
+ */
+void
+sablocksigs(sigset_t *sigs)
+{
+	sigset_t new;
+
+	if (sigs != NULL) {
+	    (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, sigs);
+	}
+}
+
+/*
+ * saunblocksigs(*sigs)
+ *
+ * unblock previously blocked signals from the sigs arg.
+ */
+void
+saunblocksigs(sigset_t *sigs)
+{
+	if (sigs != NULL)
+	    (void) sigprocmask(SIG_SETMASK, sigs, NULL);
+}
+
+/*
  * alloc_sharelist()
  *
  * allocator function to return an zfs_sharelist_t
@@ -259,9 +292,10 @@
 			    item->resource = strdup(resource);
 		    }
 		}
+		if (item != NULL && item->fstype == NULL) {
+		    item->fstype = strdup("nfs"); /* this is the default */
+		}
 	    }
-	    if (item != NULL && item->fstype == NULL)
-		item->fstype = strdup("nfs"); /* this is the default */
 	}
 	first = fix_notice(first);
 	return (first);
@@ -500,17 +534,12 @@
 {
 	FILE *dfstab;
 	xfs_sharelist_t *list;
-	sigset_t old, new;
+	sigset_t old;
 
 	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);
+		sablocksigs(&old);
 		(void) lockf(fileno(dfstab), F_LOCK, 0);
 		list = getdfstab(dfstab);
 		rewind(dfstab);
@@ -524,7 +553,7 @@
 		(void) fsync(fileno(dfstab));
 		(void) lockf(fileno(dfstab), F_ULOCK, 0);
 		(void) fclose(dfstab);
-		(void) sigprocmask(SIG_SETMASK, &old, NULL);
+		saunblocksigs(&old);
 		if (list != NULL)
 		    dfs_free_list(list);
 	}
@@ -546,17 +575,12 @@
 	char *path;
 	sa_optionset_t optionset;
 	sa_group_t parent;
-	sigset_t old, new;
+	sigset_t old;
 
 	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);
+		sablocksigs(&old);
 		path = sa_get_share_attr(share, "path");
 		parent = sa_get_parent_group(share);
 		if (parent != NULL) {
@@ -591,7 +615,7 @@
 		    (void) lockf(fileno(dfstab), F_ULOCK, 0);
 		}
 		(void) fsync(fileno(dfstab));
-		(void) sigprocmask(SIG_SETMASK, &old, NULL);
+		saunblocksigs(&old);
 		(void) fclose(dfstab);
 		sa_free_attr_string(path);
 	} else {
@@ -624,7 +648,7 @@
 	int ret = SA_OK;
 	xfs_sharelist_t *list;
 	char *path;
-	sigset_t old, new;
+	sigset_t old;
 	char *persist;
 
 	ret = sa_proto_update_legacy(proto, share);
@@ -640,12 +664,7 @@
 	    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);
+		sablocksigs(&old);
 		path = sa_get_share_attr(share, "path");
 		(void) lockf(fileno(dfstab), F_LOCK, 0);
 		list = getdfstab(dfstab);
@@ -657,7 +676,7 @@
 		(void) fflush(dfstab);
 		(void) lockf(fileno(dfstab), F_ULOCK, 0);
 		(void) fsync(fileno(dfstab));
-		(void) sigprocmask(SIG_SETMASK, &old, NULL);
+		saunblocksigs(&old);
 		(void) fclose(dfstab);
 		sa_free_attr_string(path);
 		if (list != NULL)
@@ -1058,6 +1077,7 @@
 							    list->options,
 							    list->fstype);
 			}
+			sa_format_free(oldprops);
 		    }
 		}
 	    } else {
@@ -1168,6 +1188,7 @@
 
 	if ((fp = fopen(SHARETAB, "r")) != NULL) {
 		struct share	*sharetab_entry;
+		(void) lockf(fileno(fp), F_LOCK, 0);
 
 		while (getshare(fp, &sharetab_entry) > 0) {
 		    newp = alloc_sharelist();
@@ -1203,6 +1224,7 @@
 		    if (newp->description == NULL)
 			goto err;
 		}
+		(void) lockf(fileno(fp), F_ULOCK, 0);
 		(void) fclose(fp);
 	} else {
 	    *errp = errno;
@@ -1802,6 +1824,50 @@
 }
 
 /*
+ * checkshare(struct share *)
+ *
+ * If the share to write to sharetab is not present, need to add.  If
+ * the share is present, replace if options are different else we want
+ * to keep it.
+ * Return values:
+ *	1 - keep
+ *	2 - replace
+ * The CHK_NEW value isn't currently returned.
+ */
+#define	CHK_NEW		0
+#define	CHK_KEEP	1
+#define	CHK_REPLACE	2
+static int
+checkshare(struct share *sh)
+{
+	xfs_sharelist_t *list, *head;
+	int err;
+	int ret = CHK_NEW;
+
+	head = list = get_share_list(&err);
+	while (list != NULL && ret == CHK_NEW) {
+	    if (strcmp(sh->sh_path, list->path) == 0) {
+		/* Have the same path so check if replace or keep */
+		if (strcmp(sh->sh_opts, list->options) == 0)
+		    ret = CHK_KEEP;
+		else
+		    ret = CHK_REPLACE;
+	    }
+	    list = list->next;
+	}
+	if (head != NULL) {
+	    dfs_free_list(head);
+	}
+	/*
+	 * Just in case it was added by another process after our
+	 * scan, we always replace even if we think it is new.
+	 */
+	if (ret == CHK_NEW)
+	    ret = CHK_REPLACE;
+	return (ret);
+}
+
+/*
  * sa_update_sharetab(share, proto)
  *
  * Update the sharetab file with info from the specified share.
@@ -1816,7 +1882,8 @@
 	char *path;
 	int logging = 0;
 	FILE *sharetab;
-	sigset_t old, new;
+	sigset_t old;
+	int action;
 
 	path = sa_get_share_attr(share, "path");
 	if (path != NULL) {
@@ -1827,22 +1894,32 @@
 	    }
 	    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);
+		sablocksigs(&old);
+		/*
+		 * Fill in share structure and write it out if the
+		 * share isn't already shared with the same options.
+		 */
+		(void) fillshare(share, proto, &shtab);
+		/*
+		 * If share is new or changed, remove the old,
+		 * otherwise keep it in place since it hasn't changed.
+		 */
+		action = checkshare(&shtab);
 		(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);
+		switch (action) {
+		case CHK_REPLACE:
+		    (void) remshare(sharetab, path, &logging);
+		    (void) putshare(sharetab, &shtab);
+		    break;
+		case CHK_KEEP:
+		    /* Don't do anything */
+		    break;
+		}
 		emptyshare(&shtab);
 		(void) fflush(sharetab);
 		(void) lockf(fileno(sharetab), F_ULOCK, 0);
 		(void) fsync(fileno(sharetab));
-		(void) sigprocmask(SIG_SETMASK, &old, NULL);
+		saunblocksigs(&old);
 		(void) fclose(sharetab);
 	    } else {
 		if (errno == EACCES || errno == EPERM) {
@@ -1868,7 +1945,7 @@
 	int ret = SA_OK;
 	int logging = 0;
 	FILE *sharetab;
-	sigset_t old, new;
+	sigset_t old;
 #ifdef lint
 	proto = proto;
 #endif
@@ -1880,18 +1957,13 @@
 	    }
 	    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);
+		sablocksigs(&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);
+		saunblocksigs(&old);
 		(void) fclose(sharetab);
 	    } else {
 		if (errno == EACCES || errno == EPERM) {