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 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 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 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 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 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 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 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 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