]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
namespace-util: add generic namespace_is_init() call
authorLennart Poettering <lennart@poettering.net>
Fri, 22 Nov 2024 16:11:29 +0000 (17:11 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 22 Nov 2024 23:14:20 +0000 (00:14 +0100)
src/basic/missing_namespace.h [new file with mode: 0644]
src/basic/namespace-util.c
src/basic/namespace-util.h
src/test/test-namespace.c

diff --git a/src/basic/missing_namespace.h b/src/basic/missing_namespace.h
new file mode 100644 (file)
index 0000000..318c014
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/* Root namespace inode numbers, as per include/linux/proc_ns.h in the kernel source tree, since v3.8:
+ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=98f842e675f96ffac96e6c50315790912b2812be */
+
+#define PROC_IPC_INIT_INO    ((ino_t) UINT32_C(0xEFFFFFFF))
+#define PROC_UTS_INIT_INO    ((ino_t) UINT32_C(0xEFFFFFFE))
+#define PROC_USER_INIT_INO   ((ino_t) UINT32_C(0xEFFFFFFD))
+#define PROC_PID_INIT_INO    ((ino_t) UINT32_C(0xEFFFFFFC))
+#define PROC_CGROUP_INIT_INO ((ino_t) UINT32_C(0xEFFFFFFB))
+#define PROC_TIME_INIT_INO   ((ino_t) UINT32_C(0xEFFFFFFA))
index 2c61506149f9b29e29f4109595f212db480003eb..27f6ec34e40f4141ea6c7c11aa47ba1c13059b5a 100644 (file)
@@ -12,6 +12,7 @@
 #include "fileio.h"
 #include "missing_fs.h"
 #include "missing_magic.h"
+#include "missing_namespace.h"
 #include "missing_sched.h"
 #include "missing_syscall.h"
 #include "mountpoint-util.h"
 #include "user-util.h"
 
 const struct namespace_info namespace_info[_NAMESPACE_TYPE_MAX + 1] = {
-        [NAMESPACE_CGROUP] =  { "cgroup", "ns/cgroup", CLONE_NEWCGROUP,                          },
-        [NAMESPACE_IPC]    =  { "ipc",    "ns/ipc",    CLONE_NEWIPC,                             },
-        [NAMESPACE_NET]    =  { "net",    "ns/net",    CLONE_NEWNET,                             },
+        [NAMESPACE_CGROUP] =  { "cgroup", "ns/cgroup", CLONE_NEWCGROUP, PROC_CGROUP_INIT_INO     },
+        [NAMESPACE_IPC]    =  { "ipc",    "ns/ipc",    CLONE_NEWIPC,    PROC_IPC_INIT_INO        },
+        [NAMESPACE_NET]    =  { "net",    "ns/net",    CLONE_NEWNET,    0                        },
         /* So, the mount namespace flag is called CLONE_NEWNS for historical
          * reasons. Let's expose it here under a more explanatory name: "mnt".
          * This is in-line with how the kernel exposes namespaces in /proc/$PID/ns. */
-        [NAMESPACE_MOUNT]  =  { "mnt",    "ns/mnt",    CLONE_NEWNS,                              },
-        [NAMESPACE_PID]    =  { "pid",    "ns/pid",    CLONE_NEWPID,                             },
-        [NAMESPACE_USER]   =  { "user",   "ns/user",   CLONE_NEWUSER,                            },
-        [NAMESPACE_UTS]    =  { "uts",    "ns/uts",    CLONE_NEWUTS,                             },
-        [NAMESPACE_TIME]   =  { "time",   "ns/time",   CLONE_NEWTIME,                            },
+        [NAMESPACE_MOUNT]  =  { "mnt",    "ns/mnt",    CLONE_NEWNS,     0                        },
+        [NAMESPACE_PID]    =  { "pid",    "ns/pid",    CLONE_NEWPID,    PROC_PID_INIT_INO        },
+        [NAMESPACE_USER]   =  { "user",   "ns/user",   CLONE_NEWUSER,   PROC_USER_INIT_INO       },
+        [NAMESPACE_UTS]    =  { "uts",    "ns/uts",    CLONE_NEWUTS,    PROC_UTS_INIT_INO        },
+        [NAMESPACE_TIME]   =  { "time",   "ns/time",   CLONE_NEWTIME,   PROC_TIME_INIT_INO       },
         { /* Allow callers to iterate over the array without using _NAMESPACE_TYPE_MAX. */       },
 };
 
@@ -479,6 +480,28 @@ int namespace_open_by_type(NamespaceType type) {
         return fd;
 }
 
+int namespace_is_init(NamespaceType type) {
+        int r;
+
+        assert(type >= 0);
+        assert(type <= _NAMESPACE_TYPE_MAX);
+
+        if (namespace_info[type].root_inode == 0)
+                return -EBADR; /* Cannot answer this question */
+
+        const char *p = pid_namespace_path(0, type);
+
+        struct stat st;
+        r = RET_NERRNO(stat(p, &st));
+        if (r == -ENOENT)
+                /* If the /proc/ns/<type> API is not around in /proc/ then ns is off in the kernel and we are in the init ns */
+                return proc_mounted() == 0 ? -ENOSYS : true;
+        if (r < 0)
+                return r;
+
+        return st.st_ino == namespace_info[type].root_inode;
+}
+
 int is_our_namespace(int fd, NamespaceType request_type) {
         int clone_flag;
 
index 105bab6fdb7d719898d028623c8cf1e7f334624e..e92d40786408b6298c944c4c6993218f7e81fb9d 100644 (file)
@@ -24,6 +24,7 @@ extern const struct namespace_info {
         const char *proc_name;
         const char *proc_path;
         unsigned int clone_flag;
+        ino_t root_inode;
 } namespace_info[_NAMESPACE_TYPE_MAX + 1];
 
 int pidref_namespace_open(
@@ -74,6 +75,8 @@ int parse_userns_uid_range(const char *s, uid_t *ret_uid_shift, uid_t *ret_uid_r
 
 int namespace_open_by_type(NamespaceType type);
 
+int namespace_is_init(NamespaceType type);
+
 int is_our_namespace(int fd, NamespaceType type);
 
 int is_idmapping_supported(const char *path);
index 37bce0ae1dd01a0a80b41624b5be1b5b08b99027..4d8efb23bd7a20599701439059c6d1340696585b 100644 (file)
@@ -213,6 +213,20 @@ TEST(idmapping_supported) {
         assert_se(is_idmapping_supported("/etc") >= 0);
 }
 
+TEST(namespace_is_init) {
+        int r;
+
+        for (NamespaceType t = 0; t < _NAMESPACE_TYPE_MAX; t++) {
+                r = namespace_is_init(t);
+                if (r == -EBADR)
+                        log_info_errno(r, "In root namespace of type '%s': don't know", namespace_info[t].proc_name);
+                else {
+                        ASSERT_OK(r);
+                        log_info("In root namespace of type '%s': %s", namespace_info[t].proc_name, yes_no(r));
+                }
+        }
+}
+
 static int intro(void) {
         if (!have_namespaces())
                 return log_tests_skipped("Don't have namespace support");