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 }