Yesterday a co-worker asked me about a wrong information showed by the df utility (running on a RedHat ES). One ZFS pool, and many ZFS filesystems, and the same “available” information for all filesystems (always the Pool total), even with some data written on the pool (that’s good, you do use the fs, and always have the total size to use :). I did look at the function used by df, and did find the statvfs (or statvfs64).
On a test machine (OpenSolaris), i did create two filesystems, did put some data, and here is the output:

mypool/fs-0001        2,1T   529G   1,6T    25%    /mypool/fs-0001
mypool/fs-0002        2,1T    18K   1,6T     1%    /mypool/fs-0002

Hmmm, no problem here… so, using an Ubuntu System, i did mount the same filesystems, and did execute the df utility. Here we can see the output:

x.x.x.x:/mypool/fs-0001 2,2T  529G  1,7T  25% /tmp/fs-0001
x.x.x.x:/mypool/fs-0002 1,7T     0  1,7T   0% /tmp/fs-0002

Ok, no problems here too… the statfs syscall is POSIX compliant, so should work in every flavour of UNIX-like OS’s. The structure is very simple:

           struct statvfs {
               unsigned long  f_bsize;    /* file system block size */
               unsigned long  f_frsize;   /* fragment size */
               fsblkcnt_t     f_blocks;   /* size of fs in f_frsize units */
               fsblkcnt_t     f_bfree;    /* # free blocks */
               fsblkcnt_t     f_bavail;   /* # free blocks for non-root */
               fsfilcnt_t     f_files;    /* # inodes */
               fsfilcnt_t     f_ffree;    /* # free inodes */
               fsfilcnt_t     f_favail;   /* # free inodes for non-root */
               unsigned long  f_fsid;     /* file system ID */
               unsigned long  f_flag;     /* mount flags */
               unsigned long  f_namemax;  /* maximum filename length */
           };

This time, on the GNU/Linux i did create a little c program (DisKEEE FRiIII), to see what i get:

./diskfree /tmp/fs-0001/
-----= DisKEEE FRiIII =-----
  -= byLeal - Feb/2009  =-
Filesystem block size: 1048576
Free Blocks: 1696422
Free Blocks for non root: 1696422
Maximum filename length: 255

Filesystem block size: 1048576?? Hmm, let’s do some math…
1696422 * 1048576 = 1778827395072
Did you recognize that number? Yes, 1,6TB!
Ok, but that is the filesystem that has the 529GB… and the other filesystem:

./diskfree /tmp/fs-0002/
-----= DisKEEE FRiIII =-----
  -= byLeal - Feb/2009  =-
Filesystem block size: 1048576
Free Blocks: 1696422
Free Blocks for non root: 1696422
Maximum filename length: 255

The same thing! It was perfect because i think the math is available = total – used, and that is different for ZFS pooled storage model. The filesystem has no boundaries on the pool (but can have if you configure quota), so the total size must be adjusted on an empty filesystem, for the available space be reported right. If the total size is always the same (the whole pool size), an empty filesystem will show all the space available, what is wrong. Ok, so i will put more data on that filesystem to see what happens.
First the df -h :

x.x.x.x:/mypool/fs-0001 2,2T  693G  1,5T  32% /tmp/fs-0001
x.x.x.x:/mypool/fs-0002 1,5T     0  1,5T   0% /tmp/fs-0002

Now, my DisKEEE FRiIII:

./diskfree /tmp/fs-0001/
-----= DisKEEE FRiIII =-----
  -= byLeal - Feb/2009  =-
Filesystem block size: 1048576
Free Blocks: 1528439
Free Blocks for non root: 1528439
Maximum filename length: 255

./diskfree /tmp/fs-0002/
-----= DisKEEE FRiIII =-----
  -= byLeal - Feb/2009  =-
Filesystem block size: 1048576
Free Blocks: 1528439
Free Blocks for non root: 1528439
Maximum filename length: 255

If you have interest, here is the (22 lines) program:

#include 
#include 
#include 

int main (int argc, char *argv[]) {
 struct statvfs fsd;
 printf ("-----= DisKEEE FRiIII =-----\n");
 printf ("  -= byLeal - Feb/2009  =-\n");
 if (argc < 2) {
    printf("we need at least one filesystem as argument...\n");
    exit(1);
 }
 if (statvfs (argv[1], &fsd) < 0)
    exit(1);
 printf ("Filesystem block size: %lu\n", fsd.f_bsize);
 printf ("Free Blocks: %lu\n", fsd.f_bfree);
 printf ("Free Blocks for non root: %lu\n", fsd.f_bavail);
 printf ("Maximum filename length: %lu\n", fsd.f_namemax);
 exit(0); 
}

Maybe the RedHat should take some Debian patches? ;-)
Just kidding, just kidding...
peace.