Browse code

Merge pull request #2609 from shykes/0.6.5-dm-plugin

Move aufs to a storage driver, add devicemapper and dummy drivers

Solomon Hykes authored on 2013/11/26 11:58:26
Showing 64 changed files
... ...
@@ -23,7 +23,7 @@
23 23
 # the case. Therefore, you don't have to disable it anymore.
24 24
 #
25 25
 
26
-docker-version 0.6.1
26
+docker-version	0.6.1
27 27
 from	ubuntu:12.04
28 28
 maintainer	Solomon Hykes <solomon@dotcloud.com>
29 29
 
... ...
@@ -33,13 +33,13 @@ run	apt-get update
33 33
 run	apt-get install -y -q curl
34 34
 run	apt-get install -y -q git
35 35
 run	apt-get install -y -q mercurial
36
-run apt-get install -y -q build-essential libsqlite3-dev
36
+run	apt-get install -y -q build-essential libsqlite3-dev
37 37
 
38 38
 # Install Go
39 39
 run	curl -s https://go.googlecode.com/files/go1.2rc5.src.tar.gz | tar -v -C /usr/local -xz
40 40
 env	PATH	/usr/local/go/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
41 41
 env	GOPATH	/go:/go/src/github.com/dotcloud/docker/vendor
42
-run cd /usr/local/go/src && ./make.bash && go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std
42
+run	cd /usr/local/go/src && ./make.bash && go install -ldflags '-w -linkmode external -extldflags "-static -Wl,--unresolved-symbols=ignore-in-shared-libs"' -tags netgo -a std
43 43
 
44 44
 # Ubuntu stuff
45 45
 run	apt-get install -y -q ruby1.9.3 rubygems libffi-dev
... ...
@@ -56,11 +56,20 @@ run	apt-get install -y -q iptables
56 56
 run	apt-get install -y -q lxc
57 57
 run	apt-get install -y -q aufs-tools
58 58
 
59
+# Get lvm2 source for compiling statically
60
+run	git clone https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2 && cd /usr/local/lvm2 && git checkout v2_02_103
61
+# see https://git.fedorahosted.org/cgit/lvm2.git/refs/tags for release tags
62
+# note: we can't use "git clone -b" above because it requires at least git 1.7.10 to be able to use that on a tag instead of a branch and we only have 1.7.9.5
63
+
64
+# Compile and install lvm2
65
+run	cd /usr/local/lvm2 && ./configure --enable-static_link && make device-mapper && make install_device-mapper
66
+# see https://git.fedorahosted.org/cgit/lvm2.git/tree/INSTALL
67
+
59 68
 volume	/var/lib/docker
60 69
 workdir	/go/src/github.com/dotcloud/docker
61 70
 
62 71
 # Wrap all commands in the "docker-in-docker" script to allow nested containers
63
-entrypoint ["hack/dind"]
72
+entrypoint	["hack/dind"]
64 73
 
65 74
 # Upload docker source
66
-add	.       /go/src/github.com/dotcloud/docker
75
+add	.	/go/src/github.com/dotcloud/docker
... ...
@@ -33,15 +33,17 @@ type (
33 33
 		Debug              bool
34 34
 		Containers         int
35 35
 		Images             int
36
-		NFd                int    `json:",omitempty"`
37
-		NGoroutines        int    `json:",omitempty"`
38
-		MemoryLimit        bool   `json:",omitempty"`
39
-		SwapLimit          bool   `json:",omitempty"`
40
-		IPv4Forwarding     bool   `json:",omitempty"`
41
-		LXCVersion         string `json:",omitempty"`
42
-		NEventsListener    int    `json:",omitempty"`
43
-		KernelVersion      string `json:",omitempty"`
44
-		IndexServerAddress string `json:",omitempty"`
36
+		Driver             string      `json:",omitempty"`
37
+		DriverStatus       [][2]string `json:",omitempty"`
38
+		NFd                int         `json:",omitempty"`
39
+		NGoroutines        int         `json:",omitempty"`
40
+		MemoryLimit        bool        `json:",omitempty"`
41
+		SwapLimit          bool        `json:",omitempty"`
42
+		IPv4Forwarding     bool        `json:",omitempty"`
43
+		LXCVersion         string      `json:",omitempty"`
44
+		NEventsListener    int         `json:",omitempty"`
45
+		KernelVersion      string      `json:",omitempty"`
46
+		IndexServerAddress string      `json:",omitempty"`
45 47
 	}
46 48
 
47 49
 	APITop struct {
... ...
@@ -15,7 +15,15 @@ import (
15 15
 
16 16
 type Archive io.Reader
17 17
 
18
-type Compression uint32
18
+type Compression int
19
+
20
+type TarOptions struct {
21
+	Includes    []string
22
+	Excludes    []string
23
+	Recursive   bool
24
+	Compression Compression
25
+	CreateFiles []string
26
+}
19 27
 
20 28
 const (
21 29
 	Uncompressed Compression = iota
... ...
@@ -80,20 +88,78 @@ func (compression *Compression) Extension() string {
80 80
 // Tar creates an archive from the directory at `path`, and returns it as a
81 81
 // stream of bytes.
82 82
 func Tar(path string, compression Compression) (io.Reader, error) {
83
-	return TarFilter(path, compression, nil)
83
+	return TarFilter(path, &TarOptions{Recursive: true, Compression: compression})
84
+}
85
+
86
+func escapeName(name string) string {
87
+	escaped := make([]byte, 0)
88
+	for i, c := range []byte(name) {
89
+		if i == 0 && c == '/' {
90
+			continue
91
+		}
92
+		// all printable chars except "-" which is 0x2d
93
+		if (0x20 <= c && c <= 0x7E) && c != 0x2d {
94
+			escaped = append(escaped, c)
95
+		} else {
96
+			escaped = append(escaped, fmt.Sprintf("\\%03o", c)...)
97
+		}
98
+	}
99
+	return string(escaped)
84 100
 }
85 101
 
86 102
 // Tar creates an archive from the directory at `path`, only including files whose relative
87 103
 // paths are included in `filter`. If `filter` is nil, then all files are included.
88
-func TarFilter(path string, compression Compression, filter []string) (io.Reader, error) {
89
-	args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path}
90
-	if filter == nil {
91
-		filter = []string{"."}
104
+func TarFilter(path string, options *TarOptions) (io.Reader, error) {
105
+	args := []string{"tar", "--numeric-owner", "-f", "-", "-C", path, "-T", "-"}
106
+	if options.Includes == nil {
107
+		options.Includes = []string{"."}
92 108
 	}
93
-	for _, f := range filter {
94
-		args = append(args, "-c"+compression.Flag(), f)
109
+	args = append(args, "-c"+options.Compression.Flag())
110
+
111
+	for _, exclude := range options.Excludes {
112
+		args = append(args, fmt.Sprintf("--exclude=%s", exclude))
113
+	}
114
+
115
+	if !options.Recursive {
116
+		args = append(args, "--no-recursion")
117
+	}
118
+
119
+	files := ""
120
+	for _, f := range options.Includes {
121
+		files = files + escapeName(f) + "\n"
122
+	}
123
+
124
+	tmpDir := ""
125
+
126
+	if options.CreateFiles != nil {
127
+		var err error // Can't use := here or we override the outer tmpDir
128
+		tmpDir, err = ioutil.TempDir("", "docker-tar")
129
+		if err != nil {
130
+			return nil, err
131
+		}
132
+
133
+		files = files + "-C" + tmpDir + "\n"
134
+		for _, f := range options.CreateFiles {
135
+			path := filepath.Join(tmpDir, f)
136
+			err := os.MkdirAll(filepath.Dir(path), 0600)
137
+			if err != nil {
138
+				return nil, err
139
+			}
140
+
141
+			if file, err := os.OpenFile(path, os.O_CREATE, 0600); err != nil {
142
+				return nil, err
143
+			} else {
144
+				file.Close()
145
+			}
146
+			files = files + escapeName(f) + "\n"
147
+		}
95 148
 	}
96
-	return CmdStream(exec.Command(args[0], args[1:]...))
149
+
150
+	return CmdStream(exec.Command(args[0], args[1:]...), &files, func() {
151
+		if tmpDir != "" {
152
+			_ = os.RemoveAll(tmpDir)
153
+		}
154
+	})
97 155
 }
98 156
 
99 157
 // Untar reads a stream of bytes from `archive`, parses it as a tar archive,
... ...
@@ -101,7 +167,7 @@ func TarFilter(path string, compression Compression, filter []string) (io.Reader
101 101
 // The archive may be compressed with one of the following algorithms:
102 102
 //  identity (uncompressed), gzip, bzip2, xz.
103 103
 // FIXME: specify behavior when target path exists vs. doesn't exist.
104
-func Untar(archive io.Reader, path string) error {
104
+func Untar(archive io.Reader, path string, options *TarOptions) error {
105 105
 	if archive == nil {
106 106
 		return fmt.Errorf("Empty archive")
107 107
 	}
... ...
@@ -123,8 +189,15 @@ func Untar(archive io.Reader, path string) error {
123 123
 	compression := DetectCompression(buf)
124 124
 
125 125
 	utils.Debugf("Archive compression detected: %s", compression.Extension())
126
+	args := []string{"--numeric-owner", "-f", "-", "-C", path, "-x" + compression.Flag()}
127
+
128
+	if options != nil {
129
+		for _, exclude := range options.Excludes {
130
+			args = append(args, fmt.Sprintf("--exclude=%s", exclude))
131
+		}
132
+	}
126 133
 
127
-	cmd := exec.Command("tar", "--numeric-owner", "-f", "-", "-C", path, "-x"+compression.Flag())
134
+	cmd := exec.Command("tar", args...)
128 135
 	cmd.Stdin = io.MultiReader(bytes.NewReader(buf), archive)
129 136
 	// Hardcode locale environment for predictable outcome regardless of host configuration.
130 137
 	//   (see https://github.com/dotcloud/docker/issues/355)
... ...
@@ -141,11 +214,11 @@ func Untar(archive io.Reader, path string) error {
141 141
 // TarUntar aborts and returns the error.
142 142
 func TarUntar(src string, filter []string, dst string) error {
143 143
 	utils.Debugf("TarUntar(%s %s %s)", src, filter, dst)
144
-	archive, err := TarFilter(src, Uncompressed, filter)
144
+	archive, err := TarFilter(src, &TarOptions{Compression: Uncompressed, Includes: filter, Recursive: true})
145 145
 	if err != nil {
146 146
 		return err
147 147
 	}
148
-	return Untar(archive, dst)
148
+	return Untar(archive, dst, nil)
149 149
 }
150 150
 
151 151
 // UntarPath is a convenience function which looks for an archive
... ...
@@ -153,7 +226,7 @@ func TarUntar(src string, filter []string, dst string) error {
153 153
 func UntarPath(src, dst string) error {
154 154
 	if archive, err := os.Open(src); err != nil {
155 155
 		return err
156
-	} else if err := Untar(archive, dst); err != nil {
156
+	} else if err := Untar(archive, dst, nil); err != nil {
157 157
 		return err
158 158
 	}
159 159
 	return nil
... ...
@@ -222,19 +295,39 @@ func CopyFileWithTar(src, dst string) error {
222 222
 		return err
223 223
 	}
224 224
 	tw.Close()
225
-	return Untar(buf, filepath.Dir(dst))
225
+	return Untar(buf, filepath.Dir(dst), nil)
226 226
 }
227 227
 
228 228
 // CmdStream executes a command, and returns its stdout as a stream.
229 229
 // If the command fails to run or doesn't complete successfully, an error
230 230
 // will be returned, including anything written on stderr.
231
-func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
231
+func CmdStream(cmd *exec.Cmd, input *string, atEnd func()) (io.Reader, error) {
232
+	if input != nil {
233
+		stdin, err := cmd.StdinPipe()
234
+		if err != nil {
235
+			if atEnd != nil {
236
+				atEnd()
237
+			}
238
+			return nil, err
239
+		}
240
+		// Write stdin if any
241
+		go func() {
242
+			_, _ = stdin.Write([]byte(*input))
243
+			stdin.Close()
244
+		}()
245
+	}
232 246
 	stdout, err := cmd.StdoutPipe()
233 247
 	if err != nil {
248
+		if atEnd != nil {
249
+			atEnd()
250
+		}
234 251
 		return nil, err
235 252
 	}
236 253
 	stderr, err := cmd.StderrPipe()
237 254
 	if err != nil {
255
+		if atEnd != nil {
256
+			atEnd()
257
+		}
238 258
 		return nil, err
239 259
 	}
240 260
 	pipeR, pipeW := io.Pipe()
... ...
@@ -259,6 +352,9 @@ func CmdStream(cmd *exec.Cmd) (io.Reader, error) {
259 259
 		} else {
260 260
 			pipeW.Close()
261 261
 		}
262
+		if atEnd != nil {
263
+			atEnd()
264
+		}
262 265
 	}()
263 266
 	// Run the command and return the pipe
264 267
 	if err := cmd.Start(); err != nil {
... ...
@@ -14,7 +14,7 @@ import (
14 14
 
15 15
 func TestCmdStreamLargeStderr(t *testing.T) {
16 16
 	cmd := exec.Command("/bin/sh", "-c", "dd if=/dev/zero bs=1k count=1000 of=/dev/stderr; echo hello")
17
-	out, err := CmdStream(cmd)
17
+	out, err := CmdStream(cmd, nil, nil)
18 18
 	if err != nil {
19 19
 		t.Fatalf("Failed to start command: %s", err)
20 20
 	}
... ...
@@ -35,7 +35,7 @@ func TestCmdStreamLargeStderr(t *testing.T) {
35 35
 
36 36
 func TestCmdStreamBad(t *testing.T) {
37 37
 	badCmd := exec.Command("/bin/sh", "-c", "echo hello; echo >&2 error couldn\\'t reverse the phase pulser; exit 1")
38
-	out, err := CmdStream(badCmd)
38
+	out, err := CmdStream(badCmd, nil, nil)
39 39
 	if err != nil {
40 40
 		t.Fatalf("Failed to start command: %s", err)
41 41
 	}
... ...
@@ -50,7 +50,7 @@ func TestCmdStreamBad(t *testing.T) {
50 50
 
51 51
 func TestCmdStreamGood(t *testing.T) {
52 52
 	cmd := exec.Command("/bin/sh", "-c", "echo hello; exit 0")
53
-	out, err := CmdStream(cmd)
53
+	out, err := CmdStream(cmd, nil, nil)
54 54
 	if err != nil {
55 55
 		t.Fatal(err)
56 56
 	}
... ...
@@ -83,7 +83,7 @@ func tarUntar(t *testing.T, origin string, compression Compression) error {
83 83
 		return err
84 84
 	}
85 85
 	defer os.RemoveAll(tmp)
86
-	if err := Untar(archive, tmp); err != nil {
86
+	if err := Untar(archive, tmp, nil); err != nil {
87 87
 		return err
88 88
 	}
89 89
 	if _, err := os.Stat(tmp); err != nil {
90 90
new file mode 100644
... ...
@@ -0,0 +1,317 @@
0
+package archive
1
+
2
+import (
3
+	"fmt"
4
+	"os"
5
+	"path/filepath"
6
+	"strings"
7
+	"syscall"
8
+)
9
+
10
+type ChangeType int
11
+
12
+const (
13
+	ChangeModify = iota
14
+	ChangeAdd
15
+	ChangeDelete
16
+)
17
+
18
+type Change struct {
19
+	Path string
20
+	Kind ChangeType
21
+}
22
+
23
+func (change *Change) String() string {
24
+	var kind string
25
+	switch change.Kind {
26
+	case ChangeModify:
27
+		kind = "C"
28
+	case ChangeAdd:
29
+		kind = "A"
30
+	case ChangeDelete:
31
+		kind = "D"
32
+	}
33
+	return fmt.Sprintf("%s %s", kind, change.Path)
34
+}
35
+
36
+func Changes(layers []string, rw string) ([]Change, error) {
37
+	var changes []Change
38
+	err := filepath.Walk(rw, func(path string, f os.FileInfo, err error) error {
39
+		if err != nil {
40
+			return err
41
+		}
42
+
43
+		// Rebase path
44
+		path, err = filepath.Rel(rw, path)
45
+		if err != nil {
46
+			return err
47
+		}
48
+		path = filepath.Join("/", path)
49
+
50
+		// Skip root
51
+		if path == "/" {
52
+			return nil
53
+		}
54
+
55
+		// Skip AUFS metadata
56
+		if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched {
57
+			return err
58
+		}
59
+
60
+		change := Change{
61
+			Path: path,
62
+		}
63
+
64
+		// Find out what kind of modification happened
65
+		file := filepath.Base(path)
66
+		// If there is a whiteout, then the file was removed
67
+		if strings.HasPrefix(file, ".wh.") {
68
+			originalFile := file[len(".wh."):]
69
+			change.Path = filepath.Join(filepath.Dir(path), originalFile)
70
+			change.Kind = ChangeDelete
71
+		} else {
72
+			// Otherwise, the file was added
73
+			change.Kind = ChangeAdd
74
+
75
+			// ...Unless it already existed in a top layer, in which case, it's a modification
76
+			for _, layer := range layers {
77
+				stat, err := os.Stat(filepath.Join(layer, path))
78
+				if err != nil && !os.IsNotExist(err) {
79
+					return err
80
+				}
81
+				if err == nil {
82
+					// The file existed in the top layer, so that's a modification
83
+
84
+					// However, if it's a directory, maybe it wasn't actually modified.
85
+					// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
86
+					if stat.IsDir() && f.IsDir() {
87
+						if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
88
+							// Both directories are the same, don't record the change
89
+							return nil
90
+						}
91
+					}
92
+					change.Kind = ChangeModify
93
+					break
94
+				}
95
+			}
96
+		}
97
+
98
+		// Record change
99
+		changes = append(changes, change)
100
+		return nil
101
+	})
102
+	if err != nil && !os.IsNotExist(err) {
103
+		return nil, err
104
+	}
105
+	return changes, nil
106
+}
107
+
108
+type FileInfo struct {
109
+	parent   *FileInfo
110
+	name     string
111
+	stat     syscall.Stat_t
112
+	children map[string]*FileInfo
113
+}
114
+
115
+func (root *FileInfo) LookUp(path string) *FileInfo {
116
+	parent := root
117
+	if path == "/" {
118
+		return root
119
+	}
120
+
121
+	pathElements := strings.Split(path, "/")
122
+	for _, elem := range pathElements {
123
+		if elem != "" {
124
+			child := parent.children[elem]
125
+			if child == nil {
126
+				return nil
127
+			}
128
+			parent = child
129
+		}
130
+	}
131
+	return parent
132
+}
133
+
134
+func (info *FileInfo) path() string {
135
+	if info.parent == nil {
136
+		return "/"
137
+	}
138
+	return filepath.Join(info.parent.path(), info.name)
139
+}
140
+
141
+func (info *FileInfo) isDir() bool {
142
+	return info.parent == nil || info.stat.Mode&syscall.S_IFDIR == syscall.S_IFDIR
143
+}
144
+
145
+func (info *FileInfo) addChanges(oldInfo *FileInfo, changes *[]Change) {
146
+	if oldInfo == nil {
147
+		// add
148
+		change := Change{
149
+			Path: info.path(),
150
+			Kind: ChangeAdd,
151
+		}
152
+		*changes = append(*changes, change)
153
+	}
154
+
155
+	// We make a copy so we can modify it to detect additions
156
+	// also, we only recurse on the old dir if the new info is a directory
157
+	// otherwise any previous delete/change is considered recursive
158
+	oldChildren := make(map[string]*FileInfo)
159
+	if oldInfo != nil && info.isDir() {
160
+		for k, v := range oldInfo.children {
161
+			oldChildren[k] = v
162
+		}
163
+	}
164
+
165
+	for name, newChild := range info.children {
166
+		oldChild, _ := oldChildren[name]
167
+		if oldChild != nil {
168
+			// change?
169
+			oldStat := &oldChild.stat
170
+			newStat := &newChild.stat
171
+			// Note: We can't compare inode or ctime or blocksize here, because these change
172
+			// when copying a file into a container. However, that is not generally a problem
173
+			// because any content change will change mtime, and any status change should
174
+			// be visible when actually comparing the stat fields. The only time this
175
+			// breaks down is if some code intentionally hides a change by setting
176
+			// back mtime
177
+			if oldStat.Mode != newStat.Mode ||
178
+				oldStat.Uid != newStat.Uid ||
179
+				oldStat.Gid != newStat.Gid ||
180
+				oldStat.Rdev != newStat.Rdev ||
181
+				// Don't look at size for dirs, its not a good measure of change
182
+				(oldStat.Size != newStat.Size && oldStat.Mode&syscall.S_IFDIR != syscall.S_IFDIR) ||
183
+				oldStat.Mtim != newStat.Mtim {
184
+				change := Change{
185
+					Path: newChild.path(),
186
+					Kind: ChangeModify,
187
+				}
188
+				*changes = append(*changes, change)
189
+			}
190
+
191
+			// Remove from copy so we can detect deletions
192
+			delete(oldChildren, name)
193
+		}
194
+
195
+		newChild.addChanges(oldChild, changes)
196
+	}
197
+	for _, oldChild := range oldChildren {
198
+		// delete
199
+		change := Change{
200
+			Path: oldChild.path(),
201
+			Kind: ChangeDelete,
202
+		}
203
+		*changes = append(*changes, change)
204
+	}
205
+
206
+}
207
+
208
+func (info *FileInfo) Changes(oldInfo *FileInfo) []Change {
209
+	var changes []Change
210
+
211
+	info.addChanges(oldInfo, &changes)
212
+
213
+	return changes
214
+}
215
+
216
+func newRootFileInfo() *FileInfo {
217
+	root := &FileInfo{
218
+		name:     "/",
219
+		children: make(map[string]*FileInfo),
220
+	}
221
+	return root
222
+}
223
+
224
+func collectFileInfo(sourceDir string) (*FileInfo, error) {
225
+	root := newRootFileInfo()
226
+
227
+	err := filepath.Walk(sourceDir, func(path string, f os.FileInfo, err error) error {
228
+		if err != nil {
229
+			return err
230
+		}
231
+
232
+		// Rebase path
233
+		relPath, err := filepath.Rel(sourceDir, path)
234
+		if err != nil {
235
+			return err
236
+		}
237
+		relPath = filepath.Join("/", relPath)
238
+
239
+		if relPath == "/" {
240
+			return nil
241
+		}
242
+
243
+		parent := root.LookUp(filepath.Dir(relPath))
244
+		if parent == nil {
245
+			return fmt.Errorf("collectFileInfo: Unexpectedly no parent for %s", relPath)
246
+		}
247
+
248
+		info := &FileInfo{
249
+			name:     filepath.Base(relPath),
250
+			children: make(map[string]*FileInfo),
251
+			parent:   parent,
252
+		}
253
+
254
+		if err := syscall.Lstat(path, &info.stat); err != nil {
255
+			return err
256
+		}
257
+
258
+		parent.children[info.name] = info
259
+
260
+		return nil
261
+	})
262
+	if err != nil {
263
+		return nil, err
264
+	}
265
+	return root, nil
266
+}
267
+
268
+// Compare two directories and generate an array of Change objects describing the changes
269
+func ChangesDirs(newDir, oldDir string) ([]Change, error) {
270
+	oldRoot, err := collectFileInfo(oldDir)
271
+	if err != nil {
272
+		return nil, err
273
+	}
274
+	newRoot, err := collectFileInfo(newDir)
275
+	if err != nil {
276
+		return nil, err
277
+	}
278
+
279
+	return newRoot.Changes(oldRoot), nil
280
+}
281
+
282
+func ChangesSize(newDir string, changes []Change) int64 {
283
+	var size int64
284
+	for _, change := range changes {
285
+		if change.Kind == ChangeModify || change.Kind == ChangeAdd {
286
+			file := filepath.Join(newDir, change.Path)
287
+			fileInfo, _ := os.Lstat(file)
288
+			if fileInfo != nil && !fileInfo.IsDir() {
289
+				size += fileInfo.Size()
290
+			}
291
+		}
292
+	}
293
+	return size
294
+}
295
+
296
+func ExportChanges(dir string, changes []Change) (Archive, error) {
297
+	files := make([]string, 0)
298
+	deletions := make([]string, 0)
299
+	for _, change := range changes {
300
+		if change.Kind == ChangeModify || change.Kind == ChangeAdd {
301
+			files = append(files, change.Path)
302
+		}
303
+		if change.Kind == ChangeDelete {
304
+			base := filepath.Base(change.Path)
305
+			dir := filepath.Dir(change.Path)
306
+			deletions = append(deletions, filepath.Join(dir, ".wh."+base))
307
+		}
308
+	}
309
+	// FIXME: Why do we create whiteout files inside Tar code ?
310
+	return TarFilter(dir, &TarOptions{
311
+		Compression: Uncompressed,
312
+		Includes:    files,
313
+		Recursive:   false,
314
+		CreateFiles: deletions,
315
+	})
316
+}
0 317
new file mode 100644
... ...
@@ -0,0 +1,298 @@
0
+package archive
1
+
2
+import (
3
+	"io/ioutil"
4
+	"os"
5
+	"os/exec"
6
+	"path"
7
+	"sort"
8
+	"testing"
9
+	"time"
10
+)
11
+
12
+func max(x, y int) int {
13
+	if x >= y {
14
+		return x
15
+	}
16
+	return y
17
+}
18
+
19
+func copyDir(src, dst string) error {
20
+	cmd := exec.Command("cp", "-a", src, dst)
21
+	if err := cmd.Run(); err != nil {
22
+		return err
23
+	}
24
+	return nil
25
+}
26
+
27
+// Helper to sort []Change by path
28
+type byPath struct{ changes []Change }
29
+
30
+func (b byPath) Less(i, j int) bool { return b.changes[i].Path < b.changes[j].Path }
31
+func (b byPath) Len() int           { return len(b.changes) }
32
+func (b byPath) Swap(i, j int)      { b.changes[i], b.changes[j] = b.changes[j], b.changes[i] }
33
+
34
+type FileType uint32
35
+
36
+const (
37
+	Regular FileType = iota
38
+	Dir
39
+	Symlink
40
+)
41
+
42
+type FileData struct {
43
+	filetype    FileType
44
+	path        string
45
+	contents    string
46
+	permissions os.FileMode
47
+}
48
+
49
+func createSampleDir(t *testing.T, root string) {
50
+	files := []FileData{
51
+		{Regular, "file1", "file1\n", 0600},
52
+		{Regular, "file2", "file2\n", 0666},
53
+		{Regular, "file3", "file3\n", 0404},
54
+		{Regular, "file4", "file4\n", 0600},
55
+		{Regular, "file5", "file5\n", 0600},
56
+		{Regular, "file6", "file6\n", 0600},
57
+		{Regular, "file7", "file7\n", 0600},
58
+		{Dir, "dir1", "", 0740},
59
+		{Regular, "dir1/file1-1", "file1-1\n", 01444},
60
+		{Regular, "dir1/file1-2", "file1-2\n", 0666},
61
+		{Dir, "dir2", "", 0700},
62
+		{Regular, "dir2/file2-1", "file2-1\n", 0666},
63
+		{Regular, "dir2/file2-2", "file2-2\n", 0666},
64
+		{Dir, "dir3", "", 0700},
65
+		{Regular, "dir3/file3-1", "file3-1\n", 0666},
66
+		{Regular, "dir3/file3-2", "file3-2\n", 0666},
67
+		{Dir, "dir4", "", 0700},
68
+		{Regular, "dir4/file3-1", "file4-1\n", 0666},
69
+		{Regular, "dir4/file3-2", "file4-2\n", 0666},
70
+		{Symlink, "symlink1", "target1", 0666},
71
+		{Symlink, "symlink2", "target2", 0666},
72
+	}
73
+	for _, info := range files {
74
+		if info.filetype == Dir {
75
+			if err := os.MkdirAll(path.Join(root, info.path), info.permissions); err != nil {
76
+				t.Fatal(err)
77
+			}
78
+		} else if info.filetype == Regular {
79
+			if err := ioutil.WriteFile(path.Join(root, info.path), []byte(info.contents), info.permissions); err != nil {
80
+				t.Fatal(err)
81
+			}
82
+		} else if info.filetype == Symlink {
83
+			if err := os.Symlink(info.contents, path.Join(root, info.path)); err != nil {
84
+				t.Fatal(err)
85
+			}
86
+		}
87
+	}
88
+}
89
+
90
+// Create an directory, copy it, make sure we report no changes between the two
91
+func TestChangesDirsEmpty(t *testing.T) {
92
+	src, err := ioutil.TempDir("", "docker-changes-test")
93
+	if err != nil {
94
+		t.Fatal(err)
95
+	}
96
+	createSampleDir(t, src)
97
+	dst := src + "-copy"
98
+	if err := copyDir(src, dst); err != nil {
99
+		t.Fatal(err)
100
+	}
101
+	changes, err := ChangesDirs(dst, src)
102
+	if err != nil {
103
+		t.Fatal(err)
104
+	}
105
+
106
+	if len(changes) != 0 {
107
+		t.Fatalf("Reported changes for identical dirs: %v", changes)
108
+	}
109
+	os.RemoveAll(src)
110
+	os.RemoveAll(dst)
111
+}
112
+
113
+func mutateSampleDir(t *testing.T, root string) {
114
+	// Remove a regular file
115
+	if err := os.RemoveAll(path.Join(root, "file1")); err != nil {
116
+		t.Fatal(err)
117
+	}
118
+
119
+	// Remove a directory
120
+	if err := os.RemoveAll(path.Join(root, "dir1")); err != nil {
121
+		t.Fatal(err)
122
+	}
123
+
124
+	// Remove a symlink
125
+	if err := os.RemoveAll(path.Join(root, "symlink1")); err != nil {
126
+		t.Fatal(err)
127
+	}
128
+
129
+	// Rewrite a file
130
+	if err := ioutil.WriteFile(path.Join(root, "file2"), []byte("fileN\n"), 0777); err != nil {
131
+		t.Fatal(err)
132
+	}
133
+
134
+	// Replace a file
135
+	if err := os.RemoveAll(path.Join(root, "file3")); err != nil {
136
+		t.Fatal(err)
137
+	}
138
+	if err := ioutil.WriteFile(path.Join(root, "file3"), []byte("fileM\n"), 0404); err != nil {
139
+		t.Fatal(err)
140
+	}
141
+
142
+	// Touch file
143
+	if err := os.Chtimes(path.Join(root, "file4"), time.Now(), time.Now()); err != nil {
144
+		t.Fatal(err)
145
+	}
146
+
147
+	// Replace file with dir
148
+	if err := os.RemoveAll(path.Join(root, "file5")); err != nil {
149
+		t.Fatal(err)
150
+	}
151
+	if err := os.MkdirAll(path.Join(root, "file5"), 0666); err != nil {
152
+		t.Fatal(err)
153
+	}
154
+
155
+	// Create new file
156
+	if err := ioutil.WriteFile(path.Join(root, "filenew"), []byte("filenew\n"), 0777); err != nil {
157
+		t.Fatal(err)
158
+	}
159
+
160
+	// Create new dir
161
+	if err := os.MkdirAll(path.Join(root, "dirnew"), 0766); err != nil {
162
+		t.Fatal(err)
163
+	}
164
+
165
+	// Create a new symlink
166
+	if err := os.Symlink("targetnew", path.Join(root, "symlinknew")); err != nil {
167
+		t.Fatal(err)
168
+	}
169
+
170
+	// Change a symlink
171
+	if err := os.RemoveAll(path.Join(root, "symlink2")); err != nil {
172
+		t.Fatal(err)
173
+	}
174
+	if err := os.Symlink("target2change", path.Join(root, "symlink2")); err != nil {
175
+		t.Fatal(err)
176
+	}
177
+
178
+	// Replace dir with file
179
+	if err := os.RemoveAll(path.Join(root, "dir2")); err != nil {
180
+		t.Fatal(err)
181
+	}
182
+	if err := ioutil.WriteFile(path.Join(root, "dir2"), []byte("dir2\n"), 0777); err != nil {
183
+		t.Fatal(err)
184
+	}
185
+
186
+	// Touch dir
187
+	if err := os.Chtimes(path.Join(root, "dir3"), time.Now(), time.Now()); err != nil {
188
+		t.Fatal(err)
189
+	}
190
+}
191
+
192
+func TestChangesDirsMutated(t *testing.T) {
193
+	src, err := ioutil.TempDir("", "docker-changes-test")
194
+	if err != nil {
195
+		t.Fatal(err)
196
+	}
197
+	createSampleDir(t, src)
198
+	dst := src + "-copy"
199
+	if err := copyDir(src, dst); err != nil {
200
+		t.Fatal(err)
201
+	}
202
+	mutateSampleDir(t, dst)
203
+
204
+	changes, err := ChangesDirs(dst, src)
205
+	if err != nil {
206
+		t.Fatal(err)
207
+	}
208
+
209
+	sort.Sort(byPath{changes})
210
+
211
+	expectedChanges := []Change{
212
+		{"/dir1", ChangeDelete},
213
+		{"/dir2", ChangeModify},
214
+		{"/dir3", ChangeModify},
215
+		{"/dirnew", ChangeAdd},
216
+		{"/file1", ChangeDelete},
217
+		{"/file2", ChangeModify},
218
+		{"/file3", ChangeModify},
219
+		{"/file4", ChangeModify},
220
+		{"/file5", ChangeModify},
221
+		{"/filenew", ChangeAdd},
222
+		{"/symlink1", ChangeDelete},
223
+		{"/symlink2", ChangeModify},
224
+		{"/symlinknew", ChangeAdd},
225
+	}
226
+
227
+	i := 0
228
+	for ; i < max(len(changes), len(expectedChanges)); i++ {
229
+		if i >= len(expectedChanges) {
230
+			t.Fatalf("unexpected change %s\n", changes[i].String())
231
+		}
232
+		if i >= len(changes) {
233
+			t.Fatalf("no change for expected change %s\n", expectedChanges[i].String())
234
+		}
235
+		if changes[i].Path == expectedChanges[i].Path {
236
+			if changes[i] != expectedChanges[i] {
237
+				t.Fatalf("Wrong change for %s, expected %s, got %d\n", changes[i].Path, changes[i].String(), expectedChanges[i].String())
238
+			}
239
+		} else if changes[i].Path < expectedChanges[i].Path {
240
+			t.Fatalf("unexpected change %s\n", changes[i].String())
241
+		} else {
242
+			t.Fatalf("no change for expected change %s\n", expectedChanges[i].String())
243
+		}
244
+	}
245
+	for ; i < len(expectedChanges); i++ {
246
+	}
247
+
248
+	os.RemoveAll(src)
249
+	os.RemoveAll(dst)
250
+}
251
+
252
+func TestApplyLayer(t *testing.T) {
253
+	t.Skip("Skipping TestApplyLayer due to known failures") // Disable this for now as it is broken
254
+	return
255
+
256
+	src, err := ioutil.TempDir("", "docker-changes-test")
257
+	if err != nil {
258
+		t.Fatal(err)
259
+	}
260
+	createSampleDir(t, src)
261
+	dst := src + "-copy"
262
+	if err := copyDir(src, dst); err != nil {
263
+		t.Fatal(err)
264
+	}
265
+	mutateSampleDir(t, dst)
266
+
267
+	changes, err := ChangesDirs(dst, src)
268
+	if err != nil {
269
+		t.Fatal(err)
270
+	}
271
+
272
+	layer, err := ExportChanges(dst, changes)
273
+	if err != nil {
274
+		t.Fatal(err)
275
+	}
276
+
277
+	layerCopy, err := NewTempArchive(layer, "")
278
+	if err != nil {
279
+		t.Fatal(err)
280
+	}
281
+
282
+	if err := ApplyLayer(src, layerCopy); err != nil {
283
+		t.Fatal(err)
284
+	}
285
+
286
+	changes2, err := ChangesDirs(src, dst)
287
+	if err != nil {
288
+		t.Fatal(err)
289
+	}
290
+
291
+	if len(changes2) != 0 {
292
+		t.Fatalf("Unexpected differences after re applying mutation: %v", changes)
293
+	}
294
+
295
+	os.RemoveAll(src)
296
+	os.RemoveAll(dst)
297
+}
0 298
new file mode 100644
... ...
@@ -0,0 +1,95 @@
0
+package archive
1
+
2
+import (
3
+	"os"
4
+	"path/filepath"
5
+	"strings"
6
+	"syscall"
7
+	"time"
8
+)
9
+
10
+// ApplyLayer parses a diff in the standard layer format from `layer`, and
11
+// applies it to the directory `dest`.
12
+func ApplyLayer(dest string, layer Archive) error {
13
+	// Poor man's diff applyer in 2 steps:
14
+
15
+	// Step 1: untar everything in place
16
+	if err := Untar(layer, dest, nil); err != nil {
17
+		return err
18
+	}
19
+
20
+	modifiedDirs := make(map[string]*syscall.Stat_t)
21
+	addDir := func(file string) {
22
+		d := filepath.Dir(file)
23
+		if _, exists := modifiedDirs[d]; !exists {
24
+			if s, err := os.Lstat(d); err == nil {
25
+				if sys := s.Sys(); sys != nil {
26
+					if stat, ok := sys.(*syscall.Stat_t); ok {
27
+						modifiedDirs[d] = stat
28
+					}
29
+				}
30
+			}
31
+		}
32
+	}
33
+
34
+	// Step 2: walk for whiteouts and apply them, removing them in the process
35
+	err := filepath.Walk(dest, func(fullPath string, f os.FileInfo, err error) error {
36
+		if err != nil {
37
+			if os.IsNotExist(err) {
38
+				// This happens in the case of whiteouts in parent dir removing a directory
39
+				// We just ignore it
40
+				return filepath.SkipDir
41
+			}
42
+			return err
43
+		}
44
+
45
+		// Rebase path
46
+		path, err := filepath.Rel(dest, fullPath)
47
+		if err != nil {
48
+			return err
49
+		}
50
+		path = filepath.Join("/", path)
51
+
52
+		// Skip AUFS metadata
53
+		if matched, err := filepath.Match("/.wh..wh.*", path); err != nil {
54
+			return err
55
+		} else if matched {
56
+			addDir(fullPath)
57
+			if err := os.RemoveAll(fullPath); err != nil {
58
+				return err
59
+			}
60
+		}
61
+
62
+		filename := filepath.Base(path)
63
+		if strings.HasPrefix(filename, ".wh.") {
64
+			rmTargetName := filename[len(".wh."):]
65
+			rmTargetPath := filepath.Join(filepath.Dir(fullPath), rmTargetName)
66
+
67
+			// Remove the file targeted by the whiteout
68
+			addDir(rmTargetPath)
69
+			if err := os.RemoveAll(rmTargetPath); err != nil {
70
+				return err
71
+			}
72
+			// Remove the whiteout itself
73
+			addDir(fullPath)
74
+			if err := os.RemoveAll(fullPath); err != nil {
75
+				return err
76
+			}
77
+		}
78
+		return nil
79
+	})
80
+	if err != nil {
81
+		return err
82
+	}
83
+
84
+	for k, v := range modifiedDirs {
85
+		aTime := time.Unix(v.Atim.Unix())
86
+		mTime := time.Unix(v.Mtim.Unix())
87
+
88
+		if err := os.Chtimes(k, aTime, mTime); err != nil {
89
+			return err
90
+		}
91
+	}
92
+
93
+	return nil
94
+}
... ...
@@ -476,7 +476,7 @@ func (b *buildFile) Build(context io.Reader) (string, error) {
476 476
 	if err != nil {
477 477
 		return "", err
478 478
 	}
479
-	if err := archive.Untar(context, name); err != nil {
479
+	if err := archive.Untar(context, name, nil); err != nil {
480 480
 		return "", err
481 481
 	}
482 482
 	defer os.RemoveAll(name)
483 483
deleted file mode 100644
... ...
@@ -1,106 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"fmt"
5
-	"os"
6
-	"path/filepath"
7
-	"strings"
8
-)
9
-
10
-type ChangeType int
11
-
12
-const (
13
-	ChangeModify = iota
14
-	ChangeAdd
15
-	ChangeDelete
16
-)
17
-
18
-type Change struct {
19
-	Path string
20
-	Kind ChangeType
21
-}
22
-
23
-func (change *Change) String() string {
24
-	var kind string
25
-	switch change.Kind {
26
-	case ChangeModify:
27
-		kind = "C"
28
-	case ChangeAdd:
29
-		kind = "A"
30
-	case ChangeDelete:
31
-		kind = "D"
32
-	}
33
-	return fmt.Sprintf("%s %s", kind, change.Path)
34
-}
35
-
36
-func Changes(layers []string, rw string) ([]Change, error) {
37
-	var changes []Change
38
-	err := filepath.Walk(rw, func(path string, f os.FileInfo, err error) error {
39
-		if err != nil {
40
-			return err
41
-		}
42
-
43
-		// Rebase path
44
-		path, err = filepath.Rel(rw, path)
45
-		if err != nil {
46
-			return err
47
-		}
48
-		path = filepath.Join("/", path)
49
-
50
-		// Skip root
51
-		if path == "/" {
52
-			return nil
53
-		}
54
-
55
-		// Skip AUFS metadata
56
-		if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched {
57
-			return err
58
-		}
59
-
60
-		change := Change{
61
-			Path: path,
62
-		}
63
-
64
-		// Find out what kind of modification happened
65
-		file := filepath.Base(path)
66
-		// If there is a whiteout, then the file was removed
67
-		if strings.HasPrefix(file, ".wh.") {
68
-			originalFile := file[len(".wh."):]
69
-			change.Path = filepath.Join(filepath.Dir(path), originalFile)
70
-			change.Kind = ChangeDelete
71
-		} else {
72
-			// Otherwise, the file was added
73
-			change.Kind = ChangeAdd
74
-
75
-			// ...Unless it already existed in a top layer, in which case, it's a modification
76
-			for _, layer := range layers {
77
-				stat, err := os.Stat(filepath.Join(layer, path))
78
-				if err != nil && !os.IsNotExist(err) {
79
-					return err
80
-				}
81
-				if err == nil {
82
-					// The file existed in the top layer, so that's a modification
83
-
84
-					// However, if it's a directory, maybe it wasn't actually modified.
85
-					// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
86
-					if stat.IsDir() && f.IsDir() {
87
-						if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
88
-							// Both directories are the same, don't record the change
89
-							return nil
90
-						}
91
-					}
92
-					change.Kind = ChangeModify
93
-					break
94
-				}
95
-			}
96
-		}
97
-
98
-		// Record change
99
-		changes = append(changes, change)
100
-		return nil
101
-	})
102
-	if err != nil && !os.IsNotExist(err) {
103
-		return nil, err
104
-	}
105
-	return changes, nil
106
-}
... ...
@@ -463,6 +463,10 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
463 463
 
464 464
 	fmt.Fprintf(cli.out, "Containers: %d\n", out.Containers)
465 465
 	fmt.Fprintf(cli.out, "Images: %d\n", out.Images)
466
+	fmt.Fprintf(cli.out, "Driver: %s\n", out.Driver)
467
+	for _, pair := range out.DriverStatus {
468
+		fmt.Fprintf(cli.out, " %s: %s\n", pair[0], pair[1])
469
+	}
466 470
 	if out.Debug || os.Getenv("DEBUG") != "" {
467 471
 		fmt.Fprintf(cli.out, "Debug mode (server): %v\n", out.Debug)
468 472
 		fmt.Fprintf(cli.out, "Debug mode (client): %v\n", os.Getenv("DEBUG") != "")
... ...
@@ -1128,16 +1132,18 @@ func (cli *DockerCli) CmdImages(args ...string) error {
1128 1128
 		}
1129 1129
 
1130 1130
 		var outs []APIImages
1131
-		err = json.Unmarshal(body, &outs)
1132
-		if err != nil {
1131
+		if err := json.Unmarshal(body, &outs); err != nil {
1133 1132
 			return err
1134 1133
 		}
1135 1134
 
1136
-		var startImageArg = cmd.Arg(0)
1137
-		var startImage APIImages
1135
+		var (
1136
+			startImageArg = cmd.Arg(0)
1137
+			startImage    APIImages
1138
+
1139
+			roots    []APIImages
1140
+			byParent = make(map[string][]APIImages)
1141
+		)
1138 1142
 
1139
-		var roots []APIImages
1140
-		var byParent = make(map[string][]APIImages)
1141 1143
 		for _, image := range outs {
1142 1144
 			if image.ParentId == "" {
1143 1145
 				roots = append(roots, image)
... ...
@@ -2181,7 +2187,7 @@ func (cli *DockerCli) CmdCp(args ...string) error {
2181 2181
 
2182 2182
 	if statusCode == 200 {
2183 2183
 		r := bytes.NewReader(data)
2184
-		if err := archive.Untar(r, copyData.HostPath); err != nil {
2184
+		if err := archive.Untar(r, copyData.HostPath, nil); err != nil {
2185 2185
 			return err
2186 2186
 		}
2187 2187
 	}
... ...
@@ -16,6 +16,7 @@ type DaemonConfig struct {
16 16
 	BridgeIface                 string
17 17
 	DefaultIp                   net.IP
18 18
 	InterContainerCommunication bool
19
+	GraphDriver                 string
19 20
 }
20 21
 
21 22
 // ConfigFromJob creates and returns a new DaemonConfig object
... ...
@@ -37,5 +38,6 @@ func ConfigFromJob(job *engine.Job) *DaemonConfig {
37 37
 	}
38 38
 	config.DefaultIp = net.ParseIP(job.Getenv("DefaultIp"))
39 39
 	config.InterContainerCommunication = job.GetenvBool("InterContainerCommunication")
40
+	config.GraphDriver = job.Getenv("GraphDriver")
40 41
 	return &config
41 42
 }
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"errors"
7 7
 	"fmt"
8 8
 	"github.com/dotcloud/docker/archive"
9
+	"github.com/dotcloud/docker/graphdriver"
9 10
 	"github.com/dotcloud/docker/term"
10 11
 	"github.com/dotcloud/docker/utils"
11 12
 	"github.com/kr/pty"
... ...
@@ -16,7 +17,6 @@ import (
16 16
 	"os"
17 17
 	"os/exec"
18 18
 	"path"
19
-	"path/filepath"
20 19
 	"strconv"
21 20
 	"strings"
22 21
 	"sync"
... ...
@@ -26,8 +26,8 @@ import (
26 26
 
27 27
 type Container struct {
28 28
 	sync.Mutex
29
-
30
-	root string
29
+	root   string // Path to the "home" of the container, including metadata.
30
+	rootfs string // Path to the root filesystem of the container.
31 31
 
32 32
 	ID string
33 33
 
... ...
@@ -48,6 +48,7 @@ type Container struct {
48 48
 	HostnamePath   string
49 49
 	HostsPath      string
50 50
 	Name           string
51
+	Driver         string
51 52
 
52 53
 	cmd       *exec.Cmd
53 54
 	stdout    *utils.WriteBroadcaster
... ...
@@ -196,8 +197,13 @@ func (settings *NetworkSettings) PortMappingAPI() []APIPort {
196 196
 
197 197
 // Inject the io.Reader at the given path. Note: do not close the reader
198 198
 func (container *Container) Inject(file io.Reader, pth string) error {
199
+	if err := container.EnsureMounted(); err != nil {
200
+		return fmt.Errorf("inject: error mounting container %s: %s", container.ID, err)
201
+	}
202
+
199 203
 	// Return error if path exists
200
-	if _, err := os.Stat(path.Join(container.rwPath(), pth)); err == nil {
204
+	destPath := path.Join(container.RootfsPath(), pth)
205
+	if _, err := os.Stat(destPath); err == nil {
201 206
 		// Since err is nil, the path could be stat'd and it exists
202 207
 		return fmt.Errorf("%s exists", pth)
203 208
 	} else if !os.IsNotExist(err) {
... ...
@@ -208,14 +214,16 @@ func (container *Container) Inject(file io.Reader, pth string) error {
208 208
 	}
209 209
 
210 210
 	// Make sure the directory exists
211
-	if err := os.MkdirAll(path.Join(container.rwPath(), path.Dir(pth)), 0755); err != nil {
211
+	if err := os.MkdirAll(path.Join(container.RootfsPath(), path.Dir(pth)), 0755); err != nil {
212 212
 		return err
213 213
 	}
214 214
 
215
-	dest, err := os.Create(path.Join(container.rwPath(), pth))
215
+	dest, err := os.Create(destPath)
216 216
 	if err != nil {
217 217
 		return err
218 218
 	}
219
+	defer dest.Close()
220
+
219 221
 	if _, err := io.Copy(dest, file); err != nil {
220 222
 		return err
221 223
 	}
... ...
@@ -607,6 +615,7 @@ func (container *Container) Start() (err error) {
607 607
 		}
608 608
 	}
609 609
 
610
+	volumesDriver := container.runtime.volumes.driver
610 611
 	// Create the requested volumes if they don't exist
611 612
 	for volPath := range container.Config.Volumes {
612 613
 		volPath = path.Clean(volPath)
... ...
@@ -626,13 +635,17 @@ func (container *Container) Start() (err error) {
626 626
 			}
627 627
 			// Otherwise create an directory in $ROOT/volumes/ and use that
628 628
 		} else {
629
-			c, err := container.runtime.volumes.Create(nil, container, "", "", nil)
629
+
630
+			// Do not pass a container as the parameter for the volume creation.
631
+			// The graph driver using the container's information ( Image ) to
632
+			// create the parent.
633
+			c, err := container.runtime.volumes.Create(nil, nil, "", "", nil)
630 634
 			if err != nil {
631 635
 				return err
632 636
 			}
633
-			srcPath, err = c.layer()
637
+			srcPath, err = volumesDriver.Get(c.ID)
634 638
 			if err != nil {
635
-				return err
639
+				return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
636 640
 			}
637 641
 			srcRW = true // RW by default
638 642
 		}
... ...
@@ -1231,15 +1244,14 @@ func (container *Container) Resize(h, w int) error {
1231 1231
 }
1232 1232
 
1233 1233
 func (container *Container) ExportRw() (archive.Archive, error) {
1234
-	return archive.Tar(container.rwPath(), archive.Uncompressed)
1235
-}
1236
-
1237
-func (container *Container) RwChecksum() (string, error) {
1238
-	rwData, err := archive.Tar(container.rwPath(), archive.Xz)
1239
-	if err != nil {
1240
-		return "", err
1234
+	if err := container.EnsureMounted(); err != nil {
1235
+		return nil, err
1236
+	}
1237
+	if container.runtime == nil {
1238
+		return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
1241 1239
 	}
1242
-	return utils.HashData(rwData)
1240
+
1241
+	return container.runtime.Diff(container)
1243 1242
 }
1244 1243
 
1245 1244
 func (container *Container) Export() (archive.Archive, error) {
... ...
@@ -1265,28 +1277,17 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
1265 1265
 }
1266 1266
 
1267 1267
 func (container *Container) EnsureMounted() error {
1268
-	if mounted, err := container.Mounted(); err != nil {
1269
-		return err
1270
-	} else if mounted {
1271
-		return nil
1272
-	}
1268
+	// FIXME: EnsureMounted is deprecated because drivers are now responsible
1269
+	// for re-entrant mounting in their Get() method.
1273 1270
 	return container.Mount()
1274 1271
 }
1275 1272
 
1276 1273
 func (container *Container) Mount() error {
1277
-	image, err := container.GetImage()
1278
-	if err != nil {
1279
-		return err
1280
-	}
1281
-	return image.Mount(container.RootfsPath(), container.rwPath())
1274
+	return container.runtime.Mount(container)
1282 1275
 }
1283 1276
 
1284
-func (container *Container) Changes() ([]Change, error) {
1285
-	image, err := container.GetImage()
1286
-	if err != nil {
1287
-		return nil, err
1288
-	}
1289
-	return image.Changes(container.rwPath())
1277
+func (container *Container) Changes() ([]archive.Change, error) {
1278
+	return container.runtime.Changes(container)
1290 1279
 }
1291 1280
 
1292 1281
 func (container *Container) GetImage() (*Image, error) {
... ...
@@ -1296,18 +1297,8 @@ func (container *Container) GetImage() (*Image, error) {
1296 1296
 	return container.runtime.graph.Get(container.Image)
1297 1297
 }
1298 1298
 
1299
-func (container *Container) Mounted() (bool, error) {
1300
-	return Mounted(container.RootfsPath())
1301
-}
1302
-
1303 1299
 func (container *Container) Unmount() error {
1304
-	if _, err := os.Stat(container.RootfsPath()); err != nil {
1305
-		if os.IsNotExist(err) {
1306
-			return nil
1307
-		}
1308
-		return err
1309
-	}
1310
-	return Unmount(container.RootfsPath())
1300
+	return container.runtime.Unmount(container)
1311 1301
 }
1312 1302
 
1313 1303
 func (container *Container) logPath(name string) string {
... ...
@@ -1336,11 +1327,7 @@ func (container *Container) lxcConfigPath() string {
1336 1336
 
1337 1337
 // This method must be exported to be used from the lxc template
1338 1338
 func (container *Container) RootfsPath() string {
1339
-	return path.Join(container.root, "rootfs")
1340
-}
1341
-
1342
-func (container *Container) rwPath() string {
1343
-	return path.Join(container.root, "rw")
1339
+	return container.rootfs
1344 1340
 }
1345 1341
 
1346 1342
 func validateID(id string) error {
... ...
@@ -1352,49 +1339,38 @@ func validateID(id string) error {
1352 1352
 
1353 1353
 // GetSize, return real size, virtual size
1354 1354
 func (container *Container) GetSize() (int64, int64) {
1355
-	var sizeRw, sizeRootfs int64
1356
-	data := make(map[uint64]bool)
1355
+	var (
1356
+		sizeRw, sizeRootfs int64
1357
+		err                error
1358
+		driver             = container.runtime.driver
1359
+	)
1357 1360
 
1358
-	filepath.Walk(container.rwPath(), func(path string, fileInfo os.FileInfo, err error) error {
1359
-		if fileInfo == nil {
1360
-			return nil
1361
+	if err := container.EnsureMounted(); err != nil {
1362
+		utils.Errorf("Warning: failed to compute size of container rootfs %s: %s", container.ID, err)
1363
+		return sizeRw, sizeRootfs
1364
+	}
1365
+
1366
+	if differ, ok := container.runtime.driver.(graphdriver.Differ); ok {
1367
+		sizeRw, err = differ.DiffSize(container.ID)
1368
+		if err != nil {
1369
+			utils.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
1370
+			// FIXME: GetSize should return an error. Not changing it now in case
1371
+			// there is a side-effect.
1372
+			sizeRw = -1
1361 1373
 		}
1362
-		size := fileInfo.Size()
1363
-		if size == 0 {
1364
-			return nil
1374
+	} else {
1375
+		changes, _ := container.Changes()
1376
+		if changes != nil {
1377
+			sizeRw = archive.ChangesSize(container.RootfsPath(), changes)
1378
+		} else {
1379
+			sizeRw = -1
1365 1380
 		}
1381
+	}
1366 1382
 
1367
-		inode := fileInfo.Sys().(*syscall.Stat_t).Ino
1368
-		if _, entryExists := data[inode]; entryExists {
1369
-			return nil
1383
+	if _, err = os.Stat(container.RootfsPath()); err != nil {
1384
+		if sizeRootfs, err = utils.TreeSize(container.RootfsPath()); err != nil {
1385
+			sizeRootfs = -1
1370 1386
 		}
1371
-		data[inode] = false
1372
-
1373
-		sizeRw += size
1374
-		return nil
1375
-	})
1376
-
1377
-	data = make(map[uint64]bool)
1378
-	_, err := os.Stat(container.RootfsPath())
1379
-	if err == nil {
1380
-		filepath.Walk(container.RootfsPath(), func(path string, fileInfo os.FileInfo, err error) error {
1381
-			if fileInfo == nil {
1382
-				return nil
1383
-			}
1384
-			size := fileInfo.Size()
1385
-			if size == 0 {
1386
-				return nil
1387
-			}
1388
-
1389
-			inode := fileInfo.Sys().(*syscall.Stat_t).Ino
1390
-			if _, entryExists := data[inode]; entryExists {
1391
-				return nil
1392
-			}
1393
-			data[inode] = false
1394
-
1395
-			sizeRootfs += size
1396
-			return nil
1397
-		})
1398 1387
 	}
1399 1388
 	return sizeRw, sizeRootfs
1400 1389
 }
... ...
@@ -1417,7 +1393,11 @@ func (container *Container) Copy(resource string) (archive.Archive, error) {
1417 1417
 		filter = []string{path.Base(basePath)}
1418 1418
 		basePath = path.Dir(basePath)
1419 1419
 	}
1420
-	return archive.TarFilter(basePath, archive.Uncompressed, filter)
1420
+	return archive.TarFilter(basePath, &archive.TarOptions{
1421
+		Compression: archive.Uncompressed,
1422
+		Includes:    filter,
1423
+		Recursive:   true,
1424
+	})
1421 1425
 }
1422 1426
 
1423 1427
 // Returns true if the container exposes a certain port
1424 1428
new file mode 100644
... ...
@@ -0,0 +1,170 @@
0
+package main
1
+
2
+import (
3
+	"flag"
4
+	"fmt"
5
+	"github.com/dotcloud/docker/graphdriver/devmapper"
6
+	"os"
7
+	"path"
8
+	"sort"
9
+	"strconv"
10
+	"strings"
11
+)
12
+
13
+func usage() {
14
+	fmt.Fprintf(os.Stderr, "Usage: %s <flags>  [status] | [list] | [device id]  | [resize new-pool-size] | [snap new-id base-id] | [remove id] | [mount id mountpoint]\n", os.Args[0])
15
+	flag.PrintDefaults()
16
+	os.Exit(1)
17
+}
18
+
19
+func byteSizeFromString(arg string) (int64, error) {
20
+	digits := ""
21
+	rest := ""
22
+	last := strings.LastIndexAny(arg, "0123456789")
23
+	if last >= 0 {
24
+		digits = arg[:last+1]
25
+		rest = arg[last+1:]
26
+	}
27
+
28
+	val, err := strconv.ParseInt(digits, 10, 64)
29
+	if err != nil {
30
+		return val, err
31
+	}
32
+
33
+	rest = strings.ToLower(strings.TrimSpace(rest))
34
+
35
+	var multiplier int64 = 1
36
+	switch rest {
37
+	case "":
38
+		multiplier = 1
39
+	case "k", "kb":
40
+		multiplier = 1024
41
+	case "m", "mb":
42
+		multiplier = 1024 * 1024
43
+	case "g", "gb":
44
+		multiplier = 1024 * 1024 * 1024
45
+	case "t", "tb":
46
+		multiplier = 1024 * 1024 * 1024 * 1024
47
+	default:
48
+		return 0, fmt.Errorf("Unknown size unit: %s", rest)
49
+	}
50
+
51
+	return val * multiplier, nil
52
+}
53
+
54
+func main() {
55
+	root := flag.String("r", "/var/lib/docker", "Docker root dir")
56
+	flDebug := flag.Bool("D", false, "Debug mode")
57
+
58
+	flag.Parse()
59
+
60
+	if *flDebug {
61
+		os.Setenv("DEBUG", "1")
62
+	}
63
+
64
+	if flag.NArg() < 1 {
65
+		usage()
66
+	}
67
+
68
+	args := flag.Args()
69
+
70
+	home := path.Join(*root, "devicemapper")
71
+	devices, err := devmapper.NewDeviceSet(home, false)
72
+	if err != nil {
73
+		fmt.Println("Can't initialize device mapper: ", err)
74
+		os.Exit(1)
75
+	}
76
+
77
+	switch args[0] {
78
+	case "status":
79
+		status := devices.Status()
80
+		fmt.Printf("Pool name: %s\n", status.PoolName)
81
+		fmt.Printf("Data Loopback file: %s\n", status.DataLoopback)
82
+		fmt.Printf("Metadata Loopback file: %s\n", status.MetadataLoopback)
83
+		fmt.Printf("Sector size: %d\n", status.SectorSize)
84
+		fmt.Printf("Data use: %d of %d (%.1f %%)\n", status.Data.Used, status.Data.Total, 100.0*float64(status.Data.Used)/float64(status.Data.Total))
85
+		fmt.Printf("Metadata use: %d of %d (%.1f %%)\n", status.Metadata.Used, status.Metadata.Total, 100.0*float64(status.Metadata.Used)/float64(status.Metadata.Total))
86
+		break
87
+	case "list":
88
+		ids := devices.List()
89
+		sort.Strings(ids)
90
+		for _, id := range ids {
91
+			fmt.Println(id)
92
+		}
93
+		break
94
+	case "device":
95
+		if flag.NArg() < 2 {
96
+			usage()
97
+		}
98
+		status, err := devices.GetDeviceStatus(args[1])
99
+		if err != nil {
100
+			fmt.Println("Can't get device info: ", err)
101
+			os.Exit(1)
102
+		}
103
+		fmt.Printf("Id: %d\n", status.DeviceId)
104
+		fmt.Printf("Size: %d\n", status.Size)
105
+		fmt.Printf("Transaction Id: %d\n", status.TransactionId)
106
+		fmt.Printf("Size in Sectors: %d\n", status.SizeInSectors)
107
+		fmt.Printf("Mapped Sectors: %d\n", status.MappedSectors)
108
+		fmt.Printf("Highest Mapped Sector: %d\n", status.HighestMappedSector)
109
+		break
110
+	case "resize":
111
+		if flag.NArg() < 2 {
112
+			usage()
113
+		}
114
+
115
+		size, err := byteSizeFromString(args[1])
116
+		if err != nil {
117
+			fmt.Println("Invalid size: ", err)
118
+			os.Exit(1)
119
+		}
120
+
121
+		err = devices.ResizePool(size)
122
+		if err != nil {
123
+			fmt.Println("Error resizeing pool: ", err)
124
+			os.Exit(1)
125
+		}
126
+
127
+		break
128
+	case "snap":
129
+		if flag.NArg() < 3 {
130
+			usage()
131
+		}
132
+
133
+		err := devices.AddDevice(args[1], args[2])
134
+		if err != nil {
135
+			fmt.Println("Can't create snap device: ", err)
136
+			os.Exit(1)
137
+		}
138
+		break
139
+	case "remove":
140
+		if flag.NArg() < 2 {
141
+			usage()
142
+		}
143
+
144
+		err := devices.RemoveDevice(args[1])
145
+		if err != nil {
146
+			fmt.Println("Can't remove device: ", err)
147
+			os.Exit(1)
148
+		}
149
+		break
150
+	case "mount":
151
+		if flag.NArg() < 3 {
152
+			usage()
153
+		}
154
+
155
+		err := devices.MountDevice(args[1], args[2], false)
156
+		if err != nil {
157
+			fmt.Println("Can't create snap device: ", err)
158
+			os.Exit(1)
159
+		}
160
+		break
161
+	default:
162
+		fmt.Printf("Unknown command %s\n", args[0])
163
+		usage()
164
+
165
+		os.Exit(1)
166
+	}
167
+
168
+	return
169
+}
... ...
@@ -25,19 +25,20 @@ func main() {
25 25
 	}
26 26
 	// FIXME: Switch d and D ? (to be more sshd like)
27 27
 	flVersion := flag.Bool("v", false, "Print version information and quit")
28
-	flDaemon := flag.Bool("d", false, "Daemon mode")
29
-	flDebug := flag.Bool("D", false, "Debug mode")
28
+	flDaemon := flag.Bool("d", false, "Enable daemon mode")
29
+	flDebug := flag.Bool("D", false, "Enable debug mode")
30 30
 	flAutoRestart := flag.Bool("r", true, "Restart previously running containers")
31
-	bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge. Use 'none' to disable container networking")
32
-	pidfile := flag.String("p", "/var/run/docker.pid", "File containing process PID")
33
-	flRoot := flag.String("g", "/var/lib/docker", "Path to use as the root of the docker runtime.")
34
-	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS requests in the remote api.")
35
-	flDns := flag.String("dns", "", "Set custom dns servers")
31
+	bridgeName := flag.String("b", "", "Attach containers to a pre-existing network bridge; use 'none' to disable container networking")
32
+	pidfile := flag.String("p", "/var/run/docker.pid", "Path to use for daemon PID file")
33
+	flRoot := flag.String("g", "/var/lib/docker", "Path to use as the root of the docker runtime")
34
+	flEnableCors := flag.Bool("api-enable-cors", false, "Enable CORS headers in the remote API")
35
+	flDns := flag.String("dns", "", "Force docker to use specific DNS servers")
36 36
 	flHosts := utils.ListOpts{fmt.Sprintf("unix://%s", docker.DEFAULTUNIXSOCKET)}
37
-	flag.Var(&flHosts, "H", "tcp://host:port to bind/connect to or unix://path/to/socket to use")
38
-	flEnableIptables := flag.Bool("iptables", true, "Disable iptables within docker")
39
-	flDefaultIp := flag.String("ip", "0.0.0.0", "Default ip address to use when binding a containers ports")
37
+	flag.Var(&flHosts, "H", "Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise")
38
+	flEnableIptables := flag.Bool("iptables", true, "Disable docker's addition of iptables rules")
39
+	flDefaultIp := flag.String("ip", "0.0.0.0", "Default IP address to use when binding container ports")
40 40
 	flInterContainerComm := flag.Bool("icc", true, "Enable inter-container communication")
41
+	flGraphDriver := flag.String("s", "", "Force the docker runtime to use a specific storage driver")
41 42
 
42 43
 	flag.Parse()
43 44
 
... ...
@@ -82,6 +83,7 @@ func main() {
82 82
 		job.Setenv("BridgeIface", *bridgeName)
83 83
 		job.Setenv("DefaultIp", *flDefaultIp)
84 84
 		job.SetenvBool("InterContainerCommunication", *flInterContainerComm)
85
+		job.Setenv("GraphDriver", *flGraphDriver)
85 86
 		if err := job.Run(); err != nil {
86 87
 			log.Fatal(err)
87 88
 		}
... ...
@@ -9,7 +9,7 @@ run apt-get install -y python-setuptools make
9 9
 run easy_install pip
10 10
 #from docs/requirements.txt, but here to increase cacheability
11 11
 run pip install Sphinx==1.1.3
12
-run pip install sphinxcontrib-httpdomain==1.1.8
12
+run pip install sphinxcontrib-httpdomain==1.1.9
13 13
 add . /docs
14 14
 run cd /docs; make docs
15 15
 
... ...
@@ -18,6 +18,38 @@ To list available commands, either run ``docker`` with no parameters or execute
18 18
 
19 19
     ...
20 20
 
21
+.. _cli_daemon:
22
+
23
+``daemon``
24
+----------
25
+
26
+::
27
+
28
+    Usage of docker:
29
+      -D=false: Enable debug mode
30
+      -H=[unix:///var/run/docker.sock]: Multiple tcp://host:port or unix://path/to/socket to bind in daemon mode, single connection otherwise
31
+      -api-enable-cors=false: Enable CORS headers in the remote API
32
+      -b="": Attach containers to a pre-existing network bridge; use 'none' to disable container networking
33
+      -d=false: Enable daemon mode
34
+      -dns="": Force docker to use specific DNS servers
35
+      -g="/var/lib/docker": Path to use as the root of the docker runtime
36
+      -icc=true: Enable inter-container communication
37
+      -ip="0.0.0.0": Default IP address to use when binding container ports
38
+      -iptables=true: Disable docker's addition of iptables rules
39
+      -p="/var/run/docker.pid": Path to use for daemon PID file
40
+      -r=true: Restart previously running containers
41
+      -s="": Force the docker runtime to use a specific storage driver
42
+      -v=false: Print version information and quit
43
+
44
+The docker daemon is the persistent process that manages containers.  Docker uses the same binary for both the 
45
+daemon and client.  To run the daemon you provide the ``-d`` flag.
46
+
47
+To force docker to use devicemapper as the storage driver, use ``docker -d -s devicemapper``
48
+
49
+To set the dns server for all docker containers, use ``docker -d -dns 8.8.8.8``
50
+
51
+To run the daemon with debug output, use ``docker -d -D``
52
+
21 53
 .. _cli_attach:
22 54
 
23 55
 ``attach``
... ...
@@ -11,10 +11,10 @@ In short, Docker has the following kernel requirements:
11 11
 
12 12
 - Linux version 3.8 or above.
13 13
 
14
-- `AUFS support <http://aufs.sourceforge.net/>`_.
15
-
16 14
 - Cgroups and namespaces must be enabled.
17 15
 
16
+*Note: as of 0.7 docker no longer requires aufs. AUFS support is still available as an optional driver.*
17
+
18 18
 The officially supported kernel is the one recommended by the
19 19
 :ref:`ubuntu_linux` installation path. It is the one that most developers
20 20
 will use, and the one that receives the most attention from the core
... ...
@@ -58,17 +58,6 @@ detects something older than 3.8.
58 58
 See issue `#407 <https://github.com/dotcloud/docker/issues/407>`_ for details.
59 59
 
60 60
 
61
-AUFS support
62
-
63
-Docker currently relies on AUFS, an unioning filesystem.
64
-While AUFS is included in the kernels built by the Debian and Ubuntu
65
-distributions, is not part of the standard kernel. This means that if
66
-you decide to roll your own kernel, you will have to patch your
67
-kernel tree to add AUFS. The process is documented on
68
-`AUFS webpage <http://aufs.sourceforge.net/>`_.
69
-
70
-
71 61
 Cgroups and namespaces
72 62
 ----------------------
73 63
 
... ...
@@ -14,16 +14,11 @@ Ubuntu Linux
14 14
 
15 15
 .. include:: install_header.inc
16 16
 
17
-Right now, the officially supported distribution are:
17
+Docker is supported on the following versions of Ubuntu:
18 18
 
19 19
 - :ref:`ubuntu_precise`
20 20
 - :ref:`ubuntu_raring`
21 21
 
22
-Docker has the following dependencies
23
-
24
-* Linux kernel 3.8 (read more about :ref:`kernel`)
25
-* AUFS file system support (we are working on BTRFS support as an alternative)
26
-
27 22
 Please read :ref:`ufw`, if you plan to use `UFW (Uncomplicated
28 23
 Firewall) <https://help.ubuntu.com/community/UFW>`_
29 24
 
... ...
@@ -107,10 +102,13 @@ Ubuntu Raring 13.04 (64 bit)
107 107
 Dependencies
108 108
 ------------
109 109
 
110
-**AUFS filesystem support**
110
+**Optional AUFS filesystem support**
111 111
 
112 112
 Ubuntu Raring already comes with the 3.8 kernel, so we don't need to install it. However, not all systems
113
-have AUFS filesystem support enabled, so we need to install it.
113
+have AUFS filesystem support enabled. AUFS support is optional as of version 0.7, but it's still available as
114
+a driver and we recommend using it if you can.
115
+
116
+To make sure aufs is installed, run the following commands:
114 117
 
115 118
 .. code-block:: bash
116 119
 
117 120
deleted file mode 100644
... ...
@@ -1 +0,0 @@
1
-Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
2 1
deleted file mode 100644
... ...
@@ -1,473 +0,0 @@
1
-package gograph
2
-
3
-import (
4
-	"database/sql"
5
-	"fmt"
6
-	"path"
7
-	"sync"
8
-)
9
-
10
-const (
11
-	createEntityTable = `
12
-    CREATE TABLE IF NOT EXISTS entity (
13
-        id text NOT NULL PRIMARY KEY
14
-    );`
15
-
16
-	createEdgeTable = `
17
-    CREATE TABLE IF NOT EXISTS edge (
18
-        "entity_id" text NOT NULL,
19
-        "parent_id" text NULL,
20
-        "name" text NOT NULL,
21
-        CONSTRAINT "parent_fk" FOREIGN KEY ("parent_id") REFERENCES "entity" ("id"),
22
-        CONSTRAINT "entity_fk" FOREIGN KEY ("entity_id") REFERENCES "entity" ("id")
23
-        );
24
-    `
25
-
26
-	createEdgeIndices = `
27
-    CREATE UNIQUE INDEX IF NOT EXISTS "name_parent_ix" ON "edge" (parent_id, name);
28
-    `
29
-)
30
-
31
-// Entity with a unique id
32
-type Entity struct {
33
-	id string
34
-}
35
-
36
-// An Edge connects two entities together
37
-type Edge struct {
38
-	EntityID string
39
-	Name     string
40
-	ParentID string
41
-}
42
-
43
-type Entities map[string]*Entity
44
-type Edges []*Edge
45
-
46
-type WalkFunc func(fullPath string, entity *Entity) error
47
-
48
-// Graph database for storing entities and their relationships
49
-type Database struct {
50
-	conn *sql.DB
51
-	mux  sync.RWMutex
52
-}
53
-
54
-// Create a new graph database initialized with a root entity
55
-func NewDatabase(conn *sql.DB, init bool) (*Database, error) {
56
-	if conn == nil {
57
-		return nil, fmt.Errorf("Database connection cannot be nil")
58
-	}
59
-	db := &Database{conn: conn}
60
-
61
-	if init {
62
-		if _, err := conn.Exec(createEntityTable); err != nil {
63
-			return nil, err
64
-		}
65
-		if _, err := conn.Exec(createEdgeTable); err != nil {
66
-			return nil, err
67
-		}
68
-		if _, err := conn.Exec(createEdgeIndices); err != nil {
69
-			return nil, err
70
-		}
71
-
72
-		rollback := func() {
73
-			conn.Exec("ROLLBACK")
74
-		}
75
-
76
-		// Create root entities
77
-		if _, err := conn.Exec("BEGIN"); err != nil {
78
-			return nil, err
79
-		}
80
-		if _, err := conn.Exec("INSERT INTO entity (id) VALUES (?);", "0"); err != nil {
81
-			rollback()
82
-			return nil, err
83
-		}
84
-
85
-		if _, err := conn.Exec("INSERT INTO edge (entity_id, name) VALUES(?,?);", "0", "/"); err != nil {
86
-			rollback()
87
-			return nil, err
88
-		}
89
-
90
-		if _, err := conn.Exec("COMMIT"); err != nil {
91
-			return nil, err
92
-		}
93
-	}
94
-	return db, nil
95
-}
96
-
97
-// Close the underlying connection to the database
98
-func (db *Database) Close() error {
99
-	return db.conn.Close()
100
-}
101
-
102
-// Set the entity id for a given path
103
-func (db *Database) Set(fullPath, id string) (*Entity, error) {
104
-	db.mux.Lock()
105
-	defer db.mux.Unlock()
106
-
107
-	rollback := func() {
108
-		db.conn.Exec("ROLLBACK")
109
-	}
110
-	if _, err := db.conn.Exec("BEGIN EXCLUSIVE"); err != nil {
111
-		return nil, err
112
-	}
113
-	var entityId string
114
-	if err := db.conn.QueryRow("SELECT id FROM entity WHERE id = ?;", id).Scan(&entityId); err != nil {
115
-		if err == sql.ErrNoRows {
116
-			if _, err := db.conn.Exec("INSERT INTO entity (id) VALUES(?);", id); err != nil {
117
-				rollback()
118
-				return nil, err
119
-			}
120
-		} else {
121
-			rollback()
122
-			return nil, err
123
-		}
124
-	}
125
-	e := &Entity{id}
126
-
127
-	parentPath, name := splitPath(fullPath)
128
-	if err := db.setEdge(parentPath, name, e); err != nil {
129
-		rollback()
130
-		return nil, err
131
-	}
132
-
133
-	if _, err := db.conn.Exec("COMMIT"); err != nil {
134
-		return nil, err
135
-	}
136
-	return e, nil
137
-}
138
-
139
-// Return true if a name already exists in the database
140
-func (db *Database) Exists(name string) bool {
141
-	db.mux.RLock()
142
-	defer db.mux.RUnlock()
143
-
144
-	e, err := db.get(name)
145
-	if err != nil {
146
-		return false
147
-	}
148
-	return e != nil
149
-}
150
-
151
-func (db *Database) setEdge(parentPath, name string, e *Entity) error {
152
-	parent, err := db.get(parentPath)
153
-	if err != nil {
154
-		return err
155
-	}
156
-	if parent.id == e.id {
157
-		return fmt.Errorf("Cannot set self as child")
158
-	}
159
-
160
-	if _, err := db.conn.Exec("INSERT INTO edge (parent_id, name, entity_id) VALUES (?,?,?);", parent.id, name, e.id); err != nil {
161
-		return err
162
-	}
163
-	return nil
164
-}
165
-
166
-// Return the root "/" entity for the database
167
-func (db *Database) RootEntity() *Entity {
168
-	return &Entity{
169
-		id: "0",
170
-	}
171
-}
172
-
173
-// Return the entity for a given path
174
-func (db *Database) Get(name string) *Entity {
175
-	db.mux.RLock()
176
-	defer db.mux.RUnlock()
177
-
178
-	e, err := db.get(name)
179
-	if err != nil {
180
-		return nil
181
-	}
182
-	return e
183
-}
184
-
185
-func (db *Database) get(name string) (*Entity, error) {
186
-	e := db.RootEntity()
187
-	// We always know the root name so return it if
188
-	// it is requested
189
-	if name == "/" {
190
-		return e, nil
191
-	}
192
-
193
-	parts := split(name)
194
-	for i := 1; i < len(parts); i++ {
195
-		p := parts[i]
196
-		if p == "" {
197
-			continue
198
-		}
199
-
200
-		next := db.child(e, p)
201
-		if next == nil {
202
-			return nil, fmt.Errorf("Cannot find child for %s", name)
203
-		}
204
-		e = next
205
-	}
206
-	return e, nil
207
-
208
-}
209
-
210
-// List all entities by from the name
211
-// The key will be the full path of the entity
212
-func (db *Database) List(name string, depth int) Entities {
213
-	db.mux.RLock()
214
-	defer db.mux.RUnlock()
215
-
216
-	out := Entities{}
217
-	e, err := db.get(name)
218
-	if err != nil {
219
-		return out
220
-	}
221
-
222
-	children, err := db.children(e, name, depth, nil)
223
-	if err != nil {
224
-		return out
225
-	}
226
-
227
-	for _, c := range children {
228
-		out[c.FullPath] = c.Entity
229
-	}
230
-	return out
231
-}
232
-
233
-// Walk through the child graph of an entity, calling walkFunc for each child entity.
234
-// It is safe for walkFunc to call graph functions.
235
-func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
236
-	children, err := db.Children(name, depth)
237
-	if err != nil {
238
-		return err
239
-	}
240
-
241
-	// Note: the database lock must not be held while calling walkFunc
242
-	for _, c := range children {
243
-		if err := walkFunc(c.FullPath, c.Entity); err != nil {
244
-			return err
245
-		}
246
-	}
247
-	return nil
248
-}
249
-
250
-// Return the children of the specified entity
251
-func (db *Database) Children(name string, depth int) ([]WalkMeta, error) {
252
-	db.mux.RLock()
253
-	defer db.mux.RUnlock()
254
-
255
-	e, err := db.get(name)
256
-	if err != nil {
257
-		return nil, err
258
-	}
259
-
260
-	return db.children(e, name, depth, nil)
261
-}
262
-
263
-// Return the refrence count for a specified id
264
-func (db *Database) Refs(id string) int {
265
-	db.mux.RLock()
266
-	defer db.mux.RUnlock()
267
-
268
-	var count int
269
-	if err := db.conn.QueryRow("SELECT COUNT(*) FROM edge WHERE entity_id = ?;", id).Scan(&count); err != nil {
270
-		return 0
271
-	}
272
-	return count
273
-}
274
-
275
-// Return all the id's path references
276
-func (db *Database) RefPaths(id string) Edges {
277
-	db.mux.RLock()
278
-	defer db.mux.RUnlock()
279
-
280
-	refs := Edges{}
281
-
282
-	rows, err := db.conn.Query("SELECT name, parent_id FROM edge WHERE entity_id = ?;", id)
283
-	if err != nil {
284
-		return refs
285
-	}
286
-	defer rows.Close()
287
-
288
-	for rows.Next() {
289
-		var name string
290
-		var parentId string
291
-		if err := rows.Scan(&name, &parentId); err != nil {
292
-			return refs
293
-		}
294
-		refs = append(refs, &Edge{
295
-			EntityID: id,
296
-			Name:     name,
297
-			ParentID: parentId,
298
-		})
299
-	}
300
-	return refs
301
-}
302
-
303
-// Delete the reference to an entity at a given path
304
-func (db *Database) Delete(name string) error {
305
-	db.mux.Lock()
306
-	defer db.mux.Unlock()
307
-
308
-	if name == "/" {
309
-		return fmt.Errorf("Cannot delete root entity")
310
-	}
311
-
312
-	parentPath, n := splitPath(name)
313
-	parent, err := db.get(parentPath)
314
-	if err != nil {
315
-		return err
316
-	}
317
-
318
-	if _, err := db.conn.Exec("DELETE FROM edge WHERE parent_id = ? AND name = ?;", parent.id, n); err != nil {
319
-		return err
320
-	}
321
-	return nil
322
-}
323
-
324
-// Remove the entity with the specified id
325
-// Walk the graph to make sure all references to the entity
326
-// are removed and return the number of references removed
327
-func (db *Database) Purge(id string) (int, error) {
328
-	db.mux.Lock()
329
-	defer db.mux.Unlock()
330
-
331
-	rollback := func() {
332
-		db.conn.Exec("ROLLBACK")
333
-	}
334
-
335
-	if _, err := db.conn.Exec("BEGIN"); err != nil {
336
-		return -1, err
337
-	}
338
-
339
-	// Delete all edges
340
-	rows, err := db.conn.Exec("DELETE FROM edge WHERE entity_id = ?;", id)
341
-	if err != nil {
342
-		rollback()
343
-		return -1, err
344
-	}
345
-
346
-	changes, err := rows.RowsAffected()
347
-	if err != nil {
348
-		return -1, err
349
-	}
350
-
351
-	// Delete entity
352
-	if _, err := db.conn.Exec("DELETE FROM entity where id = ?;", id); err != nil {
353
-		rollback()
354
-		return -1, err
355
-	}
356
-
357
-	if _, err := db.conn.Exec("COMMIT"); err != nil {
358
-		return -1, err
359
-	}
360
-	return int(changes), nil
361
-}
362
-
363
-// Rename an edge for a given path
364
-func (db *Database) Rename(currentName, newName string) error {
365
-	db.mux.Lock()
366
-	defer db.mux.Unlock()
367
-
368
-	parentPath, name := splitPath(currentName)
369
-	newParentPath, newEdgeName := splitPath(newName)
370
-
371
-	if parentPath != newParentPath {
372
-		return fmt.Errorf("Cannot rename when root paths do not match %s != %s", parentPath, newParentPath)
373
-	}
374
-
375
-	parent, err := db.get(parentPath)
376
-	if err != nil {
377
-		return err
378
-	}
379
-
380
-	rows, err := db.conn.Exec("UPDATE edge SET name = ? WHERE parent_id = ? AND name = ?;", newEdgeName, parent.id, name)
381
-	if err != nil {
382
-		return err
383
-	}
384
-	i, err := rows.RowsAffected()
385
-	if err != nil {
386
-		return err
387
-	}
388
-	if i == 0 {
389
-		return fmt.Errorf("Cannot locate edge for %s %s", parent.id, name)
390
-	}
391
-	return nil
392
-}
393
-
394
-type WalkMeta struct {
395
-	Parent   *Entity
396
-	Entity   *Entity
397
-	FullPath string
398
-	Edge     *Edge
399
-}
400
-
401
-func (db *Database) children(e *Entity, name string, depth int, entities []WalkMeta) ([]WalkMeta, error) {
402
-	if e == nil {
403
-		return entities, nil
404
-	}
405
-
406
-	rows, err := db.conn.Query("SELECT entity_id, name FROM edge where parent_id = ?;", e.id)
407
-	if err != nil {
408
-		return nil, err
409
-	}
410
-	defer rows.Close()
411
-
412
-	for rows.Next() {
413
-		var entityId, entityName string
414
-		if err := rows.Scan(&entityId, &entityName); err != nil {
415
-			return nil, err
416
-		}
417
-		child := &Entity{entityId}
418
-		edge := &Edge{
419
-			ParentID: e.id,
420
-			Name:     entityName,
421
-			EntityID: child.id,
422
-		}
423
-
424
-		meta := WalkMeta{
425
-			Parent:   e,
426
-			Entity:   child,
427
-			FullPath: path.Join(name, edge.Name),
428
-			Edge:     edge,
429
-		}
430
-
431
-		entities = append(entities, meta)
432
-
433
-		if depth != 0 {
434
-			nDepth := depth
435
-			if depth != -1 {
436
-				nDepth -= 1
437
-			}
438
-			entities, err = db.children(child, meta.FullPath, nDepth, entities)
439
-			if err != nil {
440
-				return nil, err
441
-			}
442
-		}
443
-	}
444
-
445
-	return entities, nil
446
-}
447
-
448
-// Return the entity based on the parent path and name
449
-func (db *Database) child(parent *Entity, name string) *Entity {
450
-	var id string
451
-	if err := db.conn.QueryRow("SELECT entity_id FROM edge WHERE parent_id = ? AND name = ?;", parent.id, name).Scan(&id); err != nil {
452
-		return nil
453
-	}
454
-	return &Entity{id}
455
-}
456
-
457
-// Return the id used to reference this entity
458
-func (e *Entity) ID() string {
459
-	return e.id
460
-}
461
-
462
-// Return the paths sorted by depth
463
-func (e Entities) Paths() []string {
464
-	out := make([]string, len(e))
465
-	var i int
466
-	for k := range e {
467
-		out[i] = k
468
-		i++
469
-	}
470
-	sortByDepth(out)
471
-
472
-	return out
473
-}
474 1
deleted file mode 100644
... ...
@@ -1,540 +0,0 @@
1
-package gograph
2
-
3
-import (
4
-	_ "code.google.com/p/gosqlite/sqlite3"
5
-	"database/sql"
6
-	"fmt"
7
-	"os"
8
-	"path"
9
-	"strconv"
10
-	"testing"
11
-)
12
-
13
-func newTestDb(t *testing.T) (*Database, string) {
14
-	p := path.Join(os.TempDir(), "sqlite.db")
15
-	conn, err := sql.Open("sqlite3", p)
16
-	db, err := NewDatabase(conn, true)
17
-	if err != nil {
18
-		t.Fatal(err)
19
-	}
20
-	return db, p
21
-}
22
-
23
-func destroyTestDb(dbPath string) {
24
-	os.Remove(dbPath)
25
-}
26
-
27
-func TestNewDatabase(t *testing.T) {
28
-	db, dbpath := newTestDb(t)
29
-	if db == nil {
30
-		t.Fatal("Database should not be nil")
31
-	}
32
-	db.Close()
33
-	defer destroyTestDb(dbpath)
34
-}
35
-
36
-func TestCreateRootEnity(t *testing.T) {
37
-	db, dbpath := newTestDb(t)
38
-	defer destroyTestDb(dbpath)
39
-	root := db.RootEntity()
40
-	if root == nil {
41
-		t.Fatal("Root entity should not be nil")
42
-	}
43
-}
44
-
45
-func TestGetRootEntity(t *testing.T) {
46
-	db, dbpath := newTestDb(t)
47
-	defer destroyTestDb(dbpath)
48
-
49
-	e := db.Get("/")
50
-	if e == nil {
51
-		t.Fatal("Entity should not be nil")
52
-	}
53
-	if e.ID() != "0" {
54
-		t.Fatalf("Enity id should be 0, got %s", e.ID())
55
-	}
56
-}
57
-
58
-func TestSetEntityWithDifferentName(t *testing.T) {
59
-	db, dbpath := newTestDb(t)
60
-	defer destroyTestDb(dbpath)
61
-
62
-	db.Set("/test", "1")
63
-	if _, err := db.Set("/other", "1"); err != nil {
64
-		t.Fatal(err)
65
-	}
66
-}
67
-
68
-func TestSetDuplicateEntity(t *testing.T) {
69
-	db, dbpath := newTestDb(t)
70
-	defer destroyTestDb(dbpath)
71
-
72
-	if _, err := db.Set("/foo", "42"); err != nil {
73
-		t.Fatal(err)
74
-	}
75
-	if _, err := db.Set("/foo", "43"); err == nil {
76
-		t.Fatalf("Creating an entry with a duplciate path did not cause an error")
77
-	}
78
-}
79
-
80
-func TestCreateChild(t *testing.T) {
81
-	db, dbpath := newTestDb(t)
82
-	defer destroyTestDb(dbpath)
83
-
84
-	child, err := db.Set("/db", "1")
85
-	if err != nil {
86
-		t.Fatal(err)
87
-	}
88
-	if child == nil {
89
-		t.Fatal("Child should not be nil")
90
-	}
91
-	if child.ID() != "1" {
92
-		t.Fail()
93
-	}
94
-}
95
-
96
-func TestListAllRootChildren(t *testing.T) {
97
-	db, dbpath := newTestDb(t)
98
-	defer destroyTestDb(dbpath)
99
-
100
-	for i := 1; i < 6; i++ {
101
-		a := strconv.Itoa(i)
102
-		if _, err := db.Set("/"+a, a); err != nil {
103
-			t.Fatal(err)
104
-		}
105
-	}
106
-	entries := db.List("/", -1)
107
-	if len(entries) != 5 {
108
-		t.Fatalf("Expect 5 entries for / got %d", len(entries))
109
-	}
110
-}
111
-
112
-func TestListAllSubChildren(t *testing.T) {
113
-	db, dbpath := newTestDb(t)
114
-	defer destroyTestDb(dbpath)
115
-
116
-	_, err := db.Set("/webapp", "1")
117
-	if err != nil {
118
-		t.Fatal(err)
119
-	}
120
-	child2, err := db.Set("/db", "2")
121
-	if err != nil {
122
-		t.Fatal(err)
123
-	}
124
-	child4, err := db.Set("/logs", "4")
125
-	if err != nil {
126
-		t.Fatal(err)
127
-	}
128
-	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
129
-		t.Fatal(err)
130
-	}
131
-
132
-	child3, err := db.Set("/sentry", "3")
133
-	if err != nil {
134
-		t.Fatal(err)
135
-	}
136
-	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
137
-		t.Fatal(err)
138
-	}
139
-	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
140
-		t.Fatal(err)
141
-	}
142
-
143
-	entries := db.List("/webapp", 1)
144
-	if len(entries) != 3 {
145
-		t.Fatalf("Expect 3 entries for / got %d", len(entries))
146
-	}
147
-
148
-	entries = db.List("/webapp", 0)
149
-	if len(entries) != 2 {
150
-		t.Fatalf("Expect 2 entries for / got %d", len(entries))
151
-	}
152
-}
153
-
154
-func TestAddSelfAsChild(t *testing.T) {
155
-	db, dbpath := newTestDb(t)
156
-	defer destroyTestDb(dbpath)
157
-
158
-	child, err := db.Set("/test", "1")
159
-	if err != nil {
160
-		t.Fatal(err)
161
-	}
162
-	if _, err := db.Set("/test/other", child.ID()); err == nil {
163
-		t.Fatal("Error should not be nil")
164
-	}
165
-}
166
-
167
-func TestAddChildToNonExistantRoot(t *testing.T) {
168
-	db, dbpath := newTestDb(t)
169
-	defer destroyTestDb(dbpath)
170
-
171
-	if _, err := db.Set("/myapp", "1"); err != nil {
172
-		t.Fatal(err)
173
-	}
174
-
175
-	if _, err := db.Set("/myapp/proxy/db", "2"); err == nil {
176
-		t.Fatal("Error should not be nil")
177
-	}
178
-}
179
-
180
-func TestWalkAll(t *testing.T) {
181
-	db, dbpath := newTestDb(t)
182
-	defer destroyTestDb(dbpath)
183
-	_, err := db.Set("/webapp", "1")
184
-	if err != nil {
185
-		t.Fatal(err)
186
-	}
187
-	child2, err := db.Set("/db", "2")
188
-	if err != nil {
189
-		t.Fatal(err)
190
-	}
191
-	child4, err := db.Set("/db/logs", "4")
192
-	if err != nil {
193
-		t.Fatal(err)
194
-	}
195
-	if _, err := db.Set("/webapp/logs", child4.ID()); err != nil {
196
-		t.Fatal(err)
197
-	}
198
-
199
-	child3, err := db.Set("/sentry", "3")
200
-	if err != nil {
201
-		t.Fatal(err)
202
-	}
203
-	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
204
-		t.Fatal(err)
205
-	}
206
-	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
207
-		t.Fatal(err)
208
-	}
209
-
210
-	child5, err := db.Set("/gograph", "5")
211
-	if err != nil {
212
-		t.Fatal(err)
213
-	}
214
-	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
215
-		t.Fatal(err)
216
-	}
217
-
218
-	if err := db.Walk("/", func(p string, e *Entity) error {
219
-		t.Logf("Path: %s Entity: %s", p, e.ID())
220
-		return nil
221
-	}, -1); err != nil {
222
-		t.Fatal(err)
223
-	}
224
-}
225
-
226
-func TestGetEntityByPath(t *testing.T) {
227
-	db, dbpath := newTestDb(t)
228
-	defer destroyTestDb(dbpath)
229
-	_, err := db.Set("/webapp", "1")
230
-	if err != nil {
231
-		t.Fatal(err)
232
-	}
233
-	child2, err := db.Set("/db", "2")
234
-	if err != nil {
235
-		t.Fatal(err)
236
-	}
237
-	child4, err := db.Set("/logs", "4")
238
-	if err != nil {
239
-		t.Fatal(err)
240
-	}
241
-	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
242
-		t.Fatal(err)
243
-	}
244
-
245
-	child3, err := db.Set("/sentry", "3")
246
-	if err != nil {
247
-		t.Fatal(err)
248
-	}
249
-	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
250
-		t.Fatal(err)
251
-	}
252
-	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
253
-		t.Fatal(err)
254
-	}
255
-
256
-	child5, err := db.Set("/gograph", "5")
257
-	if err != nil {
258
-		t.Fatal(err)
259
-	}
260
-	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
261
-		t.Fatal(err)
262
-	}
263
-
264
-	entity := db.Get("/webapp/db/logs")
265
-	if entity == nil {
266
-		t.Fatal("Entity should not be nil")
267
-	}
268
-	if entity.ID() != "4" {
269
-		t.Fatalf("Expected to get entity with id 4, got %s", entity.ID())
270
-	}
271
-}
272
-
273
-func TestEnitiesPaths(t *testing.T) {
274
-	db, dbpath := newTestDb(t)
275
-	defer destroyTestDb(dbpath)
276
-	_, err := db.Set("/webapp", "1")
277
-	if err != nil {
278
-		t.Fatal(err)
279
-	}
280
-	child2, err := db.Set("/db", "2")
281
-	if err != nil {
282
-		t.Fatal(err)
283
-	}
284
-	child4, err := db.Set("/logs", "4")
285
-	if err != nil {
286
-		t.Fatal(err)
287
-	}
288
-	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
289
-		t.Fatal(err)
290
-	}
291
-
292
-	child3, err := db.Set("/sentry", "3")
293
-	if err != nil {
294
-		t.Fatal(err)
295
-	}
296
-	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
297
-		t.Fatal(err)
298
-	}
299
-	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
300
-		t.Fatal(err)
301
-	}
302
-
303
-	child5, err := db.Set("/gograph", "5")
304
-	if err != nil {
305
-		t.Fatal(err)
306
-	}
307
-	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
308
-		t.Fatal(err)
309
-	}
310
-
311
-	out := db.List("/", -1)
312
-	for _, p := range out.Paths() {
313
-		t.Log(p)
314
-	}
315
-}
316
-
317
-func TestDeleteRootEntity(t *testing.T) {
318
-	db, dbpath := newTestDb(t)
319
-	defer destroyTestDb(dbpath)
320
-
321
-	if err := db.Delete("/"); err == nil {
322
-		t.Fatal("Error should not be nil")
323
-	}
324
-}
325
-
326
-func TestDeleteEntity(t *testing.T) {
327
-	db, dbpath := newTestDb(t)
328
-	defer destroyTestDb(dbpath)
329
-	_, err := db.Set("/webapp", "1")
330
-	if err != nil {
331
-		t.Fatal(err)
332
-	}
333
-	child2, err := db.Set("/db", "2")
334
-	if err != nil {
335
-		t.Fatal(err)
336
-	}
337
-	child4, err := db.Set("/logs", "4")
338
-	if err != nil {
339
-		t.Fatal(err)
340
-	}
341
-	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
342
-		t.Fatal(err)
343
-	}
344
-
345
-	child3, err := db.Set("/sentry", "3")
346
-	if err != nil {
347
-		t.Fatal(err)
348
-	}
349
-	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
350
-		t.Fatal(err)
351
-	}
352
-	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
353
-		t.Fatal(err)
354
-	}
355
-
356
-	child5, err := db.Set("/gograph", "5")
357
-	if err != nil {
358
-		t.Fatal(err)
359
-	}
360
-	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
361
-		t.Fatal(err)
362
-	}
363
-
364
-	if err := db.Delete("/webapp/sentry"); err != nil {
365
-		t.Fatal(err)
366
-	}
367
-	entity := db.Get("/webapp/sentry")
368
-	if entity != nil {
369
-		t.Fatal("Entity /webapp/sentry should be nil")
370
-	}
371
-}
372
-
373
-func TestCountRefs(t *testing.T) {
374
-	db, dbpath := newTestDb(t)
375
-	defer destroyTestDb(dbpath)
376
-
377
-	db.Set("/webapp", "1")
378
-
379
-	if db.Refs("1") != 1 {
380
-		t.Fatal("Expect reference count to be 1")
381
-	}
382
-
383
-	db.Set("/db", "2")
384
-	db.Set("/webapp/db", "2")
385
-	if db.Refs("2") != 2 {
386
-		t.Fatal("Expect reference count to be 2")
387
-	}
388
-}
389
-
390
-func TestPurgeId(t *testing.T) {
391
-	db, dbpath := newTestDb(t)
392
-	defer destroyTestDb(dbpath)
393
-
394
-	db.Set("/webapp", "1")
395
-
396
-	if db.Refs("1") != 1 {
397
-		t.Fatal("Expect reference count to be 1")
398
-	}
399
-
400
-	db.Set("/db", "2")
401
-	db.Set("/webapp/db", "2")
402
-
403
-	count, err := db.Purge("2")
404
-	if err != nil {
405
-		t.Fatal(err)
406
-	}
407
-	if count != 2 {
408
-		t.Fatal("Expected 2 references to be removed")
409
-	}
410
-}
411
-
412
-func TestRename(t *testing.T) {
413
-	db, dbpath := newTestDb(t)
414
-	defer destroyTestDb(dbpath)
415
-
416
-	db.Set("/webapp", "1")
417
-
418
-	if db.Refs("1") != 1 {
419
-		t.Fatal("Expect reference count to be 1")
420
-	}
421
-
422
-	db.Set("/db", "2")
423
-	db.Set("/webapp/db", "2")
424
-
425
-	if db.Get("/webapp/db") == nil {
426
-		t.Fatal("Cannot find entity at path /webapp/db")
427
-	}
428
-
429
-	if err := db.Rename("/webapp/db", "/webapp/newdb"); err != nil {
430
-		t.Fatal(err)
431
-	}
432
-	if db.Get("/webapp/db") != nil {
433
-		t.Fatal("Entity should not exist at /webapp/db")
434
-	}
435
-	if db.Get("/webapp/newdb") == nil {
436
-		t.Fatal("Cannot find entity at path /webapp/newdb")
437
-	}
438
-
439
-}
440
-
441
-func TestCreateMultipleNames(t *testing.T) {
442
-	db, dbpath := newTestDb(t)
443
-	defer destroyTestDb(dbpath)
444
-
445
-	db.Set("/db", "1")
446
-	if _, err := db.Set("/myapp", "1"); err != nil {
447
-		t.Fatal(err)
448
-	}
449
-
450
-	db.Walk("/", func(p string, e *Entity) error {
451
-		t.Logf("%s\n", p)
452
-		return nil
453
-	}, -1)
454
-}
455
-
456
-func TestRefPaths(t *testing.T) {
457
-	db, dbpath := newTestDb(t)
458
-	defer destroyTestDb(dbpath)
459
-
460
-	db.Set("/webapp", "1")
461
-
462
-	db.Set("/db", "2")
463
-	db.Set("/webapp/db", "2")
464
-
465
-	refs := db.RefPaths("2")
466
-	if len(refs) != 2 {
467
-		t.Fatalf("Expected reference count to be 2, got %d", len(refs))
468
-	}
469
-}
470
-
471
-func TestExistsTrue(t *testing.T) {
472
-	db, dbpath := newTestDb(t)
473
-	defer destroyTestDb(dbpath)
474
-
475
-	db.Set("/testing", "1")
476
-
477
-	if !db.Exists("/testing") {
478
-		t.Fatalf("/tesing should exist")
479
-	}
480
-}
481
-
482
-func TestExistsFalse(t *testing.T) {
483
-	db, dbpath := newTestDb(t)
484
-	defer destroyTestDb(dbpath)
485
-
486
-	db.Set("/toerhe", "1")
487
-
488
-	if db.Exists("/testing") {
489
-		t.Fatalf("/tesing should not exist")
490
-	}
491
-
492
-}
493
-
494
-func TestGetNameWithTrailingSlash(t *testing.T) {
495
-	db, dbpath := newTestDb(t)
496
-	defer destroyTestDb(dbpath)
497
-
498
-	db.Set("/todo", "1")
499
-
500
-	e := db.Get("/todo/")
501
-	if e == nil {
502
-		t.Fatalf("Entity should not be nil")
503
-	}
504
-}
505
-
506
-func TestConcurrentWrites(t *testing.T) {
507
-	db, dbpath := newTestDb(t)
508
-	defer destroyTestDb(dbpath)
509
-
510
-	errs := make(chan error, 2)
511
-
512
-	save := func(name string, id string) {
513
-		if _, err := db.Set(fmt.Sprintf("/%s", name), id); err != nil {
514
-			errs <- err
515
-		}
516
-		errs <- nil
517
-	}
518
-	purge := func(id string) {
519
-		if _, err := db.Purge(id); err != nil {
520
-			errs <- err
521
-		}
522
-		errs <- nil
523
-	}
524
-
525
-	save("/1", "1")
526
-
527
-	go purge("1")
528
-	go save("/2", "2")
529
-
530
-	any := false
531
-	for i := 0; i < 2; i++ {
532
-		if err := <-errs; err != nil {
533
-			any = true
534
-			t.Log(err)
535
-		}
536
-	}
537
-	if any {
538
-		t.Fatal()
539
-	}
540
-}
541 1
deleted file mode 100644
... ...
@@ -1,27 +0,0 @@
1
-package gograph
2
-
3
-import "sort"
4
-
5
-type pathSorter struct {
6
-	paths []string
7
-	by    func(i, j string) bool
8
-}
9
-
10
-func sortByDepth(paths []string) {
11
-	s := &pathSorter{paths, func(i, j string) bool {
12
-		return PathDepth(i) > PathDepth(j)
13
-	}}
14
-	sort.Sort(s)
15
-}
16
-
17
-func (s *pathSorter) Len() int {
18
-	return len(s.paths)
19
-}
20
-
21
-func (s *pathSorter) Swap(i, j int) {
22
-	s.paths[i], s.paths[j] = s.paths[j], s.paths[i]
23
-}
24
-
25
-func (s *pathSorter) Less(i, j int) bool {
26
-	return s.by(s.paths[i], s.paths[j])
27
-}
28 1
deleted file mode 100644
... ...
@@ -1,29 +0,0 @@
1
-package gograph
2
-
3
-import (
4
-	"testing"
5
-)
6
-
7
-func TestSort(t *testing.T) {
8
-	paths := []string{
9
-		"/",
10
-		"/myreallylongname",
11
-		"/app/db",
12
-	}
13
-
14
-	sortByDepth(paths)
15
-
16
-	if len(paths) != 3 {
17
-		t.Fatalf("Expected 3 parts got %d", len(paths))
18
-	}
19
-
20
-	if paths[0] != "/app/db" {
21
-		t.Fatalf("Expected /app/db got %s", paths[0])
22
-	}
23
-	if paths[1] != "/myreallylongname" {
24
-		t.Fatalf("Expected /myreallylongname got %s", paths[1])
25
-	}
26
-	if paths[2] != "/" {
27
-		t.Fatalf("Expected / got %s", paths[2])
28
-	}
29
-}
30 1
deleted file mode 100644
... ...
@@ -1,32 +0,0 @@
1
-package gograph
2
-
3
-import (
4
-	"path"
5
-	"strings"
6
-)
7
-
8
-// Split p on /
9
-func split(p string) []string {
10
-	return strings.Split(p, "/")
11
-}
12
-
13
-// Returns the depth or number of / in a given path
14
-func PathDepth(p string) int {
15
-	parts := split(p)
16
-	if len(parts) == 2 && parts[1] == "" {
17
-		return 1
18
-	}
19
-	return len(parts)
20
-}
21
-
22
-func splitPath(p string) (parent, name string) {
23
-	if p[0] != '/' {
24
-		p = "/" + p
25
-	}
26
-	parent, name = path.Split(p)
27
-	l := len(parent)
28
-	if parent[l-1] == '/' {
29
-		parent = parent[:l-1]
30
-	}
31
-	return
32
-}
... ...
@@ -3,6 +3,7 @@ package docker
3 3
 import (
4 4
 	"fmt"
5 5
 	"github.com/dotcloud/docker/archive"
6
+	"github.com/dotcloud/docker/graphdriver"
6 7
 	"github.com/dotcloud/docker/utils"
7 8
 	"io"
8 9
 	"io/ioutil"
... ...
@@ -10,6 +11,7 @@ import (
10 10
 	"path"
11 11
 	"path/filepath"
12 12
 	"strings"
13
+	"syscall"
13 14
 	"time"
14 15
 )
15 16
 
... ...
@@ -17,11 +19,12 @@ import (
17 17
 type Graph struct {
18 18
 	Root    string
19 19
 	idIndex *utils.TruncIndex
20
+	driver  graphdriver.Driver
20 21
 }
21 22
 
22 23
 // NewGraph instantiates a new graph at the given root path in the filesystem.
23 24
 // `root` will be created if it doesn't exist.
24
-func NewGraph(root string) (*Graph, error) {
25
+func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) {
25 26
 	abspath, err := filepath.Abs(root)
26 27
 	if err != nil {
27 28
 		return nil, err
... ...
@@ -30,9 +33,11 @@ func NewGraph(root string) (*Graph, error) {
30 30
 	if err := os.MkdirAll(root, 0700); err != nil && !os.IsExist(err) {
31 31
 		return nil, err
32 32
 	}
33
+
33 34
 	graph := &Graph{
34 35
 		Root:    abspath,
35 36
 		idIndex: utils.NewTruncIndex(),
37
+		driver:  driver,
36 38
 	}
37 39
 	if err := graph.restore(); err != nil {
38 40
 		return nil, err
... ...
@@ -47,7 +52,9 @@ func (graph *Graph) restore() error {
47 47
 	}
48 48
 	for _, v := range dir {
49 49
 		id := v.Name()
50
-		graph.idIndex.Add(id)
50
+		if graph.driver.Exists(id) {
51
+			graph.idIndex.Add(id)
52
+		}
51 53
 	}
52 54
 	return nil
53 55
 }
... ...
@@ -78,16 +85,22 @@ func (graph *Graph) Get(name string) (*Image, error) {
78 78
 	if err != nil {
79 79
 		return nil, err
80 80
 	}
81
+	// Check that the filesystem layer exists
82
+	rootfs, err := graph.driver.Get(img.ID)
83
+	if err != nil {
84
+		return nil, fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
85
+	}
81 86
 	if img.ID != id {
82 87
 		return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
83 88
 	}
84 89
 	img.graph = graph
85 90
 	if img.Size == 0 {
86
-		root, err := img.root()
91
+		size, err := utils.TreeSize(rootfs)
87 92
 		if err != nil {
88
-			return nil, err
93
+			return nil, fmt.Errorf("Error computing size of rootfs %s: %s", img.ID, err)
89 94
 		}
90
-		if err := StoreSize(img, root); err != nil {
95
+		img.Size = size
96
+		if err := img.SaveSize(graph.imageRoot(id)); err != nil {
91 97
 			return nil, err
92 98
 		}
93 99
 	}
... ...
@@ -126,19 +139,37 @@ func (graph *Graph) Register(jsonData []byte, layerData archive.Archive, img *Im
126 126
 	if graph.Exists(img.ID) {
127 127
 		return fmt.Errorf("Image %s already exists", img.ID)
128 128
 	}
129
+
130
+	// Ensure that the image root does not exist on the filesystem
131
+	// when it is not registered in the graph.
132
+	// This is common when you switch from one graph driver to another
133
+	if err := os.RemoveAll(graph.imageRoot(img.ID)); err != nil && !os.IsNotExist(err) {
134
+		return err
135
+	}
136
+
129 137
 	tmp, err := graph.Mktemp("")
130 138
 	defer os.RemoveAll(tmp)
131 139
 	if err != nil {
132 140
 		return fmt.Errorf("Mktemp failed: %s", err)
133 141
 	}
134
-	if err := StoreImage(img, jsonData, layerData, tmp); err != nil {
142
+
143
+	// Create root filesystem in the driver
144
+	if err := graph.driver.Create(img.ID, img.Parent); err != nil {
145
+		return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
146
+	}
147
+	// Mount the root filesystem so we can apply the diff/layer
148
+	rootfs, err := graph.driver.Get(img.ID)
149
+	if err != nil {
150
+		return fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
151
+	}
152
+	img.graph = graph
153
+	if err := StoreImage(img, jsonData, layerData, tmp, rootfs); err != nil {
135 154
 		return err
136 155
 	}
137 156
 	// Commit
138 157
 	if err := os.Rename(tmp, graph.imageRoot(img.ID)); err != nil {
139 158
 		return err
140 159
 	}
141
-	img.graph = graph
142 160
 	graph.idIndex.Add(img.ID)
143 161
 	return nil
144 162
 }
... ...
@@ -152,50 +183,33 @@ func (graph *Graph) TempLayerArchive(id string, compression archive.Compression,
152 152
 	if err != nil {
153 153
 		return nil, err
154 154
 	}
155
-	tmp, err := graph.tmp()
155
+	tmp, err := graph.Mktemp("")
156 156
 	if err != nil {
157 157
 		return nil, err
158 158
 	}
159
-	a, err := image.TarLayer(compression)
159
+	a, err := image.TarLayer()
160 160
 	if err != nil {
161 161
 		return nil, err
162 162
 	}
163
-	return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp.Root)
163
+	return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp)
164 164
 }
165 165
 
166 166
 // Mktemp creates a temporary sub-directory inside the graph's filesystem.
167 167
 func (graph *Graph) Mktemp(id string) (string, error) {
168
-	if id == "" {
169
-		id = GenerateID()
170
-	}
171
-	tmp, err := graph.tmp()
172
-	if err != nil {
173
-		return "", fmt.Errorf("Couldn't create temp: %s", err)
174
-	}
175
-	if tmp.Exists(id) {
176
-		return "", fmt.Errorf("Image %s already exists", id)
168
+	dir := path.Join(graph.Root, "_tmp", GenerateID())
169
+	if err := os.MkdirAll(dir, 0700); err != nil {
170
+		return "", err
177 171
 	}
178
-	return tmp.imageRoot(id), nil
172
+	return dir, nil
179 173
 }
180 174
 
181
-// getDockerInitLayer returns the path of a layer containing a mountpoint suitable
175
+// setupInitLayer populates a directory with mountpoints suitable
182 176
 // for bind-mounting dockerinit into the container. The mountpoint is simply an
183 177
 // empty file at /.dockerinit
184 178
 //
185 179
 // This extra layer is used by all containers as the top-most ro layer. It protects
186 180
 // the container from unwanted side-effects on the rw layer.
187
-func (graph *Graph) getDockerInitLayer() (string, error) {
188
-	tmp, err := graph.tmp()
189
-	if err != nil {
190
-		return "", err
191
-	}
192
-	initLayer := tmp.imageRoot("_dockerinit")
193
-	if err := os.Mkdir(initLayer, 0755); err != nil && !os.IsExist(err) {
194
-		// If directory already existed, keep going.
195
-		// For all other errors, abort.
196
-		return "", err
197
-	}
198
-
181
+func setupInitLayer(initLayer string) error {
199 182
 	for pth, typ := range map[string]string{
200 183
 		"/dev/pts":         "dir",
201 184
 		"/dev/shm":         "dir",
... ...
@@ -209,36 +223,38 @@ func (graph *Graph) getDockerInitLayer() (string, error) {
209 209
 		// "var/run": "dir",
210 210
 		// "var/lock": "dir",
211 211
 	} {
212
+		parts := strings.Split(pth, "/")
213
+		prev := "/"
214
+		for _, p := range parts[1:] {
215
+			prev = path.Join(prev, p)
216
+			syscall.Unlink(path.Join(initLayer, prev))
217
+		}
218
+
212 219
 		if _, err := os.Stat(path.Join(initLayer, pth)); err != nil {
213 220
 			if os.IsNotExist(err) {
214 221
 				switch typ {
215 222
 				case "dir":
216 223
 					if err := os.MkdirAll(path.Join(initLayer, pth), 0755); err != nil {
217
-						return "", err
224
+						return err
218 225
 					}
219 226
 				case "file":
220 227
 					if err := os.MkdirAll(path.Join(initLayer, path.Dir(pth)), 0755); err != nil {
221
-						return "", err
228
+						return err
222 229
 					}
223 230
 					f, err := os.OpenFile(path.Join(initLayer, pth), os.O_CREATE, 0755)
224 231
 					if err != nil {
225
-						return "", err
232
+						return err
226 233
 					}
227 234
 					f.Close()
228 235
 				}
229 236
 			} else {
230
-				return "", err
237
+				return err
231 238
 			}
232 239
 		}
233 240
 	}
234 241
 
235 242
 	// Layer is ready to use, if it wasn't before.
236
-	return initLayer, nil
237
-}
238
-
239
-func (graph *Graph) tmp() (*Graph, error) {
240
-	// Changed to _tmp from :tmp:, because it messed with ":" separators in aufs branch syntax...
241
-	return NewGraph(path.Join(graph.Root, "_tmp"))
243
+	return nil
242 244
 }
243 245
 
244 246
 // Check if given error is "not empty".
... ...
@@ -270,6 +286,9 @@ func (graph *Graph) Delete(name string) error {
270 270
 	if err != nil {
271 271
 		return err
272 272
 	}
273
+	// Remove rootfs data from the driver
274
+	graph.driver.Remove(id)
275
+	// Remove the trashed image directory
273 276
 	return os.RemoveAll(tmp)
274 277
 }
275 278
 
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"bytes"
6 6
 	"errors"
7 7
 	"github.com/dotcloud/docker/archive"
8
+	"github.com/dotcloud/docker/graphdriver"
8 9
 	"github.com/dotcloud/docker/utils"
9 10
 	"io"
10 11
 	"io/ioutil"
... ...
@@ -15,7 +16,7 @@ import (
15 15
 
16 16
 func TestInit(t *testing.T) {
17 17
 	graph := tempGraph(t)
18
-	defer os.RemoveAll(graph.Root)
18
+	defer nukeGraph(graph)
19 19
 	// Root should exist
20 20
 	if _, err := os.Stat(graph.Root); err != nil {
21 21
 		t.Fatal(err)
... ...
@@ -31,7 +32,7 @@ func TestInit(t *testing.T) {
31 31
 // Test that Register can be interrupted cleanly without side effects
32 32
 func TestInterruptedRegister(t *testing.T) {
33 33
 	graph := tempGraph(t)
34
-	defer os.RemoveAll(graph.Root)
34
+	defer nukeGraph(graph)
35 35
 	badArchive, w := io.Pipe() // Use a pipe reader as a fake archive which never yields data
36 36
 	image := &Image{
37 37
 		ID:      GenerateID(),
... ...
@@ -58,7 +59,7 @@ func TestInterruptedRegister(t *testing.T) {
58 58
 //       create multiple, check the amount of images and paths, etc..)
59 59
 func TestGraphCreate(t *testing.T) {
60 60
 	graph := tempGraph(t)
61
-	defer os.RemoveAll(graph.Root)
61
+	defer nukeGraph(graph)
62 62
 	archive, err := fakeTar()
63 63
 	if err != nil {
64 64
 		t.Fatal(err)
... ...
@@ -89,7 +90,7 @@ func TestGraphCreate(t *testing.T) {
89 89
 
90 90
 func TestRegister(t *testing.T) {
91 91
 	graph := tempGraph(t)
92
-	defer os.RemoveAll(graph.Root)
92
+	defer nukeGraph(graph)
93 93
 	archive, err := fakeTar()
94 94
 	if err != nil {
95 95
 		t.Fatal(err)
... ...
@@ -123,7 +124,7 @@ func TestRegister(t *testing.T) {
123 123
 // Test that an image can be deleted by its shorthand prefix
124 124
 func TestDeletePrefix(t *testing.T) {
125 125
 	graph := tempGraph(t)
126
-	defer os.RemoveAll(graph.Root)
126
+	defer nukeGraph(graph)
127 127
 	img := createTestImage(graph, t)
128 128
 	if err := graph.Delete(utils.TruncateID(img.ID)); err != nil {
129 129
 		t.Fatal(err)
... ...
@@ -145,7 +146,7 @@ func createTestImage(graph *Graph, t *testing.T) *Image {
145 145
 
146 146
 func TestDelete(t *testing.T) {
147 147
 	graph := tempGraph(t)
148
-	defer os.RemoveAll(graph.Root)
148
+	defer nukeGraph(graph)
149 149
 	archive, err := fakeTar()
150 150
 	if err != nil {
151 151
 		t.Fatal(err)
... ...
@@ -209,7 +210,7 @@ func TestByParent(t *testing.T) {
209 209
 	archive3, _ := fakeTar()
210 210
 
211 211
 	graph := tempGraph(t)
212
-	defer os.RemoveAll(graph.Root)
212
+	defer nukeGraph(graph)
213 213
 	parentImage := &Image{
214 214
 		ID:      GenerateID(),
215 215
 		Comment: "parent",
... ...
@@ -259,13 +260,22 @@ func tempGraph(t *testing.T) *Graph {
259 259
 	if err != nil {
260 260
 		t.Fatal(err)
261 261
 	}
262
-	graph, err := NewGraph(tmp)
262
+	backend, err := graphdriver.New(tmp)
263
+	if err != nil {
264
+		t.Fatal(err)
265
+	}
266
+	graph, err := NewGraph(tmp, backend)
263 267
 	if err != nil {
264 268
 		t.Fatal(err)
265 269
 	}
266 270
 	return graph
267 271
 }
268 272
 
273
+func nukeGraph(graph *Graph) {
274
+	graph.driver.Cleanup()
275
+	os.RemoveAll(graph.Root)
276
+}
277
+
269 278
 func testArchive(t *testing.T) archive.Archive {
270 279
 	archive, err := fakeTar()
271 280
 	if err != nil {
272 281
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
0 1
new file mode 100644
... ...
@@ -0,0 +1,473 @@
0
+package graphdb
1
+
2
+import (
3
+	"database/sql"
4
+	"fmt"
5
+	"path"
6
+	"sync"
7
+)
8
+
9
+const (
10
+	createEntityTable = `
11
+    CREATE TABLE IF NOT EXISTS entity (
12
+        id text NOT NULL PRIMARY KEY
13
+    );`
14
+
15
+	createEdgeTable = `
16
+    CREATE TABLE IF NOT EXISTS edge (
17
+        "entity_id" text NOT NULL,
18
+        "parent_id" text NULL,
19
+        "name" text NOT NULL,
20
+        CONSTRAINT "parent_fk" FOREIGN KEY ("parent_id") REFERENCES "entity" ("id"),
21
+        CONSTRAINT "entity_fk" FOREIGN KEY ("entity_id") REFERENCES "entity" ("id")
22
+        );
23
+    `
24
+
25
+	createEdgeIndices = `
26
+    CREATE UNIQUE INDEX IF NOT EXISTS "name_parent_ix" ON "edge" (parent_id, name);
27
+    `
28
+)
29
+
30
+// Entity with a unique id
31
+type Entity struct {
32
+	id string
33
+}
34
+
35
+// An Edge connects two entities together
36
+type Edge struct {
37
+	EntityID string
38
+	Name     string
39
+	ParentID string
40
+}
41
+
42
+type Entities map[string]*Entity
43
+type Edges []*Edge
44
+
45
+type WalkFunc func(fullPath string, entity *Entity) error
46
+
47
+// Graph database for storing entities and their relationships
48
+type Database struct {
49
+	conn *sql.DB
50
+	mux  sync.RWMutex
51
+}
52
+
53
+// Create a new graph database initialized with a root entity
54
+func NewDatabase(conn *sql.DB, init bool) (*Database, error) {
55
+	if conn == nil {
56
+		return nil, fmt.Errorf("Database connection cannot be nil")
57
+	}
58
+	db := &Database{conn: conn}
59
+
60
+	if init {
61
+		if _, err := conn.Exec(createEntityTable); err != nil {
62
+			return nil, err
63
+		}
64
+		if _, err := conn.Exec(createEdgeTable); err != nil {
65
+			return nil, err
66
+		}
67
+		if _, err := conn.Exec(createEdgeIndices); err != nil {
68
+			return nil, err
69
+		}
70
+
71
+		rollback := func() {
72
+			conn.Exec("ROLLBACK")
73
+		}
74
+
75
+		// Create root entities
76
+		if _, err := conn.Exec("BEGIN"); err != nil {
77
+			return nil, err
78
+		}
79
+		if _, err := conn.Exec("INSERT INTO entity (id) VALUES (?);", "0"); err != nil {
80
+			rollback()
81
+			return nil, err
82
+		}
83
+
84
+		if _, err := conn.Exec("INSERT INTO edge (entity_id, name) VALUES(?,?);", "0", "/"); err != nil {
85
+			rollback()
86
+			return nil, err
87
+		}
88
+
89
+		if _, err := conn.Exec("COMMIT"); err != nil {
90
+			return nil, err
91
+		}
92
+	}
93
+	return db, nil
94
+}
95
+
96
+// Close the underlying connection to the database
97
+func (db *Database) Close() error {
98
+	return db.conn.Close()
99
+}
100
+
101
+// Set the entity id for a given path
102
+func (db *Database) Set(fullPath, id string) (*Entity, error) {
103
+	db.mux.Lock()
104
+	defer db.mux.Unlock()
105
+
106
+	rollback := func() {
107
+		db.conn.Exec("ROLLBACK")
108
+	}
109
+	if _, err := db.conn.Exec("BEGIN EXCLUSIVE"); err != nil {
110
+		return nil, err
111
+	}
112
+	var entityId string
113
+	if err := db.conn.QueryRow("SELECT id FROM entity WHERE id = ?;", id).Scan(&entityId); err != nil {
114
+		if err == sql.ErrNoRows {
115
+			if _, err := db.conn.Exec("INSERT INTO entity (id) VALUES(?);", id); err != nil {
116
+				rollback()
117
+				return nil, err
118
+			}
119
+		} else {
120
+			rollback()
121
+			return nil, err
122
+		}
123
+	}
124
+	e := &Entity{id}
125
+
126
+	parentPath, name := splitPath(fullPath)
127
+	if err := db.setEdge(parentPath, name, e); err != nil {
128
+		rollback()
129
+		return nil, err
130
+	}
131
+
132
+	if _, err := db.conn.Exec("COMMIT"); err != nil {
133
+		return nil, err
134
+	}
135
+	return e, nil
136
+}
137
+
138
+// Return true if a name already exists in the database
139
+func (db *Database) Exists(name string) bool {
140
+	db.mux.RLock()
141
+	defer db.mux.RUnlock()
142
+
143
+	e, err := db.get(name)
144
+	if err != nil {
145
+		return false
146
+	}
147
+	return e != nil
148
+}
149
+
150
+func (db *Database) setEdge(parentPath, name string, e *Entity) error {
151
+	parent, err := db.get(parentPath)
152
+	if err != nil {
153
+		return err
154
+	}
155
+	if parent.id == e.id {
156
+		return fmt.Errorf("Cannot set self as child")
157
+	}
158
+
159
+	if _, err := db.conn.Exec("INSERT INTO edge (parent_id, name, entity_id) VALUES (?,?,?);", parent.id, name, e.id); err != nil {
160
+		return err
161
+	}
162
+	return nil
163
+}
164
+
165
+// Return the root "/" entity for the database
166
+func (db *Database) RootEntity() *Entity {
167
+	return &Entity{
168
+		id: "0",
169
+	}
170
+}
171
+
172
+// Return the entity for a given path
173
+func (db *Database) Get(name string) *Entity {
174
+	db.mux.RLock()
175
+	defer db.mux.RUnlock()
176
+
177
+	e, err := db.get(name)
178
+	if err != nil {
179
+		return nil
180
+	}
181
+	return e
182
+}
183
+
184
+func (db *Database) get(name string) (*Entity, error) {
185
+	e := db.RootEntity()
186
+	// We always know the root name so return it if
187
+	// it is requested
188
+	if name == "/" {
189
+		return e, nil
190
+	}
191
+
192
+	parts := split(name)
193
+	for i := 1; i < len(parts); i++ {
194
+		p := parts[i]
195
+		if p == "" {
196
+			continue
197
+		}
198
+
199
+		next := db.child(e, p)
200
+		if next == nil {
201
+			return nil, fmt.Errorf("Cannot find child for %s", name)
202
+		}
203
+		e = next
204
+	}
205
+	return e, nil
206
+
207
+}
208
+
209
+// List all entities by from the name
210
+// The key will be the full path of the entity
211
+func (db *Database) List(name string, depth int) Entities {
212
+	db.mux.RLock()
213
+	defer db.mux.RUnlock()
214
+
215
+	out := Entities{}
216
+	e, err := db.get(name)
217
+	if err != nil {
218
+		return out
219
+	}
220
+
221
+	children, err := db.children(e, name, depth, nil)
222
+	if err != nil {
223
+		return out
224
+	}
225
+
226
+	for _, c := range children {
227
+		out[c.FullPath] = c.Entity
228
+	}
229
+	return out
230
+}
231
+
232
+// Walk through the child graph of an entity, calling walkFunc for each child entity.
233
+// It is safe for walkFunc to call graph functions.
234
+func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
235
+	children, err := db.Children(name, depth)
236
+	if err != nil {
237
+		return err
238
+	}
239
+
240
+	// Note: the database lock must not be held while calling walkFunc
241
+	for _, c := range children {
242
+		if err := walkFunc(c.FullPath, c.Entity); err != nil {
243
+			return err
244
+		}
245
+	}
246
+	return nil
247
+}
248
+
249
+// Return the children of the specified entity
250
+func (db *Database) Children(name string, depth int) ([]WalkMeta, error) {
251
+	db.mux.RLock()
252
+	defer db.mux.RUnlock()
253
+
254
+	e, err := db.get(name)
255
+	if err != nil {
256
+		return nil, err
257
+	}
258
+
259
+	return db.children(e, name, depth, nil)
260
+}
261
+
262
+// Return the refrence count for a specified id
263
+func (db *Database) Refs(id string) int {
264
+	db.mux.RLock()
265
+	defer db.mux.RUnlock()
266
+
267
+	var count int
268
+	if err := db.conn.QueryRow("SELECT COUNT(*) FROM edge WHERE entity_id = ?;", id).Scan(&count); err != nil {
269
+		return 0
270
+	}
271
+	return count
272
+}
273
+
274
+// Return all the id's path references
275
+func (db *Database) RefPaths(id string) Edges {
276
+	db.mux.RLock()
277
+	defer db.mux.RUnlock()
278
+
279
+	refs := Edges{}
280
+
281
+	rows, err := db.conn.Query("SELECT name, parent_id FROM edge WHERE entity_id = ?;", id)
282
+	if err != nil {
283
+		return refs
284
+	}
285
+	defer rows.Close()
286
+
287
+	for rows.Next() {
288
+		var name string
289
+		var parentId string
290
+		if err := rows.Scan(&name, &parentId); err != nil {
291
+			return refs
292
+		}
293
+		refs = append(refs, &Edge{
294
+			EntityID: id,
295
+			Name:     name,
296
+			ParentID: parentId,
297
+		})
298
+	}
299
+	return refs
300
+}
301
+
302
+// Delete the reference to an entity at a given path
303
+func (db *Database) Delete(name string) error {
304
+	db.mux.Lock()
305
+	defer db.mux.Unlock()
306
+
307
+	if name == "/" {
308
+		return fmt.Errorf("Cannot delete root entity")
309
+	}
310
+
311
+	parentPath, n := splitPath(name)
312
+	parent, err := db.get(parentPath)
313
+	if err != nil {
314
+		return err
315
+	}
316
+
317
+	if _, err := db.conn.Exec("DELETE FROM edge WHERE parent_id = ? AND name = ?;", parent.id, n); err != nil {
318
+		return err
319
+	}
320
+	return nil
321
+}
322
+
323
+// Remove the entity with the specified id
324
+// Walk the graph to make sure all references to the entity
325
+// are removed and return the number of references removed
326
+func (db *Database) Purge(id string) (int, error) {
327
+	db.mux.Lock()
328
+	defer db.mux.Unlock()
329
+
330
+	rollback := func() {
331
+		db.conn.Exec("ROLLBACK")
332
+	}
333
+
334
+	if _, err := db.conn.Exec("BEGIN"); err != nil {
335
+		return -1, err
336
+	}
337
+
338
+	// Delete all edges
339
+	rows, err := db.conn.Exec("DELETE FROM edge WHERE entity_id = ?;", id)
340
+	if err != nil {
341
+		rollback()
342
+		return -1, err
343
+	}
344
+
345
+	changes, err := rows.RowsAffected()
346
+	if err != nil {
347
+		return -1, err
348
+	}
349
+
350
+	// Delete entity
351
+	if _, err := db.conn.Exec("DELETE FROM entity where id = ?;", id); err != nil {
352
+		rollback()
353
+		return -1, err
354
+	}
355
+
356
+	if _, err := db.conn.Exec("COMMIT"); err != nil {
357
+		return -1, err
358
+	}
359
+	return int(changes), nil
360
+}
361
+
362
+// Rename an edge for a given path
363
+func (db *Database) Rename(currentName, newName string) error {
364
+	db.mux.Lock()
365
+	defer db.mux.Unlock()
366
+
367
+	parentPath, name := splitPath(currentName)
368
+	newParentPath, newEdgeName := splitPath(newName)
369
+
370
+	if parentPath != newParentPath {
371
+		return fmt.Errorf("Cannot rename when root paths do not match %s != %s", parentPath, newParentPath)
372
+	}
373
+
374
+	parent, err := db.get(parentPath)
375
+	if err != nil {
376
+		return err
377
+	}
378
+
379
+	rows, err := db.conn.Exec("UPDATE edge SET name = ? WHERE parent_id = ? AND name = ?;", newEdgeName, parent.id, name)
380
+	if err != nil {
381
+		return err
382
+	}
383
+	i, err := rows.RowsAffected()
384
+	if err != nil {
385
+		return err
386
+	}
387
+	if i == 0 {
388
+		return fmt.Errorf("Cannot locate edge for %s %s", parent.id, name)
389
+	}
390
+	return nil
391
+}
392
+
393
+type WalkMeta struct {
394
+	Parent   *Entity
395
+	Entity   *Entity
396
+	FullPath string
397
+	Edge     *Edge
398
+}
399
+
400
+func (db *Database) children(e *Entity, name string, depth int, entities []WalkMeta) ([]WalkMeta, error) {
401
+	if e == nil {
402
+		return entities, nil
403
+	}
404
+
405
+	rows, err := db.conn.Query("SELECT entity_id, name FROM edge where parent_id = ?;", e.id)
406
+	if err != nil {
407
+		return nil, err
408
+	}
409
+	defer rows.Close()
410
+
411
+	for rows.Next() {
412
+		var entityId, entityName string
413
+		if err := rows.Scan(&entityId, &entityName); err != nil {
414
+			return nil, err
415
+		}
416
+		child := &Entity{entityId}
417
+		edge := &Edge{
418
+			ParentID: e.id,
419
+			Name:     entityName,
420
+			EntityID: child.id,
421
+		}
422
+
423
+		meta := WalkMeta{
424
+			Parent:   e,
425
+			Entity:   child,
426
+			FullPath: path.Join(name, edge.Name),
427
+			Edge:     edge,
428
+		}
429
+
430
+		entities = append(entities, meta)
431
+
432
+		if depth != 0 {
433
+			nDepth := depth
434
+			if depth != -1 {
435
+				nDepth -= 1
436
+			}
437
+			entities, err = db.children(child, meta.FullPath, nDepth, entities)
438
+			if err != nil {
439
+				return nil, err
440
+			}
441
+		}
442
+	}
443
+
444
+	return entities, nil
445
+}
446
+
447
+// Return the entity based on the parent path and name
448
+func (db *Database) child(parent *Entity, name string) *Entity {
449
+	var id string
450
+	if err := db.conn.QueryRow("SELECT entity_id FROM edge WHERE parent_id = ? AND name = ?;", parent.id, name).Scan(&id); err != nil {
451
+		return nil
452
+	}
453
+	return &Entity{id}
454
+}
455
+
456
+// Return the id used to reference this entity
457
+func (e *Entity) ID() string {
458
+	return e.id
459
+}
460
+
461
+// Return the paths sorted by depth
462
+func (e Entities) Paths() []string {
463
+	out := make([]string, len(e))
464
+	var i int
465
+	for k := range e {
466
+		out[i] = k
467
+		i++
468
+	}
469
+	sortByDepth(out)
470
+
471
+	return out
472
+}
0 473
new file mode 100644
... ...
@@ -0,0 +1,540 @@
0
+package graphdb
1
+
2
+import (
3
+	_ "code.google.com/p/gosqlite/sqlite3"
4
+	"database/sql"
5
+	"fmt"
6
+	"os"
7
+	"path"
8
+	"strconv"
9
+	"testing"
10
+)
11
+
12
+func newTestDb(t *testing.T) (*Database, string) {
13
+	p := path.Join(os.TempDir(), "sqlite.db")
14
+	conn, err := sql.Open("sqlite3", p)
15
+	db, err := NewDatabase(conn, true)
16
+	if err != nil {
17
+		t.Fatal(err)
18
+	}
19
+	return db, p
20
+}
21
+
22
+func destroyTestDb(dbPath string) {
23
+	os.Remove(dbPath)
24
+}
25
+
26
+func TestNewDatabase(t *testing.T) {
27
+	db, dbpath := newTestDb(t)
28
+	if db == nil {
29
+		t.Fatal("Database should not be nil")
30
+	}
31
+	db.Close()
32
+	defer destroyTestDb(dbpath)
33
+}
34
+
35
+func TestCreateRootEnity(t *testing.T) {
36
+	db, dbpath := newTestDb(t)
37
+	defer destroyTestDb(dbpath)
38
+	root := db.RootEntity()
39
+	if root == nil {
40
+		t.Fatal("Root entity should not be nil")
41
+	}
42
+}
43
+
44
+func TestGetRootEntity(t *testing.T) {
45
+	db, dbpath := newTestDb(t)
46
+	defer destroyTestDb(dbpath)
47
+
48
+	e := db.Get("/")
49
+	if e == nil {
50
+		t.Fatal("Entity should not be nil")
51
+	}
52
+	if e.ID() != "0" {
53
+		t.Fatalf("Enity id should be 0, got %s", e.ID())
54
+	}
55
+}
56
+
57
+func TestSetEntityWithDifferentName(t *testing.T) {
58
+	db, dbpath := newTestDb(t)
59
+	defer destroyTestDb(dbpath)
60
+
61
+	db.Set("/test", "1")
62
+	if _, err := db.Set("/other", "1"); err != nil {
63
+		t.Fatal(err)
64
+	}
65
+}
66
+
67
+func TestSetDuplicateEntity(t *testing.T) {
68
+	db, dbpath := newTestDb(t)
69
+	defer destroyTestDb(dbpath)
70
+
71
+	if _, err := db.Set("/foo", "42"); err != nil {
72
+		t.Fatal(err)
73
+	}
74
+	if _, err := db.Set("/foo", "43"); err == nil {
75
+		t.Fatalf("Creating an entry with a duplciate path did not cause an error")
76
+	}
77
+}
78
+
79
+func TestCreateChild(t *testing.T) {
80
+	db, dbpath := newTestDb(t)
81
+	defer destroyTestDb(dbpath)
82
+
83
+	child, err := db.Set("/db", "1")
84
+	if err != nil {
85
+		t.Fatal(err)
86
+	}
87
+	if child == nil {
88
+		t.Fatal("Child should not be nil")
89
+	}
90
+	if child.ID() != "1" {
91
+		t.Fail()
92
+	}
93
+}
94
+
95
+func TestListAllRootChildren(t *testing.T) {
96
+	db, dbpath := newTestDb(t)
97
+	defer destroyTestDb(dbpath)
98
+
99
+	for i := 1; i < 6; i++ {
100
+		a := strconv.Itoa(i)
101
+		if _, err := db.Set("/"+a, a); err != nil {
102
+			t.Fatal(err)
103
+		}
104
+	}
105
+	entries := db.List("/", -1)
106
+	if len(entries) != 5 {
107
+		t.Fatalf("Expect 5 entries for / got %d", len(entries))
108
+	}
109
+}
110
+
111
+func TestListAllSubChildren(t *testing.T) {
112
+	db, dbpath := newTestDb(t)
113
+	defer destroyTestDb(dbpath)
114
+
115
+	_, err := db.Set("/webapp", "1")
116
+	if err != nil {
117
+		t.Fatal(err)
118
+	}
119
+	child2, err := db.Set("/db", "2")
120
+	if err != nil {
121
+		t.Fatal(err)
122
+	}
123
+	child4, err := db.Set("/logs", "4")
124
+	if err != nil {
125
+		t.Fatal(err)
126
+	}
127
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
128
+		t.Fatal(err)
129
+	}
130
+
131
+	child3, err := db.Set("/sentry", "3")
132
+	if err != nil {
133
+		t.Fatal(err)
134
+	}
135
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
136
+		t.Fatal(err)
137
+	}
138
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
139
+		t.Fatal(err)
140
+	}
141
+
142
+	entries := db.List("/webapp", 1)
143
+	if len(entries) != 3 {
144
+		t.Fatalf("Expect 3 entries for / got %d", len(entries))
145
+	}
146
+
147
+	entries = db.List("/webapp", 0)
148
+	if len(entries) != 2 {
149
+		t.Fatalf("Expect 2 entries for / got %d", len(entries))
150
+	}
151
+}
152
+
153
+func TestAddSelfAsChild(t *testing.T) {
154
+	db, dbpath := newTestDb(t)
155
+	defer destroyTestDb(dbpath)
156
+
157
+	child, err := db.Set("/test", "1")
158
+	if err != nil {
159
+		t.Fatal(err)
160
+	}
161
+	if _, err := db.Set("/test/other", child.ID()); err == nil {
162
+		t.Fatal("Error should not be nil")
163
+	}
164
+}
165
+
166
+func TestAddChildToNonExistantRoot(t *testing.T) {
167
+	db, dbpath := newTestDb(t)
168
+	defer destroyTestDb(dbpath)
169
+
170
+	if _, err := db.Set("/myapp", "1"); err != nil {
171
+		t.Fatal(err)
172
+	}
173
+
174
+	if _, err := db.Set("/myapp/proxy/db", "2"); err == nil {
175
+		t.Fatal("Error should not be nil")
176
+	}
177
+}
178
+
179
+func TestWalkAll(t *testing.T) {
180
+	db, dbpath := newTestDb(t)
181
+	defer destroyTestDb(dbpath)
182
+	_, err := db.Set("/webapp", "1")
183
+	if err != nil {
184
+		t.Fatal(err)
185
+	}
186
+	child2, err := db.Set("/db", "2")
187
+	if err != nil {
188
+		t.Fatal(err)
189
+	}
190
+	child4, err := db.Set("/db/logs", "4")
191
+	if err != nil {
192
+		t.Fatal(err)
193
+	}
194
+	if _, err := db.Set("/webapp/logs", child4.ID()); err != nil {
195
+		t.Fatal(err)
196
+	}
197
+
198
+	child3, err := db.Set("/sentry", "3")
199
+	if err != nil {
200
+		t.Fatal(err)
201
+	}
202
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
203
+		t.Fatal(err)
204
+	}
205
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
206
+		t.Fatal(err)
207
+	}
208
+
209
+	child5, err := db.Set("/gograph", "5")
210
+	if err != nil {
211
+		t.Fatal(err)
212
+	}
213
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
214
+		t.Fatal(err)
215
+	}
216
+
217
+	if err := db.Walk("/", func(p string, e *Entity) error {
218
+		t.Logf("Path: %s Entity: %s", p, e.ID())
219
+		return nil
220
+	}, -1); err != nil {
221
+		t.Fatal(err)
222
+	}
223
+}
224
+
225
+func TestGetEntityByPath(t *testing.T) {
226
+	db, dbpath := newTestDb(t)
227
+	defer destroyTestDb(dbpath)
228
+	_, err := db.Set("/webapp", "1")
229
+	if err != nil {
230
+		t.Fatal(err)
231
+	}
232
+	child2, err := db.Set("/db", "2")
233
+	if err != nil {
234
+		t.Fatal(err)
235
+	}
236
+	child4, err := db.Set("/logs", "4")
237
+	if err != nil {
238
+		t.Fatal(err)
239
+	}
240
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
241
+		t.Fatal(err)
242
+	}
243
+
244
+	child3, err := db.Set("/sentry", "3")
245
+	if err != nil {
246
+		t.Fatal(err)
247
+	}
248
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
249
+		t.Fatal(err)
250
+	}
251
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
252
+		t.Fatal(err)
253
+	}
254
+
255
+	child5, err := db.Set("/gograph", "5")
256
+	if err != nil {
257
+		t.Fatal(err)
258
+	}
259
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
260
+		t.Fatal(err)
261
+	}
262
+
263
+	entity := db.Get("/webapp/db/logs")
264
+	if entity == nil {
265
+		t.Fatal("Entity should not be nil")
266
+	}
267
+	if entity.ID() != "4" {
268
+		t.Fatalf("Expected to get entity with id 4, got %s", entity.ID())
269
+	}
270
+}
271
+
272
+func TestEnitiesPaths(t *testing.T) {
273
+	db, dbpath := newTestDb(t)
274
+	defer destroyTestDb(dbpath)
275
+	_, err := db.Set("/webapp", "1")
276
+	if err != nil {
277
+		t.Fatal(err)
278
+	}
279
+	child2, err := db.Set("/db", "2")
280
+	if err != nil {
281
+		t.Fatal(err)
282
+	}
283
+	child4, err := db.Set("/logs", "4")
284
+	if err != nil {
285
+		t.Fatal(err)
286
+	}
287
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
288
+		t.Fatal(err)
289
+	}
290
+
291
+	child3, err := db.Set("/sentry", "3")
292
+	if err != nil {
293
+		t.Fatal(err)
294
+	}
295
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
296
+		t.Fatal(err)
297
+	}
298
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
299
+		t.Fatal(err)
300
+	}
301
+
302
+	child5, err := db.Set("/gograph", "5")
303
+	if err != nil {
304
+		t.Fatal(err)
305
+	}
306
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
307
+		t.Fatal(err)
308
+	}
309
+
310
+	out := db.List("/", -1)
311
+	for _, p := range out.Paths() {
312
+		t.Log(p)
313
+	}
314
+}
315
+
316
+func TestDeleteRootEntity(t *testing.T) {
317
+	db, dbpath := newTestDb(t)
318
+	defer destroyTestDb(dbpath)
319
+
320
+	if err := db.Delete("/"); err == nil {
321
+		t.Fatal("Error should not be nil")
322
+	}
323
+}
324
+
325
+func TestDeleteEntity(t *testing.T) {
326
+	db, dbpath := newTestDb(t)
327
+	defer destroyTestDb(dbpath)
328
+	_, err := db.Set("/webapp", "1")
329
+	if err != nil {
330
+		t.Fatal(err)
331
+	}
332
+	child2, err := db.Set("/db", "2")
333
+	if err != nil {
334
+		t.Fatal(err)
335
+	}
336
+	child4, err := db.Set("/logs", "4")
337
+	if err != nil {
338
+		t.Fatal(err)
339
+	}
340
+	if _, err := db.Set("/db/logs", child4.ID()); err != nil {
341
+		t.Fatal(err)
342
+	}
343
+
344
+	child3, err := db.Set("/sentry", "3")
345
+	if err != nil {
346
+		t.Fatal(err)
347
+	}
348
+	if _, err := db.Set("/webapp/sentry", child3.ID()); err != nil {
349
+		t.Fatal(err)
350
+	}
351
+	if _, err := db.Set("/webapp/db", child2.ID()); err != nil {
352
+		t.Fatal(err)
353
+	}
354
+
355
+	child5, err := db.Set("/gograph", "5")
356
+	if err != nil {
357
+		t.Fatal(err)
358
+	}
359
+	if _, err := db.Set("/webapp/same-ref-diff-name", child5.ID()); err != nil {
360
+		t.Fatal(err)
361
+	}
362
+
363
+	if err := db.Delete("/webapp/sentry"); err != nil {
364
+		t.Fatal(err)
365
+	}
366
+	entity := db.Get("/webapp/sentry")
367
+	if entity != nil {
368
+		t.Fatal("Entity /webapp/sentry should be nil")
369
+	}
370
+}
371
+
372
+func TestCountRefs(t *testing.T) {
373
+	db, dbpath := newTestDb(t)
374
+	defer destroyTestDb(dbpath)
375
+
376
+	db.Set("/webapp", "1")
377
+
378
+	if db.Refs("1") != 1 {
379
+		t.Fatal("Expect reference count to be 1")
380
+	}
381
+
382
+	db.Set("/db", "2")
383
+	db.Set("/webapp/db", "2")
384
+	if db.Refs("2") != 2 {
385
+		t.Fatal("Expect reference count to be 2")
386
+	}
387
+}
388
+
389
+func TestPurgeId(t *testing.T) {
390
+	db, dbpath := newTestDb(t)
391
+	defer destroyTestDb(dbpath)
392
+
393
+	db.Set("/webapp", "1")
394
+
395
+	if db.Refs("1") != 1 {
396
+		t.Fatal("Expect reference count to be 1")
397
+	}
398
+
399
+	db.Set("/db", "2")
400
+	db.Set("/webapp/db", "2")
401
+
402
+	count, err := db.Purge("2")
403
+	if err != nil {
404
+		t.Fatal(err)
405
+	}
406
+	if count != 2 {
407
+		t.Fatal("Expected 2 references to be removed")
408
+	}
409
+}
410
+
411
+func TestRename(t *testing.T) {
412
+	db, dbpath := newTestDb(t)
413
+	defer destroyTestDb(dbpath)
414
+
415
+	db.Set("/webapp", "1")
416
+
417
+	if db.Refs("1") != 1 {
418
+		t.Fatal("Expect reference count to be 1")
419
+	}
420
+
421
+	db.Set("/db", "2")
422
+	db.Set("/webapp/db", "2")
423
+
424
+	if db.Get("/webapp/db") == nil {
425
+		t.Fatal("Cannot find entity at path /webapp/db")
426
+	}
427
+
428
+	if err := db.Rename("/webapp/db", "/webapp/newdb"); err != nil {
429
+		t.Fatal(err)
430
+	}
431
+	if db.Get("/webapp/db") != nil {
432
+		t.Fatal("Entity should not exist at /webapp/db")
433
+	}
434
+	if db.Get("/webapp/newdb") == nil {
435
+		t.Fatal("Cannot find entity at path /webapp/newdb")
436
+	}
437
+
438
+}
439
+
440
+func TestCreateMultipleNames(t *testing.T) {
441
+	db, dbpath := newTestDb(t)
442
+	defer destroyTestDb(dbpath)
443
+
444
+	db.Set("/db", "1")
445
+	if _, err := db.Set("/myapp", "1"); err != nil {
446
+		t.Fatal(err)
447
+	}
448
+
449
+	db.Walk("/", func(p string, e *Entity) error {
450
+		t.Logf("%s\n", p)
451
+		return nil
452
+	}, -1)
453
+}
454
+
455
+func TestRefPaths(t *testing.T) {
456
+	db, dbpath := newTestDb(t)
457
+	defer destroyTestDb(dbpath)
458
+
459
+	db.Set("/webapp", "1")
460
+
461
+	db.Set("/db", "2")
462
+	db.Set("/webapp/db", "2")
463
+
464
+	refs := db.RefPaths("2")
465
+	if len(refs) != 2 {
466
+		t.Fatalf("Expected reference count to be 2, got %d", len(refs))
467
+	}
468
+}
469
+
470
+func TestExistsTrue(t *testing.T) {
471
+	db, dbpath := newTestDb(t)
472
+	defer destroyTestDb(dbpath)
473
+
474
+	db.Set("/testing", "1")
475
+
476
+	if !db.Exists("/testing") {
477
+		t.Fatalf("/tesing should exist")
478
+	}
479
+}
480
+
481
+func TestExistsFalse(t *testing.T) {
482
+	db, dbpath := newTestDb(t)
483
+	defer destroyTestDb(dbpath)
484
+
485
+	db.Set("/toerhe", "1")
486
+
487
+	if db.Exists("/testing") {
488
+		t.Fatalf("/tesing should not exist")
489
+	}
490
+
491
+}
492
+
493
+func TestGetNameWithTrailingSlash(t *testing.T) {
494
+	db, dbpath := newTestDb(t)
495
+	defer destroyTestDb(dbpath)
496
+
497
+	db.Set("/todo", "1")
498
+
499
+	e := db.Get("/todo/")
500
+	if e == nil {
501
+		t.Fatalf("Entity should not be nil")
502
+	}
503
+}
504
+
505
+func TestConcurrentWrites(t *testing.T) {
506
+	db, dbpath := newTestDb(t)
507
+	defer destroyTestDb(dbpath)
508
+
509
+	errs := make(chan error, 2)
510
+
511
+	save := func(name string, id string) {
512
+		if _, err := db.Set(fmt.Sprintf("/%s", name), id); err != nil {
513
+			errs <- err
514
+		}
515
+		errs <- nil
516
+	}
517
+	purge := func(id string) {
518
+		if _, err := db.Purge(id); err != nil {
519
+			errs <- err
520
+		}
521
+		errs <- nil
522
+	}
523
+
524
+	save("/1", "1")
525
+
526
+	go purge("1")
527
+	go save("/2", "2")
528
+
529
+	any := false
530
+	for i := 0; i < 2; i++ {
531
+		if err := <-errs; err != nil {
532
+			any = true
533
+			t.Log(err)
534
+		}
535
+	}
536
+	if any {
537
+		t.Fatal()
538
+	}
539
+}
0 540
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+package graphdb
1
+
2
+import "sort"
3
+
4
+type pathSorter struct {
5
+	paths []string
6
+	by    func(i, j string) bool
7
+}
8
+
9
+func sortByDepth(paths []string) {
10
+	s := &pathSorter{paths, func(i, j string) bool {
11
+		return PathDepth(i) > PathDepth(j)
12
+	}}
13
+	sort.Sort(s)
14
+}
15
+
16
+func (s *pathSorter) Len() int {
17
+	return len(s.paths)
18
+}
19
+
20
+func (s *pathSorter) Swap(i, j int) {
21
+	s.paths[i], s.paths[j] = s.paths[j], s.paths[i]
22
+}
23
+
24
+func (s *pathSorter) Less(i, j int) bool {
25
+	return s.by(s.paths[i], s.paths[j])
26
+}
0 27
new file mode 100644
... ...
@@ -0,0 +1,29 @@
0
+package graphdb
1
+
2
+import (
3
+	"testing"
4
+)
5
+
6
+func TestSort(t *testing.T) {
7
+	paths := []string{
8
+		"/",
9
+		"/myreallylongname",
10
+		"/app/db",
11
+	}
12
+
13
+	sortByDepth(paths)
14
+
15
+	if len(paths) != 3 {
16
+		t.Fatalf("Expected 3 parts got %d", len(paths))
17
+	}
18
+
19
+	if paths[0] != "/app/db" {
20
+		t.Fatalf("Expected /app/db got %s", paths[0])
21
+	}
22
+	if paths[1] != "/myreallylongname" {
23
+		t.Fatalf("Expected /myreallylongname got %s", paths[1])
24
+	}
25
+	if paths[2] != "/" {
26
+		t.Fatalf("Expected / got %s", paths[2])
27
+	}
28
+}
0 29
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+package graphdb
1
+
2
+import (
3
+	"path"
4
+	"strings"
5
+)
6
+
7
+// Split p on /
8
+func split(p string) []string {
9
+	return strings.Split(p, "/")
10
+}
11
+
12
+// Returns the depth or number of / in a given path
13
+func PathDepth(p string) int {
14
+	parts := split(p)
15
+	if len(parts) == 2 && parts[1] == "" {
16
+		return 1
17
+	}
18
+	return len(parts)
19
+}
20
+
21
+func splitPath(p string) (parent, name string) {
22
+	if p[0] != '/' {
23
+		p = "/" + p
24
+	}
25
+	parent, name = path.Split(p)
26
+	l := len(parent)
27
+	if parent[l-1] == '/' {
28
+		parent = parent[:l-1]
29
+	}
30
+	return
31
+}
0 32
new file mode 100644
... ...
@@ -0,0 +1,336 @@
0
+/*
1
+
2
+aufs driver directory structure
3
+
4
+.
5
+├── layers // Metadata of layers
6
+│   ├── 1
7
+│   ├── 2
8
+│   └── 3
9
+├── diffs  // Content of the layer
10
+│   ├── 1  // Contains layers that need to be mounted for the id
11
+│   ├── 2
12
+│   └── 3
13
+└── mnt    // Mount points for the rw layers to be mounted
14
+    ├── 1
15
+    ├── 2
16
+    └── 3
17
+
18
+*/
19
+
20
+package aufs
21
+
22
+import (
23
+	"bufio"
24
+	"fmt"
25
+	"github.com/dotcloud/docker/archive"
26
+	"github.com/dotcloud/docker/graphdriver"
27
+	"github.com/dotcloud/docker/utils"
28
+	"log"
29
+	"os"
30
+	"os/exec"
31
+	"path"
32
+	"strings"
33
+)
34
+
35
+func init() {
36
+	graphdriver.Register("aufs", Init)
37
+}
38
+
39
+type Driver struct {
40
+	root string
41
+}
42
+
43
+// New returns a new AUFS driver.
44
+// An error is returned if AUFS is not supported.
45
+func Init(root string) (graphdriver.Driver, error) {
46
+	// Try to load the aufs kernel module
47
+	if err := supportsAufs(); err != nil {
48
+		return nil, err
49
+	}
50
+	paths := []string{
51
+		"mnt",
52
+		"diff",
53
+		"layers",
54
+	}
55
+
56
+	// Create the root aufs driver dir and return
57
+	// if it already exists
58
+	// If not populate the dir structure
59
+	if err := os.MkdirAll(root, 0755); err != nil {
60
+		if os.IsExist(err) {
61
+			return &Driver{root}, nil
62
+		}
63
+		return nil, err
64
+	}
65
+
66
+	for _, p := range paths {
67
+		if err := os.MkdirAll(path.Join(root, p), 0755); err != nil {
68
+			return nil, err
69
+		}
70
+	}
71
+	return &Driver{root}, nil
72
+}
73
+
74
+// Return a nil error if the kernel supports aufs
75
+// We cannot modprobe because inside dind modprobe fails
76
+// to run
77
+func supportsAufs() error {
78
+	// We can try to modprobe aufs first before looking at
79
+	// proc/filesystems for when aufs is supported
80
+	exec.Command("modprobe", "aufs").Run()
81
+
82
+	f, err := os.Open("/proc/filesystems")
83
+	if err != nil {
84
+		return err
85
+	}
86
+	defer f.Close()
87
+
88
+	s := bufio.NewScanner(f)
89
+	for s.Scan() {
90
+		if strings.Contains(s.Text(), "aufs") {
91
+			return nil
92
+		}
93
+	}
94
+	return fmt.Errorf("AUFS was not found in /proc/filesystems")
95
+}
96
+
97
+func (a Driver) rootPath() string {
98
+	return a.root
99
+}
100
+
101
+func (Driver) String() string {
102
+	return "aufs"
103
+}
104
+
105
+func (a Driver) Status() [][2]string {
106
+	ids, _ := loadIds(path.Join(a.rootPath(), "layers"))
107
+	return [][2]string{
108
+		{"Root Dir", a.rootPath()},
109
+		{"Dirs", fmt.Sprintf("%d", len(ids))},
110
+	}
111
+}
112
+
113
+// Exists returns true if the given id is registered with
114
+// this driver
115
+func (a Driver) Exists(id string) bool {
116
+	if _, err := os.Lstat(path.Join(a.rootPath(), "layers", id)); err != nil {
117
+		return false
118
+	}
119
+	return true
120
+}
121
+
122
+// Three folders are created for each id
123
+// mnt, layers, and diff
124
+func (a *Driver) Create(id, parent string) error {
125
+	if err := a.createDirsFor(id); err != nil {
126
+		return err
127
+	}
128
+	// Write the layers metadata
129
+	f, err := os.Create(path.Join(a.rootPath(), "layers", id))
130
+	if err != nil {
131
+		return err
132
+	}
133
+	defer f.Close()
134
+
135
+	if parent != "" {
136
+		ids, err := getParentIds(a.rootPath(), parent)
137
+		if err != nil {
138
+			return err
139
+		}
140
+
141
+		if _, err := fmt.Fprintln(f, parent); err != nil {
142
+			return err
143
+		}
144
+		for _, i := range ids {
145
+			if _, err := fmt.Fprintln(f, i); err != nil {
146
+				return err
147
+			}
148
+		}
149
+	}
150
+	return nil
151
+}
152
+
153
+func (a *Driver) createDirsFor(id string) error {
154
+	paths := []string{
155
+		"mnt",
156
+		"diff",
157
+	}
158
+
159
+	for _, p := range paths {
160
+		if err := os.MkdirAll(path.Join(a.rootPath(), p, id), 0755); err != nil {
161
+			return err
162
+		}
163
+	}
164
+	return nil
165
+}
166
+
167
+// Unmount and remove the dir information
168
+func (a *Driver) Remove(id string) error {
169
+	// Make sure the dir is umounted first
170
+	if err := a.unmount(id); err != nil {
171
+		return err
172
+	}
173
+	tmpDirs := []string{
174
+		"mnt",
175
+		"diff",
176
+	}
177
+
178
+	// Remove the dirs atomically
179
+	for _, p := range tmpDirs {
180
+		// We need to use a temp dir in the same dir as the driver so Rename
181
+		// does not fall back to the slow copy if /tmp and the driver dir
182
+		// are on different devices
183
+		tmp := path.Join(a.rootPath(), "tmp", p, id)
184
+		if err := os.MkdirAll(tmp, 0755); err != nil {
185
+			return err
186
+		}
187
+		realPath := path.Join(a.rootPath(), p, id)
188
+		if err := os.Rename(realPath, tmp); err != nil && !os.IsNotExist(err) {
189
+			return err
190
+		}
191
+		defer os.RemoveAll(tmp)
192
+	}
193
+
194
+	// Remove the layers file for the id
195
+	if err := os.Remove(path.Join(a.rootPath(), "layers", id)); err != nil && !os.IsNotExist(err) {
196
+		return err
197
+	}
198
+	return nil
199
+}
200
+
201
+// Return the rootfs path for the id
202
+// This will mount the dir at it's given path
203
+func (a *Driver) Get(id string) (string, error) {
204
+	ids, err := getParentIds(a.rootPath(), id)
205
+	if err != nil {
206
+		if !os.IsNotExist(err) {
207
+			return "", err
208
+		}
209
+		ids = []string{}
210
+	}
211
+
212
+	// If a dir does not have a parent ( no layers )do not try to mount
213
+	// just return the diff path to the data
214
+	out := path.Join(a.rootPath(), "diff", id)
215
+	if len(ids) > 0 {
216
+		out = path.Join(a.rootPath(), "mnt", id)
217
+		if err := a.mount(id); err != nil {
218
+			return "", err
219
+		}
220
+	}
221
+	return out, nil
222
+}
223
+
224
+// Returns an archive of the contents for the id
225
+func (a *Driver) Diff(id string) (archive.Archive, error) {
226
+	return archive.TarFilter(path.Join(a.rootPath(), "diff", id), &archive.TarOptions{
227
+		Recursive:   true,
228
+		Compression: archive.Uncompressed,
229
+	})
230
+}
231
+
232
+func (a *Driver) ApplyDiff(id string, diff archive.Archive) error {
233
+	return archive.Untar(diff, path.Join(a.rootPath(), "diff", id), nil)
234
+}
235
+
236
+// Returns the size of the contents for the id
237
+func (a *Driver) DiffSize(id string) (int64, error) {
238
+	return utils.TreeSize(path.Join(a.rootPath(), "diff", id))
239
+}
240
+
241
+func (a *Driver) Changes(id string) ([]archive.Change, error) {
242
+	layers, err := a.getParentLayerPaths(id)
243
+	if err != nil {
244
+		return nil, err
245
+	}
246
+	return archive.Changes(layers, path.Join(a.rootPath(), "diff", id))
247
+}
248
+
249
+func (a *Driver) getParentLayerPaths(id string) ([]string, error) {
250
+	parentIds, err := getParentIds(a.rootPath(), id)
251
+	if err != nil {
252
+		return nil, err
253
+	}
254
+	if len(parentIds) == 0 {
255
+		return nil, fmt.Errorf("Dir %s does not have any parent layers", id)
256
+	}
257
+	layers := make([]string, len(parentIds))
258
+
259
+	// Get the diff paths for all the parent ids
260
+	for i, p := range parentIds {
261
+		layers[i] = path.Join(a.rootPath(), "diff", p)
262
+	}
263
+	return layers, nil
264
+}
265
+
266
+func (a *Driver) mount(id string) error {
267
+	// If the id is mounted or we get an error return
268
+	if mounted, err := a.mounted(id); err != nil || mounted {
269
+		return err
270
+	}
271
+
272
+	var (
273
+		target = path.Join(a.rootPath(), "mnt", id)
274
+		rw     = path.Join(a.rootPath(), "diff", id)
275
+	)
276
+
277
+	layers, err := a.getParentLayerPaths(id)
278
+	if err != nil {
279
+		return err
280
+	}
281
+
282
+	if err := a.aufsMount(layers, rw, target); err != nil {
283
+		return err
284
+	}
285
+	return nil
286
+}
287
+
288
+func (a *Driver) unmount(id string) error {
289
+	if mounted, err := a.mounted(id); err != nil || !mounted {
290
+		return err
291
+	}
292
+	target := path.Join(a.rootPath(), "mnt", id)
293
+	return Unmount(target)
294
+}
295
+
296
+func (a *Driver) mounted(id string) (bool, error) {
297
+	target := path.Join(a.rootPath(), "mnt", id)
298
+	return Mounted(target)
299
+}
300
+
301
+// During cleanup aufs needs to unmount all mountpoints
302
+func (a *Driver) Cleanup() error {
303
+	ids, err := loadIds(path.Join(a.rootPath(), "layers"))
304
+	if err != nil {
305
+		return err
306
+	}
307
+	for _, id := range ids {
308
+		if err := a.unmount(id); err != nil {
309
+			utils.Errorf("Unmounting %s: %s", utils.TruncateID(id), err)
310
+		}
311
+	}
312
+	return nil
313
+}
314
+
315
+func (a *Driver) aufsMount(ro []string, rw, target string) error {
316
+	rwBranch := fmt.Sprintf("%v=rw", rw)
317
+	roBranches := ""
318
+	for _, layer := range ro {
319
+		roBranches += fmt.Sprintf("%v=ro+wh:", layer)
320
+	}
321
+	branches := fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches)
322
+
323
+	//if error, try to load aufs kernel module
324
+	if err := mount("none", target, "aufs", 0, branches); err != nil {
325
+		log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
326
+		if err := exec.Command("modprobe", "aufs").Run(); err != nil {
327
+			return fmt.Errorf("Unable to load the AUFS module")
328
+		}
329
+		log.Printf("...module loaded.")
330
+		if err := mount("none", target, "aufs", 0, branches); err != nil {
331
+			return fmt.Errorf("Unable to mount using aufs %s", err)
332
+		}
333
+	}
334
+	return nil
335
+}
0 336
new file mode 100644
... ...
@@ -0,0 +1,623 @@
0
+package aufs
1
+
2
+import (
3
+	"github.com/dotcloud/docker/archive"
4
+	"os"
5
+	"path"
6
+	"testing"
7
+)
8
+
9
+var (
10
+	tmp = path.Join(os.TempDir(), "aufs-tests", "aufs")
11
+)
12
+
13
+func newDriver(t *testing.T) *Driver {
14
+	if err := os.MkdirAll(tmp, 0755); err != nil {
15
+		t.Fatal(err)
16
+	}
17
+
18
+	d, err := Init(tmp)
19
+	if err != nil {
20
+		t.Fatal(err)
21
+	}
22
+	return d.(*Driver)
23
+}
24
+
25
+func TestNewDriver(t *testing.T) {
26
+	if err := os.MkdirAll(tmp, 0755); err != nil {
27
+		t.Fatal(err)
28
+	}
29
+
30
+	d, err := Init(tmp)
31
+	if err != nil {
32
+		t.Fatal(err)
33
+	}
34
+	defer os.RemoveAll(tmp)
35
+	if d == nil {
36
+		t.Fatalf("Driver should not be nil")
37
+	}
38
+}
39
+
40
+func TestAufsString(t *testing.T) {
41
+	d := newDriver(t)
42
+	defer os.RemoveAll(tmp)
43
+
44
+	if d.String() != "aufs" {
45
+		t.Fatalf("Expected aufs got %s", d.String())
46
+	}
47
+}
48
+
49
+func TestCreateDirStructure(t *testing.T) {
50
+	newDriver(t)
51
+	defer os.RemoveAll(tmp)
52
+
53
+	paths := []string{
54
+		"mnt",
55
+		"layers",
56
+		"diff",
57
+	}
58
+
59
+	for _, p := range paths {
60
+		if _, err := os.Stat(path.Join(tmp, p)); err != nil {
61
+			t.Fatal(err)
62
+		}
63
+	}
64
+}
65
+
66
+// We should be able to create two drivers with the same dir structure
67
+func TestNewDriverFromExistingDir(t *testing.T) {
68
+	if err := os.MkdirAll(tmp, 0755); err != nil {
69
+		t.Fatal(err)
70
+	}
71
+
72
+	if _, err := Init(tmp); err != nil {
73
+		t.Fatal(err)
74
+	}
75
+	if _, err := Init(tmp); err != nil {
76
+		t.Fatal(err)
77
+	}
78
+	os.RemoveAll(tmp)
79
+}
80
+
81
+func TestCreateNewDir(t *testing.T) {
82
+	d := newDriver(t)
83
+	defer os.RemoveAll(tmp)
84
+
85
+	if err := d.Create("1", ""); err != nil {
86
+		t.Fatal(err)
87
+	}
88
+}
89
+
90
+func TestCreateNewDirStructure(t *testing.T) {
91
+	d := newDriver(t)
92
+	defer os.RemoveAll(tmp)
93
+
94
+	if err := d.Create("1", ""); err != nil {
95
+		t.Fatal(err)
96
+	}
97
+
98
+	paths := []string{
99
+		"mnt",
100
+		"diff",
101
+		"layers",
102
+	}
103
+
104
+	for _, p := range paths {
105
+		if _, err := os.Stat(path.Join(tmp, p, "1")); err != nil {
106
+			t.Fatal(err)
107
+		}
108
+	}
109
+}
110
+
111
+func TestRemoveImage(t *testing.T) {
112
+	d := newDriver(t)
113
+	defer os.RemoveAll(tmp)
114
+
115
+	if err := d.Create("1", ""); err != nil {
116
+		t.Fatal(err)
117
+	}
118
+
119
+	if err := d.Remove("1"); err != nil {
120
+		t.Fatal(err)
121
+	}
122
+
123
+	paths := []string{
124
+		"mnt",
125
+		"diff",
126
+		"layers",
127
+	}
128
+
129
+	for _, p := range paths {
130
+		if _, err := os.Stat(path.Join(tmp, p, "1")); err == nil {
131
+			t.Fatalf("Error should not be nil because dirs with id 1 should be delted: %s", p)
132
+		}
133
+	}
134
+}
135
+
136
+func TestGetWithoutParent(t *testing.T) {
137
+	d := newDriver(t)
138
+	defer os.RemoveAll(tmp)
139
+
140
+	if err := d.Create("1", ""); err != nil {
141
+		t.Fatal(err)
142
+	}
143
+
144
+	diffPath, err := d.Get("1")
145
+	if err != nil {
146
+		t.Fatal(err)
147
+	}
148
+	expected := path.Join(tmp, "diff", "1")
149
+	if diffPath != expected {
150
+		t.Fatalf("Expected path %s got %s", expected, diffPath)
151
+	}
152
+}
153
+
154
+func TestCleanupWithNoDirs(t *testing.T) {
155
+	d := newDriver(t)
156
+	defer os.RemoveAll(tmp)
157
+
158
+	if err := d.Cleanup(); err != nil {
159
+		t.Fatal(err)
160
+	}
161
+}
162
+
163
+func TestCleanupWithDir(t *testing.T) {
164
+	d := newDriver(t)
165
+	defer os.RemoveAll(tmp)
166
+
167
+	if err := d.Create("1", ""); err != nil {
168
+		t.Fatal(err)
169
+	}
170
+
171
+	if err := d.Cleanup(); err != nil {
172
+		t.Fatal(err)
173
+	}
174
+}
175
+
176
+func TestMountedFalseResponse(t *testing.T) {
177
+	d := newDriver(t)
178
+	defer os.RemoveAll(tmp)
179
+
180
+	if err := d.Create("1", ""); err != nil {
181
+		t.Fatal(err)
182
+	}
183
+
184
+	response, err := d.mounted("1")
185
+	if err != nil {
186
+		t.Fatal(err)
187
+	}
188
+
189
+	if response != false {
190
+		t.Fatalf("Response if dir id 1 is mounted should be false")
191
+	}
192
+}
193
+
194
+func TestMountedTrueReponse(t *testing.T) {
195
+	d := newDriver(t)
196
+	defer os.RemoveAll(tmp)
197
+	defer d.Cleanup()
198
+
199
+	if err := d.Create("1", ""); err != nil {
200
+		t.Fatal(err)
201
+	}
202
+	if err := d.Create("2", "1"); err != nil {
203
+		t.Fatal(err)
204
+	}
205
+
206
+	_, err := d.Get("2")
207
+	if err != nil {
208
+		t.Fatal(err)
209
+	}
210
+
211
+	response, err := d.mounted("2")
212
+	if err != nil {
213
+		t.Fatal(err)
214
+	}
215
+
216
+	if response != true {
217
+		t.Fatalf("Response if dir id 2 is mounted should be true")
218
+	}
219
+}
220
+
221
+func TestMountWithParent(t *testing.T) {
222
+	d := newDriver(t)
223
+	defer os.RemoveAll(tmp)
224
+
225
+	if err := d.Create("1", ""); err != nil {
226
+		t.Fatal(err)
227
+	}
228
+	if err := d.Create("2", "1"); err != nil {
229
+		t.Fatal(err)
230
+	}
231
+
232
+	defer func() {
233
+		if err := d.Cleanup(); err != nil {
234
+			t.Fatal(err)
235
+		}
236
+	}()
237
+
238
+	mntPath, err := d.Get("2")
239
+	if err != nil {
240
+		t.Fatal(err)
241
+	}
242
+	if mntPath == "" {
243
+		t.Fatal("mntPath should not be empty string")
244
+	}
245
+
246
+	expected := path.Join(tmp, "mnt", "2")
247
+	if mntPath != expected {
248
+		t.Fatalf("Expected %s got %s", expected, mntPath)
249
+	}
250
+}
251
+
252
+func TestRemoveMountedDir(t *testing.T) {
253
+	d := newDriver(t)
254
+	defer os.RemoveAll(tmp)
255
+
256
+	if err := d.Create("1", ""); err != nil {
257
+		t.Fatal(err)
258
+	}
259
+	if err := d.Create("2", "1"); err != nil {
260
+		t.Fatal(err)
261
+	}
262
+
263
+	defer func() {
264
+		if err := d.Cleanup(); err != nil {
265
+			t.Fatal(err)
266
+		}
267
+	}()
268
+
269
+	mntPath, err := d.Get("2")
270
+	if err != nil {
271
+		t.Fatal(err)
272
+	}
273
+	if mntPath == "" {
274
+		t.Fatal("mntPath should not be empty string")
275
+	}
276
+
277
+	mounted, err := d.mounted("2")
278
+	if err != nil {
279
+		t.Fatal(err)
280
+	}
281
+
282
+	if !mounted {
283
+		t.Fatalf("Dir id 2 should be mounted")
284
+	}
285
+
286
+	if err := d.Remove("2"); err != nil {
287
+		t.Fatal(err)
288
+	}
289
+}
290
+
291
+func TestCreateWithInvalidParent(t *testing.T) {
292
+	d := newDriver(t)
293
+	defer os.RemoveAll(tmp)
294
+
295
+	if err := d.Create("1", "docker"); err == nil {
296
+		t.Fatalf("Error should not be nil with parent does not exist")
297
+	}
298
+}
299
+
300
+func TestGetDiff(t *testing.T) {
301
+	d := newDriver(t)
302
+	defer os.RemoveAll(tmp)
303
+
304
+	if err := d.Create("1", ""); err != nil {
305
+		t.Fatal(err)
306
+	}
307
+
308
+	diffPath, err := d.Get("1")
309
+	if err != nil {
310
+		t.Fatal(err)
311
+	}
312
+
313
+	// Add a file to the diff path with a fixed size
314
+	size := int64(1024)
315
+
316
+	f, err := os.Create(path.Join(diffPath, "test_file"))
317
+	if err != nil {
318
+		t.Fatal(err)
319
+	}
320
+	if err := f.Truncate(size); err != nil {
321
+		t.Fatal(err)
322
+	}
323
+	f.Close()
324
+
325
+	a, err := d.Diff("1")
326
+	if err != nil {
327
+		t.Fatal(err)
328
+	}
329
+	if a == nil {
330
+		t.Fatalf("Archive should not be nil")
331
+	}
332
+}
333
+
334
+func TestChanges(t *testing.T) {
335
+	d := newDriver(t)
336
+	defer os.RemoveAll(tmp)
337
+
338
+	if err := d.Create("1", ""); err != nil {
339
+		t.Fatal(err)
340
+	}
341
+	if err := d.Create("2", "1"); err != nil {
342
+		t.Fatal(err)
343
+	}
344
+
345
+	defer func() {
346
+		if err := d.Cleanup(); err != nil {
347
+			t.Fatal(err)
348
+		}
349
+	}()
350
+
351
+	mntPoint, err := d.Get("2")
352
+	if err != nil {
353
+		t.Fatal(err)
354
+	}
355
+
356
+	// Create a file to save in the mountpoint
357
+	f, err := os.Create(path.Join(mntPoint, "test.txt"))
358
+	if err != nil {
359
+		t.Fatal(err)
360
+	}
361
+
362
+	if _, err := f.WriteString("testline"); err != nil {
363
+		t.Fatal(err)
364
+	}
365
+	if err := f.Close(); err != nil {
366
+		t.Fatal(err)
367
+	}
368
+
369
+	changes, err := d.Changes("2")
370
+	if err != nil {
371
+		t.Fatal(err)
372
+	}
373
+	if len(changes) != 1 {
374
+		t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
375
+	}
376
+	change := changes[0]
377
+
378
+	expectedPath := "/test.txt"
379
+	if change.Path != expectedPath {
380
+		t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
381
+	}
382
+
383
+	if change.Kind != archive.ChangeAdd {
384
+		t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
385
+	}
386
+
387
+	if err := d.Create("3", "2"); err != nil {
388
+		t.Fatal(err)
389
+	}
390
+	mntPoint, err = d.Get("3")
391
+	if err != nil {
392
+		t.Fatal(err)
393
+	}
394
+
395
+	// Create a file to save in the mountpoint
396
+	f, err = os.Create(path.Join(mntPoint, "test2.txt"))
397
+	if err != nil {
398
+		t.Fatal(err)
399
+	}
400
+
401
+	if _, err := f.WriteString("testline"); err != nil {
402
+		t.Fatal(err)
403
+	}
404
+	if err := f.Close(); err != nil {
405
+		t.Fatal(err)
406
+	}
407
+
408
+	changes, err = d.Changes("3")
409
+	if err != nil {
410
+		t.Fatal(err)
411
+	}
412
+
413
+	if len(changes) != 1 {
414
+		t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
415
+	}
416
+	change = changes[0]
417
+
418
+	expectedPath = "/test2.txt"
419
+	if change.Path != expectedPath {
420
+		t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
421
+	}
422
+
423
+	if change.Kind != archive.ChangeAdd {
424
+		t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
425
+	}
426
+}
427
+
428
+func TestDiffSize(t *testing.T) {
429
+	d := newDriver(t)
430
+	defer os.RemoveAll(tmp)
431
+
432
+	if err := d.Create("1", ""); err != nil {
433
+		t.Fatal(err)
434
+	}
435
+
436
+	diffPath, err := d.Get("1")
437
+	if err != nil {
438
+		t.Fatal(err)
439
+	}
440
+
441
+	// Add a file to the diff path with a fixed size
442
+	size := int64(1024)
443
+
444
+	f, err := os.Create(path.Join(diffPath, "test_file"))
445
+	if err != nil {
446
+		t.Fatal(err)
447
+	}
448
+	if err := f.Truncate(size); err != nil {
449
+		t.Fatal(err)
450
+	}
451
+	s, err := f.Stat()
452
+	if err != nil {
453
+		t.Fatal(err)
454
+	}
455
+	size = s.Size()
456
+	if err := f.Close(); err != nil {
457
+		t.Fatal(err)
458
+	}
459
+
460
+	diffSize, err := d.DiffSize("1")
461
+	if err != nil {
462
+		t.Fatal(err)
463
+	}
464
+	if diffSize != size {
465
+		t.Fatalf("Expected size to be %d got %d", size, diffSize)
466
+	}
467
+}
468
+
469
+func TestChildDiffSize(t *testing.T) {
470
+	d := newDriver(t)
471
+	defer os.RemoveAll(tmp)
472
+	defer d.Cleanup()
473
+
474
+	if err := d.Create("1", ""); err != nil {
475
+		t.Fatal(err)
476
+	}
477
+
478
+	diffPath, err := d.Get("1")
479
+	if err != nil {
480
+		t.Fatal(err)
481
+	}
482
+
483
+	// Add a file to the diff path with a fixed size
484
+	size := int64(1024)
485
+
486
+	f, err := os.Create(path.Join(diffPath, "test_file"))
487
+	if err != nil {
488
+		t.Fatal(err)
489
+	}
490
+	if err := f.Truncate(size); err != nil {
491
+		t.Fatal(err)
492
+	}
493
+	s, err := f.Stat()
494
+	if err != nil {
495
+		t.Fatal(err)
496
+	}
497
+	size = s.Size()
498
+	if err := f.Close(); err != nil {
499
+		t.Fatal(err)
500
+	}
501
+
502
+	diffSize, err := d.DiffSize("1")
503
+	if err != nil {
504
+		t.Fatal(err)
505
+	}
506
+	if diffSize != size {
507
+		t.Fatalf("Expected size to be %d got %d", size, diffSize)
508
+	}
509
+
510
+	if err := d.Create("2", "1"); err != nil {
511
+		t.Fatal(err)
512
+	}
513
+
514
+	diffSize, err = d.DiffSize("2")
515
+	if err != nil {
516
+		t.Fatal(err)
517
+	}
518
+	// The diff size for the child should be zero
519
+	if diffSize != 0 {
520
+		t.Fatalf("Expected size to be %d got %d", 0, diffSize)
521
+	}
522
+}
523
+
524
+func TestExists(t *testing.T) {
525
+	d := newDriver(t)
526
+	defer os.RemoveAll(tmp)
527
+	defer d.Cleanup()
528
+
529
+	if err := d.Create("1", ""); err != nil {
530
+		t.Fatal(err)
531
+	}
532
+
533
+	if d.Exists("none") {
534
+		t.Fatal("id name should not exist in the driver")
535
+	}
536
+
537
+	if !d.Exists("1") {
538
+		t.Fatal("id 1 should exist in the driver")
539
+	}
540
+}
541
+
542
+func TestStatus(t *testing.T) {
543
+	d := newDriver(t)
544
+	defer os.RemoveAll(tmp)
545
+	defer d.Cleanup()
546
+
547
+	if err := d.Create("1", ""); err != nil {
548
+		t.Fatal(err)
549
+	}
550
+
551
+	status := d.Status()
552
+	if status == nil || len(status) == 0 {
553
+		t.Fatal("Status should not be nil or empty")
554
+	}
555
+	rootDir := status[0]
556
+	dirs := status[1]
557
+	if rootDir[0] != "Root Dir" {
558
+		t.Fatalf("Expected Root Dir got %s", rootDir[0])
559
+	}
560
+	if rootDir[1] != d.rootPath() {
561
+		t.Fatalf("Expected %s got %s", d.rootPath(), rootDir[1])
562
+	}
563
+	if dirs[0] != "Dirs" {
564
+		t.Fatalf("Expected Dirs got %s", dirs[0])
565
+	}
566
+	if dirs[1] != "1" {
567
+		t.Fatalf("Expected 1 got %s", dirs[1])
568
+	}
569
+}
570
+
571
+func TestApplyDiff(t *testing.T) {
572
+	d := newDriver(t)
573
+	defer os.RemoveAll(tmp)
574
+	defer d.Cleanup()
575
+
576
+	if err := d.Create("1", ""); err != nil {
577
+		t.Fatal(err)
578
+	}
579
+
580
+	diffPath, err := d.Get("1")
581
+	if err != nil {
582
+		t.Fatal(err)
583
+	}
584
+
585
+	// Add a file to the diff path with a fixed size
586
+	size := int64(1024)
587
+
588
+	f, err := os.Create(path.Join(diffPath, "test_file"))
589
+	if err != nil {
590
+		t.Fatal(err)
591
+	}
592
+	if err := f.Truncate(size); err != nil {
593
+		t.Fatal(err)
594
+	}
595
+	f.Close()
596
+
597
+	diff, err := d.Diff("1")
598
+	if err != nil {
599
+		t.Fatal(err)
600
+	}
601
+
602
+	if err := d.Create("2", ""); err != nil {
603
+		t.Fatal(err)
604
+	}
605
+	if err := d.Create("3", "2"); err != nil {
606
+		t.Fatal(err)
607
+	}
608
+
609
+	if err := d.ApplyDiff("3", diff); err != nil {
610
+		t.Fatal(err)
611
+	}
612
+
613
+	// Ensure that the file is in the mount point for id 3
614
+
615
+	mountPoint, err := d.Get("3")
616
+	if err != nil {
617
+		t.Fatal(err)
618
+	}
619
+	if _, err := os.Stat(path.Join(mountPoint, "test_file")); err != nil {
620
+		t.Fatal(err)
621
+	}
622
+}
0 623
new file mode 100644
... ...
@@ -0,0 +1,46 @@
0
+package aufs
1
+
2
+import (
3
+	"bufio"
4
+	"io/ioutil"
5
+	"os"
6
+	"path"
7
+)
8
+
9
+// Return all the directories
10
+func loadIds(root string) ([]string, error) {
11
+	dirs, err := ioutil.ReadDir(root)
12
+	if err != nil {
13
+		return nil, err
14
+	}
15
+	out := []string{}
16
+	for _, d := range dirs {
17
+		if !d.IsDir() {
18
+			out = append(out, d.Name())
19
+		}
20
+	}
21
+	return out, nil
22
+}
23
+
24
+// Read the layers file for the current id and return all the
25
+// layers represented by new lines in the file
26
+//
27
+// If there are no lines in the file then the id has no parent
28
+// and an empty slice is returned.
29
+func getParentIds(root, id string) ([]string, error) {
30
+	f, err := os.Open(path.Join(root, "layers", id))
31
+	if err != nil {
32
+		return nil, err
33
+	}
34
+	defer f.Close()
35
+
36
+	out := []string{}
37
+	s := bufio.NewScanner(f)
38
+
39
+	for s.Scan() {
40
+		if t := s.Text(); t != "" {
41
+			out = append(out, s.Text())
42
+		}
43
+	}
44
+	return out, s.Err()
45
+}
0 46
new file mode 100644
... ...
@@ -0,0 +1,194 @@
0
+package aufs
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"io/ioutil"
6
+	"os"
7
+	"path"
8
+)
9
+
10
+type metadata struct {
11
+	ID       string `json:"id"`
12
+	ParentID string `json:"parent,omitempty"`
13
+	Image    string `json:"Image,omitempty"`
14
+
15
+	parent *metadata
16
+}
17
+
18
+func pathExists(pth string) bool {
19
+	if _, err := os.Stat(pth); err != nil {
20
+		return false
21
+	}
22
+	return true
23
+}
24
+
25
+// Migrate existing images and containers from docker < 0.7.x
26
+//
27
+// The format pre 0.7 is for docker to store the metadata and filesystem
28
+// content in the same directory.  For the migration to work we need to move Image layer
29
+// data from /var/lib/docker/graph/<id>/layers to the diff of the registered id.
30
+//
31
+// Next we need to migrate the container's rw layer to diff of the driver.  After the
32
+// contents are migrated we need to register the image and container ids with the
33
+// driver.
34
+//
35
+// For the migration we try to move the folder containing the layer files, if that
36
+// fails because the data is currently mounted we will fallback to creating a
37
+// symlink.
38
+func (a *Driver) Migrate(pth string, setupInit func(p string) error) error {
39
+	if pathExists(path.Join(pth, "graph")) {
40
+		if err := a.migrateRepositories(pth); err != nil {
41
+			return err
42
+		}
43
+		if err := a.migrateImages(path.Join(pth, "graph")); err != nil {
44
+			return err
45
+		}
46
+		return a.migrateContainers(path.Join(pth, "containers"), setupInit)
47
+	}
48
+	return nil
49
+}
50
+
51
+func (a *Driver) migrateRepositories(pth string) error {
52
+	name := path.Join(pth, "repositories")
53
+	if err := os.Rename(name, name+"-aufs"); err != nil && !os.IsNotExist(err) {
54
+		return err
55
+	}
56
+	return nil
57
+}
58
+
59
+func (a *Driver) migrateContainers(pth string, setupInit func(p string) error) error {
60
+	fis, err := ioutil.ReadDir(pth)
61
+	if err != nil {
62
+		return err
63
+	}
64
+
65
+	for _, fi := range fis {
66
+		if id := fi.Name(); fi.IsDir() && pathExists(path.Join(pth, id, "rw")) {
67
+			if err := tryRelocate(path.Join(pth, id, "rw"), path.Join(a.rootPath(), "diff", id)); err != nil {
68
+				return err
69
+			}
70
+
71
+			if !a.Exists(id) {
72
+
73
+				metadata, err := loadMetadata(path.Join(pth, id, "config.json"))
74
+				if err != nil {
75
+					return err
76
+				}
77
+
78
+				initID := fmt.Sprintf("%s-init", id)
79
+				if err := a.Create(initID, metadata.Image); err != nil {
80
+					return err
81
+				}
82
+
83
+				initPath, err := a.Get(initID)
84
+				if err != nil {
85
+					return err
86
+				}
87
+				// setup init layer
88
+				if err := setupInit(initPath); err != nil {
89
+					return err
90
+				}
91
+
92
+				if err := a.Create(id, initID); err != nil {
93
+					return err
94
+				}
95
+			}
96
+		}
97
+	}
98
+	return nil
99
+}
100
+
101
+func (a *Driver) migrateImages(pth string) error {
102
+	fis, err := ioutil.ReadDir(pth)
103
+	if err != nil {
104
+		return err
105
+	}
106
+	var (
107
+		m       = make(map[string]*metadata)
108
+		current *metadata
109
+		exists  bool
110
+	)
111
+
112
+	for _, fi := range fis {
113
+		if id := fi.Name(); fi.IsDir() && pathExists(path.Join(pth, id, "layer")) {
114
+			if current, exists = m[id]; !exists {
115
+				current, err = loadMetadata(path.Join(pth, id, "json"))
116
+				if err != nil {
117
+					return err
118
+				}
119
+				m[id] = current
120
+			}
121
+		}
122
+	}
123
+
124
+	for _, v := range m {
125
+		v.parent = m[v.ParentID]
126
+	}
127
+
128
+	migrated := make(map[string]bool)
129
+	for _, v := range m {
130
+		if err := a.migrateImage(v, pth, migrated); err != nil {
131
+			return err
132
+		}
133
+	}
134
+	return nil
135
+}
136
+
137
+func (a *Driver) migrateImage(m *metadata, pth string, migrated map[string]bool) error {
138
+	if !migrated[m.ID] {
139
+		if m.parent != nil {
140
+			a.migrateImage(m.parent, pth, migrated)
141
+		}
142
+		if err := tryRelocate(path.Join(pth, m.ID, "layer"), path.Join(a.rootPath(), "diff", m.ID)); err != nil {
143
+			return err
144
+		}
145
+		if !a.Exists(m.ID) {
146
+			if err := a.Create(m.ID, m.ParentID); err != nil {
147
+				return err
148
+			}
149
+		}
150
+		migrated[m.ID] = true
151
+	}
152
+	return nil
153
+}
154
+
155
+// tryRelocate will try to rename the old path to the new pack and if
156
+// the operation fails, it will fallback to a symlink
157
+func tryRelocate(oldPath, newPath string) error {
158
+	s, err := os.Lstat(newPath)
159
+	if err != nil && !os.IsNotExist(err) {
160
+		return err
161
+	}
162
+	// If the destination is a symlink then we already tried to relocate once before
163
+	// and it failed so we delete it and try to remove
164
+	if s != nil && s.Mode()&os.ModeSymlink == os.ModeSymlink {
165
+		if err := os.RemoveAll(newPath); err != nil {
166
+			return err
167
+		}
168
+	}
169
+	if err := os.Rename(oldPath, newPath); err != nil {
170
+		if sErr := os.Symlink(oldPath, newPath); sErr != nil {
171
+			return fmt.Errorf("Unable to relocate %s to %s: Rename err %s Symlink err %s", oldPath, newPath, err, sErr)
172
+		}
173
+	}
174
+	return nil
175
+}
176
+
177
+func loadMetadata(pth string) (*metadata, error) {
178
+	f, err := os.Open(pth)
179
+	if err != nil {
180
+		return nil, err
181
+	}
182
+	defer f.Close()
183
+
184
+	var (
185
+		out = &metadata{}
186
+		dec = json.NewDecoder(f)
187
+	)
188
+
189
+	if err := dec.Decode(out); err != nil {
190
+		return nil, err
191
+	}
192
+	return out, nil
193
+}
0 194
new file mode 100644
... ...
@@ -0,0 +1,37 @@
0
+package aufs
1
+
2
+import (
3
+	"github.com/dotcloud/docker/utils"
4
+	"os"
5
+	"os/exec"
6
+	"path/filepath"
7
+	"syscall"
8
+)
9
+
10
+func Unmount(target string) error {
11
+	if err := exec.Command("auplink", target, "flush").Run(); err != nil {
12
+		utils.Errorf("[warning]: couldn't run auplink before unmount: %s", err)
13
+	}
14
+	if err := syscall.Unmount(target, 0); err != nil {
15
+		return err
16
+	}
17
+	return nil
18
+}
19
+
20
+func Mounted(mountpoint string) (bool, error) {
21
+	mntpoint, err := os.Stat(mountpoint)
22
+	if err != nil {
23
+		if os.IsNotExist(err) {
24
+			return false, nil
25
+		}
26
+		return false, err
27
+	}
28
+	parent, err := os.Stat(filepath.Join(mountpoint, ".."))
29
+	if err != nil {
30
+		return false, err
31
+	}
32
+	mntpointSt := mntpoint.Sys().(*syscall.Stat_t)
33
+	parentSt := parent.Sys().(*syscall.Stat_t)
34
+
35
+	return mntpointSt.Dev != parentSt.Dev, nil
36
+}
0 37
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+package aufs
1
+
2
+import "errors"
3
+
4
+func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
5
+	return errors.New("mount is not implemented on darwin")
6
+}
0 7
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+package aufs
1
+
2
+import "syscall"
3
+
4
+func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
5
+	return syscall.Mount(source, target, fstype, flags, data)
6
+}
0 7
new file mode 100644
... ...
@@ -0,0 +1,956 @@
0
+package devmapper
1
+
2
+import (
3
+	"encoding/json"
4
+	"fmt"
5
+	"github.com/dotcloud/docker/utils"
6
+	"io"
7
+	"io/ioutil"
8
+	"path"
9
+	"path/filepath"
10
+	"strconv"
11
+	"sync"
12
+	"time"
13
+)
14
+
15
+var (
16
+	DefaultDataLoopbackSize     int64  = 100 * 1024 * 1024 * 1024
17
+	DefaultMetaDataLoopbackSize int64  = 2 * 1024 * 1024 * 1024
18
+	DefaultBaseFsSize           uint64 = 10 * 1024 * 1024 * 1024
19
+)
20
+
21
+type DevInfo struct {
22
+	Hash          string     `json:"-"`
23
+	DeviceId      int        `json:"device_id"`
24
+	Size          uint64     `json:"size"`
25
+	TransactionId uint64     `json:"transaction_id"`
26
+	Initialized   bool       `json:"initialized"`
27
+	devices       *DeviceSet `json:"-"`
28
+}
29
+
30
+type MetaData struct {
31
+	Devices map[string]*DevInfo `json:devices`
32
+}
33
+
34
+type DeviceSet struct {
35
+	MetaData
36
+	sync.Mutex
37
+	root             string
38
+	devicePrefix     string
39
+	TransactionId    uint64
40
+	NewTransactionId uint64
41
+	nextFreeDevice   int
42
+	activeMounts     map[string]int
43
+}
44
+
45
+type DiskUsage struct {
46
+	Used  uint64
47
+	Total uint64
48
+}
49
+
50
+type Status struct {
51
+	PoolName         string
52
+	DataLoopback     string
53
+	MetadataLoopback string
54
+	Data             DiskUsage
55
+	Metadata         DiskUsage
56
+	SectorSize       uint64
57
+}
58
+
59
+type DevStatus struct {
60
+	DeviceId            int
61
+	Size                uint64
62
+	TransactionId       uint64
63
+	SizeInSectors       uint64
64
+	MappedSectors       uint64
65
+	HighestMappedSector uint64
66
+}
67
+
68
+func getDevName(name string) string {
69
+	return "/dev/mapper/" + name
70
+}
71
+
72
+func (info *DevInfo) Name() string {
73
+	hash := info.Hash
74
+	if hash == "" {
75
+		hash = "base"
76
+	}
77
+	return fmt.Sprintf("%s-%s", info.devices.devicePrefix, hash)
78
+}
79
+
80
+func (info *DevInfo) DevName() string {
81
+	return getDevName(info.Name())
82
+}
83
+
84
+func (devices *DeviceSet) loopbackDir() string {
85
+	return path.Join(devices.root, "devicemapper")
86
+}
87
+
88
+func (devices *DeviceSet) jsonFile() string {
89
+	return path.Join(devices.loopbackDir(), "json")
90
+}
91
+
92
+func (devices *DeviceSet) getPoolName() string {
93
+	return devices.devicePrefix + "-pool"
94
+}
95
+
96
+func (devices *DeviceSet) getPoolDevName() string {
97
+	return getDevName(devices.getPoolName())
98
+}
99
+
100
+func (devices *DeviceSet) hasImage(name string) bool {
101
+	dirname := devices.loopbackDir()
102
+	filename := path.Join(dirname, name)
103
+
104
+	_, err := osStat(filename)
105
+	return err == nil
106
+}
107
+
108
+// ensureImage creates a sparse file of <size> bytes at the path
109
+// <root>/devicemapper/<name>.
110
+// If the file already exists, it does nothing.
111
+// Either way it returns the full path.
112
+func (devices *DeviceSet) ensureImage(name string, size int64) (string, error) {
113
+	dirname := devices.loopbackDir()
114
+	filename := path.Join(dirname, name)
115
+
116
+	if err := osMkdirAll(dirname, 0700); err != nil && !osIsExist(err) {
117
+		return "", err
118
+	}
119
+
120
+	if _, err := osStat(filename); err != nil {
121
+		if !osIsNotExist(err) {
122
+			return "", err
123
+		}
124
+		utils.Debugf("Creating loopback file %s for device-manage use", filename)
125
+		file, err := osOpenFile(filename, osORdWr|osOCreate, 0600)
126
+		if err != nil {
127
+			return "", err
128
+		}
129
+		defer file.Close()
130
+
131
+		if err = file.Truncate(size); err != nil {
132
+			return "", err
133
+		}
134
+	}
135
+	return filename, nil
136
+}
137
+
138
+func (devices *DeviceSet) allocateDeviceId() int {
139
+	// TODO: Add smarter reuse of deleted devices
140
+	id := devices.nextFreeDevice
141
+	devices.nextFreeDevice = devices.nextFreeDevice + 1
142
+	return id
143
+}
144
+
145
+func (devices *DeviceSet) allocateTransactionId() uint64 {
146
+	devices.NewTransactionId = devices.NewTransactionId + 1
147
+	return devices.NewTransactionId
148
+}
149
+
150
+func (devices *DeviceSet) saveMetadata() error {
151
+	jsonData, err := json.Marshal(devices.MetaData)
152
+	if err != nil {
153
+		return fmt.Errorf("Error encoding metaadata to json: %s", err)
154
+	}
155
+	tmpFile, err := ioutil.TempFile(filepath.Dir(devices.jsonFile()), ".json")
156
+	if err != nil {
157
+		return fmt.Errorf("Error creating metadata file: %s", err)
158
+	}
159
+
160
+	n, err := tmpFile.Write(jsonData)
161
+	if err != nil {
162
+		return fmt.Errorf("Error writing metadata to %s: %s", tmpFile.Name(), err)
163
+	}
164
+	if n < len(jsonData) {
165
+		return io.ErrShortWrite
166
+	}
167
+	if err := tmpFile.Sync(); err != nil {
168
+		return fmt.Errorf("Error syncing metadata file %s: %s", tmpFile.Name(), err)
169
+	}
170
+	if err := tmpFile.Close(); err != nil {
171
+		return fmt.Errorf("Error closing metadata file %s: %s", tmpFile.Name(), err)
172
+	}
173
+	if err := osRename(tmpFile.Name(), devices.jsonFile()); err != nil {
174
+		return fmt.Errorf("Error committing metadata file", err)
175
+	}
176
+
177
+	if devices.NewTransactionId != devices.TransactionId {
178
+		if err = setTransactionId(devices.getPoolDevName(), devices.TransactionId, devices.NewTransactionId); err != nil {
179
+			return fmt.Errorf("Error setting devmapper transition ID: %s", err)
180
+		}
181
+		devices.TransactionId = devices.NewTransactionId
182
+	}
183
+	return nil
184
+}
185
+
186
+func (devices *DeviceSet) registerDevice(id int, hash string, size uint64) (*DevInfo, error) {
187
+	utils.Debugf("registerDevice(%v, %v)", id, hash)
188
+	info := &DevInfo{
189
+		Hash:          hash,
190
+		DeviceId:      id,
191
+		Size:          size,
192
+		TransactionId: devices.allocateTransactionId(),
193
+		Initialized:   false,
194
+		devices:       devices,
195
+	}
196
+
197
+	devices.Devices[hash] = info
198
+	if err := devices.saveMetadata(); err != nil {
199
+		// Try to remove unused device
200
+		delete(devices.Devices, hash)
201
+		return nil, err
202
+	}
203
+
204
+	return info, nil
205
+}
206
+
207
+func (devices *DeviceSet) activateDeviceIfNeeded(hash string) error {
208
+	utils.Debugf("activateDeviceIfNeeded(%v)", hash)
209
+	info := devices.Devices[hash]
210
+	if info == nil {
211
+		return fmt.Errorf("Unknown device %s", hash)
212
+	}
213
+
214
+	if devinfo, _ := getInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
215
+		return nil
216
+	}
217
+
218
+	return activateDevice(devices.getPoolDevName(), info.Name(), info.DeviceId, info.Size)
219
+}
220
+
221
+func (devices *DeviceSet) createFilesystem(info *DevInfo) error {
222
+	devname := info.DevName()
223
+
224
+	err := execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0,lazy_journal_init=0", devname)
225
+	if err != nil {
226
+		err = execRun("mkfs.ext4", "-E", "discard,lazy_itable_init=0", devname)
227
+	}
228
+	if err != nil {
229
+		utils.Debugf("\n--->Err: %s\n", err)
230
+		return err
231
+	}
232
+	return nil
233
+}
234
+
235
+func (devices *DeviceSet) loadMetaData() error {
236
+	utils.Debugf("loadMetadata()")
237
+	defer utils.Debugf("loadMetadata END")
238
+	_, _, _, params, err := getStatus(devices.getPoolName())
239
+	if err != nil {
240
+		utils.Debugf("\n--->Err: %s\n", err)
241
+		return err
242
+	}
243
+
244
+	if _, err := fmt.Sscanf(params, "%d", &devices.TransactionId); err != nil {
245
+		utils.Debugf("\n--->Err: %s\n", err)
246
+		return err
247
+	}
248
+	devices.NewTransactionId = devices.TransactionId
249
+
250
+	jsonData, err := ioutil.ReadFile(devices.jsonFile())
251
+	if err != nil && !osIsNotExist(err) {
252
+		utils.Debugf("\n--->Err: %s\n", err)
253
+		return err
254
+	}
255
+
256
+	devices.MetaData.Devices = make(map[string]*DevInfo)
257
+	if jsonData != nil {
258
+		if err := json.Unmarshal(jsonData, &devices.MetaData); err != nil {
259
+			utils.Debugf("\n--->Err: %s\n", err)
260
+			return err
261
+		}
262
+	}
263
+
264
+	for hash, d := range devices.Devices {
265
+		d.Hash = hash
266
+		d.devices = devices
267
+
268
+		if d.DeviceId >= devices.nextFreeDevice {
269
+			devices.nextFreeDevice = d.DeviceId + 1
270
+		}
271
+
272
+		// If the transaction id is larger than the actual one we lost the device due to some crash
273
+		if d.TransactionId > devices.TransactionId {
274
+			utils.Debugf("Removing lost device %s with id %d", hash, d.TransactionId)
275
+			delete(devices.Devices, hash)
276
+		}
277
+	}
278
+	return nil
279
+}
280
+
281
+func (devices *DeviceSet) setupBaseImage() error {
282
+	oldInfo := devices.Devices[""]
283
+	if oldInfo != nil && oldInfo.Initialized {
284
+		return nil
285
+	}
286
+
287
+	if oldInfo != nil && !oldInfo.Initialized {
288
+		utils.Debugf("Removing uninitialized base image")
289
+		if err := devices.removeDevice(""); err != nil {
290
+			utils.Debugf("\n--->Err: %s\n", err)
291
+			return err
292
+		}
293
+	}
294
+
295
+	utils.Debugf("Initializing base device-manager snapshot")
296
+
297
+	id := devices.allocateDeviceId()
298
+
299
+	// Create initial device
300
+	if err := createDevice(devices.getPoolDevName(), id); err != nil {
301
+		utils.Debugf("\n--->Err: %s\n", err)
302
+		return err
303
+	}
304
+
305
+	utils.Debugf("Registering base device (id %v) with FS size %v", id, DefaultBaseFsSize)
306
+	info, err := devices.registerDevice(id, "", DefaultBaseFsSize)
307
+	if err != nil {
308
+		_ = deleteDevice(devices.getPoolDevName(), id)
309
+		utils.Debugf("\n--->Err: %s\n", err)
310
+		return err
311
+	}
312
+
313
+	utils.Debugf("Creating filesystem on base device-manager snapshot")
314
+
315
+	if err = devices.activateDeviceIfNeeded(""); err != nil {
316
+		utils.Debugf("\n--->Err: %s\n", err)
317
+		return err
318
+	}
319
+
320
+	if err := devices.createFilesystem(info); err != nil {
321
+		utils.Debugf("\n--->Err: %s\n", err)
322
+		return err
323
+	}
324
+
325
+	info.Initialized = true
326
+	if err = devices.saveMetadata(); err != nil {
327
+		info.Initialized = false
328
+		utils.Debugf("\n--->Err: %s\n", err)
329
+		return err
330
+	}
331
+
332
+	return nil
333
+}
334
+
335
+func setCloseOnExec(name string) {
336
+	if fileInfos, _ := ioutil.ReadDir("/proc/self/fd"); fileInfos != nil {
337
+		for _, i := range fileInfos {
338
+			link, _ := osReadlink(filepath.Join("/proc/self/fd", i.Name()))
339
+			if link == name {
340
+				fd, err := strconv.Atoi(i.Name())
341
+				if err == nil {
342
+					sysCloseOnExec(fd)
343
+				}
344
+			}
345
+		}
346
+	}
347
+}
348
+
349
+func (devices *DeviceSet) log(level int, file string, line int, dmError int, message string) {
350
+	if level >= 7 {
351
+		return // Ignore _LOG_DEBUG
352
+	}
353
+
354
+	utils.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
355
+}
356
+
357
+func major(device uint64) uint64 {
358
+	return (device >> 8) & 0xfff
359
+}
360
+
361
+func minor(device uint64) uint64 {
362
+	return (device & 0xff) | ((device >> 12) & 0xfff00)
363
+}
364
+
365
+func (devices *DeviceSet) ResizePool(size int64) error {
366
+	dirname := devices.loopbackDir()
367
+	datafilename := path.Join(dirname, "data")
368
+	metadatafilename := path.Join(dirname, "metadata")
369
+
370
+	datafile, err := osOpenFile(datafilename, osORdWr, 0)
371
+	if datafile == nil {
372
+		return err
373
+	}
374
+	defer datafile.Close()
375
+
376
+	fi, err := datafile.Stat()
377
+	if fi == nil {
378
+		return err
379
+	}
380
+
381
+	if fi.Size() > size {
382
+		return fmt.Errorf("Can't shrink file")
383
+	}
384
+
385
+	dataloopback := FindLoopDeviceFor(&osFile{File: datafile})
386
+	if dataloopback == nil {
387
+		return fmt.Errorf("Unable to find loopback mount for: %s", datafilename)
388
+	}
389
+	defer dataloopback.Close()
390
+
391
+	metadatafile, err := osOpenFile(metadatafilename, osORdWr, 0)
392
+	if metadatafile == nil {
393
+		return err
394
+	}
395
+	defer metadatafile.Close()
396
+
397
+	metadataloopback := FindLoopDeviceFor(&osFile{File: metadatafile})
398
+	if metadataloopback == nil {
399
+		return fmt.Errorf("Unable to find loopback mount for: %s", metadatafilename)
400
+	}
401
+	defer metadataloopback.Close()
402
+
403
+	// Grow loopback file
404
+	if err := datafile.Truncate(size); err != nil {
405
+		return fmt.Errorf("Unable to grow loopback file: %s", err)
406
+	}
407
+
408
+	// Reload size for loopback device
409
+	if err := LoopbackSetCapacity(dataloopback); err != nil {
410
+		return fmt.Errorf("Unable to update loopback capacity: %s", err)
411
+	}
412
+
413
+	// Suspend the pool
414
+	if err := suspendDevice(devices.getPoolName()); err != nil {
415
+		return fmt.Errorf("Unable to suspend pool: %s", err)
416
+	}
417
+
418
+	// Reload with the new block sizes
419
+	if err := reloadPool(devices.getPoolName(), dataloopback, metadataloopback); err != nil {
420
+		return fmt.Errorf("Unable to reload pool: %s", err)
421
+	}
422
+
423
+	// Resume the pool
424
+	if err := resumeDevice(devices.getPoolName()); err != nil {
425
+		return fmt.Errorf("Unable to resume pool: %s", err)
426
+	}
427
+
428
+	return nil
429
+}
430
+
431
+func (devices *DeviceSet) initDevmapper(doInit bool) error {
432
+	logInit(devices)
433
+
434
+	// Make sure the sparse images exist in <root>/devicemapper/data and
435
+	// <root>/devicemapper/metadata
436
+
437
+	hasData := devices.hasImage("data")
438
+	hasMetadata := devices.hasImage("metadata")
439
+
440
+	if !doInit && !hasData {
441
+		return fmt.Errorf("Looback data file not found %s")
442
+	}
443
+
444
+	if !doInit && !hasMetadata {
445
+		return fmt.Errorf("Looback metadata file not found %s")
446
+	}
447
+
448
+	createdLoopback := !hasData || !hasMetadata
449
+	data, err := devices.ensureImage("data", DefaultDataLoopbackSize)
450
+	if err != nil {
451
+		utils.Debugf("Error device ensureImage (data): %s\n", err)
452
+		return err
453
+	}
454
+	metadata, err := devices.ensureImage("metadata", DefaultMetaDataLoopbackSize)
455
+	if err != nil {
456
+		utils.Debugf("Error device ensureImage (metadata): %s\n", err)
457
+		return err
458
+	}
459
+
460
+	// Set the device prefix from the device id and inode of the docker root dir
461
+
462
+	st, err := osStat(devices.root)
463
+	if err != nil {
464
+		return fmt.Errorf("Error looking up dir %s: %s", devices.root, err)
465
+	}
466
+	sysSt := toSysStatT(st.Sys())
467
+	// "reg-" stands for "regular file".
468
+	// In the future we might use "dev-" for "device file", etc.
469
+	// docker-maj,min[-inode] stands for:
470
+	//	- Managed by docker
471
+	//	- The target of this device is at major <maj> and minor <min>
472
+	//	- If <inode> is defined, use that file inside the device as a loopback image. Otherwise use the device itself.
473
+	devices.devicePrefix = fmt.Sprintf("docker-%d:%d-%d", major(sysSt.Dev), minor(sysSt.Dev), sysSt.Ino)
474
+	utils.Debugf("Generated prefix: %s", devices.devicePrefix)
475
+
476
+	// Check for the existence of the device <prefix>-pool
477
+	utils.Debugf("Checking for existence of the pool '%s'", devices.getPoolName())
478
+	info, err := getInfo(devices.getPoolName())
479
+	if info == nil {
480
+		utils.Debugf("Error device getInfo: %s", err)
481
+		return err
482
+	}
483
+
484
+	// It seems libdevmapper opens this without O_CLOEXEC, and go exec will not close files
485
+	// that are not Close-on-exec, and lxc-start will die if it inherits any unexpected files,
486
+	// so we add this badhack to make sure it closes itself
487
+	setCloseOnExec("/dev/mapper/control")
488
+
489
+	// If the pool doesn't exist, create it
490
+	if info.Exists == 0 {
491
+		utils.Debugf("Pool doesn't exist. Creating it.")
492
+
493
+		dataFile, err := AttachLoopDevice(data)
494
+		if err != nil {
495
+			utils.Debugf("\n--->Err: %s\n", err)
496
+			return err
497
+		}
498
+		defer dataFile.Close()
499
+
500
+		metadataFile, err := AttachLoopDevice(metadata)
501
+		if err != nil {
502
+			utils.Debugf("\n--->Err: %s\n", err)
503
+			return err
504
+		}
505
+		defer metadataFile.Close()
506
+
507
+		if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil {
508
+			utils.Debugf("\n--->Err: %s\n", err)
509
+			return err
510
+		}
511
+	}
512
+
513
+	// If we didn't just create the data or metadata image, we need to
514
+	// load the metadata from the existing file.
515
+	if !createdLoopback {
516
+		if err = devices.loadMetaData(); err != nil {
517
+			utils.Debugf("\n--->Err: %s\n", err)
518
+			return err
519
+		}
520
+	}
521
+
522
+	// Setup the base image
523
+	if doInit {
524
+		if err := devices.setupBaseImage(); err != nil {
525
+			utils.Debugf("Error device setupBaseImage: %s\n", err)
526
+			return err
527
+		}
528
+	}
529
+
530
+	return nil
531
+}
532
+
533
+func (devices *DeviceSet) AddDevice(hash, baseHash string) error {
534
+	devices.Lock()
535
+	defer devices.Unlock()
536
+
537
+	if devices.Devices[hash] != nil {
538
+		return fmt.Errorf("hash %s already exists", hash)
539
+	}
540
+
541
+	baseInfo := devices.Devices[baseHash]
542
+	if baseInfo == nil {
543
+		return fmt.Errorf("Error adding device for '%s': can't find device for parent '%s'", hash, baseHash)
544
+	}
545
+
546
+	deviceId := devices.allocateDeviceId()
547
+
548
+	if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
549
+		utils.Debugf("Error creating snap device: %s\n", err)
550
+		return err
551
+	}
552
+
553
+	if _, err := devices.registerDevice(deviceId, hash, baseInfo.Size); err != nil {
554
+		deleteDevice(devices.getPoolDevName(), deviceId)
555
+		utils.Debugf("Error registering device: %s\n", err)
556
+		return err
557
+	}
558
+	return nil
559
+}
560
+
561
+func (devices *DeviceSet) removeDevice(hash string) error {
562
+	info := devices.Devices[hash]
563
+	if info == nil {
564
+		return fmt.Errorf("hash %s doesn't exists", hash)
565
+	}
566
+
567
+	devinfo, _ := getInfo(info.Name())
568
+	if devinfo != nil && devinfo.Exists != 0 {
569
+		if err := removeDevice(info.Name()); err != nil {
570
+			utils.Debugf("Error removing device: %s\n", err)
571
+			return err
572
+		}
573
+	}
574
+
575
+	if info.Initialized {
576
+		info.Initialized = false
577
+		if err := devices.saveMetadata(); err != nil {
578
+			utils.Debugf("Error saving meta data: %s\n", err)
579
+			return err
580
+		}
581
+	}
582
+
583
+	if err := deleteDevice(devices.getPoolDevName(), info.DeviceId); err != nil {
584
+		utils.Debugf("Error deleting device: %s\n", err)
585
+		return err
586
+	}
587
+
588
+	devices.allocateTransactionId()
589
+	delete(devices.Devices, info.Hash)
590
+
591
+	if err := devices.saveMetadata(); err != nil {
592
+		devices.Devices[info.Hash] = info
593
+		utils.Debugf("Error saving meta data: %s\n", err)
594
+		return err
595
+	}
596
+
597
+	return nil
598
+}
599
+
600
+func (devices *DeviceSet) RemoveDevice(hash string) error {
601
+	devices.Lock()
602
+	defer devices.Unlock()
603
+
604
+	return devices.removeDevice(hash)
605
+}
606
+
607
+func (devices *DeviceSet) deactivateDevice(hash string) error {
608
+	utils.Debugf("[devmapper] deactivateDevice(%s)", hash)
609
+	defer utils.Debugf("[devmapper] deactivateDevice END")
610
+	var devname string
611
+	// FIXME: shouldn't we just register the pool into devices?
612
+	devname, err := devices.byHash(hash)
613
+	if err != nil {
614
+		return err
615
+	}
616
+	devinfo, err := getInfo(devname)
617
+	if err != nil {
618
+		utils.Debugf("\n--->Err: %s\n", err)
619
+		return err
620
+	}
621
+	if devinfo.Exists != 0 {
622
+		if err := removeDevice(devname); err != nil {
623
+			utils.Debugf("\n--->Err: %s\n", err)
624
+			return err
625
+		}
626
+		if err := devices.waitRemove(hash); err != nil {
627
+			return err
628
+		}
629
+	}
630
+
631
+	return nil
632
+}
633
+
634
+// waitRemove blocks until either:
635
+// a) the device registered at <device_set_prefix>-<hash> is removed,
636
+// or b) the 1 second timeout expires.
637
+func (devices *DeviceSet) waitRemove(hash string) error {
638
+	utils.Debugf("[deviceset %s] waitRemove(%s)", devices.devicePrefix, hash)
639
+	defer utils.Debugf("[deviceset %s] waitRemove END", devices.devicePrefix, hash)
640
+	devname, err := devices.byHash(hash)
641
+	if err != nil {
642
+		return err
643
+	}
644
+	i := 0
645
+	for ; i < 1000; i += 1 {
646
+		devinfo, err := getInfo(devname)
647
+		if err != nil {
648
+			// If there is an error we assume the device doesn't exist.
649
+			// The error might actually be something else, but we can't differentiate.
650
+			return nil
651
+		}
652
+		if i%100 == 0 {
653
+			utils.Debugf("Waiting for removal of %s: exists=%d", devname, devinfo.Exists)
654
+		}
655
+		if devinfo.Exists == 0 {
656
+			break
657
+		}
658
+
659
+		time.Sleep(1 * time.Millisecond)
660
+	}
661
+	if i == 1000 {
662
+		return fmt.Errorf("Timeout while waiting for device %s to be removed", devname)
663
+	}
664
+	return nil
665
+}
666
+
667
+// waitClose blocks until either:
668
+// a) the device registered at <device_set_prefix>-<hash> is closed,
669
+// or b) the 1 second timeout expires.
670
+func (devices *DeviceSet) waitClose(hash string) error {
671
+	devname, err := devices.byHash(hash)
672
+	if err != nil {
673
+		return err
674
+	}
675
+	i := 0
676
+	for ; i < 1000; i += 1 {
677
+		devinfo, err := getInfo(devname)
678
+		if err != nil {
679
+			return err
680
+		}
681
+		if i%100 == 0 {
682
+			utils.Debugf("Waiting for unmount of %s: opencount=%d", devname, devinfo.OpenCount)
683
+		}
684
+		if devinfo.OpenCount == 0 {
685
+			break
686
+		}
687
+		time.Sleep(1 * time.Millisecond)
688
+	}
689
+	if i == 1000 {
690
+		return fmt.Errorf("Timeout while waiting for device %s to close", devname)
691
+	}
692
+	return nil
693
+}
694
+
695
+// byHash is a hack to allow looking up the deviceset's pool by the hash "pool".
696
+// FIXME: it seems probably cleaner to register the pool in devices.Devices,
697
+// but I am afraid of arcane implications deep in the devicemapper code,
698
+// so this will do.
699
+func (devices *DeviceSet) byHash(hash string) (devname string, err error) {
700
+	if hash == "pool" {
701
+		return devices.getPoolDevName(), nil
702
+	}
703
+	info := devices.Devices[hash]
704
+	if info == nil {
705
+		return "", fmt.Errorf("hash %s doesn't exists", hash)
706
+	}
707
+	return info.Name(), nil
708
+}
709
+
710
+func (devices *DeviceSet) Shutdown() error {
711
+	devices.Lock()
712
+	defer devices.Unlock()
713
+
714
+	utils.Debugf("[deviceset %s] shutdown()", devices.devicePrefix)
715
+	utils.Debugf("[devmapper] Shutting down DeviceSet: %s", devices.root)
716
+	defer utils.Debugf("[deviceset %s] shutdown END", devices.devicePrefix)
717
+
718
+	for path, count := range devices.activeMounts {
719
+		for i := count; i > 0; i-- {
720
+			if err := sysUnmount(path, 0); err != nil {
721
+				utils.Debugf("Shutdown unmounting %s, error: %s\n", path, err)
722
+			}
723
+		}
724
+		delete(devices.activeMounts, path)
725
+	}
726
+
727
+	for _, d := range devices.Devices {
728
+		if err := devices.waitClose(d.Hash); err != nil {
729
+			utils.Errorf("Warning: error waiting for device %s to unmount: %s\n", d.Hash, err)
730
+		}
731
+		if err := devices.deactivateDevice(d.Hash); err != nil {
732
+			utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err)
733
+		}
734
+	}
735
+
736
+	pool := devices.getPoolDevName()
737
+	if devinfo, err := getInfo(pool); err == nil && devinfo.Exists != 0 {
738
+		if err := devices.deactivateDevice("pool"); err != nil {
739
+			utils.Debugf("Shutdown deactivate %s , error: %s\n", pool, err)
740
+		}
741
+	}
742
+
743
+	return nil
744
+}
745
+
746
+func (devices *DeviceSet) MountDevice(hash, path string, readOnly bool) error {
747
+	devices.Lock()
748
+	defer devices.Unlock()
749
+
750
+	if err := devices.activateDeviceIfNeeded(hash); err != nil {
751
+		return fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err)
752
+	}
753
+
754
+	info := devices.Devices[hash]
755
+
756
+	var flags uintptr = sysMsMgcVal
757
+
758
+	if readOnly {
759
+		flags = flags | sysMsRdOnly
760
+	}
761
+
762
+	err := sysMount(info.DevName(), path, "ext4", flags, "discard")
763
+	if err != nil && err == sysEInval {
764
+		err = sysMount(info.DevName(), path, "ext4", flags, "")
765
+	}
766
+	if err != nil {
767
+		return fmt.Errorf("Error mounting '%s' on '%s': %s", info.DevName(), path, err)
768
+	}
769
+
770
+	count := devices.activeMounts[path]
771
+	devices.activeMounts[path] = count + 1
772
+
773
+	return devices.setInitialized(hash)
774
+}
775
+
776
+func (devices *DeviceSet) UnmountDevice(hash, path string, deactivate bool) error {
777
+	utils.Debugf("[devmapper] UnmountDevice(hash=%s path=%s)", hash, path)
778
+	defer utils.Debugf("[devmapper] UnmountDevice END")
779
+	devices.Lock()
780
+	defer devices.Unlock()
781
+
782
+	utils.Debugf("[devmapper] Unmount(%s)", path)
783
+	if err := sysUnmount(path, 0); err != nil {
784
+		utils.Debugf("\n--->Err: %s\n", err)
785
+		return err
786
+	}
787
+	utils.Debugf("[devmapper] Unmount done")
788
+	// Wait for the unmount to be effective,
789
+	// by watching the value of Info.OpenCount for the device
790
+	if err := devices.waitClose(hash); err != nil {
791
+		return err
792
+	}
793
+
794
+	if count := devices.activeMounts[path]; count > 1 {
795
+		devices.activeMounts[path] = count - 1
796
+	} else {
797
+		delete(devices.activeMounts, path)
798
+	}
799
+
800
+	if deactivate {
801
+		devices.deactivateDevice(hash)
802
+	}
803
+
804
+	return nil
805
+}
806
+
807
+func (devices *DeviceSet) HasDevice(hash string) bool {
808
+	devices.Lock()
809
+	defer devices.Unlock()
810
+
811
+	return devices.Devices[hash] != nil
812
+}
813
+
814
+func (devices *DeviceSet) HasInitializedDevice(hash string) bool {
815
+	devices.Lock()
816
+	defer devices.Unlock()
817
+
818
+	info := devices.Devices[hash]
819
+	return info != nil && info.Initialized
820
+}
821
+
822
+func (devices *DeviceSet) HasActivatedDevice(hash string) bool {
823
+	devices.Lock()
824
+	defer devices.Unlock()
825
+
826
+	info := devices.Devices[hash]
827
+	if info == nil {
828
+		return false
829
+	}
830
+	devinfo, _ := getInfo(info.Name())
831
+	return devinfo != nil && devinfo.Exists != 0
832
+}
833
+
834
+func (devices *DeviceSet) setInitialized(hash string) error {
835
+	info := devices.Devices[hash]
836
+	if info == nil {
837
+		return fmt.Errorf("Unknown device %s", hash)
838
+	}
839
+
840
+	info.Initialized = true
841
+	if err := devices.saveMetadata(); err != nil {
842
+		info.Initialized = false
843
+		utils.Debugf("\n--->Err: %s\n", err)
844
+		return err
845
+	}
846
+
847
+	return nil
848
+}
849
+
850
+func (devices *DeviceSet) List() []string {
851
+	devices.Lock()
852
+	defer devices.Unlock()
853
+
854
+	ids := make([]string, len(devices.Devices))
855
+	i := 0
856
+	for k := range devices.Devices {
857
+		ids[i] = k
858
+		i++
859
+	}
860
+	return ids
861
+}
862
+
863
+func (devices *DeviceSet) deviceStatus(devName string) (sizeInSectors, mappedSectors, highestMappedSector uint64, err error) {
864
+	var params string
865
+	_, sizeInSectors, _, params, err = getStatus(devName)
866
+	if err != nil {
867
+		return
868
+	}
869
+	if _, err = fmt.Sscanf(params, "%d %d", &mappedSectors, &highestMappedSector); err == nil {
870
+		return
871
+	}
872
+	return
873
+}
874
+
875
+func (devices *DeviceSet) GetDeviceStatus(hash string) (*DevStatus, error) {
876
+	devices.Lock()
877
+	defer devices.Unlock()
878
+
879
+	info := devices.Devices[hash]
880
+	if info == nil {
881
+		return nil, fmt.Errorf("No device %s", hash)
882
+	}
883
+
884
+	status := &DevStatus{
885
+		DeviceId:      info.DeviceId,
886
+		Size:          info.Size,
887
+		TransactionId: info.TransactionId,
888
+	}
889
+
890
+	if err := devices.activateDeviceIfNeeded(hash); err != nil {
891
+		return nil, fmt.Errorf("Error activating devmapper device for '%s': %s", hash, err)
892
+	}
893
+
894
+	if sizeInSectors, mappedSectors, highestMappedSector, err := devices.deviceStatus(info.DevName()); err != nil {
895
+		return nil, err
896
+	} else {
897
+		status.SizeInSectors = sizeInSectors
898
+		status.MappedSectors = mappedSectors
899
+		status.HighestMappedSector = highestMappedSector
900
+	}
901
+
902
+	return status, nil
903
+}
904
+
905
+func (devices *DeviceSet) poolStatus() (totalSizeInSectors, transactionId, dataUsed, dataTotal, metadataUsed, metadataTotal uint64, err error) {
906
+	var params string
907
+	if _, totalSizeInSectors, _, params, err = getStatus(devices.getPoolName()); err == nil {
908
+		_, err = fmt.Sscanf(params, "%d %d/%d %d/%d", &transactionId, &metadataUsed, &metadataTotal, &dataUsed, &dataTotal)
909
+	}
910
+	return
911
+}
912
+
913
+func (devices *DeviceSet) Status() *Status {
914
+	devices.Lock()
915
+	defer devices.Unlock()
916
+
917
+	status := &Status{}
918
+
919
+	status.PoolName = devices.getPoolName()
920
+	status.DataLoopback = path.Join(devices.loopbackDir(), "data")
921
+	status.MetadataLoopback = path.Join(devices.loopbackDir(), "metadata")
922
+
923
+	totalSizeInSectors, _, dataUsed, dataTotal, metadataUsed, metadataTotal, err := devices.poolStatus()
924
+	if err == nil {
925
+		// Convert from blocks to bytes
926
+		blockSizeInSectors := totalSizeInSectors / dataTotal
927
+
928
+		status.Data.Used = dataUsed * blockSizeInSectors * 512
929
+		status.Data.Total = dataTotal * blockSizeInSectors * 512
930
+
931
+		// metadata blocks are always 4k
932
+		status.Metadata.Used = metadataUsed * 4096
933
+		status.Metadata.Total = metadataTotal * 4096
934
+
935
+		status.SectorSize = blockSizeInSectors * 512
936
+	}
937
+
938
+	return status
939
+}
940
+
941
+func NewDeviceSet(root string, doInit bool) (*DeviceSet, error) {
942
+	SetDevDir("/dev")
943
+
944
+	devices := &DeviceSet{
945
+		root:         root,
946
+		MetaData:     MetaData{Devices: make(map[string]*DevInfo)},
947
+		activeMounts: make(map[string]int),
948
+	}
949
+
950
+	if err := devices.initDevmapper(doInit); err != nil {
951
+		return nil, err
952
+	}
953
+
954
+	return devices, nil
955
+}
0 956
new file mode 100644
... ...
@@ -0,0 +1,576 @@
0
+package devmapper
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"github.com/dotcloud/docker/utils"
6
+	"runtime"
7
+)
8
+
9
+type DevmapperLogger interface {
10
+	log(level int, file string, line int, dmError int, message string)
11
+}
12
+
13
+const (
14
+	DeviceCreate TaskType = iota
15
+	DeviceReload
16
+	DeviceRemove
17
+	DeviceRemoveAll
18
+	DeviceSuspend
19
+	DeviceResume
20
+	DeviceInfo
21
+	DeviceDeps
22
+	DeviceRename
23
+	DeviceVersion
24
+	DeviceStatus
25
+	DeviceTable
26
+	DeviceWaitevent
27
+	DeviceList
28
+	DeviceClear
29
+	DeviceMknodes
30
+	DeviceListVersions
31
+	DeviceTargetMsg
32
+	DeviceSetGeometry
33
+)
34
+
35
+const (
36
+	AddNodeOnResume AddNodeType = iota
37
+	AddNodeOnCreate
38
+)
39
+
40
+var (
41
+	ErrTaskRun                = errors.New("dm_task_run failed")
42
+	ErrTaskSetName            = errors.New("dm_task_set_name failed")
43
+	ErrTaskSetMessage         = errors.New("dm_task_set_message failed")
44
+	ErrTaskSetAddNode         = errors.New("dm_task_set_add_node failed")
45
+	ErrTaskSetRo              = errors.New("dm_task_set_ro failed")
46
+	ErrTaskAddTarget          = errors.New("dm_task_add_target failed")
47
+	ErrTaskSetSector          = errors.New("dm_task_set_sector failed")
48
+	ErrTaskGetInfo            = errors.New("dm_task_get_info failed")
49
+	ErrTaskSetCookie          = errors.New("dm_task_set_cookie failed")
50
+	ErrNilCookie              = errors.New("cookie ptr can't be nil")
51
+	ErrAttachLoopbackDevice   = errors.New("loopback mounting failed")
52
+	ErrGetBlockSize           = errors.New("Can't get block size")
53
+	ErrUdevWait               = errors.New("wait on udev cookie failed")
54
+	ErrSetDevDir              = errors.New("dm_set_dev_dir failed")
55
+	ErrGetLibraryVersion      = errors.New("dm_get_library_version failed")
56
+	ErrCreateRemoveTask       = errors.New("Can't create task of type DeviceRemove")
57
+	ErrRunRemoveDevice        = errors.New("running removeDevice failed")
58
+	ErrInvalidAddNode         = errors.New("Invalide AddNoce type")
59
+	ErrGetLoopbackBackingFile = errors.New("Unable to get loopback backing file")
60
+	ErrLoopbackSetCapacity    = errors.New("Unable set loopback capacity")
61
+)
62
+
63
+type (
64
+	Task struct {
65
+		unmanaged *CDmTask
66
+	}
67
+	Info struct {
68
+		Exists        int
69
+		Suspended     int
70
+		LiveTable     int
71
+		InactiveTable int
72
+		OpenCount     int32
73
+		EventNr       uint32
74
+		Major         uint32
75
+		Minor         uint32
76
+		ReadOnly      int
77
+		TargetCount   int32
78
+	}
79
+	TaskType    int
80
+	AddNodeType int
81
+)
82
+
83
+func (t *Task) destroy() {
84
+	if t != nil {
85
+		DmTaskDestroy(t.unmanaged)
86
+		runtime.SetFinalizer(t, nil)
87
+	}
88
+}
89
+
90
+func TaskCreate(tasktype TaskType) *Task {
91
+	Ctask := DmTaskCreate(int(tasktype))
92
+	if Ctask == nil {
93
+		return nil
94
+	}
95
+	task := &Task{unmanaged: Ctask}
96
+	runtime.SetFinalizer(task, (*Task).destroy)
97
+	return task
98
+}
99
+
100
+func (t *Task) Run() error {
101
+	if res := DmTaskRun(t.unmanaged); res != 1 {
102
+		return ErrTaskRun
103
+	}
104
+	return nil
105
+}
106
+
107
+func (t *Task) SetName(name string) error {
108
+	if res := DmTaskSetName(t.unmanaged, name); res != 1 {
109
+		return ErrTaskSetName
110
+	}
111
+	return nil
112
+}
113
+
114
+func (t *Task) SetMessage(message string) error {
115
+	if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
116
+		return ErrTaskSetMessage
117
+	}
118
+	return nil
119
+}
120
+
121
+func (t *Task) SetSector(sector uint64) error {
122
+	if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
123
+		return ErrTaskSetSector
124
+	}
125
+	return nil
126
+}
127
+
128
+func (t *Task) SetCookie(cookie *uint, flags uint16) error {
129
+	if cookie == nil {
130
+		return ErrNilCookie
131
+	}
132
+	if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 {
133
+		return ErrTaskSetCookie
134
+	}
135
+	return nil
136
+}
137
+
138
+func (t *Task) SetAddNode(addNode AddNodeType) error {
139
+	if addNode != AddNodeOnResume && addNode != AddNodeOnCreate {
140
+		return ErrInvalidAddNode
141
+	}
142
+	if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
143
+		return ErrTaskSetAddNode
144
+	}
145
+	return nil
146
+}
147
+
148
+func (t *Task) SetRo() error {
149
+	if res := DmTaskSetRo(t.unmanaged); res != 1 {
150
+		return ErrTaskSetRo
151
+	}
152
+	return nil
153
+}
154
+
155
+func (t *Task) AddTarget(start, size uint64, ttype, params string) error {
156
+	if res := DmTaskAddTarget(t.unmanaged, start, size,
157
+		ttype, params); res != 1 {
158
+		return ErrTaskAddTarget
159
+	}
160
+	return nil
161
+}
162
+
163
+func (t *Task) GetInfo() (*Info, error) {
164
+	info := &Info{}
165
+	if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
166
+		return nil, ErrTaskGetInfo
167
+	}
168
+	return info, nil
169
+}
170
+
171
+func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64,
172
+	length uint64, targetType string, params string) {
173
+
174
+	return DmGetNextTarget(t.unmanaged, next, &start, &length,
175
+			&targetType, &params),
176
+		start, length, targetType, params
177
+}
178
+
179
+func AttachLoopDevice(filename string) (*osFile, error) {
180
+	var fd int
181
+	res := DmAttachLoopDevice(filename, &fd)
182
+	if res == "" {
183
+		return nil, ErrAttachLoopbackDevice
184
+	}
185
+	return &osFile{File: osNewFile(uintptr(fd), res)}, nil
186
+}
187
+
188
+func getLoopbackBackingFile(file *osFile) (uint64, uint64, error) {
189
+	dev, inode, err := DmGetLoopbackBackingFile(file.Fd())
190
+	if err != 0 {
191
+		return 0, 0, ErrGetLoopbackBackingFile
192
+	}
193
+	return dev, inode, nil
194
+}
195
+
196
+func LoopbackSetCapacity(file *osFile) error {
197
+	if err := DmLoopbackSetCapacity(file.Fd()); err != 0 {
198
+		return ErrLoopbackSetCapacity
199
+	}
200
+	return nil
201
+}
202
+
203
+func FindLoopDeviceFor(file *osFile) *osFile {
204
+	stat, err := file.Stat()
205
+	if err != nil {
206
+		return nil
207
+	}
208
+	targetInode := stat.Sys().(*sysStatT).Ino
209
+	targetDevice := stat.Sys().(*sysStatT).Dev
210
+
211
+	for i := 0; true; i++ {
212
+		path := fmt.Sprintf("/dev/loop%d", i)
213
+
214
+		file, err := osOpenFile(path, osORdWr, 0)
215
+		if err != nil {
216
+			if osIsNotExist(err) {
217
+				return nil
218
+			}
219
+
220
+			// Ignore all errors until the first not-exist
221
+			// we want to continue looking for the file
222
+			continue
223
+		}
224
+
225
+		dev, inode, err := getLoopbackBackingFile(&osFile{File: file})
226
+		if err == nil && dev == targetDevice && inode == targetInode {
227
+			return &osFile{File: file}
228
+		}
229
+
230
+		file.Close()
231
+	}
232
+
233
+	return nil
234
+}
235
+
236
+func UdevWait(cookie uint) error {
237
+	if res := DmUdevWait(cookie); res != 1 {
238
+		utils.Debugf("Failed to wait on udev cookie %d", cookie)
239
+		return ErrUdevWait
240
+	}
241
+	return nil
242
+}
243
+
244
+func LogInitVerbose(level int) {
245
+	DmLogInitVerbose(level)
246
+}
247
+
248
+var dmLogger DevmapperLogger = nil
249
+
250
+func logInit(logger DevmapperLogger) {
251
+	dmLogger = logger
252
+	LogWithErrnoInit()
253
+}
254
+
255
+func SetDevDir(dir string) error {
256
+	if res := DmSetDevDir(dir); res != 1 {
257
+		utils.Debugf("Error dm_set_dev_dir")
258
+		return ErrSetDevDir
259
+	}
260
+	return nil
261
+}
262
+
263
+func GetLibraryVersion() (string, error) {
264
+	var version string
265
+	if res := DmGetLibraryVersion(&version); res != 1 {
266
+		return "", ErrGetLibraryVersion
267
+	}
268
+	return version, nil
269
+}
270
+
271
+// Useful helper for cleanup
272
+func RemoveDevice(name string) error {
273
+	task := TaskCreate(DeviceRemove)
274
+	if task == nil {
275
+		return ErrCreateRemoveTask
276
+	}
277
+	if err := task.SetName(name); err != nil {
278
+		utils.Debugf("Can't set task name %s", name)
279
+		return err
280
+	}
281
+	if err := task.Run(); err != nil {
282
+		return ErrRunRemoveDevice
283
+	}
284
+	return nil
285
+}
286
+
287
+func GetBlockDeviceSize(file *osFile) (uint64, error) {
288
+	size, errno := DmGetBlockSize(file.Fd())
289
+	if size == -1 || errno != 0 {
290
+		return 0, ErrGetBlockSize
291
+	}
292
+	return uint64(size), nil
293
+}
294
+
295
+// This is the programmatic example of "dmsetup create"
296
+func createPool(poolName string, dataFile, metadataFile *osFile) error {
297
+	task, err := createTask(DeviceCreate, poolName)
298
+	if task == nil {
299
+		return err
300
+	}
301
+
302
+	size, err := GetBlockDeviceSize(dataFile)
303
+	if err != nil {
304
+		return fmt.Errorf("Can't get data size")
305
+	}
306
+
307
+	params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768"
308
+	if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
309
+		return fmt.Errorf("Can't add target")
310
+	}
311
+
312
+	var cookie uint = 0
313
+	if err := task.SetCookie(&cookie, 0); err != nil {
314
+		return fmt.Errorf("Can't set cookie")
315
+	}
316
+
317
+	if err := task.Run(); err != nil {
318
+		return fmt.Errorf("Error running DeviceCreate (createPool)")
319
+	}
320
+
321
+	UdevWait(cookie)
322
+
323
+	return nil
324
+}
325
+
326
+func reloadPool(poolName string, dataFile, metadataFile *osFile) error {
327
+	task, err := createTask(DeviceReload, poolName)
328
+	if task == nil {
329
+		return err
330
+	}
331
+
332
+	size, err := GetBlockDeviceSize(dataFile)
333
+	if err != nil {
334
+		return fmt.Errorf("Can't get data size")
335
+	}
336
+
337
+	params := metadataFile.Name() + " " + dataFile.Name() + " 128 32768"
338
+	if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
339
+		return fmt.Errorf("Can't add target")
340
+	}
341
+
342
+	if err := task.Run(); err != nil {
343
+		return fmt.Errorf("Error running DeviceCreate")
344
+	}
345
+
346
+	return nil
347
+}
348
+
349
+func createTask(t TaskType, name string) (*Task, error) {
350
+	task := TaskCreate(t)
351
+	if task == nil {
352
+		return nil, fmt.Errorf("Can't create task of type %d", int(t))
353
+	}
354
+	if err := task.SetName(name); err != nil {
355
+		return nil, fmt.Errorf("Can't set task name %s", name)
356
+	}
357
+	return task, nil
358
+}
359
+
360
+func getInfo(name string) (*Info, error) {
361
+	task, err := createTask(DeviceInfo, name)
362
+	if task == nil {
363
+		return nil, err
364
+	}
365
+	if err := task.Run(); err != nil {
366
+		return nil, err
367
+	}
368
+	return task.GetInfo()
369
+}
370
+
371
+func getStatus(name string) (uint64, uint64, string, string, error) {
372
+	task, err := createTask(DeviceStatus, name)
373
+	if task == nil {
374
+		utils.Debugf("getStatus: Error createTask: %s", err)
375
+		return 0, 0, "", "", err
376
+	}
377
+	if err := task.Run(); err != nil {
378
+		utils.Debugf("getStatus: Error Run: %s", err)
379
+		return 0, 0, "", "", err
380
+	}
381
+
382
+	devinfo, err := task.GetInfo()
383
+	if err != nil {
384
+		utils.Debugf("getStatus: Error GetInfo: %s", err)
385
+		return 0, 0, "", "", err
386
+	}
387
+	if devinfo.Exists == 0 {
388
+		utils.Debugf("getStatus: Non existing device %s", name)
389
+		return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
390
+	}
391
+
392
+	_, start, length, targetType, params := task.GetNextTarget(0)
393
+	return start, length, targetType, params, nil
394
+}
395
+
396
+func setTransactionId(poolName string, oldId uint64, newId uint64) error {
397
+	task, err := createTask(DeviceTargetMsg, poolName)
398
+	if task == nil {
399
+		return err
400
+	}
401
+
402
+	if err := task.SetSector(0); err != nil {
403
+		return fmt.Errorf("Can't set sector")
404
+	}
405
+
406
+	if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil {
407
+		return fmt.Errorf("Can't set message")
408
+	}
409
+
410
+	if err := task.Run(); err != nil {
411
+		return fmt.Errorf("Error running setTransactionId")
412
+	}
413
+	return nil
414
+}
415
+
416
+func suspendDevice(name string) error {
417
+	task, err := createTask(DeviceSuspend, name)
418
+	if task == nil {
419
+		return err
420
+	}
421
+	if err := task.Run(); err != nil {
422
+		return fmt.Errorf("Error running DeviceSuspend")
423
+	}
424
+	return nil
425
+}
426
+
427
+func resumeDevice(name string) error {
428
+	task, err := createTask(DeviceResume, name)
429
+	if task == nil {
430
+		return err
431
+	}
432
+
433
+	var cookie uint = 0
434
+	if err := task.SetCookie(&cookie, 0); err != nil {
435
+		return fmt.Errorf("Can't set cookie")
436
+	}
437
+
438
+	if err := task.Run(); err != nil {
439
+		return fmt.Errorf("Error running DeviceSuspend")
440
+	}
441
+
442
+	UdevWait(cookie)
443
+
444
+	return nil
445
+}
446
+
447
+func createDevice(poolName string, deviceId int) error {
448
+	utils.Debugf("[devmapper] createDevice(poolName=%v, deviceId=%v)", poolName, deviceId)
449
+	task, err := createTask(DeviceTargetMsg, poolName)
450
+	if task == nil {
451
+		return err
452
+	}
453
+
454
+	if err := task.SetSector(0); err != nil {
455
+		return fmt.Errorf("Can't set sector")
456
+	}
457
+
458
+	if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil {
459
+		return fmt.Errorf("Can't set message")
460
+	}
461
+
462
+	if err := task.Run(); err != nil {
463
+		return fmt.Errorf("Error running createDevice")
464
+	}
465
+	return nil
466
+}
467
+
468
+func deleteDevice(poolName string, deviceId int) error {
469
+	task, err := createTask(DeviceTargetMsg, poolName)
470
+	if task == nil {
471
+		return err
472
+	}
473
+
474
+	if err := task.SetSector(0); err != nil {
475
+		return fmt.Errorf("Can't set sector")
476
+	}
477
+
478
+	if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil {
479
+		return fmt.Errorf("Can't set message")
480
+	}
481
+
482
+	if err := task.Run(); err != nil {
483
+		return fmt.Errorf("Error running deleteDevice")
484
+	}
485
+	return nil
486
+}
487
+
488
+func removeDevice(name string) error {
489
+	utils.Debugf("[devmapper] removeDevice START")
490
+	defer utils.Debugf("[devmapper] removeDevice END")
491
+	task, err := createTask(DeviceRemove, name)
492
+	if task == nil {
493
+		return err
494
+	}
495
+	if err = task.Run(); err != nil {
496
+		return fmt.Errorf("Error running removeDevice")
497
+	}
498
+	return nil
499
+}
500
+
501
+func activateDevice(poolName string, name string, deviceId int, size uint64) error {
502
+	task, err := createTask(DeviceCreate, name)
503
+	if task == nil {
504
+		return err
505
+	}
506
+
507
+	params := fmt.Sprintf("%s %d", poolName, deviceId)
508
+	if err := task.AddTarget(0, size/512, "thin", params); err != nil {
509
+		return fmt.Errorf("Can't add target")
510
+	}
511
+	if err := task.SetAddNode(AddNodeOnCreate); err != nil {
512
+		return fmt.Errorf("Can't add node")
513
+	}
514
+
515
+	var cookie uint = 0
516
+	if err := task.SetCookie(&cookie, 0); err != nil {
517
+		return fmt.Errorf("Can't set cookie")
518
+	}
519
+
520
+	if err := task.Run(); err != nil {
521
+		return fmt.Errorf("Error running DeviceCreate (activateDevice)")
522
+	}
523
+
524
+	UdevWait(cookie)
525
+
526
+	return nil
527
+}
528
+
529
+func (devices *DeviceSet) createSnapDevice(poolName string, deviceId int, baseName string, baseDeviceId int) error {
530
+	devinfo, _ := getInfo(baseName)
531
+	doSuspend := devinfo != nil && devinfo.Exists != 0
532
+
533
+	if doSuspend {
534
+		if err := suspendDevice(baseName); err != nil {
535
+			return err
536
+		}
537
+	}
538
+
539
+	task, err := createTask(DeviceTargetMsg, poolName)
540
+	if task == nil {
541
+		if doSuspend {
542
+			resumeDevice(baseName)
543
+		}
544
+		return err
545
+	}
546
+
547
+	if err := task.SetSector(0); err != nil {
548
+		if doSuspend {
549
+			resumeDevice(baseName)
550
+		}
551
+		return fmt.Errorf("Can't set sector")
552
+	}
553
+
554
+	if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseDeviceId)); err != nil {
555
+		if doSuspend {
556
+			resumeDevice(baseName)
557
+		}
558
+		return fmt.Errorf("Can't set message")
559
+	}
560
+
561
+	if err := task.Run(); err != nil {
562
+		if doSuspend {
563
+			resumeDevice(baseName)
564
+		}
565
+		return fmt.Errorf("Error running DeviceCreate (createSnapDevice)")
566
+	}
567
+
568
+	if doSuspend {
569
+		if err := resumeDevice(baseName); err != nil {
570
+			return err
571
+		}
572
+	}
573
+
574
+	return nil
575
+}
0 576
new file mode 100644
... ...
@@ -0,0 +1,106 @@
0
+package devmapper
1
+
2
+// Definition of struct dm_task and sub structures (from lvm2)
3
+//
4
+// struct dm_ioctl {
5
+// 	/*
6
+// 	 * The version number is made up of three parts:
7
+// 	 * major - no backward or forward compatibility,
8
+// 	 * minor - only backwards compatible,
9
+// 	 * patch - both backwards and forwards compatible.
10
+// 	 *
11
+// 	 * All clients of the ioctl interface should fill in the
12
+// 	 * version number of the interface that they were
13
+// 	 * compiled with.
14
+// 	 *
15
+// 	 * All recognised ioctl commands (ie. those that don't
16
+// 	 * return -ENOTTY) fill out this field, even if the
17
+// 	 * command failed.
18
+// 	 */
19
+// 	uint32_t version[3];	/* in/out */
20
+// 	uint32_t data_size;	/* total size of data passed in
21
+// 				 * including this struct */
22
+
23
+// 	uint32_t data_start;	/* offset to start of data
24
+// 				 * relative to start of this struct */
25
+
26
+// 	uint32_t target_count;	/* in/out */
27
+// 	int32_t open_count;	/* out */
28
+// 	uint32_t flags;		/* in/out */
29
+
30
+// 	/*
31
+// 	 * event_nr holds either the event number (input and output) or the
32
+// 	 * udev cookie value (input only).
33
+// 	 * The DM_DEV_WAIT ioctl takes an event number as input.
34
+// 	 * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls
35
+// 	 * use the field as a cookie to return in the DM_COOKIE
36
+// 	 * variable with the uevents they issue.
37
+// 	 * For output, the ioctls return the event number, not the cookie.
38
+// 	 */
39
+// 	uint32_t event_nr;      	/* in/out */
40
+// 	uint32_t padding;
41
+
42
+// 	uint64_t dev;		/* in/out */
43
+
44
+// 	char name[DM_NAME_LEN];	/* device name */
45
+// 	char uuid[DM_UUID_LEN];	/* unique identifier for
46
+// 				 * the block device */
47
+// 	char data[7];		/* padding or data */
48
+// };
49
+
50
+// struct target {
51
+// 	uint64_t start;
52
+// 	uint64_t length;
53
+// 	char *type;
54
+// 	char *params;
55
+
56
+// 	struct target *next;
57
+// };
58
+
59
+// typedef enum {
60
+// 	DM_ADD_NODE_ON_RESUME, /* add /dev/mapper node with dmsetup resume */
61
+// 	DM_ADD_NODE_ON_CREATE  /* add /dev/mapper node with dmsetup create */
62
+// } dm_add_node_t;
63
+
64
+// struct dm_task {
65
+// 	int type;
66
+// 	char *dev_name;
67
+// 	char *mangled_dev_name;
68
+
69
+// 	struct target *head, *tail;
70
+
71
+// 	int read_only;
72
+// 	uint32_t event_nr;
73
+// 	int major;
74
+// 	int minor;
75
+// 	int allow_default_major_fallback;
76
+// 	uid_t uid;
77
+// 	gid_t gid;
78
+// 	mode_t mode;
79
+// 	uint32_t read_ahead;
80
+// 	uint32_t read_ahead_flags;
81
+// 	union {
82
+// 		struct dm_ioctl *v4;
83
+// 	} dmi;
84
+// 	char *newname;
85
+// 	char *message;
86
+// 	char *geometry;
87
+// 	uint64_t sector;
88
+// 	int no_flush;
89
+// 	int no_open_count;
90
+// 	int skip_lockfs;
91
+// 	int query_inactive_table;
92
+// 	int suppress_identical_reload;
93
+// 	dm_add_node_t add_node;
94
+// 	uint64_t existing_table_size;
95
+// 	int cookie_set;
96
+// 	int new_uuid;
97
+// 	int secure_data;
98
+// 	int retry_remove;
99
+// 	int enable_checks;
100
+// 	int expected_errno;
101
+
102
+// 	char *uuid;
103
+// 	char *mangled_uuid;
104
+// };
105
+//
0 106
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+package devmapper
1
+
2
+import "C"
3
+
4
+// Due to the way cgo works this has to be in a separate file, as devmapper.go has
5
+// definitions in the cgo block, which is incompatible with using "//export"
6
+
7
+//export DevmapperLogCallback
8
+func DevmapperLogCallback(level C.int, file *C.char, line C.int, dm_errno_or_class C.int, message *C.char) {
9
+	if dmLogger != nil {
10
+		dmLogger.log(int(level), C.GoString(file), int(line), int(dm_errno_or_class), C.GoString(message))
11
+	}
12
+}
0 13
new file mode 100644
... ...
@@ -0,0 +1,285 @@
0
+package devmapper
1
+
2
+import (
3
+	"testing"
4
+)
5
+
6
+func TestTaskCreate(t *testing.T) {
7
+	t.Skip("FIXME: not a unit test")
8
+	// Test success
9
+	taskCreate(t, DeviceInfo)
10
+
11
+	// Test Failure
12
+	DmTaskCreate = dmTaskCreateFail
13
+	defer func() { DmTaskCreate = dmTaskCreateFct }()
14
+	if task := TaskCreate(-1); task != nil {
15
+		t.Fatalf("An error should have occured while creating an invalid task.")
16
+	}
17
+}
18
+
19
+func TestTaskRun(t *testing.T) {
20
+	t.Skip("FIXME: not a unit test")
21
+	task := taskCreate(t, DeviceInfo)
22
+
23
+	// Test success
24
+	// Perform the RUN
25
+	if err := task.Run(); err != nil {
26
+		t.Fatal(err)
27
+	}
28
+	// Make sure we don't have error with GetInfo
29
+	if _, err := task.GetInfo(); err != nil {
30
+		t.Fatal(err)
31
+	}
32
+
33
+	// Test failure
34
+	DmTaskRun = dmTaskRunFail
35
+	defer func() { DmTaskRun = dmTaskRunFct }()
36
+
37
+	task = taskCreate(t, DeviceInfo)
38
+	// Perform the RUN
39
+	if err := task.Run(); err != ErrTaskRun {
40
+		t.Fatalf("An error should have occured while running task.")
41
+	}
42
+	// Make sure GetInfo also fails
43
+	if _, err := task.GetInfo(); err != ErrTaskGetInfo {
44
+		t.Fatalf("GetInfo should fail if task.Run() failed.")
45
+	}
46
+}
47
+
48
+func TestTaskSetName(t *testing.T) {
49
+	t.Skip("FIXME: not a unit test")
50
+	task := taskCreate(t, DeviceInfo)
51
+
52
+	// Test success
53
+	if err := task.SetName("test"); err != nil {
54
+		t.Fatal(err)
55
+	}
56
+
57
+	// Test failure
58
+	DmTaskSetName = dmTaskSetNameFail
59
+	defer func() { DmTaskSetName = dmTaskSetNameFct }()
60
+
61
+	if err := task.SetName("test"); err != ErrTaskSetName {
62
+		t.Fatalf("An error should have occured while runnign SetName.")
63
+	}
64
+}
65
+
66
+func TestTaskSetMessage(t *testing.T) {
67
+	t.Skip("FIXME: not a unit test")
68
+	task := taskCreate(t, DeviceInfo)
69
+
70
+	// Test success
71
+	if err := task.SetMessage("test"); err != nil {
72
+		t.Fatal(err)
73
+	}
74
+
75
+	// Test failure
76
+	DmTaskSetMessage = dmTaskSetMessageFail
77
+	defer func() { DmTaskSetMessage = dmTaskSetMessageFct }()
78
+
79
+	if err := task.SetMessage("test"); err != ErrTaskSetMessage {
80
+		t.Fatalf("An error should have occured while runnign SetMessage.")
81
+	}
82
+}
83
+
84
+func TestTaskSetSector(t *testing.T) {
85
+	t.Skip("FIXME: not a unit test")
86
+	task := taskCreate(t, DeviceInfo)
87
+
88
+	// Test success
89
+	if err := task.SetSector(128); err != nil {
90
+		t.Fatal(err)
91
+	}
92
+
93
+	DmTaskSetSector = dmTaskSetSectorFail
94
+	defer func() { DmTaskSetSector = dmTaskSetSectorFct }()
95
+
96
+	// Test failure
97
+	if err := task.SetSector(0); err != ErrTaskSetSector {
98
+		t.Fatalf("An error should have occured while running SetSector.")
99
+	}
100
+}
101
+
102
+func TestTaskSetCookie(t *testing.T) {
103
+	t.Skip("FIXME: not a unit test")
104
+	var (
105
+		cookie uint = 0
106
+		task        = taskCreate(t, DeviceInfo)
107
+	)
108
+
109
+	// Test success
110
+	if err := task.SetCookie(&cookie, 0); err != nil {
111
+		t.Fatal(err)
112
+	}
113
+
114
+	// Test failure
115
+	if err := task.SetCookie(nil, 0); err != ErrNilCookie {
116
+		t.Fatalf("An error should have occured while running SetCookie with nil cookie.")
117
+	}
118
+
119
+	DmTaskSetCookie = dmTaskSetCookieFail
120
+	defer func() { DmTaskSetCookie = dmTaskSetCookieFct }()
121
+
122
+	if err := task.SetCookie(&cookie, 0); err != ErrTaskSetCookie {
123
+		t.Fatalf("An error should have occured while running SetCookie.")
124
+	}
125
+}
126
+
127
+func TestTaskSetAddNode(t *testing.T) {
128
+	t.Skip("FIXME: not a unit test")
129
+	task := taskCreate(t, DeviceInfo)
130
+
131
+	// Test success
132
+	if err := task.SetAddNode(0); err != nil {
133
+		t.Fatal(err)
134
+	}
135
+
136
+	// Test failure
137
+	if err := task.SetAddNode(-1); err != ErrInvalidAddNode {
138
+		t.Fatalf("An error should have occured running SetAddNode with wrong node.")
139
+	}
140
+
141
+	DmTaskSetAddNode = dmTaskSetAddNodeFail
142
+	defer func() { DmTaskSetAddNode = dmTaskSetAddNodeFct }()
143
+
144
+	if err := task.SetAddNode(0); err != ErrTaskSetAddNode {
145
+		t.Fatalf("An error should have occured running SetAddNode.")
146
+	}
147
+}
148
+
149
+func TestTaskSetRo(t *testing.T) {
150
+	t.Skip("FIXME: not a unit test")
151
+	task := taskCreate(t, DeviceInfo)
152
+
153
+	// Test success
154
+	if err := task.SetRo(); err != nil {
155
+		t.Fatal(err)
156
+	}
157
+
158
+	// Test failure
159
+	DmTaskSetRo = dmTaskSetRoFail
160
+	defer func() { DmTaskSetRo = dmTaskSetRoFct }()
161
+
162
+	if err := task.SetRo(); err != ErrTaskSetRo {
163
+		t.Fatalf("An error should have occured running SetRo.")
164
+	}
165
+}
166
+
167
+func TestTaskAddTarget(t *testing.T) {
168
+	t.Skip("FIXME: not a unit test")
169
+	task := taskCreate(t, DeviceInfo)
170
+
171
+	// Test success
172
+	if err := task.AddTarget(0, 128, "thinp", ""); err != nil {
173
+		t.Fatal(err)
174
+	}
175
+
176
+	// Test failure
177
+	DmTaskAddTarget = dmTaskAddTargetFail
178
+	defer func() { DmTaskAddTarget = dmTaskAddTargetFct }()
179
+
180
+	if err := task.AddTarget(0, 128, "thinp", ""); err != ErrTaskAddTarget {
181
+		t.Fatalf("An error should have occured running AddTarget.")
182
+	}
183
+}
184
+
185
+// func TestTaskGetInfo(t *testing.T) {
186
+// 	task := taskCreate(t, DeviceInfo)
187
+
188
+// 	// Test success
189
+// 	if _, err := task.GetInfo(); err != nil {
190
+// 		t.Fatal(err)
191
+// 	}
192
+
193
+// 	// Test failure
194
+// 	DmTaskGetInfo = dmTaskGetInfoFail
195
+// 	defer func() { DmTaskGetInfo = dmTaskGetInfoFct }()
196
+
197
+// 	if _, err := task.GetInfo(); err != ErrTaskGetInfo {
198
+// 		t.Fatalf("An error should have occured running GetInfo.")
199
+// 	}
200
+// }
201
+
202
+// func TestTaskGetNextTarget(t *testing.T) {
203
+// 	task := taskCreate(t, DeviceInfo)
204
+
205
+// 	if next, _, _, _, _ := task.GetNextTarget(0); next == 0 {
206
+// 		t.Fatalf("The next target should not be 0.")
207
+// 	}
208
+// }
209
+
210
+/// Utils
211
+func taskCreate(t *testing.T, taskType TaskType) *Task {
212
+	task := TaskCreate(taskType)
213
+	if task == nil {
214
+		t.Fatalf("Error creating task")
215
+	}
216
+	return task
217
+}
218
+
219
+/// Failure function replacement
220
+func dmTaskCreateFail(t int) *CDmTask {
221
+	return nil
222
+}
223
+
224
+func dmTaskRunFail(task *CDmTask) int {
225
+	return -1
226
+}
227
+
228
+func dmTaskSetNameFail(task *CDmTask, name string) int {
229
+	return -1
230
+}
231
+
232
+func dmTaskSetMessageFail(task *CDmTask, message string) int {
233
+	return -1
234
+}
235
+
236
+func dmTaskSetSectorFail(task *CDmTask, sector uint64) int {
237
+	return -1
238
+}
239
+
240
+func dmTaskSetCookieFail(task *CDmTask, cookie *uint, flags uint16) int {
241
+	return -1
242
+}
243
+
244
+func dmTaskSetAddNodeFail(task *CDmTask, addNode AddNodeType) int {
245
+	return -1
246
+}
247
+
248
+func dmTaskSetRoFail(task *CDmTask) int {
249
+	return -1
250
+}
251
+
252
+func dmTaskAddTargetFail(task *CDmTask,
253
+	start, size uint64, ttype, params string) int {
254
+	return -1
255
+}
256
+
257
+func dmTaskGetInfoFail(task *CDmTask, info *Info) int {
258
+	return -1
259
+}
260
+
261
+func dmGetNextTargetFail(task *CDmTask, next uintptr, start, length *uint64,
262
+	target, params *string) uintptr {
263
+	return 0
264
+}
265
+
266
+func dmAttachLoopDeviceFail(filename string, fd *int) string {
267
+	return ""
268
+}
269
+
270
+func sysGetBlockSizeFail(fd uintptr, size *uint64) sysErrno {
271
+	return 1
272
+}
273
+
274
+func dmUdevWaitFail(cookie uint) int {
275
+	return -1
276
+}
277
+
278
+func dmSetDevDirFail(dir string) int {
279
+	return -1
280
+}
281
+
282
+func dmGetLibraryVersionFail(version *string) int {
283
+	return -1
284
+}
0 285
new file mode 100644
... ...
@@ -0,0 +1,340 @@
0
+package devmapper
1
+
2
+/*
3
+#cgo LDFLAGS: -L. -ldevmapper
4
+#include <stdio.h>
5
+#include <stdlib.h>
6
+#include <unistd.h>
7
+#include <libdevmapper.h>
8
+#include <linux/loop.h>
9
+#include <sys/types.h>
10
+#include <sys/stat.h>
11
+#include <fcntl.h>
12
+#include <sys/ioctl.h>
13
+#include <linux/fs.h>
14
+#include <errno.h>
15
+
16
+#ifndef LOOP_CTL_GET_FREE
17
+#define LOOP_CTL_GET_FREE       0x4C82
18
+#endif
19
+
20
+// FIXME: this could easily be rewritten in go
21
+char*			attach_loop_device(const char *filename, int *loop_fd_out)
22
+{
23
+  struct loop_info64	loopinfo = {0};
24
+  struct stat		st;
25
+  char			buf[64];
26
+  int			i, loop_fd, fd, start_index;
27
+  char*			loopname;
28
+
29
+
30
+  *loop_fd_out = -1;
31
+
32
+  start_index = 0;
33
+  fd = open("/dev/loop-control", O_RDONLY);
34
+  if (fd >= 0) {
35
+    start_index = ioctl(fd, LOOP_CTL_GET_FREE);
36
+    close(fd);
37
+
38
+    if (start_index < 0)
39
+      start_index = 0;
40
+  }
41
+
42
+  fd = open(filename, O_RDWR);
43
+  if (fd < 0) {
44
+    perror("open");
45
+    return NULL;
46
+  }
47
+
48
+  loop_fd = -1;
49
+  for (i = start_index ; loop_fd < 0 ; i++ ) {
50
+    if (sprintf(buf, "/dev/loop%d", i) < 0) {
51
+	close(fd);
52
+	return NULL;
53
+    }
54
+
55
+    if (stat(buf, &st)) {
56
+      if (!S_ISBLK(st.st_mode)) {
57
+	 fprintf(stderr, "[error] Loopback device %s is not a block device.\n", buf);
58
+      } else if (errno == ENOENT) {
59
+	fprintf(stderr, "[error] There are no more loopback device available.\n");
60
+      } else {
61
+	fprintf(stderr, "[error] Unkown error trying to stat the loopback device %s (errno: %d).\n", buf, errno);
62
+      }
63
+      close(fd);
64
+      return NULL;
65
+    }
66
+
67
+    loop_fd = open(buf, O_RDWR);
68
+    if (loop_fd < 0 && errno == ENOENT) {
69
+      fprintf(stderr, "[error] The loopback device %s does not exists.\n", buf);
70
+      close(fd);
71
+      return NULL;
72
+    } else if (loop_fd < 0) {
73
+	fprintf(stderr, "[error] Unkown error openning the loopback device %s. (errno: %d)\n", buf, errno);
74
+	continue;
75
+    }
76
+
77
+    if (ioctl(loop_fd, LOOP_SET_FD, (void *)(size_t)fd) < 0) {
78
+      int errsv = errno;
79
+      close(loop_fd);
80
+      loop_fd = -1;
81
+      if (errsv != EBUSY) {
82
+        close(fd);
83
+        fprintf(stderr, "cannot set up loopback device %s: %s", buf, strerror(errsv));
84
+        return NULL;
85
+      }
86
+      continue;
87
+    }
88
+
89
+    close(fd);
90
+
91
+    strncpy((char*)loopinfo.lo_file_name, buf, LO_NAME_SIZE);
92
+    loopinfo.lo_offset = 0;
93
+    loopinfo.lo_flags = LO_FLAGS_AUTOCLEAR;
94
+
95
+    if (ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo) < 0) {
96
+      perror("ioctl LOOP_SET_STATUS64");
97
+      if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
98
+        perror("ioctl LOOP_CLR_FD");
99
+      }
100
+      close(loop_fd);
101
+      fprintf (stderr, "cannot set up loopback device info");
102
+      return (NULL);
103
+    }
104
+
105
+    loopname = strdup(buf);
106
+    if (loopname == NULL) {
107
+      close(loop_fd);
108
+      return (NULL);
109
+    }
110
+
111
+    *loop_fd_out = loop_fd;
112
+    return (loopname);
113
+  }
114
+
115
+  return (NULL);
116
+}
117
+
118
+extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str);
119
+
120
+static void	log_cb(int level, const char *file, int line,
121
+		       int dm_errno_or_class, const char *f, ...)
122
+{
123
+  char buffer[256];
124
+  va_list ap;
125
+
126
+  va_start(ap, f);
127
+  vsnprintf(buffer, 256, f, ap);
128
+  va_end(ap);
129
+
130
+  DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer);
131
+}
132
+
133
+static void	log_with_errno_init()
134
+{
135
+  dm_log_with_errno_init(log_cb);
136
+}
137
+
138
+*/
139
+import "C"
140
+
141
+import (
142
+	"unsafe"
143
+)
144
+
145
+type (
146
+	CDmTask C.struct_dm_task
147
+)
148
+
149
+var (
150
+	DmAttachLoopDevice       = dmAttachLoopDeviceFct
151
+	DmGetBlockSize           = dmGetBlockSizeFct
152
+	DmGetLibraryVersion      = dmGetLibraryVersionFct
153
+	DmGetNextTarget          = dmGetNextTargetFct
154
+	DmLogInitVerbose         = dmLogInitVerboseFct
155
+	DmSetDevDir              = dmSetDevDirFct
156
+	DmTaskAddTarget          = dmTaskAddTargetFct
157
+	DmTaskCreate             = dmTaskCreateFct
158
+	DmTaskDestroy            = dmTaskDestroyFct
159
+	DmTaskGetInfo            = dmTaskGetInfoFct
160
+	DmTaskRun                = dmTaskRunFct
161
+	DmTaskSetAddNode         = dmTaskSetAddNodeFct
162
+	DmTaskSetCookie          = dmTaskSetCookieFct
163
+	DmTaskSetMessage         = dmTaskSetMessageFct
164
+	DmTaskSetName            = dmTaskSetNameFct
165
+	DmTaskSetRo              = dmTaskSetRoFct
166
+	DmTaskSetSector          = dmTaskSetSectorFct
167
+	DmUdevWait               = dmUdevWaitFct
168
+	GetBlockSize             = getBlockSizeFct
169
+	LogWithErrnoInit         = logWithErrnoInitFct
170
+	DmGetLoopbackBackingFile = dmGetLoopbackBackingFileFct
171
+	DmLoopbackSetCapacity    = dmLoopbackSetCapacityFct
172
+)
173
+
174
+func free(p *C.char) {
175
+	C.free(unsafe.Pointer(p))
176
+}
177
+
178
+func dmTaskDestroyFct(task *CDmTask) {
179
+	C.dm_task_destroy((*C.struct_dm_task)(task))
180
+}
181
+
182
+func dmTaskCreateFct(taskType int) *CDmTask {
183
+	return (*CDmTask)(C.dm_task_create(C.int(taskType)))
184
+}
185
+
186
+func dmTaskRunFct(task *CDmTask) int {
187
+	return int(C.dm_task_run((*C.struct_dm_task)(task)))
188
+}
189
+
190
+func dmTaskSetNameFct(task *CDmTask, name string) int {
191
+	Cname := C.CString(name)
192
+	defer free(Cname)
193
+
194
+	return int(C.dm_task_set_name((*C.struct_dm_task)(task),
195
+		Cname))
196
+}
197
+
198
+func dmTaskSetMessageFct(task *CDmTask, message string) int {
199
+	Cmessage := C.CString(message)
200
+	defer free(Cmessage)
201
+
202
+	return int(C.dm_task_set_message((*C.struct_dm_task)(task),
203
+		Cmessage))
204
+}
205
+
206
+func dmTaskSetSectorFct(task *CDmTask, sector uint64) int {
207
+	return int(C.dm_task_set_sector((*C.struct_dm_task)(task),
208
+		C.uint64_t(sector)))
209
+}
210
+
211
+func dmTaskSetCookieFct(task *CDmTask, cookie *uint, flags uint16) int {
212
+	cCookie := C.uint32_t(*cookie)
213
+	defer func() {
214
+		*cookie = uint(cCookie)
215
+	}()
216
+	return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie,
217
+		C.uint16_t(flags)))
218
+}
219
+
220
+func dmTaskSetAddNodeFct(task *CDmTask, addNode AddNodeType) int {
221
+	return int(C.dm_task_set_add_node((*C.struct_dm_task)(task),
222
+		C.dm_add_node_t(addNode)))
223
+}
224
+
225
+func dmTaskSetRoFct(task *CDmTask) int {
226
+	return int(C.dm_task_set_ro((*C.struct_dm_task)(task)))
227
+}
228
+
229
+func dmTaskAddTargetFct(task *CDmTask,
230
+	start, size uint64, ttype, params string) int {
231
+
232
+	Cttype := C.CString(ttype)
233
+	defer free(Cttype)
234
+
235
+	Cparams := C.CString(params)
236
+	defer free(Cparams)
237
+
238
+	return int(C.dm_task_add_target((*C.struct_dm_task)(task),
239
+		C.uint64_t(start), C.uint64_t(size), Cttype, Cparams))
240
+}
241
+
242
+func dmGetLoopbackBackingFileFct(fd uintptr) (uint64, uint64, sysErrno) {
243
+	var lo64 C.struct_loop_info64
244
+	_, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_GET_STATUS64,
245
+		uintptr(unsafe.Pointer(&lo64)))
246
+	return uint64(lo64.lo_device), uint64(lo64.lo_inode), sysErrno(err)
247
+}
248
+
249
+func dmLoopbackSetCapacityFct(fd uintptr) sysErrno {
250
+	_, _, err := sysSyscall(sysSysIoctl, fd, C.LOOP_SET_CAPACITY, 0)
251
+	return sysErrno(err)
252
+}
253
+
254
+func dmGetBlockSizeFct(fd uintptr) (int64, sysErrno) {
255
+	var size int64
256
+	_, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size)))
257
+	return size, sysErrno(err)
258
+}
259
+
260
+func dmTaskGetInfoFct(task *CDmTask, info *Info) int {
261
+	Cinfo := C.struct_dm_info{}
262
+	defer func() {
263
+		info.Exists = int(Cinfo.exists)
264
+		info.Suspended = int(Cinfo.suspended)
265
+		info.LiveTable = int(Cinfo.live_table)
266
+		info.InactiveTable = int(Cinfo.inactive_table)
267
+		info.OpenCount = int32(Cinfo.open_count)
268
+		info.EventNr = uint32(Cinfo.event_nr)
269
+		info.Major = uint32(Cinfo.major)
270
+		info.Minor = uint32(Cinfo.minor)
271
+		info.ReadOnly = int(Cinfo.read_only)
272
+		info.TargetCount = int32(Cinfo.target_count)
273
+	}()
274
+	return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo))
275
+}
276
+
277
+func dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr {
278
+	var (
279
+		Cstart, Clength      C.uint64_t
280
+		CtargetType, Cparams *C.char
281
+	)
282
+	defer func() {
283
+		*start = uint64(Cstart)
284
+		*length = uint64(Clength)
285
+		*target = C.GoString(CtargetType)
286
+		*params = C.GoString(Cparams)
287
+	}()
288
+
289
+	nextp := C.dm_get_next_target((*C.struct_dm_task)(task),
290
+		unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams)
291
+	return uintptr(nextp)
292
+}
293
+
294
+func dmAttachLoopDeviceFct(filename string, fd *int) string {
295
+	cFilename := C.CString(filename)
296
+	defer free(cFilename)
297
+
298
+	var cFd C.int
299
+	defer func() {
300
+		*fd = int(cFd)
301
+	}()
302
+
303
+	ret := C.attach_loop_device(cFilename, &cFd)
304
+	defer free(ret)
305
+	return C.GoString(ret)
306
+}
307
+
308
+func getBlockSizeFct(fd uintptr, size *uint64) sysErrno {
309
+	_, _, err := sysSyscall(sysSysIoctl, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size)))
310
+	return sysErrno(err)
311
+}
312
+
313
+func dmUdevWaitFct(cookie uint) int {
314
+	return int(C.dm_udev_wait(C.uint32_t(cookie)))
315
+}
316
+
317
+func dmLogInitVerboseFct(level int) {
318
+	C.dm_log_init_verbose(C.int(level))
319
+}
320
+
321
+func logWithErrnoInitFct() {
322
+	C.log_with_errno_init()
323
+}
324
+
325
+func dmSetDevDirFct(dir string) int {
326
+	Cdir := C.CString(dir)
327
+	defer free(Cdir)
328
+
329
+	return int(C.dm_set_dev_dir(Cdir))
330
+}
331
+
332
+func dmGetLibraryVersionFct(version *string) int {
333
+	buffer := C.CString(string(make([]byte, 128)))
334
+	defer free(buffer)
335
+	defer func() {
336
+		*version = C.GoString(buffer)
337
+	}()
338
+	return int(C.dm_get_library_version(buffer, 128))
339
+}
0 340
new file mode 100644
... ...
@@ -0,0 +1,126 @@
0
+package devmapper
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/graphdriver"
5
+	"io/ioutil"
6
+	"path"
7
+)
8
+
9
+func init() {
10
+	graphdriver.Register("devicemapper", Init)
11
+}
12
+
13
+// Placeholder interfaces, to be replaced
14
+// at integration.
15
+
16
+// End of placeholder interfaces.
17
+
18
+type Driver struct {
19
+	*DeviceSet
20
+	home string
21
+}
22
+
23
+var Init = func(home string) (graphdriver.Driver, error) {
24
+	deviceSet, err := NewDeviceSet(home, true)
25
+	if err != nil {
26
+		return nil, err
27
+	}
28
+	d := &Driver{
29
+		DeviceSet: deviceSet,
30
+		home:      home,
31
+	}
32
+	return d, nil
33
+}
34
+
35
+func (d *Driver) String() string {
36
+	return "devicemapper"
37
+}
38
+
39
+func (d *Driver) Status() [][2]string {
40
+	s := d.DeviceSet.Status()
41
+
42
+	status := [][2]string{
43
+		{"Pool Name", s.PoolName},
44
+		{"Data file", s.DataLoopback},
45
+		{"Metadata file", s.MetadataLoopback},
46
+		{"Data Space Used", fmt.Sprintf("%.1f Mb", float64(s.Data.Used)/(1024*1024))},
47
+		{"Data Space Total", fmt.Sprintf("%.1f Mb", float64(s.Data.Total)/(1024*1024))},
48
+		{"Metadata Space Used", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Used)/(1024*1024))},
49
+		{"Metadata Space Total", fmt.Sprintf("%.1f Mb", float64(s.Metadata.Total)/(1024*1024))},
50
+	}
51
+	return status
52
+}
53
+
54
+func (d *Driver) Cleanup() error {
55
+	return d.DeviceSet.Shutdown()
56
+}
57
+
58
+func (d *Driver) Create(id, parent string) error {
59
+	if err := d.DeviceSet.AddDevice(id, parent); err != nil {
60
+		return err
61
+	}
62
+
63
+	mp := path.Join(d.home, "mnt", id)
64
+	if err := d.mount(id, mp); err != nil {
65
+		return err
66
+	}
67
+
68
+	if err := osMkdirAll(path.Join(mp, "rootfs"), 0755); err != nil && !osIsExist(err) {
69
+		return err
70
+	}
71
+
72
+	// Create an "id" file with the container/image id in it to help reconscruct this in case
73
+	// of later problems
74
+	if err := ioutil.WriteFile(path.Join(mp, "id"), []byte(id), 0600); err != nil {
75
+		return err
76
+	}
77
+
78
+	return nil
79
+}
80
+
81
+func (d *Driver) Remove(id string) error {
82
+	mp := path.Join(d.home, "mnt", id)
83
+	if err := d.unmount(id, mp); err != nil {
84
+		return err
85
+	}
86
+	return d.DeviceSet.RemoveDevice(id)
87
+}
88
+
89
+func (d *Driver) Get(id string) (string, error) {
90
+	mp := path.Join(d.home, "mnt", id)
91
+	if err := d.mount(id, mp); err != nil {
92
+		return "", err
93
+	}
94
+	return path.Join(mp, "rootfs"), nil
95
+}
96
+
97
+func (d *Driver) mount(id, mountPoint string) error {
98
+	// Create the target directories if they don't exist
99
+	if err := osMkdirAll(mountPoint, 0755); err != nil && !osIsExist(err) {
100
+		return err
101
+	}
102
+	// If mountpoint is already mounted, do nothing
103
+	if mounted, err := Mounted(mountPoint); err != nil {
104
+		return fmt.Errorf("Error checking mountpoint: %s", err)
105
+	} else if mounted {
106
+		return nil
107
+	}
108
+	// Mount the device
109
+	return d.DeviceSet.MountDevice(id, mountPoint, false)
110
+}
111
+
112
+func (d *Driver) unmount(id, mountPoint string) error {
113
+	// If mountpoint is not mounted, do nothing
114
+	if mounted, err := Mounted(mountPoint); err != nil {
115
+		return fmt.Errorf("Error checking mountpoint: %s", err)
116
+	} else if !mounted {
117
+		return nil
118
+	}
119
+	// Unmount the device
120
+	return d.DeviceSet.UnmountDevice(id, mountPoint, true)
121
+}
122
+
123
+func (d *Driver) Exists(id string) bool {
124
+	return d.Devices[id] != nil
125
+}
0 126
new file mode 100644
... ...
@@ -0,0 +1,872 @@
0
+package devmapper
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/graphdriver"
5
+	"io/ioutil"
6
+	"path"
7
+	"runtime"
8
+	"strings"
9
+	"syscall"
10
+	"testing"
11
+)
12
+
13
+func init() {
14
+	// Reduce the size the the base fs and loopback for the tests
15
+	DefaultDataLoopbackSize = 300 * 1024 * 1024
16
+	DefaultMetaDataLoopbackSize = 200 * 1024 * 1024
17
+	DefaultBaseFsSize = 300 * 1024 * 1024
18
+}
19
+
20
+// denyAllDevmapper mocks all calls to libdevmapper in the unit tests, and denies them by default
21
+func denyAllDevmapper() {
22
+	// Hijack all calls to libdevmapper with default panics.
23
+	// Authorized calls are selectively hijacked in each tests.
24
+	DmTaskCreate = func(t int) *CDmTask {
25
+		panic("DmTaskCreate: this method should not be called here")
26
+	}
27
+	DmTaskRun = func(task *CDmTask) int {
28
+		panic("DmTaskRun: this method should not be called here")
29
+	}
30
+	DmTaskSetName = func(task *CDmTask, name string) int {
31
+		panic("DmTaskSetName: this method should not be called here")
32
+	}
33
+	DmTaskSetMessage = func(task *CDmTask, message string) int {
34
+		panic("DmTaskSetMessage: this method should not be called here")
35
+	}
36
+	DmTaskSetSector = func(task *CDmTask, sector uint64) int {
37
+		panic("DmTaskSetSector: this method should not be called here")
38
+	}
39
+	DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int {
40
+		panic("DmTaskSetCookie: this method should not be called here")
41
+	}
42
+	DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int {
43
+		panic("DmTaskSetAddNode: this method should not be called here")
44
+	}
45
+	DmTaskSetRo = func(task *CDmTask) int {
46
+		panic("DmTaskSetRo: this method should not be called here")
47
+	}
48
+	DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int {
49
+		panic("DmTaskAddTarget: this method should not be called here")
50
+	}
51
+	DmTaskGetInfo = func(task *CDmTask, info *Info) int {
52
+		panic("DmTaskGetInfo: this method should not be called here")
53
+	}
54
+	DmGetNextTarget = func(task *CDmTask, next uintptr, start, length *uint64, target, params *string) uintptr {
55
+		panic("DmGetNextTarget: this method should not be called here")
56
+	}
57
+	DmAttachLoopDevice = func(filename string, fd *int) string {
58
+		panic("DmAttachLoopDevice: this method should not be called here")
59
+	}
60
+	DmGetBlockSize = func(fd uintptr) (int64, sysErrno) {
61
+		panic("DmGetBlockSize: this method should not be called here")
62
+	}
63
+	DmUdevWait = func(cookie uint) int {
64
+		panic("DmUdevWait: this method should not be called here")
65
+	}
66
+	DmSetDevDir = func(dir string) int {
67
+		panic("DmSetDevDir: this method should not be called here")
68
+	}
69
+	DmGetLibraryVersion = func(version *string) int {
70
+		panic("DmGetLibraryVersion: this method should not be called here")
71
+	}
72
+	DmLogInitVerbose = func(level int) {
73
+		panic("DmLogInitVerbose: this method should not be called here")
74
+	}
75
+	DmTaskDestroy = func(task *CDmTask) {
76
+		panic("DmTaskDestroy: this method should not be called here")
77
+	}
78
+	GetBlockSize = func(fd uintptr, size *uint64) sysErrno {
79
+		panic("GetBlockSize: this method should not be called here")
80
+	}
81
+	LogWithErrnoInit = func() {
82
+		panic("LogWithErrnoInit: this method should not be called here")
83
+	}
84
+}
85
+
86
+func denyAllSyscall() {
87
+	sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) {
88
+		panic("sysMount: this method should not be called here")
89
+	}
90
+	sysUnmount = func(target string, flags int) (err error) {
91
+		panic("sysUnmount: this method should not be called here")
92
+	}
93
+	sysCloseOnExec = func(fd int) {
94
+		panic("sysCloseOnExec: this method should not be called here")
95
+	}
96
+	sysSyscall = func(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
97
+		panic("sysSyscall: this method should not be called here")
98
+	}
99
+	// Not a syscall, but forbidding it here anyway
100
+	Mounted = func(mnt string) (bool, error) {
101
+		panic("devmapper.Mounted: this method should not be called here")
102
+	}
103
+	// osOpenFile = os.OpenFile
104
+	// osNewFile = os.NewFile
105
+	// osCreate = os.Create
106
+	// osStat = os.Stat
107
+	// osIsNotExist = os.IsNotExist
108
+	// osIsExist = os.IsExist
109
+	// osMkdirAll = os.MkdirAll
110
+	// osRemoveAll = os.RemoveAll
111
+	// osRename = os.Rename
112
+	// osReadlink = os.Readlink
113
+
114
+	// execRun = func(name string, args ...string) error {
115
+	// 	return exec.Command(name, args...).Run()
116
+	// }
117
+}
118
+
119
+func mkTestDirectory(t *testing.T) string {
120
+	dir, err := ioutil.TempDir("", "docker-test-devmapper-")
121
+	if err != nil {
122
+		t.Fatal(err)
123
+	}
124
+	return dir
125
+}
126
+
127
+func newDriver(t *testing.T) *Driver {
128
+	home := mkTestDirectory(t)
129
+	d, err := Init(home)
130
+	if err != nil {
131
+		t.Fatal(err)
132
+	}
133
+	return d.(*Driver)
134
+}
135
+
136
+func cleanup(d *Driver) {
137
+	d.Cleanup()
138
+	osRemoveAll(d.home)
139
+}
140
+
141
+type Set map[string]bool
142
+
143
+func (r Set) Assert(t *testing.T, names ...string) {
144
+	for _, key := range names {
145
+		if _, exists := r[key]; !exists {
146
+			t.Fatalf("Key not set: %s", key)
147
+		}
148
+		delete(r, key)
149
+	}
150
+	if len(r) != 0 {
151
+		t.Fatalf("Unexpected keys: %v", r)
152
+	}
153
+}
154
+
155
+func TestInit(t *testing.T) {
156
+	var (
157
+		calls           = make(Set)
158
+		devicesAttached = make(Set)
159
+		taskMessages    = make(Set)
160
+		taskTypes       = make(Set)
161
+		home            = mkTestDirectory(t)
162
+	)
163
+	defer osRemoveAll(home)
164
+
165
+	func() {
166
+		denyAllDevmapper()
167
+		DmSetDevDir = func(dir string) int {
168
+			calls["DmSetDevDir"] = true
169
+			expectedDir := "/dev"
170
+			if dir != expectedDir {
171
+				t.Fatalf("Wrong libdevmapper call\nExpected: DmSetDevDir(%v)\nReceived: DmSetDevDir(%v)\n", expectedDir, dir)
172
+			}
173
+			return 0
174
+		}
175
+		LogWithErrnoInit = func() {
176
+			calls["DmLogWithErrnoInit"] = true
177
+		}
178
+		var task1 CDmTask
179
+		DmTaskCreate = func(taskType int) *CDmTask {
180
+			calls["DmTaskCreate"] = true
181
+			taskTypes[fmt.Sprintf("%d", taskType)] = true
182
+			return &task1
183
+		}
184
+		DmTaskSetName = func(task *CDmTask, name string) int {
185
+			calls["DmTaskSetName"] = true
186
+			expectedTask := &task1
187
+			if task != expectedTask {
188
+				t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetName(%v)\nReceived: DmTaskSetName(%v)\n", expectedTask, task)
189
+			}
190
+			// FIXME: use Set.AssertRegexp()
191
+			if !strings.HasPrefix(name, "docker-") && !strings.HasPrefix(name, "/dev/mapper/docker-") ||
192
+				!strings.HasSuffix(name, "-pool") && !strings.HasSuffix(name, "-base") {
193
+				t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetName(%v)\nReceived: DmTaskSetName(%v)\n", "docker-...-pool", name)
194
+			}
195
+			return 1
196
+		}
197
+		DmTaskRun = func(task *CDmTask) int {
198
+			calls["DmTaskRun"] = true
199
+			expectedTask := &task1
200
+			if task != expectedTask {
201
+				t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskRun(%v)\nReceived: DmTaskRun(%v)\n", expectedTask, task)
202
+			}
203
+			return 1
204
+		}
205
+		DmTaskGetInfo = func(task *CDmTask, info *Info) int {
206
+			calls["DmTaskGetInfo"] = true
207
+			expectedTask := &task1
208
+			if task != expectedTask {
209
+				t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskGetInfo(%v)\nReceived: DmTaskGetInfo(%v)\n", expectedTask, task)
210
+			}
211
+			// This will crash if info is not dereferenceable
212
+			info.Exists = 0
213
+			return 1
214
+		}
215
+		DmTaskSetSector = func(task *CDmTask, sector uint64) int {
216
+			calls["DmTaskSetSector"] = true
217
+			expectedTask := &task1
218
+			if task != expectedTask {
219
+				t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetSector(%v)\nReceived: DmTaskSetSector(%v)\n", expectedTask, task)
220
+			}
221
+			if expectedSector := uint64(0); sector != expectedSector {
222
+				t.Fatalf("Wrong libdevmapper call to DmTaskSetSector\nExpected: %v\nReceived: %v\n", expectedSector, sector)
223
+			}
224
+			return 1
225
+		}
226
+		DmTaskSetMessage = func(task *CDmTask, message string) int {
227
+			calls["DmTaskSetMessage"] = true
228
+			expectedTask := &task1
229
+			if task != expectedTask {
230
+				t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskSetSector(%v)\nReceived: DmTaskSetSector(%v)\n", expectedTask, task)
231
+			}
232
+			taskMessages[message] = true
233
+			return 1
234
+		}
235
+		var (
236
+			fakeDataLoop       = "/dev/loop42"
237
+			fakeMetadataLoop   = "/dev/loop43"
238
+			fakeDataLoopFd     = 42
239
+			fakeMetadataLoopFd = 43
240
+		)
241
+		var attachCount int
242
+		DmAttachLoopDevice = func(filename string, fd *int) string {
243
+			calls["DmAttachLoopDevice"] = true
244
+			if _, exists := devicesAttached[filename]; exists {
245
+				t.Fatalf("Already attached %s", filename)
246
+			}
247
+			devicesAttached[filename] = true
248
+			// This will crash if fd is not dereferenceable
249
+			if attachCount == 0 {
250
+				attachCount++
251
+				*fd = fakeDataLoopFd
252
+				return fakeDataLoop
253
+			} else {
254
+				*fd = fakeMetadataLoopFd
255
+				return fakeMetadataLoop
256
+			}
257
+		}
258
+		DmTaskDestroy = func(task *CDmTask) {
259
+			calls["DmTaskDestroy"] = true
260
+			expectedTask := &task1
261
+			if task != expectedTask {
262
+				t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task)
263
+			}
264
+		}
265
+		fakeBlockSize := int64(4242 * 512)
266
+		DmGetBlockSize = func(fd uintptr) (int64, sysErrno) {
267
+			calls["DmGetBlockSize"] = true
268
+			if expectedFd := uintptr(42); fd != expectedFd {
269
+				t.Fatalf("Wrong libdevmapper call\nExpected: DmGetBlockSize(%v)\nReceived: DmGetBlockSize(%v)\n", expectedFd, fd)
270
+			}
271
+			return fakeBlockSize, 0
272
+		}
273
+		DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int {
274
+			calls["DmTaskSetTarget"] = true
275
+			expectedTask := &task1
276
+			if task != expectedTask {
277
+				t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task)
278
+			}
279
+			if start != 0 {
280
+				t.Fatalf("Wrong start: %d != %d", start, 0)
281
+			}
282
+			if ttype != "thin" && ttype != "thin-pool" {
283
+				t.Fatalf("Wrong ttype: %s", ttype)
284
+			}
285
+			// Quick smoke test
286
+			if params == "" {
287
+				t.Fatalf("Params should not be empty")
288
+			}
289
+			return 1
290
+		}
291
+		fakeCookie := uint(4321)
292
+		DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int {
293
+			calls["DmTaskSetCookie"] = true
294
+			expectedTask := &task1
295
+			if task != expectedTask {
296
+				t.Fatalf("Wrong libdevmapper call\nExpected: DmTaskDestroy(%v)\nReceived: DmTaskDestroy(%v)\n", expectedTask, task)
297
+			}
298
+			if flags != 0 {
299
+				t.Fatalf("Cookie flags should be 0 (not %x)", flags)
300
+			}
301
+			*cookie = fakeCookie
302
+			return 1
303
+		}
304
+		DmUdevWait = func(cookie uint) int {
305
+			calls["DmUdevWait"] = true
306
+			if cookie != fakeCookie {
307
+				t.Fatalf("Wrong cookie: %d != %d", cookie, fakeCookie)
308
+			}
309
+			return 1
310
+		}
311
+		DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int {
312
+			if addNode != AddNodeOnCreate {
313
+				t.Fatalf("Wrong AddNoteType: %v (expected %v)", addNode, AddNodeOnCreate)
314
+			}
315
+			calls["DmTaskSetAddNode"] = true
316
+			return 1
317
+		}
318
+		execRun = func(name string, args ...string) error {
319
+			calls["execRun"] = true
320
+			if name != "mkfs.ext4" {
321
+				t.Fatalf("Expected %s to be executed, not %s", "mkfs.ext4", name)
322
+			}
323
+			return nil
324
+		}
325
+		driver, err := Init(home)
326
+		if err != nil {
327
+			t.Fatal(err)
328
+		}
329
+		defer func() {
330
+			if err := driver.Cleanup(); err != nil {
331
+				t.Fatal(err)
332
+			}
333
+		}()
334
+	}()
335
+	// Put all tests in a funciton to make sure the garbage collection will
336
+	// occur.
337
+
338
+	// Call GC to cleanup runtime.Finalizers
339
+	runtime.GC()
340
+
341
+	calls.Assert(t,
342
+		"DmSetDevDir",
343
+		"DmLogWithErrnoInit",
344
+		"DmTaskSetName",
345
+		"DmTaskRun",
346
+		"DmTaskGetInfo",
347
+		"DmAttachLoopDevice",
348
+		"DmTaskDestroy",
349
+		"execRun",
350
+		"DmTaskCreate",
351
+		"DmGetBlockSize",
352
+		"DmTaskSetTarget",
353
+		"DmTaskSetCookie",
354
+		"DmUdevWait",
355
+		"DmTaskSetSector",
356
+		"DmTaskSetMessage",
357
+		"DmTaskSetAddNode",
358
+	)
359
+	devicesAttached.Assert(t, path.Join(home, "devicemapper", "data"), path.Join(home, "devicemapper", "metadata"))
360
+	taskTypes.Assert(t, "0", "6", "17")
361
+	taskMessages.Assert(t, "create_thin 0", "set_transaction_id 0 1")
362
+}
363
+
364
+func fakeInit() func(home string) (graphdriver.Driver, error) {
365
+	oldInit := Init
366
+	Init = func(home string) (graphdriver.Driver, error) {
367
+		return &Driver{
368
+			home: home,
369
+		}, nil
370
+	}
371
+	return oldInit
372
+}
373
+
374
+func restoreInit(init func(home string) (graphdriver.Driver, error)) {
375
+	Init = init
376
+}
377
+
378
+func mockAllDevmapper(calls Set) {
379
+	DmSetDevDir = func(dir string) int {
380
+		calls["DmSetDevDir"] = true
381
+		return 0
382
+	}
383
+	LogWithErrnoInit = func() {
384
+		calls["DmLogWithErrnoInit"] = true
385
+	}
386
+	DmTaskCreate = func(taskType int) *CDmTask {
387
+		calls["DmTaskCreate"] = true
388
+		return &CDmTask{}
389
+	}
390
+	DmTaskSetName = func(task *CDmTask, name string) int {
391
+		calls["DmTaskSetName"] = true
392
+		return 1
393
+	}
394
+	DmTaskRun = func(task *CDmTask) int {
395
+		calls["DmTaskRun"] = true
396
+		return 1
397
+	}
398
+	DmTaskGetInfo = func(task *CDmTask, info *Info) int {
399
+		calls["DmTaskGetInfo"] = true
400
+		return 1
401
+	}
402
+	DmTaskSetSector = func(task *CDmTask, sector uint64) int {
403
+		calls["DmTaskSetSector"] = true
404
+		return 1
405
+	}
406
+	DmTaskSetMessage = func(task *CDmTask, message string) int {
407
+		calls["DmTaskSetMessage"] = true
408
+		return 1
409
+	}
410
+	DmAttachLoopDevice = func(filename string, fd *int) string {
411
+		calls["DmAttachLoopDevice"] = true
412
+		return "/dev/loop42"
413
+	}
414
+	DmTaskDestroy = func(task *CDmTask) {
415
+		calls["DmTaskDestroy"] = true
416
+	}
417
+	DmGetBlockSize = func(fd uintptr) (int64, sysErrno) {
418
+		calls["DmGetBlockSize"] = true
419
+		return int64(4242 * 512), 0
420
+	}
421
+	DmTaskAddTarget = func(task *CDmTask, start, size uint64, ttype, params string) int {
422
+		calls["DmTaskSetTarget"] = true
423
+		return 1
424
+	}
425
+	DmTaskSetCookie = func(task *CDmTask, cookie *uint, flags uint16) int {
426
+		calls["DmTaskSetCookie"] = true
427
+		return 1
428
+	}
429
+	DmUdevWait = func(cookie uint) int {
430
+		calls["DmUdevWait"] = true
431
+		return 1
432
+	}
433
+	DmTaskSetAddNode = func(task *CDmTask, addNode AddNodeType) int {
434
+		calls["DmTaskSetAddNode"] = true
435
+		return 1
436
+	}
437
+	execRun = func(name string, args ...string) error {
438
+		calls["execRun"] = true
439
+		return nil
440
+	}
441
+}
442
+
443
+func TestDriverName(t *testing.T) {
444
+	denyAllDevmapper()
445
+	defer denyAllDevmapper()
446
+
447
+	oldInit := fakeInit()
448
+	defer restoreInit(oldInit)
449
+
450
+	d := newDriver(t)
451
+	if d.String() != "devicemapper" {
452
+		t.Fatalf("Expected driver name to be devicemapper got %s", d.String())
453
+	}
454
+}
455
+
456
+func TestDriverCreate(t *testing.T) {
457
+	denyAllDevmapper()
458
+	denyAllSyscall()
459
+	defer denyAllSyscall()
460
+	defer denyAllDevmapper()
461
+
462
+	calls := make(Set)
463
+	mockAllDevmapper(calls)
464
+
465
+	sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) {
466
+		calls["sysMount"] = true
467
+		// FIXME: compare the exact source and target strings (inodes + devname)
468
+		if expectedSource := "/dev/mapper/docker-"; !strings.HasPrefix(source, expectedSource) {
469
+			t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedSource, source)
470
+		}
471
+		if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) {
472
+			t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target)
473
+		}
474
+		if expectedFstype := "ext4"; fstype != expectedFstype {
475
+			t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFstype, fstype)
476
+		}
477
+		if expectedFlags := uintptr(3236757504); flags != expectedFlags {
478
+			t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags)
479
+		}
480
+		return nil
481
+	}
482
+
483
+	Mounted = func(mnt string) (bool, error) {
484
+		calls["Mounted"] = true
485
+		if !strings.HasPrefix(mnt, "/tmp/docker-test-devmapper-") || !strings.HasSuffix(mnt, "/mnt/1") {
486
+			t.Fatalf("Wrong mounted call\nExpected: Mounted(%v)\nReceived: Mounted(%v)\n", "/tmp/docker-test-devmapper-.../mnt/1", mnt)
487
+		}
488
+		return false, nil
489
+	}
490
+
491
+	func() {
492
+		d := newDriver(t)
493
+
494
+		calls.Assert(t,
495
+			"DmSetDevDir",
496
+			"DmLogWithErrnoInit",
497
+			"DmTaskSetName",
498
+			"DmTaskRun",
499
+			"DmTaskGetInfo",
500
+			"DmAttachLoopDevice",
501
+			"execRun",
502
+			"DmTaskCreate",
503
+			"DmGetBlockSize",
504
+			"DmTaskSetTarget",
505
+			"DmTaskSetCookie",
506
+			"DmUdevWait",
507
+			"DmTaskSetSector",
508
+			"DmTaskSetMessage",
509
+			"DmTaskSetAddNode",
510
+		)
511
+
512
+		if err := d.Create("1", ""); err != nil {
513
+			t.Fatal(err)
514
+		}
515
+		calls.Assert(t,
516
+			"DmTaskCreate",
517
+			"DmTaskGetInfo",
518
+			"sysMount",
519
+			"Mounted",
520
+			"DmTaskRun",
521
+			"DmTaskSetTarget",
522
+			"DmTaskSetSector",
523
+			"DmTaskSetCookie",
524
+			"DmUdevWait",
525
+			"DmTaskSetName",
526
+			"DmTaskSetMessage",
527
+			"DmTaskSetAddNode",
528
+		)
529
+
530
+	}()
531
+
532
+	runtime.GC()
533
+
534
+	calls.Assert(t,
535
+		"DmTaskDestroy",
536
+	)
537
+}
538
+
539
+func TestDriverRemove(t *testing.T) {
540
+	denyAllDevmapper()
541
+	denyAllSyscall()
542
+	defer denyAllSyscall()
543
+	defer denyAllDevmapper()
544
+
545
+	calls := make(Set)
546
+	mockAllDevmapper(calls)
547
+
548
+	sysMount = func(source, target, fstype string, flags uintptr, data string) (err error) {
549
+		calls["sysMount"] = true
550
+		// FIXME: compare the exact source and target strings (inodes + devname)
551
+		if expectedSource := "/dev/mapper/docker-"; !strings.HasPrefix(source, expectedSource) {
552
+			t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedSource, source)
553
+		}
554
+		if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) {
555
+			t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target)
556
+		}
557
+		if expectedFstype := "ext4"; fstype != expectedFstype {
558
+			t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFstype, fstype)
559
+		}
560
+		if expectedFlags := uintptr(3236757504); flags != expectedFlags {
561
+			t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags)
562
+		}
563
+		return nil
564
+	}
565
+	sysUnmount = func(target string, flags int) (err error) {
566
+		calls["sysUnmount"] = true
567
+		// FIXME: compare the exact source and target strings (inodes + devname)
568
+		if expectedTarget := "/tmp/docker-test-devmapper-"; !strings.HasPrefix(target, expectedTarget) {
569
+			t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedTarget, target)
570
+		}
571
+		if expectedFlags := 0; flags != expectedFlags {
572
+			t.Fatalf("Wrong syscall call\nExpected: Mount(%v)\nReceived: Mount(%v)\n", expectedFlags, flags)
573
+		}
574
+		return nil
575
+	}
576
+	Mounted = func(mnt string) (bool, error) {
577
+		calls["Mounted"] = true
578
+		return false, nil
579
+	}
580
+
581
+	func() {
582
+		d := newDriver(t)
583
+
584
+		calls.Assert(t,
585
+			"DmSetDevDir",
586
+			"DmLogWithErrnoInit",
587
+			"DmTaskSetName",
588
+			"DmTaskRun",
589
+			"DmTaskGetInfo",
590
+			"DmAttachLoopDevice",
591
+			"execRun",
592
+			"DmTaskCreate",
593
+			"DmGetBlockSize",
594
+			"DmTaskSetTarget",
595
+			"DmTaskSetCookie",
596
+			"DmUdevWait",
597
+			"DmTaskSetSector",
598
+			"DmTaskSetMessage",
599
+			"DmTaskSetAddNode",
600
+		)
601
+
602
+		if err := d.Create("1", ""); err != nil {
603
+			t.Fatal(err)
604
+		}
605
+
606
+		calls.Assert(t,
607
+			"DmTaskCreate",
608
+			"DmTaskGetInfo",
609
+			"sysMount",
610
+			"Mounted",
611
+			"DmTaskRun",
612
+			"DmTaskSetTarget",
613
+			"DmTaskSetSector",
614
+			"DmTaskSetCookie",
615
+			"DmUdevWait",
616
+			"DmTaskSetName",
617
+			"DmTaskSetMessage",
618
+			"DmTaskSetAddNode",
619
+		)
620
+
621
+		Mounted = func(mnt string) (bool, error) {
622
+			calls["Mounted"] = true
623
+			return true, nil
624
+		}
625
+
626
+		if err := d.Remove("1"); err != nil {
627
+			t.Fatal(err)
628
+		}
629
+
630
+		calls.Assert(t,
631
+			"DmTaskRun",
632
+			"DmTaskSetSector",
633
+			"DmTaskSetName",
634
+			"DmTaskSetMessage",
635
+			"DmTaskCreate",
636
+			"DmTaskGetInfo",
637
+			"Mounted",
638
+			"sysUnmount",
639
+		)
640
+	}()
641
+	runtime.GC()
642
+
643
+	calls.Assert(t,
644
+		"DmTaskDestroy",
645
+	)
646
+}
647
+
648
+func TestCleanup(t *testing.T) {
649
+	t.Skip("FIXME: not a unit test")
650
+	t.Skip("Unimplemented")
651
+	d := newDriver(t)
652
+	defer osRemoveAll(d.home)
653
+
654
+	mountPoints := make([]string, 2)
655
+
656
+	if err := d.Create("1", ""); err != nil {
657
+		t.Fatal(err)
658
+	}
659
+	// Mount the id
660
+	p, err := d.Get("1")
661
+	if err != nil {
662
+		t.Fatal(err)
663
+	}
664
+	mountPoints[0] = p
665
+
666
+	if err := d.Create("2", "1"); err != nil {
667
+		t.Fatal(err)
668
+	}
669
+
670
+	p, err = d.Get("2")
671
+	if err != nil {
672
+		t.Fatal(err)
673
+	}
674
+	mountPoints[1] = p
675
+
676
+	// Ensure that all the mount points are currently mounted
677
+	for _, p := range mountPoints {
678
+		if mounted, err := Mounted(p); err != nil {
679
+			t.Fatal(err)
680
+		} else if !mounted {
681
+			t.Fatalf("Expected %s to be mounted", p)
682
+		}
683
+	}
684
+
685
+	// Ensure that devices are active
686
+	for _, p := range []string{"1", "2"} {
687
+		if !d.HasActivatedDevice(p) {
688
+			t.Fatalf("Expected %s to have an active device", p)
689
+		}
690
+	}
691
+
692
+	if err := d.Cleanup(); err != nil {
693
+		t.Fatal(err)
694
+	}
695
+
696
+	// Ensure that all the mount points are no longer mounted
697
+	for _, p := range mountPoints {
698
+		if mounted, err := Mounted(p); err != nil {
699
+			t.Fatal(err)
700
+		} else if mounted {
701
+			t.Fatalf("Expected %s to not be mounted", p)
702
+		}
703
+	}
704
+
705
+	// Ensure that devices are no longer activated
706
+	for _, p := range []string{"1", "2"} {
707
+		if d.HasActivatedDevice(p) {
708
+			t.Fatalf("Expected %s not be an active device", p)
709
+		}
710
+	}
711
+}
712
+
713
+func TestNotMounted(t *testing.T) {
714
+	t.Skip("FIXME: not a unit test")
715
+	t.Skip("Not implemented")
716
+	d := newDriver(t)
717
+	defer cleanup(d)
718
+
719
+	if err := d.Create("1", ""); err != nil {
720
+		t.Fatal(err)
721
+	}
722
+
723
+	mounted, err := Mounted(path.Join(d.home, "mnt", "1"))
724
+	if err != nil {
725
+		t.Fatal(err)
726
+	}
727
+	if mounted {
728
+		t.Fatal("Id 1 should not be mounted")
729
+	}
730
+}
731
+
732
+func TestMounted(t *testing.T) {
733
+	t.Skip("FIXME: not a unit test")
734
+	d := newDriver(t)
735
+	defer cleanup(d)
736
+
737
+	if err := d.Create("1", ""); err != nil {
738
+		t.Fatal(err)
739
+	}
740
+	if _, err := d.Get("1"); err != nil {
741
+		t.Fatal(err)
742
+	}
743
+
744
+	mounted, err := Mounted(path.Join(d.home, "mnt", "1"))
745
+	if err != nil {
746
+		t.Fatal(err)
747
+	}
748
+	if !mounted {
749
+		t.Fatal("Id 1 should be mounted")
750
+	}
751
+}
752
+
753
+func TestInitCleanedDriver(t *testing.T) {
754
+	t.Skip("FIXME: not a unit test")
755
+	d := newDriver(t)
756
+
757
+	if err := d.Create("1", ""); err != nil {
758
+		t.Fatal(err)
759
+	}
760
+	if _, err := d.Get("1"); err != nil {
761
+		t.Fatal(err)
762
+	}
763
+
764
+	if err := d.Cleanup(); err != nil {
765
+		t.Fatal(err)
766
+	}
767
+
768
+	driver, err := Init(d.home)
769
+	if err != nil {
770
+		t.Fatal(err)
771
+	}
772
+	d = driver.(*Driver)
773
+	defer cleanup(d)
774
+
775
+	if _, err := d.Get("1"); err != nil {
776
+		t.Fatal(err)
777
+	}
778
+}
779
+
780
+func TestMountMountedDriver(t *testing.T) {
781
+	t.Skip("FIXME: not a unit test")
782
+	d := newDriver(t)
783
+	defer cleanup(d)
784
+
785
+	if err := d.Create("1", ""); err != nil {
786
+		t.Fatal(err)
787
+	}
788
+
789
+	// Perform get on same id to ensure that it will
790
+	// not be mounted twice
791
+	if _, err := d.Get("1"); err != nil {
792
+		t.Fatal(err)
793
+	}
794
+	if _, err := d.Get("1"); err != nil {
795
+		t.Fatal(err)
796
+	}
797
+}
798
+
799
+func TestGetReturnsValidDevice(t *testing.T) {
800
+	t.Skip("FIXME: not a unit test")
801
+	d := newDriver(t)
802
+	defer cleanup(d)
803
+
804
+	if err := d.Create("1", ""); err != nil {
805
+		t.Fatal(err)
806
+	}
807
+
808
+	if !d.HasDevice("1") {
809
+		t.Fatalf("Expected id 1 to be in device set")
810
+	}
811
+
812
+	if _, err := d.Get("1"); err != nil {
813
+		t.Fatal(err)
814
+	}
815
+
816
+	if !d.HasActivatedDevice("1") {
817
+		t.Fatalf("Expected id 1 to be activated")
818
+	}
819
+
820
+	if !d.HasInitializedDevice("1") {
821
+		t.Fatalf("Expected id 1 to be initialized")
822
+	}
823
+}
824
+
825
+func TestDriverGetSize(t *testing.T) {
826
+	t.Skip("FIXME: not a unit test")
827
+	t.Skipf("Size is currently not implemented")
828
+
829
+	d := newDriver(t)
830
+	defer cleanup(d)
831
+
832
+	if err := d.Create("1", ""); err != nil {
833
+		t.Fatal(err)
834
+	}
835
+
836
+	mountPoint, err := d.Get("1")
837
+	if err != nil {
838
+		t.Fatal(err)
839
+	}
840
+
841
+	size := int64(1024)
842
+
843
+	f, err := osCreate(path.Join(mountPoint, "test_file"))
844
+	if err != nil {
845
+		t.Fatal(err)
846
+	}
847
+	if err := f.Truncate(size); err != nil {
848
+		t.Fatal(err)
849
+	}
850
+	f.Close()
851
+
852
+	// diffSize, err := d.DiffSize("1")
853
+	// if err != nil {
854
+	// 	t.Fatal(err)
855
+	// }
856
+	// if diffSize != size {
857
+	// 	t.Fatalf("Expected size %d got %d", size, diffSize)
858
+	// }
859
+}
860
+
861
+func assertMap(t *testing.T, m map[string]bool, keys ...string) {
862
+	for _, key := range keys {
863
+		if _, exists := m[key]; !exists {
864
+			t.Fatalf("Key not set: %s", key)
865
+		}
866
+		delete(m, key)
867
+	}
868
+	if len(m) != 0 {
869
+		t.Fatalf("Unexpected keys: %v", m)
870
+	}
871
+}
0 872
new file mode 100644
... ...
@@ -0,0 +1,25 @@
0
+package devmapper
1
+
2
+import (
3
+	"path/filepath"
4
+)
5
+
6
+// FIXME: this is copy-pasted from the aufs driver.
7
+// It should be moved into the core.
8
+
9
+var Mounted = func(mountpoint string) (bool, error) {
10
+	mntpoint, err := osStat(mountpoint)
11
+	if err != nil {
12
+		if osIsNotExist(err) {
13
+			return false, nil
14
+		}
15
+		return false, err
16
+	}
17
+	parent, err := osStat(filepath.Join(mountpoint, ".."))
18
+	if err != nil {
19
+		return false, err
20
+	}
21
+	mntpointSt := toSysStatT(mntpoint.Sys())
22
+	parentSt := toSysStatT(parent.Sys())
23
+	return mntpointSt.Dev != parentSt.Dev, nil
24
+}
0 25
new file mode 100644
... ...
@@ -0,0 +1,50 @@
0
+package devmapper
1
+
2
+import (
3
+	"os"
4
+	"os/exec"
5
+	"syscall"
6
+)
7
+
8
+type (
9
+	sysStatT syscall.Stat_t
10
+	sysErrno syscall.Errno
11
+
12
+	osFile struct{ *os.File }
13
+)
14
+
15
+var (
16
+	sysMount       = syscall.Mount
17
+	sysUnmount     = syscall.Unmount
18
+	sysCloseOnExec = syscall.CloseOnExec
19
+	sysSyscall     = syscall.Syscall
20
+
21
+	osOpenFile   = os.OpenFile
22
+	osNewFile    = os.NewFile
23
+	osCreate     = os.Create
24
+	osStat       = os.Stat
25
+	osIsNotExist = os.IsNotExist
26
+	osIsExist    = os.IsExist
27
+	osMkdirAll   = os.MkdirAll
28
+	osRemoveAll  = os.RemoveAll
29
+	osRename     = os.Rename
30
+	osReadlink   = os.Readlink
31
+
32
+	execRun = func(name string, args ...string) error {
33
+		return exec.Command(name, args...).Run()
34
+	}
35
+)
36
+
37
+const (
38
+	sysMsMgcVal = syscall.MS_MGC_VAL
39
+	sysMsRdOnly = syscall.MS_RDONLY
40
+	sysEInval   = syscall.EINVAL
41
+	sysSysIoctl = syscall.SYS_IOCTL
42
+
43
+	osORdWr   = os.O_RDWR
44
+	osOCreate = os.O_CREATE
45
+)
46
+
47
+func toSysStatT(i interface{}) *sysStatT {
48
+	return (*sysStatT)(i.(*syscall.Stat_t))
49
+}
0 50
new file mode 100644
... ...
@@ -0,0 +1,90 @@
0
+package graphdriver
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/archive"
5
+	"github.com/dotcloud/docker/utils"
6
+	"os"
7
+	"path"
8
+)
9
+
10
+type InitFunc func(root string) (Driver, error)
11
+
12
+type Driver interface {
13
+	String() string
14
+
15
+	Create(id, parent string) error
16
+	Remove(id string) error
17
+
18
+	Get(id string) (dir string, err error)
19
+	Exists(id string) bool
20
+
21
+	Status() [][2]string
22
+
23
+	Cleanup() error
24
+}
25
+
26
+type Differ interface {
27
+	Diff(id string) (archive.Archive, error)
28
+	Changes(id string) ([]archive.Change, error)
29
+	ApplyDiff(id string, diff archive.Archive) error
30
+	DiffSize(id string) (bytes int64, err error)
31
+}
32
+
33
+var (
34
+	DefaultDriver string
35
+	// All registred drivers
36
+	drivers map[string]InitFunc
37
+	// Slice of drivers that should be used in an order
38
+	priority = []string{
39
+		"aufs",
40
+		"devicemapper",
41
+		"vfs",
42
+	}
43
+)
44
+
45
+func init() {
46
+	drivers = make(map[string]InitFunc)
47
+}
48
+
49
+func Register(name string, initFunc InitFunc) error {
50
+	if _, exists := drivers[name]; exists {
51
+		return fmt.Errorf("Name already registered %s", name)
52
+	}
53
+	drivers[name] = initFunc
54
+
55
+	return nil
56
+}
57
+
58
+func GetDriver(name, home string) (Driver, error) {
59
+	if initFunc, exists := drivers[name]; exists {
60
+		return initFunc(path.Join(home, name))
61
+	}
62
+	return nil, fmt.Errorf("No such driver: %s", name)
63
+}
64
+
65
+func New(root string) (driver Driver, err error) {
66
+	for _, name := range []string{os.Getenv("DOCKER_DRIVER"), DefaultDriver} {
67
+		if name != "" {
68
+			return GetDriver(name, root)
69
+		}
70
+	}
71
+
72
+	// Check for priority drivers first
73
+	for _, name := range priority {
74
+		if driver, err = GetDriver(name, root); err != nil {
75
+			utils.Debugf("Error loading driver %s: %s", name, err)
76
+			continue
77
+		}
78
+		return driver, nil
79
+	}
80
+
81
+	// Check all registered drivers if no priority driver is found
82
+	for _, initFunc := range drivers {
83
+		if driver, err = initFunc(root); err != nil {
84
+			continue
85
+		}
86
+		return driver, nil
87
+	}
88
+	return nil, err
89
+}
0 90
new file mode 100644
... ...
@@ -0,0 +1,91 @@
0
+package vfs
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/dotcloud/docker/graphdriver"
5
+	"os"
6
+	"os/exec"
7
+	"path"
8
+)
9
+
10
+func init() {
11
+	graphdriver.Register("vfs", Init)
12
+}
13
+
14
+func Init(home string) (graphdriver.Driver, error) {
15
+	d := &Driver{
16
+		home: home,
17
+	}
18
+	return d, nil
19
+}
20
+
21
+type Driver struct {
22
+	home string
23
+}
24
+
25
+func (d *Driver) String() string {
26
+	return "vfs"
27
+}
28
+
29
+func (d *Driver) Status() [][2]string {
30
+	return nil
31
+}
32
+
33
+func (d *Driver) Cleanup() error {
34
+	return nil
35
+}
36
+
37
+func copyDir(src, dst string) error {
38
+	cmd := exec.Command("cp", "-aT", "--reflink=auto", src, dst)
39
+	if err := cmd.Run(); err != nil {
40
+		return err
41
+	}
42
+	return nil
43
+}
44
+
45
+func (d *Driver) Create(id string, parent string) error {
46
+	dir := d.dir(id)
47
+	if err := os.MkdirAll(path.Dir(dir), 0700); err != nil {
48
+		return err
49
+	}
50
+	if err := os.Mkdir(dir, 0700); err != nil {
51
+		return err
52
+	}
53
+	if parent == "" {
54
+		return nil
55
+	}
56
+	parentDir, err := d.Get(parent)
57
+	if err != nil {
58
+		return fmt.Errorf("%s: %s", parent, err)
59
+	}
60
+	if err := copyDir(parentDir, dir); err != nil {
61
+		return err
62
+	}
63
+	return nil
64
+}
65
+
66
+func (d *Driver) dir(id string) string {
67
+	return path.Join(d.home, "dir", path.Base(id))
68
+}
69
+
70
+func (d *Driver) Remove(id string) error {
71
+	if _, err := os.Stat(d.dir(id)); err != nil {
72
+		return err
73
+	}
74
+	return os.RemoveAll(d.dir(id))
75
+}
76
+
77
+func (d *Driver) Get(id string) (string, error) {
78
+	dir := d.dir(id)
79
+	if st, err := os.Stat(dir); err != nil {
80
+		return "", err
81
+	} else if !st.IsDir() {
82
+		return "", fmt.Errorf("%s: not a directory", dir)
83
+	}
84
+	return dir, nil
85
+}
86
+
87
+func (d *Driver) Exists(id string) bool {
88
+	_, err := os.Stat(d.dir(id))
89
+	return err == nil
90
+}
... ...
@@ -6,17 +6,14 @@ import (
6 6
 	"encoding/json"
7 7
 	"fmt"
8 8
 	"github.com/dotcloud/docker/archive"
9
+	"github.com/dotcloud/docker/graphdriver"
9 10
 	"github.com/dotcloud/docker/utils"
10 11
 	"io"
11 12
 	"io/ioutil"
12
-	"log"
13 13
 	"os"
14
-	"os/exec"
15 14
 	"path"
16
-	"path/filepath"
17 15
 	"strconv"
18 16
 	"strings"
19
-	"syscall"
20 17
 	"time"
21 18
 )
22 19
 
... ...
@@ -62,39 +59,56 @@ func LoadImage(root string) (*Image, error) {
62 62
 		img.Size = int64(size)
63 63
 	}
64 64
 
65
-	// Check that the filesystem layer exists
66
-	if stat, err := os.Stat(layerPath(root)); err != nil {
67
-		if os.IsNotExist(err) {
68
-			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
69
-		}
70
-		return nil, err
71
-	} else if !stat.IsDir() {
72
-		return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.ID, layerPath(root))
73
-	}
74 65
 	return img, nil
75 66
 }
76 67
 
77
-func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root string) error {
78
-	// Check that root doesn't already exist
79
-	if _, err := os.Stat(root); err == nil {
80
-		return fmt.Errorf("Image %s already exists", img.ID)
81
-	} else if !os.IsNotExist(err) {
82
-		return err
83
-	}
68
+func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root, layer string) error {
84 69
 	// Store the layer
85
-	layer := layerPath(root)
70
+	var (
71
+		size   int64
72
+		err    error
73
+		driver = img.graph.driver
74
+	)
86 75
 	if err := os.MkdirAll(layer, 0755); err != nil {
87 76
 		return err
88 77
 	}
89 78
 
90 79
 	// If layerData is not nil, unpack it into the new layer
91 80
 	if layerData != nil {
92
-		start := time.Now()
93
-		utils.Debugf("Start untar layer")
94
-		if err := archive.Untar(layerData, layer); err != nil {
95
-			return err
81
+		if differ, ok := driver.(graphdriver.Differ); ok {
82
+			if err := differ.ApplyDiff(img.ID, layerData); err != nil {
83
+				return err
84
+			}
85
+
86
+			if size, err = differ.DiffSize(img.ID); err != nil {
87
+				return err
88
+			}
89
+		} else {
90
+			start := time.Now()
91
+			utils.Debugf("Start untar layer")
92
+			if err := archive.ApplyLayer(layer, layerData); err != nil {
93
+				return err
94
+			}
95
+			utils.Debugf("Untar time: %vs", time.Now().Sub(start).Seconds())
96
+
97
+			if img.Parent == "" {
98
+				if size, err = utils.TreeSize(layer); err != nil {
99
+					return err
100
+				}
101
+			} else {
102
+				parent, err := driver.Get(img.Parent)
103
+				if err != nil {
104
+					return err
105
+				}
106
+				changes, err := archive.ChangesDirs(layer, parent)
107
+				if err != nil {
108
+					return err
109
+				}
110
+				if size = archive.ChangesSize(layer, changes); err != nil {
111
+					return err
112
+				}
113
+			}
96 114
 		}
97
-		utils.Debugf("Untar time: %vs", time.Now().Sub(start).Seconds())
98 115
 	}
99 116
 
100 117
 	// If raw json is provided, then use it
... ...
@@ -102,117 +116,60 @@ func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root str
102 102
 		return ioutil.WriteFile(jsonPath(root), jsonData, 0600)
103 103
 	}
104 104
 	// Otherwise, unmarshal the image
105
-	jsonData, err := json.Marshal(img)
106
-	if err != nil {
105
+	if jsonData, err = json.Marshal(img); err != nil {
107 106
 		return err
108 107
 	}
109 108
 	if err := ioutil.WriteFile(jsonPath(root), jsonData, 0600); err != nil {
110 109
 		return err
111 110
 	}
112 111
 
113
-	return StoreSize(img, root)
114
-}
115
-
116
-func StoreSize(img *Image, root string) error {
117
-	layer := layerPath(root)
118
-	data := make(map[uint64]bool)
119
-
120
-	var totalSize int64
121
-	filepath.Walk(layer, func(path string, fileInfo os.FileInfo, err error) error {
122
-		size := fileInfo.Size()
123
-		if size == 0 {
124
-			return nil
125
-		}
126
-
127
-		inode := fileInfo.Sys().(*syscall.Stat_t).Ino
128
-		if _, entryExists := data[inode]; entryExists {
129
-			return nil
130
-		}
131
-		data[inode] = false
132
-
133
-		totalSize += size
134
-		return nil
135
-	})
136
-	img.Size = totalSize
137
-
138
-	if err := ioutil.WriteFile(path.Join(root, "layersize"), []byte(strconv.Itoa(int(totalSize))), 0600); err != nil {
139
-		return nil
112
+	img.Size = size
113
+	if err := img.SaveSize(root); err != nil {
114
+		return err
140 115
 	}
141 116
 
142 117
 	return nil
143 118
 }
144 119
 
145
-func layerPath(root string) string {
146
-	return path.Join(root, "layer")
120
+// SaveSize stores the current `size` value of `img` in the directory `root`.
121
+func (img *Image) SaveSize(root string) error {
122
+	if err := ioutil.WriteFile(path.Join(root, "layersize"), []byte(strconv.Itoa(int(img.Size))), 0600); err != nil {
123
+		return fmt.Errorf("Error storing image size in %s/layersize: %s", root, err)
124
+	}
125
+	return nil
147 126
 }
148 127
 
149 128
 func jsonPath(root string) string {
150 129
 	return path.Join(root, "json")
151 130
 }
152 131
 
153
-func MountAUFS(ro []string, rw string, target string) error {
154
-	// FIXME: Now mount the layers
155
-	rwBranch := fmt.Sprintf("%v=rw", rw)
156
-	roBranches := ""
157
-	for _, layer := range ro {
158
-		roBranches += fmt.Sprintf("%v=ro+wh:", layer)
159
-	}
160
-	branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
161
-
162
-	branches += ",xino=/dev/shm/aufs.xino"
163
-
164
-	//if error, try to load aufs kernel module
165
-	if err := mount("none", target, "aufs", 0, branches); err != nil {
166
-		log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...")
167
-		if err := exec.Command("modprobe", "aufs").Run(); err != nil {
168
-			return fmt.Errorf("Unable to load the AUFS module")
169
-		}
170
-		log.Printf("...module loaded.")
171
-		if err := mount("none", target, "aufs", 0, branches); err != nil {
172
-			return fmt.Errorf("Unable to mount using aufs")
173
-		}
174
-	}
175
-	return nil
176
-}
177
-
178 132
 // TarLayer returns a tar archive of the image's filesystem layer.
179
-func (img *Image) TarLayer(compression archive.Compression) (archive.Archive, error) {
180
-	layerPath, err := img.layer()
181
-	if err != nil {
182
-		return nil, err
183
-	}
184
-	return archive.Tar(layerPath, compression)
185
-}
186
-
187
-func (img *Image) Mount(root, rw string) error {
188
-	if mounted, err := Mounted(root); err != nil {
189
-		return err
190
-	} else if mounted {
191
-		return fmt.Errorf("%s is already mounted", root)
192
-	}
193
-	layers, err := img.layers()
194
-	if err != nil {
195
-		return err
196
-	}
197
-	// Create the target directories if they don't exist
198
-	if err := os.Mkdir(root, 0755); err != nil && !os.IsExist(err) {
199
-		return err
200
-	}
201
-	if err := os.Mkdir(rw, 0755); err != nil && !os.IsExist(err) {
202
-		return err
133
+func (img *Image) TarLayer() (archive.Archive, error) {
134
+	if img.graph == nil {
135
+		return nil, fmt.Errorf("Can't load storage driver for unregistered image %s", img.ID)
203 136
 	}
204
-	if err := MountAUFS(layers, rw, root); err != nil {
205
-		return err
137
+	driver := img.graph.driver
138
+	if differ, ok := driver.(graphdriver.Differ); ok {
139
+		return differ.Diff(img.ID)
206 140
 	}
207
-	return nil
208
-}
209 141
 
210
-func (img *Image) Changes(rw string) ([]Change, error) {
211
-	layers, err := img.layers()
142
+	imgFs, err := driver.Get(img.ID)
212 143
 	if err != nil {
213 144
 		return nil, err
214 145
 	}
215
-	return Changes(layers, rw)
146
+	if img.Parent == "" {
147
+		return archive.Tar(imgFs, archive.Uncompressed)
148
+	} else {
149
+		parentFs, err := driver.Get(img.Parent)
150
+		if err != nil {
151
+			return nil, err
152
+		}
153
+		changes, err := archive.ChangesDirs(imgFs, parentFs)
154
+		if err != nil {
155
+			return nil, err
156
+		}
157
+		return archive.ExportChanges(imgFs, changes)
158
+	}
216 159
 }
217 160
 
218 161
 func ValidateID(id string) error {
... ...
@@ -250,40 +207,6 @@ func (img *Image) History() ([]*Image, error) {
250 250
 	return parents, nil
251 251
 }
252 252
 
253
-// layers returns all the filesystem layers needed to mount an image
254
-// FIXME: @shykes refactor this function with the new error handling
255
-//        (I'll do it if I have time tonight, I focus on the rest)
256
-func (img *Image) layers() ([]string, error) {
257
-	var (
258
-		list []string
259
-		e    error
260
-	)
261
-	if err := img.WalkHistory(
262
-		func(img *Image) (err error) {
263
-			if layer, err := img.layer(); err != nil {
264
-				e = err
265
-			} else if layer != "" {
266
-				list = append(list, layer)
267
-			}
268
-			return err
269
-		},
270
-	); err != nil {
271
-		return nil, err
272
-	} else if e != nil { // Did an error occur inside the handler?
273
-		return nil, e
274
-	}
275
-	if len(list) == 0 {
276
-		return nil, fmt.Errorf("No layer found for image %s\n", img.ID)
277
-	}
278
-
279
-	// Inject the dockerinit layer (empty place-holder for mount-binding dockerinit)
280
-	dockerinitLayer, err := img.getDockerInitLayer()
281
-	if err != nil {
282
-		return nil, err
283
-	}
284
-	return append([]string{dockerinitLayer}, list...), nil
285
-}
286
-
287 253
 func (img *Image) WalkHistory(handler func(*Image) error) (err error) {
288 254
 	currentImg := img
289 255
 	for currentImg != nil {
... ...
@@ -310,13 +233,6 @@ func (img *Image) GetParent() (*Image, error) {
310 310
 	return img.graph.Get(img.Parent)
311 311
 }
312 312
 
313
-func (img *Image) getDockerInitLayer() (string, error) {
314
-	if img.graph == nil {
315
-		return "", fmt.Errorf("Can't lookup dockerinit layer of unregistered image")
316
-	}
317
-	return img.graph.getDockerInitLayer()
318
-}
319
-
320 313
 func (img *Image) root() (string, error) {
321 314
 	if img.graph == nil {
322 315
 		return "", fmt.Errorf("Can't lookup root of unregistered image")
... ...
@@ -324,15 +240,6 @@ func (img *Image) root() (string, error) {
324 324
 	return img.graph.imageRoot(img.ID), nil
325 325
 }
326 326
 
327
-// Return the path of an image's layer
328
-func (img *Image) layer() (string, error) {
329
-	root, err := img.root()
330
-	if err != nil {
331
-		return "", err
332
-	}
333
-	return layerPath(root), nil
334
-}
335
-
336 327
 func (img *Image) getParentsSize(size int64) int64 {
337 328
 	parentImage, err := img.GetParent()
338 329
 	if err != nil || parentImage == nil {
... ...
@@ -342,6 +249,25 @@ func (img *Image) getParentsSize(size int64) int64 {
342 342
 	return parentImage.getParentsSize(size)
343 343
 }
344 344
 
345
+// Depth returns the number of parents for a
346
+// current image
347
+func (img *Image) Depth() (int, error) {
348
+	var (
349
+		count  = 0
350
+		parent = img
351
+		err    error
352
+	)
353
+
354
+	for parent != nil {
355
+		count++
356
+		parent, err = parent.GetParent()
357
+		if err != nil {
358
+			return -1, err
359
+		}
360
+	}
361
+	return count, nil
362
+}
363
+
345 364
 // Build an Image object from raw json data
346 365
 func NewImgJSON(src []byte) (*Image, error) {
347 366
 	ret := &Image{}
... ...
@@ -840,13 +840,12 @@ func TestImagesTree(t *testing.T) {
840 840
 			t.Fatal(err)
841 841
 		}
842 842
 		cmdOutput := string(cmdOutputBytes)
843
-
844 843
 		regexpStrings := []string{
845 844
 			fmt.Sprintf("└─%s Size: (\\d+.\\d+ MB) \\(virtual \\d+.\\d+ MB\\) Tags: %s:latest", unitTestImageIDShort, unitTestImageName),
846
-			"(?m)^  └─[0-9a-f]+",
847
-			"(?m)^    └─[0-9a-f]+",
848
-			"(?m)^      └─[0-9a-f]+",
849
-			fmt.Sprintf("        └─%s Size: \\d+ B \\(virtual \\d+.\\d+ MB\\) Tags: test:latest", utils.TruncateID(image.ID)),
845
+			"(?m)   └─[0-9a-f]+.*",
846
+			"(?m)    └─[0-9a-f]+.*",
847
+			"(?m)      └─[0-9a-f]+.*",
848
+			fmt.Sprintf("(?m)^        └─%s Size: \\d+.\\d+ MB \\(virtual \\d+.\\d+ MB\\) Tags: test:latest", utils.TruncateID(image.ID)),
850 849
 		}
851 850
 
852 851
 		compiledRegexps := []*regexp.Regexp{}
... ...
@@ -172,7 +172,7 @@ func TestDiff(t *testing.T) {
172 172
 	// Commit the container
173 173
 	img, err := runtime.Commit(container1, "", "", "unit test commited image - diff", "", nil)
174 174
 	if err != nil {
175
-		t.Error(err)
175
+		t.Fatal(err)
176 176
 	}
177 177
 
178 178
 	// Create a new container from the commited image
... ...
@@ -2,6 +2,7 @@ package docker
2 2
 
3 3
 import (
4 4
 	"github.com/dotcloud/docker"
5
+	"github.com/dotcloud/docker/graphdriver"
5 6
 	"io/ioutil"
6 7
 	"os"
7 8
 	"path"
... ...
@@ -9,8 +10,10 @@ import (
9 9
 )
10 10
 
11 11
 func TestMount(t *testing.T) {
12
-	graph := tempGraph(t)
12
+	graph, driver := tempGraph(t)
13 13
 	defer os.RemoveAll(graph.Root)
14
+	defer driver.Cleanup()
15
+
14 16
 	archive, err := fakeTar()
15 17
 	if err != nil {
16 18
 		t.Fatal(err)
... ...
@@ -32,26 +35,25 @@ func TestMount(t *testing.T) {
32 32
 	if err := os.MkdirAll(rw, 0700); err != nil {
33 33
 		t.Fatal(err)
34 34
 	}
35
-	if err := image.Mount(rootfs, rw); err != nil {
35
+
36
+	if _, err := driver.Get(image.ID); err != nil {
36 37
 		t.Fatal(err)
37 38
 	}
38
-	// FIXME: test for mount contents
39
-	defer func() {
40
-		if err := docker.Unmount(rootfs); err != nil {
41
-			t.Error(err)
42
-		}
43
-	}()
44 39
 }
45 40
 
46 41
 //FIXME: duplicate
47
-func tempGraph(t *testing.T) *docker.Graph {
42
+func tempGraph(t *testing.T) (*docker.Graph, graphdriver.Driver) {
48 43
 	tmp, err := ioutil.TempDir("", "docker-graph-")
49 44
 	if err != nil {
50 45
 		t.Fatal(err)
51 46
 	}
52
-	graph, err := docker.NewGraph(tmp)
47
+	driver, err := graphdriver.New(tmp)
48
+	if err != nil {
49
+		t.Fatal(err)
50
+	}
51
+	graph, err := docker.NewGraph(tmp, driver)
53 52
 	if err != nil {
54 53
 		t.Fatal(err)
55 54
 	}
56
-	return graph
55
+	return graph, driver
57 56
 }
58 57
deleted file mode 100644
... ...
@@ -1,53 +0,0 @@
1
-package docker
2
-
3
-import (
4
-	"fmt"
5
-	"github.com/dotcloud/docker/utils"
6
-	"os"
7
-	"os/exec"
8
-	"path/filepath"
9
-	"syscall"
10
-	"time"
11
-)
12
-
13
-func Unmount(target string) error {
14
-	if err := exec.Command("auplink", target, "flush").Run(); err != nil {
15
-		utils.Errorf("[warning]: couldn't run auplink before unmount: %s", err)
16
-	}
17
-	if err := syscall.Unmount(target, 0); err != nil {
18
-		return err
19
-	}
20
-	// Even though we just unmounted the filesystem, AUFS will prevent deleting the mntpoint
21
-	// for some time. We'll just keep retrying until it succeeds.
22
-	for retries := 0; retries < 1000; retries++ {
23
-		err := os.Remove(target)
24
-		if err == nil {
25
-			// rm mntpoint succeeded
26
-			return nil
27
-		}
28
-		if os.IsNotExist(err) {
29
-			// mntpoint doesn't exist anymore. Success.
30
-			return nil
31
-		}
32
-		// fmt.Printf("(%v) Remove %v returned: %v\n", retries, target, err)
33
-		time.Sleep(10 * time.Millisecond)
34
-	}
35
-	return fmt.Errorf("Umount: Failed to umount %v", target)
36
-}
37
-
38
-func Mounted(mountpoint string) (bool, error) {
39
-	mntpoint, err := os.Stat(mountpoint)
40
-	if err != nil {
41
-		if os.IsNotExist(err) {
42
-			return false, nil
43
-		}
44
-		return false, err
45
-	}
46
-	parent, err := os.Stat(filepath.Join(mountpoint, ".."))
47
-	if err != nil {
48
-		return false, err
49
-	}
50
-	mntpointSt := mntpoint.Sys().(*syscall.Stat_t)
51
-	parentSt := parent.Sys().(*syscall.Stat_t)
52
-	return mntpointSt.Dev != parentSt.Dev, nil
53
-}
54 1
deleted file mode 100644
... ...
@@ -1,7 +0,0 @@
1
-package docker
2
-
3
-import "errors"
4
-
5
-func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
6
-	return errors.New("mount is not implemented on darwin")
7
-}
8 1
deleted file mode 100644
... ...
@@ -1,7 +0,0 @@
1
-package docker
2
-
3
-import "syscall"
4
-
5
-func mount(source string, target string, fstype string, flags uintptr, data string) (err error) {
6
-	return syscall.Mount(source, target, fstype, flags, data)
7
-}
... ...
@@ -5,7 +5,12 @@ import (
5 5
 	"container/list"
6 6
 	"database/sql"
7 7
 	"fmt"
8
-	"github.com/dotcloud/docker/gograph"
8
+	"github.com/dotcloud/docker/archive"
9
+	"github.com/dotcloud/docker/graphdb"
10
+	"github.com/dotcloud/docker/graphdriver"
11
+	"github.com/dotcloud/docker/graphdriver/aufs"
12
+	_ "github.com/dotcloud/docker/graphdriver/devmapper"
13
+	_ "github.com/dotcloud/docker/graphdriver/vfs"
9 14
 	"github.com/dotcloud/docker/utils"
10 15
 	"io"
11 16
 	"io/ioutil"
... ...
@@ -19,6 +24,9 @@ import (
19 19
 	"time"
20 20
 )
21 21
 
22
+// Set the max depth to the aufs restriction
23
+const MaxImageDepth = 42
24
+
22 25
 var defaultDns = []string{"8.8.8.8", "8.8.4.4"}
23 26
 
24 27
 type Capabilities struct {
... ...
@@ -39,7 +47,8 @@ type Runtime struct {
39 39
 	volumes        *Graph
40 40
 	srv            *Server
41 41
 	config         *DaemonConfig
42
-	containerGraph *gograph.Database
42
+	containerGraph *graphdb.Database
43
+	driver         graphdriver.Driver
43 44
 }
44 45
 
45 46
 // List returns an array of all containers registered in the runtime.
... ...
@@ -118,6 +127,13 @@ func (runtime *Runtime) Register(container *Container) error {
118 118
 		return err
119 119
 	}
120 120
 
121
+	// Get the root filesystem from the driver
122
+	rootfs, err := runtime.driver.Get(container.ID)
123
+	if err != nil {
124
+		return fmt.Errorf("Error getting container filesystem %s from driver %s: %s", container.ID, runtime.driver, err)
125
+	}
126
+	container.rootfs = rootfs
127
+
121 128
 	container.runtime = runtime
122 129
 
123 130
 	// Attach to stdout and stderr
... ...
@@ -216,12 +232,8 @@ func (runtime *Runtime) Destroy(container *Container) error {
216 216
 		return err
217 217
 	}
218 218
 
219
-	if mounted, err := container.Mounted(); err != nil {
220
-		return err
221
-	} else if mounted {
222
-		if err := container.Unmount(); err != nil {
223
-			return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
224
-		}
219
+	if err := runtime.driver.Remove(container.ID); err != nil {
220
+		return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", runtime.driver, container.ID, err)
225 221
 	}
226 222
 
227 223
 	if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
... ...
@@ -247,6 +259,7 @@ func (runtime *Runtime) restore() error {
247 247
 		return err
248 248
 	}
249 249
 	containers := make(map[string]*Container)
250
+	currentDriver := runtime.driver.String()
250 251
 
251 252
 	for i, v := range dir {
252 253
 		id := v.Name()
... ...
@@ -258,8 +271,14 @@ func (runtime *Runtime) restore() error {
258 258
 			utils.Errorf("Failed to load container %v: %v", id, err)
259 259
 			continue
260 260
 		}
261
-		utils.Debugf("Loaded container %v", container.ID)
262
-		containers[container.ID] = container
261
+
262
+		// Ignore the container if it does not support the current driver being used by the graph
263
+		if container.Driver == "" && currentDriver == "aufs" || container.Driver == currentDriver {
264
+			utils.Debugf("Loaded container %v", container.ID)
265
+			containers[container.ID] = container
266
+		} else {
267
+			utils.Debugf("Cannot load container %s because it was created with another graph driver.", container.ID)
268
+		}
263 269
 	}
264 270
 
265 271
 	register := func(container *Container) {
... ...
@@ -344,6 +363,17 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
344 344
 		return nil, nil, err
345 345
 	}
346 346
 
347
+	// We add 2 layers to the depth because the container's rw and
348
+	// init layer add to the restriction
349
+	depth, err := img.Depth()
350
+	if err != nil {
351
+		return nil, nil, err
352
+	}
353
+
354
+	if depth+2 >= MaxImageDepth {
355
+		return nil, nil, fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth)
356
+	}
357
+
347 358
 	checkDeprecatedExpose := func(config *Config) bool {
348 359
 		if config != nil {
349 360
 			if config.PortSpecs != nil {
... ...
@@ -431,6 +461,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
431 431
 		// FIXME: do we need to store this in the container?
432 432
 		SysInitPath: sysInitPath,
433 433
 		Name:        name,
434
+		Driver:      runtime.driver.String(),
434 435
 	}
435 436
 	container.root = runtime.containerRoot(container.ID)
436 437
 	// Step 1: create the container directory.
... ...
@@ -439,6 +470,21 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
439 439
 		return nil, nil, err
440 440
 	}
441 441
 
442
+	initID := fmt.Sprintf("%s-init", container.ID)
443
+	if err := runtime.driver.Create(initID, img.ID); err != nil {
444
+		return nil, nil, err
445
+	}
446
+	initPath, err := runtime.driver.Get(initID)
447
+	if err != nil {
448
+		return nil, nil, err
449
+	}
450
+	if err := setupInitLayer(initPath); err != nil {
451
+		return nil, nil, err
452
+	}
453
+
454
+	if err := runtime.driver.Create(container.ID, initID); err != nil {
455
+		return nil, nil, err
456
+	}
442 457
 	resolvConf, err := utils.GetResolvConf()
443 458
 	if err != nil {
444 459
 		return nil, nil, err
... ...
@@ -549,7 +595,7 @@ func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
549 549
 	}
550 550
 	children := make(map[string]*Container)
551 551
 
552
-	err = runtime.containerGraph.Walk(name, func(p string, e *gograph.Entity) error {
552
+	err = runtime.containerGraph.Walk(name, func(p string, e *graphdb.Entity) error {
553 553
 		c := runtime.Get(e.ID())
554 554
 		if c == nil {
555 555
 			return fmt.Errorf("Could not get container for name %s and id %s", e.ID(), p)
... ...
@@ -584,24 +630,48 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) {
584 584
 }
585 585
 
586 586
 func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
587
+
588
+	// Set the default driver
589
+	graphdriver.DefaultDriver = config.GraphDriver
590
+
591
+	// Load storage driver
592
+	driver, err := graphdriver.New(config.Root)
593
+	if err != nil {
594
+		return nil, err
595
+	}
596
+	utils.Debugf("Using graph driver %s", driver)
597
+
587 598
 	runtimeRepo := path.Join(config.Root, "containers")
588 599
 
589 600
 	if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
590 601
 		return nil, err
591 602
 	}
592 603
 
604
+	if ad, ok := driver.(*aufs.Driver); ok {
605
+		if err := ad.Migrate(config.Root, setupInitLayer); err != nil {
606
+			return nil, err
607
+		}
608
+	}
609
+
593 610
 	if err := linkLxcStart(config.Root); err != nil {
594 611
 		return nil, err
595 612
 	}
596
-	g, err := NewGraph(path.Join(config.Root, "graph"))
613
+	g, err := NewGraph(path.Join(config.Root, "graph"), driver)
597 614
 	if err != nil {
598 615
 		return nil, err
599 616
 	}
600
-	volumes, err := NewGraph(path.Join(config.Root, "volumes"))
617
+
618
+	// We don't want to use a complex driver like aufs or devmapper
619
+	// for volumes, just a plain filesystem
620
+	volumesDriver, err := graphdriver.GetDriver("vfs", config.Root)
601 621
 	if err != nil {
602 622
 		return nil, err
603 623
 	}
604
-	repositories, err := NewTagStore(path.Join(config.Root, "repositories"), g)
624
+	volumes, err := NewGraph(path.Join(config.Root, "volumes"), volumesDriver)
625
+	if err != nil {
626
+		return nil, err
627
+	}
628
+	repositories, err := NewTagStore(path.Join(config.Root, "repositories-"+driver.String()), g)
605 629
 	if err != nil {
606 630
 		return nil, fmt.Errorf("Couldn't create Tag store: %s", err)
607 631
 	}
... ...
@@ -613,20 +683,20 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
613 613
 		return nil, err
614 614
 	}
615 615
 
616
-	gographPath := path.Join(config.Root, "linkgraph.db")
616
+	graphdbPath := path.Join(config.Root, "linkgraph.db")
617 617
 	initDatabase := false
618
-	if _, err := os.Stat(gographPath); err != nil {
618
+	if _, err := os.Stat(graphdbPath); err != nil {
619 619
 		if os.IsNotExist(err) {
620 620
 			initDatabase = true
621 621
 		} else {
622 622
 			return nil, err
623 623
 		}
624 624
 	}
625
-	conn, err := sql.Open("sqlite3", gographPath)
625
+	conn, err := sql.Open("sqlite3", graphdbPath)
626 626
 	if err != nil {
627 627
 		return nil, err
628 628
 	}
629
-	graph, err := gograph.NewDatabase(conn, initDatabase)
629
+	graph, err := graphdb.NewDatabase(conn, initDatabase)
630 630
 	if err != nil {
631 631
 		return nil, err
632 632
 	}
... ...
@@ -642,6 +712,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
642 642
 		volumes:        volumes,
643 643
 		config:         config,
644 644
 		containerGraph: graph,
645
+		driver:         driver,
645 646
 	}
646 647
 
647 648
 	if err := runtime.restore(); err != nil {
... ...
@@ -651,8 +722,76 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
651 651
 }
652 652
 
653 653
 func (runtime *Runtime) Close() error {
654
-	runtime.networkManager.Close()
655
-	return runtime.containerGraph.Close()
654
+	errorsStrings := []string{}
655
+	if err := runtime.networkManager.Close(); err != nil {
656
+		utils.Errorf("runtime.networkManager.Close(): %s", err.Error())
657
+		errorsStrings = append(errorsStrings, err.Error())
658
+	}
659
+	if err := runtime.driver.Cleanup(); err != nil {
660
+		utils.Errorf("runtime.driver.Cleanup(): %s", err.Error())
661
+		errorsStrings = append(errorsStrings, err.Error())
662
+	}
663
+	if err := runtime.containerGraph.Close(); err != nil {
664
+		utils.Errorf("runtime.containerGraph.Close(): %s", err.Error())
665
+		errorsStrings = append(errorsStrings, err.Error())
666
+	}
667
+	if len(errorsStrings) > 0 {
668
+		return fmt.Errorf("%s", strings.Join(errorsStrings, ", "))
669
+	}
670
+	return nil
671
+}
672
+
673
+func (runtime *Runtime) Mount(container *Container) error {
674
+	dir, err := runtime.driver.Get(container.ID)
675
+	if err != nil {
676
+		return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err)
677
+	}
678
+	if container.rootfs == "" {
679
+		container.rootfs = dir
680
+	} else if container.rootfs != dir {
681
+		return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
682
+			runtime.driver, container.ID, container.rootfs, dir)
683
+	}
684
+	return nil
685
+}
686
+
687
+func (runtime *Runtime) Unmount(container *Container) error {
688
+	// FIXME: Unmount is deprecated because drivers are responsible for mounting
689
+	// and unmounting when necessary. Use driver.Remove() instead.
690
+	return nil
691
+}
692
+
693
+func (runtime *Runtime) Changes(container *Container) ([]archive.Change, error) {
694
+	if differ, ok := runtime.driver.(graphdriver.Differ); ok {
695
+		return differ.Changes(container.ID)
696
+	}
697
+	cDir, err := runtime.driver.Get(container.ID)
698
+	if err != nil {
699
+		return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
700
+	}
701
+	initDir, err := runtime.driver.Get(container.ID + "-init")
702
+	if err != nil {
703
+		return nil, fmt.Errorf("Error getting container init rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
704
+	}
705
+	return archive.ChangesDirs(cDir, initDir)
706
+}
707
+
708
+func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) {
709
+	if differ, ok := runtime.driver.(graphdriver.Differ); ok {
710
+		return differ.Diff(container.ID)
711
+	}
712
+
713
+	changes, err := runtime.Changes(container)
714
+	if err != nil {
715
+		return nil, err
716
+	}
717
+
718
+	cDir, err := runtime.driver.Get(container.ID)
719
+	if err != nil {
720
+		return nil, fmt.Errorf("Error getting container rootfs %s from driver %s: %s", container.ID, container.runtime.driver, err)
721
+	}
722
+
723
+	return archive.ExportChanges(cDir, changes)
656 724
 }
657 725
 
658 726
 // Nuke kills all containers then removes all content
... ...
@@ -8,7 +8,7 @@ import (
8 8
 	"github.com/dotcloud/docker/archive"
9 9
 	"github.com/dotcloud/docker/auth"
10 10
 	"github.com/dotcloud/docker/engine"
11
-	"github.com/dotcloud/docker/gograph"
11
+	"github.com/dotcloud/docker/graphdb"
12 12
 	"github.com/dotcloud/docker/registry"
13 13
 	"github.com/dotcloud/docker/utils"
14 14
 	"io"
... ...
@@ -285,7 +285,7 @@ func (srv *Server) exportImage(image *Image, tempdir string) error {
285 285
 		}
286 286
 
287 287
 		// serialize filesystem
288
-		fs, err := archive.Tar(path.Join(srv.runtime.graph.Root, i.ID, "layer"), archive.Uncompressed)
288
+		fs, err := i.TarLayer()
289 289
 		if err != nil {
290 290
 			return err
291 291
 		}
... ...
@@ -342,7 +342,7 @@ func (srv *Server) ImageLoad(in io.Reader) error {
342 342
 	if err := os.Mkdir(repoDir, os.ModeDir); err != nil {
343 343
 		return err
344 344
 	}
345
-	if err := archive.Untar(repoFile, repoDir); err != nil {
345
+	if err := archive.Untar(repoFile, repoDir, nil); err != nil {
346 346
 		return err
347 347
 	}
348 348
 
... ...
@@ -596,6 +596,8 @@ func (srv *Server) DockerInfo() *APIInfo {
596 596
 	return &APIInfo{
597 597
 		Containers:         len(srv.runtime.List()),
598 598
 		Images:             imgcount,
599
+		Driver:             srv.runtime.driver.String(),
600
+		DriverStatus:       srv.runtime.driver.Status(),
599 601
 		MemoryLimit:        srv.runtime.capabilities.MemoryLimit,
600 602
 		SwapLimit:          srv.runtime.capabilities.SwapLimit,
601 603
 		IPv4Forwarding:     !srv.runtime.capabilities.IPv4ForwardingDisabled,
... ...
@@ -678,7 +680,7 @@ func (srv *Server) ContainerTop(name, psArgs string) (*APITop, error) {
678 678
 	return nil, fmt.Errorf("No such container: %s", name)
679 679
 }
680 680
 
681
-func (srv *Server) ContainerChanges(name string) ([]Change, error) {
681
+func (srv *Server) ContainerChanges(name string) ([]archive.Change, error) {
682 682
 	if container := srv.runtime.Get(name); container != nil {
683 683
 		return container.Changes()
684 684
 	}
... ...
@@ -691,7 +693,7 @@ func (srv *Server) Containers(all, size bool, n int, since, before string) []API
691 691
 	out := []APIContainers{}
692 692
 
693 693
 	names := map[string][]string{}
694
-	srv.runtime.containerGraph.Walk("/", func(p string, e *gograph.Entity) error {
694
+	srv.runtime.containerGraph.Walk("/", func(p string, e *graphdb.Entity) error {
695 695
 		names[e.ID()] = append(names[e.ID()], p)
696 696
 		return nil
697 697
 	}, -1)
... ...
@@ -763,12 +765,13 @@ func (srv *Server) pullImage(r *registry.Registry, out io.Writer, imgID, endpoin
763 763
 	// FIXME: Try to stream the images?
764 764
 	// FIXME: Launch the getRemoteImage() in goroutines
765 765
 
766
-	for _, id := range history {
766
+	for i := len(history) - 1; i >= 0; i-- {
767
+		id := history[i]
767 768
 
768 769
 		// ensure no two downloads of the same layer happen at the same time
769
-		if err := srv.poolAdd("pull", "layer:"+id); err != nil {
770
+		if c, err := srv.poolAdd("pull", "layer:"+id); err != nil {
770 771
 			utils.Errorf("Image (id: %s) pull is already running, skipping: %v", id, err)
771
-			return nil
772
+			<-c
772 773
 		}
773 774
 		defer srv.poolRemove("pull", "layer:"+id)
774 775
 
... ...
@@ -863,7 +866,7 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, localName
863 863
 			}
864 864
 
865 865
 			// ensure no two downloads of the same image happen at the same time
866
-			if err := srv.poolAdd("pull", "img:"+img.ID); err != nil {
866
+			if _, err := srv.poolAdd("pull", "img:"+img.ID); err != nil {
867 867
 				utils.Errorf("Image (id: %s) pull is already running, skipping: %v", img.ID, err)
868 868
 				if parallel {
869 869
 					errors <- nil
... ...
@@ -934,28 +937,27 @@ func (srv *Server) pullRepository(r *registry.Registry, out io.Writer, localName
934 934
 	return nil
935 935
 }
936 936
 
937
-func (srv *Server) poolAdd(kind, key string) error {
937
+func (srv *Server) poolAdd(kind, key string) (chan struct{}, error) {
938 938
 	srv.Lock()
939 939
 	defer srv.Unlock()
940 940
 
941
-	if _, exists := srv.pullingPool[key]; exists {
942
-		return fmt.Errorf("pull %s is already in progress", key)
941
+	if c, exists := srv.pullingPool[key]; exists {
942
+		return c, fmt.Errorf("pull %s is already in progress", key)
943 943
 	}
944
-	if _, exists := srv.pushingPool[key]; exists {
945
-		return fmt.Errorf("push %s is already in progress", key)
944
+	if c, exists := srv.pushingPool[key]; exists {
945
+		return c, fmt.Errorf("push %s is already in progress", key)
946 946
 	}
947 947
 
948
+	c := make(chan struct{})
948 949
 	switch kind {
949 950
 	case "pull":
950
-		srv.pullingPool[key] = struct{}{}
951
-		break
951
+		srv.pullingPool[key] = c
952 952
 	case "push":
953
-		srv.pushingPool[key] = struct{}{}
954
-		break
953
+		srv.pushingPool[key] = c
955 954
 	default:
956
-		return fmt.Errorf("Unknown pool type")
955
+		return nil, fmt.Errorf("Unknown pool type")
957 956
 	}
958
-	return nil
957
+	return c, nil
959 958
 }
960 959
 
961 960
 func (srv *Server) poolRemove(kind, key string) error {
... ...
@@ -963,11 +965,15 @@ func (srv *Server) poolRemove(kind, key string) error {
963 963
 	defer srv.Unlock()
964 964
 	switch kind {
965 965
 	case "pull":
966
-		delete(srv.pullingPool, key)
967
-		break
966
+		if c, exists := srv.pullingPool[key]; exists {
967
+			close(c)
968
+			delete(srv.pullingPool, key)
969
+		}
968 970
 	case "push":
969
-		delete(srv.pushingPool, key)
970
-		break
971
+		if c, exists := srv.pushingPool[key]; exists {
972
+			close(c)
973
+			delete(srv.pushingPool, key)
974
+		}
971 975
 	default:
972 976
 		return fmt.Errorf("Unknown pool type")
973 977
 	}
... ...
@@ -979,7 +985,7 @@ func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *ut
979 979
 	if err != nil {
980 980
 		return err
981 981
 	}
982
-	if err := srv.poolAdd("pull", localName+":"+tag); err != nil {
982
+	if _, err := srv.poolAdd("pull", localName+":"+tag); err != nil {
983 983
 		return err
984 984
 	}
985 985
 	defer srv.poolRemove("pull", localName+":"+tag)
... ...
@@ -1174,7 +1180,7 @@ func (srv *Server) pushImage(r *registry.Registry, out io.Writer, remote, imgID,
1174 1174
 
1175 1175
 // FIXME: Allow to interrupt current push when new push of same image is done.
1176 1176
 func (srv *Server) ImagePush(localName string, out io.Writer, sf *utils.StreamFormatter, authConfig *auth.AuthConfig, metaHeaders map[string][]string) error {
1177
-	if err := srv.poolAdd("push", localName); err != nil {
1177
+	if _, err := srv.poolAdd("push", localName); err != nil {
1178 1178
 		return err
1179 1179
 	}
1180 1180
 	defer srv.poolRemove("push", localName)
... ...
@@ -1820,8 +1826,8 @@ func NewServer(eng *engine.Engine, config *DaemonConfig) (*Server, error) {
1820 1820
 	srv := &Server{
1821 1821
 		Eng:         eng,
1822 1822
 		runtime:     runtime,
1823
-		pullingPool: make(map[string]struct{}),
1824
-		pushingPool: make(map[string]struct{}),
1823
+		pullingPool: make(map[string]chan struct{}),
1824
+		pushingPool: make(map[string]chan struct{}),
1825 1825
 		events:      make([]utils.JSONMessage, 0, 64), //only keeps the 64 last events
1826 1826
 		listeners:   make(map[string]chan utils.JSONMessage),
1827 1827
 		reqFactory:  nil,
... ...
@@ -1872,8 +1878,8 @@ func (srv *Server) GetEvents() []utils.JSONMessage {
1872 1872
 type Server struct {
1873 1873
 	sync.RWMutex
1874 1874
 	runtime     *Runtime
1875
-	pullingPool map[string]struct{}
1876
-	pushingPool map[string]struct{}
1875
+	pullingPool map[string]chan struct{}
1876
+	pushingPool map[string]chan struct{}
1877 1877
 	events      []utils.JSONMessage
1878 1878
 	listeners   map[string]chan utils.JSONMessage
1879 1879
 	reqFactory  *utils.HTTPRequestFactory
... ...
@@ -8,49 +8,38 @@ import (
8 8
 
9 9
 func TestPools(t *testing.T) {
10 10
 	srv := &Server{
11
-		pullingPool: make(map[string]struct{}),
12
-		pushingPool: make(map[string]struct{}),
11
+		pullingPool: make(map[string]chan struct{}),
12
+		pushingPool: make(map[string]chan struct{}),
13 13
 	}
14 14
 
15
-	err := srv.poolAdd("pull", "test1")
16
-	if err != nil {
15
+	if _, err := srv.poolAdd("pull", "test1"); err != nil {
17 16
 		t.Fatal(err)
18 17
 	}
19
-	err = srv.poolAdd("pull", "test2")
20
-	if err != nil {
18
+	if _, err := srv.poolAdd("pull", "test2"); err != nil {
21 19
 		t.Fatal(err)
22 20
 	}
23
-	err = srv.poolAdd("push", "test1")
24
-	if err == nil || err.Error() != "pull test1 is already in progress" {
21
+	if _, err := srv.poolAdd("push", "test1"); err == nil || err.Error() != "pull test1 is already in progress" {
25 22
 		t.Fatalf("Expected `pull test1 is already in progress`")
26 23
 	}
27
-	err = srv.poolAdd("pull", "test1")
28
-	if err == nil || err.Error() != "pull test1 is already in progress" {
24
+	if _, err := srv.poolAdd("pull", "test1"); err == nil || err.Error() != "pull test1 is already in progress" {
29 25
 		t.Fatalf("Expected `pull test1 is already in progress`")
30 26
 	}
31
-	err = srv.poolAdd("wait", "test3")
32
-	if err == nil || err.Error() != "Unknown pool type" {
27
+	if _, err := srv.poolAdd("wait", "test3"); err == nil || err.Error() != "Unknown pool type" {
33 28
 		t.Fatalf("Expected `Unknown pool type`")
34 29
 	}
35
-
36
-	err = srv.poolRemove("pull", "test2")
37
-	if err != nil {
30
+	if err := srv.poolRemove("pull", "test2"); err != nil {
38 31
 		t.Fatal(err)
39 32
 	}
40
-	err = srv.poolRemove("pull", "test2")
41
-	if err != nil {
33
+	if err := srv.poolRemove("pull", "test2"); err != nil {
42 34
 		t.Fatal(err)
43 35
 	}
44
-	err = srv.poolRemove("pull", "test1")
45
-	if err != nil {
36
+	if err := srv.poolRemove("pull", "test1"); err != nil {
46 37
 		t.Fatal(err)
47 38
 	}
48
-	err = srv.poolRemove("push", "test1")
49
-	if err != nil {
39
+	if err := srv.poolRemove("push", "test1"); err != nil {
50 40
 		t.Fatal(err)
51 41
 	}
52
-	err = srv.poolRemove("wait", "test3")
53
-	if err == nil || err.Error() != "Unknown pool type" {
42
+	if err := srv.poolRemove("wait", "test3"); err == nil || err.Error() != "Unknown pool type" {
54 43
 		t.Fatalf("Expected `Unknown pool type`")
55 44
 	}
56 45
 }
... ...
@@ -1,6 +1,7 @@
1 1
 package docker
2 2
 
3 3
 import (
4
+	"github.com/dotcloud/docker/graphdriver"
4 5
 	"github.com/dotcloud/docker/utils"
5 6
 	"os"
6 7
 	"path"
... ...
@@ -8,12 +9,16 @@ import (
8 8
 )
9 9
 
10 10
 const (
11
-	testImageName string = "myapp"
12
-	testImageID   string = "foo"
11
+	testImageName = "myapp"
12
+	testImageID   = "foo"
13 13
 )
14 14
 
15 15
 func mkTestTagStore(root string, t *testing.T) *TagStore {
16
-	graph, err := NewGraph(root)
16
+	driver, err := graphdriver.New(root)
17
+	if err != nil {
18
+		t.Fatal(err)
19
+	}
20
+	graph, err := NewGraph(root, driver)
17 21
 	if err != nil {
18 22
 		t.Fatal(err)
19 23
 	}
... ...
@@ -42,6 +47,7 @@ func TestLookupImage(t *testing.T) {
42 42
 	}
43 43
 	defer os.RemoveAll(tmp)
44 44
 	store := mkTestTagStore(tmp, t)
45
+	defer store.graph.driver.Cleanup()
45 46
 
46 47
 	if img, err := store.LookupImage(testImageName); err != nil {
47 48
 		t.Fatal(err)
... ...
@@ -1,14 +1,43 @@
1 1
 package docker
2 2
 
3
+/*
4
+#include <sys/ioctl.h>
5
+#include <linux/fs.h>
6
+#include <errno.h>
7
+
8
+// See linux.git/fs/btrfs/ioctl.h
9
+#define BTRFS_IOCTL_MAGIC 0x94
10
+#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int)
11
+
12
+int
13
+btrfs_reflink(int fd_out, int fd_in)
14
+{
15
+  int res;
16
+  res = ioctl(fd_out, BTRFS_IOC_CLONE, fd_in);
17
+  if (res < 0)
18
+    return errno;
19
+  return 0;
20
+}
21
+
22
+*/
23
+import "C"
3 24
 import (
4 25
 	"fmt"
26
+	"github.com/dotcloud/docker/archive"
5 27
 	"github.com/dotcloud/docker/namesgenerator"
6 28
 	"github.com/dotcloud/docker/utils"
29
+	"io"
7 30
 	"io/ioutil"
31
+	"os"
8 32
 	"strconv"
9 33
 	"strings"
34
+	"syscall"
10 35
 )
11 36
 
37
+type Change struct {
38
+	archive.Change
39
+}
40
+
12 41
 // Compare two Config struct. Do not compare the "Image" nor "Hostname" fields
13 42
 // If OpenStdin is set, then it differs
14 43
 func CompareConfig(a, b *Config) bool {
... ...
@@ -317,6 +346,14 @@ func migratePortMappings(config *Config, hostConfig *HostConfig) error {
317 317
 	return nil
318 318
 }
319 319
 
320
+func BtrfsReflink(fd_out, fd_in uintptr) error {
321
+	res := C.btrfs_reflink(C.int(fd_out), C.int(fd_in))
322
+	if res != 0 {
323
+		return syscall.Errno(res)
324
+	}
325
+	return nil
326
+}
327
+
320 328
 // Links come in the format of
321 329
 // name:alias
322 330
 func parseLink(rawLink string) (map[string]string, error) {
... ...
@@ -349,3 +386,14 @@ func (c *checker) Exists(name string) bool {
349 349
 func generateRandomName(runtime *Runtime) (string, error) {
350 350
 	return namesgenerator.GenerateRandomName(&checker{runtime})
351 351
 }
352
+
353
+func CopyFile(dstFile, srcFile *os.File) error {
354
+	err := BtrfsReflink(dstFile.Fd(), srcFile.Fd())
355
+	if err == nil {
356
+		return nil
357
+	}
358
+
359
+	// Fall back to normal copy
360
+	_, err = io.Copy(dstFile, srcFile)
361
+	return err
362
+}
352 363
new file mode 100644
... ...
@@ -0,0 +1,35 @@
0
+package utils
1
+
2
+import (
3
+	"os"
4
+	"path/filepath"
5
+	"syscall"
6
+)
7
+
8
+// TreeSize walks a directory tree and returns its total size in bytes.
9
+func TreeSize(dir string) (size int64, err error) {
10
+	data := make(map[uint64]bool)
11
+	err = filepath.Walk(dir, func(d string, fileInfo os.FileInfo, e error) error {
12
+		// Ignore directory sizes
13
+		if fileInfo == nil {
14
+			return nil
15
+		}
16
+
17
+		s := fileInfo.Size()
18
+		if fileInfo.IsDir() || s == 0 {
19
+			return nil
20
+		}
21
+
22
+		// Check inode to handle hard links correctly
23
+		inode := fileInfo.Sys().(*syscall.Stat_t).Ino
24
+		if _, exists := data[inode]; exists {
25
+			return nil
26
+		}
27
+		data[inode] = false
28
+
29
+		size += s
30
+
31
+		return nil
32
+	})
33
+	return
34
+}