aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/containerd
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/containerd')
-rw-r--r--vendor/github.com/containerd/containerd/LICENSE191
-rw-r--r--vendor/github.com/containerd/containerd/NOTICE16
-rw-r--r--vendor/github.com/containerd/containerd/errdefs/errors.go93
-rw-r--r--vendor/github.com/containerd/containerd/errdefs/grpc.go147
-rw-r--r--vendor/github.com/containerd/continuity/fs/copy.go172
-rw-r--r--vendor/github.com/containerd/continuity/fs/copy_linux.go144
-rw-r--r--vendor/github.com/containerd/continuity/fs/copy_unix.go112
-rw-r--r--vendor/github.com/containerd/continuity/fs/copy_windows.go49
-rw-r--r--vendor/github.com/containerd/continuity/fs/diff.go326
-rw-r--r--vendor/github.com/containerd/continuity/fs/diff_unix.go74
-rw-r--r--vendor/github.com/containerd/continuity/fs/diff_windows.go48
-rw-r--r--vendor/github.com/containerd/continuity/fs/dtype_linux.go103
-rw-r--r--vendor/github.com/containerd/continuity/fs/du.go38
-rw-r--r--vendor/github.com/containerd/continuity/fs/du_unix.go110
-rw-r--r--vendor/github.com/containerd/continuity/fs/du_windows.go82
-rw-r--r--vendor/github.com/containerd/continuity/fs/hardlink.go43
-rw-r--r--vendor/github.com/containerd/continuity/fs/hardlink_unix.go34
-rw-r--r--vendor/github.com/containerd/continuity/fs/hardlink_windows.go23
-rw-r--r--vendor/github.com/containerd/continuity/fs/path.go313
-rw-r--r--vendor/github.com/containerd/continuity/fs/stat_bsd.go44
-rw-r--r--vendor/github.com/containerd/continuity/fs/stat_linux.go43
-rw-r--r--vendor/github.com/containerd/continuity/fs/time.go29
-rw-r--r--vendor/github.com/containerd/continuity/pathdriver/path_driver.go101
-rw-r--r--vendor/github.com/containerd/continuity/syscallx/syscall_unix.go26
-rw-r--r--vendor/github.com/containerd/continuity/syscallx/syscall_windows.go112
-rw-r--r--vendor/github.com/containerd/continuity/sysx/README.md3
-rw-r--r--vendor/github.com/containerd/continuity/sysx/file_posix.go128
-rw-r--r--vendor/github.com/containerd/continuity/sysx/generate.sh52
-rw-r--r--vendor/github.com/containerd/continuity/sysx/nodata_linux.go23
-rw-r--r--vendor/github.com/containerd/continuity/sysx/nodata_solaris.go24
-rw-r--r--vendor/github.com/containerd/continuity/sysx/nodata_unix.go25
-rw-r--r--vendor/github.com/containerd/continuity/sysx/xattr.go125
-rw-r--r--vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go67
33 files changed, 2819 insertions, 101 deletions
diff --git a/vendor/github.com/containerd/containerd/LICENSE b/vendor/github.com/containerd/containerd/LICENSE
new file mode 100644
index 000000000..584149b6e
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/LICENSE
@@ -0,0 +1,191 @@
+
+ Apache License
+ Version 2.0, January 2004
+ https://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ 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
+
+ https://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.
diff --git a/vendor/github.com/containerd/containerd/NOTICE b/vendor/github.com/containerd/containerd/NOTICE
new file mode 100644
index 000000000..8915f0277
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/NOTICE
@@ -0,0 +1,16 @@
+Docker
+Copyright 2012-2015 Docker, Inc.
+
+This product includes software developed at Docker, Inc. (https://www.docker.com).
+
+The following is courtesy of our legal counsel:
+
+
+Use and transfer of Docker may be subject to certain restrictions by the
+United States and other governments.
+It is your responsibility to ensure that your use and/or transfer does not
+violate applicable laws.
+
+For more information, please see https://www.bis.doc.gov
+
+See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.
diff --git a/vendor/github.com/containerd/containerd/errdefs/errors.go b/vendor/github.com/containerd/containerd/errdefs/errors.go
new file mode 100644
index 000000000..b5200afc0
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/errdefs/errors.go
@@ -0,0 +1,93 @@
+/*
+ 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 errdefs defines the common errors used throughout containerd
+// packages.
+//
+// Use with errors.Wrap and error.Wrapf to add context to an error.
+//
+// To detect an error class, use the IsXXX functions to tell whether an error
+// is of a certain type.
+//
+// The functions ToGRPC and FromGRPC can be used to map server-side and
+// client-side errors to the correct types.
+package errdefs
+
+import (
+ "context"
+
+ "github.com/pkg/errors"
+)
+
+// Definitions of common error types used throughout containerd. All containerd
+// errors returned by most packages will map into one of these errors classes.
+// Packages should return errors of these types when they want to instruct a
+// client to take a particular action.
+//
+// For the most part, we just try to provide local grpc errors. Most conditions
+// map very well to those defined by grpc.
+var (
+ ErrUnknown = errors.New("unknown") // used internally to represent a missed mapping.
+ ErrInvalidArgument = errors.New("invalid argument")
+ ErrNotFound = errors.New("not found")
+ ErrAlreadyExists = errors.New("already exists")
+ ErrFailedPrecondition = errors.New("failed precondition")
+ ErrUnavailable = errors.New("unavailable")
+ ErrNotImplemented = errors.New("not implemented") // represents not supported and unimplemented
+)
+
+// IsInvalidArgument returns true if the error is due to an invalid argument
+func IsInvalidArgument(err error) bool {
+ return errors.Cause(err) == ErrInvalidArgument
+}
+
+// IsNotFound returns true if the error is due to a missing object
+func IsNotFound(err error) bool {
+ return errors.Cause(err) == ErrNotFound
+}
+
+// IsAlreadyExists returns true if the error is due to an already existing
+// metadata item
+func IsAlreadyExists(err error) bool {
+ return errors.Cause(err) == ErrAlreadyExists
+}
+
+// IsFailedPrecondition returns true if an operation could not proceed to the
+// lack of a particular condition
+func IsFailedPrecondition(err error) bool {
+ return errors.Cause(err) == ErrFailedPrecondition
+}
+
+// IsUnavailable returns true if the error is due to a resource being unavailable
+func IsUnavailable(err error) bool {
+ return errors.Cause(err) == ErrUnavailable
+}
+
+// IsNotImplemented returns true if the error is due to not being implemented
+func IsNotImplemented(err error) bool {
+ return errors.Cause(err) == ErrNotImplemented
+}
+
+// IsCanceled returns true if the error is due to `context.Canceled`.
+func IsCanceled(err error) bool {
+ return errors.Cause(err) == context.Canceled
+}
+
+// IsDeadlineExceeded returns true if the error is due to
+// `context.DeadlineExceeded`.
+func IsDeadlineExceeded(err error) bool {
+ return errors.Cause(err) == context.DeadlineExceeded
+}
diff --git a/vendor/github.com/containerd/containerd/errdefs/grpc.go b/vendor/github.com/containerd/containerd/errdefs/grpc.go
new file mode 100644
index 000000000..209f63bd0
--- /dev/null
+++ b/vendor/github.com/containerd/containerd/errdefs/grpc.go
@@ -0,0 +1,147 @@
+/*
+ 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 errdefs
+
+import (
+ "context"
+ "strings"
+
+ "github.com/pkg/errors"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+// ToGRPC will attempt to map the backend containerd error into a grpc error,
+// using the original error message as a description.
+//
+// Further information may be extracted from certain errors depending on their
+// type.
+//
+// If the error is unmapped, the original error will be returned to be handled
+// by the regular grpc error handling stack.
+func ToGRPC(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ if isGRPCError(err) {
+ // error has already been mapped to grpc
+ return err
+ }
+
+ switch {
+ case IsInvalidArgument(err):
+ return status.Errorf(codes.InvalidArgument, err.Error())
+ case IsNotFound(err):
+ return status.Errorf(codes.NotFound, err.Error())
+ case IsAlreadyExists(err):
+ return status.Errorf(codes.AlreadyExists, err.Error())
+ case IsFailedPrecondition(err):
+ return status.Errorf(codes.FailedPrecondition, err.Error())
+ case IsUnavailable(err):
+ return status.Errorf(codes.Unavailable, err.Error())
+ case IsNotImplemented(err):
+ return status.Errorf(codes.Unimplemented, err.Error())
+ case IsCanceled(err):
+ return status.Errorf(codes.Canceled, err.Error())
+ case IsDeadlineExceeded(err):
+ return status.Errorf(codes.DeadlineExceeded, err.Error())
+ }
+
+ return err
+}
+
+// ToGRPCf maps the error to grpc error codes, assembling the formatting string
+// and combining it with the target error string.
+//
+// This is equivalent to errors.ToGRPC(errors.Wrapf(err, format, args...))
+func ToGRPCf(err error, format string, args ...interface{}) error {
+ return ToGRPC(errors.Wrapf(err, format, args...))
+}
+
+// FromGRPC returns the underlying error from a grpc service based on the grpc error code
+func FromGRPC(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ var cls error // divide these into error classes, becomes the cause
+
+ switch code(err) {
+ case codes.InvalidArgument:
+ cls = ErrInvalidArgument
+ case codes.AlreadyExists:
+ cls = ErrAlreadyExists
+ case codes.NotFound:
+ cls = ErrNotFound
+ case codes.Unavailable:
+ cls = ErrUnavailable
+ case codes.FailedPrecondition:
+ cls = ErrFailedPrecondition
+ case codes.Unimplemented:
+ cls = ErrNotImplemented
+ case codes.Canceled:
+ cls = context.Canceled
+ case codes.DeadlineExceeded:
+ cls = context.DeadlineExceeded
+ default:
+ cls = ErrUnknown
+ }
+
+ msg := rebaseMessage(cls, err)
+ if msg != "" {
+ err = errors.Wrap(cls, msg)
+ } else {
+ err = errors.WithStack(cls)
+ }
+
+ return err
+}
+
+// rebaseMessage removes the repeats for an error at the end of an error
+// string. This will happen when taking an error over grpc then remapping it.
+//
+// Effectively, we just remove the string of cls from the end of err if it
+// appears there.
+func rebaseMessage(cls error, err error) string {
+ desc := errDesc(err)
+ clss := cls.Error()
+ if desc == clss {
+ return ""
+ }
+
+ return strings.TrimSuffix(desc, ": "+clss)
+}
+
+func isGRPCError(err error) bool {
+ _, ok := status.FromError(err)
+ return ok
+}
+
+func code(err error) codes.Code {
+ if s, ok := status.FromError(err); ok {
+ return s.Code()
+ }
+ return codes.Unknown
+}
+
+func errDesc(err error) string {
+ if s, ok := status.FromError(err); ok {
+ return s.Message()
+ }
+ return err.Error()
+}
diff --git a/vendor/github.com/containerd/continuity/fs/copy.go b/vendor/github.com/containerd/continuity/fs/copy.go
new file mode 100644
index 000000000..ad61022ad
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/copy.go
@@ -0,0 +1,172 @@
+/*
+ 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 (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "sync"
+
+ "github.com/pkg/errors"
+)
+
+var bufferPool = &sync.Pool{
+ New: func() interface{} {
+ buffer := make([]byte, 32*1024)
+ return &buffer
+ },
+}
+
+// XAttrErrorHandlers 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
+}
+
+type CopyDirOpt func(*copyDirOpts) error
+
+// WithXAttrErrorHandler allows specifying XAttrErrorHandler
+// If nil XAttrErrorHandler is specified (default), CopyDir stops
+// on a non-nil xattr error.
+func WithXAttrErrorHandler(xeh XAttrErrorHandler) CopyDirOpt {
+ return func(o *copyDirOpts) error {
+ o.xeh = xeh
+ return nil
+ }
+}
+
+// WithAllowXAttrErrors allows ignoring xattr errors.
+func WithAllowXAttrErrors() CopyDirOpt {
+ xeh := func(dst, src, xattrKey string, err error) error {
+ return nil
+ }
+ return WithXAttrErrorHandler(xeh)
+}
+
+// CopyDir copies the directory from src to dst.
+// Most efficient copy of files is attempted.
+func CopyDir(dst, src string, opts ...CopyDirOpt) error {
+ var o copyDirOpts
+ for _, opt := range opts {
+ if err := opt(&o); err != nil {
+ return err
+ }
+ }
+ inodes := map[uint64]string{}
+ return copyDirectory(dst, src, inodes, &o)
+}
+
+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)
+ }
+ 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, o); 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(target, source); 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, o.xeh); err != nil {
+ return errors.Wrap(err, "failed to copy xattrs")
+ }
+ }
+
+ return nil
+}
+
+// CopyFile copies the source file to the target.
+// The most efficient means of copying is used for the platform.
+func CopyFile(target, source 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)
+}
diff --git a/vendor/github.com/containerd/continuity/fs/copy_linux.go b/vendor/github.com/containerd/continuity/fs/copy_linux.go
new file mode 100644
index 000000000..81c71522a
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/copy_linux.go
@@ -0,0 +1,144 @@
+/*
+ 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 (
+ "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
+}
+
+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")
+ }
+
+ size := st.Size()
+ first := true
+ srcFd := int(src.Fd())
+ dstFd := int(dst.Fd())
+
+ for size > 0 {
+ // Ensure that we are never trying to copy more than SSIZE_MAX at a
+ // time and at the same time avoids overflows when the file is larger
+ // than 4GB on 32-bit systems.
+ var copySize int
+ if size > maxSSizeT {
+ copySize = int(maxSSizeT)
+ } else {
+ copySize = int(size)
+ }
+ 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")
+ }
+
+ buf := bufferPool.Get().(*[]byte)
+ _, err = io.CopyBuffer(dst, src, *buf)
+ bufferPool.Put(buf)
+ return errors.Wrap(err, "userspace copy failed")
+ }
+
+ first = false
+ size -= int64(n)
+ }
+
+ return nil
+}
+
+func copyXAttrs(dst, src string, xeh 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)
+ }
+ return e
+ }
+ for _, xattr := range xattrKeys {
+ 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 {
+ 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 {
+ continue
+ }
+ }
+ return e
+ }
+ }
+
+ 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))
+}
diff --git a/vendor/github.com/containerd/continuity/fs/copy_unix.go b/vendor/github.com/containerd/continuity/fs/copy_unix.go
new file mode 100644
index 000000000..73c01a46d
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/copy_unix.go
@@ -0,0 +1,112 @@
+// +build solaris darwin 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 (
+ "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, xeh 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)
+ }
+ return e
+ }
+ for _, xattr := range xattrKeys {
+ 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 {
+ 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 {
+ continue
+ }
+ }
+ return e
+ }
+ }
+
+ 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))
+}
diff --git a/vendor/github.com/containerd/continuity/fs/copy_windows.go b/vendor/github.com/containerd/continuity/fs/copy_windows.go
new file mode 100644
index 000000000..27c7d7dbb
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/copy_windows.go
@@ -0,0 +1,49 @@
+/*
+ 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 (
+ "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, xeh XAttrErrorHandler) error {
+ return nil
+}
+
+func copyDevice(dst string, fi os.FileInfo) error {
+ return errors.New("device copy not supported")
+}
diff --git a/vendor/github.com/containerd/continuity/fs/diff.go b/vendor/github.com/containerd/continuity/fs/diff.go
new file mode 100644
index 000000000..e64f9e73d
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/diff.go
@@ -0,0 +1,326 @@
+/*
+ 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 (
+ "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 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()
+}
diff --git a/vendor/github.com/containerd/continuity/fs/diff_unix.go b/vendor/github.com/containerd/continuity/fs/diff_unix.go
new file mode 100644
index 000000000..7913af27d
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/diff_unix.go
@@ -0,0 +1,74 @@
+// +build !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 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
+}
diff --git a/vendor/github.com/containerd/continuity/fs/diff_windows.go b/vendor/github.com/containerd/continuity/fs/diff_windows.go
new file mode 100644
index 000000000..4bfa72d3a
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/diff_windows.go
@@ -0,0 +1,48 @@
+/*
+ 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 (
+ "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
+}
diff --git a/vendor/github.com/containerd/continuity/fs/dtype_linux.go b/vendor/github.com/containerd/continuity/fs/dtype_linux.go
new file mode 100644
index 000000000..10510d8de
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/dtype_linux.go
@@ -0,0 +1,103 @@
+// +build 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 (
+ "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
+}
diff --git a/vendor/github.com/containerd/continuity/fs/du.go b/vendor/github.com/containerd/continuity/fs/du.go
new file mode 100644
index 000000000..fccc985dc
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/du.go
@@ -0,0 +1,38 @@
+/*
+ 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 "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(ctx context.Context, roots ...string) (Usage, error) {
+ return diskUsage(ctx, 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)
+}
diff --git a/vendor/github.com/containerd/continuity/fs/du_unix.go b/vendor/github.com/containerd/continuity/fs/du_unix.go
new file mode 100644
index 000000000..e22ffbea3
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/du_unix.go
@@ -0,0 +1,110 @@
+// +build !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 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(ctx context.Context, 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
+ }
+
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ }
+
+ 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
+}
diff --git a/vendor/github.com/containerd/continuity/fs/du_windows.go b/vendor/github.com/containerd/continuity/fs/du_windows.go
new file mode 100644
index 000000000..8f25ec59c
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/du_windows.go
@@ -0,0 +1,82 @@
+// +build 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 fs
+
+import (
+ "context"
+ "os"
+ "path/filepath"
+)
+
+func diskUsage(ctx context.Context, 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
+ }
+
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ default:
+ }
+
+ 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
+}
diff --git a/vendor/github.com/containerd/continuity/fs/hardlink.go b/vendor/github.com/containerd/continuity/fs/hardlink.go
new file mode 100644
index 000000000..762aa45e6
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/hardlink.go
@@ -0,0 +1,43 @@
+/*
+ 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 "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
+}
diff --git a/vendor/github.com/containerd/continuity/fs/hardlink_unix.go b/vendor/github.com/containerd/continuity/fs/hardlink_unix.go
new file mode 100644
index 000000000..f95f0904c
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/hardlink_unix.go
@@ -0,0 +1,34 @@
+// +build !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 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
+}
diff --git a/vendor/github.com/containerd/continuity/fs/hardlink_windows.go b/vendor/github.com/containerd/continuity/fs/hardlink_windows.go
new file mode 100644
index 000000000..748554714
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/hardlink_windows.go
@@ -0,0 +1,23 @@
+/*
+ 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 "os"
+
+func getLinkInfo(fi os.FileInfo) (uint64, bool) {
+ return 0, false
+}
diff --git a/vendor/github.com/containerd/continuity/fs/path.go b/vendor/github.com/containerd/continuity/fs/path.go
new file mode 100644
index 000000000..8863caa9d
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/path.go
@@ -0,0 +1,313 @@
+/*
+ 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 (
+ "bytes"
+ "context"
+ "io"
+ "os"
+ "path/filepath"
+
+ "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
+ }
+
+ switch i := directoryCompare(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 directoryCompare(a, b string) int {
+ l := len(a)
+ if len(b) < l {
+ l = len(b)
+ }
+ for i := 0; i < l; i++ {
+ c1, c2 := a[i], b[i]
+ if c1 == filepath.Separator {
+ c1 = byte(0)
+ }
+ if c2 == filepath.Separator {
+ c2 = byte(0)
+ }
+ if c1 < c2 {
+ return -1
+ }
+ if c1 > c2 {
+ return +1
+ }
+ }
+ if len(a) < len(b) {
+ return -1
+ }
+ if len(a) > len(b) {
+ return +1
+ }
+ return 0
+}
+
+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 := &currentPath{
+ 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
+ }
+ *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
+ }
+}
diff --git a/vendor/github.com/containerd/continuity/fs/stat_bsd.go b/vendor/github.com/containerd/continuity/fs/stat_bsd.go
new file mode 100644
index 000000000..cb7400a33
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/stat_bsd.go
@@ -0,0 +1,44 @@
+// +build darwin 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 (
+ "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
+}
diff --git a/vendor/github.com/containerd/continuity/fs/stat_linux.go b/vendor/github.com/containerd/continuity/fs/stat_linux.go
new file mode 100644
index 000000000..4a678dd1f
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/stat_linux.go
@@ -0,0 +1,43 @@
+/*
+ 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"
+ "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 {
+ // 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
+}
diff --git a/vendor/github.com/containerd/continuity/fs/time.go b/vendor/github.com/containerd/continuity/fs/time.go
new file mode 100644
index 000000000..cde456123
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/fs/time.go
@@ -0,0 +1,29 @@
+/*
+ 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 "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))
+}
diff --git a/vendor/github.com/containerd/continuity/pathdriver/path_driver.go b/vendor/github.com/containerd/continuity/pathdriver/path_driver.go
deleted file mode 100644
index b0d5a6b56..000000000
--- a/vendor/github.com/containerd/continuity/pathdriver/path_driver.go
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- 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 pathdriver
-
-import (
- "path/filepath"
-)
-
-// PathDriver provides all of the path manipulation functions in a common
-// interface. The context should call these and never use the `filepath`
-// package or any other package to manipulate paths.
-type PathDriver interface {
- Join(paths ...string) string
- IsAbs(path string) bool
- Rel(base, target string) (string, error)
- Base(path string) string
- Dir(path string) string
- Clean(path string) string
- Split(path string) (dir, file string)
- Separator() byte
- Abs(path string) (string, error)
- Walk(string, filepath.WalkFunc) error
- FromSlash(path string) string
- ToSlash(path string) string
- Match(pattern, name string) (matched bool, err error)
-}
-
-// pathDriver is a simple default implementation calls the filepath package.
-type pathDriver struct{}
-
-// LocalPathDriver is the exported pathDriver struct for convenience.
-var LocalPathDriver PathDriver = &pathDriver{}
-
-func (*pathDriver) Join(paths ...string) string {
- return filepath.Join(paths...)
-}
-
-func (*pathDriver) IsAbs(path string) bool {
- return filepath.IsAbs(path)
-}
-
-func (*pathDriver) Rel(base, target string) (string, error) {
- return filepath.Rel(base, target)
-}
-
-func (*pathDriver) Base(path string) string {
- return filepath.Base(path)
-}
-
-func (*pathDriver) Dir(path string) string {
- return filepath.Dir(path)
-}
-
-func (*pathDriver) Clean(path string) string {
- return filepath.Clean(path)
-}
-
-func (*pathDriver) Split(path string) (dir, file string) {
- return filepath.Split(path)
-}
-
-func (*pathDriver) Separator() byte {
- return filepath.Separator
-}
-
-func (*pathDriver) Abs(path string) (string, error) {
- return filepath.Abs(path)
-}
-
-// Note that filepath.Walk calls os.Stat, so if the context wants to
-// to call Driver.Stat() for Walk, they need to create a new struct that
-// overrides this method.
-func (*pathDriver) Walk(root string, walkFn filepath.WalkFunc) error {
- return filepath.Walk(root, walkFn)
-}
-
-func (*pathDriver) FromSlash(path string) string {
- return filepath.FromSlash(path)
-}
-
-func (*pathDriver) ToSlash(path string) string {
- return filepath.ToSlash(path)
-}
-
-func (*pathDriver) Match(pattern, name string) (bool, error) {
- return filepath.Match(pattern, name)
-}
diff --git a/vendor/github.com/containerd/continuity/syscallx/syscall_unix.go b/vendor/github.com/containerd/continuity/syscallx/syscall_unix.go
new file mode 100644
index 000000000..0bfa6a040
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/syscallx/syscall_unix.go
@@ -0,0 +1,26 @@
+// +build !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 syscallx
+
+import "syscall"
+
+// Readlink returns the destination of the named symbolic link.
+func Readlink(path string, buf []byte) (n int, err error) {
+ return syscall.Readlink(path, buf)
+}
diff --git a/vendor/github.com/containerd/continuity/syscallx/syscall_windows.go b/vendor/github.com/containerd/continuity/syscallx/syscall_windows.go
new file mode 100644
index 000000000..2ba814990
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/syscallx/syscall_windows.go
@@ -0,0 +1,112 @@
+/*
+ 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 syscallx
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+type reparseDataBuffer struct {
+ ReparseTag uint32
+ ReparseDataLength uint16
+ Reserved uint16
+
+ // GenericReparseBuffer
+ reparseBuffer byte
+}
+
+type mountPointReparseBuffer struct {
+ SubstituteNameOffset uint16
+ SubstituteNameLength uint16
+ PrintNameOffset uint16
+ PrintNameLength uint16
+ PathBuffer [1]uint16
+}
+
+type symbolicLinkReparseBuffer struct {
+ SubstituteNameOffset uint16
+ SubstituteNameLength uint16
+ PrintNameOffset uint16
+ PrintNameLength uint16
+ Flags uint32
+ PathBuffer [1]uint16
+}
+
+const (
+ _IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
+ _SYMLINK_FLAG_RELATIVE = 1
+)
+
+// Readlink returns the destination of the named symbolic link.
+func Readlink(path string, buf []byte) (n int, err error) {
+ fd, err := syscall.CreateFile(syscall.StringToUTF16Ptr(path), syscall.GENERIC_READ, 0, nil, syscall.OPEN_EXISTING,
+ syscall.FILE_FLAG_OPEN_REPARSE_POINT|syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
+ if err != nil {
+ return -1, err
+ }
+ defer syscall.CloseHandle(fd)
+
+ rdbbuf := make([]byte, syscall.MAXIMUM_REPARSE_DATA_BUFFER_SIZE)
+ var bytesReturned uint32
+ err = syscall.DeviceIoControl(fd, syscall.FSCTL_GET_REPARSE_POINT, nil, 0, &rdbbuf[0], uint32(len(rdbbuf)), &bytesReturned, nil)
+ if err != nil {
+ return -1, err
+ }
+
+ rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0]))
+ var s string
+ switch rdb.ReparseTag {
+ case syscall.IO_REPARSE_TAG_SYMLINK:
+ data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
+ p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
+ s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
+ if data.Flags&_SYMLINK_FLAG_RELATIVE == 0 {
+ if len(s) >= 4 && s[:4] == `\??\` {
+ s = s[4:]
+ switch {
+ case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar
+ // do nothing
+ case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar
+ s = `\\` + s[4:]
+ default:
+ // unexpected; do nothing
+ }
+ } else {
+ // unexpected; do nothing
+ }
+ }
+ case _IO_REPARSE_TAG_MOUNT_POINT:
+ data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
+ p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
+ s = syscall.UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
+ if len(s) >= 4 && s[:4] == `\??\` { // \??\C:\foo\bar
+ if len(s) < 48 || s[:11] != `\??\Volume{` {
+ s = s[4:]
+ }
+ } else {
+ // unexpected; do nothing
+ }
+ default:
+ // the path is not a symlink or junction but another type of reparse
+ // point
+ return -1, syscall.ENOENT
+ }
+ n = copy(buf, []byte(s))
+
+ return n, nil
+}
diff --git a/vendor/github.com/containerd/continuity/sysx/README.md b/vendor/github.com/containerd/continuity/sysx/README.md
new file mode 100644
index 000000000..ad7aee533
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/sysx/README.md
@@ -0,0 +1,3 @@
+This package is for internal use only. It is intended to only have
+temporary changes before they are upstreamed to golang.org/x/sys/
+(a.k.a. https://github.com/golang/sys).
diff --git a/vendor/github.com/containerd/continuity/sysx/file_posix.go b/vendor/github.com/containerd/continuity/sysx/file_posix.go
new file mode 100644
index 000000000..e28f3a1b5
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/sysx/file_posix.go
@@ -0,0 +1,128 @@
+/*
+ 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 sysx
+
+import (
+ "os"
+ "path/filepath"
+
+ "github.com/containerd/continuity/syscallx"
+)
+
+// Readlink returns the destination of the named symbolic link.
+// If there is an error, it will be of type *PathError.
+func Readlink(name string) (string, error) {
+ for len := 128; ; len *= 2 {
+ b := make([]byte, len)
+ n, e := fixCount(syscallx.Readlink(fixLongPath(name), b))
+ if e != nil {
+ return "", &os.PathError{Op: "readlink", Path: name, Err: e}
+ }
+ if n < len {
+ return string(b[0:n]), nil
+ }
+ }
+}
+
+// Many functions in package syscall return a count of -1 instead of 0.
+// Using fixCount(call()) instead of call() corrects the count.
+func fixCount(n int, err error) (int, error) {
+ if n < 0 {
+ n = 0
+ }
+ return n, err
+}
+
+// fixLongPath returns the extended-length (\\?\-prefixed) form of
+// path when needed, in order to avoid the default 260 character file
+// path limit imposed by Windows. If path is not easily converted to
+// the extended-length form (for example, if path is a relative path
+// or contains .. elements), or is short enough, fixLongPath returns
+// path unmodified.
+//
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
+func fixLongPath(path string) string {
+ // Do nothing (and don't allocate) if the path is "short".
+ // Empirically (at least on the Windows Server 2013 builder),
+ // the kernel is arbitrarily okay with < 248 bytes. That
+ // matches what the docs above say:
+ // "When using an API to create a directory, the specified
+ // path cannot be so long that you cannot append an 8.3 file
+ // name (that is, the directory name cannot exceed MAX_PATH
+ // minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
+ //
+ // The MSDN docs appear to say that a normal path that is 248 bytes long
+ // will work; empirically the path must be less then 248 bytes long.
+ if len(path) < 248 {
+ // Don't fix. (This is how Go 1.7 and earlier worked,
+ // not automatically generating the \\?\ form)
+ return path
+ }
+
+ // The extended form begins with \\?\, as in
+ // \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
+ // The extended form disables evaluation of . and .. path
+ // elements and disables the interpretation of / as equivalent
+ // to \. The conversion here rewrites / to \ and elides
+ // . elements as well as trailing or duplicate separators. For
+ // simplicity it avoids the conversion entirely for relative
+ // paths or paths containing .. elements. For now,
+ // \\server\share paths are not converted to
+ // \\?\UNC\server\share paths because the rules for doing so
+ // are less well-specified.
+ if len(path) >= 2 && path[:2] == `\\` {
+ // Don't canonicalize UNC paths.
+ return path
+ }
+ if !filepath.IsAbs(path) {
+ // Relative path
+ return path
+ }
+
+ const prefix = `\\?`
+
+ pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
+ copy(pathbuf, prefix)
+ n := len(path)
+ r, w := 0, len(prefix)
+ for r < n {
+ switch {
+ case os.IsPathSeparator(path[r]):
+ // empty block
+ r++
+ case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
+ // /./
+ r++
+ case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
+ // /../ is currently unhandled
+ return path
+ default:
+ pathbuf[w] = '\\'
+ w++
+ for ; r < n && !os.IsPathSeparator(path[r]); r++ {
+ pathbuf[w] = path[r]
+ w++
+ }
+ }
+ }
+ // A drive's root directory needs a trailing \
+ if w == len(`\\?\c:`) {
+ pathbuf[w] = '\\'
+ w++
+ }
+ return string(pathbuf[:w])
+}
diff --git a/vendor/github.com/containerd/continuity/sysx/generate.sh b/vendor/github.com/containerd/continuity/sysx/generate.sh
new file mode 100644
index 000000000..87d708d7a
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/sysx/generate.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+# 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.
+
+
+set -e
+
+mksyscall="$(go env GOROOT)/src/syscall/mksyscall.pl"
+
+fix() {
+ sed 's,^package syscall$,package sysx,' \
+ | sed 's,^import "unsafe"$,import (\n\t"syscall"\n\t"unsafe"\n),' \
+ | gofmt -r='BytePtrFromString -> syscall.BytePtrFromString' \
+ | gofmt -r='Syscall6 -> syscall.Syscall6' \
+ | gofmt -r='Syscall -> syscall.Syscall' \
+ | gofmt -r='SYS_GETXATTR -> syscall.SYS_GETXATTR' \
+ | gofmt -r='SYS_LISTXATTR -> syscall.SYS_LISTXATTR' \
+ | gofmt -r='SYS_SETXATTR -> syscall.SYS_SETXATTR' \
+ | gofmt -r='SYS_REMOVEXATTR -> syscall.SYS_REMOVEXATTR' \
+ | gofmt -r='SYS_LGETXATTR -> syscall.SYS_LGETXATTR' \
+ | gofmt -r='SYS_LLISTXATTR -> syscall.SYS_LLISTXATTR' \
+ | gofmt -r='SYS_LSETXATTR -> syscall.SYS_LSETXATTR' \
+ | gofmt -r='SYS_LREMOVEXATTR -> syscall.SYS_LREMOVEXATTR'
+}
+
+if [ "$GOARCH" == "" ] || [ "$GOOS" == "" ]; then
+ echo "Must specify \$GOARCH and \$GOOS"
+ exit 1
+fi
+
+mkargs=""
+
+if [ "$GOARCH" == "386" ] || [ "$GOARCH" == "arm" ]; then
+ mkargs="-l32"
+fi
+
+for f in "$@"; do
+ $mksyscall $mkargs "${f}_${GOOS}.go" | fix > "${f}_${GOOS}_${GOARCH}.go"
+done
+
diff --git a/vendor/github.com/containerd/continuity/sysx/nodata_linux.go b/vendor/github.com/containerd/continuity/sysx/nodata_linux.go
new file mode 100644
index 000000000..28ce5d8de
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/sysx/nodata_linux.go
@@ -0,0 +1,23 @@
+/*
+ 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 sysx
+
+import (
+ "syscall"
+)
+
+const ENODATA = syscall.ENODATA
diff --git a/vendor/github.com/containerd/continuity/sysx/nodata_solaris.go b/vendor/github.com/containerd/continuity/sysx/nodata_solaris.go
new file mode 100644
index 000000000..e0575f446
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/sysx/nodata_solaris.go
@@ -0,0 +1,24 @@
+/*
+ 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 sysx
+
+import (
+ "syscall"
+)
+
+// This should actually be a set that contains ENOENT and EPERM
+const ENODATA = syscall.ENOENT
diff --git a/vendor/github.com/containerd/continuity/sysx/nodata_unix.go b/vendor/github.com/containerd/continuity/sysx/nodata_unix.go
new file mode 100644
index 000000000..b26f5b3d0
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/sysx/nodata_unix.go
@@ -0,0 +1,25 @@
+// +build darwin 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 sysx
+
+import (
+ "syscall"
+)
+
+const ENODATA = syscall.ENOATTR
diff --git a/vendor/github.com/containerd/continuity/sysx/xattr.go b/vendor/github.com/containerd/continuity/sysx/xattr.go
new file mode 100644
index 000000000..9e4326dcf
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/sysx/xattr.go
@@ -0,0 +1,125 @@
+// +build linux darwin
+
+/*
+ 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 sysx
+
+import (
+ "bytes"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+// Listxattr calls syscall listxattr and reads all content
+// and returns a string array
+func Listxattr(path string) ([]string, error) {
+ return listxattrAll(path, unix.Listxattr)
+}
+
+// Removexattr calls syscall removexattr
+func Removexattr(path string, attr string) (err error) {
+ return unix.Removexattr(path, attr)
+}
+
+// Setxattr calls syscall setxattr
+func Setxattr(path string, attr string, data []byte, flags int) (err error) {
+ return unix.Setxattr(path, attr, data, flags)
+}
+
+// Getxattr calls syscall getxattr
+func Getxattr(path, attr string) ([]byte, error) {
+ return getxattrAll(path, attr, unix.Getxattr)
+}
+
+// LListxattr lists xattrs, not following symlinks
+func LListxattr(path string) ([]string, error) {
+ return listxattrAll(path, unix.Llistxattr)
+}
+
+// LRemovexattr removes an xattr, not following symlinks
+func LRemovexattr(path string, attr string) (err error) {
+ return unix.Lremovexattr(path, attr)
+}
+
+// LSetxattr sets an xattr, not following symlinks
+func LSetxattr(path string, attr string, data []byte, flags int) (err error) {
+ return unix.Lsetxattr(path, attr, data, flags)
+}
+
+// LGetxattr gets an xattr, not following symlinks
+func LGetxattr(path, attr string) ([]byte, error) {
+ return getxattrAll(path, attr, unix.Lgetxattr)
+}
+
+const defaultXattrBufferSize = 5
+
+type listxattrFunc func(path string, dest []byte) (int, error)
+
+func listxattrAll(path string, listFunc listxattrFunc) ([]string, error) {
+ var p []byte // nil on first execution
+
+ for {
+ n, err := listFunc(path, p) // first call gets buffer size.
+ if err != nil {
+ return nil, err
+ }
+
+ if n > len(p) {
+ p = make([]byte, n)
+ continue
+ }
+
+ p = p[:n]
+
+ ps := bytes.Split(bytes.TrimSuffix(p, []byte{0}), []byte{0})
+ var entries []string
+ for _, p := range ps {
+ s := string(p)
+ if s != "" {
+ entries = append(entries, s)
+ }
+ }
+
+ return entries, nil
+ }
+}
+
+type getxattrFunc func(string, string, []byte) (int, error)
+
+func getxattrAll(path, attr string, getFunc getxattrFunc) ([]byte, error) {
+ p := make([]byte, defaultXattrBufferSize)
+ for {
+ n, err := getFunc(path, attr, p)
+ if err != nil {
+ if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERANGE {
+ p = make([]byte, len(p)*2) // this can't be ideal.
+ continue // try again!
+ }
+
+ return nil, err
+ }
+
+ // realloc to correct size and repeat
+ if n > len(p) {
+ p = make([]byte, n)
+ continue
+ }
+
+ return p[:n], nil
+ }
+}
diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go b/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go
new file mode 100644
index 000000000..c9ef3a1d2
--- /dev/null
+++ b/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go
@@ -0,0 +1,67 @@
+// +build !linux,!darwin
+
+/*
+ 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 sysx
+
+import (
+ "errors"
+ "runtime"
+)
+
+var unsupported = errors.New("extended attributes unsupported on " + runtime.GOOS)
+
+// Listxattr calls syscall listxattr and reads all content
+// and returns a string array
+func Listxattr(path string) ([]string, error) {
+ return []string{}, nil
+}
+
+// Removexattr calls syscall removexattr
+func Removexattr(path string, attr string) (err error) {
+ return unsupported
+}
+
+// Setxattr calls syscall setxattr
+func Setxattr(path string, attr string, data []byte, flags int) (err error) {
+ return unsupported
+}
+
+// Getxattr calls syscall getxattr
+func Getxattr(path, attr string) ([]byte, error) {
+ return []byte{}, unsupported
+}
+
+// LListxattr lists xattrs, not following symlinks
+func LListxattr(path string) ([]string, error) {
+ return []string{}, nil
+}
+
+// LRemovexattr removes an xattr, not following symlinks
+func LRemovexattr(path string, attr string) (err error) {
+ return unsupported
+}
+
+// LSetxattr sets an xattr, not following symlinks
+func LSetxattr(path string, attr string, data []byte, flags int) (err error) {
+ return unsupported
+}
+
+// LGetxattr gets an xattr, not following symlinks
+func LGetxattr(path, attr string) ([]byte, error) {
+ return []byte{}, nil
+}