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