mount_setattr(2)
Change VFS-layer mount attributes on an existing mount or subtree, optionally recursing.
- Since
- Linux 5.12check your kernel →
- glibc
- none (use syscall(2))
- System
- Linux
- Man page
- mount_setattr(2)
Synopsis
#include <linux/mount.h>
#include <sys/syscall.h>
#include <unistd.h>
struct mount_attr {
__u64 attr_set;
__u64 attr_clr;
__u64 propagation;
__u64 userns_fd;
};
long syscall(SYS_mount_setattr, int dirfd, const char *pathname,
unsigned int flags, struct mount_attr *attr, size_t size);Rod's take
mount(2) MS_REMOUNT silently ignores flag changes on bind mounts and has no subtree mode at all. mount_setattr fixes both: attr_set/attr_clr lets you flip one flag without clobbering others, and AT_RECURSIVE applies the change to every mount in the tree atomically. No glibc wrapper; call it with syscall(SYS_mount_setattr, ...). Pass sizeof(struct mount_attr) as size and zero any fields you're not using, or E2BIG comes back on future kernels that extend the struct. For container setup, combine with open_tree(2) to harden a cloned subtree before it enters the namespace. On kernels before 5.12, ENOSYS; fall back to a findmnt loop with mount --bind --remount and accept that it's not atomic.
Flags and arguments that matter
| Name | What it does |
|---|---|
| MOUNT_ATTR_RDONLY | Make the mount read-only. |
| MOUNT_ATTR_NOSUID | Prevent set-uid and set-gid execution on this mount. |
| MOUNT_ATTR_NODEV | Prevent access to device files on this mount. |
| MOUNT_ATTR_NOEXEC | Prevent execution of programs on this mount. |
| MOUNT_ATTR_NOSYMFOLLOWLinux 5.18 | Prevent symlink following on this mount. |
| MOUNT_ATTR__ATIME | Mask for the three mutually exclusive atime modes: MOUNT_ATTR_RELATIME, MOUNT_ATTR_NOATIME, MOUNT_ATTR_STRICTATIME. Set the desired constant in attr_set and MOUNT_ATTR__ATIME in attr_clr to switch modes. |
| AT_RECURSIVE | Apply changes to the entire mount subtree rooted at dirfd/pathname, not just the top-level mount. |
| AT_EMPTY_PATH | Operate on dirfd itself (pass "" as pathname) rather than resolving a path; useful with open_tree(2) fds. |
| userns_fd | If non-zero, shift UID/GID ownership on the mount to map into the specified user namespace. Requires CAP_SYS_ADMIN in that namespace. |
Errors you will actually hit
| errno | Meaning |
|---|---|
| E2BIG | Non-zero bytes found past the known fields in mount_attr; the caller is passing a larger struct than this kernel understands. |
| EINVAL | Invalid flags, unknown MOUNT_ATTR_* combination, or contradictory atime modes. |
| EPERM | Caller lacks CAP_SYS_ADMIN in the user namespace that owns the mount. |
| ENOSYS | Kernel older than 5.12; fall back to mount(2) with MS_REMOUNT loops. |
Example
#include <linux/mount.h>
#include <sys/syscall.h>
#include <unistd.h>
struct mount_attr attr = {
.attr_set = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID | MOUNT_ATTR_NOEXEC,
};
if (syscall(SYS_mount_setattr, AT_FDCWD, "/container/rootfs",
AT_RECURSIVE, &attr, sizeof(attr)) < 0)
err(1, "mount_setattr");Trimmed from the full walkthrough in mount_setattr(2): change mount flags on a live tree.
BSD equivalent
FreeBSD: mount(2) with MNT_UPDATE reconfigures an existing mount, and it works correctly on the named mount record. Per-jail mount restrictions go through jail_set(2) parameters (allow.noexec, etc.) rather than per-mount attribute walks. There is no AT_RECURSIVE equivalent; recursive flag changes are manual.
The long version, from the console
Sources
Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.
