Browse code

Have VFS graphdriver use accelerated in-kernel copy

This change makes the VFS graphdriver use the kernel-accelerated
(copy_file_range) mechanism of copying files, which is able to
leverage reflinks.

Signed-off-by: Sargun Dhillon <sargun@sargun.me>

Sargun Dhillon authored on 2017/11/22 03:29:27
Showing 7 changed files
... ...
@@ -113,7 +113,9 @@ type fileID struct {
113 113
 
114 114
 // DirCopy copies or hardlinks the contents of one directory to another,
115 115
 // properly handling xattrs, and soft links
116
-func DirCopy(srcDir, dstDir string, copyMode Mode) error {
116
+//
117
+// Copying xattrs can be opted out of by passing false for copyXattrs.
118
+func DirCopy(srcDir, dstDir string, copyMode Mode, copyXattrs bool) error {
117 119
 	copyWithFileRange := true
118 120
 	copyWithFileClone := true
119 121
 	// This is a map of source file inodes to dst file paths
... ...
@@ -206,16 +208,10 @@ func DirCopy(srcDir, dstDir string, copyMode Mode) error {
206 206
 			return err
207 207
 		}
208 208
 
209
-		if err := copyXattr(srcPath, dstPath, "security.capability"); err != nil {
210
-			return err
211
-		}
212
-
213
-		// We need to copy this attribute if it appears in an overlay upper layer, as
214
-		// this function is used to copy those. It is set by overlay if a directory
215
-		// is removed and then re-created and should not inherit anything from the
216
-		// same dir in the lower dir.
217
-		if err := copyXattr(srcPath, dstPath, "trusted.overlay.opaque"); err != nil {
218
-			return err
209
+		if copyXattrs {
210
+			if err := doCopyXattrs(srcPath, dstPath); err != nil {
211
+				return err
212
+			}
219 213
 		}
220 214
 
221 215
 		isSymlink := f.Mode()&os.ModeSymlink != 0
... ...
@@ -246,3 +242,18 @@ func DirCopy(srcDir, dstDir string, copyMode Mode) error {
246 246
 	})
247 247
 	return err
248 248
 }
249
+
250
+func doCopyXattrs(srcPath, dstPath string) error {
251
+	if err := copyXattr(srcPath, dstPath, "security.capability"); err != nil {
252
+		return err
253
+	}
254
+
255
+	// We need to copy this attribute if it appears in an overlay upper layer, as
256
+	// this function is used to copy those. It is set by overlay if a directory
257
+	// is removed and then re-created and should not inherit anything from the
258
+	// same dir in the lower dir.
259
+	if err := copyXattr(srcPath, dstPath, "trusted.overlay.opaque"); err != nil {
260
+		return err
261
+	}
262
+	return nil
263
+}
... ...
@@ -86,7 +86,7 @@ func TestCopyHardlink(t *testing.T) {
86 86
 	require.NoError(t, ioutil.WriteFile(srcFile1, []byte{}, 0777))
87 87
 	require.NoError(t, os.Link(srcFile1, srcFile2))
88 88
 
89
-	assert.NoError(t, DirCopy(srcDir, dstDir, Content))
89
+	assert.NoError(t, DirCopy(srcDir, dstDir, Content, false))
90 90
 
91 91
 	require.NoError(t, unix.Stat(srcFile1, &srcFile1FileInfo))
92 92
 	require.NoError(t, unix.Stat(srcFile2, &srcFile2FileInfo))
... ...
@@ -330,7 +330,7 @@ func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) (retErr
330 330
 		return err
331 331
 	}
332 332
 
333
-	return copy.DirCopy(parentUpperDir, upperDir, copy.Content)
333
+	return copy.DirCopy(parentUpperDir, upperDir, copy.Content, true)
334 334
 }
335 335
 
336 336
 func (d *Driver) dir(id string) string {
... ...
@@ -446,7 +446,7 @@ func (d *Driver) ApplyDiff(id string, parent string, diff io.Reader) (size int64
446 446
 		}
447 447
 	}()
448 448
 
449
-	if err = copy.DirCopy(parentRootDir, tmpRootDir, copy.Hardlink); err != nil {
449
+	if err = copy.DirCopy(parentRootDir, tmpRootDir, copy.Hardlink, true); err != nil {
450 450
 		return 0, err
451 451
 	}
452 452
 
453 453
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+// +build linux
1
+
2
+package vfs
3
+
4
+import "github.com/docker/docker/daemon/graphdriver/copy"
5
+
6
+func dirCopy(srcDir, dstDir string) error {
7
+	return copy.DirCopy(srcDir, dstDir, copy.Content, false)
8
+}
0 9
new file mode 100644
... ...
@@ -0,0 +1,9 @@
0
+// +build !linux
1
+
2
+package vfs
3
+
4
+import "github.com/docker/docker/pkg/chrootarchive"
5
+
6
+func dirCopy(srcDir, dstDir string) error {
7
+	return chrootarchive.NewArchiver(nil).CopyWithTar(srcDir, dstDir)
8
+}
... ...
@@ -7,7 +7,6 @@ import (
7 7
 
8 8
 	"github.com/docker/docker/daemon/graphdriver"
9 9
 	"github.com/docker/docker/daemon/graphdriver/quota"
10
-	"github.com/docker/docker/pkg/chrootarchive"
11 10
 	"github.com/docker/docker/pkg/containerfs"
12 11
 	"github.com/docker/docker/pkg/idtools"
13 12
 	"github.com/docker/docker/pkg/system"
... ...
@@ -16,8 +15,8 @@ import (
16 16
 )
17 17
 
18 18
 var (
19
-	// CopyWithTar defines the copy method to use.
20
-	CopyWithTar = chrootarchive.NewArchiver(nil).CopyWithTar
19
+	// CopyDir defines the copy method to use.
20
+	CopyDir = dirCopy
21 21
 )
22 22
 
23 23
 func init() {
... ...
@@ -133,7 +132,7 @@ func (d *Driver) create(id, parent string, size uint64) error {
133 133
 	if err != nil {
134 134
 		return fmt.Errorf("%s: %s", parent, err)
135 135
 	}
136
-	return CopyWithTar(parentDir.Path(), dir)
136
+	return CopyDir(parentDir.Path(), dir)
137 137
 }
138 138
 
139 139
 func (d *Driver) dir(id string) string {
... ...
@@ -23,7 +23,7 @@ import (
23 23
 func init() {
24 24
 	graphdriver.ApplyUncompressedLayer = archive.UnpackLayer
25 25
 	defaultArchiver := archive.NewDefaultArchiver()
26
-	vfs.CopyWithTar = defaultArchiver.CopyWithTar
26
+	vfs.CopyDir = defaultArchiver.CopyWithTar
27 27
 }
28 28
 
29 29
 func newVFSGraphDriver(td string) (graphdriver.Driver, error) {