Use continuity fs package
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
15
vendor/github.com/containerd/continuity/devices/devices_darwin.go
generated
vendored
15
vendor/github.com/containerd/continuity/devices/devices_darwin.go
generated
vendored
@@ -1,15 +0,0 @@
|
||||
package devices
|
||||
|
||||
// from /usr/include/sys/types.h
|
||||
|
||||
func getmajor(dev int32) uint64 {
|
||||
return (uint64(dev) >> 24) & 0xff
|
||||
}
|
||||
|
||||
func getminor(dev int32) uint64 {
|
||||
return uint64(dev) & 0xffffff
|
||||
}
|
||||
|
||||
func makedev(major int, minor int) int {
|
||||
return ((major << 24) | minor)
|
||||
}
|
||||
23
vendor/github.com/containerd/continuity/devices/devices_dummy.go
generated
vendored
23
vendor/github.com/containerd/continuity/devices/devices_dummy.go
generated
vendored
@@ -1,23 +0,0 @@
|
||||
// +build solaris,!cgo
|
||||
|
||||
//
|
||||
// Implementing the functions below requires cgo support. Non-cgo stubs
|
||||
// versions are defined below to enable cross-compilation of source code
|
||||
// that depends on these functions, but the resultant cross-compiled
|
||||
// binaries cannot actually be used. If the stub function(s) below are
|
||||
// actually invoked they will cause the calling process to exit.
|
||||
//
|
||||
|
||||
package devices
|
||||
|
||||
func getmajor(dev uint64) uint64 {
|
||||
panic("getmajor() support requires cgo.")
|
||||
}
|
||||
|
||||
func getminor(dev uint64) uint64 {
|
||||
panic("getminor() support requires cgo.")
|
||||
}
|
||||
|
||||
func makedev(major int, minor int) int {
|
||||
panic("makedev() support requires cgo.")
|
||||
}
|
||||
15
vendor/github.com/containerd/continuity/devices/devices_freebsd.go
generated
vendored
15
vendor/github.com/containerd/continuity/devices/devices_freebsd.go
generated
vendored
@@ -1,15 +0,0 @@
|
||||
package devices
|
||||
|
||||
// from /usr/include/sys/types.h
|
||||
|
||||
func getmajor(dev uint32) uint64 {
|
||||
return (uint64(dev) >> 24) & 0xff
|
||||
}
|
||||
|
||||
func getminor(dev uint32) uint64 {
|
||||
return uint64(dev) & 0xffffff
|
||||
}
|
||||
|
||||
func makedev(major int, minor int) int {
|
||||
return ((major << 24) | minor)
|
||||
}
|
||||
15
vendor/github.com/containerd/continuity/devices/devices_linux.go
generated
vendored
15
vendor/github.com/containerd/continuity/devices/devices_linux.go
generated
vendored
@@ -1,15 +0,0 @@
|
||||
package devices
|
||||
|
||||
// from /usr/include/linux/kdev_t.h
|
||||
|
||||
func getmajor(dev uint64) uint64 {
|
||||
return dev >> 8
|
||||
}
|
||||
|
||||
func getminor(dev uint64) uint64 {
|
||||
return dev & 0xff
|
||||
}
|
||||
|
||||
func makedev(major int, minor int) int {
|
||||
return ((major << 8) | minor)
|
||||
}
|
||||
18
vendor/github.com/containerd/continuity/devices/devices_solaris.go
generated
vendored
18
vendor/github.com/containerd/continuity/devices/devices_solaris.go
generated
vendored
@@ -1,18 +0,0 @@
|
||||
// +build cgo
|
||||
|
||||
package devices
|
||||
|
||||
//#include <sys/mkdev.h>
|
||||
import "C"
|
||||
|
||||
func getmajor(dev uint64) uint64 {
|
||||
return uint64(C.major(C.dev_t(dev)))
|
||||
}
|
||||
|
||||
func getminor(dev uint64) uint64 {
|
||||
return uint64(C.minor(C.dev_t(dev)))
|
||||
}
|
||||
|
||||
func makedev(major int, minor int) int {
|
||||
return int(C.makedev(C.major_t(major), C.minor_t(minor)))
|
||||
}
|
||||
23
vendor/github.com/containerd/continuity/devices/devices_unix.go
generated
vendored
23
vendor/github.com/containerd/continuity/devices/devices_unix.go
generated
vendored
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func DeviceInfo(fi os.FileInfo) (uint64, uint64, error) {
|
||||
@@ -14,42 +16,43 @@ func DeviceInfo(fi os.FileInfo) (uint64, uint64, error) {
|
||||
return 0, 0, fmt.Errorf("cannot extract device from os.FileInfo")
|
||||
}
|
||||
|
||||
return getmajor(sys.Rdev), getminor(sys.Rdev), nil
|
||||
dev := uint64(sys.Rdev)
|
||||
return uint64(unix.Major(dev)), uint64(unix.Minor(dev)), nil
|
||||
}
|
||||
|
||||
// mknod provides a shortcut for syscall.Mknod
|
||||
func Mknod(p string, mode os.FileMode, maj, min int) error {
|
||||
var (
|
||||
m = syscallMode(mode.Perm())
|
||||
dev int
|
||||
dev uint64
|
||||
)
|
||||
|
||||
if mode&os.ModeDevice != 0 {
|
||||
dev = makedev(maj, min)
|
||||
dev = unix.Mkdev(uint32(maj), uint32(min))
|
||||
|
||||
if mode&os.ModeCharDevice != 0 {
|
||||
m |= syscall.S_IFCHR
|
||||
m |= unix.S_IFCHR
|
||||
} else {
|
||||
m |= syscall.S_IFBLK
|
||||
m |= unix.S_IFBLK
|
||||
}
|
||||
} else if mode&os.ModeNamedPipe != 0 {
|
||||
m |= syscall.S_IFIFO
|
||||
m |= unix.S_IFIFO
|
||||
}
|
||||
|
||||
return syscall.Mknod(p, m, dev)
|
||||
return unix.Mknod(p, m, int(dev))
|
||||
}
|
||||
|
||||
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
|
||||
func syscallMode(i os.FileMode) (o uint32) {
|
||||
o |= uint32(i.Perm())
|
||||
if i&os.ModeSetuid != 0 {
|
||||
o |= syscall.S_ISUID
|
||||
o |= unix.S_ISUID
|
||||
}
|
||||
if i&os.ModeSetgid != 0 {
|
||||
o |= syscall.S_ISGID
|
||||
o |= unix.S_ISGID
|
||||
}
|
||||
if i&os.ModeSticky != 0 {
|
||||
o |= syscall.S_ISVTX
|
||||
o |= unix.S_ISVTX
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
74
vendor/github.com/containerd/continuity/driver/utils.go
generated
vendored
Normal file
74
vendor/github.com/containerd/continuity/driver/utils.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package driver
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// ReadFile works the same as ioutil.ReadFile with the Driver abstraction
|
||||
func ReadFile(r Driver, filename string) ([]byte, error) {
|
||||
f, err := r.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// WriteFile works the same as ioutil.WriteFile with the Driver abstraction
|
||||
func WriteFile(r Driver, filename string, data []byte, perm os.FileMode) error {
|
||||
f, err := r.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
n, err := f.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if n != len(data) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadDir works the same as ioutil.ReadDir with the Driver abstraction
|
||||
func ReadDir(r Driver, dirname string) ([]os.FileInfo, error) {
|
||||
f, err := r.Open(dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
dirs, err := f.Readdir(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Sort(fileInfos(dirs))
|
||||
return dirs, nil
|
||||
}
|
||||
|
||||
// Simple implementation of the sort.Interface for os.FileInfo
|
||||
type fileInfos []os.FileInfo
|
||||
|
||||
func (fis fileInfos) Len() int {
|
||||
return len(fis)
|
||||
}
|
||||
|
||||
func (fis fileInfos) Less(i, j int) bool {
|
||||
return fis[i].Name() < fis[j].Name()
|
||||
}
|
||||
|
||||
func (fis fileInfos) Swap(i, j int) {
|
||||
fis[i], fis[j] = fis[j], fis[i]
|
||||
}
|
||||
119
vendor/github.com/containerd/continuity/fs/copy.go
generated
vendored
Normal file
119
vendor/github.com/containerd/continuity/fs/copy.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var bufferPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
buffer := make([]byte, 32*1024)
|
||||
return &buffer
|
||||
},
|
||||
}
|
||||
|
||||
// CopyDir copies the directory from src to dst.
|
||||
// Most efficient copy of files is attempted.
|
||||
func CopyDir(dst, src string) error {
|
||||
inodes := map[uint64]string{}
|
||||
return copyDirectory(dst, src, inodes)
|
||||
}
|
||||
|
||||
func copyDirectory(dst, src string, inodes map[uint64]string) error {
|
||||
stat, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to stat %s", src)
|
||||
}
|
||||
if !stat.IsDir() {
|
||||
return errors.Errorf("source is not directory")
|
||||
}
|
||||
|
||||
if st, err := os.Stat(dst); err != nil {
|
||||
if err := os.Mkdir(dst, stat.Mode()); err != nil {
|
||||
return errors.Wrapf(err, "failed to mkdir %s", dst)
|
||||
}
|
||||
} else if !st.IsDir() {
|
||||
return errors.Errorf("cannot copy to non-directory: %s", dst)
|
||||
} else {
|
||||
if err := os.Chmod(dst, stat.Mode()); err != nil {
|
||||
return errors.Wrapf(err, "failed to chmod on %s", dst)
|
||||
}
|
||||
}
|
||||
|
||||
fis, err := ioutil.ReadDir(src)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to read %s", src)
|
||||
}
|
||||
|
||||
if err := copyFileInfo(stat, dst); err != nil {
|
||||
return errors.Wrapf(err, "failed to copy file info for %s", dst)
|
||||
}
|
||||
|
||||
for _, fi := range fis {
|
||||
source := filepath.Join(src, fi.Name())
|
||||
target := filepath.Join(dst, fi.Name())
|
||||
|
||||
switch {
|
||||
case fi.IsDir():
|
||||
if err := copyDirectory(target, source, inodes); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
case (fi.Mode() & os.ModeType) == 0:
|
||||
link, err := getLinkSource(target, fi, inodes)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get hardlink")
|
||||
}
|
||||
if link != "" {
|
||||
if err := os.Link(link, target); err != nil {
|
||||
return errors.Wrap(err, "failed to create hard link")
|
||||
}
|
||||
} else if err := copyFile(source, target); err != nil {
|
||||
return errors.Wrap(err, "failed to copy files")
|
||||
}
|
||||
case (fi.Mode() & os.ModeSymlink) == os.ModeSymlink:
|
||||
link, err := os.Readlink(source)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to read link: %s", source)
|
||||
}
|
||||
if err := os.Symlink(link, target); err != nil {
|
||||
return errors.Wrapf(err, "failed to create symlink: %s", target)
|
||||
}
|
||||
case (fi.Mode() & os.ModeDevice) == os.ModeDevice:
|
||||
if err := copyDevice(target, fi); err != nil {
|
||||
return errors.Wrapf(err, "failed to create device")
|
||||
}
|
||||
default:
|
||||
// TODO: Support pipes and sockets
|
||||
return errors.Wrapf(err, "unsupported mode %s", fi.Mode())
|
||||
}
|
||||
if err := copyFileInfo(fi, target); err != nil {
|
||||
return errors.Wrap(err, "failed to copy file info")
|
||||
}
|
||||
|
||||
if err := copyXAttrs(target, source); err != nil {
|
||||
return errors.Wrap(err, "failed to copy xattrs")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFile(source, target string) error {
|
||||
src, err := os.Open(source)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to open source %s", source)
|
||||
}
|
||||
defer src.Close()
|
||||
tgt, err := os.Create(target)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to open target %s", target)
|
||||
}
|
||||
defer tgt.Close()
|
||||
|
||||
return copyFileContent(tgt, src)
|
||||
}
|
||||
95
vendor/github.com/containerd/continuity/fs/copy_linux.go
generated
vendored
Normal file
95
vendor/github.com/containerd/continuity/fs/copy_linux.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/continuity/sysx"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func copyFileInfo(fi os.FileInfo, name string) error {
|
||||
st := fi.Sys().(*syscall.Stat_t)
|
||||
if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
|
||||
if os.IsPermission(err) {
|
||||
// Normally if uid/gid are the same this would be a no-op, but some
|
||||
// filesystems may still return EPERM... for instance NFS does this.
|
||||
// In such a case, this is not an error.
|
||||
if dstStat, err2 := os.Lstat(name); err2 == nil {
|
||||
st2 := dstStat.Sys().(*syscall.Stat_t)
|
||||
if st.Uid == st2.Uid && st.Gid == st2.Gid {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to chown %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
|
||||
if err := os.Chmod(name, fi.Mode()); err != nil {
|
||||
return errors.Wrapf(err, "failed to chmod %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
timespec := []unix.Timespec{unix.Timespec(StatAtime(st)), unix.Timespec(StatMtime(st))}
|
||||
if err := unix.UtimesNanoAt(unix.AT_FDCWD, name, timespec, unix.AT_SYMLINK_NOFOLLOW); err != nil {
|
||||
return errors.Wrapf(err, "failed to utime %s", name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFileContent(dst, src *os.File) error {
|
||||
st, err := src.Stat()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to stat source")
|
||||
}
|
||||
|
||||
n, err := unix.CopyFileRange(int(src.Fd()), nil, int(dst.Fd()), nil, int(st.Size()), 0)
|
||||
if err != nil {
|
||||
if err != unix.ENOSYS && err != unix.EXDEV {
|
||||
return errors.Wrap(err, "copy file range failed")
|
||||
}
|
||||
|
||||
buf := bufferPool.Get().(*[]byte)
|
||||
_, err = io.CopyBuffer(dst, src, *buf)
|
||||
bufferPool.Put(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
if int64(n) != st.Size() {
|
||||
return errors.Wrapf(err, "short copy: %d of %d", int64(n), st.Size())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyXAttrs(dst, src string) error {
|
||||
xattrKeys, err := sysx.LListxattr(src)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to list xattrs on %s", src)
|
||||
}
|
||||
for _, xattr := range xattrKeys {
|
||||
data, err := sysx.LGetxattr(src, xattr)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
|
||||
}
|
||||
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
|
||||
return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyDevice(dst string, fi os.FileInfo) error {
|
||||
st, ok := fi.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return errors.New("unsupported stat type")
|
||||
}
|
||||
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
|
||||
}
|
||||
80
vendor/github.com/containerd/continuity/fs/copy_unix.go
generated
vendored
Normal file
80
vendor/github.com/containerd/continuity/fs/copy_unix.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// +build solaris darwin freebsd
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/continuity/sysx"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func copyFileInfo(fi os.FileInfo, name string) error {
|
||||
st := fi.Sys().(*syscall.Stat_t)
|
||||
if err := os.Lchown(name, int(st.Uid), int(st.Gid)); err != nil {
|
||||
if os.IsPermission(err) {
|
||||
// Normally if uid/gid are the same this would be a no-op, but some
|
||||
// filesystems may still return EPERM... for instance NFS does this.
|
||||
// In such a case, this is not an error.
|
||||
if dstStat, err2 := os.Lstat(name); err2 == nil {
|
||||
st2 := dstStat.Sys().(*syscall.Stat_t)
|
||||
if st.Uid == st2.Uid && st.Gid == st2.Gid {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to chown %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
if (fi.Mode() & os.ModeSymlink) != os.ModeSymlink {
|
||||
if err := os.Chmod(name, fi.Mode()); err != nil {
|
||||
return errors.Wrapf(err, "failed to chmod %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
timespec := []syscall.Timespec{StatAtime(st), StatMtime(st)}
|
||||
if err := syscall.UtimesNano(name, timespec); err != nil {
|
||||
return errors.Wrapf(err, "failed to utime %s", name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFileContent(dst, src *os.File) error {
|
||||
buf := bufferPool.Get().(*[]byte)
|
||||
_, err := io.CopyBuffer(dst, src, *buf)
|
||||
bufferPool.Put(buf)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func copyXAttrs(dst, src string) error {
|
||||
xattrKeys, err := sysx.LListxattr(src)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to list xattrs on %s", src)
|
||||
}
|
||||
for _, xattr := range xattrKeys {
|
||||
data, err := sysx.LGetxattr(src, xattr)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
|
||||
}
|
||||
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
|
||||
return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyDevice(dst string, fi os.FileInfo) error {
|
||||
st, ok := fi.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return errors.New("unsupported stat type")
|
||||
}
|
||||
return unix.Mknod(dst, uint32(fi.Mode()), int(st.Rdev))
|
||||
}
|
||||
33
vendor/github.com/containerd/continuity/fs/copy_windows.go
generated
vendored
Normal file
33
vendor/github.com/containerd/continuity/fs/copy_windows.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func copyFileInfo(fi os.FileInfo, name string) error {
|
||||
if err := os.Chmod(name, fi.Mode()); err != nil {
|
||||
return errors.Wrapf(err, "failed to chmod %s", name)
|
||||
}
|
||||
|
||||
// TODO: copy windows specific metadata
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyFileContent(dst, src *os.File) error {
|
||||
buf := bufferPool.Get().(*[]byte)
|
||||
_, err := io.CopyBuffer(dst, src, *buf)
|
||||
bufferPool.Put(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
func copyXAttrs(dst, src string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyDevice(dst string, fi os.FileInfo) error {
|
||||
return errors.New("device copy not supported")
|
||||
}
|
||||
310
vendor/github.com/containerd/continuity/fs/diff.go
generated
vendored
Normal file
310
vendor/github.com/containerd/continuity/fs/diff.go
generated
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ChangeKind is the type of modification that
|
||||
// a change is making.
|
||||
type ChangeKind int
|
||||
|
||||
const (
|
||||
// ChangeKindUnmodified represents an unmodified
|
||||
// file
|
||||
ChangeKindUnmodified = iota
|
||||
|
||||
// ChangeKindAdd represents an addition of
|
||||
// a file
|
||||
ChangeKindAdd
|
||||
|
||||
// ChangeKindModify represents a change to
|
||||
// an existing file
|
||||
ChangeKindModify
|
||||
|
||||
// ChangeKindDelete represents a delete of
|
||||
// a file
|
||||
ChangeKindDelete
|
||||
)
|
||||
|
||||
func (k ChangeKind) String() string {
|
||||
switch k {
|
||||
case ChangeKindUnmodified:
|
||||
return "unmodified"
|
||||
case ChangeKindAdd:
|
||||
return "add"
|
||||
case ChangeKindModify:
|
||||
return "modify"
|
||||
case ChangeKindDelete:
|
||||
return "delete"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Change represents single change between a diff and its parent.
|
||||
type Change struct {
|
||||
Kind ChangeKind
|
||||
Path string
|
||||
}
|
||||
|
||||
// ChangeFunc is the type of function called for each change
|
||||
// computed during a directory changes calculation.
|
||||
type ChangeFunc func(ChangeKind, string, os.FileInfo, error) error
|
||||
|
||||
// Changes computes changes between two directories calling the
|
||||
// given change function for each computed change. The first
|
||||
// directory is intended to the base directory and second
|
||||
// directory the changed directory.
|
||||
//
|
||||
// The change callback is called by the order of path names and
|
||||
// should be appliable in that order.
|
||||
// Due to this apply ordering, the following is true
|
||||
// - Removed directory trees only create a single change for the root
|
||||
// directory removed. Remaining changes are implied.
|
||||
// - A directory which is modified to become a file will not have
|
||||
// delete entries for sub-path items, their removal is implied
|
||||
// by the removal of the parent directory.
|
||||
//
|
||||
// Opaque directories will not be treated specially and each file
|
||||
// removed from the base directory will show up as a removal.
|
||||
//
|
||||
// File content comparisons will be done on files which have timestamps
|
||||
// which may have been truncated. If either of the files being compared
|
||||
// has a zero value nanosecond value, each byte will be compared for
|
||||
// differences. If 2 files have the same seconds value but different
|
||||
// nanosecond values where one of those values is zero, the files will
|
||||
// be considered unchanged if the content is the same. This behavior
|
||||
// is to account for timestamp truncation during archiving.
|
||||
func Changes(ctx context.Context, a, b string, changeFn ChangeFunc) error {
|
||||
if a == "" {
|
||||
logrus.Debugf("Using single walk diff for %s", b)
|
||||
return addDirChanges(ctx, changeFn, b)
|
||||
} else if diffOptions := detectDirDiff(b, a); diffOptions != nil {
|
||||
logrus.Debugf("Using single walk diff for %s from %s", diffOptions.diffDir, a)
|
||||
return diffDirChanges(ctx, changeFn, a, diffOptions)
|
||||
}
|
||||
|
||||
logrus.Debugf("Using double walk diff for %s from %s", b, a)
|
||||
return doubleWalkDiff(ctx, changeFn, a, b)
|
||||
}
|
||||
|
||||
func addDirChanges(ctx context.Context, changeFn ChangeFunc, root string) error {
|
||||
return filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Rebase path
|
||||
path, err = filepath.Rel(root, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path = filepath.Join(string(os.PathSeparator), path)
|
||||
|
||||
// Skip root
|
||||
if path == string(os.PathSeparator) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return changeFn(ChangeKindAdd, path, f, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// diffDirOptions is used when the diff can be directly calculated from
|
||||
// a diff directory to its base, without walking both trees.
|
||||
type diffDirOptions struct {
|
||||
diffDir string
|
||||
skipChange func(string) (bool, error)
|
||||
deleteChange func(string, string, os.FileInfo) (string, error)
|
||||
}
|
||||
|
||||
// diffDirChanges walks the diff directory and compares changes against the base.
|
||||
func diffDirChanges(ctx context.Context, changeFn ChangeFunc, base string, o *diffDirOptions) error {
|
||||
changedDirs := make(map[string]struct{})
|
||||
return filepath.Walk(o.diffDir, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Rebase path
|
||||
path, err = filepath.Rel(o.diffDir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path = filepath.Join(string(os.PathSeparator), path)
|
||||
|
||||
// Skip root
|
||||
if path == string(os.PathSeparator) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: handle opaqueness, start new double walker at this
|
||||
// location to get deletes, and skip tree in single walker
|
||||
|
||||
if o.skipChange != nil {
|
||||
if skip, err := o.skipChange(path); skip {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var kind ChangeKind
|
||||
|
||||
deletedFile, err := o.deleteChange(o.diffDir, path, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find out what kind of modification happened
|
||||
if deletedFile != "" {
|
||||
path = deletedFile
|
||||
kind = ChangeKindDelete
|
||||
f = nil
|
||||
} else {
|
||||
// Otherwise, the file was added
|
||||
kind = ChangeKindAdd
|
||||
|
||||
// ...Unless it already existed in a base, in which case, it's a modification
|
||||
stat, err := os.Stat(filepath.Join(base, path))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if err == nil {
|
||||
// The file existed in the base, so that's a modification
|
||||
|
||||
// However, if it's a directory, maybe it wasn't actually modified.
|
||||
// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
|
||||
if stat.IsDir() && f.IsDir() {
|
||||
if f.Size() == stat.Size() && f.Mode() == stat.Mode() && sameFsTime(f.ModTime(), stat.ModTime()) {
|
||||
// Both directories are the same, don't record the change
|
||||
return nil
|
||||
}
|
||||
}
|
||||
kind = ChangeKindModify
|
||||
}
|
||||
}
|
||||
|
||||
// If /foo/bar/file.txt is modified, then /foo/bar must be part of the changed files.
|
||||
// This block is here to ensure the change is recorded even if the
|
||||
// modify time, mode and size of the parent directory in the rw and ro layers are all equal.
|
||||
// Check https://github.com/docker/docker/pull/13590 for details.
|
||||
if f.IsDir() {
|
||||
changedDirs[path] = struct{}{}
|
||||
}
|
||||
if kind == ChangeKindAdd || kind == ChangeKindDelete {
|
||||
parent := filepath.Dir(path)
|
||||
if _, ok := changedDirs[parent]; !ok && parent != "/" {
|
||||
pi, err := os.Stat(filepath.Join(o.diffDir, parent))
|
||||
if err := changeFn(ChangeKindModify, parent, pi, err); err != nil {
|
||||
return err
|
||||
}
|
||||
changedDirs[parent] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return changeFn(kind, path, f, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// doubleWalkDiff walks both directories to create a diff
|
||||
func doubleWalkDiff(ctx context.Context, changeFn ChangeFunc, a, b string) (err error) {
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
var (
|
||||
c1 = make(chan *currentPath)
|
||||
c2 = make(chan *currentPath)
|
||||
|
||||
f1, f2 *currentPath
|
||||
rmdir string
|
||||
)
|
||||
g.Go(func() error {
|
||||
defer close(c1)
|
||||
return pathWalk(ctx, a, c1)
|
||||
})
|
||||
g.Go(func() error {
|
||||
defer close(c2)
|
||||
return pathWalk(ctx, b, c2)
|
||||
})
|
||||
g.Go(func() error {
|
||||
for c1 != nil || c2 != nil {
|
||||
if f1 == nil && c1 != nil {
|
||||
f1, err = nextPath(ctx, c1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f1 == nil {
|
||||
c1 = nil
|
||||
}
|
||||
}
|
||||
|
||||
if f2 == nil && c2 != nil {
|
||||
f2, err = nextPath(ctx, c2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f2 == nil {
|
||||
c2 = nil
|
||||
}
|
||||
}
|
||||
if f1 == nil && f2 == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var f os.FileInfo
|
||||
k, p := pathChange(f1, f2)
|
||||
switch k {
|
||||
case ChangeKindAdd:
|
||||
if rmdir != "" {
|
||||
rmdir = ""
|
||||
}
|
||||
f = f2.f
|
||||
f2 = nil
|
||||
case ChangeKindDelete:
|
||||
// Check if this file is already removed by being
|
||||
// under of a removed directory
|
||||
if rmdir != "" && strings.HasPrefix(f1.path, rmdir) {
|
||||
f1 = nil
|
||||
continue
|
||||
} else if rmdir == "" && f1.f.IsDir() {
|
||||
rmdir = f1.path + string(os.PathSeparator)
|
||||
} else if rmdir != "" {
|
||||
rmdir = ""
|
||||
}
|
||||
f1 = nil
|
||||
case ChangeKindModify:
|
||||
same, err := sameFile(f1, f2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if f1.f.IsDir() && !f2.f.IsDir() {
|
||||
rmdir = f1.path + string(os.PathSeparator)
|
||||
} else if rmdir != "" {
|
||||
rmdir = ""
|
||||
}
|
||||
f = f2.f
|
||||
f1 = nil
|
||||
f2 = nil
|
||||
if same {
|
||||
if !isLinked(f) {
|
||||
continue
|
||||
}
|
||||
k = ChangeKindUnmodified
|
||||
}
|
||||
}
|
||||
if err := changeFn(k, p, f, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return g.Wait()
|
||||
}
|
||||
58
vendor/github.com/containerd/continuity/fs/diff_unix.go
generated
vendored
Normal file
58
vendor/github.com/containerd/continuity/fs/diff_unix.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// +build !windows
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/continuity/sysx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// detectDirDiff returns diff dir options if a directory could
|
||||
// be found in the mount info for upper which is the direct
|
||||
// diff with the provided lower directory
|
||||
func detectDirDiff(upper, lower string) *diffDirOptions {
|
||||
// TODO: get mount options for upper
|
||||
// TODO: detect AUFS
|
||||
// TODO: detect overlay
|
||||
return nil
|
||||
}
|
||||
|
||||
// compareSysStat returns whether the stats are equivalent,
|
||||
// whether the files are considered the same file, and
|
||||
// an error
|
||||
func compareSysStat(s1, s2 interface{}) (bool, error) {
|
||||
ls1, ok := s1.(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
ls2, ok := s2.(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return ls1.Mode == ls2.Mode && ls1.Uid == ls2.Uid && ls1.Gid == ls2.Gid && ls1.Rdev == ls2.Rdev, nil
|
||||
}
|
||||
|
||||
func compareCapabilities(p1, p2 string) (bool, error) {
|
||||
c1, err := sysx.LGetxattr(p1, "security.capability")
|
||||
if err != nil && err != sysx.ENODATA {
|
||||
return false, errors.Wrapf(err, "failed to get xattr for %s", p1)
|
||||
}
|
||||
c2, err := sysx.LGetxattr(p2, "security.capability")
|
||||
if err != nil && err != sysx.ENODATA {
|
||||
return false, errors.Wrapf(err, "failed to get xattr for %s", p2)
|
||||
}
|
||||
return bytes.Equal(c1, c2), nil
|
||||
}
|
||||
|
||||
func isLinked(f os.FileInfo) bool {
|
||||
s, ok := f.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return !f.IsDir() && s.Nlink > 1
|
||||
}
|
||||
32
vendor/github.com/containerd/continuity/fs/diff_windows.go
generated
vendored
Normal file
32
vendor/github.com/containerd/continuity/fs/diff_windows.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func detectDirDiff(upper, lower string) *diffDirOptions {
|
||||
return nil
|
||||
}
|
||||
|
||||
func compareSysStat(s1, s2 interface{}) (bool, error) {
|
||||
f1, ok := s1.(windows.Win32FileAttributeData)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
f2, ok := s2.(windows.Win32FileAttributeData)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
return f1.FileAttributes == f2.FileAttributes, nil
|
||||
}
|
||||
|
||||
func compareCapabilities(p1, p2 string) (bool, error) {
|
||||
// TODO: Use windows equivalent
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func isLinked(os.FileInfo) bool {
|
||||
return false
|
||||
}
|
||||
87
vendor/github.com/containerd/continuity/fs/dtype_linux.go
generated
vendored
Normal file
87
vendor/github.com/containerd/continuity/fs/dtype_linux.go
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
// +build linux
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func locateDummyIfEmpty(path string) (string, error) {
|
||||
children, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(children) != 0 {
|
||||
return "", nil
|
||||
}
|
||||
dummyFile, err := ioutil.TempFile(path, "fsutils-dummy")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
name := dummyFile.Name()
|
||||
err = dummyFile.Close()
|
||||
return name, err
|
||||
}
|
||||
|
||||
// SupportsDType returns whether the filesystem mounted on path supports d_type
|
||||
func SupportsDType(path string) (bool, error) {
|
||||
// locate dummy so that we have at least one dirent
|
||||
dummy, err := locateDummyIfEmpty(path)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if dummy != "" {
|
||||
defer os.Remove(dummy)
|
||||
}
|
||||
|
||||
visited := 0
|
||||
supportsDType := true
|
||||
fn := func(ent *syscall.Dirent) bool {
|
||||
visited++
|
||||
if ent.Type == syscall.DT_UNKNOWN {
|
||||
supportsDType = false
|
||||
// stop iteration
|
||||
return true
|
||||
}
|
||||
// continue iteration
|
||||
return false
|
||||
}
|
||||
if err = iterateReadDir(path, fn); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if visited == 0 {
|
||||
return false, fmt.Errorf("did not hit any dirent during iteration %s", path)
|
||||
}
|
||||
return supportsDType, nil
|
||||
}
|
||||
|
||||
func iterateReadDir(path string, fn func(*syscall.Dirent) bool) error {
|
||||
d, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer d.Close()
|
||||
fd := int(d.Fd())
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
nbytes, err := syscall.ReadDirent(fd, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if nbytes == 0 {
|
||||
break
|
||||
}
|
||||
for off := 0; off < nbytes; {
|
||||
ent := (*syscall.Dirent)(unsafe.Pointer(&buf[off]))
|
||||
if stop := fn(ent); stop {
|
||||
return nil
|
||||
}
|
||||
off += int(ent.Reclen)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
22
vendor/github.com/containerd/continuity/fs/du.go
generated
vendored
Normal file
22
vendor/github.com/containerd/continuity/fs/du.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package fs
|
||||
|
||||
import "context"
|
||||
|
||||
// Usage of disk information
|
||||
type Usage struct {
|
||||
Inodes int64
|
||||
Size int64
|
||||
}
|
||||
|
||||
// DiskUsage counts the number of inodes and disk usage for the resources under
|
||||
// path.
|
||||
func DiskUsage(roots ...string) (Usage, error) {
|
||||
return diskUsage(roots...)
|
||||
}
|
||||
|
||||
// DiffUsage counts the numbers of inodes and disk usage in the
|
||||
// diff between the 2 directories. The first path is intended
|
||||
// as the base directory and the second as the changed directory.
|
||||
func DiffUsage(ctx context.Context, a, b string) (Usage, error) {
|
||||
return diffUsage(ctx, a, b)
|
||||
}
|
||||
88
vendor/github.com/containerd/continuity/fs/du_unix.go
generated
vendored
Normal file
88
vendor/github.com/containerd/continuity/fs/du_unix.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// +build !windows
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type inode struct {
|
||||
// TODO(stevvooe): Can probably reduce memory usage by not tracking
|
||||
// device, but we can leave this right for now.
|
||||
dev, ino uint64
|
||||
}
|
||||
|
||||
func newInode(stat *syscall.Stat_t) inode {
|
||||
return inode{
|
||||
// Dev is uint32 on darwin/bsd, uint64 on linux/solaris
|
||||
dev: uint64(stat.Dev), // nolint: unconvert
|
||||
// Ino is uint32 on bsd, uint64 on darwin/linux/solaris
|
||||
ino: uint64(stat.Ino), // nolint: unconvert
|
||||
}
|
||||
}
|
||||
|
||||
func diskUsage(roots ...string) (Usage, error) {
|
||||
|
||||
var (
|
||||
size int64
|
||||
inodes = map[inode]struct{}{} // expensive!
|
||||
)
|
||||
|
||||
for _, root := range roots {
|
||||
if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
inoKey := newInode(fi.Sys().(*syscall.Stat_t))
|
||||
if _, ok := inodes[inoKey]; !ok {
|
||||
inodes[inoKey] = struct{}{}
|
||||
size += fi.Size()
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return Usage{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return Usage{
|
||||
Inodes: int64(len(inodes)),
|
||||
Size: size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func diffUsage(ctx context.Context, a, b string) (Usage, error) {
|
||||
var (
|
||||
size int64
|
||||
inodes = map[inode]struct{}{} // expensive!
|
||||
)
|
||||
|
||||
if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if kind == ChangeKindAdd || kind == ChangeKindModify {
|
||||
inoKey := newInode(fi.Sys().(*syscall.Stat_t))
|
||||
if _, ok := inodes[inoKey]; !ok {
|
||||
inodes[inoKey] = struct{}{}
|
||||
size += fi.Size()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return Usage{}, err
|
||||
}
|
||||
|
||||
return Usage{
|
||||
Inodes: int64(len(inodes)),
|
||||
Size: size,
|
||||
}, nil
|
||||
}
|
||||
60
vendor/github.com/containerd/continuity/fs/du_windows.go
generated
vendored
Normal file
60
vendor/github.com/containerd/continuity/fs/du_windows.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// +build windows
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func diskUsage(roots ...string) (Usage, error) {
|
||||
var (
|
||||
size int64
|
||||
)
|
||||
|
||||
// TODO(stevvooe): Support inodes (or equivalent) for windows.
|
||||
|
||||
for _, root := range roots {
|
||||
if err := filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
size += fi.Size()
|
||||
return nil
|
||||
}); err != nil {
|
||||
return Usage{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return Usage{
|
||||
Size: size,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func diffUsage(ctx context.Context, a, b string) (Usage, error) {
|
||||
var (
|
||||
size int64
|
||||
)
|
||||
|
||||
if err := Changes(ctx, a, b, func(kind ChangeKind, _ string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if kind == ChangeKindAdd || kind == ChangeKindModify {
|
||||
size += fi.Size()
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return Usage{}, err
|
||||
}
|
||||
|
||||
return Usage{
|
||||
Size: size,
|
||||
}, nil
|
||||
}
|
||||
53
vendor/github.com/containerd/continuity/fs/fstest/compare.go
generated
vendored
Normal file
53
vendor/github.com/containerd/continuity/fs/fstest/compare.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
package fstest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/continuity"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// CheckDirectoryEqual compares two directory paths to make sure that
|
||||
// the content of the directories is the same.
|
||||
func CheckDirectoryEqual(d1, d2 string) error {
|
||||
c1, err := continuity.NewContext(d1)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to build context")
|
||||
}
|
||||
|
||||
c2, err := continuity.NewContext(d2)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to build context")
|
||||
}
|
||||
|
||||
m1, err := continuity.BuildManifest(c1)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to build manifest")
|
||||
}
|
||||
|
||||
m2, err := continuity.BuildManifest(c2)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to build manifest")
|
||||
}
|
||||
|
||||
diff := diffResourceList(m1.Resources, m2.Resources)
|
||||
if diff.HasDiff() {
|
||||
return errors.Errorf("directory diff between %s and %s\n%s", d1, d2, diff.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckDirectoryEqualWithApplier compares directory against applier
|
||||
func CheckDirectoryEqualWithApplier(root string, a Applier) error {
|
||||
applied, err := ioutil.TempDir("", "fstest")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(applied)
|
||||
if err := a.Apply(applied); err != nil {
|
||||
return err
|
||||
}
|
||||
return CheckDirectoryEqual(applied, root)
|
||||
}
|
||||
189
vendor/github.com/containerd/continuity/fs/fstest/continuity_util.go
generated
vendored
Normal file
189
vendor/github.com/containerd/continuity/fs/fstest/continuity_util.go
generated
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
package fstest
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/containerd/continuity"
|
||||
)
|
||||
|
||||
type resourceUpdate struct {
|
||||
Original continuity.Resource
|
||||
Updated continuity.Resource
|
||||
}
|
||||
|
||||
func (u resourceUpdate) String() string {
|
||||
return fmt.Sprintf("%s(mode: %o, uid: %d, gid: %d) -> %s(mode: %o, uid: %d, gid: %d)",
|
||||
u.Original.Path(), u.Original.Mode(), u.Original.UID(), u.Original.GID(),
|
||||
u.Updated.Path(), u.Updated.Mode(), u.Updated.UID(), u.Updated.GID(),
|
||||
)
|
||||
}
|
||||
|
||||
type resourceListDifference struct {
|
||||
Additions []continuity.Resource
|
||||
Deletions []continuity.Resource
|
||||
Updates []resourceUpdate
|
||||
}
|
||||
|
||||
func (l resourceListDifference) HasDiff() bool {
|
||||
return len(l.Additions) > 0 || len(l.Deletions) > 0 || len(l.Updates) > 0
|
||||
}
|
||||
|
||||
func (l resourceListDifference) String() string {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
for _, add := range l.Additions {
|
||||
fmt.Fprintf(buf, "+ %s\n", add.Path())
|
||||
}
|
||||
for _, del := range l.Deletions {
|
||||
fmt.Fprintf(buf, "- %s\n", del.Path())
|
||||
}
|
||||
for _, upt := range l.Updates {
|
||||
fmt.Fprintf(buf, "~ %s\n", upt.String())
|
||||
}
|
||||
return string(buf.Bytes())
|
||||
}
|
||||
|
||||
// diffManifest compares two resource lists and returns the list
|
||||
// of adds updates and deletes, resource lists are not reordered
|
||||
// before doing difference.
|
||||
func diffResourceList(r1, r2 []continuity.Resource) resourceListDifference {
|
||||
i1 := 0
|
||||
i2 := 0
|
||||
var d resourceListDifference
|
||||
|
||||
for i1 < len(r1) && i2 < len(r2) {
|
||||
p1 := r1[i1].Path()
|
||||
p2 := r2[i2].Path()
|
||||
switch {
|
||||
case p1 < p2:
|
||||
d.Deletions = append(d.Deletions, r1[i1])
|
||||
i1++
|
||||
case p1 == p2:
|
||||
if !compareResource(r1[i1], r2[i2]) {
|
||||
d.Updates = append(d.Updates, resourceUpdate{
|
||||
Original: r1[i1],
|
||||
Updated: r2[i2],
|
||||
})
|
||||
}
|
||||
i1++
|
||||
i2++
|
||||
case p1 > p2:
|
||||
d.Additions = append(d.Additions, r2[i2])
|
||||
i2++
|
||||
}
|
||||
}
|
||||
|
||||
for i1 < len(r1) {
|
||||
d.Deletions = append(d.Deletions, r1[i1])
|
||||
i1++
|
||||
|
||||
}
|
||||
for i2 < len(r2) {
|
||||
d.Additions = append(d.Additions, r2[i2])
|
||||
i2++
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func compareResource(r1, r2 continuity.Resource) bool {
|
||||
if r1.Path() != r2.Path() {
|
||||
return false
|
||||
}
|
||||
if r1.Mode() != r2.Mode() {
|
||||
return false
|
||||
}
|
||||
if r1.UID() != r2.UID() {
|
||||
return false
|
||||
}
|
||||
if r1.GID() != r2.GID() {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO(dmcgowan): Check if is XAttrer
|
||||
|
||||
return compareResourceTypes(r1, r2)
|
||||
|
||||
}
|
||||
|
||||
func compareResourceTypes(r1, r2 continuity.Resource) bool {
|
||||
switch t1 := r1.(type) {
|
||||
case continuity.RegularFile:
|
||||
t2, ok := r2.(continuity.RegularFile)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return compareRegularFile(t1, t2)
|
||||
case continuity.Directory:
|
||||
t2, ok := r2.(continuity.Directory)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return compareDirectory(t1, t2)
|
||||
case continuity.SymLink:
|
||||
t2, ok := r2.(continuity.SymLink)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return compareSymLink(t1, t2)
|
||||
case continuity.NamedPipe:
|
||||
t2, ok := r2.(continuity.NamedPipe)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return compareNamedPipe(t1, t2)
|
||||
case continuity.Device:
|
||||
t2, ok := r2.(continuity.Device)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return compareDevice(t1, t2)
|
||||
default:
|
||||
// TODO(dmcgowan): Should this panic?
|
||||
return r1 == r2
|
||||
}
|
||||
}
|
||||
|
||||
func compareRegularFile(r1, r2 continuity.RegularFile) bool {
|
||||
if r1.Size() != r2.Size() {
|
||||
return false
|
||||
}
|
||||
p1 := r1.Paths()
|
||||
p2 := r2.Paths()
|
||||
if len(p1) != len(p2) {
|
||||
return false
|
||||
}
|
||||
for i := range p1 {
|
||||
if p1[i] != p2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
d1 := r1.Digests()
|
||||
d2 := r2.Digests()
|
||||
if len(d1) != len(d2) {
|
||||
return false
|
||||
}
|
||||
for i := range d1 {
|
||||
if d1[i] != d2[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func compareSymLink(r1, r2 continuity.SymLink) bool {
|
||||
return r1.Target() == r2.Target()
|
||||
}
|
||||
|
||||
func compareDirectory(r1, r2 continuity.Directory) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func compareNamedPipe(r1, r2 continuity.NamedPipe) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func compareDevice(r1, r2 continuity.Device) bool {
|
||||
return r1.Major() == r2.Major() && r1.Minor() == r2.Minor()
|
||||
}
|
||||
120
vendor/github.com/containerd/continuity/fs/fstest/file.go
generated
vendored
Normal file
120
vendor/github.com/containerd/continuity/fs/fstest/file.go
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
package fstest
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Applier applies single file changes
|
||||
type Applier interface {
|
||||
Apply(root string) error
|
||||
}
|
||||
|
||||
type applyFn func(root string) error
|
||||
|
||||
func (a applyFn) Apply(root string) error {
|
||||
return a(root)
|
||||
}
|
||||
|
||||
// CreateFile returns a file applier which creates a file as the
|
||||
// provided name with the given content and permission.
|
||||
func CreateFile(name string, content []byte, perm os.FileMode) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
fullPath := filepath.Join(root, name)
|
||||
if err := ioutil.WriteFile(fullPath, content, perm); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Chmod(fullPath, perm)
|
||||
})
|
||||
}
|
||||
|
||||
// Remove returns a file applier which removes the provided file name
|
||||
func Remove(name string) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
return os.Remove(filepath.Join(root, name))
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveAll returns a file applier which removes the provided file name
|
||||
// as in os.RemoveAll
|
||||
func RemoveAll(name string) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
return os.RemoveAll(filepath.Join(root, name))
|
||||
})
|
||||
}
|
||||
|
||||
// CreateDir returns a file applier to create the directory with
|
||||
// the provided name and permission
|
||||
func CreateDir(name string, perm os.FileMode) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
fullPath := filepath.Join(root, name)
|
||||
if err := os.MkdirAll(fullPath, perm); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Chmod(fullPath, perm)
|
||||
})
|
||||
}
|
||||
|
||||
// Rename returns a file applier which renames a file
|
||||
func Rename(old, new string) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
return os.Rename(filepath.Join(root, old), filepath.Join(root, new))
|
||||
})
|
||||
}
|
||||
|
||||
// Chown returns a file applier which changes the ownership of a file
|
||||
func Chown(name string, uid, gid int) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
return os.Chown(filepath.Join(root, name), uid, gid)
|
||||
})
|
||||
}
|
||||
|
||||
// Chtimes changes access and mod time of file.
|
||||
// Use Lchtimes for symbolic links.
|
||||
func Chtimes(name string, atime, mtime time.Time) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
return os.Chtimes(filepath.Join(root, name), atime, mtime)
|
||||
})
|
||||
}
|
||||
|
||||
// Chmod returns a file applier which changes the file permission
|
||||
func Chmod(name string, perm os.FileMode) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
return os.Chmod(filepath.Join(root, name), perm)
|
||||
})
|
||||
}
|
||||
|
||||
// Symlink returns a file applier which creates a symbolic link
|
||||
func Symlink(oldname, newname string) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
return os.Symlink(oldname, filepath.Join(root, newname))
|
||||
})
|
||||
}
|
||||
|
||||
// Link returns a file applier which creates a hard link
|
||||
func Link(oldname, newname string) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
return os.Link(filepath.Join(root, oldname), filepath.Join(root, newname))
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Make platform specific, windows applier is always no-op
|
||||
//func Mknod(name string, mode int32, dev int) Applier {
|
||||
// return func(root string) error {
|
||||
// return return syscall.Mknod(path, mode, dev)
|
||||
// }
|
||||
//}
|
||||
|
||||
// Apply returns a new applier from the given appliers
|
||||
func Apply(appliers ...Applier) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
for _, a := range appliers {
|
||||
if err := a.Apply(root); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
29
vendor/github.com/containerd/continuity/fs/fstest/file_unix.go
generated
vendored
Normal file
29
vendor/github.com/containerd/continuity/fs/fstest/file_unix.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// +build !windows
|
||||
|
||||
package fstest
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/continuity/sysx"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// SetXAttr sets the xatter for the file
|
||||
func SetXAttr(name, key, value string) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
return sysx.LSetxattr(name, key, []byte(value), 0)
|
||||
})
|
||||
}
|
||||
|
||||
// Lchtimes changes access and mod time of file without following symlink
|
||||
func Lchtimes(name string, atime, mtime time.Time) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
path := filepath.Join(root, name)
|
||||
at := unix.NsecToTimespec(atime.UnixNano())
|
||||
mt := unix.NsecToTimespec(mtime.UnixNano())
|
||||
utimes := [2]unix.Timespec{at, mt}
|
||||
return unix.UtimesNanoAt(unix.AT_FDCWD, path, utimes[0:], unix.AT_SYMLINK_NOFOLLOW)
|
||||
})
|
||||
}
|
||||
14
vendor/github.com/containerd/continuity/fs/fstest/file_windows.go
generated
vendored
Normal file
14
vendor/github.com/containerd/continuity/fs/fstest/file_windows.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package fstest
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Lchtimes changes access and mod time of file without following symlink
|
||||
func Lchtimes(name string, atime, mtime time.Time) Applier {
|
||||
return applyFn(func(root string) error {
|
||||
return errors.New("Not implemented")
|
||||
})
|
||||
}
|
||||
220
vendor/github.com/containerd/continuity/fs/fstest/testsuite.go
generated
vendored
Normal file
220
vendor/github.com/containerd/continuity/fs/fstest/testsuite.go
generated
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
package fstest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestApplier applies the test context
|
||||
type TestApplier interface {
|
||||
TestContext(context.Context) (context.Context, func(), error)
|
||||
Apply(context.Context, Applier) (string, func(), error)
|
||||
}
|
||||
|
||||
// FSSuite runs the path test suite
|
||||
func FSSuite(t *testing.T, a TestApplier) {
|
||||
t.Run("Basic", makeTest(t, a, basicTest))
|
||||
t.Run("Deletion", makeTest(t, a, deletionTest))
|
||||
t.Run("Update", makeTest(t, a, updateTest))
|
||||
t.Run("DirectoryPermission", makeTest(t, a, directoryPermissionsTest))
|
||||
t.Run("ParentDirectoryPermission", makeTest(t, a, parentDirectoryPermissionsTest))
|
||||
t.Run("HardlinkUnmodified", makeTest(t, a, hardlinkUnmodified))
|
||||
t.Run("HardlinkBeforeUnmodified", makeTest(t, a, hardlinkBeforeUnmodified))
|
||||
t.Run("HardlinkBeforeModified", makeTest(t, a, hardlinkBeforeModified))
|
||||
}
|
||||
|
||||
func makeTest(t *testing.T, ta TestApplier, as []Applier) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
ctx, cleanup, err := ta.TestContext(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to get test context: %+v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
applyDir, err := ioutil.TempDir("", "test-expected-")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to make temp directory: %+v", err)
|
||||
}
|
||||
defer os.RemoveAll(applyDir)
|
||||
|
||||
for i, a := range as {
|
||||
testDir, c, err := ta.Apply(ctx, a)
|
||||
if err != nil {
|
||||
t.Fatalf("Apply failed at %d: %+v", i, err)
|
||||
}
|
||||
if err := a.Apply(applyDir); err != nil {
|
||||
if c != nil {
|
||||
c()
|
||||
}
|
||||
t.Fatalf("Error applying change to apply directory: %+v", err)
|
||||
}
|
||||
|
||||
err = CheckDirectoryEqual(applyDir, testDir)
|
||||
if c != nil {
|
||||
c()
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("Directories not equal at %d (expected <> tested): %+v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// baseApplier creates a basic filesystem layout
|
||||
// with multiple types of files for basic tests.
|
||||
baseApplier = Apply(
|
||||
CreateDir("/etc/", 0755),
|
||||
CreateFile("/etc/hosts", []byte("127.0.0.1 localhost"), 0644),
|
||||
Link("/etc/hosts", "/etc/hosts.allow"),
|
||||
CreateDir("/usr/local/lib", 0755),
|
||||
CreateFile("/usr/local/lib/libnothing.so", []byte{0x00, 0x00}, 0755),
|
||||
Symlink("libnothing.so", "/usr/local/lib/libnothing.so.2"),
|
||||
CreateDir("/home", 0755),
|
||||
CreateDir("/home/derek", 0700),
|
||||
)
|
||||
|
||||
// basicTest covers basic operations
|
||||
basicTest = []Applier{
|
||||
baseApplier,
|
||||
Apply(
|
||||
CreateFile("/etc/hosts", []byte("127.0.0.1 localhost.localdomain"), 0644),
|
||||
CreateFile("/etc/fstab", []byte("/dev/sda1\t/\text4\tdefaults 1 1\n"), 0600),
|
||||
CreateFile("/etc/badfile", []byte(""), 0666),
|
||||
CreateFile("/home/derek/.zshrc", []byte("#ZSH is just better\n"), 0640),
|
||||
),
|
||||
Apply(
|
||||
Remove("/etc/badfile"),
|
||||
Rename("/home/derek", "/home/notderek"),
|
||||
),
|
||||
Apply(
|
||||
RemoveAll("/usr"),
|
||||
Remove("/etc/hosts.allow"),
|
||||
),
|
||||
Apply(
|
||||
RemoveAll("/home"),
|
||||
CreateDir("/home/derek", 0700),
|
||||
CreateFile("/home/derek/.bashrc", []byte("#not going away\n"), 0640),
|
||||
Link("/etc/hosts", "/etc/hosts.allow"),
|
||||
),
|
||||
}
|
||||
|
||||
// deletionTest covers various deletion scenarios to ensure
|
||||
// deletions are properly picked up and applied
|
||||
deletionTest = []Applier{
|
||||
Apply(
|
||||
CreateDir("/test/somedir", 0755),
|
||||
CreateDir("/lib", 0700),
|
||||
CreateFile("/lib/hidden", []byte{}, 0644),
|
||||
),
|
||||
Apply(
|
||||
CreateFile("/test/a", []byte{}, 0644),
|
||||
CreateFile("/test/b", []byte{}, 0644),
|
||||
CreateDir("/test/otherdir", 0755),
|
||||
CreateFile("/test/otherdir/.empty", []byte{}, 0644),
|
||||
RemoveAll("/lib"),
|
||||
CreateDir("/lib", 0700),
|
||||
CreateFile("/lib/not-hidden", []byte{}, 0644),
|
||||
),
|
||||
Apply(
|
||||
Remove("/test/a"),
|
||||
Remove("/test/b"),
|
||||
RemoveAll("/test/otherdir"),
|
||||
CreateFile("/lib/newfile", []byte{}, 0644),
|
||||
),
|
||||
}
|
||||
|
||||
// updateTest covers file updates for content and permission
|
||||
updateTest = []Applier{
|
||||
Apply(
|
||||
CreateDir("/d1", 0755),
|
||||
CreateDir("/d2", 0700),
|
||||
CreateFile("/d1/f1", []byte("something..."), 0644),
|
||||
CreateFile("/d1/f2", []byte("else..."), 0644),
|
||||
CreateFile("/d1/f3", []byte("entirely..."), 0644),
|
||||
),
|
||||
Apply(
|
||||
CreateFile("/d1/f1", []byte("file content of a different length"), 0664),
|
||||
Remove("/d1/f3"),
|
||||
CreateFile("/d1/f3", []byte("updated content"), 0664),
|
||||
Chmod("/d1/f2", 0766),
|
||||
Chmod("/d2", 0777),
|
||||
),
|
||||
}
|
||||
|
||||
// directoryPermissionsTest covers directory permissions on update
|
||||
directoryPermissionsTest = []Applier{
|
||||
Apply(
|
||||
CreateDir("/d1", 0700),
|
||||
CreateDir("/d2", 0751),
|
||||
CreateDir("/d3", 0777),
|
||||
),
|
||||
Apply(
|
||||
CreateFile("/d1/f", []byte("irrelevant"), 0644),
|
||||
CreateDir("/d1/d", 0700),
|
||||
CreateFile("/d1/d/f", []byte("irrelevant"), 0644),
|
||||
CreateFile("/d2/f", []byte("irrelevant"), 0644),
|
||||
CreateFile("/d3/f", []byte("irrelevant"), 0644),
|
||||
),
|
||||
}
|
||||
|
||||
// parentDirectoryPermissionsTest covers directory permissions for updated
|
||||
// files
|
||||
parentDirectoryPermissionsTest = []Applier{
|
||||
Apply(
|
||||
CreateDir("/d1", 0700),
|
||||
CreateDir("/d1/a", 0700),
|
||||
CreateDir("/d1/a/b", 0700),
|
||||
CreateDir("/d1/a/b/c", 0700),
|
||||
CreateFile("/d1/a/b/f", []byte("content1"), 0644),
|
||||
CreateDir("/d2", 0751),
|
||||
CreateDir("/d2/a/b", 0751),
|
||||
CreateDir("/d2/a/b/c", 0751),
|
||||
CreateFile("/d2/a/b/f", []byte("content1"), 0644),
|
||||
),
|
||||
Apply(
|
||||
CreateFile("/d1/a/b/f", []byte("content1"), 0644),
|
||||
Chmod("/d1/a/b/c", 0700),
|
||||
CreateFile("/d2/a/b/f", []byte("content2"), 0644),
|
||||
Chmod("/d2/a/b/c", 0751),
|
||||
),
|
||||
}
|
||||
|
||||
hardlinkUnmodified = []Applier{
|
||||
baseApplier,
|
||||
Apply(
|
||||
CreateFile("/etc/hosts", []byte("127.0.0.1 localhost.localdomain"), 0644),
|
||||
),
|
||||
Apply(
|
||||
Link("/etc/hosts", "/etc/hosts.deny"),
|
||||
),
|
||||
}
|
||||
|
||||
// Hardlink name before with modification
|
||||
// Tests link is created for unmodified files when new hardlinked file is seen first
|
||||
hardlinkBeforeUnmodified = []Applier{
|
||||
baseApplier,
|
||||
Apply(
|
||||
CreateFile("/etc/hosts", []byte("127.0.0.1 localhost.localdomain"), 0644),
|
||||
),
|
||||
Apply(
|
||||
Link("/etc/hosts", "/etc/before-hosts"),
|
||||
),
|
||||
}
|
||||
|
||||
// Hardlink name after without modification
|
||||
// tests link is created for modified file with new hardlink
|
||||
hardlinkBeforeModified = []Applier{
|
||||
baseApplier,
|
||||
Apply(
|
||||
CreateFile("/etc/hosts", []byte("127.0.0.1 localhost.localdomain"), 0644),
|
||||
),
|
||||
Apply(
|
||||
Remove("/etc/hosts"),
|
||||
CreateFile("/etc/hosts", []byte("127.0.0.1 localhost"), 0644),
|
||||
Link("/etc/hosts", "/etc/before-hosts"),
|
||||
),
|
||||
}
|
||||
)
|
||||
27
vendor/github.com/containerd/continuity/fs/hardlink.go
generated
vendored
Normal file
27
vendor/github.com/containerd/continuity/fs/hardlink.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package fs
|
||||
|
||||
import "os"
|
||||
|
||||
// GetLinkInfo returns an identifier representing the node a hardlink is pointing
|
||||
// to. If the file is not hard linked then 0 will be returned.
|
||||
func GetLinkInfo(fi os.FileInfo) (uint64, bool) {
|
||||
return getLinkInfo(fi)
|
||||
}
|
||||
|
||||
// getLinkSource returns a path for the given name and
|
||||
// file info to its link source in the provided inode
|
||||
// map. If the given file name is not in the map and
|
||||
// has other links, it is added to the inode map
|
||||
// to be a source for other link locations.
|
||||
func getLinkSource(name string, fi os.FileInfo, inodes map[uint64]string) (string, error) {
|
||||
inode, isHardlink := getLinkInfo(fi)
|
||||
if !isHardlink {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
path, ok := inodes[inode]
|
||||
if !ok {
|
||||
inodes[inode] = name
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
18
vendor/github.com/containerd/continuity/fs/hardlink_unix.go
generated
vendored
Normal file
18
vendor/github.com/containerd/continuity/fs/hardlink_unix.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// +build !windows
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func getLinkInfo(fi os.FileInfo) (uint64, bool) {
|
||||
s, ok := fi.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Ino is uint32 on bsd, uint64 on darwin/linux/solaris
|
||||
return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 // nolint: unconvert
|
||||
}
|
||||
7
vendor/github.com/containerd/continuity/fs/hardlink_windows.go
generated
vendored
Normal file
7
vendor/github.com/containerd/continuity/fs/hardlink_windows.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package fs
|
||||
|
||||
import "os"
|
||||
|
||||
func getLinkInfo(fi os.FileInfo) (uint64, bool) {
|
||||
return 0, false
|
||||
}
|
||||
276
vendor/github.com/containerd/continuity/fs/path.go
generated
vendored
Normal file
276
vendor/github.com/containerd/continuity/fs/path.go
generated
vendored
Normal file
@@ -0,0 +1,276 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
errTooManyLinks = errors.New("too many links")
|
||||
)
|
||||
|
||||
type currentPath struct {
|
||||
path string
|
||||
f os.FileInfo
|
||||
fullPath string
|
||||
}
|
||||
|
||||
func pathChange(lower, upper *currentPath) (ChangeKind, string) {
|
||||
if lower == nil {
|
||||
if upper == nil {
|
||||
panic("cannot compare nil paths")
|
||||
}
|
||||
return ChangeKindAdd, upper.path
|
||||
}
|
||||
if upper == nil {
|
||||
return ChangeKindDelete, lower.path
|
||||
}
|
||||
// TODO: compare by directory
|
||||
|
||||
switch i := strings.Compare(lower.path, upper.path); {
|
||||
case i < 0:
|
||||
// File in lower that is not in upper
|
||||
return ChangeKindDelete, lower.path
|
||||
case i > 0:
|
||||
// File in upper that is not in lower
|
||||
return ChangeKindAdd, upper.path
|
||||
default:
|
||||
return ChangeKindModify, upper.path
|
||||
}
|
||||
}
|
||||
|
||||
func sameFile(f1, f2 *currentPath) (bool, error) {
|
||||
if os.SameFile(f1.f, f2.f) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
equalStat, err := compareSysStat(f1.f.Sys(), f2.f.Sys())
|
||||
if err != nil || !equalStat {
|
||||
return equalStat, err
|
||||
}
|
||||
|
||||
if eq, err := compareCapabilities(f1.fullPath, f2.fullPath); err != nil || !eq {
|
||||
return eq, err
|
||||
}
|
||||
|
||||
// If not a directory also check size, modtime, and content
|
||||
if !f1.f.IsDir() {
|
||||
if f1.f.Size() != f2.f.Size() {
|
||||
return false, nil
|
||||
}
|
||||
t1 := f1.f.ModTime()
|
||||
t2 := f2.f.ModTime()
|
||||
|
||||
if t1.Unix() != t2.Unix() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// If the timestamp may have been truncated in both of the
|
||||
// files, check content of file to determine difference
|
||||
if t1.Nanosecond() == 0 && t2.Nanosecond() == 0 {
|
||||
var eq bool
|
||||
if (f1.f.Mode() & os.ModeSymlink) == os.ModeSymlink {
|
||||
eq, err = compareSymlinkTarget(f1.fullPath, f2.fullPath)
|
||||
} else if f1.f.Size() > 0 {
|
||||
eq, err = compareFileContent(f1.fullPath, f2.fullPath)
|
||||
}
|
||||
if err != nil || !eq {
|
||||
return eq, err
|
||||
}
|
||||
} else if t1.Nanosecond() != t2.Nanosecond() {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func compareSymlinkTarget(p1, p2 string) (bool, error) {
|
||||
t1, err := os.Readlink(p1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
t2, err := os.Readlink(p2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return t1 == t2, nil
|
||||
}
|
||||
|
||||
const compareChuckSize = 32 * 1024
|
||||
|
||||
// compareFileContent compares the content of 2 same sized files
|
||||
// by comparing each byte.
|
||||
func compareFileContent(p1, p2 string) (bool, error) {
|
||||
f1, err := os.Open(p1)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f1.Close()
|
||||
f2, err := os.Open(p2)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f2.Close()
|
||||
|
||||
b1 := make([]byte, compareChuckSize)
|
||||
b2 := make([]byte, compareChuckSize)
|
||||
for {
|
||||
n1, err1 := f1.Read(b1)
|
||||
if err1 != nil && err1 != io.EOF {
|
||||
return false, err1
|
||||
}
|
||||
n2, err2 := f2.Read(b2)
|
||||
if err2 != nil && err2 != io.EOF {
|
||||
return false, err2
|
||||
}
|
||||
if n1 != n2 || !bytes.Equal(b1[:n1], b2[:n2]) {
|
||||
return false, nil
|
||||
}
|
||||
if err1 == io.EOF && err2 == io.EOF {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func pathWalk(ctx context.Context, root string, pathC chan<- *currentPath) error {
|
||||
return filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Rebase path
|
||||
path, err = filepath.Rel(root, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
path = filepath.Join(string(os.PathSeparator), path)
|
||||
|
||||
// Skip root
|
||||
if path == string(os.PathSeparator) {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := ¤tPath{
|
||||
path: path,
|
||||
f: f,
|
||||
fullPath: filepath.Join(root, path),
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case pathC <- p:
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func nextPath(ctx context.Context, pathC <-chan *currentPath) (*currentPath, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case p := <-pathC:
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
|
||||
// RootPath joins a path with a root, evaluating and bounding any
|
||||
// symlink to the root directory.
|
||||
func RootPath(root, path string) (string, error) {
|
||||
if path == "" {
|
||||
return root, nil
|
||||
}
|
||||
var linksWalked int // to protect against cycles
|
||||
for {
|
||||
i := linksWalked
|
||||
newpath, err := walkLinks(root, path, &linksWalked)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
path = newpath
|
||||
if i == linksWalked {
|
||||
newpath = filepath.Join("/", newpath)
|
||||
if path == newpath {
|
||||
return filepath.Join(root, newpath), nil
|
||||
}
|
||||
path = newpath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func walkLink(root, path string, linksWalked *int) (newpath string, islink bool, err error) {
|
||||
if *linksWalked > 255 {
|
||||
return "", false, errTooManyLinks
|
||||
}
|
||||
|
||||
path = filepath.Join("/", path)
|
||||
if path == "/" {
|
||||
return path, false, nil
|
||||
}
|
||||
realPath := filepath.Join(root, path)
|
||||
|
||||
fi, err := os.Lstat(realPath)
|
||||
if err != nil {
|
||||
// If path does not yet exist, treat as non-symlink
|
||||
if os.IsNotExist(err) {
|
||||
return path, false, nil
|
||||
}
|
||||
return "", false, err
|
||||
}
|
||||
if fi.Mode()&os.ModeSymlink == 0 {
|
||||
return path, false, nil
|
||||
}
|
||||
newpath, err = os.Readlink(realPath)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if filepath.IsAbs(newpath) && strings.HasPrefix(newpath, root) {
|
||||
newpath = newpath[:len(root)]
|
||||
if !strings.HasPrefix(newpath, "/") {
|
||||
newpath = "/" + newpath
|
||||
}
|
||||
}
|
||||
*linksWalked++
|
||||
return newpath, true, nil
|
||||
}
|
||||
|
||||
func walkLinks(root, path string, linksWalked *int) (string, error) {
|
||||
switch dir, file := filepath.Split(path); {
|
||||
case dir == "":
|
||||
newpath, _, err := walkLink(root, file, linksWalked)
|
||||
return newpath, err
|
||||
case file == "":
|
||||
if os.IsPathSeparator(dir[len(dir)-1]) {
|
||||
if dir == "/" {
|
||||
return dir, nil
|
||||
}
|
||||
return walkLinks(root, dir[:len(dir)-1], linksWalked)
|
||||
}
|
||||
newpath, _, err := walkLink(root, dir, linksWalked)
|
||||
return newpath, err
|
||||
default:
|
||||
newdir, err := walkLinks(root, dir, linksWalked)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
newpath, islink, err := walkLink(root, filepath.Join(newdir, file), linksWalked)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !islink {
|
||||
return newpath, nil
|
||||
}
|
||||
if filepath.IsAbs(newpath) {
|
||||
return newpath, nil
|
||||
}
|
||||
return filepath.Join(newdir, newpath), nil
|
||||
}
|
||||
}
|
||||
28
vendor/github.com/containerd/continuity/fs/stat_bsd.go
generated
vendored
Normal file
28
vendor/github.com/containerd/continuity/fs/stat_bsd.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// +build darwin freebsd
|
||||
|
||||
package fs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StatAtime returns the access time from a stat struct
|
||||
func StatAtime(st *syscall.Stat_t) syscall.Timespec {
|
||||
return st.Atimespec
|
||||
}
|
||||
|
||||
// StatCtime returns the created time from a stat struct
|
||||
func StatCtime(st *syscall.Stat_t) syscall.Timespec {
|
||||
return st.Ctimespec
|
||||
}
|
||||
|
||||
// StatMtime returns the modified time from a stat struct
|
||||
func StatMtime(st *syscall.Stat_t) syscall.Timespec {
|
||||
return st.Mtimespec
|
||||
}
|
||||
|
||||
// StatATimeAsTime returns the access time as a time.Time
|
||||
func StatATimeAsTime(st *syscall.Stat_t) time.Time {
|
||||
return time.Unix(int64(st.Atimespec.Sec), int64(st.Atimespec.Nsec)) // nolint: unconvert
|
||||
}
|
||||
26
vendor/github.com/containerd/continuity/fs/stat_linux.go
generated
vendored
Normal file
26
vendor/github.com/containerd/continuity/fs/stat_linux.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StatAtime returns the Atim
|
||||
func StatAtime(st *syscall.Stat_t) syscall.Timespec {
|
||||
return st.Atim
|
||||
}
|
||||
|
||||
// StatCtime returns the Ctim
|
||||
func StatCtime(st *syscall.Stat_t) syscall.Timespec {
|
||||
return st.Ctim
|
||||
}
|
||||
|
||||
// StatMtime returns the Mtim
|
||||
func StatMtime(st *syscall.Stat_t) syscall.Timespec {
|
||||
return st.Mtim
|
||||
}
|
||||
|
||||
// StatATimeAsTime returns st.Atim as a time.Time
|
||||
func StatATimeAsTime(st *syscall.Stat_t) time.Time {
|
||||
return time.Unix(st.Atim.Sec, st.Atim.Nsec)
|
||||
}
|
||||
13
vendor/github.com/containerd/continuity/fs/time.go
generated
vendored
Normal file
13
vendor/github.com/containerd/continuity/fs/time.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package fs
|
||||
|
||||
import "time"
|
||||
|
||||
// Gnu tar and the go tar writer don't have sub-second mtime
|
||||
// precision, which is problematic when we apply changes via tar
|
||||
// files, we handle this by comparing for exact times, *or* same
|
||||
// second count and either a or b having exactly 0 nanoseconds
|
||||
func sameFsTime(a, b time.Time) bool {
|
||||
return a == b ||
|
||||
(a.Unix() == b.Unix() &&
|
||||
(a.Nanosecond() == 0 || b.Nanosecond() == 0))
|
||||
}
|
||||
4
vendor/github.com/containerd/continuity/proto/manifest.pb.go
generated
vendored
4
vendor/github.com/containerd/continuity/proto/manifest.pb.go
generated
vendored
@@ -82,9 +82,9 @@ type Resource struct {
|
||||
// Relative links do not start with a slash and are relative to the
|
||||
// resource path.
|
||||
Target string `protobuf:"bytes,9,opt,name=target" json:"target,omitempty"`
|
||||
// Major specifies the major device number for charactor and block devices.
|
||||
// Major specifies the major device number for character and block devices.
|
||||
Major uint64 `protobuf:"varint,10,opt,name=major" json:"major,omitempty"`
|
||||
// Minor specifies the minor device number for charactor and block devices.
|
||||
// Minor specifies the minor device number for character and block devices.
|
||||
Minor uint64 `protobuf:"varint,11,opt,name=minor" json:"minor,omitempty"`
|
||||
// Xattr provides storage for extended attributes for the target resource.
|
||||
Xattr []*XAttr `protobuf:"bytes,12,rep,name=xattr" json:"xattr,omitempty"`
|
||||
|
||||
6
vendor/github.com/containerd/continuity/proto/manifest.proto
generated
vendored
6
vendor/github.com/containerd/continuity/proto/manifest.proto
generated
vendored
@@ -16,7 +16,7 @@ message Resource {
|
||||
|
||||
// NOTE(stevvooe): Need to define clear precedence for user/group/uid/gid precedence.
|
||||
|
||||
// Uid specifies the user id for the resource.
|
||||
// Uid specifies the user id for the resource.
|
||||
int64 uid = 2;
|
||||
|
||||
// Gid specifies the group id for the resource.
|
||||
@@ -53,10 +53,10 @@ message Resource {
|
||||
// resource path.
|
||||
string target = 9;
|
||||
|
||||
// Major specifies the major device number for charactor and block devices.
|
||||
// Major specifies the major device number for character and block devices.
|
||||
uint64 major = 10;
|
||||
|
||||
// Minor specifies the minor device number for charactor and block devices.
|
||||
// Minor specifies the minor device number for character and block devices.
|
||||
uint64 minor = 11;
|
||||
|
||||
// Xattr provides storage for extended attributes for the target resource.
|
||||
|
||||
11
vendor/github.com/containerd/continuity/sysx/copy_linux.go
generated
vendored
11
vendor/github.com/containerd/continuity/sysx/copy_linux.go
generated
vendored
@@ -1,11 +0,0 @@
|
||||
package sysx
|
||||
|
||||
// These functions will be generated by generate.sh
|
||||
// $ GOOS=linux GOARCH=386 ./generate.sh copy
|
||||
// $ GOOS=linux GOARCH=amd64 ./generate.sh copy
|
||||
// $ GOOS=linux GOARCH=arm ./generate.sh copy
|
||||
// $ GOOS=linux GOARCH=arm64 ./generate.sh copy
|
||||
// $ GOOS=linux GOARCH=ppc64le ./generate.sh copy
|
||||
// $ GOOS=linux GOARCH=s390x ./generate.sh copy
|
||||
|
||||
//sys CopyFileRange(fdin uintptr, offin *int64, fdout uintptr, offout *int64, len int, flags int) (n int, err error)
|
||||
20
vendor/github.com/containerd/continuity/sysx/copy_linux_386.go
generated
vendored
20
vendor/github.com/containerd/continuity/sysx/copy_linux_386.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
// mksyscall.pl -l32 copy_linux.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package sysx
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func CopyFileRange(fdin uintptr, offin *int64, fdout uintptr, offout *int64, len int, flags int) (n int, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(SYS_COPY_FILE_RANGE, uintptr(fdin), uintptr(unsafe.Pointer(offin)), uintptr(fdout), uintptr(unsafe.Pointer(offout)), uintptr(len), uintptr(flags))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
20
vendor/github.com/containerd/continuity/sysx/copy_linux_amd64.go
generated
vendored
20
vendor/github.com/containerd/continuity/sysx/copy_linux_amd64.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
// mksyscall.pl copy_linux.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package sysx
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func CopyFileRange(fdin uintptr, offin *int64, fdout uintptr, offout *int64, len int, flags int) (n int, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(SYS_COPY_FILE_RANGE, uintptr(fdin), uintptr(unsafe.Pointer(offin)), uintptr(fdout), uintptr(unsafe.Pointer(offout)), uintptr(len), uintptr(flags))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
20
vendor/github.com/containerd/continuity/sysx/copy_linux_arm.go
generated
vendored
20
vendor/github.com/containerd/continuity/sysx/copy_linux_arm.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
// mksyscall.pl -l32 copy_linux.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package sysx
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func CopyFileRange(fdin uintptr, offin *int64, fdout uintptr, offout *int64, len int, flags int) (n int, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(SYS_COPY_FILE_RANGE, uintptr(fdin), uintptr(unsafe.Pointer(offin)), uintptr(fdout), uintptr(unsafe.Pointer(offout)), uintptr(len), uintptr(flags))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
20
vendor/github.com/containerd/continuity/sysx/copy_linux_arm64.go
generated
vendored
20
vendor/github.com/containerd/continuity/sysx/copy_linux_arm64.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
// mksyscall.pl copy_linux.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package sysx
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func CopyFileRange(fdin uintptr, offin *int64, fdout uintptr, offout *int64, len int, flags int) (n int, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(SYS_COPY_FILE_RANGE, uintptr(fdin), uintptr(unsafe.Pointer(offin)), uintptr(fdout), uintptr(unsafe.Pointer(offout)), uintptr(len), uintptr(flags))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
20
vendor/github.com/containerd/continuity/sysx/copy_linux_ppc64le.go
generated
vendored
20
vendor/github.com/containerd/continuity/sysx/copy_linux_ppc64le.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
// mksyscall.pl copy_linux.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package sysx
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func CopyFileRange(fdin uintptr, offin *int64, fdout uintptr, offout *int64, len int, flags int) (n int, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(SYS_COPY_FILE_RANGE, uintptr(fdin), uintptr(unsafe.Pointer(offin)), uintptr(fdout), uintptr(unsafe.Pointer(offout)), uintptr(len), uintptr(flags))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
20
vendor/github.com/containerd/continuity/sysx/copy_linux_s390x.go
generated
vendored
20
vendor/github.com/containerd/continuity/sysx/copy_linux_s390x.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
// mksyscall.pl copy_linux.go
|
||||
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
|
||||
|
||||
package sysx
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func CopyFileRange(fdin uintptr, offin *int64, fdout uintptr, offout *int64, len int, flags int) (n int, err error) {
|
||||
r0, _, e1 := syscall.Syscall6(SYS_COPY_FILE_RANGE, uintptr(fdin), uintptr(unsafe.Pointer(offin)), uintptr(fdout), uintptr(unsafe.Pointer(offout)), uintptr(len), uintptr(flags))
|
||||
n = int(r0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_386.go
generated
vendored
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_386.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
package sysx
|
||||
|
||||
const (
|
||||
// SYS_COPYFILERANGE defined in Kernel 4.5+
|
||||
// Number defined in /usr/include/asm/unistd_32.h
|
||||
SYS_COPY_FILE_RANGE = 377
|
||||
)
|
||||
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_amd64.go
generated
vendored
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_amd64.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
package sysx
|
||||
|
||||
const (
|
||||
// SYS_COPYFILERANGE defined in Kernel 4.5+
|
||||
// Number defined in /usr/include/asm/unistd_64.h
|
||||
SYS_COPY_FILE_RANGE = 326
|
||||
)
|
||||
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_arm.go
generated
vendored
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_arm.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
package sysx
|
||||
|
||||
const (
|
||||
// SYS_COPY_FILE_RANGE defined in Kernel 4.5+
|
||||
// Number defined in /usr/include/arm-linux-gnueabihf/asm/unistd.h
|
||||
SYS_COPY_FILE_RANGE = 391
|
||||
)
|
||||
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_arm64.go
generated
vendored
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_arm64.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
package sysx
|
||||
|
||||
const (
|
||||
// SYS_COPY_FILE_RANGE defined in Kernel 4.5+
|
||||
// Number defined in /usr/include/asm-generic/unistd.h
|
||||
SYS_COPY_FILE_RANGE = 285
|
||||
)
|
||||
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_ppc64le.go
generated
vendored
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_ppc64le.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
package sysx
|
||||
|
||||
const (
|
||||
// SYS_COPYFILERANGE defined in Kernel 4.5+
|
||||
// Number defined in /usr/include/asm/unistd_64.h
|
||||
SYS_COPY_FILE_RANGE = 379
|
||||
)
|
||||
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_s390x.go
generated
vendored
7
vendor/github.com/containerd/continuity/sysx/sysnum_linux_s390x.go
generated
vendored
@@ -1,7 +0,0 @@
|
||||
package sysx
|
||||
|
||||
const (
|
||||
// SYS_COPYFILERANGE defined in Kernel 4.5+
|
||||
// Number defined in /usr/include/asm/unistd_64.h
|
||||
SYS_COPY_FILE_RANGE = 375
|
||||
)
|
||||
13
vendor/github.com/containerd/continuity/vendor.conf
generated
vendored
Normal file
13
vendor/github.com/containerd/continuity/vendor.conf
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
bazil.org/fuse 371fbbdaa8987b715bdd21d6adc4c9b20155f748
|
||||
github.com/dustin/go-humanize bb3d318650d48840a39aa21a027c6630e198e626
|
||||
github.com/golang/protobuf 1e59b77b52bf8e4b449a57e6f79f21226d571845
|
||||
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
github.com/opencontainers/go-digest 279bed98673dd5bef374d3b6e4b09e2af76183bf
|
||||
github.com/pkg/errors f15c970de5b76fac0b59abb32d62c17cc7bed265
|
||||
github.com/sirupsen/logrus 89742aefa4b206dcf400792f3bd35b542998eb3b
|
||||
github.com/spf13/cobra 2da4a54c5ceefcee7ca5dd0eea1e18a3b6366489
|
||||
github.com/spf13/pflag 4c012f6dcd9546820e378d0bdda4d8fc772cdfea
|
||||
golang.org/x/crypto 9f005a07e0d31d45e6656d241bb5c0f2efd4bc94
|
||||
golang.org/x/net a337091b0525af65de94df2eb7e98bd9962dcbe2
|
||||
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
|
||||
golang.org/x/sys 665f6529cca930e27b831a0d1dafffbe1c172924
|
||||
Reference in New Issue
Block a user