Home | History | Annotate | Download | only in smbsrv
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*
     27  * SMB: query_information_disk
     28  *
     29  * The SMB_COM_QUERY_INFORMATION_DISK command is used to determine the
     30  * capacity and remaining free space on the drive hosting the directory
     31  * structure indicated by Tid in the SMB header.
     32  *
     33  * Client Request                     Description
     34  * ================================== =================================
     35  *
     36  * UCHAR WordCount;                   Count of parameter words = 0
     37  * USHORT ByteCount;                  Count of data bytes = 0
     38  *
     39  * Server Response                    Description
     40  * ================================== =================================
     41  *
     42  * UCHAR WordCount;                   Count of parameter words = 5
     43  * USHORT TotalUnits;                 Total allocation units per server
     44  * USHORT BlocksPerUnit;              Blocks per allocation unit
     45  * USHORT BlockSize;                  Block size (in bytes)
     46  * USHORT FreeUnits;                  Number of free units
     47  * USHORT Reserved;                   Reserved (client should ignore)
     48  * USHORT ByteCount;                  Count of data bytes = 0
     49  *
     50  * The blocking/allocation units used in this response may be independent
     51  * of the actual physical or logical blocking/allocation algorithm(s) used
     52  * internally by the server.  However, they must accurately reflect the
     53  * amount of space on the server.
     54  *
     55  * This SMB only returns 16 bits of information for each field, which may
     56  * not be large enough for some disk systems.  In particular TotalUnits is
     57  * commonly > 64K.  Fortunately, it turns out the all the client cares
     58  * about is the total disk size, in bytes, and the free space, in bytes.
     59  * So,  it is reasonable for a server to adjust the relative values of
     60  * BlocksPerUnit and BlockSize to accommodate.  If after all adjustment,
     61  * the numbers are still too high, the largest possible values for
     62  * TotalUnit or FreeUnits (i.e. 0xFFFF) should be returned.
     63  */
     64 
     65 #include <smbsrv/smb_kproto.h>
     66 #include <smbsrv/smb_fsops.h>
     67 
     68 smb_sdrc_t
     69 smb_pre_query_information_disk(smb_request_t *sr)
     70 {
     71 	DTRACE_SMB_1(op__QueryInformationDisk__start, smb_request_t *, sr);
     72 	return (SDRC_SUCCESS);
     73 }
     74 
     75 void
     76 smb_post_query_information_disk(smb_request_t *sr)
     77 {
     78 	DTRACE_SMB_1(op__QueryInformationDisk__done, smb_request_t *, sr);
     79 }
     80 
     81 smb_sdrc_t
     82 smb_com_query_information_disk(smb_request_t *sr)
     83 {
     84 	int			rc;
     85 	struct statvfs64	df;
     86 	fsblkcnt64_t		total_blocks, free_blocks;
     87 	unsigned long		block_size, unit_size;
     88 	unsigned short		blocks_per_unit, bytes_per_block;
     89 	unsigned short		total_units, free_units;
     90 
     91 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
     92 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, ERRnoaccess);
     93 		return (SDRC_ERROR);
     94 	}
     95 
     96 	rc = smb_fsop_statfs(sr->user_cr, sr->tid_tree->t_snode, &df);
     97 	if (rc != 0) {
     98 		smbsr_errno(sr, rc);
     99 		return (SDRC_ERROR);
    100 	}
    101 
    102 	unit_size = 1;
    103 	block_size = df.f_frsize;
    104 	total_blocks = df.f_blocks;
    105 	free_blocks = df.f_bavail;
    106 
    107 	/*
    108 	 * It seems that DOS clients cannot handle block sizes
    109 	 * bigger than 512 KB. So we have to set the block size at
    110 	 * most to 512
    111 	 */
    112 
    113 	while (block_size > 512) {
    114 		block_size >>= 1;
    115 		unit_size <<= 1;
    116 	}
    117 
    118 	/* adjust blocks and sizes until they fit into a word */
    119 
    120 	while (total_blocks >= 0xFFFF) {
    121 		total_blocks >>= 1;
    122 		free_blocks >>= 1;
    123 		if ((unit_size <<= 1) > 0xFFFF) {
    124 			unit_size >>= 1;
    125 			total_blocks = 0xFFFF;
    126 			free_blocks <<= 1;
    127 			break;
    128 		}
    129 	}
    130 
    131 	total_units = (total_blocks >= 0xFFFF) ?
    132 	    0xFFFF : (unsigned short)total_blocks;
    133 	free_units = (free_blocks >= 0xFFFF) ?
    134 	    0xFFFF : (unsigned short)free_blocks;
    135 	bytes_per_block = (unsigned short)block_size;
    136 	blocks_per_unit = (unsigned short)unit_size;
    137 
    138 	rc = smbsr_encode_result(sr, 5, 0, "bwwww2.w",
    139 	    5,
    140 	    total_units,	/* total_units */
    141 	    blocks_per_unit,	/* blocks_per_unit */
    142 	    bytes_per_block,	/* blocksize */
    143 	    free_units,		/* free_units */
    144 	    0);			/* bcc */
    145 
    146 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
    147 }
    148