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