[jcifs] SmbFile.getDiskFreeSpace bug

Thomas Krammer tkrammer at nxn-software.com
Thu Jan 27 14:55:23 GMT 2005


The Windows Explorer is reporting the correct value. Windows XP is using 
information level 1007 instead of information level 1 which JCIFS uses. 
According the the Samba sources this is

#define SMB_FS_FULL_SIZE_INFORMATION            1007

It returns the following information (see trans2.c in the Samba sources):

int64 Total allocation units
int64 Caller available allocation units
int64 Actual available allocation units
int32 Sectors per allocation unit
int32 Bytes per sector

So it returns the block counts as 64 bit integers instead of 32 bit 
integers.

I extended getDiskFreeSpace() to use SMB_FS_FULL_SIZE_INFORMATION. If 
the server returns a NT_STATUS_INVALID_INFO_CLASS error I fall back to 
SMB_INFO_ALLOCATION. I don't know if there is a way to know if the 
server supports a certain information level before making the call.

I attached a patch with my extensions. I did some limited testing 
against a Windows 2003 Server and it seems to work fine. I also asked 
our customer to try out the patched JCIFS library. I will report back as 
soon as I hear from them.


  Thomas

>Thomas Krammer said:
>  
>
>>Sorry, but I don't have access to that server. It's the server of a
>>customer and they probably wouldn't allow me to install a package
>>capturing tool on one of their machines.
>>
>>I did a package capture of my Windows XP machine querying one of our
>>servers with 1.1 TB (1226109587456 bytes) of free disk space. But JCIFS
>>reports the correct free space on this server. So these captures might
>>be useless.
>>
>>    
>>
><snip>
>  
>
>>        QUERY_FS_INFO Data
>>            Allocation Size: 299343161
>>            Caller Free Units: 299316575
>>            Actual Free Units: 299316575
>>            Sectors/Unit: 8
>>            Bytes per Sector: 512
>>    
>>
>
>So jCIFS has no problem with sizes larger than 32bits. The problem is that
>with a block size if 512 the largest size that can be reported is 512 *
>0xFFFFFFFF which is 2.199 TB. That's not enough to report 2.8 TB (BTW I
>think your "to be exact" value was short a digit).
>
>There's nothing I can do about this and I bet Windows will report the same
>bogus value. If it does not then we need a pcap to decipher how it's
>getting a correct value.
>
>If there's a way to change the reported "Bytes per Sector" then the server
>would be able to report the correct value (e.g. 1024 * 0xFFFFFFFF is 4.398
>TB max).
>
>Mike
>  
>

-------------- next part --------------
diff -Naur jcifs_1.1.7_org/src/jcifs/smb/SmbFile.java jcifs_1.1.7/src/jcifs/smb/SmbFile.java
--- jcifs_1.1.7_org/src/jcifs/smb/SmbFile.java	2005-01-16 18:55:22.000000000 +0100
+++ jcifs_1.1.7/src/jcifs/smb/SmbFile.java	2005-01-27 14:03:50.203125000 +0100
@@ -2183,21 +2183,35 @@
  */
     public long getDiskFreeSpace() throws SmbException {
         if( getType() == TYPE_SHARE || type == TYPE_FILESYSTEM ) {
-            Trans2QueryFSInformationResponse response;
-            int level = Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION;
-
-            response = new Trans2QueryFSInformationResponse( level );
-            sendTransaction( new Trans2QueryFSInformation( level ), response );
-
-            if( type == TYPE_SHARE ) {
-                size = response.info.getCapacity();
-                sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod;
+            try {
+                return queryFSInformation(Trans2QueryFSInformationResponse.SMB_FS_FULL_SIZE_INFORMATION);
             }
-
-            return response.info.getFree();
+            catch(SmbException ex) {
+                if(ex.getNtStatus() == NtStatus.NT_STATUS_INVALID_INFO_CLASS) {
+                    // SMB_FS_FULL_SIZE_INFORMATION not supported by the server.
+                    return queryFSInformation(Trans2QueryFSInformationResponse.SMB_INFO_ALLOCATION);
+                }
+                
+                throw ex;
+            }
+            
         }
         return 0L;
     }
+    
+    private long queryFSInformation( int level ) throws SmbException {
+        Trans2QueryFSInformationResponse response;
+
+        response = new Trans2QueryFSInformationResponse( level );
+        sendTransaction( new Trans2QueryFSInformation( level ), response );
+
+        if( type == TYPE_SHARE ) {
+            size = response.info.getCapacity();
+            sizeExpiration = System.currentTimeMillis() + attrExpirationPeriod;
+        }
+
+        return response.info.getFree();
+    }
 
 /**
  * Creates a directory with the path specified by this
diff -Naur jcifs_1.1.7_org/src/jcifs/smb/Trans2QueryFSInformationResponse.java jcifs_1.1.7/src/jcifs/smb/Trans2QueryFSInformationResponse.java
--- jcifs_1.1.7_org/src/jcifs/smb/Trans2QueryFSInformationResponse.java	2005-01-16 18:55:22.000000000 +0100
+++ jcifs_1.1.7/src/jcifs/smb/Trans2QueryFSInformationResponse.java	2005-01-27 13:21:28.984375000 +0100
@@ -25,6 +25,7 @@
     // information levels
     static final int SMB_INFO_ALLOCATION = 1;
     static final int SMB_QUERY_FS_SIZE_INFO = 0x103;
+    static final int SMB_FS_FULL_SIZE_INFORMATION = 1007;
 
     class SmbInfoAllocation implements AllocInfo {
         long alloc;                  // Also handles SmbQueryFSSizeInfo
@@ -77,6 +78,8 @@
                 return readSmbInfoAllocationWireFormat( buffer, bufferIndex );
             case SMB_QUERY_FS_SIZE_INFO:
                 return readSmbQueryFSSizeInfoWireFormat( buffer, bufferIndex );
+            case SMB_FS_FULL_SIZE_INFORMATION:
+                return readFsFullSizeInformationWireFormat( buffer, bufferIndex );
             default:
                 return 0;
         }
@@ -130,6 +133,38 @@
 
         return bufferIndex - start;
     }
+    int readFsFullSizeInformationWireFormat( byte[] buffer, int bufferIndex )
+    {
+        int start = bufferIndex;
+        
+        SmbInfoAllocation info = new SmbInfoAllocation();
+        
+        // Read total allocation units.
+        info.alloc = readInt4( buffer, bufferIndex );
+        bufferIndex += 4;
+        info.alloc |= readInt4( buffer, bufferIndex ) << 32L;
+        bufferIndex += 4;
+        
+        // read caller available allocation units 
+        info.free = readInt4( buffer, bufferIndex );
+        bufferIndex += 4;
+        info.free |= readInt4( buffer, bufferIndex ) << 32L;
+        bufferIndex += 4;
+        
+        // skip actual free units
+        bufferIndex += 8;
+        
+        info.sectPerAlloc = readInt4( buffer, bufferIndex );
+        bufferIndex += 4;
+
+        info.bytesPerSect = readInt4( buffer, bufferIndex );
+        bufferIndex += 4;
+        
+        this.info = info;
+
+        return bufferIndex - start;
+    }
+    
     public String toString() {
         return new String( "Trans2QueryFSInformationResponse[" +
             super.toString() + "]" );


More information about the jcifs mailing list