1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
14  */
15 
16 /*
17  * Support functions for smb2_ioctl/fsctl categories:
18  * FILE_DEVICE_FILE_SYSTEM (9)
19  * FILE_DEVICE_NETWORK_FILE_SYSTEM (20)
20  */
21 
22 #include <smbsrv/smb2_kproto.h>
23 #include <smbsrv/smb_fsops.h>
24 #include <smb/winioctl.h>
25 
26 /*
27  * XXX: Should use smb2_fsctl_invalid in place of smb2_fsctl_notsup
28  * but that will require some re-testing.
29  */
30 static uint32_t
31 smb2_fsctl_invalid(smb_request_t *sr, smb_fsctl_t *fsctl)
32 {
33 	return (NT_STATUS_INVALID_DEVICE_REQUEST);
34 }
35 
36 static uint32_t
37 smb2_fsctl_notsup(smb_request_t *sr, smb_fsctl_t *fsctl)
38 {
39 	return (NT_STATUS_NOT_SUPPORTED);
40 }
41 
42 /*
43  * Same as smb2_fsctl_notsup, but make some noise (if DEBUG)
44  * so we'll learn about new fsctl codes clients start using.
45  */
46 /* ARGSUSED */
47 static uint32_t
48 smb2_fsctl_unknown(smb_request_t *sr, smb_fsctl_t *fsctl)
49 {
50 #ifdef	DEBUG
51 	cmn_err(CE_NOTE, "smb2_fsctl_unknown: code 0x%x", fsctl->CtlCode);
52 #endif
53 	return (NT_STATUS_NOT_SUPPORTED);
54 }
55 
56 /*
57  * FSCTL_GET_COMPRESSION
58  */
59 static uint32_t
60 smb2_fsctl_get_compression(smb_request_t *sr, smb_fsctl_t *fsctl)
61 {
62 	_NOTE(ARGUNUSED(sr))
63 	uint16_t compress_state = 0;
64 	int rc;
65 
66 	rc = smb_mbc_encodef(fsctl->in_mbc, "w",
67 	    compress_state);
68 	if (rc != 0)
69 		return (NT_STATUS_BUFFER_OVERFLOW);
70 
71 	return (NT_STATUS_SUCCESS);
72 }
73 
74 /*
75  * FSCTL_SET_COMPRESSION
76  */
77 static uint32_t
78 smb2_fsctl_set_compression(smb_request_t *sr, smb_fsctl_t *fsctl)
79 {
80 	_NOTE(ARGUNUSED(sr))
81 
82 	uint16_t compress_state;
83 	(void) smb_mbc_decodef(fsctl->in_mbc, "w",
84 	    &compress_state);
85 
86 	if (compress_state > 0)
87 		return (NT_STATUS_COMPRESSION_DISABLED);
88 
89 	return (NT_STATUS_SUCCESS);
90 }
91 
92 /*
93  * FSCTL_SRV_REQUEST_RESUME_KEY
94  *
95  * The returned data is an (opaque to the client) 24-byte blob
96  * in which we stash the SMB2 "file ID" (both parts). Later,
97  * copychunk may lookup the ofile using that file ID.
98  * See: smb2_fsctl_copychunk()
99  *
100  * Note that Mac clients make this request on a directory
101  * (even though this only makes sense on a file) just to
102  * find out if the server supports server-side copy.
103  * There's no harm letting a client have a resume key
104  * for a directory.  They'll never be able to DO anything
105  * with it because we check for a plain file later.
106  */
107 static uint32_t
108 smb2_fsctl_get_resume_key(smb_request_t *sr, smb_fsctl_t *fsctl)
109 {
110 	smb_ofile_t *of = sr->fid_ofile;
111 	smb2fid_t smb2fid;
112 	int rc;
113 
114 	/* Caller makes sure we have of = sr->fid_ofile */
115 	/* Don't insist on a plain file (see above). */
116 
117 	smb2fid.persistent = of->f_persistid;
118 	smb2fid.temporal = of->f_fid;
119 
120 	rc = smb_mbc_encodef(
121 	    fsctl->out_mbc, "qq16.",
122 	    smb2fid.persistent,
123 	    smb2fid.temporal);
124 	if (rc != 0)
125 		return (NT_STATUS_BUFFER_OVERFLOW);
126 
127 	return (NT_STATUS_SUCCESS);
128 }
129 
130 /*
131  * FILE_DEVICE_FILE_SYSTEM (9)
132  */
133 uint32_t
134 smb2_fsctl_fs(smb_request_t *sr, smb_fsctl_t *fsctl)
135 {
136 	uint32_t (*func)(smb_request_t *, smb_fsctl_t *);
137 	uint32_t status;
138 
139 	switch (fsctl->CtlCode) {
140 	case FSCTL_GET_COMPRESSION:		/* 15 */
141 		func = smb2_fsctl_get_compression;
142 		break;
143 	case FSCTL_SET_COMPRESSION:		/* 16 */
144 		func = smb2_fsctl_set_compression;
145 		break;
146 	case FSCTL_SET_REPARSE_POINT:		/* 41 */
147 	case FSCTL_GET_REPARSE_POINT:		/* 42 */
148 		func = smb2_fsctl_notsup;
149 		break;
150 	case FSCTL_CREATE_OR_GET_OBJECT_ID:	/* 48 */
151 		func = smb2_fsctl_invalid;
152 		break;
153 	case FSCTL_SET_SPARSE:			/* 49 */
154 		func = smb2_fsctl_set_sparse;
155 		break;
156 	case FSCTL_SET_ZERO_DATA:		/* 50 */
157 		func = smb2_fsctl_set_zero_data;
158 		break;
159 	case FSCTL_QUERY_ALLOCATED_RANGES:	/* 51 */
160 		func = smb2_fsctl_query_alloc_ranges;
161 		break;
162 	case FSCTL_FILE_LEVEL_TRIM:		/* 130 */
163 		func = smb2_fsctl_notsup;
164 		break;
165 	case FSCTL_OFFLOAD_READ:		/* 153 */
166 		func = smb2_fsctl_odx_read;
167 		break;
168 	case FSCTL_OFFLOAD_WRITE:		/* 154 */
169 		func = smb2_fsctl_odx_write;
170 		break;
171 	case FSCTL_SET_INTEGRITY_INFORMATION:	/* 160 */
172 		func = smb2_fsctl_notsup;
173 		break;
174 	case FSCTL_QUERY_FILE_REGIONS:		/* 161 */
175 		func = smb2_fsctl_query_file_regions;
176 		break;
177 
178 	default:
179 		func = smb2_fsctl_unknown;
180 		break;
181 	}
182 
183 	/*
184 	 * All "fs" sub-codes require a disk file.
185 	 */
186 	if (sr->fid_ofile == NULL ||
187 	    !SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype))
188 		return (NT_STATUS_INVALID_PARAMETER);
189 
190 	status = (*func)(sr, fsctl);
191 	return (status);
192 }
193 
194 /*
195  * FILE_DEVICE_NETWORK_FILE_SYSTEM (20)
196  */
197 uint32_t
198 smb2_fsctl_netfs(smb_request_t *sr, smb_fsctl_t *fsctl)
199 {
200 	uint32_t (*func)(smb_request_t *, smb_fsctl_t *);
201 	uint32_t status;
202 	boolean_t need_disk_file = B_TRUE;
203 
204 	switch (fsctl->CtlCode) {
205 	case FSCTL_SRV_ENUMERATE_SNAPSHOTS:	/* 0x19 */
206 		func = smb_vss_enum_snapshots;
207 		break;
208 	case FSCTL_SRV_REQUEST_RESUME_KEY:	/* 0x1e */
209 		func = smb2_fsctl_get_resume_key;
210 		break;
211 	case FSCTL_SRV_COPYCHUNK:		/* 0x3c(r) */
212 	case FSCTL_SRV_COPYCHUNK_WRITE:		/* 0x3c(w) */
213 		func = smb2_fsctl_copychunk;
214 		break;
215 	case FSCTL_SRV_READ_HASH:		/* 0x6e */
216 		func = smb2_fsctl_notsup;
217 		break;
218 	case FSCTL_LMR_REQUEST_RESILIENCY:	/* 0x75 */
219 		func = smb2_fsctl_set_resilient;
220 		break;
221 	case FSCTL_QUERY_NETWORK_INTERFACE_INFO: /* 0x7f */
222 		need_disk_file = B_FALSE;
223 		func = smb2_fsctl_notsup;
224 		break;
225 	case FSCTL_VALIDATE_NEGOTIATE_INFO:	/* 0x81 */
226 		need_disk_file = B_FALSE;
227 		func = smb2_nego_validate;
228 		break;
229 	default:
230 		func = smb2_fsctl_unknown;
231 		break;
232 	}
233 
234 	/*
235 	 * Most "net fs" sub-codes require a disk file,
236 	 * except a couple that clear need_disk_file.
237 	 */
238 	if (need_disk_file && (sr->fid_ofile == NULL ||
239 	    !SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)))
240 		return (NT_STATUS_INVALID_PARAMETER);
241 
242 	status = (*func)(sr, fsctl);
243 	return (status);
244 }
245