]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
nsenter: improve portability to older kernels
authorKarel Zak <kzak@redhat.com>
Wed, 6 Nov 2024 09:18:17 +0000 (10:18 +0100)
committerKarel Zak <kzak@redhat.com>
Wed, 6 Nov 2024 09:22:09 +0000 (10:22 +0100)
The pidfd cannot be used to enter namespaces using setns() before
Linux 5.7. To ensure compatibility with older kernels, we will check
the kernel version before using pidfd_open() and, if necessary, fall
back to using the classic /proc/#/ns/ files.

Reported-by: Alex Xu
Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/nsenter.1.adoc
sys-utils/nsenter.c

index 31b5c4d7db5d2962761d17c5f38eced81c0f1695..5246daecdf597a51bcb63985ab8478fbfc44104d 100644 (file)
@@ -92,7 +92,7 @@ Enter the IPC namespace. If no file is specified, enter the IPC namespace of the
 Enter the network namespace. If no file is specified, enter the network namespace of the target process. If _file_ is specified, enter the network namespace specified by _file_.
 
 *-N*, *--net-socket* _fd_::
-Enter the network namespace of the target process's socket. It requires *--target* process specified.
+Enter the network namespace of the target process's socket. It requires *--target* process specified. Supported since Linux 5.6.
 
 *-p*, *--pid*[=_file_]::
 Enter the PID namespace. If no file is specified, enter the PID namespace of the target process. If _file_ is specified, enter the PID namespace specified by _file_.
index 27962a9ecdffbda481129c75d3aad1445dcdf71e..fb7c3039af43123fb3901dda450536db0a72b957 100644 (file)
@@ -52,6 +52,7 @@
 #include "statfs_magic.h"
 #include "pathnames.h"
 #include "pidfd-utils.h"
+#include "linux_version.h"
 
 static struct namespace_file {
        int nstype;
@@ -331,10 +332,18 @@ static void open_target_sk_netns(int pidfd, int sock_fd)
        struct namespace_file *nsfile;
        struct stat sb;
        int sk, nsfd;
+       bool local_fd = false;
 
        nsfile = get_nsfile(CLONE_NEWNET);
        assert(nsfile->nstype);
 
+       if (pidfd < 0) {
+               pidfd = pidfd_open(namespace_target_pid, 0);
+               if (pidfd < 0)
+                       err(EXIT_FAILURE, _("failed to pidfd_open() for %d"), namespace_target_pid);
+               local_fd = true;
+       }
+
        sk = pidfd_getfd(pidfd, sock_fd, 0);
        if (sk < 0)
                err(EXIT_FAILURE, _("pidfd_getfd(%d, %u)"), pidfd, sock_fd);
@@ -351,6 +360,9 @@ static void open_target_sk_netns(int pidfd, int sock_fd)
        nsfile->fd = nsfd;
        nsfile->enabled = true;
        close(sk);
+
+       if (local_fd)
+               close(pidfd);
 }
 
 static int get_ns_ino(const char *path, ino_t *ino)
@@ -676,13 +688,14 @@ int main(int argc, char *argv[])
                if (!namespace_target_pid)
                        errx(EXIT_FAILURE, _("no target PID specified"));
 
-               pid_fd = pidfd_open(namespace_target_pid, 0);
-               if (pid_fd < 0) {
-                       if (sock_fd >= 0)
-                               err(EXIT_FAILURE, _("failed to pidfd_open() for %d"), namespace_target_pid);
-                       if (namespaces)
-                               open_namespaces(namespaces);    /* fallback */
-               }
+               /* The syscall setns() before Linux 5.7 does not support pidfd.
+                * For other cases such as sock_fd and user-parent, the global
+                * pidfd needs to be optional.
+                */
+               if (get_linux_version() > KERNEL_VERSION(5, 7, 0))
+                       pid_fd = pidfd_open(namespace_target_pid, 0);
+               if (pid_fd < 0 && namespaces)
+                       open_namespaces(namespaces);    /* fallback */
        }
 
        if (do_rd)