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