changeset 21374:37cfd866f4cf

7529 SMB shares with no permission for root fail after restart Reviewed by: Alek Pinchuk <alek@nexenta.com> Reviewed by: Evan Layton <evan.layton@nexenta.com> Reviewed by: Matt Barden <matt.barden@nexenta.com> Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexent.com> Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com> Reviewed by: Rick McNeal <rick.mcneal@nexenta.com> Approved by: Garrett D'Amore <garrett@damore.org>
author Gordon Ross <gwr@nexenta.com>
date Tue, 13 Sep 2016 21:14:55 -0400
parents 94a46b25d60f
children 0c3e8724ed71
files usr/src/lib/smbsrv/libmlsvc/common/smb_share.c
diffstat 1 files changed, 127 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c	Thu Jun 04 23:45:27 2015 -0400
+++ b/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c	Tue Sep 13 21:14:55 2016 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  *
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
  */
 
 /*
@@ -389,6 +389,7 @@
 	struct stat st;
 	smb_share_t *cached_si;
 	nvlist_t *shrlist;
+	boolean_t created_zfs = B_FALSE;
 	uint32_t status;
 	int rc;
 
@@ -416,8 +417,34 @@
 		/*
 		 * If share type is STYPE_DISKTREE then the path to the
 		 * share should exist so that we can add the share to cache.
+		 * If path is ZFS, add the .zfs/shares/<share> entry.
+		 *
+		 * Both actions may require privileges that main dropped,
+		 * so we need to temporarily make those effective.
 		 */
-		rc = stat(si->shr_path, &st);
+		if (smb_proc_takesem() == 0) {
+
+			(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
+			    PRIV_FILE_DAC_READ,
+			    PRIV_FILE_DAC_SEARCH,
+			    PRIV_FILE_DAC_WRITE,
+			    NULL);
+
+			rc = stat(si->shr_path, &st);
+			if (rc == 0) {
+				smb_shr_zfs_add(si);
+				created_zfs = B_TRUE;
+			}
+
+			(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+			    PRIV_FILE_DAC_READ,
+			    PRIV_FILE_DAC_SEARCH,
+			    PRIV_FILE_DAC_WRITE,
+			    NULL);
+			smb_proc_givesem();
+		} else {
+			rc = NERR_InternalError;
+		}
 		if (rc != 0) {
 			smb_shr_cache_unlock();
 			return (NERR_ItemNotFound);
@@ -425,6 +452,7 @@
 	}
 
 	if ((status = smb_shr_cache_addent(si)) != NERR_Success) {
+		/* This error should be impossible after findent above. */
 		smb_shr_cache_unlock();
 		return (status);
 	}
@@ -440,9 +468,6 @@
 		if (rc == 0) {
 			smb_shr_publish(si->shr_name, si->shr_container);
 
-			/* If path is ZFS, add the .zfs/shares/<share> entry. */
-			smb_shr_zfs_add(si);
-
 			if ((si->shr_flags & SMB_SHRF_DFSROOT) != 0)
 				dfs_namespace_load(si->shr_name);
 
@@ -450,11 +475,34 @@
 		}
 	}
 
+	/*
+	 * Error code path, i.e. when the kernel could not accept
+	 * the new share for some reason.
+	 */
 	if (smb_shr_cache_lock(SMB_SHR_CACHE_WRLOCK) == NERR_Success) {
 		smb_shr_cache_delent(si->shr_name);
 		smb_shr_cache_unlock();
 	}
 
+	if (created_zfs && smb_proc_takesem() == 0) {
+
+		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_shr_zfs_remove(si);
+
+		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_proc_givesem();
+	}
+
 	/*
 	 * rc == ENOENT means the shared directory doesn't exist
 	 */
@@ -505,9 +553,28 @@
 
 	/*
 	 * If path is ZFS, remove the .zfs/shares/<share> entry.  Need
-	 * to remove before cleanup of cache occurs.
+	 * to remove before cleanup of cache occurs.  These actions
+	 * require temporary elevation of privileges.
 	 */
-	smb_shr_zfs_remove(si);
+	if (smb_proc_takesem() == 0) {
+
+		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_shr_zfs_remove(si);
+
+		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_proc_givesem();
+	}
+
 	(void) smb_shr_encode(si, &shrlist);
 
 	(void) strlcpy(container, si->shr_container, sizeof (container));
@@ -570,9 +637,25 @@
 	bcopy(from_si, &to_si, sizeof (smb_share_t));
 	(void) strlcpy(to_si.shr_name, to_name, sizeof (to_si.shr_name));
 
-
 	/* If path is ZFS, rename the .zfs/shares/<share> entry. */
