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