Print this page
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-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-2831 panic in smb_make_link
NEX-2442 regression with smbtorture test raw.sfileinfo.rename
NEX-1920 SMB rename from Win2k8 fails
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)

@@ -18,15 +18,15 @@
  *
  * CDDL HEADER END
  */
 /*
  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <sys/synch.h>
-#include <smbsrv/smb_kproto.h>
+#include <smbsrv/smb2_kproto.h>
 #include <smbsrv/smb_fsops.h>
 #include <sys/nbmlock.h>
 
 /*
  * SMB_TRANS2_SET_FILE/PATH_INFO (RENAME_INFORMATION level) flag

@@ -34,10 +34,11 @@
 #define SMB_RENAME_FLAG_OVERWRITE       0x001
 
 static int smb_rename_check_stream(smb_fqi_t *, smb_fqi_t *);
 static int smb_rename_check_attr(smb_request_t *, smb_node_t *, uint16_t);
 static int smb_rename_lookup_src(smb_request_t *);
+static uint32_t smb_rename_check_src(smb_request_t *, smb_fqi_t *);
 static void smb_rename_release_src(smb_request_t *);
 static uint32_t smb_rename_errno2status(int);
 
 /*
  * smb_setinfo_rename

@@ -97,11 +98,16 @@
         smb_node_t *src_fnode, *src_dnode, *dst_dnode;
         smb_node_t *dst_fnode = 0;
         smb_node_t *tnode;
         char *new_name, *path;
         DWORD status;
-        int rc, count;
+        int rc;
+        boolean_t have_src = B_FALSE;
+        boolean_t dst_exists = B_FALSE;
+        boolean_t do_audit;
+        char *srcpath = NULL;
+        char *dstpath = NULL;
 
         tnode = sr->tid_tree->t_snode;
         path = dst_fqi->fq_path.pn_path;
 
         /* Check if attempting to rename a stream - not yet supported */

@@ -109,28 +115,43 @@
         if (rc != 0)
                 return (smb_rename_errno2status(rc));
 
         /*
          * The source node may already have been provided,
-         * i.e. when called by SMB1/SMB2 smb_setinfo_rename.
-         * Not provided by smb_com_rename, smb_com_nt_rename.
+         * i.e. when called by SMB1/SMB2 smb_setinfo_rename
+         * with an ofile.  When we have an ofile, open has
+         * already checked for sharing violations.  For
+         * path-based operations, do sharing check here.
          */
         if (src_fqi->fq_fnode) {
-                smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
-                smb_node_ref(src_fqi->fq_fnode);
                 smb_node_ref(src_fqi->fq_dnode);
+                smb_node_ref(src_fqi->fq_fnode);
+                have_src = B_TRUE;
         } else {
                 /* lookup and validate src node */
                 rc = smb_rename_lookup_src(sr);
                 if (rc != 0)
                         return (smb_rename_errno2status(rc));
+                /* Holding refs on dnode, fnode */
         }
-
         src_fnode = src_fqi->fq_fnode;
         src_dnode = src_fqi->fq_dnode;
 
+        /* Break oplocks, and check share modes. */
+        status = smb_rename_check_src(sr, src_fqi);
+        if (status != NT_STATUS_SUCCESS) {
+                smb_node_release(src_fqi->fq_fnode);
+                smb_node_release(src_fqi->fq_dnode);
+                return (status);
+        }
         /*
+         * NB: src_fnode is now "in crit" (critical section)
+         * as if we did smb_node_start_crit(..., RW_READER);
+         * Call smb_rename_release_src(sr) on errors.
+         */
+
+        /*
          * Find the destination dnode and last component.
          * May already be provided, i.e. when called via
          * SMB1 trans2 setinfo.
          */
         if (dst_fqi->fq_dnode) {

@@ -232,28 +253,26 @@
                         smb_node_release(dst_fnode);
                         smb_node_release(dst_dnode);
                         return (NT_STATUS_OBJECT_NAME_COLLISION);
                 }
 
-                (void) smb_oplock_break(sr, dst_fnode,
-                    SMB_OPLOCK_BREAK_TO_NONE | SMB_OPLOCK_BREAK_BATCH);
+                status = smb_oplock_break_DELETE(dst_fnode, NULL);
+                if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
+                        if (sr->session->dialect >= SMB_VERS_2_BASE)
+                                (void) smb2sr_go_async(sr);
+                        (void) smb_oplock_wait_break(dst_fnode, 0);
+                        status = 0;
+                }
+                if (status != 0) {
+                        smb_rename_release_src(sr);
+                        smb_node_release(dst_fnode);
+                        smb_node_release(dst_dnode);
+                        return (status);
+                }
 
-                /*
-                 * Wait (a little) for the oplock break to be
-                 * responded to by clients closing handles.
-                 * Hold node->n_lock as reader to keep new
-                 * ofiles from showing up after we check.
-                 */
                 smb_node_rdlock(dst_fnode);
