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