Australian cloud for business software — built and run in Sydney since 2003

NetLeverage
NetLeverage

Sparse files, hard links and reflinks: why a restore can be bigger than the source

Technical article
Backup & Recovery

About this article: AI-assisted writing, reviewed against the cited sources. This is source research, not a lab validation of the procedures. Check the stated versions and your own recovery requirements before making changes.

A familiar surprise in recovery: the original server used 400 GB, the backup finished, and the restore to a disk of the same size fails partway through because the disk is full.

One possible cause is a change in how the target allocates storage. The backup may still contain the intended bytes, but capacity, metadata overhead, compression and deduplication also need checking. Three common space-sharing features explain why a plain copy can expand:

  • Sparse files, whose empty regions take no disk space.
  • Hard links, where several names point to one file.
  • Reflinks, where separate files share the same blocks on disk until one changes.

Their savings depend on how the restore represents the data on the destination. A backup or restore that copies the data naively writes it all out in full. This guide explains the three features from the primary documentation, how GNU tar, rsync and cp handle them, and how to plan so a restore fits.

Why it matters

The disk on a replacement server is often sized from what the old one used, not from the total size of every file. If the restore expands the savings, the restore stops when the disk fills, often hours in, and sometimes leaves a half-written system.

Expansion also affects behaviour, not just space. Two names that used to be one file become two separate files, so a change made through one name no longer appears through the other. Some applications depend on that link.

How it works

Two sizes for every file

The GNU coreutils du manual distinguishes the size a file reports from the space it uses. Its --apparent-size option prints "apparent sizes, rather than file system usage." The manual's own example is a file with an apparent size of 2 GiB that "on most modern file systems, it actually uses almost no space."

That gap is the first thing to measure before a restore.

Sparse files and holes

The lseek(2) manual page explains how a hole comes about. A program can move past the end of a file and write there. Reads of the gap "return null bytes" until data is actually written into it.

The GNU tar manual describes the result: "A hole in a file is a section of the file's contents which was never written. The contents of a hole reads as all zeros. On many operating systems, actual disk storage is not allocated for holes, but they are counted in the length of the file."

Our reading: virtual machine disk images, database files and some log and cache files are commonly created this way. A disk image that reports 100 GB may use only the space its guest has written.

Since Linux 3.1, programs can ask where holes are. lseek(2) describes the SEEK_DATA and SEEK_HOLE options and says they "allow applications to map holes in a sparsely allocated file." It names backup tools as the use case: "file backup tools, which can save space when creating backups and preserve holes, if they have a mechanism for discovering holes."

There is a firm limit. The same page says that "a filesystem is not obliged to report holes, so these operations are not a guaranteed mechanism for mapping the storage space actually allocated to a file." It adds that zeros actually written to disk "may not be reported as a hole."

Holes can also be created later. The fallocate(2) page describes a punch-hole operation that "deallocates space (i.e., creates a hole)". Our reading: a file can become sparse long after it was first written, so a file's history is not a guide to whether it is sparse now.

Hard links: one file, several names

The link(2) page defines a hard link as a new name for an existing file: "both names refer to the same file (and so have the same permissions and ownership) and it is impossible to tell which name was the 'original'." It also notes that hard links "cannot span filesystems."

Hard links are common in package-managed systems, in some mail stores, and especially in snapshot-style backup folders, where unchanged files in each dated folder are hard links to the same file. A folder of thirty such snapshots can contain thirty names for each unchanged file but store it once.

du counts these once. The manual says: "If two or more hard links point to the same file, only one of the hard links is counted." It warns that "The file argument order affects which links are counted". The --count-links option counts the size again for every name.

Our reading: comparing du with and without --count-links helps expose repeated hard-link names. It is not a complete target-capacity estimate: apparent size, sparse allocation, shared extents, compression and filesystem overhead remain separate factors.

Reflinks: separate files, shared blocks

Reflinks are newer. The ioctl_ficlonerange(2) page describes them: if a file system "supports files sharing physical storage between multiple files", which the page calls reflink, a program can make data from one file "appear in the dest_fd file by sharing the underlying storage, which is faster than making a separate physical copy of the data."

Unlike hard links, reflinked files are separate files. The same page says that if a write "should occur to a shared region, the filesystem must ensure that the changes remain private to the file being written." The page calls this copy on write. Both files "must reside within the same filesystem."

The page lists XFS and Btrfs when it describes errors, and the call dates from Linux 4.5; it was earlier private to Btrfs. Our reading: reflinks appear on Btrfs and XFS systems, on NAS devices built on Btrfs, and wherever administrators make quick copies of large files with cp.

The coreutils cp manual adds a caution about sharing: "the source and destination files share the same data blocks as long as they remain unmodified. Thus, if an I/O error affects data blocks of one of the files, the other suffers the same fate."

Unsharing has a cost too. fallocate(2) has an unshare mode in which shared data "will be made private to the file". Our reading: writing a shared region allocates private space for the changed region; unchanged extents can remain shared. Writing independent full copies can remove the wider savings, while deduplication or compression on the target can alter the eventual allocation.

How the tools treat each one

GNU tar

Sparse files. GNU tar does not look for holes unless asked. The manual warns that without the option, "tar could create an archive longer than the original." With --sparse, tar "records in the archive for the file where the holes (consecutive stretches of zeros) are", and then archives only the real contents of the file.

The manual adds that the option "is meaningful only when creating or updating archives. It has no effect on extraction." Holes are recreated at extraction automatically: "On extraction (using --sparse is not needed on extraction) any such files have also holes created wherever the holes were found."

