Print this page
NEX-17289 Minimal SMB 3.0.2 support
Reviewed by: Gordon Ross <gordon.ross@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-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-5844 want SMB2 ioctl FSCTL_SRV_COPYCHUNK
NEX-6124 smb_fsop_read/write should allow file != sr->fid_ofile
NEX-6125 smbtorture invalid response with smb2.ioctl
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
SMB-119 Text file contains garbage when re-opened
SMB-120 Share enumeration fails with Windows 8
SMB-122 smbd core dumps in smbd_dc_update / smb_log
SMB-117 Win7 fails to open security properties
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)
        
*** 8,27 ****
   * source.  A copy of the CDDL is also available via the Internet at
   * http://www.illumos.org/license/CDDL.
   */
  
  /*
!  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
   */
  
  /*
   * Dispatch function for SMB2_WRITE
   */
  
  #include <smbsrv/smb2_kproto.h>
  #include <smbsrv/smb_fsops.h>
  
  smb_sdrc_t
  smb2_write(smb_request_t *sr)
  {
          smb_ofile_t *of = NULL;
          smb_vdb_t *vdb = NULL;
--- 8,29 ----
   * source.  A copy of the CDDL is also available via the Internet at
   * http://www.illumos.org/license/CDDL.
   */
  
  /*
!  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
   */
  
  /*
   * Dispatch function for SMB2_WRITE
   */
  
  #include <smbsrv/smb2_kproto.h>
  #include <smbsrv/smb_fsops.h>
  
+ boolean_t smb_allow_unbuffered = B_TRUE;
+ 
  smb_sdrc_t
  smb2_write(smb_request_t *sr)
  {
          smb_ofile_t *of = NULL;
          smb_vdb_t *vdb = NULL;
*** 38,50 ****
          uint32_t XferCount;
          uint32_t status;
          int data_chain_off, skip;
          int stability = 0;
          int rc = 0;
  
          /*
!          * SMB2 Write request
           */
          rc = smb_mbc_decodef(
              &sr->smb_data,
              "wwlqqqllwwl",
              &StructSize,                /* w */
--- 40,53 ----
          uint32_t XferCount;
          uint32_t status;
          int data_chain_off, skip;
          int stability = 0;
          int rc = 0;
+         boolean_t unbuffered = B_FALSE;
  
          /*
!          * Decode SMB2 Write request
           */
          rc = smb_mbc_decodef(
              &sr->smb_data,
              "wwlqqqllwwl",
              &StructSize,                /* w */
*** 61,108 ****
          if (rc)
                  return (SDRC_ERROR);
          if (StructSize != 49)
                  return (SDRC_ERROR);
  
-         status = smb2sr_lookup_fid(sr, &smb2fid);
-         if (status) {
-                 smb2sr_put_error(sr, status);
-                 return (SDRC_SUCCESS);
-         }
-         of = sr->fid_ofile;
- 
-         if (Length > smb2_max_rwsize) {
-                 smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
-                 return (SDRC_SUCCESS);
-         }
- 
          /*
           * Skip any padding before the write data.
           */
          data_chain_off = sr->smb2_cmd_hdr + DataOff;
          skip = data_chain_off - sr->smb_data.chain_offset;
!         if (skip < 0) {
!                 smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
!                 return (SDRC_SUCCESS);
!         }
!         if (skip > 0) {
                  (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
-         }
  
!         /* This is automatically free'd. */
          vdb = smb_srm_zalloc(sr, sizeof (*vdb));
          rc = smb_mbc_decodef(&sr->smb_data, "#B", Length, vdb);
!         if (rc != 0 || vdb->vdb_len != Length) {
!                 smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
!                 return (SDRC_SUCCESS);
!         }
          vdb->vdb_uio.uio_loffset = (offset_t)Offset;
  
          XferCount = 0;
          if (Length == 0)
!                 goto doreply;
  
          switch (of->f_tree->t_res_type & STYPE_MASK) {
          case STYPE_DISKTREE:
          case STYPE_PRINTQ:
                  if (!smb_node_is_dir(of->f_node)) {
                          /* Check for conflicting locks. */
--- 64,124 ----
          if (rc)
                  return (SDRC_ERROR);
          if (StructSize != 49)
                  return (SDRC_ERROR);
  
          /*
           * Skip any padding before the write data.
           */
          data_chain_off = sr->smb2_cmd_hdr + DataOff;
          skip = data_chain_off - sr->smb_data.chain_offset;
!         if (skip < 0)
!                 return (SDRC_ERROR);
!         if (skip > 0)
                  (void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
  
!         /*
!          * Decode the write data (payload)
!          * This is automatically free'd.
!          */
!         if (Length > smb2_max_rwsize)
!                 return (SDRC_ERROR);
          vdb = smb_srm_zalloc(sr, sizeof (*vdb));
          rc = smb_mbc_decodef(&sr->smb_data, "#B", Length, vdb);
!         if (rc != 0 || vdb->vdb_len != Length)
!                 return (SDRC_ERROR);
          vdb->vdb_uio.uio_loffset = (offset_t)Offset;
  
+         /*
+          * Want FID lookup before the start probe.
+          */
+         status = smb2sr_lookup_fid(sr, &smb2fid);
+         of = sr->fid_ofile;
+ 
+         DTRACE_SMB2_START(op__Write, smb_request_t *, sr); /* arg.rw */
+ 
+         if (status)
+                 goto errout; /* Bad FID */
+ 
+ 
          XferCount = 0;
          if (Length == 0)
!                 goto errout;
  
+         /*
+          * Unbuffered refers to the MS-FSA Write argument by the same name.
+          * It indicates that the cache for this range should be flushed to disk,
+          * and data written directly to disk, bypassing the cache.
+          * We don't allow that degree of cache management.
+          * Translate this directly as FSYNC,
+          * which should at least flush the cache.
+          */
+ 
+         if (smb_allow_unbuffered &&
+             (Flags & SMB2_WRITEFLAG_WRITE_UNBUFFERED) != 0)
+                 unbuffered = B_TRUE;
+ 
          switch (of->f_tree->t_res_type & STYPE_MASK) {
          case STYPE_DISKTREE:
          case STYPE_PRINTQ:
                  if (!smb_node_is_dir(of->f_node)) {
                          /* Check for conflicting locks. */
*** 111,163 ****
                          if (rc) {
                                  rc = ERANGE;
                                  break;
                          }
                  }
!                 if ((Flags & SMB2_WRITEFLAG_WRITE_THROUGH) ||
!                     (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH)) {
                          stability = FSYNC;
                  }
!                 rc = smb_fsop_write(sr, of->f_cr, of->f_node,
                      &vdb->vdb_uio, &XferCount, stability);
                  if (rc)
                          break;
                  of->f_written = B_TRUE;
!                 if (!smb_node_is_dir(of->f_node))
!                         smb_oplock_break_levelII(of->f_node);
                  break;
  
          case STYPE_IPC:
                  rc = smb_opipe_write(sr, &vdb->vdb_uio);
                  if (rc == 0)
                          XferCount = Length;
                  break;
  
          default:
                  rc = EACCES;
                  break;
          }
  
!         if (rc) {
!                 smb2sr_put_errno(sr, rc);
                  return (SDRC_SUCCESS);
          }
  
          /*
!          * SMB2 Write reply
           */
- doreply:
          DataOff = SMB2_HDR_SIZE + 16;
          rc = smb_mbc_encodef(
              &sr->reply, "wwlll",
              17, /* StructSize */        /* w */
              0, /* reserved */           /* w */
              XferCount,                  /* l */
              0, /* DataRemaining */      /* l */
              0); /* Channel Info */      /* l */
!         if (rc)
                  return (SDRC_ERROR);
  
          mutex_enter(&of->f_mutex);
          of->f_seek_pos = Offset + XferCount;
          mutex_exit(&of->f_mutex);
  
--- 127,189 ----
                          if (rc) {
                                  rc = ERANGE;
                                  break;
                          }
                  }
! 
!                 if (unbuffered || (Flags & SMB2_WRITEFLAG_WRITE_THROUGH) != 0 ||
!                     (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH) != 0) {
                          stability = FSYNC;
                  }
!                 rc = smb_fsop_write(sr, of->f_cr, of->f_node, of,
                      &vdb->vdb_uio, &XferCount, stability);
                  if (rc)
                          break;
                  of->f_written = B_TRUE;
!                 /* This revokes read cache delegations. */
!                 (void) smb_oplock_break_WRITE(of->f_node, of);
                  break;
  
          case STYPE_IPC:
+                 if (unbuffered || (Flags & SMB2_WRITEFLAG_WRITE_THROUGH) != 0)
+                         rc = EINVAL;
+                 else
                          rc = smb_opipe_write(sr, &vdb->vdb_uio);
                  if (rc == 0)
                          XferCount = Length;
                  break;
  
          default:
                  rc = EACCES;
                  break;
          }
+         status = smb_errno2status(rc);
  
! errout:
!         sr->smb2_status = status;
!         DTRACE_SMB2_DONE(op__Write, smb_request_t *, sr); /* arg.rw */
! 
!         if (status) {
!                 smb2sr_put_error(sr, status);
                  return (SDRC_SUCCESS);
          }
  
          /*
!          * Encode SMB2 Write reply
           */
          DataOff = SMB2_HDR_SIZE + 16;
          rc = smb_mbc_encodef(
              &sr->reply, "wwlll",
              17, /* StructSize */        /* w */
              0, /* reserved */           /* w */
              XferCount,                  /* l */
              0, /* DataRemaining */      /* l */
              0); /* Channel Info */      /* l */
!         if (rc) {
!                 sr->smb2_status = NT_STATUS_INTERNAL_ERROR;
                  return (SDRC_ERROR);
+         }
  
          mutex_enter(&of->f_mutex);
          of->f_seek_pos = Offset + XferCount;
          mutex_exit(&of->f_mutex);