Separate out graphdriver mount and container root
Michael Crosby authored on 2014/02/01 04:49:14... | ... |
@@ -328,7 +328,7 @@ func (b *buildFile) checkPathForAddition(orig string) error { |
328 | 328 |
func (b *buildFile) addContext(container *Container, orig, dest string) error { |
329 | 329 |
var ( |
330 | 330 |
origPath = path.Join(b.contextPath, orig) |
331 |
- destPath = path.Join(container.RootfsPath(), dest) |
|
331 |
+ destPath = path.Join(container.BasefsPath(), dest) |
|
332 | 332 |
) |
333 | 333 |
// Preserve the trailing '/' |
334 | 334 |
if strings.HasSuffix(dest, "/") { |
... | ... |
@@ -34,7 +34,7 @@ var ( |
34 | 34 |
type Container struct { |
35 | 35 |
sync.Mutex |
36 | 36 |
root string // Path to the "home" of the container, including metadata. |
37 |
- rootfs string // Path to the root filesystem of the container. |
|
37 |
+ basefs string // Path to the graphdriver mountpoint |
|
38 | 38 |
|
39 | 39 |
ID string |
40 | 40 |
|
... | ... |
@@ -266,7 +266,7 @@ func (container *Container) Inject(file io.Reader, pth string) error { |
266 | 266 |
defer container.Unmount() |
267 | 267 |
|
268 | 268 |
// Return error if path exists |
269 |
- destPath := path.Join(container.RootfsPath(), pth) |
|
269 |
+ destPath := path.Join(container.basefs, pth) |
|
270 | 270 |
if _, err := os.Stat(destPath); err == nil { |
271 | 271 |
// Since err is nil, the path could be stat'd and it exists |
272 | 272 |
return fmt.Errorf("%s exists", pth) |
... | ... |
@@ -278,7 +278,7 @@ func (container *Container) Inject(file io.Reader, pth string) error { |
278 | 278 |
} |
279 | 279 |
|
280 | 280 |
// Make sure the directory exists |
281 |
- if err := os.MkdirAll(path.Join(container.RootfsPath(), path.Dir(pth)), 0755); err != nil { |
|
281 |
+ if err := os.MkdirAll(path.Join(container.basefs, path.Dir(pth)), 0755); err != nil { |
|
282 | 282 |
return err |
283 | 283 |
} |
284 | 284 |
|
... | ... |
@@ -707,11 +707,9 @@ func (container *Container) Start() (err error) { |
707 | 707 |
return err |
708 | 708 |
} |
709 | 709 |
|
710 |
- root := container.RootfsPath() |
|
711 |
- |
|
712 | 710 |
if container.Config.WorkingDir != "" { |
713 | 711 |
container.Config.WorkingDir = path.Clean(container.Config.WorkingDir) |
714 |
- if err := os.MkdirAll(path.Join(root, container.Config.WorkingDir), 0755); err != nil { |
|
712 |
+ if err := os.MkdirAll(path.Join(container.basefs, container.Config.WorkingDir), 0755); err != nil { |
|
715 | 713 |
return nil |
716 | 714 |
} |
717 | 715 |
} |
... | ... |
@@ -721,6 +719,23 @@ func (container *Container) Start() (err error) { |
721 | 721 |
return err |
722 | 722 |
} |
723 | 723 |
|
724 |
+ // Setup the root fs as a bind mount of the base fs |
|
725 |
+ root := container.RootfsPath() |
|
726 |
+ if err := os.MkdirAll(root, 0755); err != nil && !os.IsExist(err) { |
|
727 |
+ return nil |
|
728 |
+ } |
|
729 |
+ |
|
730 |
+ // Create a bind mount of the base fs as a place where we can add mounts |
|
731 |
+ // without affecting the ability to access the base fs |
|
732 |
+ if err := mount.Mount(container.basefs, root, "none", "bind,rw"); err != nil { |
|
733 |
+ return err |
|
734 |
+ } |
|
735 |
+ |
|
736 |
+ // Make sure the root fs is private so the mounts here don't propagate to basefs |
|
737 |
+ if err := mount.ForceMount(root, root, "none", "private"); err != nil { |
|
738 |
+ return err |
|
739 |
+ } |
|
740 |
+ |
|
724 | 741 |
// Mount docker specific files into the containers root fs |
725 | 742 |
if err := mount.Mount(runtime.sysInitPath, path.Join(root, "/.dockerinit"), "none", "bind,ro"); err != nil { |
726 | 743 |
return err |
... | ... |
@@ -907,8 +922,8 @@ func (container *Container) createVolumes() error { |
907 | 907 |
container.VolumesRW[volPath] = srcRW |
908 | 908 |
|
909 | 909 |
// Create the mountpoint |
910 |
- volPath = path.Join(container.RootfsPath(), volPath) |
|
911 |
- rootVolPath, err := utils.FollowSymlinkInScope(volPath, container.RootfsPath()) |
|
910 |
+ volPath = path.Join(container.basefs, volPath) |
|
911 |
+ rootVolPath, err := utils.FollowSymlinkInScope(volPath, container.basefs) |
|
912 | 912 |
if err != nil { |
913 | 913 |
return err |
914 | 914 |
} |
... | ... |
@@ -997,7 +1012,7 @@ func (container *Container) applyExternalVolumes() error { |
997 | 997 |
if _, exists := container.Volumes[volPath]; exists { |
998 | 998 |
continue |
999 | 999 |
} |
1000 |
- if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { |
|
1000 |
+ if err := os.MkdirAll(path.Join(container.basefs, volPath), 0755); err != nil { |
|
1001 | 1001 |
return err |
1002 | 1002 |
} |
1003 | 1003 |
container.Volumes[volPath] = id |
... | ... |
@@ -1264,6 +1279,30 @@ func (container *Container) cleanup() { |
1264 | 1264 |
} |
1265 | 1265 |
} |
1266 | 1266 |
|
1267 |
+ var ( |
|
1268 |
+ root = container.RootfsPath() |
|
1269 |
+ mounts = []string{ |
|
1270 |
+ root, |
|
1271 |
+ path.Join(root, "/.dockerinit"), |
|
1272 |
+ path.Join(root, "/.dockerenv"), |
|
1273 |
+ path.Join(root, "/etc/resolv.conf"), |
|
1274 |
+ } |
|
1275 |
+ ) |
|
1276 |
+ |
|
1277 |
+ if container.HostnamePath != "" && container.HostsPath != "" { |
|
1278 |
+ mounts = append(mounts, path.Join(root, "/etc/hostname"), path.Join(root, "/etc/hosts")) |
|
1279 |
+ } |
|
1280 |
+ |
|
1281 |
+ for r := range container.Volumes { |
|
1282 |
+ mounts = append(mounts, path.Join(root, r)) |
|
1283 |
+ } |
|
1284 |
+ |
|
1285 |
+ for i := len(mounts) - 1; i >= 0; i-- { |
|
1286 |
+ if lastError := mount.Unmount(mounts[i]); lastError != nil { |
|
1287 |
+ log.Printf("Failed to umount %v: %v", mounts[i], lastError) |
|
1288 |
+ } |
|
1289 |
+ } |
|
1290 |
+ |
|
1267 | 1291 |
if err := container.Unmount(); err != nil { |
1268 | 1292 |
log.Printf("%v: Failed to umount filesystem: %v", container.ID, err) |
1269 | 1293 |
} |
... | ... |
@@ -1367,7 +1406,7 @@ func (container *Container) Export() (archive.Archive, error) { |
1367 | 1367 |
return nil, err |
1368 | 1368 |
} |
1369 | 1369 |
|
1370 |
- archive, err := archive.Tar(container.RootfsPath(), archive.Uncompressed) |
|
1370 |
+ archive, err := archive.Tar(container.basefs, archive.Uncompressed) |
|
1371 | 1371 |
if err != nil { |
1372 | 1372 |
return nil, err |
1373 | 1373 |
} |
... | ... |
@@ -1405,32 +1444,6 @@ func (container *Container) GetImage() (*Image, error) { |
1405 | 1405 |
} |
1406 | 1406 |
|
1407 | 1407 |
func (container *Container) Unmount() error { |
1408 |
- var ( |
|
1409 |
- err error |
|
1410 |
- root = container.RootfsPath() |
|
1411 |
- mounts = []string{ |
|
1412 |
- path.Join(root, "/.dockerinit"), |
|
1413 |
- path.Join(root, "/.dockerenv"), |
|
1414 |
- path.Join(root, "/etc/resolv.conf"), |
|
1415 |
- } |
|
1416 |
- ) |
|
1417 |
- |
|
1418 |
- if container.HostnamePath != "" && container.HostsPath != "" { |
|
1419 |
- mounts = append(mounts, path.Join(root, "/etc/hostname"), path.Join(root, "/etc/hosts")) |
|
1420 |
- } |
|
1421 |
- |
|
1422 |
- for r := range container.Volumes { |
|
1423 |
- mounts = append(mounts, path.Join(root, r)) |
|
1424 |
- } |
|
1425 |
- |
|
1426 |
- for i := len(mounts) - 1; i >= 0; i-- { |
|
1427 |
- if lastError := mount.Unmount(mounts[i]); lastError != nil { |
|
1428 |
- err = fmt.Errorf("Failed to umount %v: %v", mounts[i], lastError) |
|
1429 |
- } |
|
1430 |
- } |
|
1431 |
- if err != nil { |
|
1432 |
- return err |
|
1433 |
- } |
|
1434 | 1408 |
return container.runtime.Unmount(container) |
1435 | 1409 |
} |
1436 | 1410 |
|
... | ... |
@@ -1467,8 +1480,15 @@ func (container *Container) EnvConfigPath() (string, error) { |
1467 | 1467 |
} |
1468 | 1468 |
|
1469 | 1469 |
// This method must be exported to be used from the lxc template |
1470 |
+// This directory is only usable when the container is running |
|
1470 | 1471 |
func (container *Container) RootfsPath() string { |
1471 |
- return container.rootfs |
|
1472 |
+ return path.Join(container.root, "root") |
|
1473 |
+} |
|
1474 |
+ |
|
1475 |
+// This is the stand-alone version of the root fs, without any additional mounts. |
|
1476 |
+// This directory is usable whenever the container is mounted (and not unmounted) |
|
1477 |
+func (container *Container) BasefsPath() string { |
|
1478 |
+ return container.basefs |
|
1472 | 1479 |
} |
1473 | 1480 |
|
1474 | 1481 |
func validateID(id string) error { |
... | ... |
@@ -1503,14 +1523,14 @@ func (container *Container) GetSize() (int64, int64) { |
1503 | 1503 |
} else { |
1504 | 1504 |
changes, _ := container.Changes() |
1505 | 1505 |
if changes != nil { |
1506 |
- sizeRw = archive.ChangesSize(container.RootfsPath(), changes) |
|
1506 |
+ sizeRw = archive.ChangesSize(container.basefs, changes) |
|
1507 | 1507 |
} else { |
1508 | 1508 |
sizeRw = -1 |
1509 | 1509 |
} |
1510 | 1510 |
} |
1511 | 1511 |
|
1512 |
- if _, err = os.Stat(container.RootfsPath()); err != nil { |
|
1513 |
- if sizeRootfs, err = utils.TreeSize(container.RootfsPath()); err != nil { |
|
1512 |
+ if _, err = os.Stat(container.basefs); err != nil { |
|
1513 |
+ if sizeRootfs, err = utils.TreeSize(container.basefs); err != nil { |
|
1514 | 1514 |
sizeRootfs = -1 |
1515 | 1515 |
} |
1516 | 1516 |
} |
... | ... |
@@ -1522,7 +1542,7 @@ func (container *Container) Copy(resource string) (archive.Archive, error) { |
1522 | 1522 |
return nil, err |
1523 | 1523 |
} |
1524 | 1524 |
var filter []string |
1525 |
- basePath := path.Join(container.RootfsPath(), resource) |
|
1525 |
+ basePath := path.Join(container.basefs, resource) |
|
1526 | 1526 |
stat, err := os.Stat(basePath) |
1527 | 1527 |
if err != nil { |
1528 | 1528 |
container.Unmount() |
... | ... |
@@ -75,7 +75,7 @@ func containerFileExists(eng *engine.Engine, id, dir string, t utils.Fataler) bo |
75 | 75 |
t.Fatal(err) |
76 | 76 |
} |
77 | 77 |
defer c.Unmount() |
78 |
- if _, err := os.Stat(path.Join(c.RootfsPath(), dir)); err != nil { |
|
78 |
+ if _, err := os.Stat(path.Join(c.BasefsPath(), dir)); err != nil { |
|
79 | 79 |
if os.IsNotExist(err) { |
80 | 80 |
return false |
81 | 81 |
} |
... | ... |
@@ -40,6 +40,7 @@ func parseOptions(options string) (int, string) { |
40 | 40 |
"nodiratime": {false, syscall.MS_NODIRATIME}, |
41 | 41 |
"bind": {false, syscall.MS_BIND}, |
42 | 42 |
"rbind": {false, syscall.MS_BIND | syscall.MS_REC}, |
43 |
+ "private": {false, syscall.MS_PRIVATE}, |
|
43 | 44 |
"relatime": {false, syscall.MS_RELATIME}, |
44 | 45 |
"norelatime": {true, syscall.MS_RELATIME}, |
45 | 46 |
"strictatime": {false, syscall.MS_STRICTATIME}, |
... | ... |
@@ -132,12 +132,12 @@ func (runtime *Runtime) Register(container *Container) error { |
132 | 132 |
} |
133 | 133 |
|
134 | 134 |
// Get the root filesystem from the driver |
135 |
- rootfs, err := runtime.driver.Get(container.ID) |
|
135 |
+ basefs, err := runtime.driver.Get(container.ID) |
|
136 | 136 |
if err != nil { |
137 | 137 |
return fmt.Errorf("Error getting container filesystem %s from driver %s: %s", container.ID, runtime.driver, err) |
138 | 138 |
} |
139 | 139 |
defer runtime.driver.Put(container.ID) |
140 |
- container.rootfs = rootfs |
|
140 |
+ container.basefs = basefs |
|
141 | 141 |
|
142 | 142 |
container.runtime = runtime |
143 | 143 |
|
... | ... |
@@ -765,11 +765,11 @@ func (runtime *Runtime) Mount(container *Container) error { |
765 | 765 |
if err != nil { |
766 | 766 |
return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err) |
767 | 767 |
} |
768 |
- if container.rootfs == "" { |
|
769 |
- container.rootfs = dir |
|
770 |
- } else if container.rootfs != dir { |
|
768 |
+ if container.basefs == "" { |
|
769 |
+ container.basefs = dir |
|
770 |
+ } else if container.basefs != dir { |
|
771 | 771 |
return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')", |
772 |
- runtime.driver, container.ID, container.rootfs, dir) |
|
772 |
+ runtime.driver, container.ID, container.basefs, dir) |
|
773 | 773 |
} |
774 | 774 |
return nil |
775 | 775 |
} |