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,11 +19,11 @@
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  */
 
 /*
  * SMB: locking_andx
  *

@@ -210,42 +210,55 @@
  * 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_1(op__LockingX__start, 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_1(op__LockingX__done, 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 */
-        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;
+        uint32_t        status;
         smb_ofile_t     *ofile;
         uint16_t        tmp_pid;        /* locking uses 16-bit pids */
-        uint8_t         brk;
+        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,23 +273,33 @@
                 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;
 
-        save_pid = sr->smb_pid; /* Save the original pid */
-
         if (lock_type & LOCKING_ANDX_OPLOCK_RELEASE) {
+                uint32_t NewLevel;
                 if (oplock_level == 0)
-                        brk = SMB_OPLOCK_BREAK_TO_NONE;
+                        NewLevel = OPLOCK_LEVEL_NONE;
                 else
-                        brk = SMB_OPLOCK_BREAK_TO_LEVEL_II;
-                smb_oplock_ack(ofile->f_node, ofile, brk);
+                        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,106 +310,108 @@
                 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++) {
+        /*
+         * 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, &offset64, &length64);
+                            &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);
                         }
-                        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 */
+        /*
+         * 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(sr, offset64, length64, timeout,
-                            ltype);
+                result = smb_lock_range_cancel(sr, lr->off, lr->len, lr->pid);
+
                         if (result != NT_STATUS_SUCCESS) {
-                                smb_lock_range_error(sr, result);
+                        smbsr_error(sr, 0, ERRDOS,
+                            ERROR_CANCEL_VIOLATION);
                                 return (SDRC_ERROR);
                         }
+                goto out;
                 }
-        } else {
+
+        /*
+         * Normal unlock and lock list
+         */
                 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 */
+                lr = &lrv_ul[i];
 
-                        result = smb_unlock_range(sr, sr->fid_ofile->f_node,
-                            (uint64_t)offset32, (uint64_t)length32);
+                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++) {
-                        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 */
+                lr = &lrv_lk[i];
 
-                        result = smb_lock_range(sr, (uint64_t)offset32,
-                            (uint64_t)length32, timeout, ltype);
+                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);
                         }
                 }
-        }
 
-        sr->smb_pid = save_pid;
-        if (smbsr_encode_result(sr, 2, 0, "bb.ww", 2, sr->andx_com, 7, 0))
+out:
+        if (smbsr_encode_result(sr, 2, 0, "bb.ww",
+            2, sr->andx_com, 0x27, 0) != 0)
                 return (SDRC_ERROR);
         return (SDRC_SUCCESS);
 }
 
 /*

@@ -393,25 +418,29 @@
  * 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)
+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;
 
-        switch (brk) {
+        /*
+         * Convert internal level to SMB1
+         */
+        switch (NewLevel) {
         default:
                 ASSERT(0);
                 /* FALLTHROUGH */
-        case SMB_OPLOCK_BREAK_TO_NONE:
+        case OPLOCK_LEVEL_NONE:
                 oplock_level = 0;
                 break;
-        case SMB_OPLOCK_BREAK_TO_LEVEL_II:
+
+        case OPLOCK_LEVEL_TWO:
                 oplock_level = 1;
                 break;
         }
 
         sr->smb_com = SMB_COM_LOCKING_ANDX;