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 2018 Nexenta Systems, Inc. All rights reserved.
24 */
25
26 #include <sys/synch.h>
27 #include <smbsrv/smb2_kproto.h>
28 #include <smbsrv/smb_fsops.h>
29 #include <sys/nbmlock.h>
30
31 /*
32 * SMB_TRANS2_SET_FILE/PATH_INFO (RENAME_INFORMATION level) flag
33 */
34 #define SMB_RENAME_FLAG_OVERWRITE 0x001
35
36 static int smb_rename_check_stream(smb_fqi_t *, smb_fqi_t *);
37 static int smb_rename_check_attr(smb_request_t *, smb_node_t *, uint16_t);
38 static int smb_rename_lookup_src(smb_request_t *);
39 static uint32_t smb_rename_check_src(smb_request_t *, smb_fqi_t *);
40 static void smb_rename_release_src(smb_request_t *);
41 static uint32_t smb_rename_errno2status(int);
42
43 /*
44 * smb_setinfo_rename
45 *
46 * Implements SMB_FILE_RENAME_INFORMATION level of Trans2_Set_FileInfo
47 * and Trans2_Set_PathInfo and SMB2 set_info, FileRenameInformation.
48 * If the new filename (dst_fqi) already exists it may be overwritten
49 * if flags == 1.
50 *
51 * The passed path is a full path relative to the share root.
52 *
53 * Returns NT status codes.
54 *
55 * Similar to smb_setinfo_link(), below.
56 */
57 uint32_t
58 smb_setinfo_rename(smb_request_t *sr, smb_node_t *node, char *path, int flags)
59 {
60 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
61 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
62 smb_pathname_t *dst_pn = &dst_fqi->fq_path;
63 uint32_t status;
64
65 sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0;
66 sr->arg.dirop.info_level = FileRenameInformation;
67
68 src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES;
69 src_fqi->fq_fnode = node;
70 src_fqi->fq_dnode = node->n_dnode;
71
72 /* validate the dst pathname */
73 smb_pathname_init(sr, dst_pn, path);
74 if (!smb_pathname_validate(sr, dst_pn))
75 return (NT_STATUS_OBJECT_NAME_INVALID);
76
77 status = smb_common_rename(sr, src_fqi, dst_fqi);
78 return (status);
79 }
80
81 /*
82 * smb_common_rename
83 *
84 * Common code for renaming a file.
85 *
86 * If the source and destination are identical, we go through all
87 * the checks but we don't actually do the rename. If the source
88 * and destination files differ only in case, we do a case-sensitive
89 * rename. Otherwise, we do a full case-insensitive rename.
90 *
91 * Returns NT status values.
92 *
93 * Similar to smb_make_link(), below.
94 */
95 uint32_t
96 smb_common_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
97 {
98 smb_node_t *src_fnode, *src_dnode, *dst_dnode;
99 smb_node_t *dst_fnode = 0;
100 smb_node_t *tnode;
101 char *new_name, *path;
102 DWORD status;
103 int rc;
104 boolean_t have_src = B_FALSE;
105 boolean_t dst_exists = B_FALSE;
106 boolean_t do_audit;
107 char *srcpath = NULL;
108 char *dstpath = NULL;
109
110 tnode = sr->tid_tree->t_snode;
111 path = dst_fqi->fq_path.pn_path;
112
113 /* Check if attempting to rename a stream - not yet supported */
114 rc = smb_rename_check_stream(src_fqi, dst_fqi);
115 if (rc != 0)
116 return (smb_rename_errno2status(rc));
117
118 /*
119 * The source node may already have been provided,
120 * i.e. when called by SMB1/SMB2 smb_setinfo_rename
121 * with an ofile. When we have an ofile, open has
122 * already checked for sharing violations. For
123 * path-based operations, do sharing check here.
124 */
125 if (src_fqi->fq_fnode) {
126 smb_node_ref(src_fqi->fq_dnode);
127 smb_node_ref(src_fqi->fq_fnode);
128 have_src = B_TRUE;
129 } else {
130 /* lookup and validate src node */
131 rc = smb_rename_lookup_src(sr);
132 if (rc != 0)
133 return (smb_rename_errno2status(rc));
134 /* Holding refs on dnode, fnode */
135 }
136 src_fnode = src_fqi->fq_fnode;
137 src_dnode = src_fqi->fq_dnode;
138
139 /* Break oplocks, and check share modes. */
140 status = smb_rename_check_src(sr, src_fqi);
141 if (status != NT_STATUS_SUCCESS) {
142 smb_node_release(src_fqi->fq_fnode);
143 smb_node_release(src_fqi->fq_dnode);
144 return (status);
145 }
146 /*
147 * NB: src_fnode is now "in crit" (critical section)
148 * as if we did smb_node_start_crit(..., RW_READER);
149 * Call smb_rename_release_src(sr) on errors.
150 */
151
152 /*
153 * Find the destination dnode and last component.
154 * May already be provided, i.e. when called via
155 * SMB1 trans2 setinfo.
156 */
157 if (dst_fqi->fq_dnode) {
158 /* called via smb_set_rename_info */
159 smb_node_ref(dst_fqi->fq_dnode);
160 } else {
161 /* called via smb2_setf_rename, smb_com_rename, etc. */
162 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
163 &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
164 if (rc != 0) {
165 smb_rename_release_src(sr);
166 return (smb_rename_errno2status(rc));
167 }
168 }
169
170 dst_dnode = dst_fqi->fq_dnode;
171 new_name = dst_fqi->fq_last_comp;
172
173 /* If exact name match in same directory, we're done */
174 if ((src_dnode == dst_dnode) &&
175 (strcmp(src_fnode->od_name, new_name) == 0)) {
176 smb_rename_release_src(sr);
177 smb_node_release(dst_dnode);
178 return (0);
179 }
180
181 /* Lookup destination node */
182 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
183 dst_dnode, new_name, &dst_fqi->fq_fnode);
184
185 /* If the destination node doesn't already exist, validate new_name. */
186 if (rc == ENOENT) {
187 if (smb_is_invalid_filename(new_name)) {
188 smb_rename_release_src(sr);
189 smb_node_release(dst_dnode);
190 return (NT_STATUS_OBJECT_NAME_INVALID);
191 }
192 }
193
194 /*
195 * Handle case where changing case of the same directory entry.
196 *
197 * If we found the dst node in the same directory as the src node,
198 * and their names differ only in case:
199 *
200 * If the tree is case sensitive (or mixed):
201 * Do case sensitive lookup to see if exact match exists.
202 * If the exact match is the same node as src_node we're done.
203 *
204 * If the tree is case insensitive:
205 * There is currently no way to tell if the case is different
206 * or not, so do the rename (unless the specified new name was
207 * mangled).
208 */
209 if ((rc == 0) &&
210 (src_dnode == dst_dnode) &&
211 (smb_strcasecmp(src_fnode->od_name,
212 dst_fqi->fq_fnode->od_name, 0) == 0)) {
213 smb_node_release(dst_fqi->fq_fnode);
214 dst_fqi->fq_fnode = NULL;
215
216 if (smb_tree_has_feature(sr->tid_tree,
217 SMB_TREE_NO_CASESENSITIVE)) {
218 if (smb_strcasecmp(src_fnode->od_name,
219 dst_fqi->fq_last_comp, 0) != 0) {
220 smb_rename_release_src(sr);
221 smb_node_release(dst_dnode);
222 return (0);
223 }
224 } else {
225 rc = smb_fsop_lookup(sr, sr->user_cr,
226 SMB_CASE_SENSITIVE, tnode, dst_dnode, new_name,
227 &dst_fqi->fq_fnode);
228
229 if ((rc == 0) &&
230 (dst_fqi->fq_fnode == src_fnode)) {
231 smb_rename_release_src(sr);
232 smb_node_release(dst_fqi->fq_fnode);
233 smb_node_release(dst_dnode);
234 return (0);
235 }
236 }
237 }
238
239 if ((rc != 0) && (rc != ENOENT)) {
240 smb_rename_release_src(sr);
241 smb_node_release(dst_fqi->fq_dnode);
242 return (smb_rename_errno2status(rc));
243 }
244
245 if (dst_fqi->fq_fnode) {
246 /*
247 * Destination already exists. Do delete checks.
248 */
249 dst_fnode = dst_fqi->fq_fnode;
250
251 if (!(sr->arg.dirop.flags && SMB_RENAME_FLAG_OVERWRITE)) {
252 smb_rename_release_src(sr);
253 smb_node_release(dst_fnode);
254 smb_node_release(dst_dnode);
255 return (NT_STATUS_OBJECT_NAME_COLLISION);
256 }
257
258 status = smb_oplock_break_DELETE(dst_fnode, NULL);
259 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
260 if (sr->session->dialect >= SMB_VERS_2_BASE)
261 (void) smb2sr_go_async(sr);
262 (void) smb_oplock_wait_break(dst_fnode, 0);
263 status = 0;
264 }
265 if (status != 0) {
266 smb_rename_release_src(sr);
267 smb_node_release(dst_fnode);
268 smb_node_release(dst_dnode);
269 return (status);
270 }
271
272 smb_node_rdlock(dst_fnode);
273 status = smb_node_delete_check(dst_fnode);
274 if (status != NT_STATUS_SUCCESS) {
275 smb_node_unlock(dst_fnode);
276 smb_rename_release_src(sr);
277 smb_node_release(dst_fnode);
278 smb_node_release(dst_dnode);
279 return (NT_STATUS_ACCESS_DENIED);
280 }
281
282 /*
283 * Note, the combination of these two:
284 * smb_node_rdlock(node);
285 * nbl_start_crit(node->vp, RW_READER);
286 * is equivalent to this call:
287 * smb_node_start_crit(node, RW_READER)
288 *
289 * Cleanup after this point should use:
290 * smb_node_end_crit(dst_fnode)
291 */
292 nbl_start_crit(dst_fnode->vp, RW_READER);
293
294 /*
295 * This checks nbl_share_conflict, nbl_lock_conflict
296 */
297 status = smb_nbl_conflict(dst_fnode, 0, UINT64_MAX, NBL_REMOVE);
298 if (status != NT_STATUS_SUCCESS) {
299 smb_node_end_crit(dst_fnode);
300 smb_rename_release_src(sr);
301 smb_node_release(dst_fnode);
302 smb_node_release(dst_dnode);
303 return (NT_STATUS_ACCESS_DENIED);
304 }
305
306 new_name = dst_fnode->od_name;
307 dst_exists = B_TRUE;
308 }
309
310 do_audit = smb_audit_rename_init(sr);
311 /* save paths for later auditing */
312 if (do_audit) {
313 if (!have_src) {
314 srcpath = kmem_alloc(SMB_MAXPATHLEN, KM_SLEEP);
315 smb_node_getpath_nofail(src_fnode, smb_audit_rootvp(sr),
316 srcpath, SMB_MAXPATHLEN);
317 }
318 if (dst_exists) {
319 dstpath = kmem_alloc(SMB_MAXPATHLEN, KM_SLEEP);
320 smb_node_getpath_nofail(dst_fnode, smb_audit_rootvp(sr),
321 dstpath, SMB_MAXPATHLEN);
322 }
323 }
324
325 rc = smb_fsop_rename(sr, sr->user_cr,
326 src_dnode, src_fnode->od_name,
327 dst_dnode, new_name);
328
329 if (do_audit) {
330 smb_audit_rename_fini(sr,
331 srcpath,
332 dst_dnode,
333 dstpath,
334 rc == 0,
335 smb_node_is_dir(src_fnode));
336
337 if (srcpath != NULL)
338 kmem_free(srcpath, SMB_MAXPATHLEN);
339 if (dstpath != NULL)
340 kmem_free(dstpath, SMB_MAXPATHLEN);
341 }
342
343 if (rc == 0) {
344 /*
345 * Note that renames in the same directory are normally
346 * delivered in {old,new} pairs, and clients expect them
347 * in that order, if both events are delivered.
348 */
349 int a_src, a_dst; /* action codes */
350 if (src_dnode == dst_dnode) {
351 a_src = FILE_ACTION_RENAMED_OLD_NAME;
352 a_dst = FILE_ACTION_RENAMED_NEW_NAME;
353 } else {
354 a_src = FILE_ACTION_REMOVED;
355 a_dst = FILE_ACTION_ADDED;
356 }
357 smb_node_notify_change(src_dnode, a_src, src_fnode->od_name);
358 smb_node_notify_change(dst_dnode, a_dst, new_name);
359 }
360
361 smb_rename_release_src(sr);
362
363 if (dst_fqi->fq_fnode) {
364 smb_node_end_crit(dst_fnode);
365 smb_node_release(dst_fnode);
366 }
367 smb_node_release(dst_dnode);
368
369 return (smb_rename_errno2status(rc));
370 }
371
372 /*
373 * smb_rename_check_stream
374 *
375 * For a stream rename the dst path must begin with ':', or "\\:".
376 * We don't yet support stream rename, Return EACCES.
377 *
378 * If not a stream rename, in accordance with the above rule,
379 * it is not valid for either the src or dst to be a stream.
380 * Return EINVAL.
381 */
382 static int
383 smb_rename_check_stream(smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
384 {
385 smb_node_t *src_fnode = src_fqi->fq_fnode;
386 char *src_path = src_fqi->fq_path.pn_path;
387 char *dst_path = dst_fqi->fq_path.pn_path;
388
389 /* We do not yet support named stream rename - ACCESS DENIED */
390 if ((dst_path[0] == ':') ||
391 ((dst_path[0] == '\\') && (dst_path[1] == ':'))) {
392 return (EACCES);
393 }
394
395 /*
396 * If not stream rename (above) neither src or dst can be
397 * a named stream.
398 */
399
400 if (smb_is_stream_name(dst_path))
401 return (EINVAL);
402
403 if (src_fqi->fq_fnode) {
404 if (SMB_IS_STREAM(src_fnode))
405 return (EINVAL);
406 } else {
407 if (smb_is_stream_name(src_path))
408 return (EINVAL);
409 }
410
411 return (0);
412 }
413
414
415 /*
416 * smb_setinfo_link
417 *
418 * Implements FileRenameInformation for SMB1 Trans2 setinfo, SMB2 setinfo.
419 * If the new filename (dst_fqi) already exists it may be overwritten
420 * if flags == 1.
421 *
422 * The passed path is a full path relative to the share root.
423 *
424 * Returns NT status codes.
425 *
426 * Similar to smb_setinfo_rename(), above.
427 */
428 uint32_t
429 smb_setinfo_link(smb_request_t *sr, smb_node_t *node, char *path, int flags)
430 {
431 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
432 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
433 smb_pathname_t *dst_pn = &dst_fqi->fq_path;
434 uint32_t status;
435
436 sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0;
437 sr->arg.dirop.info_level = FileLinkInformation;
438
439 src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES;
440 src_fqi->fq_fnode = node;
441 src_fqi->fq_dnode = node->n_dnode;
442
443 /* validate the dst pathname */
444 smb_pathname_init(sr, dst_pn, path);
445 if (!smb_pathname_validate(sr, dst_pn))
446 return (NT_STATUS_OBJECT_NAME_INVALID);
447
448 status = smb_make_link(sr, src_fqi, dst_fqi);
449 return (status);
450 }
451
452 /*
453 * smb_make_link
454 *
455 * Creating a hard link (adding an additional name) for a file.
456 *
457 * If the source and destination are identical, we go through all
458 * the checks but we don't create a link.
459 *
460 * If the file is a symlink we create the hardlink on the target
461 * of the symlink (i.e. use SMB_FOLLOW_LINKS when looking up src).
462 * If the target of the symlink does not exist we fail with ENOENT.
463 *
464 * Returns NT status values.
465 *
466 * Similar to smb_common_rename() above.
467 */
468 uint32_t
469 smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi)
470 {
471 smb_node_t *tnode;
472 char *path;
473 int rc;
474
475 tnode = sr->tid_tree->t_snode;
476 path = dst_fqi->fq_path.pn_path;
477
478 /* Cannnot create link on named stream */
479 if (smb_is_stream_name(src_fqi->fq_path.pn_path) ||
480 smb_is_stream_name(dst_fqi->fq_path.pn_path)) {
481 return (NT_STATUS_INVALID_PARAMETER);
482 }
483
484 /* The source node may already have been provided */
485 if (src_fqi->fq_fnode) {
486 smb_node_ref(src_fqi->fq_dnode);
487 smb_node_ref(src_fqi->fq_fnode);
488 } else {
489 /* lookup and validate src node */
490 rc = smb_rename_lookup_src(sr);
491 if (rc != 0)
492 return (smb_rename_errno2status(rc));
493 /* Holding refs on dnode, fnode */
494 }
495
496 /* Not valid to create hardlink for directory */
497 if (smb_node_is_dir(src_fqi->fq_fnode)) {
498 smb_node_release(src_fqi->fq_dnode);
499 smb_node_release(src_fqi->fq_fnode);
500 return (NT_STATUS_FILE_IS_A_DIRECTORY);
501 }
502
503 /*
504 * Unlike in rename, we will not unlink the src,
505 * so skip the smb_rename_check_src() call, and
506 * just "start crit" instead.
507 */
508 smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
509
510 /*
511 * Find the destination dnode and last component.
512 * May already be provided, i.e. when called via
513 * SMB1 trans2 setinfo.
514 */
515 if (dst_fqi->fq_dnode) {
516 smb_node_ref(dst_fqi->fq_dnode);
517 } else {
518 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
519 &dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
520 if (rc != 0) {
521 smb_rename_release_src(sr);
522 return (smb_rename_errno2status(rc));
523 }
524 }
525
526 /* If CI name match in same directory, we're done */
527 if ((src_fqi->fq_dnode == dst_fqi->fq_dnode) &&
528 (smb_strcasecmp(src_fqi->fq_fnode->od_name,
529 dst_fqi->fq_last_comp, 0) == 0)) {
530 smb_rename_release_src(sr);
531 smb_node_release(dst_fqi->fq_dnode);
532 return (0);
533 }
534
535 if (smb_is_invalid_filename(dst_fqi->fq_last_comp)) {
536 smb_rename_release_src(sr);
537 smb_node_release(dst_fqi->fq_dnode);
538 return (NT_STATUS_OBJECT_NAME_INVALID);
539 }
540
541 /* Lookup the destination node. It MUST NOT exist. */
542 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
543 dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode);
544 if (rc == 0) {
545 smb_node_release(dst_fqi->fq_fnode);
546 rc = EEXIST;
547 }
548 if (rc != ENOENT) {
549 smb_rename_release_src(sr);
550 smb_node_release(dst_fqi->fq_dnode);
551 return (smb_rename_errno2status(rc));
552 }
553
554 rc = smb_fsop_link(sr, sr->user_cr, src_fqi->fq_fnode,
555 dst_fqi->fq_dnode, dst_fqi->fq_last_comp);
556
557 if (rc == 0) {
558 smb_node_notify_change(dst_fqi->fq_dnode,
559 FILE_ACTION_ADDED, dst_fqi->fq_last_comp);
560 }
561
562 smb_rename_release_src(sr);
563 smb_node_release(dst_fqi->fq_dnode);
564 return (smb_rename_errno2status(rc));
565 }
566
567 /*
568 * smb_rename_lookup_src
569 *
570 * Lookup the src node for a path-based link or rename.
571 *
572 * On success, fills in sr->arg.dirop.fqi, and returns with
573 * holds on the source dnode and fnode.
574 *
575 * Returns errno values.
576 */
577 static int
578 smb_rename_lookup_src(smb_request_t *sr)
579 {
580 smb_node_t *tnode;
581 char *path;
582 int rc;
583
584 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
585
586 if (smb_is_stream_name(src_fqi->fq_path.pn_path))
587 return (EINVAL);
588
589 /* Lookup the source node */
590 tnode = sr->tid_tree->t_snode;
591 path = src_fqi->fq_path.pn_path;
592 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
593 &src_fqi->fq_dnode, src_fqi->fq_last_comp);
594 if (rc != 0)
595 return (rc);
596 /* hold fq_dnode */
597
598 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode,
599 src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode);
600 if (rc != 0) {
601 smb_node_release(src_fqi->fq_dnode);
602 return (rc);
603 }
604 /* hold fq_dnode, fq_fnode */
605
606 rc = smb_rename_check_attr(sr, src_fqi->fq_fnode, src_fqi->fq_sattr);
607 if (rc != 0) {
608 smb_node_release(src_fqi->fq_fnode);
609 smb_node_release(src_fqi->fq_dnode);
610 return (rc);
611 }
612
613 return (0);
614 }
615
616 /*
617 * smb_rename_check_src
618 *
619 * Check for sharing violations on the file we'll unlink, and
620 * break oplocks for the rename operation. Note that we've
621 * already done oplock breaks associated with opening a handle
622 * on the file to rename.
623 *
624 * On success, returns with fnode in a critical section,
625 * as if smb_node_start_crit were called with the node.
626 * Caller should release using smb_rename_release_src().
627 */
628 static uint32_t
629 smb_rename_check_src(smb_request_t *sr, smb_fqi_t *src_fqi)
630 {
631 smb_node_t *src_node = src_fqi->fq_fnode;
632 uint32_t status;
633
634 /*
635 * Break BATCH oplock before ofile checks. If a client
636 * has a file open, this will force a flush or close,
637 * which may affect the outcome of any share checking.
638 *
639 * This operation may have either a handle or path for
640 * the source node (that will be unlinked via rename).
641 */
642
643 if (sr->fid_ofile != NULL) {
644 status = smb_oplock_break_SETINFO(src_node, sr->fid_ofile,
645 FileRenameInformation);
646 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
647 if (sr->session->dialect >= SMB_VERS_2_BASE)
648 (void) smb2sr_go_async(sr);
649 (void) smb_oplock_wait_break(src_node, 0);
650 status = 0;
651 }
652
653 /*
654 * Sharing violations were checked at open time.
655 * Just "start crit" to be consistent with the
656 * state returned for path-based rename.
657 */
658 smb_node_start_crit(src_fqi->fq_fnode, RW_READER);
659 return (NT_STATUS_SUCCESS);
660 }
661
662 /*
663 * This code path operates without a real open, so
664 * break oplocks now as if we opened for delete.
665 * Note: SMB2 does only ofile-based rename.
666 *
667 * Todo: Use an "internal open" for path-based
668 * rename and delete, then delete this code.
669 */
670 ASSERT(sr->session->dialect < SMB_VERS_2_BASE);
671 status = smb_oplock_break_DELETE(src_node, NULL);
672 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
673 (void) smb_oplock_wait_break(src_node, 0);
674 }
675
676 /*
677 * Path-based access to the src file (no ofile)
678 * so check for sharing violations here.
679 */
680 smb_node_rdlock(src_node);
681 status = smb_node_rename_check(src_node);
682 if (status != NT_STATUS_SUCCESS) {
683 smb_node_unlock(src_node);
684 return (status);
685 }
686
687 status = smb_oplock_break_SETINFO(src_node, NULL,
688 FileRenameInformation);
689 if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) {
690 (void) smb_oplock_wait_break(src_node, 0);
691 }
692
693 /*
694 * Note, the combination of these two:
695 * smb_node_rdlock(node);
696 * nbl_start_crit(node->vp, RW_READER);
697 * is equivalent to this call:
698 * smb_node_start_crit(node, RW_READER)
699 *
700 * Cleanup after this point should use:
701 * smb_node_end_crit(src_node)
702 */
703 nbl_start_crit(src_node->vp, RW_READER);
704
705 /*
706 * This checks nbl_share_conflict, nbl_lock_conflict
707 */
708 status = smb_nbl_conflict(src_node, 0, UINT64_MAX, NBL_RENAME);
709 if (status != NT_STATUS_SUCCESS) {
710 smb_node_end_crit(src_node);
711 }
712
713 /* NB: Caller expects to be "in crit" on fnode. */
714 return (status);
715 }
716
717 /*
718 * smb_rename_release_src
719 */
720 static void
721 smb_rename_release_src(smb_request_t *sr)
722 {
723 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
724
725 smb_node_end_crit(src_fqi->fq_fnode);
726 smb_node_release(src_fqi->fq_fnode);
727 smb_node_release(src_fqi->fq_dnode);
728 }
729
730
731 static int
732 smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr)
733 {
734 smb_attr_t attr;
735
736 bzero(&attr, sizeof (attr));
737 attr.sa_mask = SMB_AT_DOSATTR;
738 if (smb_node_getattr(sr, node, zone_kcred(), NULL, &attr) != 0)
739 return (EACCES);
740
741 if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) &&
742 !(SMB_SEARCH_HIDDEN(sattr)))
743 return (ESRCH);
744
745 if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) &&
746 !(SMB_SEARCH_SYSTEM(sattr)))
747 return (ESRCH);
748
749 return (0);
750 }
751
752 /*
753 * The following values are based on observed WFWG, Windows 9x, Windows NT
754 * and Windows 2000 behaviour.
755 *
756 * ERROR_FILE_EXISTS doesn't work for Windows 98 clients.
757 *
758 * Windows 95 clients don't see the problem because the target is deleted
759 * before the rename request.
760 */
761 static uint32_t
762 smb_rename_errno2status(int errnum)
763 {
764 static struct {
765 int errnum;
766 uint32_t status32;
767 } rc_map[] = {
768 { EEXIST, NT_STATUS_OBJECT_NAME_COLLISION },
769 { EPIPE, NT_STATUS_SHARING_VIOLATION },
770 { ENOENT, NT_STATUS_OBJECT_NAME_NOT_FOUND },
771 { ESRCH, NT_STATUS_NO_SUCH_FILE },
772 { EINVAL, NT_STATUS_INVALID_PARAMETER },
773 { EACCES, NT_STATUS_ACCESS_DENIED },
774 { EISDIR, NT_STATUS_FILE_IS_A_DIRECTORY },
775 { EIO, NT_STATUS_INTERNAL_ERROR }
776 };
777
778 int i;
779
780 if (errnum == 0)
781 return (0);
782
783 for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) {
784 if (rc_map[i].errnum == errnum) {
785 return (rc_map[i].status32);
786 }
787 }
788
789 return (smb_errno2status(errnum));
790 }