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