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 2017 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 
  26 /*
  27  * Dispatch function for SMB2_QUERY_INFO
  28  *
  29  * [MS-FSCC 2.5] If a file system does not implement ...
  30  * an Information Classs, NT_STATUS_INVALID_PARAMETER...
  31  */
  32 
  33 #include <smbsrv/smb2_kproto.h>
  34 #include <smbsrv/smb_fsops.h>
  35 #include <smbsrv/ntifs.h>
  36 
  37 uint32_t smb2_qfs_volume(smb_request_t *);
  38 uint32_t smb2_qfs_size(smb_request_t *);
  39 uint32_t smb2_qfs_device(smb_request_t *);
  40 uint32_t smb2_qfs_attr(smb_request_t *);
  41 uint32_t smb2_qfs_control(smb_request_t *);
  42 uint32_t smb2_qfs_fullsize(smb_request_t *);
  43 uint32_t smb2_qfs_obj_id(smb_request_t *);
  44 uint32_t smb2_qfs_sectorsize(smb_request_t *);
  45 
  46 uint32_t
  47 smb2_qinfo_fs(smb_request_t *sr, smb_queryinfo_t *qi)
  48 {
  49         uint32_t status;
  50 
  51         switch (qi->qi_InfoClass) {
  52 
  53         /* pg 153 */
  54         case FileFsVolumeInformation:   /* 1 */
  55                 status = smb2_qfs_volume(sr);
  56                 break;
  57         case FileFsSizeInformation:     /* 3 */
  58                 status = smb2_qfs_size(sr);
  59                 break;
  60         case FileFsDeviceInformation:   /* 4 */
  61                 status = smb2_qfs_device(sr);
  62                 break;
  63         case FileFsAttributeInformation: /* 5 */
  64                 status = smb2_qfs_attr(sr);
  65                 break;
  66         case FileFsControlInformation:  /* 6 */
  67                 status = smb2_qfs_control(sr);
  68                 break;
  69         case FileFsFullSizeInformation: /* 7 */
  70                 status = smb2_qfs_fullsize(sr);
  71                 break;
  72         case FileFsObjectIdInformation: /* 8 */
  73                 status = smb2_qfs_obj_id(sr);
  74                 break;
  75         case FileFsVolumeFlagsInformation:      /* A */
  76                 status = NT_STATUS_INVALID_INFO_CLASS;
  77                 break;
  78         case FileFsSectorSizeInformation:       /* B */
  79                 status = smb2_qfs_sectorsize(sr);
  80                 break;
  81         default: /* there are some infoclasses we don't yet handle */
  82                 status = NT_STATUS_INVALID_INFO_CLASS;
  83 #ifdef  DEBUG
  84                 cmn_err(CE_NOTE, "unknown InfoClass 0x%x", qi->qi_InfoClass);
  85 #endif
  86                 break;
  87         }
  88 
  89         return (status);
  90 }
  91 
  92 /*
  93  * FileFsVolumeInformation
  94  */
  95 uint32_t
  96 smb2_qfs_volume(smb_request_t *sr)
  97 {
  98         smb_tree_t *tree = sr->tid_tree;
  99         smb_node_t *snode;
 100         fsid_t fsid;
 101         uint32_t LabelLength;
 102 
 103         if (!STYPE_ISDSK(tree->t_res_type))
 104                 return (NT_STATUS_INVALID_PARAMETER);
 105 
 106         snode = tree->t_snode;
 107         fsid = SMB_NODE_FSID(snode);
 108 
 109         LabelLength = smb_wcequiv_strlen(tree->t_volume);
 110 
 111         /*
 112          * NT has the "supports objects" flag set to 1.
 113          */
 114         (void) smb_mbc_encodef(
 115             &sr->raw_data, "qllb.U",
 116             0LL,        /* Volume creation time (q) */
 117             fsid.val[0],        /* serial no.   (l) */
 118             LabelLength,                /*      (l) */
 119             0,          /* Supports objects     (b) */
 120             /* reserved                         (.) */
 121             tree->t_volume);         /*      (U) */
 122 
 123         return (0);
 124 }
 125 
 126 /*
 127  * FileFsSizeInformation
 128  */
 129 uint32_t
 130 smb2_qfs_size(smb_request_t *sr)
 131 {
 132         smb_fssize_t            fssize;
 133         smb_tree_t *tree = sr->tid_tree;
 134         int rc;
 135 
 136         if (!STYPE_ISDSK(tree->t_res_type))
 137                 return (NT_STATUS_INVALID_PARAMETER);
 138 
 139         rc = smb_fssize(sr, &fssize);
 140         if (rc)
 141                 return (smb_errno2status(rc));
 142 
 143         (void) smb_mbc_encodef(
 144             &sr->raw_data, "qqll",
 145             fssize.fs_caller_units,
 146             fssize.fs_caller_avail,
 147             fssize.fs_sectors_per_unit,
 148             fssize.fs_bytes_per_sector);
 149 
 150         return (0);
 151 }
 152 
 153 /*
 154  * FileFsFullSizeInformation
 155  */
 156 uint32_t
 157 smb2_qfs_fullsize(smb_request_t *sr)
 158 {
 159         smb_fssize_t            fssize;
 160         smb_tree_t *tree = sr->tid_tree;
 161         int rc;
 162 
 163         if (!STYPE_ISDSK(tree->t_res_type))
 164                 return (NT_STATUS_INVALID_PARAMETER);
 165 
 166         rc = smb_fssize(sr, &fssize);
 167         if (rc)
 168                 return (smb_errno2status(rc));
 169 
 170         (void) smb_mbc_encodef(
 171             &sr->raw_data, "qqqll",
 172             fssize.fs_caller_units,
 173             fssize.fs_caller_avail,
 174             fssize.fs_volume_avail,
 175             fssize.fs_sectors_per_unit,
 176             fssize.fs_bytes_per_sector);
 177 
 178         return (0);
 179 }
 180 
 181 /*
 182  * FileFsDeviceInformation
 183  */
 184 uint32_t
 185 smb2_qfs_device(smb_request_t *sr)
 186 {
 187         smb_tree_t *tree = sr->tid_tree;
 188         uint32_t DeviceType;
 189         uint32_t Characteristics;
 190 
 191         if (!STYPE_ISDSK(tree->t_res_type))
 192                 return (NT_STATUS_INVALID_PARAMETER);
 193 
 194         DeviceType = FILE_DEVICE_DISK;
 195         Characteristics = FILE_DEVICE_IS_MOUNTED;
 196 
 197         (void) smb_mbc_encodef(
 198             &sr->raw_data, "ll",
 199             DeviceType,
 200             Characteristics);
 201 
 202         return (0);
 203 }
 204 
 205 /*
 206  * FileFsAttributeInformation
 207  */
 208 uint32_t
 209 smb2_qfs_attr(smb_request_t *sr)
 210 {
 211         smb_tree_t *tree = sr->tid_tree;
 212         char *fsname;
 213         uint32_t namelen;
 214         uint32_t FsAttr;
 215 
 216         /* This call is OK on all tree types. */
 217         switch (tree->t_res_type & STYPE_MASK) {
 218         case STYPE_IPC:
 219                 fsname = "PIPE";
 220                 break;
 221         case STYPE_DISKTREE:
 222                 fsname = "NTFS"; /* A lie, but compatible... */
 223                 break;
 224         case STYPE_PRINTQ:
 225         case STYPE_DEVICE:
 226         default: /* gcc -Wuninitialized */
 227                 return (NT_STATUS_INVALID_PARAMETER);
 228         }
 229         namelen = smb_wcequiv_strlen(fsname);
 230 
 231         /*
 232          * Todo: Store the FsAttributes in the tree object,
 233          * then just return that directly here.
 234          */
 235         FsAttr = FILE_CASE_PRESERVED_NAMES;
 236         if (tree->t_flags & SMB_TREE_UNICODE_ON_DISK)
 237                 FsAttr |= FILE_UNICODE_ON_DISK;
 238         if (tree->t_flags & SMB_TREE_SUPPORTS_ACLS)
 239                 FsAttr |= FILE_PERSISTENT_ACLS;
 240         if ((tree->t_flags & SMB_TREE_CASEINSENSITIVE) == 0)
 241                 FsAttr |= FILE_CASE_SENSITIVE_SEARCH;
 242         if (tree->t_flags & SMB_TREE_STREAMS)
 243                 FsAttr |= FILE_NAMED_STREAMS;
 244         if (tree->t_flags & SMB_TREE_QUOTA)
 245                 FsAttr |= FILE_VOLUME_QUOTAS;
 246         if (tree->t_flags & SMB_TREE_SPARSE)
 247                 FsAttr |= FILE_SUPPORTS_SPARSE_FILES;
 248 
 249         (void) smb_mbc_encodef(
 250             &sr->raw_data, "lllU",
 251             FsAttr,
 252             MAXNAMELEN-1,
 253             namelen,
 254             fsname);
 255 
 256         return (0);
 257 }
 258 
 259 /*
 260  * FileFsControlInformation
 261  */
 262 uint32_t
 263 smb2_qfs_control(smb_request_t *sr)
 264 {
 265         smb_tree_t *tree = sr->tid_tree;
 266 
 267         if (!STYPE_ISDSK(tree->t_res_type))
 268                 return (NT_STATUS_INVALID_PARAMETER);
 269         if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
 270                 /*
 271                  * Strange error per. [MS-FSCC 2.5.2]
 272                  * which means quotas not supported.
 273                  */
 274                 return (NT_STATUS_VOLUME_NOT_UPGRADED);
 275         }
 276 
 277         (void) smb_mbc_encodef(
 278             &sr->raw_data, "qqqqqll",
 279             0,          /* free space start filtering - MUST be 0 */
 280             0,          /* free space threshold - MUST be 0 */
 281             0,          /* free space stop filtering - MUST be 0 */
 282             SMB_QUOTA_UNLIMITED,        /* default quota threshold */
 283             SMB_QUOTA_UNLIMITED,        /* default quota limit */
 284             FILE_VC_QUOTA_ENFORCE,      /* fs control flag */
 285             0);                         /* pad bytes */
 286 
 287         return (0);
 288 }
 289 
 290 /*
 291  * FileFsObjectIdInformation
 292  */
 293 /* ARGSUSED */
 294 uint32_t
 295 smb2_qfs_obj_id(smb_request_t *sr)
 296 {
 297         return (NT_STATUS_INVALID_PARAMETER);
 298 }
 299 
 300 /*
 301  * Not sure yet where these should go.
 302  * Flags in FileFsSectorSizeInformation
 303  */
 304 
 305 #define SSINFO_FLAGS_ALIGNED_DEVICE     0x00000001
 306 // When set, this flag indicates that the first physical sector of the device
 307 // is aligned with the first logical sector. When not set, the first physical
 308 // sector of the device is misaligned with the first logical sector.
 309 
 310 #define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE        0x00000002
 311 // When set, this flag indicates that the partition is aligned to physical
 312 // sector boundaries on the storage device.
 313 
 314 #define SSINFO_FLAGS_NO_SEEK_PENALTY    0x00000004
 315 // When set, the device reports that it does not incur a seek penalty (this
 316 // typically indicates that the device does not have rotating media, such as
 317 // flash-based disks).
 318 
 319 #define SSINFO_FLAGS_TRIM_ENABLED       0x00000008
 320 // When set, the device supports TRIM operations, either T13 (ATA) TRIM or
 321 // T10 (SCSI/SAS) UNMAP.
 322 
 323 #define SSINFO_OFFSET_UNKNOWN           0xffffffff
 324 // For "Alignment" fields below
 325 
 326 /* We have to lie to Windows Hyper-V about our logical record size. */
 327 int smb2_max_logical_sector_size = 4096;
 328 
 329 /*
 330  * FileFsSectorSizeInformation
 331  *
 332  * Returns a FILE_FS_SECTOR_SIZE_INFORMATION
 333  * See: [MS-FSCC] 2.5.8 FileFsSizeInformation
 334  *
 335  * LogicalBytesPerSector (4 bytes): ... number of bytes in a logical sector
 336  *   for the device backing the volume. This field is the unit of logical
 337  *   addressing for the device and is not the unit of atomic write.
 338  * PhysicalBytesPerSectorForAtomicity (4 bytes): ... number of bytes in a
 339  *   physical sector for the device backing the volume.  This is the reported
 340  *   physical sector size of the device and is the unit of atomic write.
 341  * PhysicalBytesPerSectorForPerformance (4 bytes): ... number of bytes in a
 342  *   physical sector for the device backing the volume. This is the reported
 343  *   physical sector size of the device and is the unit of performance.
 344  * FileSystemEffectivePhysicalBytesPerSectorForAtomicity (4 bytes): unit, in
 345  *   bytes, that the file system on the volume will use for internal operations
 346  *   that require alignment and atomicity.
 347  * Flags (4 bytes): See ...
 348  * ByteOffsetForSectorAlignment (4 bytes): ... logical sector offset within the
 349  *   first physical sector where the first logical sector is placed, in bytes.
 350  *   If this value is set to SSINFO_OFFSET_UNKNOWN (0xffffffff), there was
 351  *   insufficient information to compute this field.
 352  * ByteOffsetForPartitionAlignment (4 bytes): ... byte offset from the first
 353  *   physical sector where the first partition is placed. If this value is
 354  *   set to SSINFO_OFFSET_UNKNOWN (0xffffffff), there was either insufficient
 355  *   information or an error was encountered in computing this field.
 356  */
 357 uint32_t
 358 smb2_qfs_sectorsize(smb_request_t *sr)
 359 {
 360         smb_fssize_t            fssize;
 361         smb_tree_t *tree = sr->tid_tree;
 362         uint32_t lbps, pbps;
 363         uint32_t flags;
 364         int rc;
 365 
 366         if (!STYPE_ISDSK(tree->t_res_type))
 367                 return (NT_STATUS_INVALID_PARAMETER);
 368 
 369         rc = smb_fssize(sr, &fssize);
 370         if (rc)
 371                 return (smb_errno2status(rc));
 372         pbps = fssize.fs_bytes_per_sector;
 373         lbps = fssize.fs_sectors_per_unit * pbps;
 374         if (lbps > smb2_max_logical_sector_size)
 375                 lbps = smb2_max_logical_sector_size;
 376 
 377         // LogicalBytesPerSector
 378         (void) smb_mbc_encodef(&sr->raw_data, "l", lbps);
 379 
 380         // PhysicalBytesPerSectorForAtomicity
 381         (void) smb_mbc_encodef(&sr->raw_data, "l", pbps);
 382 
 383         // PhysicalBytesPerSectorForPerformance
 384         // Using logical size here.
 385         (void) smb_mbc_encodef(&sr->raw_data, "l", lbps);
 386 
 387         // FileSystemEffectivePhysicalBytesPerSectorForAtomicity
 388         (void) smb_mbc_encodef(&sr->raw_data, "l", pbps);
 389 
 390         // Flags
 391         // We include "no seek penalty" because our files are
 392         // always ZFS-backed, which can reorder things on disk.
 393         // Leaving out SSINFO_FLAGS_TRIM_ENABLED for now.
 394         flags = SSINFO_FLAGS_ALIGNED_DEVICE |
 395                 SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE |
 396                 SSINFO_FLAGS_NO_SEEK_PENALTY;
 397         (void) smb_mbc_encodef(&sr->raw_data, "l", flags);
 398 
 399         // ByteOffsetForSectorAlignment
 400         // ByteOffsetForPartitionAlignment
 401         // Just say "unknown" for these two.
 402         (void) smb_mbc_encodef(
 403             &sr->raw_data, "l",
 404             SSINFO_OFFSET_UNKNOWN,
 405             SSINFO_OFFSET_UNKNOWN);
 406 
 407         return (0);
 408 }