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