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