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