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 }