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 /*
  23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
  25  */
  26 
  27 #include <sys/sdt.h>
  28 #include <smbsrv/smb_kproto.h>
  29 #include <smbsrv/smb_fsops.h>
  30 #include <smbsrv/netbios.h>
  31 
  32 
  33 static int smb_write_truncate(smb_request_t *, smb_rw_param_t *);
  34 
  35 
  36 /*
  37  * Write count bytes at the specified offset in a file.  The offset is
  38  * limited to 32-bits.  If the count is zero, the file is truncated to
  39  * the length specified by the offset.
  40  *
  41  * The response count indicates the actual number of bytes written, which
  42  * will equal the requested count on success.  If request and response
  43  * counts differ but there is no error, the client will assume that the
  44  * server encountered a resource issue.
  45  */
  46 smb_sdrc_t
  47 smb_pre_write(smb_request_t *sr)
  48 {
  49         smb_rw_param_t *param;
  50         uint32_t off;
  51         uint16_t count;
  52         int rc;
  53 
  54         param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
  55         sr->arg.rw = param;
  56         param->rw_magic = SMB_RW_MAGIC;
  57 
  58         rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &count, &off);
  59 
  60         param->rw_count = (uint32_t)count;
  61         param->rw_offset = (uint64_t)off;
  62         param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
  63 
  64         DTRACE_SMB_2(op__Write__start, smb_request_t *, sr,
  65             smb_rw_param_t *, param);
  66 
  67         return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
  68 }
  69 
  70 void
  71 smb_post_write(smb_request_t *sr)
  72 {
  73         DTRACE_SMB_2(op__Write__done, smb_request_t *, sr,
  74             smb_rw_param_t *, sr->arg.rw);
  75 
  76         kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
  77 }
  78 
  79 smb_sdrc_t
  80 smb_com_write(smb_request_t *sr)
  81 {
  82         smb_rw_param_t *param = sr->arg.rw;
  83         int rc;
  84 
  85         smbsr_lookup_file(sr);
  86         if (sr->fid_ofile == NULL) {
  87                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
  88                 return (SDRC_ERROR);
  89         }
  90 
  91         sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
  92 
  93         if (param->rw_count == 0) {
  94                 rc = smb_write_truncate(sr, param);
  95         } else {
  96                 rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
  97 
  98                 if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
  99                         smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
 100                             ERRDOS, ERROR_INVALID_PARAMETER);
 101                         return (SDRC_ERROR);
 102                 }
 103 
 104                 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
 105 
 106                 rc = smb_common_write(sr, param);
 107         }
 108 
 109         if (rc != 0) {
 110                 if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
 111                         smbsr_errno(sr, rc);
 112                 return (SDRC_ERROR);
 113         }
 114 
 115         rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
 116             (uint16_t)param->rw_count, 0);
 117         return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
 118 }
 119 
 120 /*
 121  * Write count bytes to a file and then close the file.  This function
 122  * can only be used to write to 32-bit offsets and the client must set
 123  * WordCount (6 or 12) correctly in order to locate the data to be
 124  * written.  If an error occurs on the write, the file should still be
 125  * closed.  If Count is 0, the file is truncated (or extended) to offset.
 126  *
 127  * If the last_write time is non-zero, last_write should be used to set
 128  * the mtime.  Otherwise the file system stamps the mtime.  Failure to
 129  * set mtime should not result in an error response.
 130  */
 131 smb_sdrc_t
 132 smb_pre_write_and_close(smb_request_t *sr)
 133 {
 134         smb_rw_param_t *param;
 135         uint32_t off;
 136         uint16_t count;
 137         int rc;
 138 
 139         param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
 140         sr->arg.rw = param;
 141         param->rw_magic = SMB_RW_MAGIC;
 142 
 143         if (sr->smb_wct == 12) {
 144                 rc = smbsr_decode_vwv(sr, "wwll12.", &sr->smb_fid,
 145                     &count, &off, &param->rw_last_write);
 146         } else {
 147                 rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid,
 148                     &count, &off, &param->rw_last_write);
 149         }
 150 
 151         param->rw_count = (uint32_t)count;
 152         param->rw_offset = (uint64_t)off;
 153 
 154         DTRACE_SMB_2(op__WriteAndClose__start, smb_request_t *, sr,
 155             smb_rw_param_t *, param);
 156 
 157         return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
 158 }
 159 
 160 void
 161 smb_post_write_and_close(smb_request_t *sr)
 162 {
 163         DTRACE_SMB_2(op__WriteAndClose__done, smb_request_t *, sr,
 164             smb_rw_param_t *, sr->arg.rw);
 165 
 166         kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
 167 }
 168 
 169 smb_sdrc_t
 170 smb_com_write_and_close(smb_request_t *sr)
 171 {
 172         smb_rw_param_t *param = sr->arg.rw;
 173         uint16_t count;
 174         int rc = 0;
 175 
 176         smbsr_lookup_file(sr);
 177         if (sr->fid_ofile == NULL) {
 178                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
 179                 return (SDRC_ERROR);
 180         }
 181 
 182         sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
 183 
 184         if (param->rw_count == 0) {
 185                 rc = smb_write_truncate(sr, param);
 186         } else {
 187                 /*
 188                  * There may be a bug here: should this be "3.#B"?
 189                  */
 190                 rc = smbsr_decode_data(sr, ".#B", param->rw_count,
 191                     &param->rw_vdb);
 192 
 193                 if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
 194                         smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
 195                             ERRDOS, ERROR_INVALID_PARAMETER);
 196                         return (SDRC_ERROR);
 197                 }
 198 
 199                 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
 200 
 201                 rc = smb_common_write(sr, param);
 202         }
 203 
 204         if (rc != 0) {
 205                 if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
 206                         smbsr_errno(sr, rc);
 207                 return (SDRC_ERROR);
 208         }
 209 
 210         smb_ofile_close(sr->fid_ofile, param->rw_last_write);
 211 
 212         count = (uint16_t)param->rw_count;
 213         rc = smbsr_encode_result(sr, 1, 0, "bww", 1, count, 0);
 214         return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
 215 }
 216 
 217 /*
 218  * Write count bytes to a file at the specified offset and then unlock
 219  * them.  Write behind is safe because the client should have the range
 220  * locked and this request is allowed to extend the file - note that
 221  * offset is limited to 32-bits.
 222  *
 223  * Spec advice: it is an error for count to be zero.  For compatibility,
 224  * we take no action and return success.
 225  *
 226  * The SmbLockAndRead/SmbWriteAndUnlock sub-dialect is only valid on disk
 227  * files.  Reject any attempt to use it on other shares.
 228  *
 229  * The response count indicates the actual number of bytes written, which
 230  * will equal the requested count on success.  If request and response
 231  * counts differ but there is no error, the client will assume that the
 232  * server encountered a resource issue.
 233  */
 234 smb_sdrc_t
 235 smb_pre_write_and_unlock(smb_request_t *sr)
 236 {
 237         smb_rw_param_t *param;
 238         uint32_t off;
 239         uint16_t count;
 240         uint16_t remcnt;
 241         int rc;
 242 
 243         param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
 244         sr->arg.rw = param;
 245         param->rw_magic = SMB_RW_MAGIC;
 246 
 247         rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, &count, &off, &remcnt);
 248 
 249         param->rw_count = (uint32_t)count;
 250         param->rw_offset = (uint64_t)off;
 251 
 252         DTRACE_SMB_2(op__WriteAndUnlock__start, smb_request_t *, sr,
 253             smb_rw_param_t *, param);
 254 
 255         return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
 256 }
 257 
 258 void
 259 smb_post_write_and_unlock(smb_request_t *sr)
 260 {
 261         DTRACE_SMB_2(op__WriteAndUnlock__done, smb_request_t *, sr,
 262             smb_rw_param_t *, sr->arg.rw);
 263 
 264         kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
 265 }
 266 
 267 smb_sdrc_t
 268 smb_com_write_and_unlock(smb_request_t *sr)
 269 {
 270         smb_rw_param_t *param = sr->arg.rw;
 271         uint32_t lk_pid;
 272         uint32_t status;
 273         int rc = 0;
 274 
 275         if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
 276                 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
 277                 return (SDRC_ERROR);
 278         }
 279 
 280         smbsr_lookup_file(sr);
 281         if (sr->fid_ofile == NULL) {
 282                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
 283                 return (SDRC_ERROR);
 284         }
 285 
 286         sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
 287 
 288         if (param->rw_count == 0) {
 289                 rc = smbsr_encode_result(sr, 1, 0, "bww", 1, 0, 0);
 290                 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
 291         }
 292 
 293 
 294         rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
 295 
 296         if ((rc != 0) || (param->rw_count != param->rw_vdb.vdb_len)) {
 297                 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
 298                     ERRDOS, ERROR_INVALID_PARAMETER);
 299                 return (SDRC_ERROR);
 300         }
 301 
 302         param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
 303 
 304         if ((rc = smb_common_write(sr, param)) != 0) {
 305                 if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
 306                         smbsr_errno(sr, rc);
 307                 return (SDRC_ERROR);
 308         }
 309 
 310 
 311         /* Note: SMB1 locking uses 16-bit PIDs. */
 312         lk_pid = sr->smb_pid & 0xFFFF;
 313 
 314         status = smb_unlock_range(sr, param->rw_offset,
 315             (uint64_t)param->rw_count, lk_pid);
 316         if (status != NT_STATUS_SUCCESS) {
 317                 smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
 318                     ERRDOS, ERROR_NOT_LOCKED);
 319                 return (SDRC_ERROR);
 320         }
 321 
 322         rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
 323             (uint16_t)param->rw_count, 0);
 324         return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
 325 }
 326 
 327 /*
 328  * Write bytes to a file (SMB Core).  This request was extended in
 329  * LM 0.12 to support 64-bit offsets, indicated by sending a wct of
 330  * 14, instead of 12, and including additional offset information.
 331  *
 332  * A ByteCount of 0 does not truncate the file - use SMB_COM_WRITE
 333  * to truncate a file.  A zero length merely transfers zero bytes.
 334  *
 335  * If bit 0 of WriteMode is set, Fid must refer to a disk file and
 336  * the data must be on stable storage before responding.
 337  *
 338  * MS-SMB 3.3.5.8 update to LM 0.12 4.2.5:
 339  * If CAP_LARGE_WRITEX is set, the byte count may be larger than the
 340  * negotiated buffer size and the server is expected to write the
 341  * number of bytes specified.
 342  */
 343 smb_sdrc_t
 344 smb_pre_write_andx(smb_request_t *sr)
 345 {
 346         smb_rw_param_t *param;
 347         uint32_t off_low;
 348         uint32_t off_high;
 349         uint16_t datalen_low;
 350         uint16_t datalen_high;
 351         uint16_t remcnt;
 352         int rc;
 353 
 354         param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
 355         sr->arg.rw = param;
 356         param->rw_magic = SMB_RW_MAGIC;
 357 
 358         if (sr->smb_wct == 14) {
 359                 rc = smbsr_decode_vwv(sr, "4.wl4.wwwwwl", &sr->smb_fid,
 360                     &off_low, &param->rw_mode, &remcnt, &datalen_high,
 361                     &datalen_low, &param->rw_dsoff, &off_high);
 362 
 363                 if (param->rw_dsoff >= 63)
 364                         param->rw_dsoff -= 63;
 365                 param->rw_offset = ((uint64_t)off_high << 32) | off_low;
 366         } else if (sr->smb_wct == 12) {
 367                 rc = smbsr_decode_vwv(sr, "4.wl4.wwwww", &sr->smb_fid,
 368                     &off_low, &param->rw_mode, &remcnt, &datalen_high,
 369                     &datalen_low, &param->rw_dsoff);
 370 
 371                 if (param->rw_dsoff >= 59)
 372                         param->rw_dsoff -= 59;
 373                 param->rw_offset = (uint64_t)off_low;
 374                 /* off_high not present */
 375         } else {
 376                 rc = -1;
 377         }
 378 
 379         param->rw_count = (uint32_t)datalen_low;
 380 
 381         /*
 382          * Work-around a Win7 bug, where it fails to set the
 383          * CAP_LARGE_WRITEX flag during session setup.  Assume
 384          * a large write if the data remaining is >= 64k.
 385          */
 386         if ((sr->session->capabilities & CAP_LARGE_WRITEX) != 0 ||
 387             (sr->smb_data.max_bytes > (sr->smb_data.chain_offset + 0xFFFF)))
 388                 param->rw_count |= ((uint32_t)datalen_high << 16);
 389 
 390         DTRACE_SMB_2(op__WriteX__start, smb_request_t *, sr,
 391             smb_rw_param_t *, param);
 392 
 393         return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
 394 }
 395 
 396 void
 397 smb_post_write_andx(smb_request_t *sr)
 398 {
 399         DTRACE_SMB_2(op__WriteX__done, smb_request_t *, sr,
 400             smb_rw_param_t *, sr->arg.rw);
 401 
 402         kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
 403 }
 404 
 405 smb_sdrc_t
 406 smb_com_write_andx(smb_request_t *sr)
 407 {
 408         smb_rw_param_t *param = sr->arg.rw;
 409         uint16_t count_high;
 410         uint16_t count_low;
 411         int rc;
 412 
 413         ASSERT(param);
 414         ASSERT(param->rw_magic == SMB_RW_MAGIC);
 415 
 416         smbsr_lookup_file(sr);
 417         if (sr->fid_ofile == NULL) {
 418                 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
 419                 return (SDRC_ERROR);
 420         }
 421 
 422         sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
 423 
 424         if (SMB_WRMODE_IS_STABLE(param->rw_mode) &&
 425             STYPE_ISIPC(sr->tid_tree->t_res_type)) {
 426                 smbsr_error(sr, 0, ERRSRV, ERRaccess);
 427                 return (SDRC_ERROR);
 428         }
 429 
 430         rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count,
 431             &param->rw_vdb);
 432 
 433         if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
 434                 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
 435                     ERRDOS, ERROR_INVALID_PARAMETER);
 436                 return (SDRC_ERROR);
 437         }
 438 
 439         param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
 440 
 441         if (param->rw_count != 0) {
 442                 if ((rc = smb_common_write(sr, param)) != 0) {
 443                         if (sr->smb_error.status !=
 444                             NT_STATUS_FILE_LOCK_CONFLICT)
 445                                 smbsr_errno(sr, rc);
 446                         return (SDRC_ERROR);
 447                 }
 448         }
 449 
 450         count_low = param->rw_count & 0xFFFF;
 451         count_high = (param->rw_count >> 16) & 0xFF;
 452 
 453         rc = smbsr_encode_result(sr, 6, 0, "bb1.wwwwww",
 454             6, sr->andx_com, 15, count_low, 0, count_high, 0, 0);
 455 
 456         return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
 457 }
 458 
 459 /*
 460  * Common function for writing files or IPC/MSRPC named pipes.
 461  *
 462  * Returns errno values.
 463  */
 464 int
 465 smb_common_write(smb_request_t *sr, smb_rw_param_t *param)
 466 {
 467         smb_ofile_t *ofile = sr->fid_ofile;
 468         smb_node_t *node;
 469         int stability = 0;
 470         uint32_t lcount;
 471         int rc = 0;
 472 
 473         switch (sr->tid_tree->t_res_type & STYPE_MASK) {
 474         case STYPE_DISKTREE:
 475         case STYPE_PRINTQ:
 476                 node = ofile->f_node;
 477 
 478                 if (!smb_node_is_dir(node)) {
 479                         rc = smb_lock_range_access(sr, node, param->rw_offset,
 480                             param->rw_count, B_TRUE);
 481                         if (rc != NT_STATUS_SUCCESS) {
 482                                 smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
 483                                     ERRDOS, ERROR_LOCK_VIOLATION);
 484                                 return (EACCES);
 485                         }
 486                 }
 487 
 488                 if (SMB_WRMODE_IS_STABLE(param->rw_mode) ||
 489                     (node->flags & NODE_FLAGS_WRITE_THROUGH)) {
 490                         stability = FSYNC;
 491                 }
 492 
 493                 rc = smb_fsop_write(sr, sr->user_cr, node, ofile,
 494                     &param->rw_vdb.vdb_uio, &lcount, stability);
 495 
 496                 if (rc)
 497                         return (rc);
 498 
 499                 /*
 500                  * Used to have code here to set mtime.
 501                  * We have just done a write, so we know
 502                  * the file system will update mtime.
 503                  * No need to do it again here.
 504                  *
 505                  * However, keep track of the fact that
 506                  * we have written data via this handle.
 507                  */
 508                 ofile->f_written = B_TRUE;
 509 
 510                 if (!smb_node_is_dir(node))
 511                         smb_oplock_break_levelII(node);
 512 
 513                 param->rw_count = lcount;
 514                 break;
 515 
 516         case STYPE_IPC:
 517                 param->rw_count = param->rw_vdb.vdb_uio.uio_resid;
 518 
 519                 if ((rc = smb_opipe_write(sr, &param->rw_vdb.vdb_uio)) != 0)
 520                         param->rw_count = 0;
 521                 break;
 522 
 523         default:
 524                 rc = EACCES;
 525                 break;
 526         }
 527 
 528         if (rc != 0)
 529                 return (rc);
 530 
 531         mutex_enter(&ofile->f_mutex);
 532         ofile->f_seek_pos = param->rw_offset + param->rw_count;
 533         mutex_exit(&ofile->f_mutex);
 534         return (rc);
 535 }
 536 
 537 /*
 538  * Truncate a disk file to the specified offset.
 539  * Typically, w_count will be zero here.
 540  *
 541  * Note that smb_write_andx cannot be used to reduce the file size so,
 542  * if this is required, smb_write is called with a count of zero and
 543  * the appropriate file length in offset. The file should be resized
 544  * to the length specified by the offset.
 545  *
 546  * Returns errno values.
 547  */
 548 static int
 549 smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param)
 550 {
 551         smb_ofile_t *ofile = sr->fid_ofile;
 552         smb_node_t *node = ofile->f_node;
 553         smb_attr_t attr;
 554         uint32_t status;
 555         int rc;
 556 
 557         if (STYPE_ISIPC(sr->tid_tree->t_res_type))
 558                 return (0);
 559 
 560         mutex_enter(&node->n_mutex);
 561         if (!smb_node_is_dir(node)) {
 562                 status = smb_lock_range_access(sr, node, param->rw_offset,
 563                     param->rw_count, B_TRUE);
 564                 if (status != NT_STATUS_SUCCESS) {
 565                         mutex_exit(&node->n_mutex);
 566                         smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
 567                             ERRDOS, ERROR_LOCK_VIOLATION);
 568                         return (EACCES);
 569                 }
 570         }
 571         mutex_exit(&node->n_mutex);
 572 
 573         bzero(&attr, sizeof (smb_attr_t));
 574         attr.sa_mask = SMB_AT_SIZE;
 575         attr.sa_vattr.va_size = param->rw_offset;
 576         rc = smb_node_setattr(sr, node, sr->user_cr, ofile, &attr);
 577         if (rc != 0)
 578                 return (rc);
 579 
 580         mutex_enter(&ofile->f_mutex);
 581         ofile->f_seek_pos = param->rw_offset + param->rw_count;
 582         mutex_exit(&ofile->f_mutex);
 583         return (0);
 584 }