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