From 24d1290710f79aff53c482f492c3904e4d95ba31 Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Thu, 25 Aug 2022 08:12:29 +0100 Subject: [PATCH 1/5] libpod: Move container_stat_linux.go to container_stat_common.go [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson --- libpod/{container_stat_linux.go => container_stat_common.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libpod/{container_stat_linux.go => container_stat_common.go} (100%) diff --git a/libpod/container_stat_linux.go b/libpod/container_stat_common.go similarity index 100% rename from libpod/container_stat_linux.go rename to libpod/container_stat_common.go From 0b02d4cee6e3edd8a98ebe584f03169120ef5be4 Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Thu, 25 Aug 2022 08:12:52 +0100 Subject: [PATCH 2/5] libpod: Move container_copy_linux.go to container_copy_common.go [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson --- libpod/{container_copy_linux.go => container_copy_common.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename libpod/{container_copy_linux.go => container_copy_common.go} (100%) diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_common.go similarity index 100% rename from libpod/container_copy_linux.go rename to libpod/container_copy_common.go From 75d6e7bae5abb73fb248a6c4766ab799a030cb93 Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Thu, 25 Aug 2022 08:23:54 +0100 Subject: [PATCH 3/5] libpod: Move part of (*Container).stat to container_stat_linux.go The logic that treats running containers differently from stopped containers is not needed on FreeBSD where the container mounts live in a global mount namespace. [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson --- libpod/container_stat_common.go | 30 +------------------------- libpod/container_stat_linux.go | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 29 deletions(-) create mode 100644 libpod/container_stat_linux.go diff --git a/libpod/container_stat_common.go b/libpod/container_stat_common.go index dc3a524f50..4d6726946c 100644 --- a/libpod/container_stat_common.go +++ b/libpod/container_stat_common.go @@ -15,25 +15,6 @@ import ( "github.com/containers/podman/v4/pkg/copy" ) -// statInsideMount stats the specified path *inside* the container's mount and PID -// namespace. It returns the file info along with the resolved root ("/") and -// the resolved path (relative to the root). -func (c *Container) statInsideMount(containerPath string) (*copier.StatForItem, string, string, error) { - resolvedRoot := "/" - resolvedPath := c.pathAbs(containerPath) - var statInfo *copier.StatForItem - - err := c.joinMountAndExec( - func() error { - var statErr error - statInfo, statErr = secureStat(resolvedRoot, resolvedPath) - return statErr - }, - ) - - return statInfo, resolvedRoot, resolvedPath, err -} - // statOnHost stats the specified path *on the host*. It returns the file info // along with the resolved root and the resolved path. Both paths are absolute // to the host's root. Note that the paths may resolved outside the @@ -72,16 +53,7 @@ func (c *Container) stat(containerMountPoint string, containerPath string) (*def return nil, "", "", copy.ErrENOENT } - if c.state.State == define.ContainerStateRunning { - // If the container is running, we need to join it's mount namespace - // and stat there. - statInfo, resolvedRoot, resolvedPath, statErr = c.statInsideMount(containerPath) - } else { - // If the container is NOT running, we need to resolve the path - // on the host. - statInfo, resolvedRoot, resolvedPath, statErr = c.statOnHost(containerMountPoint, containerPath) - } - + statInfo, resolvedRoot, resolvedPath, statErr = c.statInContainer(containerMountPoint, containerPath) if statErr != nil { if statInfo == nil { return nil, "", "", statErr diff --git a/libpod/container_stat_linux.go b/libpod/container_stat_linux.go new file mode 100644 index 0000000000..5e5ef3c1af --- /dev/null +++ b/libpod/container_stat_linux.go @@ -0,0 +1,38 @@ +package libpod + +import ( + "github.com/containers/buildah/copier" + "github.com/containers/podman/v4/libpod/define" +) + +// statInsideMount stats the specified path *inside* the container's mount and PID +// namespace. It returns the file info along with the resolved root ("/") and +// the resolved path (relative to the root). +func (c *Container) statInsideMount(containerPath string) (*copier.StatForItem, string, string, error) { + resolvedRoot := "/" + resolvedPath := c.pathAbs(containerPath) + var statInfo *copier.StatForItem + + err := c.joinMountAndExec( + func() error { + var statErr error + statInfo, statErr = secureStat(resolvedRoot, resolvedPath) + return statErr + }, + ) + + return statInfo, resolvedRoot, resolvedPath, err +} + +// Calls either statOnHost or statInsideMount depending on whether the +// container is running +func (c *Container) statInContainer(mountPoint string, containerPath string) (*copier.StatForItem, string, string, error) { + if c.state.State == define.ContainerStateRunning { + // If the container is running, we need to join it's mount namespace + // and stat there. + return c.statInsideMount(containerPath) + } + // If the container is NOT running, we need to resolve the path + // on the host. + return c.statOnHost(mountPoint, containerPath) +} From 88f82ceab2b14b0523a56cd8c6b3a9cec8a323e2 Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Thu, 25 Aug 2022 08:47:42 +0100 Subject: [PATCH 4/5] libpod: Move jointMountAndExec to container_copy_linux.go This also moves the logic for resolving paths in running and stopped containers tp container_copy_linux.go. On FreeBSD, we can execute the function argument to joinMountAndExec directly using host-relative paths since the host mount namespace includes all the container mounts. [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson --- libpod/container_copy_common.go | 85 ++----------------------------- libpod/container_copy_linux.go | 88 +++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 81 deletions(-) create mode 100644 libpod/container_copy_linux.go diff --git a/libpod/container_copy_common.go b/libpod/container_copy_common.go index 557fead1ea..d09a8b17d5 100644 --- a/libpod/container_copy_common.go +++ b/libpod/container_copy_common.go @@ -5,11 +5,8 @@ package libpod import ( "errors" - "fmt" "io" - "os" "path/filepath" - "runtime" "strings" buildahCopiah "github.com/containers/buildah/copier" @@ -21,7 +18,6 @@ import ( "github.com/containers/storage/pkg/idtools" "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" ) func (c *Container) copyFromArchive(path string, chown, noOverwriteDirNonDir bool, rename map[string]string, reader io.Reader) (func() error, error) { @@ -56,15 +52,10 @@ func (c *Container) copyFromArchive(path string, chown, noOverwriteDirNonDir boo } } - if c.state.State == define.ContainerStateRunning { - resolvedRoot = "/" - resolvedPath = c.pathAbs(path) - } else { - resolvedRoot, resolvedPath, err = c.resolvePath(mountPoint, path) - if err != nil { - unmount() - return nil, err - } + resolvedRoot, resolvedPath, err = c.resolveCopyTarget(mountPoint, path) + if err != nil { + unmount() + return nil, err } var idPair *idtools.IDPair @@ -220,71 +211,3 @@ func idtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxI } return convertedIDMap } - -// joinMountAndExec executes the specified function `f` inside the container's -// mount and PID namespace. That allows for having the exact view on the -// container's file system. -// -// Note, if the container is not running `f()` will be executed as is. -func (c *Container) joinMountAndExec(f func() error) error { - if c.state.State != define.ContainerStateRunning { - return f() - } - - // Container's running, so we need to execute `f()` inside its mount NS. - errChan := make(chan error) - go func() { - runtime.LockOSThread() - - // Join the mount and PID NS of the container. - getFD := func(ns LinuxNS) (*os.File, error) { - nsPath, err := c.namespacePath(ns) - if err != nil { - return nil, err - } - return os.Open(nsPath) - } - - mountFD, err := getFD(MountNS) - if err != nil { - errChan <- err - return - } - defer mountFD.Close() - - inHostPidNS, err := c.inHostPidNS() - if err != nil { - errChan <- fmt.Errorf("checking inHostPidNS: %w", err) - return - } - var pidFD *os.File - if !inHostPidNS { - pidFD, err = getFD(PIDNS) - if err != nil { - errChan <- err - return - } - defer pidFD.Close() - } - - if err := unix.Unshare(unix.CLONE_NEWNS); err != nil { - errChan <- err - return - } - - if pidFD != nil { - if err := unix.Setns(int(pidFD.Fd()), unix.CLONE_NEWPID); err != nil { - errChan <- err - return - } - } - if err := unix.Setns(int(mountFD.Fd()), unix.CLONE_NEWNS); err != nil { - errChan <- err - return - } - - // Last but not least, execute the workload. - errChan <- f() - }() - return <-errChan -} diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go new file mode 100644 index 0000000000..3b029f08f6 --- /dev/null +++ b/libpod/container_copy_linux.go @@ -0,0 +1,88 @@ +package libpod + +import ( + "fmt" + "os" + "runtime" + + "github.com/containers/podman/v4/libpod/define" + "golang.org/x/sys/unix" +) + +// joinMountAndExec executes the specified function `f` inside the container's +// mount and PID namespace. That allows for having the exact view on the +// container's file system. +// +// Note, if the container is not running `f()` will be executed as is. +func (c *Container) joinMountAndExec(f func() error) error { + if c.state.State != define.ContainerStateRunning { + return f() + } + + // Container's running, so we need to execute `f()` inside its mount NS. + errChan := make(chan error) + go func() { + runtime.LockOSThread() + + // Join the mount and PID NS of the container. + getFD := func(ns LinuxNS) (*os.File, error) { + nsPath, err := c.namespacePath(ns) + if err != nil { + return nil, err + } + return os.Open(nsPath) + } + + mountFD, err := getFD(MountNS) + if err != nil { + errChan <- err + return + } + defer mountFD.Close() + + inHostPidNS, err := c.inHostPidNS() + if err != nil { + errChan <- fmt.Errorf("checking inHostPidNS: %w", err) + return + } + var pidFD *os.File + if !inHostPidNS { + pidFD, err = getFD(PIDNS) + if err != nil { + errChan <- err + return + } + defer pidFD.Close() + } + + if err := unix.Unshare(unix.CLONE_NEWNS); err != nil { + errChan <- err + return + } + + if pidFD != nil { + if err := unix.Setns(int(pidFD.Fd()), unix.CLONE_NEWPID); err != nil { + errChan <- err + return + } + } + if err := unix.Setns(int(mountFD.Fd()), unix.CLONE_NEWNS); err != nil { + errChan <- err + return + } + + // Last but not least, execute the workload. + errChan <- f() + }() + return <-errChan +} + +func (c *Container) resolveCopyTarget(mountPoint string, containerPath string) (string, string, error) { + // If the container is running, we will execute the copy + // inside the container's mount namespace so we return a path + // relative to the container's root. + if c.state.State == define.ContainerStateRunning { + return "/", c.pathAbs(containerPath), nil + } + return c.resolvePath(mountPoint, containerPath) +} From bb160be12be137116c2ee2b5e448ec8756f2ed64 Mon Sep 17 00:00:00 2001 From: Doug Rabson Date: Thu, 25 Aug 2022 08:24:20 +0100 Subject: [PATCH 5/5] libpod: Implement 'podman cp' for FreeBSD [NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson --- libpod/container_copy_common.go | 4 ++-- libpod/container_copy_freebsd.go | 13 +++++++++++++ libpod/container_copy_unsupported.go | 4 ++-- libpod/container_stat_common.go | 4 ++-- libpod/container_stat_freebsd.go | 13 +++++++++++++ libpod/container_stat_unsupported.go | 4 ++-- 6 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 libpod/container_copy_freebsd.go create mode 100644 libpod/container_stat_freebsd.go diff --git a/libpod/container_copy_common.go b/libpod/container_copy_common.go index d09a8b17d5..d07b4c6928 100644 --- a/libpod/container_copy_common.go +++ b/libpod/container_copy_common.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package libpod diff --git a/libpod/container_copy_freebsd.go b/libpod/container_copy_freebsd.go new file mode 100644 index 0000000000..218f3917f7 --- /dev/null +++ b/libpod/container_copy_freebsd.go @@ -0,0 +1,13 @@ +package libpod + +// On FreeBSD, the container's mounts are in the global mount +// namespace so we can just execute the function directly. +func (c *Container) joinMountAndExec(f func() error) error { + return f() +} + +// Similarly, we can just use resolvePath for both running and stopped +// containers. +func (c *Container) resolveCopyTarget(mountPoint string, containerPath string) (string, string, error) { + return c.resolvePath(mountPoint, containerPath) +} diff --git a/libpod/container_copy_unsupported.go b/libpod/container_copy_unsupported.go index 62937279a2..703b0a74ed 100644 --- a/libpod/container_copy_unsupported.go +++ b/libpod/container_copy_unsupported.go @@ -1,5 +1,5 @@ -//go:build !linux -// +build !linux +//go:build !linux && !freebsd +// +build !linux,!freebsd package libpod diff --git a/libpod/container_stat_common.go b/libpod/container_stat_common.go index 4d6726946c..e59a52eded 100644 --- a/libpod/container_stat_common.go +++ b/libpod/container_stat_common.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || freebsd +// +build linux freebsd package libpod diff --git a/libpod/container_stat_freebsd.go b/libpod/container_stat_freebsd.go new file mode 100644 index 0000000000..d1e0db3482 --- /dev/null +++ b/libpod/container_stat_freebsd.go @@ -0,0 +1,13 @@ +package libpod + +import ( + "github.com/containers/buildah/copier" +) + +// On FreeBSD, jails use the global mount namespace, filtered to only +// the mounts the jail should see. This means that we can use +// statOnHost whether the container is running or not. +// container is running +func (c *Container) statInContainer(mountPoint string, containerPath string) (*copier.StatForItem, string, string, error) { + return c.statOnHost(mountPoint, containerPath) +} diff --git a/libpod/container_stat_unsupported.go b/libpod/container_stat_unsupported.go index 2f1acd44d3..e88b88bb1f 100644 --- a/libpod/container_stat_unsupported.go +++ b/libpod/container_stat_unsupported.go @@ -1,5 +1,5 @@ -//go:build !linux -// +build !linux +//go:build !linux && !freebsd +// +build !linux,!freebsd package libpod