chiark / gitweb /
dissect: automatically mark partitions read-only that have a read-only file system
[elogind.git] / src / basic / mount-util.c
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
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/mount.h>
24 #include <sys/stat.h>
25 #include <sys/statvfs.h>
26 #include <unistd.h>
27
28 #include "alloc-util.h"
29 #include "escape.h"
30 #include "fd-util.h"
31 #include "fileio.h"
32 #include "fs-util.h"
33 #include "hashmap.h"
34 #include "mount-util.h"
35 #include "parse-util.h"
36 #include "path-util.h"
37 #include "set.h"
38 #include "stdio-util.h"
39 #include "string-util.h"
40
41 static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
42         char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
43         _cleanup_free_ char *fdinfo = NULL;
44         _cleanup_close_ int subfd = -1;
45         char *p;
46         int r;
47
48         if ((flags & AT_EMPTY_PATH) && isempty(filename))
49                 xsprintf(path, "/proc/self/fdinfo/%i", fd);
50         else {
51                 subfd = openat(fd, filename, O_CLOEXEC|O_PATH);
52                 if (subfd < 0)
53                         return -errno;
54
55                 xsprintf(path, "/proc/self/fdinfo/%i", subfd);
56         }
57
58         r = read_full_file(path, &fdinfo, NULL);
59         if (r == -ENOENT) /* The fdinfo directory is a relatively new addition */
60                 return -EOPNOTSUPP;
61         if (r < 0)
62                 return -errno;
63
64         p = startswith(fdinfo, "mnt_id:");
65         if (!p) {
66                 p = strstr(fdinfo, "\nmnt_id:");
67                 if (!p) /* The mnt_id field is a relatively new addition */
68                         return -EOPNOTSUPP;
69
70                 p += 8;
71         }
72
73         p += strspn(p, WHITESPACE);
74         p[strcspn(p, WHITESPACE)] = 0;
75
76         return safe_atoi(p, mnt_id);
77 }
78
79 int fd_is_mount_point(int fd, const char *filename, int flags) {
80         union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
81         int mount_id = -1, mount_id_parent = -1;
82         bool nosupp = false, check_st_dev = true;
83         struct stat a, b;
84         int r;
85
86         assert(fd >= 0);
87         assert(filename);
88
89         /* First we will try the name_to_handle_at() syscall, which
90          * tells us the mount id and an opaque file "handle". It is
91          * not supported everywhere though (kernel compile-time
92          * option, not all file systems are hooked up). If it works
93          * the mount id is usually good enough to tell us whether
94          * something is a mount point.
95          *
96          * If that didn't work we will try to read the mount id from
97          * /proc/self/fdinfo/<fd>. This is almost as good as
98          * name_to_handle_at(), however, does not return the
99          * opaque file handle. The opaque file handle is pretty useful
100          * to detect the root directory, which we should always
101          * consider a mount point. Hence we use this only as
102          * fallback. Exporting the mnt_id in fdinfo is a pretty recent
103          * kernel addition.
104          *
105          * As last fallback we do traditional fstat() based st_dev
106          * comparisons. This is how things were traditionally done,
107          * but unionfs breaks this since it exposes file
108          * systems with a variety of st_dev reported. Also, btrfs
109          * subvolumes have different st_dev, even though they aren't
110          * real mounts of their own. */
111
112         r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
113         if (r < 0) {
114                 if (IN_SET(errno, ENOSYS, EACCES, EPERM))
115                         /* This kernel does not support name_to_handle_at() at all, or the syscall was blocked (maybe
116                          * through seccomp, because we are running inside of a container?): fall back to simpler
117                          * logic. */
118                         goto fallback_fdinfo;
119                 else if (errno == EOPNOTSUPP)
120                         /* This kernel or file system does not support
121                          * name_to_handle_at(), hence let's see if the
122                          * upper fs supports it (in which case it is a
123                          * mount point), otherwise fallback to the
124                          * traditional stat() logic */
125                         nosupp = true;
126                 else
127                         return -errno;
128         }
129
130         r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
131         if (r < 0) {
132                 if (errno == EOPNOTSUPP) {
133                         if (nosupp)
134                                 /* Neither parent nor child do name_to_handle_at()?
135                                    We have no choice but to fall back. */
136                                 goto fallback_fdinfo;
137                         else
138                                 /* The parent can't do name_to_handle_at() but the
139                                  * directory we are interested in can?
140                                  * If so, it must be a mount point. */
141                                 return 1;
142                 } else
143                         return -errno;
144         }
145
146         /* The parent can do name_to_handle_at() but the
147          * directory we are interested in can't? If so, it
148          * must be a mount point. */
149         if (nosupp)
150                 return 1;
151
152         /* If the file handle for the directory we are
153          * interested in and its parent are identical, we
154          * assume this is the root directory, which is a mount
155          * point. */
156
157         if (h.handle.handle_bytes == h_parent.handle.handle_bytes &&
158             h.handle.handle_type == h_parent.handle.handle_type &&
159             memcmp(h.handle.f_handle, h_parent.handle.f_handle, h.handle.handle_bytes) == 0)
160                 return 1;
161
162         return mount_id != mount_id_parent;
163
164 fallback_fdinfo:
165         r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
166         if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM))
167                 goto fallback_fstat;
168         if (r < 0)
169                 return r;
170
171         r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
172         if (r < 0)
173                 return r;
174
175         if (mount_id != mount_id_parent)
176                 return 1;
177
178         /* Hmm, so, the mount ids are the same. This leaves one
179          * special case though for the root file system. For that,
180          * let's see if the parent directory has the same inode as we
181          * are interested in. Hence, let's also do fstat() checks now,
182          * too, but avoid the st_dev comparisons, since they aren't
183          * that useful on unionfs mounts. */
184         check_st_dev = false;
185
186 fallback_fstat:
187         /* yay for fstatat() taking a different set of flags than the other
188          * _at() above */
189         if (flags & AT_SYMLINK_FOLLOW)
190                 flags &= ~AT_SYMLINK_FOLLOW;
191         else
192                 flags |= AT_SYMLINK_NOFOLLOW;
193         if (fstatat(fd, filename, &a, flags) < 0)
194                 return -errno;
195
196         if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
197                 return -errno;
198
199         /* A directory with same device and inode as its parent? Must
200          * be the root directory */
201         if (a.st_dev == b.st_dev &&
202             a.st_ino == b.st_ino)
203                 return 1;
204
205         return check_st_dev && (a.st_dev != b.st_dev);
206 }
207
208 /* flags can be AT_SYMLINK_FOLLOW or 0 */
209 int path_is_mount_point(const char *t, const char *root, int flags) {
210         _cleanup_free_ char *canonical = NULL, *parent = NULL;
211         _cleanup_close_ int fd = -1;
212         int r;
213
214         assert(t);
215
216         if (path_equal(t, "/"))
217                 return 1;
218
219         /* we need to resolve symlinks manually, we can't just rely on
220          * fd_is_mount_point() to do that for us; if we have a structure like
221          * /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
222          * look at needs to be /usr, not /. */
223         if (flags & AT_SYMLINK_FOLLOW) {
224                 r = chase_symlinks(t, root, 0, &canonical);
225                 if (r < 0)
226                         return r;
227
228                 t = canonical;
229         }
230
231         parent = dirname_malloc(t);
232         if (!parent)
233                 return -ENOMEM;
234
235         fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH);
236         if (fd < 0)
237                 return -errno;
238
239         return fd_is_mount_point(fd, basename(t), flags);
240 }
241
242 #if 0 /// UNNEEDED by elogind
243 int 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) {
294                                 r = log_debug_errno(errno, "Failed to umount %s: %m", p);
295                                 continue;
296                         }
297
298                         log_debug("Successfully unmounted %s", p);
299
300                         again = true;
301                         n++;
302
303                         break;
304                 }
305
306         } while (again);
307
308         return r ? r : n;
309 }
310
311 static 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
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. */
323 int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **blacklist, FILE *proc_self_mountinfo) {
324         _cleanup_set_free_free_ Set *done = NULL;
325         _cleanup_free_ char *cleaned = NULL;
326         int r;
327
328         assert(proc_self_mountinfo);
329
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. */
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 (;;) {
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
361                 rewind(proc_self_mountinfo);
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
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
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
431                         if (!set_contains(done, p)) {
432                                 r = set_consume(todo, p);
433                                 p = NULL;
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)) {
450                         /* The prefix directory itself is not yet a mount, make it one. */
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
461                         log_debug("Made top-level directory %s a mount point.", prefix);
462
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
480                         /* Deal with mount points that are obstructed by a later mount */
481                         r = path_is_mount_point(x, NULL, 0);
482                         if (r == -ENOENT || r == 0)
483                                 continue;
484                         if (r < 0)
485                                 return r;
486
487                         /* Try to reuse the original flag set */
488                         orig_flags = 0;
489                         (void) get_mount_flags(x, &orig_flags);
490                         orig_flags &= ~MS_RDONLY;
491
492                         if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
493                                 return -errno;
494
495                         log_debug("Remounted %s read-only.", x);
496                 }
497         }
498 }
499
500 int 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
510 int 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 }
527
528 bool 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"
540                 "glusterfs\0"
541                 "pvfs2\0" /* OrangeFS */
542                 "ocfs2\0"
543                 "lustre\0"
544                 ;
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 }
554
555 bool 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
581 bool fstype_is_ro(const char *fstype) {
582
583         /* All Linux file systems that are necessarily read-only */
584
585         static const char table[] =
586                 "DM_verity_hash\0"
587                 "iso9660\0"
588                 "squashfs\0"
589                 ;
590
591         return nulstr_contains(table, fstype);
592 }
593
594 int repeat_unmount(const char *path, int flags) {
595         bool done = false;
596
597         assert(path);
598
599         /* If there are multiple mounts on a mount point, this
600          * removes them all */
601
602         for (;;) {
603                 if (umount2(path, flags) < 0) {
604
605                         if (errno == EINVAL)
606                                 return done;
607
608                         return -errno;
609                 }
610
611                 done = true;
612         }
613 }
614 #endif // 0
615
616 const char* mode_to_inaccessible_node(mode_t mode) {
617         /* This function maps a node type to the correspondent inaccessible node type.
618          * Character and block inaccessible devices may not be created (because major=0 and minor=0),
619          * in such case we map character and block devices to the inaccessible node type socket. */
620         switch(mode & S_IFMT) {
621                 case S_IFREG:
622                         return "/run/systemd/inaccessible/reg";
623                 case S_IFDIR:
624                         return "/run/systemd/inaccessible/dir";
625                 case S_IFCHR:
626                         if (access("/run/systemd/inaccessible/chr", F_OK) == 0)
627                                 return "/run/systemd/inaccessible/chr";
628                         return "/run/systemd/inaccessible/sock";
629                 case S_IFBLK:
630                         if (access("/run/systemd/inaccessible/blk", F_OK) == 0)
631                                 return "/run/systemd/inaccessible/blk";
632                         return "/run/systemd/inaccessible/sock";
633                 case S_IFIFO:
634                         return "/run/systemd/inaccessible/fifo";
635                 case S_IFSOCK:
636                         return "/run/systemd/inaccessible/sock";
637         }
638         return NULL;
639 }
640
641 #if 0 /// UNNEEDED by elogind
642 #define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "")
643 static char* mount_flags_to_string(long unsigned flags) {
644         char *x;
645         _cleanup_free_ char *y = NULL;
646         long unsigned overflow;
647
648         overflow = flags & ~(MS_RDONLY |
649                              MS_NOSUID |
650                              MS_NODEV |
651                              MS_NOEXEC |
652                              MS_SYNCHRONOUS |
653                              MS_REMOUNT |
654                              MS_MANDLOCK |
655                              MS_DIRSYNC |
656                              MS_NOATIME |
657                              MS_NODIRATIME |
658                              MS_BIND |
659                              MS_MOVE |
660                              MS_REC |
661                              MS_SILENT |
662                              MS_POSIXACL |
663                              MS_UNBINDABLE |
664                              MS_PRIVATE |
665                              MS_SLAVE |
666                              MS_SHARED |
667                              MS_RELATIME |
668                              MS_KERNMOUNT |
669                              MS_I_VERSION |
670                              MS_STRICTATIME |
671                              MS_LAZYTIME);
672
673         if (flags == 0 || overflow != 0)
674                 if (asprintf(&y, "%lx", overflow) < 0)
675                         return NULL;
676
677         x = strjoin(FLAG(MS_RDONLY),
678                     FLAG(MS_NOSUID),
679                     FLAG(MS_NODEV),
680                     FLAG(MS_NOEXEC),
681                     FLAG(MS_SYNCHRONOUS),
682                     FLAG(MS_REMOUNT),
683                     FLAG(MS_MANDLOCK),
684                     FLAG(MS_DIRSYNC),
685                     FLAG(MS_NOATIME),
686                     FLAG(MS_NODIRATIME),
687                     FLAG(MS_BIND),
688                     FLAG(MS_MOVE),
689                     FLAG(MS_REC),
690                     FLAG(MS_SILENT),
691                     FLAG(MS_POSIXACL),
692                     FLAG(MS_UNBINDABLE),
693                     FLAG(MS_PRIVATE),
694                     FLAG(MS_SLAVE),
695                     FLAG(MS_SHARED),
696                     FLAG(MS_RELATIME),
697                     FLAG(MS_KERNMOUNT),
698                     FLAG(MS_I_VERSION),
699                     FLAG(MS_STRICTATIME),
700                     FLAG(MS_LAZYTIME),
701                     y);
702         if (!x)
703                 return NULL;
704         if (!y)
705                 x[strlen(x) - 1] = '\0'; /* truncate the last | */
706         return x;
707 }
708
709 int mount_verbose(
710                 int error_log_level,
711                 const char *what,
712                 const char *where,
713                 const char *type,
714                 unsigned long flags,
715                 const char *options) {
716
717         _cleanup_free_ char *fl = NULL;
718
719         fl = mount_flags_to_string(flags);
720
721         if ((flags & MS_REMOUNT) && !what && !type)
722                 log_debug("Remounting %s (%s \"%s\")...",
723                           where, strnull(fl), strempty(options));
724         else if (!what && !type)
725                 log_debug("Mounting %s (%s \"%s\")...",
726                           where, strnull(fl), strempty(options));
727         else if ((flags & MS_BIND) && !type)
728                 log_debug("Bind-mounting %s on %s (%s \"%s\")...",
729                           what, where, strnull(fl), strempty(options));
730         else if (flags & MS_MOVE)
731                 log_debug("Moving mount %s → %s (%s \"%s\")...",
732                           what, where, strnull(fl), strempty(options));
733         else
734                 log_debug("Mounting %s on %s (%s \"%s\")...",
735                           strna(type), where, strnull(fl), strempty(options));
736         if (mount(what, where, type, flags, options) < 0)
737                 return log_full_errno(error_log_level, errno,
738                                       "Failed to mount %s on %s (%s \"%s\"): %m",
739                                       strna(type), where, strnull(fl), strempty(options));
740         return 0;
741 }
742
743 int umount_verbose(const char *what) {
744         log_debug("Umounting %s...", what);
745         if (umount(what) < 0)
746                 return log_error_errno(errno, "Failed to unmount %s: %m", what);
747         return 0;
748 }
749 #endif // 0
750
751 const char *mount_propagation_flags_to_string(unsigned long flags) {
752
753         switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
754         case 0:
755                 return "";
756         case MS_SHARED:
757                 return "shared";
758         case MS_SLAVE:
759                 return "slave";
760         case MS_PRIVATE:
761                 return "private";
762         }
763
764         return NULL;
765 }
766
767
768 int mount_propagation_flags_from_string(const char *name, unsigned long *ret) {
769
770         if (isempty(name))
771                 *ret = 0;
772         else if (streq(name, "shared"))
773                 *ret = MS_SHARED;
774         else if (streq(name, "slave"))
775                 *ret = MS_SLAVE;
776         else if (streq(name, "private"))
777                 *ret = MS_PRIVATE;
778         else
779                 return -EINVAL;
780         return 0;
781 }