xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb2_write.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
14  */
15 
16 /*
17  * Dispatch function for SMB2_WRITE
18  */
19 
20 #include <smbsrv/smb2_kproto.h>
21 #include <smbsrv/smb_fsops.h>
22 
23 smb_sdrc_t
24 smb2_write(smb_request_t *sr)
25 {
26 	smb_ofile_t *of = NULL;
27 	smb_vdb_t *vdb = NULL;
28 	uint16_t StructSize;
29 	uint16_t DataOff;
30 	uint32_t Length;
31 	uint64_t Offset;
32 	smb2fid_t smb2fid;
33 	uint32_t Channel;
34 	uint32_t Remaining;
35 	uint16_t ChanInfoOffset;
36 	uint16_t ChanInfoLength;
37 	uint32_t Flags;
38 	uint32_t XferCount;
39 	uint32_t status;
40 	int data_chain_off, skip;
41 	int stability = 0;
42 	int rc = 0;
43 
44 	/*
45 	 * SMB2 Write request
46 	 */
47 	rc = smb_mbc_decodef(
48 	    &sr->smb_data,
49 	    "wwlqqqllwwl",
50 	    &StructSize,		/* w */
51 	    &DataOff,			/* w */
52 	    &Length,			/* l */
53 	    &Offset,			/* q */
54 	    &smb2fid.persistent,	/* q */
55 	    &smb2fid.temporal,		/* q */
56 	    &Channel,			/* l */
57 	    &Remaining,			/* l */
58 	    &ChanInfoOffset,		/* w */
59 	    &ChanInfoLength,		/* w */
60 	    &Flags);			/* l */
61 	if (rc)
62 		return (SDRC_ERROR);
63 	if (StructSize != 49)
64 		return (SDRC_ERROR);
65 
66 	status = smb2sr_lookup_fid(sr, &smb2fid);
67 	if (status) {
68 		smb2sr_put_error(sr, status);
69 		return (SDRC_SUCCESS);
70 	}
71 	of = sr->fid_ofile;
72 
73 	if (Length > smb2_max_rwsize) {
74 		smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
75 		return (SDRC_SUCCESS);
76 	}
77 
78 	/*
79 	 * Skip any padding before the write data.
80 	 */
81 	data_chain_off = sr->smb2_cmd_hdr + DataOff;
82 	skip = data_chain_off - sr->smb_data.chain_offset;
83 	if (skip < 0) {
84 		smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
85 		return (SDRC_SUCCESS);
86 	}
87 	if (skip > 0) {
88 		(void) smb_mbc_decodef(&sr->smb_data, "#.", skip);
89 	}
90 
91 	/* This is automatically free'd. */
92 	vdb = smb_srm_zalloc(sr, sizeof (*vdb));
93 	rc = smb_mbc_decodef(&sr->smb_data, "#B", Length, vdb);
94 	if (rc != 0 || vdb->vdb_len != Length) {
95 		smb2sr_put_error(sr, NT_STATUS_INVALID_PARAMETER);
96 		return (SDRC_SUCCESS);
97 	}
98 	vdb->vdb_uio.uio_loffset = (offset_t)Offset;
99 
100 	XferCount = 0;
101 	if (Length == 0)
102 		goto doreply;
103 
104 	switch (of->f_tree->t_res_type & STYPE_MASK) {
105 	case STYPE_DISKTREE:
106 	case STYPE_PRINTQ:
107 		if (!smb_node_is_dir(of->f_node)) {
108 			/* Check for conflicting locks. */
109 			rc = smb_lock_range_access(sr, of->f_node,
110 			    Offset, Length, B_TRUE);
111 			if (rc) {
112 				rc = ERANGE;
113 				break;
114 			}
115 		}
116 		if ((Flags & SMB2_WRITEFLAG_WRITE_THROUGH) ||
117 		    (of->f_node->flags & NODE_FLAGS_WRITE_THROUGH)) {
118 			stability = FSYNC;
119 		}
120 		rc = smb_fsop_write(sr, of->f_cr, of->f_node,
121 		    &vdb->vdb_uio, &XferCount, stability);
122 		if (rc)
123 			break;
124 		of->f_written = B_TRUE;
125 		if (!smb_node_is_dir(of->f_node))
126 			smb_oplock_break_levelII(of->f_node);
127 		break;
128 
129 	case STYPE_IPC:
130 		rc = smb_opipe_write(sr, &vdb->vdb_uio);
131 		if (rc == 0)
132 			XferCount = Length;
133 		break;
134 
135 	default:
136 		rc = EACCES;
137 		break;
138 	}
139 
140 	if (rc) {
141 		smb2sr_put_errno(sr, rc);
142 		return (SDRC_SUCCESS);
143 	}
144 
145 	/*
146 	 * SMB2 Write reply
147 	 */
148 doreply:
149 	DataOff = SMB2_HDR_SIZE + 16;
150 	rc = smb_mbc_encodef(
151 	    &sr->reply, "wwlll",
152 	    17,	/* StructSize */	/* w */
153 	    0, /* reserved */		/* w */
154 	    XferCount,			/* l */
155 	    0, /* DataRemaining */	/* l */
156 	    0); /* Channel Info */	/* l */
157 	if (rc)
158 		return (SDRC_ERROR);
159 
160 	mutex_enter(&of->f_mutex);
161 	of->f_seek_pos = Offset + XferCount;
162 	mutex_exit(&of->f_mutex);
163 
164 	return (SDRC_SUCCESS);
165 }
166