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 2010 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 int rc; 105 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 106 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi; 107 smb_pathname_t *src_pn = &src_fqi->fq_path; 108 smb_pathname_t *dst_pn = &dst_fqi->fq_path; 109 110 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 111 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 112 ERRDOS, ERROR_ACCESS_DENIED); 113 return (SDRC_ERROR); 114 } 115 116 smb_pathname_init(sr, src_pn, src_pn->pn_path); 117 smb_pathname_init(sr, dst_pn, dst_pn->pn_path); 118 if (!smb_pathname_validate(sr, src_pn) || 119 !smb_pathname_validate(sr, dst_pn)) { 120 return (SDRC_ERROR); 121 } 122 123 rc = smb_common_rename(sr, src_fqi, dst_fqi); 124 125 if (rc != 0) { 126 smb_rename_set_error(sr, rc); 127 return (SDRC_ERROR); 128 } 129 130 rc = smbsr_encode_empty_result(sr); 131 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 132 } 133 134 /* 135 * smb_com_nt_rename 136 * 137 * Rename a file. Files OldFileName must exist and NewFileName must not. 138 * Both pathnames must be relative to the Tid specified in the request. 139 * Open files may be renamed. 140 * 141 * SearchAttributes indicates the attributes that the target file(s) must 142 * have. If SearchAttributes is zero then only normal files are renamed. 143 * If the system file or hidden attributes are specified then the rename 144 * is inclusive - both the specified type(s) of files and normal files are 145 * renamed. 146 */ 147 smb_sdrc_t 148 smb_pre_nt_rename(smb_request_t *sr) 149 { 150 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 151 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi; 152 uint32_t clusters; 153 int rc; 154 155 rc = smbsr_decode_vwv(sr, "wwl", &src_fqi->fq_sattr, 156 &sr->arg.dirop.info_level, &clusters); 157 if (rc == 0) { 158 rc = smbsr_decode_data(sr, "%SS", sr, 159 &src_fqi->fq_path.pn_path, &dst_fqi->fq_path.pn_path); 160 161 dst_fqi->fq_sattr = 0; 162 } 163 164 DTRACE_SMB_2(op__NtRename__start, smb_request_t *, sr, 165 struct dirop *, &sr->arg.dirop); 166 167 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 168 } 169 170 void 171 smb_post_nt_rename(smb_request_t *sr) 172 { 173 DTRACE_SMB_1(op__NtRename__done, smb_request_t *, sr); 174 } 175 176 smb_sdrc_t 177 smb_com_nt_rename(smb_request_t *sr) 178 { 179 int rc; 180 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 181 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi; 182 smb_pathname_t *src_pn = &src_fqi->fq_path; 183 smb_pathname_t *dst_pn = &dst_fqi->fq_path; 184 185 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 186 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 187 ERRDOS, ERROR_ACCESS_DENIED); 188 return (SDRC_ERROR); 189 } 190 191 smb_pathname_init(sr, src_pn, src_pn->pn_path); 192 smb_pathname_init(sr, dst_pn, dst_pn->pn_path); 193 if (!smb_pathname_validate(sr, src_pn) || 194 !smb_pathname_validate(sr, dst_pn)) { 195 return (SDRC_ERROR); 196 } 197 198 if (smb_contains_wildcards(src_pn->pn_path)) { 199 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, 200 ERRDOS, ERROR_BAD_PATHNAME); 201 return (SDRC_ERROR); 202 } 203 204 switch (sr->arg.dirop.info_level) { 205 case SMB_NT_RENAME_SET_LINK_INFO: 206 rc = smb_make_link(sr, src_fqi, dst_fqi); 207 break; 208 case SMB_NT_RENAME_RENAME_FILE: 209 case SMB_NT_RENAME_MOVE_FILE: 210 rc = smb_common_rename(sr, src_fqi, dst_fqi); 211 break; 212 case SMB_NT_RENAME_MOVE_CLUSTER_INFO: 213 rc = EINVAL; 214 break; 215 default: 216 rc = EACCES; 217 break; 218 } 219 220 if (rc != 0) { 221 smb_rename_set_error(sr, rc); 222 return (SDRC_ERROR); 223 } 224 225 rc = smbsr_encode_empty_result(sr); 226 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 227 } 228 229 /* 230 * smb_nt_transact_rename 231 * 232 * Windows servers return SUCCESS without renaming file. 233 * The only check required is to check that the handle (fid) is valid. 234 */ 235 smb_sdrc_t 236 smb_nt_transact_rename(smb_request_t *sr, smb_xa_t *xa) 237 { 238 if (smb_mbc_decodef(&xa->req_param_mb, "w", &sr->smb_fid) != 0) 239 return (SDRC_ERROR); 240 241 smbsr_lookup_file(sr); 242 if (sr->fid_ofile == NULL) { 243 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); 244 return (SDRC_ERROR); 245 } 246 smbsr_release_file(sr); 247 248 return (SDRC_SUCCESS); 249 } 250 251 /* 252 * smb_trans2_rename 253 * 254 * Implements SMB_FILE_RENAME_INFORMATION level of Trans2_Set_FileInfo 255 * and Trans2_Set_PathInfo. 256 * If the new filename (dst_fqi) already exists it may be overwritten 257 * if flags == 1. 258 */ 259 int 260 smb_trans2_rename(smb_request_t *sr, smb_node_t *node, char *fname, int flags) 261 { 262 int rc = 0; 263 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 264 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi; 265 smb_pathname_t *dst_pn = &dst_fqi->fq_path; 266 char *path; 267 int len; 268 269 sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0; 270 sr->arg.dirop.info_level = SMB_NT_RENAME_RENAME_FILE; 271 272 src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES; 273 src_fqi->fq_fnode = node; 274 src_fqi->fq_dnode = node->n_dnode; 275 276 /* costruct and validate the dst pathname */ 277 path = smb_srm_zalloc(sr, MAXPATHLEN); 278 if (src_fqi->fq_path.pn_pname) { 279 (void) snprintf(path, MAXPATHLEN, "%s\\%s", 280 src_fqi->fq_path.pn_pname, fname); 281 } else { 282 rc = smb_node_getshrpath(node->n_dnode, sr->tid_tree, 283 path, MAXPATHLEN); 284 if (rc != 0) { 285 smb_rename_set_error(sr, rc); 286 return (-1); 287 } 288 len = strlen(path); 289 (void) snprintf(path + len, MAXPATHLEN - len, "\\%s", fname); 290 } 291 292 smb_pathname_init(sr, dst_pn, path); 293 if (!smb_pathname_validate(sr, dst_pn)) 294 return (-1); 295 296 dst_fqi->fq_dnode = node->n_dnode; 297 (void) strlcpy(dst_fqi->fq_last_comp, dst_pn->pn_fname, MAXNAMELEN); 298 299 rc = smb_common_rename(sr, src_fqi, dst_fqi); 300 if (rc != 0) { 301 smb_rename_set_error(sr, rc); 302 return (-1); 303 } 304 305 return (0); 306 } 307 308 /* 309 * smb_common_rename 310 * 311 * Common code for renaming a file. 312 * 313 * If the source and destination are identical, we go through all 314 * the checks but we don't actually do the rename. If the source 315 * and destination files differ only in case, we do a case-sensitive 316 * rename. Otherwise, we do a full case-insensitive rename. 317 * 318 * Returns errno values. 319 */ 320 static int 321 smb_common_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) 322 { 323 smb_node_t *src_fnode, *src_dnode, *dst_fnode, *dst_dnode; 324 smb_node_t *tnode; 325 int rc, count; 326 DWORD status; 327 char *new_name, *path; 328 329 path = dst_fqi->fq_path.pn_path; 330 331 /* Check if attempting to rename a stream - not yet supported */ 332 rc = smb_rename_check_stream(src_fqi, dst_fqi); 333 if (rc != 0) 334 return (rc); 335 336 /* The source node may already have been provided */ 337 if (src_fqi->fq_fnode) { 338 smb_node_start_crit(src_fqi->fq_fnode, RW_READER); 339 smb_node_ref(src_fqi->fq_fnode); 340 smb_node_ref(src_fqi->fq_dnode); 341 } else { 342 /* lookup and validate src node */ 343 rc = smb_rename_lookup_src(sr); 344 if (rc != 0) 345 return (rc); 346 } 347 348 src_fnode = src_fqi->fq_fnode; 349 src_dnode = src_fqi->fq_dnode; 350 351 /* Find destination dnode and last_comp */ 352 if (dst_fqi->fq_dnode) { 353 smb_node_ref(dst_fqi->fq_dnode); 354 } else { 355 tnode = sr->tid_tree->t_snode; 356 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, 357 &dst_fqi->fq_dnode, dst_fqi->fq_last_comp); 358 if (rc != 0) { 359 smb_rename_release_src(sr); 360 return (rc); 361 } 362 } 363 364 dst_dnode = dst_fqi->fq_dnode; 365 new_name = dst_fqi->fq_last_comp; 366 367 /* If exact name match in same directory, we're done */ 368 if ((src_dnode == dst_dnode) && 369 (strcmp(src_fnode->od_name, new_name) == 0)) { 370 smb_rename_release_src(sr); 371 smb_node_release(dst_dnode); 372 return (0); 373 } 374 375 /* Lookup destination node */ 376 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode, 377 dst_dnode, new_name, &dst_fqi->fq_fnode); 378 379 /* If the destination node doesn't already exist, validate new_name. */ 380 if (rc == ENOENT) { 381 if (smb_is_invalid_filename(new_name)) { 382 smb_rename_release_src(sr); 383 smb_node_release(dst_dnode); 384 return (EILSEQ); /* NT_STATUS_OBJECT_NAME_INVALID */ 385 } 386 } 387 388 /* 389 * Handle case where changing case of the same directory entry. 390 * 391 * If we found the dst node in the same directory as the src node, 392 * and their names differ only in case: 393 * 394 * If the tree is case sensitive (or mixed): 395 * Do case sensitive lookup to see if exact match exists. 396 * If the exact match is the same node as src_node we're done. 397 * 398 * If the tree is case insensitive: 399 * There is currently no way to tell if the case is different 400 * or not, so do the rename (unless the specified new name was 401 * mangled). 402 */ 403 if ((rc == 0) && 404 (src_dnode == dst_dnode) && 405 (smb_strcasecmp(src_fnode->od_name, 406 dst_fqi->fq_fnode->od_name, 0) == 0)) { 407 smb_node_release(dst_fqi->fq_fnode); 408 dst_fqi->fq_fnode = NULL; 409 410 if (smb_tree_has_feature(sr->tid_tree, 411 SMB_TREE_NO_CASESENSITIVE)) { 412 if (smb_strcasecmp(src_fnode->od_name, 413 dst_fqi->fq_last_comp, 0) != 0) { 414 smb_rename_release_src(sr); 415 smb_node_release(dst_dnode); 416 return (0); 417 } 418 } else { 419 rc = smb_fsop_lookup(sr, sr->user_cr, 420 SMB_CASE_SENSITIVE, tnode, dst_dnode, new_name, 421 &dst_fqi->fq_fnode); 422 423 if ((rc == 0) && 424 (dst_fqi->fq_fnode == src_fnode)) { 425 smb_rename_release_src(sr); 426 smb_node_release(dst_fqi->fq_fnode); 427 smb_node_release(dst_dnode); 428 return (0); 429 } 430 } 431 } 432 433 if ((rc != 0) && (rc != ENOENT)) { 434 smb_rename_release_src(sr); 435 smb_node_release(dst_fqi->fq_dnode); 436 return (rc); 437 } 438 439 if (dst_fqi->fq_fnode) { 440 dst_fnode = dst_fqi->fq_fnode; 441 442 if (!(sr->arg.dirop.flags && SMB_RENAME_FLAG_OVERWRITE)) { 443 smb_rename_release_src(sr); 444 smb_node_release(dst_fnode); 445 smb_node_release(dst_dnode); 446 return (EEXIST); 447 } 448 449 (void) smb_oplock_break(dst_fnode, sr->session, B_FALSE); 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 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 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 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 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 the 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(src_node, sr->session, B_FALSE); 693 694 for (count = 0; count <= 3; count++) { 695 if (count) { 696 smb_node_end_crit(src_node); 697 delay(MSEC_TO_TICK(400)); 698 } 699 700 smb_node_start_crit(src_node, RW_READER); 701 702 status = smb_node_rename_check(src_node); 703 if (status != NT_STATUS_SHARING_VIOLATION) 704 break; 705 } 706 707 if (status == NT_STATUS_SHARING_VIOLATION) { 708 smb_node_end_crit(src_node); 709 smb_node_release(src_fqi->fq_fnode); 710 smb_node_release(src_fqi->fq_dnode); 711 return (EPIPE); /* = ERRbadshare */ 712 } 713 714 status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE); 715 if (status != NT_STATUS_SUCCESS) { 716 smb_node_end_crit(src_node); 717 smb_node_release(src_fqi->fq_fnode); 718 smb_node_release(src_fqi->fq_dnode); 719 return (EACCES); 720 } 721 722 return (0); 723 } 724 725 /* 726 * smb_rename_release_src 727 */ 728 static void 729 smb_rename_release_src(smb_request_t *sr) 730 { 731 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 732 733 smb_node_end_crit(src_fqi->fq_fnode); 734 smb_node_release(src_fqi->fq_fnode); 735 smb_node_release(src_fqi->fq_dnode); 736 } 737 738 739 static int 740 smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr) 741 { 742 smb_attr_t attr; 743 744 if (smb_node_getattr(sr, node, &attr) != 0) 745 return (EIO); 746 747 if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) && 748 !(SMB_SEARCH_HIDDEN(sattr))) 749 return (ESRCH); 750 751 if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) && 752 !(SMB_SEARCH_SYSTEM(sattr))) 753 return (ESRCH); 754 755 return (0); 756 } 757 758 /* 759 * The following values are based on observed WFWG, Windows 9x, Windows NT 760 * and Windows 2000 behaviour. 761 * 762 * ERROR_FILE_EXISTS doesn't work for Windows 98 clients. 763 * 764 * Windows 95 clients don't see the problem because the target is deleted 765 * before the rename request. 766 */ 767 static void 768 smb_rename_set_error(smb_request_t *sr, int errnum) 769 { 770 static struct { 771 int errnum; 772 uint16_t errcode; 773 uint32_t status32; 774 } rc_map[] = { 775 { EEXIST, ERROR_ALREADY_EXISTS, NT_STATUS_OBJECT_NAME_COLLISION }, 776 { EPIPE, ERROR_SHARING_VIOLATION, NT_STATUS_SHARING_VIOLATION }, 777 { ENOENT, ERROR_FILE_NOT_FOUND, NT_STATUS_OBJECT_NAME_NOT_FOUND }, 778 { ESRCH, ERROR_FILE_NOT_FOUND, NT_STATUS_NO_SUCH_FILE }, 779 { EINVAL, ERROR_INVALID_PARAMETER, NT_STATUS_INVALID_PARAMETER }, 780 { EACCES, ERROR_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED }, 781 { EISDIR, ERROR_ACCESS_DENIED, NT_STATUS_FILE_IS_A_DIRECTORY }, 782 { EIO, ERROR_INTERNAL_ERROR, NT_STATUS_INTERNAL_ERROR } 783 }; 784 785 int i; 786 787 if (errnum == 0) 788 return; 789 790 for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) { 791 if (rc_map[i].errnum == errnum) { 792 smbsr_error(sr, rc_map[i].status32, 793 ERRDOS, rc_map[i].errcode); 794 return; 795 } 796 } 797 798 smbsr_errno(sr, errnum); 799 } 800