libpod: Move isWorkDirSymlink, resolveWorkDir to container_internal_common.go
[NO NEW TESTS NEEDED] Signed-off-by: Doug Rabson <dfr@rabson.org>
This commit is contained in:
parent
a5ee3cb790
commit
232eea5a00
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
cdi "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
|
cdi "github.com/container-orchestrated-devices/container-device-interface/pkg/cdi"
|
||||||
|
"github.com/containers/buildah/pkg/chrootuser"
|
||||||
"github.com/containers/buildah/pkg/overlay"
|
"github.com/containers/buildah/pkg/overlay"
|
||||||
butil "github.com/containers/buildah/util"
|
butil "github.com/containers/buildah/util"
|
||||||
"github.com/containers/common/pkg/apparmor"
|
"github.com/containers/common/pkg/apparmor"
|
||||||
|
|
@ -480,3 +481,115 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
|
||||||
|
|
||||||
return g.Config, nil
|
return g.Config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isWorkDirSymlink returns true if resolved workdir is symlink or a chain of symlinks,
|
||||||
|
// and final resolved target is present either on volume, mount or inside of container
|
||||||
|
// otherwise it returns false. Following function is meant for internal use only and
|
||||||
|
// can change at any point of time.
|
||||||
|
func (c *Container) isWorkDirSymlink(resolvedPath string) bool {
|
||||||
|
// We cannot create workdir since explicit --workdir is
|
||||||
|
// set in config but workdir could also be a symlink.
|
||||||
|
// If it's a symlink, check if the resolved target is present in the container.
|
||||||
|
// If so, that's a valid use case: return nil.
|
||||||
|
|
||||||
|
maxSymLinks := 0
|
||||||
|
for {
|
||||||
|
// Linux only supports a chain of 40 links.
|
||||||
|
// Reference: https://github.com/torvalds/linux/blob/master/include/linux/namei.h#L13
|
||||||
|
if maxSymLinks > 40 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
resolvedSymlink, err := os.Readlink(resolvedPath)
|
||||||
|
if err != nil {
|
||||||
|
// End sym-link resolution loop.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if resolvedSymlink != "" {
|
||||||
|
_, resolvedSymlinkWorkdir, err := c.resolvePath(c.state.Mountpoint, resolvedSymlink)
|
||||||
|
if isPathOnVolume(c, resolvedSymlinkWorkdir) || isPathOnBindMount(c, resolvedSymlinkWorkdir) {
|
||||||
|
// Resolved symlink exists on external volume or mount
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
// Could not resolve path so end sym-link resolution loop.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if resolvedSymlinkWorkdir != "" {
|
||||||
|
resolvedPath = resolvedSymlinkWorkdir
|
||||||
|
_, err := os.Stat(resolvedSymlinkWorkdir)
|
||||||
|
if err == nil {
|
||||||
|
// Symlink resolved successfully and resolved path exists on container,
|
||||||
|
// this is a valid use-case so return nil.
|
||||||
|
logrus.Debugf("Workdir is a symlink with target to %q and resolved symlink exists on container", resolvedSymlink)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maxSymLinks++
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveWorkDir resolves the container's workdir and, depending on the
|
||||||
|
// configuration, will create it, or error out if it does not exist.
|
||||||
|
// Note that the container must be mounted before.
|
||||||
|
func (c *Container) resolveWorkDir() error {
|
||||||
|
workdir := c.WorkingDir()
|
||||||
|
|
||||||
|
// If the specified workdir is a subdir of a volume or mount,
|
||||||
|
// we don't need to do anything. The runtime is taking care of
|
||||||
|
// that.
|
||||||
|
if isPathOnVolume(c, workdir) || isPathOnBindMount(c, workdir) {
|
||||||
|
logrus.Debugf("Workdir %q resolved to a volume or mount", workdir)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, resolvedWorkdir, err := c.resolvePath(c.state.Mountpoint, workdir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logrus.Debugf("Workdir %q resolved to host path %q", workdir, resolvedWorkdir)
|
||||||
|
|
||||||
|
st, err := os.Stat(resolvedWorkdir)
|
||||||
|
if err == nil {
|
||||||
|
if !st.IsDir() {
|
||||||
|
return fmt.Errorf("workdir %q exists on container %s, but is not a directory", workdir, c.ID())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !c.config.CreateWorkingDir {
|
||||||
|
// No need to create it (e.g., `--workdir=/foo`), so let's make sure
|
||||||
|
// the path exists on the container.
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
// If resolved Workdir path gets marked as a valid symlink,
|
||||||
|
// return nil cause this is valid use-case.
|
||||||
|
if c.isWorkDirSymlink(resolvedWorkdir) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("workdir %q does not exist on container %s", workdir, c.ID())
|
||||||
|
}
|
||||||
|
// This might be a serious error (e.g., permission), so
|
||||||
|
// we need to return the full error.
|
||||||
|
return fmt.Errorf("error detecting workdir %q on container %s: %w", workdir, c.ID(), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(resolvedWorkdir, 0755); err != nil {
|
||||||
|
if os.IsExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error creating container %s workdir: %w", c.ID(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure container entrypoint is created (if required).
|
||||||
|
uid, gid, _, err := chrootuser.GetUser(c.state.Mountpoint, c.User())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error looking up %s inside of the container %s: %w", c.User(), c.ID(), err)
|
||||||
|
}
|
||||||
|
if err := os.Chown(resolvedWorkdir, int(uid), int(gid)); err != nil {
|
||||||
|
return fmt.Errorf("error chowning container %s workdir to container root: %w", c.ID(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import (
|
||||||
|
|
||||||
metadata "github.com/checkpoint-restore/checkpointctl/lib"
|
metadata "github.com/checkpoint-restore/checkpointctl/lib"
|
||||||
"github.com/checkpoint-restore/go-criu/v5/stats"
|
"github.com/checkpoint-restore/go-criu/v5/stats"
|
||||||
"github.com/containers/buildah/pkg/chrootuser"
|
|
||||||
"github.com/containers/buildah/pkg/overlay"
|
"github.com/containers/buildah/pkg/overlay"
|
||||||
"github.com/containers/common/libnetwork/etchosts"
|
"github.com/containers/common/libnetwork/etchosts"
|
||||||
"github.com/containers/common/libnetwork/resolvconf"
|
"github.com/containers/common/libnetwork/resolvconf"
|
||||||
|
|
@ -150,118 +149,6 @@ func (c *Container) prepare() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isWorkDirSymlink returns true if resolved workdir is symlink or a chain of symlinks,
|
|
||||||
// and final resolved target is present either on volume, mount or inside of container
|
|
||||||
// otherwise it returns false. Following function is meant for internal use only and
|
|
||||||
// can change at any point of time.
|
|
||||||
func (c *Container) isWorkDirSymlink(resolvedPath string) bool {
|
|
||||||
// We cannot create workdir since explicit --workdir is
|
|
||||||
// set in config but workdir could also be a symlink.
|
|
||||||
// If it's a symlink, check if the resolved target is present in the container.
|
|
||||||
// If so, that's a valid use case: return nil.
|
|
||||||
|
|
||||||
maxSymLinks := 0
|
|
||||||
for {
|
|
||||||
// Linux only supports a chain of 40 links.
|
|
||||||
// Reference: https://github.com/torvalds/linux/blob/master/include/linux/namei.h#L13
|
|
||||||
if maxSymLinks > 40 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
resolvedSymlink, err := os.Readlink(resolvedPath)
|
|
||||||
if err != nil {
|
|
||||||
// End sym-link resolution loop.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if resolvedSymlink != "" {
|
|
||||||
_, resolvedSymlinkWorkdir, err := c.resolvePath(c.state.Mountpoint, resolvedSymlink)
|
|
||||||
if isPathOnVolume(c, resolvedSymlinkWorkdir) || isPathOnBindMount(c, resolvedSymlinkWorkdir) {
|
|
||||||
// Resolved symlink exists on external volume or mount
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// Could not resolve path so end sym-link resolution loop.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if resolvedSymlinkWorkdir != "" {
|
|
||||||
resolvedPath = resolvedSymlinkWorkdir
|
|
||||||
_, err := os.Stat(resolvedSymlinkWorkdir)
|
|
||||||
if err == nil {
|
|
||||||
// Symlink resolved successfully and resolved path exists on container,
|
|
||||||
// this is a valid use-case so return nil.
|
|
||||||
logrus.Debugf("Workdir is a symlink with target to %q and resolved symlink exists on container", resolvedSymlink)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
maxSymLinks++
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolveWorkDir resolves the container's workdir and, depending on the
|
|
||||||
// configuration, will create it, or error out if it does not exist.
|
|
||||||
// Note that the container must be mounted before.
|
|
||||||
func (c *Container) resolveWorkDir() error {
|
|
||||||
workdir := c.WorkingDir()
|
|
||||||
|
|
||||||
// If the specified workdir is a subdir of a volume or mount,
|
|
||||||
// we don't need to do anything. The runtime is taking care of
|
|
||||||
// that.
|
|
||||||
if isPathOnVolume(c, workdir) || isPathOnBindMount(c, workdir) {
|
|
||||||
logrus.Debugf("Workdir %q resolved to a volume or mount", workdir)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
_, resolvedWorkdir, err := c.resolvePath(c.state.Mountpoint, workdir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logrus.Debugf("Workdir %q resolved to host path %q", workdir, resolvedWorkdir)
|
|
||||||
|
|
||||||
st, err := os.Stat(resolvedWorkdir)
|
|
||||||
if err == nil {
|
|
||||||
if !st.IsDir() {
|
|
||||||
return fmt.Errorf("workdir %q exists on container %s, but is not a directory", workdir, c.ID())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !c.config.CreateWorkingDir {
|
|
||||||
// No need to create it (e.g., `--workdir=/foo`), so let's make sure
|
|
||||||
// the path exists on the container.
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
// If resolved Workdir path gets marked as a valid symlink,
|
|
||||||
// return nil cause this is valid use-case.
|
|
||||||
if c.isWorkDirSymlink(resolvedWorkdir) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("workdir %q does not exist on container %s", workdir, c.ID())
|
|
||||||
}
|
|
||||||
// This might be a serious error (e.g., permission), so
|
|
||||||
// we need to return the full error.
|
|
||||||
return fmt.Errorf("error detecting workdir %q on container %s: %w", workdir, c.ID(), err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(resolvedWorkdir, 0755); err != nil {
|
|
||||||
if os.IsExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("error creating container %s workdir: %w", c.ID(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure container entrypoint is created (if required).
|
|
||||||
uid, gid, _, err := chrootuser.GetUser(c.state.Mountpoint, c.User())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error looking up %s inside of the container %s: %w", c.User(), c.ID(), err)
|
|
||||||
}
|
|
||||||
if err := os.Chown(resolvedWorkdir, int(uid), int(gid)); err != nil {
|
|
||||||
return fmt.Errorf("error chowning container %s workdir to container root: %w", c.ID(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanupNetwork unmounts and cleans up the container's network
|
// cleanupNetwork unmounts and cleans up the container's network
|
||||||
func (c *Container) cleanupNetwork() error {
|
func (c *Container) cleanupNetwork() error {
|
||||||
if c.config.NetNsCtr != "" {
|
if c.config.NetNsCtr != "" {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"github.com/checkpoint-restore/go-criu/v5/stats"
|
"github.com/checkpoint-restore/go-criu/v5/stats"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containers/buildah"
|
"github.com/containers/buildah"
|
||||||
"github.com/containers/buildah/pkg/chrootuser"
|
|
||||||
"github.com/containers/buildah/pkg/overlay"
|
"github.com/containers/buildah/pkg/overlay"
|
||||||
"github.com/containers/common/libnetwork/etchosts"
|
"github.com/containers/common/libnetwork/etchosts"
|
||||||
"github.com/containers/common/libnetwork/resolvconf"
|
"github.com/containers/common/libnetwork/resolvconf"
|
||||||
|
|
@ -175,118 +174,6 @@ func (c *Container) prepare() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isWorkDirSymlink returns true if resolved workdir is symlink or a chain of symlinks,
|
|
||||||
// and final resolved target is present either on volume, mount or inside of container
|
|
||||||
// otherwise it returns false. Following function is meant for internal use only and
|
|
||||||
// can change at any point of time.
|
|
||||||
func (c *Container) isWorkDirSymlink(resolvedPath string) bool {
|
|
||||||
// We cannot create workdir since explicit --workdir is
|
|
||||||
// set in config but workdir could also be a symlink.
|
|
||||||
// If it's a symlink, check if the resolved target is present in the container.
|
|
||||||
// If so, that's a valid use case: return nil.
|
|
||||||
|
|
||||||
maxSymLinks := 0
|
|
||||||
for {
|
|
||||||
// Linux only supports a chain of 40 links.
|
|
||||||
// Reference: https://github.com/torvalds/linux/blob/master/include/linux/namei.h#L13
|
|
||||||
if maxSymLinks > 40 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
resolvedSymlink, err := os.Readlink(resolvedPath)
|
|
||||||
if err != nil {
|
|
||||||
// End sym-link resolution loop.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if resolvedSymlink != "" {
|
|
||||||
_, resolvedSymlinkWorkdir, err := c.resolvePath(c.state.Mountpoint, resolvedSymlink)
|
|
||||||
if isPathOnVolume(c, resolvedSymlinkWorkdir) || isPathOnBindMount(c, resolvedSymlinkWorkdir) {
|
|
||||||
// Resolved symlink exists on external volume or mount
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// Could not resolve path so end sym-link resolution loop.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if resolvedSymlinkWorkdir != "" {
|
|
||||||
resolvedPath = resolvedSymlinkWorkdir
|
|
||||||
_, err := os.Stat(resolvedSymlinkWorkdir)
|
|
||||||
if err == nil {
|
|
||||||
// Symlink resolved successfully and resolved path exists on container,
|
|
||||||
// this is a valid use-case so return nil.
|
|
||||||
logrus.Debugf("Workdir is a symlink with target to %q and resolved symlink exists on container", resolvedSymlink)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
maxSymLinks++
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolveWorkDir resolves the container's workdir and, depending on the
|
|
||||||
// configuration, will create it, or error out if it does not exist.
|
|
||||||
// Note that the container must be mounted before.
|
|
||||||
func (c *Container) resolveWorkDir() error {
|
|
||||||
workdir := c.WorkingDir()
|
|
||||||
|
|
||||||
// If the specified workdir is a subdir of a volume or mount,
|
|
||||||
// we don't need to do anything. The runtime is taking care of
|
|
||||||
// that.
|
|
||||||
if isPathOnVolume(c, workdir) || isPathOnBindMount(c, workdir) {
|
|
||||||
logrus.Debugf("Workdir %q resolved to a volume or mount", workdir)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
_, resolvedWorkdir, err := c.resolvePath(c.state.Mountpoint, workdir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logrus.Debugf("Workdir %q resolved to host path %q", workdir, resolvedWorkdir)
|
|
||||||
|
|
||||||
st, err := os.Stat(resolvedWorkdir)
|
|
||||||
if err == nil {
|
|
||||||
if !st.IsDir() {
|
|
||||||
return fmt.Errorf("workdir %q exists on container %s, but is not a directory", workdir, c.ID())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if !c.config.CreateWorkingDir {
|
|
||||||
// No need to create it (e.g., `--workdir=/foo`), so let's make sure
|
|
||||||
// the path exists on the container.
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
// If resolved Workdir path gets marked as a valid symlink,
|
|
||||||
// return nil cause this is valid use-case.
|
|
||||||
if c.isWorkDirSymlink(resolvedWorkdir) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("workdir %q does not exist on container %s", workdir, c.ID())
|
|
||||||
}
|
|
||||||
// This might be a serious error (e.g., permission), so
|
|
||||||
// we need to return the full error.
|
|
||||||
return fmt.Errorf("error detecting workdir %q on container %s: %w", workdir, c.ID(), err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := os.MkdirAll(resolvedWorkdir, 0755); err != nil {
|
|
||||||
if os.IsExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("error creating container %s workdir: %w", c.ID(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure container entrypoint is created (if required).
|
|
||||||
uid, gid, _, err := chrootuser.GetUser(c.state.Mountpoint, c.User())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error looking up %s inside of the container %s: %w", c.User(), c.ID(), err)
|
|
||||||
}
|
|
||||||
if err := os.Chown(resolvedWorkdir, int(uid), int(gid)); err != nil {
|
|
||||||
return fmt.Errorf("error chowning container %s workdir to container root: %w", c.ID(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanupNetwork unmounts and cleans up the container's network
|
// cleanupNetwork unmounts and cleans up the container's network
|
||||||
func (c *Container) cleanupNetwork() error {
|
func (c *Container) cleanupNetwork() error {
|
||||||
if c.config.NetNsCtr != "" {
|
if c.config.NetNsCtr != "" {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue