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