Merge pull request #43770 from xinfengliu/20.10_backport_update_continuity

[20.10] vendor: github.com/containerd/continuity v0.3.0
This commit is contained in:
Sebastiaan van Stijn 2022-07-06 14:52:17 +02:00 committed by GitHub
commit 3949ff121e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 381 additions and 156 deletions

View file

@ -134,7 +134,7 @@ google.golang.org/genproto 3f1135a288c9a07e340ae8ba4cc6
# containerd
github.com/containerd/containerd 7cfa023d95d37076d5ab035003d4839f4b6ba791 https://github.com/moby/containerd.git # master (v1.5.0-dev) + patch for CVE-2021-41190 and CVE-2022-24769
github.com/containerd/fifo 0724c46b320cf96bb172a0550c19a4b1fca4dacb
github.com/containerd/continuity efbc4488d8fe1bdc16bde3b2d2990d9b3a899165
github.com/containerd/continuity 5ad51c7aca47b8e742f5e6e7dc841d50f5f6affd # v0.3.0
github.com/containerd/cgroups b9de8a2212026c07cec67baf3323f1fc0121e048 # v1.0.1
github.com/containerd/console 5d7e1412f07b502a01029ea20e20e0d2be31fa7c # v1.0.1
github.com/containerd/go-runc 16b287bc67d069a60fa48db15f330b790b74365b

View file

