diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb2_fsctl_odx.c | 2 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb2_fsctl_sparse.c | 5 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb_server.c | 76 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb_tree.c | 2 | ||||
-rw-r--r-- | usr/src/uts/common/fs/smbsrv/smb_user.c | 49 | ||||
-rw-r--r-- | usr/src/uts/common/smbsrv/smb_kproto.h | 3 | ||||
-rw-r--r-- | usr/src/uts/common/smbsrv/smb_ktypes.h | 2 |
7 files changed, 123 insertions, 16 deletions
diff --git a/usr/src/uts/common/fs/smbsrv/smb2_fsctl_odx.c b/usr/src/uts/common/fs/smbsrv/smb2_fsctl_odx.c index fe748bbd62..6a9040a5db 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_fsctl_odx.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_fsctl_odx.c @@ -308,6 +308,8 @@ smb2_fsctl_odx_read(smb_request_t *sr, smb_fsctl_t *fsctl) */ data = in_file_off; tok_type = STORAGE_OFFLOAD_TOKEN_TYPE_NATIVE1; + if (sr->sr_state != SMB_REQ_STATE_ACTIVE) + return (NT_STATUS_SUCCESS); rc = smb_fsop_next_alloc_range(ofile->f_cr, ofile->f_node, &data, &hole); switch (rc) { diff --git a/usr/src/uts/common/fs/smbsrv/smb2_fsctl_sparse.c b/usr/src/uts/common/fs/smbsrv/smb2_fsctl_sparse.c index 90bb254670..91f13a150e 100644 --- a/usr/src/uts/common/fs/smbsrv/smb2_fsctl_sparse.c +++ b/usr/src/uts/common/fs/smbsrv/smb2_fsctl_sparse.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2018 Nexenta Systems, Inc. All rights reserved. + * Copyright 2020 Tintri by DDN, Inc. All rights reserved. */ /* @@ -306,6 +306,9 @@ smb2_sparse_copy( while (*residp > 0) { + if (sr->sr_state != SMB_REQ_STATE_ACTIVE) + break; + data = src_off; rc = smb_fsop_next_alloc_range(src_ofile->f_cr, src_ofile->f_node, &data, &hole); diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c index 7f86fd478d..63ca6343bc 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_server.c +++ b/usr/src/uts/common/fs/smbsrv/smb_server.c @@ -1998,6 +1998,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) @@ -2005,6 +2009,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; @@ -2016,38 +2023,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 */ diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree.c b/usr/src/uts/common/fs/smbsrv/smb_tree.c index 45f381ffb1..2aadc3bf38 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_tree.c +++ b/usr/src/uts/common/fs/smbsrv/smb_tree.c @@ -963,6 +963,7 @@ smb_tree_alloc(smb_request_t *sr, const smb_kshare_t *si, /* grab a ref for tree->t_owner */ smb_user_hold_internal(sr->uid_user); + smb_user_inc_trees(sr->uid_user); tree->t_owner = sr->uid_user; /* if FS is readonly, enforce that here */ @@ -1031,6 +1032,7 @@ smb_tree_dealloc(void *arg) smb_idpool_destructor(&tree->t_odid_pool); SMB_USER_VALID(tree->t_owner); + smb_user_dec_trees(tree->t_owner); smb_user_release(tree->t_owner); kmem_cache_free(smb_cache_tree, tree); diff --git a/usr/src/uts/common/fs/smbsrv/smb_user.c b/usr/src/uts/common/fs/smbsrv/smb_user.c index 8934a213eb..a5dc57315f 100644 --- a/usr/src/uts/common/fs/smbsrv/smb_user.c +++ b/usr/src/uts/common/fs/smbsrv/smb_user.c @@ -662,6 +662,55 @@ smb_user_enum(smb_user_t *user, smb_svcenum_t *svcenum) return (rc); } +/* + * Count references by trees this user owns, + * and allow waiting for them to go away. + */ +void +smb_user_inc_trees(smb_user_t *user) +{ + mutex_enter(&user->u_mutex); + user->u_owned_tree_cnt++; + mutex_exit(&user->u_mutex); +} + +void +smb_user_dec_trees(smb_user_t *user) +{ + mutex_enter(&user->u_mutex); + user->u_owned_tree_cnt--; + if (user->u_owned_tree_cnt == 0) + cv_broadcast(&user->u_owned_tree_cv); + mutex_exit(&user->u_mutex); +} + +int smb_user_wait_tree_tmo = 30; + +/* + * Wait (up to 30 sec.) for trees to go away. + * Should happen in less than a second. + */ +void +smb_user_wait_trees(smb_user_t *user) +{ + clock_t time; + + time = SEC_TO_TICK(smb_user_wait_tree_tmo) + ddi_get_lbolt(); + mutex_enter(&user->u_mutex); + while (user->u_owned_tree_cnt != 0) { + if (cv_timedwait(&user->u_owned_tree_cv, + &user->u_mutex, time) < 0) + break; + } + mutex_exit(&user->u_mutex); +#ifdef DEBUG + if (user->u_owned_tree_cnt != 0) { + cmn_err(CE_NOTE, "smb_user_wait_trees failed"); + debug_enter("smb_user_wait_trees debug"); + } +#endif +} + /* *************************** Static Functions ***************************** */ /* diff --git a/usr/src/uts/common/smbsrv/smb_kproto.h b/usr/src/uts/common/smbsrv/smb_kproto.h index d830739802..f61b9aa9a2 100644 --- a/usr/src/uts/common/smbsrv/smb_kproto.h +++ b/usr/src/uts/common/smbsrv/smb_kproto.h @@ -742,6 +742,9 @@ cred_t *smb_kcred_create(void); void smb_user_setcred(smb_user_t *, cred_t *, uint32_t); boolean_t smb_is_same_user(cred_t *, cred_t *); boolean_t smb_user_has_security_priv(smb_user_t *, cred_t *); +void smb_user_inc_trees(smb_user_t *); +void smb_user_dec_trees(smb_user_t *); +void smb_user_wait_trees(smb_user_t *); /* * SMB tree functions (file smb_tree.c) diff --git a/usr/src/uts/common/smbsrv/smb_ktypes.h b/usr/src/uts/common/smbsrv/smb_ktypes.h index e105d297e3..89b057ff91 100644 --- a/usr/src/uts/common/smbsrv/smb_ktypes.h +++ b/usr/src/uts/common/smbsrv/smb_ktypes.h @@ -1077,6 +1077,8 @@ typedef struct smb_user { uint32_t u_privileges; uint16_t u_uid; /* unique per-session */ uint32_t u_audit_sid; + uint32_t u_owned_tree_cnt; + kcondvar_t u_owned_tree_cv; uint32_t u_sign_flags; struct smb_key u_sign_key; /* SMB2 signing */ |