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 that fd is pointing at πŸ‘‘
  • NS_GET_PARENT β€” gives you back a file descriptor that refers to the parent namespace of the namespace that fd is pointing at. but heads up bestie: this one only works for hierarchical namespaces (aka PID and user namespaces). for user namespaces, NS_GET_PARENT is literally synonymous with NS_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 β€” fd refers 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 🌟

ioctl(2), ioctl_nsfs(2)


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
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms