Print this page
NEX-19373 SMB2 file ID re-use can confuse clients
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-16519 Panic while running IOmeter to a pool through an SMB share
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-5599 SMB needs a pointer-based hash table for durable handles
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-4458 Incorrect directory listing response for non-UNICODE clients
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3787 Sync. up SMB server with: Merge with illumos-gate 12380e1e
NEX-3611 CLONE NEX-3550 Replace smb2_enable with max_protocol
Reviewed by: Yuri Pankov <Yuri.Pankov@nexenta.com>
NEX-2894 Using a date that is outside of the UNIX epoch fails on CIFS
NEX-2781 SMB2 credit handling needs work
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
SMB-50 User-mode SMB server
 Includes work by these authors:
 Thomas Keiser <thomas.keiser@nexenta.com>
 Albert Lee <trisk@nexenta.com>
SUP-694 panic on bad mutex in smb_event_wait() - nits
SUP-694 panic on bad mutex in smb_event_wait()
SMB-65 SMB server in non-global zones (data structure changes)
Many things move to the smb_server_t object, and
many functions gain an sv arg (which server).
SMB-65 SMB server in non-global zones (kmem_caches)
common kmem_cache instances across zones
separate GZ-only init from NGZ init
SMB-64 smbsrv workers run at excessively high priority
SMB-48 Panic with smbtorture raw.scan-eamax
re #6854 FindFirstFile,FindFirstFileEx,... are not working correctly on Nexenta CIFS-shares

