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_low; 382 383 if (sr->session->capabilities & CAP_LARGE_WRITEX) 384 param->rw_count |= ((uint32_t)datalen_high << 16); 385 386 DTRACE_SMB_2(op__WriteX__start, smb_request_t *, sr, 387 smb_rw_param_t *, param); 388 389 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 390 } 391 392 void 393 smb_post_write_andx(smb_request_t *sr) 394 { 395 DTRACE_SMB_2(op__WriteX__done, smb_request_t *, sr, 396 smb_rw_param_t *, sr->arg.rw); 397 398 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t)); 399 } 400 401 smb_sdrc_t 402 smb_com_write_andx(smb_request_t *sr) 403 { 404 smb_rw_param_t *param = sr->arg.rw; 405 uint16_t count_high; 406 uint16_t count_low; 407 int rc; 408 409 ASSERT(param); 410 ASSERT(param->rw_magic == SMB_RW_MAGIC); 411 412 smbsr_lookup_file(sr); 413 if (sr->fid_ofile == NULL) { 414 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); 415 return (SDRC_ERROR); 416 } 417 418 sr->user_cr = smb_ofile_getcred(sr->fid_ofile); 419 420 if (SMB_WRMODE_IS_STABLE(param->rw_mode) && 421 STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) { 422 smbsr_error(sr, 0, ERRSRV, ERRaccess); 423 return (SDRC_ERROR); 424 } 425 426 sr->smb_data.max_bytes = SMB_WRITEX_MAX; 427 rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count, 428 ¶m->rw_vdb); 429 430 if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) { 431 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 432 ERRDOS, ERROR_INVALID_PARAMETER); 433 return (SDRC_ERROR); 434 } 435 436 param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset; 437 438 if (param->rw_count != 0) { 439 if ((rc = smb_write_common(sr, param)) != 0) { 440 if (sr->smb_error.status != 441 NT_STATUS_FILE_LOCK_CONFLICT) 442 smbsr_errno(sr, rc); 443 return (SDRC_ERROR); 444 } 445 } 446 447 count_low = param->rw_count & 0xFFFF; 448 count_high = (param->rw_count >> 16) & 0xFF; 449 450 rc = smbsr_encode_result(sr, 6, 0, "bb1.wwwwww", 451 6, sr->andx_com, 15, count_low, 0, count_high, 0, 0); 452 453 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 454 } 455 456 /* 457 * Common function for writing files or IPC/MSRPC named pipes. 458 * 459 * Returns errno values. 460 */ 461 static int 462 smb_write_common(smb_request_t *sr, smb_rw_param_t *param) 463 { 464 smb_ofile_t *ofile = sr->fid_ofile; 465 smb_node_t *node; 466 int stability = 0; 467 uint32_t lcount; 468 int rc = 0; 469 470 switch (sr->tid_tree->t_res_type & STYPE_MASK) { 471 case STYPE_DISKTREE: 472 node = ofile->f_node; 473 474 if (!smb_node_is_dir(node)) { 475 rc = smb_lock_range_access(sr, node, param->rw_offset, 476 param->rw_count, B_TRUE); 477 if (rc != NT_STATUS_SUCCESS) { 478 smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT, 479 ERRDOS, ERROR_LOCK_VIOLATION); 480 return (EACCES); 481 } 482 } 483 484 if (SMB_WRMODE_IS_STABLE(param->rw_mode) || 485 (node->flags & NODE_FLAGS_WRITE_THROUGH)) { 486 stability = FSYNC; 487 } 488 489 rc = smb_fsop_write(sr, sr->user_cr, node, 490 ¶m->rw_vdb.vdb_uio, &lcount, stability); 491 492 if (rc) 493 return (rc); 494 495 smb_ofile_set_write_time_pending(ofile); 496 497 param->rw_count = lcount; 498 break; 499 500 case STYPE_IPC: 501 param->rw_count = param->rw_vdb.vdb_uio.uio_resid; 502 503 if ((rc = smb_opipe_write(sr, ¶m->rw_vdb.vdb_uio)) != 0) 504 param->rw_count = 0; 505 break; 506 507 default: 508 rc = EACCES; 509 break; 510 } 511 512 if (rc != 0) 513 return (rc); 514 515 mutex_enter(&ofile->f_mutex); 516 ofile->f_seek_pos = param->rw_offset + param->rw_count; 517 mutex_exit(&ofile->f_mutex); 518 return (rc); 519 } 520 521 /* 522 * Truncate a disk file to the specified offset. 523 * Typically, w_count will be zero here. 524 * 525 * Note that smb_write_andx cannot be used to reduce the file size so, 526 * if this is required, smb_write is called with a count of zero and 527 * the appropriate file length in offset. The file should be resized 528 * to the length specified by the offset. 529 * 530 * Returns errno values. 531 */ 532 static int 533 smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param) 534 { 535 smb_ofile_t *ofile = sr->fid_ofile; 536 smb_node_t *node = ofile->f_node; 537 smb_attr_t attr; 538 uint32_t status; 539 int rc; 540 541 if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) 542 return (0); 543 544 mutex_enter(&node->n_mutex); 545 if (!smb_node_is_dir(node)) { 546 status = smb_lock_range_access(sr, node, param->rw_offset, 547 param->rw_count, B_TRUE); 548 if (status != NT_STATUS_SUCCESS) { 549 mutex_exit(&node->n_mutex); 550 smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT, 551 ERRDOS, ERROR_LOCK_VIOLATION); 552 return (EACCES); 553 } 554 } 555 mutex_exit(&node->n_mutex); 556 557 bzero(&attr, sizeof (smb_attr_t)); 558 attr.sa_mask = SMB_AT_SIZE; 559 attr.sa_vattr.va_size = param->rw_offset; 560 rc = smb_node_setattr(sr, node, sr->user_cr, ofile, &attr); 561 if (rc != 0) 562 return (rc); 563 564 mutex_enter(&ofile->f_mutex); 565 ofile->f_seek_pos = param->rw_offset + param->rw_count; 566 mutex_exit(&ofile->f_mutex); 567 return (0); 568 } 569