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 2017 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_START(op__SetInformation, 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_DONE(op__SetInformation, 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_START(op__SetInformation2, 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_DONE(op__SetInformation2, 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                 smbsr_errno(sr, rc);
 297                 return (-1);
 298         }
 299 
 300         bzero(&sinfo, sizeof (sinfo));
 301         sinfo.si_node = node;
 302         if (xa != NULL)
 303                 sinfo.si_data = xa->req_data_mb;
 304         status = smb_set_fileinfo(sr, &sinfo, infolev);
 305         if (status != 0) {
 306                 smbsr_error(sr, status, 0, 0);
 307                 rc = -1;
 308         }
 309 
 310         smb_node_release(node);
 311         return (rc);
 312 }
 313 
 314 /*
 315  * smb_set_fileinfo
 316  *
 317  * For compatibility with windows servers, SMB_FILE_LINK_INFORMATION
 318  * is handled by returning NT_STATUS_NOT_SUPPORTED.
 319  */
 320 static uint32_t
 321 smb_set_fileinfo(smb_request_t *sr, smb_setinfo_t *sinfo, int infolev)
 322 {
 323         uint32_t status;
 324 
 325         switch (infolev) {
 326         case SMB_SET_INFORMATION:
 327                 status = smb_set_information(sr, sinfo);
 328                 break;
 329 
 330         case SMB_SET_INFORMATION2:
 331                 status = smb_set_information2(sr, sinfo);
 332                 break;
 333 
 334         case SMB_INFO_STANDARD:
 335                 status = smb_set_standard_info(sr, sinfo);
 336                 break;
 337 
 338         case SMB_INFO_SET_EAS:
 339                 status = NT_STATUS_EAS_NOT_SUPPORTED;
 340                 break;
 341 
 342         case SMB_SET_FILE_BASIC_INFO:
 343         case SMB_FILE_BASIC_INFORMATION:
 344                 status = smb_set_basic_info(sr, sinfo);
 345                 break;
 346 
 347         case SMB_SET_FILE_DISPOSITION_INFO:
 348         case SMB_FILE_DISPOSITION_INFORMATION:
 349                 status = smb_set_disposition_info(sr, sinfo);
 350                 break;
 351 
 352         case SMB_SET_FILE_END_OF_FILE_INFO:
 353         case SMB_FILE_END_OF_FILE_INFORMATION:
 354                 status = smb_set_eof_info(sr, sinfo);
 355                 break;
 356 
 357         case SMB_SET_FILE_ALLOCATION_INFO:
 358         case SMB_FILE_ALLOCATION_INFORMATION:
 359                 status = smb_set_alloc_info(sr, sinfo);
 360                 break;
 361 
 362         case SMB_FILE_RENAME_INFORMATION:
 363                 status = smb_set_rename_info(sr, sinfo);
 364                 break;
 365 
 366         case SMB_FILE_LINK_INFORMATION:
 367                 status = NT_STATUS_NOT_SUPPORTED;
 368                 break;
 369 
 370         default:
 371                 status = NT_STATUS_INVALID_INFO_CLASS;
 372                 break;
 373         }
 374 
 375         return (status);
 376 }
 377 
 378 /*
 379  * smb_set_information
 380  *
 381  * It is not valid to set FILE_ATTRIBUTE_DIRECTORY if the
 382  * target is not a directory.
 383  *
 384  * For compatibility with Windows Servers, if the specified
 385  * attributes have ONLY FILE_ATTRIBUTE_NORMAL set do NOT change
 386  * the file's attributes.
 387  */
 388 static uint32_t
 389 smb_set_information(smb_request_t *sr, smb_setinfo_t *sinfo)
 390 {
 391         smb_attr_t attr;
 392         smb_node_t *node = sinfo->si_node;
 393         uint32_t status = 0;
 394         uint32_t mtime;
 395         uint16_t attributes;
 396         int rc;
 397 
 398         if (smbsr_decode_vwv(sr, "wl10.", &attributes, &mtime) != 0)
 399                 return (NT_STATUS_INFO_LENGTH_MISMATCH);
 400 
 401         if ((attributes & FILE_ATTRIBUTE_DIRECTORY) &&
 402             (!smb_node_is_dir(node))) {
 403                 return (NT_STATUS_INVALID_PARAMETER);
 404         }
 405 
 406         bzero(&attr, sizeof (smb_attr_t));
 407         if (attributes != FILE_ATTRIBUTE_NORMAL) {
 408                 attr.sa_dosattr = attributes;
 409                 attr.sa_mask |= SMB_AT_DOSATTR;
 410         }
 411 
 412         if (mtime != 0 && mtime != UINT_MAX) {
 413                 attr.sa_vattr.va_mtime.tv_sec =
 414                     smb_time_local_to_gmt(sr, mtime);
 415                 attr.sa_mask |= SMB_AT_MTIME;
 416         }
 417 
 418         rc = smb_node_setattr(sr, node, sr->user_cr, NULL, &attr);
 419         if (rc != 0)
 420                 status = smb_errno2status(rc);
 421 
 422         return (status);
 423 }
 424 
 425 /*
 426  * smb_set_information2
 427  */
 428 static uint32_t
 429 smb_set_information2(smb_request_t *sr, smb_setinfo_t *sinfo)
 430 {
 431         smb_attr_t attr;
 432         uint32_t crtime, atime, mtime;
 433         uint32_t status = 0;
 434         int rc;
 435 
 436         if (smbsr_decode_vwv(sr, "yyy", &crtime, &atime, &mtime) != 0)
 437                 return (NT_STATUS_INFO_LENGTH_MISMATCH);
 438 
 439         bzero(&attr, sizeof (smb_attr_t));
 440         if (mtime != 0 && mtime != UINT_MAX) {
 441                 attr.sa_vattr.va_mtime.tv_sec =
 442                     smb_time_local_to_gmt(sr, mtime);
 443                 attr.sa_mask |= SMB_AT_MTIME;
 444         }
 445 
 446         if (crtime != 0 && crtime != UINT_MAX) {
 447                 attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime);
 448                 attr.sa_mask |= SMB_AT_CRTIME;
 449         }
 450 
 451         if (atime != 0 && atime != UINT_MAX) {
 452                 attr.sa_vattr.va_atime.tv_sec =
 453                     smb_time_local_to_gmt(sr, atime);
 454                 attr.sa_mask |= SMB_AT_ATIME;
 455         }
 456 
 457         rc = smb_node_setattr(sr, sinfo->si_node, sr->user_cr,
 458             sr->fid_ofile, &attr);
 459         if (rc != 0)
 460                 status = smb_errno2status(rc);
 461 
 462         return (status);
 463 }
 464 
 465 /*
 466  * smb_set_standard_info
 467  *
 468  * Sets standard file/path information.
 469  */
 470 static uint32_t
 471 smb_set_standard_info(smb_request_t *sr, smb_setinfo_t *sinfo)
 472 {
 473         smb_attr_t attr;
 474         smb_node_t *node = sinfo->si_node;
 475         uint32_t crtime, atime, mtime;
 476         uint32_t status = 0;
 477         int rc;
 478 
 479         if (smb_mbc_decodef(&sinfo->si_data, "yyy",
 480             &crtime, &atime, &mtime) != 0)
 481                 return (NT_STATUS_INFO_LENGTH_MISMATCH);
 482 
 483         bzero(&attr, sizeof (smb_attr_t));
 484         if (mtime != 0 && mtime != (uint32_t)-1) {
 485                 attr.sa_vattr.va_mtime.tv_sec =
 486                     smb_time_local_to_gmt(sr, mtime);
 487                 attr.sa_mask |= SMB_AT_MTIME;
 488         }
 489 
 490         if (crtime != 0 && crtime != (uint32_t)-1) {
 491                 attr.sa_crtime.tv_sec = smb_time_local_to_gmt(sr, crtime);
 492                 attr.sa_mask |= SMB_AT_CRTIME;
 493         }
 494 
 495         if (atime != 0 && atime != (uint32_t)-1) {
 496                 attr.sa_vattr.va_atime.tv_sec =
 497                     smb_time_local_to_gmt(sr, atime);
 498                 attr.sa_mask |= SMB_AT_ATIME;
 499         }
 500 
 501         rc = smb_node_setattr(sr, node, sr->user_cr, sr->fid_ofile, &attr);
 502         if (rc != 0)
 503                 status = smb_errno2status(rc);
 504 
 505         return (status);
 506 }
 507 
 508 /*
 509  * smb_set_rename_info
 510  *
 511  * This call only allows a rename in the same directory, and the
 512  * directory name is not part of the new name provided.
 513  *
 514  * Explicitly specified parameter validation rules:
 515  * - If rootdir is not NULL respond with NT_STATUS_INVALID_PARAMETER.
 516  * - If the filename contains a separator character respond with
 517  *   NT_STATUS_INVALID_PARAMETER.
 518  *
 519  * Oplock break:
 520  * Some Windows servers break BATCH oplocks prior to the rename.
 521  * W2K3 does not. We behave as W2K3; we do not send an oplock break.
 522  */
 523 static uint32_t
 524 smb_set_rename_info(smb_request_t *sr, smb_setinfo_t *sinfo)
 525 {
 526         smb_fqi_t *src_fqi = &sr->arg.dirop.fqi;
 527         smb_fqi_t *dst_fqi = &sr->arg.dirop.dst_fqi;
 528         char *fname;
 529         char *path;
 530         uint8_t flags;
 531         uint32_t rootdir, namelen;
 532         uint32_t status = 0;
 533         int rc;
 534 
 535         rc = smb_mbc_decodef(&sinfo->si_data, "b...ll",
 536             &flags, &rootdir, &namelen);
 537         if (rc == 0) {
 538                 rc = smb_mbc_decodef(&sinfo->si_data, "%#U",
 539                     sr, namelen, &fname);
 540         }
 541         if (rc != 0)
 542                 return (NT_STATUS_INFO_LENGTH_MISMATCH);
 543 
 544         if ((rootdir != 0) || (namelen == 0) || (namelen >= MAXNAMELEN)) {
 545                 return (NT_STATUS_INVALID_PARAMETER);
 546         }
 547 
 548         if (strchr(fname, '\\') != NULL) {
 549                 return (NT_STATUS_NOT_SUPPORTED);
 550         }
 551 
 552         /*
 553          * Construct the full dst. path relative to the share root.
 554          * Allocated path is free'd in smb_request_free.
 555          */
 556         path = smb_srm_zalloc(sr, SMB_MAXPATHLEN);
 557         if (src_fqi->fq_path.pn_pname) {
 558                 /* Got here via: smb_set_by_path */
 559                 (void) snprintf(path, SMB_MAXPATHLEN, "%s\\%s",
 560                     src_fqi->fq_path.pn_pname, fname);
 561         } else {
 562                 /* Got here via: smb_set_by_fid */
 563                 rc = smb_node_getshrpath(sinfo->si_node->n_dnode,
 564                     sr->tid_tree, path, SMB_MAXPATHLEN);
 565                 if (rc != 0) {
 566                         status = smb_errno2status(rc);
 567                         return (status);
 568                 }
 569                 (void) strlcat(path, "\\", SMB_MAXPATHLEN);
 570                 (void) strlcat(path, fname, SMB_MAXPATHLEN);
 571         }
 572 
 573         /*
 574          * The common rename code can slightly optimize a
 575          * rename in the same directory when we set the
 576          * dst_fqi->fq_dnode, dst_fqi->fq_last_comp
 577          */
 578         dst_fqi->fq_dnode = sinfo->si_node->n_dnode;
 579         (void) strlcpy(dst_fqi->fq_last_comp, fname, MAXNAMELEN);
 580 
 581         status = smb_setinfo_rename(sr, sinfo->si_node, path, flags);
 582         return (status);
 583 }