NS_GET_USERNS(2const)
π« NS_GET_USERNS(2const) β bestie edition π«
okay so this is a page from section 2const of the Linux manual, and it's literally all about how you peek at the relationships between namespaces πβ¨ the two stars of the show are NS_GET_USERNS and NS_GET_PARENT, and honestly? iconic. let's get into it π«Ά
β¨ NAME β¨
NS_GET_USERNS, NS_GET_PARENT β discovering namespace relationships π³
πΈ SYNOPSIS πΈ
#include <linux/nsfs.h> /* Definition of NS_GET_* constants */
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long op);
π¦ DESCRIPTION π¦
so basically, the following ioctl(2) operations exist so you can go discover namespace relationships (go peek at user_namespaces(7) and pid_namespaces(7) if you wanna know more, bestie, they're the lore).
in every single case, fd points at a /proc/pid/ns/* file. and both operations? they hand you back a brand new file descriptor on success. new fd, who dis π
NS_GET_USERNSβ gives you back a file descriptor that refers to the owning user namespace for the namespace thatfdis pointing at πNS_GET_PARENTβ gives you back a file descriptor that refers to the parent namespace of the namespace thatfdis pointing at. but heads up bestie: this one only works for hierarchical namespaces (aka PID and user namespaces). for user namespaces,NS_GET_PARENTis literally synonymous withNS_GET_USERNS, like same thing, no notes π
the new file descriptor that comes back from these operations is opened with the O_RDONLY and O_CLOEXEC (close-on-exec; go look at fcntl(2)) flags. no cap.
and here's the trick that makes it all make sense: if you hit that returned file descriptor with fstat(2), you get a stat structure, and its st_dev (resident device) and st_ino (inode number) fields together identify the owning/parent namespace. so you can take that inode number and match it against the inode number of another /proc/pid/ns/{pid,user} file to figure out if that's the owning/parent namespace. it's giving detective work π΅οΈββοΈπ
π₯ RETURN VALUE π₯
on success, you get a file descriptor back. or error, -1 is returned, and errno is set to tell you what went wrong π
π ERRORS π
EPERMβ the namespace you're asking about is outside of the caller's namespace scope. this can happen if, for example, the owning user namespace is an ancestor of the caller's current user namespace. it can also happen when you try to get the parent of the initial user or PID namespace. ngl that one's just not available to you, sorry bestie π«ENOTTYβ the operation just isn't supported by this kernel version. old kernel vibes, it said no π
and on top of all that, the NS_GET_PARENT operation can fail with this one too:
EINVALβfdrefers to a nonhierarchical namespace. can't get a parent out of something that doesn't have one, that's just facts β
π STANDARDS π
Linux. periodt π
π HISTORY π
NS_GET_USERNSβ Linux 4.9. βNS_GET_PARENTβ Linux 4.9. β
π EXAMPLES π
the example below uses the ioctl(2) operations we just talked about to do some simple discovery of namespace relationships β¨ and the shell sessions below show various examples of this program being used, so you can see it in the wild.
trying to get the parent of the initial user namespace fails, because it has no parent. it's the origin story, it has nobody above it π
$ ./ns_show /proc/self/ns/user p;
The parent namespace is outside your namespace scope
okay now create a process running sleep(1) that lives in new user and UTS namespaces, and show that the new UTS namespace is associated with the new user namespace π§
$ unshare -Uu sleep 1000 &
[1] 23235
$ ./ns_show /proc/23235/ns/uts u;
Device/Inode of owning user namespace is: [0,3] / 4026532448
$ readlink /proc/23235/ns/user;
user:[4026532448]
then show that the parent of the new user namespace from the last example is the initial user namespace π
$ readlink /proc/self/ns/user;
user:[4026531837]
$ ./ns_show /proc/23235/ns/user p;
Device/Inode of parent namespace is: [0,3] / 4026531837
now start a shell in a new user namespace, and show that from inside that shell, the parent user namespace can't be discovered. same energy for the UTS namespace (which is associated with the initial user namespace) β also can't be discovered π
$ PS1="sh2$ " unshare -U bash;
sh2$ ./ns_show /proc/self/ns/user p;
The parent namespace is outside your namespace scope
sh2$ ./ns_show /proc/self/ns/uts u;
The owning user namespace is outside your namespace scope
π₯οΈ Program source π₯οΈ
/* ns_show.c
Licensed under the GNU General Public License v2 or later.
*/
#include <errno.h>
#include <fcntl.h>
#include <linux/nsfs.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
int fd, userns_fd, parent_fd;
struct stat sb;
if (argc < 2) {
fprintf(stderr, "Usage: %s /proc/[pid]/ns/[file] [p|u]\n",
argv[0]);
fprintf(stderr, "\nDisplay the result of one or both "
"of NS_GET_USERNS (u) or NS_GET_PARENT (p)\n"
"for the specified /proc/[pid]/ns/[file]. If neither "
"'p' nor 'u' is specified,\n"
"NS_GET_USERNS is the default.\n");
exit(EXIT_FAILURE);
}
/* Obtain a file descriptor for the 'ns' file specified
in argv[1]. */
fd = open(argv[1], O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
/* Obtain a file descriptor for the owning user namespace and
then obtain and display the inode number of that namespace. */
if (argc < 3 || strchr(argv[2], 'u')) {
userns_fd = ioctl(fd, NS_GET_USERNS);
if (userns_fd == -1) {
if (errno == EPERM)
printf("The owning user namespace is outside "
"your namespace scope\n");
else
perror("ioctl-NS_GET_USERNS");
exit(EXIT_FAILURE);
}
if (fstat(userns_fd, &sb) == -1) {
perror("fstat-userns");
exit(EXIT_FAILURE);
}
printf("Device/Inode of owning user namespace is: "
"[%x,%x] / %ju\n",
major(sb.st_dev),
minor(sb.st_dev),
(uintmax_t) sb.st_ino);
close(userns_fd);
}
/* Obtain a file descriptor for the parent namespace and
then obtain and display the inode number of that namespace. */
if (argc > 2 && strchr(argv[2], 'p')) {
parent_fd = ioctl(fd, NS_GET_PARENT);
if (parent_fd == -1) {
if (errno == EINVAL)
printf("Can' get parent namespace of a "
"nonhierarchical namespace\n");
else if (errno == EPERM)
printf("The parent namespace is outside "
"your namespace scope\n");
else
perror("ioctl-NS_GET_PARENT");
exit(EXIT_FAILURE);
}
if (fstat(parent_fd, &sb) == -1) {
perror("fstat-parentns");
exit(EXIT_FAILURE);
}
printf("Device/Inode of parent namespace is: [%x,%x] / %ju\n",
major(sb.st_dev),
minor(sb.st_dev),
(uintmax_t) sb.st_ino);
close(parent_fd);
}
exit(EXIT_SUCCESS);
}
π SEE ALSO π
and that's the whole page bestie, you ate it up π this one came from Linux man-pages 6.20, dated 2026-02-08, manual page NS_GET_USERNS(2const). go forth and inspect those namespaces, mwah π«Άβ¨
π¦ where this page comes from
- arch rolling: man-pages 6.20-1 Β· BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- π the page's license: Linux-man-pages-copyleft
- Copyright 2017, Michael Kerrisk <mtk.manpages@gmail.com>
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms