diff options
-rw-r--r-- | cmd/podman/cp.go | 80 | ||||
-rw-r--r-- | docs/podman-cp.1.md | 3 | ||||
-rw-r--r-- | test/e2e/cp_test.go | 75 |
3 files changed, 125 insertions, 33 deletions
diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go index 82f2d3f20..4cb8a8c54 100644 --- a/cmd/podman/cp.go +++ b/cmd/podman/cp.go @@ -1,7 +1,9 @@ package main import ( + "archive/tar" "fmt" + "io" "os" "path/filepath" "strings" @@ -142,7 +144,11 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin var lastError error for _, src := range glob { - err := copy(src, destPath, dest, idMappingOpts, &containerOwner, extract) + if src == "-" { + src = os.Stdin.Name() + extract = true + } + err := copy(src, destPath, dest, idMappingOpts, &containerOwner, extract, isFromHostToCtr) if lastError != nil { logrus.Error(lastError) } @@ -195,7 +201,7 @@ func getPathInfo(path string) (string, os.FileInfo, error) { return path, srcfi, nil } -func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair, extract bool) error { +func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair, extract, isFromHostToCtr bool) error { srcPath, err := filepath.EvalSymlinks(src) if err != nil { return errors.Wrapf(err, "error evaluating symlinks %q", srcPath) @@ -205,6 +211,16 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch if err != nil { return err } + + filename := filepath.Base(destPath) + if filename == "-" && !isFromHostToCtr { + err := streamFileToStdout(srcPath, srcfi) + if err != nil { + return errors.Wrapf(err, "error streaming source file %s to Stdout", srcPath) + } + return nil + } + destdir := destPath if !srcfi.IsDir() && !strings.HasSuffix(dest, string(os.PathSeparator)) { destdir = filepath.Dir(destPath) @@ -224,7 +240,6 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch untarPath := chrootarchive.UntarPathAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap) if srcfi.IsDir() { - logrus.Debugf("copying %q to %q", srcPath+string(os.PathSeparator)+"*", dest+string(os.PathSeparator)+"*") if destDirIsExist && !strings.HasSuffix(src, fmt.Sprintf("%s.", string(os.PathSeparator))) { destPath = filepath.Join(destPath, filepath.Base(srcPath)) @@ -276,3 +291,62 @@ func convertIDMap(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping } return convertedIDMap } + +func streamFileToStdout(srcPath string, srcfi os.FileInfo) error { + if srcfi.IsDir() { + tw := tar.NewWriter(os.Stdout) + err := filepath.Walk(srcPath, func(path string, info os.FileInfo, err error) error { + if err != nil || !info.Mode().IsRegular() || path == srcPath { + return err + } + hdr, err := tar.FileInfoHeader(info, "") + if err != nil { + return err + } + + if err = tw.WriteHeader(hdr); err != nil { + return err + } + fh, err := os.Open(path) + if err != nil { + return err + } + defer fh.Close() + + _, err = io.Copy(tw, fh) + return err + }) + if err != nil { + return errors.Wrapf(err, "error streaming directory %s to Stdout", srcPath) + } + return nil + } + + file, err := os.Open(srcPath) + if err != nil { + return errors.Wrapf(err, "error opening file %s", srcPath) + } + defer file.Close() + if !archive.IsArchivePath(srcPath) { + tw := tar.NewWriter(os.Stdout) + hdr, err := tar.FileInfoHeader(srcfi, "") + if err != nil { + return err + } + err = tw.WriteHeader(hdr) + if err != nil { + return err + } + _, err = io.Copy(tw, file) + if err != nil { + return errors.Wrapf(err, "error streaming archive %s to Stdout", srcPath) + } + return nil + } + + _, err = io.Copy(os.Stdout, file) + if err != nil { + return errors.Wrapf(err, "error streaming file to Stdout") + } + return nil +} diff --git a/docs/podman-cp.1.md b/docs/podman-cp.1.md index 44612003d..406dd51df 100644 --- a/docs/podman-cp.1.md +++ b/docs/podman-cp.1.md @@ -8,6 +8,7 @@ podman\-cp - Copy files/folders between a container and the local filesystem ## DESCRIPTION Copies the contents of **src_path** to the **dest_path**. You can copy from the containers's filesystem to the local machine or the reverse, from the local filesystem to the container. +If - is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT. The CONTAINER can be a running or stopped container. The **src_path** or **dest_path** can be a file or directory. @@ -107,5 +108,7 @@ podman cp containerID:/home/myuser/. /home/myuser/ podman cp --extract /home/myuser/myfiles.tar.gz containerID:/myfiles +podman cp - containerID:/myfiles.tar.gz < myfiles.tar.gz + ## SEE ALSO podman(1), podman-mount(1), podman-umount(1) diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go index 591f533d6..273668f35 100644 --- a/test/e2e/cp_test.go +++ b/test/e2e/cp_test.go @@ -7,6 +7,7 @@ import ( "os" "os/exec" "path/filepath" + "strings" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" @@ -58,21 +59,12 @@ var _ = Describe("Podman cp", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"start", "-a", name}) - session.WaitWithDefaultTimeout() - - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(Equal("copy from host to container")) - session = podmanTest.Podman([]string{"cp", name + ":foo", filepath.Join(path, "cp_from_container")}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - c := exec.Command("cat", filepath.Join(path, "cp_from_container")) - output, err := c.Output() - if err != nil { - os.Exit(1) - } - Expect(string(output)).To(Equal("copy from host to container")) + + os.Remove("cp_from_container") + os.Remove("cp_test.txt") }) It("podman cp file to dir", func() { @@ -89,28 +81,18 @@ var _ = Describe("Podman cp", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls", "foodir/"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"ps", "-a", "-q"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) name := session.OutputToString() session = podmanTest.Podman([]string{"cp", filepath.Join(path, "cp_test.txt"), name + ":foodir/"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"start", "-a", name}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(Equal("cp_test.txt")) session = podmanTest.Podman([]string{"cp", name + ":foodir/cp_test.txt", path + "/receive/"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - c := exec.Command("cat", filepath.Join(path, "receive", "cp_test.txt")) - output, err := c.Output() - if err != nil { - os.Exit(1) - } - Expect(string(output)).To(Equal("copy from host to container directory")) + + os.Remove("cp_test.txt") + os.RemoveAll("receive") }) It("podman cp dir to dir", func() { @@ -132,17 +114,50 @@ var _ = Describe("Podman cp", func() { session = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"start", "-a", name}) + + session = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(len(session.OutputToStringArray())).To(Equal(0)) - session = podmanTest.Podman([]string{"cp", testDirPath, name + ":/foodir"}) + os.RemoveAll(testDirPath) + }) + + It("podman cp stdin/stdout", func() { + path, err := os.Getwd() + if err != nil { + os.Exit(1) + } + testDirPath := filepath.Join(path, "TestDir") + err = os.Mkdir(testDirPath, 0777) + if err != nil { + os.Exit(1) + } + cmd := exec.Command("tar", "-zcvf", "file.tar.gz", testDirPath) + _, err = cmd.Output() + if err != nil { + os.Exit(1) + } + + session := podmanTest.Podman([]string{"create", ALPINE, "ls", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + name := session.OutputToString() + + data, err := ioutil.ReadFile("foo.tar.gz") + reader := strings.NewReader(string(data)) + cmd.Stdin = reader + session = podmanTest.Podman([]string{"cp", "-", name + ":/foo"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"start", "-a", name}) + + session = podmanTest.Podman([]string{"cp", "file.tar.gz", name + ":/foo.tar.gz"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(Equal("TestDir")) + session = podmanTest.Podman([]string{"cp", name + ":/foo.tar.gz", "-"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + os.Remove("file.tar.gz") + os.RemoveAll(testDirPath) }) }) |