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