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