Merge pull request #10992 from cpuguy83/add_volume_mounting_for_cp
Make `docker cp` bind-mount volumes
This commit is contained in:
commit
1d48cccc99
3 changed files with 90 additions and 77 deletions
|
@ -958,37 +958,35 @@ func (container *Container) GetSize() (int64, int64) {
|
|||
}
|
||||
|
||||
func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
||||
container.Lock()
|
||||
defer container.Unlock()
|
||||
var err error
|
||||
if err := container.Mount(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
container.Unmount()
|
||||
}
|
||||
}()
|
||||
|
||||
if err = container.mountVolumes(); err != nil {
|
||||
container.unmountVolumes()
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
container.unmountVolumes()
|
||||
}
|
||||
}()
|
||||
|
||||
basePath, err := container.getResourcePath(resource)
|
||||
if err != nil {
|
||||
container.Unmount()
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
stat, err := os.Stat(basePath)
|
||||
if err != nil {
|
||||
container.Unmount()
|
||||
return nil, err
|
||||
}
|
||||
var filter []string
|
||||
|
@ -1006,11 +1004,12 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
|||
IncludeFiles: filter,
|
||||
})
|
||||
if err != nil {
|
||||
container.Unmount()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ioutils.NewReadCloserWrapper(archive, func() error {
|
||||
err := archive.Close()
|
||||
container.unmountVolumes()
|
||||
container.Unmount()
|
||||
return err
|
||||
}),
|
||||
|
|
|
@ -2,7 +2,6 @@ package daemon
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -12,6 +11,7 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/daemon/execdriver"
|
||||
"github.com/docker/docker/pkg/chrootarchive"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/volumes"
|
||||
|
@ -27,18 +27,6 @@ type Mount struct {
|
|||
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 {
|
||||
if container.Volumes == nil || len(container.Volumes) == 0 {
|
||||
container.Volumes = make(map[string]string)
|
||||
|
@ -320,6 +308,20 @@ func validMountMode(mode string) bool {
|
|||
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 {
|
||||
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
|
||||
return nil
|
||||
|
@ -401,3 +393,57 @@ func copyOwnership(source, destination string) error {
|
|||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,11 @@ package volumes
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
)
|
||||
|
||||
|
@ -24,35 +21,6 @@ type Volume struct {
|
|||
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) {
|
||||
stat, err := os.Stat(v.Path)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in a new issue