69 lines
1.9 KiB
Go
Executable File
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
|
|
)
|