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