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 }