From fab19d197c6cc362182f6cee4d0a6e65e799875f Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Thu, 30 Jan 2014 16:43:53 +0100 Subject: [PATCH] Separate out graphdriver mount and container root This separates out the directory as returned from the graphdriver (the "base" fs) from the root filesystem of the live container. This is necessary as the "diff" operation needs access to the base filesystem without all the mounts that the running container needs (/.dockerinit, volumes, etc). We change container in the following way: Container.RootfsPath() returns the the directory which will be used as the root in a running container. It is always of the form "/var/lib/docker/container//root" and is a private bind mount to the base filesystem. It is only available while the container is running. Container.BasefsPath() returns the raw directory from the graph driver without the container runtime mounts. It is availible whenever the container is mounted (in between a container.Mount()/Unmount() pair, which are properly refcounted). This fixes issue #3840 Docker-DCO-1.1-Signed-off-by: Alexander Larsson (github: alexlarsson) --- buildfile.go | 2 +- container.go | 102 +++++++++++++++++++++++--------------- integration/utils_test.go | 2 +- runtime.go | 12 ++--- 4 files changed, 69 insertions(+), 49 deletions(-) diff --git a/buildfile.go b/buildfile.go index 89afccebbd..3b45057f2b 100644 --- a/buildfile.go +++ b/buildfile.go @@ -328,7 +328,7 @@ func (b *buildFile) checkPathForAddition(orig string) error { func (b *buildFile) addContext(container *Container, orig, dest string) error { var ( origPath = path.Join(b.contextPath, orig) - destPath = path.Join(container.RootfsPath(), dest) + destPath = path.Join(container.BasefsPath(), dest) ) // Preserve the trailing '/' if strings.HasSuffix(dest, "/") { diff --git a/container.go b/container.go index c5df1f4b58..5006faafd1 100644 --- a/container.go +++ b/container.go @@ -34,7 +34,7 @@ var ( type Container struct { sync.Mutex root string // Path to the "home" of the container, including metadata. - rootfs string // Path to the root filesystem of the container. + basefs string // Path to the graphdriver mountpoint ID string @@ -208,7 +208,7 @@ func (container *Container) Inject(file io.Reader, pth string) error { defer container.Unmount() // Return error if path exists - destPath := path.Join(container.RootfsPath(), pth) + destPath := path.Join(container.basefs, pth) if _, err := os.Stat(destPath); err == nil { // Since err is nil, the path could be stat'd and it exists return fmt.Errorf("%s exists", pth) @@ -220,7 +220,7 @@ func (container *Container) Inject(file io.Reader, pth string) error { } // Make sure the directory exists - if err := os.MkdirAll(path.Join(container.RootfsPath(), path.Dir(pth)), 0755); err != nil { + if err := os.MkdirAll(path.Join(container.basefs, path.Dir(pth)), 0755); err != nil { return err } @@ -649,11 +649,9 @@ func (container *Container) Start() (err error) { return err } - root := container.RootfsPath() - if container.Config.WorkingDir != "" { container.Config.WorkingDir = path.Clean(container.Config.WorkingDir) - if err := os.MkdirAll(path.Join(root, container.Config.WorkingDir), 0755); err != nil { + if err := os.MkdirAll(path.Join(container.basefs, container.Config.WorkingDir), 0755); err != nil { return nil } } @@ -663,6 +661,23 @@ func (container *Container) Start() (err error) { return err } + // Setup the root fs as a bind mount of the base fs + root := container.RootfsPath() + if err := os.MkdirAll(root, 0755); err != nil && !os.IsExist(err) { + return nil + } + + // Create a bind mount of the base fs as a place where we can add mounts + // without affecting the ability to access the base fs + if err := mount.Mount(container.basefs, root, "none", "bind,rw"); err != nil { + return err + } + + // Make sure the root fs is private so the mounts here don't propagate to basefs + if err := mount.ForceMount(root, root, "none", "private"); err != nil { + return err + } + // Mount docker specific files into the containers root fs if err := mount.Mount(runtime.sysInitPath, path.Join(root, "/.dockerinit"), "none", "bind,ro"); err != nil { return err @@ -849,8 +864,8 @@ func (container *Container) createVolumes() error { container.VolumesRW[volPath] = srcRW // Create the mountpoint - volPath = path.Join(container.RootfsPath(), volPath) - rootVolPath, err := utils.FollowSymlinkInScope(volPath, container.RootfsPath()) + volPath = path.Join(container.basefs, volPath) + rootVolPath, err := utils.FollowSymlinkInScope(volPath, container.basefs) if err != nil { return err } @@ -939,7 +954,7 @@ func (container *Container) applyExternalVolumes() error { if _, exists := container.Volumes[volPath]; exists { continue } - if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil { + if err := os.MkdirAll(path.Join(container.basefs, volPath), 0755); err != nil { return err } container.Volumes[volPath] = id @@ -1206,6 +1221,30 @@ func (container *Container) cleanup() { } } + var ( + root = container.RootfsPath() + mounts = []string{ + root, + path.Join(root, "/.dockerinit"), + path.Join(root, "/.dockerenv"), + path.Join(root, "/etc/resolv.conf"), + } + ) + + if container.HostnamePath != "" && container.HostsPath != "" { + mounts = append(mounts, path.Join(root, "/etc/hostname"), path.Join(root, "/etc/hosts")) + } + + for r := range container.Volumes { + mounts = append(mounts, path.Join(root, r)) + } + + for i := len(mounts) - 1; i >= 0; i-- { + if lastError := mount.Unmount(mounts[i]); lastError != nil { + log.Printf("Failed to umount %v: %v", mounts[i], lastError) + } + } + if err := container.Unmount(); err != nil { log.Printf("%v: Failed to umount filesystem: %v", container.ID, err) } @@ -1309,7 +1348,7 @@ func (container *Container) Export() (archive.Archive, error) { return nil, err } - archive, err := archive.Tar(container.RootfsPath(), archive.Uncompressed) + archive, err := archive.Tar(container.basefs, archive.Uncompressed) if err != nil { return nil, err } @@ -1347,32 +1386,6 @@ func (container *Container) GetImage() (*Image, error) { } func (container *Container) Unmount() error { - var ( - err error - root = container.RootfsPath() - mounts = []string{ - path.Join(root, "/.dockerinit"), - path.Join(root, "/.dockerenv"), - path.Join(root, "/etc/resolv.conf"), - } - ) - - if container.HostnamePath != "" && container.HostsPath != "" { - mounts = append(mounts, path.Join(root, "/etc/hostname"), path.Join(root, "/etc/hosts")) - } - - for r := range container.Volumes { - mounts = append(mounts, path.Join(root, r)) - } - - for i := len(mounts) - 1; i >= 0; i-- { - if lastError := mount.Unmount(mounts[i]); lastError != nil { - err = fmt.Errorf("Failed to umount %v: %v", mounts[i], lastError) - } - } - if err != nil { - return err - } return container.runtime.Unmount(container) } @@ -1409,8 +1422,15 @@ func (container *Container) EnvConfigPath() (string, error) { } // This method must be exported to be used from the lxc template +// This directory is only usable when the container is running func (container *Container) RootfsPath() string { - return container.rootfs + return path.Join(container.root, "root") +} + +// This is the stand-alone version of the root fs, without any additional mounts. +// This directory is usable whenever the container is mounted (and not unmounted) +func (container *Container) BasefsPath() string { + return container.basefs } func validateID(id string) error { @@ -1445,14 +1465,14 @@ func (container *Container) GetSize() (int64, int64) { } else { changes, _ := container.Changes() if changes != nil { - sizeRw = archive.ChangesSize(container.RootfsPath(), changes) + sizeRw = archive.ChangesSize(container.basefs, changes) } else { sizeRw = -1 } } - if _, err = os.Stat(container.RootfsPath()); err != nil { - if sizeRootfs, err = utils.TreeSize(container.RootfsPath()); err != nil { + if _, err = os.Stat(container.basefs); err != nil { + if sizeRootfs, err = utils.TreeSize(container.basefs); err != nil { sizeRootfs = -1 } } @@ -1464,7 +1484,7 @@ func (container *Container) Copy(resource string) (archive.Archive, error) { return nil, err } var filter []string - basePath := path.Join(container.RootfsPath(), resource) + basePath := path.Join(container.basefs, resource) stat, err := os.Stat(basePath) if err != nil { container.Unmount() diff --git a/integration/utils_test.go b/integration/utils_test.go index d7a2814472..2eff13c81d 100644 --- a/integration/utils_test.go +++ b/integration/utils_test.go @@ -75,7 +75,7 @@ func containerFileExists(eng *engine.Engine, id, dir string, t utils.Fataler) bo t.Fatal(err) } defer c.Unmount() - if _, err := os.Stat(path.Join(c.RootfsPath(), dir)); err != nil { + if _, err := os.Stat(path.Join(c.BasefsPath(), dir)); err != nil { if os.IsNotExist(err) { return false } diff --git a/runtime.go b/runtime.go index 3d47a50398..51e90681bc 100644 --- a/runtime.go +++ b/runtime.go @@ -131,12 +131,12 @@ func (runtime *Runtime) Register(container *Container) error { } // Get the root filesystem from the driver - rootfs, err := runtime.driver.Get(container.ID) + basefs, err := runtime.driver.Get(container.ID) if err != nil { return fmt.Errorf("Error getting container filesystem %s from driver %s: %s", container.ID, runtime.driver, err) } defer runtime.driver.Put(container.ID) - container.rootfs = rootfs + container.basefs = basefs container.runtime = runtime @@ -764,11 +764,11 @@ func (runtime *Runtime) Mount(container *Container) error { if err != nil { return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err) } - if container.rootfs == "" { - container.rootfs = dir - } else if container.rootfs != dir { + if container.basefs == "" { + container.basefs = dir + } else if container.basefs != dir { return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')", - runtime.driver, container.ID, container.rootfs, dir) + runtime.driver, container.ID, container.basefs, dir) } return nil }