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