Browse code

Simplify graphdriver interface: Create, Get. No more external mounting or Dir/Image interface

Solomon Hykes authored on 2013/11/08 05:34:01
Showing 7 changed files
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"github.com/dotcloud/docker/archive"
10 10
 	"github.com/dotcloud/docker/term"
11 11
 	"github.com/dotcloud/docker/utils"
12
+	"github.com/dotcloud/docker/graphdriver" // FIXME: graphdriver.Change is a placeholder for archive.Change
12 13
 	"github.com/kr/pty"
13 14
 	"io"
14 15
 	"io/ioutil"
... ...
@@ -25,7 +26,8 @@ import (
25 25
 )
26 26
 
27 27
 type Container struct {
28
-	root string
28
+	root string	// Path to the "home" of the container, including metadata.
29
+	rootfs string	// Path to the root filesystem of the container.
29 30
 
30 31
 	ID string
31 32
 
... ...
@@ -767,6 +769,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
767 767
 		}
768 768
 	}
769 769
 
770
+	volumesDriver := container.runtime.volumes.driver
770 771
 	// Create the requested volumes if they don't exist
771 772
 	for volPath := range container.Config.Volumes {
772 773
 		volPath = path.Clean(volPath)
... ...
@@ -790,9 +793,9 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
790 790
 			if err != nil {
791 791
 				return err
792 792
 			}
793
-			srcPath, err = c.layer()
793
+			srcPath, err = volumesDriver.Get(c.ID)
794 794
 			if err != nil {
795
-				return err
795
+				return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
796 796
 			}
797 797
 			srcRW = true // RW by default
798 798
 		}
... ...
@@ -1338,15 +1341,10 @@ func (container *Container) Resize(h, w int) error {
1338 1338
 }
1339 1339
 
