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