Skip to content

Commit

Permalink
has*: use sync.OnceValue[s]
Browse files Browse the repository at this point in the history
Change has* functions from using sync.Once to sync.OnceValue[s], which
results in less global variables and simplifies code a little.

The only notable changes are in haveProcThreadSelf:
1. Renamed to hasProcThreadSelf to match others;
2. Changed to use absolute path (assuming /proc is always available),
   as we can't pass a parameter to a sync.OnceValue function;
3. Changed to use unix.Access (simpler and faster).

Signed-off-by: Kir Kolyshkin <[email protected]>
  • Loading branch information
kolyshkin committed Sep 23, 2024
1 parent 1a62e62 commit 3d752fb
Showing 1 changed file with 39 additions and 71 deletions.
110 changes: 39 additions & 71 deletions procfs_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,33 +54,26 @@ func verifyProcRoot(procRoot *os.File) error {
return nil
}

var (
hasNewMountApiBool bool
hasNewMountApiOnce sync.Once
)

func hasNewMountApi() bool {
hasNewMountApiOnce.Do(func() {
// All of the pieces of the new mount API we use (fsopen, fsconfig,
// fsmount, open_tree) were added together in Linux 5.1[1,2], so we can
// just check for one of the syscalls and the others should also be
// available.
//
// Just try to use open_tree(2) to open a file without OPEN_TREE_CLONE.
// This is equivalent to openat(2), but tells us if open_tree is
// available (and thus all of the other basic new mount API syscalls).
// open_tree(2) is most light-weight syscall to test here.
//
// [1]: merge commit 400913252d09
// [2]: <https://lore.kernel.org/lkml/153754740781.17872.7869536526927736855.stgit@warthog.procyon.org.uk/>
fd, err := unix.OpenTree(-int(unix.EBADF), "/", unix.OPEN_TREE_CLOEXEC)
if err == nil {
hasNewMountApiBool = true
_ = unix.Close(fd)
}
})
return hasNewMountApiBool
}
var hasNewMountApi = sync.OnceValue(func() bool {
// All of the pieces of the new mount API we use (fsopen, fsconfig,
// fsmount, open_tree) were added together in Linux 5.1[1,2], so we can
// just check for one of the syscalls and the others should also be
// available.
//
// Just try to use open_tree(2) to open a file without OPEN_TREE_CLONE.
// This is equivalent to openat(2), but tells us if open_tree is
// available (and thus all of the other basic new mount API syscalls).
// open_tree(2) is most light-weight syscall to test here.
//
// [1]: merge commit 400913252d09
// [2]: <https://lore.kernel.org/lkml/153754740781.17872.7869536526927736855.stgit@warthog.procyon.org.uk/>
fd, err := unix.OpenTree(-int(unix.EBADF), "/", unix.OPEN_TREE_CLOEXEC)
if err != nil {
return false
}
_ = unix.Close(fd)
return true
})

func fsopen(fsName string, flags int) (*os.File, error) {
// Make sure we always set O_CLOEXEC.
Expand Down Expand Up @@ -172,14 +165,6 @@ func privateProcRoot() (*os.File, error) {
return procRoot, err
}

var (
procRootHandle *os.File
procRootError error
procRootOnce sync.Once

errUnsafeProcfs = errors.New("unsafe procfs detected")
)

func unsafeHostProcRoot() (_ *os.File, Err error) {
procRoot, err := os.OpenFile("/proc", unix.O_PATH|unix.O_NOFOLLOW|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
if err != nil {
Expand Down Expand Up @@ -207,17 +192,15 @@ func doGetProcRoot() (*os.File, error) {
return procRoot, err
}

func getProcRoot() (*os.File, error) {
procRootOnce.Do(func() {
procRootHandle, procRootError = doGetProcRoot()
})
return procRootHandle, procRootError
}
var getProcRoot = sync.OnceValues(func() (*os.File, error) {
return doGetProcRoot()
})

var (
haveProcThreadSelf bool
haveProcThreadSelfOnce sync.Once
)
var hasProcThreadSelf = sync.OnceValue(func() bool {
return unix.Access("/proc/thread-self/", unix.F_OK) == nil
})

var errUnsafeProcfs = errors.New("unsafe procfs detected")

type procThreadSelfCloser func()

Expand All @@ -230,13 +213,6 @@ type procThreadSelfCloser func()
// This is similar to ProcThreadSelf from runc, but with extra hardening
// applied and using *os.File.
func procThreadSelf(procRoot *os.File, subpath string) (_ *os.File, _ procThreadSelfCloser, Err error) {
haveProcThreadSelfOnce.Do(func() {
// If the kernel doesn't support thread-self, it doesn't matter which
// /proc handle we use.
_, err := fstatatFile(procRoot, "thread-self", unix.AT_SYMLINK_NOFOLLOW)
haveProcThreadSelf = (err == nil)
})

// We need to lock our thread until the caller is done with the handle
// because between getting the handle and using it we could get interrupted
// by the Go runtime and hit the case where the underlying thread is
Expand All @@ -251,7 +227,7 @@ func procThreadSelf(procRoot *os.File, subpath string) (_ *os.File, _ procThread

// Figure out what prefix we want to use.
threadSelf := "thread-self/"
if !haveProcThreadSelf || testingForceProcSelfTask() {
if !hasProcThreadSelf() || testingForceProcSelfTask() {
/// Pre-3.17 kernels don't have /proc/thread-self, so do it manually.
threadSelf = "self/task/" + strconv.Itoa(unix.Gettid()) + "/"
if _, err := fstatatFile(procRoot, threadSelf, unix.AT_SYMLINK_NOFOLLOW); err != nil || testingForceProcSelf() {
Expand Down Expand Up @@ -313,24 +289,16 @@ func procThreadSelf(procRoot *os.File, subpath string) (_ *os.File, _ procThread
return handle, runtime.UnlockOSThread, nil
}

var (
hasStatxMountIdBool bool
hasStatxMountIdOnce sync.Once
)

func hasStatxMountId() bool {
hasStatxMountIdOnce.Do(func() {
var (
stx unix.Statx_t
// We don't care which mount ID we get. The kernel will give us the
// unique one if it is supported.
wantStxMask uint32 = unix.STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID
)
err := unix.Statx(-int(unix.EBADF), "/", 0, int(wantStxMask), &stx)
hasStatxMountIdBool = (err == nil && (stx.Mask&wantStxMask != 0))
})
return hasStatxMountIdBool
}
var hasStatxMountId = sync.OnceValue(func() bool {
var (
stx unix.Statx_t
// We don't care which mount ID we get. The kernel will give us the
// unique one if it is supported.
wantStxMask uint32 = unix.STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID
)
err := unix.Statx(-int(unix.EBADF), "/", 0, int(wantStxMask), &stx)
return err == nil && stx.Mask&wantStxMask != 0
})

func getMountId(dir *os.File, path string) (uint64, error) {
// If we don't have statx(STATX_MNT_ID*) support, we can't do anything.
Expand Down

0 comments on commit 3d752fb

Please sign in to comment.