-                for (count = 0; count <= 12; count++) {
                         status = smb_node_delete_check(dst_fnode);
-                        if (status != NT_STATUS_SHARING_VIOLATION)
-                                break;
-                        smb_node_unlock(dst_fnode);
-                        delay(MSEC_TO_TICK(100));
-                        smb_node_rdlock(dst_fnode);
-                }
                 if (status != NT_STATUS_SUCCESS) {
                         smb_node_unlock(dst_fnode);
                         smb_rename_release_src(sr);
                         smb_node_release(dst_fnode);
                         smb_node_release(dst_dnode);

@@ -283,16 +302,46 @@
                         smb_node_release(dst_dnode);
                         return (NT_STATUS_ACCESS_DENIED);
                 }
 
                 new_name = dst_fnode->od_name;
+                dst_exists = B_TRUE;
         }
 
+        do_audit = smb_audit_rename_init(sr);
+        /* save paths for later auditing */
+        if (do_audit) {
+                if (!have_src) {
+                        srcpath = kmem_alloc(SMB_MAXPATHLEN, KM_SLEEP);
+                        smb_node_getpath_nofail(src_fnode, smb_audit_rootvp(sr),
+                            srcpath, SMB_MAXPATHLEN);
+                }
+                if (dst_exists) {
+                        dstpath = kmem_alloc(SMB_MAXPATHLEN, KM_SLEEP);
+                        smb_node_getpath_nofail(dst_fnode, smb_audit_rootvp(sr),
+                            dstpath, SMB_MAXPATHLEN);
+                }
+        }
+
         rc = smb_fsop_rename(sr, sr->user_cr,
             src_dnode, src_fnode->od_name,
             dst_dnode, new_name);
 
+        if (do_audit) {
+                smb_audit_rename_fini(sr,
+                    srcpath,
+                    dst_dnode,
+                    dstpath,
+                    rc == 0,
+                    smb_node_is_dir(src_fnode));
+
+                if (srcpath != NULL)
+                        kmem_free(srcpath, SMB_MAXPATHLEN);
+                if (dstpath != NULL)
+                        kmem_free(dstpath, SMB_MAXPATHLEN);
+        }
+
         if (rc == 0) {
                 /*
                  * Note that renames in the same directory are normally
                  * delivered in {old,new} pairs, and clients expect them
                  * in that order, if both events are delivered.

@@ -432,27 +481,35 @@
                 return (NT_STATUS_INVALID_PARAMETER);
         }
 
         /* The source node may already have been provided */
         if (src_fqi->fq_fnode) {
-                smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
-                smb_node_ref(src_fqi->fq_fnode);
                 smb_node_ref(src_fqi->fq_dnode);
+                smb_node_ref(src_fqi->fq_fnode);
         } else {
                 /* lookup and validate src node */
                 rc = smb_rename_lookup_src(sr);
                 if (rc != 0)
                         return (smb_rename_errno2status(rc));
+                /* Holding refs on dnode, fnode */
         }
 
         /* Not valid to create hardlink for directory */
         if (smb_node_is_dir(src_fqi->fq_fnode)) {
-                smb_rename_release_src(sr);
+                smb_node_release(src_fqi->fq_dnode);
+                smb_node_release(src_fqi->fq_fnode);
                 return (NT_STATUS_FILE_IS_A_DIRECTORY);
         }
 
         /*
+         * Unlike in rename, we will not unlink the src,
+         * so skip the smb_rename_check_src() call, and
+         * just "start crit" instead.
+         */
+        smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
+
+        /*
          * Find the destination dnode and last component.
          * May already be provided, i.e. when called via
          * SMB1 trans2 setinfo.
          */
         if (dst_fqi->fq_dnode) {

@@ -508,28 +565,23 @@
 }
 
 /*
  * smb_rename_lookup_src
  *
- * Lookup the src node, checking for sharing violations and
- * breaking any existing BATCH oplock.
- * Populate sr->arg.dirop.fqi
+ * Lookup the src node for a path-based link or rename.
  *
- * Upon success, the dnode and fnode will have holds and the
- * fnode will be in a critical section. These should be
- * released using smb_rename_release_src().
+ * On success, fills in sr->arg.dirop.fqi, and returns with
+ * holds on the source dnode and fnode.
  *
  * Returns errno values.
  */
 static int
 smb_rename_lookup_src(smb_request_t *sr)
 {
-        smb_node_t *src_node, *tnode;
-        DWORD status;
-        int rc;
-        int count;
+        smb_node_t *tnode;
         char *path;
+        int rc;
 
         smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
 
         if (smb_is_stream_name(src_fqi->fq_path.pn_path))
                 return (EINVAL);

@@ -539,56 +591,107 @@
         path = src_fqi->fq_path.pn_path;
         rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
             &src_fqi->fq_dnode, src_fqi->fq_last_comp);
         if (rc != 0)
                 return (rc);
+        /* hold fq_dnode */
 
         rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
             src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode);
         if (rc != 0) {
                 smb_node_release(src_fqi->fq_dnode);
                 return (rc);
         }
