From 0a3211f131b63b0646be4a19c3ea5147bd557844 Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Mon, 15 Sep 2014 17:51:06 -0700 Subject: [PATCH] Fix #7792 - Order mounts Docker-DCO-1.1-Signed-off-by: Brian Goff (github: cpuguy83) --- daemon/volumes.go | 22 +++++++++++--- integration-cli/docker_cli_run_test.go | 42 ++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/daemon/volumes.go b/daemon/volumes.go index 6486a62b0b8a9..d48f8f19f626e 100644 --- a/daemon/volumes.go +++ b/daemon/volumes.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" "path/filepath" + "sort" "strings" "syscall" @@ -73,19 +74,30 @@ func setupMountsForContainer(container *Container) error { // Note, these are not private because you may want propagation of (un)mounts from host // volumes. For instance if you use -v /usr:/usr and the host later mounts /usr/share you // want this new mount in the container - for r, v := range container.Volumes { + // These mounts must be ordered based on the length of the path that it is being mounted to (lexicographic) + for _, path := range container.sortedVolumeMounts() { mounts = append(mounts, execdriver.Mount{ - Source: v, - Destination: r, - Writable: container.VolumesRW[r], + Source: container.Volumes[path], + Destination: path, + Writable: container.VolumesRW[path], }) } container.command.Mounts = mounts - return nil } +// sortedVolumeMounts returns the list of container volume mount points sorted in lexicographic order +func (container *Container) sortedVolumeMounts() []string { + var mountPaths []string + for path := range container.Volumes { + mountPaths = append(mountPaths, path) + } + + sort.Strings(mountPaths) + return mountPaths +} + func parseVolumesFromSpec(container *Container, spec string) (map[string]*Volume, error) { specParts := strings.SplitN(spec, ":", 2) if len(specParts) == 0 { diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 7bc22b3299fca..fc7cb4d32c734 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -2069,3 +2069,45 @@ func TestDockerExecInteractive(t *testing.T) { logDone("exec - Interactive test") } + +// Regression test for #7792 +func TestMountOrdering(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "docker_nested_mount_test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + tmpDir2, err := ioutil.TempDir("", "docker_nested_mount_test2") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir2) + + // Create a temporary tmpfs mount. + fooDir := filepath.Join(tmpDir, "foo") + if err := os.MkdirAll(filepath.Join(tmpDir, "foo"), 0755); err != nil { + t.Fatalf("failed to mkdir at %s - %s", fooDir, err) + } + + if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", fooDir), []byte{}, 0644); err != nil { + t.Fatal(err) + } + + if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", tmpDir), []byte{}, 0644); err != nil { + t.Fatal(err) + } + + if err := ioutil.WriteFile(fmt.Sprintf("%s/touch-me", tmpDir2), []byte{}, 0644); err != nil { + t.Fatal(err) + } + + cmd := exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp", tmpDir), "-v", fmt.Sprintf("%s:/tmp/foo", fooDir), "-v", fmt.Sprintf("%s:/tmp/tmp2", tmpDir2), "-v", fmt.Sprintf("%s:/tmp/tmp2/foo", fooDir), "busybox:latest", "sh", "-c", "ls /tmp/touch-me && ls /tmp/foo/touch-me && ls /tmp/tmp2/touch-me && ls /tmp/tmp2/foo/touch-me") + out, _, err := runCommandWithOutput(cmd) + if err != nil { + t.Fatal(out, err) + } + + deleteAllContainers() + logDone("run - volumes are mounted in the correct order") +}