$RodHat_
Syscall reference · Filesystem

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

NameWhat it does
MOUNT_ATTR_RDONLYMake the mount read-only.
MOUNT_ATTR_NOSUIDPrevent set-uid and set-gid execution on this mount.
MOUNT_ATTR_NODEVPrevent access to device files on this mount.
MOUNT_ATTR_NOEXECPrevent execution of programs on this mount.
MOUNT_ATTR_NOSYMFOLLOWLinux 5.18Prevent symlink following on this mount.
MOUNT_ATTR__ATIMEMask 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_RECURSIVEApply changes to the entire mount subtree rooted at dirfd/pathname, not just the top-level mount.
AT_EMPTY_PATHOperate on dirfd itself (pass "" as pathname) rather than resolving a path; useful with open_tree(2) fds.
userns_fdIf 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

errnoMeaning
E2BIGNon-zero bytes found past the known fields in mount_attr; the caller is passing a larger struct than this kernel understands.
EINVALInvalid flags, unknown MOUNT_ATTR_* combination, or contradictory atime modes.
EPERMCaller lacks CAP_SYS_ADMIN in the user namespace that owns the mount.
ENOSYSKernel 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

  1. mount_setattr(2), man7.org
  2. Improving the mount API, LWN.net

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.