-        src_node = src_fqi->fq_fnode;
+        /* hold fq_dnode, fq_fnode */
 
-        rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr);
+        rc = smb_rename_check_attr(sr, src_fqi->fq_fnode, src_fqi->fq_sattr);
         if (rc != 0) {
                 smb_node_release(src_fqi->fq_fnode);
                 smb_node_release(src_fqi->fq_dnode);
                 return (rc);
         }
 
+        return (0);
+}
+
+/*
+ * smb_rename_check_src
+ *
+ * Check for sharing violations on the file we'll unlink, and
+ * break oplocks for the rename operation.  Note that we've
+ * already done oplock breaks associated with opening a handle
+ * on the file to rename.
+ *
+ * On success, returns with fnode in a critical section,
+ * as if smb_node_start_crit were called with the node.
+ * Caller should release using smb_rename_release_src().
+ */
+static uint32_t
+smb_rename_check_src(smb_request_t *sr, smb_fqi_t *src_fqi)
+{
+        smb_node_t *src_node = src_fqi->fq_fnode;
+        uint32_t status;
+
         /*
          * Break BATCH oplock before ofile checks. If a client
          * has a file open, this will force a flush or close,
          * which may affect the outcome of any share checking.
+         *
+         * This operation may have either a handle or path for
+         * the source node (that will be unlinked via rename).
          */
-        (void) smb_oplock_break(sr, src_node,
-            SMB_OPLOCK_BREAK_TO_LEVEL_II | SMB_OPLOCK_BREAK_BATCH);
 
+        if (sr->fid_ofile != NULL) {
+                status = smb_oplock_break_SETINFO(src_node, sr->fid_ofile,
+                    FileRenameInformation);
+                if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
+                        if (sr->session->dialect >= SMB_VERS_2_BASE)
+                                (void) smb2sr_go_async(sr);
+                        (void) smb_oplock_wait_break(src_node, 0);
+                        status = 0;
+                }
+
         /*
-         * Wait (a little) for the oplock break to be
-         * responded to by clients closing handles.
-         * Hold node->n_lock as reader to keep new
-         * ofiles from showing up after we check.
+                 * Sharing violations were checked at open time.
+                 * Just "start crit" to be consistent with the
+                 * state returned for path-based rename.
          */
+                smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
+                return (NT_STATUS_SUCCESS);
+        }
+
+        /*
+         * This code path operates without a real open, so
+         * break oplocks now as if we opened for delete.
+         * Note: SMB2 does only ofile-based rename.
+         *
+         * Todo:  Use an "internal open" for path-based
+         * rename and delete, then delete this code.
+         */
+        ASSERT(sr->session->dialect < SMB_VERS_2_BASE);
+        status = smb_oplock_break_DELETE(src_node, NULL);
+        if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
+                (void) smb_oplock_wait_break(src_node, 0);
+        }
+
+        /*
+         * Path-based access to the src file (no ofile)
+         * so check for sharing violations here.
+         */
         smb_node_rdlock(src_node);
-        for (count = 0; count <= 12; count++) {
                 status = smb_node_rename_check(src_node);
-                if (status != NT_STATUS_SHARING_VIOLATION)
-                        break;
-                smb_node_unlock(src_node);
-                delay(MSEC_TO_TICK(100));
-                smb_node_rdlock(src_node);
-        }
         if (status != NT_STATUS_SUCCESS) {
                 smb_node_unlock(src_node);
-                smb_node_release(src_fqi->fq_fnode);
-                smb_node_release(src_fqi->fq_dnode);
-                return (EPIPE); /* = ERRbadshare */
+                return (status);
         }
 
+        status = smb_oplock_break_SETINFO(src_node, NULL,
+            FileRenameInformation);
+        if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
+                (void) smb_oplock_wait_break(src_node, 0);
+        }
+
         /*
          * Note, the combination of these two:
          *      smb_node_rdlock(node);
          *      nbl_start_crit(node->vp, RW_READER);
          * is equivalent to this call:

@@ -603,19 +706,14 @@
          * This checks nbl_share_conflict, nbl_lock_conflict
          */
         status = smb_nbl_conflict(src_node, 0, UINT64_MAX, NBL_RENAME);
         if (status != NT_STATUS_SUCCESS) {
                 smb_node_end_crit(src_node);
-                smb_node_release(src_fqi->fq_fnode);
-                smb_node_release(src_fqi->fq_dnode);
-                if (status == NT_STATUS_SHARING_VIOLATION)
-                        return (EPIPE); /* = ERRbadshare */
-                return (EACCES);
         }
 
-        /* NB: Caller expects holds on src_fqi fnode, dnode */
-        return (0);
+        /* NB: Caller expects to be "in crit" on fnode. */
+        return (status);
 }
 
 /*
  * smb_rename_release_src
  */