Replace mount fork hack with CLONE_FS

This change spins up a new goroutine, locks it to a thread, then
unshares CLONE_FS which allows us to `Chdir` from inside the thread
without affecting the rest of the program.

The thread is no longer usable after unshare so it leaves the thread
locked to prevent go from returning the thread to the thread pool.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
Brian Goff
2022-10-11 23:47:16 +00:00
parent c21d1baa88
commit a24ef09937
7 changed files with 68 additions and 406 deletions

View File

@@ -21,6 +21,7 @@ import (
"fmt"
"os"
"path"
"runtime"
"strings"
"time"
@@ -363,24 +364,29 @@ func mountAt(chdir string, source, target, fstype string, flags uintptr, data st
return unix.Mount(source, target, fstype, flags, data)
}
f, err := os.Open(chdir)
if err != nil {
return fmt.Errorf("failed to mountat: %w", err)
}
defer f.Close()
ch := make(chan error, 1)
go func() {
runtime.LockOSThread()
fs, err := f.Stat()
if err != nil {
return fmt.Errorf("failed to mountat: %w", err)
}
// Do not unlock this thread.
// If the thread is unlocked go will try to use it for other goroutines.
// However it is not possible to restore the thread state after CLONE_FS.
//
// Once the goroutine exits the thread should eventually be terminated by go.
if !fs.IsDir() {
return fmt.Errorf("failed to mountat: %s is not dir", chdir)
}
if err := fMountat(f.Fd(), source, target, fstype, flags, data); err != nil {
return fmt.Errorf("failed to mountat: %w", err)
}
return nil
if err := unix.Unshare(unix.CLONE_FS); err != nil {
ch <- err
return
}
if err := unix.Chdir(chdir); err != nil {
ch <- err
return
}
ch <- unix.Mount(source, target, fstype, flags, data)
}()
return <-ch
}
func (m *Mount) mountWithHelper(helperBinary, typePrefix, target string) error {