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