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 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 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 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 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 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 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 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 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 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 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