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