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