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
smb_pre_rename(smb_request_t * sr)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
smb_post_rename(smb_request_t * sr)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
smb_com_rename(smb_request_t * sr)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
smb_pre_nt_rename(smb_request_t * sr)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
smb_post_nt_rename(smb_request_t * sr)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
smb_com_nt_rename(smb_request_t * sr)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
smb_nt_transact_rename(smb_request_t * sr,smb_xa_t * xa)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
smb_trans2_rename(smb_request_t * sr,smb_node_t * node,char * fname,int flags)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
smb_common_rename(smb_request_t * sr,smb_fqi_t * src_fqi,smb_fqi_t * dst_fqi)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
smb_rename_check_stream(smb_fqi_t * src_fqi,smb_fqi_t * dst_fqi)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
smb_make_link(smb_request_t * sr,smb_fqi_t * src_fqi,smb_fqi_t * dst_fqi)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
smb_rename_lookup_src(smb_request_t * sr)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
smb_rename_release_src(smb_request_t * sr)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
smb_rename_check_attr(smb_request_t * sr,smb_node_t * node,uint16_t sattr)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
smb_rename_set_error(smb_request_t * sr,int errnum)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