mirror of
https://github.com/restic/restic.git
synced 2025-03-09 00:00:02 +01:00
fs: add Clone() and fallback implementation via io.Copy()
This commit is contained in:
parent
98b51357b0
commit
e42ad4987a
1 changed files with 66 additions and 0 deletions
|
@ -2,10 +2,35 @@ package fs
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type cloneMethod func(src, dst *os.File) error
|
||||
|
||||
var (
|
||||
mCloneMethods = &sync.Mutex{}
|
||||
cloneMethods = make([]cloneMethod, 0, 1)
|
||||
)
|
||||
|
||||
func registerCloneMethod(method cloneMethod) {
|
||||
mCloneMethods.Lock()
|
||||
defer mCloneMethods.Unlock()
|
||||
|
||||
cloneMethods = append(cloneMethods, method)
|
||||
if len(cloneMethods) > 1 {
|
||||
var names []string
|
||||
for _, m := range cloneMethods {
|
||||
names = append(names, runtime.FuncForPC(reflect.ValueOf(m).Pointer()).Name())
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "warning: more than one clone method: %v\n", names)
|
||||
}
|
||||
}
|
||||
|
||||
// MkdirAll creates a directory named path, along with any necessary parents,
|
||||
// and returns nil, or else returns an error. The permission bits perm are used
|
||||
// for all directories that MkdirAll creates. If path is already a directory,
|
||||
|
@ -89,3 +114,44 @@ func Readdirnames(filesystem FS, dir string, flags int) ([]string, error) {
|
|||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func doCloneCopy(src, dest *os.File) error {
|
||||
srcInfo, err := src.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := io.Copy(dest, src)
|
||||
if n > 0 && n != srcInfo.Size() {
|
||||
return errors.Wrapf(err, "io.Copy() wrote %d of %d bytes", n, srcInfo.Size())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func doClone(src, dest *os.File) (cloned bool, err error) {
|
||||
for _, fn := range cloneMethods {
|
||||
return true, fn(src, dest)
|
||||
}
|
||||
return false, doCloneCopy(src, dest)
|
||||
}
|
||||
|
||||
// Clone performs a local possibly accelerated copy of srcName to destName.
|
||||
// The cloned flag reports whether an accelerated copy (reflink) was performed.
|
||||
func Clone(srcName, destName string) (cloned bool, err error) {
|
||||
src, err := OpenFile(srcName, O_RDONLY|O_NOFOLLOW, 0)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer func() {
|
||||
_ = src.Close()
|
||||
}()
|
||||
|
||||
dest, err := OpenFile(destName, O_CREATE|O_WRONLY|O_NOFOLLOW, 0600)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer func() {
|
||||
_ = dest.Close()
|
||||
}()
|
||||
|
||||
return doClone(src, dest)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue