Print this page
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-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-1643 dtrace provider for smbsrv
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-6041 Should pass the smbtorture lock tests
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-1192 add context options to xpg4 grep (nits)
NEX-1192 add context options to xpg4 grep
SUP-867 panic in smb_com_locking_andx
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)
        
*** 19,29 ****
   * CDDL HEADER END
   */
  
  /*
   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
!  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
   */
  
  /*
   * SMB: locking_andx
   *
--- 19,29 ----
   * CDDL HEADER END
   */
  
  /*
   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
!  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
   */
  
  /*
   * SMB: locking_andx
   *
*** 210,251 ****
   * ERRSRV/ERRbaduid
   */
  
  #include <smbsrv/smb_kproto.h>
  
  smb_sdrc_t
  smb_pre_locking_andx(smb_request_t *sr)
  {
!         DTRACE_SMB_1(op__LockingX__start, smb_request_t *, sr);
          return (SDRC_SUCCESS);
  }
  
  void
  smb_post_locking_andx(smb_request_t *sr)
  {
!         DTRACE_SMB_1(op__LockingX__done, smb_request_t *, sr);
  }
  
  smb_sdrc_t
  smb_com_locking_andx(smb_request_t *sr)
  {
          unsigned short  i;
          unsigned char   lock_type;      /* See lock_type table above */
          unsigned char   oplock_level;   /* The new oplock level */
          uint32_t        timeout;        /* Milliseconds to wait for lock */
          unsigned short  unlock_num;     /* # unlock range structs */
          unsigned short  lock_num;       /* # lock range structs */
-         uint32_t        save_pid;       /* Process Id of owner */
-         uint32_t        offset32, length32;
-         uint64_t        offset64;
-         uint64_t        length64;
          DWORD           result;
          int             rc;
          uint32_t        ltype;
          smb_ofile_t     *ofile;
          uint16_t        tmp_pid;        /* locking uses 16-bit pids */
!         uint8_t         brk;
  
          rc = smbsr_decode_vwv(sr, "4.wbblww", &sr->smb_fid, &lock_type,
              &oplock_level, &timeout, &unlock_num, &lock_num);
          if (rc != 0)
                  return (SDRC_ERROR);
--- 210,264 ----
   * ERRSRV/ERRbaduid
   */
  
  #include <smbsrv/smb_kproto.h>
  
+ /*
+  * This is a somewhat arbitrary sanity limit on the length of the
+  * SMB2_LOCK_ELEMENT array.  It usually has length one or two.
+  */
+ int smb_lock_max_elem = 1024;
+ 
  smb_sdrc_t
  smb_pre_locking_andx(smb_request_t *sr)
  {
!         DTRACE_SMB_START(op__LockingX, smb_request_t *, sr);
          return (SDRC_SUCCESS);
  }
  
  void
  smb_post_locking_andx(smb_request_t *sr)
  {
!         DTRACE_SMB_DONE(op__LockingX, smb_request_t *, sr);
  }
  
+ struct lreq {
+         uint64_t off;
+         uint64_t len;
+         uint32_t pid;
+         uint32_t _x;
+ };
+ 
  smb_sdrc_t
  smb_com_locking_andx(smb_request_t *sr)
  {
          unsigned short  i;
          unsigned char   lock_type;      /* See lock_type table above */
          unsigned char   oplock_level;   /* The new oplock level */
          uint32_t        timeout;        /* Milliseconds to wait for lock */
          unsigned short  unlock_num;     /* # unlock range structs */
          unsigned short  lock_num;       /* # lock range structs */
          DWORD           result;
          int             rc;
          uint32_t        ltype;
+         uint32_t        status;
          smb_ofile_t     *ofile;
          uint16_t        tmp_pid;        /* locking uses 16-bit pids */
!         uint32_t        lrv_tot;
!         struct lreq     *lrv_ul;
!         struct lreq     *lrv_lk;
!         struct lreq     *lr;
  
          rc = smbsr_decode_vwv(sr, "4.wbblww", &sr->smb_fid, &lock_type,
              &oplock_level, &timeout, &unlock_num, &lock_num);
          if (rc != 0)
                  return (SDRC_ERROR);
*** 260,282 ****
                  smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
                      ERRDOS, ERROR_INVALID_PARAMETER);
                  return (SDRC_ERROR);
          }
  
          if (lock_type & LOCKING_ANDX_SHARED_LOCK)
                  ltype = SMB_LOCK_TYPE_READONLY;
          else
                  ltype = SMB_LOCK_TYPE_READWRITE;
  
