Skip to content

Commit

Permalink
podman copy files to the volume with a container
Browse files Browse the repository at this point in the history
enabls podman to cpoy files between the host machine and the volume related with a container.
Close #3059

Signed-off-by: Qi Wang <[email protected]>
  • Loading branch information
QiWang19 committed Jun 3, 2019
1 parent 0ede794 commit c1b4060
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 2 deletions.
97 changes: 95 additions & 2 deletions cmd/podman/cp.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,19 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin

var glob []string
if isFromHostToCtr {
if filepath.IsAbs(destPath) {
if isVol, volDestName, volName := isVolumeDestName(destPath, ctr); isVol {
path, err := pathWithVolumeMount(ctr, runtime, volDestName, volName, destPath)
if err != nil {
return errors.Wrapf(err, "error getting destination path from volume %s", volDestName)
}
destPath = path
} else if isBindMount, mount := isBindMountDestName(destPath, ctr); isBindMount {
path, err := pathWithBindMountSource(mount, destPath)
if err != nil {
return errors.Wrapf(err, "error getting destination path from bind mount %s", mount.Destination)
}
destPath = path
} else if filepath.IsAbs(destPath) {
cleanedPath, err := securejoin.SecureJoin(mountPoint, destPath)
if err != nil {
return err
Expand All @@ -166,7 +178,19 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin
destPath = cleanedPath
}
} else {
if filepath.IsAbs(srcPath) {
if isVol, volDestName, volName := isVolumeDestName(srcPath, ctr); isVol {
path, err := pathWithVolumeMount(ctr, runtime, volDestName, volName, srcPath)
if err != nil {
return errors.Wrapf(err, "error getting source path from volume %s", volDestName)
}
srcPath = path
} else if isBindMount, mount := isBindMountDestName(srcPath, ctr); isBindMount {
path, err := pathWithBindMountSource(mount, srcPath)
if err != nil {
return errors.Wrapf(err, "error getting source path from bind moutn %s", mount.Destination)
}
srcPath = path
} else if filepath.IsAbs(srcPath) {
cleanedPath, err := securejoin.SecureJoin(mountPoint, srcPath)
if err != nil {
return err
Expand Down Expand Up @@ -407,3 +431,72 @@ func streamFileToStdout(srcPath string, srcfi os.FileInfo) error {
}
return nil
}

func isVolumeDestName(path string, ctr *libpod.Container) (bool, string, string) {
separator := string(os.PathSeparator)
if filepath.IsAbs(path) {
path = strings.TrimPrefix(path, separator)
}
if path == "" {
return false, "", ""
}
for _, vol := range ctr.Config().NamedVolumes {
volNamePath := strings.TrimPrefix(vol.Dest, separator)
if matchVolumePath(path, volNamePath) {
return true, vol.Dest, vol.Name
}
}
return false, "", ""
}

// if SRCPATH or DESTPATH is from volume mount's destination -v or --mount type=volume, generates the path with volume mount point
func pathWithVolumeMount(ctr *libpod.Container, runtime *libpod.Runtime, volDestName, volName, path string) (string, error) {
destVolume, err := runtime.GetVolume(volName)
if err != nil {
return "", errors.Wrapf(err, "error getting volume destination %s", volName)
}
if !filepath.IsAbs(path) {
path = filepath.Join(string(os.PathSeparator), path)
}
path, err = securejoin.SecureJoin(destVolume.MountPoint(), strings.TrimPrefix(path, volDestName))
return path, err
}

func isBindMountDestName(path string, ctr *libpod.Container) (bool, specs.Mount) {
separator := string(os.PathSeparator)
if filepath.IsAbs(path) {
path = strings.TrimPrefix(path, string(os.PathSeparator))
}
if path == "" {
return false, specs.Mount{}
}
for _, m := range ctr.Config().Spec.Mounts {
if m.Type != "bind" {
continue
}
mDest := strings.TrimPrefix(m.Destination, separator)
if matchVolumePath(path, mDest) {
return true, m
}
}
return false, specs.Mount{}
}

func matchVolumePath(path, target string) bool {
pathStr := filepath.Clean(path)
target = filepath.Clean(target)
for len(pathStr) > len(target) && strings.Contains(pathStr, string(os.PathSeparator)) {
pathStr = pathStr[:strings.LastIndex(pathStr, string(os.PathSeparator))]
}
if pathStr == target {
return true
}
return false
}

func pathWithBindMountSource(m specs.Mount, path string) (string, error) {
if !filepath.IsAbs(path) {
path = filepath.Join(string(os.PathSeparator), path)
}
return securejoin.SecureJoin(m.Source, strings.TrimPrefix(path, m.Destination))
}
21 changes: 21 additions & 0 deletions test/e2e/cp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,25 @@ var _ = Describe("Podman cp", func() {
_, err = os.Stat("/tmp/cp_test.txt")
Expect(err).To(Not(BeNil()))
})
It("podman cp volume", func() {
session := podmanTest.Podman([]string{"volume", "create", "data"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

session = podmanTest.Podman([]string{"create", "-v", "data:/data", "--name", "container1", ALPINE})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

err = ioutil.WriteFile("cp_vol", []byte("copy to the volume"), 0644)
if err != nil {
os.Exit(1)
}
session = podmanTest.Podman([]string{"cp", "cp_vol", "container1" + ":/data/cp_vol1"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

session = podmanTest.Podman([]string{"cp", "container1" + ":/data/cp_vol1", "cp_vol2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
})
})

0 comments on commit c1b4060

Please sign in to comment.