Our reading: the decision is made at backup time. Without recorded sparse metadata, ordinary extraction may allocate the zero-filled regions. Adding tar's sparse option at extraction does not invent the missing map. A later sparse-aware copy can sometimes turn long zero runs into holes, as the cp documentation describes, but requires suitable space, a supported target and validation; it is not preservation of the original allocation map.

The manual recommends the option for whole-system backups: "Consider using --sparse when performing file system backups, to avoid archiving the expanded forms of files stored sparsely in the system." It warns of a cost: where the system lacks SEEK_HOLE support, "the file may be read twice".

Hard links. GNU tar keeps hard links by default. The Hard Links page says it writes a block naming the link target, so "the actual file contents is stored in file only once."

The same page describes a restore trap. Extracting one linked name on its own, without the name the archive stored the data under, fails with an error that tar "Cannot hard link to" the missing name. The manual explains that tar "cannot seek back in the archive to the previous member". Our reading: a partial restore of a few files from a heavily linked tree may need the other names too.

The --check-links option warns when not all links to a file were archived. The --hard-dereference option stores each name as a full copy, "at the cost of a bigger archive".

Reflinks. The tar pages we read do not mention shared blocks. Our reading: do not assume tar preserves cross-file reflink relationships. Budget for independent file contents unless the selected restore path explicitly proves sharing, sparse allocation or another saving.

rsync

The rsync manual is explicit that archive mode leaves things out. -a "does not include preserving ACLs (-A), xattrs (-X), atimes (-U), crtimes (-N), nor the finding and preserving of hardlinks (-H)."

Hard links need -H. Without it, "hard-linked files in the source are treated as though they were separate files." Even with it, the manual cautions that it "does NOT necessarily ensure that the pattern of hard links on the destination exactly matches that on the source", and "rsync can only detect hard links between files that are inside the transfer set."

Our reading: restoring a snapshot-style backup folder one dated folder at a time breaks the links between folders, even with -H, because each run sees only its own folder.

Sparse files need -S, which tries "to handle sparse files efficiently so they take up less space on the destination." It "relies on the destination filesystem supporting sparse files". The manual warns that combined with --inplace, "the file created may not end up with sparse blocks (depending on the filesystem type and kernel version), unless the --whole-file option is also in effect."

--inplace has another effect worth knowing in a restore: "Hard links are not broken. This means the new data will be visible through other hard links to the destination file." Our reading: updating a file in place on a destination with stray hard links changes every linked name.

Reflinks. None of the rsync options we read describe preserving shared blocks between separate files. Our reading: ordinary rsync transfer does not establish preservation of the source's cross-file shared extents. Target allocation can still differ because of sparse handling, compression or storage-level deduplication.

cp

GNU cp handles sparse files by default. The manual says: "By default, cp detects holes in input source files via a crude heuristic and makes the corresponding output file sparse as well." The always setting creates holes for long runs of zeros even when the source is not sparse; never writes every byte.

For hard links, cp's links attribute will "Preserve in the destination files any links between corresponding source files", and archive mode (-a) includes all attributes.

For reflinks, cp makes a copy-on-write copy where the file system supports it. In the current upstream GNU cp manual, without a --reflink option the default is auto, which falls back to a normal copy if sharing is not possible. Check the installed version's manual before relying on that default. Our reading: cp creates a new reflink from a source to a copy; it does not discover that two source files already shared blocks and recreate that sharing on the destination.

A worked example

This illustration applies the documented rules to an imagined server; it was not run.

A file server reports 400 GB used. It holds:

  • 120 GB of ordinary files.
  • Two virtual machine disk images with an apparent size of 200 GB each, of which 80 GB in total is written.
  • A snapshot-style backup folder of thirty dated copies of a 20 GB tree; unchanged files are hard links, and the folder uses 100 GB of real space but 600 GB counting each name.
  • A 100 GB dataset on Btrfs, plus reflinked copies of it made with cp that add another 100 GB of file size but share the original's blocks, so they use almost no extra space.

A restore of everything with rsync's archive mode alone, to a non-reflink file system, needs about 120 + 400 + 600 + 100 + 100 = 1,320 GB. With -S and -H in one run, about 120 + 80 + 100 + 100 + 100 = 500 GB, because reflinks still expand. These are illustrative logical-data totals, not measured capacity requirements; metadata, free-space reserve, compression and block rounding are omitted. A method that preserves the sharing, or a target that safely recreates equivalent savings, could approach the smaller total. Size an actual recovery from measured allocation and a tested representative restore.

A planning checklist

These are our recommendations, drawn from the sources above.

  1. Measure both sizes at backup time. Record du with and without apparent sizes, and with and without counting links, for the main folders.
  2. Back up sparse files as sparse. With GNU tar, sparse metadata is captured at creation; the extraction flag cannot recover a map that was never recorded.
  3. Keep hard links in one pass. With rsync, use -H and restore linked trees in a single run that includes every linked name.
  4. Expect reflinks to expand with file-level tools. Size the target for the unshared total, or plan a file-system-level method.
  5. Check the destination file system. Sparse files and reflinks both depend on it; a target that cannot store holes or shared blocks will hold full copies.
  6. Test partial restores of hard-linked trees, so an error naming a missing link target does not come as a surprise during an incident.
  7. Compare after the restore. Measure the restored data the same way and compare it with the record from step 1.

Risks and limits

  • Space savings are fragile. Sparse files, hard links and reflinks each expand when a tool does not keep them.
  • Hole reports are not guaranteed. A file system is not obliged to report holes, and written zeros may not count as holes.
  • rsync archive mode does not keep hard links. -H is separate, and works only inside one transfer.
  • Sparse handling in GNU tar is chosen when the archive is made.
  • Reflinked files share faults. An I/O error on a shared block affects every file using it.
  • Nothing here was run. Behaviour is quoted from the documentation for planning; rehearse on a non-production system.

Sources