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