xref: /titanic_41/usr/src/uts/common/fs/smbsrv/smb_rename.c (revision e4d060fb4c00d44cd578713eb9a921f594b733b8)
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