Re: Understanding reported block count for sparse files

From: Alan Somers <asomers_at_freebsd.org>
Date: Wed, 23 Jul 2025 18:52:11 UTC
On Wed, Jul 23, 2025 at 12:33 PM Roman Bogorodskiy <novel@freebsd.org>
wrote:

> Hi,
>
> I'm porting a test from Linux, and this test checks the allocated block
> count for the sparse file. Its expectation is that the block count
> should be close to zero.
>
> To isolate things, it roughly does the following:
>
> Linux:
>
> $ truncate -s +52428800 test.raw
> $ stat test.raw
>   File: test.raw
>   Size: 52428800        Blocks: 0          IO Block: 4096   regular file
> Device: 252,0   Inode: 25218599    Links: 1
> Access: (0644/-rw-r--r--)  Uid: ( 1000/   novel)   Gid: ( 1000/   novel)
> Context: unconfined_u:object_r:user_home_t:s0
> Access: 2025-07-23 14:20:31.640547511 -0400
> Modify: 2025-07-23 14:20:31.642105574 -0400
> Change: 2025-07-23 14:20:31.642105574 -0400
>  Birth: 2025-07-23 14:20:31.640547511 -0400
>
> It reports 0 blocks.
> Filesystem here is xfs.
>
> FreeBSD:
>
> $ truncate -s +52428800 test.raw
> $ stat -f "%z %k %b" test.raw
> 52428800 32768 128
>
> This reports 128 blocks. Filesystem here is UFS.
>
> What's reason behind this difference?
>
> Thanks,
> Roman


That "Blocks" figure comes from the "st_blocks" field, returned by
VOP_STAT.  It is entirely up to the file system what to put in that field.
For example, if the file system is compressed, should st_blocks describe
the blocks used before or after compression?  ZFS reports blocks after
compression, but IIRC btrfs reports blocks before compression.  Both are
legal.

In this very specific case I can tell you why you are seeing this
difference.  UFS organizes files in a tree of indirect blocks.  The tree
can be up to 4 levels high, IIRC.  So what's probably happening is that UFS
is actually allocating all of the L2 and/or L1 blocks for the file, even
though it's totally sparse, but not allocating any L0 blocks.  If you
really want to know exactly what's going on, we can probably explore the
file in detail with filesystems/fuse-ufs.

XFS, OTOH, is different.  It's an extent-based file system.  There's no
such thing as indirect blocks.  So when you create a huge but sparse file
in XFS, all it needs to allocate is the inode.  The number of metadata
blocks is determined not so much by the file's size as by its
fragmentation.  It's technically possible, if the file is fully
defragmented, to represent a 1 TB file with no metadata blocks except for
the inode.  But unlikely in practice.  You could try using
filesystems/xfuse or filesystems/lkl to see what that XFS file system looks
like when mounted on FreeBSD, too.

-Alan