tags:

views:

119

answers:

2

I am scanning a file system and wish to determine the allocated size of a file on disk.

Using stat() I can get the file size as ( stat(_) )[7] , the blocks allocated on disk can be obtained from ( stat(_) )[12], and the "preferred block size for file system I/O" is ( stat(_) )[11].

If I merely multiply stat column 11 and 12, however, I do not get what appears to be the allocated space on disk (on Solaris 5.10 sparc).

How can I programmatically get the space allocated on disk for a file from the stat() function call in Perl?

+2  A: 

Is this on a ZFS filesystem? I'm not sure how it would interact with Perl's stat function call, but there is normally a disparity between the actual size of a file and what's reported on disk in ZFS, varying according to metadata and compression. Per the documentation in perldoc -f stat I don't see any way enough information could be extracted from Perl's stat to divine what's actually on disk in the unique case of ZFS. If all else fails and you need to know for certain what's actually on the disk at all costs, as an ugly last recourse you could simply shell out to du.

See here.

cikkle
`du` would be my choice here, as it provides the data the OP is looking for and it's easy to call it for either an individual file or for an entire directory of content.
Ether
+2  A: 

The value exposed in (stat _)[11] is st_blksize, which is documented as

A hint as to the "best" unit size for I/O operations. This field is not defined for block special or character special files.

This is not necessarily the block size of the particular filesystem on which your file resides, but the same manual page contains a convenient definition:

 blkcnt_t st_blocks;   /* Number of 512 byte blocks allocated*/

So you could use code such as

#! /usr/bin/perl

use warnings;
use strict;

sub usage { "Usage: $0 file ..\n" }

die usage unless @ARGV;

foreach my $file (@ARGV) {
  my $dir = dirname $file;

  my $blocks = (stat $file)[12];
  unless (defined $blocks) {
    warn "$0: stat $file: $!\n";
    next;
  }

  print "$file - ", $blocks * 512, "\n";
}

If you're concerned that the block sizes of your filesystems aren't multiples of 512, double-check with either

df -g <directory>

or if you have root

fstyp -v /dev/dsk/...

For an ordinary file, the size of the file itself, i.e., (stat _)[7], is usually smaller than the total size of all blocks allocated because filesystems allocate whole blocks.

Greg Bacon