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 #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 static int smb_write_common(smb_request_t *, smb_rw_param_t *); 38 static int smb_write_truncate(smb_request_t *, smb_rw_param_t *); 39 40 41 /* 42 * Write count bytes at the specified offset in a file. The offset is 43 * limited to 32-bits. If the count is zero, the file is truncated to 44 * the length specified by the offset. 45 * 46 * The response count indicates the actual number of bytes written, which 47 * will equal the requested count on success. If request and response 48 * counts differ but there is no error, the client will assume that the 49 * server encountered a resource issue. 50 */ 51 smb_sdrc_t 52 smb_pre_write(smb_request_t *sr) 53 { 54 smb_rw_param_t *param; 55 uint32_t off; 56 int rc; 57 58 param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP); 59 sr->arg.rw = param; 60 param->rw_magic = SMB_RW_MAGIC; 61 62 rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, ¶m->rw_count, &off); 63 64 param->rw_offset = (uint64_t)off; 65 param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset; 66 67 DTRACE_SMB_2(op__Write__start, smb_request_t *, sr, 68 smb_rw_param_t *, param); 69 70 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 71 } 72 73 void 74 smb_post_write(smb_request_t *sr) 75 { 76 DTRACE_SMB_2(op__Write__done, smb_request_t *, sr, 77 smb_rw_param_t *, sr->arg.rw); 78 79 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t)); 80 } 81 82 smb_sdrc_t 83 smb_com_write(smb_request_t *sr) 84 { 85 smb_rw_param_t *param = sr->arg.rw; 86 int rc; 87 88 sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); 89 if (sr->fid_ofile == NULL) { 90 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); 91 return (SDRC_ERROR); 92 } 93 94 sr->user_cr = smb_ofile_getcred(sr->fid_ofile); 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 sr->user_cr = smb_ofile_getcred(sr->fid_ofile); 182 183 if (param->rw_count == 0) { 184 rc = smb_write_truncate(sr, param); 185 } else { 186 /* 187 * There may be a bug here: should this be "3.#B"? 188 */ 189 rc = smbsr_decode_data(sr, ".#B", param->rw_count, 190 ¶m->rw_vdb); 191 192 if ((rc != 0) || (param->rw_vdb.len != param->rw_count)) { 193 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 194 ERRDOS, ERROR_INVALID_PARAMETER); 195 return (SDRC_ERROR); 196 } 197 198 param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset; 199 200 rc = smb_write_common(sr, param); 201 } 202 203 if (rc != 0) { 204 if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT) 205 smbsr_errno(sr, rc); 206 return (SDRC_ERROR); 207 } 208 209 smb_ofile_close(sr->fid_ofile, param->rw_last_write); 210 211 rc = smbsr_encode_result(sr, 1, 0, "bww", 1, param->rw_count, 0); 212 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 213 } 214 215 /* 216 * Write count bytes to a file at the specified offset and then unlock 217 * them. Write behind is safe because the client should have the range 218 * locked and this request is allowed to extend the file - note that 219 * offset is limited to 32-bits. 220 * 221 * Spec advice: it is an error for count to be zero. For compatibility, 222 * we take no action and return success. 223 * 224 * The SmbLockAndRead/SmbWriteAndUnlock sub-dialect is only valid on disk 225 * files. Reject any attempt to use it on other shares. 226 * 227 * The response count indicates the actual number of bytes written, which 228 * will equal the requested count on success. If request and response 229 * counts differ but there is no error, the client will assume that the 230 * server encountered a resource issue. 231 */ 232 smb_sdrc_t 233 smb_pre_write_and_unlock(smb_request_t *sr) 234 { 235 smb_rw_param_t *param; 236 uint32_t off; 237 uint16_t remcnt; 238 int rc; 239 240 param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP); 241 sr->arg.rw = param; 242 param->rw_magic = SMB_RW_MAGIC; 243 244 rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, ¶m->rw_count, &off, 245 &remcnt); 246 247 param->rw_offset = (uint64_t)off; 248 249 DTRACE_SMB_2(op__WriteAndUnlock__start, smb_request_t *, sr, 250 smb_rw_param_t *, param); 251 252 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 253 } 254 255 void 256 smb_post_write_and_unlock(smb_request_t *sr) 257 { 258 DTRACE_SMB_2(op__WriteAndUnlock__done, smb_request_t *, sr, 259 smb_rw_param_t *, sr->arg.rw); 260 261 kmem_free(sr->arg.rw, sizeof (smb_rw_param_t)); 262 } 263 264 smb_sdrc_t 265 smb_com_write_and_unlock(smb_request_t *sr) 266 { 267 smb_rw_param_t *param = sr->arg.rw; 268 uint32_t status; 269 int rc = 0; 270 271 if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) { 272 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess); 273 return (SDRC_ERROR); 274 } 275 276 sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid); 277 if (sr->fid_ofile == NULL) { 278 smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid); 279 return (SDRC_ERROR); 280 } 281 282 sr->user_cr = smb_ofile_getcred(sr->fid_ofile); 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 sr->user_cr = smb_ofile_getcred(sr->fid_ofile); 388 389 if (SMB_WRMODE_IS_STABLE(param->rw_mode) && 390 STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) { 391 smbsr_error(sr, 0, ERRSRV, ERRaccess); 392 return (SDRC_ERROR); 393 } 394 395 rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count, 396 ¶m->rw_vdb); 397 if ((rc != 0) || (param->rw_vdb.len != param->rw_count)) { 398 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 399 ERRDOS, ERROR_INVALID_PARAMETER); 400 return (SDRC_ERROR); 401 } 402 403 param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset; 404 405 if (param->rw_count != 0) { 406 if ((rc = smb_write_common(sr, param)) != 0) { 407 if (sr->smb_error.status != 408 NT_STATUS_FILE_LOCK_CONFLICT) 409 smbsr_errno(sr, rc); 410 return (SDRC_ERROR); 411 } 412 } 413 414 rc = smbsr_encode_result(sr, 6, 0, "bb1.ww6.w", 415 6, sr->andx_com, 15, param->rw_count, 0); 416 417 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 418 } 419 420 /* 421 * Common function for writing files or IPC/MSRPC named pipes. 422 * 423 * Returns errno values. 424 */ 425 static int 426 smb_write_common(smb_request_t *sr, smb_rw_param_t *param) 427 { 428 struct smb_ofile *ofile = sr->fid_ofile; 429 smb_node_t *node; 430 int stability = 0; 431 uint32_t lcount; 432 int rc = 0; 433 434 switch (sr->tid_tree->t_res_type & STYPE_MASK) { 435 case STYPE_DISKTREE: 436 node = ofile->f_node; 437 438 if (node->attr.sa_vattr.va_type != VDIR) { 439 rc = smb_lock_range_access(sr, node, param->rw_offset, 440 param->rw_count, B_TRUE); 441 if (rc != NT_STATUS_SUCCESS) { 442 smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT, 443 ERRDOS, ERROR_LOCK_VIOLATION); 444 return (EACCES); 445 } 446 } 447 448 if (SMB_WRMODE_IS_STABLE(param->rw_mode) || 449 (node->flags & NODE_FLAGS_WRITE_THROUGH)) { 450 stability = FSYNC; 451 } 452 453 rc = smb_fsop_write(sr, sr->user_cr, node, 454 ¶m->rw_vdb.uio, &lcount, &node->attr, stability); 455 456 if (rc) 457 return (rc); 458 459 node->flags |= NODE_FLAGS_SYNCATIME; 460 461 if (node->flags & NODE_FLAGS_SET_SIZE) { 462 if ((param->rw_offset + lcount) >= node->n_size) { 463 node->flags &= ~NODE_FLAGS_SET_SIZE; 464 node->n_size = param->rw_offset + lcount; 465 } 466 } 467 468 param->rw_count = (uint16_t)lcount; 469 break; 470 471 case STYPE_IPC: 472 param->rw_count = (uint16_t)param->rw_vdb.uio.uio_resid; 473 474 if ((rc = smb_opipe_write(sr, ¶m->rw_vdb.uio)) != 0) 475 param->rw_count = 0; 476 break; 477 478 default: 479 rc = EACCES; 480 break; 481 } 482 483 if (rc != 0) 484 return (rc); 485 486 mutex_enter(&ofile->f_mutex); 487 ofile->f_seek_pos = param->rw_offset + param->rw_count; 488 mutex_exit(&ofile->f_mutex); 489 return (rc); 490 } 491 492 /* 493 * Truncate a disk file to the specified offset. 494 * Typically, w_count will be zero here. 495 * 496 * Returns errno values. 497 */ 498 static int 499 smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param) 500 { 501 struct smb_ofile *ofile = sr->fid_ofile; 502 smb_node_t *node = ofile->f_node; 503 boolean_t append_only = B_FALSE; 504 uint32_t status; 505 int rc; 506 507 if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) 508 return (0); 509 510 status = smb_ofile_access(sr->fid_ofile, sr->user_cr, FILE_WRITE_DATA); 511 if (status != NT_STATUS_SUCCESS) { 512 status = smb_ofile_access(sr->fid_ofile, sr->user_cr, 513 FILE_APPEND_DATA); 514 if (status != NT_STATUS_SUCCESS) 515 return (EACCES); 516 else 517 append_only = B_TRUE; 518 } 519 520 smb_rwx_xenter(&node->n_lock); 521 522 if (append_only && (param->rw_offset < node->n_size)) { 523 smb_rwx_xexit(&node->n_lock); 524 return (EACCES); 525 } 526 527 if (node->attr.sa_vattr.va_type != VDIR) { 528 status = smb_lock_range_access(sr, node, param->rw_offset, 529 param->rw_count, B_TRUE); 530 if (status != NT_STATUS_SUCCESS) { 531 smb_rwx_xexit(&node->n_lock); 532 smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT, 533 ERRDOS, ERROR_LOCK_VIOLATION); 534 return (EACCES); 535 } 536 } 537 538 node->flags |= NODE_FLAGS_SET_SIZE; 539 node->n_size = param->rw_offset; 540 541 smb_rwx_xexit(&node->n_lock); 542 543 if ((rc = smb_set_file_size(sr, node)) != 0) 544 return (rc); 545 546 mutex_enter(&ofile->f_mutex); 547 ofile->f_seek_pos = param->rw_offset + param->rw_count; 548 mutex_exit(&ofile->f_mutex); 549 return (0); 550 } 551 552 /* 553 * Set the file size using the value in the node. The file will only be 554 * updated if NODE_FLAGS_SET_SIZE is set. It is safe to pass a null node 555 * pointer, we just return success. 556 * 557 * The node attributes are refreshed here from the file system. So any 558 * attributes that are affected by file size changes, i.e. the mtime, 559 * will be current. 560 * 561 * Note that smb_write_andx cannot be used to reduce the file size so, 562 * if this is required, smb_write is called with a count of zero and 563 * the appropriate file length in offset. The file should be resized 564 * to the length specified by the offset. 565 */ 566 int 567 smb_set_file_size(smb_request_t *sr, smb_node_t *node) 568 { 569 smb_attr_t new_attr; 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 bzero(&new_attr, sizeof (new_attr)); 580 new_attr.sa_vattr.va_size = node->n_size; 581 new_attr.sa_mask = SMB_AT_SIZE; 582 583 return (smb_fsop_setattr(sr, sr->user_cr, node, &new_attr, 584 &node->attr)); 585 } 586