diff options
author | Valentin Rothberg <rothberg@redhat.com> | 2021-03-02 09:20:53 +0100 |
---|---|---|
committer | Valentin Rothberg <rothberg@redhat.com> | 2021-03-04 15:43:12 +0100 |
commit | a090301bbb10424ce4f99e40c97959f0e8664718 (patch) | |
tree | 3b2596e3d152204d35162b1ca89f524c5803ad8c /libpod/container_api.go | |
parent | 833670079c5b1f95fbb7c9bb8ba9095f1c66c7b4 (diff) | |
download | podman-a090301bbb10424ce4f99e40c97959f0e8664718.tar.gz podman-a090301bbb10424ce4f99e40c97959f0e8664718.tar.bz2 podman-a090301bbb10424ce4f99e40c97959f0e8664718.zip |
podman cp: support copying on tmpfs mounts
Traditionally, the path resolution for containers has been resolved on
the *host*; relative to the container's mount point or relative to
specified bind mounts or volumes.
While this works nicely for non-running containers, it poses a problem
for running ones. In that case, certain kinds of mounts (e.g., tmpfs)
will not resolve correctly. A tmpfs is held in memory and hence cannot
be resolved relatively to the container's mount point. A copy operation
will succeed but the data will not show up inside the container.
To support these kinds of mounts, we need to join the *running*
container's mount namespace (and PID namespace) when copying.
Note that this change implies moving the copy and stat logic into
`libpod` since we need to keep the container locked to avoid race
conditions. The immediate benefit is that all logic is now inside
`libpod`; the code isn't scattered anymore.
Further note that Docker does not support copying to tmpfs mounts.
Tests have been extended to cover *both* path resolutions for running
and created containers. New tests have been added to exercise the
tmpfs-mount case.
For the record: Some tests could be improved by using `start -a` instead
of a start-exec sequence. Unfortunately, `start -a` is flaky in the CI
which forced me to use the more expensive start-exec option.
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
Diffstat (limited to 'libpod/container_api.go')
-rw-r--r-- | libpod/container_api.go | 64 |
1 files changed, 44 insertions, 20 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go index 2818ac841..6fa8b27cd 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -2,6 +2,7 @@ package libpod import ( "context" + "io" "io/ioutil" "net/http" "os" @@ -349,10 +350,6 @@ func (c *Container) Mount() (string, error) { } } - if c.state.State == define.ContainerStateRemoving { - return "", errors.Wrapf(define.ErrCtrStateInvalid, "cannot mount container %s as it is being removed", c.ID()) - } - defer c.newContainerEvent(events.Mount) return c.mount() } @@ -367,7 +364,6 @@ func (c *Container) Unmount(force bool) error { return err } } - if c.state.Mounted { mounted, err := c.runtime.storageService.MountedContainerImage(c.ID()) if err != nil { @@ -847,31 +843,59 @@ func (c *Container) ShouldRestart(ctx context.Context) bool { return c.shouldRestart() } -// ResolvePath resolves the specified path on the root for the container. The -// root must either be the mounted image of the container or the already -// mounted container storage. -// -// It returns the resolved root and the resolved path. Note that the path may -// resolve to the container's mount point or to a volume or bind mount. -func (c *Container) ResolvePath(ctx context.Context, root string, path string) (string, string, error) { - logrus.Debugf("Resolving path %q (root %q) on container %s", path, root, c.ID()) +// CopyFromArchive copies the contents from the specified tarStream to path +// *inside* the container. +func (c *Container) CopyFromArchive(ctx context.Context, containerPath string, tarStream io.Reader) (func() error, error) { + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() - // Minimal sanity checks. - if len(root)*len(path) == 0 { - return "", "", errors.Wrapf(define.ErrInternal, "ResolvePath: root (%q) and path (%q) must be non empty", root, path) + if err := c.syncContainer(); err != nil { + return nil, err + } } - if _, err := os.Stat(root); err != nil { - return "", "", errors.Wrapf(err, "cannot locate root to resolve path on container %s", c.ID()) + + return c.copyFromArchive(ctx, containerPath, tarStream) +} + +// CopyToArchive copies the contents from the specified path *inside* the +// container to the tarStream. +func (c *Container) CopyToArchive(ctx context.Context, containerPath string, tarStream io.Writer) (func() error, error) { + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + + if err := c.syncContainer(); err != nil { + return nil, err + } } + return c.copyToArchive(ctx, containerPath, tarStream) +} + +// Stat the specified path *inside* the container and return a file info. +func (c *Container) Stat(ctx context.Context, containerPath string) (*define.FileInfo, error) { if !c.batched { c.lock.Lock() defer c.lock.Unlock() if err := c.syncContainer(); err != nil { - return "", "", err + return nil, err + } + } + + var mountPoint string + var err error + if c.state.Mounted { + mountPoint = c.state.Mountpoint + } else { + mountPoint, err = c.mount() + if err != nil { + return nil, err } + defer c.unmount(false) } - return c.resolvePath(root, path) + info, _, _, err := c.stat(ctx, mountPoint, containerPath) + return info, err } |