-         save_pid = sr->smb_pid; /* Save the original pid */
- 
          if (lock_type & LOCKING_ANDX_OPLOCK_RELEASE) {
                  if (oplock_level == 0)
!                         brk = SMB_OPLOCK_BREAK_TO_NONE;
                  else
!                         brk = SMB_OPLOCK_BREAK_TO_LEVEL_II;
!                 smb_oplock_ack(ofile->f_node, ofile, brk);
                  if (unlock_num == 0 && lock_num == 0)
                          return (SDRC_NO_REPLY);
          }
  
          /*
--- 273,305 ----
                  smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
                      ERRDOS, ERROR_INVALID_PARAMETER);
                  return (SDRC_ERROR);
          }
  
+         if (unlock_num > smb_lock_max_elem ||
+             lock_num > smb_lock_max_elem) {
+                 smbsr_error(sr, NT_STATUS_INSUFFICIENT_RESOURCES,
+                     ERRDOS, ERROR_NO_SYSTEM_RESOURCES);
+                 return (SDRC_ERROR);
+         }
+ 
          if (lock_type & LOCKING_ANDX_SHARED_LOCK)
                  ltype = SMB_LOCK_TYPE_READONLY;
          else
                  ltype = SMB_LOCK_TYPE_READWRITE;
  
          if (lock_type & LOCKING_ANDX_OPLOCK_RELEASE) {
+                 uint32_t NewLevel;
                  if (oplock_level == 0)
!                         NewLevel = OPLOCK_LEVEL_NONE;
                  else
!                         NewLevel = OPLOCK_LEVEL_TWO;
!                 status = smb_oplock_ack_break(sr, ofile, &NewLevel);
!                 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
!                         (void) smb_oplock_wait_break(ofile->f_node, 0);
!                         status = 0;
!                 }
                  if (unlock_num == 0 && lock_num == 0)
                          return (SDRC_NO_REPLY);
          }
  
          /*
*** 287,392 ****
                  smbsr_error(sr, 0, ERRDOS,
                      ERROR_ATOMIC_LOCKS_NOT_SUPPORTED);
                  return (SDRC_ERROR);
          }
  
-         /*
-          * No support for cancel lock (smbtorture expects this)
-          */
-         if (lock_type & LOCKING_ANDX_CANCEL_LOCK) {
-                 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
-                     ERRDOS, ERROR_INVALID_PARAMETER);
-                 return (SDRC_ERROR);
-         }
- 
          if (lock_type & LOCKING_ANDX_LARGE_FILES) {
                  /*
                   * negotiated protocol should be NT LM 0.12 or later
                   */
                  if (sr->session->dialect < NT_LM_0_12) {
                          smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
                              ERRDOS, ERROR_INVALID_PARAMETER);
                          return (SDRC_ERROR);
                  }
  
!                 for (i = 0; i < unlock_num; i++) {
                          rc = smb_mbc_decodef(&sr->smb_data, "w2.QQ",
!                             &tmp_pid, &offset64, &length64);
                          if (rc) {
                                  /*
                                   * This is the error returned by Windows 2000
                                   * even when STATUS32 has been negotiated.
                                   */
                                  smbsr_error(sr, 0, ERRSRV, ERRerror);
                                  return (SDRC_ERROR);
                          }
-                         sr->smb_pid = tmp_pid;  /* NB: 16-bit */
- 
-                         result = smb_unlock_range(sr, sr->fid_ofile->f_node,
-                             offset64, length64);
-                         if (result != NT_STATUS_SUCCESS) {
-                                 smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
-                                     ERRDOS, ERROR_NOT_LOCKED);
-                                 return (SDRC_ERROR);
                          }
-                 }
  
