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", ¶m->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, ¶m->rw_last_write); 144 } else { 145 rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid, 146 &count, &off, ¶m->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 ¶m->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", ¶m->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, ¶m->rw_mode, &datalen, 346 ¶m->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, ¶m->rw_mode, &datalen, 352 ¶m->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, ¶m->rw_mode, &remcnt, &datalen_high, 415 &datalen_low, ¶m->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, ¶m->rw_mode, &remcnt, &datalen_high, 423 &datalen_low, ¶m->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 ¶m->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 ¶m->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, ¶m->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 }