summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Goff <cpuguy83@gmail.com>2017-04-03 13:10:48 -0400
committerVictor Vieux <victorvieux@gmail.com>2017-05-04 13:56:32 -0700
commitd1c9e9cfe9b87f37a6d5ea0d8962a3ef5d8c72fb (patch)
tree9a1c04e7d59544ac0b3c2b6afb7918f0ea75b6e1
parentc4bd13b65074aaf55662ae3a625fb2206aefede0 (diff)
downloaddocker-d1c9e9cfe9b87f37a6d5ea0d8962a3ef5d8c72fb.tar.gz
Ensure unmount before removing local volume.
When there is an error unmounting a local volume, it is still possible to call `Remove()` on the volume causing removal of the mounted resources which is generally not desirable. This ensures that resources are unmounted before attempting removal. Signed-off-by: Brian Goff <cpuguy83@gmail.com> (cherry picked from commit db3576f8a08ca70287bd3fdf9b21e162537f9d3a) Signed-off-by: Victor Vieux <victorvieux@gmail.com>
-rw-r--r--integration-cli/docker_cli_daemon_test.go4
-rw-r--r--volume/local/local.go35
2 files changed, 32 insertions, 7 deletions
diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go
index bab94d8fe1..0e7cb27c49 100644
--- a/integration-cli/docker_cli_daemon_test.go
+++ b/integration-cli/docker_cli_daemon_test.go
@@ -88,8 +88,8 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithVolumesRefs(c *check.C) {
s.d.Restart(c)
- if _, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil {
- c.Fatal(err)
+ if out, err := s.d.Cmd("run", "-d", "--volumes-from", "volrestarttest1", "--name", "volrestarttest2", "busybox", "top"); err != nil {
+ c.Fatal(err, out)
}
if out, err := s.d.Cmd("rm", "-fv", "volrestarttest2"); err != nil {
diff --git a/volume/local/local.go b/volume/local/local.go
index d29559d487..6631423bbc 100644
--- a/volume/local/local.go
+++ b/volume/local/local.go
@@ -218,6 +218,14 @@ func (r *Root) Remove(v volume.Volume) error {
return fmt.Errorf("unknown volume type %T", v)
}
+ if lv.active.count > 0 {
+ return fmt.Errorf("volume has active mounts")
+ }
+
+ if err := lv.unmount(); err != nil {
+ return err
+ }
+
realPath, err := filepath.EvalSymlinks(lv.path)
if err != nil {
if !os.IsNotExist(err) {
@@ -306,6 +314,7 @@ func (v *localVolume) Path() string {
}
// Mount implements the localVolume interface, returning the data location.
+// If there are any provided mount options, the resources will be mounted at this point
func (v *localVolume) Mount(id string) (string, error) {
v.m.Lock()
defer v.m.Unlock()
@@ -321,19 +330,35 @@ func (v *localVolume) Mount(id string) (string, error) {
return v.path, nil
}
-// Umount is for satisfying the localVolume interface and does not do anything in this driver.
+// Unmount dereferences the id, and if it is the last reference will unmount any resources
+// that were previously mounted.
func (v *localVolume) Unmount(id string) error {
v.m.Lock()
defer v.m.Unlock()
+
+ // Always decrement the count, even if the unmount fails
+ // Essentially docker doesn't care if this fails, it will send an error, but
+ // ultimately there's nothing that can be done. If we don't decrement the count
+ // this volume can never be removed until a daemon restart occurs.
if v.opts != nil {
v.active.count--
- if v.active.count == 0 {
- if err := mount.Unmount(v.path); err != nil {
- v.active.count++
+ }
+
+ if v.active.count > 0 {
+ return nil
+ }
+
+ return v.unmount()
+}
+
+func (v *localVolume) unmount() error {
+ if v.opts != nil {
+ if err := mount.Unmount(v.path); err != nil {
+ if mounted, mErr := mount.Mounted(v.path); mounted || mErr != nil {
return errors.Wrapf(err, "error while unmounting volume path '%s'", v.path)
}
- v.active.mounted = false
}
+ v.active.mounted = false
}
return nil
}