summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/fs/smbsrv/smb_server.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/fs/smbsrv/smb_server.c')
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_server.c77
1 files changed, 62 insertions, 15 deletions
diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c
index 5988f121eb..af568986c9 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_server.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_server.c
@@ -1532,6 +1532,7 @@ smb_server_shutdown(smb_server_t *sv)
sv->sv_rootuser = NULL;
}
if (sv->sv_session != NULL) {
+ smb_session_cancel_requests(sv->sv_session, NULL, NULL);
smb_slist_wait_for_empty(&sv->sv_session->s_req_list);
/* Just in case import left users and trees */
@@ -2013,6 +2014,10 @@ smb_server_fclose(smb_server_t *sv, uint32_t uniqid)
* so it can force a logoff that we haven't noticed yet.
* This is not called frequently, so we just walk the list of
* connections searching for the user.
+ *
+ * Note that this must wait for any durable handles (ofiles)
+ * owned by this user to become "orphaned", so that a reconnect
+ * that may immediately follow can find and use such ofiles.
*/
void
smb_server_logoff_ssnid(smb_request_t *sr, uint64_t ssnid)
@@ -2020,6 +2025,9 @@ smb_server_logoff_ssnid(smb_request_t *sr, uint64_t ssnid)
smb_server_t *sv = sr->sr_server;
smb_llist_t *sess_list;
smb_session_t *sess;
+ smb_user_t *user = NULL;
+
+ SMB_SERVER_VALID(sv);
if (sv->sv_state != SMB_SERVER_STATE_RUNNING)
return;
@@ -2031,38 +2039,77 @@ smb_server_logoff_ssnid(smb_request_t *sr, uint64_t ssnid)
sess != NULL;
sess = smb_llist_next(sess_list, sess)) {
- smb_user_t *user;
-
SMB_SESSION_VALID(sess);
if (sess->dialect < SMB_VERS_2_BASE)
continue;
- if (sess->s_state != SMB_SESSION_STATE_NEGOTIATED)
+ switch (sess->s_state) {
+ case SMB_SESSION_STATE_NEGOTIATED:
+ case SMB_SESSION_STATE_TERMINATED:
+ case SMB_SESSION_STATE_DISCONNECTED:
+ break;
+ default:
continue;
+ }
- user = smb_session_lookup_ssnid(sess, ssnid);
- if (user == NULL)
- continue;
+ /*
+ * Normal situation is to find a LOGGED_ON user.
+ */
+ user = smb_session_lookup_uid_st(sess, ssnid, 0,
+ SMB_USER_STATE_LOGGED_ON);
+ if (user != NULL) {
+
+ if (smb_is_same_user(user->u_cred, sr->user_cr)) {
+ /* Treat this as if we lost the connection */
+ user->preserve_opens = SMB2_DH_PRESERVE_SOME;
+ smb_user_logoff(user);
+ break;
+ }
+ smb_user_release(user);
+ user = NULL;
+ }
- if (!smb_is_same_user(user->u_cred, sr->user_cr)) {
+ /*
+ * If we raced with disconnect, may find LOGGING_OFF,
+ * in which case we want to just wait for it.
+ */
+ user = smb_session_lookup_uid_st(sess, ssnid, 0,
+ SMB_USER_STATE_LOGGING_OFF);
+ if (user != NULL) {
+ if (smb_is_same_user(user->u_cred, sr->user_cr))
+ break;
smb_user_release(user);
- continue;
+ user = NULL;
}
+ }
- /* Treat this as if we lost the connection */
- user->preserve_opens = SMB2_DH_PRESERVE_SOME;
- smb_user_logoff(user);
- smb_user_release(user);
+ smb_llist_exit(sess_list);
+ if (user != NULL) {
/*
- * The above may have left work on the delete queues
+ * Wait for durable handles to be orphaned.
+ * Note: not holding the sess list rwlock.
*/
+ smb_user_wait_trees(user);
+
+ /*
+ * Could be doing the last release on a user below,
+ * which can leave work on the delete queues for
+ * s_user_list or s_tree_list so flush those.
+ * Must hold the session list after the user release
+ * so that the session can't go away while we flush.
+ */
+ smb_llist_enter(sess_list, RW_READER);
+
+ sess = user->u_session;
+ smb_user_release(user);
+
smb_llist_flush(&sess->s_tree_list);
smb_llist_flush(&sess->s_user_list);
- }
- smb_llist_exit(sess_list);
+ smb_llist_exit(sess_list);
+ }
}
/* See also: libsmb smb_kmod_setcfg */