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 2014 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 * SMB_TRANS2_SET_FILE/PATH_INFO (RENAME_INFORMATION level) flag 33 */ 34 #define SMB_RENAME_FLAG_OVERWRITE 0x001 35 36 static int smb_rename_check_stream(smb_fqi_t *, smb_fqi_t *); 37 static int smb_rename_check_attr(smb_request_t *, smb_node_t *, uint16_t); 38 static int smb_rename_lookup_src(smb_request_t *); 39 static void smb_rename_release_src(smb_request_t *); 40 static uint32_t smb_rename_errno2status(int); 41 42 /* 43 * smb_setinfo_rename 44 * 45 * Implements SMB_FILE_RENAME_INFORMATION level of Trans2_Set_FileInfo 46 * and Trans2_Set_PathInfo and SMB2 set_info, FileRenameInformation. 47 * If the new filename (dst_fqi) already exists it may be overwritten 48 * if flags == 1. 49 * 50 * The passed path is a full path relative to the share root. 51 * 52 * Returns NT status codes. 53 * 54 * Similar to smb_setinfo_link(), below. 55 */ 56 uint32_t 57 smb_setinfo_rename(smb_request_t *sr, smb_node_t *node, char *path, int flags) 58 { 59 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 60 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi; 61 smb_pathname_t *dst_pn = &dst_fqi->fq_path; 62 uint32_t status; 63 64 sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0; 65 sr->arg.dirop.info_level = FileRenameInformation; 66 67 src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES; 68 src_fqi->fq_fnode = node; 69 src_fqi->fq_dnode = node->n_dnode; 70 71 /* validate the dst pathname */ 72 smb_pathname_init(sr, dst_pn, path); 73 if (!smb_pathname_validate(sr, dst_pn)) 74 return (NT_STATUS_OBJECT_NAME_INVALID); 75 76 status = smb_common_rename(sr, src_fqi, dst_fqi); 77 return (status); 78 } 79 80 /* 81 * smb_common_rename 82 * 83 * Common code for renaming a file. 84 * 85 * If the source and destination are identical, we go through all 86 * the checks but we don't actually do the rename. If the source 87 * and destination files differ only in case, we do a case-sensitive 88 * rename. Otherwise, we do a full case-insensitive rename. 89 * 90 * Returns NT status values. 91 * 92 * Similar to smb_make_link(), below. 93 */ 94 uint32_t 95 smb_common_rename(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) 96 { 97 smb_node_t *src_fnode, *src_dnode, *dst_dnode; 98 smb_node_t *dst_fnode = 0; 99 smb_node_t *tnode; 100 char *new_name, *path; 101 DWORD status; 102 int rc, count; 103 104 tnode = sr->tid_tree->t_snode; 105 path = dst_fqi->fq_path.pn_path; 106 107 /* Check if attempting to rename a stream - not yet supported */ 108 rc = smb_rename_check_stream(src_fqi, dst_fqi); 109 if (rc != 0) 110 return (smb_rename_errno2status(rc)); 111 112 /* 113 * The source node may already have been provided, 114 * i.e. when called by SMB1/SMB2 smb_setinfo_rename. 115 * Not provided by smb_com_rename, smb_com_nt_rename. 116 */ 117 if (src_fqi->fq_fnode) { 118 smb_node_start_crit(src_fqi->fq_fnode, RW_READER); 119 smb_node_ref(src_fqi->fq_fnode); 120 smb_node_ref(src_fqi->fq_dnode); 121 } else { 122 /* lookup and validate src node */ 123 rc = smb_rename_lookup_src(sr); 124 if (rc != 0) 125 return (smb_rename_errno2status(rc)); 126 } 127 128 src_fnode = src_fqi->fq_fnode; 129 src_dnode = src_fqi->fq_dnode; 130 131 /* 132 * Find the destination dnode and last component. 133 * May already be provided, i.e. when called via 134 * SMB1 trans2 setinfo. 135 */ 136 if (dst_fqi->fq_dnode) { 137 /* called via smb_set_rename_info */ 138 smb_node_ref(dst_fqi->fq_dnode); 139 } else { 140 /* called via smb2_setf_rename, smb_com_rename, etc. */ 141 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, 142 &dst_fqi->fq_dnode, dst_fqi->fq_last_comp); 143 if (rc != 0) { 144 smb_rename_release_src(sr); 145 return (smb_rename_errno2status(rc)); 146 } 147 } 148 149 dst_dnode = dst_fqi->fq_dnode; 150 new_name = dst_fqi->fq_last_comp; 151 152 /* If exact name match in same directory, we're done */ 153 if ((src_dnode == dst_dnode) && 154 (strcmp(src_fnode->od_name, new_name) == 0)) { 155 smb_rename_release_src(sr); 156 smb_node_release(dst_dnode); 157 return (0); 158 } 159 160 /* Lookup destination node */ 161 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode, 162 dst_dnode, new_name, &dst_fqi->fq_fnode); 163 164 /* If the destination node doesn't already exist, validate new_name. */ 165 if (rc == ENOENT) { 166 if (smb_is_invalid_filename(new_name)) { 167 smb_rename_release_src(sr); 168 smb_node_release(dst_dnode); 169 return (NT_STATUS_OBJECT_NAME_INVALID); 170 } 171 } 172 173 /* 174 * Handle case where changing case of the same directory entry. 175 * 176 * If we found the dst node in the same directory as the src node, 177 * and their names differ only in case: 178 * 179 * If the tree is case sensitive (or mixed): 180 * Do case sensitive lookup to see if exact match exists. 181 * If the exact match is the same node as src_node we're done. 182 * 183 * If the tree is case insensitive: 184 * There is currently no way to tell if the case is different 185 * or not, so do the rename (unless the specified new name was 186 * mangled). 187 */ 188 if ((rc == 0) && 189 (src_dnode == dst_dnode) && 190 (smb_strcasecmp(src_fnode->od_name, 191 dst_fqi->fq_fnode->od_name, 0) == 0)) { 192 smb_node_release(dst_fqi->fq_fnode); 193 dst_fqi->fq_fnode = NULL; 194 195 if (smb_tree_has_feature(sr->tid_tree, 196 SMB_TREE_NO_CASESENSITIVE)) { 197 if (smb_strcasecmp(src_fnode->od_name, 198 dst_fqi->fq_last_comp, 0) != 0) { 199 smb_rename_release_src(sr); 200 smb_node_release(dst_dnode); 201 return (0); 202 } 203 } else { 204 rc = smb_fsop_lookup(sr, sr->user_cr, 205 SMB_CASE_SENSITIVE, tnode, dst_dnode, new_name, 206 &dst_fqi->fq_fnode); 207 208 if ((rc == 0) && 209 (dst_fqi->fq_fnode == src_fnode)) { 210 smb_rename_release_src(sr); 211 smb_node_release(dst_fqi->fq_fnode); 212 smb_node_release(dst_dnode); 213 return (0); 214 } 215 } 216 } 217 218 if ((rc != 0) && (rc != ENOENT)) { 219 smb_rename_release_src(sr); 220 smb_node_release(dst_fqi->fq_dnode); 221 return (smb_rename_errno2status(rc)); 222 } 223 224 if (dst_fqi->fq_fnode) { 225 /* 226 * Destination already exists. Do delete checks. 227 */ 228 dst_fnode = dst_fqi->fq_fnode; 229 230 if ((sr->arg.dirop.flags & SMB_RENAME_FLAG_OVERWRITE) == 0) { 231 smb_rename_release_src(sr); 232 smb_node_release(dst_fnode); 233 smb_node_release(dst_dnode); 234 return (NT_STATUS_OBJECT_NAME_COLLISION); 235 } 236 237 (void) smb_oplock_break(sr, dst_fnode, 238 SMB_OPLOCK_BREAK_TO_NONE | SMB_OPLOCK_BREAK_BATCH); 239 240 /* 241 * Wait (a little) for the oplock break to be 242 * responded to by clients closing handles. 243 * Hold node->n_lock as reader to keep new 244 * ofiles from showing up after we check. 245 */ 246 smb_node_rdlock(dst_fnode); 247 for (count = 0; count <= 12; count++) { 248 status = smb_node_delete_check(dst_fnode); 249 if (status != NT_STATUS_SHARING_VIOLATION) 250 break; 251 smb_node_unlock(dst_fnode); 252 delay(MSEC_TO_TICK(100)); 253 smb_node_rdlock(dst_fnode); 254 } 255 if (status != NT_STATUS_SUCCESS) { 256 smb_node_unlock(dst_fnode); 257 smb_rename_release_src(sr); 258 smb_node_release(dst_fnode); 259 smb_node_release(dst_dnode); 260 return (NT_STATUS_ACCESS_DENIED); 261 } 262 263 /* 264 * Note, the combination of these two: 265 * smb_node_rdlock(node); 266 * nbl_start_crit(node->vp, RW_READER); 267 * is equivalent to this call: 268 * smb_node_start_crit(node, RW_READER) 269 * 270 * Cleanup after this point should use: 271 * smb_node_end_crit(dst_fnode) 272 */ 273 nbl_start_crit(dst_fnode->vp, RW_READER); 274 275 /* 276 * This checks nbl_share_conflict, nbl_lock_conflict 277 */ 278 status = smb_nbl_conflict(dst_fnode, 0, UINT64_MAX, NBL_REMOVE); 279 if (status != NT_STATUS_SUCCESS) { 280 smb_node_end_crit(dst_fnode); 281 smb_rename_release_src(sr); 282 smb_node_release(dst_fnode); 283 smb_node_release(dst_dnode); 284 return (NT_STATUS_ACCESS_DENIED); 285 } 286 287 new_name = dst_fnode->od_name; 288 } 289 290 rc = smb_fsop_rename(sr, sr->user_cr, 291 src_dnode, src_fnode->od_name, 292 dst_dnode, new_name); 293 294 if (rc == 0) { 295 /* 296 * Note that renames in the same directory are normally 297 * delivered in {old,new} pairs, and clients expect them 298 * in that order, if both events are delivered. 299 */ 300 int a_src, a_dst; /* action codes */ 301 if (src_dnode == dst_dnode) { 302 a_src = FILE_ACTION_RENAMED_OLD_NAME; 303 a_dst = FILE_ACTION_RENAMED_NEW_NAME; 304 } else { 305 a_src = FILE_ACTION_REMOVED; 306 a_dst = FILE_ACTION_ADDED; 307 } 308 smb_node_notify_change(src_dnode, a_src, src_fnode->od_name); 309 smb_node_notify_change(dst_dnode, a_dst, new_name); 310 } 311 312 smb_rename_release_src(sr); 313 314 if (dst_fqi->fq_fnode) { 315 smb_node_end_crit(dst_fnode); 316 smb_node_release(dst_fnode); 317 } 318 smb_node_release(dst_dnode); 319 320 return (smb_rename_errno2status(rc)); 321 } 322 323 /* 324 * smb_rename_check_stream 325 * 326 * For a stream rename the dst path must begin with ':', or "\\:". 327 * We don't yet support stream rename, Return EACCES. 328 * 329 * If not a stream rename, in accordance with the above rule, 330 * it is not valid for either the src or dst to be a stream. 331 * Return EINVAL. 332 */ 333 static int 334 smb_rename_check_stream(smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) 335 { 336 smb_node_t *src_fnode = src_fqi->fq_fnode; 337 char *src_path = src_fqi->fq_path.pn_path; 338 char *dst_path = dst_fqi->fq_path.pn_path; 339 340 /* We do not yet support named stream rename - ACCESS DENIED */ 341 if ((dst_path[0] == ':') || 342 ((dst_path[0] == '\\') && (dst_path[1] == ':'))) { 343 return (EACCES); 344 } 345 346 /* 347 * If not stream rename (above) neither src or dst can be 348 * a named stream. 349 */ 350 351 if (smb_is_stream_name(dst_path)) 352 return (EINVAL); 353 354 if (src_fqi->fq_fnode) { 355 if (SMB_IS_STREAM(src_fnode)) 356 return (EINVAL); 357 } else { 358 if (smb_is_stream_name(src_path)) 359 return (EINVAL); 360 } 361 362 return (0); 363 } 364 365 366 /* 367 * smb_setinfo_link 368 * 369 * Implements FileRenameInformation for SMB1 Trans2 setinfo, SMB2 setinfo. 370 * If the new filename (dst_fqi) already exists it may be overwritten 371 * if flags == 1. 372 * 373 * The passed path is a full path relative to the share root. 374 * 375 * Returns NT status codes. 376 * 377 * Similar to smb_setinfo_rename(), above. 378 */ 379 uint32_t 380 smb_setinfo_link(smb_request_t *sr, smb_node_t *node, char *path, int flags) 381 { 382 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 383 smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi; 384 smb_pathname_t *dst_pn = &dst_fqi->fq_path; 385 uint32_t status; 386 387 sr->arg.dirop.flags = flags ? SMB_RENAME_FLAG_OVERWRITE : 0; 388 sr->arg.dirop.info_level = FileLinkInformation; 389 390 src_fqi->fq_sattr = SMB_SEARCH_ATTRIBUTES; 391 src_fqi->fq_fnode = node; 392 src_fqi->fq_dnode = node->n_dnode; 393 394 /* validate the dst pathname */ 395 smb_pathname_init(sr, dst_pn, path); 396 if (!smb_pathname_validate(sr, dst_pn)) 397 return (NT_STATUS_OBJECT_NAME_INVALID); 398 399 status = smb_make_link(sr, src_fqi, dst_fqi); 400 return (status); 401 } 402 403 /* 404 * smb_make_link 405 * 406 * Creating a hard link (adding an additional name) for a file. 407 * 408 * If the source and destination are identical, we go through all 409 * the checks but we don't create a link. 410 * 411 * If the file is a symlink we create the hardlink on the target 412 * of the symlink (i.e. use SMB_FOLLOW_LINKS when looking up src). 413 * If the target of the symlink does not exist we fail with ENOENT. 414 * 415 * Returns NT status values. 416 * 417 * Similar to smb_common_rename() above. 418 */ 419 uint32_t 420 smb_make_link(smb_request_t *sr, smb_fqi_t *src_fqi, smb_fqi_t *dst_fqi) 421 { 422 smb_node_t *tnode; 423 char *path; 424 int rc; 425 426 tnode = sr->tid_tree->t_snode; 427 path = dst_fqi->fq_path.pn_path; 428 429 /* Cannnot create link on named stream */ 430 if (smb_is_stream_name(src_fqi->fq_path.pn_path) || 431 smb_is_stream_name(dst_fqi->fq_path.pn_path)) { 432 return (NT_STATUS_INVALID_PARAMETER); 433 } 434 435 /* The source node may already have been provided */ 436 if (src_fqi->fq_fnode) { 437 smb_node_start_crit(src_fqi->fq_fnode, RW_READER); 438 smb_node_ref(src_fqi->fq_fnode); 439 smb_node_ref(src_fqi->fq_dnode); 440 } else { 441 /* lookup and validate src node */ 442 rc = smb_rename_lookup_src(sr); 443 if (rc != 0) 444 return (smb_rename_errno2status(rc)); 445 } 446 447 /* Not valid to create hardlink for directory */ 448 if (smb_node_is_dir(src_fqi->fq_fnode)) { 449 smb_rename_release_src(sr); 450 return (NT_STATUS_FILE_IS_A_DIRECTORY); 451 } 452 453 /* 454 * Find the destination dnode and last component. 455 * May already be provided, i.e. when called via 456 * SMB1 trans2 setinfo. 457 */ 458 if (dst_fqi->fq_dnode) { 459 smb_node_ref(dst_fqi->fq_dnode); 460 } else { 461 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, 462 &dst_fqi->fq_dnode, dst_fqi->fq_last_comp); 463 if (rc != 0) { 464 smb_rename_release_src(sr); 465 return (smb_rename_errno2status(rc)); 466 } 467 } 468 469 /* If CI name match in same directory, we're done */ 470 if ((src_fqi->fq_dnode == dst_fqi->fq_dnode) && 471 (smb_strcasecmp(src_fqi->fq_fnode->od_name, 472 dst_fqi->fq_last_comp, 0) == 0)) { 473 smb_rename_release_src(sr); 474 smb_node_release(dst_fqi->fq_dnode); 475 return (0); 476 } 477 478 if (smb_is_invalid_filename(dst_fqi->fq_last_comp)) { 479 smb_rename_release_src(sr); 480 smb_node_release(dst_fqi->fq_dnode); 481 return (NT_STATUS_OBJECT_NAME_INVALID); 482 } 483 484 /* Lookup the destination node. It MUST NOT exist. */ 485 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode, 486 dst_fqi->fq_dnode, dst_fqi->fq_last_comp, &dst_fqi->fq_fnode); 487 if (rc == 0) { 488 smb_node_release(dst_fqi->fq_fnode); 489 rc = EEXIST; 490 } 491 if (rc != ENOENT) { 492 smb_rename_release_src(sr); 493 smb_node_release(dst_fqi->fq_dnode); 494 return (smb_rename_errno2status(rc)); 495 } 496 497 rc = smb_fsop_link(sr, sr->user_cr, src_fqi->fq_fnode, 498 dst_fqi->fq_dnode, dst_fqi->fq_last_comp); 499 500 if (rc == 0) { 501 smb_node_notify_change(dst_fqi->fq_dnode, 502 FILE_ACTION_ADDED, dst_fqi->fq_last_comp); 503 } 504 505 smb_rename_release_src(sr); 506 smb_node_release(dst_fqi->fq_dnode); 507 return (smb_rename_errno2status(rc)); 508 } 509 510 /* 511 * smb_rename_lookup_src 512 * 513 * Lookup the src node, checking for sharing violations and 514 * breaking any existing BATCH oplock. 515 * Populate sr->arg.dirop.fqi 516 * 517 * Upon success, the dnode and fnode will have holds and the 518 * fnode will be in a critical section. These should be 519 * released using smb_rename_release_src(). 520 * 521 * Returns errno values. 522 */ 523 static int 524 smb_rename_lookup_src(smb_request_t *sr) 525 { 526 smb_node_t *src_node, *tnode; 527 DWORD status; 528 int rc; 529 int count; 530 char *path; 531 532 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 533 534 if (smb_is_stream_name(src_fqi->fq_path.pn_path)) 535 return (EINVAL); 536 537 /* Lookup the source node */ 538 tnode = sr->tid_tree->t_snode; 539 path = src_fqi->fq_path.pn_path; 540 rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode, 541 &src_fqi->fq_dnode, src_fqi->fq_last_comp); 542 if (rc != 0) 543 return (rc); 544 545 rc = smb_fsop_lookup(sr, sr->user_cr, 0, tnode, 546 src_fqi->fq_dnode, src_fqi->fq_last_comp, &src_fqi->fq_fnode); 547 if (rc != 0) { 548 smb_node_release(src_fqi->fq_dnode); 549 return (rc); 550 } 551 src_node = src_fqi->fq_fnode; 552 553 rc = smb_rename_check_attr(sr, src_node, src_fqi->fq_sattr); 554 if (rc != 0) { 555 smb_node_release(src_fqi->fq_fnode); 556 smb_node_release(src_fqi->fq_dnode); 557 return (rc); 558 } 559 560 /* 561 * Break BATCH oplock before ofile checks. If a client 562 * has a file open, this will force a flush or close, 563 * which may affect the outcome of any share checking. 564 */ 565 (void) smb_oplock_break(sr, src_node, 566 SMB_OPLOCK_BREAK_TO_LEVEL_II | SMB_OPLOCK_BREAK_BATCH); 567 568 /* 569 * Wait (a little) for the oplock break to be 570 * responded to by clients closing handles. 571 * Hold node->n_lock as reader to keep new 572 * ofiles from showing up after we check. 573 */ 574 smb_node_rdlock(src_node); 575 for (count = 0; count <= 12; count++) { 576 status = smb_node_rename_check(src_node); 577 if (status != NT_STATUS_SHARING_VIOLATION) 578 break; 579 smb_node_unlock(src_node); 580 delay(MSEC_TO_TICK(100)); 581 smb_node_rdlock(src_node); 582 } 583 if (status != NT_STATUS_SUCCESS) { 584 smb_node_unlock(src_node); 585 smb_node_release(src_fqi->fq_fnode); 586 smb_node_release(src_fqi->fq_dnode); 587 return (EPIPE); /* = ERRbadshare */ 588 } 589 590 /* 591 * Note, the combination of these two: 592 * smb_node_rdlock(node); 593 * nbl_start_crit(node->vp, RW_READER); 594 * is equivalent to this call: 595 * smb_node_start_crit(node, RW_READER) 596 * 597 * Cleanup after this point should use: 598 * smb_node_end_crit(src_node) 599 */ 600 nbl_start_crit(src_node->vp, RW_READER); 601 602 /* 603 * This checks nbl_share_conflict, nbl_lock_conflict 604 */ 605 status = smb_nbl_conflict(src_node, 0, UINT64_MAX, NBL_RENAME); 606 if (status != NT_STATUS_SUCCESS) { 607 smb_node_end_crit(src_node); 608 smb_node_release(src_fqi->fq_fnode); 609 smb_node_release(src_fqi->fq_dnode); 610 if (status == NT_STATUS_SHARING_VIOLATION) 611 return (EPIPE); /* = ERRbadshare */ 612 return (EACCES); 613 } 614 615 /* NB: Caller expects holds on src_fqi fnode, dnode */ 616 return (0); 617 } 618 619 /* 620 * smb_rename_release_src 621 */ 622 static void 623 smb_rename_release_src(smb_request_t *sr) 624 { 625 smb_fqi_t *src_fqi = &sr->arg.dirop.fqi; 626 627 smb_node_end_crit(src_fqi->fq_fnode); 628 smb_node_release(src_fqi->fq_fnode); 629 smb_node_release(src_fqi->fq_dnode); 630 } 631 632 633 static int 634 smb_rename_check_attr(smb_request_t *sr, smb_node_t *node, uint16_t sattr) 635 { 636 smb_attr_t attr; 637 638 bzero(&attr, sizeof (attr)); 639 attr.sa_mask = SMB_AT_DOSATTR; 640 if (smb_node_getattr(sr, node, zone_kcred(), NULL, &attr) != 0) 641 return (EACCES); 642 643 if ((attr.sa_dosattr & FILE_ATTRIBUTE_HIDDEN) && 644 !(SMB_SEARCH_HIDDEN(sattr))) 645 return (ESRCH); 646 647 if ((attr.sa_dosattr & FILE_ATTRIBUTE_SYSTEM) && 648 !(SMB_SEARCH_SYSTEM(sattr))) 649 return (ESRCH); 650 651 return (0); 652 } 653 654 /* 655 * The following values are based on observed WFWG, Windows 9x, Windows NT 656 * and Windows 2000 behaviour. 657 * 658 * ERROR_FILE_EXISTS doesn't work for Windows 98 clients. 659 * 660 * Windows 95 clients don't see the problem because the target is deleted 661 * before the rename request. 662 */ 663 static uint32_t 664 smb_rename_errno2status(int errnum) 665 { 666 static struct { 667 int errnum; 668 uint32_t status32; 669 } rc_map[] = { 670 { EEXIST, NT_STATUS_OBJECT_NAME_COLLISION }, 671 { EPIPE, NT_STATUS_SHARING_VIOLATION }, 672 { ENOENT, NT_STATUS_OBJECT_NAME_NOT_FOUND }, 673 { ESRCH, NT_STATUS_NO_SUCH_FILE }, 674 { EINVAL, NT_STATUS_INVALID_PARAMETER }, 675 { EACCES, NT_STATUS_ACCESS_DENIED }, 676 { EISDIR, NT_STATUS_FILE_IS_A_DIRECTORY }, 677 { EIO, NT_STATUS_INTERNAL_ERROR } 678 }; 679 680 int i; 681 682 if (errnum == 0) 683 return (0); 684 685 for (i = 0; i < sizeof (rc_map)/sizeof (rc_map[0]); ++i) { 686 if (rc_map[i].errnum == errnum) { 687 return (rc_map[i].status32); 688 } 689 } 690 691 return (smb_errno2status(errnum)); 692 } 693