Allows `docker cp` to work seamlessly, and a lot more cleanly.
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
| ... | ... |
@@ -970,37 +970,35 @@ func (container *Container) GetSize() (int64, int64) {
|
| 970 | 970 |
} |
| 971 | 971 |
|
| 972 | 972 |
func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
| 973 |
+ container.Lock() |
|
| 974 |
+ defer container.Unlock() |
|
| 975 |
+ var err error |
|
| 973 | 976 |
if err := container.Mount(); err != nil {
|
| 974 | 977 |
return nil, err |
| 975 | 978 |
} |
| 979 |
+ defer func() {
|
|
| 980 |
+ if err != nil {
|
|
| 981 |
+ container.Unmount() |
|
| 982 |
+ } |
|
| 983 |
+ }() |
|
| 976 | 984 |
|
| 977 |
- basePath, err := container.getResourcePath(resource) |
|
| 978 |
- if err != nil {
|
|
| 979 |
- container.Unmount() |
|
| 985 |
+ if err = container.mountVolumes(); err != nil {
|
|
| 986 |
+ container.unmountVolumes() |
|
| 980 | 987 |
return nil, err |
| 981 | 988 |
} |
| 982 |
- |
|
| 983 |
- // Check if this is actually in a volume |
|
| 984 |
- for _, mnt := range container.VolumeMounts() {
|
|
| 985 |
- if len(mnt.MountToPath) > 0 && strings.HasPrefix(resource, mnt.MountToPath[1:]) {
|
|
| 986 |
- return mnt.Export(resource) |
|
| 989 |
+ defer func() {
|
|
| 990 |
+ if err != nil {
|
|
| 991 |
+ container.unmountVolumes() |
|
| 987 | 992 |
} |
| 988 |
- } |
|
| 993 |
+ }() |
|
| 989 | 994 |
|
| 990 |
- // Check if this is a special one (resolv.conf, hostname, ..) |
|
| 991 |
- if resource == "etc/resolv.conf" {
|
|
| 992 |
- basePath = container.ResolvConfPath |
|
| 993 |
- } |
|
| 994 |
- if resource == "etc/hostname" {
|
|
| 995 |
- basePath = container.HostnamePath |
|
| 996 |
- } |
|
| 997 |
- if resource == "etc/hosts" {
|
|
| 998 |
- basePath = container.HostsPath |
|
| 995 |
+ basePath, err := container.getResourcePath(resource) |
|
| 996 |
+ if err != nil {
|
|
| 997 |
+ return nil, err |
|
| 999 | 998 |
} |
| 1000 | 999 |
|
| 1001 | 1000 |
stat, err := os.Stat(basePath) |
| 1002 | 1001 |
if err != nil {
|
| 1003 |
- container.Unmount() |
|
| 1004 | 1002 |
return nil, err |
| 1005 | 1003 |
} |
| 1006 | 1004 |
var filter []string |
| ... | ... |
@@ -1018,11 +1016,12 @@ func (container *Container) Copy(resource string) (io.ReadCloser, error) {
|
| 1018 | 1018 |
IncludeFiles: filter, |
| 1019 | 1019 |
}) |
| 1020 | 1020 |
if err != nil {
|
| 1021 |
- container.Unmount() |
|
| 1022 | 1021 |
return nil, err |
| 1023 | 1022 |
} |
| 1023 |
+ |
|
| 1024 | 1024 |
return ioutils.NewReadCloserWrapper(archive, func() error {
|
| 1025 | 1025 |
err := archive.Close() |
| 1026 |
+ container.unmountVolumes() |
|
| 1026 | 1027 |
container.Unmount() |
| 1027 | 1028 |
return err |
| 1028 | 1029 |
}), |
| ... | ... |
@@ -2,7 +2,6 @@ package daemon |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
- "io" |
|
| 6 | 5 |
"io/ioutil" |
| 7 | 6 |
"os" |
| 8 | 7 |
"path/filepath" |
| ... | ... |
@@ -12,6 +11,7 @@ import ( |
| 12 | 12 |
"github.com/Sirupsen/logrus" |
| 13 | 13 |
"github.com/docker/docker/daemon/execdriver" |
| 14 | 14 |
"github.com/docker/docker/pkg/chrootarchive" |
| 15 |
+ "github.com/docker/docker/pkg/mount" |
|
| 15 | 16 |
"github.com/docker/docker/pkg/symlink" |
| 16 | 17 |
"github.com/docker/docker/pkg/system" |
| 17 | 18 |
"github.com/docker/docker/volumes" |
| ... | ... |
@@ -27,18 +27,6 @@ type Mount struct {
|
| 27 | 27 |
isBind bool |
| 28 | 28 |
} |
| 29 | 29 |
|
| 30 |
-func (mnt *Mount) Export(resource string) (io.ReadCloser, error) {
|
|
| 31 |
- var name string |
|
| 32 |
- if resource == mnt.MountToPath[1:] {
|
|
| 33 |
- name = filepath.Base(resource) |
|
| 34 |
- } |
|
| 35 |
- path, err := filepath.Rel(mnt.MountToPath[1:], resource) |
|
| 36 |
- if err != nil {
|
|
| 37 |
- return nil, err |
|
| 38 |
- } |
|
| 39 |
- return mnt.volume.Export(path, name) |
|
| 40 |
-} |
|
| 41 |
- |
|
| 42 | 30 |
func (container *Container) prepareVolumes() error {
|
| 43 | 31 |
if container.Volumes == nil || len(container.Volumes) == 0 {
|
| 44 | 32 |
container.Volumes = make(map[string]string) |
| ... | ... |
@@ -320,6 +308,20 @@ func validMountMode(mode string) bool {
|
| 320 | 320 |
return validModes[mode] |
| 321 | 321 |
} |
| 322 | 322 |
|
| 323 |
+func (container *Container) specialMounts() []execdriver.Mount {
|
|
| 324 |
+ var mounts []execdriver.Mount |
|
| 325 |
+ if container.ResolvConfPath != "" {
|
|
| 326 |
+ mounts = append(mounts, execdriver.Mount{Source: container.ResolvConfPath, Destination: "/etc/resolv.conf", Writable: true, Private: true})
|
|
| 327 |
+ } |
|
| 328 |
+ if container.HostnamePath != "" {
|
|
| 329 |
+ mounts = append(mounts, execdriver.Mount{Source: container.HostnamePath, Destination: "/etc/hostname", Writable: true, Private: true})
|
|
| 330 |
+ } |
|
| 331 |
+ if container.HostsPath != "" {
|
|
| 332 |
+ mounts = append(mounts, execdriver.Mount{Source: container.HostsPath, Destination: "/etc/hosts", Writable: true, Private: true})
|
|
| 333 |
+ } |
|
| 334 |
+ return mounts |
|
| 335 |
+} |
|
| 336 |
+ |
|
| 323 | 337 |
func (container *Container) setupMounts() error {
|
| 324 | 338 |
mounts := []execdriver.Mount{}
|
| 325 | 339 |
|
| ... | ... |
@@ -336,17 +338,7 @@ func (container *Container) setupMounts() error {
|
| 336 | 336 |
}) |
| 337 | 337 |
} |
| 338 | 338 |
|
| 339 |
- if container.ResolvConfPath != "" {
|
|
| 340 |
- mounts = append(mounts, execdriver.Mount{Source: container.ResolvConfPath, Destination: "/etc/resolv.conf", Writable: true, Private: true})
|
|
| 341 |
- } |
|
| 342 |
- |
|
| 343 |
- if container.HostnamePath != "" {
|
|
| 344 |
- mounts = append(mounts, execdriver.Mount{Source: container.HostnamePath, Destination: "/etc/hostname", Writable: true, Private: true})
|
|
| 345 |
- } |
|
| 346 |
- |
|
| 347 |
- if container.HostsPath != "" {
|
|
| 348 |
- mounts = append(mounts, execdriver.Mount{Source: container.HostsPath, Destination: "/etc/hosts", Writable: true, Private: true})
|
|
| 349 |
- } |
|
| 339 |
+ mounts = append(mounts, container.specialMounts()...) |
|
| 350 | 340 |
|
| 351 | 341 |
container.command.Mounts = mounts |
| 352 | 342 |
return nil |
| ... | ... |
@@ -401,3 +393,57 @@ func copyOwnership(source, destination string) error {
|
| 401 | 401 |
|
| 402 | 402 |
return os.Chmod(destination, os.FileMode(stat.Mode())) |
| 403 | 403 |
} |
| 404 |
+ |
|
| 405 |
+func (container *Container) mountVolumes() error {
|
|
| 406 |
+ for dest, source := range container.Volumes {
|
|
| 407 |
+ v := container.daemon.volumes.Get(source) |
|
| 408 |
+ if v == nil {
|
|
| 409 |
+ return fmt.Errorf("could not find volume for %s:%s, impossible to mount", source, dest)
|
|
| 410 |
+ } |
|
| 411 |
+ |
|
| 412 |
+ destPath, err := container.getResourcePath(dest) |
|
| 413 |
+ if err != nil {
|
|
| 414 |
+ return err |
|
| 415 |
+ } |
|
| 416 |
+ |
|
| 417 |
+ if err := mount.Mount(source, destPath, "bind", "rbind,rw"); err != nil {
|
|
| 418 |
+ return fmt.Errorf("error while mounting volume %s: %v", source, err)
|
|
| 419 |
+ } |
|
| 420 |
+ } |
|
| 421 |
+ |
|
| 422 |
+ for _, mnt := range container.specialMounts() {
|
|
| 423 |
+ destPath, err := container.getResourcePath(mnt.Destination) |
|
| 424 |
+ if err != nil {
|
|
| 425 |
+ return err |
|
| 426 |
+ } |
|
| 427 |
+ if err := mount.Mount(mnt.Source, destPath, "bind", "bind,rw"); err != nil {
|
|
| 428 |
+ return fmt.Errorf("error while mounting volume %s: %v", mnt.Source, err)
|
|
| 429 |
+ } |
|
| 430 |
+ } |
|
| 431 |
+ return nil |
|
| 432 |
+} |
|
| 433 |
+ |
|
| 434 |
+func (container *Container) unmountVolumes() {
|
|
| 435 |
+ for dest := range container.Volumes {
|
|
| 436 |
+ destPath, err := container.getResourcePath(dest) |
|
| 437 |
+ if err != nil {
|
|
| 438 |
+ logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
|
|
| 439 |
+ continue |
|
| 440 |
+ } |
|
| 441 |
+ if err := mount.ForceUnmount(destPath); err != nil {
|
|
| 442 |
+ logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
|
|
| 443 |
+ continue |
|
| 444 |
+ } |
|
| 445 |
+ } |
|
| 446 |
+ |
|
| 447 |
+ for _, mnt := range container.specialMounts() {
|
|
| 448 |
+ destPath, err := container.getResourcePath(mnt.Destination) |
|
| 449 |
+ if err != nil {
|
|
| 450 |
+ logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
|
|
| 451 |
+ continue |
|
| 452 |
+ } |
|
| 453 |
+ if err := mount.ForceUnmount(destPath); err != nil {
|
|
| 454 |
+ logrus.Errorf("error while unmounting volumes %s: %v", destPath, err)
|
|
| 455 |
+ } |
|
| 456 |
+ } |
|
| 457 |
+} |
| ... | ... |
@@ -2,14 +2,11 @@ package volumes |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/json" |
| 5 |
- "io" |
|
| 6 | 5 |
"io/ioutil" |
| 7 | 6 |
"os" |
| 8 |
- "path" |
|
| 9 | 7 |
"path/filepath" |
| 10 | 8 |
"sync" |
| 11 | 9 |
|
| 12 |
- "github.com/docker/docker/pkg/archive" |
|
| 13 | 10 |
"github.com/docker/docker/pkg/symlink" |
| 14 | 11 |
) |
| 15 | 12 |
|
| ... | ... |
@@ -24,35 +21,6 @@ type Volume struct {
|
| 24 | 24 |
lock sync.Mutex |
| 25 | 25 |
} |
| 26 | 26 |
|
| 27 |
-func (v *Volume) Export(resource, name string) (io.ReadCloser, error) {
|
|
| 28 |
- if v.IsBindMount && filepath.Base(resource) == name {
|
|
| 29 |
- name = "" |
|
| 30 |
- } |
|
| 31 |
- |
|
| 32 |
- basePath, err := v.getResourcePath(resource) |
|
| 33 |
- if err != nil {
|
|
| 34 |
- return nil, err |
|
| 35 |
- } |
|
| 36 |
- stat, err := os.Stat(basePath) |
|
| 37 |
- if err != nil {
|
|
| 38 |
- return nil, err |
|
| 39 |
- } |
|
| 40 |
- var filter []string |
|
| 41 |
- if !stat.IsDir() {
|
|
| 42 |
- d, f := path.Split(basePath) |
|
| 43 |
- basePath = d |
|
| 44 |
- filter = []string{f}
|
|
| 45 |
- } else {
|
|
| 46 |
- filter = []string{path.Base(basePath)}
|
|
| 47 |
- basePath = path.Dir(basePath) |
|
| 48 |
- } |
|
| 49 |
- return archive.TarWithOptions(basePath, &archive.TarOptions{
|
|
| 50 |
- Compression: archive.Uncompressed, |
|
| 51 |
- Name: name, |
|
| 52 |
- IncludeFiles: filter, |
|
| 53 |
- }) |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 | 27 |
func (v *Volume) IsDir() (bool, error) {
|
| 57 | 28 |
stat, err := os.Stat(v.Path) |
| 58 | 29 |
if err != nil {
|