diff options
Diffstat (limited to 'cmd/podman/containers/cp.go')
-rw-r--r-- | cmd/podman/containers/cp.go | 135 |
1 files changed, 62 insertions, 73 deletions
diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go index 67bb13aa1..7b5846a35 100644 --- a/cmd/podman/containers/cp.go +++ b/cmd/podman/containers/cp.go @@ -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) } @@ -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) } @@ -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} @@ -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, "/") { @@ -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. @@ -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) } @@ -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} @@ -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 @@ -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) } @@ -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} @@ -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). |