xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_fsinfo.c (revision 93bc28dbaee6387120d48b12b3dc1ba5f7418e6e)
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 #include <smbsrv/smb_kproto.h>
27 #include <smbsrv/smb_fsops.h>
28 #include <smbsrv/smbinfo.h>
29 
30 static int smb_trans2_set_fs_ctrl_info(smb_request_t *, smb_xa_t *);
31 
32 /*
33  * smb_com_query_information_disk
34  *
35  * The SMB_COM_QUERY_INFORMATION_DISK command is used to determine the
36  * capacity and remaining free space on the drive hosting the directory
37  * structure indicated by Tid in the SMB header.
38  *
39  * The blocking/allocation units used in this response may be independent
40  * of the actual physical or logical blocking/allocation algorithm(s) used
41  * internally by the server.  However, they must accurately reflect the
42  * amount of space on the server.
43  *
44  * This SMB only returns 16 bits of information for each field, which may
45  * not be large enough for some disk systems.  In particular TotalUnits is
46  * commonly > 64K.  Fortunately, it turns out the all the client cares
47  * about is the total disk size, in bytes, and the free space, in bytes.
48  * So,  it is reasonable for a server to adjust the relative values of
49  * BlocksPerUnit and BlockSize to accommodate.  If after all adjustment,
50  * the numbers are still too high, the largest possible values for
51  * TotalUnit or FreeUnits (i.e. 0xFFFF) should be returned.
52  */
53 
54 smb_sdrc_t
smb_pre_query_information_disk(smb_request_t * sr)55 smb_pre_query_information_disk(smb_request_t *sr)
56 {
57 	DTRACE_SMB_START(op__QueryInformationDisk, smb_request_t *, sr);
58 	return (SDRC_SUCCESS);
59 }
60 
61 void
smb_post_query_information_disk(smb_request_t * sr)62 smb_post_query_information_disk(smb_request_t *sr)
63 {
64 	DTRACE_SMB_DONE(op__QueryInformationDisk, smb_request_t *, sr);
65 }
66 
67 smb_sdrc_t
smb_com_query_information_disk(smb_request_t * sr)68 smb_com_query_information_disk(smb_request_t *sr)
69 {
70 	int			rc;
71 	fsblkcnt64_t		total_blocks, free_blocks;
72 	unsigned long		block_size, unit_size;
73 	unsigned short		blocks_per_unit, bytes_per_block;
74 	unsigned short		total_units, free_units;
75 	smb_fssize_t		fssize;
76 
77 	if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
78 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
79 		return (SDRC_ERROR);
80 	}
81 
82 	if (smb_fssize(sr, &fssize) != 0)
83 		return (SDRC_ERROR);
84 
85 	unit_size = fssize.fs_sectors_per_unit;
86 	block_size = fssize.fs_bytes_per_sector;
87 	total_blocks = fssize.fs_caller_units;
88 	free_blocks = fssize.fs_caller_avail;
89 
90 	/*
91 	 * It seems that DOS clients cannot handle block sizes
92 	 * bigger than 512 KB. So we have to set the block size at
93 	 * most to 512
94 	 */
95 	while (block_size > 512) {
96 		block_size >>= 1;
97 		unit_size <<= 1;
98 	}
99 
100 	/* adjust blocks and sizes until they fit into a word */
101 	while (total_blocks >= 0xFFFF) {
102 		total_blocks >>= 1;
103 		free_blocks >>= 1;
104 		if ((unit_size <<= 1) > 0xFFFF) {
105 			unit_size >>= 1;
106 			total_blocks = 0xFFFF;
107 			free_blocks <<= 1;
108 			break;
109 		}
110 	}
111 
112 	total_units = (total_blocks >= 0xFFFF) ?
113 	    0xFFFF : (unsigned short)total_blocks;
114 	free_units = (free_blocks >= 0xFFFF) ?
115 	    0xFFFF : (unsigned short)free_blocks;
116 	bytes_per_block = (unsigned short)block_size;
117 	blocks_per_unit = (unsigned short)unit_size;
118 
119 	rc = smbsr_encode_result(sr, 5, 0, "bwwww2.w",
120 	    5,
121 	    total_units,	/* total_units */
122 	    blocks_per_unit,	/* blocks_per_unit */
123 	    bytes_per_block,	/* blocksize */
124 	    free_units,		/* free_units */
125 	    0);			/* bcc */
126 
127 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
128 }
129 
130 /*
131  * smb_com_trans2_query_fs_information
132  *
133  * This transaction requests information about the filesystem.
134  * The following information levels are supported:
135  *
136  *  InformationLevel               	Value
137  *  ==================================  ======
138  *  SMB_INFO_ALLOCATION            	1
139  *  SMB_INFO_VOLUME                	2
140  *  SMB_QUERY_FS_VOLUME_INFO       	0x102
141  *  SMB_QUERY_FS_SIZE_INFO         	0x103
142  *  SMB_QUERY_FS_DEVICE_INFO       	0x104
143  *  SMB_QUERY_FS_ATTRIBUTE_INFO    	0x105
144  *  SMB_FILE_FS_VOLUME_INFORMATION	1001
145  *  SMB_FILE_FS_SIZE_INFORMATION	1003
146  *  SMB_FILE_FS_DEVICE_INFORMATION	1004
147  *  SMB_FILE_FS_ATTRIBUTE_INFORMATION	1005
148  *  SMB_FILE_FS_CONTROL_INFORMATION	1006
149  *  SMB_FILE_FS_FULLSIZE_INFORMATION	1007
150  *
151  * The fsid provides a system-wide unique file system ID.
152  * fsid.val[0] is the 32-bit dev for the file system of the share root
153  * smb_node.
154  * fsid.val[1] is the file system type.
155  */
156 smb_sdrc_t
smb_com_trans2_query_fs_information(smb_request_t * sr,smb_xa_t * xa)157 smb_com_trans2_query_fs_information(smb_request_t *sr, smb_xa_t *xa)
158 {
159 	uint32_t		flags;
160 	char			*encode_str, *tmpbuf;
161 	uint64_t		max_int;
162 	uint16_t		infolev;
163 	int			rc, length, buflen;
164 	smb_tree_t		*tree;
165 	smb_node_t		*snode;
166 	char 			*fsname = "NTFS";
167 	fsid_t			fsid;
168 	smb_fssize_t		fssize;
169 	smb_msgbuf_t		mb;
170 
171 	tree = sr->tid_tree;
172 
173 	if (!STYPE_ISDSK(tree->t_res_type)) {
174 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
175 		    ERRDOS, ERROR_ACCESS_DENIED);
176 		return (SDRC_ERROR);
177 	}
178 
179 	if (smb_mbc_decodef(&xa->req_param_mb, "w", &infolev) != 0)
180 		return (SDRC_ERROR);
181 
182 	snode = tree->t_snode;
183 	fsid = SMB_NODE_FSID(snode);
184 
185 	switch (infolev) {
186 	case SMB_INFO_ALLOCATION:
187 		if (smb_fssize(sr, &fssize) != 0)
188 			return (SDRC_ERROR);
189 
190 		max_int = (uint64_t)UINT_MAX;
191 		if (fssize.fs_caller_units > max_int)
192 			fssize.fs_caller_units = max_int;
193 		if (fssize.fs_caller_avail > max_int)
194 			fssize.fs_caller_avail = max_int;
195 
196 		(void) smb_mbc_encodef(&xa->rep_data_mb, "llllw",
197 		    0,
198 		    fssize.fs_sectors_per_unit,
199 		    fssize.fs_caller_units,
200 		    fssize.fs_caller_avail,
201 		    fssize.fs_bytes_per_sector);
202 		break;
203 
204 	case SMB_INFO_VOLUME:
205 		/*
206 		 * In this response, the unicode volume label is NOT
207 		 * expected to be aligned. Encode ('U') into a temporary
208 		 * buffer, then encode buffer as a byte stream ('#c').
209 		 */
210 		if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) ||
211 		    (sr->session->native_os == NATIVE_OS_WIN95)) {
212 			length = smb_wcequiv_strlen(tree->t_volume);
213 			buflen = length + sizeof (smb_wchar_t);
214 			tmpbuf = smb_srm_zalloc(sr, buflen);
215 			smb_msgbuf_init(&mb, (uint8_t *)tmpbuf, buflen,
216 			    SMB_MSGBUF_UNICODE);
217 			rc = smb_msgbuf_encode(&mb, "U", tree->t_volume);
218 			if (rc >= 0) {
219 				rc = smb_mbc_encodef(&xa->rep_data_mb,
220 				    "%lb#c", sr, fsid.val[0],
221 				    length, length, tmpbuf);
222 			}
223 			smb_msgbuf_term(&mb);
224 		} else {
225 			length = strlen(tree->t_volume);
226 			rc = smb_mbc_encodef(&xa->rep_data_mb, "%lbs", sr,
227 			    fsid.val[0], length, tree->t_volume);
228 		}
229 
230 		if (rc < 0)
231 			return (SDRC_ERROR);
232 		break;
233 
234 	case SMB_QUERY_FS_VOLUME_INFO:
235 	case SMB_FILE_FS_VOLUME_INFORMATION:
236 		if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) ||
237 		    (sr->session->native_os == NATIVE_OS_WIN95)) {
238 			length = smb_wcequiv_strlen(tree->t_volume);
239 			encode_str = "%qllb.U";
240 		} else {
241 			length = strlen(tree->t_volume);
242 			encode_str = "%qllb.s";
243 		}
244 
245 		/*
246 		 * NT has the "supports objects" flag set to 1.
247 		 */
248 		(void) smb_mbc_encodef(&xa->rep_data_mb, encode_str, sr,
249 		    0ll,			/* Volume creation time */
250 		    fsid.val[0],		/* Volume serial number */
251 		    length,			/* label length */
252 		    0,				/* Supports objects */
253 		    tree->t_volume);
254 		break;
255 
256 	case SMB_QUERY_FS_SIZE_INFO:
257 	case SMB_FILE_FS_SIZE_INFORMATION:
258 		if (smb_fssize(sr, &fssize) != 0)
259 			return (SDRC_ERROR);
260 
261 		(void) smb_mbc_encodef(&xa->rep_data_mb, "qqll",
262 		    fssize.fs_caller_units,
263 		    fssize.fs_caller_avail,
264 		    fssize.fs_sectors_per_unit,
265 		    fssize.fs_bytes_per_sector);
266 		break;
267 
268 	case SMB_QUERY_FS_DEVICE_INFO:
269 	case SMB_FILE_FS_DEVICE_INFORMATION:
270 		(void) smb_mbc_encodef(&xa->rep_data_mb, "ll",
271 		    FILE_DEVICE_FILE_SYSTEM,
272 		    FILE_DEVICE_IS_MOUNTED);
273 		break;
274 
275 	case SMB_QUERY_FS_ATTRIBUTE_INFO:
276 	case SMB_FILE_FS_ATTRIBUTE_INFORMATION:
277 		if ((sr->smb_flg2 & SMB_FLAGS2_UNICODE) ||
278 		    (sr->session->native_os == NATIVE_OS_WINNT) ||
279 		    (sr->session->native_os == NATIVE_OS_WIN2000) ||
280 		    (sr->session->native_os == NATIVE_OS_WIN95) ||
281 		    (sr->session->native_os == NATIVE_OS_MACOS)) {
282 			length = smb_wcequiv_strlen(fsname);
283 			encode_str = "%lllU";
284 			sr->smb_flg2 |= SMB_FLAGS2_UNICODE;
285 		} else {
286 			length = strlen(fsname);
287 			encode_str = "%llls";
288 		}
289 
290 		flags = FILE_CASE_PRESERVED_NAMES;
291 
292 		if (tree->t_flags & SMB_TREE_UNICODE_ON_DISK)
293 			flags |= FILE_UNICODE_ON_DISK;
294 
295 		if (tree->t_flags & SMB_TREE_SUPPORTS_ACLS)
296 			flags |= FILE_PERSISTENT_ACLS;
297 
298 		if ((tree->t_flags & SMB_TREE_CASEINSENSITIVE) == 0)
299 			flags |= FILE_CASE_SENSITIVE_SEARCH;
300 
301 		if (tree->t_flags & SMB_TREE_STREAMS)
302 			flags |= FILE_NAMED_STREAMS;
303 
304 		if (tree->t_flags & SMB_TREE_QUOTA)
305 			flags |= FILE_VOLUME_QUOTAS;
306 
307 		if (tree->t_flags & SMB_TREE_SPARSE)
308 			flags |= FILE_SUPPORTS_SPARSE_FILES;
309 
310 		(void) smb_mbc_encodef(&xa->rep_data_mb, encode_str, sr,
311 		    flags,
312 		    MAXNAMELEN,	/* max name */
313 		    length,	/* label length */
314 		    fsname);
315 		break;
316 
317 	case SMB_FILE_FS_CONTROL_INFORMATION:
318 		if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
319 			smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
320 			    ERRDOS, ERROR_NOT_SUPPORTED);
321 			return (SDRC_ERROR);
322 		}
323 
324 		(void) smb_mbc_encodef(&xa->rep_data_mb, "qqqqqll",
325 		    0,		/* free space start filtering - MUST be 0 */
326 		    0,		/* free space threshold - MUST be 0 */
327 		    0,		/* free space stop filtering - MUST be 0 */
328 		    SMB_QUOTA_UNLIMITED,	/* default quota threshold */
329 		    SMB_QUOTA_UNLIMITED,	/* default quota limit */
330 		    FILE_VC_QUOTA_ENFORCE,	/* fs control flag */
331 		    0);				/* pad bytes */
332 		break;
333 
334 	case SMB_FILE_FS_FULLSIZE_INFORMATION:
335 		if (smb_fssize(sr, &fssize) != 0)
336 			return (SDRC_ERROR);
337 
338 		(void) smb_mbc_encodef(&xa->rep_data_mb, "qqqll",
339 		    fssize.fs_caller_units,
340 		    fssize.fs_caller_avail,
341 		    fssize.fs_volume_avail,
342 		    fssize.fs_sectors_per_unit,
343 		    fssize.fs_bytes_per_sector);
344 		break;
345 
346 	case SMB_FILE_FS_LABEL_INFORMATION:
347 	case SMB_FILE_FS_OBJECTID_INFORMATION:
348 	case SMB_FILE_FS_DRIVERPATH_INFORMATION:
349 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
350 		    ERRDOS, ERROR_NOT_SUPPORTED);
351 		return (SDRC_ERROR);
352 
353 	default:
354 		smbsr_error(sr, NT_STATUS_INVALID_LEVEL,
355 		    ERRDOS, ERROR_INVALID_LEVEL);
356 		return (SDRC_ERROR);
357 	}
358 
359 	return (SDRC_SUCCESS);
360 }
361 
362 /*
363  * smb_fssize
364  *
365  * File system size information, for the volume and for the user
366  * initiating the request.
367  *
368  * If there's no quota entry for the user initiating the request,
369  * caller_units and caller_avail are the total and available units
370  * for the volume (volume_units, volume_avail).
371  * If there is a quota entry for the user initiating the request,
372  * and it is not SMB_QUOTA_UNLIMITED, calculate caller_units and
373  * caller_avail as follows:
374  *   caller_units = quota limit / bytes_per_unit
375  *   caller_avail = remaining quota / bytes_per_unit
376  *
377  * A quota limit of SMB_QUOTA_UNLIMITED means that the user's quota
378  * is specfied as unlimited. A quota limit of 0 means there is no
379  * quota specified for the user.
380  *
381  * Returns: 0 (success) or an errno value
382  */
383 int
smb_fssize(smb_request_t * sr,smb_fssize_t * fssize)384 smb_fssize(smb_request_t *sr, smb_fssize_t *fssize)
385 {
386 	smb_node_t *node;
387 	struct statvfs64 df;
388 	uid_t uid;
389 	smb_quota_t quota;
390 	int spu;	/* sectors per unit */
391 	int rc;
392 
393 	bzero(fssize, sizeof (smb_fssize_t));
394 	node = sr->tid_tree->t_snode;
395 	if ((rc = smb_fsop_statfs(sr->user_cr, node, &df)) != 0)
396 		return (rc);
397 
398 	if (df.f_frsize < DEV_BSIZE)
399 		df.f_frsize = DEV_BSIZE;
400 	if (df.f_bsize < df.f_frsize)
401 		df.f_bsize = df.f_frsize;
402 	spu = df.f_bsize / df.f_frsize;
403 
404 	fssize->fs_bytes_per_sector = (uint16_t)df.f_frsize;
405 	fssize->fs_sectors_per_unit = spu;
406 
407 	if (df.f_bavail > df.f_blocks)
408 		df.f_bavail = 0;
409 
410 	fssize->fs_volume_units = df.f_blocks / spu;
411 	fssize->fs_volume_avail = df.f_bavail / spu;
412 	fssize->fs_caller_units = df.f_blocks / spu;
413 	fssize->fs_caller_avail = df.f_bavail / spu;
414 
415 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA))
416 		return (0);
417 
418 	uid = crgetuid(sr->uid_user->u_cred);
419 	if (smb_quota_query_user_quota(sr, uid, &quota) != NT_STATUS_SUCCESS)
420 		return (0);
421 
422 	if ((quota.q_limit != SMB_QUOTA_UNLIMITED) && (quota.q_limit != 0)) {
423 		fssize->fs_caller_units = quota.q_limit / df.f_bsize;
424 		if (quota.q_limit <= quota.q_used)
425 			fssize->fs_caller_avail = 0;
426 		else
427 			fssize->fs_caller_avail =
428 			    (quota.q_limit - quota.q_used) / df.f_bsize;
429 	}
430 
431 	return (0);
432 }
433 
434 /*
435  * smb_com_trans2_set_fs_information
436  *
437  * This transaction sets filesystem information.
438  * The following information levels are supported:
439  *
440  *  InformationLevel               	Value
441  *  ==================================  ======
442  *  SMB_FILE_FS_CONTROL_INFORMATION	1006
443  */
444 smb_sdrc_t
smb_com_trans2_set_fs_information(smb_request_t * sr,smb_xa_t * xa)445 smb_com_trans2_set_fs_information(smb_request_t *sr, smb_xa_t *xa)
446 {
447 	smb_tree_t		*tree;
448 	uint16_t		infolev;
449 
450 	tree = sr->tid_tree;
451 	if (!STYPE_ISDSK(tree->t_res_type)) {
452 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
453 		    ERRDOS, ERROR_ACCESS_DENIED);
454 		return (SDRC_ERROR);
455 	}
456 
457 	if (smb_mbc_decodef(&xa->req_param_mb, "ww",
458 	    &sr->smb_fid, &infolev) != 0)
459 		return (SDRC_ERROR);
460 
461 	switch (infolev) {
462 	case SMB_FILE_FS_CONTROL_INFORMATION:
463 		if (smb_trans2_set_fs_ctrl_info(sr, xa) != 0)
464 			return (SDRC_ERROR);
465 		break;
466 
467 	case SMB_FILE_FS_VOLUME_INFORMATION:
468 	case SMB_FILE_FS_LABEL_INFORMATION:
469 	case SMB_FILE_FS_SIZE_INFORMATION:
470 	case SMB_FILE_FS_DEVICE_INFORMATION:
471 	case SMB_FILE_FS_ATTRIBUTE_INFORMATION:
472 	case SMB_FILE_FS_FULLSIZE_INFORMATION:
473 	case SMB_FILE_FS_OBJECTID_INFORMATION:
474 	case SMB_FILE_FS_DRIVERPATH_INFORMATION:
475 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
476 		    ERRDOS, ERROR_NOT_SUPPORTED);
477 		return (SDRC_ERROR);
478 
479 	default:
480 		smbsr_error(sr, NT_STATUS_INVALID_LEVEL,
481 		    ERRDOS, ERROR_INVALID_LEVEL);
482 		return (SDRC_ERROR);
483 	}
484 
485 	return (SDRC_SUCCESS);
486 }
487 
488 /*
489  * smb_trans2_set_fs_ctrl_info
490  *
491  * Only users with Admin privileges (i.e. of the BUILTIN/Administrators
492  * group) will be allowed to set quotas.
493  *
494  * Currently QUOTAS are always ENFORCED and the default values
495  * are always SMB_QUOTA_UNLIMITED (none). Any attempt to set
496  * values other than these will result in NT_STATUS_NOT_SUPPORTED.
497  */
498 static int
smb_trans2_set_fs_ctrl_info(smb_request_t * sr,smb_xa_t * xa)499 smb_trans2_set_fs_ctrl_info(smb_request_t *sr, smb_xa_t *xa)
500 {
501 	int rc;
502 	uint64_t fstart, fthresh, fstop, qthresh, qlimit;
503 	uint32_t qctrl, qpad;
504 
505 	if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_QUOTA)) {
506 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
507 		    ERRDOS, ERROR_NOT_SUPPORTED);
508 		return (-1);
509 	}
510 
511 	if (!smb_user_is_admin(sr->uid_user)) {
512 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
513 		    ERRDOS, ERROR_ACCESS_DENIED);
514 		return (-1);
515 	}
516 
517 	rc = smb_mbc_decodef(&xa->req_data_mb, "qqqqqll", &fstart,
518 	    &fthresh, &fstop, &qthresh, &qlimit, &qctrl, &qpad);
519 
520 	if ((rc != 0) || (fstart != 0) || (fthresh != 0) || (fstop != 0)) {
521 		smbsr_error(sr, NT_STATUS_INVALID_PARAMETER,
522 		    ERRDOS, ERROR_INVALID_PARAMETER);
523 		return (-1);
524 	}
525 
526 	/* Only support ENFORCED quotas with UNLIMITED default */
527 	if ((qctrl != FILE_VC_QUOTA_ENFORCE) ||
528 	    (qlimit != SMB_QUOTA_UNLIMITED) ||
529 	    (qthresh != SMB_QUOTA_UNLIMITED)) {
530 		smbsr_error(sr, NT_STATUS_NOT_SUPPORTED,
531 		    ERRDOS, ERROR_NOT_SUPPORTED);
532 		return (-1);
533 	}
534 
535 	return (0);
536 }
537