Add support for copying files and folders.

This commit is contained in:
2021-12-19 14:31:57 +01:00
parent 161cb79b88
commit 311339685c
450 changed files with 232338 additions and 3 deletions

5
vendor/github.com/otiai10/copy/.gitignore generated vendored Normal file
View File

@ -0,0 +1,5 @@
test/data.copy
coverage.txt
vendor
.vagrant
.idea/

21
vendor/github.com/otiai10/copy/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 otiai10
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

77
vendor/github.com/otiai10/copy/README.md generated vendored Normal file
View File

@ -0,0 +1,77 @@
# copy
[![Go Reference](https://pkg.go.dev/badge/github.com/otiai10/copy.svg)](https://pkg.go.dev/github.com/otiai10/copy)
[![Actions Status](https://github.com/otiai10/copy/workflows/Go/badge.svg)](https://github.com/otiai10/copy/actions)
[![codecov](https://codecov.io/gh/otiai10/copy/branch/main/graph/badge.svg)](https://codecov.io/gh/otiai10/copy)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/otiai10/copy/blob/main/LICENSE)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fotiai10%2Fcopy.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fotiai10%2Fcopy?ref=badge_shield)
[![CodeQL](https://github.com/otiai10/copy/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/otiai10/copy/actions/workflows/codeql-analysis.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/otiai10/copy)](https://goreportcard.com/report/github.com/otiai10/copy)
[![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/otiai10/copy?sort=semver)](https://pkg.go.dev/github.com/otiai10/copy)
[![Docker Test](https://github.com/otiai10/copy/actions/workflows/docker-test.yml/badge.svg)](https://github.com/otiai10/copy/actions/workflows/docker-test.yml)
[![Vagrant Test](https://github.com/otiai10/copy/actions/workflows/vagrant-test.yml/badge.svg)](https://github.com/otiai10/copy/actions/workflows/vagrant-test.yml)
`copy` copies directories recursively.
# Example Usage
```go
err := Copy("your/directory", "your/directory.copy")
```
# Advanced Usage
```go
// Options specifies optional actions on copying.
type Options struct {
// OnSymlink can specify what to do on symlink
OnSymlink func(src string) SymlinkAction
// OnDirExists can specify what to do when there is a directory already existing in destination.
OnDirExists func(src, dest string) DirExistsAction
// Skip can specify which files should be skipped
Skip func(src string) (bool, error)
// AddPermission to every entry,
// NO MORE THAN 0777
AddPermission os.FileMode
// Sync file after copy.
// Useful in case when file must be on the disk
// (in case crash happens, for example),
// at the expense of some performance penalty
Sync bool
// Preserve the atime and the mtime of the entries
// On linux we can preserve only up to 1 millisecond accuracy
PreserveTimes bool
// Preserve the uid and the gid of all entries.
PreserveOwner bool
// The byte size of the buffer to use for copying files.
// If zero, the internal default buffer of 32KB is used.
// See https://golang.org/pkg/io/#CopyBuffer for more information.
CopyBufferSize uint
}
```
```go
// For example...
opt := Options{
Skip: func(src string) (bool, error) {
return strings.HasSuffix(src, ".git"), nil
},
}
err := Copy("your/directory", "your/directory.copy", opt)
```
# Issues
- https://github.com/otiai10/copy/issues
## License
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fotiai10%2Fcopy.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fotiai10%2Fcopy?ref=badge_large)

243
vendor/github.com/otiai10/copy/copy.go generated vendored Normal file
View File

@ -0,0 +1,243 @@
package copy
import (
"io"
"io/ioutil"
"os"
"path/filepath"
"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 {
Mtime time.Time
Atime time.Time
Ctime time.Time
}
// Copy copies src to dest, doesn't matter if src is a directory or a file.
func Copy(src, dest string, opt ...Options) error {
info, err := os.Lstat(src)
if err != nil {
return err
}
return switchboard(src, dest, info, assure(src, dest, opt...))
}
// switchboard switches proper copy functions regarding file type, etc...
// 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) {
switch {
case info.Mode()&os.ModeSymlink != 0:
err = onsymlink(src, dest, opt)
case info.IsDir():
err = dcopy(src, dest, info, opt)
case info.Mode()&os.ModeNamedPipe != 0:
err = pcopy(dest, info)
default:
err = fcopy(src, dest, info, opt)
}
return err
}
// copyNextOrSkip decide if this src should be copied or not.
// Because this "copy" could be called recursively,
// "info" MUST be given here, NOT nil.
func copyNextOrSkip(src, dest string, info os.FileInfo, opt Options) error {
skip, err := opt.Skip(src)
if err != nil {
return err
}
if skip {
return nil
}
return switchboard(src, dest, info, opt)
}
// fcopy is for just a file,
// with considering existence of parent directory
// and file permission.
func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {
if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
return
}
f, err := os.Create(dest)
if err != nil {
return
}
defer fclose(f, &err)
if err = os.Chmod(f.Name(), info.Mode()|opt.AddPermission); err != nil {
return
}
s, err := os.Open(src)
if err != nil {
return
}
defer fclose(s, &err)
var buf []byte = nil
var w io.Writer = f
// var r io.Reader = s
if opt.CopyBufferSize != 0 {
buf = make([]byte, opt.CopyBufferSize)
// Disable using `ReadFrom` by io.CopyBuffer.
// See https://github.com/otiai10/copy/pull/60#discussion_r627320811 for more details.
w = struct{ io.Writer }{f}
// r = struct{ io.Reader }{s}
}
if _, err = io.CopyBuffer(w, s, buf); err != nil {
return err
}
if opt.Sync {
err = f.Sync()
}
if opt.PreserveOwner {
if err := preserveOwner(src, dest, info); err != nil {
return err
}
}
if opt.PreserveTimes {
if err := preserveTimes(info, dest); err != nil {
return err
}
}
return
}
// dcopy is for a directory,
// with scanning contents inside the directory
// and pass everything to "copy" recursively.
func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err 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 err
}
case Untouchable:
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.
if err = os.MkdirAll(destdir, tmpPermissionForDirectory); err != nil {
return
}
// Recover dir mode with original one.
defer chmod(destdir, originalMode|opt.AddPermission, &err)
contents, err := ioutil.ReadDir(srcdir)
if err != nil {
return
}
for _, content := range contents {
cs, cd := filepath.Join(srcdir, content.Name()), filepath.Join(destdir, content.Name())
if err = copyNextOrSkip(cs, cd, content, opt); err != nil {
// If any error, exit immediately
return
}
}
if opt.PreserveTimes {
if err := preserveTimes(info, destdir); err != nil {
return err
}
}
if opt.PreserveOwner {
if err := preserveOwner(srcdir, destdir, info); err != nil {
return err
}
}
return
}
func onsymlink(src, dest string, opt Options) error {
switch opt.OnSymlink(src) {
case Shallow:
return lcopy(src, dest)
case Deep:
orig, err := os.Readlink(src)
if err != nil {
return err
}
info, err := os.Lstat(orig)
if err != nil {
return err
}
return copyNextOrSkip(orig, dest, info, opt)
case Skip:
fallthrough
default:
return nil // do nothing
}
}
// lcopy is for a symlink,
// with just creating a new symlink by replicating src symlink.
func lcopy(src, dest string) error {
src, err := os.Readlink(src)
if err != nil {
return err
}
return os.Symlink(src, dest)
}
// fclose ANYHOW closes file,
// with asiging error raised during Close,
// BUT respecting the error already reported.
func fclose(f *os.File, reported *error) {
if err := f.Close(); *reported == nil {
*reported = err
}
}
// 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
}
}
// assure Options struct, should be called only once.
// 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]
}

17
vendor/github.com/otiai10/copy/copy_namedpipes.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
// +build !windows,!plan9,!netbsd,!aix,!illumos,!solaris
package copy
import (
"os"
"path/filepath"
"syscall"
)
// pcopy is for just named pipes
func pcopy(dest string, info os.FileInfo) error {
if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
return err
}
return syscall.Mkfifo(dest, uint32(info.Mode()))
}

14
vendor/github.com/otiai10/copy/copy_namedpipes_x.go generated vendored Normal file
View File

@ -0,0 +1,14 @@
// +build windows plan9 netbsd aix illumos solaris
package copy
import (
"os"
)
// TODO: check plan9 netbsd aix illumos solaris in future
// pcopy is for just named pipes. Windows doesn't support them
func pcopy(dest string, info os.FileInfo) error {
return nil
}

12
vendor/github.com/otiai10/copy/fileinfo.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
package copy
// 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() FileMode // file mode bits
// ModTime() time.Time // modification time
// IsDir() bool // abbreviation for Mode().IsDir()
Sys() interface{} // underlying data source (can return nil)
}

89
vendor/github.com/otiai10/copy/options.go generated vendored Normal file
View File

@ -0,0 +1,89 @@
package copy
import "os"
// Options specifies optional actions on copying.
type Options struct {
// OnSymlink can specify what to do on symlink
OnSymlink func(src string) SymlinkAction
// OnDirExists can specify what to do when there is a directory already existing in destination.
OnDirExists func(src, dest string) DirExistsAction
// Skip can specify which files should be skipped
Skip func(src string) (bool, error)
// AddPermission to every entities,
// NO MORE THAN 0777
AddPermission os.FileMode
// Sync file after copy.
// Useful in case when file must be on the disk
// (in case crash happens, for example),
// at the expense of some performance penalty
Sync bool
// Preserve the atime and the mtime of the entries.
// On linux we can preserve only up to 1 millisecond accuracy.
PreserveTimes bool
// Preserve the uid and the gid of all entries.
PreserveOwner bool
// The byte size of the buffer to use for copying files.
// If zero, the internal default buffer of 32KB is used.
// See https://golang.org/pkg/io/#CopyBuffer for more information.
CopyBufferSize uint
intent struct {
src string
dest string
}
}
// SymlinkAction represents what to do on symlink.
type SymlinkAction int
const (
// Deep creates hard-copy of contents.
Deep SymlinkAction = iota
// Shallow creates new symlink to the dest of symlink.
Shallow
// Skip does nothing with symlink.
Skip
)
// DirExistsAction represents what to do on dest dir.
type DirExistsAction int
const (
// Merge preserves or overwrites existing files under the dir (default behavior).
Merge DirExistsAction = iota
// Replace deletes all contents under the dir and copy src files.
Replace
// Untouchable does nothing for the dir, and leaves it as it is.
Untouchable
)
// getDefaultOptions provides default options,
// which would be modified by usage-side.
func getDefaultOptions(src, dest string) Options {
return Options{
OnSymlink: func(string) SymlinkAction {
return Shallow // Do shallow copy
},
OnDirExists: nil, // Default behavior is "Merge".
Skip: func(string) (bool, error) {
return false, nil // Don't skip
},
AddPermission: 0, // Add nothing
Sync: false, // Do not sync
PreserveTimes: false, // Do not preserve the modification time
CopyBufferSize: 0, // Do not specify, use default bufsize (32*1024)
intent: struct {
src string
dest string
}{src, dest},
}
}

22
vendor/github.com/otiai10/copy/preserve_owner.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
//+build !windows
package copy
import (
"os"
"syscall"
)
func preserveOwner(src, dest string, info fileInfo) (err error) {
if info == nil {
if info, err = os.Stat(src); err != nil {
return err
}
}
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
if err := os.Chown(dest, int(stat.Uid), int(stat.Gid)); err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,7 @@
//+build windows
package copy
func preserveOwner(src, dest string, info fileInfo) (err error) {
return nil
}

11
vendor/github.com/otiai10/copy/preserve_times.go generated vendored Normal file
View File

@ -0,0 +1,11 @@
package copy
import "os"
func preserveTimes(srcinfo os.FileInfo, dest string) error {
spec := getTimeSpec(srcinfo)
if err := os.Chtimes(dest, spec.Atime, spec.Mtime); err != nil {
return err
}
return nil
}

21
vendor/github.com/otiai10/copy/stat_times.go generated vendored Normal file
View File

@ -0,0 +1,21 @@
// +build !windows,!darwin,!freebsd,!plan9,!netbsd
// TODO: add more runtimes
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.Atim.Sec), int64(stat.Atim.Nsec)),
Ctime: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)),
}
return times
}