@ -1,19 +1,20 @@
# continuity
[![GoDoc](https://godoc.org/github.com/containerd/continuity?status.svg)](https://godoc.org/github.com/containerd/continuity)
[![Build Status](https://travis-ci.org/containerd/continuity.svg?branch=master)](https://travis-ci.org/containerd/continuity)
[![Go Reference](https://pkg.go.dev/badge/github.com/containerd/continuity.svg)](https://pkg.go.dev/github.com/containerd/continuity)
[![Build Status](https://github.com/containerd/continuity/workflows/Continuity/badge.svg)](https://github.com/containerd/continuity/actions?query=workflow%3AContinuity+branch%3Amain)
A transport-agnostic, filesystem metadata manifest system
This project is a staging area for experiments in providing transport agnostic
metadata storage.
Please see https://github.com/opencontainers/specs/issues/11 for more details.
See [opencontainers/runtime-spec#11](https://github.com/opencontainers/runtime-spec/issues/11)
for more details.
## Manifest Format
A continuity manifest encodes filesystem metadata in Protocol Buffers.
Please refer to [proto/manifest.proto](proto/manifest.proto).
Refer to [proto/manifest.proto](proto/manifest.proto) for more details.
## Usage
@ -63,6 +64,10 @@ $ stat -c %a Makefile
$ ./bin/continuity verify . /tmp/a.pb
```
## Platforms
continuity primarily targets Linux. Continuity may compile for and work on
other operating systems, but those platforms are not tested.
## Contribution Guide
### Building Proto Package
@ -77,8 +82,8 @@ $ go generate ./proto
continuity is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
* [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.

View file

@ -1,4 +1,5 @@
// +build linux darwin freebsd solaris
//go:build !windows
// +build !windows
/*
Copyright The containerd Authors.
@ -56,7 +57,7 @@ func Mknod(p string, mode os.FileMode, maj, min int) error {
m |= unix.S_IFIFO
}
return unix.Mknod(p, m, int(dev))
return mknod(p, m, dev)
}
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.

View file

@ -17,11 +17,10 @@
package devices
import (
"fmt"
"os"
"github.com/pkg/errors"
)
func DeviceInfo(fi os.FileInfo) (uint64, uint64, error) {
return 0, 0, errors.Wrap(ErrNotSupported, "cannot get device info on windows")
return 0, 0, fmt.Errorf("cannot get device info on windows: %w", ErrNotSupported)
}

View file

@ -0,0 +1,26 @@
//go:build freebsd
// +build freebsd
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package devices
import "golang.org/x/sys/unix"
func mknod(path string, mode uint32, dev uint64) (err error) {
return unix.Mknod(path, mode, dev)
}

View file

@ -0,0 +1,26 @@
//go:build !(freebsd || windows)
// +build !freebsd,!windows
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package devices
import "golang.org/x/sys/unix"
func mknod(path string, mode uint32, dev uint64) (err error) {
return unix.Mknod(path, mode, int(dev))
}

View file

@ -1,4 +1,5 @@
// +build linux darwin freebsd solaris
//go:build !windows
// +build !windows
/*
Copyright The containerd Authors.
@ -53,7 +54,7 @@ func (d *driver) Mkfifo(path string, mode os.FileMode) error {
func (d *driver) Getxattr(p string) (map[string][]byte, error) {
xattrs, err := sysx.Listxattr(p)
if err != nil {
return nil, fmt.Errorf("listing %s xattrs: %v", p, err)
return nil, fmt.Errorf("listing %s xattrs: %w", p, err)
}
sort.Strings(xattrs)
@ -62,7 +63,7 @@ func (d *driver) Getxattr(p string) (map[string][]byte, error) {
for _, attr := range xattrs {
value, err := sysx.Getxattr(p, attr)
if err != nil {
return nil, fmt.Errorf("getting %q xattr on %s: %v", attr, p, err)
return nil, fmt.Errorf("getting %q xattr on %s: %w", attr, p, err)
}
// NOTE(stevvooe): This append/copy tricky relies on unique
@ -81,7 +82,7 @@ func (d *driver) Getxattr(p string) (map[string][]byte, error) {
func (d *driver) Setxattr(path string, attrMap map[string][]byte) error {
for attr, value := range attrMap {
if err := sysx.Setxattr(path, attr, value, 0); err != nil {
return fmt.Errorf("error setting xattr %q on %s: %v", attr, path, err)
return fmt.Errorf("error setting xattr %q on %s: %w", attr, path, err)
}
}
@ -93,7 +94,7 @@ func (d *driver) Setxattr(path string, attrMap map[string][]byte) error {
func (d *driver) LGetxattr(p string) (map[string][]byte, error) {
xattrs, err := sysx.LListxattr(p)
if err != nil {
return nil, fmt.Errorf("listing %s xattrs: %v", p, err)
return nil, fmt.Errorf("listing %s xattrs: %w", p, err)
}
sort.Strings(xattrs)
@ -102,7 +103,7 @@ func (d *driver) LGetxattr(p string) (map[string][]byte, error) {
for _, attr := range xattrs {
value, err := sysx.LGetxattr(p, attr)
if err != nil {
return nil, fmt.Errorf("getting %q xattr on %s: %v", attr, p, err)
return nil, fmt.Errorf("getting %q xattr on %s: %w", attr, p, err)
}
// NOTE(stevvooe): This append/copy tricky relies on unique
@ -121,7 +122,7 @@ func (d *driver) LGetxattr(p string) (map[string][]byte, error) {
func (d *driver) LSetxattr(path string, attrMap map[string][]byte) error {
for attr, value := range attrMap {
if err := sysx.LSetxattr(path, attr, value, 0); err != nil {
return fmt.Errorf("error setting xattr %q on %s: %v", attr, path, err)
return fmt.Errorf("error setting xattr %q on %s: %w", attr, path, err)
}
}

View file

@ -1,3 +1,4 @@
//go:build go1.13
// +build go1.13
/*

View file

@ -1,4 +1,5 @@
// +build darwin freebsd solaris
//go:build darwin || freebsd || netbsd || openbsd || solaris
// +build darwin freebsd netbsd openbsd solaris
/*
Copyright The containerd Authors.

View file

@ -18,12 +18,11 @@ package driver
import (
"io"
"io/ioutil"
"os"
"sort"
)
// ReadFile works the same as ioutil.ReadFile with the Driver abstraction
// ReadFile works the same as os.ReadFile with the Driver abstraction
func ReadFile(r Driver, filename string) ([]byte, error) {
f, err := r.Open(filename)
if err != nil {
@ -31,7 +30,7 @@ func ReadFile(r Driver, filename string) ([]byte, error) {
}
defer f.Close()
data, err := ioutil.ReadAll(f)
data, err := io.ReadAll(f)
if err != nil {
return nil, err
}
@ -39,7 +38,7 @@ func ReadFile(r Driver, filename string) ([]byte, error) {
return data, nil
}
// WriteFile works the same as ioutil.WriteFile with the Driver abstraction
// WriteFile works the same as os.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 {

View file

@ -17,12 +17,13 @@
package fs
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sync"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var bufferPool = &sync.Pool{
@ -32,13 +33,15 @@ var bufferPool = &sync.Pool{
},
}
// XAttrErrorHandlers transform a non-nil xattr error.
// XAttrErrorHandler transform a non-nil xattr error.
// Return nil to ignore an error.
// xattrKey can be empty for listxattr operation.
type XAttrErrorHandler func(dst, src, xattrKey string, err error) error
type copyDirOpts struct {
xeh XAttrErrorHandler
// xex contains a set of xattrs to exclude when copying
xex map[string]struct{}
}
type CopyDirOpt func(*copyDirOpts) error
@ -61,6 +64,19 @@ func WithAllowXAttrErrors() CopyDirOpt {
return WithXAttrErrorHandler(xeh)
}
// WithXAttrExclude allows for exclusion of specified xattr during CopyDir operation.
func WithXAttrExclude(keys ...string) CopyDirOpt {
return func(o *copyDirOpts) error {
if o.xex == nil {
o.xex = make(map[string]struct{}, len(keys))
}
for _, key := range keys {
o.xex[key] = struct{}{}
}
return nil
}
}
// CopyDir copies the directory from src to dst.
// Most efficient copy of files is attempted.
func CopyDir(dst, src string, opts ...CopyDirOpt) error {
@ -77,35 +93,35 @@ func CopyDir(dst, src string, opts ...CopyDirOpt) error {
func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) error {
stat, err := os.Stat(src)
if err != nil {
return errors.Wrapf(err, "failed to stat %s", src)
return fmt.Errorf("failed to stat %s: %w", src, err)
}
if !stat.IsDir() {
return errors.Errorf("source %s is not directory", src)
return fmt.Errorf("source %s is not directory", src)
}
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)
return fmt.Errorf("failed to mkdir %s: %w", dst, err)
}
} else if !st.IsDir() {
return errors.Errorf("cannot copy to non-directory: %s", dst)
return fmt.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)
return fmt.Errorf("failed to chmod on %s: %w", dst, err)
}
}
fis, err := ioutil.ReadDir(src)
if err != nil {
return errors.Wrapf(err, "failed to read %s", src)
return fmt.Errorf("failed to read %s: %w", src, err)
}
if err := copyFileInfo(stat, dst); err != nil {
return errors.Wrapf(err, "failed to copy file info for %s", dst)
if err := copyFileInfo(stat, src, dst); err != nil {
return fmt.Errorf("failed to copy file info for %s: %w", dst, err)
}
if err := copyXAttrs(dst, src, o.xeh); err != nil {
return errors.Wrap(err, "failed to copy xattrs")
if err := copyXAttrs(dst, src, o.xex, o.xeh); err != nil {
return fmt.Errorf("failed to copy xattrs: %w", err)
}
for _, fi := range fis {
@ -121,37 +137,40 @@ func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) er
case (fi.Mode() & os.ModeType) == 0:
link, err := getLinkSource(target, fi, inodes)
if err != nil {
return errors.Wrap(err, "failed to get hardlink")
return fmt.Errorf("failed to get hardlink: %w", err)
}
if link != "" {
if err := os.Link(link, target); err != nil {
return errors.Wrap(err, "failed to create hard link")
return fmt.Errorf("failed to create hard link: %w", err)
}
} else if err := CopyFile(target, source); err != nil {
return errors.Wrap(err, "failed to copy files")
return fmt.Errorf("failed to copy files: %w", err)
}
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)
return fmt.Errorf("failed to read link: %s: %w", source, err)
}
if err := os.Symlink(link, target); err != nil {
return errors.Wrapf(err, "failed to create symlink: %s", target)
return fmt.Errorf("failed to create symlink: %s: %w", target, err)
}
case (fi.Mode() & os.ModeDevice) == os.ModeDevice:
if err := copyDevice(target, fi); err != nil {
return errors.Wrapf(err, "failed to create device")
case (fi.Mode() & os.ModeDevice) == os.ModeDevice,
(fi.Mode() & os.ModeNamedPipe) == os.ModeNamedPipe,
(fi.Mode() & os.ModeSocket) == os.ModeSocket:
if err := copyIrregular(target, fi); err != nil {
return fmt.Errorf("failed to create irregular file: %w", err)
}
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")
logrus.Warnf("unsupported mode: %s: %s", source, fi.Mode())
continue
}
if err := copyXAttrs(target, source, o.xeh); err != nil {
return errors.Wrap(err, "failed to copy xattrs")
if err := copyFileInfo(fi, source, target); err != nil {
return fmt.Errorf("failed to copy file info: %w", err)
}
if err := copyXAttrs(target, source, o.xex, o.xeh); err != nil {
return fmt.Errorf("failed to copy xattrs: %w", err)
}
}
@ -163,12 +182,12 @@ func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) er
func CopyFile(target, source string) error {
src, err := os.Open(source)
if err != nil {
return errors.Wrapf(err, "failed to open source %s", source)
return fmt.Errorf("failed to open source %s: %w", source, err)
}
defer src.Close()
tgt, err := os.Create(target)
if err != nil {
return errors.Wrapf(err, "failed to open target %s", target)
return fmt.Errorf("failed to open target %s: %w", target, err)
}
defer tgt.Close()

View file

@ -0,0 +1,36 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fs
import (
"fmt"
"os"
"syscall"
)
// copyIrregular covers devices, pipes, and sockets
func copyIrregular(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t) // not *unix.Stat_t
if !ok {
return fmt.Errorf("unsupported stat type: %s: %v", dst, fi.Mode())
}
var rDev uint64 // uint64 on FreeBSD, int on other unixen
if fi.Mode()&os.ModeDevice == os.ModeDevice {
rDev = st.Rdev
}
return syscall.Mknod(dst, uint32(st.Mode), rDev)
}

View file

@ -0,0 +1,40 @@
//go:build !windows && !freebsd
// +build !windows,!freebsd
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fs
import (
"fmt"
"os"
"syscall"
)
// copyIrregular covers devices, pipes, and sockets
func copyIrregular(dst string, fi os.FileInfo) error {
st, ok := fi.Sys().(*syscall.Stat_t) // not *unix.Stat_t
if !ok {
return fmt.Errorf("unsupported stat type: %s: %v", dst, fi.Mode())
}
var rDev int
if fi.Mode()&os.ModeDevice == os.ModeDevice {
rDev = int(st.Rdev)
}
//nolint:unconvert
return syscall.Mknod(dst, uint32(st.Mode), rDev)
}

View file

@ -17,16 +17,16 @@
package fs
import (
"fmt"
"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 {
func copyFileInfo(fi os.FileInfo, src, 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) {
@ -41,13 +41,13 @@ func copyFileInfo(fi os.FileInfo, name string) error {
}
}
if err != nil {
return errors.Wrapf(err, "failed to chown %s", name)
return fmt.Errorf("failed to chown %s: %w", name, err)
}
}
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)
return fmt.Errorf("failed to chmod %s: %w", name, err)
}
}
@ -56,7 +56,7 @@ func copyFileInfo(fi os.FileInfo, name string) error {
unix.NsecToTimespec(syscall.TimespecToNsec(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 fmt.Errorf("failed to utime %s: %w", name, err)
}
return nil
@ -67,7 +67,7 @@ const maxSSizeT = int64(^uint(0) >> 1)
func copyFileContent(dst, src *os.File) error {
st, err := src.Stat()
if err != nil {
return errors.Wrap(err, "unable to stat source")
return fmt.Errorf("unable to stat source: %w", err)
}
size := st.Size()
@ -88,13 +88,16 @@ func copyFileContent(dst, src *os.File) error {
n, err := unix.CopyFileRange(srcFd, nil, dstFd, nil, copySize, 0)
if err != nil {
if (err != unix.ENOSYS && err != unix.EXDEV) || !first {
return errors.Wrap(err, "copy file range failed")
return fmt.Errorf("copy file range failed: %w", err)
}
buf := bufferPool.Get().(*[]byte)
_, err = io.CopyBuffer(dst, src, *buf)
bufferPool.Put(buf)
return errors.Wrap(err, "userspace copy failed")
if err != nil {
return fmt.Errorf("userspace copy failed: %w", err)
}
return nil
}
first = false
@ -104,30 +107,33 @@ func copyFileContent(dst, src *os.File) error {
return nil
}
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error {
xattrKeys, err := sysx.LListxattr(src)
if err != nil {
e := errors.Wrapf(err, "failed to list xattrs on %s", src)
if xeh != nil {
e = xeh(dst, src, "", e)
e := fmt.Errorf("failed to list xattrs on %s: %w", src, err)
if errorHandler != nil {
e = errorHandler(dst, src, "", e)
}
return e
}
for _, xattr := range xattrKeys {
if _, exclude := excludes[xattr]; exclude {
continue
}
data, err := sysx.LGetxattr(src, xattr)
if err != nil {
e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
if xeh != nil {
if e = xeh(dst, src, xattr, e); e == nil {
e := fmt.Errorf("failed to get xattr %q on %s: %w", xattr, src, err)
if errorHandler != nil {
if e = errorHandler(dst, src, xattr, e); e == nil {
continue
}
}
return e
}
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
if xeh != nil {
if e = xeh(dst, src, xattr, e); e == nil {
e := fmt.Errorf("failed to set xattr %q on %s: %w", xattr, dst, err)
if errorHandler != nil {
if e = errorHandler(dst, src, xattr, e); e == nil {
continue
}
}
@ -137,11 +143,3 @@ func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
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))
}

View file

@ -1,4 +1,5 @@
// +build darwin freebsd openbsd solaris
//go:build darwin || freebsd || openbsd || netbsd || solaris
// +build darwin freebsd openbsd netbsd solaris
/*
Copyright The containerd Authors.
@ -19,16 +20,15 @@
package fs
import (
"fmt"
"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 {
func copyFileInfo(fi os.FileInfo, src, 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) {
@ -43,19 +43,18 @@ func copyFileInfo(fi os.FileInfo, name string) error {
}
}
if err != nil {
return errors.Wrapf(err, "failed to chown %s", name)
return fmt.Errorf("failed to chown %s: %w", name, err)
}
}
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)
return fmt.Errorf("failed to chmod %s: %w", name, err)
}
}
timespec := []syscall.Timespec{StatAtime(st), StatMtime(st)}
if err := syscall.UtimesNano(name, timespec); err != nil {
return errors.Wrapf(err, "failed to utime %s", name)
if err := utimesNano(name, StatAtime(st), StatMtime(st)); err != nil {
return fmt.Errorf("failed to utime %s: %w", name, err)
}
return nil
@ -69,30 +68,33 @@ func copyFileContent(dst, src *os.File) error {
return err
}
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error {
xattrKeys, err := sysx.LListxattr(src)
if err != nil {
e := errors.Wrapf(err, "failed to list xattrs on %s", src)
if xeh != nil {
e = xeh(dst, src, "", e)
e := fmt.Errorf("failed to list xattrs on %s: %w", src, err)
if errorHandler != nil {
e = errorHandler(dst, src, "", e)
}
return e
}
for _, xattr := range xattrKeys {
if _, exclude := excludes[xattr]; exclude {
continue
}
data, err := sysx.LGetxattr(src, xattr)
if err != nil {
e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src)
if xeh != nil {
if e = xeh(dst, src, xattr, e); e == nil {
e := fmt.Errorf("failed to get xattr %q on %s: %w", xattr, src, err)
if errorHandler != nil {
if e = errorHandler(dst, src, xattr, e); e == nil {
continue
}
}
return e
}
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst)
if xeh != nil {
if e = xeh(dst, src, xattr, e); e == nil {
e := fmt.Errorf("failed to set xattr %q on %s: %w", xattr, dst, err)
if errorHandler != nil {
if e = errorHandler(dst, src, xattr, e); e == nil {
continue
}
}
@ -102,11 +104,3 @@ func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
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))
}

View file

@ -17,19 +17,60 @@
package fs
import (
"errors"
"fmt"
"io"
"os"
"github.com/pkg/errors"
winio "github.com/Microsoft/go-winio"
"golang.org/x/sys/windows"
)
func copyFileInfo(fi os.FileInfo, name string) error {
const (
seTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
)
func copyFileInfo(fi os.FileInfo, src, name string) error {
if err := os.Chmod(name, fi.Mode()); err != nil {
return errors.Wrapf(err, "failed to chmod %s", name)
return fmt.Errorf("failed to chmod %s: %w", name, err)
}
// TODO: copy windows specific metadata
// Copy file ownership and ACL
// We need SeRestorePrivilege and SeTakeOwnershipPrivilege in order
// to restore security info on a file, especially if we're trying to
// apply security info which includes SIDs not necessarily present on
// the host.
privileges := []string{winio.SeRestorePrivilege, seTakeOwnershipPrivilege}
if err := winio.EnableProcessPrivileges(privileges); err != nil {
return err
}
defer winio.DisableProcessPrivileges(privileges)
secInfo, err := windows.GetNamedSecurityInfo(
src, windows.SE_FILE_OBJECT,
windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION)
if err != nil {
return err
}
dacl, _, err := secInfo.DACL()
if err != nil {
return err
}
sid, _, err := secInfo.Owner()
if err != nil {
return err
}
if err := windows.SetNamedSecurityInfo(
name, windows.SE_FILE_OBJECT,
windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION,
sid, nil, dacl, nil); err != nil {
return err
}
return nil
}
@ -40,10 +81,10 @@ func copyFileContent(dst, src *os.File) error {
return err
}
func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
func copyXAttrs(dst, src string, excludes map[string]struct{}, errorHandler XAttrErrorHandler) error {
return nil
}
func copyDevice(dst string, fi os.FileInfo) error {
return errors.New("device copy not supported")
func copyIrregular(dst string, fi os.FileInfo) error {
return errors.New("irregular copy not supported")
}

View file

@ -22,9 +22,8 @@ import (
"path/filepath"
"strings"
"golang.org/x/sync/errgroup"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
)
// ChangeKind is the type of modification that

View file

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*
@ -20,11 +21,11 @@ package fs
import (
"bytes"
"fmt"
"os"
"syscall"
"github.com/containerd/continuity/sysx"
"github.com/pkg/errors"
)
// detectDirDiff returns diff dir options if a directory could
@ -56,11 +57,11 @@ func compareSysStat(s1, s2 interface{}) (bool, error) {
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)
return false, fmt.Errorf("failed to get xattr for %s: %w", p1, err)
}
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 false, fmt.Errorf("failed to get xattr for %s: %w", p2, err)
}
return bytes.Equal(c1, c2), nil
}

View file

@ -1,3 +1,4 @@
//go:build linux
// +build linux
/*
@ -34,7 +35,7 @@ func locateDummyIfEmpty(path string) (string, error) {
if len(children) != 0 {
return "", nil
}
dummyFile, err := ioutil.TempFile(path, "fsutils-dummy")
dummyFile, err := os.CreateTemp(path, "fsutils-dummy")
if err != nil {
return "", err
}

View file

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*
@ -25,6 +26,14 @@ import (
"syscall"
)
// blocksUnitSize is the unit used by `st_blocks` in `stat` in bytes.
// See https://man7.org/linux/man-pages/man2/stat.2.html
// st_blocks
// This field indicates the number of blocks allocated to the
// file, in 512-byte units. (This may be smaller than
// st_size/512 when the file has holes.)
const blocksUnitSize = 512
type inode struct {
// TODO(stevvooe): Can probably reduce memory usage by not tracking
// device, but we can leave this right for now.
@ -33,10 +42,8 @@ type inode struct {
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
dev: uint64(stat.Dev), //nolint: unconvert // dev is uint32 on darwin/bsd, uint64 on linux/solaris/freebsd
ino: uint64(stat.Ino), //nolint: unconvert // ino is uint32 on bsd, uint64 on darwin/linux/solaris/freebsd
}
}
@ -59,10 +66,11 @@ func diskUsage(ctx context.Context, roots ...string) (Usage, error) {
default:
}
inoKey := newInode(fi.Sys().(*syscall.Stat_t))
stat := fi.Sys().(*syscall.Stat_t)
inoKey := newInode(stat)
if _, ok := inodes[inoKey]; !ok {
inodes[inoKey] = struct{}{}
size += fi.Size()
size += stat.Blocks * blocksUnitSize
}
return nil
@ -89,10 +97,11 @@ func diffUsage(ctx context.Context, a, b string) (Usage, error) {
}
if kind == ChangeKindAdd || kind == ChangeKindModify {
inoKey := newInode(fi.Sys().(*syscall.Stat_t))
stat := fi.Sys().(*syscall.Stat_t)
inoKey := newInode(stat)
if _, ok := inodes[inoKey]; !ok {
inodes[inoKey] = struct{}{}
size += fi.Size()
size += stat.Blocks * blocksUnitSize
}
return nil

View file

@ -1,3 +1,4 @@
//go:build windows
// +build windows
/*

View file

@ -1,3 +1,4 @@
//go:build !windows
// +build !windows
/*
@ -29,6 +30,5 @@ func getLinkInfo(fi os.FileInfo) (uint64, bool) {
return 0, false
}
// Ino is uint32 on bsd, uint64 on darwin/linux/solaris
return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 // nolint: unconvert
return uint64(s.Ino), !fi.IsDir() && s.Nlink > 1 //nolint: unconvert // ino is uint32 on bsd, uint64 on darwin/linux/solaris
}

View file

@ -19,11 +19,10 @@ package fs
import (
"bytes"
"context"
"errors"
"io"
"os"
"path/filepath"
"github.com/pkg/errors"
)
var (

View file

@ -1,4 +1,5 @@
// +build linux openbsd
//go:build linux || openbsd || solaris
// +build linux openbsd solaris
/*
Copyright The containerd Authors.
@ -40,6 +41,5 @@ func StatMtime(st *syscall.Stat_t) syscall.Timespec {
// StatATimeAsTime returns st.Atim as a time.Time
func StatATimeAsTime(st *syscall.Stat_t) time.Time {
// The int64 conversions ensure the line compiles for 32-bit systems as well.
return time.Unix(int64(st.Atim.Sec), int64(st.Atim.Nsec)) // nolint: unconvert
return time.Unix(st.Atim.Unix())
}

View file

@ -1,4 +1,5 @@
// +build darwin freebsd
//go:build darwin || freebsd || netbsd
// +build darwin freebsd netbsd
/*
Copyright The containerd Authors.
@ -40,5 +41,5 @@ func StatMtime(st *syscall.Stat_t) syscall.Timespec {
// 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
return time.Unix(st.Atimespec.Unix())
}

View file

@ -0,0 +1,33 @@
//go:build !(windows || linux)
// +build !windows,!linux
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fs
import (
"syscall"
"golang.org/x/sys/unix"
)
func utimesNano(name string, atime, mtime syscall.Timespec) error {
at := unix.NsecToTimespec(atime.Nano())
mt := unix.NsecToTimespec(mtime.Nano())
utimes := [2]unix.Timespec{at, mt}
return unix.UtimesNanoAt(unix.AT_FDCWD, name, utimes[0:], unix.AT_SYMLINK_NOFOLLOW)
}

View file

@ -1,23 +1,14 @@
module github.com/containerd/continuity
go 1.13
go 1.17
require (
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4
github.com/golang/protobuf v1.2.0
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/onsi/ginkgo v1.10.1 // indirect
github.com/onsi/gomega v1.7.0 // indirect
github.com/opencontainers/go-digest v1.0.0-rc1
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95 // indirect
github.com/stretchr/testify v1.4.0 // indirect
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3 // indirect
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect
github.com/Microsoft/go-winio v0.5.2
github.com/opencontainers/go-digest v1.0.0
github.com/sirupsen/logrus v1.8.1
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220405210540-1e041c57c461
google.golang.org/protobuf v1.26.0
)
require github.com/stretchr/testify v1.3.0 // indirect

View file

@ -1,4 +1,5 @@
// +build darwin freebsd openbsd
//go:build !(linux || solaris || windows)
// +build !linux,!solaris,!windows
/*
Copyright The containerd Authors.

View file

@ -1,3 +1,4 @@
//go:build linux || darwin
// +build linux darwin
/*

View file

@ -1,3 +1,4 @@
//go:build !linux && !darwin
// +build !linux,!darwin
/*