*** 19,29 **** * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> #include <sys/types.h> #include <sys/tzfile.h> --- 19,29 ---- * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #include <sys/param.h> #include <sys/types.h> #include <sys/tzfile.h>
*** 38,47 **** --- 38,48 ---- #include <smbsrv/smb_vops.h> #include <smbsrv/smb_idmap.h> #include <sys/sid.h> #include <sys/priv_names.h> + #include <sys/bitmap.h> static kmem_cache_t *smb_dtor_cache = NULL; static boolean_t smb_avl_hold(smb_avl_t *); static void smb_avl_rele(smb_avl_t *);
*** 68,93 **** static const int days_in_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int smb_ascii_or_unicode_strlen(struct smb_request *sr, char *str) { if (sr->session->dialect >= SMB_VERS_2_BASE || (sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0) return (smb_wcequiv_strlen(str)); ! return (strlen(str)); } int smb_ascii_or_unicode_strlen_null(struct smb_request *sr, char *str) { if (sr->session->dialect >= SMB_VERS_2_BASE || (sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0) return (smb_wcequiv_strlen(str) + 2); ! return (strlen(str) + 1); } int smb_ascii_or_unicode_null_len(struct smb_request *sr) { --- 69,108 ---- static const int days_in_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + /* + * Given a UTF-8 string (our internal form everywhere) + * return either the Unicode (UTF-16) length in bytes, + * or the OEM length in bytes. Which we return is + * determined by whether the client supports Unicode. + * This length does NOT include the null. + */ int smb_ascii_or_unicode_strlen(struct smb_request *sr, char *str) { if (sr->session->dialect >= SMB_VERS_2_BASE || (sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0) return (smb_wcequiv_strlen(str)); ! return (smb_sbequiv_strlen(str)); } + /* + * Given a UTF-8 string (our internal form everywhere) + * return either the Unicode (UTF-16) length in bytes, + * or the OEM length in bytes. Which we return is + * determined by whether the client supports Unicode. + * This length DOES include the null. + */ int smb_ascii_or_unicode_strlen_null(struct smb_request *sr, char *str) { if (sr->session->dialect >= SMB_VERS_2_BASE || (sr->smb_flg2 & SMB_FLAGS2_UNICODE) != 0) return (smb_wcequiv_strlen(str) + 2); ! return (smb_sbequiv_strlen(str) + 1); } int smb_ascii_or_unicode_null_len(struct smb_request *sr) {
*** 316,327 **** continue; } pool->id_pool[pool->id_idx] |= bit; *id = (uint16_t)(pool->id_idx * 8 + (uint32_t)bit_idx); pool->id_free_counter--; ! pool->id_bit = bit; ! pool->id_bit_idx = bit_idx; mutex_exit(&pool->id_mutex); return (0); } pool->id_bit = 1; pool->id_bit_idx = 0; --- 331,356 ---- continue; } pool->id_pool[pool->id_idx] |= bit; *id = (uint16_t)(pool->id_idx * 8 + (uint32_t)bit_idx); pool->id_free_counter--; ! /* ! * Leave position at next bit to allocate, ! * so we don't keep re-using the last in an ! * alloc/free/alloc/free sequence. Doing ! * that can confuse some SMB clients. ! */ ! if (bit & 0x80) { ! pool->id_bit = 1; ! pool->id_bit_idx = 0; ! pool->id_idx++; ! pool->id_idx &= pool->id_idx_msk; ! } else { ! pool->id_bit = (bit << 1); ! pool->id_bit_idx = bit_idx + 1; ! /* keep id_idx */ ! } mutex_exit(&pool->id_mutex); return (0); } pool->id_bit = 1; pool->id_bit_idx = 0;
*** 453,462 **** --- 482,497 ---- list_insert_tail(&ll->ll_deleteq, dtor); ++ll->ll_deleteq_count; mutex_exit(&ll->ll_mutex); } + void + smb_llist_enter(smb_llist_t *ll, krw_t mode) + { + rw_enter(&ll->ll_lock, mode); + } + /* * Exit the list lock and process the delete queue. */ void smb_llist_exit(smb_llist_t *ll)
*** 786,900 **** /* * smb_rwx_rwexit */ void ! smb_rwx_rwexit( ! smb_rwx_t *rwx) { ! if (rw_write_held(&rwx->rwx_lock)) { ! ASSERT(rw_owner(&rwx->rwx_lock) == curthread); ! mutex_enter(&rwx->rwx_mutex); ! if (rwx->rwx_waiting) { ! rwx->rwx_waiting = B_FALSE; ! cv_broadcast(&rwx->rwx_cv); ! } ! mutex_exit(&rwx->rwx_mutex); ! } ! rw_exit(&rwx->rwx_lock); } /* ! * smb_rwx_rwupgrade */ ! krw_t ! smb_rwx_rwupgrade( smb_rwx_t *rwx) { - if (rw_write_held(&rwx->rwx_lock)) { - ASSERT(rw_owner(&rwx->rwx_lock) == curthread); - return (RW_WRITER); - } - if (!rw_tryupgrade(&rwx->rwx_lock)) { rw_exit(&rwx->rwx_lock); - rw_enter(&rwx->rwx_lock, RW_WRITER); - } - return (RW_READER); } - /* - * smb_rwx_rwrestore - */ - void - smb_rwx_rwdowngrade( - smb_rwx_t *rwx, - krw_t mode) - { - ASSERT(rw_write_held(&rwx->rwx_lock)); - ASSERT(rw_owner(&rwx->rwx_lock) == curthread); - if (mode == RW_WRITER) { - return; - } - ASSERT(mode == RW_READER); - mutex_enter(&rwx->rwx_mutex); - if (rwx->rwx_waiting) { - rwx->rwx_waiting = B_FALSE; - cv_broadcast(&rwx->rwx_cv); - } - mutex_exit(&rwx->rwx_mutex); - rw_downgrade(&rwx->rwx_lock); - } - /* ! * smb_rwx_wait * ! * This function assumes the smb_rwx lock was enter in RW_READER or RW_WRITER * mode. It will: * * 1) release the lock and save its current mode. ! * 2) wait until the condition variable is signaled. This can happen for ! * 2 reasons: When a writer releases the lock or when the time out (if ! * provided) expires. * 3) re-acquire the lock in the mode saved in (1). */ int ! smb_rwx_rwwait( smb_rwx_t *rwx, clock_t timeout) { krw_t mode; int rc = 1; - mutex_enter(&rwx->rwx_mutex); - rwx->rwx_waiting = B_TRUE; - mutex_exit(&rwx->rwx_mutex); - if (rw_write_held(&rwx->rwx_lock)) { ASSERT(rw_owner(&rwx->rwx_lock) == curthread); mode = RW_WRITER; } else { ASSERT(rw_read_held(&rwx->rwx_lock)); mode = RW_READER; } - rw_exit(&rwx->rwx_lock); mutex_enter(&rwx->rwx_mutex); ! if (rwx->rwx_waiting) { if (timeout == -1) { cv_wait(&rwx->rwx_cv, &rwx->rwx_mutex); } else { rc = cv_reltimedwait(&rwx->rwx_cv, &rwx->rwx_mutex, timeout, TR_CLOCK_TICK); } - } mutex_exit(&rwx->rwx_mutex); rw_enter(&rwx->rwx_lock, mode); return (rc); } /* smb_idmap_... moved to smb_idmap.c */ uint64_t smb_time_unix_to_nt(timestruc_t *unix_time) { --- 821,909 ---- /* * smb_rwx_rwexit */ void ! smb_rwx_rwenter(smb_rwx_t *rwx, krw_t mode) { ! rw_enter(&rwx->rwx_lock, mode); } /* ! * smb_rwx_rwexit */ ! void ! smb_rwx_rwexit( smb_rwx_t *rwx) { rw_exit(&rwx->rwx_lock); } /* ! * smb_rwx_cvwait * ! * Wait on rwx->rw_cv, dropping the rw lock and retake after wakeup. ! * Assumes the smb_rwx lock was entered in RW_READER or RW_WRITER * mode. It will: * * 1) release the lock and save its current mode. ! * 2) wait until the condition variable is signaled. * 3) re-acquire the lock in the mode saved in (1). + * + * Lock order: rwlock, mutex */ int ! smb_rwx_cvwait( smb_rwx_t *rwx, clock_t timeout) { krw_t mode; int rc = 1; if (rw_write_held(&rwx->rwx_lock)) { ASSERT(rw_owner(&rwx->rwx_lock) == curthread); mode = RW_WRITER; } else { ASSERT(rw_read_held(&rwx->rwx_lock)); mode = RW_READER; } mutex_enter(&rwx->rwx_mutex); ! rw_exit(&rwx->rwx_lock); ! ! rwx->rwx_waiting = B_TRUE; if (timeout == -1) { cv_wait(&rwx->rwx_cv, &rwx->rwx_mutex); } else { rc = cv_reltimedwait(&rwx->rwx_cv, &rwx->rwx_mutex, timeout, TR_CLOCK_TICK); } mutex_exit(&rwx->rwx_mutex); rw_enter(&rwx->rwx_lock, mode); return (rc); } + /* + * smb_rwx_cvbcast + * + * Wake up threads waiting on rx_cv + * The rw lock may or may not be held. + * The mutex MUST NOT be held. + */ + void + smb_rwx_cvbcast(smb_rwx_t *rwx) + { + mutex_enter(&rwx->rwx_mutex); + if (rwx->rwx_waiting) { + rwx->rwx_waiting = B_FALSE; + cv_broadcast(&rwx->rwx_cv); + } + mutex_exit(&rwx->rwx_mutex); + } + /* smb_idmap_... moved to smb_idmap.c */ uint64_t smb_time_unix_to_nt(timestruc_t *unix_time) {
*** 1720,1725 **** --- 1729,1782 ---- { mutex_enter(&ct->ct_mutex); ct->ct_threshold = 0; cv_broadcast(&ct->ct_cond); mutex_exit(&ct->ct_mutex); + } + + /* taken from mod_hash_byptr */ + uint_t + smb_hash_uint64(smb_hash_t *hash, uint64_t val) + { + uint64_t k = val >> hash->rshift; + uint_t idx = ((uint_t)k) & (hash->num_buckets - 1); + + return (idx); + } + + boolean_t + smb_is_pow2(size_t n) + { + return ((n & (n - 1)) == 0); + } + + smb_hash_t * + smb_hash_create(size_t elemsz, size_t link_offset, + uint32_t num_buckets) + { + smb_hash_t *hash = kmem_alloc(sizeof (*hash), KM_SLEEP); + int i; + + if (!smb_is_pow2(num_buckets)) + num_buckets = 1 << highbit(num_buckets); + + hash->rshift = highbit(elemsz); + hash->num_buckets = num_buckets; + hash->buckets = kmem_zalloc(num_buckets * sizeof (smb_bucket_t), + KM_SLEEP); + for (i = 0; i < num_buckets; i++) + smb_llist_constructor(&hash->buckets[i].b_list, elemsz, + link_offset); + return (hash); + } + + void + smb_hash_destroy(smb_hash_t *hash) + { + int i; + + for (i = 0; i < hash->num_buckets; i++) + smb_llist_destructor(&hash->buckets[i].b_list); + + kmem_free(hash->buckets, hash->num_buckets * sizeof (smb_bucket_t)); + kmem_free(hash, sizeof (*hash)); }