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