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 }