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));
  }