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