xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_write.c (revision dc20a3024900c47dd2ee44b9707e6df38f7d62a5)
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 typedef struct smb_write_param {
40 	struct vardata_block w_vdb;
41 	uint64_t w_offset;
42 	uint16_t w_mode;
43 	uint16_t w_count;
44 } smb_write_param_t;
45 
46 
47 int smb_write_common(struct smb_request *sr, smb_write_param_t *param);
48 int smb_write_truncate(struct smb_request *sr, smb_write_param_t *param);
49 int smb_set_file_size(struct smb_request *sr);
50 
51 
52 /*
53  * Write count bytes at the specified offset in a file.  The offset is
54  * limited to 32-bits.  If the count is zero, the file is truncated to
55  * the length specified by the offset.
56  *
57  * The response count indicates the actual number of bytes written, which
58  * will equal the requested count on success.  If request and response
59  * counts differ but there is no error, the client will assume that the
60  * server encountered a resource issue.
61  */
62 int
63 smb_com_write(struct smb_request *sr)
64 {
65 	smb_write_param_t *param;
66 	uint32_t off;
67 	int rc;
68 
69 	param = kmem_zalloc(sizeof (smb_write_param_t), KM_SLEEP);
70 
71 	rc = smbsr_decode_vwv(sr, "wwl", &sr->smb_fid, &param->w_count, &off);
72 	if (rc != 0) {
73 		kmem_free(param, sizeof (smb_write_param_t));
74 		smbsr_decode_error(sr);
75 		/* NOTREACHED */
76 	}
77 
78 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
79 	if (sr->fid_ofile == NULL) {
80 		kmem_free(param, sizeof (smb_write_param_t));
81 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
82 		/* NOTREACHED */
83 	}
84 
85 	param->w_offset = (uint64_t)off;
86 	param->w_vdb.uio.uio_loffset = (offset_t)param->w_offset;
87 
88 	if (param->w_count == 0) {
89 		rc = smb_write_truncate(sr, param);
90 	} else {
91 		rc = smbsr_decode_data(sr, "D", &param->w_vdb);
92 
93 		if ((rc != 0) || (param->w_vdb.len != param->w_count)) {
94 			kmem_free(param, sizeof (smb_write_param_t));
95 			smbsr_decode_error(sr);
96 			/* NOTREACHED */
97 		}
98 
99 		param->w_vdb.uio.uio_loffset = (offset_t)param->w_offset;
100 
101 		rc = smb_write_common(sr, param);
102 	}
103 
104 	if (rc != 0) {
105 		kmem_free(param, sizeof (smb_write_param_t));
106 		smbsr_errno(sr, rc);
107 		/* NOTREACHED */
108 	}
109 
110 	smbsr_encode_result(sr, 1, 0, "bww", 1, param->w_count, 0);
111 	kmem_free(param, sizeof (smb_write_param_t));
112 	return (SDRC_NORMAL_REPLY);
113 }
114 
115 /*
116  * Write count bytes to a file and then close the file.  This function
117  * can only be used to write to 32-bit offsets and the client must set
118  * WordCount (6 or 12) correctly in order to locate the data to be
119  * written.  If an error occurs on the write, the file should still be
120  * closed.  If Count is 0, the file is truncated (or extended) to offset.
121  *
122  * If the last_write time is non-zero, last_write should be used to set
123  * the mtime.  Otherwise the file system stamps the mtime.  Failure to
124  * set mtime should not result in an error response.
125  */
126 int
127 smb_com_write_and_close(struct smb_request *sr)
128 {
129 	smb_write_param_t *param;
130 	uint32_t last_write;
131 	uint32_t off;
132 	int rc = 0;
133 
134 	param = kmem_zalloc(sizeof (smb_write_param_t), KM_SLEEP);
135 
136 	if (sr->smb_wct == 12) {
137 		rc = smbsr_decode_vwv(sr, "wwll12.", &sr->smb_fid,
138 		    &param->w_count, &off, &last_write);
139 	} else {
140 		rc = smbsr_decode_vwv(sr, "wwll", &sr->smb_fid,
141 		    &param->w_count, &off, &last_write);
142 	}
143 
144 	if (rc != 0) {
145 		kmem_free(param, sizeof (smb_write_param_t));
146 		smbsr_decode_error(sr);
147 		/* NOTREACHED */
148 	}
149 
150 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
151 	if (sr->fid_ofile == NULL) {
152 		kmem_free(param, sizeof (smb_write_param_t));
153 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
154 		/* NOTREACHED */
155 	}
156 
157 	param->w_offset = (uint64_t)off;
158 
159 	if (param->w_count == 0) {
160 		rc = smb_write_truncate(sr, param);
161 	} else {
162 		/*
163 		 * There may be a bug here: should this be "3.#B"?
164 		 */
165 		rc = smbsr_decode_data(sr, ".#B", param->w_count,
166 		    &param->w_vdb);
167 
168 		if ((rc != 0) || (param->w_vdb.len != param->w_count)) {
169 			kmem_free(param, sizeof (smb_write_param_t));
170 			smbsr_decode_error(sr);
171 			/* NOTREACHED */
172 		}
173 
174 		param->w_vdb.uio.uio_loffset = (offset_t)param->w_offset;
175 
176 		rc = smb_write_common(sr, param);
177 	}
178 
179 	if (rc != 0) {
180 		kmem_free(param, sizeof (smb_write_param_t));
181 		smbsr_errno(sr, rc);
182 		/* NOTREACHED */
183 	}
184 
185 	if ((rc = smb_common_close(sr, last_write)) != 0) {
186 		kmem_free(param, sizeof (smb_write_param_t));
187 		smbsr_errno(sr, rc);
188 		/* NOTREACHED */
189 	}
190 
191 	smbsr_encode_result(sr, 1, 0, "bww", 1, param->w_count, 0);
192 	kmem_free(param, sizeof (smb_write_param_t));
193 	return (SDRC_NORMAL_REPLY);
194 }
195 
196 /*
197  * Write count bytes to a file at the specified offset and then unlock
198  * them.  Write behind is safe because the client should have the range
199  * locked and this request is allowed to extend the file - note that
200  * offest is limited to 32-bits.  It is an error for count to be zero.
201  *
202  * The SmbLockAndRead/SmbWriteAndUnlock sub-dialect is only valid on disk
203  * files.  Reject any attempt to use it on other shares.
204  *
205  * The response count indicates the actual number of bytes written, which
206  * will equal the requested count on success.  If request and response
207  * counts differ but there is no error, the client will assume that the
208  * server encountered a resource issue.
209  */
210 int
211 smb_com_write_and_unlock(struct smb_request *sr)
212 {
213 	smb_write_param_t *param;
214 	uint32_t off;
215 	uint32_t result;
216 	uint16_t remcnt;
217 	int rc = 0;
218 
219 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
220 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
221 		/* NOTREACHED */
222 	}
223 
224 	param = kmem_zalloc(sizeof (smb_write_param_t), KM_SLEEP);
225 
226 	rc = smbsr_decode_vwv(sr, "wwlw", &sr->smb_fid, &param->w_count, &off,
227 	    &remcnt);
228 	if (rc != 0) {
229 		kmem_free(param, sizeof (smb_write_param_t));
230 		smbsr_decode_error(sr);
231 		/* NOTREACHED */
232 	}
233 
234 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
235 	if (sr->fid_ofile == NULL) {
236 		kmem_free(param, sizeof (smb_write_param_t));
237 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
238 		/* NOTREACHED */
239 	}
240 
241 	if (param->w_count == 0) {
242 		smbsr_decode_error(sr);
243 		/* NOTREACHED */
244 	}
245 
246 	rc = smbsr_decode_data(sr, "D", &param->w_vdb);
247 
248 	if ((rc != 0) || (param->w_count != param->w_vdb.len)) {
249 		kmem_free(param, sizeof (smb_write_param_t));
250 		smbsr_decode_error(sr);
251 		/* NOTREACHED */
252 	}
253 
254 	param->w_offset = (uint64_t)off;
255 	param->w_vdb.uio.uio_loffset = (offset_t)param->w_offset;
256 
257 	if ((rc = smb_write_common(sr, param)) != 0) {
258 		kmem_free(param, sizeof (smb_write_param_t));
259 		smbsr_errno(sr, rc);
260 		/* NOTREACHED */
261 	}
262 
263 	result = smb_unlock_range(sr, sr->fid_ofile->f_node, param->w_offset,
264 	    (uint64_t)param->w_count);
265 	if (result != NT_STATUS_SUCCESS) {
266 		kmem_free(param, sizeof (smb_write_param_t));
267 		smbsr_error(sr, NT_STATUS_RANGE_NOT_LOCKED,
268 		    ERRDOS, ERRnotlocked);
269 		/* NOTREACHED */
270 	}
271 
272 	smbsr_encode_result(sr, 1, 0, "bww", 1, param->w_count, 0);
273 	kmem_free(param, sizeof (smb_write_param_t));
274 	return (SDRC_NORMAL_REPLY);
275 }
276 
277 /*
278  * Write bytes to a file (SMB Core).  This request was extended in
279  * LM 0.12 to support 64-bit offsets, indicated by sending a wct of
280  * 14, instead of 12, and including additional offset information.
281  *
282  * A ByteCount of 0 does not truncate the file - use SMB_COM_WRITE
283  * to truncate a file.  A zero length merely transfers zero bytes.
284  *
285  * If bit 0 of WriteMode is set, Fid must refer to a disk file and
286  * the data must be on stable storage before responding.
287  */
288 int
289 smb_com_write_andx(struct smb_request *sr)
290 {
291 	smb_write_param_t *param;
292 	uint32_t off_low;
293 	uint32_t off_high;
294 	uint16_t data_offset;
295 	uint16_t remcnt;
296 	int rc = 0;
297 
298 	param = kmem_zalloc(sizeof (smb_write_param_t), KM_SLEEP);
299 
300 	if (sr->smb_wct == 14) {
301 		rc = smbsr_decode_vwv(sr, "4.wl4.ww2.wwl", &sr->smb_fid,
302 		    &off_low, &param->w_mode, &remcnt, &param->w_count,
303 		    &data_offset, &off_high);
304 
305 		data_offset -= 63;
306 		param->w_offset = ((uint64_t)off_high << 32) | off_low;
307 	} else {
308 		rc = smbsr_decode_vwv(sr, "4.wl4.ww2.ww", &sr->smb_fid,
309 		    &off_low, &param->w_mode, &remcnt, &param->w_count,
310 		    &data_offset);
311 
312 		param->w_offset = (uint64_t)off_low;
313 		data_offset -= 59;
314 	}
315 
316 	if (rc != 0) {
317 		kmem_free(param, sizeof (smb_write_param_t));
318 		smbsr_decode_error(sr);
319 		/* NOTREACHED */
320 	}
321 
322 	sr->fid_ofile = smb_ofile_lookup_by_fid(sr->tid_tree, sr->smb_fid);
323 	if (sr->fid_ofile == NULL) {
324 		kmem_free(param, sizeof (smb_write_param_t));
325 		smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
326 		/* NOTREACHED */
327 	}
328 
329 	if (SMB_WRMODE_IS_STABLE(param->w_mode) &&
330 	    STYPE_ISDSK(sr->tid_tree->t_res_type) == 0) {
331 		kmem_free(param, sizeof (smb_write_param_t));
332 		smbsr_error(sr, 0, ERRSRV, ERRaccess);
333 		/* NOTREACHED */
334 	}
335 
336 	rc = smbsr_decode_data(sr, "#.#B", data_offset, param->w_count,
337 	    &param->w_vdb);
338 	if ((rc != 0) || (param->w_vdb.len != param->w_count)) {
339 		kmem_free(param, sizeof (smb_write_param_t));
340 		smbsr_decode_error(sr);
341 		/* NOTREACHED */
342 	}
343 
344 	param->w_vdb.uio.uio_loffset = (offset_t)param->w_offset;
345 
346 	if (param->w_count != 0) {
347 		if ((rc = smb_write_common(sr, param)) != 0) {
348 			kmem_free(param, sizeof (smb_write_param_t));
349 			smbsr_errno(sr, rc);
350 			/* NOTREACHED */
351 		}
352 	}
353 
354 	smbsr_encode_result(sr, 6, 0, "bb1.ww6.w",
355 	    6, sr->andx_com, 15, param->w_count, 0);
356 
357 	kmem_free(param, sizeof (smb_write_param_t));
358 	return (SDRC_NORMAL_REPLY);
359 }
360 
361 /*
362  * Common function for writing files or IPC/MSRPC named pipes.
363  *
364  * Returns errno values.
365  */
366 int
367 smb_write_common(struct smb_request *sr, smb_write_param_t *param)
368 {
369 	struct smb_ofile *ofile = sr->fid_ofile;
370 	smb_node_t *node;
371 	uint32_t stability = FSSTAB_UNSTABLE;
372 	uint32_t lcount;
373 	int rc = 0;
374 
375 	switch (sr->tid_tree->t_res_type & STYPE_MASK) {
376 	case STYPE_DISKTREE:
377 		node = ofile->f_node;
378 
379 		if (node->attr.sa_vattr.va_type != VDIR) {
380 			rc = smb_lock_range_access(sr, node, param->w_offset,
381 			    param->w_count, B_TRUE);
382 			if (rc != NT_STATUS_SUCCESS) {
383 				smbsr_error(sr, rc, ERRSRV, ERRaccess);
384 				/* NOTREACHED */
385 			}
386 		}
387 
388 		if (SMB_WRMODE_IS_STABLE(param->w_mode) ||
389 		    (node->flags & NODE_FLAGS_WRITE_THROUGH)) {
390 			stability = FSSTAB_FILE_SYNC;
391 		}
392 
393 		rc = smb_fsop_write(sr, sr->user_cr, node,
394 		    &param->w_vdb.uio, &lcount, &node->attr, &stability);
395 
396 		if (rc)
397 			return (rc);
398 
399 		node->flags |= NODE_FLAGS_SYNCATIME;
400 
401 		if (node->flags & NODE_FLAGS_SET_SIZE) {
402 			if ((param->w_offset + lcount) >= node->n_size) {
403 				node->flags &= ~NODE_FLAGS_SET_SIZE;
404 				node->n_size = param->w_offset + lcount;
405 			}
406 		}
407 
408 		param->w_count = (uint16_t)lcount;
409 		break;
410 
411 	case STYPE_IPC:
412 		param->w_count = (uint16_t)param->w_vdb.uio.uio_resid;
413 
414 		if ((rc = smb_rpc_write(sr, &param->w_vdb.uio)) != 0)
415 			param->w_count = 0;
416 		break;
417 
418 	default:
419 		rc = EACCES;
420 		break;
421 	}
422 
423 	if (rc != 0)
424 		return (rc);
425 
426 	mutex_enter(&ofile->f_mutex);
427 	ofile->f_seek_pos = param->w_offset + param->w_count;
428 	mutex_exit(&ofile->f_mutex);
429 	return (rc);
430 }
431 
432 /*
433  * Truncate a disk file to the specified offset.
434  * Typically, w_count will be zero here.
435  *
436  * Returns errno values.
437  */
438 int
439 smb_write_truncate(struct smb_request *sr, smb_write_param_t *param)
440 {
441 	struct smb_ofile *ofile = sr->fid_ofile;
442 	smb_node_t *node = ofile->f_node;
443 	boolean_t append_only = B_FALSE;
444 	int rc;
445 
446 	if (STYPE_ISDSK(sr->tid_tree->t_res_type) == 0)
447 		return (0);
448 
449 	rc = smb_ofile_access(sr->fid_ofile, sr->user_cr, FILE_WRITE_DATA);
450 	if (rc != NT_STATUS_SUCCESS) {
451 		rc = smb_ofile_access(sr->fid_ofile, sr->user_cr,
452 		    FILE_APPEND_DATA);
453 		if (rc != NT_STATUS_SUCCESS) {
454 			smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
455 			    ERROR_ACCESS_DENIED);
456 			/* NOTREACHED */
457 		} else {
458 			append_only = B_TRUE;
459 		}
460 	}
461 
462 	smb_rwx_xenter(&node->n_lock);
463 
464 	if (append_only && (param->w_offset < node->n_size)) {
465 		smb_rwx_xexit(&node->n_lock);
466 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
467 		    ERRDOS, ERRnoaccess);
468 		/* NOTREACHED */
469 	}
470 
471 	if (node->attr.sa_vattr.va_type != VDIR) {
472 		rc = smb_lock_range_access(sr, node, param->w_offset,
473 		    param->w_count, B_TRUE);
474 		if (rc != NT_STATUS_SUCCESS) {
475 			smb_rwx_xexit(&node->n_lock);
476 			smbsr_error(sr, rc, ERRSRV, ERRaccess);
477 			/* NOTREACHED */
478 		}
479 	}
480 
481 	node->flags |= NODE_FLAGS_SET_SIZE;
482 	node->n_size = param->w_offset;
483 
484 	smb_rwx_xexit(&node->n_lock);
485 
486 	if ((rc = smb_set_file_size(sr)) != 0)
487 		return (rc);
488 
489 	mutex_enter(&ofile->f_mutex);
490 	ofile->f_seek_pos = param->w_offset + param->w_count;
491 	mutex_exit(&ofile->f_mutex);
492 	return (0);
493 }
494 
495 /*
496  * Set the file size using the value in the node. The file will only be
497  * updated if NODE_FLAGS_SET_SIZE is set.  It is safe to pass a null node
498  * pointer, we just return success.
499  *
500  * The node attributes are refreshed here from the file system. So any
501  * attributes that are affected by file size changes, i.e. the mtime,
502  * will be current.
503  *
504  * Note that smb_write_andx cannot be used to reduce the file size so,
505  * if this is required, smb_write is called with a count of zero and
506  * the appropriate file length in offset. The file should be resized
507  * to the length specified by the offset.
508  *
509  * Returns 0 on success. Otherwise returns EACCES.
510  */
511 int
512 smb_set_file_size(struct smb_request *sr)
513 {
514 	struct smb_node *node;
515 	smb_attr_t new_attr;
516 	uint32_t dosattr;
517 
518 	if ((node = sr->fid_ofile->f_node) == 0)
519 		return (0);
520 
521 	if ((node->flags & NODE_FLAGS_SET_SIZE) == 0)
522 		return (0);
523 
524 	node->flags &= ~NODE_FLAGS_SET_SIZE;
525 
526 	dosattr = smb_node_get_dosattr(node);
527 
528 	if (dosattr & SMB_FA_READONLY) {
529 		if (((node->flags & NODE_FLAGS_CREATED) == 0) ||
530 		    (sr->session->s_kid != node->n_orig_session_id))
531 			return (EACCES);
532 	}
533 
534 	bzero(&new_attr, sizeof (new_attr));
535 	new_attr.sa_vattr.va_size = node->n_size;
536 	new_attr.sa_mask = SMB_AT_SIZE;
537 
538 	(void) smb_fsop_setattr(sr, sr->user_cr, node, &new_attr,
539 	    &node->attr);
540 
541 	return (0);
542 }
543 
544 /*
545  * write_complete is sent acknowledge completion of raw write requests.
546  * We never send raw write commands to other servers so, if we receive a
547  * write_complete, we treat it as an error.
548  */
549 int
550 smb_com_write_complete(struct smb_request *sr)
551 {
552 	smbsr_decode_error(sr);
553 	/* NOT REACHED */
554 	return (0);
555 }
556 
557 /*
558  * The Write Block Multiplexed protocol is used to maximize performance
559  * when writing a large block of data.
560  *
561  * The mpx sub protocol is not supported because we support only
562  * connection oriented transports and NT supports SMB_COM_READ_MPX
563  * only over connectionless transports.
564  */
565 int /*ARGSUSED*/
566 smb_com_write_mpx(struct smb_request *sr)
567 {
568 	return (SDRC_UNIMPLEMENTED);
569 }
570 
571 int /*ARGSUSED*/
572 smb_com_write_mpx_secondary(struct smb_request *sr)
573 {
574 	return (SDRC_UNIMPLEMENTED);
575 }
576