Preallocate and Reclaim File Space with fallocate

A disk image that grows one write at a time gets fragmented, and fallocate fixes that by reserving space up front without writing a byte. You will also learn to punch holes back out of a file and check exactly what changed on disk. The examples use fallocate from util-linux 2.41.3 installed on this machine; the installed manual page identifies util-linux 2.39.3, so the command's own --help output is the better authority for the newer options.

Before you start

You need a Linux filesystem that implements the operation you choose, a writable target path, and enough free space for any allocation. Most ordinary files need no elevated privileges. Use sudo only when the target is owned by another account or sits in a protected directory, and check the path carefully before doing so.

Allow about five minutes for the examples. The operations themselves are usually quick, but support and filesystem block alignment can vary. Keep a backup before changing a file that contains data. fallocate has no general undo command.

1. Check the local command

  1. Confirm the version and options you actually have.
fallocate --version
fallocate --help

You should see version 2.41.3 here. The normal allocation form needs -l for a length and then a filename. Lengths and offsets are bytes by default. Binary suffixes such as MiB and decimal suffixes such as MB are accepted; K means the same as KiB.

2. Reserve space for a file

  1. Create or extend a file with one mebibyte of allocated space.
fallocate -l 1MiB /path/to/work/cache.bin

The command creates the file if the default allocation operation can do so, or extends an existing file. It allocates filesystem blocks without first filling the data blocks with a stream of zero bytes, which is normally much faster than using a shell loop or dd for the same reservation.

Check both the apparent length and the allocated blocks:

stat -c 'size=%s bytes, blocks=%b' /path/to/work/cache.bin
du -h /path/to/work/cache.bin

For a one mebibyte file, stat should report size=1048576. The block count depends on the filesystem, so do not treat a particular number as portable. du reports space charged to the file, while stat's size reports its apparent length.

Reserve space without changing the apparent length

Use --keep-size when you need to allocate beyond the current end of file without making the file appear longer:

fallocate --keep-size --offset 0 --length 1MiB /path/to/work/existing.bin

This can allocate blocks past EOF. The manual page says those blocks can later be removed with truncate. Do not confuse this with writing data: reads beyond EOF still report end of file.

3. Create a hole in an existing range

Punching a hole deallocates whole filesystem blocks in a range and makes later reads from that range return zeroes. Partial blocks are zeroed as needed, and the file's apparent length stays the same.

  1. Make a disposable test file, then punch a range from it.
printf 'abcdefghij' > /tmp/fallocate-example.bin
fallocate --punch-hole --offset 2 --length 4 /tmp/fallocate-example.bin
od -An -tx1 -v /tmp/fallocate-example.bin
stat -c 'size=%s bytes, blocks=%b' /tmp/fallocate-example.bin

The bytes shown by od should be 61 62 00 00 00 00 67 68 69 6a. The file remains ten bytes long. On a small file, the filesystem may not reclaim a whole block, so a lower block count is not guaranteed. The operation is still destructive to the original bytes in the selected range. Restore the file from a backup if the range was wrong; there is no inverse fallocate operation that reconstructs the old contents.

The manual lists hole punching support for XFS, ext4, Btrfs, tmpfs and gfs2, with filesystem and kernel version qualifications. Other filesystems can reject it. A failure returns exit status 1; test that status in scripts rather than assuming the request was accepted.

Other range operations and their boundaries

These options change file layout or contents and deserve a backup first:

--collapse-range, --dig-holes, --punch-hole and --zero-range are mutually exclusive. Do not combine them to express a multi-stage plan. Run separate, verified commands and stop after each one if the data matters.

When allocation fails

A successful exit status is 0; failure is 1. The most common causes are insufficient free space, a read-only filesystem, a filesystem that does not support the requested operation, invalid ranges, and alignment requirements. Check the target filesystem and free space before changing the command:

df -h /path/to/work/cache.bin
findmnt -T /path/to/work/cache.bin

For a range operation, verify that the offset and length are non-negative, that the range fits the intended file, and that the filesystem's block-size rules are satisfied. --posix uses posix_fallocate(3) instead of the Linux fallocate(2) call. It can take longer, but is useful when the fast filesystem-specific operation is unavailable. It does not make destructive range operations portable.

Done means