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/<id>/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 <alexl@redhat.com> (github: alexlarsson)
This commit is contained in:
Alexander Larsson 2014-01-30 16:43:53 +01:00
parent fc1169a220
commit fab19d197c
4 changed files with 69 additions and 49 deletions

View file

@ -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, "/") {

View file

@ -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()

View file

@ -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
}

View file

@ -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
}