!                 for (i = 0; i < lock_num; i++) {
!                         rc = smb_mbc_decodef(&sr->smb_data, "w2.QQ",
!                             &tmp_pid, &offset64, &length64);
!                         if (rc) {
!                                 smbsr_error(sr, 0, ERRSRV, ERRerror);
!                                 return (SDRC_ERROR);
!                         }
!                         sr->smb_pid = tmp_pid;  /* NB: 16-bit */
  
!                         result = smb_lock_range(sr, offset64, length64, timeout,
!                             ltype);
                          if (result != NT_STATUS_SUCCESS) {
!                                 smb_lock_range_error(sr, result);
                                  return (SDRC_ERROR);
                          }
                  }
!         } else {
                  for (i = 0; i < unlock_num; i++) {
!                         rc = smb_mbc_decodef(&sr->smb_data, "wll", &tmp_pid,
!                             &offset32, &length32);
!                         if (rc) {
!                                 smbsr_error(sr, 0, ERRSRV, ERRerror);
!                                 return (SDRC_ERROR);
!                         }
!                         sr->smb_pid = tmp_pid;  /* NB: 16-bit */
  
!                         result = smb_unlock_range(sr, sr->fid_ofile->f_node,
!                             (uint64_t)offset32, (uint64_t)length32);
                          if (result != NT_STATUS_SUCCESS) {
                                  smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
                                      ERRDOS, ERROR_NOT_LOCKED);
                                  return (SDRC_ERROR);
                          }
                  }
- 
                  for (i = 0; i < lock_num; i++) {
!                         rc = smb_mbc_decodef(&sr->smb_data, "wll", &tmp_pid,
!                             &offset32, &length32);
!                         if (rc) {
!                                 smbsr_error(sr, 0, ERRSRV, ERRerror);
!                                 return (SDRC_ERROR);
!                         }
!                         sr->smb_pid = tmp_pid;  /* NB: 16-bit */
  
!                         result = smb_lock_range(sr, (uint64_t)offset32,
!                             (uint64_t)length32, timeout, ltype);
                          if (result != NT_STATUS_SUCCESS) {
                                  smb_lock_range_error(sr, result);
                                  return (SDRC_ERROR);
                          }
                  }
-         }
  
!         sr->smb_pid = save_pid;
!         if (smbsr_encode_result(sr, 2, 0, "bb.ww", 2, sr->andx_com, 7, 0))
                  return (SDRC_ERROR);
          return (SDRC_SUCCESS);
  }
  
  /*
--- 310,417 ----
                  smbsr_error(sr, 0, ERRDOS,
                      ERROR_ATOMIC_LOCKS_NOT_SUPPORTED);
                  return (SDRC_ERROR);
          }
  
          if (lock_type & LOCKING_ANDX_LARGE_FILES) {
                  /*
                   * negotiated protocol should be NT LM 0.12 or later
                   */
                  if (sr->session->dialect < NT_LM_0_12) {
                          smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
                              ERRDOS, ERROR_INVALID_PARAMETER);
                          return (SDRC_ERROR);
                  }
+         }
  
!         /*
!          * Parse the unlock, lock vectors.  Will parse all the
!          * unlock + lock records into one array, and then use
!          * pointers to the unlock and lock parts.
!          */
!         lrv_tot = unlock_num + lock_num;
!         lrv_ul = smb_srm_zalloc(sr, lrv_tot * sizeof (*lrv_ul));
!         lrv_lk = &lrv_ul[unlock_num];
! 
!         for (i = 0; i < lrv_tot; i++) {
!                 lr = &lrv_ul[i];
!                 if (lock_type & LOCKING_ANDX_LARGE_FILES) {
                          rc = smb_mbc_decodef(&sr->smb_data, "w2.QQ",
!                             &tmp_pid, &lr->off, &lr->len);
!                 } else {
!                         uint32_t        offset32, length32;
!                         rc = smb_mbc_decodef(&sr->smb_data, "wll",
!                             &tmp_pid, &offset32, &length32);
!                         lr->off = offset32;
!                         lr->len = length32;
!                 }
!                 lr->pid = tmp_pid;      /* 16-bit PID */
                  if (rc) {
                          /*
                           * This is the error returned by Windows 2000
                           * even when STATUS32 has been negotiated.
                           */
                          smbsr_error(sr, 0, ERRSRV, ERRerror);
                          return (SDRC_ERROR);
                  }
          }
  