-	smb_shr_zfs_rename(from_si, &to_si);
+	if (smb_proc_takesem() == 0) {
+
+		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_shr_zfs_rename(from_si, &to_si);
+
+		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_proc_givesem();
+	}
 
 	if ((status = smb_shr_cache_addent(&to_si)) != NERR_Success) {
 		smb_shr_cache_unlock();
@@ -730,9 +813,25 @@
 		smb_shr_publish(new_si->shr_name, new_si->shr_container);
 	}
 
-	if (quota_flag_changed) {
+	/* The following required privileges we dropped. */
+	if (quota_flag_changed && smb_proc_takesem() == 0) {
+
+		(void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
 		smb_shr_zfs_remove(&old_si);
 		smb_shr_zfs_add(si);
+
+		(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+		    PRIV_FILE_DAC_READ,
+		    PRIV_FILE_DAC_SEARCH,
+		    PRIV_FILE_DAC_WRITE,
+		    NULL);
+
+		smb_proc_givesem();
 	}
 
 	return (NERR_Success);
@@ -2049,8 +2148,8 @@
 
 /*
  * If the share path refers to a ZFS file system, add the
- * .zfs/shares/<share> object and call smb_quota_add_fs()
- * to initialize quota support for the share.
+ * .zfs/shares/<share> object and add or remove the special
+ * directory and file telling clients about quota support.
  */
 static void
 smb_shr_zfs_add(smb_share_t *si)
@@ -2077,14 +2176,16 @@
 		syslog(LOG_INFO, "share: failed to add ACL object: %s: %s\n",
 		    si->shr_name, strerror(errno));
 
-	if ((si->shr_flags & SMB_SHRF_QUOTAS) != 0) {
-		ret = zfs_prop_get(zfshd, ZFS_PROP_MOUNTPOINT,
-		    buf, MAXPATHLEN, NULL, NULL, 0, B_FALSE);
-		if (ret != 0) {
-			syslog(LOG_INFO, "share: failed to get mountpoint: "
-			    "%s\n", si->shr_name);
+	ret = zfs_prop_get(zfshd, ZFS_PROP_MOUNTPOINT,
+	    buf, MAXPATHLEN, NULL, NULL, 0, B_FALSE);
+	if (ret != 0) {
+		syslog(LOG_INFO, "share: failed to get mountpoint: "
+		    "%s\n", si->shr_name);
+	} else {
+		if ((si->shr_flags & SMB_SHRF_QUOTAS) != 0) {
+			smb_quota_add_fs(buf);
 		} else {
-			smb_quota_add_fs(buf);
+			smb_quota_remove_fs(buf);
 		}
 	}
 
@@ -2094,14 +2195,12 @@
 
 /*
  * If the share path refers to a ZFS file system, remove the
- * .zfs/shares/<share> object, and call smb_quota_remove_fs()
- * to end quota support for the share.
+ * .zfs/shares/<share> object.
  */
 static void
 smb_shr_zfs_remove(smb_share_t *si)
 {
 	libzfs_handle_t *libhd;
-	zfs_handle_t *zfshd;
 	int ret;
 	char buf[MAXPATHLEN];	/* dataset or mountpoint */
 
@@ -2111,29 +2210,18 @@
 	if ((libhd = libzfs_init()) == NULL)
 		return;
 
-	if ((zfshd = zfs_open(libhd, buf, ZFS_TYPE_FILESYSTEM)) == NULL) {
-		libzfs_fini(libhd);
-		return;
-	}
-
 	errno = 0;
 	ret = zfs_smb_acl_remove(libhd, buf, si->shr_path, si->shr_name);
 	if (ret != 0 && errno != EAGAIN)
 		syslog(LOG_INFO, "share: failed to remove ACL object: %s: %s\n",
 		    si->shr_name, strerror(errno));
 
-	if ((si->shr_flags & SMB_SHRF_QUOTAS) != 0) {
-		ret = zfs_prop_get(zfshd, ZFS_PROP_MOUNTPOINT,
-		    buf, MAXPATHLEN, NULL, NULL, 0, B_FALSE);
-		if (ret != 0) {
-			syslog(LOG_INFO, "share: failed to get mountpoint: "
-			    "%s\n", si->shr_name);
-		} else {
-			smb_quota_remove_fs(buf);
-		}
-	}
-
-	zfs_close(zfshd);
+	/*
+	 * We could remove the quotas directory here, but that adds
+	 * significantly to the time required for a zpool export,
+	 * so just leave it here and fixup when we share next.
+	 */
+
 	libzfs_fini(libhd);
 }