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
*/