mount_setattr(2) | System Calls Manual | mount_setattr(2) |
mount_setattr - change properties of a mount or mount tree
Standard C library (libc, -lc)
#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:
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:
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:
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.
mount_setattr() first appeared in Linux 5.12.
mount_setattr() is Linux-specific.
Creating an ID-mapped mount makes it possible to change the ownership of all files located under a mount. Thus, ID-mapped mounts make it possible to change ownership in a temporary and localized way. It is a localized change because the ownership changes are visible only via a specific mount. All other users and locations where the filesystem is exposed are unaffected. It is a temporary change because the ownership changes are tied to the lifetime of the mount.
Whenever callers interact with the filesystem through an ID-mapped mount, the ID mapping of the mount will be applied to user and group IDs associated with filesystem objects. This encompasses the user and group IDs associated with inodes and also the following xattr(7) keys:
The following conditions must be met in order to create an ID-mapped mount:
ID mappings can be created for user IDs, group IDs, and project IDs. An ID mapping is essentially a mapping of a range of user or group IDs into another or the same range of user or group IDs. ID mappings are written to map files as three numbers separated by white space. The first two numbers specify the starting user or group ID in each of the two user namespaces. The third number specifies the range of the ID mapping. For example, a mapping for user IDs such as "1000 1001 1" would indicate that user ID 1000 in the caller's user namespace is mapped to user ID 1001 in its ancestor user namespace. Since the map range is 1, only user ID 1000 is mapped.
It is possible to specify up to 340 ID mappings for each ID mapping type. If any user IDs or group IDs are not mapped, all files owned by that unmapped user or group ID will appear as being owned by the overflow user ID or overflow group ID respectively.
Further details on setting up ID mappings can be found in user_namespaces(7).
In the common case, the user namespace passed in userns_fd (together with MOUNT_ATTR_IDMAP in attr_set) to create an ID-mapped mount will be the user namespace of a container. In other scenarios it will be a dedicated user namespace associated with a user's login session as is the case for portable home directories in systemd-homed.service(8)). It is also perfectly fine to create a dedicated user namespace for the sake of ID mapping a mount.
ID-mapped mounts can be useful in the following and a variety of other scenarios:
In order to allow for future extensibility, mount_setattr() requires the user-space application to specify the size of the mount_attr structure that it is passing. By providing this information, it is possible for mount_setattr() to provide both forwards- and backwards-compatibility, with 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 perf_setattr(2), perf_event_open(2), clone3(2) and openat2(2).
Let usize be the size of the structure as specified by the user-space application, and let ksize be the size of the structure which the kernel supports, then there are three cases to consider:
Because the definition of struct mount_attr may change in the future (with new fields being added when system headers are updated), user-space applications should zero-fill 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:
struct mount_attr attr = {
.attr_set = MOUNT_ATTR_RDONLY,
.attr_clr = MOUNT_ATTR_NODEV };
Alternatively, the structure can be zero-filled using memset(3) or similar functions:
struct mount_attr attr; memset(&attr, 0, sizeof(attr)); attr.attr_set = MOUNT_ATTR_RDONLY; attr.attr_clr = MOUNT_ATTR_NODEV;
A user-space application that wishes to determine which extensions the running kernel supports can do so by conducting a binary search on size with a structure which has every byte nonzero (to find the largest value which doesn't produce an error of E2BIG).
/*
* 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)
2023-02-10 | Linux man-pages 6.03 |