Browse Source

Merge pull request #10992 from cpuguy83/add_volume_mounting_for_cp

Make `docker cp` bind-mount volumes
Jessie Frazelle 10 years ago
parent
commit
1d48cccc99
3 changed files with 89 additions and 76 deletions
  1. 19 20
      daemon/container.go
  2. 70 24
      daemon/volumes.go
  3. 0 32
      volumes/volume.go

+ 19 - 20
daemon/container.go

@@ -958,37 +958,35 @@ func (container *Container) GetSize() (int64, int64) {
 }
 }
 
 
 func (container *Container) Copy(resource string) (io.ReadCloser, error) {
 func (container *Container) Copy(resource string) (io.ReadCloser, error) {
+	container.Lock()
+	defer container.Unlock()
+	var err error
 	if err := container.Mount(); err != nil {
 	if err := container.Mount(); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
+	defer func() {
+		if err != nil {
+			container.Unmount()
+		}
+	}()
 
 
-	basePath, err := container.getResourcePath(resource)
-	if err != nil {
-		container.Unmount()
+	if err = container.mountVolumes(); err != nil {
+		container.unmountVolumes()
 		return nil, err
 		return nil, err
 	}
 	}
-
-	// Check if this is actually in a volume
-	for _, mnt := range container.VolumeMounts() {
-		if len(mnt.MountToPath) > 0 && strings.HasPrefix(resource, mnt.MountToPath[1:]) {
-			return mnt.Export(resource)
+	defer func() {
+		if err != nil {
+			container.unmountVolumes()
 		}
 		}
-	}
+	}()
 
 
-	// Check if this is a special one (resolv.conf, hostname, ..)
-	if resource == "etc/resolv.conf" {
-		basePath = container.ResolvConfPath
-	}
-	if resource == "etc/hostname" {
-		basePath = container.HostnamePath
-	}
-	if resource == "etc/hosts" {
-		basePath = container.HostsPath
+	basePath, err := container.getResourcePath(resource)
+	if err != nil {
+		return nil, err
 	}
 	}
 
 
 	stat, err := os.Stat(basePath)
 	stat, err := os.Stat(basePath)
 	if err != nil {
 	if err != nil {
-		container.Unmount()
 		return nil, err
 		return nil, err
 	}
 	}
 	var filter []string
 	var filter []string
@@ -1006,11 +1004,12 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
 		IncludeFiles: filter,
 		IncludeFiles: filter,
 	})
 	})
 	if err != nil {
 	if err != nil {
-		container.Unmount()
 		return nil, err
 		return nil, err
 	}
 	}
+
 	return ioutils.NewReadCloserWrapper(archive, func() error {
 	return ioutils.NewReadCloserWrapper(archive, func() error {
 			err := archive.Close()
 			err := archive.Close()
+			container.unmountVolumes()
 			container.Unmount()
 			container.Unmount()
 			return err
 			return err
 		}),
 		}),

+ 70 - 24
daemon/volumes.go

@@ -2,7 +2,6 @@ package daemon
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
@@ -12,6 +11,7 @@ import (
 	"github.com/Sirupsen/logrus"
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/daemon/execdriver"
 	"github.com/docker/docker/daemon/execdriver"
 	"github.com/docker/docker/pkg/chrootarchive"
 	"github.com/docker/docker/pkg/chrootarchive"
+	"github.com/docker/docker/pkg/mount"
 	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/volumes"
 	"github.com/docker/docker/volumes"
@@ -27,18 +27,6 @@ type Mount struct {
 	isBind      bool
 	isBind      bool
 }
 }
 
 
-func (mnt *Mount) Export(resource string) (io.ReadCloser, error) {
-	var name string
-	if resource == mnt.MountToPath[1:] {
-		name = filepath.Base(resource)
-	}
-	path, err := filepath.Rel(mnt.MountToPath[1:], resource)
-	if err != nil {
-		return nil, err
-	}
-	return mnt.volume.Export(path, name)
-}
-
 func (container *Container) prepareVolumes() error {
 func (container *Container) prepareVolumes() error {
 	if container.Volumes == nil || len(container.Volumes) == 0 {
 	if container.Volumes == nil || len(container.Volumes) == 0 {
 		container.Volumes = make(map[string]string)
 		container.Volumes = make(map[string]string)
@@ -320,6 +308,20 @@ func validMountMode(mode string) bool {
 	return validModes[mode]
 	return validModes[mode]
 }
 }
 
 
+func (container *Container) specialMounts() []execdriver.Mount {
+	var mounts []execdriver.Mount
+	if container.ResolvConfPath != "" {
+		mounts = append(mounts, execdriver.Mount{Source: container.ResolvConfPath, Destination: "/etc/resolv.conf", Writable: true, Private: true})
+	}
+	if container.HostnamePath != "" {
+		mounts = append(mounts, execdriver.Mount{Source: container.HostnamePath, Destination: "/etc/hostname", Writable: true, Private: true})
+	}
+	if container.HostsPath != "" {
+		mounts = append(mounts, execdriver.Mount{Source: container.HostsPath, Destination: "/etc/hosts", Writable: true, Private: true})
+	}
+	return mounts
+}
+
 func (container *Container) setupMounts() error {
 func (container *Container) setupMounts() error {
 	mounts := []execdriver.Mount{}
 	mounts := []execdriver.Mount{}
 
 
@@ -336,17 +338,7 @@ func (container *Container) setupMounts() error {
 		})
 		})
 	}
 	}
 
 
