Print this page
NEX-19471 A handle to a newly created Named Stream may not cause sharing violations
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@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-17431 (HyperV) Windows VM goes panic after failover
NEX-18639 Panic in smb_ofile_release with expired ofile
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@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-9808 SMB3 persistent handles
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-15555 SMB2 async redesign
NEX-15061 smtorture smb2.lock.cancel.cancel is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Also follow-up change to:
 NEX-1643 dtrace provider for smbsrv (remove "done2" probes,
 which don't make sense with the new async design)
NEX-15579 SMB should not create in directories marked delete-on-close
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-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-15555 SMB2 async redesign
NEX-15061 smtorture smb2.lock.cancel.cancel is failed
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Also follow-up change to:
 NEX-1643 dtrace provider for smbsrv (remove "done2" probes,
 which don't make sense with the new async design)
NEX-15579 SMB should not create in directories marked delete-on-close
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-14832 smb fails to delete read-only file after NEX-9604
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@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-9604 SMB: smb2 does not delete a read-only file, where smb1 does
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9864 Some SMB cancel races remain after NEX-5845
Revert (part of) "NEX-5845 rework SMB immediate cancel"
reverts (part of) commit 7a5da69f6d42b17ebcc95ca3d02925d07a01343e.
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-9098 Hitting file handler count limit of 16k
Reviewed by: Alek Pinchuk <alek.pinchuk@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-6258 SMB grants WRITE_ATTRIBUTES when it should not
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-5844 want SMB2 ioctl FSCTL_SRV_COPYCHUNK
NEX-6124 smb_fsop_read/write should allow file != sr->fid_ofile
NEX-6125 smbtorture invalid response with smb2.ioctl
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-4538 SMB1 create file should support extended_response format (2)
NEX-6116 Failures in smbtorture raw.open
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Include this commit if upstreaming/backporting any of:
NEX-4540 SMB server declines EA support incorrectly
NEX-4239 smbtorture create failures re. allocation size
(illumos) 6398 SMB should support path names longer than 1024
NEX-3553 SMB2/3 durable handles
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-5312 delete_on_close should be acted on earlier
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-3733 Want SMB2 Apple extensions
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-4239 smbtorture create failures re. allocation size
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-3432 CLONE - NEX-3232 Symantec Backup Exec fails opening files over SMB
Reviewed by: Alek Pinchuk <alek@nexenta.com>
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Daniel Borek <daniel.borek@nexenta.com>
OS-266 sync v8 mdb module to illumos-joyent f1ab5c2
NEX-1633 smb_open_attr_only() does not return true if READ_CONTROL is set
SMB-115 Support SMB path names with length > 1024
SMB-100 Internal error if filename is too long
Approved by: Gordon Ross <gwr@nexenta.com>
SMB-103 SMB2_create with access MAXIMUM_ALLOWED fails
SMB-92 Panic with smbtorture raw.samba3oplocklogoff
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-39 Use AF_UNIX pipes for RPC
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())
SUP-694 panic on bad mutex in smb_event_wait()
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 #14152 Race between ipmi_submit_driver_request() and kcs_loop() (sync with illumos fix 3902)
SMB-46 File handle leaks exposed by mtime fixes (rm 7815)
re #7815 SMB server delivers old modification time...

*** 19,29 **** * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ /* * This module provides the common open functionality to the various * open and create SMB interface functions. --- 19,29 ---- * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. ! * Copyright 2019 Nexenta Systems, Inc. All rights reserved. */ /* * This module provides the common open functionality to the various * open and create SMB interface functions.
*** 35,55 **** #include <sys/nbmlock.h> #include <smbsrv/string.h> #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_fsops.h> #include <smbsrv/smbinfo.h> ! static volatile uint32_t smb_fids = 0; ! #define SMB_UNIQ_FID() atomic_inc_32_nv(&smb_fids) - static uint32_t smb_open_subr(smb_request_t *); extern uint32_t smb_is_executable(char *); static void smb_delete_new_object(smb_request_t *); static int smb_set_open_attributes(smb_request_t *, smb_ofile_t *); - static void smb_open_oplock_break(smb_request_t *, smb_node_t *); - static boolean_t smb_open_attr_only(smb_arg_open_t *); - static boolean_t smb_open_overwrite(smb_arg_open_t *); /* * smb_access_generic_to_file * * Search MSDN for IoCreateFile to see following mapping. --- 35,51 ---- #include <sys/nbmlock.h> #include <smbsrv/string.h> #include <smbsrv/smb_kproto.h> #include <smbsrv/smb_fsops.h> #include <smbsrv/smbinfo.h> + #include <smbsrv/smb2_kproto.h> ! int smb_session_ofile_max = 32768; extern uint32_t smb_is_executable(char *); static void smb_delete_new_object(smb_request_t *); static int smb_set_open_attributes(smb_request_t *, smb_ofile_t *); /* * smb_access_generic_to_file * * Search MSDN for IoCreateFile to see following mapping.
*** 107,117 **** case SMB_DA_ACCESS_READ_WRITE: return (FILE_GENERIC_READ | FILE_GENERIC_WRITE); case SMB_DA_ACCESS_EXECUTE: ! return (FILE_GENERIC_EXECUTE); default: return (FILE_GENERIC_ALL); } } --- 103,113 ---- case SMB_DA_ACCESS_READ_WRITE: return (FILE_GENERIC_READ | FILE_GENERIC_WRITE); case SMB_DA_ACCESS_EXECUTE: ! return (FILE_GENERIC_READ | FILE_GENERIC_EXECUTE); default: return (FILE_GENERIC_ALL); } }
*** 171,214 **** return (ofun_cr_map[row][col]); } /* ! * Retry opens to avoid spurious sharing violations, due to timing ! * issues between closes and opens. The client that already has the ! * file open may be in the process of closing it. ! */ ! uint32_t ! smb_common_open(smb_request_t *sr) ! { ! smb_arg_open_t *parg; ! uint32_t status = NT_STATUS_SUCCESS; ! int count; ! ! parg = kmem_alloc(sizeof (*parg), KM_SLEEP); ! bcopy(&sr->arg.open, parg, sizeof (*parg)); ! ! for (count = 0; count <= 4; count++) { ! if (count != 0) ! delay(MSEC_TO_TICK(400)); ! ! status = smb_open_subr(sr); ! if (status != NT_STATUS_SHARING_VIOLATION) ! break; ! ! bcopy(parg, &sr->arg.open, sizeof (*parg)); ! } ! ! if (status == NT_STATUS_NO_SUCH_FILE) ! status = NT_STATUS_OBJECT_NAME_NOT_FOUND; ! ! kmem_free(parg, sizeof (*parg)); ! return (status); ! } ! ! /* ! * smb_open_subr * * Notes on write-through behaviour. It looks like pre-LM0.12 versions * of the protocol specify the write-through mode when a file is opened, * (SmbOpen, SmbOpenAndX) so the write calls (SmbWrite, SmbWriteAndClose, * SmbWriteAndUnlock) don't need to contain a write-through flag. --- 167,177 ---- return (ofun_cr_map[row][col]); } /* ! * smb_common_open * * Notes on write-through behaviour. It looks like pre-LM0.12 versions * of the protocol specify the write-through mode when a file is opened, * (SmbOpen, SmbOpenAndX) so the write calls (SmbWrite, SmbWriteAndClose, * SmbWriteAndUnlock) don't need to contain a write-through flag.
*** 240,257 **** * DOS readonly bit rules * * 1. The creator of a readonly file can write to/modify the size of the file * using the original create fid, even though the file will appear as readonly * to all other fids and via a CIFS getattr call. - * The readonly bit therefore cannot be set in the filesystem until the file - * is closed (smb_ofile_close). It is accounted for via ofile and node flags. * * 2. A setinfo operation (using either an open fid or a path) to set/unset * readonly will be successful regardless of whether a creator of a readonly ! * file has an open fid (and has the special privilege mentioned in #1, ! * above). I.e., the creator of a readonly fid holding that fid will no longer ! * have a special privilege. * * 3. The DOS readonly bit affects only data and some metadata. * The following metadata can be changed regardless of the readonly bit: * - security descriptors * - DOS attributes --- 203,216 ---- * DOS readonly bit rules * * 1. The creator of a readonly file can write to/modify the size of the file * using the original create fid, even though the file will appear as readonly * to all other fids and via a CIFS getattr call. * * 2. A setinfo operation (using either an open fid or a path) to set/unset * readonly will be successful regardless of whether a creator of a readonly ! * file has an open fid. * * 3. The DOS readonly bit affects only data and some metadata. * The following metadata can be changed regardless of the readonly bit: * - security descriptors * - DOS attributes
*** 285,317 **** * - otherwise, applies attributes + FILE_ATTRIBUTE_ARCHIVE. * * 4. Opening an existing file or directory * The request attributes are ignored. */ ! static uint32_t ! smb_open_subr(smb_request_t *sr) { ! boolean_t created = B_FALSE; ! boolean_t last_comp_found = B_FALSE; ! smb_node_t *node = NULL; smb_node_t *dnode = NULL; smb_node_t *cur_node = NULL; smb_arg_open_t *op = &sr->sr_open; ! int rc; ! smb_ofile_t *of; smb_attr_t new_attr; int max_requested = 0; uint32_t max_allowed; uint32_t status = NT_STATUS_SUCCESS; int is_dir; ! smb_error_t err; boolean_t is_stream = B_FALSE; int lookup_flags = SMB_FOLLOW_LINKS; ! uint32_t uniq_fid; ! smb_pathname_t *pn = &op->fqi.fq_path; ! smb_server_t *sv = sr->sr_server; is_dir = (op->create_options & FILE_DIRECTORY_FILE) ? 1 : 0; /* * If the object being created or opened is a directory * the Disposition parameter must be one of FILE_CREATE, --- 244,299 ---- * - otherwise, applies attributes + FILE_ATTRIBUTE_ARCHIVE. * * 4. Opening an existing file or directory * The request attributes are ignored. */ ! uint32_t ! smb_common_open(smb_request_t *sr) { ! smb_server_t *sv = sr->sr_server; ! smb_tree_t *tree = sr->tid_tree; ! smb_node_t *fnode = NULL; smb_node_t *dnode = NULL; smb_node_t *cur_node = NULL; + smb_node_t *tmp_node = NULL; smb_arg_open_t *op = &sr->sr_open; ! smb_pathname_t *pn = &op->fqi.fq_path; ! smb_ofile_t *of = NULL; smb_attr_t new_attr; + hrtime_t shrlock_t0; int max_requested = 0; uint32_t max_allowed; uint32_t status = NT_STATUS_SUCCESS; int is_dir; ! int rc; boolean_t is_stream = B_FALSE; int lookup_flags = SMB_FOLLOW_LINKS; ! uint32_t uniq_fid = 0; ! uint16_t tree_fid = 0; ! boolean_t created = B_FALSE; ! boolean_t last_comp_found = B_FALSE; ! boolean_t stream_found = B_FALSE; ! boolean_t opening_incr = B_FALSE; ! boolean_t dnode_held = B_FALSE; ! boolean_t dnode_wlock = B_FALSE; ! boolean_t fnode_held = B_FALSE; ! boolean_t fnode_wlock = B_FALSE; ! boolean_t fnode_shrlk = B_FALSE; ! boolean_t did_open = B_FALSE; ! boolean_t did_break_handle = B_FALSE; ! boolean_t did_cleanup_orphans = B_FALSE; ! char *sname = NULL; ! boolean_t do_audit = B_FALSE; + /* Get out now if we've been cancelled. */ + mutex_enter(&sr->sr_mutex); + if (sr->sr_state != SMB_REQ_STATE_ACTIVE) { + mutex_exit(&sr->sr_mutex); + return (NT_STATUS_CANCELLED); + } + mutex_exit(&sr->sr_mutex); + is_dir = (op->create_options & FILE_DIRECTORY_FILE) ? 1 : 0; /* * If the object being created or opened is a directory * the Disposition parameter must be one of FILE_CREATE,
*** 329,345 **** max_requested = 1; op->desired_access &= ~MAXIMUM_ALLOWED; } op->desired_access = smb_access_generic_to_file(op->desired_access); ! if (sr->session->s_file_cnt >= SMB_SESSION_OFILE_MAX) { ASSERT(sr->uid_user); cmn_err(CE_NOTE, "smbsrv[%s\\%s]: TOO_MANY_OPENED_FILES", sr->uid_user->u_domain, sr->uid_user->u_name); return (NT_STATUS_TOO_MANY_OPENED_FILES); } /* This must be NULL at this point */ sr->fid_ofile = NULL; op->devstate = 0; --- 311,330 ---- max_requested = 1; op->desired_access &= ~MAXIMUM_ALLOWED; } op->desired_access = smb_access_generic_to_file(op->desired_access); ! if (sr->session->s_file_cnt >= smb_session_ofile_max) { ASSERT(sr->uid_user); cmn_err(CE_NOTE, "smbsrv[%s\\%s]: TOO_MANY_OPENED_FILES", sr->uid_user->u_domain, sr->uid_user->u_name); return (NT_STATUS_TOO_MANY_OPENED_FILES); } + if (smb_idpool_alloc(&tree->t_fid_pool, &tree_fid)) + return (NT_STATUS_TOO_MANY_OPENED_FILES); + /* This must be NULL at this point */ sr->fid_ofile = NULL; op->devstate = 0;
*** 360,683 **** /* * Limit the number of open pipe instances. */ if ((rc = smb_threshold_enter(&sv->sv_opipe_ct)) != 0) { status = RPC_NT_SERVER_TOO_BUSY; ! return (status); } /* ! * No further processing for IPC, we need to either ! * raise an exception or return success here. */ ! uniq_fid = SMB_UNIQ_FID(); ! status = smb_opipe_open(sr, uniq_fid); smb_threshold_exit(&sv->sv_opipe_ct); ! return (status); default: ! return (NT_STATUS_BAD_DEVICE_TYPE); } smb_pathname_init(sr, pn, pn->pn_path); ! if (!smb_pathname_validate(sr, pn)) ! return (sr->smb_error.status); if (strlen(pn->pn_path) >= SMB_MAXPATHLEN) { ! return (NT_STATUS_OBJECT_PATH_INVALID); } if (is_dir) { ! if (!smb_validate_dirname(sr, pn)) ! return (sr->smb_error.status); } else { ! if (!smb_validate_object_name(sr, pn)) ! return (sr->smb_error.status); } cur_node = op->fqi.fq_dnode ? op->fqi.fq_dnode : sr->tid_tree->t_snode; - /* - * if no path or filename are specified the stream should be - * created on cur_node - */ - if (!is_dir && !pn->pn_pname && !pn->pn_fname && pn->pn_sname) { - /* - * Can't currently handle a stream on the tree root. - * If a stream is being opened return "not found", otherwise - * return "access denied". - */ - if (cur_node == sr->tid_tree->t_snode) { - if (op->create_disposition == FILE_OPEN) { - return (NT_STATUS_OBJECT_NAME_NOT_FOUND); - } - return (NT_STATUS_ACCESS_DENIED); - } - - (void) snprintf(op->fqi.fq_last_comp, - sizeof (op->fqi.fq_last_comp), - "%s%s", cur_node->od_name, pn->pn_sname); - - op->fqi.fq_dnode = cur_node->n_dnode; - smb_node_ref(op->fqi.fq_dnode); - } else { rc = smb_pathname_reduce(sr, sr->user_cr, pn->pn_path, sr->tid_tree->t_snode, cur_node, &op->fqi.fq_dnode, op->fqi.fq_last_comp); if (rc != 0) { ! return (smb_errno2status(rc)); } ! } /* * If the access mask has only DELETE set (ignore * FILE_READ_ATTRIBUTES), then assume that this * is a request to delete the link (if a link) * and do not follow links. Otherwise, follow * the link to the target. */ if ((op->desired_access & ~FILE_READ_ATTRIBUTES) == DELETE) lookup_flags &= ~SMB_FOLLOW_LINKS; ! rc = smb_fsop_lookup_name(sr, zone_kcred(), lookup_flags, sr->tid_tree->t_snode, op->fqi.fq_dnode, op->fqi.fq_last_comp, ! &op->fqi.fq_fnode); if (rc == 0) { last_comp_found = B_TRUE; /* * Need the DOS attributes below, where we * check the search attributes (sattr). */ ! op->fqi.fq_fattr.sa_mask = SMB_AT_DOSATTR; rc = smb_node_getattr(sr, op->fqi.fq_fnode, zone_kcred(), NULL, &op->fqi.fq_fattr); if (rc != 0) { ! smb_node_release(op->fqi.fq_fnode); ! smb_node_release(op->fqi.fq_dnode); ! return (NT_STATUS_INTERNAL_ERROR); } } else if (rc == ENOENT) { last_comp_found = B_FALSE; op->fqi.fq_fnode = NULL; rc = 0; } else { ! smb_node_release(op->fqi.fq_dnode); ! return (smb_errno2status(rc)); } - - /* - * The uniq_fid is a CIFS-server-wide unique identifier for an ofile - * which is used to uniquely identify open instances for the - * VFS share reservation and POSIX locks. - */ - - uniq_fid = SMB_UNIQ_FID(); - if (last_comp_found) { ! node = op->fqi.fq_fnode; dnode = op->fqi.fq_dnode; ! if (!smb_node_is_file(node) && !smb_node_is_dir(node) && ! !smb_node_is_symlink(node)) { ! smb_node_release(node); ! smb_node_release(dnode); ! return (NT_STATUS_ACCESS_DENIED); } /* * Reject this request if either: * - the target IS a directory and the client requires that * it must NOT be (required by Lotus Notes) * - the target is NOT a directory and client requires that * it MUST be. */ ! if (smb_node_is_dir(node)) { if (op->create_options & FILE_NON_DIRECTORY_FILE) { ! smb_node_release(node); ! smb_node_release(dnode); ! return (NT_STATUS_FILE_IS_A_DIRECTORY); } } else { if ((op->create_options & FILE_DIRECTORY_FILE) || (op->nt_flags & NT_CREATE_FLAG_OPEN_TARGET_DIR)) { ! smb_node_release(node); ! smb_node_release(dnode); ! return (NT_STATUS_NOT_A_DIRECTORY); } } ! /* ! * No more open should be accepted when "Delete on close" ! * flag is set. ! */ ! if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { ! smb_node_release(node); ! smb_node_release(dnode); ! return (NT_STATUS_DELETE_PENDING); } /* ! * Specified file already exists so the operation should fail. */ ! if (op->create_disposition == FILE_CREATE) { ! smb_node_release(node); ! smb_node_release(dnode); ! return (NT_STATUS_OBJECT_NAME_COLLISION); } /* ! * Windows seems to check read-only access before file ! * sharing check. ! * ! * Check to see if the file is currently readonly (irrespective ! * of whether this open will make it readonly). */ ! if (SMB_PATHFILE_IS_READONLY(sr, node)) { ! /* Files data only */ ! if (!smb_node_is_dir(node)) { ! if (op->desired_access & (FILE_WRITE_DATA | ! FILE_APPEND_DATA)) { ! smb_node_release(node); ! smb_node_release(dnode); ! return (NT_STATUS_ACCESS_DENIED); } - } - } if ((op->create_disposition == FILE_SUPERSEDE) || (op->create_disposition == FILE_OVERWRITE_IF) || (op->create_disposition == FILE_OVERWRITE)) { ! if (!smb_sattr_check(op->fqi.fq_fattr.sa_dosattr, op->dattr)) { ! smb_node_release(node); ! smb_node_release(dnode); ! return (NT_STATUS_ACCESS_DENIED); } ! if (smb_node_is_dir(node)) { ! smb_node_release(node); ! smb_node_release(dnode); ! return (NT_STATUS_ACCESS_DENIED); } } /* MS-FSA 2.1.5.1.2 */ if (op->create_disposition == FILE_SUPERSEDE) op->desired_access |= DELETE; if ((op->create_disposition == FILE_OVERWRITE_IF) || (op->create_disposition == FILE_OVERWRITE)) op->desired_access |= FILE_WRITE_DATA; ! status = smb_fsop_access(sr, sr->user_cr, node, ! op->desired_access); ! if (status != NT_STATUS_SUCCESS) { ! smb_node_release(node); ! smb_node_release(dnode); ! /* SMB1 specific? NT_STATUS_PRIVILEGE_NOT_HELD */ ! if (status == NT_STATUS_PRIVILEGE_NOT_HELD) { ! return (status); ! } else { ! return (NT_STATUS_ACCESS_DENIED); } } if (max_requested) { ! smb_fsop_eaccess(sr, sr->user_cr, node, &max_allowed); op->desired_access |= max_allowed; } /* * According to MS "dochelp" mail in Mar 2015, any handle * on which read or write access is granted implicitly * gets "read attributes", even if it was not requested. - * This avoids unexpected access failures later that - * would happen if these were not granted. */ ! if ((op->desired_access & FILE_DATA_ALL) != 0) { ! op->desired_access |= (READ_CONTROL | ! FILE_READ_ATTRIBUTES); } /* * Oplock break is done prior to sharing checks as the break * may cause other clients to close the file which would ! * affect the sharing checks. This may block, so set the ! * file opening count before oplock stuff. */ ! smb_node_inc_opening_count(node); ! smb_open_oplock_break(sr, node); ! smb_node_wrlock(node); /* ! * Check for sharing violations */ ! status = smb_fsop_shrlock(sr->user_cr, node, uniq_fid, op->desired_access, op->share_access); ! if (status == NT_STATUS_SHARING_VIOLATION) { ! smb_node_unlock(node); ! smb_node_dec_opening_count(node); ! smb_node_release(node); ! smb_node_release(dnode); ! return (status); } /* * Go ahead with modifications as necessary. */ switch (op->create_disposition) { case FILE_SUPERSEDE: case FILE_OVERWRITE_IF: case FILE_OVERWRITE: op->dattr |= FILE_ATTRIBUTE_ARCHIVE; ! /* Don't apply readonly bit until smb_ofile_close */ if (op->dattr & FILE_ATTRIBUTE_READONLY) { - op->created_readonly = B_TRUE; op->dattr &= ~FILE_ATTRIBUTE_READONLY; } /* * Truncate the file data here. * We set alloc_size = op->dsize later, * after we have an ofile. See: * smb_set_open_attributes */ - bzero(&new_attr, sizeof (new_attr)); - new_attr.sa_dosattr = op->dattr; new_attr.sa_vattr.va_size = 0; new_attr.sa_mask = SMB_AT_DOSATTR | SMB_AT_SIZE; ! rc = smb_fsop_setattr(sr, sr->user_cr, node, &new_attr); if (rc != 0) { ! smb_fsop_unshrlock(sr->user_cr, node, uniq_fid); ! smb_node_unlock(node); ! smb_node_dec_opening_count(node); ! smb_node_release(node); ! smb_node_release(dnode); ! return (smb_errno2status(rc)); } /* * If file is being replaced, remove existing streams */ ! if (SMB_IS_STREAM(node) == 0) { status = smb_fsop_remove_streams(sr, ! sr->user_cr, node); ! if (status != 0) { ! smb_fsop_unshrlock(sr->user_cr, node, ! uniq_fid); ! smb_node_unlock(node); ! smb_node_dec_opening_count(node); ! smb_node_release(node); ! smb_node_release(dnode); ! return (status); } - } op->action_taken = SMB_OACT_TRUNCATED; break; default: --- 345,915 ---- /* * Limit the number of open pipe instances. */ if ((rc = smb_threshold_enter(&sv->sv_opipe_ct)) != 0) { status = RPC_NT_SERVER_TOO_BUSY; ! goto errout; } /* ! * Most of IPC open is handled in smb_opipe_open() */ ! op->create_options = 0; ! of = smb_ofile_alloc(sr, op, NULL, SMB_FTYPE_MESG_PIPE, ! tree_fid); ! tree_fid = 0; // given to the ofile ! status = smb_opipe_open(sr, of); smb_threshold_exit(&sv->sv_opipe_ct); ! if (status != NT_STATUS_SUCCESS) ! goto errout; ! return (NT_STATUS_SUCCESS); default: ! status = NT_STATUS_BAD_DEVICE_TYPE; ! goto errout; } smb_pathname_init(sr, pn, pn->pn_path); ! if (!smb_pathname_validate(sr, pn)) { ! status = sr->smb_error.status; ! goto errout; ! } if (strlen(pn->pn_path) >= SMB_MAXPATHLEN) { ! status = NT_STATUS_OBJECT_PATH_INVALID; ! goto errout; } if (is_dir) { ! if (!smb_validate_dirname(sr, pn)) { ! status = sr->smb_error.status; ! goto errout; ! } } else { ! if (!smb_validate_object_name(sr, pn)) { ! status = sr->smb_error.status; ! goto errout; } + } cur_node = op->fqi.fq_dnode ? op->fqi.fq_dnode : sr->tid_tree->t_snode; rc = smb_pathname_reduce(sr, sr->user_cr, pn->pn_path, sr->tid_tree->t_snode, cur_node, &op->fqi.fq_dnode, op->fqi.fq_last_comp); if (rc != 0) { ! status = smb_errno2status(rc); ! goto errout; } ! dnode = op->fqi.fq_dnode; ! dnode_held = B_TRUE; /* + * Lock the parent dir node in case another create + * request to the same parent directory comes in. + * Drop this once either lookup succeeds, or we've + * created the object in this directory. + */ + smb_node_wrlock(dnode); + dnode_wlock = B_TRUE; + + /* * If the access mask has only DELETE set (ignore * FILE_READ_ATTRIBUTES), then assume that this * is a request to delete the link (if a link) * and do not follow links. Otherwise, follow * the link to the target. */ if ((op->desired_access & ~FILE_READ_ATTRIBUTES) == DELETE) lookup_flags &= ~SMB_FOLLOW_LINKS; ! /* ! * Lookup *just* the file portion of the name. ! * Returns stream name in sname, which this allocates ! */ ! rc = smb_fsop_lookup_file(sr, zone_kcred(), lookup_flags, sr->tid_tree->t_snode, op->fqi.fq_dnode, op->fqi.fq_last_comp, ! &sname, &op->fqi.fq_fnode); if (rc == 0) { last_comp_found = B_TRUE; + fnode_held = B_TRUE; + /* * Need the DOS attributes below, where we * check the search attributes (sattr). + * Also UID, for owner check below. */ ! op->fqi.fq_fattr.sa_mask = SMB_AT_DOSATTR | SMB_AT_UID; rc = smb_node_getattr(sr, op->fqi.fq_fnode, zone_kcred(), NULL, &op->fqi.fq_fattr); if (rc != 0) { ! status = NT_STATUS_INTERNAL_ERROR; ! goto errout; } } else if (rc == ENOENT) { last_comp_found = B_FALSE; op->fqi.fq_fnode = NULL; rc = 0; } else { ! status = smb_errno2status(rc); ! goto errout; } if (last_comp_found) { ! fnode = op->fqi.fq_fnode; dnode = op->fqi.fq_dnode; ! if (!smb_node_is_file(fnode) && ! !smb_node_is_dir(fnode) && ! !smb_node_is_symlink(fnode)) { ! status = NT_STATUS_ACCESS_DENIED; ! goto errout; } /* * Reject this request if either: * - the target IS a directory and the client requires that * it must NOT be (required by Lotus Notes) * - the target is NOT a directory and client requires that * it MUST be. + * Streams are never directories. */ ! if (smb_node_is_dir(fnode) && sname == NULL) { if (op->create_options & FILE_NON_DIRECTORY_FILE) { ! status = NT_STATUS_FILE_IS_A_DIRECTORY; ! goto errout; } } else { if ((op->create_options & FILE_DIRECTORY_FILE) || (op->nt_flags & NT_CREATE_FLAG_OPEN_TARGET_DIR)) { ! status = NT_STATUS_NOT_A_DIRECTORY; ! goto errout; } } ! /* If we're given a stream name, look it up now */ ! if (sname != NULL) { ! tmp_node = fnode; ! rc = smb_fsop_lookup_stream(sr, zone_kcred(), ! lookup_flags, sr->tid_tree->t_snode, fnode, sname, ! &fnode); ! } else { ! rc = 0; } + if (rc == 0) { /* Stream Exists (including unnamed stream) */ + stream_found = B_TRUE; + smb_node_unlock(dnode); + dnode_wlock = B_FALSE; + + if (tmp_node != NULL) + smb_node_release(tmp_node); + /* ! * No more open should be accepted when ! * "Delete on close" flag is set. */ ! if (fnode->flags & NODE_FLAGS_DELETE_ON_CLOSE) { ! status = NT_STATUS_DELETE_PENDING; ! goto errout; } /* ! * Specified file already exists ! * so the operation should fail. */ ! if (op->create_disposition == FILE_CREATE) { ! status = NT_STATUS_OBJECT_NAME_COLLISION; ! goto errout; } if ((op->create_disposition == FILE_SUPERSEDE) || (op->create_disposition == FILE_OVERWRITE_IF) || (op->create_disposition == FILE_OVERWRITE)) { ! if (sname == NULL) { ! if (!smb_sattr_check( ! op->fqi.fq_fattr.sa_dosattr, op->dattr)) { ! status = ! NT_STATUS_ACCESS_DENIED; ! goto errout; } + op->desired_access |= + FILE_WRITE_ATTRIBUTES; + } ! if (smb_node_is_dir(fnode)) { ! status = NT_STATUS_ACCESS_DENIED; ! goto errout; } } /* MS-FSA 2.1.5.1.2 */ if (op->create_disposition == FILE_SUPERSEDE) op->desired_access |= DELETE; if ((op->create_disposition == FILE_OVERWRITE_IF) || (op->create_disposition == FILE_OVERWRITE)) op->desired_access |= FILE_WRITE_DATA; + } else if (rc == ENOENT) { /* File Exists, but Stream doesn't */ + if (op->create_disposition == FILE_OPEN || + op->create_disposition == FILE_OVERWRITE) { + status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + goto errout; + } ! op->desired_access |= FILE_WRITE_DATA; ! } else { /* Error looking up stream */ ! status = smb_errno2status(rc); ! fnode = tmp_node; ! goto errout; ! } ! /* ! * Windows seems to check read-only access before file ! * sharing check. ! * ! * Check to see if the file is currently readonly (regardless ! * of whether this open will make it readonly). ! * Readonly is ignored on directories. ! */ ! if (SMB_PATHFILE_IS_READONLY(sr, fnode) && ! !smb_node_is_dir(fnode)) { ! if (op->desired_access & ! (FILE_WRITE_DATA | FILE_APPEND_DATA)) { ! status = NT_STATUS_ACCESS_DENIED; ! goto errout; } + if (op->create_options & FILE_DELETE_ON_CLOSE) { + status = NT_STATUS_CANNOT_DELETE; + goto errout; } + } + do_audit = smb_audit_init(sr); + status = smb_fsop_access(sr, sr->user_cr, fnode, + op->desired_access); + if (max_requested) { ! smb_fsop_eaccess(sr, sr->user_cr, fnode, &max_allowed); op->desired_access |= max_allowed; } + + if (do_audit) { + smb_audit_fini(sr, op->desired_access, fnode, + status == NT_STATUS_SUCCESS); + } + + if (status != NT_STATUS_SUCCESS) + goto errout; + /* + * File owner should always get read control + read attr. + */ + if (crgetuid(sr->user_cr) == op->fqi.fq_fattr.sa_vattr.va_uid) + op->desired_access |= + (READ_CONTROL | FILE_READ_ATTRIBUTES); + + /* * According to MS "dochelp" mail in Mar 2015, any handle * on which read or write access is granted implicitly * gets "read attributes", even if it was not requested. */ ! if ((op->desired_access & FILE_DATA_ALL) != 0) ! op->desired_access |= FILE_READ_ATTRIBUTES; ! ! /* If the stream didn't exist, create it now */ ! if (!stream_found) { ! smb_node_t *tmp_node = fnode; ! ! bzero(&new_attr, sizeof (new_attr)); ! new_attr.sa_vattr.va_type = VREG; ! new_attr.sa_vattr.va_mode = S_IRUSR; ! new_attr.sa_mask |= SMB_AT_TYPE | SMB_AT_MODE; ! ! rc = smb_fsop_create_stream(sr, sr->user_cr, dnode, ! fnode, sname, lookup_flags, &new_attr, &fnode); ! smb_node_release(tmp_node); ! ! if (rc != 0) { ! status = smb_errno2status(rc); ! fnode_held = B_FALSE; ! goto errout; } + op->action_taken = SMB_OACT_CREATED; + created = B_TRUE; + smb_node_unlock(dnode); + dnode_wlock = B_FALSE; + } + /* * Oplock break is done prior to sharing checks as the break * may cause other clients to close the file which would ! * affect the sharing checks, and may delete the file due to ! * DELETE_ON_CLOSE. This may block, so set the file opening ! * count before oplock stuff. ! * ! * Need the "proposed" ofile (and it's TargetOplockKey) for ! * correct oplock break semantics. */ ! of = smb_ofile_alloc(sr, op, fnode, SMB_FTYPE_DISK, ! tree_fid); ! tree_fid = 0; // given to the ofile ! uniq_fid = of->f_uniqid; ! smb_node_inc_opening_count(fnode); ! opening_incr = B_TRUE; + if (!stream_found) { /* ! * Stake our Share Access claim. */ ! smb_node_wrlock(fnode); ! fnode_wlock = B_TRUE; ! ! status = smb_fsop_shrlock(sr->user_cr, fnode, uniq_fid, op->desired_access, op->share_access); ! if (status != 0) ! goto errout; ! ! fnode_shrlk = B_TRUE; ! smb_node_unlock(fnode); ! fnode_wlock = B_FALSE; ! goto stream_created; } /* + * XXX Supposed to do share access checks next. + * [MS-FSA] describes that as part of access check: + * 2.1.5.1.2.1 Alg... Check Access to an Existing File + * + * If CreateDisposition is FILE_OPEN or FILE_OPEN_IF: + * If Open.Stream.Oplock is not empty and + * Open.Stream.Oplock.State contains BATCH_OPLOCK, + * the object store MUST check for an oplock + * break according to the algorithm in section 2.1.4.12, + * with input values as follows: + * Open equal to this operation's Open + * Oplock equal to Open.Stream.Oplock + * Operation equal to "OPEN" + * OpParams containing two members: + * DesiredAccess, CreateDisposition + * + * It's not clear how Windows would ask the FS layer if + * the file has a BATCH oplock. We'll use a call to the + * common oplock code, which calls smb_oplock_break_OPEN + * only if the oplock state contains BATCH_OPLOCK. + * See: smb_oplock_break_BATCH() + * + * Also note: There's a nearly identical section in the + * spec. at the start of the "else" part of the above + * "if (disposition is overwrite, overwrite_if)" so this + * section (oplock break, the share mode check, and the + * next oplock_break_HANDLE) are all factored out to be + * in all cases above that if/else from the spec. + */ + status = smb_oplock_break_BATCH(fnode, of, + op->desired_access, op->create_disposition); + 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(fnode, 0); + status = 0; + } + if (status != NT_STATUS_SUCCESS) + goto errout; + + /* + * Check for sharing violations, and if any, + * do oplock break of handle caching. + * + * Need node_wrlock during shrlock checks, + * and not locked during oplock breaks etc. + */ + shrlock_t0 = gethrtime(); + shrlock_again: + smb_node_wrlock(fnode); + fnode_wlock = B_TRUE; + status = smb_fsop_shrlock(sr->user_cr, fnode, uniq_fid, + op->desired_access, op->share_access); + smb_node_unlock(fnode); + fnode_wlock = B_FALSE; + + /* + * [MS-FSA] "OPEN_BREAK_H" + * If the (proposed) new open would violate sharing rules, + * indicate an oplock break with OPEN_BREAK_H (to break + * handle level caching rights) then try again. + */ + if (status == NT_STATUS_SHARING_VIOLATION && + did_break_handle == B_FALSE) { + did_break_handle = B_TRUE; + + status = smb_oplock_break_HANDLE(fnode, of); + 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(fnode, 0); + status = 0; + } else { + /* + * Even when the oplock layer does NOT + * give us the special status indicating + * we should wait, it may have scheduled + * taskq jobs that may close handles. + * Give those a chance to run before we + * check again for sharing violations. + */ + delay(MSEC_TO_TICK(10)); + } + if (status != NT_STATUS_SUCCESS) + goto errout; + + goto shrlock_again; + } + + /* + * If we still have orphaned durable handles on this file, + * let's assume the client has lost interest in those and + * close them so they don't cause sharing violations. + * See longer comment at smb2_dh_close_my_orphans(). + */ + if (status == NT_STATUS_SHARING_VIOLATION && + sr->session->dialect >= SMB_VERS_2_BASE && + did_cleanup_orphans == B_FALSE) { + + did_cleanup_orphans = B_TRUE; + smb2_dh_close_my_orphans(sr, of); + + goto shrlock_again; + } + + /* + * SMB1 expects a 1 sec. delay before returning a + * sharing violation error. If breaking oplocks + * above took less than a sec, wait some more. + * See: smbtorture base.defer_open + */ + if (status == NT_STATUS_SHARING_VIOLATION && + sr->session->dialect < SMB_VERS_2_BASE) { + hrtime_t t1 = shrlock_t0 + NANOSEC; + hrtime_t now = gethrtime(); + if (now < t1) { + delay(NSEC_TO_TICK_ROUNDUP(t1 - now)); + } + } + + if (status != NT_STATUS_SUCCESS) + goto errout; + fnode_shrlk = B_TRUE; + + /* + * The [MS-FSA] spec. describes this oplock break as + * part of the sharing access checks. See: + * 2.1.5.1.2.2 Algorithm to Check Sharing Access... + * At the end of the share mode tests described there, + * if it has not returned "sharing violation", it + * specifies a call to the alg. in sec. 2.1.4.12, + * that boils down to: smb_oplock_break_OPEN() + */ + status = smb_oplock_break_OPEN(fnode, of, + op->desired_access, + op->create_disposition); + 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(fnode, 0); + status = 0; + } + if (status != NT_STATUS_SUCCESS) + goto errout; + + if ((fnode->flags & NODE_FLAGS_DELETE_COMMITTED) != 0) { + /* + * Breaking the oplock caused the file to be deleted, + * so let's bail and pretend the file wasn't found. + * Have to duplicate much of the logic found a the + * "errout" label here. + * + * This code path is exercised by smbtorture + * smb2.durable-open.delete_on_close1 + */ + DTRACE_PROBE1(node_deleted, smb_node_t, fnode); + smb_ofile_free(of); + of = NULL; + last_comp_found = B_FALSE; + + /* + * Get all the holds and locks into the state + * they would have if lookup had failed. + */ + fnode_shrlk = B_FALSE; + smb_fsop_unshrlock(sr->user_cr, fnode, uniq_fid); + + opening_incr = B_FALSE; + smb_node_dec_opening_count(fnode); + + fnode_held = B_FALSE; + smb_node_release(fnode); + + dnode_wlock = B_TRUE; + smb_node_wrlock(dnode); + + goto create; + } + + /* * Go ahead with modifications as necessary. */ switch (op->create_disposition) { case FILE_SUPERSEDE: case FILE_OVERWRITE_IF: case FILE_OVERWRITE: + bzero(&new_attr, sizeof (new_attr)); + if (sname == NULL) { op->dattr |= FILE_ATTRIBUTE_ARCHIVE; ! /* ! * Don't apply readonly until ! * smb_set_open_attributes ! */ if (op->dattr & FILE_ATTRIBUTE_READONLY) { op->dattr &= ~FILE_ATTRIBUTE_READONLY; + op->created_readonly = B_TRUE; } + new_attr.sa_dosattr = op->dattr; + } else { + new_attr.sa_dosattr = FILE_ATTRIBUTE_ARCHIVE; + } /* * Truncate the file data here. * We set alloc_size = op->dsize later, * after we have an ofile. See: * smb_set_open_attributes */ new_attr.sa_vattr.va_size = 0; new_attr.sa_mask = SMB_AT_DOSATTR | SMB_AT_SIZE; ! rc = smb_fsop_setattr(sr, sr->user_cr, fnode, ! &new_attr); if (rc != 0) { ! status = smb_errno2status(rc); ! goto errout; } /* * If file is being replaced, remove existing streams */ ! if (SMB_IS_STREAM(fnode) == 0) { status = smb_fsop_remove_streams(sr, ! sr->user_cr, fnode); ! if (status != 0) ! goto errout; } op->action_taken = SMB_OACT_TRUNCATED; break; default:
*** 692,893 **** op->dsize = 0L; op->action_taken = SMB_OACT_OPENED; break; } } else { /* Last component was not found. */ dnode = op->fqi.fq_dnode; if (is_dir == 0) is_stream = smb_is_stream_name(pn->pn_path); if ((op->create_disposition == FILE_OPEN) || (op->create_disposition == FILE_OVERWRITE)) { ! smb_node_release(dnode); ! return (NT_STATUS_OBJECT_NAME_NOT_FOUND); } if (pn->pn_fname && smb_is_invalid_filename(pn->pn_fname)) { ! smb_node_release(dnode); ! return (NT_STATUS_OBJECT_NAME_INVALID); } /* ! * lock the parent dir node in case another create ! * request to the same parent directory comes in. */ ! smb_node_wrlock(dnode); ! /* Don't apply readonly bit until smb_ofile_close */ if (op->dattr & FILE_ATTRIBUTE_READONLY) { op->dattr &= ~FILE_ATTRIBUTE_READONLY; op->created_readonly = B_TRUE; } ! bzero(&new_attr, sizeof (new_attr)); if ((op->crtime.tv_sec != 0) && (op->crtime.tv_sec != UINT_MAX)) { - new_attr.sa_mask |= SMB_AT_CRTIME; new_attr.sa_crtime = op->crtime; } if (is_dir == 0) { op->dattr |= FILE_ATTRIBUTE_ARCHIVE; new_attr.sa_dosattr = op->dattr; new_attr.sa_vattr.va_type = VREG; ! new_attr.sa_vattr.va_mode = is_stream ? S_IRUSR : S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH; - new_attr.sa_mask |= - SMB_AT_DOSATTR | SMB_AT_TYPE | SMB_AT_MODE; /* * We set alloc_size = op->dsize later, ! * after we have an ofile. See: ! * smb_set_open_attributes */ rc = smb_fsop_create(sr, sr->user_cr, dnode, op->fqi.fq_last_comp, &new_attr, &op->fqi.fq_fnode); - - if (rc != 0) { - smb_node_unlock(dnode); - smb_node_release(dnode); - return (smb_errno2status(rc)); - } - - node = op->fqi.fq_fnode; - smb_node_inc_opening_count(node); - smb_node_wrlock(node); - - status = smb_fsop_shrlock(sr->user_cr, node, uniq_fid, - op->desired_access, op->share_access); - - if (status == NT_STATUS_SHARING_VIOLATION) { - smb_node_unlock(node); - smb_node_dec_opening_count(node); - smb_delete_new_object(sr); - smb_node_release(node); - smb_node_unlock(dnode); - smb_node_release(dnode); - return (status); - } } else { op->dattr |= FILE_ATTRIBUTE_DIRECTORY; new_attr.sa_dosattr = op->dattr; new_attr.sa_vattr.va_type = VDIR; new_attr.sa_vattr.va_mode = 0777; - new_attr.sa_mask |= - SMB_AT_DOSATTR | SMB_AT_TYPE | SMB_AT_MODE; rc = smb_fsop_mkdir(sr, sr->user_cr, dnode, op->fqi.fq_last_comp, &new_attr, &op->fqi.fq_fnode); if (rc != 0) { ! smb_node_unlock(dnode); ! smb_node_release(dnode); ! return (smb_errno2status(rc)); } ! node = op->fqi.fq_fnode; ! smb_node_inc_opening_count(node); ! smb_node_wrlock(node); ! } created = B_TRUE; op->action_taken = SMB_OACT_CREATED; if (max_requested) { ! smb_fsop_eaccess(sr, sr->user_cr, node, &max_allowed); op->desired_access |= max_allowed; } /* ! * We created created this object (we own it) so ! * grant read/write attributes on this handle, * even if that was not requested. This avoids ! * unexpected access failures later that would ! * happen if these were not granted. */ ! op->desired_access |= (READ_CONTROL | ! FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES); ! } ! status = NT_STATUS_SUCCESS; ! of = smb_ofile_open(sr, node, op, SMB_FTYPE_DISK, uniq_fid, ! &err); ! if (of == NULL) { ! status = err.status; } /* ! * We might have blocked in smb_ofile_open long enough so a ! * tree disconnect might have happened. In that case, we've ! * just added an ofile to a tree that's disconnecting, and ! * need to undo that to avoid interfering with tear-down of ! * the tree connection. */ ! if (status == NT_STATUS_SUCCESS && ! !smb_tree_is_connected(sr->tid_tree)) { status = NT_STATUS_INVALID_PARAMETER; } /* * This MUST be done after ofile creation, so that explicitly ! * set timestamps can be remembered on the ofile, and the ! * readonly flag will be stored "pending" on the node. */ - if (status == NT_STATUS_SUCCESS) { if ((rc = smb_set_open_attributes(sr, of)) != 0) { status = smb_errno2status(rc); } - } - if (status == NT_STATUS_SUCCESS) { /* * We've already done access checks above, * and want this call to succeed even when * !(desired_access & FILE_READ_ATTRIBUTES), * so pass kcred here. */ op->fqi.fq_fattr.sa_mask = SMB_AT_ALL; ! rc = smb_node_getattr(sr, node, zone_kcred(), of, &op->fqi.fq_fattr); - if (rc != 0) { - status = NT_STATUS_INTERNAL_ERROR; - } - } /* - * smb_fsop_unshrlock is a no-op if node is a directory - * smb_fsop_unshrlock is done in smb_ofile_close - */ - if (status != NT_STATUS_SUCCESS) { - if (of == NULL) { - smb_fsop_unshrlock(sr->user_cr, node, uniq_fid); - } else { - smb_ofile_close(of, 0); - smb_ofile_release(of); - } - if (created) - smb_delete_new_object(sr); - smb_node_unlock(node); - smb_node_dec_opening_count(node); - smb_node_release(node); - if (created) - smb_node_unlock(dnode); - smb_node_release(dnode); - return (status); - } - - /* * Propagate the write-through mode from the open params * to the node: see the notes in the function header. */ if (sr->sr_cfg->skc_sync_enable || (op->create_options & FILE_WRITE_THROUGH)) ! node->flags |= NODE_FLAGS_WRITE_THROUGH; /* * Set up the fileid and dosattr in open_param for response */ op->fileid = op->fqi.fq_fattr.sa_vattr.va_nodeid; --- 924,1140 ---- op->dsize = 0L; op->action_taken = SMB_OACT_OPENED; break; } } else { + create: /* Last component was not found. */ dnode = op->fqi.fq_dnode; if (is_dir == 0) is_stream = smb_is_stream_name(pn->pn_path); if ((op->create_disposition == FILE_OPEN) || (op->create_disposition == FILE_OVERWRITE)) { ! status = NT_STATUS_OBJECT_NAME_NOT_FOUND; ! goto errout; } if (pn->pn_fname && smb_is_invalid_filename(pn->pn_fname)) { ! status = NT_STATUS_OBJECT_NAME_INVALID; ! goto errout; } /* ! * Don't create in directories marked "Delete on close". */ ! if (dnode->flags & NODE_FLAGS_DELETE_ON_CLOSE) { ! status = NT_STATUS_DELETE_PENDING; ! goto errout; ! } ! /* ! * Create always sets the DOS attributes, type, and mode ! * in the if/else below (different for file vs directory). ! * Don't set the readonly bit until smb_set_open_attributes ! * or that would prevent this open. Note that op->dattr ! * needs to be what smb_set_open_attributes will use, ! * except for the readonly bit. ! */ ! bzero(&new_attr, sizeof (new_attr)); ! new_attr.sa_mask = SMB_AT_DOSATTR | SMB_AT_TYPE | SMB_AT_MODE; if (op->dattr & FILE_ATTRIBUTE_READONLY) { op->dattr &= ~FILE_ATTRIBUTE_READONLY; op->created_readonly = B_TRUE; } ! /* ! * SMB create can specify the create time. ! */ if ((op->crtime.tv_sec != 0) && (op->crtime.tv_sec != UINT_MAX)) { new_attr.sa_mask |= SMB_AT_CRTIME; new_attr.sa_crtime = op->crtime; } if (is_dir == 0) { op->dattr |= FILE_ATTRIBUTE_ARCHIVE; new_attr.sa_dosattr = op->dattr; new_attr.sa_vattr.va_type = VREG; ! if (is_stream) ! new_attr.sa_vattr.va_mode = S_IRUSR | S_IWUSR; ! else ! new_attr.sa_vattr.va_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH; /* * We set alloc_size = op->dsize later, ! * (in smb_set_open_attributes) after we ! * have an ofile on which to save that. ! * ! * Legacy Open&X sets size to alloc_size ! * when creating a new file. */ + if (sr->smb_com == SMB_COM_OPEN_ANDX) { + new_attr.sa_vattr.va_size = op->dsize; + new_attr.sa_mask |= SMB_AT_SIZE; + } + /* auditing handled by fsop layer */ rc = smb_fsop_create(sr, sr->user_cr, dnode, op->fqi.fq_last_comp, &new_attr, &op->fqi.fq_fnode); } else { op->dattr |= FILE_ATTRIBUTE_DIRECTORY; new_attr.sa_dosattr = op->dattr; new_attr.sa_vattr.va_type = VDIR; new_attr.sa_vattr.va_mode = 0777; + /* auditing handled by fsop layer */ rc = smb_fsop_mkdir(sr, sr->user_cr, dnode, op->fqi.fq_last_comp, &new_attr, &op->fqi.fq_fnode); + } if (rc != 0) { ! status = smb_errno2status(rc); ! goto errout; } ! /* Create done. */ ! smb_node_unlock(dnode); ! dnode_wlock = B_FALSE; created = B_TRUE; op->action_taken = SMB_OACT_CREATED; + /* Note: hold from create */ + fnode = op->fqi.fq_fnode; + fnode_held = B_TRUE; + if (max_requested) { ! smb_fsop_eaccess(sr, sr->user_cr, fnode, &max_allowed); op->desired_access |= max_allowed; } /* ! * We created this object (we own it) so grant ! * read_control + read_attributes on this handle, * even if that was not requested. This avoids ! * unexpected access failures later. */ ! op->desired_access |= (READ_CONTROL | FILE_READ_ATTRIBUTES); ! /* Allocate the ofile and fill in most of it. */ ! of = smb_ofile_alloc(sr, op, fnode, SMB_FTYPE_DISK, ! tree_fid); ! tree_fid = 0; // given to the ofile ! uniq_fid = of->f_uniqid; ! smb_node_inc_opening_count(fnode); ! opening_incr = B_TRUE; ! ! /* ! * Share access checks... ! */ ! smb_node_wrlock(fnode); ! fnode_wlock = B_TRUE; ! ! status = smb_fsop_shrlock(sr->user_cr, fnode, uniq_fid, ! op->desired_access, op->share_access); ! if (status != 0) ! goto errout; ! fnode_shrlk = B_TRUE; ! ! /* ! * MS-FSA 2.1.5.1.1 ! * If the Oplock member of the DirectoryStream in ! * Link.ParentFile.StreamList (ParentOplock) is ! * not empty ... oplock break on the parent... ! * (dnode is the parent directory) ! * ! * This compares of->ParentOplockKey with each ! * oplock of->TargetOplockKey and breaks... ! * so it's OK that we're passing an OF that's ! * NOT a member of dnode->n_ofile_list ! * ! * The break never blocks, so ignore the return. ! */ ! (void) smb_oplock_break_PARENT(dnode, of); } + stream_created: /* ! * We might have blocked in smb_oplock_break_OPEN long enough ! * so a tree disconnect might have happened. In that case, ! * we would be adding an ofile to a tree that's disconnecting, ! * which would interfere with tear-down. If so, error out. */ ! if (!smb_tree_is_connected(sr->tid_tree)) { status = NT_STATUS_INVALID_PARAMETER; + goto errout; } /* + * Moved this up from smb_ofile_open() + */ + if ((rc = smb_fsop_open(fnode, of->f_mode, of->f_cr)) != 0) { + status = smb_errno2status(rc); + goto errout; + } + + /* + * Complete this open (add to ofile lists) + */ + smb_ofile_open(sr, op, of); + did_open = B_TRUE; + + /* * This MUST be done after ofile creation, so that explicitly ! * set timestamps can be remembered on the ofile, and setting ! * the readonly flag won't affect access via this open. */ if ((rc = smb_set_open_attributes(sr, of)) != 0) { status = smb_errno2status(rc); + goto errout; } /* * We've already done access checks above, * and want this call to succeed even when * !(desired_access & FILE_READ_ATTRIBUTES), * so pass kcred here. */ op->fqi.fq_fattr.sa_mask = SMB_AT_ALL; ! (void) smb_node_getattr(sr, fnode, zone_kcred(), of, &op->fqi.fq_fattr); /* * Propagate the write-through mode from the open params * to the node: see the notes in the function header. + * XXX: write_through should be a flag on the ofile. */ if (sr->sr_cfg->skc_sync_enable || (op->create_options & FILE_WRITE_THROUGH)) ! fnode->flags |= NODE_FLAGS_WRITE_THROUGH; /* * Set up the fileid and dosattr in open_param for response */ op->fileid = op->fqi.fq_fattr.sa_vattr.va_nodeid;
*** 898,984 **** */ op->ftype = SMB_FTYPE_DISK; sr->smb_fid = of->f_fid; sr->fid_ofile = of; ! if (smb_node_is_file(node)) { ! smb_oplock_acquire(sr, node, of); op->dsize = op->fqi.fq_fattr.sa_vattr.va_size; } else { /* directory or symlink */ - op->op_oplock_level = SMB_OPLOCK_NONE; op->dsize = 0; } ! smb_node_dec_opening_count(node); ! smb_node_unlock(node); ! if (created) smb_node_unlock(dnode); ! ! smb_node_release(node); smb_node_release(dnode); return (NT_STATUS_SUCCESS); - } ! /* ! * smb_open_oplock_break ! * ! * If the node has an ofile opened with share access none, ! * (smb_node_share_check = FALSE) only break BATCH oplock. ! * Otherwise: ! * If overwriting, break to SMB_OPLOCK_NONE, else ! * If opening for anything other than attribute access, ! * break oplock to LEVEL_II. ! */ ! static void ! smb_open_oplock_break(smb_request_t *sr, smb_node_t *node) ! { ! smb_arg_open_t *op = &sr->sr_open; ! uint32_t flags = 0; ! if (!smb_node_share_check(node)) ! flags |= SMB_OPLOCK_BREAK_BATCH; ! if (smb_open_overwrite(op)) { ! flags |= SMB_OPLOCK_BREAK_TO_NONE; ! (void) smb_oplock_break(sr, node, flags); ! } else if (!smb_open_attr_only(op)) { ! flags |= SMB_OPLOCK_BREAK_TO_LEVEL_II; ! (void) smb_oplock_break(sr, node, flags); } - } ! /* ! * smb_open_attr_only ! * ! * Determine if file is being opened for attribute access only. ! * This is used to determine whether it is necessary to break ! * existing oplocks on the file. ! */ ! static boolean_t ! smb_open_attr_only(smb_arg_open_t *op) ! { ! if (((op->desired_access & ~(FILE_READ_ATTRIBUTES | ! FILE_WRITE_ATTRIBUTES | SYNCHRONIZE | READ_CONTROL)) == 0) && ! (op->create_disposition != FILE_SUPERSEDE) && ! (op->create_disposition != FILE_OVERWRITE)) { ! return (B_TRUE); ! } ! return (B_FALSE); ! } ! static boolean_t ! smb_open_overwrite(smb_arg_open_t *op) ! { ! if ((op->create_disposition == FILE_SUPERSEDE) || ! (op->create_disposition == FILE_OVERWRITE_IF) || ! (op->create_disposition == FILE_OVERWRITE)) { ! return (B_TRUE); ! } ! return (B_FALSE); } /* * smb_set_open_attributes * --- 1145,1215 ---- */ op->ftype = SMB_FTYPE_DISK; sr->smb_fid = of->f_fid; sr->fid_ofile = of; ! if (smb_node_is_file(fnode)) { op->dsize = op->fqi.fq_fattr.sa_vattr.va_size; } else { /* directory or symlink */ op->dsize = 0; } ! /* ! * Note: oplock_acquire happens in callers, because ! * how that happens is protocol-specific. ! */ ! if (sname != NULL) ! kmem_free(sname, MAXNAMELEN); ! if (fnode_wlock) ! smb_node_unlock(fnode); ! if (opening_incr) ! smb_node_dec_opening_count(fnode); ! if (fnode_held) ! smb_node_release(fnode); ! if (dnode_wlock) smb_node_unlock(dnode); ! if (dnode_held) smb_node_release(dnode); return (NT_STATUS_SUCCESS); ! errout: ! if (did_open) { ! smb_ofile_close(of, 0); ! /* rele via sr->fid_ofile */ ! } else if (of != NULL) { ! /* No other refs possible */ ! smb_ofile_free(of); ! } ! if (fnode_shrlk) ! smb_fsop_unshrlock(sr->user_cr, fnode, uniq_fid); ! if (created) { ! /* Try to roll-back create. */ ! smb_delete_new_object(sr); } ! if (sname != NULL) ! kmem_free(sname, MAXNAMELEN); ! if (fnode_wlock) ! smb_node_unlock(fnode); ! if (opening_incr) ! smb_node_dec_opening_count(fnode); ! if (fnode_held) ! smb_node_release(fnode); ! if (dnode_wlock) ! smb_node_unlock(dnode); ! if (dnode_held) ! smb_node_release(dnode); ! if (tree_fid != 0) ! smb_idpool_free(&tree->t_fid_pool, tree_fid); ! ! return (status); } /* * smb_set_open_attributes *
*** 989,1000 **** * * DOS attributes * - If we created_readonly, we now store the real DOS attributes * (including the readonly bit) so subsequent opens will see it. * - * Both are stored "pending" rather than in the file system. - * * Returns: errno */ static int smb_set_open_attributes(smb_request_t *sr, smb_ofile_t *of) { --- 1220,1229 ----
*** 1029,1039 **** * update those times, so we don't have to. * * However, keep track of the fact that we modified * the file via this handle, so we can do the evil, * gratuitious mtime update on close that Windows ! * clients appear to expect. */ if (op->action_taken == SMB_OACT_TRUNCATED) of->f_written = B_TRUE; if (attr.sa_mask != 0) --- 1258,1268 ---- * update those times, so we don't have to. * * However, keep track of the fact that we modified * the file via this handle, so we can do the evil, * gratuitious mtime update on close that Windows ! * clients expect. */ if (op->action_taken == SMB_OACT_TRUNCATED) of->f_written = B_TRUE; if (attr.sa_mask != 0)