summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/fs/smbsrv
diff options
context:
space:
mode:
authorGordon Ross <gwr@nexenta.com>2017-09-07 17:38:42 -0400
committerGordon Ross <gwr@nexenta.com>2019-08-18 12:49:34 -0400
commit8d94f651a44d41a7147253bb5dad1a53941e8f50 (patch)
treecba3775c8f1f6ef216013772f9d391f1a4ff0297 /usr/src/uts/common/fs/smbsrv
parent2f57b5e005e6dce9d124b3dbd5fdcad1cc0372d2 (diff)
downloadillumos-joyent-8d94f651a44d41a7147253bb5dad1a53941e8f50.tar.gz
11031 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com> Reviewed by: Evan Layton <evan.layton@nexenta.com> Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com> Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com> Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com> Reviewed by: Jason King <jason.king@joyent.com> Approved by: Garrett D'Amore <garrett@damore.org>
Diffstat (limited to 'usr/src/uts/common/fs/smbsrv')
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_close.c4
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_create.c36
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_dispatch.c23
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_durable.c1241
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_lease.c3
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_lock.c4
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_negotiate.c32
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c6
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_common_open.c77
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_cred.c18
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_fsops.c38
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_init.c9
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_kshare.c102
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_node.c12
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_ofile.c185
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_pathname.c30
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_server.c108
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_session.c234
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c13
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_tree.c243
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_user.c4
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_vfs.c164
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_vops.c10
23 files changed, 1929 insertions, 667 deletions
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_close.c b/usr/src/uts/common/fs/smbsrv/smb2_close.c
index e019a3c3da..bbb000f329 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_close.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_close.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -71,6 +71,8 @@ smb2_close(smb_request_t *sr)
}
}
+ if (of->dh_persist)
+ smb2_dh_setdoc_persistent(of);
smb_ofile_close(of, 0);
errout:
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_create.c b/usr/src/uts/common/fs/smbsrv/smb2_create.c
index 6aab3c5127..582efbae28 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_create.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_create.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -280,7 +280,6 @@ smb2_create(smb_request_t *sr)
* many create context types are ignored too.
*/
op->dh_vers = SMB2_NOT_DURABLE;
- op->dh_v2_flags = 0;
if ((cctx.cc_in_flags &
(CCTX_DH_RECONNECT|CCTX_DH_RECONNECT_V2)) != 0) {
@@ -388,6 +387,9 @@ smb2_create(smb_request_t *sr)
cctx.cc_in_flags &= ~CCTX_REQUEST_LEASE;
}
+ if ((sr->tid_tree->t_flags & SMB_TREE_CA) == 0)
+ op->dh_v2_flags &= ~DH_PERSISTENT;
+
if ((cctx.cc_in_flags &
(CCTX_DH_REQUEST|CCTX_DH_REQUEST_V2)) != 0) {
if ((cctx.cc_in_flags & CCTX_DH_REQUEST_V2) != 0)
@@ -441,15 +443,19 @@ smb2_create(smb_request_t *sr)
* non-durable handles in case we get the ioctl
* to set "resiliency" on this handle.
*/
- if (of->f_ftype == SMB_FTYPE_DISK)
- smb_ofile_set_persistid(of);
+ if (of->f_ftype == SMB_FTYPE_DISK) {
+ if ((op->dh_v2_flags & DH_PERSISTENT) != 0)
+ smb_ofile_set_persistid_ph(of);
+ else
+ smb_ofile_set_persistid_dh(of);
+ }
/*
* [MS-SMB2] 3.3.5.9.8
* Handling the SMB2_CREATE_REQUEST_LEASE Create Context
*/
if ((cctx.cc_in_flags & CCTX_REQUEST_LEASE) != 0) {
- status = smb2_lease_create(sr);
+ status = smb2_lease_create(sr, sr->session->clnt_uuid);
if (status != NT_STATUS_SUCCESS) {
if (op->action_taken == SMB_OACT_CREATED) {
smb_ofile_set_delete_on_close(sr, of);
@@ -479,7 +485,8 @@ smb2_create(smb_request_t *sr)
if ((cctx.cc_in_flags &
(CCTX_DH_REQUEST|CCTX_DH_REQUEST_V2)) != 0 &&
smb_node_is_file(of->f_node) &&
- ((op->op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH) ||
+ ((op->dh_v2_flags & DH_PERSISTENT) != 0 ||
+ (op->op_oplock_level == SMB2_OPLOCK_LEVEL_BATCH) ||
(op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE &&
(op->lease_state & OPLOCK_LEVEL_CACHE_HANDLE) != 0))) {
/*
@@ -489,8 +496,13 @@ smb2_create(smb_request_t *sr)
(void) memcpy(of->dh_create_guid,
op->create_guid, UUID_LEN);
- /* no persistent handles yet */
- of->dh_persist = B_FALSE;
+ if ((op->dh_v2_flags & DH_PERSISTENT) != 0) {
+ if (smb2_dh_make_persistent(sr, of) == 0) {
+ of->dh_persist = B_TRUE;
+ } else {
+ op->dh_v2_flags = 0;
+ }
+ }
}
if (op->dh_vers != SMB2_NOT_DURABLE) {
uint32_t msto;
@@ -503,8 +515,11 @@ smb2_create(smb_request_t *sr)
* the default timeout (in mSec.)
*/
msto = op->dh_timeout;
- if (msto == 0)
- msto = smb2_dh_def_timeout;
+ if (msto == 0) {
+ msto = (of->dh_persist) ?
+ smb2_persist_timeout :
+ smb2_dh_def_timeout;
+ }
if (msto > smb2_dh_max_timeout)
msto = smb2_dh_max_timeout;
op->dh_timeout = msto;
@@ -512,6 +527,7 @@ smb2_create(smb_request_t *sr)
}
} else {
op->dh_vers = SMB2_NOT_DURABLE;
+ op->dh_v2_flags = 0;
}
/*
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c b/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c
index b592dc4c5f..88c4b6d600 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_dispatch.c
@@ -979,6 +979,16 @@ cmd_done:
*/
if (!sr->smb2_async && sr->smb2_next_command != 0)
goto cmd_start;
+
+ /*
+ * If we have a durable handle, and this operation updated
+ * the nvlist, write it out (before smb2_send_reply).
+ */
+ if (sr->dh_nvl_dirty) {
+ sr->dh_nvl_dirty = B_FALSE;
+ smb2_dh_update_nvfile(sr);
+ }
+
smb2_send_reply(sr);
if (sr->smb2_async && sr->smb2_next_command != 0) {
MBC_FLUSH(&sr->reply); /* New reply buffer. */
@@ -990,6 +1000,9 @@ cleanup:
if (disconnect)
smb_session_disconnect(session);
+ /*
+ * Do "postwork" for oplock (and maybe other things)
+ */
if (sr->sr_postwork != NULL)
smb2sr_run_postwork(sr);
@@ -1728,6 +1741,16 @@ smb2sr_run_postwork(smb_request_t *top_sr)
default:
ASSERT(0);
}
+
+ /*
+ * If we have a durable handle, and this operation
+ * updated the nvlist, write it out.
+ */
+ if (post_sr->dh_nvl_dirty) {
+ post_sr->dh_nvl_dirty = B_FALSE;
+ smb2_dh_update_nvfile(post_sr);
+ }
+
post_sr->sr_state = SMB_REQ_STATE_COMPLETED;
smb_request_free(post_sr);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_durable.c b/usr/src/uts/common/fs/smbsrv/smb2_durable.c
index 9ba3dd9c07..7b65924ca4 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_durable.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_durable.c
@@ -21,6 +21,7 @@
#include <sys/cmn_err.h>
#include <sys/fcntl.h>
#include <sys/nbmlock.h>
+#include <sys/sid.h>
#include <smbsrv/string.h>
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
@@ -53,6 +54,48 @@ uint32_t smb2_dh_max_timeout = 300 * MILLISEC; /* mSec. */
uint32_t smb2_res_def_timeout = 120 * MILLISEC; /* mSec. */
uint32_t smb2_res_max_timeout = 300 * MILLISEC; /* mSec. */
+uint32_t smb2_persist_timeout = 300 * MILLISEC; /* mSec. */
+
+/* Max. size of the file used to store a CA handle. */
+static uint32_t smb2_dh_max_cah_size = 64 * 1024;
+static uint32_t smb2_ca_info_version = 1;
+
+/*
+ * Want this to have invariant layout on disk, where the
+ * last two uint32_t values are stored as a uint64_t
+ */
+struct nvlk {
+ uint64_t lk_start;
+ uint64_t lk_len;
+ /* (lk_pid << 32) | lk_type */
+#ifdef _BIG_ENDIAN
+ uint32_t lk_pid, lk_type;
+#else
+ uint32_t lk_type, lk_pid;
+#endif
+};
+
+static void smb2_dh_import_share(void *);
+static smb_ofile_t *smb2_dh_import_handle(smb_request_t *, smb_node_t *,
+ uint64_t);
+static int smb2_dh_read_nvlist(smb_request_t *, smb_node_t *, struct nvlist **);
+static int smb2_dh_import_cred(smb_ofile_t *, char *);
+
+#define DH_SN_SIZE 24 /* size of DH stream name buffers */
+/*
+ * Build the stream name used to store a CA handle.
+ * i.e. ":0123456789abcdef:$CA"
+ * Note: smb_fsop_create adds the SUNWsmb prefix,
+ * so we compose the name without the prefix.
+ */
+static inline void
+smb2_dh_make_stream_name(char *buf, size_t buflen, uint64_t id)
+{
+ ASSERT(buflen >= DH_SN_SIZE);
+ (void) snprintf(buf, buflen,
+ ":%016" PRIx64 ":$CA", id);
+}
+
/*
* smb_dh_should_save
*
@@ -80,6 +123,11 @@ uint32_t smb2_res_max_timeout = 300 * MILLISEC; /* mSec. */
* Open.OplockState == Held, and Open.IsDurable is TRUE.
*
* - Open.IsPersistent is TRUE.
+ *
+ * We also deal with some special cases for shutdown of the
+ * server, session, user, tree (in that order). Other than
+ * the cases above, shutdown (or forced termination) should
+ * destroy durable handles.
*/
boolean_t
smb_dh_should_save(smb_ofile_t *of)
@@ -87,12 +135,49 @@ smb_dh_should_save(smb_ofile_t *of)
ASSERT(MUTEX_HELD(&of->f_mutex));
ASSERT(of->dh_vers != SMB2_NOT_DURABLE);
- if (of->f_user->preserve_opens == SMB2_DH_PRESERVE_NONE)
+ /* SMB service shutting down, destroy DH */
+ if (of->f_server->sv_state == SMB_SERVER_STATE_STOPPING)
return (B_FALSE);
- if (of->f_user->preserve_opens == SMB2_DH_PRESERVE_ALL)
+ /*
+ * SMB Session (connection) going away (server up).
+ * If server initiated disconnect, destroy DH
+ * If client initiated disconnect, save all DH.
+ */
+ if (of->f_session->s_state == SMB_SESSION_STATE_TERMINATED)
+ return (B_FALSE);
+ if (of->f_session->s_state == SMB_SESSION_STATE_DISCONNECTED)
return (B_TRUE);
+ /*
+ * SMB User logoff, session still "up".
+ * Action depends on why/how this logoff happened,
+ * determined based on user->preserve_opens
+ */
+ if (of->f_user->u_state == SMB_USER_STATE_LOGGING_OFF) {
+ switch (of->f_user->preserve_opens) {
+ case SMB2_DH_PRESERVE_NONE:
+ /* Server-initiated */
+ return (B_FALSE);
+ case SMB2_DH_PRESERVE_SOME:
+ /* Previous session logoff. */
+ goto preserve_some;
+ case SMB2_DH_PRESERVE_ALL:
+ /* Protocol logoff request */
+ return (B_TRUE);
+ }
+ }
+
+ /*
+ * SMB tree disconnecting (user still logged on)
+ * i.e. when kshare export forces disconnection.
+ */
+ if (of->f_tree->t_state == SMB_TREE_STATE_DISCONNECTING)
+ return (B_FALSE);
+
+preserve_some:
+ /* preserve_opens == SMB2_DH_PRESERVE_SOME */
+
switch (of->dh_vers) {
case SMB2_RESILIENT:
return (B_TRUE);
@@ -116,6 +201,1063 @@ smb_dh_should_save(smb_ofile_t *of)
}
/*
+ * Is this stream name a CA handle? i.e.
+ * ":0123456789abcdef:$CA"
+ */
+static boolean_t
+smb2_dh_match_ca_name(const char *name, uint64_t *idp)
+{
+ static const char suffix[] = ":$CA";
+ u_longlong_t ull;
+ const char *p = name;
+ char *p2 = NULL;
+ int len, rc;
+
+ if (*p++ != ':')
+ return (B_FALSE);
+
+ rc = ddi_strtoull(p, &p2, 16, &ull);
+ if (rc != 0 || p2 != (p + 16))
+ return (B_FALSE);
+ p += 16;
+
+ len = sizeof (suffix) - 1;
+ if (strncmp(p, suffix, len) != 0)
+ return (B_FALSE);
+ p += len;
+
+ if (*p != '\0')
+ return (B_FALSE);
+
+ *idp = (uint64_t)ull;
+ return (B_TRUE);
+}
+
+/*
+ * smb2_dh_new_ca_share
+ *
+ * Called when a new share has ca=true. Find or create the CA dir,
+ * and start a thread to import persistent handles.
+ */
+int
+smb2_dh_new_ca_share(smb_server_t *sv, smb_kshare_t *shr)
+{
+ smb_kshare_t *shr2;
+ smb_request_t *sr;
+
+ ASSERT(STYPE_ISDSK(shr->shr_type));
+
+ /*
+ * Need to lookup the kshare again, to get a hold.
+ * Add a function to just get the hold?
+ */
+ shr2 = smb_kshare_lookup(sv, shr->shr_name);
+ if (shr2 != shr)
+ return (EINVAL);
+
+ sr = smb_request_alloc(sv->sv_session, 0);
+ if (sr == NULL) {
+ /* shutting down? */
+ smb_kshare_release(sv, shr);
+ return (EINTR);
+ }
+ sr->sr_state = SMB_REQ_STATE_SUBMITTED;
+
+ /*
+ * Mark this share as "busy importing persistent handles"
+ * so we can hold off tree connect until that's done.
+ * Will clear and wakeup below.
+ */
+ mutex_enter(&shr->shr_mutex);
+ shr->shr_import_busy = sr;
+ mutex_exit(&shr->shr_mutex);
+
+ /*
+ * Start a taskq job to import any CA handles.
+ * The hold on the kshare is given to this job,
+ * which releases it when it's done.
+ */
+ sr->arg.tcon.si = shr; /* hold from above */
+ (void) taskq_dispatch(
+ sv->sv_worker_pool,
+ smb2_dh_import_share, sr, TQ_SLEEP);
+
+ return (0);
+}
+
+int smb2_dh_import_delay = 0;
+
+static void
+smb2_dh_import_share(void *arg)
+{
+ smb_request_t *sr = arg;
+ smb_kshare_t *shr = sr->arg.tcon.si;
+ smb_node_t *snode;
+ cred_t *kcr = zone_kcred();
+ smb_streaminfo_t *str_info = NULL;
+ uint64_t id;
+ smb_node_t *str_node;
+ smb_odir_t *od = NULL;
+ smb_ofile_t *of;
+ int rc;
+ boolean_t eof;
+
+ sr->sr_state = SMB_REQ_STATE_ACTIVE;
+
+ if (smb2_dh_import_delay > 0)
+ delay(SEC_TO_TICK(smb2_dh_import_delay));
+
+ /*
+ * Borrow the server's "root" user.
+ *
+ * This takes the place of smb_session_lookup_ssnid()
+ * that would happen in smb2_dispatch for a normal SR.
+ * As usual, this hold is released in smb_request_free.
+ */
+ sr->uid_user = sr->sr_server->sv_rootuser;
+ smb_user_hold_internal(sr->uid_user);
+ sr->user_cr = sr->uid_user->u_cred;
+
+ /*
+ * Create a temporary tree connect
+ */
+ sr->arg.tcon.path = shr->shr_name;
+ sr->tid_tree = smb_tree_alloc(sr, shr, shr->shr_root_node,
+ ACE_ALL_PERMS, 0);
+ if (sr->tid_tree == NULL) {
+ cmn_err(CE_NOTE, "smb2_dh_import_share: "
+ "failed connect share <%s>", shr->shr_name);
+ goto out;
+ }
+ snode = sr->tid_tree->t_snode;
+
+ /*
+ * Get the buffers we'll use to read CA handle data.
+ * Stash in sr_request_buf for smb2_dh_import_handle().
+ * Also a buffer for the stream name info.
+ */
+ sr->sr_req_length = smb2_dh_max_cah_size;
+ sr->sr_request_buf = kmem_alloc(sr->sr_req_length, KM_SLEEP);
+ str_info = kmem_alloc(sizeof (smb_streaminfo_t), KM_SLEEP);
+
+ /*
+ * Open the ext. attr dir under the share root and
+ * import CA handles for this share.
+ */
+ if (smb_odir_openat(sr, snode, &od) != 0) {
+ cmn_err(CE_NOTE, "Share [%s] CA import, no xattr dir?",
+ shr->shr_name);
+ goto out;
+ }
+
+ eof = B_FALSE;
+ do {
+ /*
+ * If the kshare gets unshared before we finish,
+ * bail out so we don't hold things up.
+ */
+ if (shr->shr_flags & SMB_SHRF_REMOVED)
+ break;
+
+ /*
+ * Read a stream name and info
+ */
+ rc = smb_odir_read_streaminfo(sr, od, str_info, &eof);
+ if ((rc != 0) || (eof))
+ break;
+
+ /*
+ * Skip anything not a CA handle.
+ */
+ if (!smb2_dh_match_ca_name(str_info->si_name, &id)) {
+ continue;
+ }
+
+ /*
+ * Lookup stream node and import
+ */
+ str_node = NULL;
+ rc = smb_fsop_lookup_name(sr, kcr, SMB_CASE_SENSITIVE,
+ snode, snode, str_info->si_name, &str_node);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "Share [%s] CA import, "
+ "lookup <%s> failed rc=%d",
+ shr->shr_name, str_info->si_name, rc);
+ continue;
+ }
+ of = smb2_dh_import_handle(sr, str_node, id);
+ smb_node_release(str_node);
+ if (of != NULL) {
+ smb_ofile_release(of);
+ of = NULL;
+ }
+ sr->fid_ofile = NULL;
+
+ } while (!eof);
+
+out:
+ if (od != NULL) {
+ smb_odir_close(od);
+ smb_odir_release(od);
+ }
+
+ if (str_info != NULL)
+ kmem_free(str_info, sizeof (smb_streaminfo_t));
+ /* Let smb_request_free clean up sr->sr_request_buf */
+
+ /*
+ * We did a (temporary, internal) tree connect above,
+ * which we need to undo before we return. Note that
+ * smb_request_free will do the final release of
+ * sr->tid_tree, sr->uid_user
+ */
+ if (sr->tid_tree != NULL)
+ smb_tree_disconnect(sr->tid_tree, B_FALSE);
+
+ /*
+ * Wake up any waiting tree connect(s).
+ * See smb_tree_connect_disk().
+ */
+ mutex_enter(&shr->shr_mutex);
+ shr->shr_import_busy = NULL;
+ cv_broadcast(&shr->shr_cv);
+ mutex_exit(&shr->shr_mutex);
+
+ smb_kshare_release(sr->sr_server, shr);
+ smb_request_free(sr);
+}
+
+/*
+ * This returns the new ofile mostly for dtrace.
+ */
+static smb_ofile_t *
+smb2_dh_import_handle(smb_request_t *sr, smb_node_t *str_node,
+ uint64_t persist_id)
+{
+ uint8_t client_uuid[UUID_LEN];
+ smb_tree_t *tree = sr->tid_tree;
+ smb_arg_open_t *op = &sr->arg.open;
+ smb_pathname_t *pn = &op->fqi.fq_path;
+ cred_t *kcr = zone_kcred();
+ struct nvlist *nvl = NULL;
+ char *sidstr = NULL;
+ smb_ofile_t *of = NULL;
+ smb_attr_t *pa;
+ boolean_t did_open = B_FALSE;
+ boolean_t have_lease = B_FALSE;
+ hrtime_t hrt;
+ uint64_t *u64p;
+ uint64_t u64;
+ uint32_t u32;
+ uint32_t status;
+ char *s;
+ uint8_t *u8p;
+ uint_t alen;
+ int rc;
+
+ /*
+ * While we're called with arg.tcon, we now want to use
+ * smb_arg_open for the rest of import, so clear it.
+ */
+ bzero(op, sizeof (*op));
+ op->create_disposition = FILE_OPEN;
+
+ /*
+ * Read and unpack the NVL
+ */
+ rc = smb2_dh_read_nvlist(sr, str_node, &nvl);
+ if (rc != 0)
+ return (NULL);
+
+ /*
+ * Known CA info version?
+ */
+ u32 = 0;
+ rc = nvlist_lookup_uint32(nvl, "info_version", &u32);
+ if (rc != 0 || u32 != smb2_ca_info_version) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) bad vers=%d",
+ tree->t_resource, str_node->od_name, u32);
+ goto errout;
+ }
+
+ /*
+ * The persist ID in the nvlist should match the one
+ * encoded in the file name. (not enforced)
+ */
+ u64 = 0;
+ rc = nvlist_lookup_uint64(nvl, "file_persistid", &u64);
+ if (rc != 0 || u64 != persist_id) {
+ cmn_err(CE_WARN, "CA import (%s/%s) bad id=%016" PRIx64,
+ tree->t_resource, str_node->od_name, u64);
+ /* goto errout? (allow) */
+ }
+
+ /*
+ * Does it belong in the share being imported?
+ */
+ s = NULL;
+ rc = nvlist_lookup_string(nvl, "share_name", &s);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) no share_name",
+ tree->t_resource, str_node->od_name);
+ goto errout;
+ }
+ if (smb_strcasecmp(s, tree->t_sharename, 0) != 0) {
+ /* Normal (not an error) */
+#ifdef DEBUG
+ cmn_err(CE_NOTE, "CA import (%s/%s) other share",
+ tree->t_resource, str_node->od_name);
+#endif
+ goto errout;
+ }
+
+ /*
+ * Get the path name (for lookup)
+ */
+ rc = nvlist_lookup_string(nvl, "path_name", &pn->pn_path);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) no path_name",
+ tree->t_resource, str_node->od_name);
+ goto errout;
+ }
+
+ /*
+ * owner sid
+ */
+ rc = nvlist_lookup_string(nvl, "owner_sid", &sidstr);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) no owner_sid",
+ tree->t_resource, str_node->od_name);
+ goto errout;
+ }
+
+ /*
+ * granted access
+ */
+ rc = nvlist_lookup_uint32(nvl,
+ "granted_access", &op->desired_access);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) no granted_access",
+ tree->t_resource, str_node->od_name);
+ goto errout;
+ }
+
+ /*
+ * share access
+ */
+ rc = nvlist_lookup_uint32(nvl,
+ "share_access", &op->share_access);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) no share_access",
+ tree->t_resource, str_node->od_name);
+ goto errout;
+ }
+
+ /*
+ * create options
+ */
+ rc = nvlist_lookup_uint32(nvl,
+ "create_options", &op->create_options);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) no create_options",
+ tree->t_resource, str_node->od_name);
+ goto errout;
+ }
+
+ /*
+ * create guid (client-assigned)
+ */
+ alen = UUID_LEN;
+ u8p = NULL;
+ rc = nvlist_lookup_uint8_array(nvl, "file_guid", &u8p, &alen);
+ if (rc != 0 || alen != UUID_LEN) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) bad file_guid",
+ tree->t_resource, str_node->od_name);
+ goto errout;
+ }
+ bcopy(u8p, op->create_guid, UUID_LEN);
+
+ /*
+ * client uuid (identifies the client)
+ */
+ alen = UUID_LEN;
+ u8p = NULL;
+ rc = nvlist_lookup_uint8_array(nvl, "client_uuid", &u8p, &alen);
+ if (rc != 0 || alen != UUID_LEN) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) no client_uuid",
+ tree->t_resource, str_node->od_name);
+ goto errout;
+ }
+ bcopy(u8p, client_uuid, UUID_LEN);
+
+ /*
+ * Lease key (optional)
+ */
+ alen = SMB_LEASE_KEY_SZ;
+ u8p = NULL;
+ rc = nvlist_lookup_uint8_array(nvl, "lease_uuid", &u8p, &alen);
+ if (rc == 0) {
+ bcopy(u8p, op->lease_key, UUID_LEN);
+ (void) nvlist_lookup_uint32(nvl,
+ "lease_state", &op->lease_state);
+ (void) nvlist_lookup_uint16(nvl,
+ "lease_epoch", &op->lease_epoch);
+ (void) nvlist_lookup_uint16(nvl,
+ "lease_version", &op->lease_version);
+ have_lease = B_TRUE;
+ } else {
+ (void) nvlist_lookup_uint32(nvl,
+ "oplock_state", &op->op_oplock_state);
+ }
+
+ /*
+ * Done getting what we need from the NV list.
+ * (re)open the file
+ */
+ status = smb_common_open(sr);
+ if (status != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) open failed 0x%x",
+ tree->t_resource, str_node->od_name, status);
+ (void) smb_node_set_delete_on_close(str_node, kcr, 0);
+ goto errout;
+ }
+ of = sr->fid_ofile;
+ did_open = B_TRUE;
+
+ /*
+ * Now restore the rest of the SMB2 level state.
+ * See smb2_create after smb_common_open
+ */
+
+ /*
+ * Setup of->f_cr with owner SID
+ */
+ rc = smb2_dh_import_cred(of, sidstr);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) import cred failed",
+ tree->t_resource, str_node->od_name);
+ goto errout;
+ }
+
+ /*
+ * Use the persist ID we previously assigned.
+ * Like smb_ofile_set_persistid_ph()
+ */
+ rc = smb_ofile_insert_persistid(of, persist_id);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) "
+ "insert_persistid rc=%d",
+ tree->t_resource, str_node->od_name, rc);
+ goto errout;
+ }
+
+ /*
+ * Like smb2_lease_create()
+ *
+ * Lease state is stored in each persistent handle, but
+ * only one handle has the state we want. As we import
+ * each handle, "upgrade" the lease if the handle we're
+ * importing has a "better" lease state (higher epoch or
+ * more cache rights). After all handles are imported,
+ * that will get the lease to the right state.
+ */
+ if (have_lease) {
+ smb_lease_t *ls;
+ status = smb2_lease_create(sr, client_uuid);
+ if (status != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) get lease 0x%x",
+ tree->t_resource, str_node->od_name, status);
+ goto errout;
+ }
+ ls = of->f_lease;
+
+ /* Use most current "epoch". */
+ mutex_enter(&ls->ls_mutex);
+ if (ls->ls_epoch < op->lease_epoch)
+ ls->ls_epoch = op->lease_epoch;
+ mutex_exit(&ls->ls_mutex);
+
+ /*
+ * Get the lease (and oplock)
+ * uses op->lease_state
+ */
+ op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE;
+ smb2_lease_acquire(sr);
+
+ } else {
+ /*
+ * No lease; maybe get an oplock
+ * uses: op->op_oplock_level
+ */
+ if (op->op_oplock_state & OPLOCK_LEVEL_BATCH) {
+ op->op_oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
+ } else if (op->op_oplock_state & OPLOCK_LEVEL_ONE) {
+ op->op_oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+ } else if (op->op_oplock_state & OPLOCK_LEVEL_TWO) {
+ op->op_oplock_level = SMB2_OPLOCK_LEVEL_II;
+ } else {
+ op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+ }
+ smb2_oplock_acquire(sr);
+ }
+
+ /*
+ * Byte range locks
+ */
+ alen = 0;
+ u64p = NULL;
+ if (nvlist_lookup_uint64_array(nvl, "locks", &u64p, &alen) == 0) {
+ uint_t i;
+ uint_t nlocks = alen / 3;
+ struct nvlk *nlp;
+
+ nlp = (struct nvlk *)u64p;
+ for (i = 0; i < nlocks; i++) {
+ status = smb_lock_range(
+ sr,
+ nlp->lk_start,
+ nlp->lk_len,
+ nlp->lk_pid,
+ nlp->lk_type,
+ 0);
+ if (status != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) "
+ "get lock %d failed 0x%x",
+ tree->t_resource,
+ str_node->od_name,
+ i, status);
+ }
+ nlp++;
+ }
+ }
+ alen = SMB_OFILE_LSEQ_MAX;
+ u8p = NULL;
+ if (nvlist_lookup_uint8_array(nvl, "lockseq", &u8p, &alen) == 0) {
+ if (alen != SMB_OFILE_LSEQ_MAX) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) "
+ "get lockseq bad len=%d",
+ tree->t_resource,
+ str_node->od_name,
+ alen);
+ } else {
+ mutex_enter(&of->f_mutex);
+ bcopy(u8p, of->f_lock_seq, alen);
+ mutex_exit(&of->f_mutex);
+ }
+ }
+
+ /*
+ * Optional "sticky" times (set pending attributes)
+ */
+ mutex_enter(&of->f_mutex);
+ pa = &of->f_pending_attr;
+ if (nvlist_lookup_hrtime(nvl, "atime", &hrt) == 0) {
+ hrt2ts(hrt, &pa->sa_vattr.va_atime);
+ pa->sa_mask |= SMB_AT_ATIME;
+ }
+ if (nvlist_lookup_hrtime(nvl, "mtime", &hrt) == 0) {
+ hrt2ts(hrt, &pa->sa_vattr.va_mtime);
+ pa->sa_mask |= SMB_AT_MTIME;
+ }
+ if (nvlist_lookup_hrtime(nvl, "ctime", &hrt) == 0) {
+ hrt2ts(hrt, &pa->sa_vattr.va_ctime);
+ pa->sa_mask |= SMB_AT_CTIME;
+ }
+ mutex_exit(&of->f_mutex);
+
+ /*
+ * Make durable and persistent.
+ * See smb2_dh_make_persistent()
+ */
+ of->dh_vers = SMB2_DURABLE_V2;
+ bcopy(op->create_guid, of->dh_create_guid, UUID_LEN);
+ of->dh_persist = B_TRUE;
+ of->dh_nvfile = str_node;
+ smb_node_ref(str_node);
+ of->dh_nvlist = nvl;
+ nvl = NULL;
+
+ /*
+ * Now make it state orphaned...
+ * See smb_ofile_drop(), then
+ * smb_ofile_save_dh()
+ */
+ mutex_enter(&of->f_mutex);
+ of->f_state = SMB_OFILE_STATE_SAVE_DH;
+ of->dh_timeout_offset = MSEC2NSEC(smb2_persist_timeout);
+ mutex_exit(&of->f_mutex);
+
+ /*
+ * Finished!
+ */
+ return (of);
+
+errout:
+ if (did_open) {
+ smb_ofile_close(of, 0);
+ smb_ofile_release(of);
+ } else {
+ ASSERT(of == NULL);
+ }
+
+ if (nvl != NULL)
+ nvlist_free(nvl);
+
+ return (NULL);
+}
+
+static int
+smb2_dh_read_nvlist(smb_request_t *sr, smb_node_t *node,
+ struct nvlist **nvlpp)
+{
+ smb_attr_t attr;
+ iovec_t iov;
+ uio_t uio;
+ smb_kshare_t *shr = sr->arg.tcon.si;
+ cred_t *kcr = zone_kcred();
+ size_t flen;
+ int rc;
+
+ bzero(&attr, sizeof (attr));
+ attr.sa_mask = SMB_AT_SIZE;
+ rc = smb_node_getattr(NULL, node, kcr, NULL, &attr);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) getattr rc=%d",
+ shr->shr_path, node->od_name, rc);
+ return (rc);
+ }
+
+ if (attr.sa_vattr.va_size < 4 ||
+ attr.sa_vattr.va_size > sr->sr_req_length) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) bad size=%" PRIu64,
+ shr->shr_path, node->od_name,
+ (uint64_t)attr.sa_vattr.va_size);
+ return (EINVAL);
+ }
+ flen = (size_t)attr.sa_vattr.va_size;
+
+ bzero(&uio, sizeof (uio));
+ iov.iov_base = sr->sr_request_buf;
+ iov.iov_len = flen;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_resid = flen;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_extflg = UIO_COPY_DEFAULT;
+ rc = smb_fsop_read(sr, kcr, node, NULL, &uio);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) read, rc=%d",
+ shr->shr_path, node->od_name, rc);
+ return (rc);
+ }
+ if (uio.uio_resid != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) short read",
+ shr->shr_path, node->od_name);
+ return (EIO);
+ }
+
+ rc = nvlist_unpack(sr->sr_request_buf, flen, nvlpp, KM_SLEEP);
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "CA import (%s/%s) unpack, rc=%d",
+ shr->shr_path, node->od_name, rc);
+ return (rc);
+ }
+
+ return (0);
+}
+
+/*
+ * Setup a vestigial credential in of->f_cr just good enough for
+ * smb_is_same_user to determine if the caller owned this ofile.
+ * At reconnect, of->f_cr will be replaced with the caller's.
+ */
+static int
+smb2_dh_import_cred(smb_ofile_t *of, char *sidstr)
+{
+#ifdef _FAKE_KERNEL
+ _NOTE(ARGUNUSED(sidstr))
+ /* fksmbd doesn't have real credentials. */
+ of->f_cr = CRED();
+ crhold(of->f_cr);
+#else
+ char tmpstr[SMB_SID_STRSZ];
+ ksid_t ksid;
+ cred_t *cr, *oldcr;
+ int rc;
+
+ (void) strlcpy(tmpstr, sidstr, sizeof (tmpstr));
+ bzero(&ksid, sizeof (ksid));
+
+ rc = smb_sid_splitstr(tmpstr, &ksid.ks_rid);
+ if (rc != 0)
+ return (rc);
+ cr = crget();
+
+ ksid.ks_domain = ksid_lookupdomain(tmpstr);
+ crsetsid(cr, &ksid, KSID_USER);
+ ksiddomain_hold(ksid.ks_domain);
+ crsetsid(cr, &ksid, KSID_OWNER);
+
+ /*
+ * Just to avoid leaving the KSID_GROUP slot NULL,
+ * put the "everyone" SID there (S-1-1-0).
+ */
+ ksid.ks_domain = ksid_lookupdomain("S-1-1");
+ ksid.ks_rid = 0;
+ crsetsid(cr, &ksid, KSID_GROUP);
+
+ oldcr = of->f_cr;
+ of->f_cr = cr;
+ if (oldcr != NULL)
+ crfree(oldcr);
+#endif
+
+ return (0);
+}
+
+/*
+ * Set Delete-on-Close (DoC) on the persistent state file so it will be
+ * removed when the last ref. goes away (in smb2_dh_close_persistent).
+ *
+ * This is called in just two places:
+ * (1) SMB2_close request -- client tells us to destroy the handle.
+ * (2) smb2_dh_expire -- client has forgotten about this handle.
+ * All other (server-initiated) close calls should leave these
+ * persistent state files in the file system.
+ */
+void
+smb2_dh_setdoc_persistent(smb_ofile_t *of)
+{
+ smb_node_t *strnode;
+ uint32_t status;
+
+ mutex_enter(&of->dh_nvlock);
+ if ((strnode = of->dh_nvfile) != NULL)
+ smb_node_ref(strnode);
+ mutex_exit(&of->dh_nvlock);
+
+ if (strnode != NULL) {
+ status = smb_node_set_delete_on_close(strnode,
+ zone_kcred(), SMB_CASE_SENSITIVE);
+ if (status != 0) {
+ cmn_err(CE_WARN, "Can't set DoC on CA file: %s",
+ strnode->od_name);
+ DTRACE_PROBE1(rm__ca__err, smb_ofile_t *, of);
+ }
+ smb_node_release(strnode);
+ }
+}
+
+/*
+ * During ofile close, free the persistent handle state nvlist and
+ * drop our reference to the state file node (which may unlink it
+ * if smb2_dh_setdoc_persistent was called).
+ */
+void
+smb2_dh_close_persistent(smb_ofile_t *of)
+{
+ smb_node_t *strnode;
+ struct nvlist *nvl;
+
+ /*
+ * Clear out nvlist and stream linkage
+ */
+ mutex_enter(&of->dh_nvlock);
+ strnode = of->dh_nvfile;
+ of->dh_nvfile = NULL;
+ nvl = of->dh_nvlist;
+ of->dh_nvlist = NULL;
+ mutex_exit(&of->dh_nvlock);
+
+ if (nvl != NULL)
+ nvlist_free(nvl);
+
+ if (strnode != NULL)
+ smb_node_release(strnode);
+}
+
+/*
+ * Make this durable handle persistent.
+ * If we succeed, set of->dh_persist = TRUE.
+ */
+int
+smb2_dh_make_persistent(smb_request_t *sr, smb_ofile_t *of)
+{
+ char fname[DH_SN_SIZE];
+ char sidstr[SMB_SID_STRSZ];
+ smb_attr_t attr;
+ smb_arg_open_t *op = &sr->arg.open;
+ cred_t *kcr = zone_kcred();
+ smb_node_t *dnode = of->f_tree->t_snode;
+ smb_node_t *fnode = NULL;
+ ksid_t *ksid;
+ int rc;
+
+ ASSERT(of->dh_nvfile == NULL);
+
+ /*
+ * Create the persistent handle nvlist file.
+ * It's a named stream in the share root.
+ */
+ smb2_dh_make_stream_name(fname, sizeof (fname), of->f_persistid);
+
+ bzero(&attr, sizeof (attr));
+ attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE | SMB_AT_SIZE;
+ attr.sa_vattr.va_type = VREG;
+ attr.sa_vattr.va_mode = 0640;
+ attr.sa_vattr.va_size = 4;
+ rc = smb_fsop_create(sr, kcr, dnode, fname, &attr, &fnode);
+ if (rc != 0)
+ return (rc);
+
+ mutex_enter(&of->dh_nvlock);
+
+ /* fnode is held. rele in smb2_dh_close_persistent */
+ of->dh_nvfile = fnode;
+ (void) nvlist_alloc(&of->dh_nvlist, NV_UNIQUE_NAME, KM_SLEEP);
+
+ /*
+ * Want the ksid as a string
+ */
+ ksid = crgetsid(of->f_user->u_cred, KSID_USER);
+ (void) snprintf(sidstr, sizeof (sidstr), "%s-%u",
+ ksid->ks_domain->kd_name, ksid->ks_rid);
+
+ /*
+ * Fill in the fixed parts of the nvlist
+ */
+ (void) nvlist_add_uint32(of->dh_nvlist,
+ "info_version", smb2_ca_info_version);
+ (void) nvlist_add_string(of->dh_nvlist,
+ "owner_sid", sidstr);
+ (void) nvlist_add_string(of->dh_nvlist,
+ "share_name", of->f_tree->t_sharename);
+ (void) nvlist_add_uint64(of->dh_nvlist,
+ "file_persistid", of->f_persistid);
+ (void) nvlist_add_uint8_array(of->dh_nvlist,
+ "file_guid", of->dh_create_guid, UUID_LEN);
+ (void) nvlist_add_string(of->dh_nvlist,
+ "client_ipaddr", sr->session->ip_addr_str);
+ (void) nvlist_add_uint8_array(of->dh_nvlist,
+ "client_uuid", sr->session->clnt_uuid, UUID_LEN);
+ (void) nvlist_add_string(of->dh_nvlist,
+ "path_name", op->fqi.fq_path.pn_path);
+ (void) nvlist_add_uint32(of->dh_nvlist,
+ "granted_access", of->f_granted_access);
+ (void) nvlist_add_uint32(of->dh_nvlist,
+ "share_access", of->f_share_access);
+ (void) nvlist_add_uint32(of->dh_nvlist,
+ "create_options", of->f_create_options);
+ if (of->f_lease != NULL) {
+ smb_lease_t *ls = of->f_lease;
+ (void) nvlist_add_uint8_array(of->dh_nvlist,
+ "lease_uuid", ls->ls_key, 16);
+ (void) nvlist_add_uint32(of->dh_nvlist,
+ "lease_state", ls->ls_state);
+ (void) nvlist_add_uint16(of->dh_nvlist,
+ "lease_epoch", ls->ls_epoch);
+ (void) nvlist_add_uint16(of->dh_nvlist,
+ "lease_version", ls->ls_version);
+ } else {
+ (void) nvlist_add_uint32(of->dh_nvlist,
+ "oplock_state", of->f_oplock.og_state);
+ }
+ mutex_exit(&of->dh_nvlock);
+
+ smb2_dh_update_locks(sr, of);
+
+ /* Tell sr update nvlist file */
+ sr->dh_nvl_dirty = B_TRUE;
+
+ return (0);
+}
+
+void
+smb2_dh_update_nvfile(smb_request_t *sr)
+{
+ smb_attr_t attr;
+ iovec_t iov;
+ uio_t uio;
+ smb_ofile_t *of = sr->fid_ofile;
+ cred_t *kcr = zone_kcred();
+ char *buf = NULL;
+ size_t buflen = 0;
+ uint32_t wcnt;
+ int rc;
+
+ if (of == NULL || of->dh_persist == B_FALSE)
+ return;
+
+ mutex_enter(&of->dh_nvlock);
+ if (of->dh_nvlist == NULL || of->dh_nvfile == NULL) {
+ mutex_exit(&of->dh_nvlock);
+ return;
+ }
+
+ rc = nvlist_size(of->dh_nvlist, &buflen, NV_ENCODE_XDR);
+ if (rc != 0)
+ goto out;
+ buf = kmem_zalloc(buflen, KM_SLEEP);
+
+ rc = nvlist_pack(of->dh_nvlist, &buf, &buflen,
+ NV_ENCODE_XDR, KM_SLEEP);
+ if (rc != 0)
+ goto out;
+
+ bzero(&attr, sizeof (attr));
+ attr.sa_mask = SMB_AT_SIZE;
+ attr.sa_vattr.va_size = buflen;
+ rc = smb_node_setattr(sr, of->dh_nvfile, kcr, NULL, &attr);
+ if (rc != 0)
+ goto out;
+
+ bzero(&uio, sizeof (uio));
+ iov.iov_base = (void *) buf;
+ iov.iov_len = buflen;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_resid = buflen;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_extflg = UIO_COPY_DEFAULT;
+ rc = smb_fsop_write(sr, kcr, of->dh_nvfile,
+ NULL, &uio, &wcnt, 0);
+ if (rc == 0 && wcnt != buflen)
+ rc = EIO;
+
+out:
+ mutex_exit(&of->dh_nvlock);
+
+ if (rc != 0) {
+ cmn_err(CE_WARN,
+ "clnt(%s) failed to update persistent handle, rc=%d",
+ sr->session->ip_addr_str, rc);
+ }
+
+ if (buf != NULL) {
+ kmem_free(buf, buflen);
+ }
+}
+
+/*
+ * Called after f_oplock (and lease) changes
+ * If lease, update: lease_state, lease_epoch
+ * else (oplock) update: oplock_state
+ */
+void
+smb2_dh_update_oplock(smb_request_t *sr, smb_ofile_t *of)
+{
+ smb_lease_t *ls;
+
+ mutex_enter(&of->dh_nvlock);
+ if (of->dh_nvlist == NULL) {
+ mutex_exit(&of->dh_nvlock);
+ return;
+ }
+
+ if (of->f_lease != NULL) {
+ ls = of->f_lease;
+ (void) nvlist_add_uint32(of->dh_nvlist,
+ "lease_state", ls->ls_state);
+ (void) nvlist_add_uint16(of->dh_nvlist,
+ "lease_epoch", ls->ls_epoch);
+ } else {
+ (void) nvlist_add_uint32(of->dh_nvlist,
+ "oplock_state", of->f_oplock.og_state);
+ }
+ mutex_exit(&of->dh_nvlock);
+
+ sr->dh_nvl_dirty = B_TRUE;
+}
+
+/*
+ * Save locks from this ofile as an array of uint64_t, where the
+ * elements are triplets: (start, length, (pid << 32) | type)
+ * Note pid should always be zero for SMB2, so we could use
+ * that 32-bit spot for something else if needed.
+ */
+void
+smb2_dh_update_locks(smb_request_t *sr, smb_ofile_t *of)
+{
+ uint8_t lseq[SMB_OFILE_LSEQ_MAX];
+ smb_node_t *node = of->f_node;
+ smb_llist_t *llist = &node->n_lock_list;
+ size_t vec_sz; // storage size
+ uint_t my_cnt = 0;
+ uint64_t *vec = NULL;
+ struct nvlk *nlp;
+ smb_lock_t *lock;
+
+ smb_llist_enter(llist, RW_READER);
+ vec_sz = (llist->ll_count + 1) * sizeof (struct nvlk);
+ vec = kmem_alloc(vec_sz, KM_SLEEP);
+ nlp = (struct nvlk *)vec;
+ for (lock = smb_llist_head(llist);
+ lock != NULL;
+ lock = smb_llist_next(llist, lock)) {
+ if (lock->l_file != of)
+ continue;
+ nlp->lk_start = lock->l_start;
+ nlp->lk_len = lock->l_length;
+ nlp->lk_pid = lock->l_pid;
+ nlp->lk_type = lock->l_type;
+ nlp++;
+ my_cnt++;
+ }
+ smb_llist_exit(llist);
+
+ mutex_enter(&of->f_mutex);
+ bcopy(of->f_lock_seq, lseq, sizeof (lseq));
+ mutex_exit(&of->f_mutex);
+
+ mutex_enter(&of->dh_nvlock);
+ if (of->dh_nvlist != NULL) {
+
+ (void) nvlist_add_uint64_array(of->dh_nvlist,
+ "locks", vec, my_cnt * 3);
+
+ (void) nvlist_add_uint8_array(of->dh_nvlist,
+ "lockseq", lseq, sizeof (lseq));
+ }
+ mutex_exit(&of->dh_nvlock);
+
+ kmem_free(vec, vec_sz);
+
+ sr->dh_nvl_dirty = B_TRUE;
+}
+
+/*
+ * Save "sticky" times
+ */
+void
+smb2_dh_update_times(smb_request_t *sr, smb_ofile_t *of, smb_attr_t *attr)
+{
+ hrtime_t t;
+
+ mutex_enter(&of->dh_nvlock);
+ if (of->dh_nvlist == NULL) {
+ mutex_exit(&of->dh_nvlock);
+ return;
+ }
+
+ if (attr->sa_mask & SMB_AT_ATIME) {
+ t = ts2hrt(&attr->sa_vattr.va_atime);
+ (void) nvlist_add_hrtime(of->dh_nvlist, "atime", t);
+ }
+ if (attr->sa_mask & SMB_AT_MTIME) {
+ t = ts2hrt(&attr->sa_vattr.va_mtime);
+ (void) nvlist_add_hrtime(of->dh_nvlist, "mtime", t);
+ }
+ if (attr->sa_mask & SMB_AT_CTIME) {
+ t = ts2hrt(&attr->sa_vattr.va_ctime);
+ (void) nvlist_add_hrtime(of->dh_nvlist, "ctime", t);
+ }
+ mutex_exit(&of->dh_nvlock);
+
+ sr->dh_nvl_dirty = B_TRUE;
+}
+
+
+/*
* Requirements for ofile found during reconnect (MS-SMB2 3.3.5.9.7):
* - security descriptor must match provided descriptor
*
@@ -332,6 +1474,8 @@ smb2_dh_expire(void *arg)
{
smb_ofile_t *of = (smb_ofile_t *)arg;
+ if (of->dh_persist)
+ smb2_dh_setdoc_persistent(of);
smb_ofile_close(of, 0);
smb_ofile_release(of);
}
@@ -383,9 +1527,96 @@ smb2_durable_timers(smb_server_t *sv)
}
/*
+ * This is called when we're about to add a new open to some node.
+ * If we still have orphaned durable handles on this node, let's
+ * assume the client has lost interest in those and close them,
+ * otherwise we might conflict with our own orphaned handles.
+ *
+ * We need this because we import persistent handles "speculatively"
+ * during share import (before the client ever asks for reconnect).
+ * That allows us to avoid any need for a "create blackout" (or
+ * "grace period") because the imported handles prevent unwanted
+ * conflicting opens from other clients. However, if some client
+ * "forgets" about a persistent handle (*cough* Hyper-V) and tries
+ * a new (conflicting) open instead of a reconnect, that might
+ * fail unless we expire our orphaned durables handle first.
+ *
+ * Logic similar to smb_node_open_check()
+ */
+void
+smb2_dh_close_my_orphans(smb_request_t *sr, smb_ofile_t *new_of)
+{
+ smb_node_t *node = new_of->f_node;
+ smb_ofile_t *of;
+
+ SMB_NODE_VALID(node);
+
+ smb_llist_enter(&node->n_ofile_list, RW_READER);
+ for (of = smb_llist_head(&node->n_ofile_list);
+ of != NULL;
+ of = smb_llist_next(&node->n_ofile_list, of)) {
+
+ /* Same client? */
+ if (of->f_lease != NULL &&
+ bcmp(sr->session->clnt_uuid,
+ of->f_lease->ls_clnt, 16) != 0)
+ continue;
+
+ if (!smb_is_same_user(sr->user_cr, of->f_cr))
+ continue;
+
+ mutex_enter(&of->f_mutex);
+ if (of->f_state == SMB_OFILE_STATE_ORPHANED) {
+ of->f_state = SMB_OFILE_STATE_EXPIRED;
+ /* inline smb_ofile_hold_internal() */
+ of->f_refcnt++;
+ smb_llist_post(&node->n_ofile_list,
+ of, smb2_dh_expire);
+ }
+ mutex_exit(&of->f_mutex);
+ }
+
+ smb_llist_exit(&node->n_ofile_list);
+}
+
+/*
+ * Called for each orphaned DH during shutdown.
+ * Clean out any in-memory state, but leave any
+ * on-disk persistent handle state in place.
+ */
+static void
+smb2_dh_cleanup(void *arg)
+{
+ smb_ofile_t *of = (smb_ofile_t *)arg;
+ smb_node_t *strnode;
+ struct nvlist *nvl;
+
+ /*
+ * Intentionally skip smb2_dh_close_persistent by
+ * clearing dh_nvfile before smb_ofile_close().
+ */
+ mutex_enter(&of->dh_nvlock);
+ strnode = of->dh_nvfile;
+ of->dh_nvfile = NULL;
+ nvl = of->dh_nvlist;
+ of->dh_nvlist = NULL;
+ mutex_exit(&of->dh_nvlock);
+
+ if (nvl != NULL)
+ nvlist_free(nvl);
+
+ if (strnode != NULL)
+ smb_node_release(strnode);
+
+ smb_ofile_close(of, 0);
+ smb_ofile_release(of);
+}
+
+/*
* Clean out durable handles during shutdown.
- * Like, smb2_durable_timers but expire all,
- * and make sure the hash buckets are empty.
+ *
+ * Like, smb2_durable_timers but cleanup only in-memory state,
+ * and leave any persistent state there for later reconnect.
*/
void
smb2_dh_shutdown(smb_server_t *sv)
@@ -410,7 +1641,7 @@ smb2_dh_shutdown(smb_server_t *sv)
of->f_state = SMB_OFILE_STATE_EXPIRED;
/* inline smb_ofile_hold_internal() */
of->f_refcnt++;
- smb_llist_post(bucket, of, smb2_dh_expire);
+ smb_llist_post(bucket, of, smb2_dh_cleanup);
break;
default:
break;
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_lease.c b/usr/src/uts/common/fs/smbsrv/smb2_lease.c
index d2bf4805b3..95d7d9c7f1 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_lease.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_lease.c
@@ -122,11 +122,10 @@ smb_hash_uuid(const uint8_t *uuid)
* Handling the SMB2_CREATE_REQUEST_LEASE Create Context
*/
uint32_t
-smb2_lease_create(smb_request_t *sr)
+smb2_lease_create(smb_request_t *sr, uint8_t *clnt)
{
smb_arg_open_t *op = &sr->arg.open;
uint8_t *key = op->lease_key;
- uint8_t *clnt = sr->session->clnt_uuid;
smb_ofile_t *of = sr->fid_ofile;
smb_hash_t *ht = sr->sr_server->sv_lease_ht;
smb_llist_t *bucket;
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_lock.c b/usr/src/uts/common/fs/smbsrv/smb2_lock.c
index c6e8236cce..cc05f96e75 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_lock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_lock.c
@@ -142,6 +142,10 @@ smb2_lock(smb_request_t *sr)
status = smb2_locks(sr);
}
+ if (sr->fid_ofile->dh_persist) {
+ smb2_dh_update_locks(sr, sr->fid_ofile);
+ }
+
errout:
sr->smb2_status = status;
DTRACE_SMB2_DONE(op__Lock, smb_request_t *, sr);
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c b/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c
index cbdd5f9fb5..5bc7b01260 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_negotiate.c
@@ -26,8 +26,12 @@ uint32_t smb2srv_capabilities =
SMB2_CAP_DFS |
SMB2_CAP_LEASING |
SMB2_CAP_LARGE_MTU |
+ SMB2_CAP_PERSISTENT_HANDLES |
SMB2_CAP_ENCRYPTION;
+/* These are the only capabilities defined for SMB2.X */
+#define SMB_2X_CAPS (SMB2_CAP_DFS | SMB2_CAP_LEASING | SMB2_CAP_LARGE_MTU)
+
/*
* These are not intended as customer tunables, but dev. & test folks
* might want to adjust them (with caution).
@@ -350,16 +354,26 @@ smb2_negotiate_common(smb_request_t *sr, uint16_t version)
/*
* [MS-SMB2] 3.3.5.4 Receiving an SMB2 NEGOTIATE Request
*
- * Only set CAP_ENCRYPTION if this is 3.0 or 3.0.2 and
- * the client has it set.
+ * The SMB2.x capabilities are returned without regard for
+ * what capabilities the client provided in the request.
+ * The SMB3.x capabilities returned are the traditional
+ * logical AND of server and client capabilities.
+ *
+ * One additional check: If KCF is missing something we
+ * require for encryption, turn off that capability.
*/
-
- if (s->dialect < SMB_VERS_3_0 ||
- !SMB3_CLIENT_ENCRYPTS(sr) ||
- smb3_encrypt_init_mech(s) != 0)
- s->srv_cap = smb2srv_capabilities & ~SMB2_CAP_ENCRYPTION;
- else
- s->srv_cap = smb2srv_capabilities;
+ if (s->dialect < SMB_VERS_3_0) {
+ /* SMB 2.x */
+ s->srv_cap = smb2srv_capabilities & SMB_2X_CAPS;
+ } else {
+ /* SMB 3.0 or later */
+ s->srv_cap = smb2srv_capabilities &
+ (SMB_2X_CAPS | s->capabilities);
+ if ((s->srv_cap & SMB2_CAP_ENCRYPTION) != 0 &&
+ smb3_encrypt_init_mech(s) != 0) {
+ s->srv_cap &= ~SMB2_CAP_ENCRYPTION;
+ }
+ }
/*
* See notes above smb2_max_rwsize, smb2_old_rwsize
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c b/usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c
index e11a8855f7..34a74f564b 100644
--- a/usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c
+++ b/usr/src/uts/common/fs/smbsrv/smb2_tree_connect.c
@@ -19,6 +19,8 @@
#include <smbsrv/smb2_kproto.h>
+#define SMB2_SHARE_CAP_CA SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY
+
smb_sdrc_t
smb2_tree_connect(smb_request_t *sr)
{
@@ -114,6 +116,10 @@ smb2_tree_connect(smb_request_t *sr)
ShareFlags = 0;
Capabilities = 0;
+ if ((tree->t_flags & SMB_TREE_DFSROOT) != 0)
+ Capabilities |= SMB2_SHARE_CAP_DFS;
+ if ((tree->t_flags & SMB_TREE_CA) != 0)
+ Capabilities |= SMB2_SHARE_CAP_CA;
/*
* SMB2 Tree Connect reply
diff --git a/usr/src/uts/common/fs/smbsrv/smb_common_open.c b/usr/src/uts/common/fs/smbsrv/smb_common_open.c
index 161f2790f6..0ef06a3c3e 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_common_open.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_common_open.c
@@ -40,9 +40,6 @@
int smb_session_ofile_max = 32768;
-static volatile uint32_t smb_fids = 0;
-#define SMB_UNIQ_FID() atomic_inc_32_nv(&smb_fids)
-
extern uint32_t smb_is_executable(char *);
static void smb_delete_new_object(smb_request_t *);
static int smb_set_open_attributes(smb_request_t *, smb_ofile_t *);
@@ -280,6 +277,7 @@ smb_common_open(smb_request_t *sr)
boolean_t fnode_shrlk = B_FALSE;
boolean_t did_open = B_FALSE;
boolean_t did_break_handle = B_FALSE;
+ boolean_t did_cleanup_orphans = B_FALSE;
/* Get out now if we've been cancelled. */
mutex_enter(&sr->sr_mutex);
@@ -350,10 +348,9 @@ smb_common_open(smb_request_t *sr)
/*
* Most of IPC open is handled in smb_opipe_open()
*/
- uniq_fid = SMB_UNIQ_FID();
op->create_options = 0;
of = smb_ofile_alloc(sr, op, NULL, SMB_FTYPE_MESG_PIPE,
- tree_fid, uniq_fid);
+ tree_fid);
tree_fid = 0; // given to the ofile
status = smb_opipe_open(sr, of);
smb_threshold_exit(&sv->sv_opipe_ct);
@@ -450,13 +447,6 @@ smb_common_open(smb_request_t *sr)
goto errout;
}
- /*
- * The uniq_fid is a CIFS-server-wide unique identifier for an ofile
- * which is used to uniquely identify open instances for the
- * VFS share reservation and POSIX locks.
- */
- uniq_fid = SMB_UNIQ_FID();
-
if (last_comp_found) {
smb_node_unlock(dnode);
@@ -584,10 +574,14 @@ smb_common_open(smb_request_t *sr)
* affect the sharing checks, and may delete the file due to
* DELETE_ON_CLOSE. This may block, so set the file opening
* count before oplock stuff.
+ *
+ * Need the "proposed" ofile (and its TargetOplockKey) for
+ * correct oplock break semantics.
*/
of = smb_ofile_alloc(sr, op, fnode, SMB_FTYPE_DISK,
- tree_fid, uniq_fid);
+ tree_fid);
tree_fid = 0; // given to the ofile
+ uniq_fid = of->f_uniqid;
smb_node_inc_opening_count(fnode);
opening_incr = B_TRUE;
@@ -683,6 +677,22 @@ smb_common_open(smb_request_t *sr)
}
/*
+ * If we still have orphaned durable handles on this file,
+ * let's assume the client has lost interest in those and
+ * close them so they don't cause sharing violations.
+ * See longer comment at smb2_dh_close_my_orphans().
+ */
+ if (status == NT_STATUS_SHARING_VIOLATION &&
+ sr->session->dialect >= SMB_VERS_2_BASE &&
+ did_cleanup_orphans == B_FALSE) {
+
+ did_cleanup_orphans = B_TRUE;
+ smb2_dh_close_my_orphans(sr, of);
+
+ goto shrlock_again;
+ }
+
+ /*
* SMB1 expects a 1 sec. delay before returning a
* sharing violation error. If breaking oplocks
* above took less than a sec, wait some more.
@@ -904,27 +914,17 @@ create:
goto errout;
}
+ /* Create done. */
smb_node_unlock(dnode);
dnode_wlock = B_FALSE;
created = B_TRUE;
op->action_taken = SMB_OACT_CREATED;
+ /* Note: hold from create */
fnode = op->fqi.fq_fnode;
fnode_held = B_TRUE;
- smb_node_inc_opening_count(fnode);
- opening_incr = B_TRUE;
-
- smb_node_wrlock(fnode);
- fnode_wlock = B_TRUE;
-
- status = smb_fsop_shrlock(sr->user_cr, fnode, uniq_fid,
- op->desired_access, op->share_access);
- if (status != 0)
- goto errout;
- fnode_shrlk = B_TRUE;
-
if (max_requested) {
smb_fsop_eaccess(sr, sr->user_cr, fnode, &max_allowed);
op->desired_access |= max_allowed;
@@ -937,6 +937,27 @@ create:
*/
op->desired_access |= (READ_CONTROL | FILE_READ_ATTRIBUTES);
+ /* Allocate the ofile and fill in most of it. */
+ of = smb_ofile_alloc(sr, op, fnode, SMB_FTYPE_DISK,
+ tree_fid);
+ tree_fid = 0; // given to the ofile
+ uniq_fid = of->f_uniqid;
+
+ smb_node_inc_opening_count(fnode);
+ opening_incr = B_TRUE;
+
+ /*
+ * Share access checks...
+ */
+ smb_node_wrlock(fnode);
+ fnode_wlock = B_TRUE;
+
+ status = smb_fsop_shrlock(sr->user_cr, fnode, uniq_fid,
+ op->desired_access, op->share_access);
+ if (status != 0)
+ goto errout;
+ fnode_shrlk = B_TRUE;
+
/*
* MS-FSA 2.1.5.1.1
* If the Oplock member of the DirectoryStream in
@@ -951,9 +972,6 @@ create:
*
* The break never blocks, so ignore the return.
*/
- of = smb_ofile_alloc(sr, op, fnode, SMB_FTYPE_DISK,
- tree_fid, uniq_fid);
- tree_fid = 0; // given to the ofile
(void) smb_oplock_break_PARENT(dnode, of);
}
@@ -1052,8 +1070,9 @@ create:
errout:
if (did_open) {
smb_ofile_close(of, 0);
- /* Don't also ofile_free */
+ /* rele via sr->fid_ofile */
} else if (of != NULL) {
+ /* No other refs possible */
smb_ofile_free(of);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_cred.c b/usr/src/uts/common/fs/smbsrv/smb_cred.c
index f47f5e72a5..8431db4653 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_cred.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_cred.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -172,3 +172,19 @@ smb_cred_set_sidlist(smb_ids_t *token_grps)
return (lp);
}
+
+/*
+ * Special variant of smb_cred_create() used when we need an
+ * SMB kcred (e.g. DH import). The returned cred must be
+ * from crget() so it can be passed to smb_user_setcred().
+ */
+cred_t *
+smb_kcred_create(void)
+{
+ cred_t *cr;
+
+ cr = crget();
+ ASSERT(cr != NULL);
+
+ return (cr);
+}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_fsops.c b/usr/src/uts/common/fs/smbsrv/smb_fsops.c
index 6aa4074221..1b7c3a9fa9 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_fsops.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_fsops.c
@@ -365,6 +365,9 @@ smb_fsop_create(smb_request_t *sr, cred_t *cr, smb_node_t *dnode,
* because we want to set the UID and GID on the named
* stream in this case for consistency with the (unnamed
* stream) file (see comments for smb_vop_setattr()).
+ *
+ * Note that some stream "types" are "restricted" and only
+ * internal callers (cr == kcred) can create those.
*/
static int
smb_fsop_create_stream(smb_request_t *sr, cred_t *cr,
@@ -379,6 +382,9 @@ smb_fsop_create_stream(smb_request_t *sr, cred_t *cr,
int rc = 0;
boolean_t fcreate = B_FALSE;
+ if (cr != kcr && smb_strname_restricted(sname))
+ return (EACCES);
+
/* Look up / create the unnamed stream, fname */
rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
sr->tid_tree->t_snode, dnode, fname, &fnode);
@@ -663,6 +669,9 @@ smb_fsop_mkdir(
* It is assumed that a reference exists on snode coming into this routine.
*
* A null smb_request might be passed to this function.
+ *
+ * Note that some stream "types" are "restricted" and only
+ * internal callers (cr == kcred) can remove those.
*/
int
smb_fsop_remove(
@@ -698,6 +707,11 @@ smb_fsop_remove(
sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
if (dnode->flags & NODE_XATTR_DIR) {
+ if (cr != zone_kcred() && smb_strname_restricted(name)) {
+ rc = EACCES;
+ goto out;
+ }
+
fnode = dnode->n_dnode;
rc = smb_vop_stream_remove(fnode->vp, name, flags, cr);
@@ -709,6 +723,11 @@ smb_fsop_remove(
} else if (smb_is_stream_name(name)) {
smb_stream_parse_name(name, fname, sname);
+ if (cr != zone_kcred() && smb_strname_restricted(sname)) {
+ rc = EACCES;
+ goto out;
+ }
+
/*
* Look up the unnamed stream (i.e. fname).
* Unmangle processing will be done on fname
@@ -719,9 +738,7 @@ smb_fsop_remove(
sr->tid_tree->t_snode, dnode, fname, &fnode);
if (rc != 0) {
- kmem_free(fname, MAXNAMELEN);
- kmem_free(sname, MAXNAMELEN);
- return (rc);
+ goto out;
}
/*
@@ -744,9 +761,7 @@ smb_fsop_remove(
if (rc == ENOENT) {
if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) ||
!smb_maybe_mangled(name)) {
- kmem_free(fname, MAXNAMELEN);
- kmem_free(sname, MAXNAMELEN);
- return (rc);
+ goto out;
}
longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
@@ -776,6 +791,7 @@ smb_fsop_remove(
}
}
+out:
kmem_free(fname, MAXNAMELEN);
kmem_free(sname, MAXNAMELEN);
@@ -1609,6 +1625,9 @@ smb_fsop_statfs(
* check is performed on the named stream in case it has been
* quarantined. kcred is used to avoid issues with the permissions
* set on the extended attribute file representing the named stream.
+ *
+ * Note that some stream "types" are "restricted" and only
+ * internal callers (cr == kcred) can access those.
*/
int
smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
@@ -1639,9 +1658,14 @@ smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
unnamed_node = SMB_IS_STREAM(snode);
if (unnamed_node) {
+ cred_t *kcr = zone_kcred();
+
ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
+ if (cr != kcr && smb_strname_restricted(snode->od_name))
+ return (NT_STATUS_ACCESS_DENIED);
+
/*
* Perform VREAD access check on the named stream in case it
* is quarantined. kcred is passed to smb_vop_access so it
@@ -1649,7 +1673,7 @@ smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
*/
if (faccess & (FILE_READ_DATA | FILE_EXECUTE)) {
error = smb_vop_access(snode->vp, VREAD,
- 0, NULL, zone_kcred());
+ 0, NULL, kcr);
if (error)
return (NT_STATUS_ACCESS_DENIED);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_init.c b/usr/src/uts/common/fs/smbsrv/smb_init.c
index 88d804723e..f7e1739367 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_init.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_init.c
@@ -247,7 +247,14 @@ smb_drv_open(dev_t *devp, int flag, int otyp, cred_t *cr)
static int
smb_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
{
- return (smb_server_delete());
+ smb_server_t *sv;
+ int rc;
+
+ rc = smb_server_lookup(&sv);
+ if (rc == 0)
+ rc = smb_server_delete(sv);
+
+ return (rc);
}
/* ARGSUSED */
diff --git a/usr/src/uts/common/fs/smbsrv/smb_kshare.c b/usr/src/uts/common/fs/smbsrv/smb_kshare.c
index ef501a949a..0bdd85ddba 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_kshare.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_kshare.c
@@ -26,8 +26,9 @@
*/
#include <smbsrv/smb_door.h>
-#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_ktypes.h>
+#include <smbsrv/smb2_kproto.h>
+#include <smbsrv/smb_kstat.h>
typedef struct smb_unshare {
list_node_t us_lnd;
@@ -36,7 +37,6 @@ typedef struct smb_unshare {
static kmem_cache_t *smb_kshare_cache_share;
static kmem_cache_t *smb_kshare_cache_unexport;
-kmem_cache_t *smb_kshare_cache_vfs;
static int smb_kshare_cmp(const void *, const void *);
static void smb_kshare_hold(const void *);
@@ -294,7 +294,6 @@ smb_export_stop(smb_server_t *sv)
mutex_exit(&sv->sv_export.e_mutex);
smb_avl_destroy(&sv->sv_export.e_share_avl);
- smb_vfs_rele_all(&sv->sv_export);
}
void
@@ -305,18 +304,12 @@ smb_kshare_g_init(void)
smb_kshare_cache_unexport = kmem_cache_create("smb_unexport_cache",
sizeof (smb_unshare_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
-
- smb_kshare_cache_vfs = kmem_cache_create("smb_vfs_cache",
- sizeof (smb_vfs_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
}
void
smb_kshare_init(smb_server_t *sv)
{
- smb_llist_constructor(&sv->sv_export.e_vfs_list, sizeof (smb_vfs_t),
- offsetof(smb_vfs_t, sv_lnd));
-
smb_slist_constructor(&sv->sv_export.e_unexport_list,
sizeof (smb_unshare_t), offsetof(smb_unshare_t, us_lnd));
}
@@ -348,10 +341,6 @@ smb_kshare_fini(smb_server_t *sv)
kmem_cache_free(smb_kshare_cache_unexport, ux);
}
smb_slist_destructor(&sv->sv_export.e_unexport_list);
-
- smb_vfs_rele_all(&sv->sv_export);
-
- smb_llist_destructor(&sv->sv_export.e_vfs_list);
}
void
@@ -359,7 +348,6 @@ smb_kshare_g_fini(void)
{
kmem_cache_destroy(smb_kshare_cache_unexport);
kmem_cache_destroy(smb_kshare_cache_share);
- kmem_cache_destroy(smb_kshare_cache_vfs);
}
/*
@@ -683,10 +671,8 @@ smb_kshare_release(smb_server_t *sv, smb_kshare_t *shr)
/*
* Add the given share in the specified server.
- * If the share is a disk share, smb_vfs_hold() is
- * invoked to ensure that there is a hold on the
- * corresponding file system before the share is
- * added to shares AVL.
+ * If the share is a disk share, lookup the share path
+ * and hold the smb_node_t for the share root.
*
* If the share is an Autohome share and it is
* already in the AVL only a reference count for
@@ -697,7 +683,7 @@ smb_kshare_export(smb_server_t *sv, smb_kshare_t *shr)
{
smb_avl_t *share_avl;
smb_kshare_t *auto_shr;
- vnode_t *vp;
+ smb_node_t *snode = NULL;
int rc = 0;
share_avl = &sv->sv_export.e_share_avl;
@@ -712,36 +698,53 @@ smb_kshare_export(smb_server_t *sv, smb_kshare_t *shr)
}
if ((auto_shr = smb_avl_lookup(share_avl, shr)) != NULL) {
- if ((auto_shr->shr_flags & SMB_SHRF_AUTOHOME) == 0) {
- smb_avl_release(share_avl, auto_shr);
- return (EEXIST);
+ rc = EEXIST;
+ if ((auto_shr->shr_flags & SMB_SHRF_AUTOHOME) != 0) {
+ mutex_enter(&auto_shr->shr_mutex);
+ auto_shr->shr_autocnt++;
+ mutex_exit(&auto_shr->shr_mutex);
+ rc = 0;
}
-
- mutex_enter(&auto_shr->shr_mutex);
- auto_shr->shr_autocnt++;
- mutex_exit(&auto_shr->shr_mutex);
smb_avl_release(share_avl, auto_shr);
- return (0);
+ return (rc);
}
- if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) {
- cmn_err(CE_WARN, "export[%s(%s)]: failed obtaining vnode (%d)",
+ /*
+ * Get the root smb_node_t for this share, held.
+ * This hold is normally released during AVL destroy,
+ * via the element destructor: smb_kshare_destroy
+ */
+ rc = smb_server_share_lookup(sv, shr->shr_path, &snode);
+ if (rc != 0) {
+ cmn_err(CE_WARN, "export[%s(%s)]: lookup failed (%d)",
shr->shr_name, shr->shr_path, rc);
return (rc);
}
- if ((rc = smb_vfs_hold(&sv->sv_export, vp->v_vfsp)) == 0) {
- if ((rc = smb_avl_add(share_avl, shr)) != 0) {
- cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
- shr->shr_name, rc);
- smb_vfs_rele(&sv->sv_export, vp->v_vfsp);
+ shr->shr_root_node = snode;
+ if ((rc = smb_avl_add(share_avl, shr)) != 0) {
+ cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
+ shr->shr_name, rc);
+ shr->shr_root_node = NULL;
+ smb_node_release(snode);
+ return (rc);
+ }
+
+ /*
+ * For CA shares, find or create the CA handle dir,
+ * and (if restarted) import persistent handles.
+ */
+ if ((shr->shr_flags & SMB_SHRF_CA) != 0) {
+ rc = smb2_dh_new_ca_share(sv, shr);
+ if (rc != 0) {
+ /* Just make it a non-CA share. */
+ mutex_enter(&shr->shr_mutex);
+ shr->shr_flags &= ~SMB_SHRF_CA;
+ mutex_exit(&shr->shr_mutex);
+ rc = 0;
}
- } else {
- cmn_err(CE_WARN, "export[%s(%s)]: failed holding VFS (%d)",
- shr->shr_name, shr->shr_path, rc);
}
- VN_RELE(vp);
return (rc);
}
@@ -763,8 +766,6 @@ smb_kshare_unexport(smb_server_t *sv, const char *shrname)
smb_avl_t *share_avl;
smb_kshare_t key;
smb_kshare_t *shr;
- vnode_t *vp;
- int rc;
boolean_t auto_unexport;
share_avl = &sv->sv_export.e_share_avl;
@@ -784,19 +785,12 @@ smb_kshare_unexport(smb_server_t *sv, const char *shrname)
}
}
- if (STYPE_ISDSK(shr->shr_type)) {
- if ((rc = smb_server_sharevp(sv, shr->shr_path, &vp)) != 0) {
- smb_avl_release(share_avl, shr);
- cmn_err(CE_WARN, "unexport[%s]: failed obtaining vnode"
- " (%d)", shrname, rc);
- return (rc);
- }
+ smb_avl_remove(share_avl, shr);
- smb_vfs_rele(&sv->sv_export, vp->v_vfsp);
- VN_RELE(vp);
- }
+ mutex_enter(&shr->shr_mutex);
+ shr->shr_flags |= SMB_SHRF_REMOVED;
+ mutex_exit(&shr->shr_mutex);
- smb_avl_remove(share_avl, shr);
smb_avl_release(share_avl, shr);
return (0);
@@ -891,6 +885,7 @@ smb_kshare_decode(nvlist_t *share)
SMB_SHRF_DFSROOT);
tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_QUOTAS,
SMB_SHRF_QUOTAS);
+ tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_CA, SMB_SHRF_CA);
tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_FSO, SMB_SHRF_FSO);
tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_AUTOHOME,
SMB_SHRF_AUTOHOME);
@@ -1040,6 +1035,11 @@ smb_kshare_destroy(void *p)
ASSERT(shr);
ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
+ if (shr->shr_ca_dir != NULL)
+ smb_node_release(shr->shr_ca_dir);
+ if (shr->shr_root_node)
+ smb_node_release(shr->shr_root_node);
+
smb_mem_free(shr->shr_name);
smb_mem_free(shr->shr_path);
smb_mem_free(shr->shr_cmnt);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_node.c b/usr/src/uts/common/fs/smbsrv/smb_node.c
index 63756f9037..3e9933d51a 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_node.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_node.c
@@ -88,7 +88,7 @@
* course the state of the node should be tested/updated under the
* protection of the mutex).
*/
-#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb2_kproto.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smb_kstat.h>
#include <sys/ddi.h>
@@ -1574,10 +1574,20 @@ smb_node_setattr(smb_request_t *sr, smb_node_t *node,
attr->sa_crtime;
mutex_exit(&of->f_mutex);
+
/*
* The f_pending_attr times are reapplied in
* smb_ofile_close().
*/
+
+ /*
+ * If this change is coming directly from a client
+ * (sr != NULL) and it's a persistent handle, save
+ * the "sticky times" in the handle.
+ */
+ if (sr != NULL && of->dh_persist) {
+ smb2_dh_update_times(sr, of, attr);
+ }
}
if ((attr->sa_mask & SMB_AT_ALLOCSZ) != 0) {
diff --git a/usr/src/uts/common/fs/smbsrv/smb_ofile.c b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
index 0142bf9164..531ca314fb 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_ofile.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_ofile.c
@@ -280,11 +280,7 @@
#include <smbsrv/smb2_kproto.h>
#include <smbsrv/smb_fsops.h>
#include <sys/time.h>
-
-/* XXX: May need to actually assign GUIDs for these. */
-/* Don't leak object addresses */
-#define SMB_OFILE_PERSISTID(of) \
- ((uintptr_t)&smb_cache_ofile ^ (uintptr_t)(of))
+#include <sys/random.h>
static boolean_t smb_ofile_is_open_locked(smb_ofile_t *);
static void smb_ofile_delete(void *arg);
@@ -296,6 +292,14 @@ static int smb_ofile_netinfo_init(smb_ofile_t *, smb_netfileinfo_t *);
static void smb_ofile_netinfo_fini(smb_netfileinfo_t *);
/*
+ * The uniq_fid is a CIFS-server-wide unique identifier for an ofile
+ * which is used to uniquely identify open instances for the
+ * VFS share reservation and POSIX locks.
+ */
+static volatile uint32_t smb_fids = 0;
+#define SMB_UNIQ_FID() atomic_inc_32_nv(&smb_fids)
+
+/*
* smb_ofile_alloc
* Allocate an ofile and fill in it's "up" pointers, but
* do NOT link it into the tree's list of ofiles or the
@@ -304,6 +308,9 @@ static void smb_ofile_netinfo_fini(smb_netfileinfo_t *);
*
* If we don't get as far as smb_ofile_open with this OF,
* call smb_ofile_free() to free this object.
+ *
+ * Note: The following sr members may be null during
+ * persistent handle import: session, uid_usr, tid_tree
*/
smb_ofile_t *
smb_ofile_alloc(
@@ -311,10 +318,10 @@ smb_ofile_alloc(
smb_arg_open_t *op,
smb_node_t *node, /* optional (may be NULL) */
uint16_t ftype,
- uint16_t tree_fid,
- uint32_t uniqid)
+ uint16_t tree_fid)
{
- smb_tree_t *tree = sr->tid_tree;
+ smb_user_t *user = sr->uid_user; /* optional */
+ smb_tree_t *tree = sr->tid_tree; /* optional */
smb_ofile_t *of;
of = kmem_cache_alloc(smb_cache_ofile, KM_SLEEP);
@@ -324,22 +331,28 @@ smb_ofile_alloc(
mutex_init(&of->f_mutex, NULL, MUTEX_DEFAULT, NULL);
list_create(&of->f_notify.nc_waiters, sizeof (smb_request_t),
offsetof(smb_request_t, sr_waiters));
+ mutex_init(&of->dh_nvlock, NULL, MUTEX_DEFAULT, NULL);
of->f_state = SMB_OFILE_STATE_ALLOC;
of->f_refcnt = 1;
of->f_ftype = ftype;
of->f_fid = tree_fid;
/* of->f_persistid see smb2_create */
- of->f_uniqid = uniqid;
+ of->f_uniqid = SMB_UNIQ_FID();
of->f_opened_by_pid = sr->smb_pid;
of->f_granted_access = op->desired_access;
of->f_share_access = op->share_access;
of->f_create_options = op->create_options;
- of->f_cr = (op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) ?
- smb_user_getprivcred(sr->uid_user) : sr->uid_user->u_cred;
- crhold(of->f_cr);
- of->f_server = tree->t_server;
- of->f_session = tree->t_session;
+ if (user != NULL) {
+ if ((op->create_options & FILE_OPEN_FOR_BACKUP_INTENT) != 0)
+ of->f_cr = smb_user_getprivcred(user);
+ else
+ of->f_cr = user->u_cred;
+ crhold(of->f_cr);
+ }
+ of->f_server = sr->sr_server;
+ of->f_session = sr->session; /* may be NULL */
+
(void) memset(of->f_lock_seq, -1, SMB_OFILE_LSEQ_MAX);
of->f_mode = smb_fsop_amask_to_omode(of->f_granted_access);
@@ -361,11 +374,15 @@ smb_ofile_alloc(
* held by our caller, until smb_ofile_open puts this
* ofile on the node ofile list with smb_node_add_ofile.
*/
- smb_user_hold_internal(sr->uid_user);
- smb_tree_hold_internal(tree);
- of->f_user = sr->uid_user;
- of->f_tree = tree;
- of->f_node = node;
+ if (user != NULL) {
+ smb_user_hold_internal(user);
+ of->f_user = user;
+ }
+ if (tree != NULL) {
+ smb_tree_hold_internal(tree);
+ of->f_tree = tree;
+ }
+ of->f_node = node; /* may be NULL */
return (of);
}
@@ -448,6 +465,9 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
return;
}
+ /*
+ * Only one thread here (the one that that set f_state closing)
+ */
switch (of->f_ftype) {
case SMB_FTYPE_BYTE_PIPE:
case SMB_FTYPE_MESG_PIPE:
@@ -456,6 +476,8 @@ smb_ofile_close(smb_ofile_t *of, int32_t mtime_sec)
break;
case SMB_FTYPE_DISK:
+ if (of->dh_persist)
+ smb2_dh_close_persistent(of);
if (of->f_persistid != 0)
smb_ofile_del_persistid(of);
if (of->f_lease != NULL)
@@ -961,6 +983,9 @@ smb_ofile_lookup_by_persistid(smb_request_t *sr, uint64_t persistid)
smb_ofile_t *of;
uint_t idx;
+ if (persistid == 0)
+ return (NULL);
+
hash = sr->sr_server->sv_persistid_ht;
idx = smb_hash_uint64(hash, persistid);
bucket = &hash->buckets[idx];
@@ -981,28 +1006,132 @@ smb_ofile_lookup_by_persistid(smb_request_t *sr, uint64_t persistid)
}
/*
- * Create a (unique) persistent ID for a new ofile,
- * and add this ofile to the persistid hash table.
+ * Create a (unique) durable/persistent ID for a new ofile,
+ * and add this ofile to the persistid hash table. This ID
+ * is referred to as the persistent ID in the protocol spec,
+ * so that's what we call it too, though the persistence may
+ * vary. "Durable" handles are persistent across reconnects
+ * but not server reboots. Persistent handles are persistent
+ * across server reboots too.
+ *
+ * Note that persistent IDs need to be unique for the lifetime of
+ * any given ofile. For normal (non-persistent) ofiles we can just
+ * use a persistent ID derived from the ofile memory address, as
+ * these don't ever live beyond the current OS boot lifetime.
+ *
+ * Persistent handles are re-imported after server restart, and
+ * generally have a different memory address after import than
+ * they had in the previous OS boot lifetime, so for these we
+ * use a randomly assigned value that won't conflict with any
+ * non-persistent (durable) handles. Ensuring that a randomly
+ * generated ID is unique requires a search of the ofiles in one
+ * hash bucket, which we'd rather avoid for non-persistent opens.
+ *
+ * The solution used here is to divide the persistent ID space
+ * in half (odd and even values) where durable opens use an ID
+ * derived from the ofile address (which is always even), and
+ * persistent opens use an ID generated randomly (always odd).
+ *
+ * smb_ofile_set_persistid_dh() sets a durable handle ID and
+ * smb_ofile_set_persistid_ph() sets a persistent handle ID.
*/
void
-smb_ofile_set_persistid(smb_ofile_t *of)
+smb_ofile_set_persistid_dh(smb_ofile_t *of)
{
smb_hash_t *hash = of->f_server->sv_persistid_ht;
smb_bucket_t *bucket;
smb_llist_t *ll;
+ uint64_t persistid;
uint_t idx;
- of->f_persistid = SMB_OFILE_PERSISTID(of);
+ persistid = (uintptr_t)of;
+ /* Avoid showing object addresses */
+ persistid ^= ((uintptr_t)&smb_cache_ofile);
+ /* make sure it's even */
+ persistid &= ~((uint64_t)1);
- idx = smb_hash_uint64(hash, of->f_persistid);
+ idx = smb_hash_uint64(hash, persistid);
bucket = &hash->buckets[idx];
ll = &bucket->b_list;
smb_llist_enter(ll, RW_WRITER);
- smb_llist_insert_tail(ll, of);
+ if (of->f_persistid == 0) {
+ of->f_persistid = persistid;
+ smb_llist_insert_tail(ll, of);
+ }
smb_llist_exit(ll);
}
void
+smb_ofile_set_persistid_ph(smb_ofile_t *of)
+{
+ uint64_t persistid;
+ int rc;
+
+top:
+ (void) random_get_pseudo_bytes((uint8_t *)&persistid,
+ sizeof (persistid));
+ if (persistid == 0) {
+ cmn_err(CE_NOTE, "random gave all zeros!");
+ goto top;
+ }
+ /* make sure it's odd */
+ persistid |= (uint64_t)1;
+
+ /*
+ * Try inserting with this persistent ID.
+ */
+ rc = smb_ofile_insert_persistid(of, persistid);
+ if (rc == EEXIST)
+ goto top;
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "set persistid rc=%d", rc);
+ }
+}
+
+/*
+ * Insert an ofile into the persistid hash table.
+ * If the persistent ID is in use, error.
+ */
+int
+smb_ofile_insert_persistid(smb_ofile_t *new_of, uint64_t persistid)
+{
+ smb_hash_t *hash = new_of->f_server->sv_persistid_ht;
+ smb_bucket_t *bucket;
+ smb_llist_t *ll;
+ smb_ofile_t *of;
+ uint_t idx;
+
+ ASSERT(persistid != 0);
+
+ /*
+ * Look to see if this key alreay exists.
+ */
+ idx = smb_hash_uint64(hash, persistid);
+ bucket = &hash->buckets[idx];
+ ll = &bucket->b_list;
+
+ smb_llist_enter(ll, RW_WRITER);
+ of = smb_llist_head(ll);
+ while (of != NULL) {
+ if (of->f_persistid == persistid) {
+ /* already in use */
+ smb_llist_exit(ll);
+ return (EEXIST);
+ }
+ of = smb_llist_next(ll, of);
+ }
+
+ /* Not found, so OK to insert. */
+ if (new_of->f_persistid == 0) {
+ new_of->f_persistid = persistid;
+ smb_llist_insert_tail(ll, new_of);
+ }
+ smb_llist_exit(ll);
+
+ return (0);
+}
+
+void
smb_ofile_del_persistid(smb_ofile_t *of)
{
smb_hash_t *hash = of->f_server->sv_persistid_ht;
@@ -1014,7 +1143,10 @@ smb_ofile_del_persistid(smb_ofile_t *of)
bucket = &hash->buckets[idx];
ll = &bucket->b_list;
smb_llist_enter(ll, RW_WRITER);
- smb_llist_remove(ll, of);
+ if (of->f_persistid != 0) {
+ smb_llist_remove(ll, of);
+ of->f_persistid = 0;
+ }
smb_llist_exit(ll);
}
@@ -1390,6 +1522,7 @@ smb_ofile_free(smb_ofile_t *of)
of->f_magic = (uint32_t)~SMB_OFILE_MAGIC;
list_destroy(&of->f_notify.nc_waiters);
+ mutex_destroy(&of->dh_nvlock);
mutex_destroy(&of->f_mutex);
kmem_cache_free(smb_cache_ofile, of);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_pathname.c b/usr/src/uts/common/fs/smbsrv/smb_pathname.c
index a8f5ae3aa4..fbf003c7c0 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_pathname.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_pathname.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
*/
#include <smbsrv/smb_kproto.h>
@@ -154,7 +154,7 @@ smb_pathname_reduce(
pathname_t ppn;
char *usepath;
int lookup_flags = FOLLOW;
- int trailing_slash = 0;
+ int trailing_slash = 0;
int err = 0;
int len;
smb_node_t *vss_cur_node;
@@ -423,6 +423,10 @@ smb_pathname(smb_request_t *sr, char *path, int flags,
if ((err = pn_set(&pn, namep)) != 0)
break;
+ /* We want the DOS attributes. */
+ bzero(&attr, sizeof (attr));
+ attr.sa_mask = SMB_AT_DOSATTR;
+
local_flags = flags & FIGNORECASE;
err = smb_pathname_lookup(&pn, &rpn, local_flags,
&vp, rootvp, dnode->vp, &attr, cred);
@@ -1066,6 +1070,27 @@ smb_is_stream_name(char *path)
}
/*
+ * Is this stream node a "restricted" type?
+ */
+boolean_t
+smb_strname_restricted(char *strname)
+{
+ char *stype;
+
+ stype = strrchr(strname, ':');
+ if (stype == NULL)
+ return (B_FALSE);
+
+ /*
+ * Only ":$CA" is restricted (for now).
+ */
+ if (strcmp(stype, ":$CA") == 0)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+/*
* smb_validate_stream_name
*
* B_FALSE will be returned, and the error status ser in the sr, if:
@@ -1079,6 +1104,7 @@ boolean_t
smb_validate_stream_name(smb_request_t *sr, smb_pathname_t *pn)
{
static char *strmtype[] = {
+ "$CA",
"$DATA",
"$INDEX_ALLOCATION"
};
diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c
index 0291cacc1c..6fc64e0fac 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_server.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_server.c
@@ -20,8 +20,8 @@
*/
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2017 by Delphix. All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
*/
/*
@@ -229,12 +229,12 @@ static void smb_server_fsop_stop(smb_server_t *);
static void smb_event_cancel(smb_server_t *, uint32_t);
static uint32_t smb_event_alloc_txid(void);
-static void smb_server_disconnect_share(smb_llist_t *, const char *);
-static void smb_server_enum_users(smb_llist_t *, smb_svcenum_t *);
-static void smb_server_enum_trees(smb_llist_t *, smb_svcenum_t *);
-static int smb_server_session_disconnect(smb_llist_t *, const char *,
+static void smb_server_disconnect_share(smb_server_t *, const char *);
+static void smb_server_enum_users(smb_server_t *, smb_svcenum_t *);
+static void smb_server_enum_trees(smb_server_t *, smb_svcenum_t *);
+static int smb_server_session_disconnect(smb_server_t *, const char *,
const char *);
-static int smb_server_fclose(smb_llist_t *, uint32_t);
+static int smb_server_fclose(smb_server_t *, uint32_t);
static int smb_server_kstat_update(kstat_t *, int);
static int smb_server_legacy_kstat_update(kstat_t *, int);
static void smb_server_listener_init(smb_server_t *, smb_listener_daemon_t *,
@@ -473,14 +473,8 @@ smb_server_create(void)
* activity associated that server has ceased before destroying it.
*/
int
-smb_server_delete(void)
+smb_server_delete(smb_server_t *sv)
{
- smb_server_t *sv;
- int rc;
-
- rc = smb_server_lookup(&sv);
- if (rc != 0)
- return (rc);
mutex_enter(&sv->sv_mutex);
switch (sv->sv_state) {
@@ -608,6 +602,7 @@ smb_server_start(smb_ioc_start_t *ioc)
int rc = 0;
int family;
smb_server_t *sv;
+ cred_t *ucr;
rc = smb_server_lookup(&sv);
if (rc)
@@ -620,6 +615,31 @@ smb_server_start(smb_ioc_start_t *ioc)
if ((rc = smb_server_fsop_start(sv)) != 0)
break;
+ /*
+ * Note: smb_kshare_start needs sv_session.
+ */
+ sv->sv_session = smb_session_create(NULL, 0, sv, 0);
+ if (sv->sv_session == NULL) {
+ rc = ENOMEM;
+ break;
+ }
+
+ /*
+ * Create a logon on the server session,
+ * used when importing CA shares.
+ */
+ sv->sv_rootuser = smb_user_new(sv->sv_session);
+ ucr = smb_kcred_create();
+ rc = smb_user_logon(sv->sv_rootuser, ucr, "", "root",
+ SMB_USER_FLAG_ADMIN, 0, 0);
+ crfree(ucr);
+ ucr = NULL;
+ if (rc != 0) {
+ cmn_err(CE_NOTE, "smb_server_start: "
+ "failed to create root user");
+ break;
+ }
+
if ((rc = smb_kshare_start(sv)) != 0)
break;
@@ -637,9 +657,8 @@ smb_server_start(smb_ioc_start_t *ioc)
sv->sv_cfg.skc_maxconnections, INT_MAX,
curzone->zone_zsched, TASKQ_DYNAMIC);
- sv->sv_session = smb_session_create(NULL, 0, sv, 0);
-
- if (sv->sv_worker_pool == NULL || sv->sv_session == NULL) {
+ if (sv->sv_worker_pool == NULL ||
+ sv->sv_receiver_pool == NULL) {
rc = ENOMEM;
break;
}
@@ -888,11 +907,11 @@ smb_server_enum(smb_ioc_svcenum_t *ioc)
switch (svcenum->se_type) {
case SMB_SVCENUM_TYPE_USER:
- smb_server_enum_users(&sv->sv_session_list, svcenum);
+ smb_server_enum_users(sv, svcenum);
break;
case SMB_SVCENUM_TYPE_TREE:
case SMB_SVCENUM_TYPE_FILE:
- smb_server_enum_trees(&sv->sv_session_list, svcenum);
+ smb_server_enum_trees(sv, svcenum);
break;
default:
rc = EINVAL;
@@ -908,7 +927,6 @@ smb_server_enum(smb_ioc_svcenum_t *ioc)
int
smb_server_session_close(smb_ioc_session_t *ioc)
{
- smb_llist_t *ll;
smb_server_t *sv;
int cnt;
int rc;
@@ -916,8 +934,7 @@ smb_server_session_close(smb_ioc_session_t *ioc)
if ((rc = smb_server_lookup(&sv)) != 0)
return (rc);
- ll = &sv->sv_session_list;
- cnt = smb_server_session_disconnect(ll, ioc->client, ioc->username);
+ cnt = smb_server_session_disconnect(sv, ioc->client, ioc->username);
smb_server_release(sv);
@@ -933,15 +950,13 @@ int
smb_server_file_close(smb_ioc_fileid_t *ioc)
{
uint32_t uniqid = ioc->uniqid;
- smb_llist_t *ll;
smb_server_t *sv;
int rc;
if ((rc = smb_server_lookup(&sv)) != 0)
return (rc);
- ll = &sv->sv_session_list;
- rc = smb_server_fclose(ll, uniqid);
+ rc = smb_server_fclose(sv, uniqid);
smb_server_release(sv);
return (rc);
@@ -962,17 +977,16 @@ smb_server_get_session_count(smb_server_t *sv)
}
/*
- * Gets the vnode of the specified share path.
- *
- * A hold on the returned vnode pointer is taken so the caller
- * must call VN_RELE.
+ * Gets the smb_node of the specified share path.
+ * Node is returned held (caller must rele.)
*/
int
-smb_server_sharevp(smb_server_t *sv, const char *shr_path, vnode_t **vp)
+smb_server_share_lookup(smb_server_t *sv, const char *shr_path,
+ smb_node_t **nodepp)
{
smb_request_t *sr;
smb_node_t *fnode = NULL;
- smb_node_t *dnode;
+ smb_node_t *dnode = NULL;
char last_comp[MAXNAMELEN];
int rc = 0;
@@ -1009,10 +1023,7 @@ smb_server_sharevp(smb_server_t *sv, const char *shr_path, vnode_t **vp)
ASSERT(fnode->vp && fnode->vp->v_vfsp);
- VN_HOLD(fnode->vp);
- *vp = fnode->vp;
-
- smb_node_release(fnode);
+ *nodepp = fnode;
return (0);
}
@@ -1054,7 +1065,6 @@ int
smb_server_unshare(const char *sharename)
{
smb_server_t *sv;
- smb_llist_t *ll;
int rc;
if ((rc = smb_server_lookup(&sv)))
@@ -1072,8 +1082,7 @@ smb_server_unshare(const char *sharename)
}
mutex_exit(&sv->sv_mutex);
- ll = &sv->sv_session_list;
- smb_server_disconnect_share(ll, sharename);
+ smb_server_disconnect_share(sv, sharename);
smb_server_release(sv);
return (0);
@@ -1084,10 +1093,12 @@ smb_server_unshare(const char *sharename)
* Typically called when a share has been removed.
*/
static void
-smb_server_disconnect_share(smb_llist_t *ll, const char *sharename)
+smb_server_disconnect_share(smb_server_t *sv, const char *sharename)
{
+ smb_llist_t *ll;
smb_session_t *session;
+ ll = &sv->sv_session_list;
smb_llist_enter(ll, RW_READER);
session = smb_llist_head(ll);
@@ -1498,9 +1509,17 @@ smb_server_shutdown(smb_server_t *sv)
* normal sessions, this happens in smb_session_cancel,
* but that's not called for the server session.
*/
+ if (sv->sv_rootuser != NULL) {
+ smb_user_logoff(sv->sv_rootuser);
+ smb_user_release(sv->sv_rootuser);
+ sv->sv_rootuser = NULL;
+ }
if (sv->sv_session != NULL) {
smb_slist_wait_for_empty(&sv->sv_session->s_req_list);
+ /* Just in case import left users and trees */
+ smb_session_logoff(sv->sv_session);
+
smb_session_delete(sv->sv_session);
sv->sv_session = NULL;
}
@@ -1801,8 +1820,9 @@ smb_server_release(smb_server_t *sv)
* Enumerate the users associated with a session list.
*/
static void
-smb_server_enum_users(smb_llist_t *ll, smb_svcenum_t *svcenum)
+smb_server_enum_users(smb_server_t *sv, smb_svcenum_t *svcenum)
{
+ smb_llist_t *ll = &sv->sv_session_list;
smb_session_t *sn;
smb_llist_t *ulist;
smb_user_t *user;
@@ -1843,8 +1863,9 @@ smb_server_enum_users(smb_llist_t *ll, smb_svcenum_t *svcenum)
* Enumerate the trees/files associated with a session list.
*/
static void
-smb_server_enum_trees(smb_llist_t *ll, smb_svcenum_t *svcenum)
+smb_server_enum_trees(smb_server_t *sv, smb_svcenum_t *svcenum)
{
+ smb_llist_t *ll = &sv->sv_session_list;
smb_session_t *sn;
smb_llist_t *tlist;
smb_tree_t *tree;
@@ -1886,9 +1907,10 @@ smb_server_enum_trees(smb_llist_t *ll, smb_svcenum_t *svcenum)
* Empty strings are treated as wildcards.
*/
static int
-smb_server_session_disconnect(smb_llist_t *ll,
+smb_server_session_disconnect(smb_server_t *sv,
const char *client, const char *name)
{
+ smb_llist_t *ll = &sv->sv_session_list;
smb_session_t *sn;
smb_llist_t *ulist;
smb_user_t *user;
@@ -1933,13 +1955,15 @@ smb_server_session_disconnect(smb_llist_t *ll,
* Close a file by its unique id.
*/
static int
-smb_server_fclose(smb_llist_t *ll, uint32_t uniqid)
+smb_server_fclose(smb_server_t *sv, uint32_t uniqid)
{
+ smb_llist_t *ll;
smb_session_t *sn;
smb_llist_t *tlist;
smb_tree_t *tree;
int rc = ENOENT;
+ ll = &sv->sv_session_list;
smb_llist_enter(ll, RW_READER);
sn = smb_llist_head(ll);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_session.c b/usr/src/uts/common/fs/smbsrv/smb_session.c
index 205c21179b..2878df28e7 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_session.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_session.c
@@ -20,7 +20,7 @@
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2019 Nexenta Systems, Inc. All rights reserved.
*/
#include <sys/atomic.h>
@@ -72,8 +72,6 @@ static int smb_session_reader(smb_session_t *);
static int smb_session_xprt_puthdr(smb_session_t *,
uint8_t msg_type, uint32_t msg_len,
uint8_t *dst, size_t dstlen);
-static smb_tree_t *smb_session_get_tree(smb_session_t *, smb_tree_t *);
-static void smb_session_logoff(smb_session_t *);
static void smb_session_disconnect_trees(smb_session_t *);
static void smb_request_init_command_mbuf(smb_request_t *sr);
static void smb_session_genkey(smb_session_t *);
@@ -752,7 +750,22 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
smb_rwx_init(&session->s_lock);
- if (new_so != NULL) {
+ session->s_srqueue = &sv->sv_srqueue;
+ smb_server_get_cfg(sv, &session->s_cfg);
+
+ if (new_so == NULL) {
+ /*
+ * This call is creating the special "server" session,
+ * used for kshare export, oplock breaks, CA import.
+ * CA import creates temporary trees on this session
+ * and those should never get map/unmap up-calls, so
+ * force the map/unmap flags zero on this session.
+ * Set a "modern" dialect for CA import too, so
+ * pathname parse doesn't do OS/2 stuff, etc.
+ */
+ session->s_cfg.skc_execflags = 0;
+ session->dialect = session->s_cfg.skc_max_protocol;
+ } else {
if (family == AF_INET) {
slen = sizeof (sin);
(void) ksocket_getsockname(new_so,
@@ -794,8 +807,6 @@ smb_session_create(ksocket_t new_so, uint16_t port, smb_server_t *sv,
else
smb_server_inc_tcp_sess(sv);
}
- smb_server_get_cfg(sv, &session->s_cfg);
- session->s_srqueue = &sv->sv_srqueue;
/*
* The initial new request handler is special,
@@ -1006,117 +1017,35 @@ smb_session_lookup_tree(
}
/*
- * Find the first connected tree that matches the specified sharename.
- * If the specified tree is NULL the search starts from the beginning of
- * the user's tree list. If a tree is provided the search starts just
- * after that tree.
- */
-smb_tree_t *
-smb_session_lookup_share(
- smb_session_t *session,
- const char *sharename,
- smb_tree_t *tree)
-{
- SMB_SESSION_VALID(session);
- ASSERT(sharename);
-
- smb_llist_enter(&session->s_tree_list, RW_READER);
-
- if (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
- tree = smb_llist_next(&session->s_tree_list, tree);
- } else {
- tree = smb_llist_head(&session->s_tree_list);
- }
-
- while (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
- if (smb_strcasecmp(tree->t_sharename, sharename, 0) == 0) {
- if (smb_tree_hold(tree)) {
- smb_llist_exit(&session->s_tree_list);
- return (tree);
- }
- }
- tree = smb_llist_next(&session->s_tree_list, tree);
- }
-
- smb_llist_exit(&session->s_tree_list);
- return (NULL);
-}
-
-/*
- * Find the first connected tree that matches the specified volume name.
- * If the specified tree is NULL the search starts from the beginning of
- * the user's tree list. If a tree is provided the search starts just
- * after that tree.
- */
-smb_tree_t *
-smb_session_lookup_volume(
- smb_session_t *session,
- const char *name,
- smb_tree_t *tree)
-{
- SMB_SESSION_VALID(session);
- ASSERT(name);
-
- smb_llist_enter(&session->s_tree_list, RW_READER);
-
- if (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
- tree = smb_llist_next(&session->s_tree_list, tree);
- } else {
- tree = smb_llist_head(&session->s_tree_list);
- }
-
- while (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
-
- if (smb_strcasecmp(tree->t_volume, name, 0) == 0) {
- if (smb_tree_hold(tree)) {
- smb_llist_exit(&session->s_tree_list);
- return (tree);
- }
- }
-
- tree = smb_llist_next(&session->s_tree_list, tree);
- }
-
- smb_llist_exit(&session->s_tree_list);
- return (NULL);
-}
-
-/*
* Disconnect all trees that match the specified client process-id.
+ * Used by the SMB1 "process exit" request.
*/
void
smb_session_close_pid(
smb_session_t *session,
uint32_t pid)
{
+ smb_llist_t *tree_list = &session->s_tree_list;
smb_tree_t *tree;
- SMB_SESSION_VALID(session);
+ smb_llist_enter(tree_list, RW_READER);
- tree = smb_session_get_tree(session, NULL);
+ tree = smb_llist_head(tree_list);
while (tree) {
- smb_tree_t *next;
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
- smb_tree_close_pid(tree, pid);
- next = smb_session_get_tree(session, tree);
- smb_tree_release(tree);
- tree = next;
+ if (smb_tree_hold(tree)) {
+ smb_tree_close_pid(tree, pid);
+ smb_tree_release(tree);
+ }
+ tree = smb_llist_next(tree_list, tree);
}
+
+ smb_llist_exit(tree_list);
}
static void
-smb_session_tree_dtor(void *t)
+smb_session_tree_dtor(void *arg)
{
- smb_tree_t *tree = (smb_tree_t *)t;
+ smb_tree_t *tree = arg;
smb_tree_disconnect(tree, B_TRUE);
/* release the ref acquired during the traversal loop */
@@ -1167,84 +1096,76 @@ static void
smb_session_disconnect_trees(
smb_session_t *session)
{
- smb_tree_t *tree, *next_tree;
+ smb_llist_t *tree_list = &session->s_tree_list;
+ smb_tree_t *tree;
- SMB_SESSION_VALID(session);
+ smb_llist_enter(tree_list, RW_READER);
- tree = smb_session_get_tree(session, NULL);
+ tree = smb_llist_head(tree_list);
while (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
- smb_tree_disconnect(tree, B_TRUE);
- next_tree = smb_session_get_tree(session, tree);
- smb_tree_release(tree);
- tree = next_tree;
+ if (smb_tree_hold(tree)) {
+ smb_llist_post(tree_list, tree,
+ smb_session_tree_dtor);
+ }
+ tree = smb_llist_next(tree_list, tree);
}
+
+ /* drop the lock and flush the dtor queue */
+ smb_llist_exit(tree_list);
}
/*
- * Disconnect all trees that match the specified share name.
+ * Variant of smb_session_tree_dtor that also
+ * cancels requests using this tree.
*/
-void
-smb_session_disconnect_share(
- smb_session_t *session,
- const char *sharename)
+static void
+smb_session_tree_kill(void *arg)
{
- smb_tree_t *tree;
- smb_tree_t *next;
+ smb_tree_t *tree = arg;
- SMB_SESSION_VALID(session);
+ SMB_TREE_VALID(tree);
- tree = smb_session_lookup_share(session, sharename, NULL);
- while (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- ASSERT(tree->t_session == session);
- smb_tree_disconnect(tree, B_TRUE);
- smb_session_cancel_requests(session, tree, NULL);
- next = smb_session_lookup_share(session, sharename, tree);
- smb_tree_release(tree);
- tree = next;
- }
+ smb_tree_disconnect(tree, B_TRUE);
+ smb_session_cancel_requests(tree->t_session, tree, NULL);
+
+ /* release the ref acquired during the traversal loop */
+ smb_tree_release(tree);
}
/*
- * Get the next connected tree in the list. A reference is taken on
- * the tree, which can be released later with smb_tree_release().
- *
- * If the specified tree is NULL the search starts from the beginning of
- * the tree list. If a tree is provided the search starts just after
- * that tree.
- *
- * Returns NULL if there are no connected trees in the list.
+ * Disconnect all trees that match the specified share name,
+ * and kill requests using those trees.
*/
-static smb_tree_t *
-smb_session_get_tree(
+void
+smb_session_disconnect_share(
smb_session_t *session,
- smb_tree_t *tree)
+ const char *sharename)
{
- smb_llist_t *tree_list;
+ smb_llist_t *ll;
+ smb_tree_t *tree;
SMB_SESSION_VALID(session);
- tree_list = &session->s_tree_list;
- smb_llist_enter(tree_list, RW_READER);
+ ll = &session->s_tree_list;
+ smb_llist_enter(ll, RW_READER);
- if (tree) {
- ASSERT3U(tree->t_magic, ==, SMB_TREE_MAGIC);
- tree = smb_llist_next(tree_list, tree);
- } else {
- tree = smb_llist_head(tree_list);
- }
+ for (tree = smb_llist_head(ll);
+ tree != NULL;
+ tree = smb_llist_next(ll, tree)) {
- while (tree) {
- if (smb_tree_hold(tree))
- break;
+ SMB_TREE_VALID(tree);
+ ASSERT(tree->t_session == session);
- tree = smb_llist_next(tree_list, tree);
+ if (smb_strcasecmp(tree->t_sharename, sharename, 0) != 0)
+ continue;
+
+ if (smb_tree_hold(tree)) {
+ smb_llist_post(ll, tree,
+ smb_session_tree_kill);
+ }
}
- smb_llist_exit(tree_list);
- return (tree);
+ smb_llist_exit(ll);
}
/*
@@ -1255,7 +1176,7 @@ smb_session_get_tree(
* disconnect (SMB_SESSION_STATE_DISCONNECTED).
* If client-initiated, save durable handles.
*/
-static void
+void
smb_session_logoff(smb_session_t *session)
{
smb_llist_t *ulist;
@@ -1279,9 +1200,6 @@ top:
// smb_user_hold_internal(user);
user->u_refcnt++;
mutex_exit(&user->u_mutex);
- if (user->u_session->s_state ==
- SMB_SESSION_STATE_DISCONNECTED)
- user->preserve_opens = SMB2_DH_PRESERVE_ALL;
smb_user_logoff(user);
smb_user_release(user);
break;
diff --git a/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c b/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c
index 86ce24c0b0..7c4be2f56e 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_srv_oplock.c
@@ -346,6 +346,11 @@ smb_oplock_async_break(void *arg)
break;
}
+ if (sr->dh_nvl_dirty) {
+ sr->dh_nvl_dirty = B_FALSE;
+ smb2_dh_update_nvfile(sr);
+ }
+
sr->sr_state = SMB_REQ_STATE_COMPLETED;
smb_request_free(sr);
}
@@ -444,6 +449,10 @@ smb_oplock_send_brk(smb_request_t *sr)
if (lease != NULL)
lease->ls_state = NewLevel & CACHE_RWH;
ofile->f_oplock.og_state = NewLevel;
+
+ if (ofile->dh_persist) {
+ smb2_dh_update_oplock(sr, ofile);
+ }
}
/*
@@ -583,6 +592,10 @@ smb_oplock_send_brk(smb_request_t *sr)
if (lease != NULL) {
lease->ls_state = NewLevel & CACHE_RWH;
}
+
+ if (ofile->dh_persist) {
+ smb2_dh_update_oplock(sr, ofile);
+ }
}
/*
diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree.c b/usr/src/uts/common/fs/smbsrv/smb_tree.c
index 5020dec794..aedacf2123 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_tree.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_tree.c
@@ -184,8 +184,6 @@ uint32_t smb_tree_connect_core(smb_request_t *);
uint32_t smb_tree_connect_disk(smb_request_t *, smb_arg_tcon_t *);
uint32_t smb_tree_connect_printq(smb_request_t *, smb_arg_tcon_t *);
uint32_t smb_tree_connect_ipc(smb_request_t *, smb_arg_tcon_t *);
-static smb_tree_t *smb_tree_alloc(smb_request_t *, const smb_kshare_t *,
- smb_node_t *, uint32_t, uint32_t);
static void smb_tree_dealloc(void *);
static boolean_t smb_tree_is_connected_locked(smb_tree_t *);
static char *smb_tree_get_sharename(char *);
@@ -193,9 +191,7 @@ static int smb_tree_getattr(const smb_kshare_t *, smb_node_t *, smb_tree_t *);
static void smb_tree_get_volname(vfs_t *, smb_tree_t *);
static void smb_tree_get_flags(const smb_kshare_t *, vfs_t *, smb_tree_t *);
static void smb_tree_log(smb_request_t *, const char *, const char *, ...);
-static void smb_tree_close_odirs(smb_tree_t *, uint16_t);
-static smb_ofile_t *smb_tree_get_ofile(smb_tree_t *, smb_ofile_t *);
-static smb_odir_t *smb_tree_get_odir(smb_tree_t *, smb_odir_t *);
+static void smb_tree_close_odirs(smb_tree_t *, uint32_t);
static void smb_tree_set_execinfo(smb_tree_t *, smb_shr_execinfo_t *, int);
static int smb_tree_enum_private(smb_tree_t *, smb_svcenum_t *);
static int smb_tree_netinfo_encode(smb_tree_t *, uint8_t *, size_t, uint32_t *);
@@ -303,10 +299,13 @@ out:
/*
* Disconnect a tree.
+ *
+ * The "do_exec" arg is obsolete and ignored.
*/
void
smb_tree_disconnect(smb_tree_t *tree, boolean_t do_exec)
{
+ _NOTE(ARGUNUSED(do_exec))
smb_shr_execinfo_t execinfo;
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
@@ -314,34 +313,27 @@ smb_tree_disconnect(smb_tree_t *tree, boolean_t do_exec)
mutex_enter(&tree->t_mutex);
ASSERT(tree->t_refcnt);
- if (smb_tree_is_connected_locked(tree)) {
- /*
- * Indicate that the disconnect process has started.
- */
- tree->t_state = SMB_TREE_STATE_DISCONNECTING;
+ if (!smb_tree_is_connected_locked(tree)) {
mutex_exit(&tree->t_mutex);
-
- if (do_exec) {
- /*
- * The files opened under this tree are closed.
- */
- smb_ofile_close_all(tree, 0);
- /*
- * The directories opened under this tree are closed.
- */
- smb_tree_close_odirs(tree, 0);
- }
-
- mutex_enter(&tree->t_mutex);
- tree->t_state = SMB_TREE_STATE_DISCONNECTED;
- smb_server_dec_trees(tree->t_server);
+ return;
}
+ /*
+ * Indicate that the disconnect process has started.
+ */
+ tree->t_state = SMB_TREE_STATE_DISCONNECTING;
mutex_exit(&tree->t_mutex);
- if (do_exec && (tree->t_state == SMB_TREE_STATE_DISCONNECTED) &&
- (tree->t_execflags & SMB_EXEC_UNMAP)) {
+ /*
+ * The files opened under this tree are closed.
+ */
+ smb_ofile_close_all(tree, 0);
+ /*
+ * The directories opened under this tree are closed.
+ */
+ smb_tree_close_odirs(tree, 0);
+ if ((tree->t_execflags & SMB_EXEC_UNMAP) != 0) {
smb_tree_set_execinfo(tree, &execinfo, SMB_EXEC_UNMAP);
(void) smb_kshare_exec(tree->t_server, &execinfo);
}
@@ -408,7 +400,7 @@ smb_tree_release(
tree->t_refcnt--;
switch (tree->t_state) {
- case SMB_TREE_STATE_DISCONNECTED:
+ case SMB_TREE_STATE_DISCONNECTING:
if (tree->t_refcnt == 0) {
smb_session_t *ssn = tree->t_session;
tree->t_state = SMB_TREE_STATE_DISCONNECTED;
@@ -417,7 +409,6 @@ smb_tree_release(
}
break;
case SMB_TREE_STATE_CONNECTED:
- case SMB_TREE_STATE_DISCONNECTING:
break;
default:
ASSERT(0);
@@ -463,31 +454,29 @@ smb_tree_has_feature(smb_tree_t *tree, uint32_t flags)
int
smb_tree_enum(smb_tree_t *tree, smb_svcenum_t *svcenum)
{
+ smb_llist_t *of_list;
smb_ofile_t *of;
- smb_ofile_t *next;
int rc = 0;
- ASSERT(tree);
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
-
if (svcenum->se_type == SMB_SVCENUM_TYPE_TREE)
return (smb_tree_enum_private(tree, svcenum));
- of = smb_tree_get_ofile(tree, NULL);
- while (of) {
- ASSERT(of->f_tree == tree);
+ of_list = &tree->t_ofile_list;
+ smb_llist_enter(of_list, RW_READER);
- rc = smb_ofile_enum(of, svcenum);
- if (rc != 0) {
+ of = smb_llist_head(of_list);
+ while (of) {
+ if (smb_ofile_hold(of)) {
+ rc = smb_ofile_enum(of, svcenum);
smb_ofile_release(of);
- break;
}
-
- next = smb_tree_get_ofile(tree, of);
- smb_ofile_release(of);
- of = next;
+ if (rc != 0)
+ break;
+ of = smb_llist_next(of_list, of);
}
+ smb_llist_exit(of_list);
+
return (rc);
}
@@ -662,6 +651,9 @@ smb_tree_chkaccess(smb_request_t *sr, smb_kshare_t *shr, vnode_t *vp)
return (access);
}
+/* How long should tree connect wait for DH import to complete? */
+int smb_tcon_import_wait = 20; /* sec. */
+
/*
* Connect a share for use with files and directories.
*/
@@ -671,16 +663,14 @@ smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon)
char *sharename = tcon->path;
const char *any = "?????";
smb_user_t *user = sr->uid_user;
- smb_node_t *dnode = NULL;
smb_node_t *snode = NULL;
smb_kshare_t *si = tcon->si;
char *service = tcon->service;
- char last_component[MAXNAMELEN];
smb_tree_t *tree;
- cred_t *kcr;
int rc;
uint32_t access;
smb_shr_execinfo_t execinfo;
+ clock_t time;
ASSERT(user);
ASSERT(user->u_cred);
@@ -694,34 +684,34 @@ smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon)
/*
* Check that the shared directory exists.
- * Client might not have access to the path _leading_ to the share,
- * so we use "kcred" to get to the share root.
*/
- kcr = zone_kcred();
- rc = smb_pathname_reduce(sr, kcr, si->shr_path, 0, 0, &dnode,
- last_component);
- if (rc == 0) {
- rc = smb_fsop_lookup(sr, kcr, SMB_FOLLOW_LINKS,
- sr->sr_server->si_root_smb_node, dnode, last_component,
- &snode);
-
- smb_node_release(dnode);
- }
-
- if (rc) {
- if (snode)
- smb_node_release(snode);
-
+ snode = si->shr_root_node;
+ if (snode == NULL) {
smb_tree_log(sr, sharename, "bad path: %s", si->shr_path);
return (NT_STATUS_BAD_NETWORK_NAME);
}
if ((access = smb_tree_chkaccess(sr, si, snode->vp)) == 0) {
- smb_node_release(snode);
return (NT_STATUS_ACCESS_DENIED);
}
/*
+ * Wait for DH import of persistent handles to finish.
+ * If we timeout, it's not clear what status to return,
+ * but as the share is not really available yet, let's
+ * return the status for "no such share".
+ */
+ time = SEC_TO_TICK(smb_tcon_import_wait) + ddi_get_lbolt();
+ mutex_enter(&si->shr_mutex);
+ while (si->shr_import_busy != NULL) {
+ if (cv_timedwait(&si->shr_cv, &si->shr_mutex, time) < 0) {
+ mutex_exit(&si->shr_mutex);
+ return (NT_STATUS_BAD_NETWORK_NAME);
+ }
+ }
+ mutex_exit(&si->shr_mutex);
+
+ /*
* Set up the OptionalSupport for this share.
*/
tcon->optional_support = SMB_SUPPORT_SEARCH_BITS;
@@ -758,8 +748,6 @@ smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon)
tree = smb_tree_alloc(sr, si, snode, access, sr->sr_cfg->skc_execflags);
- smb_node_release(snode);
-
if (tree == NULL)
return (NT_STATUS_INSUFF_SERVER_RESOURCES);
@@ -769,7 +757,17 @@ smb_tree_connect_disk(smb_request_t *sr, smb_arg_tcon_t *tcon)
rc = smb_kshare_exec(tree->t_server, &execinfo);
if ((rc != 0) && (tree->t_execflags & SMB_EXEC_TERM)) {
- smb_tree_disconnect(tree, B_FALSE);
+ /*
+ * Inline parts of: smb_tree_disconnect()
+ * Not using smb_tree_disconnect() for cleanup
+ * here because: we don't want an exec up-call,
+ * and there can't be any opens as we never
+ * returned this TID to the client.
+ */
+ mutex_enter(&tree->t_mutex);
+ tree->t_state = SMB_TREE_STATE_DISCONNECTING;
+ mutex_exit(&tree->t_mutex);
+
smb_tree_release(tree);
return (NT_STATUS_ACCESS_DENIED);
}
@@ -901,7 +899,7 @@ smb_tree_connect_ipc(smb_request_t *sr, smb_arg_tcon_t *tcon)
/*
* Allocate a tree.
*/
-static smb_tree_t *
+smb_tree_t *
smb_tree_alloc(smb_request_t *sr, const smb_kshare_t *si,
smb_node_t *snode, uint32_t access, uint32_t execflags)
{
@@ -1001,6 +999,8 @@ smb_tree_dealloc(void *arg)
ASSERT(tree->t_state == SMB_TREE_STATE_DISCONNECTED);
ASSERT(tree->t_refcnt == 0);
+ smb_server_dec_trees(tree->t_server);
+
session = tree->t_session;
smb_llist_enter(&session->s_tree_list, RW_WRITER);
smb_llist_remove(&session->s_tree_list, tree);
@@ -1199,6 +1199,9 @@ smb_tree_get_flags(const smb_kshare_t *si, vfs_t *vfsp, smb_tree_t *tree)
if (si->shr_flags & SMB_SHRF_ABE)
flags |= SMB_TREE_ABE;
+ if (si->shr_flags & SMB_SHRF_CA)
+ flags |= SMB_TREE_CA;
+
if (si->shr_flags & SMB_SHRF_FSO)
flags |= SMB_TREE_FORCE_L2_OPLOCK;
@@ -1361,83 +1364,6 @@ smb_tree_is_connected(smb_tree_t *tree)
}
/*
- * Get the next open ofile in the list. A reference is taken on
- * the ofile, which can be released later with smb_ofile_release().
- *
- * If the specified ofile is NULL, search from the beginning of the
- * list. Otherwise, the search starts just after that ofile.
- *
- * Returns NULL if there are no open files in the list.
- */
-static smb_ofile_t *
-smb_tree_get_ofile(smb_tree_t *tree, smb_ofile_t *of)
-{
- smb_llist_t *ofile_list;
-
- ASSERT(tree);
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
-
- ofile_list = &tree->t_ofile_list;
- smb_llist_enter(ofile_list, RW_READER);
-
- if (of) {
- ASSERT(of->f_magic == SMB_OFILE_MAGIC);
- of = smb_llist_next(ofile_list, of);
- } else {
- of = smb_llist_head(ofile_list);
- }
-
- while (of) {
- if (smb_ofile_hold(of))
- break;
-
- of = smb_llist_next(ofile_list, of);
- }
-
- smb_llist_exit(ofile_list);
- return (of);
-}
-
-/*
- * smb_tree_get_odir
- *
- * Find the next odir in the tree's list of odirs, and obtain a
- * hold on it.
- * If the specified odir is NULL the search starts at the beginning
- * of the tree's odir list, otherwise the search starts after the
- * specified odir.
- */
-static smb_odir_t *
-smb_tree_get_odir(smb_tree_t *tree, smb_odir_t *od)
-{
- smb_llist_t *od_list;
-
- ASSERT(tree);
- ASSERT(tree->t_magic == SMB_TREE_MAGIC);
-
- od_list = &tree->t_odir_list;
- smb_llist_enter(od_list, RW_READER);
-
- if (od) {
- ASSERT(od->d_magic == SMB_ODIR_MAGIC);
- od = smb_llist_next(od_list, od);
- } else {
- od = smb_llist_head(od_list);
- }
-
- while (od) {
- ASSERT(od->d_magic == SMB_ODIR_MAGIC);
-
- if (smb_odir_hold(od))
- break;
- od = smb_llist_next(od_list, od);
- }
-
- smb_llist_exit(od_list);
- return (od);
-}
-
-/*
* smb_tree_close_odirs
*
* Close all open odirs in the tree's list which were opened by
@@ -1445,25 +1371,34 @@ smb_tree_get_odir(smb_tree_t *tree, smb_odir_t *od)
* If pid is zero, close all open odirs in the tree's list.
*/
static void
-smb_tree_close_odirs(smb_tree_t *tree, uint16_t pid)
+smb_tree_close_odirs(smb_tree_t *tree, uint32_t pid)
{
- smb_odir_t *od, *next_od;
+ smb_llist_t *od_list;
+ smb_odir_t *od;
ASSERT(tree);
ASSERT(tree->t_magic == SMB_TREE_MAGIC);
- od = smb_tree_get_odir(tree, NULL);
- while (od) {
+ od_list = &tree->t_odir_list;
+ smb_llist_enter(od_list, RW_READER);
+
+ for (od = smb_llist_head(od_list);
+ od != NULL;
+ od = smb_llist_next(od_list, od)) {
+
ASSERT(od->d_magic == SMB_ODIR_MAGIC);
ASSERT(od->d_tree == tree);
- next_od = smb_tree_get_odir(tree, od);
- if ((pid == 0) || (od->d_opened_by_pid == pid))
- smb_odir_close(od);
- smb_odir_release(od);
+ if (pid != 0 && od->d_opened_by_pid != pid)
+ continue;
- od = next_od;
+ if (smb_odir_hold(od)) {
+ smb_odir_close(od);
+ smb_odir_release(od);
+ }
}
+
+ smb_llist_exit(od_list);
}
static void
diff --git a/usr/src/uts/common/fs/smbsrv/smb_user.c b/usr/src/uts/common/fs/smbsrv/smb_user.c
index 0bfceb4ff4..74bb502c56 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_user.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_user.c
@@ -303,7 +303,6 @@ smb_user_logon(
* we always have an auth. socket to close.
*/
authsock = user->u_authsock;
- ASSERT(authsock != NULL);
user->u_authsock = NULL;
tmo = user->u_auth_tmo;
user->u_auth_tmo = NULL;
@@ -325,7 +324,8 @@ smb_user_logon(
(void) untimeout(tmo);
/* This close can block, so not under the mutex. */
- smb_authsock_close(user, authsock);
+ if (authsock != NULL)
+ smb_authsock_close(user, authsock);
return (0);
}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_vfs.c b/usr/src/uts/common/fs/smbsrv/smb_vfs.c
deleted file mode 100644
index ae631e4ffa..0000000000
--- a/usr/src/uts/common/fs/smbsrv/smb_vfs.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
- */
-
-#include <sys/vfs.h>
-#include <smbsrv/smb_ktypes.h>
-#include <smbsrv/smb_kproto.h>
-
-static smb_vfs_t *smb_vfs_find(smb_export_t *, vfs_t *);
-static void smb_vfs_destroy(smb_vfs_t *);
-
-/*
- * If a hold on the specified VFS has already been taken
- * then only increment the reference count of the corresponding
- * smb_vfs_t structure. If no smb_vfs_t structure has been created
- * yet for the specified VFS then create one and take a hold on
- * the VFS.
- */
-int
-smb_vfs_hold(smb_export_t *se, vfs_t *vfsp)
-{
- smb_vfs_t *smb_vfs;
- vnode_t *rootvp;
- int rc;
-
- if (se == NULL || vfsp == NULL)
- return (EINVAL);
-
- smb_llist_enter(&se->e_vfs_list, RW_WRITER);
-
- if ((smb_vfs = smb_vfs_find(se, vfsp)) != NULL) {
- smb_vfs->sv_refcnt++;
- DTRACE_PROBE1(smb_vfs_hold_hit, smb_vfs_t *, smb_vfs);
- smb_llist_exit(&se->e_vfs_list);
- return (0);
- }
-
- if ((rc = VFS_ROOT(vfsp, &rootvp)) != 0) {
- smb_llist_exit(&se->e_vfs_list);
- return (rc);
- }
-
- smb_vfs = kmem_cache_alloc(smb_kshare_cache_vfs, KM_SLEEP);
-
- bzero(smb_vfs, sizeof (smb_vfs_t));
-
- smb_vfs->sv_magic = SMB_VFS_MAGIC;
- smb_vfs->sv_refcnt = 1;
- smb_vfs->sv_vfsp = vfsp;
- /*
- * We have a hold on the root vnode of the file system
- * from the VFS_ROOT call above.
- */
- smb_vfs->sv_rootvp = rootvp;
-
- smb_llist_insert_head(&se->e_vfs_list, smb_vfs);
- DTRACE_PROBE1(smb_vfs_hold_miss, smb_vfs_t *, smb_vfs);
- smb_llist_exit(&se->e_vfs_list);
-
- return (0);
-}
-
-/*
- * smb_vfs_rele
- *
- * Decrements the reference count of the fs passed in. If the reference count
- * drops to zero the smb_vfs_t structure associated with the fs is freed.
- */
-void
-smb_vfs_rele(smb_export_t *se, vfs_t *vfsp)
-{
- smb_vfs_t *smb_vfs;
-
- ASSERT(vfsp);
-
- smb_llist_enter(&se->e_vfs_list, RW_WRITER);
- smb_vfs = smb_vfs_find(se, vfsp);
- DTRACE_PROBE1(smb_vfs_release, smb_vfs_t *, smb_vfs);
- if (smb_vfs) {
- ASSERT(smb_vfs->sv_refcnt);
- if (--smb_vfs->sv_refcnt == 0) {
- smb_llist_remove(&se->e_vfs_list, smb_vfs);
- smb_llist_exit(&se->e_vfs_list);
- smb_vfs_destroy(smb_vfs);
- return;
- }
- }
- smb_llist_exit(&se->e_vfs_list);
-}
-
-/*
- * smb_vfs_rele_all()
- *
- * Release all holds on root vnodes of file systems which were taken
- * due to the existence of at least one enabled share on the file system.
- * Called at driver close time.
- */
-void
-smb_vfs_rele_all(smb_export_t *se)
-{
- smb_vfs_t *smb_vfs;
-
- smb_llist_enter(&se->e_vfs_list, RW_WRITER);
- while ((smb_vfs = smb_llist_head(&se->e_vfs_list)) != NULL) {
-
- ASSERT(smb_vfs->sv_magic == SMB_VFS_MAGIC);
- DTRACE_PROBE1(smb_vfs_rele_all_hit, smb_vfs_t *, smb_vfs);
- smb_llist_remove(&se->e_vfs_list, smb_vfs);
- smb_vfs_destroy(smb_vfs);
- }
- smb_llist_exit(&se->e_vfs_list);
-}
-
-/*
- * Goes through the list of smb_vfs_t structure and returns the one matching
- * the vnode passed in. If no match is found a NULL pointer is returned.
- *
- * The list of smb_vfs_t structures has to have been entered prior calling
- * this function.
- */
-static smb_vfs_t *
-smb_vfs_find(smb_export_t *se, vfs_t *vfsp)
-{
- smb_vfs_t *smb_vfs;
-
- smb_vfs = smb_llist_head(&se->e_vfs_list);
- while (smb_vfs) {
- ASSERT(smb_vfs->sv_magic == SMB_VFS_MAGIC);
- if (smb_vfs->sv_vfsp == vfsp)
- return (smb_vfs);
- smb_vfs = smb_llist_next(&se->e_vfs_list, smb_vfs);
- }
-
- return (NULL);
-}
-
-static void
-smb_vfs_destroy(smb_vfs_t *smb_vfs)
-{
- VN_RELE(smb_vfs->sv_rootvp);
- smb_vfs->sv_magic = (uint32_t)~SMB_VFS_MAGIC;
- kmem_cache_free(smb_kshare_cache_vfs, smb_vfs);
-}
diff --git a/usr/src/uts/common/fs/smbsrv/smb_vops.c b/usr/src/uts/common/fs/smbsrv/smb_vops.c
index d2f0fd7085..4b0f99839f 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_vops.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_vops.c
@@ -608,8 +608,14 @@ smb_vop_lookup(
char *np = name;
char namebuf[MAXNAMELEN];
- if (*name == '\0')
- return (EINVAL);
+ if (*name == '\0') {
+ /*
+ * This happens creating named streams at the share root.
+ */
+ VN_HOLD(dvp);
+ *vpp = dvp;
+ return (0);
+ }
ASSERT(vpp);
*vpp = NULL;