Browse code

Fix #8259 - Can't reuse symlink'd bindmount

volumes.Get was not checking for symlinked paths meanwhile when adding a
new volume it was following the symlink.
So when trying to use a bind-mount that is a symlink, the volume is
added with the correct path, but when another container tries to use the
same volume it got a "Volume exists" error because volumes.Get returned
nil and as such attempted to create a new volume.

Docker-DCO-1.1-Signed-off-by: Brian Goff <cpuguy83@gmail.com> (github: cpuguy83)

Brian Goff authored on 2014/09/27 03:28:21
Showing 2 changed files
... ...
@@ -2221,3 +2221,34 @@ func TestRunRedirectStdout(t *testing.T) {
2221 2221
 
2222 2222
 	logDone("run - redirect stdout")
2223 2223
 }
2224
+
2225
+// Regression test for https://github.com/docker/docker/issues/8259
2226
+func TestRunReuseBindVolumeThatIsSymlink(t *testing.T) {
2227
+	tmpDir, err := ioutil.TempDir(os.TempDir(), "testlink")
2228
+	if err != nil {
2229
+		t.Fatal(err)
2230
+	}
2231
+	defer os.RemoveAll(tmpDir)
2232
+
2233
+	linkPath := os.TempDir() + "/testlink2"
2234
+	if err := os.Symlink(tmpDir, linkPath); err != nil {
2235
+		t.Fatal(err)
2236
+	}
2237
+	defer os.RemoveAll(linkPath)
2238
+
2239
+	// Create first container
2240
+	cmd := exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp/test", linkPath), "busybox", "ls", "-lh", "/tmp/test")
2241
+	if _, err := runCommand(cmd); err != nil {
2242
+		t.Fatal(err)
2243
+	}
2244
+
2245
+	// Create second container with same symlinked path
2246
+	// This will fail if the referenced issue is hit with a "Volume exists" error
2247
+	cmd = exec.Command(dockerBinary, "run", "-v", fmt.Sprintf("%s:/tmp/test", linkPath), "busybox", "ls", "-lh", "/tmp/test")
2248
+	if out, _, err := runCommandWithOutput(cmd); err != nil {
2249
+		t.Fatal(err, out)
2250
+	}
2251
+
2252
+	deleteAllContainers()
2253
+	logDone("run - can remount old bindmount volume")
2254
+}
... ...
@@ -97,12 +97,16 @@ func (r *Repository) restore() error {
97 97
 
98 98
 func (r *Repository) Get(path string) *Volume {
99 99
 	r.lock.Lock()
100
-	vol := r.volumes[path]
100
+	vol := r.get(path)
101 101
 	r.lock.Unlock()
102 102
 	return vol
103 103
 }
104 104
 
105 105
 func (r *Repository) get(path string) *Volume {
106
+	path, err := filepath.EvalSymlinks(path)
107
+	if err != nil {
108
+		return nil
109
+	}
106 110
 	return r.volumes[path]
107 111
 }
108 112
 
... ...
@@ -129,6 +133,10 @@ func (r *Repository) remove(volume *Volume) {
129 129
 func (r *Repository) Delete(path string) error {
130 130
 	r.lock.Lock()
131 131
 	defer r.lock.Unlock()
132
+	path, err := filepath.EvalSymlinks(path)
133
+	if err != nil {
134
+		return err
135
+	}
132 136
 	volume := r.get(path)
133 137
 	if volume == nil {
134 138
 		return fmt.Errorf("Volume %s does not exist", path)