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