1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2018 Nexenta Systems, Inc. All rights reserved. 24 */ 25 26 #include <smbsrv/smb_kproto.h> 27 #include <smb/winioctl.h> 28 29 30 static uint32_t smb_nt_trans_ioctl_noop(smb_request_t *, smb_xa_t *); 31 static uint32_t smb_nt_trans_ioctl_invalid_parm(smb_request_t *, smb_xa_t *); 32 static uint32_t smb_nt_trans_ioctl_set_sparse(smb_request_t *, smb_xa_t *); 33 static uint32_t smb_nt_trans_ioctl_query_alloc_ranges(smb_request_t *, 34 smb_xa_t *); 35 static uint32_t smb_nt_trans_ioctl_set_zero_data(smb_request_t *, smb_xa_t *); 36 static uint32_t smb_nt_trans_ioctl_enum_snaps(smb_request_t *, smb_xa_t *); 37 38 /* 39 * This table defines the list of FSCTL values for which we'll 40 * call a funtion to perform specific processing. 41 */ 42 static const struct { 43 uint32_t fcode; 44 uint32_t (*ioctl_func)(smb_request_t *sr, smb_xa_t *xa); 45 } ioctl_ret_tbl[] = { 46 { FSCTL_GET_OBJECT_ID, smb_nt_trans_ioctl_invalid_parm }, 47 { FSCTL_QUERY_ALLOCATED_RANGES, smb_nt_trans_ioctl_query_alloc_ranges }, 48 { FSCTL_SET_ZERO_DATA, smb_nt_trans_ioctl_set_zero_data }, 49 { FSCTL_SRV_ENUMERATE_SNAPSHOTS, smb_nt_trans_ioctl_enum_snaps }, 50 { FSCTL_SET_SPARSE, smb_nt_trans_ioctl_set_sparse }, 51 { FSCTL_FIND_FILES_BY_SID, smb_nt_trans_ioctl_noop } 52 }; 53 54 /* 55 * smb_nt_transact_ioctl 56 * 57 * This command allows device and file system control functions to be 58 * transferred transparently from client to server. 59 * 60 * Setup Words Encoding Description 61 * =========================== ========================================= 62 * ULONG FunctionCode; NT device or file system control code 63 * USHORT Fid; Handle for io or fs control. Unless BIT0 64 * of ISFLAGS is set. 65 * BOOLEAN IsFsctl; Indicates whether the command is a device 66 * control (FALSE) or a file system control 67 * (TRUE). 68 * UCHAR IsFlags; BIT0 - command is to be applied to share 69 * root handle. Share must be a DFS share. 70 * 71 * Data Block Encoding Description 72 * =========================== ========================================= 73 * Data[ TotalDataCount ] Passed to the Fsctl or Ioctl 74 * 75 * Server Response Description 76 * =========================== ================================== 77 * SetupCount 1 78 * Setup[0] Length of information returned by 79 * io or fs control. 80 * DataCount Length of information returned by 81 * io or fs control. 82 * Data[ DataCount ] The results of the io or fs control. 83 */ 84 smb_sdrc_t 85 smb_nt_transact_ioctl(smb_request_t *sr, smb_xa_t *xa) 86 { 87 uint32_t status = NT_STATUS_NOT_SUPPORTED; 88 uint32_t fcode; 89 unsigned char is_fsctl; 90 unsigned char is_flags; 91 int i; 92 93 if (smb_mbc_decodef(&xa->req_setup_mb, "lwbb", 94 &fcode, &sr->smb_fid, &is_fsctl, &is_flags) != 0) { 95 smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0); 96 return (SDRC_ERROR); 97 } 98 99 /* 100 * Invoke handler if specified, otherwise the default 101 * behavior is to return NT_STATUS_NOT_SUPPORTED 102 */ 103 for (i = 0; i < sizeof (ioctl_ret_tbl) / sizeof (ioctl_ret_tbl[0]); 104 i++) { 105 if (ioctl_ret_tbl[i].fcode == fcode) { 106 status = ioctl_ret_tbl[i].ioctl_func(sr, xa); 107 break; 108 } 109 } 110 111 if (status != NT_STATUS_SUCCESS) { 112 smbsr_error(sr, status, 0, 0); 113 return (SDRC_ERROR); 114 } 115 116 (void) smb_mbc_encodef(&xa->rep_param_mb, "l", 0); 117 return (SDRC_SUCCESS); 118 } 119 120 /* ARGSUSED */ 121 static uint32_t 122 smb_nt_trans_ioctl_noop(smb_request_t *sr, smb_xa_t *xa) 123 { 124 return (NT_STATUS_SUCCESS); 125 } 126 127 /* ARGSUSED */ 128 static uint32_t 129 smb_nt_trans_ioctl_invalid_parm(smb_request_t *sr, smb_xa_t *xa) 130 { 131 return (NT_STATUS_INVALID_PARAMETER); 132 } 133 134 /* 135 * smb_nt_trans_ioctl_set_sparse 136 * 137 * There may, or may not be a data block in this request. 138 * If there IS a data block, the first byte is a boolean 139 * specifying whether to set (non zero) or clear (zero) 140 * the sparse attribute of the file. 141 * If there is no data block, this indicates a request to 142 * set the sparse attribute. 143 */ 144 static uint32_t 145 smb_nt_trans_ioctl_set_sparse(smb_request_t *sr, smb_xa_t *xa) 146 { 147 int rc = 0; 148 uint8_t set = 1; 149 smb_ofile_t *of; 150 smb_attr_t attr; 151 152 if (SMB_TREE_IS_READONLY(sr)) 153 return (NT_STATUS_ACCESS_DENIED); 154 155 if (STYPE_ISIPC(sr->tid_tree->t_res_type)) 156 return (NT_STATUS_INVALID_PARAMETER); 157 158 smbsr_lookup_file(sr); 159 if (sr->fid_ofile == NULL) 160 return (NT_STATUS_INVALID_HANDLE); 161 162 if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) { 163 smbsr_release_file(sr); 164 return (NT_STATUS_INVALID_PARAMETER); 165 } 166 167 of = sr->fid_ofile; 168 if (smb_node_is_dir(of->f_node)) { 169 smbsr_release_file(sr); 170 return (NT_STATUS_INVALID_PARAMETER); 171 } 172 173 if (smbsr_decode_data_avail(sr)) { 174 if (smb_mbc_decodef(&xa->req_data_mb, "b", &set) != 0) { 175 smbsr_release_file(sr); 176 return (sr->smb_error.status); 177 } 178 } 179 180 /* 181 * Using kcred because we just want the DOS attrs 182 * and don't want access errors for this. 183 */ 184 bzero(&attr, sizeof (smb_attr_t)); 185 attr.sa_mask = SMB_AT_DOSATTR; 186 rc = smb_node_getattr(sr, of->f_node, zone_kcred(), of, &attr); 187 if (rc != 0) { 188 smbsr_errno(sr, rc); 189 smbsr_release_file(sr); 190 return (sr->smb_error.status); 191 } 192 193 attr.sa_mask = 0; 194 if ((set == 0) && 195 (attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE)) { 196 attr.sa_dosattr &= ~FILE_ATTRIBUTE_SPARSE_FILE; 197 attr.sa_mask = SMB_AT_DOSATTR; 198 } else if ((set != 0) && 199 !(attr.sa_dosattr & FILE_ATTRIBUTE_SPARSE_FILE)) { 200 attr.sa_dosattr |= FILE_ATTRIBUTE_SPARSE_FILE; 201 attr.sa_mask = SMB_AT_DOSATTR; 202 } 203 204 if (attr.sa_mask != 0) { 205 rc = smb_node_setattr(sr, of->f_node, of->f_cr, of, &attr); 206 if (rc != 0) { 207 smbsr_errno(sr, rc); 208 smbsr_release_file(sr); 209 return (sr->smb_error.status); 210 } 211 } 212 213 smbsr_release_file(sr); 214 return (NT_STATUS_SUCCESS); 215 } 216 217 /* 218 * smb_nt_trans_ioctl_set_zero_data 219 * 220 * Check that the request is valid on the specified file. 221 * The implementation is a noop. XXX - bug! 222 * XXX: We have this in the fsclt module now. Call that. 223 * 224 * Note: When support is added for FSCTL_SET_ZERO_DATA, it must 225 * break any oplocks on the file to none: 226 * (void) smb_oplock_break_WRITE(node, ofile); 227 */ 228 /* ARGSUSED */ 229 static uint32_t 230 smb_nt_trans_ioctl_set_zero_data(smb_request_t *sr, smb_xa_t *xa) 231 { 232 smb_node_t *node; 233 234 if (SMB_TREE_IS_READONLY(sr)) 235 return (NT_STATUS_ACCESS_DENIED); 236 237 if (STYPE_ISIPC(sr->tid_tree->t_res_type)) 238 return (NT_STATUS_INVALID_PARAMETER); 239 240 smbsr_lookup_file(sr); 241 if (sr->fid_ofile == NULL) 242 return (NT_STATUS_INVALID_HANDLE); 243 244 if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) { 245 smbsr_release_file(sr); 246 return (NT_STATUS_INVALID_PARAMETER); 247 } 248 249 node = sr->fid_ofile->f_node; 250 if (smb_node_is_dir(node)) { 251 smbsr_release_file(sr); 252 return (NT_STATUS_INVALID_PARAMETER); 253 } 254 255 smbsr_release_file(sr); 256 return (NT_STATUS_SUCCESS); 257 } 258 259 /* 260 * smb_nt_trans_ioctl_query_alloc_ranges 261 * 262 * Responds with either: 263 * - no data if the file is zero size 264 * - a single range containing the starting point and length requested 265 */ 266 static uint32_t 267 smb_nt_trans_ioctl_query_alloc_ranges(smb_request_t *sr, smb_xa_t *xa) 268 { 269 int rc; 270 uint64_t offset, len; 271 smb_ofile_t *of; 272 smb_attr_t attr; 273 274 if (STYPE_ISIPC(sr->tid_tree->t_res_type)) 275 return (NT_STATUS_INVALID_PARAMETER); 276 277 smbsr_lookup_file(sr); 278 if (sr->fid_ofile == NULL) 279 return (NT_STATUS_INVALID_HANDLE); 280 281 if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) { 282 smbsr_release_file(sr); 283 return (NT_STATUS_INVALID_PARAMETER); 284 } 285 286 of = sr->fid_ofile; 287 if (smb_node_is_dir(of->f_node)) { 288 smbsr_release_file(sr); 289 return (NT_STATUS_INVALID_PARAMETER); 290 } 291 292 /* If zero size file don't return any data */ 293 bzero(&attr, sizeof (smb_attr_t)); 294 attr.sa_mask = SMB_AT_SIZE; 295 rc = smb_node_getattr(sr, of->f_node, of->f_cr, of, &attr); 296 if (rc != 0) { 297 smbsr_errno(sr, rc); 298 smbsr_release_file(sr); 299 return (sr->smb_error.status); 300 } 301 302 if (attr.sa_vattr.va_size == 0) { 303 smbsr_release_file(sr); 304 return (NT_STATUS_SUCCESS); 305 } 306 307 if (smb_mbc_decodef(&xa->req_data_mb, "qq", &offset, &len) != 0) { 308 smbsr_release_file(sr); 309 return (sr->smb_error.status); 310 } 311 312 /* 313 * Return a single range regardless of whether the file 314 * is sparse or not. 315 */ 316 if (MBC_ROOM_FOR(&xa->rep_data_mb, 16) == 0) { 317 smbsr_release_file(sr); 318 return (NT_STATUS_BUFFER_TOO_SMALL); 319 } 320 321 if (smb_mbc_encodef(&xa->rep_data_mb, "qq", offset, len) != 0) { 322 smbsr_release_file(sr); 323 return (sr->smb_error.status); 324 } 325 326 smbsr_release_file(sr); 327 return (NT_STATUS_SUCCESS); 328 } 329 330 static uint32_t 331 smb_nt_trans_ioctl_enum_snaps(smb_request_t *sr, smb_xa_t *xa) 332 { 333 smb_fsctl_t fsctl; 334 uint32_t status; 335 336 if (STYPE_ISIPC(sr->tid_tree->t_res_type)) 337 return (NT_STATUS_INVALID_PARAMETER); 338 339 smbsr_lookup_file(sr); 340 if (sr->fid_ofile == NULL) 341 return (NT_STATUS_INVALID_HANDLE); 342 343 if (!SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)) { 344 smbsr_release_file(sr); 345 return (NT_STATUS_INVALID_PARAMETER); 346 } 347 348 fsctl.CtlCode = FSCTL_SRV_ENUMERATE_SNAPSHOTS; 349 fsctl.InputCount = xa->smb_tpscnt; 350 fsctl.OutputCount = 0; 351 fsctl.MaxOutputResp = xa->smb_mdrcnt; 352 fsctl.in_mbc = &xa->req_param_mb; 353 fsctl.out_mbc = &xa->rep_data_mb; 354 355 status = smb_vss_enum_snapshots(sr, &fsctl); 356 357 smbsr_release_file(sr); 358 return (status); 359 } 360