19
vendor/github.com/otiai10/copy/stat_times_darwin.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// +build darwin
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(stat.Atimespec.Sec, stat.Atimespec.Nsec),
Ctime: time.Unix(stat.Ctimespec.Sec, stat.Ctimespec.Nsec),
}
return times
}

19
vendor/github.com/otiai10/copy/stat_times_freebsd.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// +build freebsd
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.Atimespec.Sec), int64(stat.Atimespec.Nsec)),
Ctime: time.Unix(int64(stat.Ctimespec.Sec), int64(stat.Ctimespec.Nsec)),
}
return times
}

18
vendor/github.com/otiai10/copy/stat_times_windows.go generated vendored Normal file
View File

@ -0,0 +1,18 @@
// +build windows
package copy
import (
"os"
"syscall"
"time"
)
func getTimeSpec(info os.FileInfo) timespec {
stat := info.Sys().(*syscall.Win32FileAttributeData)
return timespec{
Mtime: time.Unix(0, stat.LastWriteTime.Nanoseconds()),
Atime: time.Unix(0, stat.LastAccessTime.Nanoseconds()),
Ctime: time.Unix(0, stat.CreationTime.Nanoseconds()),
}
}

17
vendor/github.com/otiai10/copy/stat_times_x.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
// +build plan9 netbsd
package copy
import (
"os"
)
// TODO: check plan9 netbsd in future
func getTimeSpec(info os.FileInfo) timespec {
times := timespec{
Mtime: info.ModTime(),
Atime: info.ModTime(),
Ctime: info.ModTime(),
}
return times
}

17
vendor/github.com/otiai10/copy/test_setup.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
// +build !windows,!plan9,!netbsd,!aix,!illumos,!solaris
package copy
import (
"os"
"syscall"
"testing"
)
func setup(m *testing.M) {
os.MkdirAll("test/data.copy", os.ModePerm)
os.Symlink("test/data/case01", "test/data/case03/case01")
os.Chmod("test/data/case07/dir_0555", 0555)
os.Chmod("test/data/case07/file_0444", 0444)
syscall.Mkfifo("test/data/case11/foo/bar", 0555)
}

15
vendor/github.com/otiai10/copy/test_setup_x.go generated vendored Normal file
View File

@ -0,0 +1,15 @@
// +build windows plan9 netbsd aix illumos solaris
package copy
import (
"os"
"testing"
)
func setup(m *testing.M) {
os.MkdirAll("test/data.copy", os.ModePerm)
os.Symlink("test/data/case01", "test/data/case03/case01")
os.Chmod("test/data/case07/dir_0555", 0555)
os.Chmod("test/data/case07/file_0444", 0444)
}