Home | History | Annotate | Download | only in smbsrv
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/synch.h>
     27 #include <smbsrv/smb_kproto.h>
     28 #include <smbsrv/smb_fsops.h>
     29 #include <sys/nbmlock.h>
     30 
     31 /*
     32  * NT_RENAME InformationLevels:
     33  *
     34  * SMB_NT_RENAME_MOVE_CLUSTER_INFO	Server returns invalid parameter.
     35  * SMB_NT_RENAME_SET_LINK_INFO		Create a hard link to a file.
     36  * SMB_NT_RENAME_RENAME_FILE		In-place rename of a file.
     37  * SMB_NT_RENAME_MOVE_FILE		Move (rename) a file.
     38  */
     39 #define	SMB_NT_RENAME_MOVE_CLUSTER_INFO	0x0102
     40 #define	SMB_NT_RENAME_SET_LINK_INFO	0x0103
     41 #define	SMB_NT_RENAME_RENAME_FILE	0x0104
     42 #define	SMB_NT_RENAME_MOVE_FILE		0x0105
     43 
     44 /*
     45  * SMB_TRANS2_SET_FILE/PATH_INFO (RENAME_INFORMATION level) flag
     46  */
     47 #define	SMB_RENAME_FLAG_OVERWRITE	0x001
     48 
     49 static int smb_common_rename(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
     50 static int smb_make_link(smb_request_t *, smb_fqi_t *, smb_fqi_t *);
     51 static int smb_rename_check_stream(smb_fqi_t *, smb_fqi_t *);
     52 static int smb_rename_check_attr(smb_request_t *, smb_node_t *, uint16_t);
     53 static void smb_rename_set_error(smb_request_t *, int);
     54 
     55 static int smb_rename_lookup_src(smb_request_t *);
     56 static void smb_rename_release_src(smb_request_t *);
     57 
     58 /*
     59  * smb_com_rename
     60  *
     61  * Rename a file. Files OldFileName must exist and NewFileName must not.
     62  * Both pathnames must be relative to the Tid specified in the request.
     63  * Open files may be renamed.
     64  *
     65  * Multiple files may be renamed in response to a single request as Rename
     66  * File supports wildcards in the file name (last component of the path).
     67  * NOTE: we don't support rename with wildcards.
     68  *
     69  * SearchAttributes indicates the attributes that the target file(s) must
     70  * have. If SearchAttributes is zero then only normal files are renamed.
     71  * If the system file or hidden attributes are specified then the rename
     72  * is inclusive - both the specified type(s) of files and normal files are
     73  * renamed.
     74  */
     75 smb_sdrc_t
     76 smb_pre_rename(smb_request_t *sr)
     77 {
     78 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
     79 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
     80 	int rc;
     81 
     82 	if ((rc = smbsr_decode_vwv(sr, "w", &src_fqi->fq_sattr)) == 0) {
     83 		rc = smbsr_decode_data(sr, "%SS", sr, &src_fqi->fq_path.pn_path,
     84 		    &dst_fqi->fq_path.pn_path);
     85 
     86 		dst_fqi->fq_sattr = 0;
     87 	}
     88 
     89 	DTRACE_SMB_2(op__Rename__start, smb_request_t *, sr,
     90 	    struct dirop *, &sr->arg.dirop);
     91 
     92 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
     93 }
     94 
     95 void
     96 smb_post_rename(smb_request_t *sr)
     97 {
     98 	DTRACE_SMB_1(op__Rename__done, smb_request_t *, sr);
     99 }
    100 
    101 smb_sdrc_t
    102 smb_com_rename(smb_request_t *sr)
    103 {
    104 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
    105 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
    106 	int rc;
    107 
    108 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
    109 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
    110 		    ERRDOS, ERROR_ACCESS_DENIED);
    111 		return (SDRC_ERROR);
    112 	}
    113 
    114 	rc = smb_common_rename(sr, src_fqi, dst_fqi);
    115 
    116 	if (rc != 0) {
    117 		smb_rename_set_error(sr, rc);
    118 		return (SDRC_ERROR);
    119 	}
    120 
    121 	rc = smbsr_encode_empty_result(sr);
    122 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
    123 }
    124 
    125 /*
    126  * smb_com_nt_rename
    127  *
    128  * Rename a file. Files OldFileName must exist and NewFileName must not.
    129  * Both pathnames must be relative to the Tid specified in the request.
    130  * Open files may be renamed.
    131  *
    132  * SearchAttributes indicates the attributes that the target file(s) must
    133  * have. If SearchAttributes is zero then only normal files are renamed.
    134  * If the system file or hidden attributes are specified then the rename
    135  * is inclusive - both the specified type(s) of files and normal files are
    136  * renamed.
    137  */
    138 smb_sdrc_t
    139 smb_pre_nt_rename(smb_request_t *sr)
    140 {
    141 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
    142 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
    143 	uint32_t clusters;
    144 	int rc;
    145 
    146 	rc = smbsr_decode_vwv(sr, "wwl", &src_fqi->fq_sattr,
    147 	    &sr->arg.dirop.info_level, &clusters);
    148 	if (rc == 0) {
    149 		rc = smbsr_decode_data(sr, "%SS", sr,
    150 		    &src_fqi->fq_path.pn_path, &dst_fqi->fq_path.pn_path);
    151 
    152 		dst_fqi->fq_sattr = 0;
    153 	}
    154 
    155 	DTRACE_SMB_2(op__NtRename__start, smb_request_t *, sr,
    156 	    struct dirop *, &sr->arg.dirop);
    157 
    158 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
    159 }
    160 
    161 void
    162 smb_post_nt_rename(smb_request_t *sr)
    163 {
    164 	DTRACE_SMB_1(op__NtRename__done, smb_request_t *, sr);
    165 }
    166 
    167 smb_sdrc_t
    168 smb_com_nt_rename(smb_request_t *sr)
    169 {
    170 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
    171 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
    172 	int rc;
    173 
    174 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
    175 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
    176 		    ERRDOS, ERROR_ACCESS_DENIED);
    177 		return (SDRC_ERROR);
    178 	}
    179 
    180 	if (smb_convert_wildcards(src_fqi->fq_path.pn_path) != 0) {
    181 		smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
    182 		    ERRDOS, ERROR_BAD_PATHNAME);
    183 		return (SDRC_ERROR);
    184 	}
    185 
    186 	switch (sr->arg.dirop.info_level) {
    187 	case SMB_NT_RENAME_SET_LINK_INFO:
    188 		rc = smb_make_link(sr, src_fqi, dst_fqi);
    189 		break;
    190 	case SMB_NT_RENAME_RENAME_FILE:
    191 	case SMB_NT_RENAME_MOVE_FILE:
    192 		rc = smb_common_rename(sr, src_fqi, dst_fqi);
    193 		break;
    194 	case SMB_NT_RENAME_MOVE_CLUSTER_INFO:
    195 		rc = EINVAL;
    196 		break;
    197 	default:
    198 		rc = EACCES;
    199 		break;
    200 	}
    201 
    202 	if (rc != 0) {
    203 		smb_rename_set_error(sr, rc);
    204 		return (SDRC_ERROR);
    205 	}
    206 
    207 	rc = smbsr_encode_empty_result(sr);
    208 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
    209 }
    210 
    211 /*
    212  * smb_nt_transact_rename
    213  *
    214  * Windows servers return SUCCESS without renaming file.
    215  * The only check required is to check that the handle (fid) is valid.
    216  */
    217 smb_sdrc_t
    218 smb_nt_transact_rename(smb_request_t *sr, smb_xa_t *xa)
    219 {
    220 	if (smb_mbc_decodef(&xa->req_param_mb, "w", &sr->smb_fid) != 0)
    221 		return (SDRC_ERROR);
    222 
    223 	smbsr_lookup_file(sr);
    224 	if (sr->fid_ofile == NULL) {
    225 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
    226 		return (SDRC_ERROR);
    227 	}
    228 	smbsr_release_file(sr);
    229 
    230 	return (SDRC_SUCCESS);
    231 }
    232 
    233 /*
    234  * smb_trans2_rename
    235  *
    236  * Implements SMB_FILE_RENAME_INFORMATION level of Trans2_Set_FileInfo
    237  * and Trans2_Set_PathInfo.
    238  * If the new filename (dst_fqi) already exists it may be overwritten
    239  * if flags == 1.
    240  */
    241 int
    242 smb_trans2_rename(smb_request_t *sr, smb_node_t *node, char *fname, int flags)
    243 {
    244 	int rc;
    245 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
    246 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
    247 
    248 	sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0;
    249 	sr->arg.dirop.info_level = SMB_NT_RENAME_RENAME_FILE;
    250 
    251 	src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES;
    252 	src_fqi->fq_fnode = node;
    253 	src_fqi->fq_dnode = node->n_dnode;
    254 
    255 	dst_fqi->fq_path.pn_path = fname;
    256 	dst_fqi->fq_dnode = node->n_dnode;
    257 	(void) strlcpy(dst_fqi->fq_last_comp, fname, MAXNAMELEN);
    258 
    259 	rc = smb_common_rename(sr, src_fqi, dst_fqi);
    260 	if (rc != 0) {
    261 		smb_rename_set_error(sr, rc);
    262 		return (-1);
    263 	}
    264 
    265 	return (0);
    266 }
    267 
    268 /*
    269  * smb_common_rename
    270  *
    271  * Common code for renaming a file.
    272  *
    273  * If the source and destination are identical, we go through all
    274  * the checks but we don't actually do the rename.  If the source
    275  * and destination files differ only in case, we do a case-sensitive
    276  * rename.  Otherwise, we do a full case-insensitive rename.
    277  *
    278  * Returns errno values.
    279  */
    280 static int
    281 smb_common_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
    282 {
    283 	smb_node_t *src_fnode, *src_dnode, *dst_fnode, *dst_dnode;
    284 	smb_node_t *tnode;
    285 	int rc, count;
    286 	DWORD status;
    287 	char *new_name, *path;
    288 
    289 	path = dst_fqi->fq_path.pn_path;
    290 
    291 	/* Check if attempting to rename a stream - not yet supported */
    292 	rc = smb_rename_check_stream(src_fqi, dst_fqi);
    293 	if (rc != 0)
    294 		return (rc);
    295 
    296 	/* The source node may already have been provided */
    297 	if (src_fqi->fq_fnode) {
    298 		smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
    299 		smb_node_ref(src_fqi->fq_fnode);
    300 		smb_node_ref(src_fqi->fq_dnode);
    301 	} else {
    302 		/* lookup and validate src node */
    303 		rc = smb_rename_lookup_src(sr);
    304 		if (rc != 0)
    305 			return (rc);
    306 	}
    307 
    308 	src_fnode = src_fqi->fq_fnode;
    309 	src_dnode = src_fqi->fq_dnode;
    310 
    311 	/* Find destination dnode and last_comp */
    312 	if (dst_fqi->fq_dnode) {
    313 		smb_node_ref(dst_fqi->fq_dnode);
    314 	} else {
    315 		tnode = sr->tid_tree->t_snode;
    316 		rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
    317 		    &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
    318 		if (rc != 0) {
    319 			smb_rename_release_src(sr);
    320 			return (rc);
    321 		}
    322 	}
    323 
    324 	dst_dnode = dst_fqi->fq_dnode;
    325 	new_name = dst_fqi->fq_last_comp;
    326 
    327 	/* If exact name match in same directory, we're done */
    328 	if ((src_dnode == dst_dnode) &&
    329 	    (strcmp(src_fnode->od_name, new_name) == 0)) {
    330 		smb_rename_release_src(sr);
    331 		smb_node_release(dst_dnode);
    332 		return (0);
    333 	}
    334 
    335 	/* Lookup destination node */
    336 	rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
    337 	    dst_dnode, new_name, &dst_fqi->fq_fnode);
    338 
    339 	/*
    340 	 * Handle case where changing case of the same directory entry.
    341 	 *
    342 	 * If we found the dst node in the same directory as the src node,
    343 	 * and their names differ only in case:
    344 	 *
    345 	 * If the tree is case sensitive (or mixed):
    346 	 *  Do case sensitive lookup to see if exact match exists.
    347 	 *  If the exact match is the same node as src_node we're done.
    348 	 *
    349 	 * If the tree is case insensitive:
    350 	 *  There is currently no way to tell if the case is different
    351 	 *  or not, so do the rename (unless the specified new name was
    352 	 *  mangled).
    353 	 */
    354 	if ((rc == 0) &&
    355 	    (src_dnode == dst_dnode) &&
    356 	    (smb_strcasecmp(src_fnode->od_name,
    357 	    dst_fqi->fq_fnode->od_name, 0) == 0)) {
    358 		smb_node_release(dst_fqi->fq_fnode);
    359 		dst_fqi->fq_fnode = NULL;
    360 
    361 		if (smb_tree_has_feature(sr->tid_tree,
    362 		    SMB_TREE_NO_CASESENSITIVE)) {
    363 			if (smb_strcasecmp(src_fnode->od_name,
    364 			    dst_fqi->fq_last_comp, 0) != 0) {
    365 				smb_rename_release_src(sr);
    366 				smb_node_release(dst_dnode);
    367 				return (0);
    368 			}
    369 		} else {
    370 			rc = smb_fsop_lookup(sr, sr->user_cr,
    371 			    SMB_CASE_SENSITIVE, tnode, dst_dnode, new_name,
    372 			    &dst_fqi->fq_fnode);
    373 
    374 			if ((rc == 0) &&
    375 			    (dst_fqi->fq_fnode == src_fnode)) {
    376 				smb_rename_release_src(sr);
    377 				smb_node_release(dst_fqi->fq_fnode);
    378 				smb_node_release(dst_dnode);
    379 				return (0);
    380 			}
    381 		}
    382 	}
    383 
    384 	if ((rc != 0) && (rc != ENOENT)) {
    385 		smb_rename_release_src(sr);
    386 		smb_node_release(dst_fqi->fq_dnode);
    387 		return (rc);
    388 	}
    389 
    390 	if (dst_fqi->fq_fnode) {
    391 		dst_fnode = dst_fqi->fq_fnode;
    392 
    393 		if (!(sr->arg.dirop.flags && SMB_RENAME_FLAG_OVERWRITE)) {
    394 			smb_rename_release_src(sr);
    395 			smb_node_release(dst_fnode);
    396 			smb_node_release(dst_dnode);
    397 			return (EEXIST);
    398 		}
    399 
    400 		(void) smb_oplock_break(dst_fnode, sr->session, B_FALSE);
    401 
    402 		for (count = 0; count <= 3; count++) {
    403 			if (count) {
    404 				smb_node_end_crit(dst_fnode);
    405 				delay(MSEC_TO_TICK(400));
    406 			}
    407 
    408 			smb_node_start_crit(dst_fnode, RW_READER);
    409 			status = smb_node_delete_check(dst_fnode);
    410 
    411 			if (status != NT_STATUS_SHARING_VIOLATION)
    412 				break;
    413 		}
    414 
    415 		if (status != NT_STATUS_SHARING_VIOLATION)
    416 			status = smb_range_check(sr, dst_fnode,
    417 			    0, UINT64_MAX, B_TRUE);
    418 
    419 		if (status != NT_STATUS_SUCCESS) {
    420 			smb_rename_release_src(sr);
    421 			smb_node_end_crit(dst_fnode);
    422 			smb_node_release(dst_fnode);
    423 			smb_node_release(dst_dnode);
    424 			return (EACCES);
    425 		}
    426 
    427 		if (smb_maybe_mangled_name(new_name)) {
    428 			(void) strlcpy(new_name, dst_fnode->od_name,
    429 			    MAXNAMELEN);
    430 		}
    431 	}
    432 
    433 	rc = smb_fsop_rename(sr, sr->user_cr,
    434 	    src_dnode, src_fnode->od_name,
    435 	    dst_dnode, new_name);
    436 
    437 	smb_rename_release_src(sr);
    438 
    439 	if (rc == 0)
    440 		smb_node_notify_change(dst_dnode);
    441 
    442 	if (dst_fqi->fq_fnode) {
    443 		smb_node_end_crit(dst_fnode);
    444 		smb_node_release(dst_fnode);
    445 	}
    446 	smb_node_release(dst_dnode);
    447 
    448 	return (rc);
    449 }
    450 
    451 /*
    452  * smb_rename_check_stream
    453  *
    454  * For a stream rename the dst path must begin with ':', or "\\:".
    455  * We don't yet support stream rename, Return EACCES.
    456  *
    457  * If not a stream rename, in accordance with the above rule,
    458  * it is not valid for either the src or dst to be a stream.
    459  * Return EINVAL.
    460  */
    461 static int
    462 smb_rename_check_stream(smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
    463 {
    464 	smb_node_t *src_fnode = src_fqi->fq_fnode;
    465 	char *src_path = src_fqi->fq_path.pn_path;
    466 	char *dst_path = dst_fqi->fq_path.pn_path;
    467 
    468 	/* We do not yet support named stream rename - ACCESS DENIED */
    469 	if ((dst_path[0] == ':') ||
    470 	    ((dst_path[0] == '\\') && (dst_path[1] == ':'))) {
    471 		return (EACCES);
    472 	}
    473 
    474 	/*
    475 	 * If not stream rename (above) neither src or dst can be
    476 	 * a named stream.
    477 	 */
    478 
    479 	if (smb_is_stream_name(dst_path))
    480 		return (EINVAL);
    481 
    482 	if (src_fqi->fq_fnode) {
    483 		if (SMB_IS_STREAM(src_fnode))
    484 			return (EINVAL);
    485 	} else {
    486 		if (smb_is_stream_name(src_path))
    487 			return (EINVAL);
    488 	}
    489 
    490 	return (0);
    491 }
    492 
    493 
    494 /*
    495  * smb_make_link
    496  *
    497  * Creating a hard link (adding an additional name) for a file.
    498  *
    499  * If the source and destination are identical, we go through all
    500  * the checks but we don't create a link.
    501  *
    502  * If the file is a symlink we create the hardlink on the target
    503  * of the symlink (i.e. use SMB_FOLLOW_LINKS when looking up src).
    504  * If the target of the symlink does not exist we fail with ENOENT.
    505  *
    506  * Returns errno values.
    507  */
    508 static int
    509 smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
    510 {
    511 	smb_node_t *tnode;
    512 	char *path;
    513 	int rc;
    514 
    515 	/* Cannnot create link on named stream */
    516 	if (smb_is_stream_name(src_fqi->fq_path.pn_path) ||
    517 	    smb_is_stream_name(dst_fqi->fq_path.pn_path)) {
    518 		return (EINVAL);
    519 	}
    520 
    521 	/* lookup and validate src node */
    522 	rc = smb_rename_lookup_src(sr);
    523 	if (rc != 0)
    524 		return (rc);
    525 
    526 	/* if src and dest paths match we're done */
    527 	if (smb_strcasecmp(src_fqi->fq_path.pn_path,
    528 	    dst_fqi->fq_path.pn_path, 0) == 0) {
    529 		smb_rename_release_src(sr);
    530 		return (0);
    531 	}
    532 
    533 	/* find the destination dnode and last_comp */
    534 	tnode = sr->tid_tree->t_snode;
    535 	path = dst_fqi->fq_path.pn_path;
    536 	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
    537 	    &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
    538 	if (rc != 0) {
    539 		smb_rename_release_src(sr);
    540 		return (rc);
    541 	}
    542 
    543 	/* If name match in same directory, we're done */
    544 	if ((src_fqi->fq_dnode == dst_fqi->fq_dnode) &&
    545 	    (smb_strcasecmp(src_fqi->fq_fnode->od_name,
    546 	    dst_fqi->fq_last_comp, 0) == 0)) {
    547 		smb_rename_release_src(sr);
    548 		smb_node_release(dst_fqi->fq_dnode);
    549 		return (0);
    550 	}
    551 
    552 	/* Lookup the destination node. It MUST NOT exist. */
    553 	rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
    554 	    dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode);
    555 	if (rc == 0) {
    556 		smb_node_release(dst_fqi->fq_fnode);
    557 		rc = EEXIST;
    558 	}
    559 	if (rc != ENOENT) {
    560 		smb_rename_release_src(sr);
    561 		smb_node_release(dst_fqi->fq_dnode);
    562 		return (rc);
    563 	}
    564 
    565 	rc = smb_fsop_link(sr, sr->user_cr, src_fqi->fq_fnode,
    566 	    dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
    567 
    568 	smb_rename_release_src(sr);
    569 	if (rc == 0)
    570 		smb_node_notify_change(dst_fqi->fq_dnode);
    571 	smb_node_release(dst_fqi->fq_dnode);
    572 	return (rc);
    573 }
    574 
    575 /*
    576  * smb_rename_lookup_src
    577  *
    578  * Lookup the src node, checking for sharing violations and
    579  * breaking any existing oplock.
    580  * Populate sr->arg.dirop.fqi
    581  *
    582  * Upon success, the dnode and fnode will have holds and the
    583  * fnode will be in a critical section. These should be
    584  * released using smb_rename_release_src().
    585  *
    586  * Returns errno values.
    587  */
    588 static int
    589 smb_rename_lookup_src(smb_request_t *sr)
    590 {
    591 	smb_node_t *src_node, *tnode;
    592 	DWORD status;
    593 	int rc;
    594 	int count;
    595 	char *path;
    596 
    597 	struct dirop *dirop = &sr->arg.dirop;
    598 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
    599 
    600 	if (smb_is_stream_name(src_fqi->fq_path.pn_path))
    601 		return (EINVAL);
    602 
    603 	/* Lookup the source node */
    604 	tnode = sr->tid_tree->t_snode;
    605 	path = src_fqi->fq_path.pn_path;
    606 	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
    607 	    &src_fqi->fq_dnode, src_fqi->fq_last_comp);
    608 	if (rc != 0)
    609 		return (rc);
    610 
    611 	rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
    612 	    src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode);
    613 	if (rc != 0) {
    614 		smb_node_release(src_fqi->fq_dnode);
    615 		return (rc);
    616 	}
    617 
    618 	/* Not valid to create hardlink for directory */
    619 	if ((dirop->info_level == SMB_NT_RENAME_SET_LINK_INFO) &&
    620 	    (smb_node_is_dir(src_fqi->fq_fnode))) {
    621 		smb_node_release(src_fqi->fq_fnode);
    622 		smb_node_release(src_fqi->fq_dnode);
    623 		return (EISDIR);
    624 	}
    625 
    626 	src_node = src_fqi->fq_fnode;
    627 
    628 	rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr);
    629 	if (rc != 0) {
    630 		smb_node_release(src_fqi->fq_fnode);
    631 		smb_node_release(src_fqi->fq_dnode);
    632 		return (rc);
    633 	}
    634 
    635 	/*
    636 	 * Break the oplock before access checks. If a client
    637 	 * has a file open, this will force a flush or close,
    638 	 * which may affect the outcome of any share checking.
    639 	 */
    640 	(void) smb_oplock_break(src_node, sr->session, B_FALSE);
    641 
    642 	for (count = 0; count <= 3; count++) {
    643 		if (count) {
    644 			smb_node_end_crit(src_node);
    645 			delay(MSEC_TO_TICK(400));
    646 		}
    647 
    648 		smb_node_start_crit(src_node, RW_READER);
    649 
    650 		status = smb_node_rename_check(src_node);
    651 		if (status != NT_STATUS_SHARING_VIOLATION)
    652 			break;
    653 	}
    654 
    655 	if (status == NT_STATUS_SHARING_VIOLATION) {
    656 		smb_node_end_crit(src_node);
    657 		smb_node_release(src_fqi->fq_fnode);
    658 		smb_node_release(src_fqi->fq_dnode);
    659 		return (EPIPE); /* = ERRbadshare */
    660 	}
    661 
    662 	status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE);
    663 	if (status != NT_STATUS_SUCCESS) {
    664 		smb_node_end_crit(src_node);
    665 		smb_node_release(src_fqi->fq_fnode);
    666 		smb_node_release(src_fqi->fq_dnode);
    667 		return (EACCES);
    668 	}
    669 
    670 	return (0);
    671 }
    672 
    673 /*
    674  * smb_rename_release_src
    675  */
    676 static void
    677 smb_rename_release_src(smb_request_t *sr)
    678 {
    679 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
    680 
    681 	smb_node_end_crit(src_fqi->fq_fnode);
    682 	smb_node_release(src_fqi->fq_fnode);
    683 	smb_node_release(src_fqi->fq_dnode);
    684 }
    685 
    686 
    687 static int
    688 smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr)
    689 {
    690 	smb_attr_t attr;
    691 
    692 	if (smb_node_getattr(sr, node, &attr) != 0)
    693 		return (EIO);
    694 
    695 	if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) &&
    696 	    !(SMB_SEARCH_HIDDEN(sattr)))
    697 		return (ESRCH);
    698 
    699 	if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) &&
    700 	    !(SMB_SEARCH_SYSTEM(sattr)))
    701 		return (ESRCH);
    702 
    703 	return (0);
    704 }
    705 
    706 /*
    707  * The following values are based on observed WFWG, Windows 9x, Windows NT
    708  * and Windows 2000 behaviour.
    709  *
    710  * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
    711  *
    712  * Windows 95 clients don't see the problem because the target is deleted
    713  * before the rename request.
    714  */
    715 static void
    716 smb_rename_set_error(smb_request_t *sr, int errnum)
    717 {
    718 	static struct {
    719 		int errnum;
    720 		uint16_t errcode;
    721 		uint32_t status32;
    722 	} rc_map[] = {
    723 	{ EEXIST, ERROR_ALREADY_EXISTS,	NT_STATUS_OBJECT_NAME_COLLISION },
    724 	{ EPIPE,  ERROR_SHARING_VIOLATION, NT_STATUS_SHARING_VIOLATION },
    725 	{ ENOENT, ERROR_FILE_NOT_FOUND,	NT_STATUS_OBJECT_NAME_NOT_FOUND },
    726 	{ ESRCH,  ERROR_FILE_NOT_FOUND,	NT_STATUS_NO_SUCH_FILE },
    727 	{ EINVAL, ERROR_INVALID_PARAMETER, NT_STATUS_INVALID_PARAMETER },
    728 	{ EACCES, ERROR_ACCESS_DENIED,	NT_STATUS_ACCESS_DENIED },
    729 	{ EISDIR, ERROR_ACCESS_DENIED,	NT_STATUS_FILE_IS_A_DIRECTORY },
    730 	{ EIO,    ERROR_INTERNAL_ERROR,	NT_STATUS_INTERNAL_ERROR }
    731 	};
    732 
    733 	int i;
    734 
    735 	if (errnum == 0)
    736 		return;
    737 
    738 	for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) {
    739 		if (rc_map[i].errnum == errnum) {
    740 			smbsr_error(sr, rc_map[i].status32,
    741 			    ERRDOS, rc_map[i].errcode);
    742 			return;
    743 		}
    744 	}
    745 
    746 	smbsr_errno(sr, errnum);
    747 }
    748