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