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