1340 1340
 func (container *Container) ExportRw() (archive.Archive, error) {
1341
-	return archive.Tar(container.rwPath(), archive.Uncompressed)
1342
-}
1343
-
1344
-func (container *Container) RwChecksum() (string, error) {
1345
-	rwData, err := archive.Tar(container.rwPath(), archive.Xz)
1346
-	if err != nil {
1347
-		return "", err
1341
+	if container.runtime == nil {
1342
+		return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
1348 1343
 	}
1349
-	return utils.HashData(rwData)
1344
+	return container.runtime.driver.Diff(container.ID)
1350 1345
 }
1351 1346
 
1352 1347
 func (container *Container) Export() (archive.Archive, error) {
... ...
@@ -1372,11 +1370,8 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
1372 1372
 }
1373 1373
 
1374 1374
 func (container *Container) EnsureMounted() error {
1375
-	if mounted, err := container.Mounted(); err != nil {
1376
-		return err
1377
-	} else if mounted {
1378
-		return nil
1379
-	}
1375
+	// FIXME: EnsureMounted is deprecated because drivers are now responsible
1376
+	// for re-entrant mounting in their Get() method.
1380 1377
 	return container.Mount()
1381 1378
 }
1382 1379
 
... ...
@@ -1384,7 +1379,7 @@ func (container *Container) Mount() error {
1384 1384
 	return container.runtime.Mount(container)
1385 1385
 }
1386 1386
 
1387
-func (container *Container) Changes() ([]Change, error) {
1387
+func (container *Container) Changes() ([]graphdriver.Change, error) {
1388 1388
 	return container.runtime.Changes(container)
1389 1389
 }
1390 1390
 
... ...
@@ -1395,10 +1390,6 @@ func (container *Container) GetImage() (*Image, error) {
1395 1395
 	return container.runtime.graph.Get(container.Image)
1396 1396
 }
1397 1397
 
1398
-func (container *Container) Mounted() (bool, error) {
1399
-	return container.runtime.Mounted(container)
1400
-}
1401
-
1402 1398
 func (container *Container) Unmount() error {
1403 1399
 	return container.runtime.Unmount(container)
1404 1400
 }
... ...
@@ -1437,11 +1428,7 @@ func (container *Container) lxcConfigPath() string {
1437 1437
 
1438 1438
 // This method must be exported to be used from the lxc template
1439 1439
 func (container *Container) RootfsPath() string {
1440
-	return path.Join(container.root, "rootfs")
1441
-}
1442
-
1443
-func (container *Container) rwPath() string {
1444
-	return path.Join(container.root, "rw")
1440
+	return container.rootfs
1445 1441
 }
1446 1442
 
1447 1443
 func validateID(id string) error {
... ...
@@ -1455,18 +1442,20 @@ func validateID(id string) error {
1455 1455
 func (container *Container) GetSize() (int64, int64) {
1456 1456
 	var sizeRw, sizeRootfs int64
1457 1457
 
1458
-	filepath.Walk(container.rwPath(), func(path string, fileInfo os.FileInfo, err error) error {
1459
-		if fileInfo != nil {
1460
-			sizeRw += fileInfo.Size()
1461
-		}
1462
-		return nil
1463
-	})
1458
+	driver := container.runtime.driver
1459
+	sizeRw, err := driver.DiffSize(container.ID)
1460
+	if err != nil {
1461
+		utils.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
1462
+		// FIXME: GetSize should return an error. Not changing it now in case
1463
+		// there is a side-effect.
1464
+		sizeRw = -1
1465
+	}
1464 1466
 
1465 1467
 	if err := container.EnsureMounted(); err != nil {
1466 1468
 		utils.Errorf("Warning: failed to compute size of container rootfs %s: %s", container.ID, err)
1467 1469
 		return sizeRw, sizeRootfs
1468 1470
 	}
1469
-	_, err := os.Stat(container.RootfsPath())
1471
+	_, err = os.Stat(container.RootfsPath())
1470 1472
 	if err == nil {
1471 1473
 		filepath.Walk(container.RootfsPath(), func(path string, fileInfo os.FileInfo, err error) error {
1472 1474
 			if fileInfo != nil {
... ...
@@ -3,10 +3,8 @@ package docker
3 3
 import (
4 4
 	"fmt"
5 5
 	"github.com/dotcloud/docker/archive"
6
-	_ "github.com/dotcloud/docker/aufs"
7
-	_ "github.com/dotcloud/docker/devmapper"
8
-	"github.com/dotcloud/docker/graphdriver"
9 6
 	"github.com/dotcloud/docker/utils"
7
+	"github.com/dotcloud/docker/graphdriver"
10 8
 	"io"
11 9
 	"io/ioutil"
12 10
 	"os"
... ...
@@ -25,7 +23,7 @@ type Graph struct {
25 25
 
26 26
 // NewGraph instantiates a new graph at the given root path in the filesystem.
27 27
 // `root` will be created if it doesn't exist.
28
-func NewGraph(root string) (*Graph, error) {
28
+func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) {
29 29
 	abspath, err := filepath.Abs(root)
30 30
 	if err != nil {
31 31
 		return nil, err
... ...
@@ -35,10 +33,6 @@ func NewGraph(root string) (*Graph, error) {
35 35
 		return nil, err
36 36
 	}
37 37
 
38
-	driver, err := graphdriver.New(root)
39
-	if err != nil {
40
-		return nil, err
41
-	}
42 38
 
43 39
 	graph := &Graph{
44 40
 		Root:    abspath,
... ...
@@ -89,16 +83,22 @@ func (graph *Graph) Get(name string) (*Image, error) {
89 89
 	if err != nil {
90 90
 		return nil, err
91 91
 	}
92
+	// Check that the filesystem layer exists
93
+	rootfs, err := graph.driver.Get(img.ID)
94
+	if err != nil {
95
+		return nil, fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
96
+	}
92 97
 	if img.ID != id {
93 98
 		return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
94 99
 	}
95 100
 	img.graph = graph
96 101
 	if img.Size == 0 {
97
-		root, err := img.root()
102
+		size, err := utils.TreeSize(rootfs)
98 103
 		if err != nil {
99
-			return nil, err
104
+			return nil, fmt.Errorf("Error computing size of rootfs %s: %s", img.ID, err)
100 105
 		}
101
-		if err := StoreSize(img, root); err != nil {
106
+		img.Size = size
107
+		if err := img.SaveSize(graph.imageRoot(id)); err != nil {
102 108
 			return nil, err
103 109
 		}
104 110
 	}
... ...
@@ -142,7 +142,17 @@ func (graph *Graph) Register(jsonData []byte, layerData archive.Archive, img *Im
142 142
 	if err != nil {
143 143
 		return fmt.Errorf("Mktemp failed: %s", err)
144 144
 	}
145
-	if err := StoreImage(img, jsonData, layerData, tmp); err != nil {
145
+
146
+	// Create root filesystem in the driver
147
+	if err := graph.driver.Create(img.ID, img.Parent); err != nil {
148
+		return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
149
+	}
150
+	// Mount the root filesystem so we can apply the diff/layer
151
+	rootfs, err := graph.driver.Get(img.ID)
152
+	if err != nil {
153
+		return fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
154
+	}
155
+	if err := StoreImage(img, jsonData, layerData, tmp, rootfs); err != nil {
146 156
 		return err
147 157
 	}
148 158
 	// Commit
... ...
@@ -163,7 +173,7 @@ func (graph *Graph) TempLayerArchive(id string, compression archive.Compression,
163 163
 	if err != nil {
164 164
 		return nil, err
165 165
 	}
166
-	tmp, err := graph.tmp()
166
+	tmp, err := graph.Mktemp("")
167 167
 	if err != nil {
168 168
 		return nil, err
169 169
 	}
... ...
@@ -171,7 +181,7 @@ func (graph *Graph) TempLayerArchive(id string, compression archive.Compression,
171 171
 	if err != nil {
172 172
 		return nil, err
173 173
 	}
174
-	return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp.Root)
174
+	return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp)
175 175
 }
176 176
 
177 177
 // Mktemp creates a temporary sub-directory inside the graph's filesystem.
... ...
@@ -179,34 +189,26 @@ func (graph *Graph) Mktemp(id string) (string, error) {
179 179
 	if id == "" {
180 180
 		id = GenerateID()
181 181
 	}
182
-	tmp, err := graph.tmp()
183
-	if err != nil {
184
-		return "", fmt.Errorf("Couldn't create temp: %s", err)
182
+	// FIXME: use a separate "tmp" driver instead of the regular driver,
183
+	// to allow for removal at cleanup.
184
+	// Right now temp directories are never removed!
185
+	if err := graph.driver.Create(id, ""); err != nil {
186
+		return "", fmt.Errorf("Driver %s couldn't create temporary directory %s: %s", graph.driver, id, err)
185 187
 	}
186
-	if tmp.Exists(id) {
187
-		return "", fmt.Errorf("Image %s already exists", id)
188
+	dir, err := graph.driver.Get(id)
189
+	if err != nil {
190
+		return "", fmt.Errorf("Driver %s couldn't get temporary directory %s: %s", graph.driver, id, err)
188 191
 	}
189
-	return tmp.imageRoot(id), nil
192
+	return dir, nil
190 193
 }
191 194
 
192
-// getDockerInitLayer returns the path of a layer containing a mountpoint suitable
195
+// setupInitLayer populates a directory with mountpoints suitable
193 196
 // for bind-mounting dockerinit into the container. The mountpoint is simply an
194 197
 // empty file at /.dockerinit
195 198
 //
196 199
 // This extra layer is used by all containers as the top-most ro layer. It protects
197 200
 // the container from unwanted side-effects on the rw layer.
198
-func (graph *Graph) getDockerInitLayer() (string, error) {
199
-	tmp, err := graph.tmp()
200
-	if err != nil {
201
-		return "", err
202
-	}
203
-	initLayer := tmp.imageRoot("_dockerinit")
204
-	if err := os.Mkdir(initLayer, 0755); err != nil && !os.IsExist(err) {
205
-		// If directory already existed, keep going.
206
-		// For all other errors, abort.
207
-		return "", err
208
-	}
209
-
201
+func setupInitLayer(initLayer string) error {
210 202
 	for pth, typ := range map[string]string{
211 203
 		"/dev/pts":         "dir",
212 204
 		"/dev/shm":         "dir",
... ...
@@ -225,32 +227,27 @@ func (graph *Graph) getDockerInitLayer() (string, error) {
225 225
 				switch typ {
226 226
 				case "dir":
227 227
 					if err := os.MkdirAll(path.Join(initLayer, pth), 0755); err != nil {
228
-						return "", err
228
+						return err
229 229
 					}
230 230
 				case "file":
231 231
 					if err := os.MkdirAll(path.Join(initLayer, path.Dir(pth)), 0755); err != nil {
232
-						return "", err
232
+						return err
233 233
 					}
234 234
 
235 235
 					if f, err := os.OpenFile(path.Join(initLayer, pth), os.O_CREATE, 0755); err != nil {
236
-						return "", err
236
+						return err
237 237
 					} else {
238 238
 						f.Close()
239 239
 					}
240 240
 				}
241 241
 			} else {
242
-				return "", err
242
+				return err
243 243
 			}
244 244
 		}
245 245
 	}
246 246
 
247 247
 	// Layer is ready to use, if it wasn't before.
248
-	return initLayer, nil
249
-}
250
-
251
-func (graph *Graph) tmp() (*Graph, error) {
252
-	// Changed to _tmp from :tmp:, because it messed with ":" separators in aufs branch syntax...
253
-	return NewGraph(path.Join(graph.Root, "_tmp"))
248
+	return nil
254 249
 }
255 250
 
256 251
 // Check if given error is "not empty".
... ...
@@ -282,6 +279,9 @@ func (graph *Graph) Delete(name string) error {
282 282
 	if err != nil {
283 283
 		return err
284 284
 	}
285
+	// Remove rootfs data from the driver
286
+	graph.driver.Remove(id)
287
+	// Remove the trashed image directory
285 288
 	return os.RemoveAll(tmp)
286 289
 }
287 290
 
... ...
@@ -7,21 +7,20 @@ import (
7 7
 
8 8
 type InitFunc func(root string) (Driver, error)
9 9
 
10
-type Dir interface {
11
-	ID() string
12
-	Path() string
13
-	Parent() (Dir, error)
10
+// FIXME: this is a temporary placeholder for archive.Change
11
+// (to be merged from master)
12
+type Change interface {
14 13
 }
15 14
 
16 15
 type Driver interface {
17
-	OnCreate(dir Dir, layer archive.Archive) error
18
-	OnRemove(dir Dir) error
16
+	Create(id, parent string) error
17
+	Remove(id string) error
19 18
 
20
-	OnMount(dir Dir, dest string) error
21
-	OnUnmount(dest string) error
22
-	Mounted(dest string) (bool, error)
19
+	Get(id string) (dir string, err error)
23 20
 
24
-	Layer(dir Dir, dest string) (archive.Archive, error)
21
+	Diff(id string) (archive.Archive, error)
22
+	DiffSize(id string) (bytes int64, err error)
23
+	Changes(id string) ([]Change, error)
25 24
 
26 25
 	Cleanup() error
27 26
 }
... ...
@@ -11,7 +11,6 @@ import (
11 11
 	"io/ioutil"
12 12
 	"os"
13 13
 	"path"
14
-	"path/filepath"
15 14
 	"strconv"
16 15
 	"strings"
17 16
 	"time"
... ...
@@ -59,19 +58,10 @@ func LoadImage(root string) (*Image, error) {
59 59
 		}
60 60
 	}
61 61
 
62
-	// Check that the filesystem layer exists
63
-	if stat, err := os.Stat(layerPath(root)); err != nil {
64
-		if os.IsNotExist(err) {
65
-			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
66
-		}
67
-		return nil, err
68
-	} else if !stat.IsDir() {
69
-		return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.ID, layerPath(root))
70
-	}
71 62
 	return img, nil
72 63
 }
73 64
 
74
-func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root string) error {
65
+func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root, rootfs string) error {
75 66
 	// Check that root doesn't already exist
76 67
 	if _, err := os.Stat(root); err == nil {
77 68
 		return fmt.Errorf("Image %s already exists", img.ID)
... ...
@@ -79,7 +69,7 @@ func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root str
79 79
 		return err
80 80
 	}
81 81
 	// Store the layer
82
-	layer := layerPath(root)
82
+	layer := rootfs
83 83
 	if err := os.MkdirAll(layer, 0755); err != nil {
84 84
 		return err
85 85
 	}
... ...
@@ -106,29 +96,25 @@ func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root str
106 106
 			return err
107 107
 		}
108 108
 	}
109
-
110
-	return StoreSize(img, root)
111
-}
112
-
113
-func StoreSize(img *Image, root string) error {
114
-	layer := layerPath(root)
115
-
116
-	var totalSize int64 = 0
117
-	filepath.Walk(layer, func(path string, fileInfo os.FileInfo, err error) error {
118
-		totalSize += fileInfo.Size()
119
-		return nil
120
-	})
121
-	img.Size = totalSize
122
-
123
-	if err := ioutil.WriteFile(path.Join(root, "layersize"), []byte(strconv.Itoa(int(totalSize))), 0600); err != nil {
124
-		return nil
109
+	// Compute and save the size of the rootfs
110
+	size, err := utils.TreeSize(rootfs)
111
+	if err != nil {
112
+		return fmt.Errorf("Error computing size of rootfs %s: %s", img.ID, err)
113
+	}
114
+	img.Size = size
115
+	if err := img.SaveSize(root); err != nil {
116
+		return err
125 117
 	}
126 118
 
127 119
 	return nil
128 120
 }
129 121
 
130
-func layerPath(root string) string {
131
-	return path.Join(root, "layer")
122
+// SaveSize stores the current `size` value of `img` in the directory `root`.
123
+func (img *Image) SaveSize(root string) error {
124
+	if err := ioutil.WriteFile(path.Join(root, "layersize"), []byte(strconv.Itoa(int(img.Size))), 0600); err != nil {
125
+		return fmt.Errorf("Error storing image size in %s/layersize: %s", root, err)
126
+	}
127
+	return nil
132 128
 }
133 129
 
134 130
 func jsonPath(root string) string {
... ...
@@ -137,7 +123,10 @@ func jsonPath(root string) string {
137 137
 
138 138
 // TarLayer returns a tar archive of the image's filesystem layer.
139 139
 func (image *Image) TarLayer(compression archive.Compression) (archive.Archive, error) {
140
-	layerPath, err := image.layer()
140
+	if image.graph == nil {
141
+		return nil, fmt.Errorf("Can't load storage driver for unregistered image %s", image.ID)
142
+	}
143
+	layerPath, err := image.graph.driver.Get(image.ID)
141 144
 	if err != nil {
142 145
 		return nil, err
143 146
 	}
... ...
@@ -216,15 +205,6 @@ func (img *Image) root() (string, error) {
216 216
 	return img.graph.imageRoot(img.ID), nil
217 217
 }
218 218
 
219
-// Return the path of an image's layer
220
-func (img *Image) layer() (string, error) {
221
-	root, err := img.root()
222
-	if err != nil {
223
-		return "", err
224
-	}
225
-	return layerPath(root), nil
226
-}
227
-
228 219
 func (img *Image) getParentsSize(size int64) int64 {
229 220
 	parentImage, err := img.GetParent()
230 221
 	if err != nil || parentImage == nil {
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"fmt"
8 8
 	"github.com/dotcloud/docker/gograph"
9 9
 	"github.com/dotcloud/docker/utils"
10
+	"github.com/dotcloud/docker/graphdriver"
10 11
 	"io"
11 12
 	"io/ioutil"
12 13
 	"log"
... ...
@@ -38,6 +39,7 @@ type Runtime struct {
38 38
 	srv            *Server
39 39
 	config         *DaemonConfig
40 40
 	containerGraph *gograph.Database
41
+	driver         graphdriver.Driver
41 42
 }
42 43
 
43 44
 // List returns an array of all containers registered in the runtime.
... ...
@@ -113,6 +115,13 @@ func (runtime *Runtime) Register(container *Container) error {
113 113
 		return err
114 114
 	}
115 115
 
116
+	// Get the root filesystem from the driver
117
+	rootfs, err := runtime.driver.Get(container.ID)
118
+	if err != nil {
119
+		return fmt.Errorf("Error getting container filesystem %s from driver %s: %s", container.ID, runtime.driver, err)
120
+	}
121
+	container.rootfs = rootfs
122
+
116 123
 	// init the wait lock
117 124
 	container.waitLock = make(chan struct{})
118 125
 
... ...
@@ -200,12 +209,8 @@ func (runtime *Runtime) Destroy(container *Container) error {
200 200
 		return err
201 201
 	}
202 202
 
203
-	if mounted, err := container.Mounted(); err != nil {
204
-		return err
205
-	} else if mounted {
206
-		if err := container.Unmount(); err != nil {
207
-			return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
208
-		}
203
+	if err := runtime.driver.Remove(container.ID); err != nil {
204
+		return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", runtime.driver, container.ID, err)
209 205
 	}
210 206
 
211 207
 	if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
... ...
@@ -413,6 +418,21 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
413 413
 		return nil, nil, err
414 414
 	}
415 415
 
416
+	initID := fmt.Sprintf("%s-init", container.ID)
417
+	if err := runtime.driver.Create(initID, img.ID); err != nil {
418
+		return nil, nil, err
419
+	}
420
+	initPath, err := runtime.driver.Get(initID)
421
+	if err != nil {
422
+		return nil, nil, err
423
+	}
424
+	if err := setupInitLayer(initPath); err != nil {
425
+		return nil, nil, err
426
+	}
427
+
428
+	if err := runtime.driver.Create(container.ID, initID); err != nil {
429
+		return nil, nil, err
430
+	}
416 431
 	resolvConf, err := utils.GetResolvConf()
417 432
 	if err != nil {
418 433
 		return nil, nil, err
... ...
@@ -568,17 +588,23 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) {
568 568
 }
569 569
 
570 570
 func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
571
+	// Load storage driver
572
+	driver, err := graphdriver.New(config.Root)
573
+	if err != nil {
574
+		return nil, err
575
+	}
576
+
571 577
 	runtimeRepo := path.Join(config.Root, "containers")
572 578
 
573 579
 	if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
574 580
 		return nil, err
575 581
 	}
576 582
 
577
-	g, err := NewGraph(path.Join(config.Root, "graph"))
583
+	g, err := NewGraph(path.Join(config.Root, "graph"), driver)
578 584
 	if err != nil {
579 585
 		return nil, err
580 586
 	}
581
-	volumes, err := NewGraph(path.Join(config.Root, "volumes"))
587
+	volumes, err := NewGraph(path.Join(config.Root, "volumes"), driver)
582 588
 	if err != nil {
583 589
 		return nil, err
584 590
 	}
... ...
@@ -612,6 +638,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
612 612
 		return nil, err
613 613
 	}
614 614
 
615
+
615 616
 	runtime := &Runtime{
616 617
 		repository:     runtimeRepo,
617 618
 		containers:     list.New(),
... ...
@@ -623,6 +650,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
623 623
 		volumes:        volumes,
624 624
 		config:         config,
625 625
 		containerGraph: graph,
626
+		driver:		driver,
626 627
 	}
627 628
 
628 629
 	if err := runtime.restore(); err != nil {
... ...
@@ -633,40 +661,32 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
633 633
 
634 634
 func (runtime *Runtime) Close() error {
635 635
 	runtime.networkManager.Close()
636
+	runtime.driver.Cleanup()
636 637
 	return runtime.containerGraph.Close()
637 638
 }
638 639
 
639 640
 func (runtime *Runtime) Mount(container *Container) error {
640
-	if mounted, err := runtime.Mounted(container); err != nil {
641
-		return err
642
-	} else if mounted {
643
-		return fmt.Errorf("%s is already mounted", container.RootfsPath())
644
-	}
645
-	img, err := container.GetImage()
641
+	dir, err := runtime.driver.Get(container.ID)
646 642
 	if err != nil {
647
-		return err
643
+		return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err)
644
+	}
645
+	if container.rootfs == "" {
646
+		container.rootfs = dir
647
+	} else if container.rootfs != dir {
648
+		return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
649
+			runtime.driver, container.ID, container.rootfs, dir)
648 650
 	}
649
-	return runtime.graph.driver.Mount(img, container.root)
651
+	return nil
650 652
 }
651 653
 
652 654
 func (runtime *Runtime) Unmount(container *Container) error {
653
-	return runtime.graph.driver.Unmount(container.root)
654
-}
655
-
656
-func (runtime *Runtime) Mounted(container *Container) (bool, error) {
657
-	return runtime.graph.driver.Mounted(container.root)
655
+	// FIXME: Unmount is deprecated because drivers are responsible for mounting
656
+	// and unmounting when necessary. Use driver.Remove() instead.
657
+	return nil
658 658
 }
659 659
 
660
-func (runtime *Runtime) Changes(container *Container) ([]Change, error) {
661
-	img, err := container.GetImage()
662
-	if err != nil {
663
-		return nil, err
664
-	}
665
-	layers, err := img.Layers()
666
-	if err != nil {
667
-		return nil, err
668
-	}
669
-	return Changes(layers, container.rwPath())
660
+func (runtime *Runtime) Changes(container *Container) ([]graphdriver.Change, error) {
661
+	return runtime.driver.Changes(container.ID)
670 662
 }
671 663
 
672 664
 // History is a convenience type for storing a list of containers,
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"github.com/dotcloud/docker/gograph"
12 12
 	"github.com/dotcloud/docker/registry"
13 13
 	"github.com/dotcloud/docker/utils"
14
+	"github.com/dotcloud/docker/graphdriver" // FIXME: graphdriver.Change is a placeholder for archive.Change
14 15
 	"io"
15 16
 	"io/ioutil"
16 17
 	"log"
... ...
@@ -430,7 +431,7 @@ func (srv *Server) ContainerTop(name, ps_args string) (*APITop, error) {
430 430
 	return nil, fmt.Errorf("No such container: %s", name)
431 431
 }
432 432
 
433
-func (srv *Server) ContainerChanges(name string) ([]Change, error) {
433
+func (srv *Server) ContainerChanges(name string) ([]graphdriver.Change, error) {
434 434
 	if container := srv.runtime.Get(name); container != nil {
435 435
 		return container.Changes()
436 436
 	}
437 437
new file mode 100644
... ...
@@ -0,0 +1,15 @@
0
+package utils
1
+
2
+import (
3
+	"os"
4
+	"path/filepath"
5
+)
6
+
7
+// TreeSize walks a directory tree and returns its total size in bytes.
8
+func TreeSize(dir string) (size int64, err error) {
9
+	err = filepath.Walk(dir, func(d string, fileInfo os.FileInfo, e error) error {
10
+		size += fileInfo.Size()
11
+		return nil
12
+	})
13
+	return
14
+}