1da6c28aaSamw /* 2da6c28aaSamw * CDDL HEADER START 3da6c28aaSamw * 4da6c28aaSamw * The contents of this file are subject to the terms of the 5da6c28aaSamw * Common Development and Distribution License (the "License"). 6da6c28aaSamw * You may not use this file except in compliance with the License. 7da6c28aaSamw * 8da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9da6c28aaSamw * or http://www.opensolaris.org/os/licensing. 10da6c28aaSamw * See the License for the specific language governing permissions 11da6c28aaSamw * and limitations under the License. 12da6c28aaSamw * 13da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each 14da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the 16da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying 17da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner] 18da6c28aaSamw * 19da6c28aaSamw * CDDL HEADER END 20da6c28aaSamw */ 21da6c28aaSamw /* 22dc20a302Sas200622 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23da6c28aaSamw * Use is subject to license terms. 24da6c28aaSamw */ 25da6c28aaSamw 26da6c28aaSamw #pragma ident "%Z%%M% %I% %E% SMI" 27da6c28aaSamw 28da6c28aaSamw /* 29da6c28aaSamw * This module provides the common open functionality to the various 30da6c28aaSamw * open and create SMB interface functions. 31da6c28aaSamw */ 32da6c28aaSamw 33da6c28aaSamw #include <smbsrv/smb_incl.h> 34da6c28aaSamw #include <smbsrv/smb_fsops.h> 35da6c28aaSamw #include <smbsrv/mlsvc.h> 36da6c28aaSamw #include <smbsrv/nterror.h> 37da6c28aaSamw #include <smbsrv/ntstatus.h> 38da6c28aaSamw #include <smbsrv/smbinfo.h> 39da6c28aaSamw #include <sys/fcntl.h> 40dc20a302Sas200622 #include <sys/nbmlock.h> 41da6c28aaSamw 42*7b59d02dSjb150015 extern uint32_t smb_is_executable(char *); 43*7b59d02dSjb150015 44*7b59d02dSjb150015 static uint32_t smb_open_subr(smb_request_t *); 45da6c28aaSamw 46da6c28aaSamw /* 47da6c28aaSamw * The default stability mode is to perform the write-through 48da6c28aaSamw * behaviour requested by the client. 49da6c28aaSamw */ 50da6c28aaSamw int smb_stable_mode = 0; 51da6c28aaSamw 52da6c28aaSamw 53da6c28aaSamw /* 54da6c28aaSamw * This macro is used to delete a newly created object 55da6c28aaSamw * if any error happens after creation of object. 56da6c28aaSamw */ 57da6c28aaSamw #define SMB_DEL_NEWOBJ(obj) \ 58da6c28aaSamw if (created) { \ 59da6c28aaSamw if (is_dir) \ 60da6c28aaSamw (void) smb_fsop_rmdir(sr, sr->user_cr, \ 61da6c28aaSamw obj.dir_snode, obj.last_comp, 0); \ 62da6c28aaSamw else \ 63da6c28aaSamw (void) smb_fsop_remove(sr, sr->user_cr, \ 64da6c28aaSamw obj.dir_snode, obj.last_comp, 0); \ 65da6c28aaSamw } 66da6c28aaSamw 67da6c28aaSamw /* 68da6c28aaSamw * smb_set_stability 69da6c28aaSamw * 70da6c28aaSamw * Set the default stability mode. Normal (mode is zero) means perform 71da6c28aaSamw * the write-through behaviour requested by the client. Synchronous 72da6c28aaSamw * (mode is non-zero) means journal everything regardless of the write 73da6c28aaSamw * through behaviour requested by the client. 74da6c28aaSamw */ 75da6c28aaSamw void 76da6c28aaSamw smb_set_stability(int mode) 77da6c28aaSamw { 78da6c28aaSamw smb_stable_mode = mode; 79da6c28aaSamw } 80da6c28aaSamw 81da6c28aaSamw /* 82da6c28aaSamw * smb_access_generic_to_file 83da6c28aaSamw * 84da6c28aaSamw * Search MSDN for IoCreateFile to see following mapping. 85da6c28aaSamw * 86da6c28aaSamw * GENERIC_READ STANDARD_RIGHTS_READ, FILE_READ_DATA, 87da6c28aaSamw * FILE_READ_ATTRIBUTES and FILE_READ_EA 88da6c28aaSamw * 89da6c28aaSamw * GENERIC_WRITE STANDARD_RIGHTS_WRITE, FILE_WRITE_DATA, 90da6c28aaSamw * FILE_WRITE_ATTRIBUTES, FILE_WRITE_EA, and FILE_APPEND_DATA 91da6c28aaSamw * 92da6c28aaSamw * GENERIC_EXECUTE STANDARD_RIGHTS_EXECUTE, SYNCHRONIZE, and FILE_EXECUTE. 93da6c28aaSamw */ 94da6c28aaSamw uint32_t 95da6c28aaSamw smb_access_generic_to_file(uint32_t desired_access) 96da6c28aaSamw { 97da6c28aaSamw uint32_t access = 0; 98da6c28aaSamw 99da6c28aaSamw if (desired_access & GENERIC_ALL) 100da6c28aaSamw return (FILE_ALL_ACCESS & ~SYNCHRONIZE); 101da6c28aaSamw 102da6c28aaSamw if (desired_access & GENERIC_EXECUTE) { 103da6c28aaSamw desired_access &= ~GENERIC_EXECUTE; 104da6c28aaSamw access |= (STANDARD_RIGHTS_EXECUTE | 105da6c28aaSamw SYNCHRONIZE | FILE_EXECUTE); 106da6c28aaSamw } 107da6c28aaSamw 108da6c28aaSamw if (desired_access & GENERIC_WRITE) { 109da6c28aaSamw desired_access &= ~GENERIC_WRITE; 110da6c28aaSamw access |= (FILE_GENERIC_WRITE & ~SYNCHRONIZE); 111da6c28aaSamw } 112da6c28aaSamw 113da6c28aaSamw if (desired_access & GENERIC_READ) { 114da6c28aaSamw desired_access &= ~GENERIC_READ; 115da6c28aaSamw access |= FILE_GENERIC_READ; 116da6c28aaSamw } 117da6c28aaSamw 118da6c28aaSamw return (access | desired_access); 119da6c28aaSamw } 120da6c28aaSamw 121da6c28aaSamw /* 122da6c28aaSamw * smb_omode_to_amask 123da6c28aaSamw * 124da6c28aaSamw * This function converts open modes used by Open and Open AndX 125da6c28aaSamw * commands to desired access bits used by NT Create AndX command. 126da6c28aaSamw */ 127da6c28aaSamw uint32_t 128da6c28aaSamw smb_omode_to_amask(uint32_t desired_access) 129da6c28aaSamw { 130da6c28aaSamw switch (desired_access & SMB_DA_ACCESS_MASK) { 131da6c28aaSamw case SMB_DA_ACCESS_READ: 132da6c28aaSamw return (FILE_GENERIC_READ); 133da6c28aaSamw 134da6c28aaSamw case SMB_DA_ACCESS_WRITE: 135da6c28aaSamw return (FILE_GENERIC_WRITE); 136da6c28aaSamw 137da6c28aaSamw case SMB_DA_ACCESS_READ_WRITE: 138da6c28aaSamw return (FILE_GENERIC_READ | FILE_GENERIC_WRITE); 139da6c28aaSamw 140da6c28aaSamw case SMB_DA_ACCESS_EXECUTE: 141da6c28aaSamw return (FILE_GENERIC_EXECUTE); 142da6c28aaSamw } 143da6c28aaSamw 144da6c28aaSamw /* invalid open mode */ 145da6c28aaSamw return ((uint32_t)SMB_INVALID_AMASK); 146da6c28aaSamw } 147da6c28aaSamw 148da6c28aaSamw /* 149da6c28aaSamw * smb_denymode_to_sharemode 150da6c28aaSamw * 151da6c28aaSamw * This function converts deny modes used by Open and Open AndX 152da6c28aaSamw * commands to share access bits used by NT Create AndX command. 153da6c28aaSamw */ 154da6c28aaSamw uint32_t 155da6c28aaSamw smb_denymode_to_sharemode(uint32_t desired_access, char *fname) 156da6c28aaSamw { 157da6c28aaSamw switch (desired_access & SMB_DA_SHARE_MASK) { 158da6c28aaSamw case SMB_DA_SHARE_COMPATIBILITY: 159da6c28aaSamw if (smb_is_executable(fname)) 160da6c28aaSamw return (FILE_SHARE_READ | FILE_SHARE_WRITE); 161da6c28aaSamw else { 162da6c28aaSamw if ((desired_access & 163da6c28aaSamw SMB_DA_ACCESS_MASK) == SMB_DA_ACCESS_READ) 164da6c28aaSamw return (FILE_SHARE_READ); 165da6c28aaSamw else 166da6c28aaSamw return (FILE_SHARE_NONE); 167da6c28aaSamw } 168da6c28aaSamw 169da6c28aaSamw case SMB_DA_SHARE_EXCLUSIVE: 170da6c28aaSamw return (FILE_SHARE_NONE); 171da6c28aaSamw 172da6c28aaSamw case SMB_DA_SHARE_DENY_WRITE: 173da6c28aaSamw return (FILE_SHARE_READ); 174da6c28aaSamw 175da6c28aaSamw case SMB_DA_SHARE_DENY_READ: 176da6c28aaSamw return (FILE_SHARE_WRITE); 177da6c28aaSamw 178da6c28aaSamw case SMB_DA_SHARE_DENY_NONE: 179da6c28aaSamw return (FILE_SHARE_READ | FILE_SHARE_WRITE); 180da6c28aaSamw } 181da6c28aaSamw 182da6c28aaSamw /* invalid deny mode */ 183da6c28aaSamw return ((uint32_t)SMB_INVALID_SHAREMODE); 184da6c28aaSamw } 185da6c28aaSamw 186da6c28aaSamw /* 187da6c28aaSamw * smb_ofun_to_crdisposition 188da6c28aaSamw * 189da6c28aaSamw * This function converts open function values used by Open and Open AndX 190da6c28aaSamw * commands to create disposition values used by NT Create AndX command. 191da6c28aaSamw */ 192da6c28aaSamw uint32_t 193da6c28aaSamw smb_ofun_to_crdisposition(uint16_t ofun) 194da6c28aaSamw { 195da6c28aaSamw static int ofun_cr_map[3][2] = 196da6c28aaSamw { 197da6c28aaSamw { -1, FILE_CREATE }, 198da6c28aaSamw { FILE_OPEN, FILE_OPEN_IF }, 199da6c28aaSamw { FILE_OVERWRITE, FILE_OVERWRITE_IF } 200da6c28aaSamw }; 201da6c28aaSamw 202da6c28aaSamw int row = ofun & SMB_OFUN_OPEN_MASK; 203da6c28aaSamw int col = (ofun & SMB_OFUN_CREATE_MASK) >> 4; 204da6c28aaSamw 205da6c28aaSamw if (row == 3) 206da6c28aaSamw return ((uint32_t)SMB_INVALID_CRDISPOSITION); 207da6c28aaSamw 208da6c28aaSamw return (ofun_cr_map[row][col]); 209da6c28aaSamw } 210da6c28aaSamw 211da6c28aaSamw /* 212*7b59d02dSjb150015 * Retry opens to avoid spurious sharing violations, due to timing 213*7b59d02dSjb150015 * issues between closes and opens. The client that already has the 214*7b59d02dSjb150015 * file open may be in the process of closing it. 215*7b59d02dSjb150015 */ 216*7b59d02dSjb150015 uint32_t 217*7b59d02dSjb150015 smb_common_open(smb_request_t *sr) 218*7b59d02dSjb150015 { 219*7b59d02dSjb150015 uint32_t status = NT_STATUS_SUCCESS; 220*7b59d02dSjb150015 int count; 221*7b59d02dSjb150015 222*7b59d02dSjb150015 for (count = 0; count <= 4; count++) { 223*7b59d02dSjb150015 if (count) 224*7b59d02dSjb150015 delay(MSEC_TO_TICK(400)); 225*7b59d02dSjb150015 226*7b59d02dSjb150015 if ((status = smb_open_subr(sr)) == NT_STATUS_SUCCESS) 227*7b59d02dSjb150015 break; 228*7b59d02dSjb150015 } 229*7b59d02dSjb150015 230*7b59d02dSjb150015 switch (status) { 231*7b59d02dSjb150015 case NT_STATUS_SUCCESS: 232*7b59d02dSjb150015 break; 233*7b59d02dSjb150015 234*7b59d02dSjb150015 case NT_STATUS_SHARING_VIOLATION: 235*7b59d02dSjb150015 smbsr_error(sr, NT_STATUS_SHARING_VIOLATION, 236*7b59d02dSjb150015 ERRDOS, ERROR_SHARING_VIOLATION); 237*7b59d02dSjb150015 break; 238*7b59d02dSjb150015 239*7b59d02dSjb150015 default: 240*7b59d02dSjb150015 /* Error already set. */ 241*7b59d02dSjb150015 break; 242*7b59d02dSjb150015 } 243*7b59d02dSjb150015 244*7b59d02dSjb150015 return (status); 245*7b59d02dSjb150015 } 246*7b59d02dSjb150015 247*7b59d02dSjb150015 /* 248da6c28aaSamw * smb_open_subr 249da6c28aaSamw * 250da6c28aaSamw * Notes on write-through behaviour. It looks like pre-LM0.12 versions 251da6c28aaSamw * of the protocol specify the write-through mode when a file is opened, 252da6c28aaSamw * (SmbOpen, SmbOpenAndX) so the write calls (SmbWrite, SmbWriteAndClose, 253da6c28aaSamw * SmbWriteAndUnlock) don't need to contain a write-through flag. 254da6c28aaSamw * 255da6c28aaSamw * With LM0.12, the open calls (SmbCreateAndX, SmbNtTransactCreate) 256da6c28aaSamw * don't indicate which write-through mode to use. Instead the write 257da6c28aaSamw * calls (SmbWriteAndX, SmbWriteRaw) specify the mode on a per call 258da6c28aaSamw * basis. 259da6c28aaSamw * 260da6c28aaSamw * We don't care which open call was used to get us here, we just need 261da6c28aaSamw * to ensure that the write-through mode flag is copied from the open 262da6c28aaSamw * parameters to the node. We test the omode write-through flag in all 263da6c28aaSamw * write functions. 264da6c28aaSamw * 265da6c28aaSamw * This function will return NT status codes but it also raises errors, 266da6c28aaSamw * in which case it won't return to the caller. Be careful how you 267da6c28aaSamw * handle things in here. 268da6c28aaSamw */ 269dc20a302Sas200622 270*7b59d02dSjb150015 static uint32_t 271*7b59d02dSjb150015 smb_open_subr(smb_request_t *sr) 272da6c28aaSamw { 273da6c28aaSamw int created = 0; 274da6c28aaSamw struct smb_node *node = 0; 275da6c28aaSamw struct smb_node *dnode = 0; 276da6c28aaSamw struct smb_node *cur_node; 277da6c28aaSamw struct open_param *op = &sr->arg.open; 278da6c28aaSamw int rc; 279da6c28aaSamw struct smb_ofile *of; 280da6c28aaSamw smb_attr_t new_attr; 281da6c28aaSamw int pathlen; 282da6c28aaSamw int max_requested = 0; 283da6c28aaSamw uint32_t max_allowed; 284da6c28aaSamw unsigned int granted_oplock; 285da6c28aaSamw uint32_t status = NT_STATUS_SUCCESS; 286da6c28aaSamw int is_dir; 287da6c28aaSamw smb_error_t err; 288*7b59d02dSjb150015 int is_stream = 0; 289da6c28aaSamw int lookup_flags = SMB_FOLLOW_LINKS; 290da6c28aaSamw uint32_t daccess; 291dc20a302Sas200622 uint32_t share_access = op->share_access; 292dc20a302Sas200622 uint32_t uniq_fid; 293da6c28aaSamw 294da6c28aaSamw is_dir = (op->create_options & FILE_DIRECTORY_FILE) ? 1 : 0; 295da6c28aaSamw 296da6c28aaSamw if (is_dir) { 297da6c28aaSamw /* 298*7b59d02dSjb150015 * The object being created or opened is a directory, 299*7b59d02dSjb150015 * and the Disposition parameter must be one of 300*7b59d02dSjb150015 * FILE_CREATE, FILE_OPEN, or FILE_OPEN_IF 301da6c28aaSamw */ 302da6c28aaSamw if ((op->create_disposition != FILE_CREATE) && 303da6c28aaSamw (op->create_disposition != FILE_OPEN_IF) && 304da6c28aaSamw (op->create_disposition != FILE_OPEN)) { 305dc20a302Sas200622 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 306da6c28aaSamw ERRDOS, ERROR_INVALID_ACCESS); 307*7b59d02dSjb150015 return (NT_STATUS_INVALID_PARAMETER); 308da6c28aaSamw } 309da6c28aaSamw } 310da6c28aaSamw 311da6c28aaSamw if (op->desired_access & MAXIMUM_ALLOWED) { 312da6c28aaSamw max_requested = 1; 313da6c28aaSamw op->desired_access &= ~MAXIMUM_ALLOWED; 314da6c28aaSamw } 315da6c28aaSamw op->desired_access = smb_access_generic_to_file(op->desired_access); 316da6c28aaSamw 317da6c28aaSamw if (sr->session->s_file_cnt >= SMB_SESSION_OFILE_MAX) { 318da6c28aaSamw ASSERT(sr->uid_user); 319da6c28aaSamw cmn_err(CE_NOTE, "smbd[%s\\%s]: %s", sr->uid_user->u_domain, 320da6c28aaSamw sr->uid_user->u_name, 321da6c28aaSamw xlate_nt_status(NT_STATUS_TOO_MANY_OPENED_FILES)); 322da6c28aaSamw 323dc20a302Sas200622 smbsr_error(sr, NT_STATUS_TOO_MANY_OPENED_FILES, 324da6c28aaSamw ERRDOS, ERROR_TOO_MANY_OPEN_FILES); 325*7b59d02dSjb150015 return (NT_STATUS_TOO_MANY_OPENED_FILES); 326da6c28aaSamw } 327da6c28aaSamw 328da6c28aaSamw /* This must be NULL at this point */ 329da6c28aaSamw sr->fid_ofile = NULL; 330da6c28aaSamw 331da6c28aaSamw op->devstate = 0; 332da6c28aaSamw 333da6c28aaSamw switch (sr->tid_tree->t_res_type & STYPE_MASK) { 334da6c28aaSamw case STYPE_DISKTREE: 335da6c28aaSamw break; 336da6c28aaSamw 337da6c28aaSamw case STYPE_IPC: 338da6c28aaSamw /* 339da6c28aaSamw * No further processing for IPC, we need to either 340da6c28aaSamw * raise an exception or return success here. 341da6c28aaSamw */ 342*7b59d02dSjb150015 if ((status = smb_rpc_open(sr)) != NT_STATUS_SUCCESS) 343*7b59d02dSjb150015 smbsr_error(sr, status, 0, 0); 344*7b59d02dSjb150015 return (status); 345da6c28aaSamw 346da6c28aaSamw default: 347*7b59d02dSjb150015 smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE, 348*7b59d02dSjb150015 ERRDOS, ERROR_BAD_DEV_TYPE); 349*7b59d02dSjb150015 return (NT_STATUS_BAD_DEVICE_TYPE); 350da6c28aaSamw } 351da6c28aaSamw 352da6c28aaSamw if ((pathlen = strlen(op->fqi.path)) >= MAXPATHLEN) { 353dc20a302Sas200622 smbsr_error(sr, 0, ERRSRV, ERRfilespecs); 354*7b59d02dSjb150015 return (NT_STATUS_NAME_TOO_LONG); 355da6c28aaSamw } 356da6c28aaSamw 357da6c28aaSamw /* 358da6c28aaSamw * Some clients pass null file names; NT interprets this as "\". 359da6c28aaSamw */ 360da6c28aaSamw if (pathlen == 0) { 361da6c28aaSamw op->fqi.path = "\\"; 362da6c28aaSamw pathlen = 1; 363da6c28aaSamw } 364da6c28aaSamw 365da6c28aaSamw op->fqi.srch_attr = op->fqi.srch_attr; 366da6c28aaSamw 367da6c28aaSamw if ((status = smb_validate_object_name(op->fqi.path, is_dir)) != 0) { 368dc20a302Sas200622 smbsr_error(sr, status, ERRDOS, ERROR_INVALID_NAME); 369*7b59d02dSjb150015 return (status); 370da6c28aaSamw } 371da6c28aaSamw 372da6c28aaSamw cur_node = op->fqi.dir_snode ? 373da6c28aaSamw op->fqi.dir_snode : sr->tid_tree->t_snode; 374da6c28aaSamw 375da6c28aaSamw if (rc = smb_pathname_reduce(sr, sr->user_cr, op->fqi.path, 376da6c28aaSamw sr->tid_tree->t_snode, cur_node, &op->fqi.dir_snode, 377da6c28aaSamw op->fqi.last_comp)) { 378dc20a302Sas200622 smbsr_errno(sr, rc); 379*7b59d02dSjb150015 return (sr->smb_error.status); 380da6c28aaSamw } 381da6c28aaSamw 382da6c28aaSamw /* 383da6c28aaSamw * If the access mask has only DELETE set (ignore 384da6c28aaSamw * FILE_READ_ATTRIBUTES), then assume that this 385da6c28aaSamw * is a request to delete the link (if a link) 386da6c28aaSamw * and do not follow links. Otherwise, follow 387da6c28aaSamw * the link to the target. 388da6c28aaSamw */ 389da6c28aaSamw 390da6c28aaSamw daccess = op->desired_access & ~FILE_READ_ATTRIBUTES; 391da6c28aaSamw 392da6c28aaSamw if (daccess == DELETE) 393da6c28aaSamw lookup_flags &= ~SMB_FOLLOW_LINKS; 394da6c28aaSamw 395da6c28aaSamw rc = smb_fsop_lookup_name(sr, kcred, lookup_flags, 396da6c28aaSamw sr->tid_tree->t_snode, op->fqi.dir_snode, op->fqi.last_comp, 397da6c28aaSamw &op->fqi.last_snode, &op->fqi.last_attr); 398da6c28aaSamw 399da6c28aaSamw if (rc == 0) { 400da6c28aaSamw op->fqi.last_comp_was_found = 1; 401da6c28aaSamw (void) strcpy(op->fqi.last_comp_od, 402da6c28aaSamw op->fqi.last_snode->od_name); 403da6c28aaSamw } else if (rc == ENOENT) { 404da6c28aaSamw op->fqi.last_comp_was_found = 0; 405da6c28aaSamw op->fqi.last_snode = NULL; 406da6c28aaSamw rc = 0; 407da6c28aaSamw } else { 408da6c28aaSamw smb_node_release(op->fqi.dir_snode); 409da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 410dc20a302Sas200622 smbsr_errno(sr, rc); 411*7b59d02dSjb150015 return (sr->smb_error.status); 412da6c28aaSamw } 413da6c28aaSamw 414dc20a302Sas200622 /* 415dc20a302Sas200622 * The uniq_fid is a CIFS-server-wide unique identifier for an ofile 416dc20a302Sas200622 * which is used to uniquely identify open instances for the 417dc20a302Sas200622 * VFS share reservation mechanism (accessed via smb_fsop_shrlock()). 418dc20a302Sas200622 */ 419dc20a302Sas200622 420dc20a302Sas200622 uniq_fid = SMB_UNIQ_FID(); 421dc20a302Sas200622 422da6c28aaSamw if (op->fqi.last_comp_was_found) { 423da6c28aaSamw node = op->fqi.last_snode; 424da6c28aaSamw dnode = op->fqi.dir_snode; 425da6c28aaSamw 426da6c28aaSamw /* 427dc20a302Sas200622 * Enter critical region for share reservations. 428dc20a302Sas200622 * (See comments above smb_fsop_shrlock().) 429dc20a302Sas200622 */ 430dc20a302Sas200622 431dc20a302Sas200622 rw_enter(&node->n_share_lock, RW_WRITER); 432dc20a302Sas200622 433dc20a302Sas200622 /* 434da6c28aaSamw * Reject this request if the target is a directory 435da6c28aaSamw * and the client has specified that it must not be 436da6c28aaSamw * a directory (required by Lotus Notes). 437da6c28aaSamw */ 438da6c28aaSamw if ((op->create_options & FILE_NON_DIRECTORY_FILE) && 439da6c28aaSamw (op->fqi.last_attr.sa_vattr.va_type == VDIR)) { 440dc20a302Sas200622 rw_exit(&node->n_share_lock); 441da6c28aaSamw smb_node_release(node); 442da6c28aaSamw smb_node_release(dnode); 443da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 444dc20a302Sas200622 smbsr_error(sr, NT_STATUS_FILE_IS_A_DIRECTORY, 445da6c28aaSamw ERRDOS, ERROR_ACCESS_DENIED); 446*7b59d02dSjb150015 return (NT_STATUS_FILE_IS_A_DIRECTORY); 447da6c28aaSamw } 448da6c28aaSamw 449da6c28aaSamw if (op->fqi.last_attr.sa_vattr.va_type == VDIR) { 450da6c28aaSamw if ((sr->smb_com == SMB_COM_OPEN_ANDX) || 451da6c28aaSamw (sr->smb_com == SMB_COM_OPEN)) { 452da6c28aaSamw /* 453da6c28aaSamw * Directories cannot be opened 454da6c28aaSamw * with the above commands 455da6c28aaSamw */ 456dc20a302Sas200622 rw_exit(&node->n_share_lock); 457da6c28aaSamw smb_node_release(node); 458da6c28aaSamw smb_node_release(dnode); 459da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 460dc20a302Sas200622 smbsr_error(sr, NT_STATUS_FILE_IS_A_DIRECTORY, 461da6c28aaSamw ERRDOS, ERROR_ACCESS_DENIED); 462*7b59d02dSjb150015 return (NT_STATUS_FILE_IS_A_DIRECTORY); 463da6c28aaSamw } 464da6c28aaSamw } else if (op->my_flags & MYF_MUST_BE_DIRECTORY) { 465dc20a302Sas200622 rw_exit(&node->n_share_lock); 466da6c28aaSamw smb_node_release(node); 467da6c28aaSamw smb_node_release(dnode); 468da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 469dc20a302Sas200622 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 470da6c28aaSamw ERRDOS, ERROR_DIRECTORY); 471*7b59d02dSjb150015 return (NT_STATUS_NOT_A_DIRECTORY); 472da6c28aaSamw } 473da6c28aaSamw 474da6c28aaSamw /* 475da6c28aaSamw * No more open should be accepted when "Delete on close" 476da6c28aaSamw * flag is set. 477da6c28aaSamw */ 478da6c28aaSamw if (node->flags & NODE_FLAGS_DELETE_ON_CLOSE) { 479dc20a302Sas200622 rw_exit(&node->n_share_lock); 480da6c28aaSamw smb_node_release(node); 481da6c28aaSamw smb_node_release(dnode); 482da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 483dc20a302Sas200622 smbsr_error(sr, NT_STATUS_DELETE_PENDING, 484da6c28aaSamw ERRDOS, ERROR_ACCESS_DENIED); 485*7b59d02dSjb150015 return (NT_STATUS_DELETE_PENDING); 486da6c28aaSamw } 487da6c28aaSamw 488da6c28aaSamw /* 489da6c28aaSamw * Specified file already exists so the operation should fail. 490da6c28aaSamw */ 491da6c28aaSamw if (op->create_disposition == FILE_CREATE) { 492dc20a302Sas200622 rw_exit(&node->n_share_lock); 493da6c28aaSamw smb_node_release(node); 494da6c28aaSamw smb_node_release(dnode); 495da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 496dc20a302Sas200622 smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION, 497dc20a302Sas200622 ERRDOS, ERROR_ALREADY_EXISTS); 498*7b59d02dSjb150015 return (NT_STATUS_OBJECT_NAME_COLLISION); 499da6c28aaSamw } 500da6c28aaSamw 501da6c28aaSamw /* 502da6c28aaSamw * Windows seems to check read-only access before file 503da6c28aaSamw * sharing check. 504da6c28aaSamw */ 505da6c28aaSamw if (NODE_IS_READONLY(node)) { 506da6c28aaSamw /* Files data only */ 507da6c28aaSamw if (node->attr.sa_vattr.va_type != VDIR) { 508da6c28aaSamw if (op->desired_access & (FILE_WRITE_DATA | 509da6c28aaSamw FILE_APPEND_DATA)) { 510dc20a302Sas200622 rw_exit(&node->n_share_lock); 511da6c28aaSamw smb_node_release(node); 512da6c28aaSamw smb_node_release(dnode); 513da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 514dc20a302Sas200622 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 515dc20a302Sas200622 ERRDOS, ERRnoaccess); 516*7b59d02dSjb150015 return (NT_STATUS_ACCESS_DENIED); 517da6c28aaSamw } 518da6c28aaSamw } 519da6c28aaSamw } 520da6c28aaSamw 521dc20a302Sas200622 /* 522dc20a302Sas200622 * The following check removes the need to check share 523dc20a302Sas200622 * reservations again when a truncate is done. 524dc20a302Sas200622 */ 525dc20a302Sas200622 526dc20a302Sas200622 if ((op->create_disposition == FILE_SUPERSEDE) || 527dc20a302Sas200622 (op->create_disposition == FILE_OVERWRITE_IF) || 528dc20a302Sas200622 (op->create_disposition == FILE_OVERWRITE)) { 529dc20a302Sas200622 530dc20a302Sas200622 if (!(op->desired_access & 531dc20a302Sas200622 (FILE_WRITE_DATA | FILE_APPEND_DATA))) { 532dc20a302Sas200622 rw_exit(&node->n_share_lock); 533dc20a302Sas200622 smb_node_release(node); 534dc20a302Sas200622 smb_node_release(dnode); 535dc20a302Sas200622 SMB_NULL_FQI_NODES(op->fqi); 536dc20a302Sas200622 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 537dc20a302Sas200622 ERRDOS, ERRnoaccess); 538*7b59d02dSjb150015 return (NT_STATUS_ACCESS_DENIED); 539dc20a302Sas200622 } 540dc20a302Sas200622 } 541dc20a302Sas200622 542dc20a302Sas200622 status = smb_fsop_shrlock(sr->user_cr, node, uniq_fid, 543dc20a302Sas200622 op->desired_access, share_access); 544dc20a302Sas200622 545da6c28aaSamw if (status == NT_STATUS_SHARING_VIOLATION) { 546dc20a302Sas200622 rw_exit(&node->n_share_lock); 547da6c28aaSamw smb_node_release(node); 548da6c28aaSamw smb_node_release(dnode); 549da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 550da6c28aaSamw return (status); 551da6c28aaSamw } 552da6c28aaSamw 553da6c28aaSamw status = smb_fsop_access(sr, sr->user_cr, node, 554da6c28aaSamw op->desired_access); 555da6c28aaSamw 556da6c28aaSamw if (status != NT_STATUS_SUCCESS) { 557dc20a302Sas200622 smb_fsop_unshrlock(sr->user_cr, node, uniq_fid); 558dc20a302Sas200622 559dc20a302Sas200622 rw_exit(&node->n_share_lock); 560da6c28aaSamw smb_node_release(node); 561da6c28aaSamw smb_node_release(dnode); 562da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 563dc20a302Sas200622 564da6c28aaSamw if (status == NT_STATUS_PRIVILEGE_NOT_HELD) { 565dc20a302Sas200622 smbsr_error(sr, status, 566dc20a302Sas200622 ERRDOS, ERROR_PRIVILEGE_NOT_HELD); 567*7b59d02dSjb150015 return (status); 568da6c28aaSamw } else { 569dc20a302Sas200622 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 570dc20a302Sas200622 ERRDOS, ERROR_ACCESS_DENIED); 571*7b59d02dSjb150015 return (NT_STATUS_ACCESS_DENIED); 572da6c28aaSamw } 573da6c28aaSamw } 574da6c28aaSamw 575da6c28aaSamw /* 576da6c28aaSamw * Break the oplock before share checks. If another client 577da6c28aaSamw * has the file open, this will force a flush or close, 578da6c28aaSamw * which may affect the outcome of any share checking. 579da6c28aaSamw */ 580dc20a302Sas200622 581da6c28aaSamw if (OPLOCKS_IN_FORCE(node)) { 582da6c28aaSamw status = smb_break_oplock(sr, node); 583da6c28aaSamw 584da6c28aaSamw if (status != NT_STATUS_SUCCESS) { 585dc20a302Sas200622 rw_exit(&node->n_share_lock); 586da6c28aaSamw smb_node_release(node); 587da6c28aaSamw smb_node_release(dnode); 588da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 589dc20a302Sas200622 smbsr_error(sr, status, 590da6c28aaSamw ERRDOS, ERROR_VC_DISCONNECTED); 591*7b59d02dSjb150015 return (status); 592da6c28aaSamw } 593da6c28aaSamw } 594da6c28aaSamw 595da6c28aaSamw switch (op->create_disposition) { 596da6c28aaSamw case FILE_SUPERSEDE: 597da6c28aaSamw case FILE_OVERWRITE_IF: 598da6c28aaSamw case FILE_OVERWRITE: 599da6c28aaSamw if (node->attr.sa_vattr.va_type == VDIR) { 600dc20a302Sas200622 smb_fsop_unshrlock(sr->user_cr, node, uniq_fid); 601dc20a302Sas200622 rw_exit(&node->n_share_lock); 602da6c28aaSamw smb_node_release(node); 603da6c28aaSamw smb_node_release(dnode); 604da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 605dc20a302Sas200622 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 606dc20a302Sas200622 ERRDOS, ERROR_ACCESS_DENIED); 607*7b59d02dSjb150015 return (NT_STATUS_ACCESS_DENIED); 608da6c28aaSamw } 609da6c28aaSamw 610da6c28aaSamw if (node->attr.sa_vattr.va_size != op->dsize) { 611da6c28aaSamw node->flags &= ~NODE_FLAGS_SET_SIZE; 612dc20a302Sas200622 bzero(&new_attr, sizeof (new_attr)); 613da6c28aaSamw new_attr.sa_vattr.va_size = op->dsize; 614da6c28aaSamw new_attr.sa_mask = SMB_AT_SIZE; 615dc20a302Sas200622 616dc20a302Sas200622 rc = smb_fsop_setattr(sr, sr->user_cr, 617dc20a302Sas200622 node, &new_attr, &op->fqi.last_attr); 618dc20a302Sas200622 619dc20a302Sas200622 if (rc) { 620dc20a302Sas200622 smb_fsop_unshrlock(sr->user_cr, 621dc20a302Sas200622 node, uniq_fid); 622dc20a302Sas200622 rw_exit(&node->n_share_lock); 623da6c28aaSamw smb_node_release(node); 624da6c28aaSamw smb_node_release(dnode); 625da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 626dc20a302Sas200622 smbsr_errno(sr, rc); 627*7b59d02dSjb150015 return (sr->smb_error.status); 628da6c28aaSamw } 629da6c28aaSamw 630dc20a302Sas200622 op->dsize = op->fqi.last_attr.sa_vattr.va_size; 631da6c28aaSamw } 632da6c28aaSamw 633da6c28aaSamw /* 634da6c28aaSamw * If file is being replaced, 635da6c28aaSamw * we should remove existing streams 636da6c28aaSamw */ 637da6c28aaSamw if (SMB_IS_STREAM(node) == 0) 638da6c28aaSamw (void) smb_fsop_remove_streams(sr, sr->user_cr, 639da6c28aaSamw node); 640da6c28aaSamw 641da6c28aaSamw op->action_taken = SMB_OACT_TRUNCATED; 642da6c28aaSamw break; 643da6c28aaSamw 644da6c28aaSamw default: 645da6c28aaSamw /* 646da6c28aaSamw * FILE_OPEN or FILE_OPEN_IF. 647da6c28aaSamw */ 648da6c28aaSamw op->action_taken = SMB_OACT_OPENED; 649da6c28aaSamw break; 650da6c28aaSamw } 651da6c28aaSamw } else { 652da6c28aaSamw 653da6c28aaSamw /* Last component was not found. */ 654da6c28aaSamw dnode = op->fqi.dir_snode; 655da6c28aaSamw 656*7b59d02dSjb150015 if (is_dir == 0) 657*7b59d02dSjb150015 is_stream = smb_stream_parse_name(op->fqi.path, 658*7b59d02dSjb150015 NULL, NULL); 659*7b59d02dSjb150015 660da6c28aaSamw if ((op->create_disposition == FILE_OPEN) || 661da6c28aaSamw (op->create_disposition == FILE_OVERWRITE)) { 662da6c28aaSamw smb_node_release(dnode); 663da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 664da6c28aaSamw 665da6c28aaSamw /* 666da6c28aaSamw * The requested file not found so the operation should 667da6c28aaSamw * fail with these two dispositions 668da6c28aaSamw */ 669da6c28aaSamw if (is_stream) 670dc20a302Sas200622 smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, 671da6c28aaSamw ERRDOS, ERROR_FILE_NOT_FOUND); 672da6c28aaSamw else 673dc20a302Sas200622 smbsr_error(sr, 0, ERRDOS, ERRbadfile); 674*7b59d02dSjb150015 675*7b59d02dSjb150015 return (NT_STATUS_OBJECT_NAME_NOT_FOUND); 676da6c28aaSamw } 677da6c28aaSamw 678da6c28aaSamw /* 679da6c28aaSamw * lock the parent dir node in case another create 680da6c28aaSamw * request to the same parent directory comes in. 681da6c28aaSamw */ 682da6c28aaSamw smb_rwx_rwenter(&dnode->n_lock, RW_WRITER); 683da6c28aaSamw 684da6c28aaSamw bzero(&new_attr, sizeof (new_attr)); 685da6c28aaSamw if (is_dir == 0) { 686da6c28aaSamw new_attr.sa_vattr.va_type = VREG; 687*7b59d02dSjb150015 new_attr.sa_vattr.va_mode = is_stream ? S_IRUSR : 688*7b59d02dSjb150015 S_IRUSR | S_IRGRP | S_IROTH | 689*7b59d02dSjb150015 S_IWUSR | S_IWGRP | S_IWOTH; 690da6c28aaSamw new_attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE; 691dc20a302Sas200622 692dc20a302Sas200622 /* 693dc20a302Sas200622 * A problem with setting the readonly bit at 694dc20a302Sas200622 * create time is that this bit will prevent 695dc20a302Sas200622 * writes to the file from the same fid (which 696dc20a302Sas200622 * should be allowed). 697dc20a302Sas200622 * 698dc20a302Sas200622 * The solution is to set the bit at close time. 699dc20a302Sas200622 * Meanwhile, to prevent racing opens from being 700dc20a302Sas200622 * able to write to the file, the bit is set at 701dc20a302Sas200622 * create time until share reservations can be set 702dc20a302Sas200622 * to prevent write and delete access. At that point, 703dc20a302Sas200622 * the bit can be turned off until close (so as to 704dc20a302Sas200622 * allow writes from the same fid to the file). 705dc20a302Sas200622 */ 706dc20a302Sas200622 707dc20a302Sas200622 if (op->dattr & SMB_FA_READONLY) { 708dc20a302Sas200622 new_attr.sa_dosattr = FILE_ATTRIBUTE_READONLY; 709dc20a302Sas200622 new_attr.sa_mask |= SMB_AT_DOSATTR; 710dc20a302Sas200622 } 711dc20a302Sas200622 712da6c28aaSamw rc = smb_fsop_create(sr, sr->user_cr, dnode, 713da6c28aaSamw op->fqi.last_comp, &new_attr, 714da6c28aaSamw &op->fqi.last_snode, &op->fqi.last_attr); 715dc20a302Sas200622 716da6c28aaSamw if (rc != 0) { 717da6c28aaSamw smb_rwx_rwexit(&dnode->n_lock); 718da6c28aaSamw smb_node_release(dnode); 719da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 720dc20a302Sas200622 smbsr_errno(sr, rc); 721*7b59d02dSjb150015 return (sr->smb_error.status); 722da6c28aaSamw } 723da6c28aaSamw 724dc20a302Sas200622 if (op->dattr & SMB_FA_READONLY) { 725dc20a302Sas200622 share_access &= ~(FILE_SHARE_WRITE | 726dc20a302Sas200622 FILE_SHARE_DELETE); 727dc20a302Sas200622 } 728dc20a302Sas200622 729dc20a302Sas200622 node = op->fqi.last_snode; 730dc20a302Sas200622 731dc20a302Sas200622 rw_enter(&node->n_share_lock, RW_WRITER); 732dc20a302Sas200622 733dc20a302Sas200622 status = smb_fsop_shrlock(sr->user_cr, node, 734dc20a302Sas200622 uniq_fid, op->desired_access, 735dc20a302Sas200622 share_access); 736dc20a302Sas200622 737dc20a302Sas200622 if (status == NT_STATUS_SHARING_VIOLATION) { 738dc20a302Sas200622 rw_exit(&node->n_share_lock); 739dc20a302Sas200622 smb_node_release(node); 740dc20a302Sas200622 smb_node_release(dnode); 741dc20a302Sas200622 SMB_NULL_FQI_NODES(op->fqi); 742dc20a302Sas200622 return (status); 743dc20a302Sas200622 } 744dc20a302Sas200622 745dc20a302Sas200622 new_attr = op->fqi.last_attr; 746dc20a302Sas200622 new_attr.sa_mask = 0; 747dc20a302Sas200622 748dc20a302Sas200622 if (op->dattr & SMB_FA_READONLY) { 749dc20a302Sas200622 new_attr.sa_dosattr &= ~FILE_ATTRIBUTE_READONLY; 750dc20a302Sas200622 new_attr.sa_mask |= SMB_AT_DOSATTR; 751dc20a302Sas200622 } 752dc20a302Sas200622 753da6c28aaSamw if (op->dsize) { 754da6c28aaSamw new_attr.sa_vattr.va_size = op->dsize; 755dc20a302Sas200622 new_attr.sa_mask |= SMB_AT_SIZE; 756dc20a302Sas200622 } 757dc20a302Sas200622 758dc20a302Sas200622 if (new_attr.sa_mask) { 759dc20a302Sas200622 node->attr = new_attr; 760dc20a302Sas200622 node->what = new_attr.sa_mask; 761dc20a302Sas200622 rc = smb_sync_fsattr(sr, sr->user_cr, node); 762dc20a302Sas200622 763da6c28aaSamw if (rc != 0) { 764dc20a302Sas200622 smb_fsop_unshrlock(sr->user_cr, node, 765dc20a302Sas200622 uniq_fid); 766dc20a302Sas200622 767dc20a302Sas200622 rw_exit(&node->n_share_lock); 768dc20a302Sas200622 smb_node_release(node); 769da6c28aaSamw (void) smb_fsop_remove(sr, sr->user_cr, 770da6c28aaSamw dnode, op->fqi.last_comp, 0); 771da6c28aaSamw smb_rwx_rwexit(&dnode->n_lock); 772da6c28aaSamw smb_node_release(dnode); 773da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 774dc20a302Sas200622 smbsr_errno(sr, rc); 775*7b59d02dSjb150015 return (sr->smb_error.status); 776dc20a302Sas200622 } else { 777dc20a302Sas200622 op->fqi.last_attr = node->attr; 778da6c28aaSamw } 779da6c28aaSamw } 780da6c28aaSamw 781da6c28aaSamw } else { 782da6c28aaSamw op->dattr |= SMB_FA_DIRECTORY; 783da6c28aaSamw new_attr.sa_vattr.va_type = VDIR; 784da6c28aaSamw new_attr.sa_vattr.va_mode = 0777; 785da6c28aaSamw new_attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE; 786da6c28aaSamw rc = smb_fsop_mkdir(sr, sr->user_cr, dnode, 787da6c28aaSamw op->fqi.last_comp, &new_attr, 788da6c28aaSamw &op->fqi.last_snode, &op->fqi.last_attr); 789da6c28aaSamw if (rc != 0) { 790da6c28aaSamw smb_rwx_rwexit(&dnode->n_lock); 791da6c28aaSamw smb_node_release(dnode); 792da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 793dc20a302Sas200622 smbsr_errno(sr, rc); 794*7b59d02dSjb150015 return (sr->smb_error.status); 795da6c28aaSamw } 796dc20a302Sas200622 797dc20a302Sas200622 node = op->fqi.last_snode; 798dc20a302Sas200622 rw_enter(&node->n_share_lock, RW_WRITER); 799da6c28aaSamw } 800da6c28aaSamw 801da6c28aaSamw created = 1; 802da6c28aaSamw op->action_taken = SMB_OACT_CREATED; 803da6c28aaSamw } 804da6c28aaSamw 805da6c28aaSamw if ((op->fqi.last_attr.sa_vattr.va_type != VREG) && 806da6c28aaSamw (op->fqi.last_attr.sa_vattr.va_type != VDIR) && 807da6c28aaSamw (op->fqi.last_attr.sa_vattr.va_type != VLNK)) { 808da6c28aaSamw /* not allowed to do this */ 809dc20a302Sas200622 810dc20a302Sas200622 smb_fsop_unshrlock(sr->user_cr, node, uniq_fid); 811dc20a302Sas200622 812da6c28aaSamw SMB_DEL_NEWOBJ(op->fqi); 813dc20a302Sas200622 rw_exit(&node->n_share_lock); 814da6c28aaSamw smb_node_release(node); 815da6c28aaSamw if (created) 816da6c28aaSamw smb_rwx_rwexit(&dnode->n_lock); 817da6c28aaSamw smb_node_release(dnode); 818da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 819dc20a302Sas200622 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess); 820*7b59d02dSjb150015 return (NT_STATUS_ACCESS_DENIED); 821da6c28aaSamw } 822da6c28aaSamw 823da6c28aaSamw if (max_requested) { 824da6c28aaSamw smb_fsop_eaccess(sr, sr->user_cr, node, &max_allowed); 825da6c28aaSamw op->desired_access |= max_allowed; 826da6c28aaSamw } 827da6c28aaSamw 828da6c28aaSamw /* 829da6c28aaSamw * smb_ofile_open() will copy node to of->node. Hence 830da6c28aaSamw * the hold on node (i.e. op->fqi.last_snode) will be "transferred" 831da6c28aaSamw * to the "of" structure. 832da6c28aaSamw */ 833da6c28aaSamw 834da6c28aaSamw of = smb_ofile_open(sr->tid_tree, node, sr->smb_pid, op->desired_access, 835dc20a302Sas200622 op->create_options, share_access, SMB_FTYPE_DISK, NULL, 0, 836dc20a302Sas200622 uniq_fid, &err); 837da6c28aaSamw 838da6c28aaSamw if (of == NULL) { 839dc20a302Sas200622 smb_fsop_unshrlock(sr->user_cr, node, uniq_fid); 840dc20a302Sas200622 841da6c28aaSamw SMB_DEL_NEWOBJ(op->fqi); 842dc20a302Sas200622 rw_exit(&node->n_share_lock); 843da6c28aaSamw smb_node_release(node); 844da6c28aaSamw if (created) 845da6c28aaSamw smb_rwx_rwexit(&dnode->n_lock); 846da6c28aaSamw smb_node_release(dnode); 847da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 848dc20a302Sas200622 smbsr_error(sr, err.status, err.errcls, err.errcode); 849*7b59d02dSjb150015 return (err.status); 850da6c28aaSamw } 851da6c28aaSamw 852da6c28aaSamw /* 853da6c28aaSamw * Propagate the write-through mode from the open params 854da6c28aaSamw * to the node: see the notes in the function header. 855da6c28aaSamw * 856da6c28aaSamw * IR #102318 Mirroring may force synchronous 857da6c28aaSamw * writes regardless of what we specify here. 858da6c28aaSamw */ 859da6c28aaSamw if (smb_stable_mode || (op->create_options & FILE_WRITE_THROUGH)) 860da6c28aaSamw node->flags |= NODE_FLAGS_WRITE_THROUGH; 861da6c28aaSamw 862da6c28aaSamw op->fileid = op->fqi.last_attr.sa_vattr.va_nodeid; 863da6c28aaSamw 864da6c28aaSamw if (op->fqi.last_attr.sa_vattr.va_type == VDIR) { 865da6c28aaSamw /* We don't oplock directories */ 866da6c28aaSamw op->my_flags &= ~MYF_OPLOCK_MASK; 867da6c28aaSamw op->dsize = 0; 868da6c28aaSamw } else { 869da6c28aaSamw status = smb_acquire_oplock(sr, of, op->my_flags, 870da6c28aaSamw &granted_oplock); 871da6c28aaSamw op->my_flags &= ~MYF_OPLOCK_MASK; 872da6c28aaSamw 873da6c28aaSamw if (status != NT_STATUS_SUCCESS) { 874dc20a302Sas200622 rw_exit(&node->n_share_lock); 875dc20a302Sas200622 /* 876dc20a302Sas200622 * smb_fsop_unshrlock() and smb_fsop_close() 877dc20a302Sas200622 * are called from smb_ofile_close() 878dc20a302Sas200622 */ 879da6c28aaSamw (void) smb_ofile_close(of, 0); 880da6c28aaSamw smb_ofile_release(of); 881da6c28aaSamw if (created) 882da6c28aaSamw smb_rwx_rwexit(&dnode->n_lock); 883dc20a302Sas200622 884da6c28aaSamw smb_node_release(dnode); 885da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 886da6c28aaSamw 887dc20a302Sas200622 smbsr_error(sr, status, 888da6c28aaSamw ERRDOS, ERROR_SHARING_VIOLATION); 889*7b59d02dSjb150015 return (status); 890da6c28aaSamw } 891da6c28aaSamw 892da6c28aaSamw op->my_flags |= granted_oplock; 893da6c28aaSamw op->dsize = op->fqi.last_attr.sa_vattr.va_size; 894da6c28aaSamw } 895da6c28aaSamw 896da6c28aaSamw if (created) { 897da6c28aaSamw node->flags |= NODE_FLAGS_CREATED; 898da6c28aaSamw /* 899da6c28aaSamw * Clients may set the DOS readonly bit on create but they 900da6c28aaSamw * expect subsequent write operations on the open fid to 901dc20a302Sas200622 * succeed. Thus the DOS readonly bit is not set permanently 902dc20a302Sas200622 * until the file is closed. The NODE_CREATED_READONLY flag 903dc20a302Sas200622 * will act as the indicator to set the DOS readonly bit on 904da6c28aaSamw * close. 905dc20a302Sas200622 * Above, the readonly bit is set on create, share 906dc20a302Sas200622 * reservations are set, and then the bit is unset. 907dc20a302Sas200622 * These actions allow writes to the open fid to succeed 908dc20a302Sas200622 * until the file is closed while preventing write access 909dc20a302Sas200622 * from other opens while this fid is active. 910da6c28aaSamw */ 911da6c28aaSamw if (op->dattr & SMB_FA_READONLY) { 912da6c28aaSamw node->flags |= NODE_CREATED_READONLY; 913da6c28aaSamw op->dattr &= ~SMB_FA_READONLY; 914da6c28aaSamw } 915da6c28aaSamw smb_node_set_dosattr(node, op->dattr | SMB_FA_ARCHIVE); 91655bf511dSas200622 if (op->utime.tv_sec == 0 || op->utime.tv_sec == UINT_MAX) 917da6c28aaSamw (void) microtime(&op->utime); 918da6c28aaSamw smb_node_set_time(node, NULL, &op->utime, 0, 0, SMB_AT_MTIME); 919da6c28aaSamw (void) smb_sync_fsattr(sr, sr->user_cr, node); 920da6c28aaSamw } else { 921da6c28aaSamw /* 922da6c28aaSamw * If we reach here, it means that file already exists 923da6c28aaSamw * and if create disposition is one of: FILE_SUPERSEDE, 924da6c28aaSamw * FILE_OVERWRITE_IF, or FILE_OVERWRITE it 925da6c28aaSamw * means that client wants to overwrite (or truncate) 926da6c28aaSamw * the existing file. So we should overwrite the dos 927da6c28aaSamw * attributes of destination file with the dos attributes 928da6c28aaSamw * of source file. 929da6c28aaSamw */ 930da6c28aaSamw 931da6c28aaSamw switch (op->create_disposition) { 932da6c28aaSamw case FILE_SUPERSEDE: 933da6c28aaSamw case FILE_OVERWRITE_IF: 934da6c28aaSamw case FILE_OVERWRITE: 935da6c28aaSamw smb_node_set_dosattr(node, 936da6c28aaSamw op->dattr | SMB_FA_ARCHIVE); 937da6c28aaSamw (void) smb_sync_fsattr(sr, sr->user_cr, node); 938da6c28aaSamw } 939da6c28aaSamw op->utime = *smb_node_get_crtime(node); 940da6c28aaSamw op->dattr = smb_node_get_dosattr(node); 941da6c28aaSamw } 942da6c28aaSamw 943da6c28aaSamw /* 944da6c28aaSamw * Set up the file type in open_param for the response 945da6c28aaSamw */ 946da6c28aaSamw op->ftype = SMB_FTYPE_DISK; 947da6c28aaSamw sr->smb_fid = of->f_fid; 948da6c28aaSamw sr->fid_ofile = of; 949da6c28aaSamw 950dc20a302Sas200622 rw_exit(&node->n_share_lock); 951dc20a302Sas200622 952dc20a302Sas200622 if (created) 953da6c28aaSamw smb_rwx_rwexit(&dnode->n_lock); 954dc20a302Sas200622 955da6c28aaSamw smb_node_release(dnode); 956da6c28aaSamw SMB_NULL_FQI_NODES(op->fqi); 957da6c28aaSamw 958da6c28aaSamw return (NT_STATUS_SUCCESS); 959da6c28aaSamw } 960da6c28aaSamw 961da6c28aaSamw /* 962da6c28aaSamw * smb_validate_object_name 963da6c28aaSamw * 964da6c28aaSamw * Very basic file name validation. Directory validation is handed off 965da6c28aaSamw * to smb_validate_dirname. For filenames, we check for names of the 966da6c28aaSamw * form "AAAn:". Names that contain three characters, a single digit 967da6c28aaSamw * and a colon (:) are reserved as DOS device names, i.e. "COM1:". 968da6c28aaSamw * 969da6c28aaSamw * Returns NT status codes. 970da6c28aaSamw */ 971da6c28aaSamw uint32_t 972da6c28aaSamw smb_validate_object_name(char *path, unsigned int ftype) 973da6c28aaSamw { 974da6c28aaSamw char *filename; 975da6c28aaSamw 976da6c28aaSamw if (path == 0) 977da6c28aaSamw return (0); 978da6c28aaSamw 979da6c28aaSamw if (ftype) 980da6c28aaSamw return (smb_validate_dirname(path)); 981da6c28aaSamw 982da6c28aaSamw /* 983da6c28aaSamw * Basename with backslashes. 984da6c28aaSamw */ 985da6c28aaSamw if ((filename = strrchr(path, '\\')) != 0) 986da6c28aaSamw ++filename; 987da6c28aaSamw else 988da6c28aaSamw filename = path; 989da6c28aaSamw 990da6c28aaSamw if (strlen(filename) == 5 && 991da6c28aaSamw mts_isdigit(filename[3]) && 992da6c28aaSamw filename[4] == ':') { 993da6c28aaSamw return (NT_STATUS_OBJECT_NAME_INVALID); 994da6c28aaSamw } 995da6c28aaSamw 996da6c28aaSamw return (0); 997da6c28aaSamw } 998da6c28aaSamw 999da6c28aaSamw /* 1000da6c28aaSamw * smb_preset_delete_on_close 1001da6c28aaSamw * 1002da6c28aaSamw * Set the DeleteOnClose flag on the smb file. When the file is closed, 1003da6c28aaSamw * the flag will be transferred to the smb node, which will commit the 1004da6c28aaSamw * delete operation and inhibit subsequent open requests. 1005da6c28aaSamw * 1006da6c28aaSamw * When DeleteOnClose is set on an smb_node, the common open code will 1007da6c28aaSamw * reject subsequent open requests for the file. Observation of Windows 1008da6c28aaSamw * 2000 indicates that subsequent opens should be allowed (assuming 1009da6c28aaSamw * there would be no sharing violation) until the file is closed using 1010da6c28aaSamw * the fid on which the DeleteOnClose was requested. 1011da6c28aaSamw */ 1012da6c28aaSamw void 1013da6c28aaSamw smb_preset_delete_on_close(smb_ofile_t *file) 1014da6c28aaSamw { 1015da6c28aaSamw mutex_enter(&file->f_mutex); 1016da6c28aaSamw file->f_flags |= SMB_OFLAGS_SET_DELETE_ON_CLOSE; 1017da6c28aaSamw mutex_exit(&file->f_mutex); 1018da6c28aaSamw } 1019