mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-19 07:14:22 +01:00
virtcontainers: split out Linux parts from mount
Mount handling is often unique in Linux. Let's ensure that the common parts remain in mount.go, while Linux speific parts are within a linux file. Fixes: #6049 Signed-off-by: Eric Ernst <eric_ernst@apple.com>
This commit is contained in:
@@ -6,29 +6,22 @@
|
||||
package virtcontainers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
merr "github.com/hashicorp/go-multierror"
|
||||
volume "github.com/kata-containers/kata-containers/src/runtime/pkg/direct-volume"
|
||||
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils/katatrace"
|
||||
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/utils"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
otelLabel "go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
// DefaultShmSize is the default shm size to be used in case host
|
||||
// IPC is used.
|
||||
const DefaultShmSize = 65536 * 1024
|
||||
|
||||
// Sadly golang/sys doesn't have UmountNoFollow although it's there since Linux 2.6.34
|
||||
const UmountNoFollow = 0x8
|
||||
|
||||
const (
|
||||
rootfsDir = "rootfs"
|
||||
lowerDir = "lowerdir"
|
||||
@@ -50,13 +43,6 @@ func mountLogger() *logrus.Entry {
|
||||
return virtLog.WithField("subsystem", "mount")
|
||||
}
|
||||
|
||||
var propagationTypes = map[string]uintptr{
|
||||
"shared": syscall.MS_SHARED,
|
||||
"private": syscall.MS_PRIVATE,
|
||||
"slave": syscall.MS_SLAVE,
|
||||
"ubind": syscall.MS_UNBINDABLE,
|
||||
}
|
||||
|
||||
func isSystemMount(m string) bool {
|
||||
for _, p := range systemMountPrefixes {
|
||||
if m == p || strings.HasPrefix(m, p+"/") {
|
||||
@@ -248,83 +234,6 @@ func evalMountPath(source, destination string) (string, string, error) {
|
||||
return absSource, destination, nil
|
||||
}
|
||||
|
||||
// bindMount bind mounts a source in to a destination. This will
|
||||
// do some bookkeeping:
|
||||
// * evaluate all symlinks
|
||||
// * ensure the source exists
|
||||
// * recursively create the destination
|
||||
// pgtypes stands for propagation types, which are shared, private, slave, and ubind.
|
||||
func bindMount(ctx context.Context, source, destination string, readonly bool, pgtypes string) error {
|
||||
span, _ := katatrace.Trace(ctx, nil, "bindMount", mountTracingTags)
|
||||
defer span.End()
|
||||
span.SetAttributes(otelLabel.String("source", source), otelLabel.String("destination", destination))
|
||||
|
||||
absSource, destination, err := evalMountPath(source, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
span.SetAttributes(otelLabel.String("source_after_eval", absSource))
|
||||
|
||||
if err := syscall.Mount(absSource, destination, "bind", syscall.MS_BIND, ""); err != nil {
|
||||
return fmt.Errorf("Could not bind mount %v to %v: %v", absSource, destination, err)
|
||||
}
|
||||
|
||||
if pgtype, exist := propagationTypes[pgtypes]; exist {
|
||||
if err := syscall.Mount("none", destination, "", pgtype, ""); err != nil {
|
||||
return fmt.Errorf("Could not make mount point %v %s: %v", destination, pgtypes, err)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Wrong propagation type %s", pgtypes)
|
||||
}
|
||||
|
||||
// For readonly bind mounts, we need to remount with the readonly flag.
|
||||
// This is needed as only very recent versions of libmount/util-linux support "bind,ro"
|
||||
if readonly {
|
||||
return syscall.Mount(absSource, destination, "bind", uintptr(syscall.MS_BIND|syscall.MS_REMOUNT|syscall.MS_RDONLY), "")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// An existing mount may be remounted by specifying `MS_REMOUNT` in
|
||||
// mountflags.
|
||||
// This allows you to change the mountflags of an existing mount.
|
||||
// The mountflags should match the values used in the original mount() call,
|
||||
// except for those parameters that you are trying to change.
|
||||
func remount(ctx context.Context, mountflags uintptr, src string) error {
|
||||
span, _ := katatrace.Trace(ctx, nil, "remount", mountTracingTags)
|
||||
defer span.End()
|
||||
span.SetAttributes(otelLabel.String("source", src))
|
||||
|
||||
absSrc, err := filepath.EvalSymlinks(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not resolve symlink for %s", src)
|
||||
}
|
||||
span.SetAttributes(otelLabel.String("source_after_eval", absSrc))
|
||||
|
||||
if err := syscall.Mount(absSrc, absSrc, "", syscall.MS_REMOUNT|mountflags, ""); err != nil {
|
||||
return fmt.Errorf("remount %s failed: %v", absSrc, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// remount a mount point as readonly
|
||||
func remountRo(ctx context.Context, src string) error {
|
||||
return remount(ctx, syscall.MS_BIND|syscall.MS_RDONLY, src)
|
||||
}
|
||||
|
||||
// bindMountContainerRootfs bind mounts a container rootfs into a 9pfs shared
|
||||
// directory between the guest and the host.
|
||||
func bindMountContainerRootfs(ctx context.Context, shareDir, cid, cRootFs string, readonly bool) error {
|
||||
span, _ := katatrace.Trace(ctx, nil, "bindMountContainerRootfs", mountTracingTags)
|
||||
defer span.End()
|
||||
|
||||
rootfsDest := filepath.Join(shareDir, cid, rootfsDir)
|
||||
|
||||
return bindMount(ctx, cRootFs, rootfsDest, readonly, "private")
|
||||
}
|
||||
|
||||
// Mount describes a container mount.
|
||||
// nolint: govet
|
||||
type Mount struct {
|
||||
@@ -372,96 +281,6 @@ func isSymlink(path string) bool {
|
||||
return stat.Mode()&os.ModeSymlink != 0
|
||||
}
|
||||
|
||||
func bindUnmountContainerShareDir(ctx context.Context, sharedDir, cID, target string) error {
|
||||
destDir := filepath.Join(sharedDir, cID, target)
|
||||
if isSymlink(filepath.Join(sharedDir, cID)) || isSymlink(destDir) {
|
||||
mountLogger().WithField("container", cID).Warnf("container dir is a symlink, malicious guest?")
|
||||
return nil
|
||||
}
|
||||
|
||||
err := syscall.Unmount(destDir, syscall.MNT_DETACH|UmountNoFollow)
|
||||
if err == syscall.ENOENT {
|
||||
mountLogger().WithError(err).WithField("share-dir", destDir).Warn()
|
||||
return nil
|
||||
}
|
||||
if err := syscall.Rmdir(destDir); err != nil {
|
||||
mountLogger().WithError(err).WithField("share-dir", destDir).Warn("Could not remove container share dir")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func bindUnmountContainerRootfs(ctx context.Context, sharedDir, cID string) error {
|
||||
span, _ := katatrace.Trace(ctx, nil, "bindUnmountContainerRootfs", mountTracingTags)
|
||||
defer span.End()
|
||||
span.SetAttributes(otelLabel.String("shared-dir", sharedDir), otelLabel.String("container-id", cID))
|
||||
return bindUnmountContainerShareDir(ctx, sharedDir, cID, rootfsDir)
|
||||
}
|
||||
|
||||
func bindUnmountContainerSnapshotDir(ctx context.Context, sharedDir, cID string) error {
|
||||
span, _ := katatrace.Trace(ctx, nil, "bindUnmountContainerSnapshotDir", mountTracingTags)
|
||||
defer span.End()
|
||||
span.SetAttributes(otelLabel.String("shared-dir", sharedDir), otelLabel.String("container-id", cID))
|
||||
return bindUnmountContainerShareDir(ctx, sharedDir, cID, snapshotDir)
|
||||
}
|
||||
|
||||
func getVirtiofsDaemonForNydus(sandbox *Sandbox) (VirtiofsDaemon, error) {
|
||||
var virtiofsDaemon VirtiofsDaemon
|
||||
switch sandbox.GetHypervisorType() {
|
||||
case string(QemuHypervisor):
|
||||
virtiofsDaemon = sandbox.hypervisor.(*qemu).virtiofsDaemon
|
||||
case string(ClhHypervisor):
|
||||
virtiofsDaemon = sandbox.hypervisor.(*cloudHypervisor).virtiofsDaemon
|
||||
default:
|
||||
return nil, errNydusdNotSupport
|
||||
}
|
||||
return virtiofsDaemon, nil
|
||||
}
|
||||
|
||||
func nydusContainerCleanup(ctx context.Context, sharedDir string, c *Container) error {
|
||||
sandbox := c.sandbox
|
||||
virtiofsDaemon, err := getVirtiofsDaemonForNydus(sandbox)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := virtiofsDaemon.Umount(rafsMountPath(c.id)); err != nil {
|
||||
return errors.Wrap(err, "umount rafs failed")
|
||||
}
|
||||
if err := bindUnmountContainerSnapshotDir(ctx, sharedDir, c.id); err != nil {
|
||||
return errors.Wrap(err, "umount snapshotdir err")
|
||||
}
|
||||
destDir := filepath.Join(sharedDir, c.id, c.rootfsSuffix)
|
||||
if err := syscall.Rmdir(destDir); err != nil {
|
||||
return errors.Wrap(err, "remove container rootfs err")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func bindUnmountAllRootfs(ctx context.Context, sharedDir string, sandbox *Sandbox) error {
|
||||
span, ctx := katatrace.Trace(ctx, nil, "bindUnmountAllRootfs", mountTracingTags)
|
||||
defer span.End()
|
||||
span.SetAttributes(otelLabel.String("shared-dir", sharedDir), otelLabel.String("sandbox-id", sandbox.id))
|
||||
|
||||
var errors *merr.Error
|
||||
for _, c := range sandbox.containers {
|
||||
if isSymlink(filepath.Join(sharedDir, c.id)) {
|
||||
mountLogger().WithField("container", c.id).Warnf("container dir is a symlink, malicious guest?")
|
||||
continue
|
||||
}
|
||||
c.unmountHostMounts(ctx)
|
||||
if c.state.Fstype == "" {
|
||||
// even if error found, don't break out of loop until all mounts attempted
|
||||
// to be unmounted, and collect all errors
|
||||
if c.rootFs.Type == NydusRootFSType {
|
||||
errors = merr.Append(errors, nydusContainerCleanup(ctx, sharedDir, c))
|
||||
} else {
|
||||
errors = merr.Append(errors, bindUnmountContainerRootfs(ctx, sharedDir, c.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.ErrorOrNil()
|
||||
}
|
||||
|
||||
const (
|
||||
dockerVolumePrefix = "/var/lib/docker/volumes"
|
||||
dockerVolumeSuffix = "_data"
|
||||
|
||||
Reference in New Issue
Block a user