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