reflink

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2025 License: MIT Imports: 8 Imported by: 5

README

GoDoc

A Go library to perform efficient file copies using reflink operations on compatible filesystems (btrfs or xfs).

There are several types of links in file systems:

  • symlinks (symbolic links) - pointer to another file path
  • hardlinks - multiple directory entries pointing to the same inode/data
  • reflinks - copy-on-write links where files initially share the same data blocks

Reflinks are a modern file system feature found in btrfs and xfs that act similar to hard links but with a key difference: modifying one file doesn't affect the other. Only the modified portions will consume additional disk space (copy-on-write). This makes reflinks ideal for efficient file copies, snapshots, and deduplication.

  • Space efficiency: Initial reflink copies use virtually no additional disk space
  • Performance: Creating a reflink is nearly instantaneous, even for very large files
  • Safety: Changes to one file don't affect the other file's contents
  • Transparency: Applications don't need to be aware they're using reflinked files

Compatibility

A system needs a compatible OS and filesystem to perform reflinks. Currently supported:

  • btrfs on Linux
  • xfs on Linux (with reflink=1 mount option)

Other operating systems have similar features that may be implemented in future versions:

  • Windows has DUPLICATE_EXTENTS_TO_FILE
  • Solaris has reflink
  • MacOS has clonefile

Usage

Basic usage
// Creates a reflink copy or fails if reflink is not supported
err := reflink.Always("original_file.bin", "snapshot-001.bin")

// Creates a reflink copy if supported, falls back to regular copy if not
err := reflink.Auto("source_img.png", "modified_img.png")
Working with file handles
src, _ := os.Open("source.dat")
defer src.Close()
dst, _ := os.Create("dest.dat")
defer dst.Close()

// Reflink the entire file
err := reflink.Reflink(dst, src, true) // true enables fallback to regular copy

// Reflink just a portion of the file
// Copy 1MB from offset 2MB in source to offset 0 in destination
err := reflink.Partial(dst, src, 0, 2*1024*1024, 1*1024*1024, true)

Error handling

The library defines these specific errors:

  • ErrReflinkUnsupported - Returned when reflink is not supported on this OS
  • ErrReflinkFailed - Returned when reflink is not supported on the specific filesystem or files

Use standard Go error handling to check for these:

err := reflink.Always(src, dst)
if err != nil {
    if errors.Is(err, reflink.ErrReflinkUnsupported) {
        // OS doesn't support reflinks
    } else if errors.Is(err, reflink.ErrReflinkFailed) {
        // These specific files or filesystem don't support reflinks
    } else {
        // Other error (permissions, file not found, etc.)
    }
}

Fallback mechanism

When using Auto() or setting fallback=true in other functions, the library tries these methods in order:

  1. FICLONE ioctl (real reflink)
  2. copy_file_range syscall (efficient kernel-space copy)
  3. io.Copy (regular userspace copy)

Requirements

  • Source and destination files must be on the same filesystem
  • The filesystem must support reflinks (btrfs, xfs with reflink=1)
  • Appropriate file permissions are needed

Notes

  • The arguments are in the same order as os.Link or os.Rename (src, dst) rather than io.Copy (dst, src) as we are dealing with filenames
  • For optimal performance with large files, always try to use reflinks instead of regular copies
  • Reflinks are transparent to applications - a reflinked file behaves exactly like a regular file

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrReflinkUnsupported indicates that reflink operations are not supported on the current OS
	ErrReflinkUnsupported = errors.New("reflink is not supported on this OS")

	// ErrReflinkFailed indicates that the reflink operation failed because the specific
	// filesystem or files don't support it. This can happen if the files are on different
	// filesystems or if the filesystem doesn't support reflinks (e.g., ext4).
	ErrReflinkFailed = errors.New("reflink is not supported on this OS or file")
)

ErrReflinkUnsupported is returned by Always() if the reflink operation is not supported on the current operating system. This happens on non-Linux systems or when the necessary system calls aren't available.

Auto() will never return this error since it falls back to regular copy methods.

Functions

func Always

func Always(src, dst string) error

Always will perform a reflink operation and fail on error if reflink is not supported. It creates a copy of the source file at the destination using the filesystem's copy-on-write mechanism, which is extremely fast and space-efficient.

This is equivalent to command `cp --reflink=always` on Linux systems. Both files must be on the same filesystem that supports reflinks (btrfs, xfs).

Returns ErrReflinkUnsupported if the OS doesn't support reflinks, or ErrReflinkFailed if the specific filesystem doesn't support reflinks.

func Auto

func Auto(src, dst string) error

Auto will attempt to perform a reflink operation and fallback to normal data copy if reflink is not supported. This is the safer option for general use.

The fallback mechanism follows this priority: 1. Try reflink (FICLONE ioctl) 2. Try copy_file_range syscall (more efficient than userspace copy) 3. Fallback to regular io.Copy

This is equivalent to `cp --reflink=auto` on Linux systems.

func Partial

func Partial(dst, src *os.File, dstOffset, srcOffset, n int64, fallback bool) error

Partial performs a range reflink operation on the passed files, replacing part of dst's contents with data from src. This allows for more fine-grained control over which parts of the file are copied.

Parameters:

  • dst: Destination file handle
  • src: Source file handle
  • dstOffset: Offset in the destination file where data should be written
  • srcOffset: Offset in the source file where data should be read from
  • n: Number of bytes to copy
  • fallback: Whether to fall back to regular copy methods if reflink fails

If fallback is true and reflink fails, copy_file_range will be tried first, and if that fails too, io.CopyN with appropriate readers/writers will be used.

This function is useful for selectively copying parts of large files without having to read and write the entire file contents.

func Reflink(dst, src *os.File, fallback bool) error

Reflink performs the reflink operation on the passed files, replacing dst's contents with src. This function works with already-open file handles.

If fallback is true and reflink fails (on unsupported filesystems), copy_file_range will be tried first, and if that fails too, io.Copy will be used to copy the data. When using io.Copy, the destination file will be truncated first.

Note: Unlike Always() and Auto(), this function requires you to open and close the file handles yourself, which gives more control but requires more careful handling.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL