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