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 	smb_convert_wildcards(src_fqi->fq_path.pn_path);
    181 	if (smb_contains_wildcards(src_fqi->fq_path.pn_path)) {
    182 		smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
    183 		    ERRDOS, ERROR_BAD_PATHNAME);
    184 		return (SDRC_ERROR);
    185 	}
    186 
    187 	switch (sr->arg.dirop.info_level) {
    188 	case SMB_NT_RENAME_SET_LINK_INFO:
    189 		rc = smb_make_link(sr, src_fqi, dst_fqi);
    190 		break;
    191 	case SMB_NT_RENAME_RENAME_FILE:
    192 	case SMB_NT_RENAME_MOVE_FILE:
    193 		rc = smb_common_rename(sr, src_fqi, dst_fqi);
    194 		break;
    195 	case SMB_NT_RENAME_MOVE_CLUSTER_INFO:
    196 		rc = EINVAL;
    197 		break;
    198 	default:
    199 		rc = EACCES;
    200 		break;
    201 	}
    202 
    203 	if (rc != 0) {
    204 		smb_rename_set_error(sr, rc);
    205 		return (SDRC_ERROR);
    206 	}
    207 
    208 	rc = smbsr_encode_empty_result(sr);
    209 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
    210 }
    211 
    212 /*
    213  * smb_nt_transact_rename
    214  *
    215  * Windows servers return SUCCESS without renaming file.
    216  * The only check required is to check that the handle (fid) is valid.
    217  */
    218 smb_sdrc_t
    219 smb_nt_transact_rename(smb_request_t *sr, smb_xa_t *xa)
    220 {
    221 	if (smb_mbc_decodef(&xa->req_param_mb, "w", &sr->smb_fid) != 0)
    222 		return (SDRC_ERROR);
    223 
    224 	smbsr_lookup_file(sr);
    225 	if (sr->fid_ofile == NULL) {
    226 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
    227 		return (SDRC_ERROR);
    228 	}
    229 	smbsr_release_file(sr);
    230 
    231 	return (SDRC_SUCCESS);
    232 }
    233 
    234 /*
    235  * smb_trans2_rename
    236  *
    237  * Implements SMB_FILE_RENAME_INFORMATION level of Trans2_Set_FileInfo
    238  * and Trans2_Set_PathInfo.
    239  * If the new filename (dst_fqi) already exists it may be overwritten
    240  * if flags == 1.
    241  */
    242 int
    243 smb_trans2_rename(smb_request_t *sr, smb_node_t *node, char *fname, int flags)
    244 {
    245 	int rc;
    246 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
    247 	smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
    248 
    249 	sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0;
    250 	sr->arg.dirop.info_level = SMB_NT_RENAME_RENAME_FILE;
    251 
    252 	src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES;
    253 	src_fqi->fq_fnode = node;
    254 	src_fqi->fq_dnode = node->n_dnode;
    255 
    256 	dst_fqi->fq_path.pn_path = fname;
    257 	dst_fqi->fq_dnode = node->n_dnode;
    258 	(void) strlcpy(dst_fqi->fq_last_comp, fname, MAXNAMELEN);
    259 
    260 	rc = smb_common_rename(sr, src_fqi, dst_fqi);
    261 	if (rc != 0) {
    262 		smb_rename_set_error(sr, rc);
    263 		return (-1);
    264 	}
    265 
    266 	return (0);
    267 }
    268 
    269 /*
    270  * smb_common_rename
    271  *
    272  * Common code for renaming a file.
    273  *
    274  * If the source and destination are identical, we go through all
    275  * the checks but we don't actually do the rename.  If the source
    276  * and destination files differ only in case, we do a case-sensitive
    277  * rename.  Otherwise, we do a full case-insensitive rename.
    278  *
    279  * Returns errno values.
    280  */
    281 static int
    282 smb_common_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
    283 {
    284 	smb_node_t *src_fnode, *src_dnode, *dst_fnode, *dst_dnode;
    285 	smb_node_t *tnode;
    286 	int rc, count;
    287 	DWORD status;
    288 	char *new_name, *path;
    289 
    290 	path = dst_fqi->fq_path.pn_path;
    291 
    292 	/* Check if attempting to rename a stream - not yet supported */
    293 	rc = smb_rename_check_stream(src_fqi, dst_fqi);
    294 	if (rc != 0)
    295 		return (rc);
    296 
    297 	/* The source node may already have been provided */
    298 	if (src_fqi->fq_fnode) {
    299 		smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
    300 		smb_node_ref(src_fqi->fq_fnode);
    301 		smb_node_ref(src_fqi->fq_dnode);
    302 	} else {
    303 		/* lookup and validate src node */
    304 		rc = smb_rename_lookup_src(sr);
    305 		if (rc != 0)
    306 			return (rc);
    307 	}
    308 
    309 	src_fnode = src_fqi->fq_fnode;
    310 	src_dnode = src_fqi->fq_dnode;
    311 
    312 	/* Find destination dnode and last_comp */
    313 	if (dst_fqi->fq_dnode) {
    314 		smb_node_ref(dst_fqi->fq_dnode);
    315 	} else {
    316 		tnode = sr->tid_tree->t_snode;
    317 		rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
    318 		    &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
    319 		if (rc != 0) {
    320 			smb_rename_release_src(sr);
    321 			return (rc);
    322 		}
    323 	}
    324 
    325 	dst_dnode = dst_fqi->fq_dnode;
    326 	new_name = dst_fqi->fq_last_comp;
    327 
    328 	/* If exact name match in same directory, we're done */
    329 	if ((src_dnode == dst_dnode) &&
    330 	    (strcmp(src_fnode->od_name, new_name) == 0)) {
    331 		smb_rename_release_src(sr);
    332 		smb_node_release(dst_dnode);
    333 		return (0);
    334 	}
    335 
    336 	/* Lookup destination node */
    337 	rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
    338 	    dst_dnode, new_name, &dst_fqi->fq_fnode);
    339 
    340 	/* If the destination node doesn't already exist, validate new_name. */
    341 	if (rc == ENOENT) {
    342 		if (smb_is_invalid_filename(new_name)) {
    343 			smb_rename_release_src(sr);
    344 			smb_node_release(dst_dnode);
    345 			return (EILSEQ); /* NT_STATUS_OBJECT_NAME_INVALID */
    346 		}
    347 	}
    348 
    349 	/*
    350 	 * Handle case where changing case of the same directory entry.
    351 	 *
    352 	 * If we found the dst node in the same directory as the src node,
    353 	 * and their names differ only in case:
    354 	 *
    355 	 * If the tree is case sensitive (or mixed):
    356 	 *  Do case sensitive lookup to see if exact match exists.
    357 	 *  If the exact match is the same node as src_node we're done.
    358 	 *
    359 	 * If the tree is case insensitive:
    360 	 *  There is currently no way to tell if the case is different
    361 	 *  or not, so do the rename (unless the specified new name was
    362 	 *  mangled).
    363 	 */
    364 	if ((rc == 0) &&
    365 	    (src_dnode == dst_dnode) &&
    366 	    (smb_strcasecmp(src_fnode->od_name,
    367 	    dst_fqi->fq_fnode->od_name, 0) == 0)) {
    368 		smb_node_release(dst_fqi->fq_fnode);
    369 		dst_fqi->fq_fnode = NULL;
    370 
    371 		if (smb_tree_has_feature(sr->tid_tree,
    372 		    SMB_TREE_NO_CASESENSITIVE)) {
    373 			if (smb_strcasecmp(src_fnode->od_name,
    374 			    dst_fqi->fq_last_comp, 0) != 0) {
    375 				smb_rename_release_src(sr);
    376 				smb_node_release(dst_dnode);
    377 				return (0);
    378 			}
    379 		} else {
    380 			rc = smb_fsop_lookup(sr, sr->user_cr,
    381 			    SMB_CASE_SENSITIVE, tnode, dst_dnode, new_name,
    382 			    &dst_fqi->fq_fnode);
    383 
    384 			if ((rc == 0) &&
    385 			    (dst_fqi->fq_fnode == src_fnode)) {
    386 				smb_rename_release_src(sr);
    387 				smb_node_release(dst_fqi->fq_fnode);
    388 				smb_node_release(dst_dnode);
    389 				return (0);
    390 			}
    391 		}
    392 	}
    393 
    394 	if ((rc != 0) && (rc != ENOENT)) {
    395 		smb_rename_release_src(sr);
    396 		smb_node_release(dst_fqi->fq_dnode);
    397 		return (rc);
    398 	}
    399 
    400 	if (dst_fqi->fq_fnode) {
    401 		dst_fnode = dst_fqi->fq_fnode;
    402 
    403 		if (!(sr->arg.dirop.flags && SMB_RENAME_FLAG_OVERWRITE)) {
    404 			smb_rename_release_src(sr);
    405 			smb_node_release(dst_fnode);
    406 			smb_node_release(dst_dnode);
    407 			return (EEXIST);
    408 		}
    409 
    410 		(void) smb_oplock_break(dst_fnode, sr->session, B_FALSE);
    411 
    412 		for (count = 0; count <= 3; count++) {
    413 			if (count) {
    414 				smb_node_end_crit(dst_fnode);
    415 				delay(MSEC_TO_TICK(400));
    416 			}
    417 
    418 			smb_node_start_crit(dst_fnode, RW_READER);
    419 			status = smb_node_delete_check(dst_fnode);
    420 
    421 			if (status != NT_STATUS_SHARING_VIOLATION)
    422 				break;
    423 		}
    424 
    425 		if (status != NT_STATUS_SHARING_VIOLATION)
    426 			status = smb_range_check(sr, dst_fnode,
    427 			    0, UINT64_MAX, B_TRUE);
    428 
    429 		if (status != NT_STATUS_SUCCESS) {
    430 			smb_rename_release_src(sr);
    431 			smb_node_end_crit(dst_fnode);
    432 			smb_node_release(dst_fnode);
    433 			smb_node_release(dst_dnode);
    434 			return (EACCES);
    435 		}
    436 
    437 		new_name = dst_fnode->od_name;
    438 	}
    439 
    440 	rc = smb_fsop_rename(sr, sr->user_cr,
    441 	    src_dnode, src_fnode->od_name,
    442 	    dst_dnode, new_name);
    443 
    444 	smb_rename_release_src(sr);
    445 
    446 	if (rc == 0)
    447 		smb_node_notify_change(dst_dnode);
    448 
    449 	if (dst_fqi->fq_fnode) {
    450 		smb_node_end_crit(dst_fnode);
    451 		smb_node_release(dst_fnode);
    452 	}
    453 	smb_node_release(dst_dnode);
    454 
    455 	return (rc);
    456 }
    457 
    458 /*
    459  * smb_rename_check_stream
    460  *
    461  * For a stream rename the dst path must begin with ':', or "\\:".
    462  * We don't yet support stream rename, Return EACCES.
    463  *
    464  * If not a stream rename, in accordance with the above rule,
    465  * it is not valid for either the src or dst to be a stream.
    466  * Return EINVAL.
    467  */
    468 static int
    469 smb_rename_check_stream(smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
    470 {
    471 	smb_node_t *src_fnode = src_fqi->fq_fnode;
    472 	char *src_path = src_fqi->fq_path.pn_path;
    473 	char *dst_path = dst_fqi->fq_path.pn_path;
    474 
    475 	/* We do not yet support named stream rename - ACCESS DENIED */
    476 	if ((dst_path[0] == ':') ||
    477 	    ((dst_path[0] == '\\') && (dst_path[1] == ':'))) {
    478 		return (EACCES);
    479 	}
    480 
    481 	/*
    482 	 * If not stream rename (above) neither src or dst can be
    483 	 * a named stream.
    484 	 */
    485 
    486 	if (smb_is_stream_name(dst_path))
    487 		return (EINVAL);
    488 
    489 	if (src_fqi->fq_fnode) {
    490 		if (SMB_IS_STREAM(src_fnode))
    491 			return (EINVAL);
    492 	} else {
    493 		if (smb_is_stream_name(src_path))
    494 			return (EINVAL);
    495 	}
    496 
    497 	return (0);
    498 }
    499 
    500 
    501 /*
    502  * smb_make_link
    503  *
    504  * Creating a hard link (adding an additional name) for a file.
    505  *
    506  * If the source and destination are identical, we go through all
    507  * the checks but we don't create a link.
    508  *
    509  * If the file is a symlink we create the hardlink on the target
    510  * of the symlink (i.e. use SMB_FOLLOW_LINKS when looking up src).
    511  * If the target of the symlink does not exist we fail with ENOENT.
    512  *
    513  * Returns errno values.
    514  */
    515 static int
    516 smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
    517 {
    518 	smb_node_t *tnode;
    519 	char *path;
    520 	int rc;
    521 
    522 	/* Cannnot create link on named stream */
    523 	if (smb_is_stream_name(src_fqi->fq_path.pn_path) ||
    524 	    smb_is_stream_name(dst_fqi->fq_path.pn_path)) {
    525 		return (EINVAL);
    526 	}
    527 
    528 	/* lookup and validate src node */
    529 	rc = smb_rename_lookup_src(sr);
    530 	if (rc != 0)
    531 		return (rc);
    532 
    533 	/* if src and dest paths match we're done */
    534 	if (smb_strcasecmp(src_fqi->fq_path.pn_path,
    535 	    dst_fqi->fq_path.pn_path, 0) == 0) {
    536 		smb_rename_release_src(sr);
    537 		return (0);
    538 	}
    539 
    540 	/* find the destination dnode and last_comp */
    541 	tnode = sr->tid_tree->t_snode;
    542 	path = dst_fqi->fq_path.pn_path;
    543 	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
    544 	    &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
    545 	if (rc != 0) {
    546 		smb_rename_release_src(sr);
    547 		return (rc);
    548 	}
    549 
    550 	/* If name match in same directory, we're done */
    551 	if ((src_fqi->fq_dnode == dst_fqi->fq_dnode) &&
    552 	    (smb_strcasecmp(src_fqi->fq_fnode->od_name,
    553 	    dst_fqi->fq_last_comp, 0) == 0)) {
    554 		smb_rename_release_src(sr);
    555 		smb_node_release(dst_fqi->fq_dnode);
    556 		return (0);
    557 	}
    558 
    559 	if (smb_is_invalid_filename(dst_fqi->fq_last_comp)) {
    560 		smb_rename_release_src(sr);
    561 		smb_node_release(dst_fqi->fq_dnode);
    562 		return (EILSEQ); /* NT_STATUS_INVALID_OBJECT_NAME */
    563 	}
    564 
    565 	/* Lookup the destination node. It MUST NOT exist. */
    566 	rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
    567 	    dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode);
    568 	if (rc == 0) {
    569 		smb_node_release(dst_fqi->fq_fnode);
    570 		rc = EEXIST;
    571 	}
    572 	if (rc != ENOENT) {
    573 		smb_rename_release_src(sr);
    574 		smb_node_release(dst_fqi->fq_dnode);
    575 		return (rc);
    576 	}
    577 
    578 	rc = smb_fsop_link(sr, sr->user_cr, src_fqi->fq_fnode,
    579 	    dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
    580 
    581 	smb_rename_release_src(sr);
    582 	if (rc == 0)
    583 		smb_node_notify_change(dst_fqi->fq_dnode);
    584 	smb_node_release(dst_fqi->fq_dnode);
    585 	return (rc);
    586 }
    587 
    588 /*
    589  * smb_rename_lookup_src
    590  *
    591  * Lookup the src node, checking for sharing violations and
    592  * breaking any existing oplock.
    593  * Populate sr->arg.dirop.fqi
    594  *
    595  * Upon success, the dnode and fnode will have holds and the
    596  * fnode will be in a critical section. These should be
    597  * released using smb_rename_release_src().
    598  *
    599  * Returns errno values.
    600  */
    601 static int
    602 smb_rename_lookup_src(smb_request_t *sr)
    603 {
    604 	smb_node_t *src_node, *tnode;
    605 	DWORD status;
    606 	int rc;
    607 	int count;
    608 	char *path;
    609 
    610 	struct dirop *dirop = &sr->arg.dirop;
    611 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
    612 
    613 	if (smb_is_stream_name(src_fqi->fq_path.pn_path))
    614 		return (EINVAL);
    615 
    616 	/* Lookup the source node */
    617 	tnode = sr->tid_tree->t_snode;
    618 	path = src_fqi->fq_path.pn_path;
    619 	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
    620 	    &src_fqi->fq_dnode, src_fqi->fq_last_comp);
    621 	if (rc != 0)
    622 		return (rc);
    623 
    624 	rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
    625 	    src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode);
    626 	if (rc != 0) {
    627 		smb_node_release(src_fqi->fq_dnode);
    628 		return (rc);
    629 	}
    630 
    631 	/* Not valid to create hardlink for directory */
    632 	if ((dirop->info_level == SMB_NT_RENAME_SET_LINK_INFO) &&
    633 	    (smb_node_is_dir(src_fqi->fq_fnode))) {
    634 		smb_node_release(src_fqi->fq_fnode);
    635 		smb_node_release(src_fqi->fq_dnode);
    636 		return (EISDIR);
    637 	}
    638 
    639 	src_node = src_fqi->fq_fnode;
    640 
    641 	rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr);
    642 	if (rc != 0) {
    643 		smb_node_release(src_fqi->fq_fnode);
    644 		smb_node_release(src_fqi->fq_dnode);
    645 		return (rc);
    646 	}
    647 
    648 	/*
    649 	 * Break the oplock before access checks. If a client
    650 	 * has a file open, this will force a flush or close,
    651 	 * which may affect the outcome of any share checking.
    652 	 */
    653 	(void) smb_oplock_break(src_node, sr->session, B_FALSE);
    654 
    655 	for (count = 0; count <= 3; count++) {
    656 		if (count) {
    657 			smb_node_end_crit(src_node);
    658 			delay(MSEC_TO_TICK(400));
    659 		}
    660 
    661 		smb_node_start_crit(src_node, RW_READER);
    662 
    663 		status = smb_node_rename_check(src_node);
    664 		if (status != NT_STATUS_SHARING_VIOLATION)
    665 			break;
    666 	}
    667 
    668 	if (status == NT_STATUS_SHARING_VIOLATION) {
    669 		smb_node_end_crit(src_node);
    670 		smb_node_release(src_fqi->fq_fnode);
    671 		smb_node_release(src_fqi->fq_dnode);
    672 		return (EPIPE); /* = ERRbadshare */
    673 	}
    674 
    675 	status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE);
    676 	if (status != NT_STATUS_SUCCESS) {
    677 		smb_node_end_crit(src_node);
    678 		smb_node_release(src_fqi->fq_fnode);
    679 		smb_node_release(src_fqi->fq_dnode);
    680 		return (EACCES);
    681 	}
    682 
    683 	return (0);
    684 }
    685 
    686 /*
    687  * smb_rename_release_src
    688  */
    689 static void
    690 smb_rename_release_src(smb_request_t *sr)
    691 {
    692 	smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
    693 
    694 	smb_node_end_crit(src_fqi->fq_fnode);
    695 	smb_node_release(src_fqi->fq_fnode);
    696 	smb_node_release(src_fqi->fq_dnode);
    697 }
    698 
    699 
    700 static int
    701 smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr)
    702 {
    703 	smb_attr_t attr;
    704 
    705 	if (smb_node_getattr(sr, node, &attr) != 0)
    706 		return (EIO);
    707 
    708 	if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) &&
    709 	    !(SMB_SEARCH_HIDDEN(sattr)))
    710 		return (ESRCH);
    711 
    712 	if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) &&
    713 	    !(SMB_SEARCH_SYSTEM(sattr)))
    714 		return (ESRCH);
    715 
    716 	return (0);
    717 }
    718 
    719 /*
    720  * The following values are based on observed WFWG, Windows 9x, Windows NT
    721  * and Windows 2000 behaviour.
    722  *
    723  * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
    724  *
    725  * Windows 95 clients don't see the problem because the target is deleted
    726  * before the rename request.
    727  */
    728 static void
    729 smb_rename_set_error(smb_request_t *sr, int errnum)
    730 {
    731 	static struct {
    732 		int errnum;
    733 		uint16_t errcode;
    734 		uint32_t status32;
    735 	} rc_map[] = {
    736 	{ EEXIST, ERROR_ALREADY_EXISTS,	NT_STATUS_OBJECT_NAME_COLLISION },
    737 	{ EPIPE,  ERROR_SHARING_VIOLATION, NT_STATUS_SHARING_VIOLATION },
    738 	{ ENOENT, ERROR_FILE_NOT_FOUND,	NT_STATUS_OBJECT_NAME_NOT_FOUND },
    739 	{ ESRCH,  ERROR_FILE_NOT_FOUND,	NT_STATUS_NO_SUCH_FILE },
    740 	{ EINVAL, ERROR_INVALID_PARAMETER, NT_STATUS_INVALID_PARAMETER },
    741 	{ EACCES, ERROR_ACCESS_DENIED,	NT_STATUS_ACCESS_DENIED },
    742 	{ EISDIR, ERROR_ACCESS_DENIED,	NT_STATUS_FILE_IS_A_DIRECTORY },
    743 	{ EIO,    ERROR_INTERNAL_ERROR,	NT_STATUS_INTERNAL_ERROR }
    744 	};
    745 
    746 	int i;
    747 
    748 	if (errnum == 0)
    749 		return;
    750 
    751 	for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) {
    752 		if (rc_map[i].errnum == errnum) {
    753 			smbsr_error(sr, rc_map[i].status32,
    754 			    ERRDOS, rc_map[i].errcode);
    755 			return;
    756 		}
    757 	}
    758 
    759 	smbsr_errno(sr, errnum);
    760 }
    761