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,32 **** * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ #include <sys/synch.h> ! #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_fsops.h> #include <sys/nbmlock.h> /* * SMB_TRANS2_SET_FILE/PATH_INFO (RENAME_INFORMATION level) flag --- 18,32 ---- * * 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/synch.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,43 **** --- 34,44 ---- #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,107 **** 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; tnode = sr->tid_tree->t_snode; path = dst_fqi->fq_path.pn_path; /* Check if attempting to rename a stream - not yet supported */ --- 98,113 ---- 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; ! 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,136 **** 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. */ 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); } else { /* lookup and validate src node */ rc = smb_rename_lookup_src(sr); if (rc != 0) return (smb_rename_errno2status(rc)); } - src_fnode = src_fqi->fq_fnode; src_dnode = src_fqi->fq_dnode; /* * Find the destination dnode and last component. * May already be provided, i.e. when called via * SMB1 trans2 setinfo. */ if (dst_fqi->fq_dnode) { --- 115,157 ---- 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 ! * 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_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,259 **** 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); - /* - * 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); --- 253,278 ---- smb_node_release(dst_fnode); smb_node_release(dst_dnode); return (NT_STATUS_OBJECT_NAME_COLLISION); } ! 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); ! } smb_node_rdlock(dst_fnode); status = smb_node_delete_check(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,298 **** --- 302,347 ---- 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,458 **** 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); } else { /* lookup and validate src node */ rc = smb_rename_lookup_src(sr); if (rc != 0) return (smb_rename_errno2status(rc)); } /* Not valid to create hardlink for directory */ if (smb_node_is_dir(src_fqi->fq_fnode)) { ! smb_rename_release_src(sr); return (NT_STATUS_FILE_IS_A_DIRECTORY); } /* * Find the destination dnode and last component. * May already be provided, i.e. when called via * SMB1 trans2 setinfo. */ if (dst_fqi->fq_dnode) { --- 481,515 ---- return (NT_STATUS_INVALID_PARAMETER); } /* The source node may already have been provided */ if (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_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,535 **** } /* * smb_rename_lookup_src * ! * Lookup the src node, checking for sharing violations and ! * breaking any existing BATCH oplock. ! * Populate sr->arg.dirop.fqi * ! * 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(). * * 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; char *path; smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; if (smb_is_stream_name(src_fqi->fq_path.pn_path)) return (EINVAL); --- 565,587 ---- } /* * smb_rename_lookup_src * ! * Lookup the src node for a path-based link or rename. * ! * 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 *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,594 **** 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); 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; ! rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr); if (rc != 0) { smb_node_release(src_fqi->fq_fnode); smb_node_release(src_fqi->fq_dnode); return (rc); } /* * 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. */ - (void) smb_oplock_break(sr, src_node, - SMB_OPLOCK_BREAK_TO_LEVEL_II | SMB_OPLOCK_BREAK_BATCH); /* ! * 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(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 */ } /* * Note, the combination of these two: * smb_node_rdlock(node); * nbl_start_crit(node->vp, RW_READER); * is equivalent to this call: --- 591,697 ---- 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); } ! /* hold fq_dnode, fq_fnode */ ! 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). */ + 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; + } + /* ! * 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); status = smb_node_rename_check(src_node); if (status != NT_STATUS_SUCCESS) { smb_node_unlock(src_node); ! 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,621 **** * 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); } /* * smb_rename_release_src */ --- 706,719 ---- * 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); } ! /* NB: Caller expects to be "in crit" on fnode. */ ! return (status); } /* * smb_rename_release_src */