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