1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 //#include <stdio_ext.h>
25 #include <sys/mount.h>
27 #include <sys/statvfs.h>
31 //#include <libmount.h>
33 #include "alloc-util.h"
35 //#include "extract-word.h"
40 #include "mount-util.h"
41 #include "parse-util.h"
42 #include "path-util.h"
44 #include "stdio-util.h"
45 #include "string-util.h"
48 /* This is the original MAX_HANDLE_SZ definition from the kernel, when the API was introduced. We use that in place of
49 * any more currently defined value to future-proof things: if the size is increased in the API headers, and our code
50 * is recompiled then it would cease working on old kernels, as those refuse any sizes larger than this value with
51 * EINVAL right-away. Hence, let's disconnect ourselves from any such API changes, and stick to the original definition
52 * from when it was introduced. We use it as a start value only anyway (see below), and hence should be able to deal
53 * with large file handles anyway. */
54 #define ORIGINAL_MAX_HANDLE_SZ 128
56 int name_to_handle_at_loop(
59 struct file_handle **ret_handle,
63 _cleanup_free_ struct file_handle *h = NULL;
64 size_t n = ORIGINAL_MAX_HANDLE_SZ;
66 /* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
67 * buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
68 * start value, it is not an upper bound on the buffer size required.
70 * This improves on raw name_to_handle_at() also in one other regard: ret_handle and ret_mnt_id can be passed
71 * as NULL if there's no interest in either. */
76 h = malloc0(offsetof(struct file_handle, f_handle) + n);
82 if (name_to_handle_at(fd, path, h, &mnt_id, flags) >= 0) {
85 *ret_handle = TAKE_PTR(h);
92 if (errno != EOVERFLOW)
95 if (!ret_handle && ret_mnt_id && mnt_id >= 0) {
97 /* As it appears, name_to_handle_at() fills in mnt_id even when it returns EOVERFLOW when the
98 * buffer is too small, but that's undocumented. Hence, let's make use of this if it appears to
99 * be filled in, and the caller was interested in only the mount ID an nothing else. */
101 *ret_mnt_id = mnt_id;
105 /* If name_to_handle_at() didn't increase the byte size, then this EOVERFLOW is caused by something
106 * else (apparently EOVERFLOW is returned for untriggered nfs4 mounts sometimes), not by the too small
107 * buffer. In that case propagate EOVERFLOW */
108 if (h->handle_bytes <= n)
111 /* The buffer was too small. Size the new buffer by what name_to_handle_at() returned. */
113 if (offsetof(struct file_handle, f_handle) + n < n) /* check for addition overflow */
120 static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
121 char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
122 _cleanup_free_ char *fdinfo = NULL;
123 _cleanup_close_ int subfd = -1;
127 if ((flags & AT_EMPTY_PATH) && isempty(filename))
128 xsprintf(path, "/proc/self/fdinfo/%i", fd);
130 subfd = openat(fd, filename, O_CLOEXEC|O_PATH);
134 xsprintf(path, "/proc/self/fdinfo/%i", subfd);
137 r = read_full_file(path, &fdinfo, NULL);
138 if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
143 p = startswith(fdinfo, "mnt_id:");
145 p = strstr(fdinfo, "\nmnt_id:");
146 if (!p) /* The mnt_id field is a relatively new addition */
152 p += strspn(p, WHITESPACE);
153 p[strcspn(p, WHITESPACE)] = 0;
155 return safe_atoi(p, mnt_id);
158 int fd_is_mount_point(int fd, const char *filename, int flags) {
159 _cleanup_free_ struct file_handle *h = NULL, *h_parent = NULL;
160 int mount_id = -1, mount_id_parent = -1;
161 bool nosupp = false, check_st_dev = true;
168 /* First we will try the name_to_handle_at() syscall, which
169 * tells us the mount id and an opaque file "handle". It is
170 * not supported everywhere though (kernel compile-time
171 * option, not all file systems are hooked up). If it works
172 * the mount id is usually good enough to tell us whether
173 * something is a mount point.
175 * If that didn't work we will try to read the mount id from
176 * /proc/self/fdinfo/<fd>. This is almost as good as
177 * name_to_handle_at(), however, does not return the
178 * opaque file handle. The opaque file handle is pretty useful
179 * to detect the root directory, which we should always
180 * consider a mount point. Hence we use this only as
181 * fallback. Exporting the mnt_id in fdinfo is a pretty recent
184 * As last fallback we do traditional fstat() based st_dev
185 * comparisons. This is how things were traditionally done,
186 * but unionfs breaks this since it exposes file
187 * systems with a variety of st_dev reported. Also, btrfs
188 * subvolumes have different st_dev, even though they aren't
189 * real mounts of their own. */
191 r = name_to_handle_at_loop(fd, filename, &h, &mount_id, flags);
192 if (IN_SET(r, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL))
193 /* This kernel does not support name_to_handle_at() at all (ENOSYS), or the syscall was blocked
194 * (EACCES/EPERM; maybe through seccomp, because we are running inside of a container?), or the mount
195 * point is not triggered yet (EOVERFLOW, think nfs4), or some general name_to_handle_at() flakiness
196 * (EINVAL): fall back to simpler logic. */
197 goto fallback_fdinfo;
198 else if (r == -EOPNOTSUPP)
199 /* This kernel or file system does not support name_to_handle_at(), hence let's see if the upper fs
200 * supports it (in which case it is a mount point), otherwise fallback to the traditional stat()
206 r = name_to_handle_at_loop(fd, "", &h_parent, &mount_id_parent, AT_EMPTY_PATH);
207 if (r == -EOPNOTSUPP) {
209 /* Neither parent nor child do name_to_handle_at()? We have no choice but to fall back. */
210 goto fallback_fdinfo;
212 /* The parent can't do name_to_handle_at() but the directory we are interested in can? If so,
213 * it must be a mount point. */
218 /* The parent can do name_to_handle_at() but the
219 * directory we are interested in can't? If so, it
220 * must be a mount point. */
224 /* If the file handle for the directory we are
225 * interested in and its parent are identical, we
226 * assume this is the root directory, which is a mount
229 if (h->handle_bytes == h_parent->handle_bytes &&
230 h->handle_type == h_parent->handle_type &&
231 memcmp(h->f_handle, h_parent->f_handle, h->handle_bytes) == 0)
234 return mount_id != mount_id_parent;
237 r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
238 if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM))
243 r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
247 if (mount_id != mount_id_parent)
250 /* Hmm, so, the mount ids are the same. This leaves one
251 * special case though for the root file system. For that,
252 * let's see if the parent directory has the same inode as we
253 * are interested in. Hence, let's also do fstat() checks now,
254 * too, but avoid the st_dev comparisons, since they aren't
255 * that useful on unionfs mounts. */
256 check_st_dev = false;
259 /* yay for fstatat() taking a different set of flags than the other
261 if (flags & AT_SYMLINK_FOLLOW)
262 flags &= ~AT_SYMLINK_FOLLOW;
264 flags |= AT_SYMLINK_NOFOLLOW;
265 if (fstatat(fd, filename, &a, flags) < 0)
268 if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
271 /* A directory with same device and inode as its parent? Must
272 * be the root directory */
273 if (a.st_dev == b.st_dev &&
274 a.st_ino == b.st_ino)
277 return check_st_dev && (a.st_dev != b.st_dev);
280 /* flags can be AT_SYMLINK_FOLLOW or 0 */
281 int path_is_mount_point(const char *t, const char *root, int flags) {
282 _cleanup_free_ char *canonical = NULL, *parent = NULL;
283 _cleanup_close_ int fd = -1;
287 assert((flags & ~AT_SYMLINK_FOLLOW) == 0);
289 if (path_equal(t, "/"))
292 /* we need to resolve symlinks manually, we can't just rely on
293 * fd_is_mount_point() to do that for us; if we have a structure like
294 * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
295 * look at needs to be /usr, not /. */
296 if (flags & AT_SYMLINK_FOLLOW) {
297 r = chase_symlinks(t, root, 0, &canonical);
304 parent = dirname_malloc(t);
308 fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH);
312 return fd_is_mount_point(fd, last_path_component(t), flags);
315 int path_get_mnt_id(const char *path, int *ret) {
318 r = name_to_handle_at_loop(AT_FDCWD, path, NULL, ret, 0);
319 if (IN_SET(r, -EOPNOTSUPP, -ENOSYS, -EACCES, -EPERM, -EOVERFLOW, -EINVAL)) /* kernel/fs don't support this, or seccomp blocks access, or untriggered mount, or name_to_handle_at() is flaky */
320 return fd_fdinfo_mnt_id(AT_FDCWD, path, 0, ret);
325 #if 0 /// UNNEEDED by elogind
326 int umount_recursive(const char *prefix, int flags) {
330 /* Try to umount everything recursively below a
331 * directory. Also, take care of stacked mounts, and keep
332 * unmounting them until they are gone. */
335 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
340 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
341 if (!proc_self_mountinfo)
344 (void) __fsetlocking(proc_self_mountinfo, FSETLOCKING_BYCALLER);
347 _cleanup_free_ char *path = NULL, *p = NULL;
350 k = fscanf(proc_self_mountinfo,
351 "%*s " /* (1) mount id */
352 "%*s " /* (2) parent id */
353 "%*s " /* (3) major:minor */
354 "%*s " /* (4) root */
355 "%ms " /* (5) mount point */
356 "%*s" /* (6) mount options */
357 "%*[^-]" /* (7) optional fields */
358 "- " /* (8) separator */
359 "%*s " /* (9) file system type */
360 "%*s" /* (10) mount source */
361 "%*s" /* (11) mount options 2 */
362 "%*[^\n]", /* some rubbish at the end */
371 r = cunescape(path, UNESCAPE_RELAX, &p);
375 if (!path_startswith(p, prefix))
378 if (umount2(p, flags) < 0) {
379 r = log_debug_errno(errno, "Failed to umount %s: %m", p);
383 log_debug("Successfully unmounted %s", p);
396 static int get_mount_flags(const char *path, unsigned long *flags) {
399 if (statvfs(path, &buf) < 0)
405 /* Use this function only if do you have direct access to /proc/self/mountinfo
406 * and need the caller to open it for you. This is the case when /proc is
407 * masked or not mounted. Otherwise, use bind_remount_recursive. */
408 int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **blacklist, FILE *proc_self_mountinfo) {
409 _cleanup_set_free_free_ Set *done = NULL;
410 _cleanup_free_ char *cleaned = NULL;
413 assert(proc_self_mountinfo);
415 /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already
416 * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
417 * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to
418 * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each
419 * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We
420 * do not have any effect on future submounts that might get propagated, they migt be writable. This includes
421 * future submounts that have been triggered via autofs.
423 * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
424 * remount operation. Note that we'll ignore the blacklist for the top-level path. */
426 cleaned = strdup(prefix);
430 path_kill_slashes(cleaned);
432 done = set_new(&path_hash_ops);
437 _cleanup_set_free_free_ Set *todo = NULL;
438 bool top_autofs = false;
440 unsigned long orig_flags;
442 todo = set_new(&path_hash_ops);
446 rewind(proc_self_mountinfo);
449 _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
452 k = fscanf(proc_self_mountinfo,
453 "%*s " /* (1) mount id */
454 "%*s " /* (2) parent id */
455 "%*s " /* (3) major:minor */
456 "%*s " /* (4) root */
457 "%ms " /* (5) mount point */
458 "%*s" /* (6) mount options (superblock) */
459 "%*[^-]" /* (7) optional fields */
460 "- " /* (8) separator */
461 "%ms " /* (9) file system type */
462 "%*s" /* (10) mount source */
463 "%*s" /* (11) mount options (bind mount) */
464 "%*[^\n]", /* some rubbish at the end */
474 r = cunescape(path, UNESCAPE_RELAX, &p);
478 if (!path_startswith(p, cleaned))
481 /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall
483 if (!path_equal(cleaned, p)) {
484 bool blacklisted = false;
487 STRV_FOREACH(i, blacklist) {
489 if (path_equal(*i, cleaned))
492 if (!path_startswith(*i, cleaned))
495 if (path_startswith(p, *i)) {
497 log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned);
505 /* Let's ignore autofs mounts. If they aren't
506 * triggered yet, we want to avoid triggering
507 * them, as we don't make any guarantees for
508 * future submounts anyway. If they are
509 * already triggered, then we will find
510 * another entry for this. */
511 if (streq(type, "autofs")) {
512 top_autofs = top_autofs || path_equal(cleaned, p);
516 if (!set_contains(done, p)) {
517 r = set_consume(todo, p);
526 /* If we have no submounts to process anymore and if
527 * the root is either already done, or an autofs, we
529 if (set_isempty(todo) &&
530 (top_autofs || set_contains(done, cleaned)))
533 if (!set_contains(done, cleaned) &&
534 !set_contains(todo, cleaned)) {
535 /* The prefix directory itself is not yet a mount, make it one. */
536 if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
540 (void) get_mount_flags(cleaned, &orig_flags);
541 orig_flags &= ~MS_RDONLY;
543 if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
546 log_debug("Made top-level directory %s a mount point.", prefix);
552 r = set_consume(done, x);
557 while ((x = set_steal_first(todo))) {
559 r = set_consume(done, x);
560 if (IN_SET(r, 0, -EEXIST))
565 /* Deal with mount points that are obstructed by a later mount */
566 r = path_is_mount_point(x, NULL, 0);
567 if (IN_SET(r, 0, -ENOENT))
572 /* Try to reuse the original flag set */
574 (void) get_mount_flags(x, &orig_flags);
575 orig_flags &= ~MS_RDONLY;
577 if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
580 log_debug("Remounted %s read-only.", x);
585 int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
586 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
588 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
589 if (!proc_self_mountinfo)
592 (void) __fsetlocking(proc_self_mountinfo, FSETLOCKING_BYCALLER);
594 return bind_remount_recursive_with_mountinfo(prefix, ro, blacklist, proc_self_mountinfo);
597 int mount_move_root(const char *path) {
603 if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
615 bool fstype_is_network(const char *fstype) {
618 x = startswith(fstype, "fuse.");
622 return STR_IN_SET(fstype,
634 "pvfs2", /* OrangeFS */
639 bool fstype_is_api_vfs(const char *fstype) {
640 return STR_IN_SET(fstype,
663 bool fstype_is_ro(const char *fstype) {
664 /* All Linux file systems that are necessarily read-only */
665 return STR_IN_SET(fstype,
671 bool fstype_can_discard(const char *fstype) {
672 return STR_IN_SET(fstype,
679 bool fstype_can_uid_gid(const char *fstype) {
681 /* All file systems that have a uid=/gid= mount option that fixates the owners of all files and directories,
682 * current and future. */
684 return STR_IN_SET(fstype,
695 int repeat_unmount(const char *path, int flags) {
700 /* If there are multiple mounts on a mount point, this
701 * removes them all */
704 if (umount2(path, flags) < 0) {
717 const char* mode_to_inaccessible_node(mode_t mode) {
718 /* This function maps a node type to the correspondent inaccessible node type.
719 * Character and block inaccessible devices may not be created (because major=0 and minor=0),
720 * in such case we map character and block devices to the inaccessible node type socket. */
721 switch(mode & S_IFMT) {
723 return "/run/systemd/inaccessible/reg";
725 return "/run/systemd/inaccessible/dir";
727 if (access("/run/systemd/inaccessible/chr", F_OK) == 0)
728 return "/run/systemd/inaccessible/chr";
729 return "/run/systemd/inaccessible/sock";
731 if (access("/run/systemd/inaccessible/blk", F_OK) == 0)
732 return "/run/systemd/inaccessible/blk";
733 return "/run/systemd/inaccessible/sock";
735 return "/run/systemd/inaccessible/fifo";
737 return "/run/systemd/inaccessible/sock";
742 #if 0 /// UNNEEDED by elogind
743 #define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "")
744 static char* mount_flags_to_string(long unsigned flags) {
746 _cleanup_free_ char *y = NULL;
747 long unsigned overflow;
749 overflow = flags & ~(MS_RDONLY |
774 if (flags == 0 || overflow != 0)
775 if (asprintf(&y, "%lx", overflow) < 0)
778 x = strjoin(FLAG(MS_RDONLY),
782 FLAG(MS_SYNCHRONOUS),
800 FLAG(MS_STRICTATIME),
806 x[strlen(x) - 1] = '\0'; /* truncate the last | */
816 const char *options) {
818 _cleanup_free_ char *fl = NULL, *o = NULL;
822 r = mount_option_mangle(options, flags, &f, &o);
824 return log_full_errno(error_log_level, r,
825 "Failed to mangle mount options %s: %m",
828 fl = mount_flags_to_string(f);
830 if ((f & MS_REMOUNT) && !what && !type)
831 log_debug("Remounting %s (%s \"%s\")...",
832 where, strnull(fl), strempty(o));
833 else if (!what && !type)
834 log_debug("Mounting %s (%s \"%s\")...",
835 where, strnull(fl), strempty(o));
836 else if ((f & MS_BIND) && !type)
837 log_debug("Bind-mounting %s on %s (%s \"%s\")...",
838 what, where, strnull(fl), strempty(o));
839 else if (f & MS_MOVE)
840 log_debug("Moving mount %s → %s (%s \"%s\")...",
841 what, where, strnull(fl), strempty(o));
843 log_debug("Mounting %s on %s (%s \"%s\")...",
844 strna(type), where, strnull(fl), strempty(o));
845 if (mount(what, where, type, f, o) < 0)
846 return log_full_errno(error_log_level, errno,
847 "Failed to mount %s on %s (%s \"%s\"): %m",
848 strna(type), where, strnull(fl), strempty(o));
852 int umount_verbose(const char *what) {
853 log_debug("Umounting %s...", what);
854 if (umount(what) < 0)
855 return log_error_errno(errno, "Failed to unmount %s: %m", what);
860 const char *mount_propagation_flags_to_string(unsigned long flags) {
862 switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
877 int mount_propagation_flags_from_string(const char *name, unsigned long *ret) {
881 else if (streq(name, "shared"))
883 else if (streq(name, "slave"))
885 else if (streq(name, "private"))
892 #if 0 /// UNNEEDED by elogind
893 int mount_option_mangle(
895 unsigned long mount_flags,
896 unsigned long *ret_mount_flags,
897 char **ret_remaining_options) {
899 const struct libmnt_optmap *map;
900 _cleanup_free_ char *ret = NULL;
904 /* This extracts mount flags from the mount options, and store
905 * non-mount-flag options to '*ret_remaining_options'.
907 * "rw,nosuid,nodev,relatime,size=1630748k,mode=700,uid=1000,gid=1000"
908 * is split to MS_NOSUID|MS_NODEV|MS_RELATIME and
909 * "size=1630748k,mode=700,uid=1000,gid=1000".
910 * See more examples in test-mount-utils.c.
912 * Note that if 'options' does not contain any non-mount-flag options,
913 * then '*ret_remaining_options' is set to NULL instread of empty string.
914 * Note that this does not check validity of options stored in
915 * '*ret_remaining_options'.
916 * Note that if 'options' is NULL, then this just copies 'mount_flags'
917 * to '*ret_mount_flags'. */
919 assert(ret_mount_flags);
920 assert(ret_remaining_options);
922 map = mnt_get_builtin_optmap(MNT_LINUX_MAP);
928 _cleanup_free_ char *word = NULL;
929 const struct libmnt_optmap *ent;
931 r = extract_first_word(&p, &word, ",", EXTRACT_QUOTES);
937 for (ent = map; ent->name; ent++) {
938 /* All entries in MNT_LINUX_MAP do not take any argument.
939 * Thus, ent->name does not contain "=" or "[=]". */
940 if (!streq(word, ent->name))
943 if (!(ent->mask & MNT_INVERT))
944 mount_flags |= ent->id;
945 else if (mount_flags & ent->id)
946 mount_flags ^= ent->id;
951 /* If 'word' is not a mount flag, then store it in '*ret_remaining_options'. */
952 if (!ent->name && !strextend_with_separator(&ret, ",", word, NULL))
956 *ret_mount_flags = mount_flags;
957 *ret_remaining_options = TAKE_PTR(ret);