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