diff --git a/man2/mount_setattr.2 b/man2/mount_setattr.2
new file mode 100644
index 000000000..23d1a1036
--- /dev/null
+++ b/man2/mount_setattr.2
@@ -0,0 +1,1071 @@
+.\" Copyright (c) 2021 by Christian Brauner <christian.brauner@ubuntu.com>
+.\"
+.\" %%%LICENSE_START(VERBATIM)
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one.
+.\"
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date.  The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein.  The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\"
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\" %%%LICENSE_END
+.\"
+.TH MOUNT_SETATTR 2 2020-07-14 "Linux" "Linux Programmer's Manual"
+.SH NAME
+mount_setattr \- change mount options of a mount or mount tree
+.SH SYNOPSIS
+.nf
+.BI "int mount_setattr(int " dfd ", const char *" path ", unsigned int " flags ,
+.BI "                  struct mount_attr *" attr ", size_t " size );
+.fi
+.PP
+.IR Note :
+There is no glibc wrapper for this system call; see NOTES.
+.SH DESCRIPTION
+The
+.BR mount_setattr (2)
+system call changes the mount properties of a mount or whole mount tree.
+If
+.I path
+is a relative pathname, then it is interpreted relative to the directory
+referred to by the file descriptor
+.I dirfd
+(or the current working directory of the calling process, if
+.I dirfd
+is the special value
+.BR AT_FDCWD ).
+If
+.BR AT_EMPTY_PATH
+is specified in
+.I flags
+then the mount properties of the mount identified by
+.I dirfd
+are changed.
+.PP
+The
+.BR mount_setattr (2)
+syscall uses an extensible structure (\fIstruct mount_attr\fP) to allow for
+future extensions. Any future extensions to
+.BR mount_setattr (2)
+will be implemented as new fields appended to the above structure,
+with a zero value in a new field resulting in the kernel behaving
+as though that extension field was not present.
+Therefore, the caller
+.I must
+zero-fill this structure on
+initialization.
+(See the "Extensibility" section of the
+.B NOTES
+for more detail on why this is necessary.)
+.PP
+The
+.I size
+argument should usually be specified as
+.IR "sizeof(struct mount_attr)" .
+However, if the caller does not intend to make use of features that got
+introduced after the initial version of \fIstruct mount_attr\fP they are free
+to pass the size of the initial struct together with the larger struct. This
+allows the kernel to not copy later parts of the struct that aren't used
+anyway. With each extension that changes the size of \fIstruct mount_attr\fP
+the kernel will expose a define of the form
+.B MOUNT_ATTR_SIZE_VER<number> .
+For example the macro for the size of the initial version of \fIstruct
+mount_attr\fP is
+.BR MOUNT_ATTR_SIZE_VER0
+.\"
+.PP
+The
+.I flags
+argument can be used to alter the path resolution behavior. The supported
+values are:
+.TP
+.in +4n
+.B AT_EMPTY_PATH
+.in +4n
+The mount properties of the mount identified by
+.I dfd
+are changed.
+.TP
+.in +4n
+.B AT_RECURSIVE
+.in +4n
+Change the mount properties of the whole mount tree.
+.TP
+.in +4n
+.B AT_SYMLINK_NOFOLLOW
+.in +4n
+Don't follow trailing symlinks.
+.TP
+.in +4n
+.B AT_NO_AUTOMOUNT
+.in +4n
+Don't trigger automounts.
+.PP
+The
+.I attr
+argument of
+.BR mount_setattr (2)
+is a structure of the following form:
+.PP
+.in +4n
+.EX
+struct mount_attr {
+    u64 attr_set;    /* Mount properties to set. */
+    u64 attr_clr;    /* Mount properties to clear. */
+    u64 propagation; /* Mount propagation type. */
+    u64 userns_fd;   /* User namespace file descriptor. */
+};
+.EE
+.in
+.PP
+The
+.I attr_set
+and
+.I attr_clr
+members are used to specify the mount options that are supposed to be set or
+cleared for a given mount or mount tree.
+.PP
+When changing mount properties the kernel will first lower the flags specified
+in the
+.I attr_clr
+field and then raise the flags specified in the
+.I attr_set
+field:
+.PP
+.in +4n
+.EX
+	struct mount_attr attr = {
+	    .attr_clr |= MOUNT_ATTR_NOEXEC | MOUNT_ATTR_NODEV,
+	    .attr_set |= MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID,
+	};
+	unsigned int current_mnt_flags = mnt->mnt_flags;
+
+	/*
+	 * Clear all flags raised in .attr_clr, i.e
+	 * clear MOUNT_ATTR_NOEXEC and MOUNT_ATTR_NODEV.
+	 */
+	current_mnt_flags &= ~attr->attr_clr;
+
+	/*
+	 * Now raise all flags raised in .attr_set, i.e.
+	 * set MOUNT_ATTR_RDONLY and MOUNT_ATTR_NOSUID.
+	 */
+	current_mnt_flags |= attr->attr_set;
+
+	mnt->mnt_flags = current_mnt_flags;
+.EE
+.in
+.PP
+The effect of this change will be a mount or mount tree that is read-only,
+blocks the execution of setuid binaries but does allow interactions with
+executables and devices nodes. Multiple changes with the same set of flags
+requested in
+.I attr_clr
+and
+.I attr_set
+are guaranteed to be idempotent after the changes have been applied.
+.PP
+The following mount attributes can be specified in the
+.I attr_set
+or
+.I attr_clr
+fields:
+.TP
+.in +4n
+.B MOUNT_ATTR_RDONLY
+.in +4n
+If set in
+.I attr_set
+makes the mount read only and if set in
+.I attr_clr
+removes the read only setting if set on the mount.
+.TP
+.in +4n
+.B MOUNT_ATTR_NOSUID
+.in +4n
+If set in
+.I attr_set
+makes the mount not honor setuid, setgid binaries, and file capabilities when
+executing programs. If set in
+.I attr_clr
+clears the setuid, setgid, and file capability restriction if set on this
+mount.
+.TP
+.in +4n
+.B MOUNT_ATTR_NODEV
+.in +4n
+If set in
+.I attr_set
+prevents access to devices on this mount
+and if set in
+.I attr_clr
+removes the device access restriction if set on this mount.
+.TP
+.in +4n
+.B MOUNT_ATTR_NOEXEC
+.in +4n
+If set in
+.I attr_set
+prevents executing programs on this mount
+and if set in
+.I attr_clr
+removes the restriction to execute programs on this mount.
+.TP
+.in +4n
+.B MOUNT_ATTR_NODIRATIME
+.in +4n
+If set in
+.I attr_set
+prevents updating access time for directories on this mount
+and if set in
+.I attr_clr
+removes access time restriction for directories. Note that
+.BR MOUNT_ATTR_NODIRATIME
+can be combined with other access time settings and is implied
+by the noatime setting. All other access time settings are mutually
+exclusive.
+.TP
+.in +4n
+.B MOUNT_ATTR__ATIME - Changing access time settings
+.in +4n
+In the new mount api the access time values are an enum starting from 0.
+Even though they are an enum in contrast to the other mount flags such as
+.BR MOUNT_ATTR_NOEXEC
+they are nonetheless passed in
+.I attr_set
+and
+.I attr_clr
+to keep the uapi consistent since
+.BR fsmount (2)
+has the same behavior.
+.IP
+.in +4n
+Note, since access times are an enum, not a bitmap, users wanting to transition
+to a different access time setting cannot simply specify the access time in
+.I attr_set
+but must also set
+.BR MOUNT_ATTR__ATIME
+in the
+.I attr_clr
+field. The kernel will verify that
+.BR MOUNT_ATTR__ATIME
+isn't partially set in
+.I attr_clr
+and that
+.I attr_set
+doesn't have any access time bits set if
+.BR MOUNT_ATTR__ATIME
+isn't set in
+.I attr_clr.
+.TP
+.in +8n
+.B MOUNT_ATTR_RELATIME
+.in +8n
+When a file is accessed via this mount, update the file's last access time
+(atime) only if the current value of atime is less than or equal to the file's
+last modification time (mtime) or last status change time (ctime).
+.IP
+.in +8n
+To enable this access time setting on a mount or mount tree
+.BR MOUNT_ATTR_RELATIME
+must be set in
+.I attr_set
+and
+.BR MOUNT_ATTR__ATIME
+must be set in the
+.I attr_clr
+field.
+.TP
+.in +8n
+.BR MOUNT_ATTR_NOATIME
+.in +8n
+Do not update access times for (all types of) files on this mount.
+.IP
+.in +8n
+To enable this access time setting on a mount or mount tree
+.BR MOUNT_ATTR_NOATIME
+must be set in
+.I attr_set
+and
+.BR MOUNT_ATTR__ATIME
+must be set in the
+.I attr_clr
+field.
+.TP
+.in +8n
+.BR MOUNT_ATTR_STRICTATIME
+.in +8n
+Always update the last access time (atime) when files are
+accessed on this mount.
+.IP
+.in +8n
+To enable this access time setting on a mount or mount tree
+.BR MOUNT_ATTR_STRICTATIME
+must be set in
+.I attr_set
+and
+.BR MOUNT_ATTR__ATIME
+must be set in the
+.I attr_clr
+field.
+.TP
+.in +4n
+.BR MOUNT_ATTR_IDMAP
+.in +4n
+If set in
+.I attr_set
+creates an idmapped mount. The idmapping is taken from the user namespace
+specified in
+.I userns_fd
+and attached to the mount. It is currently not supported to change the
+idmapping of a mount after it has been idmapped. Therefore, it is invalid to
+specify
+.BR MOUNT_ATTR_IDMAP
+in
+.I attr_clr.
+More details can be found in subsequent paragraphs.
+.IP
+.in +4n
+Creating an idmapped mount allows to change the ownership of all files located
+under a given mount. Other mounts that expose the same files will not be
+affected, i.e. the ownership will not be changed. Consequently, a caller
+accessing files through an idmapped mount will see files under an idmapped
+mount owned by the uid and gid as specified in the idmapping attached to the
+mount.
+.IP
+.in +4n
+The idmapping is also applied to the following
+.BR xattr (7)
+namespaces:
+.RS
+.RS
+.IP \(bu 2
+The
+.I security.
+namespace when interacting with filesystem capabilities through the
+.I security.capability
+key whenever filesystem
+.BR capabilities (7)
+are stored or returned in the
+.I VFS_CAP_REVISION_3
+format which stores a rootid alongside the capabilities.
+.IP \(bu 2
+The
+.I system.posix_acl_access
+and
+.I system.posix_acl_default
+keys whenever uids or gids are stored in
+.BR ACL_USER
+and
+.BR ACL_GROUP
+entries.
+.RE
+.RE
+.IP
+.in +4n
+The following conditions must be met in order to create an idmapped mount:
+.RS
+.RS
+.IP \(bu 2
+The caller must currently have the
+.I CAP_SYS_ADMIN
+capability in the user namespace the underlying filesystem has been mounted in.
+.IP \(bu
+The underlying filesystem must support idmapped mounts. Currently
+.BR xfs (5),
+.BR ext4 (5)
+and
+.BR fat
+filesystems support idmapped mounts with more filesystems being actively worked
+on.
+.IP \(bu
+The mount must not already be idmapped. This also implies that the idmapping of
+a mount cannot be altered.
+.IP \(bu
+The mount must be a detached/anonymous mount, i.e. it must have been created by
+calling
+.BR open_tree (2)
+with the
+.I OPEN_TREE_CLONE
+flag and it must not already have been visible in the filesystem.
+.RE
+.IP
+.RE
+.IP
+.in +4n
+In the common case the user namespace passed in
+.I userns_fd
+together with
+.BR MOUNT_ATTR_IDMAP
+in
+.I attr_set
+to create an idmapped mount will be the user namespace of a container. In other
+scenarios it will be a dedicated user namespace associated with a given user's
+login session as is the case for portable home directories in
+.BR systemd-homed.service (8)).
+Details on how to create user namespaces and how to setup idmappings can be
+gathered from
+.BR user_namespaces (7).
+.IP
+.in +4n
+In essence, an idmapping associated with a user namespace is a 1-to-1 mapping
+between source and target ids for a given range. Specifically, an idmapping
+always has the abstract form
+.I [type of id] [source id] [target id] [range].
+For example, uid 1000 1001 1 would mean that uid 1000 is mapped to uid 1001,
+gid 1000 1001 2 would mean that gid 1000 will be mapped to gid 1001 and gid
+1001 to gid 1002. If we were to attach the idmapping of uid 1000 1001 1 to a
+mount it would cause all files owned by uid 1000 to be owned by uid 1001. It is
+possible to specify up to 340 of such idmappings providing for a great deal of
+flexibility. If any source ids are not mapped to a target id all files owned by
+that unmapped source id will appear as being owned by the overflow uid or
+overflow gid respectively (see
+.BR user_namespaces (7)
+and
+.BR proc (5)).
+.IP
+.in +4n
+Idmapped mounts can be useful in the following and a variety of other
+scenarios:
+.RS
+.RS
+.IP \(bu 2
+Idmapped mounts make it possible to easily share files between multiple users
+or multiple machines especially in complex scenarios. For example, idmapped
+mounts are used to implement portable home directories in
+.BR systemd-homed.service (8)
+whre they allow users to move their home directory to an external storage
+device and use it on multiple computers where they are assigned different uids
+and gids. This effectively makes it possible to assign random uids and gids at
+login time.
+.IP \(bu
+It is possible to share files from the host with unprivileged containers
+without having to change ownership permanently through
+.BR chown (2).
+.IP \(bu
+It is possible to idmap a container's rootfs without having to mangle every
+file.
+.IP \(bu
+It is possible to share files between containers with non-overlapping
+idmappings
+.IP \(bu
+Filesystem that lack a proper concept of ownership such as fat can use idmapped
+mounts to implement discretionary access (DAC) permission checking.
+.IP \(bu
+They allow users to
+efficiently change ownership on a per-mount basis without having to
+(recursively)
+.BR chown (2)
+all files. In contrast to
+.BR chown (2)
+changing ownership of large sets of files is instantenous with idmapped mounts.
+This is especially useful when ownership of a whole root filesystem of a
+virtual machine or container is to be changed.  With idmapped mounts a single
+.BR mount_setattr (2)
+syscall will be sufficient to change the ownership of all files.
+.IP \(bu
+Idmapped mounts always take the current ownership into account as
+idmappings specify what a given uid or gid is supposed to be mapped to. This
+contrasts with the
+.BR chown (2)
+syscall which cannot by itself take the current ownership of the files it
+changes into account. It simply changes the ownership to the specified uid and
+gid.
+.IP \(bu
+Idmapped mounts allow to change ownership locally, restricting it
+to specific mounts, and temporarily as the ownership changes only apply as long
+as the mount exists. In contrast, changing ownership via the
+.BR chown (2)
+syscall changes the ownership globally and permanently.
+.RE
+.RE
+.IP
+.in +4n
+.PP
+The
+.I propagation
+field is used to specify the propagation type of the mount or mount tree. Only
+one propagation type can be specified, i.e. the propagation values behave like
+an enum. The supported mount propagation settings are:
+.TP
+.in +4n
+.B MS_PRIVATE
+.in +4n
+Turn all mounts into private mounts. Mount and umount events do not propagate
+into or out of this mount point.
+.TP
+.in +4n
+.B MS_SHARED
+.in +4n
+Turn all mounts into shared mounts. Mount points share events with members of a
+peer group. Mount and unmount events immediately under this mount point
+will propagate to the other mount points that are members of the peer group.
+Propagation here means that the same mount or unmount will automatically occur
+under all of the other mount points in the peer group. Conversely, mount and
+unmount events that take place under peer mount points will propagate to this
+mount point.
+.TP
+.in +4n
+.B MS_SLAVE
+.in +4n
+Turn all mounts into dependent mounts. Mount and unmount events propagate into
+this mount point from a shared  peer group. Mount and unmount events under this
+mount point do not propagate to any peer.
+.TP
+.in +4n
+.B MS_UNBINDABLE
+.in +4n
+This is like a private mount, and in addition this mount can't be bind mounted.
+Attempts to bind mount this mount will fail.
+When a recursive bind mount is performed on a directory subtree, any bind
+mounts within the subtree are automatically pruned (i.e., not replicated) when
+replicating that subtree to produce the target subtree.
+.PP
+.SH RETURN VALUE
+On success,
+.BR mount_setattr (2)
+zero is returned. On error, \-1 is returned and
+.I errno
+is set to indicate the cause of the error.
+.SH ERRORS
+.TP
+.B EBADF
+.I dfd
+is not a valid file descriptor.
+.TP
+.B EBADF
+An invalid file descriptor value was specified in
+.I userns_fd.
+.TP
+.B EBUSY
+The caller tried to change the mount to
+.BR MOUNT_ATTR_RDONLY
+but the mount had writers.
+.TP
+.B EINVAL
+The path specified via the
+.I dfd
+and
+.I path
+arguments to
+.BR mount_setattr (2)
+isn't a mountpoint.
+.TP
+.B EINVAL
+Unsupported value in
+.I flags
+.TP
+.B EINVAL
+Unsupported value was specified in the
+.I attr_set
+field of
+.IR mount_attr.
+.TP
+.B EINVAL
+Unsupported value was specified in the
+.I attr_clr
+field of
+.IR mount_attr.
+.TP
+.B EINVAL
+Unsupported value was specified in the
+.I propagation
+field of
+.IR mount_attr.
+.TP
+.B EINVAL
+More than one of
+.BR MS_SHARED,
+.BR MS_SLAVE,
+.BR MS_PRIVATE,
+and
+.BR MS_UNBINDABLE
+was set in
+.I propagation
+field of
+.IR mount_attr.
+.TP
+.B EINVAL
+An access time setting was specified in the
+.I attr_set
+field without
+.BR MOUNT_ATTR__ATIME
+being set in the
+.I attr_clr
+field.
+.TP
+.B EINVAL
+.BR MOUNT_ATTR_IDMAP
+was specified in
+.I attr_clr.
+.TP
+.B EINVAL
+A file descriptor value was specified in
+.I userns_fd
+which exceeds
+.BR INT_MAX.
+.TP
+.B EINVAL
+A valid file descriptor value was specified in
+.I userns_fd
+but the file descriptor wasn't a namespace file descriptor or did not refer to
+a user namespace.
+.TP
+.B EINVAL
+The underlying filesystem does not support idmapped mounts.
+.TP
+.B EINVAL
+The mount to idmap is not a detached/anonymous mount, i.e. the mount is already
+visible in the filesystem.
+.TP
+.B EINVAL
+A partial access time setting was specified in
+.I attr_clr
+instead of
+.BR MOUNT_ATTR__ATIME
+being set.
+.TP
+.B EINVAL
+Caller tried to change the mount properties of a mount or mount tree
+in another mount namespace.
+.TP
+.B ENOENT
+A pathname was empty or had a nonexistent component.
+.TP
+.B ENOMEM
+When changing mount propagation to
+.BR MS_SHARED
+a new peer group id needs to be allocated for all mounts without a peer group
+id set which are
+.BR MS_SHARED.
+Allocation of this peer group id has failed.
+.TP
+.B ENOSPC
+When changing mount propagation to
+.BR MS_SHARED
+a new peer group id needs to be allocated for all mounts without a peer group
+id set which are
+.BR MS_SHARED. Allocation of this peer group id can fail. Note that technically
+further error codes are possible that are specific to the id allocation
+implementation used.
+.TP
+.B EPERM
+One of the mounts had at least one of
+.BR MOUNT_ATTR_RDONLY,
+.BR MOUNT_ATTR_NODEV,
+.BR MOUNT_ATTR_NOSUID,
+.BR MOUNT_ATTR_NOEXEC,
+.BR MOUNT_ATTR_NOATIME,
+or
+.BR MOUNT_ATTR_NODIRATIME
+set and the flag is locked. Mount attributes become locked on a mount if:
+.RS
+.IP \(bu 2
+a new mount or mount tree is created causing mount propagation across user
+namespaces. The kernel will lock the aforementioned flags to protect these
+sensitive properties from being altered.
+.IP \(bu
+a new mount and user namespace pair is created. This happens for example when
+specifying
+.BR CLONE_NEWUSER | CLONE_NEWNS
+in
+.BR unshare (2),
+.BR clone (2),
+or
+.BR clone3 (2).
+The aformentioned flags become locked to protect user namespaces from altering
+sensitive mount properties.
+.RE
+.TP
+.B EPERM
+A valid file descriptor value was specified in
+.I userns_fd
+but the file descriptor refers to the initial user namespace.
+.TP
+.B EPERM
+An already idmapped mount was supposed to be idmapped.
+.TP
+.B EPERM
+The caller does not have
+.I CAP_SYS_ADMIN
+in the user namespace the underlying filesystem is mounted in.
+.SH VERSIONS
+.BR mount_setattr (2)
+first appeared in Linux 5.12.
+.\" commit 7d6beb71da3cc033649d641e1e608713b8220290
+.\" commit 2a1867219c7b27f928e2545782b86daaf9ad50bd
+.\" commit 9caccd41541a6f7d6279928d9f971f6642c361af
+.SH CONFORMING TO
+.BR mount_setattr (2)
+is Linux specific.
+.SH NOTES
+Currently, there is no glibc wrapper for this system call; call it using
+.BR syscall (2).
+.\"
+.SS Extensibility
+In order to allow for future extensibility,
+.BR mount_setattr (2)
+equivalent to
+.BR openat2 (2)
+and
+.BR clone3 (2)
+requires the user-space application to specify the size of the
+.I mount_attr
+structure that it is passing.
+By providing this information, it is possible for
+.BR mount_setattr (2)
+to provide both forwards- and backwards-compatibility, with
+.I size
+acting as an implicit version number.
+(Because new extension fields will always
+be appended, the structure size will always increase.)
+This extensibility design is very similar to other system calls such as
+.BR perf_setattr (2),
+.BR perf_event_open (2),
+.BR clone3 (2)
+and
+.BR openat2 (2)
+.PP
+If we let
+.I usize
+be the size of the structure as specified by the user-space application, and
+.I ksize
+be the size of the structure which the kernel supports, then there are
+three cases to consider:
+.IP \(bu 2
+If
+.IR ksize
+equals
+.IR usize ,
+then there is no version mismatch and
+.I how
+can be used verbatim.
+.IP \(bu
+If
+.IR ksize
+is larger than
+.IR usize ,
+then there are some extension fields that the kernel supports
+which the user-space application
+is unaware of.
+Because a zero value in any added extension field signifies a no-op,
+the kernel
+treats all of the extension fields not provided by the user-space application
+as having zero values.
+This provides backwards-compatibility.
+.IP \(bu
+If
+.IR ksize
+is smaller than
+.IR usize ,
+then there are some extension fields which the user-space application
+is aware of but which the kernel does not support.
+Because any extension field must have its zero values signify a no-op,
+the kernel can
+safely ignore the unsupported extension fields if they are all-zero.
+If any unsupported extension fields are non-zero, then \-1 is returned and
+.I errno
+is set to
+.BR E2BIG .
+This provides forwards-compatibility.
+.PP
+Because the definition of
+.I struct mount_attr
+may change in the future (with new fields being added when system headers are
+updated), user-space applications should zero-fill
+.I struct mount_attr
+to ensure that recompiling the program with new headers will not result in
+spurious errors at runtime.
+The simplest way is to use a designated
+initializer:
+.PP
+.in +4n
+.EX
+struct mount_attr attr = {
+    .attr_set = MOUNT_ATTR_RDONLY,
+    .attr_clr = MOUNT_ATTR_NODEV
+};
+.EE
+.in
+.PP
+or explicitly using
+.BR memset (3)
+or similar:
+.PP
+.in +4n
+.EX
+struct mount_attr attr;
+memset(&attr, 0, sizeof(attr));
+attr.attr_set = MOUNT_ATTR_RDONLY;
+attr.attr_clr = MOUNT_ATTR_NODEV;
+.EE
+.in
+.PP
+A user-space application that wishes to determine which extensions
+the running kernel supports can do so by conducting a binary search on
+.IR size
+with a structure which has every byte nonzero (to find the largest value
+which doesn't produce an error of
+.BR E2BIG ).
+.SH EXAMPLES
+The following program allows the caller to create a new detached mount and set
+various properties on it.
+.\"
+.SS Program source
+\&
+.nf
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/mount.h>
+#include <linux/types.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+/* mount_setattr() */
+#ifndef MOUNT_ATTR_RDONLY
+#define MOUNT_ATTR_RDONLY 0x00000001
+#endif
+
+#ifndef MOUNT_ATTR_NOSUID
+#define MOUNT_ATTR_NOSUID 0x00000002
+#endif
+
+#ifndef MOUNT_ATTR_NOEXEC
+#define MOUNT_ATTR_NOEXEC 0x00000008
+#endif
+
+#ifndef MOUNT_ATTR__ATIME
+#define MOUNT_ATTR__ATIME 0x00000070
+#endif
+
+#ifndef MOUNT_ATTR_NOATIME
+#define MOUNT_ATTR_NOATIME 0x00000010
+#endif
+
+#ifndef MOUNT_ATTR_IDMAP
+#define MOUNT_ATTR_IDMAP 0x00100000
+#endif
+
+#ifndef AT_RECURSIVE
+#define AT_RECURSIVE 0x8000
+#endif
+
+#ifndef __NR_mount_setattr
+    #if defined __alpha__
+        #define __NR_mount_setattr 552
+    #elif defined _MIPS_SIM
+        #if _MIPS_SIM == _MIPS_SIM_ABI32    /* o32 */
+            #define __NR_mount_setattr (442 + 4000)
+        #endif
+        #if _MIPS_SIM == _MIPS_SIM_NABI32   /* n32 */
+            #define __NR_mount_setattr (442 + 6000)
+        #endif
+        #if _MIPS_SIM == _MIPS_SIM_ABI64    /* n64 */
+            #define __NR_mount_setattr (442 + 5000)
+        #endif
+    #elif defined __ia64__
+        #define __NR_mount_setattr (442 + 1024)
+    #else
+        #define __NR_mount_setattr 442
+    #endif
+struct mount_attr {
+    __u64 attr_set;
+    __u64 attr_clr;
+    __u64 propagation;
+    __u64 userns_fd;
+};
+#endif
+
+/* open_tree() */
+#ifndef OPEN_TREE_CLONE
+#define OPEN_TREE_CLONE 1
+#endif
+
+#ifndef OPEN_TREE_CLOEXEC
+#define OPEN_TREE_CLOEXEC O_CLOEXEC
+#endif
+
+#ifndef __NR_open_tree
+    #if defined __alpha__
+        #define __NR_open_tree 538
+    #elif defined _MIPS_SIM
+        #if _MIPS_SIM == _MIPS_SIM_ABI32    /* o32 */
+            #define __NR_open_tree 4428
+        #endif
+        #if _MIPS_SIM == _MIPS_SIM_NABI32   /* n32 */
+            #define __NR_open_tree 6428
+        #endif
+        #if _MIPS_SIM == _MIPS_SIM_ABI64    /* n64 */
+            #define __NR_open_tree 5428
+        #endif
+    #elif defined __ia64__
+        #define __NR_open_tree (428 + 1024)
+    #else
+        #define __NR_open_tree 428
+    #endif
+#endif
+
+/* move_mount() */
+#ifndef MOVE_MOUNT_F_EMPTY_PATH
+#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004
+#endif
+
+#ifndef __NR_move_mount
+    #if defined __alpha__
+        #define __NR_move_mount 539
+    #elif defined _MIPS_SIM
+        #if _MIPS_SIM == _MIPS_SIM_ABI32    /* o32 */
+            #define __NR_move_mount 4429
+        #endif
+        #if _MIPS_SIM == _MIPS_SIM_NABI32   /* n32 */
+            #define __NR_move_mount 6429
+        #endif
+        #if _MIPS_SIM == _MIPS_SIM_ABI64    /* n64 */
+            #define __NR_move_mount 5429
+        #endif
+    #elif defined __ia64__
+        #define __NR_move_mount (428 + 1024)
+    #else
+        #define __NR_move_mount 429
+    #endif
+#endif
+
+static inline int mount_setattr(int dfd, const char *path, unsigned int flags,
+                                struct mount_attr *attr, size_t size)
+{
+    return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
+}
+
+static inline int open_tree(int dfd, const char *filename, unsigned int flags)
+{
+    return syscall(__NR_open_tree, dfd, filename, flags);
+}
+
+static inline int move_mount(int from_dfd, const char *from_pathname, int to_dfd,
+                 const char *to_pathname, unsigned int flags)
+{
+    return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd,
+                   to_pathname, flags);
+}
+
+static const struct option longopts[] = {
+    {"map-mount",       required_argument,  0,  'a'},
+    {"recursive",       no_argument,        0,  'b'},
+    {"read-only",       no_argument,        0,  'c'},
+    {"block-setid",     no_argument,        0,  'd'},
+    {"block-devices",   no_argument,        0,  'e'},
+    {"block-exec",      no_argument,        0,  'f'},
+    {"no-access-time",  no_argument,        0,  'g'},
+    { NULL,             0,                  0,   0 },
+};
+
+#define exit_log(format, ...)                   \\
+    ({                                          \\
+        fprintf(stderr, format, ##__VA_ARGS__); \\
+        exit(EXIT_FAILURE);                     \\
+    })
+
+int main(int argc, char *argv[])
+{
+    int fd_userns = -EBADF, index = 0;
+    bool recursive = false;
+    struct mount_attr *attr = &(struct mount_attr){};
+    const char *source, *target;
+    int fd_tree, new_argc, ret;
+    char *const *new_argv;
+
+    while ((ret = getopt_long_only(argc, argv, "", longopts, &index)) != -1) {
+        switch (ret) {
+        case 'a':
+            fd_userns = open(optarg, O_RDONLY | O_CLOEXEC);
+            if (fd_userns < 0)
+                exit_log("%m - Failed top open user namespace path %s\n", optarg);
+            break;
+        case 'b':
+            recursive = true;
+            break;
+        case 'c':
+            attr->attr_set |= MOUNT_ATTR_RDONLY;
+            break;
+        case 'd':
+            attr->attr_set |= MOUNT_ATTR_NOSUID;
+            break;
+        case 'e':
+            attr->attr_set |= MOUNT_ATTR_NODEV;
+            break;
+        case 'f':
+            attr->attr_set |= MOUNT_ATTR_NOEXEC;
+            break;
+        case 'g':
+            attr->attr_set |= MOUNT_ATTR_NOATIME;
+            attr->attr_clr |= MOUNT_ATTR__ATIME;
+            break;
+        default:
+            exit_log("Invalid argument specified");
+        }
+    }
+
+    new_argv = &argv[optind];
+    new_argc = argc - optind;
+    if (new_argc < 2)
+        exit_log("Missing source or target mountpoint\n");
+    source = new_argv[0];
+    target = new_argv[1];
+
+    fd_tree = open_tree(-EBADF, source,
+                        OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_EMPTY_PATH |
+                        (recursive ? AT_RECURSIVE : 0));
+    if (fd_tree < 0)
+        exit_log("%m - Failed to open %s\n", source);
+
+    if (fd_userns >= 0) {
+        attr->attr_set  |= MOUNT_ATTR_IDMAP;
+        attr->userns_fd = fd_userns;
+    }
+    ret = mount_setattr(fd_tree, "",
+                        AT_EMPTY_PATH | (recursive ? AT_RECURSIVE : 0),
+                        attr, sizeof(struct mount_attr));
+    if (ret < 0)
+        exit_log("%m - Failed to change mount attributes\n");
+    close(fd_userns);
+
+    ret = move_mount(fd_tree, "", -EBADF, target, MOVE_MOUNT_F_EMPTY_PATH);
+    if (ret < 0)
+        exit_log("%m - Failed to attach mount to %s\n", target);
+    close(fd_tree);
+
+    exit(EXIT_SUCCESS);
+}
+.fi
+.SH SEE ALSO
+.BR capabilities (7),
+.BR clone (2),
+.BR clone3 (2),
+.BR ext4 (5),
+.BR mount (2),
+.BR mount_namespaces (7),
+.BR newuidmap (1),
+.BR newgidmap (1),
+.BR proc (5),
+.BR unshare (2),
+.BR user_namespaces (7),
+.BR xattr (7),
+.BR xfs (5)
