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