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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <smbsrv/smb_kproto.h>
26 #include <smbsrv/winioctl.h>
27
28
29 static uint32_t smb_nt_trans_ioctl_noop(smb_request_t *, smb_xa_t *);
30 static uint32_t smb_nt_trans_ioctl_invalid_parm(smb_request_t *, smb_xa_t *);
31 static uint32_t smb_nt_trans_ioctl_set_sparse(smb_request_t *, smb_xa_t *);
32 static uint32_t smb_nt_trans_ioctl_query_alloc_ranges(smb_request_t *,
33 smb_xa_t *);
34 static uint32_t smb_nt_trans_ioctl_set_zero_data(smb_request_t *, smb_xa_t *);
35
36 /*
37 * This table defines the list of FSCTL values for which we'll
38 * call a funtion to perform specific processing.
39 *
40 * Note: If support is added for FSCTL_SET_ZERO_DATA, it must break
41 * any oplocks on the file to none:
42 * smb_oplock_break(sr, node, SMB_OPLOCK_BREAK_TO_NONE);
43 */
44 static struct {
45 uint32_t fcode;
46 uint32_t (*ioctl_func)(smb_request_t *sr, smb_xa_t *xa);
47 } ioctl_ret_tbl[] = {
48 { FSCTL_GET_OBJECT_ID, smb_nt_trans_ioctl_invalid_parm },
49 { FSCTL_QUERY_ALLOCATED_RANGES, smb_nt_trans_ioctl_query_alloc_ranges },
50 { FSCTL_SET_ZERO_DATA, smb_nt_trans_ioctl_set_zero_data },
51 { FSCTL_SRV_ENUMERATE_SNAPSHOTS, smb_vss_ioctl_enumerate_snaps },
52 { FSCTL_SET_SPARSE, smb_nt_trans_ioctl_set_sparse },
53 { FSCTL_FIND_FILES_BY_SID, smb_nt_trans_ioctl_noop }
54 };
55
56 /*
57 * smb_nt_transact_ioctl
58 *
59 * This command allows device and file system control functions to be
60 * transferred transparently from client to server.
61 *
62 * Setup Words Encoding Description
63 * =========================== =========================================
64 * ULONG FunctionCode; NT device or file system control code
65 * USHORT Fid; Handle for io or fs control. Unless BIT0
66 * of ISFLAGS is set.
67 * BOOLEAN IsFsctl; Indicates whether the command is a device
68 * control (FALSE) or a file system control
69 * (TRUE).
70 * UCHAR IsFlags; BIT0 - command is to be applied to share
71 * root handle. Share must be a DFS share.
72 *
73 * Data Block Encoding Description
74 * =========================== =========================================
75 * Data[ TotalDataCount ] Passed to the Fsctl or Ioctl
76 *
77 * Server Response Description
78 * =========================== ==================================
79 * SetupCount 1
80 * Setup[0] Length of information returned by
81 * io or fs control.
82 * DataCount Length of information returned by
83 * io or fs control.
84 * Data[ DataCount ] The results of the io or fs control.
85 */
86 smb_sdrc_t
smb_nt_transact_ioctl(smb_request_t * sr,smb_xa_t * xa)87 smb_nt_transact_ioctl(smb_request_t *sr, smb_xa_t *xa)
88 {
89 uint32_t status = NT_STATUS_NOT_SUPPORTED;
90 uint32_t fcode;
91 unsigned char is_fsctl;
92 unsigned char is_flags;
93 int i;
94
95 if (smb_mbc_decodef(&xa->req_setup_mb, "lwbb",
96 &fcode, &sr->smb_fid, &is_fsctl, &is_flags) != 0) {
97 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
98 return (SDRC_ERROR);
99 }
100
101 /*
102 * Invoke handler if specified, otherwise the default
103 * behavior is to return NT_STATUS_NOT_SUPPORTED
104 */
105 for (i = 0; i < sizeof (ioctl_ret_tbl) / sizeof (ioctl_ret_tbl[0]);
106 i++) {
107 if (ioctl_ret_tbl[i].fcode == fcode) {
108 status = ioctl_ret_tbl[i].ioctl_func(sr, xa);
109 break;
110 }
111 }
112
113 if (status != NT_STATUS_SUCCESS) {
114 smbsr_error(sr, status, 0, 0);
115 return (SDRC_ERROR);
116 }
117
118 (void) smb_mbc_encodef(&xa->rep_param_mb, "l", 0);
119 return (SDRC_SUCCESS);
120 }
121
122 /* ARGSUSED */
123 static uint32_t
smb_nt_trans_ioctl_noop(smb_request_t * sr,smb_xa_t * xa)124 smb_nt_trans_ioctl_noop(smb_request_t *sr, smb_xa_t *xa)
125 {
126 return (NT_STATUS_SUCCESS);
127 }
128
129 /* ARGSUSED */
130 static uint32_t
smb_nt_trans_ioctl_invalid_parm(smb_request_t * sr,smb_xa_t * xa)131 smb_nt_trans_ioctl_invalid_parm(smb_request_t *sr, smb_xa_t *xa)
132 {
133 return (NT_STATUS_INVALID_PARAMETER);
134 }
135
136 /*
137 * smb_nt_trans_ioctl_set_sparse
138 *
139 * There may, or may not be a data block in this request.
140 * If there IS a data block, the first byte is a boolean
141 * specifying whether to set (non zero) or clear (zero)
142 * the sparse attribute of the file.
143 * If there is no data block, this indicates a request to
144 * set the sparse attribute.
145 */
146 static uint32_t
smb_nt_trans_ioctl_set_sparse(smb_request_t * sr,smb_xa_t * xa)147 smb_nt_trans_ioctl_set_sparse(smb_request_t *sr, smb_xa_t *xa)
148 {
149 int rc = 0;
150 uint8_t set = 1;
151 smb_node_t *node;
152 smb_attr_t attr;
153
154 if (SMB_TREE_IS_READONLY(sr))
155 return (NT_STATUS_ACCESS_DENIED);
156
157 if (STYPE_ISIPC(sr->tid_tree->t_res_type))
158 return (NT_STATUS_INVALID_PARAMETER);
159
160 smbsr_lookup_file(sr);
161 if (sr->fid_ofile == NULL)
162 return (NT_STATUS_INVALID_HANDLE);
163
164 if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) {
165 smbsr_release_file(sr);
166 return (NT_STATUS_INVALID_PARAMETER);
167 }
168
169 node = sr->fid_ofile->f_node;
170 if (smb_node_is_dir(node)) {
171 smbsr_release_file(sr);
172 return (NT_STATUS_INVALID_PARAMETER);
173 }
174
175 if (smbsr_decode_data_avail(sr)) {
176 if (smb_mbc_decodef(&xa->req_data_mb, "b", &set) != 0) {
177 smbsr_release_file(sr);
178 return (sr->smb_error.status);
179 }
180 }
181
182 bzero(&attr, sizeof (smb_attr_t));
183 attr.sa_mask = SMB_AT_DOSATTR;
184 if ((rc = smb_node_getattr(sr, node, &attr)) != 0) {
185 smbsr_errno(sr, rc);
186 smbsr_release_file(sr);
187 return (sr->smb_error.status);
188 }
189
190 attr.sa_mask = 0;
191 if ((set == 0) &&
192 (attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE)) {
193 attr.sa_dosattr &= ~FILE_ATTRIBUTE_SPARSE_FILE;
194 attr.sa_mask = SMB_AT_DOSATTR;
195 } else if ((set != 0) &&
196 !(attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE)) {
197 attr.sa_dosattr |= FILE_ATTRIBUTE_SPARSE_FILE;
198 attr.sa_mask = SMB_AT_DOSATTR;
199 }
200
201 if (attr.sa_mask != 0) {
202 rc = smb_node_setattr(sr, node, sr->user_cr, NULL, &attr);
203 if (rc != 0) {
204 smbsr_errno(sr, rc);
205 smbsr_release_file(sr);
206 return (sr->smb_error.status);
207 }
208 }
209
210 smbsr_release_file(sr);
211 return (NT_STATUS_SUCCESS);
212 }
213
214 /*
215 * smb_nt_trans_ioctl_set_zero_data
216 *
217 * Check that the request is valid on the specified file.
218 * The implementation is a noop.
219 */
220 /* ARGSUSED */
221 static uint32_t
smb_nt_trans_ioctl_set_zero_data(smb_request_t * sr,smb_xa_t * xa)222 smb_nt_trans_ioctl_set_zero_data(smb_request_t *sr, smb_xa_t *xa)
223 {
224 smb_node_t *node;
225
226 if (SMB_TREE_IS_READONLY(sr))
227 return (NT_STATUS_ACCESS_DENIED);
228
229 if (STYPE_ISIPC(sr->tid_tree->t_res_type))
230 return (NT_STATUS_INVALID_PARAMETER);
231
232 smbsr_lookup_file(sr);
233 if (sr->fid_ofile == NULL)
234 return (NT_STATUS_INVALID_HANDLE);
235
236 if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) {
237 smbsr_release_file(sr);
238 return (NT_STATUS_INVALID_PARAMETER);
239 }
240
241 node = sr->fid_ofile->f_node;
242 if (smb_node_is_dir(node)) {
243 smbsr_release_file(sr);
244 return (NT_STATUS_INVALID_PARAMETER);
245 }
246
247 smbsr_release_file(sr);
248 return (NT_STATUS_SUCCESS);
249 }
250
251 /*
252 * smb_nt_trans_ioctl_query_alloc_ranges
253 *
254 * Responds with either:
255 * - no data if the file is zero size
256 * - a single range containing the starting point and length requested
257 */
258 static uint32_t
smb_nt_trans_ioctl_query_alloc_ranges(smb_request_t * sr,smb_xa_t * xa)259 smb_nt_trans_ioctl_query_alloc_ranges(smb_request_t *sr, smb_xa_t *xa)
260 {
261 int rc;
262 uint64_t offset, len;
263 smb_node_t *node;
264 smb_attr_t attr;
265
266 if (STYPE_ISIPC(sr->tid_tree->t_res_type))
267 return (NT_STATUS_INVALID_PARAMETER);
268
269 smbsr_lookup_file(sr);
270 if (sr->fid_ofile == NULL)
271 return (NT_STATUS_INVALID_HANDLE);
272
273 if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) {
274 smbsr_release_file(sr);
275 return (NT_STATUS_INVALID_PARAMETER);
276 }
277
278 node = sr->fid_ofile->f_node;
279 if (smb_node_is_dir(node)) {
280 smbsr_release_file(sr);
281 return (NT_STATUS_INVALID_PARAMETER);
282 }
283
284 /* If zero size file don't return any data */
285 bzero(&attr, sizeof (smb_attr_t));
286 attr.sa_mask = SMB_AT_SIZE;
287 if ((rc = smb_node_getattr(sr, node, &attr)) != 0) {
288 smbsr_errno(sr, rc);
289 smbsr_release_file(sr);
290 return (sr->smb_error.status);
291 }
292
293 if (attr.sa_vattr.va_size == 0) {
294 smbsr_release_file(sr);
295 return (NT_STATUS_SUCCESS);
296 }
297
298 if (smb_mbc_decodef(&xa->req_data_mb, "qq", &offset, &len) != 0) {
299 smbsr_release_file(sr);
300 return (sr->smb_error.status);
301 }
302
303 /*
304 * Return a single range regardless of whether the file
305 * is sparse or not.
306 */
307 if (MBC_ROOM_FOR(&xa->rep_data_mb, 16) == 0) {
308 smbsr_release_file(sr);
309 return (NT_STATUS_BUFFER_TOO_SMALL);
310 }
311
312 if (smb_mbc_encodef(&xa->rep_data_mb, "qq", offset, len) != 0) {
313 smbsr_release_file(sr);
314 return (sr->smb_error.status);
315 }
316
317 smbsr_release_file(sr);
318 return (NT_STATUS_SUCCESS);
319 }
320