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