Skip to content

Commit

Permalink
cp: consolidate and simplify
Browse files Browse the repository at this point in the history
Consolidate and simplify code in `podman cp` a bit.  PR containers#11049
introduced some code duplicates that were worth tackling.

[NO TESTS NEEDED]

Signed-off-by: Valentin Rothberg <[email protected]>
  • Loading branch information
vrothberg committed Jul 28, 2021
1 parent a5de831 commit 0132670
Showing 1 changed file with 62 additions and 73 deletions.
135 changes: 62 additions & 73 deletions cmd/podman/containers/cp.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,43 +131,17 @@ func copyContainerToContainer(sourceContainer string, sourcePath string, destCon
return errors.Wrapf(err, "%q could not be found on container %s", sourcePath, sourceContainer)
}

var destContainerBaseName string
destContainerInfo, destContainerInfoErr := registry.ContainerEngine().ContainerStat(registry.GetContext(), destContainer, destPath)
if destContainerInfoErr != nil {
if strings.HasSuffix(destPath, "/") {
return errors.Wrapf(destContainerInfoErr, "%q could not be found on container %s", destPath, destContainer)
}
// NOTE: containerInfo may actually be set. That happens when
// the container path is a symlink into nirvana. In that case,
// we must use the symlinked path instead.
path := destPath
if destContainerInfo != nil {
destContainerBaseName = filepath.Base(destContainerInfo.LinkTarget)
path = destContainerInfo.LinkTarget
} else {
destContainerBaseName = filepath.Base(destPath)
}

parentDir, err := containerParentDir(destContainer, path)
if err != nil {
return errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, destContainer)
}
destContainerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), destContainer, parentDir)
if err != nil {
return errors.Wrapf(err, "%q could not be found on container %s", destPath, destContainer)
}
} else {
// If the specified path exists on the container, we must use
// its base path as it may have changed due to symlink
// evaluations.
destContainerBaseName = filepath.Base(destContainerInfo.LinkTarget)
destContainerBaseName, destContainerInfo, destResolvedToParentDir, err := resolvePathOnDestinationContainer(destContainer, destPath, false)
if err != nil {
return err
}

if sourceContainerInfo.IsDir && !destContainerInfo.IsDir {
return errors.New("destination must be a directory when copying a directory")
}

sourceContainerTarget, destContainerTarget := sourceContainerInfo.LinkTarget, destContainerInfo.LinkTarget
sourceContainerTarget := sourceContainerInfo.LinkTarget
destContainerTarget := destContainerInfo.LinkTarget
if !destContainerInfo.IsDir {
destContainerTarget = filepath.Dir(destPath)
}
Expand All @@ -180,7 +154,7 @@ func copyContainerToContainer(sourceContainer string, sourcePath string, destCon
// Hence, whenever "." is the source and the destination does not
// exist, we copy the source's parent and let the copier package create
// the destination via the Rename option.
if destContainerInfoErr != nil && sourceContainerInfo.IsDir && strings.HasSuffix(sourcePath, ".") {
if destResolvedToParentDir && sourceContainerInfo.IsDir && strings.HasSuffix(sourcePath, ".") {
sourceContainerTarget = filepath.Dir(sourceContainerTarget)
}

Expand All @@ -202,7 +176,7 @@ func copyContainerToContainer(sourceContainer string, sourcePath string, destCon
defer reader.Close()

copyOptions := entities.CopyOptions{Chown: chown}
if (!sourceContainerInfo.IsDir && !destContainerInfo.IsDir) || destContainerInfoErr != nil {
if (!sourceContainerInfo.IsDir && !destContainerInfo.IsDir) || destResolvedToParentDir {
// If we're having a file-to-file copy, make sure to
// rename accordingly.
copyOptions.Rename = map[string]string{filepath.Base(sourceContainerTarget): destContainerBaseName}
Expand Down Expand Up @@ -239,6 +213,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
}

var hostBaseName string
var resolvedToHostParentDir bool
hostInfo, hostInfoErr := copy.ResolveHostPath(hostPath)
if hostInfoErr != nil {
if strings.HasSuffix(hostPath, "/") {
Expand All @@ -254,6 +229,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
// it'll be created while copying. Hence, we use it as the
// base path.
hostBaseName = filepath.Base(hostPath)
resolvedToHostParentDir = true
} else {
// If the specified path exists on the host, we must use its
// base path as it may have changed due to symlink evaluations.
Expand Down Expand Up @@ -281,7 +257,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
// we copy the source's parent and let the copier package create the
// destination via the Rename option.
containerTarget := containerInfo.LinkTarget
if hostInfoErr != nil && containerInfo.IsDir && strings.HasSuffix(containerTarget, ".") {
if resolvedToHostParentDir && containerInfo.IsDir && strings.HasSuffix(containerTarget, ".") {
containerTarget = filepath.Dir(containerTarget)
}

Expand Down Expand Up @@ -322,7 +298,7 @@ func copyFromContainer(container string, containerPath string, hostPath string)
ChownFiles: &idPair,
IgnoreDevices: true,
}
if (!containerInfo.IsDir && !hostInfo.IsDir) || hostInfoErr != nil {
if (!containerInfo.IsDir && !hostInfo.IsDir) || resolvedToHostParentDir {
// If we're having a file-to-file copy, make sure to
// rename accordingly.
putOptions.Rename = map[string]string{filepath.Base(containerTarget): hostBaseName}
Expand Down Expand Up @@ -369,42 +345,9 @@ func copyToContainer(container string, containerPath string, hostPath string) er
return errors.Wrapf(err, "%q could not be found on the host", hostPath)
}

// If the path on the container does not exist. We need to make sure
// that it's parent directory exists. The destination may be created
// while copying.
var containerBaseName string
containerInfo, containerInfoErr := registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath)
if containerInfoErr != nil {
if strings.HasSuffix(containerPath, "/") {
return errors.Wrapf(containerInfoErr, "%q could not be found on container %s", containerPath, container)
}
if isStdin {
return errors.New("destination must be a directory when copying from stdin")
}
// NOTE: containerInfo may actually be set. That happens when
// the container path is a symlink into nirvana. In that case,
// we must use the symlinked path instead.
path := containerPath
if containerInfo != nil {
containerBaseName = filepath.Base(containerInfo.LinkTarget)
path = containerInfo.LinkTarget
} else {
containerBaseName = filepath.Base(containerPath)
}

parentDir, err := containerParentDir(container, path)
if err != nil {
return errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, container)
}
containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, parentDir)
if err != nil {
return errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
}
} else {
// If the specified path exists on the container, we must use
// its base path as it may have changed due to symlink
// evaluations.
containerBaseName = filepath.Base(containerInfo.LinkTarget)
containerBaseName, containerInfo, containerResolvedToParentDir, err := resolvePathOnDestinationContainer(container, containerPath, isStdin)
if err != nil {
return err
}

// If we copy a directory via the "." notation and the container path
Expand All @@ -416,7 +359,7 @@ func copyToContainer(container string, containerPath string, hostPath string) er
// exist, we copy the source's parent and let the copier package create
// the destination via the Rename option.
hostTarget := hostInfo.LinkTarget
if containerInfoErr != nil && hostInfo.IsDir && strings.HasSuffix(hostTarget, ".") {
if containerResolvedToParentDir && hostInfo.IsDir && strings.HasSuffix(hostTarget, ".") {
hostTarget = filepath.Dir(hostTarget)
}

Expand Down Expand Up @@ -468,7 +411,7 @@ func copyToContainer(container string, containerPath string, hostPath string) er
// copy the base directory.
KeepDirectoryNames: hostInfo.IsDir && filepath.Base(hostTarget) != ".",
}
if (!hostInfo.IsDir && !containerInfo.IsDir) || containerInfoErr != nil {
if (!hostInfo.IsDir && !containerInfo.IsDir) || containerResolvedToParentDir {
// If we're having a file-to-file copy, make sure to
// rename accordingly.
getOptions.Rename = map[string]string{filepath.Base(hostTarget): containerBaseName}
Expand Down Expand Up @@ -499,6 +442,52 @@ func copyToContainer(container string, containerPath string, hostPath string) er
return doCopy(hostCopy, containerCopy)
}

// resolvePathOnDestinationContainer resolves the specified path on the
// container. If the path does not exist, it attempts to use the parent
// directory.
func resolvePathOnDestinationContainer(container string, containerPath string, isStdin bool) (baseName string, containerInfo *entities.ContainerStatReport, resolvedToParentDir bool, err error) {
containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath)
if err == nil {
baseName = filepath.Base(containerInfo.LinkTarget)
return
}

if strings.HasSuffix(containerPath, "/") {
err = errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
return
}
if isStdin {
err = errors.New("destination must be a directory when copying from stdin")
return
}

// NOTE: containerInfo may actually be set. That happens when
// the container path is a symlink into nirvana. In that case,
// we must use the symlinked path instead.
path := containerPath
if containerInfo != nil {
baseName = filepath.Base(containerInfo.LinkTarget)
path = containerInfo.LinkTarget
} else {
baseName = filepath.Base(containerPath)
}

parentDir, err := containerParentDir(container, path)
if err != nil {
err = errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, container)
return
}

containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, parentDir)
if err != nil {
err = errors.Wrapf(err, "%q could not be found on container %s", containerPath, container)
return
}

resolvedToParentDir = true
return baseName, containerInfo, resolvedToParentDir, nil
}

// containerParentDir returns the parent directory of the specified path on the
// container. If the path is relative, it will be resolved relative to the
// container's working directory (or "/" if the work dir isn't set).
Expand Down

0 comments on commit 0132670

Please sign in to comment.