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