sorcery-go/pkg/state/reflink_linux.go

69 lines
1.9 KiB
Go
Executable File

package state
import (
"fmt"
"os"
"syscall"
)
// reflink tries the Linux FICLONE ioctl to share data extents between
// src and dst (instant on btrfs/xfs, zero extra disk). Returns an error
// on unsupported filesystems; the caller falls back to io.Copy.
//
// We deliberately don't try FICLONE_RANGE — whole-file clone is all we
// need and it keeps the syscall arg structure trivial.
func reflink(src, dst string) error {
srcF, err := os.Open(src)
if err != nil {
return err
}
defer srcF.Close()
dstF, err := os.Create(dst)
if err != nil {
return err
}
defer dstF.Close()
// FICLONE = _IOW(0x94, 9, int) on Linux. The ioctl takes the source
// file's fd as its third argument.
const FICLONE = 0x40049409
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL,
dstF.Fd(),
uintptr(FICLONE),
srcF.Fd())
if errno != 0 {
return fmt.Errorf("reflink: ioctl FICLONE failed (fs=%s): %w",
detectFSType(dst), errno)
}
return nil
}
// detectFSType returns the filesystem magic name for path, best-effort.
// Used only for nicer error messages.
func detectFSType(path string) string {
var statfs syscall.Statfs_t
if err := syscall.Statfs(path, &statfs); err != nil {
return "unknown"
}
switch statfs.Type {
case fsMagicBTRFS:
return "btrfs"
case fsMagicXFS:
return "xfs"
case fsMagicFUSE:
return "fuse"
case fsMagicEXT4:
return "ext4"
}
return fmt.Sprintf("magic=0x%x", statfs.Type)
}
// Filesystem magic numbers from linux/magic.h.
const (
fsMagicBTRFS = 0x9123683E
fsMagicXFS = 0x58465342
fsMagicFUSE = 0x65735546
fsMagicEXT4 = 0xEF53
)