-	if container.ResolvConfPath != "" {
-		mounts = append(mounts, execdriver.Mount{Source: container.ResolvConfPath, Destination: "/etc/resolv.conf", Writable: true, Private: true})
-	}
-
-	if container.HostnamePath != "" {
-		mounts = append(mounts, execdriver.Mount{Source: container.HostnamePath, Destination: "/etc/hostname", Writable: true, Private: true})
-	}
-
-	if container.HostsPath != "" {
-		mounts = append(mounts, execdriver.Mount{Source: container.HostsPath, Destination: "/etc/hosts", Writable: true, Private: true})
-	}
+	mounts = append(mounts, container.specialMounts()...)
 
 
 	container.command.Mounts = mounts
 	container.command.Mounts = mounts
 	return nil
 	return nil
@@ -401,3 +393,57 @@ func copyOwnership(source, destination string) error {
 
 
 	return os.Chmod(destination, os.FileMode(stat.Mode()))
 	return os.Chmod(destination, os.FileMode(stat.Mode()))
 }
 }
+
+func (container *Container) mountVolumes() error {
+	for dest, source := range container.Volumes {
+		v := container.daemon.volumes.Get(source)
+		if v == nil {
+			return fmt.Errorf("could not find volume for %s:%s, impossible to mount", source, dest)
+		}
+
+		destPath, err := container.getResourcePath(dest)
+		if err != nil {
+			return err
+		}
+
+		if err := mount.Mount(source, destPath, "bind", "rbind,rw"); err != nil {
+			return fmt.Errorf("error while mounting volume %s: %v", source, err)
+		}
+	}
+
+	for _, mnt := range container.specialMounts() {
+		destPath, err := container.getResourcePath(mnt.Destination)
+		if err != nil {
+			return err
+		}
+		if err := mount.Mount(mnt.Source, destPath, "bind", "bind,rw"); err != nil {
+			return fmt.Errorf("error while mounting volume %s: %v", mnt.Source, err)
+		}
+	}
+	return nil
+}
+
+func (container *Container) unmountVolumes() {
+	for dest := range container.Volumes {
+		destPath, err := container.getResourcePath(dest)
+		if err != nil {
+			logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
+			continue
+		}
+		if err := mount.ForceUnmount(destPath); err != nil {
+			logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
+			continue
+		}
+	}
+
+	for _, mnt := range container.specialMounts() {
+		destPath, err := container.getResourcePath(mnt.Destination)
+		if err != nil {
+			logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
+			continue
+		}
+		if err := mount.ForceUnmount(destPath); err != nil {
+			logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
+		}
+	}
+}

+ 0 - 32
volumes/volume.go

@@ -2,14 +2,11 @@ package volumes
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
-	"io"
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
-	"path"
 	"path/filepath"
 	"path/filepath"
 	"sync"
 	"sync"
 
 
-	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/symlink"
 	"github.com/docker/docker/pkg/symlink"
 )
 )
 
 
@@ -24,35 +21,6 @@ type Volume struct {
 	lock        sync.Mutex
 	lock        sync.Mutex
 }
 }
 
 
-func (v *Volume) Export(resource, name string) (io.ReadCloser, error) {
-	if v.IsBindMount && filepath.Base(resource) == name {
-		name = ""
-	}
-
-	basePath, err := v.getResourcePath(resource)
-	if err != nil {
-		return nil, err
-	}
-	stat, err := os.Stat(basePath)
-	if err != nil {
-		return nil, err
-	}
-	var filter []string
-	if !stat.IsDir() {
-		d, f := path.Split(basePath)
-		basePath = d
-		filter = []string{f}
-	} else {
-		filter = []string{path.Base(basePath)}
-		basePath = path.Dir(basePath)
-	}
-	return archive.TarWithOptions(basePath, &archive.TarOptions{
-		Compression:  archive.Uncompressed,
-		Name:         name,
-		IncludeFiles: filter,
-	})
-}
-
 func (v *Volume) IsDir() (bool, error) {
 func (v *Volume) IsDir() (bool, error) {
 	stat, err := os.Stat(v.Path)
 	stat, err := os.Stat(v.Path)
 	if err != nil {
 	if err != nil {