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