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