mount_setattr - change properties of a mount or mount tree
#include <linux/fcntl.h> /* Definition of AT_* constants */
#include <linux/mount.h> /* Definition of MOUNT_ATTR_* constants */
#include <sys/syscall.h> /* Definition of SYS_* constants */
#include <unistd.h>
int syscall(SYS_mount_setattr, int dirfd, const char *pathname,
unsigned int flags, struct mount_attr *attr, size_t size);
Note
: glibc provides no wrapper for
mount_setattr(), necessitating the use of
syscall(2).
The mount_setattr() system call changes the mount
properties of a mount or an entire mount tree. If pathname
is a
relative pathname, then it is interpreted relative to the directory
referred to by the file descriptor dirfd
. If dirfd
is
the special value AT_FDCWD, then pathname
is
interpreted relative to the current working directory of the calling
process. If pathname
is the empty string and
AT_EMPTY_PATH is specified in flags
, then the
mount properties of the mount identified by dirfd
are changed.
(See openat(2) for an explanation of why the
dirfd
argument is useful.)
The mount_setattr() system call uses an extensible
structure (struct mount_attr
) to allow for future extensions.
Any non-flag extensions to mount_setattr() will be
implemented as new fields appended to the this 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 must
zero-fill this structure on initialization. See the "Extensibility"
subsection under NOTES for more details.
The size
argument should usually be specified as
sizeof(struct mount_attr)
. However, if the caller is using a
kernel that supports an extended struct mount_attr
, but the
caller does not intend to make use of these features, it is possible to
pass the size of an earlier version of the structure together with the
extended structure. This allows the kernel to not copy later parts of
the structure that aren't used anyway. With each extension that changes
the size of struct mount_attr
, the kernel will expose a
definition of the form
MOUNT_ATTR_SIZE_VERnumber
. For example, the
macro for the size of the initial version of struct mount_attr
is MOUNT_ATTR_SIZE_VER0.
The flags
argument can be used to alter the pathname
resolution behavior. The supported values are:
If pathname
is the empty string, change the mount properties
on dirfd
itself.
Change the mount properties of the entire mount tree.
Don't follow trailing symbolic links.
Don't trigger automounts.
The attr
argument of mount_setattr() is a
structure of the following form:
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 */
};
The attr_set
and attr_clr
members are used to
specify the mount properties that are supposed to be set or cleared for
a mount or mount tree. Flags set in attr_set
enable a property
on a mount or mount tree, and flags set in attr_clr
remove a
property from a mount or mount tree.
When changing mount properties, the kernel will first clear the flags
specified in the attr_clr
field, and then set the flags
specified in the attr_set
field. For example, these
settings:
struct mount_attr attr = {
.attr_clr = MOUNT_ATTR_NOEXEC | MOUNT_ATTR_NODEV,
.attr_set = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID,
};
are equivalent to the following steps:
unsigned int current_mnt_flags = mnt->mnt_flags;
/*
* Clear all flags set in .attr_clr,
* clearing MOUNT_ATTR_NOEXEC and MOUNT_ATTR_NODEV.
*/
current_mnt_flags &= ~attr->attr_clr;
/*
* Now set all flags set in .attr_set,
* applying MOUNT_ATTR_RDONLY and MOUNT_ATTR_NOSUID.
*/
current_mnt_flags |= attr->attr_set;
mnt->mnt_flags = current_mnt_flags;
As a result of this change, the mount or mount tree (a) is read-only; (b) blocks the execution of set-user-ID and set-group-ID programs; (c) allows execution of programs; and (d) allows access to devices.
Multiple changes with the same set of flags requested in
attr_clr
and attr_set
are guaranteed to be idempotent
after the changes have been applied.
The following mount attributes can be specified in the
attr_set
or attr_clr
fields:
If set in attr_set
, makes the mount read-only. If set in
attr_clr
, removes the read-only setting if set on the
mount.
If set in attr_set
, causes the mount not to honor the
set-user-ID and set-group-ID mode bits and file capabilities when
executing programs. If set in attr_clr
, clears the set-user-ID,
set-group-ID, and file capability restriction if set on this mount.
If set in attr_set
, prevents access to devices on this
mount. If set in attr_clr
, removes the restriction that
prevented accessing devices on this mount.
If set in attr_set
, prevents executing programs on this
mount. If set in attr_clr
, removes the restriction that
prevented executing programs on this mount.
If set in attr_set
, prevents following symbolic links on
this mount. If set in attr_clr
, removes the restriction that
prevented following symbolic links on this mount.
If set in attr_set
, prevents updating access time for
directories on this mount. If set in attr_clr
, removes the
restriction that prevented updating access time for directories. Note
that 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.
The access-time values listed below are an enumeration that includes
the value zero, expressed in the bits defined by the mask
MOUNT_ATTR__ATIME. Even though these bits are an
enumeration (in contrast to the other mount flags such as
MOUNT_ATTR_NOEXEC), they are nonetheless passed in
attr_set
and attr_clr
for consistency with
fsmount(2), which introduced this behavior.
Note that, since the access-time values are an enumeration rather
than bit values, a caller wanting to transition to a different
access-time setting cannot simply specify the access-time setting in
attr_set
, but must also include
MOUNT_ATTR__ATIME in the attr_clr
field. The
kernel will verify that MOUNT_ATTR__ATIME isn't
partially set in attr_clr
(i.e., either all bits in the
MOUNT_ATTR__ATIME bit field are either set or clear),
and that attr_set
doesn't have any access-time bits set if
MOUNT_ATTR__ATIME isn't set in attr_clr
.
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).
To enable this access-time setting on a mount or mount tree,
MOUNT_ATTR_RELATIME must be set in attr_set
and MOUNT_ATTR__ATIME must be set in the
attr_clr
field.
Do not update access times for (all types of) files on this mount.
To enable this access-time setting on a mount or mount tree,
MOUNT_ATTR_NOATIME must be set in attr_set
and
MOUNT_ATTR__ATIME must be set in the attr_clr
field.
Always update the last access time (atime) when files are accessed on this mount.
To enable this access-time setting on a mount or mount tree,
MOUNT_ATTR_STRICTATIME must be set in attr_set
and MOUNT_ATTR__ATIME must be set in the
attr_clr
field.
If set in attr_set
, creates an ID-mapped mount. The ID
mapping is taken from the user namespace specified in userns_fd
and attached to the mount.
Since it is not supported to change the ID mapping of a mount after
it has been ID mapped, it is invalid to specify
MOUNT_ATTR_IDMAP in attr_clr
.
For further details, see the subsection "ID-mapped mounts" under NOTES.
The propagation
field is used to specify the propagation
type of the mount or mount tree. This field either has the value zero,
meaning leave the propagation type unchanged, or it has one of the
following values:
Turn all mounts into private mounts.
Turn all mounts into shared mounts.
Turn all mounts into dependent mounts.
Turn all mounts into unbindable mounts.
For further details on the above propagation types, see mount_namespaces(7).
On success, mount_setattr() returns zero. On error,
-1 is returned and errno
is set to indicate the cause of the
error.
/*
* This program allows the caller to create a new detached mount
* and set various properties on it.
*/
#define _GNU_SOURCE
#include <err.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>
static inline int
mount_setattr(int dirfd, const char *pathname, unsigned int flags,
struct mount_attr *attr, size_t size)
{
return syscall(SYS_mount_setattr, dirfd, pathname, flags,
attr, size);
}
static inline int
open_tree(int dirfd, const char *filename, unsigned int flags)
{
return syscall(SYS_open_tree, dirfd, filename, flags);
}
static inline int
move_mount(int from_dirfd, const char *from_pathname,
int to_dirfd, const char *to_pathname, unsigned int flags)
{
return syscall(SYS_move_mount, from_dirfd, from_pathname,
to_dirfd, to_pathname, flags);
}
static const struct option longopts[] = {
{"map-mount", required_argument, NULL, 'a'},
{"recursive", no_argument, NULL, 'b'},
{"read-only", no_argument, NULL, 'c'},
{"block-setid", no_argument, NULL, 'd'},
{"block-devices", no_argument, NULL, 'e'},
{"block-exec", no_argument, NULL, 'f'},
{"no-access-time", no_argument, NULL, 'g'},
{ NULL, 0, NULL, 0 },
};
int
main(int argc, char *argv[])
{
int fd_userns = -1;
int fd_tree;
int index = 0;
int ret;
bool recursive = false;
const char *source;
const char *target;
struct mount_attr *attr = &(struct mount_attr){};
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 == -1)
err(EXIT_FAILURE, "open(%s)", 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:
errx(EXIT_FAILURE, "Invalid argument specified");
}
}
if ((argc - optind) < 2)
errx(EXIT_FAILURE, "Missing source or target mount point");
source = argv[optind];
target = argv[optind + 1];
/* In the following, -1 as the 'dirfd' argument ensures that
open_tree() fails if 'source' is not an absolute pathname. */
fd_tree = open_tree(-1, source,
OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC |
AT_EMPTY_PATH | (recursive ? AT_RECURSIVE : 0));
if (fd_tree == -1)
err(EXIT_FAILURE, "open(%s)", 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 == -1)
err(EXIT_FAILURE, "mount_setattr");
close(fd_userns);
/* In the following, -1 as the 'to_dirfd' argument ensures that
open_tree() fails if 'target' is not an absolute pathname. */
ret = move_mount(fd_tree, "", -1, target,
MOVE_MOUNT_F_EMPTY_PATH);
if (ret == -1)
err(EXIT_FAILURE, "move_mount() to %s", target);
close(fd_tree);
exit(EXIT_SUCCESS);
}
newgidmap(1), newuidmap(1), clone(2), mount(2), unshare(2), proc(5), capabilities(7), mount_namespaces(7), user_namespaces(7), xattr(7)