Browse code

Merge pull request #2897 from crosbymichael/aufs-42

Increase max image depth to 127

Guillaume J. Charmes authored on 2013/12/14 09:03:57
Showing 4 changed files
... ...
@@ -26,11 +26,11 @@ import (
26 26
 	"github.com/dotcloud/docker/archive"
27 27
 	"github.com/dotcloud/docker/graphdriver"
28 28
 	"github.com/dotcloud/docker/utils"
29
-	"log"
30 29
 	"os"
31 30
 	"os/exec"
32 31
 	"path"
33 32
 	"strings"
33
+	"syscall"
34 34
 )
35 35
 
36 36
 func init() {
... ...
@@ -313,24 +313,44 @@ func (a *Driver) Cleanup() error {
313 313
 	return nil
314 314
 }
315 315
 
316
-func (a *Driver) aufsMount(ro []string, rw, target string) error {
317
-	rwBranch := fmt.Sprintf("%v=rw", rw)
318
-	roBranches := ""
319
-	for _, layer := range ro {
320
-		roBranches += fmt.Sprintf("%v=ro+wh:", layer)
321
-	}
322
-	branches := fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches)
316
+func (a *Driver) aufsMount(ro []string, rw, target string) (err error) {
317
+	defer func() {
318
+		if err != nil {
319
+			Unmount(target)
320
+		}
321
+	}()
323 322
 
324
-	//if error, try to load aufs kernel module
325
-	if err := mount("none", target, "aufs", 0, branches); err != nil {
326
-		log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
327
-		if err := exec.Command("modprobe", "aufs").Run(); err != nil {
328
-			return fmt.Errorf("Unable to load the AUFS module")
323
+	if err = a.tryMount(ro, rw, target); err != nil {
324
+		if err = a.mountRw(rw, target); err != nil {
325
+			return
329 326
 		}
330
-		log.Printf("...module loaded.")
331
-		if err := mount("none", target, "aufs", 0, branches); err != nil {
332
-			return fmt.Errorf("Unable to mount using aufs %s", err)
327
+
328
+		for _, layer := range ro {
329
+			branch := fmt.Sprintf("append:%s=ro+wh", layer)
330
+			if err = mount("none", target, "aufs", syscall.MS_REMOUNT, branch); err != nil {
331
+				return
332
+			}
333 333
 		}
334 334
 	}
335
-	return nil
335
+	return
336
+}
337
+
338
+// Try to mount using the aufs fast path, if this fails then
339
+// append ro layers.
340
+func (a *Driver) tryMount(ro []string, rw, target string) (err error) {
341
+	var (
342
+		rwBranch   = fmt.Sprintf("%s=rw", rw)
343
+		roBranches = fmt.Sprintf("%s=ro+wh:", strings.Join(ro, "=ro+wh:"))
344
+	)
345
+	return mount("none", target, "aufs", 0, fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches))
346
+}
347
+
348
+func (a *Driver) mountRw(rw, target string) error {
349
+	return mount("none", target, "aufs", 0, fmt.Sprintf("br:%s,xino=/dev/shm/aufs.xino", rw))
350
+}
351
+
352
+func rollbackMount(target string, err error) {
353
+	if err != nil {
354
+		Unmount(target)
355
+	}
336 356
 }
... ...
@@ -1,7 +1,11 @@
1 1
 package aufs
2 2
 
3 3
 import (
4
+	"crypto/sha256"
5
+	"encoding/hex"
6
+	"fmt"
4 7
 	"github.com/dotcloud/docker/archive"
8
+	"io/ioutil"
5 9
 	"os"
6 10
 	"path"
7 11
 	"testing"
... ...
@@ -621,3 +625,70 @@ func TestApplyDiff(t *testing.T) {
621 621
 		t.Fatal(err)
622 622
 	}
623 623
 }
624
+
625
+func hash(c string) string {
626
+	h := sha256.New()
627
+	fmt.Fprint(h, c)
628
+	return hex.EncodeToString(h.Sum(nil))
629
+}
630
+
631
+func TestMountMoreThan42Layers(t *testing.T) {
632
+	d := newDriver(t)
633
+	defer os.RemoveAll(tmp)
634
+	defer d.Cleanup()
635
+	var last string
636
+	var expected int
637
+
638
+	for i := 1; i < 127; i++ {
639
+		expected++
640
+		var (
641
+			parent  = fmt.Sprintf("%d", i-1)
642
+			current = fmt.Sprintf("%d", i)
643
+		)
644
+
645
+		if parent == "0" {
646
+			parent = ""
647
+		} else {
648
+			parent = hash(parent)
649
+		}
650
+		current = hash(current)
651
+
652
+		if err := d.Create(current, parent); err != nil {
653
+			t.Logf("Current layer %d", i)
654
+			t.Fatal(err)
655
+		}
656
+		point, err := d.Get(current)
657
+		if err != nil {
658
+			t.Logf("Current layer %d", i)
659
+			t.Fatal(err)
660
+		}
661
+		f, err := os.Create(path.Join(point, current))
662
+		if err != nil {
663
+			t.Logf("Current layer %d", i)
664
+			t.Fatal(err)
665
+		}
666
+		f.Close()
667
+
668
+		if i%10 == 0 {
669
+			if err := os.Remove(path.Join(point, parent)); err != nil {
670
+				t.Logf("Current layer %d", i)
671
+				t.Fatal(err)
672
+			}
673
+			expected--
674
+		}
675
+		last = current
676
+	}
677
+
678
+	// Perform the actual mount for the top most image
679
+	point, err := d.Get(last)
680
+	if err != nil {
681
+		t.Fatal(err)
682
+	}
683
+	files, err := ioutil.ReadDir(point)
684
+	if err != nil {
685
+		t.Fatal(err)
686
+	}
687
+	if len(files) != expected {
688
+		t.Fatalf("Expected %d got %d", expected, len(files))
689
+	}
690
+}
... ...
@@ -2,6 +2,6 @@ package aufs
2 2
 
3 3
 import "syscall"
4 4
 
5
-func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
5
+func mount(source string, target string, fstype string, flags uintptr, data string) error {
6 6
 	return syscall.Mount(source, target, fstype, flags, data)
7 7
 }
... ...
@@ -24,8 +24,10 @@ import (
24 24
 	"time"
25 25
 )
26 26
 
27
-// Set the max depth to the aufs restriction
28
-const MaxImageDepth = 42
27
+// Set the max depth to the aufs default that most
28
+// kernels are compiled with
29
+// For more information see: http://sourceforge.net/p/aufs/aufs3-standalone/ci/aufs3.12/tree/config.mk
30
+const MaxImageDepth = 127
29 31
 
30 32
 var defaultDns = []string{"8.8.8.8", "8.8.4.4"}
31 33