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