Browse code

Ignore xattr ENOTSUP errors on copy (fixes #38155)

Signed-off-by: Dimitris Mandalidis <dimitris.mandalidis@gmail.com>

Dimitris Mandalidis authored on 2018/12/04 15:07:06
Showing 8 changed files
... ...
@@ -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
 
... ...
@@ -40,7 +40,7 @@ func copyFileContent(dst, src *os.File) error {
40 40
 	return err
41 41
 }
42 42
 
43
-func copyXAttrs(dst, src string) error {
43
+func copyXAttrs(dst, src string, xeh XAttrErrorHandler) error {
44 44
 	return nil
45 45
 }
46 46