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