Print this page
NEX-19378 Access problem with SMB server
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-19152 MacOS HighSierra Finder crashes...
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-13644 File access audit logging
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
NEX-17779 Creating named streams on existing files is not quite right
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
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-2807 Restoring previous versions from snapshots doesn't work with nested folders.
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15069 smtorture smb2.create.blob is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-13653 Obsolete SMB server work-around for ZFS read-only
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-6276 SMB sparse file support
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@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>
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-4474 SMB open with access=MAXIMUM_ALLOWED fails after NEX-3232
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
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-58 smbsrv should be immune to its own FEM hooks
SMB-50 User-mode SMB server
 Includes work by these authors:
 Thomas Keiser <thomas.keiser@nexenta.com>
 Albert Lee <trisk@nexenta.com>
SMB-65 SMB server in non-global zones (use zone_kcred())
SMB-63 taskq_create_proc ... TQ_DYNAMIC puts tasks in p0
re #11974 CIFS Share - Tree connect fails from Windows 7 Clients
SUS-172 Excel 2003 warning dialog when re-saving a file
SUS-173 Open fails if the client does not ask for read_attribute permission
re #7815 SMB server delivers old modification time...
re #11215 rb3676 sesctl to SGI JBOD hangs in biowait() with a command stuck in mptsas driver
re #10734 NT Trans. Notify returning too quickly
        
*** 18,43 ****
   *
   * 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/sid.h>
  #include <sys/nbmlock.h>
  #include <smbsrv/smb_fsops.h>
  #include <smbsrv/smb_kproto.h>
  #include <acl/acl_common.h>
  #include <sys/fcntl.h>
  #include <sys/flock.h>
  #include <fs/fs_subr.h>
  
  extern caller_context_t smb_ct;
  
! static int smb_fsop_create_stream(smb_request_t *, cred_t *, smb_node_t *,
!     char *, char *, int, smb_attr_t *, smb_node_t **);
  
  static int smb_fsop_create_file(smb_request_t *, cred_t *, smb_node_t *,
      char *, int, smb_attr_t *, smb_node_t **);
  
  #ifdef  _KERNEL
--- 18,44 ----
   *
   * 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/sid.h>
  #include <sys/nbmlock.h>
  #include <smbsrv/smb_fsops.h>
  #include <smbsrv/smb_kproto.h>
  #include <acl/acl_common.h>
  #include <sys/fcntl.h>
+ #include <sys/filio.h>
  #include <sys/flock.h>
  #include <fs/fs_subr.h>
  
  extern caller_context_t smb_ct;
  
! static int smb_fsop_create_file_with_stream(smb_request_t *, cred_t *,
!     smb_node_t *, char *, char *, int, smb_attr_t *, smb_node_t **);
  
  static int smb_fsop_create_file(smb_request_t *, cred_t *, smb_node_t *,
      char *, int, smb_attr_t *, smb_node_t **);
  
  #ifdef  _KERNEL
*** 131,142 ****
--- 132,145 ----
          cred_t *kcr = zone_kcred();
          int aclbsize = 0;       /* size of acl list in bytes */
          int flags = 0;
          int rc;
          boolean_t is_dir;
+         boolean_t do_audit;
  
          ASSERT(fs_sd);
+         ASSERT(ret_snode != NULL);
  
          if (SMB_TREE_IS_CASEINSENSITIVE(sr))
                  flags = SMB_IGNORE_CASE;
          if (SMB_TREE_SUPPORTS_CATIA(sr))
                  flags |= SMB_CATIA;
*** 143,152 ****
--- 146,156 ----
  
          ASSERT(cr);
  
          is_dir = ((fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR) != 0);
  
+         do_audit = smb_audit_init(sr);
          if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_ACLONCREATE)) {
                  if (fs_sd->sd_secinfo & SMB_ACL_SECINFO) {
                          dacl = fs_sd->sd_zdacl;
                          sacl = fs_sd->sd_zsacl;
                          ASSERT(dacl || sacl);
*** 181,190 ****
--- 185,200 ----
                          if (SMB_TREE_HAS_ACCESS(sr, ACE_ADD_FILE) != 0)
                                  rc = smb_vop_create(dnode->vp, name, attr,
                                      &vp, flags, cr, vsap);
                  }
  
+                 if (do_audit) {
+                         smb_audit_fini(sr,
+                             is_dir ? ACE_ADD_SUBDIRECTORY : ACE_ADD_FILE,
+                             dnode, rc == 0);
+                 }
+ 
                  if (vsap != NULL)
                          kmem_free(vsap->vsa_aclentp, aclbsize);
  
                  if (rc != 0)
                          return (rc);
*** 235,244 ****
--- 245,260 ----
                  } else {
                          rc = smb_vop_create(dnode->vp, name, attr, &vp,
                              flags, cr, NULL);
                  }
  
+                 if (do_audit) {
+                         smb_audit_fini(sr,
+                             is_dir ? ACE_ADD_SUBDIRECTORY : ACE_ADD_FILE,
+                             dnode, rc == 0);
+                 }
+ 
                  if (rc != 0)
                          return (rc);
  
                  *ret_snode = smb_node_lookup(sr, &sr->arg.open, cr, vp,
                      name, dnode, NULL);
*** 317,327 ****
          if (smb_is_stream_name(name)) {
                  fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
                  sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
                  smb_stream_parse_name(name, fname, sname);
  
!                 rc = smb_fsop_create_stream(sr, cr, dnode,
                      fname, sname, flags, attr, ret_snode);
  
                  kmem_free(fname, MAXNAMELEN);
                  kmem_free(sname, MAXNAMELEN);
                  return (rc);
--- 333,343 ----
          if (smb_is_stream_name(name)) {
                  fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
                  sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
                  smb_stream_parse_name(name, fname, sname);
  
!                 rc = smb_fsop_create_file_with_stream(sr, cr, dnode,
                      fname, sname, flags, attr, ret_snode);
  
                  kmem_free(fname, MAXNAMELEN);
                  kmem_free(sname, MAXNAMELEN);
                  return (rc);
*** 346,385 ****
  
  }
  
  
  /*
!  * smb_fsop_create_stream
   *
!  * Create NTFS named stream file (sname) on unnamed stream
!  * file (fname), creating the unnamed stream file if it
   * doesn't exist.
!  * If we created the unnamed stream file and then creation
!  * of the named stream file fails, we delete the unnamed stream.
   * Since we use the real file name for the smb_vop_remove we
   * clear the SMB_IGNORE_CASE flag to ensure a case sensitive
   * match.
   *
!  * The second parameter of smb_vop_setattr() is set to
!  * NULL, even though an unnamed stream exists.  This is
!  * because we want to set the UID and GID on the named
!  * stream in this case for consistency with the (unnamed
!  * stream) file (see comments for smb_vop_setattr()).
   */
  static int
! smb_fsop_create_stream(smb_request_t *sr, cred_t *cr,
      smb_node_t *dnode, char *fname, char *sname, int flags,
      smb_attr_t *attr, smb_node_t **ret_snode)
  {
-         smb_attr_t      fattr;
          smb_node_t      *fnode;
-         vnode_t         *xattrdvp;
-         vnode_t         *vp;
          cred_t          *kcr = zone_kcred();
          int             rc = 0;
          boolean_t       fcreate = B_FALSE;
  
          /* Look up / create the unnamed stream, fname */
          rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
              sr->tid_tree->t_snode, dnode, fname, &fnode);
          if (rc == ENOENT) {
                  fcreate = B_TRUE;
--- 362,399 ----
  
  }
  
  
  /*
!  * smb_fsop_create_file_with_stream
   *
!  * Create named stream (sname) on file (fname), creating the file if it
   * doesn't exist.
!  * If we created the file and then creation of the named stream fails,
!  * we delete the file.
   * Since we use the real file name for the smb_vop_remove we
   * clear the SMB_IGNORE_CASE flag to ensure a case sensitive
   * match.
   *
!  * Note that some stream "types" are "restricted" and only
!  * internal callers (cr == kcred) can create those.
   */
  static int
! smb_fsop_create_file_with_stream(smb_request_t *sr, cred_t *cr,
      smb_node_t *dnode, char *fname, char *sname, int flags,
      smb_attr_t *attr, smb_node_t **ret_snode)
  {
          smb_node_t      *fnode;
          cred_t          *kcr = zone_kcred();
          int             rc = 0;
          boolean_t       fcreate = B_FALSE;
  
+         ASSERT(ret_snode != NULL);
+ 
+         if (cr != kcr && smb_strname_restricted(sname))
+                 return (EACCES);
+ 
          /* Look up / create the unnamed stream, fname */
          rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
              sr->tid_tree->t_snode, dnode, fname, &fnode);
          if (rc == ENOENT) {
                  fcreate = B_TRUE;
*** 387,438 ****
                      attr, &fnode);
          }
          if (rc != 0)
                  return (rc);
  
