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 2012 Nexenta Systems, Inc. All rights reserved. 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(sr, dst_fnode, 450 SMB_OPLOCK_BREAK_TO_NONE | SMB_OPLOCK_BREAK_BATCH); 451 452 for (count = 0; count <= 3; count++) { 453 if (count) { 454 smb_node_end_crit(dst_fnode); 455 delay(MSEC_TO_TICK(400)); 456 } 457 458 smb_node_start_crit(dst_fnode, RW_READER); 459 status = smb_node_delete_check(dst_fnode); 460 461 if (status != NT_STATUS_SHARING_VIOLATION) 462 break; 463 } 464 465 if (status != NT_STATUS_SHARING_VIOLATION) 466 status = smb_range_check(sr, dst_fnode, 467 0, UINT64_MAX, B_TRUE); 468 469 if (status != NT_STATUS_SUCCESS) { 470 smb_rename_release_src(sr); 471 smb_node_end_crit(dst_fnode); 472 smb_node_release(dst_fnode); 473 smb_node_release(dst_dnode); 474 return (EACCES); 475 } 476 477 new_name = dst_fnode->od_name; 478 } 479 480 rc = smb_fsop_rename(sr, sr->user_cr, 481 src_dnode, src_fnode->od_name, 482 dst_dnode, new_name); 483 484 if (rc == 0) { 485 /* 486 * Note that renames in the same directory are normally 487 * delivered in {old,new} pairs, and clients expect them 488 * in that order, if both events are delivered. 489 */ 490 int a_src, a_dst; /* action codes */ 491 if (src_dnode == dst_dnode) { 492 a_src = FILE_ACTION_RENAMED_OLD_NAME; 493 a_dst = FILE_ACTION_RENAMED_NEW_NAME; 494 } else { 495 a_src = FILE_ACTION_REMOVED; 496 a_dst = FILE_ACTION_ADDED; 497 } 498 smb_node_notify_change(src_dnode, a_src, src_fnode->od_name); 499 smb_node_notify_change(dst_dnode, a_dst, new_name); 500 } 501 502 smb_rename_release_src(sr); 503 504 if (dst_fqi->fq_fnode) { 505 smb_node_end_crit(dst_fnode); 506 smb_node_release(dst_fnode); 507 } 508 smb_node_release(dst_dnode); 509 510 return (rc); 511 } 512 513 /* 514 * smb_rename_check_stream 515 * 516 * For a stream rename the dst path must begin with ':', or "\\:". 517 * We don't yet support stream rename, Return EACCES. 518 * 519 * If not a stream rename, in accordance with the above rule, 520 * it is not valid for either the src or dst to be a stream. 521 * Return EINVAL. 522 */ 523 static int 524 smb_rename_check_stream(smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) 525 { 526 smb_node_t *src_fnode = src_fqi->fq_fnode; 527 char *src_path = src_fqi->fq_path.pn_path; 528 char *dst_path = dst_fqi->fq_path.pn_path; 529 530 /* We do not yet support named stream rename - ACCESS DENIED */ 531 if ((dst_path[0] == ':') || 532 ((dst_path[0] == '\\') && (dst_path[1] == ':'))) { 533 return (EACCES); 534 } 535 536 /* 537 * If not stream rename (above) neither src or dst can be 538 * a named stream. 539 */ 540 541 if (smb_is_stream_name(dst_path)) 542 return (EINVAL); 543 544 if (src_fqi->fq_fnode) { 545 if (SMB_IS_STREAM(src_fnode)) 546 return (EINVAL); 547 } else { 548 if (smb_is_stream_name(src_path)) 549 return (EINVAL); 550 } 551 552 return (0); 553 } 554 555 556 /* 557 * smb_make_link 558 * 559 * Creating a hard link (adding an additional name) for a file. 560 * 561 * If the source and destination are identical, we go through all 562 * the checks but we don't create a link. 563 * 564 * If the file is a symlink we create the hardlink on the target 565 * of the symlink (i.e. use SMB_FOLLOW_LINKS when looking up src). 566 * If the target of the symlink does not exist we fail with ENOENT. 567 * 568 * Returns errno values. 569 */ 570 static int 571 smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) 572 { 573 smb_node_t *tnode; 574 char *path; 575 int rc; 576 577 /* Cannnot create link on named stream */ 578 if (smb_is_stream_name(src_fqi->fq_path.pn_path) || 579 smb_is_stream_name(dst_fqi->fq_path.pn_path)) { 580 return (EINVAL); 581 } 582 583 /* lookup and validate src node */ 584 rc = smb_rename_lookup_src(sr); 585 if (rc != 0) 586 return (rc); 587 588 /* if src and dest paths match we're done */ 589 if (smb_strcasecmp(src_fqi->fq_path.pn_path, 590 dst_fqi->fq_path.pn_path, 0) == 0) { 591 smb_rename_release_src(sr); 592 return (0); 593 } 594 595 /* find the destination dnode and last_comp */ 596 tnode = sr->tid_tree->t_snode; 597 path = dst_fqi->fq_path.pn_path; 598 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, 599 &dst_fqi->fq_dnode, dst_fqi->fq_last_comp); 600 if (rc != 0) { 601 smb_rename_release_src(sr); 602 return (rc); 603 } 604 605 /* If name match in same directory, we're done */ 606 if ((src_fqi->fq_dnode == dst_fqi->fq_dnode) && 607 (smb_strcasecmp(src_fqi->fq_fnode->od_name, 608 dst_fqi->fq_last_comp, 0) == 0)) { 609 smb_rename_release_src(sr); 610 smb_node_release(dst_fqi->fq_dnode); 611 return (0); 612 } 613 614 if (smb_is_invalid_filename(dst_fqi->fq_last_comp)) { 615 smb_rename_release_src(sr); 616 smb_node_release(dst_fqi->fq_dnode); 617 return (EILSEQ); /* NT_STATUS_INVALID_OBJECT_NAME */ 618 } 619 620 /* Lookup the destination node. It MUST NOT exist. */ 621 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode, 622 dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode); 623 if (rc == 0) { 624 smb_node_release(dst_fqi->fq_fnode); 625 rc = EEXIST; 626 } 627 if (rc != ENOENT) { 628 smb_rename_release_src(sr); 629 smb_node_release(dst_fqi->fq_dnode); 630 return (rc); 631 } 632 633 rc = smb_fsop_link(sr, sr->user_cr, src_fqi->fq_fnode, 634 dst_fqi->fq_dnode, dst_fqi->fq_last_comp); 635 636 if (rc == 0) { 637 smb_node_notify_change(dst_fqi->fq_dnode, 638 FILE_ACTION_ADDED, dst_fqi->fq_last_comp); 639 } 640 641 smb_rename_release_src(sr); 642 smb_node_release(dst_fqi->fq_dnode); 643 return (rc); 644 } 645 646 /* 647 * smb_rename_lookup_src 648 * 649 * Lookup the src node, checking for sharing violations and 650 * breaking any existing BATCH oplock. 651 * Populate sr->arg.dirop.fqi 652 * 653 * Upon success, the dnode and fnode will have holds and the 654 * fnode will be in a critical section. These should be 655 * released using smb_rename_release_src(). 656 * 657 * Returns errno values. 658 */ 659 static int 660 smb_rename_lookup_src(smb_request_t *sr) 661 { 662 smb_node_t *src_node, *tnode; 663 DWORD status; 664 int rc; 665 int count; 666 char *path; 667 668 struct dirop *dirop = &sr->arg.dirop; 669 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 670 671 if (smb_is_stream_name(src_fqi->fq_path.pn_path)) 672 return (EINVAL); 673 674 /* Lookup the source node */ 675 tnode = sr->tid_tree->t_snode; 676 path = src_fqi->fq_path.pn_path; 677 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, 678 &src_fqi->fq_dnode, src_fqi->fq_last_comp); 679 if (rc != 0) 680 return (rc); 681 682 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode, 683 src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode); 684 if (rc != 0) { 685 smb_node_release(src_fqi->fq_dnode); 686 return (rc); 687 } 688 689 /* Not valid to create hardlink for directory */ 690 if ((dirop->info_level == SMB_NT_RENAME_SET_LINK_INFO) && 691 (smb_node_is_dir(src_fqi->fq_fnode))) { 692 smb_node_release(src_fqi->fq_fnode); 693 smb_node_release(src_fqi->fq_dnode); 694 return (EISDIR); 695 } 696 697 src_node = src_fqi->fq_fnode; 698 699 rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr); 700 if (rc != 0) { 701 smb_node_release(src_fqi->fq_fnode); 702 smb_node_release(src_fqi->fq_dnode); 703 return (rc); 704 } 705 706 /* 707 * Break BATCH oplock before access checks. If a client 708 * has a file open, this will force a flush or close, 709 * which may affect the outcome of any share checking. 710 */ 711 (void) smb_oplock_break(sr, src_node, 712 SMB_OPLOCK_BREAK_TO_LEVEL_II | SMB_OPLOCK_BREAK_BATCH); 713 714 for (count = 0; count <= 3; count++) { 715 if (count) { 716 smb_node_end_crit(src_node); 717 delay(MSEC_TO_TICK(400)); 718 } 719 720 smb_node_start_crit(src_node, RW_READER); 721 722 status = smb_node_rename_check(src_node); 723 if (status != NT_STATUS_SHARING_VIOLATION) 724 break; 725 } 726 727 if (status == NT_STATUS_SHARING_VIOLATION) { 728 smb_node_end_crit(src_node); 729 smb_node_release(src_fqi->fq_fnode); 730 smb_node_release(src_fqi->fq_dnode); 731 return (EPIPE); /* = ERRbadshare */ 732 } 733 734 status = smb_range_check(sr, src_node, 0, UINT64_MAX, B_TRUE); 735 if (status != NT_STATUS_SUCCESS) { 736 smb_node_end_crit(src_node); 737 smb_node_release(src_fqi->fq_fnode); 738 smb_node_release(src_fqi->fq_dnode); 739 return (EACCES); 740 } 741 742 return (0); 743 } 744 745 /* 746 * smb_rename_release_src 747 */ 748 static void 749 smb_rename_release_src(smb_request_t *sr) 750 { 751 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 752 753 smb_node_end_crit(src_fqi->fq_fnode); 754 smb_node_release(src_fqi->fq_fnode); 755 smb_node_release(src_fqi->fq_dnode); 756 } 757 758 759 static int 760 smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr) 761 { 762 smb_attr_t attr; 763 764 if (smb_node_getattr(sr, node, &attr) != 0) 765 return (EIO); 766 767 if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) && 768 !(SMB_SEARCH_HIDDEN(sattr))) 769 return (ESRCH); 770 771 if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) && 772 !(SMB_SEARCH_SYSTEM(sattr))) 773 return (ESRCH); 774 775 return (0); 776 } 777 778 /* 779 * The following values are based on observed WFWG, Windows 9x, Windows NT 780 * and Windows 2000 behaviour. 781 * 782 * ERROR_FILE_EXISTS doesn't work for Windows 98 clients. 783 * 784 * Windows 95 clients don't see the problem because the target is deleted 785 * before the rename request. 786 */ 787 static void 788 smb_rename_set_error(smb_request_t *sr, int errnum) 789 { 790 static struct { 791 int errnum; 792 uint16_t errcode; 793 uint32_t status32; 794 } rc_map[] = { 795 { EEXIST, ERROR_ALREADY_EXISTS, NT_STATUS_OBJECT_NAME_COLLISION }, 796 { EPIPE, ERROR_SHARING_VIOLATION, NT_STATUS_SHARING_VIOLATION }, 797 { ENOENT, ERROR_FILE_NOT_FOUND, NT_STATUS_OBJECT_NAME_NOT_FOUND }, 798 { ESRCH, ERROR_FILE_NOT_FOUND, NT_STATUS_NO_SUCH_FILE }, 799 { EINVAL, ERROR_INVALID_PARAMETER, NT_STATUS_INVALID_PARAMETER }, 800 { EACCES, ERROR_ACCESS_DENIED, NT_STATUS_ACCESS_DENIED }, 801 { EISDIR, ERROR_ACCESS_DENIED, NT_STATUS_FILE_IS_A_DIRECTORY }, 802 { EIO, ERROR_INTERNAL_ERROR, NT_STATUS_INTERNAL_ERROR } 803 }; 804 805 int i; 806 807 if (errnum == 0) 808 return; 809 810 for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) { 811 if (rc_map[i].errnum == errnum) { 812 smbsr_error(sr, rc_map[i].status32, 813 ERRDOS, rc_map[i].errcode); 814 return; 815 } 816 } 817 818 smbsr_errno(sr, errnum); 819 } 820