1 /* SPDX-License-Identifier: LGPL-2.1+ */
9 #include <sys/statvfs.h>
15 #include "alloc-util.h"
17 #include "extract-word.h"
22 #include "mount-util.h"
23 #include "mountpoint-util.h"
24 #include "parse-util.h"
25 #include "path-util.h"
27 #include "stdio-util.h"
28 #include "string-util.h"
31 int umount_recursive(const char *prefix
, int flags
) {
35 /* Try to umount everything recursively below a
36 * directory. Also, take care of stacked mounts, and keep
37 * unmounting them until they are gone. */
40 _cleanup_fclose_
FILE *proc_self_mountinfo
= NULL
;
45 proc_self_mountinfo
= fopen("/proc/self/mountinfo", "re");
46 if (!proc_self_mountinfo
)
49 (void) __fsetlocking(proc_self_mountinfo
, FSETLOCKING_BYCALLER
);
52 _cleanup_free_
char *path
= NULL
, *p
= NULL
;
55 k
= fscanf(proc_self_mountinfo
,
56 "%*s " /* (1) mount id */
57 "%*s " /* (2) parent id */
58 "%*s " /* (3) major:minor */
60 "%ms " /* (5) mount point */
61 "%*s" /* (6) mount options */
62 "%*[^-]" /* (7) optional fields */
63 "- " /* (8) separator */
64 "%*s " /* (9) file system type */
65 "%*s" /* (10) mount source */
66 "%*s" /* (11) mount options 2 */
67 "%*[^\n]", /* some rubbish at the end */
76 k
= cunescape(path
, UNESCAPE_RELAX
, &p
);
80 if (!path_startswith(p
, prefix
))
83 if (umount2(p
, flags
) < 0) {
84 r
= log_debug_errno(errno
, "Failed to umount %s: %m", p
);
88 log_debug("Successfully unmounted %s", p
);
101 static int get_mount_flags(const char *path
, unsigned long *flags
) {
104 if (statvfs(path
, &buf
) < 0)
110 /* Use this function only if you do not have direct access to /proc/self/mountinfo but the caller can open it
111 * for you. This is the case when /proc is masked or not mounted. Otherwise, use bind_remount_recursive. */
112 int bind_remount_recursive_with_mountinfo(
114 unsigned long new_flags
,
115 unsigned long flags_mask
,
117 FILE *proc_self_mountinfo
) {
119 _cleanup_set_free_free_ Set
*done
= NULL
;
120 _cleanup_free_
char *cleaned
= NULL
;
123 assert(proc_self_mountinfo
);
125 /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already
126 * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
127 * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to
128 * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each
129 * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We
130 * do not have any effect on future submounts that might get propagated, they migt be writable. This includes
131 * future submounts that have been triggered via autofs.
133 * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
134 * remount operation. Note that we'll ignore the blacklist for the top-level path. */
136 cleaned
= strdup(prefix
);
140 path_simplify(cleaned
, false);
142 done
= set_new(&path_hash_ops
);
147 _cleanup_set_free_free_ Set
*todo
= NULL
;
148 bool top_autofs
= false;
150 unsigned long orig_flags
;
152 todo
= set_new(&path_hash_ops
);
156 rewind(proc_self_mountinfo
);
159 _cleanup_free_
char *path
= NULL
, *p
= NULL
, *type
= NULL
;
162 k
= fscanf(proc_self_mountinfo
,
163 "%*s " /* (1) mount id */
164 "%*s " /* (2) parent id */
165 "%*s " /* (3) major:minor */
166 "%*s " /* (4) root */
167 "%ms " /* (5) mount point */
168 "%*s" /* (6) mount options (superblock) */
169 "%*[^-]" /* (7) optional fields */
170 "- " /* (8) separator */
171 "%ms " /* (9) file system type */
172 "%*s" /* (10) mount source */
173 "%*s" /* (11) mount options (bind mount) */
174 "%*[^\n]", /* some rubbish at the end */
184 r
= cunescape(path
, UNESCAPE_RELAX
, &p
);
188 if (!path_startswith(p
, cleaned
))
191 /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall
193 if (!path_equal(cleaned
, p
)) {
194 bool blacklisted
= false;
197 STRV_FOREACH(i
, blacklist
) {
199 if (path_equal(*i
, cleaned
))
202 if (!path_startswith(*i
, cleaned
))
205 if (path_startswith(p
, *i
)) {
207 log_debug("Not remounting %s blacklisted by %s, called for %s", p
, *i
, cleaned
);
215 /* Let's ignore autofs mounts. If they aren't
216 * triggered yet, we want to avoid triggering
217 * them, as we don't make any guarantees for
218 * future submounts anyway. If they are
219 * already triggered, then we will find
220 * another entry for this. */
221 if (streq(type
, "autofs")) {
222 top_autofs
= top_autofs
|| path_equal(cleaned
, p
);
226 if (!set_contains(done
, p
)) {
227 r
= set_consume(todo
, p
);
236 /* If we have no submounts to process anymore and if
237 * the root is either already done, or an autofs, we
239 if (set_isempty(todo
) &&
240 (top_autofs
|| set_contains(done
, cleaned
)))
243 if (!set_contains(done
, cleaned
) &&
244 !set_contains(todo
, cleaned
)) {
245 /* The prefix directory itself is not yet a mount, make it one. */
246 if (mount(cleaned
, cleaned
, NULL
, MS_BIND
|MS_REC
, NULL
) < 0)
250 (void) get_mount_flags(cleaned
, &orig_flags
);
251 orig_flags
&= ~MS_RDONLY
;
253 if (mount(NULL
, cleaned
, NULL
, (orig_flags
& ~flags_mask
)|MS_BIND
|MS_REMOUNT
|new_flags
, NULL
) < 0)
256 log_debug("Made top-level directory %s a mount point.", prefix
);
258 r
= set_put_strdup(done
, cleaned
);
263 while ((x
= set_steal_first(todo
))) {
265 r
= set_consume(done
, x
);
266 if (IN_SET(r
, 0, -EEXIST
))
271 /* Deal with mount points that are obstructed by a later mount */
272 r
= path_is_mount_point(x
, NULL
, 0);
273 if (IN_SET(r
, 0, -ENOENT
))
275 if (IN_SET(r
, -EACCES
, -EPERM
)) {
276 /* Even if root user invoke this, submounts under private FUSE or NFS mount points
277 * may not be acceessed. E.g.,
279 * $ bindfs --no-allow-other ~/mnt/mnt ~/mnt/mnt
280 * $ bindfs --no-allow-other ~/mnt ~/mnt
282 * Then, root user cannot access the mount point ~/mnt/mnt.
283 * In such cases, the submounts are ignored, as we have no way to manage them. */
284 log_debug_errno(r
, "Failed to determine '%s' is mount point or not, ignoring: %m", x
);
290 /* Try to reuse the original flag set */
292 (void) get_mount_flags(x
, &orig_flags
);
293 orig_flags
&= ~MS_RDONLY
;
295 if (mount(NULL
, x
, NULL
, (orig_flags
& ~flags_mask
)|MS_BIND
|MS_REMOUNT
|new_flags
, NULL
) < 0)
298 log_debug("Remounted %s read-only.", x
);
303 int bind_remount_recursive(const char *prefix
, unsigned long new_flags
, unsigned long flags_mask
, char **blacklist
) {
304 _cleanup_fclose_
FILE *proc_self_mountinfo
= NULL
;
306 proc_self_mountinfo
= fopen("/proc/self/mountinfo", "re");
307 if (!proc_self_mountinfo
)
310 (void) __fsetlocking(proc_self_mountinfo
, FSETLOCKING_BYCALLER
);
312 return bind_remount_recursive_with_mountinfo(prefix
, new_flags
, flags_mask
, blacklist
, proc_self_mountinfo
);
315 int mount_move_root(const char *path
) {
321 if (mount(path
, "/", NULL
, MS_MOVE
, NULL
) < 0)
333 int repeat_unmount(const char *path
, int flags
) {
338 /* If there are multiple mounts on a mount point, this
339 * removes them all */
342 if (umount2(path
, flags
) < 0) {
354 const char* mode_to_inaccessible_node(mode_t mode
) {
355 /* This function maps a node type to a corresponding inaccessible file node. These nodes are created during
356 * early boot by PID 1. In some cases we lacked the privs to create the character and block devices (maybe
357 * because we run in an userns environment, or miss CAP_SYS_MKNOD, or run with a devices policy that excludes
358 * device nodes with major and minor of 0), but that's fine, in that case we use an AF_UNIX file node instead,
359 * which is not the same, but close enough for most uses. And most importantly, the kernel allows bind mounts
360 * from socket nodes to any non-directory file nodes, and that's the most important thing that matters. */
362 switch(mode
& S_IFMT
) {
364 return "/run/systemd/inaccessible/reg";
367 return "/run/systemd/inaccessible/dir";
370 if (access("/run/systemd/inaccessible/chr", F_OK
) == 0)
371 return "/run/systemd/inaccessible/chr";
372 return "/run/systemd/inaccessible/sock";
375 if (access("/run/systemd/inaccessible/blk", F_OK
) == 0)
376 return "/run/systemd/inaccessible/blk";
377 return "/run/systemd/inaccessible/sock";
380 return "/run/systemd/inaccessible/fifo";
383 return "/run/systemd/inaccessible/sock";
388 #define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "")
389 static char* mount_flags_to_string(long unsigned flags
) {
391 _cleanup_free_
char *y
= NULL
;
392 long unsigned overflow
;
394 overflow
= flags
& ~(MS_RDONLY
|
419 if (flags
== 0 || overflow
!= 0)
420 if (asprintf(&y
, "%lx", overflow
) < 0)
423 x
= strjoin(FLAG(MS_RDONLY
),
427 FLAG(MS_SYNCHRONOUS
),
445 FLAG(MS_STRICTATIME
),
451 x
[strlen(x
) - 1] = '\0'; /* truncate the last | */
461 const char *options
) {
463 _cleanup_free_
char *fl
= NULL
, *o
= NULL
;
467 r
= mount_option_mangle(options
, flags
, &f
, &o
);
469 return log_full_errno(error_log_level
, r
,
470 "Failed to mangle mount options %s: %m",
473 fl
= mount_flags_to_string(f
);
475 if ((f
& MS_REMOUNT
) && !what
&& !type
)
476 log_debug("Remounting %s (%s \"%s\")...",
477 where
, strnull(fl
), strempty(o
));
478 else if (!what
&& !type
)
479 log_debug("Mounting %s (%s \"%s\")...",
480 where
, strnull(fl
), strempty(o
));
481 else if ((f
& MS_BIND
) && !type
)
482 log_debug("Bind-mounting %s on %s (%s \"%s\")...",
483 what
, where
, strnull(fl
), strempty(o
));
484 else if (f
& MS_MOVE
)
485 log_debug("Moving mount %s → %s (%s \"%s\")...",
486 what
, where
, strnull(fl
), strempty(o
));
488 log_debug("Mounting %s on %s (%s \"%s\")...",
489 strna(type
), where
, strnull(fl
), strempty(o
));
490 if (mount(what
, where
, type
, f
, o
) < 0)
491 return log_full_errno(error_log_level
, errno
,
492 "Failed to mount %s (type %s) on %s (%s \"%s\"): %m",
493 strna(what
), strna(type
), where
, strnull(fl
), strempty(o
));
497 int umount_verbose(const char *what
) {
498 log_debug("Umounting %s...", what
);
499 if (umount(what
) < 0)
500 return log_error_errno(errno
, "Failed to unmount %s: %m", what
);
504 int mount_option_mangle(
506 unsigned long mount_flags
,
507 unsigned long *ret_mount_flags
,
508 char **ret_remaining_options
) {
510 const struct libmnt_optmap
*map
;
511 _cleanup_free_
char *ret
= NULL
;
515 /* This extracts mount flags from the mount options, and store
516 * non-mount-flag options to '*ret_remaining_options'.
518 * "rw,nosuid,nodev,relatime,size=1630748k,mode=700,uid=1000,gid=1000"
519 * is split to MS_NOSUID|MS_NODEV|MS_RELATIME and
520 * "size=1630748k,mode=700,uid=1000,gid=1000".
521 * See more examples in test-mount-utils.c.
523 * Note that if 'options' does not contain any non-mount-flag options,
524 * then '*ret_remaining_options' is set to NULL instread of empty string.
525 * Note that this does not check validity of options stored in
526 * '*ret_remaining_options'.
527 * Note that if 'options' is NULL, then this just copies 'mount_flags'
528 * to '*ret_mount_flags'. */
530 assert(ret_mount_flags
);
531 assert(ret_remaining_options
);
533 map
= mnt_get_builtin_optmap(MNT_LINUX_MAP
);
539 _cleanup_free_
char *word
= NULL
;
540 const struct libmnt_optmap
*ent
;
542 r
= extract_first_word(&p
, &word
, ",", EXTRACT_QUOTES
);
548 for (ent
= map
; ent
->name
; ent
++) {
549 /* All entries in MNT_LINUX_MAP do not take any argument.
550 * Thus, ent->name does not contain "=" or "[=]". */
551 if (!streq(word
, ent
->name
))
554 if (!(ent
->mask
& MNT_INVERT
))
555 mount_flags
|= ent
->id
;
556 else if (mount_flags
& ent
->id
)
557 mount_flags
^= ent
->id
;
562 /* If 'word' is not a mount flag, then store it in '*ret_remaining_options'. */
563 if (!ent
->name
&& !strextend_with_separator(&ret
, ",", word
, NULL
))
567 *ret_mount_flags
= mount_flags
;
568 *ret_remaining_options
= TAKE_PTR(ret
);