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