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