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