Signed-off-by: Dimitris Mandalidis <dimitris.mandalidis@gmail.com>
| ... | ... |
@@ -6,6 +6,7 @@ import ( |
| 6 | 6 |
"io/ioutil" |
| 7 | 7 |
"os" |
| 8 | 8 |
"path/filepath" |
| 9 |
+ "syscall" |
|
| 9 | 10 |
|
| 10 | 11 |
"github.com/containerd/continuity/fs" |
| 11 | 12 |
"github.com/docker/docker/api/types" |
| ... | ... |
@@ -388,6 +389,18 @@ func (container *Container) DetachAndUnmount(volumeEventLog func(name, action st |
| 388 | 388 |
return container.UnmountVolumes(volumeEventLog) |
| 389 | 389 |
} |
| 390 | 390 |
|
| 391 |
+// ignoreUnsupportedXAttrs ignores errors when extended attributes |
|
| 392 |
+// are not supported |
|
| 393 |
+func ignoreUnsupportedXAttrs() fs.CopyDirOpt {
|
|
| 394 |
+ xeh := func(dst, src, xattrKey string, err error) error {
|
|
| 395 |
+ if errors.Cause(err) != syscall.ENOTSUP {
|
|
| 396 |
+ return err |
|
| 397 |
+ } |
|
| 398 |
+ return nil |
|
| 399 |
+ } |
|
| 400 |
+ return fs.WithXAttrErrorHandler(xeh) |
|
| 401 |
+} |
|
| 402 |
+ |
|
| 391 | 403 |
// copyExistingContents copies from the source to the destination and |
| 392 | 404 |
// ensures the ownership is appropriately set. |
| 393 | 405 |
func copyExistingContents(source, destination string) error {
|
| ... | ... |
@@ -399,7 +412,7 @@ func copyExistingContents(source, destination string) error {
|
| 399 | 399 |
// destination is not empty, do not copy |
| 400 | 400 |
return nil |
| 401 | 401 |
} |
| 402 |
- return fs.CopyDir(destination, source) |
|
| 402 |
+ return fs.CopyDir(destination, source, ignoreUnsupportedXAttrs()) |
|
| 403 | 403 |
} |
| 404 | 404 |
|
| 405 | 405 |
// TmpfsMounts returns the list of tmpfs mounts |
| ... | ... |
@@ -120,7 +120,7 @@ google.golang.org/genproto 694d95ba50e67b2e363f3483057db5d4910c18f9 |
| 120 | 120 |
# containerd |
| 121 | 121 |
github.com/containerd/containerd 9b32062dc1f5a7c2564315c269b5059754f12b9d # v1.2.1 |
| 122 | 122 |
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c |
| 123 |
-github.com/containerd/continuity bd77b46c8352f74eb12c85bdc01f4b90f69d66b4 |
|
| 123 |
+github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d |
|
| 124 | 124 |
github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 |
| 125 | 125 |
github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 |
| 126 | 126 |
github.com/containerd/cri 0ca1e3c2b73b5c38e72f29bb76338d0078b23d6c # release/1.2 branch |
| ... | ... |
@@ -1,6 +1,7 @@ |
| 1 |
+ |
|
| 1 | 2 |
Apache License |
| 2 | 3 |
Version 2.0, January 2004 |
| 3 |
- http://www.apache.org/licenses/ |
|
| 4 |
+ https://www.apache.org/licenses/ |
|
| 4 | 5 |
|
| 5 | 6 |
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
| 6 | 7 |
|
| ... | ... |
@@ -175,28 +176,16 @@ |
| 175 | 175 |
|
| 176 | 176 |
END OF TERMS AND CONDITIONS |
| 177 | 177 |
|
| 178 |
- APPENDIX: How to apply the Apache License to your work. |
|
| 179 |
- |
|
| 180 |
- To apply the Apache License to your work, attach the following |
|
| 181 |
- boilerplate notice, with the fields enclosed by brackets "{}"
|
|
| 182 |
- replaced with your own identifying information. (Don't include |
|
| 183 |
- the brackets!) The text should be enclosed in the appropriate |
|
| 184 |
- comment syntax for the file format. We also recommend that a |
|
| 185 |
- file or class name and description of purpose be included on the |
|
| 186 |
- same "printed page" as the copyright notice for easier |
|
| 187 |
- identification within third-party archives. |
|
| 188 |
- |
|
| 189 |
- Copyright {yyyy} {name of copyright owner}
|
|
| 178 |
+ Copyright The containerd Authors |
|
| 190 | 179 |
|
| 191 | 180 |
Licensed under the Apache License, Version 2.0 (the "License"); |
| 192 | 181 |
you may not use this file except in compliance with the License. |
| 193 | 182 |
You may obtain a copy of the License at |
| 194 | 183 |
|
| 195 |
- http://www.apache.org/licenses/LICENSE-2.0 |
|
| 184 |
+ https://www.apache.org/licenses/LICENSE-2.0 |
|
| 196 | 185 |
|
| 197 | 186 |
Unless required by applicable law or agreed to in writing, software |
| 198 | 187 |
distributed under the License is distributed on an "AS IS" BASIS, |
| 199 | 188 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 200 | 189 |
See the License for the specific language governing permissions and |
| 201 | 190 |
limitations under the License. |
| 202 |
- |
| ... | ... |
@@ -72,3 +72,13 @@ If you change the proto file you will need to rebuild the generated Go with `go |
| 72 | 72 |
```console |
| 73 | 73 |
$ go generate ./proto |
| 74 | 74 |
``` |
| 75 |
+ |
|
| 76 |
+## Project details |
|
| 77 |
+ |
|
| 78 |
+continuity is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). |
|
| 79 |
+As a containerd sub-project, you will find the: |
|
| 80 |
+ * [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md), |
|
| 81 |
+ * [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS), |
|
| 82 |
+ * and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md) |
|
| 83 |
+ |
|
| 84 |
+information in our [`containerd/project`](https://github.com/containerd/project) repository. |
| ... | ... |
@@ -32,14 +32,49 @@ var bufferPool = &sync.Pool{
|
| 32 | 32 |
}, |
| 33 | 33 |
} |
| 34 | 34 |
|
| 35 |
+// XAttrErrorHandlers transform a non-nil xattr error. |
|
| 36 |
+// Return nil to ignore an error. |
|
| 37 |
+// xattrKey can be empty for listxattr operation. |
|
| 38 |
+type XAttrErrorHandler func(dst, src, xattrKey string, err error) error |
|
| 39 |
+ |
|
| 40 |
+type copyDirOpts struct {
|
|
| 41 |
+ xeh XAttrErrorHandler |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+type CopyDirOpt func(*copyDirOpts) error |
|
| 45 |
+ |
|
| 46 |
+// WithXAttrErrorHandler allows specifying XAttrErrorHandler |
|
| 47 |
+// If nil XAttrErrorHandler is specified (default), CopyDir stops |
|
| 48 |
+// on a non-nil xattr error. |
|
| 49 |
+func WithXAttrErrorHandler(xeh XAttrErrorHandler) CopyDirOpt {
|
|
| 50 |
+ return func(o *copyDirOpts) error {
|
|
| 51 |
+ o.xeh = xeh |
|
| 52 |
+ return nil |
|
| 53 |
+ } |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// WithAllowXAttrErrors allows ignoring xattr errors. |
|
| 57 |
+func WithAllowXAttrErrors() CopyDirOpt {
|
|
| 58 |
+ xeh := func(dst, src, xattrKey string, err error) error {
|
|
| 59 |
+ return nil |
|
| 60 |
+ } |
|
| 61 |
+ return WithXAttrErrorHandler(xeh) |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 35 | 64 |
// CopyDir copies the directory from src to dst. |
| 36 | 65 |
// Most efficient copy of files is attempted. |
| 37 |
-func CopyDir(dst, src string) error {
|
|
| 66 |
+func CopyDir(dst, src string, opts ...CopyDirOpt) error {
|
|
| 67 |
+ var o copyDirOpts |
|
| 68 |
+ for _, opt := range opts {
|
|
| 69 |
+ if err := opt(&o); err != nil {
|
|
| 70 |
+ return err |
|
| 71 |
+ } |
|
| 72 |
+ } |
|
| 38 | 73 |
inodes := map[uint64]string{}
|
| 39 |
- return copyDirectory(dst, src, inodes) |
|
| 74 |
+ return copyDirectory(dst, src, inodes, &o) |
|
| 40 | 75 |
} |
| 41 | 76 |
|
| 42 |
-func copyDirectory(dst, src string, inodes map[uint64]string) error {
|
|
| 77 |
+func copyDirectory(dst, src string, inodes map[uint64]string, o *copyDirOpts) error {
|
|
| 43 | 78 |
stat, err := os.Stat(src) |
| 44 | 79 |
if err != nil {
|
| 45 | 80 |
return errors.Wrapf(err, "failed to stat %s", src) |
| ... | ... |
@@ -75,7 +110,7 @@ func copyDirectory(dst, src string, inodes map[uint64]string) error {
|
| 75 | 75 |
|
| 76 | 76 |
switch {
|
| 77 | 77 |
case fi.IsDir(): |
| 78 |
- if err := copyDirectory(target, source, inodes); err != nil {
|
|
| 78 |
+ if err := copyDirectory(target, source, inodes, o); err != nil {
|
|
| 79 | 79 |
return err |
| 80 | 80 |
} |
| 81 | 81 |
continue |
| ... | ... |
@@ -111,7 +146,7 @@ func copyDirectory(dst, src string, inodes map[uint64]string) error {
|
| 111 | 111 |
return errors.Wrap(err, "failed to copy file info") |
| 112 | 112 |
} |
| 113 | 113 |
|
| 114 |
- if err := copyXAttrs(target, source); err != nil {
|
|
| 114 |
+ if err := copyXAttrs(target, source, o.xeh); err != nil {
|
|
| 115 | 115 |
return errors.Wrap(err, "failed to copy xattrs") |
| 116 | 116 |
} |
| 117 | 117 |
} |
| ... | ... |
@@ -59,6 +59,8 @@ func copyFileInfo(fi os.FileInfo, name string) error {
|
| 59 | 59 |
return nil |
| 60 | 60 |
} |
| 61 | 61 |
|
| 62 |
+const maxSSizeT = int64(^uint(0) >> 1) |
|
| 63 |
+ |
|
| 62 | 64 |
func copyFileContent(dst, src *os.File) error {
|
| 63 | 65 |
st, err := src.Stat() |
| 64 | 66 |
if err != nil {
|
| ... | ... |
@@ -71,7 +73,16 @@ func copyFileContent(dst, src *os.File) error {
|
| 71 | 71 |
dstFd := int(dst.Fd()) |
| 72 | 72 |
|
| 73 | 73 |
for size > 0 {
|
| 74 |
- n, err := unix.CopyFileRange(srcFd, nil, dstFd, nil, int(size), 0) |
|
| 74 |
+ // Ensure that we are never trying to copy more than SSIZE_MAX at a |
|
| 75 |
+ // time and at the same time avoids overflows when the file is larger |
|
| 76 |
+ // than 4GB on 32-bit systems. |
|
| 77 |
+ var copySize int |
|
| 78 |
+ if size > maxSSizeT {
|
|
| 79 |
+ copySize = int(maxSSizeT) |
|
| 80 |
+ } else {
|
|
| 81 |
+ copySize = int(size) |
|
| 82 |
+ } |
|
| 83 |
+ n, err := unix.CopyFileRange(srcFd, nil, dstFd, nil, copySize, 0) |
|
| 75 | 84 |
if err != nil {
|
| 76 | 85 |
if (err != unix.ENOSYS && err != unix.EXDEV) || !first {
|
| 77 | 86 |
return errors.Wrap(err, "copy file range failed") |
| ... | ... |
@@ -90,18 +101,34 @@ func copyFileContent(dst, src *os.File) error {
|
| 90 | 90 |
return nil |
| 91 | 91 |
} |
| 92 | 92 |
|
| 93 |
-func copyXAttrs(dst, src string) error {
|
|
| 93 |
+func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
|
|
| 94 | 94 |
xattrKeys, err := sysx.LListxattr(src) |
| 95 | 95 |
if err != nil {
|
| 96 |
- return errors.Wrapf(err, "failed to list xattrs on %s", src) |
|
| 96 |
+ e := errors.Wrapf(err, "failed to list xattrs on %s", src) |
|
| 97 |
+ if xeh != nil {
|
|
| 98 |
+ e = xeh(dst, src, "", e) |
|
| 99 |
+ } |
|
| 100 |
+ return e |
|
| 97 | 101 |
} |
| 98 | 102 |
for _, xattr := range xattrKeys {
|
| 99 | 103 |
data, err := sysx.LGetxattr(src, xattr) |
| 100 | 104 |
if err != nil {
|
| 101 |
- return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src) |
|
| 105 |
+ e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src) |
|
| 106 |
+ if xeh != nil {
|
|
| 107 |
+ if e = xeh(dst, src, xattr, e); e == nil {
|
|
| 108 |
+ continue |
|
| 109 |
+ } |
|
| 110 |
+ } |
|
| 111 |
+ return e |
|
| 102 | 112 |
} |
| 103 | 113 |
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
|
| 104 |
- return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst) |
|
| 114 |
+ e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst) |
|
| 115 |
+ if xeh != nil {
|
|
| 116 |
+ if e = xeh(dst, src, xattr, e); e == nil {
|
|
| 117 |
+ continue |
|
| 118 |
+ } |
|
| 119 |
+ } |
|
| 120 |
+ return e |
|
| 105 | 121 |
} |
| 106 | 122 |
} |
| 107 | 123 |
|
| ... | ... |
@@ -69,18 +69,34 @@ func copyFileContent(dst, src *os.File) error {
|
| 69 | 69 |
return err |
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 |
-func copyXAttrs(dst, src string) error {
|
|
| 72 |
+func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
|
|
| 73 | 73 |
xattrKeys, err := sysx.LListxattr(src) |
| 74 | 74 |
if err != nil {
|
| 75 |
- return errors.Wrapf(err, "failed to list xattrs on %s", src) |
|
| 75 |
+ e := errors.Wrapf(err, "failed to list xattrs on %s", src) |
|
| 76 |
+ if xeh != nil {
|
|
| 77 |
+ e = xeh(dst, src, "", e) |
|
| 78 |
+ } |
|
| 79 |
+ return e |
|
| 76 | 80 |
} |
| 77 | 81 |
for _, xattr := range xattrKeys {
|
| 78 | 82 |
data, err := sysx.LGetxattr(src, xattr) |
| 79 | 83 |
if err != nil {
|
| 80 |
- return errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src) |
|
| 84 |
+ e := errors.Wrapf(err, "failed to get xattr %q on %s", xattr, src) |
|
| 85 |
+ if xeh != nil {
|
|
| 86 |
+ if e = xeh(dst, src, xattr, e); e == nil {
|
|
| 87 |
+ continue |
|
| 88 |
+ } |
|
| 89 |
+ } |
|
| 90 |
+ return e |
|
| 81 | 91 |
} |
| 82 | 92 |
if err := sysx.LSetxattr(dst, xattr, data, 0); err != nil {
|
| 83 |
- return errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst) |
|
| 93 |
+ e := errors.Wrapf(err, "failed to set xattr %q on %s", xattr, dst) |
|
| 94 |
+ if xeh != nil {
|
|
| 95 |
+ if e = xeh(dst, src, xattr, e); e == nil {
|
|
| 96 |
+ continue |
|
| 97 |
+ } |
|
| 98 |
+ } |
|
| 99 |
+ return e |
|
| 84 | 100 |
} |
| 85 | 101 |
} |
| 86 | 102 |
|