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