xref: /titanic_44/usr/src/uts/common/fs/smbsrv/smb_write.c (revision 1fb4a876cbadf3a151ab8a149be6956199e83e22)
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 /*
23  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
25  */
26 
27 #include <sys/sdt.h>
28 #include <smbsrv/smb_kproto.h>
29 #include <smbsrv/smb_fsops.h>
30 #include <smbsrv/netbios.h>
31 
32 
33 static int smb_write_truncate(smb_request_t *, smb_rw_param_t *);
34 
35 
36 /*
37  * Write count bytes at the specified offset in a file.  The offset is
38  * limited to 32-bits.  If the count is zero, the file is truncated to
39  * the length specified by the offset.
40  *
41  * The response count indicates the actual number of bytes written, which
42  * will equal the requested count on success.  If request and response
43  * counts differ but there is no error, the client will assume that the
44  * server encountered a resource issue.
45  */
46 smb_sdrc_t
smb_pre_write(smb_request_t * sr)47 smb_pre_write(smb_request_t *sr)
48 {
49 	smb_rw_param_t *param;
50 	uint32_t off;
51 	uint16_t count;
52 	int rc;
53 
54 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
55 	sr->arg.rw = param;
56 	param->rw_magic = SMB_RW_MAGIC;
57 
58 	rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &count, &off);
59 
60 	param->rw_count = (uint32_t)count;
61 	param->rw_offset = (uint64_t)off;
62 	param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
63 
64 	DTRACE_SMB_2(op__Write__start, smb_request_t *, sr,
65 	    smb_rw_param_t *, param);
66 
67 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
68 }
69 
70 void
smb_post_write(smb_request_t * sr)71 smb_post_write(smb_request_t *sr)
72 {
73 	DTRACE_SMB_2(op__Write__done, smb_request_t *, sr,
74 	    smb_rw_param_t *, sr->arg.rw);
75 
76 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
77 }
78 
79 smb_sdrc_t
smb_com_write(smb_request_t * sr)80 smb_com_write(smb_request_t *sr)
81 {
82 	smb_rw_param_t *param = sr->arg.rw;
83 	int rc;
84 
85 	smbsr_lookup_file(sr);
86 	if (sr->fid_ofile == NULL) {
87 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
88 		return (SDRC_ERROR);
89 	}
90 
91 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
92 
93 	if (param->rw_count == 0) {
94 		rc = smb_write_truncate(sr, param);
95 	} else {
96 		rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
97 
98 		if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
99 			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
100 			    ERRDOS, ERROR_INVALID_PARAMETER);
101 			return (SDRC_ERROR);
102 		}
103 
104 		param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
105 
106 		rc = smb_common_write(sr, param);
107 	}
108 
109 	if (rc != 0) {
110 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
111 			smbsr_errno(sr, rc);
112 		return (SDRC_ERROR);
113 	}
114 
115 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
116 	    (uint16_t)param->rw_count, 0);
117 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
118 }
119 
120 /*
121  * Write count bytes to a file and then close the file.  This function
122  * can only be used to write to 32-bit offsets and the client must set
123  * WordCount (6 or 12) correctly in order to locate the data to be
124  * written.  If an error occurs on the write, the file should still be
125  * closed.  If Count is 0, the file is truncated (or extended) to offset.
126  *
127  * If the last_write time is non-zero, last_write should be used to set
128  * the mtime.  Otherwise the file system stamps the mtime.  Failure to
129  * set mtime should not result in an error response.
130  */
131 smb_sdrc_t
smb_pre_write_and_close(smb_request_t * sr)132 smb_pre_write_and_close(smb_request_t *sr)
133 {
134 	smb_rw_param_t *param;
135 	uint32_t off;
136 	uint16_t count;
137 	int rc;
138 
139 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
140 	sr->arg.rw = param;
141 	param->rw_magic = SMB_RW_MAGIC;
142 
143 	if (sr->smb_wct == 12) {
144 		rc = smbsr_decode_vwv(sr, "wwll12.", &sr->smb_fid,
145 		    &count, &off, &param->rw_last_write);
146 	} else {
147 		rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid,
148 		    &count, &off, &param->rw_last_write);
149 	}
150 
151 	param->rw_count = (uint32_t)count;
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
smb_post_write_and_close(smb_request_t * sr)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
smb_com_write_and_close(smb_request_t * sr)170 smb_com_write_and_close(smb_request_t *sr)
171 {
172 	smb_rw_param_t *param = sr->arg.rw;
173 	uint16_t count;
174 	int rc = 0;
175 
176 	smbsr_lookup_file(sr);
177 	if (sr->fid_ofile == NULL) {
178 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
179 		return (SDRC_ERROR);
180 	}
181 
182 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
183 
184 	if (param->rw_count == 0) {
185 		rc = smb_write_truncate(sr, param);
186 	} else {
187 		/*
188 		 * There may be a bug here: should this be "3.#B"?
189 		 */
190 		rc = smbsr_decode_data(sr, ".#B", param->rw_count,
191 		    &param->rw_vdb);
192 
193 		if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
194 			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
195 			    ERRDOS, ERROR_INVALID_PARAMETER);
196 			return (SDRC_ERROR);
197 		}
198 
199 		param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
200 
201 		rc = smb_common_write(sr, param);
202 	}
203 
204 	if (rc != 0) {
205 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
206 			smbsr_errno(sr, rc);
207 		return (SDRC_ERROR);
208 	}
209 
210 	smb_ofile_close(sr->fid_ofile, param->rw_last_write);
211 
212 	count = (uint16_t)param->rw_count;
213 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1, 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
smb_pre_write_and_unlock(smb_request_t * sr)235 smb_pre_write_and_unlock(smb_request_t *sr)
236 {
237 	smb_rw_param_t *param;
238 	uint32_t off;
239 	uint16_t count;
240 	uint16_t remcnt;
241 	int rc;
242 
243 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
244 	sr->arg.rw = param;
245 	param->rw_magic = SMB_RW_MAGIC;
246 
247 	rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, &count, &off, &remcnt);
248 
249 	param->rw_count = (uint32_t)count;
250 	param->rw_offset = (uint64_t)off;
251 
252 	DTRACE_SMB_2(op__WriteAndUnlock__start, smb_request_t *, sr,
253 	    smb_rw_param_t *, param);
254 
255 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
256 }
257 
258 void
smb_post_write_and_unlock(smb_request_t * sr)259 smb_post_write_and_unlock(smb_request_t *sr)
260 {
261 	DTRACE_SMB_2(op__WriteAndUnlock__done, smb_request_t *, sr,
262 	    smb_rw_param_t *, sr->arg.rw);
263 
264 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
265 }
266 
267 smb_sdrc_t
smb_com_write_and_unlock(smb_request_t * sr)268 smb_com_write_and_unlock(smb_request_t *sr)
269 {
270 	smb_rw_param_t *param = sr->arg.rw;
271 	uint32_t status;
272 	int rc = 0;
273 
274 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
275 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
276 		return (SDRC_ERROR);
277 	}
278 
279 	smbsr_lookup_file(sr);
280 	if (sr->fid_ofile == NULL) {
281 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
282 		return (SDRC_ERROR);
283 	}
284 
285 	sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
286 
287 	if (param->rw_count == 0) {
288 		rc = smbsr_encode_result(sr, 1, 0, "bww", 1, 0, 0);
289 		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
290 	}
291 
292 
293 	rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
294 
295 	if ((rc != 0) || (param->rw_count != param->rw_vdb.vdb_len)) {
296 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
297 		    ERRDOS, ERROR_INVALID_PARAMETER);
298 		return (SDRC_ERROR);
299 	}
300 
301 	param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
302 
303 	if ((rc = smb_common_write(sr, param)) != 0) {
304 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
305 			smbsr_errno(sr, rc);
306 		return (SDRC_ERROR);
307 	}
308 
309 	status = smb_unlock_range(sr, sr->fid_ofile->f_node, param->rw_offset,
310 	    (uint64_t)param->rw_count);
311 	if (status != NT_STATUS_SUCCESS) {
312 		smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
313 		    ERRDOS, ERROR_NOT_LOCKED);
314 		return (SDRC_ERROR);
315 	}
316 
317 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1,
318 	    (uint16_t)param->rw_count, 0);
319 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
320 }
321 
322 /*
323  * Write bytes to a file (SMB Core).  This request was extended in
324  * LM 0.12 to support 64-bit offsets, indicated by sending a wct of
325  * 14, instead of 12, and including additional offset information.
326  *
327  * A ByteCount of 0 does not truncate the file - use SMB_COM_WRITE
328  * to truncate a file.  A zero length merely transfers zero bytes.
329  *
330  * If bit 0 of WriteMode is set, Fid must refer to a disk file and
331  * the data must be on stable storage before responding.
332  *
333  * MS-SMB 3.3.5.8 update to LM 0.12 4.2.5:
334  * If CAP_LARGE_WRITEX is set, the byte count may be larger than the
335  * negotiated buffer size and the server is expected to write the
336  * number of bytes specified.
337  */
338 smb_sdrc_t
smb_pre_write_andx(smb_request_t * sr)339 smb_pre_write_andx(smb_request_t *sr)
340 {
341 	smb_rw_param_t *param;
342 	uint32_t off_low;
343 	uint32_t off_high;
344 	uint16_t datalen_low;
345 	uint16_t datalen_high;
346 	uint16_t remcnt;
347 	int rc;
348 
349 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
350 	sr->arg.rw = param;
351 	param->rw_magic = SMB_RW_MAGIC;
352 
353 	if (sr->smb_wct == 14) {
354 		rc = smbsr_decode_vwv(sr, "4.wl4.wwwwwl", &sr->smb_fid,
355 		    &off_low, &param->rw_mode, &remcnt, &datalen_high,
356 		    &datalen_low, &param->rw_dsoff, &off_high);
357 
358 		if (param->rw_dsoff >= 63)
359 			param->rw_dsoff -= 63;
360 		param->rw_offset = ((uint64_t)off_high << 32) | off_low;
361 	} else if (sr->smb_wct == 12) {
362 		rc = smbsr_decode_vwv(sr, "4.wl4.wwwww", &sr->smb_fid,
363 		    &off_low, &param->rw_mode, &remcnt, &datalen_high,
364 		    &datalen_low, &param->rw_dsoff);
365 
366 		if (param->rw_dsoff >= 59)
367 			param->rw_dsoff -= 59;
368 		param->rw_offset = (uint64_t)off_low;
369 		/* off_high not present */
370 	} else {
371 		rc = -1;
372 	}
373 
374 	param->rw_count = (uint32_t)datalen_low;
375 
376 	/*
377 	 * Work-around a Win7 bug, where it fails to set the
378 	 * CAP_LARGE_WRITEX flag during session setup.  Assume
379 	 * a large write if the data remaining is >= 64k.
380 	 */
381 	if ((sr->session->capabilities & CAP_LARGE_WRITEX) != 0 ||
382 	    (sr->smb_data.max_bytes > (sr->smb_data.chain_offset + 0xFFFF)))
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
smb_post_write_andx(smb_request_t * sr)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
smb_com_write_andx(smb_request_t * sr)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_ISIPC(sr->tid_tree->t_res_type)) {
421 		smbsr_error(sr, 0, ERRSRV, ERRaccess);
422 		return (SDRC_ERROR);
423 	}
424 
425 	rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count,
426 	    &param->rw_vdb);
427 
428 	if ((rc != 0) || (param->rw_vdb.vdb_len != param->rw_count)) {
429 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
430 		    ERRDOS, ERROR_INVALID_PARAMETER);
431 		return (SDRC_ERROR);
432 	}
433 
434 	param->rw_vdb.vdb_uio.uio_loffset = (offset_t)param->rw_offset;
435 
436 	if (param->rw_count != 0) {
437 		if ((rc = smb_common_write(sr, param)) != 0) {
438 			if (sr->smb_error.status !=
439 			    NT_STATUS_FILE_LOCK_CONFLICT)
440 				smbsr_errno(sr, rc);
441 			return (SDRC_ERROR);
442 		}
443 	}
444 
445 	count_low = param->rw_count & 0xFFFF;
446 	count_high = (param->rw_count >> 16) & 0xFF;
447 
448 	rc = smbsr_encode_result(sr, 6, 0, "bb1.wwwwww",
449 	    6, sr->andx_com, 15, count_low, 0, count_high, 0, 0);
450 
451 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
452 }
453 
454 /*
455  * Common function for writing files or IPC/MSRPC named pipes.
456  *
457  * Returns errno values.
458  */
459 int
smb_common_write(smb_request_t * sr,smb_rw_param_t * param)460 smb_common_write(smb_request_t *sr, smb_rw_param_t *param)
461 {
462 	smb_ofile_t *ofile = sr->fid_ofile;
463 	smb_node_t *node;
464 	int stability = 0;
465 	uint32_t lcount;
466 	int rc = 0;
467 
468 	switch (sr->tid_tree->t_res_type & STYPE_MASK) {
469 	case STYPE_DISKTREE:
470 	case STYPE_PRINTQ:
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 		/*
495 		 * Used to have code here to set mtime.
496 		 * We have just done a write, so we know
497 		 * the file system will update mtime.
498 		 * No need to do it again here.
499 		 *
500 		 * However, keep track of the fact that
501 		 * we have written data via this handle.
502 		 */
503 		ofile->f_written = B_TRUE;
504 
505 		if (!smb_node_is_dir(node))
506 			smb_oplock_break_levelII(node);
507 
508 		param->rw_count = lcount;
509 		break;
510 
511 	case STYPE_IPC:
512 		param->rw_count = param->rw_vdb.vdb_uio.uio_resid;
513 
514 		if ((rc = smb_opipe_write(sr, &param->rw_vdb.vdb_uio)) != 0)
515 			param->rw_count = 0;
516 		break;
517 
518 	default:
519 		rc = EACCES;
520 		break;
521 	}
522 
523 	if (rc != 0)
524 		return (rc);
525 
526 	mutex_enter(&ofile->f_mutex);
527 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
528 	mutex_exit(&ofile->f_mutex);
529 	return (rc);
530 }
531 
532 /*
533  * Truncate a disk file to the specified offset.
534  * Typically, w_count will be zero here.
535  *
536  * Note that smb_write_andx cannot be used to reduce the file size so,
537  * if this is required, smb_write is called with a count of zero and
538  * the appropriate file length in offset. The file should be resized
539  * to the length specified by the offset.
540  *
541  * Returns errno values.
542  */
543 static int
smb_write_truncate(smb_request_t * sr,smb_rw_param_t * param)544 smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param)
545 {
546 	smb_ofile_t *ofile = sr->fid_ofile;
547 	smb_node_t *node = ofile->f_node;
548 	smb_attr_t attr;
549 	uint32_t status;
550 	int rc;
551 
552 	if (STYPE_ISIPC(sr->tid_tree->t_res_type))
553 		return (0);
554 
555 	mutex_enter(&node->n_mutex);
556 	if (!smb_node_is_dir(node)) {
557 		status = smb_lock_range_access(sr, node, param->rw_offset,
558 		    param->rw_count, B_TRUE);
559 		if (status != NT_STATUS_SUCCESS) {
560 			mutex_exit(&node->n_mutex);
561 			smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
562 			    ERRDOS, ERROR_LOCK_VIOLATION);
563 			return (EACCES);
564 		}
565 	}
566 	mutex_exit(&node->n_mutex);
567 
568 	bzero(&attr, sizeof (smb_attr_t));
569 	attr.sa_mask = SMB_AT_SIZE;
570 	attr.sa_vattr.va_size = param->rw_offset;
571 	rc = smb_node_setattr(sr, node, sr->user_cr, ofile, &attr);
572 	if (rc != 0)
573 		return (rc);
574 
575 	mutex_enter(&ofile->f_mutex);
576 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
577 	mutex_exit(&ofile->f_mutex);
578 	return (0);
579 }
580