Implement volume copy up.

This pulls in and uses github.com/docker/docker/pkg/chrootarchive for the
actual copy up which is some battle hardened code to unpack avoiding things
like symlink traversal security issues.

However it does pull in a pretty huge pile of vendoring, including
github.com/docker/docker/pkg/reexec which we must then call at startup. It's
not immediately clear that this tradeoff is the correct one.

Signed-off-by: Ian Campbell <ijc@docker.com>
This commit is contained in:
Ian Campbell
2017-09-15 09:59:18 +01:00
parent a8d4940285
commit 8c6ba35038
50 changed files with 5212 additions and 2 deletions

View File

@@ -21,6 +21,7 @@ import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/containers"
"github.com/docker/docker/pkg/chrootarchive"
"github.com/docker/docker/pkg/system"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
@@ -56,14 +57,37 @@ func WithVolumes(volumeMounts map[string]string) containerd.NewContainerOpts {
defer unix.Unmount(root, 0) // nolint: errcheck
for host, volume := range volumeMounts {
if err := copyOwnership(filepath.Join(root, volume), host); err != nil {
return err
if err := copyExistingContents(filepath.Join(root, volume), host); err != nil {
return errors.Wrap(err, "taking runtime copy of volume")
}
}
return nil
}
}
// copyExistingContents copies from the source to the destination and
// ensures the ownership is appropriately set.
func copyExistingContents(source, destination string) error {
srcList, err := ioutil.ReadDir(source)
if err != nil {
return err
}
if len(srcList) > 0 {
dstList, err := ioutil.ReadDir(destination)
if err != nil {
return err
}
if len(dstList) != 0 {
return errors.Errorf("volume at %q is not initially empty", destination)
}
if err := chrootarchive.NewArchiver(nil).CopyWithTar(source, destination); err != nil {
return err
}
}
return copyOwnership(source, destination)
}
// copyOwnership copies the permissions and uid:gid of the src file
// to the dst file
func copyOwnership(src, dst string) error {