!         fattr.sa_mask = SMB_AT_UID | SMB_AT_GID;
!         rc = smb_vop_getattr(fnode->vp, NULL, &fattr, 0, kcr);
  
-         if (rc == 0) {
-                 /* create the named stream, sname */
-                 rc = smb_vop_stream_create(fnode->vp, sname, attr,
-                     &vp, &xattrdvp, flags, cr);
-         }
          if (rc != 0) {
                  if (fcreate) {
                          flags &= ~SMB_IGNORE_CASE;
                          (void) smb_vop_remove(dnode->vp,
                              fnode->od_name, flags, cr);
                  }
                  smb_node_release(fnode);
                  return (rc);
          }
  
          attr->sa_vattr.va_uid = fattr.sa_vattr.va_uid;
          attr->sa_vattr.va_gid = fattr.sa_vattr.va_gid;
          attr->sa_mask = SMB_AT_UID | SMB_AT_GID;
  
          rc = smb_vop_setattr(vp, NULL, attr, 0, kcr);
          if (rc != 0) {
!                 smb_node_release(fnode);
                  return (rc);
          }
  
          *ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdvp,
              vp, sname);
  
-         smb_node_release(fnode);
          VN_RELE(xattrdvp);
          VN_RELE(vp);
  
          if (*ret_snode == NULL)
                  rc = ENOMEM;
  
          /* notify change to the unnamed stream */
          if (rc == 0)
                  smb_node_notify_change(dnode,
!                     FILE_ACTION_ADDED_STREAM, fname);
  
          return (rc);
  }
  
  /*
--- 401,491 ----
                      attr, &fnode);
          }
          if (rc != 0)
                  return (rc);
  
!         rc = smb_fsop_create_stream(sr, cr, dnode, fnode, sname, flags, attr,
!             ret_snode);
  
          if (rc != 0) {
                  if (fcreate) {
                          flags &= ~SMB_IGNORE_CASE;
                          (void) smb_vop_remove(dnode->vp,
                              fnode->od_name, flags, cr);
                  }
+         }
+ 
          smb_node_release(fnode);
          return (rc);
+ }
+ 
+ /*
+  * smb_fsop_create_stream
+  *
+  * Create named stream (sname) on existing file (fnode).
+  *
+  * The second parameter of smb_vop_setattr() is set to
+  * NULL, even though an unnamed stream exists.  This is
+  * because we want to set the UID and GID on the named
+  * stream in this case for consistency with the (unnamed
+  * stream) file (see comments for smb_vop_setattr()).
+  *
+  * Note that some stream "types" are "restricted" and only
+  * internal callers (cr == kcred) can create those.
+  */
+ int
+ smb_fsop_create_stream(smb_request_t *sr, cred_t *cr,
+     smb_node_t *dnode, smb_node_t *fnode, char *sname, int flags,
+     smb_attr_t *attr, smb_node_t **ret_snode)
+ {
+         smb_attr_t      fattr;
+         vnode_t         *xattrdvp;
+         vnode_t         *vp;
+         cred_t          *kcr = zone_kcred();
+         int             rc = 0;
+ 
+         ASSERT(ret_snode != NULL);
+ 
+         if (cr != kcr && smb_strname_restricted(sname))
+                 return (EACCES);
+ 
+         bzero(&fattr, sizeof (fattr));
+         fattr.sa_mask = SMB_AT_UID | SMB_AT_GID;
+         rc = smb_vop_getattr(fnode->vp, NULL, &fattr, 0, kcr);
+ 
+         if (rc == 0) {
+                 /* create the named stream, sname */
+                 rc = smb_vop_stream_create(fnode->vp, sname,
+                     attr, &vp, &xattrdvp, flags, cr);
          }
+         if (rc != 0)
+                 return (rc);
  
          attr->sa_vattr.va_uid = fattr.sa_vattr.va_uid;
          attr->sa_vattr.va_gid = fattr.sa_vattr.va_gid;
          attr->sa_mask = SMB_AT_UID | SMB_AT_GID;
  
          rc = smb_vop_setattr(vp, NULL, attr, 0, kcr);
          if (rc != 0) {
!                 VN_RELE(xattrdvp);
!                 VN_RELE(vp);
                  return (rc);
          }
  
          *ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdvp,
              vp, sname);
  
          VN_RELE(xattrdvp);
          VN_RELE(vp);
  
          if (*ret_snode == NULL)
                  rc = ENOMEM;
  
          /* notify change to the unnamed stream */
          if (rc == 0)
                  smb_node_notify_change(dnode,
!                     FILE_ACTION_ADDED_STREAM, fnode->od_name);
  
          return (rc);
  }
  
  /*
*** 445,454 ****
--- 498,509 ----
  {
          smb_arg_open_t  *op = &sr->sr_open;
          vnode_t         *vp;
          int             rc;
  
+         ASSERT(ret_snode != NULL);
+ 
  #ifdef  _KERNEL
          smb_fssd_t      fs_sd;
          uint32_t        secinfo;
          uint32_t        status;
  
*** 486,499 ****
--- 541,563 ----
  #endif  /* _KERNEL */
          {
                  /*
                   * No incoming SD and filesystem is not ZFS
                   * let the filesystem handles the inheritance.
+                  *
+                  * fsop_create_with_sd handles auditing in the other cases.
+                  * Handle it explicitly here.
                   */
+                 boolean_t do_audit = smb_audit_init(sr);
+ 
                  rc = smb_vop_create(dnode->vp, name, attr, &vp,
                      flags, cr, NULL);
  
+                 if (do_audit) {
+                         smb_audit_fini(sr, ACE_ADD_FILE, dnode, rc == 0);
+                 }
+ 
                  if (rc == 0) {
                          *ret_snode = smb_node_lookup(sr, op, cr, vp,
                              name, dnode, NULL);
  
                          if (*ret_snode == NULL)
*** 625,637 ****
--- 689,712 ----
                  smb_fssd_term(&fs_sd);
  
          } else
  #endif  /* _KERNEL */
          {
+                 /*
+                  * fsop_create_with_sd handles auditing in the other cases.
+                  * Handle it explicitly here.
+                  */
+                 boolean_t do_audit = smb_audit_init(sr);
+ 
                  rc = smb_vop_mkdir(dnode->vp, name, attr, &vp, flags, cr,
                      NULL);
  
+                 if (do_audit) {
+                         smb_audit_fini(sr, ACE_ADD_SUBDIRECTORY, dnode,
+                             rc == 0);
+                 }
+ 
                  if (rc == 0) {
                          *ret_snode = smb_node_lookup(sr, op, cr, vp, name,
                              dnode, NULL);
  
                          if (*ret_snode == NULL)
*** 656,665 ****
--- 731,743 ----
   * for avoiding this wrapper.
   *
   * It is assumed that a reference exists on snode coming into this routine.
   *
   * A null smb_request might be passed to this function.
+  *
+  * Note that some stream "types" are "restricted" and only
+  * internal callers (cr == kcred) can remove those.
   */
  int
  smb_fsop_remove(
      smb_request_t       *sr,
      cred_t              *cr,
*** 691,700 ****
--- 769,783 ----
  
          fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
          sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
  
          if (dnode->flags & NODE_XATTR_DIR) {
+                 if (cr != zone_kcred() && smb_strname_restricted(name)) {
+                         rc = EACCES;
+                         goto out;
+                 }
+ 
                  fnode = dnode->n_dnode;
                  rc = smb_vop_stream_remove(fnode->vp, name, flags, cr);
  
                  /* notify change to the unnamed stream */
                  if ((rc == 0) && fnode->n_dnode) {
*** 702,711 ****
--- 785,799 ----
                              FILE_ACTION_REMOVED_STREAM, fnode->od_name);
                  }
          } else if (smb_is_stream_name(name)) {
                  smb_stream_parse_name(name, fname, sname);
  
+                 if (cr != zone_kcred() && smb_strname_restricted(sname)) {
+                         rc = EACCES;
+                         goto out;
+                 }
+ 
                  /*
                   * Look up the unnamed stream (i.e. fname).
                   * Unmangle processing will be done on fname
                   * as well as any link target.
                   */
*** 712,724 ****
  
                  rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
                      sr->tid_tree->t_snode, dnode, fname, &fnode);
  
                  if (rc != 0) {
!                         kmem_free(fname, MAXNAMELEN);
!                         kmem_free(sname, MAXNAMELEN);
!                         return (rc);
                  }
  
                  /*
                   * XXX
                   * Need to find out what permission is required by NTFS
--- 800,810 ----
  
                  rc = smb_fsop_lookup(sr, cr, flags | SMB_FOLLOW_LINKS,
                      sr->tid_tree->t_snode, dnode, fname, &fnode);
  
                  if (rc != 0) {
!                         goto out;
                  }
  
                  /*
                   * XXX
                   * Need to find out what permission is required by NTFS
*** 737,749 ****
                  rc = smb_vop_remove(dnode->vp, name, flags, cr);
  
                  if (rc == ENOENT) {
                          if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) ||
                              !smb_maybe_mangled(name)) {
!                                 kmem_free(fname, MAXNAMELEN);
!                                 kmem_free(sname, MAXNAMELEN);
!                                 return (rc);
                          }
                          longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
  
                          if (SMB_TREE_SUPPORTS_ABE(sr))
                                  flags |= SMB_ABE;
--- 823,833 ----
                  rc = smb_vop_remove(dnode->vp, name, flags, cr);
  
                  if (rc == ENOENT) {
                          if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) ||
                              !smb_maybe_mangled(name)) {
!                                 goto out;
                          }
                          longname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
  
                          if (SMB_TREE_SUPPORTS_ABE(sr))
                                  flags |= SMB_ABE;
*** 769,778 ****
--- 853,863 ----
                          smb_node_notify_change(dnode,
                              FILE_ACTION_REMOVED, name);
                  }
          }
  
+ out:
          kmem_free(fname, MAXNAMELEN);
          kmem_free(sname, MAXNAMELEN);
  
          return (rc);
  }
*** 814,823 ****
--- 899,909 ----
  
          status = smb_odir_openat(sr, fnode, &od);
          switch (status) {
          case 0:
                  break;
+         case NT_STATUS_OBJECT_NAME_NOT_FOUND:
          case NT_STATUS_NO_SUCH_FILE:
          case NT_STATUS_NOT_SUPPORTED:
                  /* No streams to remove. */
                  return (0);
          default:
*** 1112,1123 ****
--- 1198,1214 ----
  
          /*
           * XXX: Lock required through smb_node_release() below?
           */
  
+         /*
+          * Don't audit the lookup
+          */
+         smb_audit_save();
          rc = smb_vop_lookup(from_dnode->vp, from_name, &from_vp, NULL,
              flags, &ret_flags, NULL, &from_attr, cr);
+         smb_audit_load();
  
          if (rc != 0)
                  return (rc);
  
          if (from_attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) {
*** 1153,1162 ****
--- 1244,1254 ----
                  if (rc != NT_STATUS_SUCCESS) {
                          VN_RELE(from_vp);
                          return (EACCES);
                  }
  
+                 /* TODO: rename drops ATTR_NOACLCHECK, so this is a no-op. */
                  if (smb_tree_has_feature(sr->tid_tree,
                      SMB_TREE_ACEMASKONACCESS))
                          flags = ATTR_NOACLCHECK;
          }
  
*** 1237,1265 ****
          if (SMB_TREE_HAS_ACCESS(sr,
              ACE_WRITE_ATTRIBUTES | ACE_WRITE_NAMED_ATTRS) == 0)
                  return (EACCES);
  
          /*
-          * The file system cannot detect pending READDONLY
-          * (i.e. if the file has been opened readonly but
-          * not yet closed) so we need to test READONLY here.
-          *
-          * Note that file handle that were opened before the
-          * READONLY flag was set in the node (or the FS) are
-          * immune to that change, and remain writable.
-          */
-         if (sr && (set_attr->sa_mask & SMB_AT_SIZE)) {
-                 if (sr->fid_ofile) {
-                         if (SMB_OFILE_IS_READONLY(sr->fid_ofile))
-                                 return (EACCES);
-                 } else {
-                         if (SMB_PATHFILE_IS_READONLY(sr, snode))
-                                 return (EACCES);
-                 }
-         }
- 
-         /*
           * SMB checks access on open and retains an access granted
           * mask for use while the file is open.  ACL changes should
           * not affect access to an open file.
           *
           * If the setattr is being performed on an ofile:
--- 1329,1338 ----
*** 1311,1335 ****
          return (rc);
  }
  
  /*
   * Support for SMB2 setinfo FileValidDataLengthInformation.
!  * Free data from the specified offset to EoF.
!  *
!  * This can effectively truncate data.  It truncates the data
!  * leaving the file size as it was, leaving zeros after the
!  * offset specified here.  That is effectively modifying the
!  * file content, so for access control this is a write.
   */
  int
! smb_fsop_set_data_length(
      smb_request_t       *sr,
      cred_t              *cr,
!     smb_node_t          *node,
!     offset_t            end_of_data)
  {
          flock64_t flk;
          uint32_t status;
          uint32_t access = FILE_WRITE_DATA;
          int rc;
  
          ASSERT(cr);
--- 1384,1405 ----
          return (rc);
  }
  
  /*
   * Support for SMB2 setinfo FileValidDataLengthInformation.
!  * Free (zero out) data in the range off, off+len
   */
  int
! smb_fsop_freesp(
      smb_request_t       *sr,
      cred_t              *cr,
!     smb_ofile_t         *ofile,
!     off64_t             off,
!     off64_t             len)
  {
          flock64_t flk;
+         smb_node_t *node = ofile->f_node;
          uint32_t status;
          uint32_t access = FILE_WRITE_DATA;
          int rc;
  
          ASSERT(cr);
*** 1345,1371 ****
  
          if (SMB_TREE_HAS_ACCESS(sr, access) == 0)
                  return (EACCES);
  
          /*
-          * The file system cannot detect pending READDONLY
-          * (i.e. if the file has been opened readonly but
-          * not yet closed) so we need to test READONLY here.
-          *
-          * Note that file handle that were opened before the
-          * READONLY flag was set in the node (or the FS) are
-          * immune to that change, and remain writable.
-          */
-         if (sr->fid_ofile) {
-                 if (SMB_OFILE_IS_READONLY(sr->fid_ofile))
-                         return (EACCES);
-         } else {
-                 /* This requires an open file. */
-                 return (EACCES);
-         }
- 
-         /*
           * SMB checks access on open and retains an access granted
           * mask for use while the file is open.  ACL changes should
           * not affect access to an open file.
           *
           * If the setattr is being performed on an ofile:
--- 1415,1424 ----
*** 1375,1385 ****
          status = smb_ofile_access(sr->fid_ofile, cr, access);
          if (status != NT_STATUS_SUCCESS)
                  return (EACCES);
  
          bzero(&flk, sizeof (flk));
!         flk.l_start = end_of_data;
  
          rc = smb_vop_space(node->vp, F_FREESP, &flk, FWRITE, 0LL, cr);
          return (rc);
  }
  
--- 1428,1439 ----
          status = smb_ofile_access(sr->fid_ofile, cr, access);
          if (status != NT_STATUS_SUCCESS)
                  return (EACCES);
  
          bzero(&flk, sizeof (flk));
!         flk.l_start = off;
!         flk.l_len = len;
  
          rc = smb_vop_space(node->vp, F_FREESP, &flk, FWRITE, 0LL, cr);
          return (rc);
  }
  
*** 1390,1426 ****
   * the the calls are performed with the appropriate credentials.
   * Please document any direct call to explain the reason
   * for avoiding this wrapper.
   *
   * It is assumed that a reference exists on snode coming into this routine.
   */
  int
! smb_fsop_read(smb_request_t *sr, cred_t *cr, smb_node_t *snode, uio_t *uio)
  {
          caller_context_t ct;
          cred_t *kcr = zone_kcred();
          int svmand;
          int rc;
  
          ASSERT(cr);
          ASSERT(snode);
          ASSERT(snode->n_magic == SMB_NODE_MAGIC);
          ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
  
          ASSERT(sr);
-         ASSERT(sr->fid_ofile);
  
!         if (SMB_TREE_HAS_ACCESS(sr, ACE_READ_DATA) == 0)
                  return (EACCES);
  
!         rc = smb_ofile_access(sr->fid_ofile, cr, FILE_READ_DATA);
!         if ((rc != NT_STATUS_SUCCESS) &&
!             (sr->smb_flg2 & SMB_FLAGS2_READ_IF_EXECUTE))
!                 rc = smb_ofile_access(sr->fid_ofile, cr, FILE_EXECUTE);
! 
!         if (rc != NT_STATUS_SUCCESS)
                  return (EACCES);
  
          /*
           * Streams permission are checked against the unnamed stream,
           * but in FS level they have their own permissions. To avoid
           * rejection by FS due to lack of permission on the actual
--- 1444,1491 ----
   * the the calls are performed with the appropriate credentials.
   * Please document any direct call to explain the reason
   * for avoiding this wrapper.
   *
   * It is assumed that a reference exists on snode coming into this routine.
+  * Note that ofile may be different from sr->fid_ofile, or may be NULL.
   */
  int
! smb_fsop_read(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
!     smb_ofile_t *ofile, uio_t *uio, int ioflag)
  {
          caller_context_t ct;
          cred_t *kcr = zone_kcred();
+         uint32_t amask;
          int svmand;
          int rc;
  
          ASSERT(cr);
          ASSERT(snode);
          ASSERT(snode->n_magic == SMB_NODE_MAGIC);
          ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
  
          ASSERT(sr);
  
!         if (ofile != NULL) {
!                 /*
!                  * Check tree access.  Not SMB_TREE_HAS_ACCESS
!                  * because we need to use ofile->f_tree
!                  */
!                 if ((ofile->f_tree->t_access & ACE_READ_DATA) == 0)
                          return (EACCES);
  
!                 /*
!                  * Check ofile access.  Use in-line smb_ofile_access
!                  * so we can check both amask bits at the same time.
!                  * If any bit in amask is granted, allow this read.
!                  */
!                 amask = FILE_READ_DATA;
!                 if (sr->smb_flg2 & SMB_FLAGS2_READ_IF_EXECUTE)
!                         amask |= FILE_EXECUTE;
!                 if (cr != kcr && (ofile->f_granted_access & amask) == 0)
                          return (EACCES);
+         }
  
          /*
           * Streams permission are checked against the unnamed stream,
           * but in FS level they have their own permissions. To avoid
           * rejection by FS due to lack of permission on the actual
*** 1434,1507 ****
          if (rc) {
                  smb_node_end_crit(snode);
                  return (rc);
          }
  
          ct = smb_ct;
!         ct.cc_pid = sr->fid_ofile->f_uniqid;
          rc = nbl_lock_conflict(snode->vp, NBL_READ, uio->uio_loffset,
!             uio->uio_iov->iov_len, svmand, &ct);
! 
!         if (rc) {
                  smb_node_end_crit(snode);
                  return (ERANGE);
          }
  
!         rc = smb_vop_read(snode->vp, uio, cr);
          smb_node_end_crit(snode);
  
          return (rc);
  }
  
  /*
   * smb_fsop_write
   *
-  * This is a wrapper function used for smb_write and smb_write_raw operations.
-  *
   * It is assumed that a reference exists on snode coming into this routine.
   */
  int
  smb_fsop_write(
      smb_request_t *sr,
      cred_t *cr,
      smb_node_t *snode,
      uio_t *uio,
      uint32_t *lcount,
      int ioflag)
  {
          caller_context_t ct;
          smb_attr_t attr;
          smb_node_t *u_node;
          vnode_t *u_vp = NULL;
-         smb_ofile_t *of;
          vnode_t *vp;
!         cred_t *kcr = zone_kcred();
          int svmand;
          int rc;
  
          ASSERT(cr);
          ASSERT(snode);
          ASSERT(snode->n_magic == SMB_NODE_MAGIC);
          ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
  
          ASSERT(sr);
-         ASSERT(sr->tid_tree);
-         of = sr->fid_ofile;
          vp = snode->vp;
  
!         if (SMB_TREE_IS_READONLY(sr))
                  return (EROFS);
  
!         if (SMB_OFILE_IS_READONLY(of) ||
!             SMB_TREE_HAS_ACCESS(sr, ACE_WRITE_DATA | ACE_APPEND_DATA) == 0)
                  return (EACCES);
- 
-         rc = smb_ofile_access(of, cr, FILE_WRITE_DATA);
-         if (rc != NT_STATUS_SUCCESS) {
-                 rc = smb_ofile_access(of, cr, FILE_APPEND_DATA);
-                 if (rc != NT_STATUS_SUCCESS)
-                         return (EACCES);
          }
  
          /*
           * Streams permission are checked against the unnamed stream,
           * but in FS level they have their own permissions. To avoid
--- 1499,1580 ----
          if (rc) {
                  smb_node_end_crit(snode);
                  return (rc);
          }
  
+         /*
+          * Note: SMB allows a zero-byte read, which should not
+          * conflict with any locks.  However nbl_lock_conflict
+          * takes a zero-byte length as lock to EOF, so we must
+          * special case that here.
+          */
+         if (uio->uio_resid > 0) {
                  ct = smb_ct;
!                 if (ofile != NULL)
!                         ct.cc_pid = ofile->f_uniqid;
                  rc = nbl_lock_conflict(snode->vp, NBL_READ, uio->uio_loffset,
!                     uio->uio_resid, svmand, &ct);
!                 if (rc != 0) {
                          smb_node_end_crit(snode);
                          return (ERANGE);
                  }
+         }
  
!         rc = smb_vop_read(snode->vp, uio, ioflag, cr);
          smb_node_end_crit(snode);
  
          return (rc);
  }
  
  /*
   * smb_fsop_write
   *
   * It is assumed that a reference exists on snode coming into this routine.
+  * Note that ofile may be different from sr->fid_ofile, or may be NULL.
   */
  int
  smb_fsop_write(
      smb_request_t *sr,
      cred_t *cr,
      smb_node_t *snode,
+     smb_ofile_t *ofile,
      uio_t *uio,
      uint32_t *lcount,
      int ioflag)
  {
          caller_context_t ct;
          smb_attr_t attr;
+         cred_t *kcr = zone_kcred();
          smb_node_t *u_node;
          vnode_t *u_vp = NULL;
          vnode_t *vp;
!         uint32_t amask;
          int svmand;
          int rc;
  
          ASSERT(cr);
          ASSERT(snode);
          ASSERT(snode->n_magic == SMB_NODE_MAGIC);
          ASSERT(snode->n_state != SMB_NODE_STATE_DESTROYING);
  
          ASSERT(sr);
          vp = snode->vp;
  
!         if (ofile != NULL) {
!                 amask = FILE_WRITE_DATA | FILE_APPEND_DATA;
! 
!                 /* Check tree access. */
!                 if ((ofile->f_tree->t_access & amask) == 0)
                          return (EROFS);
  
!                 /*
!                  * Check ofile access.  Use in-line smb_ofile_access
!                  * so we can check both amask bits at the same time.
!                  * If any bit in amask is granted, allow this write.
!                  */
!                 if (cr != kcr && (ofile->f_granted_access & amask) == 0)
                          return (EACCES);
          }
  
          /*
           * Streams permission are checked against the unnamed stream,
           * but in FS level they have their own permissions. To avoid
*** 1521,1539 ****
          if (rc) {
                  smb_node_end_crit(snode);
                  return (rc);
          }
  
          ct = smb_ct;
!         ct.cc_pid = of->f_uniqid;
          rc = nbl_lock_conflict(vp, NBL_WRITE, uio->uio_loffset,
!             uio->uio_iov->iov_len, svmand, &ct);
! 
!         if (rc) {
                  smb_node_end_crit(snode);
                  return (ERANGE);
          }
  
          rc = smb_vop_write(vp, uio, ioflag, lcount, cr);
  
          /*
           * Once the mtime has been set via this ofile, the
--- 1594,1620 ----
          if (rc) {
                  smb_node_end_crit(snode);
                  return (rc);
          }
  
+         /*
+          * Note: SMB allows a zero-byte write, which should not
+          * conflict with any locks.  However nbl_lock_conflict
+          * takes a zero-byte length as lock to EOF, so we must
+          * special case that here.
+          */
+         if (uio->uio_resid > 0) {
                  ct = smb_ct;
!                 if (ofile != NULL)
!                         ct.cc_pid = ofile->f_uniqid;
                  rc = nbl_lock_conflict(vp, NBL_WRITE, uio->uio_loffset,
!                     uio->uio_resid, svmand, &ct);
!                 if (rc != 0) {
                          smb_node_end_crit(snode);
                          return (ERANGE);
                  }
+         }
  
          rc = smb_vop_write(vp, uio, ioflag, lcount, cr);
  
          /*
           * Once the mtime has been set via this ofile, the
*** 1544,1555 ****
           * The VFS interface does not offer a way to ask it to
           * skip the mtime updates, so we simulate the desired
           * behavior by re-setting the mtime after writes on a
           * handle where the mtime has been set.
           */
!         if (of->f_pending_attr.sa_mask & SMB_AT_MTIME) {
!                 bcopy(&of->f_pending_attr, &attr, sizeof (attr));
                  attr.sa_mask = SMB_AT_MTIME;
                  (void) smb_vop_setattr(vp, u_vp, &attr, 0, kcr);
          }
  
          smb_node_end_crit(snode);
--- 1625,1637 ----
           * The VFS interface does not offer a way to ask it to
           * skip the mtime updates, so we simulate the desired
           * behavior by re-setting the mtime after writes on a
           * handle where the mtime has been set.
           */
!         if (ofile != NULL &&
!             (ofile->f_pending_attr.sa_mask & SMB_AT_MTIME) != 0) {
!                 bcopy(&ofile->f_pending_attr, &attr, sizeof (attr));
                  attr.sa_mask = SMB_AT_MTIME;
                  (void) smb_vop_setattr(vp, u_vp, &attr, 0, kcr);
          }
  
          smb_node_end_crit(snode);
*** 1556,1565 ****
--- 1638,1671 ----
  
          return (rc);
  }
  
  /*
+  * Find the next allocated range starting at or after
+  * the offset (*datap), returning the start/end of
+  * that range in (*datap, *holep)
+  */
+ int
+ smb_fsop_next_alloc_range(
+     cred_t *cr,
+     smb_node_t *node,
+     off64_t *datap,
+     off64_t *holep)
+ {
+         int err;
+ 
+         err = smb_vop_ioctl(node->vp, _FIO_SEEK_DATA, datap, cr);
+         if (err != 0)
+                 return (err);
+ 
+         *holep = *datap;
+         err = smb_vop_ioctl(node->vp, _FIO_SEEK_HOLE, holep, cr);
+ 
+         return (err);
+ }
+ 
+ /*
   * smb_fsop_statfs
   *
   * This is a wrapper function used for stat operations.
   */
  int
*** 1587,1596 ****
--- 1693,1705 ----
   * separate from that on the unnamed stream. If READ or EXECUTE
   * access has been requested on a named stream, an additional access
   * check is performed on the named stream in case it has been
   * quarantined.  kcred is used to avoid issues with the permissions
   * set on the extended attribute file representing the named stream.
+  *
+  * Note that some stream "types" are "restricted" and only
+  * internal callers (cr == kcred) can access those.
   */
  int
  smb_fsop_access(smb_request_t *sr, cred_t *cr, smb_node_t *snode,
      uint32_t faccess)
  {
*** 1617,1637 ****
          if (smb_node_is_reparse(snode) && (faccess & DELETE))
                  return (NT_STATUS_ACCESS_DENIED);
  
          unnamed_node = SMB_IS_STREAM(snode);
          if (unnamed_node) {
                  ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
                  ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
  
                  /*
                   * Perform VREAD access check on the named stream in case it
                   * is quarantined. kcred is passed to smb_vop_access so it
                   * doesn't fail due to lack of permission.
                   */
                  if (faccess & (FILE_READ_DATA | FILE_EXECUTE)) {
                          error = smb_vop_access(snode->vp, VREAD,
!                             0, NULL, zone_kcred());
                          if (error)
                                  return (NT_STATUS_ACCESS_DENIED);
                  }
  
                  /*
--- 1726,1751 ----
          if (smb_node_is_reparse(snode) && (faccess & DELETE))
                  return (NT_STATUS_ACCESS_DENIED);
  
          unnamed_node = SMB_IS_STREAM(snode);
          if (unnamed_node) {
+                 cred_t *kcr = zone_kcred();
+ 
                  ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
                  ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
  
+                 if (cr != kcr && smb_strname_restricted(snode->od_name))
+                         return (NT_STATUS_ACCESS_DENIED);
+ 
                  /*
                   * Perform VREAD access check on the named stream in case it
                   * is quarantined. kcred is passed to smb_vop_access so it
                   * doesn't fail due to lack of permission.
                   */
                  if (faccess & (FILE_READ_DATA | FILE_EXECUTE)) {
                          error = smb_vop_access(snode->vp, VREAD,
!                             0, NULL, kcr);
                          if (error)
                                  return (NT_STATUS_ACCESS_DENIED);
                  }
  
                  /*
*** 1690,1702 ****
--- 1804,1820 ----
  }
  
  /*
   * smb_fsop_lookup_name()
   *
+  * Lookup both the file and stream specified in 'name'.
   * If name indicates that the file is a stream file, perform
   * stream specific lookup, otherwise call smb_fsop_lookup.
   *
+  * On success, returns the found node in *ret_snode. This will be either a named
+  * or unnamed stream node, depending on the name specified.
+  *
   * Return an error if the looked-up file is in outside the tree.
   * (Required when invoked from open path.)
   *
   * Case sensitivity flags (SMB_IGNORE_CASE, SMB_CASE_SENSITIVE):
   * if SMB_CASE_SENSITIVE is set, the SMB_IGNORE_CASE flag will NOT be set
*** 1714,1735 ****
      smb_node_t  *root_node,
      smb_node_t  *dnode,
      char        *name,
      smb_node_t  **ret_snode)
  {
!         smb_node_t      *fnode;
!         vnode_t         *xattrdirvp;
!         vnode_t         *vp;
!         char            *od_name;
          char            *fname;
-         char            *sname;
          int             rc;
  
          ASSERT(cr);
          ASSERT(dnode);
          ASSERT(dnode->n_magic == SMB_NODE_MAGIC);
          ASSERT(dnode->n_state != SMB_NODE_STATE_DESTROYING);
  
          /*
           * The following check is required for streams processing, below
           */
  
--- 1832,1899 ----
      smb_node_t  *root_node,
      smb_node_t  *dnode,
      char        *name,
      smb_node_t  **ret_snode)
  {
!         char *sname = NULL;
!         int rc;
!         smb_node_t *tmp_node;
! 
!         ASSERT(ret_snode != NULL);
! 
!         rc = smb_fsop_lookup_file(sr, cr, flags, root_node, dnode, name,
!             &sname, ret_snode);
! 
!         if (rc != 0 || sname == NULL)
!                 return (rc);
! 
!         tmp_node = *ret_snode;
!         rc = smb_fsop_lookup_stream(sr, cr, flags, root_node, tmp_node, sname,
!             ret_snode);
!         kmem_free(sname, MAXNAMELEN);
!         smb_node_release(tmp_node);
! 
!         return (rc);
! }
! 
! /*
!  * smb_fsop_lookup_file()
!  *
!  * Look up of the file portion of 'name'. If a Stream is specified,
!  * return the stream name in 'sname', which this allocates.
!  * The caller must free 'sname'.
!  *
!  * Return an error if the looked-up file is outside the tree.
!  * (Required when invoked from open path.)
!  *
!  * Case sensitivity flags (SMB_IGNORE_CASE, SMB_CASE_SENSITIVE):
!  * if SMB_CASE_SENSITIVE is set, the SMB_IGNORE_CASE flag will NOT be set
!  * based on the tree's case sensitivity. However, if the SMB_IGNORE_CASE
!  * flag is set in the flags value passed as a parameter, a case insensitive
!  * lookup WILL be done (regardless of whether SMB_CASE_SENSITIVE is set
!  * or not).
!  */
! 
! int
! smb_fsop_lookup_file(
!     smb_request_t *sr,
!     cred_t      *cr,
!     int         flags,
!     smb_node_t  *root_node,
!     smb_node_t  *dnode,
!     char        *name,
!     char        **sname,
!     smb_node_t  **ret_snode)
! {
          char            *fname;
          int             rc;
  
          ASSERT(cr);
          ASSERT(dnode);
          ASSERT(dnode->n_magic == SMB_NODE_MAGIC);
          ASSERT(dnode->n_state != SMB_NODE_STATE_DESTROYING);
+         ASSERT(ret_snode != NULL);
  
          /*
           * The following check is required for streams processing, below
           */
  
*** 1736,1817 ****
          if (!(flags & SMB_CASE_SENSITIVE)) {
                  if (SMB_TREE_IS_CASEINSENSITIVE(sr))
                          flags |= SMB_IGNORE_CASE;
          }
  
          fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
!         sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
  
-         if (smb_is_stream_name(name)) {
-                 smb_stream_parse_name(name, fname, sname);
- 
                  /*
                   * Look up the unnamed stream (i.e. fname).
                   * Unmangle processing will be done on fname
                   * as well as any link target.
                   */
                  rc = smb_fsop_lookup(sr, cr, flags, root_node, dnode,
!                     fname, &fnode);
! 
!                 if (rc != 0) {
                          kmem_free(fname, MAXNAMELEN);
!                         kmem_free(sname, MAXNAMELEN);
                          return (rc);
                  }
  
                  od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
  
                  /*
                   * od_name is the on-disk name of the stream, except
                   * without the prepended stream prefix (SMB_STREAM_PREFIX)
                   */
  
-                 /*
-                  * XXX
-                  * What permissions NTFS requires for stream lookup if any?
-                  */
                  rc = smb_vop_stream_lookup(fnode->vp, sname, &vp, od_name,
                      &xattrdirvp, flags, root_node->vp, cr);
  
                  if (rc != 0) {
-                         smb_node_release(fnode);
-                         kmem_free(fname, MAXNAMELEN);
-                         kmem_free(sname, MAXNAMELEN);
                          kmem_free(od_name, MAXNAMELEN);
                          return (rc);
                  }
  
                  *ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp,
                      vp, od_name);
  
                  kmem_free(od_name, MAXNAMELEN);
-                 smb_node_release(fnode);
                  VN_RELE(xattrdirvp);
                  VN_RELE(vp);
  
!                 if (*ret_snode == NULL) {
!                         kmem_free(fname, MAXNAMELEN);
!                         kmem_free(sname, MAXNAMELEN);
                          return (ENOMEM);
-                 }
-         } else {
-                 rc = smb_fsop_lookup(sr, cr, flags, root_node, dnode, name,
-                     ret_snode);
-         }
  
-         if (rc == 0) {
-                 ASSERT(ret_snode);
-                 if (SMB_TREE_CONTAINS_NODE(sr, *ret_snode) == 0) {
-                         smb_node_release(*ret_snode);
-                         *ret_snode = NULL;
-                         rc = EACCES;
-                 }
-         }
- 
-         kmem_free(fname, MAXNAMELEN);
-         kmem_free(sname, MAXNAMELEN);
- 
          return (rc);
  }
  
  /*
   * smb_fsop_lookup
--- 1900,1998 ----
          if (!(flags & SMB_CASE_SENSITIVE)) {
                  if (SMB_TREE_IS_CASEINSENSITIVE(sr))
                          flags |= SMB_IGNORE_CASE;
          }
  
+         *sname = NULL;
+         if (smb_is_stream_name(name)) {
+                 *sname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
                  fname = kmem_alloc(MAXNAMELEN, KM_SLEEP);
!                 smb_stream_parse_name(name, fname, *sname);
  
                  /*
                   * Look up the unnamed stream (i.e. fname).
                   * Unmangle processing will be done on fname
                   * as well as any link target.
                   */
                  rc = smb_fsop_lookup(sr, cr, flags, root_node, dnode,
!                     fname, ret_snode);
                  kmem_free(fname, MAXNAMELEN);
!         } else {
!                 rc = smb_fsop_lookup(sr, cr, flags, root_node, dnode, name,
!                     ret_snode);
!         }
! 
!         if (rc == 0) {
!                 ASSERT(ret_snode);
!                 if (SMB_TREE_CONTAINS_NODE(sr, *ret_snode) == 0) {
!                         smb_node_release(*ret_snode);
!                         *ret_snode = NULL;
!                         rc = EACCES;
!                 }
!         }
! 
!         if (rc != 0 && *sname != NULL) {
!                 kmem_free(*sname, MAXNAMELEN);
!                 *sname = NULL;
!         }
          return (rc);
+ }
+ 
+ /*
+  * smb_fsop_lookup_stream
+  *
+  * The file exists, see if the stream exists.
+  */
+ int
+ smb_fsop_lookup_stream(
+     smb_request_t *sr,
+     cred_t *cr,
+     int flags,
+     smb_node_t *root_node,
+     smb_node_t *fnode,
+     char *sname,
+     smb_node_t **ret_snode)
+ {
+         char            *od_name;
+         vnode_t         *xattrdirvp;
+         vnode_t         *vp;
+         int rc;
+ 
+         /*
+          * The following check is required for streams processing, below
+          */
+ 
+         if (!(flags & SMB_CASE_SENSITIVE)) {
+                 if (SMB_TREE_IS_CASEINSENSITIVE(sr))
+                         flags |= SMB_IGNORE_CASE;
          }
  
          od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
  
          /*
           * od_name is the on-disk name of the stream, except
           * without the prepended stream prefix (SMB_STREAM_PREFIX)
           */
  
          rc = smb_vop_stream_lookup(fnode->vp, sname, &vp, od_name,
              &xattrdirvp, flags, root_node->vp, cr);
  
          if (rc != 0) {
                  kmem_free(od_name, MAXNAMELEN);
                  return (rc);
          }
  
          *ret_snode = smb_stream_node_lookup(sr, cr, fnode, xattrdirvp,
              vp, od_name);
  
          kmem_free(od_name, MAXNAMELEN);
          VN_RELE(xattrdirvp);
          VN_RELE(vp);
  
!         if (*ret_snode == NULL)
                  return (ENOMEM);
  
          return (rc);
  }
  
  /*
   * smb_fsop_lookup
*** 1879,1890 ****
          if (SMB_TREE_SUPPORTS_CATIA(sr))
                  flags |= SMB_CATIA;
          if (SMB_TREE_SUPPORTS_ABE(sr))
                  flags |= SMB_ABE;
  
!         od_name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
  
          rc = smb_vop_lookup(dnode->vp, name, &vp, od_name, flags,
              &ret_flags, root_node ? root_node->vp : NULL, &attr, cr);
  
          if (rc != 0) {
                  if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) ||
--- 2060,2080 ----
          if (SMB_TREE_SUPPORTS_CATIA(sr))
                  flags |= SMB_CATIA;
          if (SMB_TREE_SUPPORTS_ABE(sr))
                  flags |= SMB_ABE;
  
!         /*
!          * Can have "" or "." when opening named streams on a directory.
!          */
!         if (name[0] == '\0' || (name[0] == '.' && name[1] == '\0')) {
!                 smb_node_ref(dnode);
!                 *ret_snode = dnode;
!                 return (0);
!         }
  
+         od_name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
+ 
          rc = smb_vop_lookup(dnode->vp, name, &vp, od_name, flags,
              &ret_flags, root_node ? root_node->vp : NULL, &attr, cr);
  
          if (rc != 0) {
                  if (!SMB_TREE_SUPPORTS_SHORTNAMES(sr) ||
*** 1925,1935 ****
          }
  
          if ((flags & SMB_FOLLOW_LINKS) && (vp->v_type == VLNK) &&
              ((attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)) {
                  rc = smb_pathname(sr, od_name, FOLLOW, root_node, dnode,
!                     &lnk_dnode, &lnk_target_node, cr);
  
                  if (rc != 0) {
                          /*
                           * The link is assumed to be for the last component
                           * of a path.  Hence any ENOTDIR error will be returned
--- 2115,2125 ----
          }
  
          if ((flags & SMB_FOLLOW_LINKS) && (vp->v_type == VLNK) &&
              ((attr.sa_dosattr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)) {
                  rc = smb_pathname(sr, od_name, FOLLOW, root_node, dnode,
!                     &lnk_dnode, &lnk_target_node, cr, NULL);
  
                  if (rc != 0) {
                          /*
                           * The link is assumed to be for the last component
                           * of a path.  Hence any ENOTDIR error will be returned
*** 2034,2047 ****
  {
          int error = 0;
          int flags = 0;
          int access = 0;
          acl_t *acl;
-         smb_node_t *unnamed_node;
  
          ASSERT(cr);
  
          if (SMB_TREE_HAS_ACCESS(sr, ACE_READ_ACL) == 0)
                  return (EACCES);
  
          if (sr->fid_ofile) {
                  if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
--- 2224,2240 ----
  {
          int error = 0;
          int flags = 0;
          int access = 0;
          acl_t *acl;
  
          ASSERT(cr);
  
+         /* Can't query security on named streams */
+         if (SMB_IS_STREAM(snode) != NULL)
+                 return (EINVAL);
+ 
          if (SMB_TREE_HAS_ACCESS(sr, ACE_READ_ACL) == 0)
                  return (EACCES);
  
          if (sr->fid_ofile) {
                  if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
*** 2054,2073 ****
                  if (error != NT_STATUS_SUCCESS) {
                          return (EACCES);
                  }
          }
  
-         unnamed_node = SMB_IS_STREAM(snode);
-         if (unnamed_node) {
-                 ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
-                 ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
-                 /*
-                  * Streams don't have ACL, any read ACL attempt on a stream
-                  * should be performed on the unnamed stream.
-                  */
-                 snode = unnamed_node;
-         }
  
          if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_ACEMASKONACCESS))
                  flags = ATTR_NOACLCHECK;
  
          error = smb_vop_acl_read(snode->vp, &acl, flags,
--- 2247,2256 ----
*** 2100,2118 ****
          int target_flavor;
          int error = 0;
          int flags = 0;
          int access = 0;
          acl_t *acl, *dacl, *sacl;
-         smb_node_t *unnamed_node;
  
          ASSERT(cr);
  
          ASSERT(sr);
          ASSERT(sr->tid_tree);
          if (SMB_TREE_IS_READONLY(sr))
                  return (EROFS);
  
          if (SMB_TREE_HAS_ACCESS(sr, ACE_WRITE_ACL) == 0)
                  return (EACCES);
  
          if (sr->fid_ofile) {
                  if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
--- 2283,2304 ----
          int target_flavor;
          int error = 0;
          int flags = 0;
          int access = 0;
          acl_t *acl, *dacl, *sacl;
  
          ASSERT(cr);
  
          ASSERT(sr);
          ASSERT(sr->tid_tree);
          if (SMB_TREE_IS_READONLY(sr))
                  return (EROFS);
  
+         /* Can't set security on named streams */
+         if (SMB_IS_STREAM(snode) != NULL)
+                 return (EINVAL);
+ 
          if (SMB_TREE_HAS_ACCESS(sr, ACE_WRITE_ACL) == 0)
                  return (EACCES);
  
          if (sr->fid_ofile) {
                  if (fs_sd->sd_secinfo & SMB_DACL_SECINFO)
*** 2136,2156 ****
                  break;
          default:
                  return (EINVAL);
          }
  
-         unnamed_node = SMB_IS_STREAM(snode);
-         if (unnamed_node) {
-                 ASSERT(unnamed_node->n_magic == SMB_NODE_MAGIC);
-                 ASSERT(unnamed_node->n_state != SMB_NODE_STATE_DESTROYING);
-                 /*
-                  * Streams don't have ACL, any write ACL attempt on a stream
-                  * should be performed on the unnamed stream.
-                  */
-                 snode = unnamed_node;
-         }
- 
          dacl = fs_sd->sd_zdacl;
          sacl = fs_sd->sd_zsacl;
  
          ASSERT(dacl || sacl);
          if ((dacl == NULL) && (sacl == NULL))
--- 2322,2331 ----
*** 2201,2210 ****
--- 2376,2389 ----
          smb_attr_t attr;
  
          ASSERT(cr);
          ASSERT(fs_sd);
  
+         /* Can't query security on named streams */
+         if (SMB_IS_STREAM(snode) != NULL)
+                 return (EINVAL);
+ 
          /*
           * File's uid/gid is fetched in two cases:
           *
           * 1. it's explicitly requested
           *
*** 2366,2375 ****
--- 2545,2558 ----
          ASSERT(sr);
          ASSERT(sr->tid_tree);
          if (SMB_TREE_IS_READONLY(sr))
                  return (EROFS);
  
+         /* Can't set security on named streams */
+         if (SMB_IS_STREAM(snode) != NULL)
+                 return (EINVAL);
+ 
          bzero(&set_attr, sizeof (smb_attr_t));
  
          if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) {
                  set_attr.sa_vattr.va_uid = fs_sd->sd_uid;
                  set_attr.sa_mask |= SMB_AT_UID;
*** 2556,2565 ****
--- 2739,2757 ----
                  *eaccess |= FILE_EXECUTE;
  
          if (access & VWRITE)
                  *eaccess |= FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES |
                      FILE_WRITE_EA | FILE_APPEND_DATA | FILE_DELETE_CHILD;
+ 
+         if (access & (VREAD | VWRITE))
+                 *eaccess |= SYNCHRONIZE;
+ 
+ #ifdef  _FAKE_KERNEL
+         /* Should be: if (we are the owner)... */
+         if (access & VWRITE)
+                 *eaccess |= DELETE | WRITE_DAC | WRITE_OWNER;
+ #endif
  }
  
  /*
   * smb_fsop_shrlock
   *
*** 2636,2645 ****
--- 2828,2845 ----
           *    incompatibilities between POSIX and Windows. In the Windows world,
           *    if a client submits such a lock, the server will not lock any
           *    bytes. Interestingly if the same lock (same offset and length) is
           *    resubmitted Windows will consider that there is an overlap and
           *    the granting rules will then apply.
+          *
+          * 3) The SMB-level process IDs (smb_pid) are not passed down to the
+          *    POSIX level in l_pid because (a) the rules about lock PIDs are
+          *    different in SMB, and (b) we're putting our ofile f_uniqid in
+          *    the POSIX l_pid field to segregate locks per SMB ofile.
+          *    (We're also using a "remote" system ID in l_sysid.)
+          *    All SMB locking PIDs are handled at the SMB level and
+          *    not exposed in POSIX locking.
           */
          if ((lock->l_length == 0) ||
              ((lock->l_start + lock->l_length - 1) < lock->l_start))
                  return (0);