diff --git a/userns/userns_linux.go b/userns/userns_linux.go index 87c1c38e..bfd0f768 100644 --- a/userns/userns_linux.go +++ b/userns/userns_linux.go @@ -5,8 +5,12 @@ import ( "fmt" "os" "sync" + "syscall" ) +// See PROC_USER_INIT_INO in https://github.com/torvalds/linux/blob/v7.1/include/uapi/linux/nsfs.h#L50. +const procUserInitIno = 0xEFFFFFFD + var inUserNS = sync.OnceValue(runningInUserNS) // runningInUserNS detects whether we are currently running in a user namespace. @@ -17,6 +21,11 @@ var inUserNS = sync.OnceValue(runningInUserNS) // [libcontainer/runc]: https://github.com/opencontainers/runc/blob/3778ae603c706494fd1e2c2faf83b406e38d687d/libcontainer/userns/userns_linux.go#L12-L49 // [lcx/incus]: https://github.com/lxc/incus/blob/e45085dd42f826b3c8c3228e9733c0b6f998eafe/shared/util.go#L678-L700 func runningInUserNS() bool { + var st syscall.Stat_t + if err := syscall.Stat("/proc/self/ns/user", &st); err == nil { + return st.Ino != procUserInitIno + } + file, err := os.Open("/proc/self/uid_map") if err != nil { // This kernel-provided file only exists if user namespaces are supported. @@ -30,12 +39,9 @@ func runningInUserNS() bool { return false } - return uidMapInUserNS(string(l)) -} - -func uidMapInUserNS(uidMap string) bool { + uidMap := string(l) if uidMap == "" { - // File exist but empty (the initial state when userns is created, + // File exists but is empty (the initial state when userns is created, // see user_namespaces(7)). return true } diff --git a/userns/userns_linux_test.go b/userns/userns_linux_test.go deleted file mode 100644 index 25c4ac30..00000000 --- a/userns/userns_linux_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package userns - -import "testing" - -func TestUIDMapInUserNS(t *testing.T) { - cases := []struct { - s string - expected bool - }{ - { - s: " 0 0 4294967295\n", - expected: false, - }, - { - s: " 0 0 1\n", - expected: true, - }, - { - s: " 0 1001 1\n 1 231072 65536\n", - expected: true, - }, - { - // file exist but empty (the initial state when userns is created. see man 7 user_namespaces) - s: "", - expected: true, - }, - } - for _, c := range cases { - actual := uidMapInUserNS(c.s) - if c.expected != actual { - t.Fatalf("expected %v, got %v for %q", c.expected, actual, c.s) - } - } -}