1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * Support functions for smb2_ioctl/fsctl categories:
  18  * FILE_DEVICE_FILE_SYSTEM (9)
  19  * FILE_DEVICE_NETWORK_FILE_SYSTEM (20)
  20  */
  21 
  22 #include <smbsrv/smb2_kproto.h>
  23 #include <smbsrv/smb_fsops.h>
  24 #include <smb/winioctl.h>
  25 
  26 /* ARGSUSED */
  27 static uint32_t
  28 smb2_fsctl_notsup(smb_request_t *sr, smb_fsctl_t *fsctl)
  29 {
  30         return (NT_STATUS_NOT_SUPPORTED);
  31 }
  32 
  33 /*
  34  * Same as smb2_fsctl_notsup, but make some noise (if DEBUG)
  35  * so we'll learn about new fsctl codes clients start using.
  36  */
  37 /* ARGSUSED */
  38 static uint32_t
  39 smb2_fsctl_unknown(smb_request_t *sr, smb_fsctl_t *fsctl)
  40 {
  41 #ifdef  DEBUG
  42         cmn_err(CE_NOTE, "smb2_fsctl_unknown: code 0x%x", fsctl->CtlCode);
  43 #endif
  44         return (NT_STATUS_NOT_SUPPORTED);
  45 }
  46 
  47 /*
  48  * FSCTL_GET_COMPRESSION
  49  */
  50 static uint32_t
  51 smb2_fsctl_get_compression(smb_request_t *sr, smb_fsctl_t *fsctl)
  52 {
  53         _NOTE(ARGUNUSED(sr))
  54         uint16_t compress_state = 0;
  55 
  56         (void) smb_mbc_encodef(fsctl->in_mbc, "w",
  57             compress_state);
  58 
  59         return (NT_STATUS_SUCCESS);
  60 }
  61 
  62 /*
  63  * FSCTL_SET_COMPRESSION
  64  */
  65 static uint32_t
  66 smb2_fsctl_set_compression(smb_request_t *sr, smb_fsctl_t *fsctl)
  67 {
  68         _NOTE(ARGUNUSED(sr))
  69 
  70         uint16_t compress_state;
  71         (void) smb_mbc_decodef(fsctl->in_mbc, "w",
  72             &compress_state);
  73 
  74         if (compress_state > 0)
  75                 return (NT_STATUS_COMPRESSION_DISABLED);
  76 
  77         return (NT_STATUS_SUCCESS);
  78 }
  79 
  80 /*
  81  * FSCTL_SRV_REQUEST_RESUME_KEY
  82  *
  83  * The returned data is an (opaque to the client) 24-byte blob
  84  * in which we stash the SMB2 "file ID" (both parts). Later,
  85  * copychunk may lookup the ofile using that file ID.
  86  * See: smb2_fsctl_copychunk()
  87  *
  88  * Note that Mac clients make this request on a directory
  89  * (even though this only makes sense on a file) just to
  90  * find out if the server supports server-side copy.
  91  * There's no harm letting a client have a resume key
  92  * for a directory.  They'll never be able to DO anything
  93  * with it because we check for a plain file later.
  94  */
  95 static uint32_t
  96 smb2_fsctl_get_resume_key(smb_request_t *sr, smb_fsctl_t *fsctl)
  97 {
  98         smb_ofile_t *of = sr->fid_ofile;
  99         smb2fid_t smb2fid;
 100 
 101         /* Caller makes sure we have of = sr->fid_ofile */
 102         /* Don't insist on a plain file (see above). */
 103 
 104         smb2fid.persistent = of->f_persistid;
 105         smb2fid.temporal = of->f_fid;
 106 
 107         (void) smb_mbc_encodef(
 108             fsctl->out_mbc, "qq16.",
 109             smb2fid.persistent,
 110             smb2fid.temporal);
 111 
 112         return (NT_STATUS_SUCCESS);
 113 }
 114 
 115 /*
 116  * FILE_DEVICE_FILE_SYSTEM (9)
 117  */
 118 uint32_t
 119 smb2_fsctl_fs(smb_request_t *sr, smb_fsctl_t *fsctl)
 120 {
 121         uint32_t (*func)(smb_request_t *, smb_fsctl_t *);
 122         uint32_t status;
 123 
 124         switch (fsctl->CtlCode) {
 125         case FSCTL_GET_COMPRESSION:             /* 15 */
 126                 func = smb2_fsctl_get_compression;
 127                 break;
 128         case FSCTL_SET_COMPRESSION:             /* 16 */
 129                 func = smb2_fsctl_set_compression;
 130                 break;
 131         case FSCTL_SET_REPARSE_POINT:           /* 41 */
 132         case FSCTL_GET_REPARSE_POINT:           /* 42 */
 133         case FSCTL_CREATE_OR_GET_OBJECT_ID:     /* 48 */
 134                 func = smb2_fsctl_notsup;
 135                 break;
 136         case FSCTL_SET_SPARSE:                  /* 49 */
 137                 func = smb2_fsctl_set_sparse;
 138                 break;
 139         case FSCTL_SET_ZERO_DATA:               /* 50 */
 140                 func = smb2_fsctl_set_zero_data;
 141                 break;
 142         case FSCTL_QUERY_ALLOCATED_RANGES:      /* 51 */
 143                 func = smb2_fsctl_query_alloc_ranges;
 144                 break;
 145         case FSCTL_FILE_LEVEL_TRIM:             /* 130 */
 146                 func = smb2_fsctl_notsup;
 147                 break;
 148         case FSCTL_OFFLOAD_READ:                /* 153 */
 149                 func = smb2_fsctl_odx_read;
 150                 break;
 151         case FSCTL_OFFLOAD_WRITE:               /* 154 */
 152                 func = smb2_fsctl_odx_write;
 153                 break;
 154         case FSCTL_SET_INTEGRITY_INFORMATION:   /* 160 */
 155                 func = smb2_fsctl_notsup;
 156                 break;
 157         case FSCTL_QUERY_FILE_REGIONS:          /* 161 */
 158                 func = smb2_fsctl_query_file_regions;
 159                 break;
 160 
 161         default:
 162                 func = smb2_fsctl_unknown;
 163                 break;
 164         }
 165 
 166         /*
 167          * All "fs" sub-codes require a disk file.
 168          */
 169         if (sr->fid_ofile == NULL ||
 170             !SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype))
 171                 return (NT_STATUS_INVALID_PARAMETER);
 172 
 173         status = (*func)(sr, fsctl);
 174         return (status);
 175 }
 176 
 177 /*
 178  * FILE_DEVICE_NETWORK_FILE_SYSTEM (20)
 179  */
 180 uint32_t
 181 smb2_fsctl_netfs(smb_request_t *sr, smb_fsctl_t *fsctl)
 182 {
 183         uint32_t (*func)(smb_request_t *, smb_fsctl_t *);
 184         uint32_t status;
 185         boolean_t need_disk_file = B_TRUE;
 186 
 187         switch (fsctl->CtlCode) {
 188         case FSCTL_SRV_ENUMERATE_SNAPSHOTS:     /* 0x19 */
 189                 func = smb_vss_enum_snapshots;
 190                 break;
 191         case FSCTL_SRV_REQUEST_RESUME_KEY:      /* 0x1e */
 192                 func = smb2_fsctl_get_resume_key;
 193                 break;
 194         case FSCTL_SRV_COPYCHUNK:               /* 0x3c(r) */
 195         case FSCTL_SRV_COPYCHUNK_WRITE:         /* 0x3c(w) */
 196                 func = smb2_fsctl_copychunk;
 197                 break;
 198         case FSCTL_SRV_READ_HASH:               /* 0x6e */
 199                 func = smb2_fsctl_notsup;
 200                 break;
 201         case FSCTL_LMR_REQUEST_RESILIENCY:      /* 0x75 */
 202                 func = smb2_fsctl_set_resilient;
 203                 break;
 204         case FSCTL_QUERY_NETWORK_INTERFACE_INFO: /* 0x7f */
 205                 need_disk_file = B_FALSE;
 206                 func = smb2_fsctl_notsup;
 207                 break;
 208         case FSCTL_VALIDATE_NEGOTIATE_INFO:     /* 0x81 */
 209                 need_disk_file = B_FALSE;
 210                 func = smb2_nego_validate;
 211                 break;
 212         default:
 213                 func = smb2_fsctl_unknown;
 214                 break;
 215         }
 216 
 217         /*
 218          * Most "net fs" sub-codes require a disk file,
 219          * except a couple that clear need_disk_file.
 220          */
 221         if (need_disk_file && (sr->fid_ofile == NULL ||
 222             !SMB_FTYPE_IS_DISK(sr->fid_ofile->f_ftype)))
 223                 return (NT_STATUS_INVALID_PARAMETER);
 224 
 225         status = (*func)(sr, fsctl);
 226         return (status);
 227 }