chiark / gitweb /
udevadm,basic: replace nulstr_contains with STR_IN_SET (#6965)
[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 #include "strv.h"
41
42 static 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 {
52                 subfd = openat(fd, filename, O_CLOEXEC|O_PATH);
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
80 int 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,
108          * but unionfs breaks this since it exposes file
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) {
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. */
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
165 fallback_fdinfo:
166         r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
167         if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM))
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
187 fallback_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 */
210 int path_is_mount_point(const char *t, const char *root, int flags) {
211         _cleanup_free_ char *canonical = NULL, *parent = NULL;
212         _cleanup_close_ int fd = -1;
213         int r;
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) {
225                 r = chase_symlinks(t, root, 0, &canonical);
226                 if (r < 0)
227                         return r;
228
229                 t = canonical;
230         }
231
232         parent = dirname_malloc(t);
233         if (!parent)
234                 return -ENOMEM;
235
236         fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH);
237         if (fd < 0)
238                 return -errno;
239
240         return fd_is_mount_point(fd, basename(t), flags);
241 }
242
243 #if 0 /// UNNEEDED by elogind
244 int umount_recursive(const char *prefix, int flags) {
245         bool again;
246         int n = 0, r;
247
248         /* Try to umount everything recursively below a
249          * directory. Also, take care of stacked mounts, and keep
250          * unmounting them until they are gone. */
251
252         do {
253                 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
254
255                 again = false;
256                 r = 0;
257
258                 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
259                 if (!proc_self_mountinfo)
260                         return -errno;
261
262                 for (;;) {
263                         _cleanup_free_ char *path = NULL, *p = NULL;
264                         int k;
265
266                         k = fscanf(proc_self_mountinfo,
267                                    "%*s "       /* (1) mount id */
268                                    "%*s "       /* (2) parent id */
269                                    "%*s "       /* (3) major:minor */
270                                    "%*s "       /* (4) root */
271                                    "%ms "       /* (5) mount point */
272                                    "%*s"        /* (6) mount options */
273                                    "%*[^-]"     /* (7) optional fields */
274                                    "- "         /* (8) separator */
275                                    "%*s "       /* (9) file system type */
276                                    "%*s"        /* (10) mount source */
277                                    "%*s"        /* (11) mount options 2 */
278                                    "%*[^\n]",   /* some rubbish at the end */
279                                    &path);
280                         if (k != 1) {
281                                 if (k == EOF)
282                                         break;
283
284                                 continue;
285                         }
286
287                         r = cunescape(path, UNESCAPE_RELAX, &p);
288                         if (r < 0)
289                                 return r;
290
291                         if (!path_startswith(p, prefix))
292                                 continue;
293
294                         if (umount2(p, flags) < 0) {
295                                 r = log_debug_errno(errno, "Failed to umount %s: %m", p);
296                                 continue;
297                         }
298
299                         log_debug("Successfully unmounted %s", p);
300
301                         again = true;
302                         n++;
303
304                         break;
305                 }
306
307         } while (again);
308
309         return r ? r : n;
310 }
311
312 static int get_mount_flags(const char *path, unsigned long *flags) {
313         struct statvfs buf;
314
315         if (statvfs(path, &buf) < 0)
316                 return -errno;
317         *flags = buf.f_flag;
318         return 0;
319 }
320
321 /* Use this function only if do you have direct access to /proc/self/mountinfo
322  * and need the caller to open it for you. This is the case when /proc is
323  * masked or not mounted. Otherwise, use bind_remount_recursive. */
324 int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **blacklist, FILE *proc_self_mountinfo) {
325         _cleanup_set_free_free_ Set *done = NULL;
326         _cleanup_free_ char *cleaned = NULL;
327         int r;
328
329         assert(proc_self_mountinfo);
330
331         /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already
332          * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
333          * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to
334          * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each
335          * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We
336          * do not have any effect on future submounts that might get propagated, they migt be writable. This includes
337          * future submounts that have been triggered via autofs.
338          *
339          * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
340          * remount operation. Note that we'll ignore the blacklist for the top-level path. */
341
342         cleaned = strdup(prefix);
343         if (!cleaned)
344                 return -ENOMEM;
345
346         path_kill_slashes(cleaned);
347
348         done = set_new(&string_hash_ops);
349         if (!done)
350                 return -ENOMEM;
351
352         for (;;) {
353                 _cleanup_set_free_free_ Set *todo = NULL;
354                 bool top_autofs = false;
355                 char *x;
356                 unsigned long orig_flags;
357
358                 todo = set_new(&string_hash_ops);
359                 if (!todo)
360                         return -ENOMEM;
361
362                 rewind(proc_self_mountinfo);
363
364                 for (;;) {
365                         _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
366                         int k;
367
368                         k = fscanf(proc_self_mountinfo,
369                                    "%*s "       /* (1) mount id */
370                                    "%*s "       /* (2) parent id */
371                                    "%*s "       /* (3) major:minor */
372                                    "%*s "       /* (4) root */
373                                    "%ms "       /* (5) mount point */
374                                    "%*s"        /* (6) mount options (superblock) */
375                                    "%*[^-]"     /* (7) optional fields */
376                                    "- "         /* (8) separator */
377                                    "%ms "       /* (9) file system type */
378                                    "%*s"        /* (10) mount source */
379                                    "%*s"        /* (11) mount options (bind mount) */
380                                    "%*[^\n]",   /* some rubbish at the end */
381                                    &path,
382                                    &type);
383                         if (k != 2) {
384                                 if (k == EOF)
385                                         break;
386
387                                 continue;
388                         }
389
390                         r = cunescape(path, UNESCAPE_RELAX, &p);
391                         if (r < 0)
392                                 return r;
393
394                         if (!path_startswith(p, cleaned))
395                                 continue;
396
397                         /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall
398                          * operate on. */
399                         if (!path_equal(cleaned, p)) {
400                                 bool blacklisted = false;
401                                 char **i;
402
403                                 STRV_FOREACH(i, blacklist) {
404
405                                         if (path_equal(*i, cleaned))
406                                                 continue;
407
408                                         if (!path_startswith(*i, cleaned))
409                                                 continue;
410
411                                         if (path_startswith(p, *i)) {
412                                                 blacklisted = true;
413                                                 log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned);
414                                                 break;
415                                         }
416                                 }
417                                 if (blacklisted)
418                                         continue;
419                         }
420
421                         /* Let's ignore autofs mounts.  If they aren't
422                          * triggered yet, we want to avoid triggering
423                          * them, as we don't make any guarantees for
424                          * future submounts anyway.  If they are
425                          * already triggered, then we will find
426                          * another entry for this. */
427                         if (streq(type, "autofs")) {
428                                 top_autofs = top_autofs || path_equal(cleaned, p);
429                                 continue;
430                         }
431
432                         if (!set_contains(done, p)) {
433                                 r = set_consume(todo, p);
434                                 p = NULL;
435                                 if (r == -EEXIST)
436                                         continue;
437                                 if (r < 0)
438                                         return r;
439                         }
440                 }
441
442                 /* If we have no submounts to process anymore and if
443                  * the root is either already done, or an autofs, we
444                  * are done */
445                 if (set_isempty(todo) &&
446                     (top_autofs || set_contains(done, cleaned)))
447                         return 0;
448
449                 if (!set_contains(done, cleaned) &&
450                     !set_contains(todo, cleaned)) {
451                         /* The prefix directory itself is not yet a mount, make it one. */
452                         if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
453                                 return -errno;
454
455                         orig_flags = 0;
456                         (void) get_mount_flags(cleaned, &orig_flags);
457                         orig_flags &= ~MS_RDONLY;
458
459                         if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
460                                 return -errno;
461
462                         log_debug("Made top-level directory %s a mount point.", prefix);
463
464                         x = strdup(cleaned);
465                         if (!x)
466                                 return -ENOMEM;
467
468                         r = set_consume(done, x);
469                         if (r < 0)
470                                 return r;
471                 }
472
473                 while ((x = set_steal_first(todo))) {
474
475                         r = set_consume(done, x);
476                         if (IN_SET(r, 0, -EEXIST))
477                                 continue;
478                         if (r < 0)
479                                 return r;
480
481                         /* Deal with mount points that are obstructed by a later mount */
482                         r = path_is_mount_point(x, NULL, 0);
483                         if (IN_SET(r, 0, -ENOENT))
484                                 continue;
485                         if (r < 0)
486                                 return r;
487
488                         /* Try to reuse the original flag set */
489                         orig_flags = 0;
490                         (void) get_mount_flags(x, &orig_flags);
491                         orig_flags &= ~MS_RDONLY;
492
493                         if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
494                                 return -errno;
495
496                         log_debug("Remounted %s read-only.", x);
497                 }
498         }
499 }
500
501 int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
502         _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
503
504         proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
505         if (!proc_self_mountinfo)
506                 return -errno;
507
508         return bind_remount_recursive_with_mountinfo(prefix, ro, blacklist, proc_self_mountinfo);
509 }
510
511 int mount_move_root(const char *path) {
512         assert(path);
513
514         if (chdir(path) < 0)
515                 return -errno;
516
517         if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
518                 return -errno;
519
520         if (chroot(".") < 0)
521                 return -errno;
522
523         if (chdir("/") < 0)
524                 return -errno;
525
526         return 0;
527 }
528
529 bool fstype_is_network(const char *fstype) {
530         const char *x;
531
532         x = startswith(fstype, "fuse.");
533         if (x)
534                 fstype = x;
535
536         return STR_IN_SET(fstype,
537                           "afs",
538                           "cifs",
539                           "smbfs",
540                           "sshfs",
541                           "ncpfs",
542                           "ncp",
543                           "nfs",
544                           "nfs4",
545                           "gfs",
546                           "gfs2",
547                           "glusterfs",
548                           "pvfs2", /* OrangeFS */
549                           "ocfs2",
550                           "lustre");
551 }
552
553 bool fstype_is_api_vfs(const char *fstype) {
554         return STR_IN_SET(fstype,
555                           "autofs",
556                           "bpf",
557                           "cgroup",
558                           "cgroup2",
559                           "configfs",
560                           "cpuset",
561                           "debugfs",
562                           "devpts",
563                           "devtmpfs",
564                           "efivarfs",
565                           "fusectl",
566                           "hugetlbfs",
567                           "mqueue",
568                           "proc",
569                           "pstore",
570                           "ramfs",
571                           "securityfs",
572                           "sysfs",
573                           "tmpfs",
574                           "tracefs");
575 }
576
577 bool fstype_is_ro(const char *fstype) {
578         /* All Linux file systems that are necessarily read-only */
579         return STR_IN_SET(fstype,
580                           "DM_verity_hash",
581                           "iso9660",
582                           "squashfs");
583 }
584
585 bool fstype_can_discard(const char *fstype) {
586         return STR_IN_SET(fstype,
587                           "btrfs",
588                           "ext4",
589                           "vfat",
590                           "xfs");
591 }
592
593 int repeat_unmount(const char *path, int flags) {
594         bool done = false;
595
596         assert(path);
597
598         /* If there are multiple mounts on a mount point, this
599          * removes them all */
600
601         for (;;) {
602                 if (umount2(path, flags) < 0) {
603
604                         if (errno == EINVAL)
605                                 return done;
606
607                         return -errno;
608                 }
609
610                 done = true;
611         }
612 }
613 #endif // 0
614
615 const char* mode_to_inaccessible_node(mode_t mode) {
616         /* This function maps a node type to the correspondent inaccessible node type.
617          * Character and block inaccessible devices may not be created (because major=0 and minor=0),
618          * in such case we map character and block devices to the inaccessible node type socket. */
619         switch(mode & S_IFMT) {
620                 case S_IFREG:
621                         return "/run/systemd/inaccessible/reg";
622                 case S_IFDIR:
623                         return "/run/systemd/inaccessible/dir";
624                 case S_IFCHR:
625                         if (access("/run/systemd/inaccessible/chr", F_OK) == 0)
626                                 return "/run/systemd/inaccessible/chr";
627                         return "/run/systemd/inaccessible/sock";
628                 case S_IFBLK:
629                         if (access("/run/systemd/inaccessible/blk", F_OK) == 0)
630                                 return "/run/systemd/inaccessible/blk";
631                         return "/run/systemd/inaccessible/sock";
632                 case S_IFIFO:
633                         return "/run/systemd/inaccessible/fifo";
634                 case S_IFSOCK:
635                         return "/run/systemd/inaccessible/sock";
636         }
637         return NULL;
638 }
639
640 #if 0 /// UNNEEDED by elogind
641 #define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "")
642 static char* mount_flags_to_string(long unsigned flags) {
643         char *x;
644         _cleanup_free_ char *y = NULL;
645         long unsigned overflow;
646
647         overflow = flags & ~(MS_RDONLY |
648                              MS_NOSUID |
649                              MS_NODEV |
650                              MS_NOEXEC |
651                              MS_SYNCHRONOUS |
652                              MS_REMOUNT |
653                              MS_MANDLOCK |
654                              MS_DIRSYNC |
655                              MS_NOATIME |
656                              MS_NODIRATIME |
657                              MS_BIND |
658                              MS_MOVE |
659                              MS_REC |
660                              MS_SILENT |
661                              MS_POSIXACL |
662                              MS_UNBINDABLE |
663                              MS_PRIVATE |
664                              MS_SLAVE |
665                              MS_SHARED |
666                              MS_RELATIME |
667                              MS_KERNMOUNT |
668                              MS_I_VERSION |
669                              MS_STRICTATIME |
670                              MS_LAZYTIME);
671
672         if (flags == 0 || overflow != 0)
673                 if (asprintf(&y, "%lx", overflow) < 0)
674                         return NULL;
675
676         x = strjoin(FLAG(MS_RDONLY),
677                     FLAG(MS_NOSUID),
678                     FLAG(MS_NODEV),
679                     FLAG(MS_NOEXEC),
680                     FLAG(MS_SYNCHRONOUS),
681                     FLAG(MS_REMOUNT),
682                     FLAG(MS_MANDLOCK),
683                     FLAG(MS_DIRSYNC),
684                     FLAG(MS_NOATIME),
685                     FLAG(MS_NODIRATIME),
686                     FLAG(MS_BIND),
687                     FLAG(MS_MOVE),
688                     FLAG(MS_REC),
689                     FLAG(MS_SILENT),
690                     FLAG(MS_POSIXACL),
691                     FLAG(MS_UNBINDABLE),
692                     FLAG(MS_PRIVATE),
693                     FLAG(MS_SLAVE),
694                     FLAG(MS_SHARED),
695                     FLAG(MS_RELATIME),
696                     FLAG(MS_KERNMOUNT),
697                     FLAG(MS_I_VERSION),
698                     FLAG(MS_STRICTATIME),
699                     FLAG(MS_LAZYTIME),
700                     y);
701         if (!x)
702                 return NULL;
703         if (!y)
704                 x[strlen(x) - 1] = '\0'; /* truncate the last | */
705         return x;
706 }
707
708 int mount_verbose(
709                 int error_log_level,
710                 const char *what,
711                 const char *where,
712                 const char *type,
713                 unsigned long flags,
714                 const char *options) {
715
716         _cleanup_free_ char *fl = NULL;
717
718         fl = mount_flags_to_string(flags);
719
720         if ((flags & MS_REMOUNT) && !what && !type)
721                 log_debug("Remounting %s (%s \"%s\")...",
722                           where, strnull(fl), strempty(options));
723         else if (!what && !type)
724                 log_debug("Mounting %s (%s \"%s\")...",
725                           where, strnull(fl), strempty(options));
726         else if ((flags & MS_BIND) && !type)
727                 log_debug("Bind-mounting %s on %s (%s \"%s\")...",
728                           what, where, strnull(fl), strempty(options));
729         else if (flags & MS_MOVE)
730                 log_debug("Moving mount %s → %s (%s \"%s\")...",
731                           what, where, strnull(fl), strempty(options));
732         else
733                 log_debug("Mounting %s on %s (%s \"%s\")...",
734                           strna(type), where, strnull(fl), strempty(options));
735         if (mount(what, where, type, flags, options) < 0)
736                 return log_full_errno(error_log_level, errno,
737                                       "Failed to mount %s on %s (%s \"%s\"): %m",
738                                       strna(type), where, strnull(fl), strempty(options));
739         return 0;
740 }
741
742 int umount_verbose(const char *what) {
743         log_debug("Umounting %s...", what);
744         if (umount(what) < 0)
745                 return log_error_errno(errno, "Failed to unmount %s: %m", what);
746         return 0;
747 }
748 #endif // 0
749
750 const char *mount_propagation_flags_to_string(unsigned long flags) {
751
752         switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
753         case 0:
754                 return "";
755         case MS_SHARED:
756                 return "shared";
757         case MS_SLAVE:
758                 return "slave";
759         case MS_PRIVATE:
760                 return "private";
761         }
762
763         return NULL;
764 }
765
766
767 int mount_propagation_flags_from_string(const char *name, unsigned long *ret) {
768
769         if (isempty(name))
770                 *ret = 0;
771         else if (streq(name, "shared"))
772                 *ret = MS_SHARED;
773         else if (streq(name, "slave"))
774                 *ret = MS_SLAVE;
775         else if (streq(name, "private"))
776                 *ret = MS_PRIVATE;
777         else
778                 return -EINVAL;
779         return 0;
780 }