]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/mount-util.c
man: move non-target units together (#6934)
[thirdparty/systemd.git] / src / basic / mount-util.c
CommitLineData
4349cd7c
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
11c3a366
TA
20#include <errno.h>
21#include <stdlib.h>
4349cd7c
LP
22#include <string.h>
23#include <sys/mount.h>
11c3a366 24#include <sys/stat.h>
4349cd7c 25#include <sys/statvfs.h>
11c3a366 26#include <unistd.h>
4349cd7c 27
b5efdb8a 28#include "alloc-util.h"
4349cd7c
LP
29#include "escape.h"
30#include "fd-util.h"
31#include "fileio.h"
e1873695 32#include "fs-util.h"
93cc7779 33#include "hashmap.h"
4349cd7c
LP
34#include "mount-util.h"
35#include "parse-util.h"
36#include "path-util.h"
37#include "set.h"
15a5e950 38#include "stdio-util.h"
4349cd7c 39#include "string-util.h"
6b7c9f8b 40#include "strv.h"
4349cd7c
LP
41
42static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
43 char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
44 _cleanup_free_ char *fdinfo = NULL;
45 _cleanup_close_ int subfd = -1;
46 char *p;
47 int r;
48
49 if ((flags & AT_EMPTY_PATH) && isempty(filename))
50 xsprintf(path, "/proc/self/fdinfo/%i", fd);
51 else {
c4b69156 52 subfd = openat(fd, filename, O_CLOEXEC|O_PATH);
4349cd7c
LP
53 if (subfd < 0)
54 return -errno;
55
56 xsprintf(path, "/proc/self/fdinfo/%i", subfd);
57 }
58
59 r = read_full_file(path, &fdinfo, NULL);
60 if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
61 return -EOPNOTSUPP;
62 if (r < 0)
63 return -errno;
64
65 p = startswith(fdinfo, "mnt_id:");
66 if (!p) {
67 p = strstr(fdinfo, "\nmnt_id:");
68 if (!p) /* The mnt_id field is a relatively new addition */
69 return -EOPNOTSUPP;
70
71 p += 8;
72 }
73
74 p += strspn(p, WHITESPACE);
75 p[strcspn(p, WHITESPACE)] = 0;
76
77 return safe_atoi(p, mnt_id);
78}
79
4349cd7c
LP
80int fd_is_mount_point(int fd, const char *filename, int flags) {
81 union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
82 int mount_id = -1, mount_id_parent = -1;
83 bool nosupp = false, check_st_dev = true;
84 struct stat a, b;
85 int r;
86
87 assert(fd >= 0);
88 assert(filename);
89
90 /* First we will try the name_to_handle_at() syscall, which
91 * tells us the mount id and an opaque file "handle". It is
92 * not supported everywhere though (kernel compile-time
93 * option, not all file systems are hooked up). If it works
94 * the mount id is usually good enough to tell us whether
95 * something is a mount point.
96 *
97 * If that didn't work we will try to read the mount id from
98 * /proc/self/fdinfo/<fd>. This is almost as good as
99 * name_to_handle_at(), however, does not return the
100 * opaque file handle. The opaque file handle is pretty useful
101 * to detect the root directory, which we should always
102 * consider a mount point. Hence we use this only as
103 * fallback. Exporting the mnt_id in fdinfo is a pretty recent
104 * kernel addition.
105 *
106 * As last fallback we do traditional fstat() based st_dev
107 * comparisons. This is how things were traditionally done,
61233823 108 * but unionfs breaks this since it exposes file
4349cd7c
LP
109 * systems with a variety of st_dev reported. Also, btrfs
110 * subvolumes have different st_dev, even though they aren't
111 * real mounts of their own. */
112
113 r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
114 if (r < 0) {
059c35f5
LP
115 if (IN_SET(errno, ENOSYS, EACCES, EPERM))
116 /* This kernel does not support name_to_handle_at() at all, or the syscall was blocked (maybe
117 * through seccomp, because we are running inside of a container?): fall back to simpler
118 * logic. */
4349cd7c
LP
119 goto fallback_fdinfo;
120 else if (errno == EOPNOTSUPP)
121 /* This kernel or file system does not support
122 * name_to_handle_at(), hence let's see if the
123 * upper fs supports it (in which case it is a
124 * mount point), otherwise fallback to the
125 * traditional stat() logic */
126 nosupp = true;
127 else
128 return -errno;
129 }
130
131 r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
132 if (r < 0) {
133 if (errno == EOPNOTSUPP) {
134 if (nosupp)
135 /* Neither parent nor child do name_to_handle_at()?
136 We have no choice but to fall back. */
137 goto fallback_fdinfo;
138 else
139 /* The parent can't do name_to_handle_at() but the
140 * directory we are interested in can?
141 * If so, it must be a mount point. */
142 return 1;
143 } else
144 return -errno;
145 }
146
147 /* The parent can do name_to_handle_at() but the
148 * directory we are interested in can't? If so, it
149 * must be a mount point. */
150 if (nosupp)
151 return 1;
152
153 /* If the file handle for the directory we are
154 * interested in and its parent are identical, we
155 * assume this is the root directory, which is a mount
156 * point. */
157
158 if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
159 h.handle.handle_type == h_parent.handle.handle_type &&
160 memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
161 return 1;
162
163 return mount_id != mount_id_parent;
164
165fallback_fdinfo:
166 r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
059c35f5 167 if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM))
4349cd7c
LP
168 goto fallback_fstat;
169 if (r < 0)
170 return r;
171
172 r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
173 if (r < 0)
174 return r;
175
176 if (mount_id != mount_id_parent)
177 return 1;
178
179 /* Hmm, so, the mount ids are the same. This leaves one
180 * special case though for the root file system. For that,
181 * let's see if the parent directory has the same inode as we
182 * are interested in. Hence, let's also do fstat() checks now,
183 * too, but avoid the st_dev comparisons, since they aren't
184 * that useful on unionfs mounts. */
185 check_st_dev = false;
186
187fallback_fstat:
188 /* yay for fstatat() taking a different set of flags than the other
189 * _at() above */
190 if (flags & AT_SYMLINK_FOLLOW)
191 flags &= ~AT_SYMLINK_FOLLOW;
192 else
193 flags |= AT_SYMLINK_NOFOLLOW;
194 if (fstatat(fd, filename, &a, flags) < 0)
195 return -errno;
196
197 if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
198 return -errno;
199
200 /* A directory with same device and inode as its parent? Must
201 * be the root directory */
202 if (a.st_dev == b.st_dev &&
203 a.st_ino == b.st_ino)
204 return 1;
205
206 return check_st_dev && (a.st_dev != b.st_dev);
207}
208
209/* flags can be AT_SYMLINK_FOLLOW or 0 */
e1873695 210int path_is_mount_point(const char *t, const char *root, int flags) {
4349cd7c 211 _cleanup_free_ char *canonical = NULL, *parent = NULL;
e1873695
LP
212 _cleanup_close_ int fd = -1;
213 int r;
4349cd7c
LP
214
215 assert(t);
216
217 if (path_equal(t, "/"))
218 return 1;
219
220 /* we need to resolve symlinks manually, we can't just rely on
221 * fd_is_mount_point() to do that for us; if we have a structure like
222 * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
223 * look at needs to be /usr, not /. */
224 if (flags & AT_SYMLINK_FOLLOW) {
c4f4fce7 225 r = chase_symlinks(t, root, 0, &canonical);
e1873695
LP
226 if (r < 0)
227 return r;
4349cd7c
LP
228
229 t = canonical;
230 }
231
232 parent = dirname_malloc(t);
233 if (!parent)
234 return -ENOMEM;
235
c4b69156 236 fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH);
4349cd7c
LP
237 if (fd < 0)
238 return -errno;
239
240 return fd_is_mount_point(fd, basename(t), flags);
241}
242
243int umount_recursive(const char *prefix, int flags) {
244 bool again;
245 int n = 0, r;
246
247 /* Try to umount everything recursively below a
248 * directory. Also, take care of stacked mounts, and keep
249 * unmounting them until they are gone. */
250
251 do {
252 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
253
254 again = false;
255 r = 0;
256
257 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
258 if (!proc_self_mountinfo)
259 return -errno;
260
261 for (;;) {
262 _cleanup_free_ char *path = NULL, *p = NULL;
263 int k;
264
265 k = fscanf(proc_self_mountinfo,
266 "%*s " /* (1) mount id */
267 "%*s " /* (2) parent id */
268 "%*s " /* (3) major:minor */
269 "%*s " /* (4) root */
270 "%ms " /* (5) mount point */
271 "%*s" /* (6) mount options */
272 "%*[^-]" /* (7) optional fields */
273 "- " /* (8) separator */
274 "%*s " /* (9) file system type */
275 "%*s" /* (10) mount source */
276 "%*s" /* (11) mount options 2 */
277 "%*[^\n]", /* some rubbish at the end */
278 &path);
279 if (k != 1) {
280 if (k == EOF)
281 break;
282
283 continue;
284 }
285
286 r = cunescape(path, UNESCAPE_RELAX, &p);
287 if (r < 0)
288 return r;
289
290 if (!path_startswith(p, prefix))
291 continue;
292
293 if (umount2(p, flags) < 0) {
6b7c9f8b 294 r = log_debug_errno(errno, "Failed to umount %s: %m", p);
4349cd7c
LP
295 continue;
296 }
297
6b7c9f8b
LP
298 log_debug("Successfully unmounted %s", p);
299
4349cd7c
LP
300 again = true;
301 n++;
302
303 break;
304 }
305
306 } while (again);
307
308 return r ? r : n;
309}
310
311static int get_mount_flags(const char *path, unsigned long *flags) {
312 struct statvfs buf;
313
314 if (statvfs(path, &buf) < 0)
315 return -errno;
316 *flags = buf.f_flag;
317 return 0;
318}
319
ac9de0b3
TR
320/* Use this function only if do you have direct access to /proc/self/mountinfo
321 * and need the caller to open it for you. This is the case when /proc is
322 * masked or not mounted. Otherwise, use bind_remount_recursive. */
323int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **blacklist, FILE *proc_self_mountinfo) {
4349cd7c
LP
324 _cleanup_set_free_free_ Set *done = NULL;
325 _cleanup_free_ char *cleaned = NULL;
326 int r;
327
ac9de0b3
TR
328 assert(proc_self_mountinfo);
329
6b7c9f8b
LP
330 /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already
331 * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
332 * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to
333 * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each
334 * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We
335 * do not have any effect on future submounts that might get propagated, they migt be writable. This includes
336 * future submounts that have been triggered via autofs.
337 *
338 * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
339 * remount operation. Note that we'll ignore the blacklist for the top-level path. */
4349cd7c
LP
340
341 cleaned = strdup(prefix);
342 if (!cleaned)
343 return -ENOMEM;
344
345 path_kill_slashes(cleaned);
346
347 done = set_new(&string_hash_ops);
348 if (!done)
349 return -ENOMEM;
350
351 for (;;) {
4349cd7c
LP
352 _cleanup_set_free_free_ Set *todo = NULL;
353 bool top_autofs = false;
354 char *x;
355 unsigned long orig_flags;
356
357 todo = set_new(&string_hash_ops);
358 if (!todo)
359 return -ENOMEM;
360
ac9de0b3 361 rewind(proc_self_mountinfo);
4349cd7c
LP
362
363 for (;;) {
364 _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
365 int k;
366
367 k = fscanf(proc_self_mountinfo,
368 "%*s " /* (1) mount id */
369 "%*s " /* (2) parent id */
370 "%*s " /* (3) major:minor */
371 "%*s " /* (4) root */
372 "%ms " /* (5) mount point */
373 "%*s" /* (6) mount options (superblock) */
374 "%*[^-]" /* (7) optional fields */
375 "- " /* (8) separator */
376 "%ms " /* (9) file system type */
377 "%*s" /* (10) mount source */
378 "%*s" /* (11) mount options (bind mount) */
379 "%*[^\n]", /* some rubbish at the end */
380 &path,
381 &type);
382 if (k != 2) {
383 if (k == EOF)
384 break;
385
386 continue;
387 }
388
389 r = cunescape(path, UNESCAPE_RELAX, &p);
390 if (r < 0)
391 return r;
392
6b7c9f8b
LP
393 if (!path_startswith(p, cleaned))
394 continue;
395
396 /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall
397 * operate on. */
398 if (!path_equal(cleaned, p)) {
399 bool blacklisted = false;
400 char **i;
401
402 STRV_FOREACH(i, blacklist) {
403
404 if (path_equal(*i, cleaned))
405 continue;
406
407 if (!path_startswith(*i, cleaned))
408 continue;
409
410 if (path_startswith(p, *i)) {
411 blacklisted = true;
412 log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned);
413 break;
414 }
415 }
416 if (blacklisted)
417 continue;
418 }
419
4349cd7c
LP
420 /* Let's ignore autofs mounts. If they aren't
421 * triggered yet, we want to avoid triggering
422 * them, as we don't make any guarantees for
423 * future submounts anyway. If they are
424 * already triggered, then we will find
425 * another entry for this. */
426 if (streq(type, "autofs")) {
427 top_autofs = top_autofs || path_equal(cleaned, p);
428 continue;
429 }
430
6b7c9f8b 431 if (!set_contains(done, p)) {
4349cd7c
LP
432 r = set_consume(todo, p);
433 p = NULL;
4349cd7c
LP
434 if (r == -EEXIST)
435 continue;
436 if (r < 0)
437 return r;
438 }
439 }
440
441 /* If we have no submounts to process anymore and if
442 * the root is either already done, or an autofs, we
443 * are done */
444 if (set_isempty(todo) &&
445 (top_autofs || set_contains(done, cleaned)))
446 return 0;
447
448 if (!set_contains(done, cleaned) &&
449 !set_contains(todo, cleaned)) {
6b7c9f8b 450 /* The prefix directory itself is not yet a mount, make it one. */
4349cd7c
LP
451 if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
452 return -errno;
453
454 orig_flags = 0;
455 (void) get_mount_flags(cleaned, &orig_flags);
456 orig_flags &= ~MS_RDONLY;
457
458 if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
459 return -errno;
460
6b7c9f8b
LP
461 log_debug("Made top-level directory %s a mount point.", prefix);
462
4349cd7c
LP
463 x = strdup(cleaned);
464 if (!x)
465 return -ENOMEM;
466
467 r = set_consume(done, x);
468 if (r < 0)
469 return r;
470 }
471
472 while ((x = set_steal_first(todo))) {
473
474 r = set_consume(done, x);
475 if (r == -EEXIST || r == 0)
476 continue;
477 if (r < 0)
478 return r;
479
6b7c9f8b 480 /* Deal with mount points that are obstructed by a later mount */
e1873695 481 r = path_is_mount_point(x, NULL, 0);
98df8089
AC
482 if (r == -ENOENT || r == 0)
483 continue;
484 if (r < 0)
485 return r;
486
487 /* Try to reuse the original flag set */
4349cd7c
LP
488 orig_flags = 0;
489 (void) get_mount_flags(x, &orig_flags);
490 orig_flags &= ~MS_RDONLY;
491
98df8089
AC
492 if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
493 return -errno;
4349cd7c 494
6b7c9f8b 495 log_debug("Remounted %s read-only.", x);
4349cd7c
LP
496 }
497 }
498}
499
ac9de0b3
TR
500int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
501 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
502
503 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
504 if (!proc_self_mountinfo)
505 return -errno;
506
507 return bind_remount_recursive_with_mountinfo(prefix, ro, blacklist, proc_self_mountinfo);
508}
509
4349cd7c
LP
510int mount_move_root(const char *path) {
511 assert(path);
512
513 if (chdir(path) < 0)
514 return -errno;
515
516 if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
517 return -errno;
518
519 if (chroot(".") < 0)
520 return -errno;
521
522 if (chdir("/") < 0)
523 return -errno;
524
525 return 0;
526}
4e036b7a
LP
527
528bool fstype_is_network(const char *fstype) {
529 static const char table[] =
530 "afs\0"
531 "cifs\0"
532 "smbfs\0"
533 "sshfs\0"
534 "ncpfs\0"
535 "ncp\0"
536 "nfs\0"
537 "nfs4\0"
538 "gfs\0"
539 "gfs2\0"
a44cb5cb
ZJS
540 "glusterfs\0"
541 "pvfs2\0" /* OrangeFS */
0a86e681 542 "ocfs2\0"
67ae4366 543 "lustre\0"
a44cb5cb 544 ;
4e036b7a
LP
545
546 const char *x;
547
548 x = startswith(fstype, "fuse.");
549 if (x)
550 fstype = x;
551
552 return nulstr_contains(table, fstype);
553}
3f2c0bec 554
e2be442e
YW
555bool fstype_is_api_vfs(const char *fstype) {
556 static const char table[] =
557 "autofs\0"
558 "bpf\0"
559 "cgroup\0"
560 "cgroup2\0"
561 "configfs\0"
562 "cpuset\0"
563 "debugfs\0"
564 "devpts\0"
565 "devtmpfs\0"
566 "efivarfs\0"
567 "hugetlbfs\0"
568 "mqueue\0"
569 "proc\0"
570 "pstore\0"
571 "ramfs\0"
572 "securityfs\0"
573 "sysfs\0"
574 "tmpfs\0"
575 "tracefs\0"
576 ;
577
578 return nulstr_contains(table, fstype);
579}
580
3f2c0bec
LP
581int repeat_unmount(const char *path, int flags) {
582 bool done = false;
583
584 assert(path);
585
586 /* If there are multiple mounts on a mount point, this
587 * removes them all */
588
589 for (;;) {
590 if (umount2(path, flags) < 0) {
591
592 if (errno == EINVAL)
593 return done;
594
595 return -errno;
596 }
597
598 done = true;
599 }
600}
c4b41707
AP
601
602const char* mode_to_inaccessible_node(mode_t mode) {
b3d1d516
AP
603 /* This function maps a node type to the correspondent inaccessible node type.
604 * Character and block inaccessible devices may not be created (because major=0 and minor=0),
605 * in such case we map character and block devices to the inaccessible node type socket. */
c4b41707
AP
606 switch(mode & S_IFMT) {
607 case S_IFREG:
608 return "/run/systemd/inaccessible/reg";
609 case S_IFDIR:
610 return "/run/systemd/inaccessible/dir";
611 case S_IFCHR:
b3d1d516
AP
612 if (access("/run/systemd/inaccessible/chr", F_OK) == 0)
613 return "/run/systemd/inaccessible/chr";
614 return "/run/systemd/inaccessible/sock";
c4b41707 615 case S_IFBLK:
b3d1d516
AP
616 if (access("/run/systemd/inaccessible/blk", F_OK) == 0)
617 return "/run/systemd/inaccessible/blk";
618 return "/run/systemd/inaccessible/sock";
c4b41707
AP
619 case S_IFIFO:
620 return "/run/systemd/inaccessible/fifo";
621 case S_IFSOCK:
622 return "/run/systemd/inaccessible/sock";
623 }
624 return NULL;
625}
60e76d48
ZJS
626
627#define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "")
628static char* mount_flags_to_string(long unsigned flags) {
629 char *x;
630 _cleanup_free_ char *y = NULL;
631 long unsigned overflow;
632
633 overflow = flags & ~(MS_RDONLY |
634 MS_NOSUID |
635 MS_NODEV |
636 MS_NOEXEC |
637 MS_SYNCHRONOUS |
638 MS_REMOUNT |
639 MS_MANDLOCK |
640 MS_DIRSYNC |
641 MS_NOATIME |
642 MS_NODIRATIME |
643 MS_BIND |
644 MS_MOVE |
645 MS_REC |
646 MS_SILENT |
647 MS_POSIXACL |
648 MS_UNBINDABLE |
649 MS_PRIVATE |
650 MS_SLAVE |
651 MS_SHARED |
652 MS_RELATIME |
653 MS_KERNMOUNT |
654 MS_I_VERSION |
655 MS_STRICTATIME |
656 MS_LAZYTIME);
657
658 if (flags == 0 || overflow != 0)
659 if (asprintf(&y, "%lx", overflow) < 0)
660 return NULL;
661
662 x = strjoin(FLAG(MS_RDONLY),
663 FLAG(MS_NOSUID),
664 FLAG(MS_NODEV),
665 FLAG(MS_NOEXEC),
666 FLAG(MS_SYNCHRONOUS),
667 FLAG(MS_REMOUNT),
668 FLAG(MS_MANDLOCK),
669 FLAG(MS_DIRSYNC),
670 FLAG(MS_NOATIME),
671 FLAG(MS_NODIRATIME),
672 FLAG(MS_BIND),
673 FLAG(MS_MOVE),
674 FLAG(MS_REC),
675 FLAG(MS_SILENT),
676 FLAG(MS_POSIXACL),
677 FLAG(MS_UNBINDABLE),
678 FLAG(MS_PRIVATE),
679 FLAG(MS_SLAVE),
680 FLAG(MS_SHARED),
681 FLAG(MS_RELATIME),
682 FLAG(MS_KERNMOUNT),
683 FLAG(MS_I_VERSION),
684 FLAG(MS_STRICTATIME),
685 FLAG(MS_LAZYTIME),
605405c6 686 y);
60e76d48
ZJS
687 if (!x)
688 return NULL;
689 if (!y)
690 x[strlen(x) - 1] = '\0'; /* truncate the last | */
691 return x;
692}
693
694int mount_verbose(
695 int error_log_level,
696 const char *what,
697 const char *where,
698 const char *type,
699 unsigned long flags,
700 const char *options) {
701
702 _cleanup_free_ char *fl = NULL;
703
704 fl = mount_flags_to_string(flags);
705
706 if ((flags & MS_REMOUNT) && !what && !type)
707 log_debug("Remounting %s (%s \"%s\")...",
708 where, strnull(fl), strempty(options));
709 else if (!what && !type)
710 log_debug("Mounting %s (%s \"%s\")...",
711 where, strnull(fl), strempty(options));
712 else if ((flags & MS_BIND) && !type)
713 log_debug("Bind-mounting %s on %s (%s \"%s\")...",
714 what, where, strnull(fl), strempty(options));
afe682bc
LP
715 else if (flags & MS_MOVE)
716 log_debug("Moving mount %s → %s (%s \"%s\")...",
717 what, where, strnull(fl), strempty(options));
60e76d48
ZJS
718 else
719 log_debug("Mounting %s on %s (%s \"%s\")...",
720 strna(type), where, strnull(fl), strempty(options));
721 if (mount(what, where, type, flags, options) < 0)
722 return log_full_errno(error_log_level, errno,
723 "Failed to mount %s on %s (%s \"%s\"): %m",
724 strna(type), where, strnull(fl), strempty(options));
725 return 0;
726}
727
728int umount_verbose(const char *what) {
729 log_debug("Umounting %s...", what);
730 if (umount(what) < 0)
731 return log_error_errno(errno, "Failed to unmount %s: %m", what);
732 return 0;
733}
83555251
LP
734
735const char *mount_propagation_flags_to_string(unsigned long flags) {
736
737 switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
c7383828
ZJS
738 case 0:
739 return "";
83555251
LP
740 case MS_SHARED:
741 return "shared";
83555251
LP
742 case MS_SLAVE:
743 return "slave";
83555251
LP
744 case MS_PRIVATE:
745 return "private";
746 }
747
748 return NULL;
749}
750
83555251 751
c7383828 752int mount_propagation_flags_from_string(const char *name, unsigned long *ret) {
83555251 753
c7383828
ZJS
754 if (isempty(name))
755 *ret = 0;
756 else if (streq(name, "shared"))
757 *ret = MS_SHARED;
758 else if (streq(name, "slave"))
759 *ret = MS_SLAVE;
760 else if (streq(name, "private"))
761 *ret = MS_PRIVATE;
762 else
763 return -EINVAL;
83555251
LP
764 return 0;
765}