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	"@(#)smb_write.c	1.10	08/08/08 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 static int smb_write_common(smb_request_t *, smb_rw_param_t *);
40 static int smb_write_truncate(smb_request_t *, smb_rw_param_t *);
41 
42 
43 /*
44  * Write count bytes at the specified offset in a file.  The offset is
45  * limited to 32-bits.  If the count is zero, the file is truncated to
46  * the length specified by the offset.
47  *
48  * The response count indicates the actual number of bytes written, which
49  * will equal the requested count on success.  If request and response
50  * counts differ but there is no error, the client will assume that the
51  * server encountered a resource issue.
52  */
53 smb_sdrc_t
54 smb_pre_write(smb_request_t *sr)
55 {
56 	smb_rw_param_t *param;
57 	uint32_t off;
58 	int rc;
59 
60 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
61 	sr->arg.rw = param;
62 	param->rw_magic = SMB_RW_MAGIC;
63 
64 	rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &param->rw_count, &off);
65 
66 	param->rw_offset = (uint64_t)off;
67 	param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
68 
69 	DTRACE_SMB_2(op__Write__start, smb_request_t *, sr,
70 	    smb_rw_param_t *, param);
71 
72 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
73 }
74 
75 void
76 smb_post_write(smb_request_t *sr)
77 {
78 	DTRACE_SMB_2(op__Write__done, smb_request_t *, sr,
79 	    smb_rw_param_t *, sr->arg.rw);
80 
81 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
82 }
83 
84 smb_sdrc_t
85 smb_com_write(smb_request_t *sr)
86 {
87 	smb_rw_param_t *param = sr->arg.rw;
88 	int rc;
89 
90 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
91 	if (sr->fid_ofile == NULL) {
92 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
93 		return (SDRC_ERROR);
94 	}
95 
96 	if (param->rw_count == 0) {
97 		rc = smb_write_truncate(sr, param);
98 	} else {
99 		rc = smbsr_decode_data(sr, "D", &param->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 		    &param->rw_count, &off, &param->rw_last_write);
147 	} else {
148 		rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid,
149 		    &param->rw_count, &off, &param->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 	if (param->rw_count == 0) {
182 		rc = smb_write_truncate(sr, param);
183 	} else {
184 		/*
185 		 * There may be a bug here: should this be "3.#B"?
186 		 */
187 		rc = smbsr_decode_data(sr, ".#B", param->rw_count,
188 		    &param->rw_vdb);
189 
190 		if ((rc != 0) || (param->rw_vdb.len != param->rw_count)) {
191 			smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
192 			    ERRDOS, ERROR_INVALID_PARAMETER);
193 			return (SDRC_ERROR);
194 		}
195 
196 		param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
197 
198 		rc = smb_write_common(sr, param);
199 	}
200 
201 	if (rc != 0) {
202 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
203 			smbsr_errno(sr, rc);
204 		return (SDRC_ERROR);
205 	}
206 
207 	smb_ofile_close(sr->fid_ofile, param->rw_last_write);
208 
209 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1, param->rw_count, 0);
210 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
211 }
212 
213 /*
214  * Write count bytes to a file at the specified offset and then unlock
215  * them.  Write behind is safe because the client should have the range
216  * locked and this request is allowed to extend the file - note that
217  * offset is limited to 32-bits.
218  *
219  * Spec advice: it is an error for count to be zero.  For compatibility,
220  * we take no action and return success.
221  *
222  * The SmbLockAndRead/SmbWriteAndUnlock sub-dialect is only valid on disk
223  * files.  Reject any attempt to use it on other shares.
224  *
225  * The response count indicates the actual number of bytes written, which
226  * will equal the requested count on success.  If request and response
227  * counts differ but there is no error, the client will assume that the
228  * server encountered a resource issue.
229  */
230 smb_sdrc_t
231 smb_pre_write_and_unlock(smb_request_t *sr)
232 {
233 	smb_rw_param_t *param;
234 	uint32_t off;
235 	uint16_t remcnt;
236 	int rc;
237 
238 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
239 	sr->arg.rw = param;
240 	param->rw_magic = SMB_RW_MAGIC;
241 
242 	rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, &param->rw_count, &off,
243 	    &remcnt);
244 
245 	param->rw_offset = (uint64_t)off;
246 
247 	DTRACE_SMB_2(op__WriteAndUnlock__start, smb_request_t *, sr,
248 	    smb_rw_param_t *, param);
249 
250 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
251 }
252 
253 void
254 smb_post_write_and_unlock(smb_request_t *sr)
255 {
256 	DTRACE_SMB_2(op__WriteAndUnlock__done, smb_request_t *, sr,
257 	    smb_rw_param_t *, sr->arg.rw);
258 
259 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
260 }
261 
262 smb_sdrc_t
263 smb_com_write_and_unlock(smb_request_t *sr)
264 {
265 	smb_rw_param_t *param = sr->arg.rw;
266 	uint32_t status;
267 	int rc = 0;
268 
269 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
270 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
271 		return (SDRC_ERROR);
272 	}
273 
274 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
275 	if (sr->fid_ofile == NULL) {
276 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
277 		return (SDRC_ERROR);
278 	}
279 
280 	if (param->rw_count == 0) {
281 		rc = smbsr_encode_result(sr, 1, 0, "bww", 1, 0, 0);
282 		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
283 	}
284 
285 	rc = smbsr_decode_data(sr, "D", &param->rw_vdb);
286 
287 	if ((rc != 0) || (param->rw_count != param->rw_vdb.len)) {
288 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
289 		    ERRDOS, ERROR_INVALID_PARAMETER);
290 		return (SDRC_ERROR);
291 	}
292 
293 	param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
294 
295 	if ((rc = smb_write_common(sr, param)) != 0) {
296 		if (sr->smb_error.status != NT_STATUS_FILE_LOCK_CONFLICT)
297 			smbsr_errno(sr, rc);
298 		return (SDRC_ERROR);
299 	}
300 
301 	status = smb_unlock_range(sr, sr->fid_ofile->f_node, param->rw_offset,
302 	    (uint64_t)param->rw_count);
303 	if (status != NT_STATUS_SUCCESS) {
304 		smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
305 		    ERRDOS, ERRnotlocked);
306 		return (SDRC_ERROR);
307 	}
308 
309 	rc = smbsr_encode_result(sr, 1, 0, "bww", 1, param->rw_count, 0);
310 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
311 }
312 
313 /*
314  * Write bytes to a file (SMB Core).  This request was extended in
315  * LM 0.12 to support 64-bit offsets, indicated by sending a wct of
316  * 14, instead of 12, and including additional offset information.
317  *
318  * A ByteCount of 0 does not truncate the file - use SMB_COM_WRITE
319  * to truncate a file.  A zero length merely transfers zero bytes.
320  *
321  * If bit 0 of WriteMode is set, Fid must refer to a disk file and
322  * the data must be on stable storage before responding.
323  */
324 smb_sdrc_t
325 smb_pre_write_andx(smb_request_t *sr)
326 {
327 	smb_rw_param_t *param;
328 	uint32_t off_low;
329 	uint32_t off_high;
330 	uint16_t remcnt;
331 	int rc;
332 
333 	param = kmem_zalloc(sizeof (smb_rw_param_t), KM_SLEEP);
334 	sr->arg.rw = param;
335 	param->rw_magic = SMB_RW_MAGIC;
336 
337 	if (sr->smb_wct == 14) {
338 		rc = smbsr_decode_vwv(sr, "4.wl4.ww2.wwl", &sr->smb_fid,
339 		    &off_low, &param->rw_mode, &remcnt, &param->rw_count,
340 		    &param->rw_dsoff, &off_high);
341 
342 		param->rw_dsoff -= 63;
343 		param->rw_offset = ((uint64_t)off_high << 32) | off_low;
344 	} else {
345 		rc = smbsr_decode_vwv(sr, "4.wl4.ww2.ww", &sr->smb_fid,
346 		    &off_low, &param->rw_mode, &remcnt, &param->rw_count,
347 		    &param->rw_dsoff);
348 
349 		param->rw_offset = (uint64_t)off_low;
350 		param->rw_dsoff -= 59;
351 	}
352 
353 	DTRACE_SMB_2(op__WriteX__start, smb_request_t *, sr,
354 	    smb_rw_param_t *, param);
355 
356 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
357 }
358 
359 void
360 smb_post_write_andx(smb_request_t *sr)
361 {
362 	DTRACE_SMB_2(op__WriteX__done, smb_request_t *, sr,
363 	    smb_rw_param_t *, sr->arg.rw);
364 
365 	kmem_free(sr->arg.rw, sizeof (smb_rw_param_t));
366 }
367 
368 smb_sdrc_t
369 smb_com_write_andx(smb_request_t *sr)
370 {
371 	smb_rw_param_t *param = sr->arg.rw;
372 	int rc;
373 
374 	ASSERT(param);
375 	ASSERT(param->rw_magic == SMB_RW_MAGIC);
376 
377 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
378 	if (sr->fid_ofile == NULL) {
379 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
380 		return (SDRC_ERROR);
381 	}
382 
383 	if (SMB_WRMODE_IS_STABLE(param->rw_mode) &&
384 	    STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
385 		smbsr_error(sr, 0, ERRSRV, ERRaccess);
386 		return (SDRC_ERROR);
387 	}
388 
389 	rc = smbsr_decode_data(sr, "#.#B", param->rw_dsoff, param->rw_count,
390 	    &param->rw_vdb);
391 	if ((rc != 0) || (param->rw_vdb.len != param->rw_count)) {
392 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
393 		    ERRDOS, ERROR_INVALID_PARAMETER);
394 		return (SDRC_ERROR);
395 	}
396 
397 	param->rw_vdb.uio.uio_loffset = (offset_t)param->rw_offset;
398 
399 	if (param->rw_count != 0) {
400 		if ((rc = smb_write_common(sr, param)) != 0) {
401 			if (sr->smb_error.status !=
402 			    NT_STATUS_FILE_LOCK_CONFLICT)
403 				smbsr_errno(sr, rc);
404 			return (SDRC_ERROR);
405 		}
406 	}
407 
408 	rc = smbsr_encode_result(sr, 6, 0, "bb1.ww6.w",
409 	    6, sr->andx_com, 15, param->rw_count, 0);
410 
411 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
412 }
413 
414 /*
415  * Common function for writing files or IPC/MSRPC named pipes.
416  *
417  * Returns errno values.
418  */
419 static int
420 smb_write_common(smb_request_t *sr, smb_rw_param_t *param)
421 {
422 	struct smb_ofile *ofile = sr->fid_ofile;
423 	smb_node_t *node;
424 	int stability = 0;
425 	uint32_t lcount;
426 	int rc = 0;
427 
428 	switch (sr->tid_tree->t_res_type & STYPE_MASK) {
429 	case STYPE_DISKTREE:
430 		node = ofile->f_node;
431 
432 		if (node->attr.sa_vattr.va_type != VDIR) {
433 			rc = smb_lock_range_access(sr, node, param->rw_offset,
434 			    param->rw_count, B_TRUE);
435 			if (rc != NT_STATUS_SUCCESS) {
436 				smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
437 				    ERRDOS, ERROR_LOCK_VIOLATION);
438 				return (EACCES);
439 			}
440 		}
441 
442 		if (SMB_WRMODE_IS_STABLE(param->rw_mode) ||
443 		    (node->flags & NODE_FLAGS_WRITE_THROUGH)) {
444 			stability = FSYNC;
445 		}
446 
447 		rc = smb_fsop_write(sr, sr->user_cr, node,
448 		    &param->rw_vdb.uio, &lcount, &node->attr, stability);
449 
450 		if (rc)
451 			return (rc);
452 
453 		node->flags |= NODE_FLAGS_SYNCATIME;
454 
455 		if (node->flags & NODE_FLAGS_SET_SIZE) {
456 			if ((param->rw_offset + lcount) >= node->n_size) {
457 				node->flags &= ~NODE_FLAGS_SET_SIZE;
458 				node->n_size = param->rw_offset + lcount;
459 			}
460 		}
461 
462 		param->rw_count = (uint16_t)lcount;
463 		break;
464 
465 	case STYPE_IPC:
466 		param->rw_count = (uint16_t)param->rw_vdb.uio.uio_resid;
467 
468 		if ((rc = smb_opipe_write(sr, &param->rw_vdb.uio)) != 0)
469 			param->rw_count = 0;
470 		break;
471 
472 	default:
473 		rc = EACCES;
474 		break;
475 	}
476 
477 	if (rc != 0)
478 		return (rc);
479 
480 	mutex_enter(&ofile->f_mutex);
481 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
482 	mutex_exit(&ofile->f_mutex);
483 	return (rc);
484 }
485 
486 /*
487  * Truncate a disk file to the specified offset.
488  * Typically, w_count will be zero here.
489  *
490  * Returns errno values.
491  */
492 static int
493 smb_write_truncate(smb_request_t *sr, smb_rw_param_t *param)
494 {
495 	struct smb_ofile *ofile = sr->fid_ofile;
496 	smb_node_t *node = ofile->f_node;
497 	boolean_t append_only = B_FALSE;
498 	uint32_t status;
499 	int rc;
500 
501 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0)
502 		return (0);
503 
504 	status = smb_ofile_access(sr->fid_ofile, sr->user_cr, FILE_WRITE_DATA);
505 	if (status != NT_STATUS_SUCCESS) {
506 		status = smb_ofile_access(sr->fid_ofile, sr->user_cr,
507 		    FILE_APPEND_DATA);
508 		if (status != NT_STATUS_SUCCESS)
509 			return (EACCES);
510 		else
511 			append_only = B_TRUE;
512 	}
513 
514 	smb_rwx_xenter(&node->n_lock);
515 
516 	if (append_only && (param->rw_offset < node->n_size)) {
517 		smb_rwx_xexit(&node->n_lock);
518 		return (EACCES);
519 	}
520 
521 	if (node->attr.sa_vattr.va_type != VDIR) {
522 		status = smb_lock_range_access(sr, node, param->rw_offset,
523 		    param->rw_count, B_TRUE);
524 		if (status != NT_STATUS_SUCCESS) {
525 			smb_rwx_xexit(&node->n_lock);
526 			smbsr_error(sr, NT_STATUS_FILE_LOCK_CONFLICT,
527 			    ERRDOS, ERROR_LOCK_VIOLATION);
528 			return (EACCES);
529 		}
530 	}
531 
532 	node->flags |= NODE_FLAGS_SET_SIZE;
533 	node->n_size = param->rw_offset;
534 
535 	smb_rwx_xexit(&node->n_lock);
536 
537 	if ((rc = smb_set_file_size(sr, node)) != 0)
538 		return (rc);
539 
540 	mutex_enter(&ofile->f_mutex);
541 	ofile->f_seek_pos = param->rw_offset + param->rw_count;
542 	mutex_exit(&ofile->f_mutex);
543 	return (0);
544 }
545 
546 /*
547  * Set the file size using the value in the node. The file will only be
548  * updated if NODE_FLAGS_SET_SIZE is set.  It is safe to pass a null node
549  * pointer, we just return success.
550  *
551  * The node attributes are refreshed here from the file system. So any
552  * attributes that are affected by file size changes, i.e. the mtime,
553  * will be current.
554  *
555  * Note that smb_write_andx cannot be used to reduce the file size so,
556  * if this is required, smb_write is called with a count of zero and
557  * the appropriate file length in offset. The file should be resized
558  * to the length specified by the offset.
559  */
560 int
561 smb_set_file_size(smb_request_t *sr, smb_node_t *node)
562 {
563 	smb_attr_t new_attr;
564 
565 	if (node == NULL)
566 		return (0);
567 
568 	if ((node->flags & NODE_FLAGS_SET_SIZE) == 0)
569 		return (0);
570 
571 	node->flags &= ~NODE_FLAGS_SET_SIZE;
572 
573 	bzero(&new_attr, sizeof (new_attr));
574 	new_attr.sa_vattr.va_size = node->n_size;
575 	new_attr.sa_mask = SMB_AT_SIZE;
576 
577 	return (smb_fsop_setattr(sr, sr->user_cr, node, &new_attr,
578 	    &node->attr));
579 }
580