xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_nt_transact_ioctl.c (revision 94047d49916b669576decf2f622a1ee718646882)
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
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
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
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
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
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
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
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