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