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)) {
 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 }