From: Xiao Liang Date: Sat, 14 Feb 2026 03:23:42 +0000 (+0800) Subject: nsenter: Support specifying namespace by ID X-Git-Tag: v2.43-devel~72 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f2a5997869;p=thirdparty%2Futil-linux.git nsenter: Support specifying namespace by ID Linux kernel v6.19 introduces listns() syscall that lists namespace IDs, as well as the support for opening an nsfile by a file_handle of ns ID. This patch allows specifying namespaces by ID for nsenter. For example: # nsenter --net=:7 enters init net namespace (7 for NET_NS_INIT_ID). In the rare case that a ns file name starts with a colon, prepend "./" for disambiguation. Signed-off-by: Xiao Liang --- diff --git a/configure.ac b/configure.ac index 912aa85d0..a0c8d0752 100644 --- a/configure.ac +++ b/configure.ac @@ -1052,6 +1052,10 @@ char *c = crypt("abc","pw"); ]) AM_CONDITIONAL([HAVE_LIBCRYPT], [test "x$have_libcrypt" = xyes]) +AC_CHECK_TYPES([struct nsfs_file_handle], [], [], [[ +#include +]]) + AC_ARG_WITH([selinux], AS_HELP_STRING([--with-selinux], [compile with SELinux support]), diff --git a/meson.build b/meson.build index 8ef9d087b..6ab1bd85c 100644 --- a/meson.build +++ b/meson.build @@ -915,6 +915,9 @@ conf.set('USE_COLORS_BY_DEFAULT', get_option('colors-default') ? 1 : false) is_glibc = cc.has_header_symbol('limits.h', '__GLIBC__') +have = cc.has_type('struct nsfs_file_handle', prefix : '#include ') +conf.set('HAVE_STRUCT_NSFS_FILE_HANDLE', have ? 1 : false) + ############################################################ diff --git a/sys-utils/nsenter.1.adoc b/sys-utils/nsenter.1.adoc index d12097472..3dec46d66 100644 --- a/sys-utils/nsenter.1.adoc +++ b/sys-utils/nsenter.1.adoc @@ -87,36 +87,36 @@ Optionally, a process can be addressed with the format _PID:inode_. The _inode_ identifies the unique process's file descriptor. To retrieve a process's inode number you can use the *getino*(1) utility. -*-m*, *--mount*[**=**_file_]:: -Enter the mount namespace. If no file is specified, enter the mount namespace of the target process. If _file_ is specified, enter the mount namespace specified by _file_. +*-m*, *--mount*[**=**<__file__|:__nsid__>]:: +Enter the mount namespace. If no argument is specified, enter the mount namespace of the target process. If _file_ or :__nsid__ is specified, enter the mount namespace specified by _file_ or _nsid_. -*-u*, *--uts*[**=**_file_]:: -Enter the UTS namespace. If no file is specified, enter the UTS namespace of the target process. If _file_ is specified, enter the UTS namespace specified by _file_. +*-u*, *--uts*[**=**<__file__|:__nsid__>]:: +Enter the UTS namespace. If no argument is specified, enter the UTS namespace of the target process. If _file_ or :__nsid__ is specified, enter the UTS namespace specified by _file_ or _nsid_. -*-i*, *--ipc*[**=**_file_]:: -Enter the IPC namespace. If no file is specified, enter the IPC namespace of the target process. If _file_ is specified, enter the IPC namespace specified by _file_. +*-i*, *--ipc*[**=**<__file__|:__nsid__>]:: +Enter the IPC namespace. If no argument is specified, enter the IPC namespace of the target process. If _file_ or :__nsid__ is specified, enter the IPC namespace specified by _file_ or _nsid_. -*-n*, *--net*[**=**_file_]:: -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*[**=**<__file__|:__nsid__>]:: +Enter the network namespace. If no argument is specified, enter the network namespace of the target process. If _file_ or :__nsid__ is specified, enter the network namespace specified by _file_ or _nsid_. *-N*, *--net-socket* _fd_:: 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_. +*-p*, *--pid*[**=**<__file__|:__nsid__>]:: +Enter the PID namespace. If no argument is specified, enter the PID namespace of the target process. If _file_ or :__nsid__ is specified, enter the PID namespace specified by _file_ or _nsid_. -*-U*, *--user*[**=**_file_]:: -Enter the user namespace. If no file is specified, enter the user namespace of the target process. If _file_ is specified, enter the user namespace specified by _file_. See also the *--setuid* and *--setgid* options. +*-U*, *--user*[**=**<__file__|:__nsid__>]:: +Enter the user namespace. If no argument is specified, enter the user namespace of the target process. If _file_ or :__nsid__ is specified, enter the user namespace specified by _file_ or _nsid_. See also the *--setuid* and *--setgid* options. *--user-parent*:: Enter the parent user namespace. Parent user namespace will be acquired from any other enabled namespace. If combined with *--user* option the parent user namespace will be fetched from the user namespace and replace it. -*-C*, *--cgroup*[**=**_file_]:: -Enter the cgroup namespace. If no file is specified, enter the cgroup namespace of the target process. If _file_ is specified, enter the cgroup namespace specified by _file_. +*-C*, *--cgroup*[**=**<__file__|:__nsid__>]:: +Enter the cgroup namespace. If no argument is specified, enter the cgroup namespace of the target process. If _file_ or :__nsid__ is specified, enter the cgroup namespace specified by _file_ or _nsid_. -*-T*, *--time*[**=**_file_]:: -Enter the time namespace. If no file is specified, enter the time namespace of the target process. If _file_ is specified, enter the time namespace specified by _file_. +*-T*, *--time*[**=**<__file__|:__nsid__>]:: +Enter the time namespace. If no argument is specified, enter the time namespace of the target process. If _file_ or :__nsid__ is specified, enter the time namespace specified by _file_ or _nsid_. *-G*, *--setgid* _gid_:: Set the group ID which will be used in the entered namespace and drop supplementary groups. @@ -161,6 +161,8 @@ include::man-common/help-version.adoc[] The *--user-parent* option requires Linux 4.9 or higher, older kernels will raise inappropriate ioctl for device error. +Specifying namesapces by _nsid_ requires Linux 6.19 or higher. + == AUTHORS mailto:biederm@xmission.com[Eric Biederman], diff --git a/sys-utils/nsenter.c b/sys-utils/nsenter.c index b45f626b1..2820a7123 100644 --- a/sys-utils/nsenter.c +++ b/sys-utils/nsenter.c @@ -93,16 +93,16 @@ static void __attribute__((__noreturn__)) usage(void) fputs(USAGE_OPTIONS, out); fputs(_(" -a, --all enter all namespaces\n"), out); fputs(_(" -t, --target target process to get namespaces from\n"), out); - fputs(_(" -m, --mount[=] enter mount namespace\n"), out); - fputs(_(" -u, --uts[=] enter UTS namespace (hostname etc)\n"), out); - fputs(_(" -i, --ipc[=] enter System V IPC namespace\n"), out); - fputs(_(" -n, --net[=] enter network namespace\n"), out); + fputs(_(" -m, --mount[=] enter mount namespace\n"), out); + fputs(_(" -u, --uts[=] enter UTS namespace (hostname etc)\n"), out); + fputs(_(" -i, --ipc[=] enter System V IPC namespace\n"), out); + fputs(_(" -n, --net[=] enter network namespace\n"), out); fputs(_(" -N, --net-socket enter socket's network namespace (use with --target)\n"), out); - fputs(_(" -p, --pid[=] enter pid namespace\n"), out); - fputs(_(" -C, --cgroup[=] enter cgroup namespace\n"), out); - fputs(_(" -U, --user[=] enter user namespace\n"), out); + fputs(_(" -p, --pid[=] enter pid namespace\n"), out); + fputs(_(" -C, --cgroup[=] enter cgroup namespace\n"), out); + fputs(_(" -U, --user[=] enter user namespace\n"), out); fputs(_(" --user-parent enter parent user namespace\n"), out); - fputs(_(" -T, --time[=] enter time namespace\n"), out); + fputs(_(" -T, --time[=] enter time namespace\n"), out); fputs(_(" -S, --setuid[=] set uid in entered namespace\n"), out); fputs(_(" -G, --setgid[=] set gid in entered namespace\n"), out); fputs(_(" --preserve-credentials do not touch uids or gids\n"), out); @@ -177,10 +177,89 @@ static void open_target_fd(int *fd, const char *type, const char *path) err(EXIT_FAILURE, _("cannot open %s"), path); } +#ifdef HAVE_STRUCT_NSFS_FILE_HANDLE + +static struct file_handle nsfs_fh_tmpl = {}; +static int nsfs_fd = -1; + +static int fill_nsfs_file_handle(struct file_handle *fh, uint64_t ns_id) +{ + struct nsfs_file_handle *nsfh = (struct nsfs_file_handle *)fh->f_handle; + int pidfd_self; + int mount_id; + + if (nsfs_fh_tmpl.handle_type) { + *fh = nsfs_fh_tmpl; + goto out; + } + + /* + * Before we can build a file handle of the namespace file of given + * nsid and open it, we need the handle_type of nsfs and a "mount_fd" + * for open_by_handle_at(2)). However, + * + * 1. FILEID_NSFS is currently not exposed to userspace, and + * 2. linux/fcntl.h, in which FD_NSFS_ROOT is defined, conflicts with + * fcntl.h, + * + * so we retrieve the information by grabbing a temporary ns file. + */ + pidfd_self = pidfd_open(getpid(), 0); + if (pidfd_self < 0) + return -errno; + + /* Mount namespace can not be disabled by kernel config */ + nsfs_fd = ioctl(pidfd_self, PIDFD_GET_MNT_NAMESPACE, 0); + if (nsfs_fd < 0) + return -errno; + close(pidfd_self); + + fh->handle_bytes = sizeof(struct nsfs_file_handle); + if (name_to_handle_at(nsfs_fd, "", fh, &mount_id, + AT_EMPTY_PATH | AT_HANDLE_FID) == -1) + return -errno; + assert(fh->handle_type); + nsfs_fh_tmpl = *fh; + +out: + memset(nsfh, 0, sizeof(*nsfh)); + nsfh->ns_id = ns_id; + return 0; +} + +static void open_target_fd_by_nsid(int *fd, const char *idstr) +{ + unsigned char fh_buf[sizeof(struct file_handle) + + sizeof(struct nsfs_file_handle)]; + struct file_handle *fh = (struct file_handle *)fh_buf; + uint64_t ns_id; + + if (ul_strtou64(idstr, &ns_id, 10) != 0) + errx(EXIT_FAILURE, _("failed to parse nsid: %s"), idstr); + + if (fill_nsfs_file_handle(fh, ns_id) != 0) + err(EXIT_FAILURE, _("failed to fill namespace file handle")); + + if (*fd >= 0) + close(*fd); + + *fd = open_by_handle_at(nsfs_fd, fh, O_RDONLY); + if (*fd < 0) + err(EXIT_FAILURE, _("cannot open namespace of id %"PRIu64), + ns_id); +} +#endif /* HAVE_STRUCT_NSFS_FILE_HANDLE */ + static void enable_nsfile(struct namespace_file *n, const char *path) { - if (path) - open_target_fd(&n->fd, n->name, path); + if (path) { +#ifdef HAVE_STRUCT_NSFS_FILE_HANDLE + if (*path == ':') + open_target_fd_by_nsid(&n->fd, path + 1); + else +#endif + open_target_fd(&n->fd, n->name, path); + } n->enabled = true; } @@ -776,6 +855,11 @@ int main(int argc, char *argv[]) if (pid_fd >= 0) close(pid_fd); +#ifdef HAVE_STRUCT_NSFS_FILE_HANDLE + if (nsfs_fd >= 0) + close(nsfs_fd); +#endif + /* Remember the current working directory if I'm not changing it */ if (root_fd >= 0 && wd_fd < 0 && wdns == NULL) { wd_fd = open(".", O_RDONLY);