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