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