package cowutils import ( "context" "fmt" "io" "io/fs" "os" ) // Reflink performs the reflink operation on the passed files, replacing // dst's contents with src. If fallback is true and reflink fails, // copy_file_range will be used first, and if that fails too io.Copy will // be used to copy the data. func Reflink(ctx context.Context, dst, src *os.File, fallback bool) error { err := reflink(dst, src) if (err != nil) && fallback { // reflink failed, but we can fallback, but first we need to know the file's size var st fs.FileInfo st, err = src.Stat() if err != nil { // couldn't stat source, this can't be helped return fmt.Errorf("failed to stat source: %w", err) } _, err = copyFileRange(dst, src, 0, 0, st.Size()) if err != nil { // copyFileRange failed too, switch to simple io copy reader := io.NewSectionReader(src, 0, st.Size()) writer := §ionWriter{w: dst} _ = dst.Truncate(0) // assuming any error in trucate will result in copy error _, err = io.Copy(writer, reader) } } return err } // ReflinkRange performs a range reflink operation on the passed files, replacing // part of dst's contents with data from src. If fallback is true and reflink // fails, copy_file_range will be used first, and if that fails too io.CopyN // will be used to copy the data. func ReflinkRange(ctx context.Context, dst, src *os.File, dstOffset, srcOffset, n int64, fallback bool) error { err := reflinkRange(dst, src, dstOffset, srcOffset, n) if (err != nil) && fallback { _, err = copyFileRange(dst, src, dstOffset, srcOffset, n) } if (err != nil) && fallback { // seek both src & dst reader := io.NewSectionReader(src, srcOffset, n) writer := §ionWriter{w: dst, base: dstOffset} _, err = io.CopyN(writer, reader, n) } return err }