open_tree(2)
π open_tree(2) β straight outta the System Calls Manual, section 2 β¨
okay bestie sit DOWN because this one is a whole vibe. open_tree(2) is one of those file-descriptor-based mount facilities in Linux, and she is iconic, she is powerful, and she has two flavours (yes, two, keep reading). Also hi, this is the manpage for it, from the System Calls Manual, section 2. Buckle up, we're getting into the weeds and the weeds are gorgeous π¦
πΈ NAME πΈ
open_tree β open path or create detached mount object and attach to fd
π LIBRARY π
Standard C library (libc, -lc)
β¨ SYNOPSIS β¨
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Definition of AT_* constants */
#include <sys/mount.h>
int open_tree(int dirfd, const char *path, unsigned int flags);
#include <sys/syscall.h> /* Definition of SYS_* constants */
int syscall(SYS_open_tree_attr,
int dirfd, const char *path, unsigned int flags,
struct mount_attr *_Nullable attr, size_t size);
heads up bestie: glibc provides no wrapper foropen_tree_attr(), which is lowkey annoying but whatever β you gotta usesyscall(2)for that one. fr fr.
π DESCRIPTION π
The open_tree() system call is part of the suite of file-descriptor-based mount facilities in Linux. she's in the family, she belongs there, periodt.
- If
flagscontainsOPEN_TREE_CLONE,open_tree()creates a detached mount object which consists of a bind-mount of the path specified by thepath. A new file descriptor associated with the detached mount object is then returned. The mount object is equivalent to a bind-mount that would be created bymount(2)called withMS_BIND, except that it is tied to a file descriptor and is not mounted onto the filesystem.
As with file descriptors returned from fsmount(2), the resultant file descriptor can then be used with move_mount(2), mount_setattr(2), or other such system calls to do further mount operations.
This mount object will be unmounted and destroyed when the file descriptor is closed if it was not otherwise attached to a mount point by calling move_mount(2). This implicit unmount operation is lazyβakin to calling umount2(2) with MNT_DETACH; thus, any existing open references to files from the mount object will continue to work, and the mount object will only be completely destroyed once it ceases to be busy. π (busy mount = still hanging on, it's giving "I'll leave when I'm ready")
- If
flagsdoes not containOPEN_TREE_CLONE,open_tree()returns a file descriptor that is exactly equivalent to one produced byopenat(2)when called with the samedirfdandpath.
In either case, the resultant file descriptor acts the same as one produced by open(2) with O_PATH, meaning it can also be used as a dirfd argument to "*at()" system calls. However, unlike open(2) called with O_PATH, automounts will by default be triggered by open_tree() unless AT_NO_AUTOMOUNT is included in flags. π
As with "*at()" system calls, open_tree() uses the dirfd argument in conjunction with the path argument to determine the path to operate on, as follows:
- If the pathname given in
pathis absolute, thendirfdis ignored. (she said what she said) - If the pathname given in
pathis relative anddirfdis the special valueAT_FDCWD, thenpathis interpreted relative to the current working directory of the calling process (likeopen(2)). - If the pathname given in
pathis relative, then it is interpreted relative to the directory referred to by the file descriptordirfd(rather than relative to the current working directory of the calling process, as is done byopen(2)for a relative pathname). In this case,dirfdmust be a directory that was opened for reading (O_RDONLY) or using theO_PATHflag. - If
pathis an empty string, andflagscontainsAT_EMPTY_PATH, then the file descriptordirfdis operated on directly. In this case,dirfdmay refer to any type of file, not just a directory.
See openat(2) for an explanation of why the dirfd argument is useful.
flags can be used to control aspects of the path lookup and properties of the returned file descriptor. A value for flags is constructed by bitwise ORing zero or more of the following constants:
AT_EMPTY_PATHβ Ifpathis an empty string, operate on the file referred to bydirfd(which may have been obtained fromopen(2),fsmount(2), or from anotheropen_tree()call). In this case,dirfdmay refer to any type of file, not just a directory. IfdirfdisAT_FDCWD,open_tree()will operate on the current working directory of the calling process. This flag is Linux-specific; define_GNU_SOURCEto obtain its definition.AT_NO_AUTOMOUNTβ Do not automount the terminal ("basename") component ofpathif it is a directory that is an automount point. This allows you to create a handle to the automount point itself, rather than the location it would mount. This flag has no effect if the mount point has already been mounted over. This flag is Linux-specific; define_GNU_SOURCEto obtain its definition.AT_SYMLINK_NOFOLLOWβ Ifpathis a symbolic link, do not dereference it; instead, create either a handle to the link itself or a bind-mount of it. The resultant file descriptor is indistinguishable from one produced byopenat(2)withO_PATH|O_NOFOLLLOW.OPEN_TREE_CLOEXECβ Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. See the description of theO_CLOEXECflag inopen(2)for reasons why this may be useful.OPEN_TREE_CLONEβ Rather than creating anopenat(2)-styleO_PATHfile descriptor, create a bind-mount ofpath(akin tomount --bind) as a detached mount object. In order to do this operation, the calling process must have theCAP_SYS_ADMINcapability. πAT_RECURSIVEβ Create a recursive bind-mount of thepath(akin tomount --rbind) as a detached mount object. This flag is only permitted in conjunction withOPEN_TREE_CLONE.
π§ open_tree_attr() π§
The open_tree_attr() system call operates in exactly the same way as open_tree(), except for the differences described here.
After performing the same operation as with open_tree(), open_tree_attr() will apply the mount attribute changes described in attr to the file descriptor before it is returned. (See mount_attr(2type) for a description of the mount_attr structure. As described in mount_setattr(2), size must be set to sizeof(struct mount_attr) in order to support future extensions.) If attr is NULL, or has attr.attr_clr, attr.attr_set, and attr.propagation all set to zero, then open_tree_attr() has identical behavior to open_tree().
The application of attr to the resultant file descriptor has identical semantics to mount_setattr(2), except for the following extensions and general caveats:
- Unlike
mount_setattr(2)called with a regularOPEN_TREE_CLONEdetached mount object fromopen_tree(),open_tree_attr()can specify a different setting forMOUNT_ATTR_IDMAPto the original mount object cloned withOPEN_TREE_CLONE.
Adding MOUNT_ATTR_IDMAP to attr.attr_clr will disable ID-mapping for the new mount object; adding MOUNT_ATTR_IDMAP to attr.attr_set will configure the mount object to have the ID-mapping defined by the user namespace referenced by the file descriptor attr.userns_fd. (The semantics of which are identical to when mount_setattr(2) is used to configure MOUNT_ATTR_IDMAP.)
Changing or removing the mapping of an ID-mapped mount is only permitted if a new detached mount object is being created with flags including OPEN_TREE_CLONE. no cap.
- If
flagscontainsAT_RECURSIVE, then the attributes described inattrare applied recursively (just as whenmount_setattr(2)is called withAT_RECURSIVE). However, this applies in addition to theopen_tree()-specific behavior regardingAT_RECURSIVE, and thusflagsmust also containOPEN_TREE_CLONE.
Note that if flags does not contain OPEN_TREE_CLONE, open_tree_attr() will attempt to modify the mount attributes of the mount object attached at the path described by dirfd and path. As with mount_setattr(2), if said path is not a mount point, open_tree_attr() will return an error. π
π RETURN VALUE π
On success, a new file descriptor is returned. π On error, -1 is returned, and errno is set to indicate the error.
π ERRORS π
EACCESβ Search permission is denied for one of the directories in the path prefix ofpath. (See alsopath_resolution(7).)EBADFβpathis relative butdirfdis neitherAT_FDCWDnor a valid file descriptor.EFAULTβpathisNULLor a pointer to a location outside the calling process's accessible address space.EINVALβ Invalid flag specified inflags.ELOOPβ Too many symbolic links encountered when resolvingpath. (the link loop said "one more time" π)EMFILEβ The calling process has too many open files to create more.ENAMETOOLONGβpathis longer thanPATH_MAX.ENFILEβ The system has too many open files to create more.ENOENTβ A component ofpathdoes not exist, or is a dangling symbolic link.ENOENTβpathis an empty string, butAT_EMPTY_PATHis not specified inflags.ENOTDIRβ A component of the path prefix ofpathis not a directory, orpathis relative anddirfdis a file descriptor referring to a file other than a directory.ENOSPCβ The "anonymous" mount namespace necessary to contain theOPEN_TREE_CLONEdetached bind-mount mount object could not be allocated, as doing so would exceed the configured per-user limit on the number of mount namespaces in the current user namespace. (See alsonamespaces(7).)ENOMEMβ The kernel could not allocate sufficient memory to complete the operation.EPERMβflagscontainsOPEN_TREE_CLONEbut the calling process does not have the requiredCAP_SYS_ADMINcapability.
β STANDARDS β
Linux.
π HISTORY π
open_tree()β Linux 5.2, glibc 2.36.open_tree_attr()β Linux 6.15.
π¦ NOTES π¦
π Mount propagation π
The bind-mount mount objects created by open_tree() with OPEN_TREE_CLONE are not associated with the mount namespace of the calling process. Instead, each mount object is placed in a newly allocated "anonymous" mount namespace associated with the calling process.
One of the side-effects of this is that (unlike bind-mounts created with mount(2)), mount propagation (as described in mount_namespaces(7)) will not be applied to bind-mounts created by open_tree() until the bind-mount is attached with move_mount(2), at which point the mount object will be associated with the mount namespace where it was attached and mount propagation will resume. Note that any mount propagation events that occurred before the mount object was attached will not be propagated to the mount object, even after it is attached. π«Ά (it's giving "you weren't there, so you don't get the tea retroactively")
β¨ EXAMPLES β¨
okay bestie, this is the part where it all clicks. the following examples show how open_tree() can be used in place of the more traditional mount(2) calls with MS_BIND. like, the old way but make it β¨dirfdβ¨.
int srcfd = open_tree(AT_FDCWD, "/var", OPEN_TREE_CLONE);
move_mount(srcfd, "", AT_FDCWD, "/mnt", MOVE_MOUNT_F_EMPTY_PATH);
so first up: a detached bind-mount mount object of /var gets created and associated with the file descriptor srcfd. π
then that mount object gets attached to /mnt using move_mount(2) with MOVE_MOUNT_F_EMPTY_PATH, which is the lil flag that says "hey, take the detached mount object hanging out with the file descriptor srcfd and move it (and thus attach it) onto /mnt."
and the above procedure? functionally equivalent to this mount(2) operation:
mount("/var", "/mnt", NULL, MS_BIND, NULL);
but wait, it gets better. π«Ά OPEN_TREE_CLONE can be combined with AT_RECURSIVE to create recursive detached bind-mount mount objects, which in turn can be attached to mount points to create recursive bind-mounts. it's giving layers. it's giving depth.
int srcfd = open_tree(AT_FDCWD, "/var",
OPEN_TREE_CLONE | AT_RECURSIVE);
move_mount(srcfd, "", AT_FDCWD, "/mnt", MOVE_MOUNT_F_EMPTY_PATH);
and that one is functionally equivalent to:
mount("/var", "/mnt", NULL, MS_BIND | MS_REC, NULL);
ngl one of the primary benefits of using open_tree() and move_mount(2) over the traditional mount(2) is that operating with dirfd-style file descriptors is far easier and more intuitive. no cap. far easier. more intuitive.
int srcfd = open_tree(100, "", AT_EMPTY_PATH | OPEN_TREE_CLONE);
move_mount(srcfd, "", 200, "foo", MOVE_MOUNT_F_EMPTY_PATH);
and that is roughly equivalent to:
mount("/proc/self/fd/100",
"/proc/self/fd/200/foo",
NULL, MS_BIND, NULL);
in addition β and this is the part that lives in my head rent free π β you can use the file descriptor returned by open_tree() as the dirfd argument to any "*at()" system calls:
int dirfd, fd;
dirfd = open_tree(AT_FDCWD, "/etc", OPEN_TREE_CLONE);
fd = openat(dirfd, "passwd", O_RDONLY);
fchmodat(dirfd, "shadow", 0000, 0);
close(dirfd);
close(fd);
/* The bind-mount is now destroyed */
the bind-mount is now destroyed, bestie. it's gone. it served its purpose. π¦
π open_tree_attr() π
okay so the following is an example of how open_tree_attr() can be used to take an existing id-mapped mount and construct a new bind-mount mount object with a different MOUNT_ATTR_IDMAP attribute. and the resultant detached mount object? it can be used like any other mount object returned by open_tree(). iconic behaviour fr fr.
int nsfd1, nsfd2;
int mntfd1, mntfd2, mntfd3;
struct mount_attr attr;
mntfd1 = open_tree(AT_FDCWD, "/foo", OPEN_TREE_CLONE);
/* Configure the id-mapping of mntfd1 */
nsfd1 = open("/proc/1234/ns/user", O_RDONLY);
memset(&attr, 0, sizeof(attr));
attr.attr_set = MOUNT_ATTR_IDMAP;
attr.userns_fd = nsfd1;
mount_setattr(mntfd1, "", AT_EMPTY_PATH, &attr, sizeof(attr));
/* Create a new copy with a different id-mapping */
nsfd2 = open("/proc/5678/ns/user", O_RDONLY);
memset(&attr, 0, sizeof(attr));
attr.attr_clr = MOUNT_ATTR_IDMAP;
attr.attr_set = MOUNT_ATTR_IDMAP;
attr.userns_fd = nsfd2;
mntfd2 = open_tree_attr(mntfd1, "", OPEN_TREE_CLONE,
&attr, sizeof(attr));
/* Create a new copy with the id-mapping cleared */
memset(&attr, 0, sizeof(attr));
attr.attr_clr = MOUNT_ATTR_IDMAP;
mntfd3 = open_tree_attr(mntfd1, "", OPEN_TREE_CLONE,
&attr, sizeof(attr));
and heads up π open_tree_attr() can also be used with attached mount objects β the above example is only intended to be illustrative, so don't come for me if your setup looks different.
π SEE ALSO π
fsconfig(2)fsmount(2)fsopen(2)fspick(2)mount(2)mount_setattr(2)move_mount(2)mount_namespaces(7)
and that's the whole page, bestie, you ate that π this little icon of a manpage is from Linux man-pages 6.20, dated 2026-02-22 β go forth and mount things the cool dirfd way ππ¦β¨
π¦ 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