mirror of
https://github.com/containers/podman.git
synced 2025-06-21 01:19:15 +08:00
podmanv2 cp
enable podman to copy files between container and host, local mode only. this is a straight port of v1 code to v2. Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
55
cmd/podman/containers/cp.go
Normal file
55
cmd/podman/containers/cp.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package containers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/containers/libpod/cmd/podman/registry"
|
||||||
|
"github.com/containers/libpod/pkg/cgroups"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cpDescription = `Command copies the contents of SRC_PATH to the DEST_PATH.
|
||||||
|
|
||||||
|
You can copy from the container's file system 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.
|
||||||
|
`
|
||||||
|
cpCommand = &cobra.Command{
|
||||||
|
Use: "cp [flags] SRC_PATH DEST_PATH",
|
||||||
|
Short: "Copy files/folders between a container and the local filesystem",
|
||||||
|
Long: cpDescription,
|
||||||
|
Args: cobra.ExactArgs(2),
|
||||||
|
RunE: cp,
|
||||||
|
Example: "podman cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cpOpts entities.ContainerCpOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
|
Mode: []entities.EngineMode{entities.ABIMode},
|
||||||
|
Command: cpCommand,
|
||||||
|
})
|
||||||
|
flags := cpCommand.Flags()
|
||||||
|
flags.BoolVar(&cpOpts.Extract, "extract", false, "Extract the tar file into the destination directory.")
|
||||||
|
flags.BoolVar(&cpOpts.Pause, "pause", copyPause(), "Pause the container while copying")
|
||||||
|
}
|
||||||
|
|
||||||
|
func cp(cmd *cobra.Command, args []string) error {
|
||||||
|
_, err := registry.ContainerEngine().ContainerCp(registry.GetContext(), args[0], args[1], cpOpts)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyPause() bool {
|
||||||
|
if rootless.IsRootless() {
|
||||||
|
cgroupv2, _ := cgroups.IsCgroup2UnifiedMode()
|
||||||
|
if !cgroupv2 {
|
||||||
|
logrus.Debugf("defaulting to pause==false on rootless cp in cgroupv1 systems")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
@ -356,3 +356,13 @@ type ContainerPortReport struct {
|
|||||||
Id string
|
Id string
|
||||||
Ports []ocicni.PortMapping
|
Ports []ocicni.PortMapping
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContainerCpOptions describes input options for cp
|
||||||
|
type ContainerCpOptions struct {
|
||||||
|
Pause bool
|
||||||
|
Extract bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerCpReport describes the output from a cp operation
|
||||||
|
type ContainerCpReport struct {
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@ type ContainerEngine interface {
|
|||||||
ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error)
|
ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error)
|
||||||
ContainerPrune(ctx context.Context, options ContainerPruneOptions) (*ContainerPruneReport, error)
|
ContainerPrune(ctx context.Context, options ContainerPruneOptions) (*ContainerPruneReport, error)
|
||||||
ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error)
|
ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error)
|
||||||
|
ContainerCp(ctx context.Context, source, dest string, options ContainerCpOptions) (*ContainerCpReport, error)
|
||||||
ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
|
ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error)
|
||||||
ContainerDiff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error)
|
ContainerDiff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error)
|
||||||
ContainerExec(ctx context.Context, nameOrId string, options ExecOptions) (int, error)
|
ContainerExec(ctx context.Context, nameOrId string, options ExecOptions) (int, error)
|
||||||
|
433
pkg/domain/infra/abi/cp.go
Normal file
433
pkg/domain/infra/abi/cp.go
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
package abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/buildah/pkg/chrootuser"
|
||||||
|
"github.com/containers/buildah/util"
|
||||||
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/storage"
|
||||||
|
"github.com/containers/storage/pkg/chrootarchive"
|
||||||
|
"github.com/containers/storage/pkg/idtools"
|
||||||
|
securejoin "github.com/cyphar/filepath-securejoin"
|
||||||
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) (*entities.ContainerCpReport, error) {
|
||||||
|
var extract bool
|
||||||
|
|
||||||
|
srcCtr, srcPath := parsePath(ic.Libpod, source)
|
||||||
|
destCtr, destPath := parsePath(ic.Libpod, dest)
|
||||||
|
|
||||||
|
if (srcCtr == nil && destCtr == nil) || (srcCtr != nil && destCtr != nil) {
|
||||||
|
return nil, errors.Errorf("invalid arguments %s, %s you must use just one container", source, dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(srcPath) == 0 || len(destPath) == 0 {
|
||||||
|
return nil, errors.Errorf("invalid arguments %s, %s you must specify paths", source, dest)
|
||||||
|
}
|
||||||
|
ctr := srcCtr
|
||||||
|
isFromHostToCtr := ctr == nil
|
||||||
|
if isFromHostToCtr {
|
||||||
|
ctr = destCtr
|
||||||
|
}
|
||||||
|
|
||||||
|
mountPoint, err := ctr.Mount()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := ctr.Unmount(false); err != nil {
|
||||||
|
logrus.Errorf("unable to umount container '%s': %q", ctr.ID(), err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if options.Pause {
|
||||||
|
if err := ctr.Pause(); err != nil {
|
||||||
|
// An invalid state error is fine.
|
||||||
|
// The container isn't running or is already paused.
|
||||||
|
// TODO: We can potentially start the container while
|
||||||
|
// the copy is running, which still allows a race where
|
||||||
|
// malicious code could mess with the symlink.
|
||||||
|
if errors.Cause(err) != define.ErrCtrStateInvalid {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Only add the defer if we actually paused
|
||||||
|
defer func() {
|
||||||
|
if err := ctr.Unpause(); err != nil {
|
||||||
|
logrus.Errorf("Error unpausing container after copying: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := getUser(mountPoint, ctr.User())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
idMappingOpts, err := ctr.IDMappings()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error getting IDMappingOptions")
|
||||||
|
}
|
||||||
|
destOwner := idtools.IDPair{UID: int(user.UID), GID: int(user.GID)}
|
||||||
|
hostUID, hostGID, err := util.GetHostIDs(convertIDMap(idMappingOpts.UIDMap), convertIDMap(idMappingOpts.GIDMap), user.UID, user.GID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hostOwner := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)}
|
||||||
|
|
||||||
|
if isFromHostToCtr {
|
||||||
|
if isVol, volDestName, volName := isVolumeDestName(destPath, ctr); isVol { //nolint(gocritic)
|
||||||
|
path, err := pathWithVolumeMount(ctr, ic.Libpod, volDestName, volName, destPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error getting destination path from volume %s", volDestName)
|
||||||
|
}
|
||||||
|
destPath = path
|
||||||
|
} else if isBindMount, mount := isBindMountDestName(destPath, ctr); isBindMount { //nolint(gocritic)
|
||||||
|
path, err := pathWithBindMountSource(mount, destPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error getting destination path from bind mount %s", mount.Destination)
|
||||||
|
}
|
||||||
|
destPath = path
|
||||||
|
} else if filepath.IsAbs(destPath) { //nolint(gocritic)
|
||||||
|
cleanedPath, err := securejoin.SecureJoin(mountPoint, destPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
destPath = cleanedPath
|
||||||
|
} else { //nolint(gocritic)
|
||||||
|
ctrWorkDir, err := securejoin.SecureJoin(mountPoint, ctr.WorkingDir())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = idtools.MkdirAllAndChownNew(ctrWorkDir, 0755, hostOwner); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error creating directory %q", destPath)
|
||||||
|
}
|
||||||
|
cleanedPath, err := securejoin.SecureJoin(mountPoint, filepath.Join(ctr.WorkingDir(), destPath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
destPath = cleanedPath
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
destOwner = idtools.IDPair{UID: os.Getuid(), GID: os.Getgid()}
|
||||||
|
if isVol, volDestName, volName := isVolumeDestName(srcPath, ctr); isVol { //nolint(gocritic)
|
||||||
|
path, err := pathWithVolumeMount(ctr, ic.Libpod, volDestName, volName, srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error getting source path from volume %s", volDestName)
|
||||||
|
}
|
||||||
|
srcPath = path
|
||||||
|
} else if isBindMount, mount := isBindMountDestName(srcPath, ctr); isBindMount { //nolint(gocritic)
|
||||||
|
path, err := pathWithBindMountSource(mount, srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error getting source path from bind mount %s", mount.Destination)
|
||||||
|
}
|
||||||
|
srcPath = path
|
||||||
|
} else if filepath.IsAbs(srcPath) { //nolint(gocritic)
|
||||||
|
cleanedPath, err := securejoin.SecureJoin(mountPoint, srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
srcPath = cleanedPath
|
||||||
|
} else { //nolint(gocritic)
|
||||||
|
cleanedPath, err := securejoin.SecureJoin(mountPoint, filepath.Join(ctr.WorkingDir(), srcPath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
srcPath = cleanedPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !filepath.IsAbs(destPath) {
|
||||||
|
dir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "err getting current working directory")
|
||||||
|
}
|
||||||
|
destPath = filepath.Join(dir, destPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if source == "-" {
|
||||||
|
srcPath = os.Stdin.Name()
|
||||||
|
extract = true
|
||||||
|
}
|
||||||
|
err = containerCopy(srcPath, destPath, source, dest, idMappingOpts, &destOwner, extract, isFromHostToCtr)
|
||||||
|
return &entities.ContainerCpReport{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUser(mountPoint string, userspec string) (specs.User, error) {
|
||||||
|
uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec)
|
||||||
|
u := specs.User{
|
||||||
|
UID: uid,
|
||||||
|
GID: gid,
|
||||||
|
Username: userspec,
|
||||||
|
}
|
||||||
|
if !strings.Contains(userspec, ":") {
|
||||||
|
groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID))
|
||||||
|
if err2 != nil {
|
||||||
|
if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil {
|
||||||
|
err = err2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
u.AdditionalGids = groups
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return u, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePath(runtime *libpod.Runtime, path string) (*libpod.Container, string) {
|
||||||
|
pathArr := strings.SplitN(path, ":", 2)
|
||||||
|
if len(pathArr) == 2 {
|
||||||
|
ctr, err := runtime.LookupContainer(pathArr[0])
|
||||||
|
if err == nil {
|
||||||
|
return ctr, pathArr[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, path
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalSymlinks(path string) (string, error) {
|
||||||
|
if path == os.Stdin.Name() {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
return filepath.EvalSymlinks(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPathInfo(path string) (string, os.FileInfo, error) {
|
||||||
|
path, err := evalSymlinks(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, errors.Wrapf(err, "error evaluating symlinks %q", path)
|
||||||
|
}
|
||||||
|
srcfi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, errors.Wrapf(err, "error reading path %q", path)
|
||||||
|
}
|
||||||
|
return path, srcfi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func containerCopy(srcPath, destPath, src, dest string, idMappingOpts storage.IDMappingOptions, chownOpts *idtools.IDPair, extract, isFromHostToCtr bool) error {
|
||||||
|
srcPath, err := evalSymlinks(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error evaluating symlinks %q", srcPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
srcPath, srcfi, err := getPathInfo(srcPath)
|
||||||
|
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() {
|
||||||
|
destdir = filepath.Dir(destPath)
|
||||||
|
}
|
||||||
|
_, err = os.Stat(destdir)
|
||||||
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
return errors.Wrapf(err, "error checking directory %q", destdir)
|
||||||
|
}
|
||||||
|
destDirIsExist := err == nil
|
||||||
|
if err = os.MkdirAll(destdir, 0755); err != nil {
|
||||||
|
return errors.Wrapf(err, "error creating directory %q", destdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// return functions for copying items
|
||||||
|
copyFileWithTar := chrootarchive.CopyFileWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap)
|
||||||
|
copyWithTar := chrootarchive.CopyWithTarAndChown(chownOpts, digest.Canonical.Digester().Hash(), idMappingOpts.UIDMap, idMappingOpts.GIDMap)
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
if err = copyWithTar(srcPath, destPath); err != nil {
|
||||||
|
return errors.Wrapf(err, "error copying %q to %q", srcPath, dest)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if extract {
|
||||||
|
// We're extracting an archive into the destination directory.
|
||||||
|
logrus.Debugf("extracting contents of %q into %q", srcPath, destPath)
|
||||||
|
if err = untarPath(srcPath, destPath); err != nil {
|
||||||
|
return errors.Wrapf(err, "error extracting %q into %q", srcPath, destPath)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
destfi, err := os.Stat(destPath)
|
||||||
|
if err != nil {
|
||||||
|
if !os.IsNotExist(err) || strings.HasSuffix(dest, string(os.PathSeparator)) {
|
||||||
|
return errors.Wrapf(err, "failed to get stat of dest path %s", destPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if destfi != nil && destfi.IsDir() {
|
||||||
|
destPath = filepath.Join(destPath, filepath.Base(srcPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the file, preserving attributes.
|
||||||
|
logrus.Debugf("copying %q to %q", srcPath, destPath)
|
||||||
|
if err = copyFileWithTar(srcPath, destPath); err != nil {
|
||||||
|
return errors.Wrapf(err, "error copying %q to %q", srcPath, destPath)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertIDMap(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) {
|
||||||
|
for _, idmap := range idMaps {
|
||||||
|
tempIDMap := specs.LinuxIDMapping{
|
||||||
|
ContainerID: uint32(idmap.ContainerID),
|
||||||
|
HostID: uint32(idmap.HostID),
|
||||||
|
Size: uint32(idmap.Size),
|
||||||
|
}
|
||||||
|
convertedIDMap = append(convertedIDMap, tempIDMap)
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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))]
|
||||||
|
}
|
||||||
|
return pathStr == target
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
@ -375,3 +375,7 @@ func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) {
|
|||||||
func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrId string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) {
|
func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrId string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) {
|
||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) (*entities.ContainerCpReport, error) {
|
||||||
|
return nil, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user