chore(deps): bump github.com/otiai10/copy from 1.7.0 to 1.11.0 (#2523)
Bumps [github.com/otiai10/copy](https://github.com/otiai10/copy) from 1.7.0 to 1.11.0. - [Release notes](https://github.com/otiai10/copy/releases) - [Commits](https://github.com/otiai10/copy/compare/v1.7.0...v1.11.0) --- updated-dependencies: - dependency-name: github.com/otiai10/copy dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
parent
b4f3885a92
commit
74b2b6c9ef
2
go.mod
2
go.mod
|
|
@ -20,7 +20,7 @@ require (
|
||||||
github.com/karrick/godirwalk v1.16.1
|
github.com/karrick/godirwalk v1.16.1
|
||||||
github.com/minio/highwayhash v1.0.2
|
github.com/minio/highwayhash v1.0.2
|
||||||
github.com/moby/buildkit v0.11.4
|
github.com/moby/buildkit v0.11.4
|
||||||
github.com/otiai10/copy v1.7.0
|
github.com/otiai10/copy v1.11.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/spf13/afero v1.9.5
|
github.com/spf13/afero v1.9.5
|
||||||
|
|
|
||||||
8
go.sum
8
go.sum
|
|
@ -2104,14 +2104,14 @@ github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm
|
||||||
github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||||
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
||||||
github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE=
|
github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc=
|
||||||
github.com/otiai10/copy v1.7.0/go.mod h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
|
github.com/otiai10/copy v1.11.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww=
|
||||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||||
github.com/otiai10/mint v1.3.3 h1:7JgpsBaN0uMkyju4tbYHu0mnM55hNKVYLsXmwr15NQI=
|
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
|
||||||
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||||
github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
|
github.com/package-url/packageurl-go v0.1.1-0.20220428063043-89078438f170/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
test/data.copy
|
test/data.copy
|
||||||
|
test/owned-by-root
|
||||||
coverage.txt
|
coverage.txt
|
||||||
vendor
|
vendor
|
||||||
.vagrant
|
.vagrant
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# Test Specific
|
||||||
|
test/data/case16/large.file
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,17 @@
|
||||||
# Example Usage
|
# Example Usage
|
||||||
|
|
||||||
```go
|
```go
|
||||||
err := Copy("your/directory", "your/directory.copy")
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
cp "github.com/otiai10/copy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
err := cp.Copy("your/src", "your/dest")
|
||||||
|
fmt.Println(err) // nil
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
# Advanced Usage
|
# Advanced Usage
|
||||||
|
|
@ -31,12 +41,24 @@ type Options struct {
|
||||||
// OnDirExists can specify what to do when there is a directory already existing in destination.
|
// OnDirExists can specify what to do when there is a directory already existing in destination.
|
||||||
OnDirExists func(src, dest string) DirExistsAction
|
OnDirExists func(src, dest string) DirExistsAction
|
||||||
|
|
||||||
// Skip can specify which files should be skipped
|
// OnError can let users decide how to handle errors (e.g., you can suppress specific error).
|
||||||
Skip func(src string) (bool, error)
|
OnError func(src, dest, string, err error) error
|
||||||
|
|
||||||
// AddPermission to every entry,
|
// Skip can specify which files should be skipped
|
||||||
// NO MORE THAN 0777
|
Skip func(srcinfo os.FileInfo, src, dest string) (bool, error)
|
||||||
AddPermission os.FileMode
|
|
||||||
|
// PermissionControl can control permission of
|
||||||
|
// every entry.
|
||||||
|
// When you want to add permission 0222, do like
|
||||||
|
//
|
||||||
|
// PermissionControl = AddPermission(0222)
|
||||||
|
//
|
||||||
|
// or if you even don't want to touch permission,
|
||||||
|
//
|
||||||
|
// PermissionControl = DoNothing
|
||||||
|
//
|
||||||
|
// By default, PermissionControl = PreservePermission
|
||||||
|
PermissionControl PermissionControlFunc
|
||||||
|
|
||||||
// Sync file after copy.
|
// Sync file after copy.
|
||||||
// Useful in case when file must be on the disk
|
// Useful in case when file must be on the disk
|
||||||
|
|
@ -61,7 +83,7 @@ type Options struct {
|
||||||
```go
|
```go
|
||||||
// For example...
|
// For example...
|
||||||
opt := Options{
|
opt := Options{
|
||||||
Skip: func(src string) (bool, error) {
|
Skip: func(info os.FileInfo, src, dest string) (bool, error) {
|
||||||
return strings.HasSuffix(src, ".git"), nil
|
return strings.HasSuffix(src, ".git"), nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// tmpPermissionForDirectory makes the destination directory writable,
|
|
||||||
// so that stuff can be copied recursively even if any original directory is NOT writable.
|
|
||||||
// See https://github.com/otiai10/copy/pull/9 for more information.
|
|
||||||
tmpPermissionForDirectory = os.FileMode(0755)
|
|
||||||
)
|
|
||||||
|
|
||||||
type timespec struct {
|
type timespec struct {
|
||||||
Mtime time.Time
|
Mtime time.Time
|
||||||
Atime time.Time
|
Atime time.Time
|
||||||
|
|
@ -22,17 +15,22 @@ type timespec struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy copies src to dest, doesn't matter if src is a directory or a file.
|
// Copy copies src to dest, doesn't matter if src is a directory or a file.
|
||||||
func Copy(src, dest string, opt ...Options) error {
|
func Copy(src, dest string, opts ...Options) error {
|
||||||
|
opt := assureOptions(src, dest, opts...)
|
||||||
info, err := os.Lstat(src)
|
info, err := os.Lstat(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return onError(src, dest, err, opt)
|
||||||
}
|
}
|
||||||
return switchboard(src, dest, info, assure(src, dest, opt...))
|
return switchboard(src, dest, info, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// switchboard switches proper copy functions regarding file type, etc...
|
// switchboard switches proper copy functions regarding file type, etc...
|
||||||
// If there would be anything else here, add a case to this switchboard.
|
// If there would be anything else here, add a case to this switchboard.
|
||||||
func switchboard(src, dest string, info os.FileInfo, opt Options) (err error) {
|
func switchboard(src, dest string, info os.FileInfo, opt Options) (err error) {
|
||||||
|
if info.Mode()&os.ModeDevice != 0 && !opt.Specials {
|
||||||
|
return onError(src, dest, err, opt)
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case info.Mode()&os.ModeSymlink != 0:
|
case info.Mode()&os.ModeSymlink != 0:
|
||||||
err = onsymlink(src, dest, opt)
|
err = onsymlink(src, dest, opt)
|
||||||
|
|
@ -44,20 +42,22 @@ func switchboard(src, dest string, info os.FileInfo, opt Options) (err error) {
|
||||||
err = fcopy(src, dest, info, opt)
|
err = fcopy(src, dest, info, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return onError(src, dest, err, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyNextOrSkip decide if this src should be copied or not.
|
// copyNextOrSkip decide if this src should be copied or not.
|
||||||
// Because this "copy" could be called recursively,
|
// Because this "copy" could be called recursively,
|
||||||
// "info" MUST be given here, NOT nil.
|
// "info" MUST be given here, NOT nil.
|
||||||
func copyNextOrSkip(src, dest string, info os.FileInfo, opt Options) error {
|
func copyNextOrSkip(src, dest string, info os.FileInfo, opt Options) error {
|
||||||
skip, err := opt.Skip(src)
|
if opt.Skip != nil {
|
||||||
|
skip, err := opt.Skip(info, src, dest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if skip {
|
if skip {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return switchboard(src, dest, info, opt)
|
return switchboard(src, dest, info, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,6 +65,14 @@ func copyNextOrSkip(src, dest string, info os.FileInfo, opt Options) error {
|
||||||
// with considering existence of parent directory
|
// with considering existence of parent directory
|
||||||
// and file permission.
|
// and file permission.
|
||||||
func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {
|
func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {
|
||||||
|
s, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fclose(s, &err)
|
||||||
|
|
||||||
if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
|
if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -76,19 +84,20 @@ func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {
|
||||||
}
|
}
|
||||||
defer fclose(f, &err)
|
defer fclose(f, &err)
|
||||||
|
|
||||||
if err = os.Chmod(f.Name(), info.Mode()|opt.AddPermission); err != nil {
|
chmodfunc, err := opt.PermissionControl(info, dest)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := os.Open(src)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
defer fclose(s, &err)
|
chmodfunc(&err)
|
||||||
|
|
||||||
var buf []byte = nil
|
var buf []byte = nil
|
||||||
var w io.Writer = f
|
var w io.Writer = f
|
||||||
// var r io.Reader = s
|
var r io.Reader = s
|
||||||
|
|
||||||
|
if opt.WrapReader != nil {
|
||||||
|
r = opt.WrapReader(s)
|
||||||
|
}
|
||||||
|
|
||||||
if opt.CopyBufferSize != 0 {
|
if opt.CopyBufferSize != 0 {
|
||||||
buf = make([]byte, opt.CopyBufferSize)
|
buf = make([]byte, opt.CopyBufferSize)
|
||||||
// Disable using `ReadFrom` by io.CopyBuffer.
|
// Disable using `ReadFrom` by io.CopyBuffer.
|
||||||
|
|
@ -96,7 +105,8 @@ func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {
|
||||||
w = struct{ io.Writer }{f}
|
w = struct{ io.Writer }{f}
|
||||||
// r = struct{ io.Reader }{s}
|
// r = struct{ io.Reader }{s}
|
||||||
}
|
}
|
||||||
if _, err = io.CopyBuffer(w, s, buf); err != nil {
|
|
||||||
|
if _, err = io.CopyBuffer(w, r, buf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,32 +132,24 @@ func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {
|
||||||
// with scanning contents inside the directory
|
// with scanning contents inside the directory
|
||||||
// and pass everything to "copy" recursively.
|
// and pass everything to "copy" recursively.
|
||||||
func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) {
|
func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) {
|
||||||
|
if skip, err := onDirExists(opt, srcdir, destdir); err != nil {
|
||||||
_, err = os.Stat(destdir)
|
|
||||||
if err == nil && opt.OnDirExists != nil && destdir != opt.intent.dest {
|
|
||||||
switch opt.OnDirExists(srcdir, destdir) {
|
|
||||||
case Replace:
|
|
||||||
if err := os.RemoveAll(destdir); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
} else if skip {
|
||||||
case Untouchable:
|
|
||||||
return nil
|
return nil
|
||||||
} // case "Merge" is default behaviour. Go through.
|
|
||||||
} else if err != nil && !os.IsNotExist(err) {
|
|
||||||
return err // Unwelcome error type...!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
originalMode := info.Mode()
|
|
||||||
|
|
||||||
// Make dest dir with 0755 so that everything writable.
|
// Make dest dir with 0755 so that everything writable.
|
||||||
if err = os.MkdirAll(destdir, tmpPermissionForDirectory); err != nil {
|
chmodfunc, err := opt.PermissionControl(info, destdir)
|
||||||
return
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
// Recover dir mode with original one.
|
defer chmodfunc(&err)
|
||||||
defer chmod(destdir, originalMode|opt.AddPermission, &err)
|
|
||||||
|
|
||||||
contents, err := ioutil.ReadDir(srcdir)
|
contents, err := ioutil.ReadDir(srcdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,10 +177,33 @@ func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func onDirExists(opt Options, srcdir, destdir string) (bool, error) {
|
||||||
|
_, err := os.Stat(destdir)
|
||||||
|
if err == nil && opt.OnDirExists != nil && destdir != opt.intent.dest {
|
||||||
|
switch opt.OnDirExists(srcdir, destdir) {
|
||||||
|
case Replace:
|
||||||
|
if err := os.RemoveAll(destdir); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
case Untouchable:
|
||||||
|
return true, nil
|
||||||
|
} // case "Merge" is default behaviour. Go through.
|
||||||
|
} else if err != nil && !os.IsNotExist(err) {
|
||||||
|
return true, err // Unwelcome error type...!
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
func onsymlink(src, dest string, opt Options) error {
|
func onsymlink(src, dest string, opt Options) error {
|
||||||
switch opt.OnSymlink(src) {
|
switch opt.OnSymlink(src) {
|
||||||
case Shallow:
|
case Shallow:
|
||||||
return lcopy(src, dest)
|
if err := lcopy(src, dest); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if opt.PreserveTimes {
|
||||||
|
return preserveLtimes(src, dest)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
case Deep:
|
case Deep:
|
||||||
orig, err := os.Readlink(src)
|
orig, err := os.Readlink(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -201,6 +226,9 @@ func onsymlink(src, dest string, opt Options) error {
|
||||||
func lcopy(src, dest string) error {
|
func lcopy(src, dest string) error {
|
||||||
src, err := os.Readlink(src)
|
src, err := os.Readlink(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return os.Symlink(src, dest)
|
return os.Symlink(src, dest)
|
||||||
|
|
@ -215,29 +243,12 @@ func fclose(f *os.File, reported *error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// chmod ANYHOW changes file mode,
|
// onError lets caller to handle errors
|
||||||
// with asiging error raised during Chmod,
|
// occured when copying a file.
|
||||||
// BUT respecting the error already reported.
|
func onError(src, dest string, err error, opt Options) error {
|
||||||
func chmod(dir string, mode os.FileMode, reported *error) {
|
if opt.OnError == nil {
|
||||||
if err := os.Chmod(dir, mode); *reported == nil {
|
return err
|
||||||
*reported = err
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// assure Options struct, should be called only once.
|
return opt.OnError(src, dest, err)
|
||||||
// All optional values MUST NOT BE nil/zero after assured.
|
|
||||||
func assure(src, dest string, opts ...Options) Options {
|
|
||||||
defopt := getDefaultOptions(src, dest)
|
|
||||||
if len(opts) == 0 {
|
|
||||||
return defopt
|
|
||||||
}
|
|
||||||
if opts[0].OnSymlink == nil {
|
|
||||||
opts[0].OnSymlink = defopt.OnSymlink
|
|
||||||
}
|
|
||||||
if opts[0].Skip == nil {
|
|
||||||
opts[0].Skip = defopt.Skip
|
|
||||||
}
|
|
||||||
opts[0].intent.src = defopt.intent.src
|
|
||||||
opts[0].intent.dest = defopt.intent.dest
|
|
||||||
return opts[0]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build !windows,!plan9,!netbsd,!aix,!illumos,!solaris
|
//go:build !windows && !plan9 && !netbsd && !aix && !illumos && !solaris && !js
|
||||||
|
// +build !windows,!plan9,!netbsd,!aix,!illumos,!solaris,!js
|
||||||
|
|
||||||
package copy
|
package copy
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build windows plan9 netbsd aix illumos solaris
|
//go:build windows || plan9 || netbsd || aix || illumos || solaris || js
|
||||||
|
// +build windows plan9 netbsd aix illumos solaris js
|
||||||
|
|
||||||
package copy
|
package copy
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,17 @@
|
||||||
|
//go:build !go1.16
|
||||||
|
// +build !go1.16
|
||||||
|
|
||||||
package copy
|
package copy
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
// This is a cloned definition of os.FileInfo (go1.15) or fs.FileInfo (go1.16~)
|
// This is a cloned definition of os.FileInfo (go1.15) or fs.FileInfo (go1.16~)
|
||||||
// A FileInfo describes a file and is returned by Stat.
|
// A FileInfo describes a file and is returned by Stat.
|
||||||
type fileInfo interface {
|
type fileInfo interface {
|
||||||
// Name() string // base name of the file
|
// Name() string // base name of the file
|
||||||
// Size() int64 // length in bytes for regular files; system-dependent for others
|
// Size() int64 // length in bytes for regular files; system-dependent for others
|
||||||
// Mode() FileMode // file mode bits
|
Mode() os.FileMode // file mode bits
|
||||||
// ModTime() time.Time // modification time
|
// ModTime() time.Time // modification time
|
||||||
// IsDir() bool // abbreviation for Mode().IsDir()
|
IsDir() bool // abbreviation for Mode().IsDir()
|
||||||
Sys() interface{} // underlying data source (can return nil)
|
Sys() interface{} // underlying data source (can return nil)
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
//go:build go1.16
|
||||||
|
// +build go1.16
|
||||||
|
|
||||||
|
package copy
|
||||||
|
|
||||||
|
import "io/fs"
|
||||||
|
|
||||||
|
// This is a cloned definition of os.FileInfo (go1.15) or fs.FileInfo (go1.16~)
|
||||||
|
// A FileInfo describes a file and is returned by Stat.
|
||||||
|
type fileInfo interface {
|
||||||
|
// Name() string // base name of the file
|
||||||
|
// Size() int64 // length in bytes for regular files; system-dependent for others
|
||||||
|
Mode() fs.FileMode // file mode bits
|
||||||
|
// ModTime() time.Time // modification time
|
||||||
|
IsDir() bool // abbreviation for Mode().IsDir()
|
||||||
|
Sys() interface{} // underlying data source (can return nil)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package copy
|
package copy
|
||||||
|
|
||||||
import "os"
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
// Options specifies optional actions on copying.
|
// Options specifies optional actions on copying.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
|
@ -11,13 +14,29 @@ type Options struct {
|
||||||
// OnDirExists can specify what to do when there is a directory already existing in destination.
|
// OnDirExists can specify what to do when there is a directory already existing in destination.
|
||||||
OnDirExists func(src, dest string) DirExistsAction
|
OnDirExists func(src, dest string) DirExistsAction
|
||||||
|
|
||||||
|
// OnErr lets called decide whether or not to continue on particular copy error.
|
||||||
|
OnError func(src, dest string, err error) error
|
||||||
|
|
||||||
// Skip can specify which files should be skipped
|
// Skip can specify which files should be skipped
|
||||||
Skip func(src string) (bool, error)
|
Skip func(srcinfo os.FileInfo, src, dest string) (bool, error)
|
||||||
|
|
||||||
|
// Specials includes special files to be copied. default false.
|
||||||
|
Specials bool
|
||||||
|
|
||||||
// AddPermission to every entities,
|
// AddPermission to every entities,
|
||||||
// NO MORE THAN 0777
|
// NO MORE THAN 0777
|
||||||
|
// @OBSOLETE
|
||||||
|
// Use `PermissionControl = AddPermission(perm)` instead
|
||||||
AddPermission os.FileMode
|
AddPermission os.FileMode
|
||||||
|
|
||||||
|
// PermissionControl can preserve or even add permission to
|
||||||
|
// every entries, for example
|
||||||
|
//
|
||||||
|
// opt.PermissionControl = AddPermission(0222)
|
||||||
|
//
|
||||||
|
// See permission_control.go for more detail.
|
||||||
|
PermissionControl PermissionControlFunc
|
||||||
|
|
||||||
// Sync file after copy.
|
// Sync file after copy.
|
||||||
// Useful in case when file must be on the disk
|
// Useful in case when file must be on the disk
|
||||||
// (in case crash happens, for example),
|
// (in case crash happens, for example),
|
||||||
|
|
@ -36,6 +55,11 @@ type Options struct {
|
||||||
// See https://golang.org/pkg/io/#CopyBuffer for more information.
|
// See https://golang.org/pkg/io/#CopyBuffer for more information.
|
||||||
CopyBufferSize uint
|
CopyBufferSize uint
|
||||||
|
|
||||||
|
// If you want to add some limitation on reading src file,
|
||||||
|
// you can wrap the src and provide new reader,
|
||||||
|
// such as `RateLimitReader` in the test case.
|
||||||
|
WrapReader func(src *os.File) io.Reader
|
||||||
|
|
||||||
intent struct {
|
intent struct {
|
||||||
src string
|
src string
|
||||||
dest string
|
dest string
|
||||||
|
|
@ -74,16 +98,41 @@ func getDefaultOptions(src, dest string) Options {
|
||||||
return Shallow // Do shallow copy
|
return Shallow // Do shallow copy
|
||||||
},
|
},
|
||||||
OnDirExists: nil, // Default behavior is "Merge".
|
OnDirExists: nil, // Default behavior is "Merge".
|
||||||
Skip: func(string) (bool, error) {
|
OnError: nil, // Default is "accept error"
|
||||||
return false, nil // Don't skip
|
Skip: nil, // Do not skip anything
|
||||||
},
|
|
||||||
AddPermission: 0, // Add nothing
|
AddPermission: 0, // Add nothing
|
||||||
|
PermissionControl: PerservePermission, // Just preserve permission
|
||||||
Sync: false, // Do not sync
|
Sync: false, // Do not sync
|
||||||
|
Specials: false, // Do not copy special files
|
||||||
PreserveTimes: false, // Do not preserve the modification time
|
PreserveTimes: false, // Do not preserve the modification time
|
||||||
CopyBufferSize: 0, // Do not specify, use default bufsize (32*1024)
|
CopyBufferSize: 0, // Do not specify, use default bufsize (32*1024)
|
||||||
|
WrapReader: nil, // Do not wrap src files, use them as they are.
|
||||||
intent: struct {
|
intent: struct {
|
||||||
src string
|
src string
|
||||||
dest string
|
dest string
|
||||||
}{src, dest},
|
}{src, dest},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// assureOptions struct, should be called only once.
|
||||||
|
// All optional values MUST NOT BE nil/zero after assured.
|
||||||
|
func assureOptions(src, dest string, opts ...Options) Options {
|
||||||
|
defopt := getDefaultOptions(src, dest)
|
||||||
|
if len(opts) == 0 {
|
||||||
|
return defopt
|
||||||
|
}
|
||||||
|
if opts[0].OnSymlink == nil {
|
||||||
|
opts[0].OnSymlink = defopt.OnSymlink
|
||||||
|
}
|
||||||
|
if opts[0].Skip == nil {
|
||||||
|
opts[0].Skip = defopt.Skip
|
||||||
|
}
|
||||||
|
if opts[0].AddPermission > 0 {
|
||||||
|
opts[0].PermissionControl = AddPermission(opts[0].AddPermission)
|
||||||
|
} else if opts[0].PermissionControl == nil {
|
||||||
|
opts[0].PermissionControl = PerservePermission
|
||||||
|
}
|
||||||
|
opts[0].intent.src = defopt.intent.src
|
||||||
|
opts[0].intent.dest = defopt.intent.dest
|
||||||
|
return opts[0]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package copy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// tmpPermissionForDirectory makes the destination directory writable,
|
||||||
|
// so that stuff can be copied recursively even if any original directory is NOT writable.
|
||||||
|
// See https://github.com/otiai10/copy/pull/9 for more information.
|
||||||
|
tmpPermissionForDirectory = os.FileMode(0755)
|
||||||
|
)
|
||||||
|
|
||||||
|
type PermissionControlFunc func(srcinfo fileInfo, dest string) (chmodfunc func(*error), err error)
|
||||||
|
|
||||||
|
var (
|
||||||
|
AddPermission = func(perm os.FileMode) PermissionControlFunc {
|
||||||
|
return func(srcinfo fileInfo, dest string) (func(*error), error) {
|
||||||
|
orig := srcinfo.Mode()
|
||||||
|
if srcinfo.IsDir() {
|
||||||
|
if err := os.MkdirAll(dest, tmpPermissionForDirectory); err != nil {
|
||||||
|
return func(*error) {}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func(err *error) {
|
||||||
|
chmod(dest, orig|perm, err)
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PerservePermission PermissionControlFunc = AddPermission(0)
|
||||||
|
DoNothing PermissionControlFunc = func(srcinfo fileInfo, dest string) (func(*error), error) {
|
||||||
|
if srcinfo.IsDir() {
|
||||||
|
if err := os.MkdirAll(dest, srcinfo.Mode()); err != nil {
|
||||||
|
return func(*error) {}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func(*error) {}, nil
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// chmod ANYHOW changes file mode,
|
||||||
|
// with asiging error raised during Chmod,
|
||||||
|
// BUT respecting the error already reported.
|
||||||
|
func chmod(dir string, mode os.FileMode, reported *error) {
|
||||||
|
if err := os.Chmod(dir, mode); *reported == nil {
|
||||||
|
*reported = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
//go:build !windows && !plan9 && !js
|
||||||
|
// +build !windows,!plan9,!js
|
||||||
|
|
||||||
|
package copy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
func preserveLtimes(src, dest string) error {
|
||||||
|
info := new(unix.Stat_t)
|
||||||
|
if err := unix.Lstat(src, info); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unix.Lutimes(dest, []unix.Timeval{
|
||||||
|
unix.NsecToTimeval(info.Atim.Nano()),
|
||||||
|
unix.NsecToTimeval(info.Mtim.Nano()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
//go:build windows || js || plan9
|
||||||
|
// +build windows js plan9
|
||||||
|
|
||||||
|
package copy
|
||||||
|
|
||||||
|
func preserveLtimes(src, dest string) error {
|
||||||
|
return nil // Unsupported
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
//+build !windows
|
//go:build !windows && !plan9
|
||||||
|
// +build !windows,!plan9
|
||||||
|
|
||||||
package copy
|
package copy
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
//+build windows
|
//go:build windows || plan9
|
||||||
|
// +build windows plan9
|
||||||
|
|
||||||
package copy
|
package copy
|
||||||
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build !windows,!darwin,!freebsd,!plan9,!netbsd
|
//go:build !windows && !darwin && !freebsd && !plan9 && !netbsd && !js
|
||||||
|
// +build !windows,!darwin,!freebsd,!plan9,!netbsd,!js
|
||||||
|
|
||||||
// TODO: add more runtimes
|
// TODO: add more runtimes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build darwin
|
||||||
// +build darwin
|
// +build darwin
|
||||||
|
|
||||||
package copy
|
package copy
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build freebsd
|
||||||
// +build freebsd
|
// +build freebsd
|
||||||
|
|
||||||
package copy
|
package copy
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
//go:build js
|
||||||
|
// +build js
|
||||||
|
|
||||||
|
package copy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTimeSpec(info os.FileInfo) timespec {
|
||||||
|
stat := info.Sys().(*syscall.Stat_t)
|
||||||
|
times := timespec{
|
||||||
|
Mtime: info.ModTime(),
|
||||||
|
Atime: time.Unix(int64(stat.Atime), int64(stat.AtimeNsec)),
|
||||||
|
Ctime: time.Unix(int64(stat.Ctime), int64(stat.CtimeNsec)),
|
||||||
|
}
|
||||||
|
return times
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build windows
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package copy
|
package copy
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build plan9 || netbsd
|
||||||
// +build plan9 netbsd
|
// +build plan9 netbsd
|
||||||
|
|
||||||
package copy
|
package copy
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build !windows,!plan9,!netbsd,!aix,!illumos,!solaris
|
//go:build !windows && !plan9 && !netbsd && !aix && !illumos && !solaris && !js
|
||||||
|
// +build !windows,!plan9,!netbsd,!aix,!illumos,!solaris,!js
|
||||||
|
|
||||||
package copy
|
package copy
|
||||||
|
|
||||||
|
|
@ -9,9 +10,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func setup(m *testing.M) {
|
func setup(m *testing.M) {
|
||||||
|
os.RemoveAll("test/data.copy")
|
||||||
os.MkdirAll("test/data.copy", os.ModePerm)
|
os.MkdirAll("test/data.copy", os.ModePerm)
|
||||||
os.Symlink("test/data/case01", "test/data/case03/case01")
|
os.Symlink("test/data/case01", "test/data/case03/case01")
|
||||||
os.Chmod("test/data/case07/dir_0555", 0555)
|
os.Chmod("test/data/case07/dir_0555", 0o555)
|
||||||
os.Chmod("test/data/case07/file_0444", 0444)
|
os.Chmod("test/data/case07/file_0444", 0o444)
|
||||||
syscall.Mkfifo("test/data/case11/foo/bar", 0555)
|
syscall.Mkfifo("test/data/case11/foo/bar", 0o555)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
// +build windows plan9 netbsd aix illumos solaris
|
//go:build windows || plan9 || netbsd || aix || illumos || solaris || js
|
||||||
|
// +build windows plan9 netbsd aix illumos solaris js
|
||||||
|
|
||||||
package copy
|
package copy
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -696,8 +696,8 @@ github.com/opencontainers/runtime-spec/specs-go
|
||||||
github.com/opencontainers/selinux/go-selinux
|
github.com/opencontainers/selinux/go-selinux
|
||||||
github.com/opencontainers/selinux/go-selinux/label
|
github.com/opencontainers/selinux/go-selinux/label
|
||||||
github.com/opencontainers/selinux/pkg/pwalkdir
|
github.com/opencontainers/selinux/pkg/pwalkdir
|
||||||
# github.com/otiai10/copy v1.7.0
|
# github.com/otiai10/copy v1.11.0
|
||||||
## explicit; go 1.14
|
## explicit; go 1.18
|
||||||
github.com/otiai10/copy
|
github.com/otiai10/copy
|
||||||
# github.com/pkg/errors v0.9.1
|
# github.com/pkg/errors v0.9.1
|
||||||
## explicit
|
## explicit
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue