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