!         /*
!          * Cancel waiting locks.  MS-CIFS says one place that
!          * this cancels all waiting locks for this FID+PID,
!          * but smbtorture insists this cancels just one.
!          * Tests with Windows 7 confirms that.
!          */
!         if ((lock_type & LOCKING_ANDX_CANCEL_LOCK) != 0) {
!                 lr = lrv_lk;
  
!                 result = smb_lock_range_cancel(sr, lr->off, lr->len, lr->pid);
! 
                  if (result != NT_STATUS_SUCCESS) {
!                         smbsr_error(sr, 0, ERRDOS,
!                             ERROR_CANCEL_VIOLATION);
                          return (SDRC_ERROR);
                  }
+                 goto out;
          }
! 
!         /*
!          * Normal unlock and lock list
!          */
          for (i = 0; i < unlock_num; i++) {
!                 lr = &lrv_ul[i];
  
!                 result = smb_unlock_range(sr, lr->off, lr->len, lr->pid);
                  if (result != NT_STATUS_SUCCESS) {
                          smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
                              ERRDOS, ERROR_NOT_LOCKED);
                          return (SDRC_ERROR);
                  }
          }
          for (i = 0; i < lock_num; i++) {
!                 lr = &lrv_lk[i];
  
!                 result = smb_lock_range(sr, lr->off, lr->len, lr->pid,
!                     ltype, timeout);
                  if (result != NT_STATUS_SUCCESS) {
+                         /*
+                          * Oh... we have to rollback.
+                          */
+                         while (i > 0) {
+                                 --i;
+                                 lr = &lrv_lk[i];
+                                 (void) smb_unlock_range(sr,
+                                     lr->off, lr->len, lr->pid);
+                         }
                          smb_lock_range_error(sr, result);
                          return (SDRC_ERROR);
                  }
          }
  
! out:
!         if (smbsr_encode_result(sr, 2, 0, "bb.ww",
!             2, sr->andx_com, 0x27, 0) != 0)
                  return (SDRC_ERROR);
          return (SDRC_SUCCESS);
  }
  
  /*
*** 393,417 ****
   * Compose an SMB1 Oplock Break Notification packet, including
   * the SMB1 header and everything, in sr->reply.
   * The caller will send it and free the request.
   */
  void
! smb1_oplock_break_notification(smb_request_t *sr, uint8_t brk)
  {
          smb_ofile_t *ofile = sr->fid_ofile;
          uint16_t fid;
          uint8_t lock_type;
          uint8_t oplock_level;
  
!         switch (brk) {
          default:
                  ASSERT(0);
                  /* FALLTHROUGH */
!         case SMB_OPLOCK_BREAK_TO_NONE:
                  oplock_level = 0;
                  break;
!         case SMB_OPLOCK_BREAK_TO_LEVEL_II:
                  oplock_level = 1;
                  break;
          }
  
          sr->smb_com = SMB_COM_LOCKING_ANDX;
--- 418,446 ----
   * Compose an SMB1 Oplock Break Notification packet, including
   * the SMB1 header and everything, in sr->reply.
   * The caller will send it and free the request.
   */
  void
! smb1_oplock_break_notification(smb_request_t *sr, uint32_t NewLevel)
  {
          smb_ofile_t *ofile = sr->fid_ofile;
          uint16_t fid;
          uint8_t lock_type;
          uint8_t oplock_level;
  
!         /*
!          * Convert internal level to SMB1
!          */
!         switch (NewLevel) {
          default:
                  ASSERT(0);
                  /* FALLTHROUGH */
!         case OPLOCK_LEVEL_NONE:
                  oplock_level = 0;
                  break;
! 
!         case OPLOCK_LEVEL_TWO:
                  oplock_level = 1;
                  break;
          }
  
          sr->smb_com = SMB_COM_LOCKING_ANDX;