diff --git a/pkg/csi/driver/mount_linux.go b/pkg/csi/driver/mount_linux.go index 1a1c505..12dcba1 100644 --- a/pkg/csi/driver/mount_linux.go +++ b/pkg/csi/driver/mount_linux.go @@ -106,16 +106,38 @@ func cleanupMountPoint(target string, mounter *mount.SafeFormatAndMount) error { return mount.CleanupMountPoint(target, mounter, true) } +func hostMount(args ...string) ([]byte, error) { + return exec.Command("nsenter", append([]string{"--target", "1", "--mount", "--"}, args...)...).CombinedOutput() +} + +func isHostMounted(path string) bool { + out, err := hostMount("findmnt", "-n", path) + return err == nil && strings.TrimSpace(string(out)) != "" +} + func bindMount(source, target string) error { - if !isMounted(source) { + if !isHostMounted(source) && !isMounted(source) { return fmt.Errorf("staging path %s is not mounted", source) } - if isMounted(target) { + if isHostMounted(target) { return nil } - mounter := mount.New("") - if err := mounter.Mount(source, target, "", []string{"bind"}); err != nil { - return fmt.Errorf("bind mount %s -> %s: %w", source, target, err) + if out, err := hostMount("mkdir", "-p", target); err != nil { + return fmt.Errorf("create bind mount target %s: %w: %s", target, err, strings.TrimSpace(string(out))) + } + if out, err := hostMount("mount", "--bind", source, target); err != nil { + return fmt.Errorf("bind mount %s -> %s: %w: %s", source, target, err, strings.TrimSpace(string(out))) + } + return nil +} + +func unmountHost(path string) error { + if !isHostMounted(path) { + return nil + } + out, err := hostMount("umount", path) + if err != nil { + return fmt.Errorf("umount %s: %w: %s", path, err, strings.TrimSpace(string(out))) } return nil } diff --git a/pkg/csi/driver/mount_linux_test.go b/pkg/csi/driver/mount_linux_test.go index 3486e41..488e277 100644 --- a/pkg/csi/driver/mount_linux_test.go +++ b/pkg/csi/driver/mount_linux_test.go @@ -4,45 +4,23 @@ package driver import ( "os" - "path/filepath" + "os/exec" "testing" ) -func TestBindMountCreatesTargetAndIsIdempotent(t *testing.T) { - t.Parallel() - - root := t.TempDir() - source := filepath.Join(root, "globalmount") - target := filepath.Join(root, "pods", "test", "mount") - if err := os.MkdirAll(source, 0o755); err != nil { - t.Fatalf("mkdir source: %v", err) +func TestHostMountRequiresNsenter(t *testing.T) { + if _, err := exec.LookPath("nsenter"); err != nil { + t.Skip("nsenter not available") } - if err := os.WriteFile(filepath.Join(source, "probe"), []byte("ok"), 0o644); err != nil { - t.Fatalf("write probe file: %v", err) + if os.Geteuid() != 0 { + t.Skip("host mount namespace tests require root") } - mounter := newMounter() - if err := mounter.Mount("tmpfs", source, "tmpfs", []string{}); err != nil { - t.Fatalf("mount tmpfs at source: %v", err) + tmp := t.TempDir() + if out, err := hostMount("mkdir", "-p", tmp); err != nil { + t.Fatalf("hostMount mkdir failed: %v: %s", err, string(out)) } - t.Cleanup(func() { - _ = cleanupMountPoint(source, mounter) - }) - - if err := bindMount(source, target); err != nil { - t.Fatalf("first bindMount failed: %v", err) - } - t.Cleanup(func() { - _ = cleanupMountPoint(target, mounter) - }) - - if _, err := os.Stat(target); err != nil { - t.Fatalf("target mount path missing: %v", err) - } - if _, err := os.Stat(filepath.Join(target, "probe")); err != nil { - t.Fatalf("bind mount did not expose source contents: %v", err) - } - if err := bindMount(source, target); err != nil { - t.Fatalf("second bindMount failed: %v", err) + if _, err := os.Stat(tmp); err != nil { + t.Fatalf("expected host-visible directory: %v", err) } } diff --git a/pkg/csi/driver/node.go b/pkg/csi/driver/node.go index 8e5fee1..888cba2 100644 --- a/pkg/csi/driver/node.go +++ b/pkg/csi/driver/node.go @@ -72,6 +72,9 @@ func (s *NodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstage } mounter := newMounter() + if err := unmountHost(req.GetStagingTargetPath()); err != nil { + return nil, internalError(err) + } if err := cleanupMountPoint(req.GetStagingTargetPath(), mounter); err != nil { return nil, internalError(err) } @@ -90,7 +93,7 @@ func (s *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublish return nil, invalidArgument("staging target path is required") } - if !isMounted(req.GetStagingTargetPath()) { + if !isMounted(req.GetStagingTargetPath()) && !isHostMounted(req.GetStagingTargetPath()) { return nil, internalError(fmt.Errorf("staging path %s is not mounted", req.GetStagingTargetPath())) } @@ -111,6 +114,9 @@ func (s *NodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpub } mounter := newMounter() + if err := unmountHost(req.GetTargetPath()); err != nil { + return nil, internalError(err) + } if err := cleanupMountPoint(req.GetTargetPath(), mounter); err != nil { return nil, internalError(err) }