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 /*
  27  * Trans2 Set File/Path Information Levels:
  28  *
  29  * SMB_INFO_STANDARD
  30  * SMB_INFO_SET_EAS
  31  * SMB_SET_FILE_BASIC_INFO
  32  * SMB_SET_FILE_DISPOSITION_INFO
  33  * SMB_SET_FILE_END_OF_FILE_INFO
  34  * SMB_SET_FILE_ALLOCATION_INFO
  35  *
  36  * Handled Passthrough levels:
  37  * SMB_FILE_BASIC_INFORMATION
  38  * SMB_FILE_RENAME_INFORMATION
  39  * SMB_FILE_LINK_INFORMATION
  40  * SMB_FILE_DISPOSITION_INFORMATION
  41  * SMB_FILE_END_OF_FILE_INFORMATION
  42  * SMB_FILE_ALLOCATION_INFORMATION
  43  *
  44  * Internal levels representing non trans2 requests
  45  * SMB_SET_INFORMATION
  46  * SMB_SET_INFORMATION2
  47  */
  48 
  49 /*
  50  * Setting timestamps:
  51  * The behaviour when the time field is set to -1 is not documented
  52  * but is generally treated like 0, meaning that that server file
  53  * system assigned value need not be changed.
  54  *
  55  * Setting attributes - FILE_ATTRIBUTE_NORMAL:
  56  * SMB_SET_INFORMATION -
  57  * - if the specified attributes have ONLY FILE_ATTRIBUTE_NORMAL set
  58  *   do NOT change the file's attributes.
  59  * SMB_SET_BASIC_INFO -
  60  * - if the specified attributes have ONLY FILE_ATTRIBUTE_NORMAL set
  61  *   clear (0) the file's attributes.
  62  * - if the specified attributes are 0 do NOT change the file's
  63  *   attributes.
  64  */
  65 
  66 #include <smbsrv/smb_kproto.h>
  67 #include <smbsrv/smb_fsops.h>
  68 
  69 static int smb_set_by_fid(smb_request_t *, smb_xa_t *, uint16_t);
  70 static int smb_set_by_path(smb_request_t *, smb_xa_t *, uint16_t);
  71 
  72 /*
  73  * These functions all return and NT status code.
  74  */
  75 static uint32_t smb_set_fileinfo(smb_request_t *, smb_setinfo_t *, int);
  76 static uint32_t smb_set_information(smb_request_t *, smb_setinfo_t *);
  77 static uint32_t smb_set_information2(smb_request_t *, smb_setinfo_t *);
  78 static uint32_t smb_set_standard_info(smb_request_t *, smb_setinfo_t *);
  79 static uint32_t smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *);
  80 
  81 /*
  82  * smb_com_trans2_set_file_information
  83  */
  84 smb_sdrc_t
  85 smb_com_trans2_set_file_information(smb_request_t *sr, smb_xa_t *xa)
  86 {
  87         uint16_t infolev;
  88 
  89         if (smb_mbc_decodef(&xa->req_param_mb, "ww",
  90             &sr->smb_fid, &infolev) != 0)
  91                 return (SDRC_ERROR);
  92 
  93         if (smb_set_by_fid(sr, xa, infolev) != 0)
  94                 return (SDRC_ERROR);
  95 
  96         return (SDRC_SUCCESS);
  97 }
  98 
  99 /*
 100  * smb_com_trans2_set_path_information
 101  */
 102 smb_sdrc_t
 103 smb_com_trans2_set_path_information(smb_request_t *sr, smb_xa_t *xa)
 104 {
 105         uint16_t        infolev;
 106         smb_fqi_t       *fqi = &sr->arg.dirop.fqi;
 107 
 108         if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
 109                 smbsr_error(sr, NT_STATUS_INVALID_DEVICE_REQUEST,
 110                     ERRDOS, ERROR_INVALID_FUNCTION);
 111                 return (SDRC_ERROR);
 112         }
 113 
 114         if (smb_mbc_decodef(&xa->req_param_mb, "%w4.u",
 115             sr, &infolev, &fqi->fq_path.pn_path) != 0)
 116                 return (SDRC_ERROR);
 117 
 118         if (smb_set_by_path(sr, xa, infolev) != 0)
 119                 return (SDRC_ERROR);
 120 
 121         return (SDRC_SUCCESS);
 122 }
 123 
 124 /*
 125  * smb_com_set_information (aka setattr)
 126  */
 127 smb_sdrc_t
 128 smb_pre_set_information(smb_request_t *sr)
 129 {
 130         DTRACE_SMB_1(op__SetInformation__start, smb_request_t *, sr);
 131         return (SDRC_SUCCESS);
 132 }
 133 
 134 void
 135 smb_post_set_information(smb_request_t *sr)
 136 {
 137         DTRACE_SMB_1(op__SetInformation__done, smb_request_t *, sr);
 138 }
 139 
 140 smb_sdrc_t
 141 smb_com_set_information(smb_request_t *sr)
 142 {
 143         uint16_t        infolev = SMB_SET_INFORMATION;
 144         smb_fqi_t       *fqi = &sr->arg.dirop.fqi;
 145 
 146         if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
 147                 smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
 148                     ERRDOS, ERROR_ACCESS_DENIED);
 149                 return (SDRC_ERROR);
 150         }
 151 
 152         if (smbsr_decode_data(sr, "%S", sr, &fqi->fq_path.pn_path) != 0)
 153                 return (SDRC_ERROR);
 154 
 155         if (smb_set_by_path(sr, NULL, infolev) != 0)
 156                 return (SDRC_ERROR);
 157 
 158         if (smbsr_encode_empty_result(sr) != 0)
 159                 return (SDRC_ERROR);
 160 
 161         return (SDRC_SUCCESS);
 162 }
 163 
 164 /*
 165  * smb_com_set_information2 (aka setattre)
 166  */
 167 smb_sdrc_t
 168 smb_pre_set_information2(smb_request_t *sr)
 169 {
 170         DTRACE_SMB_1(op__SetInformation2__start, smb_request_t *, sr);
 171         return (SDRC_SUCCESS);
 172 }
 173 
 174 void
 175 smb_post_set_information2(smb_request_t *sr)
 176 {
 177         DTRACE_SMB_1(op__SetInformation2__done, smb_request_t *, sr);
 178 }
 179 
 180 smb_sdrc_t
 181 smb_com_set_information2(smb_request_t *sr)
 182 {
 183         uint16_t infolev = SMB_SET_INFORMATION2;
 184 
 185         if (smbsr_decode_vwv(sr, "w", &sr->smb_fid) != 0)
 186                 return (SDRC_ERROR);
 187 
 188         if (smb_set_by_fid(sr, NULL, infolev) != 0)
 189                 return (SDRC_ERROR);
 190 
 191         if (smbsr_encode_empty_result(sr) != 0)
 192                 return (SDRC_ERROR);
 193 
 194         return (SDRC_SUCCESS);
 195 }
 196 
 197 /*
 198  * smb_set_by_fid
 199  *
 200  * Common code for setting file information by open file id.
 201  * Use the id to identify the node object and invoke smb_set_fileinfo
 202  * for that node.
 203  *
 204  * Setting attributes on a named pipe by id is handled by simply
 205  * returning success.
 206  */
 207 static int
 208 smb_set_by_fid(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
 209 {
 210         smb_setinfo_t sinfo;
 211         uint32_t status;
 212         int rc = 0;
 213 
 214         if (SMB_TREE_IS_READONLY(sr)) {
 215                 smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
 216                     ERRDOS, ERROR_ACCESS_DENIED);
 217                 return (-1);
 218         }
 219 
 220         if (STYPE_ISIPC(sr->tid_tree->t_res_type))
 221                 return (0);
 222 
 223         smbsr_lookup_file(sr);
 224         if (sr->fid_ofile == NULL) {
 225                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
 226                 return (-1);
 227         }
 228 
 229         if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) {
 230                 smbsr_release_file(sr);
 231                 return (0);
 232         }
 233 
 234         sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
 235 
 236         bzero(&sinfo, sizeof (sinfo));
 237         sinfo.si_node = sr->fid_ofile->f_node;
 238         if (xa != NULL)
 239                 sinfo.si_data = xa->req_data_mb;
 240         status = smb_set_fileinfo(sr, &sinfo, infolev);
 241         if (status != 0) {
 242                 smbsr_error(sr, status, 0, 0);
 243                 rc = -1;
 244         }
 245 
 246         smbsr_release_file(sr);
 247         return (rc);
 248 }
 249 
 250 /*
 251  * smb_set_by_path
 252  *
 253  * Common code for setting file information by file name.
 254  * Use the file name to identify the node object and invoke
 255  * smb_set_fileinfo for that node.
 256  *
 257  * Path should be set in sr->arg.dirop.fqi.fq_path prior to
 258  * calling smb_set_by_path.
 259  *
 260  * Setting attributes on a named pipe by name is an error and
 261  * is handled in the calling functions so that they can return
 262  * the appropriate error status code (which differs by caller).
 263  */
 264 static int
 265 smb_set_by_path(smb_request_t *sr, smb_xa_t *xa, uint16_t infolev)
 266 {
 267         int rc;
 268         uint32_t status;
 269         smb_setinfo_t sinfo;
 270         smb_node_t *node, *dnode;
 271         char *name;
 272         smb_pathname_t  *pn;
 273 
 274         if (SMB_TREE_IS_READONLY(sr)) {
 275                 smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
 276                     ERRDOS, ERROR_ACCESS_DENIED);
 277                 return (-1);
 278         }
 279 
 280         pn = &sr->arg.dirop.fqi.fq_path;
 281         smb_pathname_init(sr, pn, pn->pn_path);
 282         if (!smb_pathname_validate(sr, pn))
 283                 return (-1);
 284 
 285         name = kmem_alloc(MAXNAMELEN, KM_SLEEP);
 286         rc = smb_pathname_reduce(sr, sr->user_cr, pn->pn_path,
 287             sr->tid_tree->t_snode, sr->tid_tree->t_snode, &dnode, name);
 288         if (rc == 0) {
 289                 rc = smb_fsop_lookup_name(sr, sr->user_cr, SMB_FOLLOW_LINKS,
 290                     sr->tid_tree->t_snode, dnode, name, &node);
 291                 smb_node_release(dnode);
 292         }
 293         kmem_free(name, MAXNAMELEN);
 294 
 295         if (rc != 0) {
 296                 if (rc == ENOENT) {
 297                         smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
 298                             ERRDOS, ERROR_FILE_NOT_FOUND);
 299                 } else {
 300                         smbsr_errno(sr, rc);
 301                 }
 302                 return (-1);
 303         }
 304 
 305         bzero(&sinfo, sizeof (sinfo));
 306         sinfo.si_node = node;
 307         if (xa != NULL)
 308                 sinfo.si_data = xa->req_data_mb;
 309         status = smb_set_fileinfo(sr, &sinfo, infolev);
 310         if (status != 0) {
 311                 smbsr_error(sr, status, 0, 0);
 312                 rc = -1;
 313         }
 314 
 315         smb_node_release(node);
 316         return (rc);
 317 }
 318 
 319 /*
 320  * smb_set_fileinfo
 321  *
 322  * For compatibility with windows servers, SMB_FILE_LINK_INFORMATION
 323  * is handled by returning NT_STATUS_NOT_SUPPORTED.
 324  */
 325 static uint32_t
 326 smb_set_fileinfo(smb_request_t *sr, smb_setinfo_t *sinfo, int infolev)
 327 {
 328         uint32_t status;
 329 
 330         switch (infolev) {
 331         case SMB_SET_INFORMATION:
 332                 status = smb_set_information(sr, sinfo);
 333                 break;
 334 
 335         case SMB_SET_INFORMATION2:
 336                 status = smb_set_information2(sr, sinfo);
 337                 break;
 338 
 339         case SMB_INFO_STANDARD:
 340                 status = smb_set_standard_info(sr, sinfo);
 341                 break;
 342 
 343         case SMB_INFO_SET_EAS:
 344                 /* EAs not supported */
 345                 status = 0;
 346                 break;
 347 
 348         case SMB_SET_FILE_BASIC_INFO:
 349         case SMB_FILE_BASIC_INFORMATION:
 350                 status = smb_set_basic_info(sr, sinfo);
 351                 break;
 352 
 353         case SMB_SET_FILE_DISPOSITION_INFO:
 354         case SMB_FILE_DISPOSITION_INFORMATION:
 355                 status = smb_set_disposition_info(sr, sinfo);
 356                 break;
 357 
 358         case SMB_SET_FILE_END_OF_FILE_INFO:
 359         case SMB_FILE_END_OF_FILE_INFORMATION:
 360                 status = smb_set_eof_info(sr, sinfo);
 361                 break;
 362 
 363         case SMB_SET_FILE_ALLOCATION_INFO:
 364         case SMB_FILE_ALLOCATION_INFORMATION:
 365                 status = smb_set_alloc_info(sr, sinfo);
 366                 break;
 367 
 368         case SMB_FILE_RENAME_INFORMATION:
 369                 status = smb_set_rename_info(sr, sinfo);
 370                 break;
 371 
 372         case SMB_FILE_LINK_INFORMATION:
 373                 status = NT_STATUS_NOT_SUPPORTED;
 374                 break;
 375 
 376         default:
 377                 status = NT_STATUS_INVALID_INFO_CLASS;
 378                 break;
 379         }
 380 
 381         return (status);
 382 }
 383 
 384 /*
 385  * smb_set_information
 386  *
 387  * It is not valid to set FILE_ATTRIBUTE_DIRECTORY if the
 388  * target is not a directory.
 389  *
 390  * For compatibility with Windows Servers, if the specified
 391  * attributes have ONLY FILE_ATTRIBUTE_NORMAL set do NOT change
 392  * the file's attributes.
 393  */
 394 static uint32_t
 395 smb_set_information(smb_request_t *sr, smb_setinfo_t *sinfo)
 396 {
 397         smb_attr_t attr;
 398         smb_node_t *node = sinfo->si_node;
 399         uint32_t status = 0;
 400         uint32_t mtime;
 401         uint16_t attributes;
 402         int rc;
 403 
 404         if (smbsr_decode_vwv(sr, "wl10.", &attributes, &mtime) != 0)
 405                 return (NT_STATUS_INFO_LENGTH_MISMATCH);
 406 
 407         if ((attributes & FILE_ATTRIBUTE_DIRECTORY) &&
 408             (!smb_node_is_dir(node))) {
 409                 return (NT_STATUS_INVALID_PARAMETER);
 410         }
 411 
 412         bzero(&attr, sizeof (smb_attr_t));
 413         if (attributes != FILE_ATTRIBUTE_NORMAL) {
 414                 attr.sa_dosattr = attributes;
 415                 attr.sa_mask |= SMB_AT_DOSATTR;
 416         }
 417 
 418         if (mtime != 0 && mtime != UINT_MAX) {
 419                 attr.sa_vattr.va_mtime.tv_sec =
 420                     smb_time_local_to_gmt(sr, mtime);
 421                 attr.sa_mask |= SMB_AT_MTIME;
 422         }
 423 
 424         rc = smb_node_setattr(sr, node, sr->user_cr, NULL, &attr);
 425         if (rc != 0)
 426                 status = smb_errno2status(rc);
 427 
 428         return (status);
 429 }
 430 
 431 /*
 432  * smb_set_information2
 433  */
 434 static uint32_t
 435 smb_set_information2(smb_request_t *sr, smb_setinfo_t *sinfo)
 436 {
 437         smb_attr_t attr;
 438         uint32_t crtime, atime, mtime;
 439         uint32_t status = 0;
 440         int rc;
 441 
 442         if (smbsr_decode_vwv(sr, "yyy", &crtime, &atime, &mtime) != 0)
 443                 return (NT_STATUS_INFO_LENGTH_MISMATCH);
 444 
 445         bzero(&attr, sizeof (smb_attr_t));
 446         if (mtime != 0 && mtime != UINT_MAX) {
 447                 attr.sa_vattr.va_mtime.tv_sec =
 448                     smb_time_local_to_gmt(sr, mtime);
 449                 attr.sa_mask |= SMB_AT_MTIME;
 450         }
 451 
 452         if (crtime != 0 && crtime != UINT_MAX) {
 453                 attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime);
 454                 attr.sa_mask |= SMB_AT_CRTIME;
 455         }
 456 
 457         if (atime != 0 && atime != UINT_MAX) {
 458                 attr.sa_vattr.va_atime.tv_sec =
 459                     smb_time_local_to_gmt(sr, atime);
 460                 attr.sa_mask |= SMB_AT_ATIME;
 461         }
 462 
 463         rc = smb_node_setattr(sr, sinfo->si_node, sr->user_cr,
 464             sr->fid_ofile, &attr);
 465         if (rc != 0)
 466                 status = smb_errno2status(rc);
 467 
 468         return (status);
 469 }
 470 
 471 /*
 472  * smb_set_standard_info
 473  *
 474  * Sets standard file/path information.
 475  */
 476 static uint32_t
 477 smb_set_standard_info(smb_request_t *sr, smb_setinfo_t *sinfo)
 478 {
 479         smb_attr_t attr;
 480         smb_node_t *node = sinfo->si_node;
 481         uint32_t crtime, atime, mtime;
 482         uint32_t status = 0;
 483         int rc;
 484 
 485         if (smb_mbc_decodef(&sinfo->si_data, "yyy",
 486             &crtime, &atime, &mtime) != 0)
 487                 return (NT_STATUS_INFO_LENGTH_MISMATCH);
 488 
 489         bzero(&attr, sizeof (smb_attr_t));
 490         if (mtime != 0 && mtime != (uint32_t)-1) {
 491                 attr.sa_vattr.va_mtime.tv_sec =
 492                     smb_time_local_to_gmt(sr, mtime);
 493                 attr.sa_mask |= SMB_AT_MTIME;
 494         }
 495 
 496         if (crtime != 0 && crtime != (uint32_t)-1) {
 497                 attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime);
 498                 attr.sa_mask |= SMB_AT_CRTIME;
 499         }
 500 
 501         if (atime != 0 && atime != (uint32_t)-1) {
 502                 attr.sa_vattr.va_atime.tv_sec =
 503                     smb_time_local_to_gmt(sr, atime);
 504                 attr.sa_mask |= SMB_AT_ATIME;
 505         }
 506 
 507         rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr);
 508         if (rc != 0)
 509                 status = smb_errno2status(rc);
 510 
 511         return (status);
 512 }
 513 
 514 /*
 515  * smb_set_rename_info
 516  *
 517  * This call only allows a rename in the same directory, and the
 518  * directory name is not part of the new name provided.
 519  *
 520  * Explicitly specified parameter validation rules:
 521  * - If rootdir is not NULL respond with NT_STATUS_INVALID_PARAMETER.
 522  * - If the filename contains a separator character respond with
 523  *   NT_STATUS_INVALID_PARAMETER.
 524  *
 525  * Oplock break:
 526  * Some Windows servers break BATCH oplocks prior to the rename.
 527  * W2K3 does not. We behave as W2K3; we do not send an oplock break.
 528  */
 529 static uint32_t
 530 smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *sinfo)
 531 {
 532         smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
 533         smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
 534         char *fname;
 535         char *path;
 536         uint8_t flags;
 537         uint32_t rootdir, namelen;
 538         uint32_t status = 0;
 539         int rc;
 540 
 541         rc = smb_mbc_decodef(&sinfo->si_data, "b...ll",
 542             &flags, &rootdir, &namelen);
 543         if (rc == 0) {
 544                 rc = smb_mbc_decodef(&sinfo->si_data, "%#U",
 545                     sr, namelen, &fname);
 546         }
 547         if (rc != 0)
 548                 return (NT_STATUS_INFO_LENGTH_MISMATCH);
 549 
 550         if ((rootdir != 0) || (namelen == 0) || (namelen >= MAXNAMELEN)) {
 551                 return (NT_STATUS_INVALID_PARAMETER);
 552         }
 553 
 554         if (strchr(fname, '\\') != NULL) {
 555                 return (NT_STATUS_NOT_SUPPORTED);
 556         }
 557 
 558         /*
 559          * Construct the full dst. path relative to the share root.
 560          * Allocated path is free'd in smb_request_free.
 561          */
 562         path = smb_srm_zalloc(sr, SMB_MAXPATHLEN);
 563         if (src_fqi->fq_path.pn_pname) {
 564                 /* Got here via: smb_set_by_path */
 565                 (void) snprintf(path, SMB_MAXPATHLEN, "%s\\%s",
 566                     src_fqi->fq_path.pn_pname, fname);
 567         } else {
 568                 /* Got here via: smb_set_by_fid */
 569                 rc = smb_node_getshrpath(sinfo->si_node->n_dnode,
 570                     sr->tid_tree, path, SMB_MAXPATHLEN);
 571                 if (rc != 0) {
 572                         status = smb_errno2status(rc);
 573                         return (status);
 574                 }
 575                 (void) strlcat(path, "\\", SMB_MAXPATHLEN);
 576                 (void) strlcat(path, fname, SMB_MAXPATHLEN);
 577         }
 578 
 579         /*
 580          * The common rename code can slightly optimize a
 581          * rename in the same directory when we set the
 582          * dst_fqi->fq_dnode, dst_fqi->fq_last_comp
 583          */
 584         dst_fqi->fq_dnode = sinfo->si_node->n_dnode;
 585         (void) strlcpy(dst_fqi->fq_last_comp, fname, MAXNAMELEN);
 586 
 587         status = smb_setinfo_rename(sr, sinfo->si_node, path, flags);
 588         return (status);
 589 }