From: Karel Zak Date: Wed, 6 Nov 2024 09:18:17 +0000 (+0100) Subject: nsenter: improve portability to older kernels X-Git-Tag: v2.42-start~151^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e8a6b41016bf2b059c6f177505c75f19b47e5c9b;p=thirdparty%2Futil-linux.git nsenter: improve portability to older kernels 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 --- diff --git a/sys-utils/nsenter.1.adoc b/sys-utils/nsenter.1.adoc index 31b5c4d7d..5246daecd 100644 --- a/sys-utils/nsenter.1.adoc +++ b/sys-utils/nsenter.1.adoc @@ -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_. diff --git a/sys-utils/nsenter.c b/sys-utils/nsenter.c index 27962a9ec..fb7c3039a 100644 --- a/sys-utils/nsenter.c +++ b/sys-utils/nsenter.c @@ -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)