Skip to content

Commit e22eb88

Browse files
committed
Fix windows path handling in podman cp
Signed-off-by: David Negstad <[email protected]>
1 parent 2ec79ad commit e22eb88

File tree

5 files changed

+239
-95
lines changed

5 files changed

+239
-95
lines changed

cmd/podman/containers/cp.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,11 @@ func copyToContainer(container string, containerPath string, hostPath string) er
428428
// rename accordingly.
429429
getOptions.Rename = map[string]string{filepath.Base(hostTarget): containerBaseName}
430430
}
431-
if err := buildahCopiah.Get("/", "", getOptions, []string{hostTarget}, writer); err != nil {
431+
432+
// On Windows, the root path needs to be <drive>:\, while otherwise
433+
// it needs to be /. Combining filepath.VolumeName() + string(os.PathSeparator)
434+
// gives us the correct path for the current OS.
435+
if err := buildahCopiah.Get(filepath.VolumeName(hostTarget)+string(os.PathSeparator), "", getOptions, []string{hostTarget}, writer); err != nil {
432436
return fmt.Errorf("copying from host: %w", err)
433437
}
434438
return nil

pkg/copy/fileinfo.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -83,20 +83,20 @@ func ResolveHostPath(path string) (*FileInfo, error) {
8383
// is preserved. The filepath API among tends to clean up a bit too much but
8484
// we *must* preserve this data by all means.
8585
func PreserveBasePath(original, resolved string) string {
86-
// Handle "/"
87-
if strings.HasSuffix(original, "/") {
88-
if !strings.HasSuffix(resolved, "/") {
89-
resolved += "/"
86+
// Handle "/" or "\\" on Windows.
87+
if strings.HasSuffix(original, string(os.PathSeparator)) {
88+
if !strings.HasSuffix(resolved, string(os.PathSeparator)) {
89+
resolved += string(os.PathSeparator)
9090
}
9191
return resolved
9292
}
9393

9494
// Handle "/."
95-
if strings.HasSuffix(original, "/.") {
96-
if strings.HasSuffix(resolved, "/") { // could be root!
95+
if strings.HasSuffix(original, string(os.PathSeparator)+".") {
96+
if strings.HasSuffix(resolved, string(os.PathSeparator)) { // could be root!
9797
resolved += "."
98-
} else if !strings.HasSuffix(resolved, "/.") {
99-
resolved += "/."
98+
} else if !strings.HasSuffix(resolved, string(os.PathSeparator)+".") {
99+
resolved += string(os.PathSeparator) + "."
100100
}
101101
return resolved
102102
}

pkg/copy/parse.go

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package copy
22

33
import (
44
"fmt"
5+
"path/filepath"
56
"strings"
67
)
78

@@ -40,6 +41,14 @@ func parseUserInput(input string) (container string, path string) {
4041
return
4142
}
4243

44+
// If the input is an absolute path, it cannot refer to a container.
45+
// This is necessary because absolute paths on Windows will include
46+
// a colon, which would cause the drive letter to be parsed as a
47+
// container name.
48+
if filepath.IsAbs(input) {
49+
return
50+
}
51+
4352
if parsedContainer, parsedPath, ok := strings.Cut(path, ":"); ok {
4453
container = parsedContainer
4554
path = parsedPath

pkg/machine/e2e/basic_test.go

-84
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package e2e_test
22

33
import (
4-
"archive/tar"
5-
"bytes"
64
"fmt"
75
"io"
8-
"io/fs"
96
"net"
107
"net/http"
118
"net/url"
@@ -262,87 +259,6 @@ var _ = Describe("run basic podman commands", func() {
262259
Expect(run).To(Exit(0))
263260
Expect(build.outputToString()).To(ContainSubstring(name))
264261
})
265-
266-
It("Copy ops", func() {
267-
var (
268-
stdinDirectory = "stdin-dir"
269-
stdinFile = "file.txt"
270-
)
271-
272-
now := time.Now()
273-
274-
tarBuffer := &bytes.Buffer{}
275-
tw := tar.NewWriter(tarBuffer)
276-
277-
// Write a directory header to the tar
278-
err := tw.WriteHeader(&tar.Header{
279-
Name: stdinDirectory,
280-
Mode: int64(0640 | fs.ModeDir),
281-
Gid: 1000,
282-
ModTime: now,
283-
ChangeTime: now,
284-
AccessTime: now,
285-
Typeflag: tar.TypeDir,
286-
})
287-
Expect(err).ToNot(HaveOccurred())
288-
289-
// Write a file header to the tar
290-
err = tw.WriteHeader(&tar.Header{
291-
Name: path.Join(stdinDirectory, stdinFile),
292-
Mode: 0755,
293-
Uid: 1000,
294-
ModTime: now,
295-
ChangeTime: now,
296-
AccessTime: now,
297-
})
298-
Expect(err).ToNot(HaveOccurred())
299-
300-
err = tw.Close()
301-
Expect(err).ToNot(HaveOccurred())
302-
303-
name := randomString()
304-
i := new(initMachine)
305-
session, err := mb.setName(name).setCmd(i.withImage(mb.imagePath).withNow()).run()
306-
Expect(err).ToNot(HaveOccurred())
307-
Expect(session).To(Exit(0))
308-
309-
bm := basicMachine{}
310-
newImgs, err := mb.setCmd(bm.withPodmanCommand([]string{"pull", TESTIMAGE})).run()
311-
Expect(err).ToNot(HaveOccurred())
312-
Expect(newImgs).To(Exit(0))
313-
Expect(newImgs.outputToStringSlice()).To(HaveLen(1))
314-
315-
createAlp, err := mb.setCmd(bm.withPodmanCommand([]string{"create", TESTIMAGE, "top"})).run()
316-
Expect(err).ToNot(HaveOccurred())
317-
Expect(createAlp).To(Exit(0))
318-
Expect(createAlp.outputToStringSlice()).To(HaveLen(1))
319-
320-
// Testing stdin copy with archive mode disabled (ownership will be determined by the tar file)
321-
containerID := createAlp.outputToStringSlice()[0]
322-
cpTar, err := mb.setCmd(bm.withPodmanCommand([]string{"cp", "-a=false", "-", containerID + ":/tmp"})).setStdin(tarBuffer).run()
323-
Expect(err).ToNot(HaveOccurred())
324-
Expect(cpTar).To(Exit(0))
325-
326-
start, err := mb.setCmd(bm.withPodmanCommand([]string{"start", containerID})).run()
327-
Expect(err).ToNot(HaveOccurred())
328-
Expect(start).To(Exit(0))
329-
330-
// Check the directory is created with the appropriate mode, uid, gid
331-
exec, err := mb.setCmd(bm.withPodmanCommand([]string{"exec", containerID, "stat", "-c", "%a %u %g", "/tmp/stdin-dir"})).run()
332-
Expect(err).ToNot(HaveOccurred())
333-
Expect(exec).To(Exit(0))
334-
execStdOut := exec.outputToStringSlice()
335-
Expect(execStdOut).To(HaveLen(1))
336-
Expect(execStdOut[0]).To(Equal("640 0 1000"))
337-
338-
// Check the file is created with the appropriate mode, uid, gid
339-
exec, err = mb.setCmd(bm.withPodmanCommand([]string{"exec", containerID, "stat", "-c", "%a %u %g", "/tmp/stdin-dir/file.txt"})).run()
340-
Expect(err).ToNot(HaveOccurred())
341-
Expect(exec).To(Exit(0))
342-
execStdOut = exec.outputToStringSlice()
343-
Expect(execStdOut).To(HaveLen(1))
344-
Expect(execStdOut[0]).To(Equal("755 1000 0"))
345-
})
346262
})
347263

348264
func testHTTPServer(port string, shouldErr bool, expectedResponse string) {

0 commit comments

Comments
 (0)