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