chiark / gitweb /
sysusers: add minimal tool to reconstruct /etc/passwd and /etc/group from static...
[elogind.git] / src / shared / util.c
index dd67c2264164b0c04c32a630e11cc872e162245e..a7aec5c54f53114b991084277055901e77f74a96 100644 (file)
@@ -54,6 +54,7 @@
 #include <grp.h>
 #include <sys/mman.h>
 #include <sys/vfs.h>
+#include <sys/mount.h>
 #include <linux/magic.h>
 #include <limits.h>
 #include <langinfo.h>
@@ -73,6 +74,7 @@
 #include "log.h"
 #include "strv.h"
 #include "label.h"
+#include "mkdir.h"
 #include "path-util.h"
 #include "exit-status.h"
 #include "hashmap.h"
@@ -83,7 +85,6 @@
 #include "gunicode.h"
 #include "virt.h"
 #include "def.h"
-#include "missing.h"
 
 int saved_argc = 0;
 char **saved_argv = NULL;
@@ -166,19 +167,19 @@ int close_nointr(int fd) {
 
         assert(fd >= 0);
         r = close(fd);
-
-        /* Just ignore EINTR; a retry loop is the wrong
-         * thing to do on Linux.
-         *
-         * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
-         * https://bugzilla.gnome.org/show_bug.cgi?id=682819
-         * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
-         * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
-         */
-        if (_unlikely_(r < 0 && errno == EINTR))
-                return 0;
-        else if (r >= 0)
+        if (r >= 0)
                 return r;
+        else if (errno == EINTR)
+                /*
+                 * Just ignore EINTR; a retry loop is the wrong
+                 * thing to do on Linux.
+                 *
+                 * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html
+                 * https://bugzilla.gnome.org/show_bug.cgi?id=682819
+                 * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR
+                 * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain
+                 */
+                return 0;
         else
                 return -errno;
 }
@@ -195,7 +196,13 @@ int safe_close(int fd) {
 
         if (fd >= 0) {
                 PROTECT_ERRNO;
-                assert_se(close_nointr(fd) == 0);
+
+                /* The kernel might return pretty much any error code
+                 * via close(), but the fd will be closed anyway. The
+                 * only condition we want to check for here is whether
+                 * the fd was invalid at all... */
+
+                assert_se(close_nointr(fd) != -EBADF);
         }
 
         return -1;
@@ -797,7 +804,7 @@ char *strappend(const char *s, const char *suffix) {
         return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
 }
 
-int readlink_malloc(const char *p, char **ret) {
+int readlinkat_malloc(int fd, const char *p, char **ret) {
         size_t l = 100;
         int r;
 
@@ -812,7 +819,7 @@ int readlink_malloc(const char *p, char **ret) {
                 if (!c)
                         return -ENOMEM;
 
-                n = readlink(p, c, l-1);
+                n = readlinkat(fd, p, c, l-1);
                 if (n < 0) {
                         r = -errno;
                         free(c);
@@ -830,6 +837,10 @@ int readlink_malloc(const char *p, char **ret) {
         }
 }
 
+int readlink_malloc(const char *p, char **ret) {
+        return readlinkat_malloc(AT_FDCWD, p, ret);
+}
+
 int readlink_and_make_absolute(const char *p, char **r) {
         _cleanup_free_ char *target = NULL;
         char *k;
@@ -1365,44 +1376,52 @@ bool ignore_file(const char *filename) {
         assert(filename);
 
         if (endswith(filename, "~"))
-                return false;
+                return true;
 
         return ignore_file_allow_backup(filename);
 }
 
 int fd_nonblock(int fd, bool nonblock) {
-        int flags;
+        int flags, nflags;
 
         assert(fd >= 0);
 
-        if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
+        flags = fcntl(fd, F_GETFL, 0);
+        if (flags < 0)
                 return -errno;
 
         if (nonblock)
-                flags |= O_NONBLOCK;
+                nflags = flags | O_NONBLOCK;
         else
-                flags &= ~O_NONBLOCK;
+                nflags = flags & ~O_NONBLOCK;
 
-        if (fcntl(fd, F_SETFL, flags) < 0)
+        if (nflags == flags)
+                return 0;
+
+        if (fcntl(fd, F_SETFL, nflags) < 0)
                 return -errno;
 
         return 0;
 }
 
 int fd_cloexec(int fd, bool cloexec) {
-        int flags;
+        int flags, nflags;
 
         assert(fd >= 0);
 
-        if ((flags = fcntl(fd, F_GETFD, 0)) < 0)
+        flags = fcntl(fd, F_GETFD, 0);
+        if (flags < 0)
                 return -errno;
 
         if (cloexec)
-                flags |= FD_CLOEXEC;
+                nflags = flags | FD_CLOEXEC;
         else
-                flags &= ~FD_CLOEXEC;
+                nflags = flags & ~FD_CLOEXEC;
+
+        if (nflags == flags)
+                return 0;
 
-        if (fcntl(fd, F_SETFD, flags) < 0)
+        if (fcntl(fd, F_SETFD, nflags) < 0)
                 return -errno;
 
         return 0;
@@ -1500,7 +1519,14 @@ bool fstype_is_network(const char *fstype) {
                 "nfs\0"
                 "nfs4\0"
                 "gfs\0"
-                "gfs2\0";
+                "gfs2\0"
+                "glusterfs\0";
+
+        const char *x;
+
+        x = startswith(fstype, "fuse.");
+        if (x)
+                fstype = x;
 
         return nulstr_contains(table, fstype);
 }
@@ -2004,7 +2030,6 @@ int ignore_signals(int sig, ...) {
         va_list ap;
         int r = 0;
 
-
         if (sigaction(sig, &sa, NULL) < 0)
                 r = -errno;
 
@@ -2430,6 +2455,24 @@ void sigset_add_many(sigset_t *ss, ...) {
         va_end(ap);
 }
 
+int sigprocmask_many(int how, ...) {
+        va_list ap;
+        sigset_t ss;
+        int sig;
+
+        assert_se(sigemptyset(&ss) == 0);
+
+        va_start(ap, how);
+        while ((sig = va_arg(ap, int)) > 0)
+                assert_se(sigaddset(&ss, sig) == 0);
+        va_end(ap);
+
+        if (sigprocmask(how, &ss, NULL) < 0)
+                return -errno;
+
+        return 0;
+}
+
 char* gethostname_malloc(void) {
         struct utsname u;
 
@@ -2470,7 +2513,7 @@ static char *lookup_uid(uid_t uid) {
         if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw)
                 return strdup(pw->pw_name);
 
-        if (asprintf(&name, "%lu", (unsigned long) uid) < 0)
+        if (asprintf(&name, UID_FMT, uid) < 0)
                 return NULL;
 
         return name;
@@ -2595,7 +2638,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
 
                 /* This is an ugly hack */
                 if (major(devnr) == 136) {
-                        asprintf(&b, "pts/%lu", (unsigned long) minor(devnr));
+                        asprintf(&b, "pts/%u", minor(devnr));
                         goto finish;
                 }
 
@@ -3315,22 +3358,49 @@ char *ellipsize(const char *s, size_t length, unsigned percent) {
         return ellipsize_mem(s, strlen(s), length, percent);
 }
 
-int touch(const char *path) {
+int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
         _cleanup_close_ int fd;
+        int r;
 
         assert(path);
 
-        /* This just opens the file for writing, ensuring it
-         * exists. It doesn't call utimensat() the way /usr/bin/touch
-         * does it. */
+        if (parents)
+                mkdir_parents(path, 0755);
 
-        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644);
+        fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode > 0 ? mode : 0644);
         if (fd < 0)
                 return -errno;
 
+        if (mode > 0) {
+                r = fchmod(fd, mode);
+                if (r < 0)
+                        return -errno;
+        }
+
+        if (uid != (uid_t) -1 || gid != (gid_t) -1) {
+                r = fchown(fd, uid, gid);
+                if (r < 0)
+                        return -errno;
+        }
+
+        if (stamp != (usec_t) -1) {
+                struct timespec ts[2];
+
+                timespec_store(&ts[0], stamp);
+                ts[1] = ts[0];
+                r = futimens(fd, ts);
+        } else
+                r = futimens(fd, NULL);
+        if (r < 0)
+                return -errno;
+
         return 0;
 }
 
+int touch(const char *path) {
+        return touch_file(path, false, (usec_t) -1, (uid_t) -1, (gid_t) -1, 0);
+}
+
 char *unquote(const char *s, const char* quotes) {
         size_t l;
         assert(s);
@@ -3937,24 +4007,16 @@ int fd_wait_for_event(int fd, int event, usec_t t) {
 int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
         FILE *f;
         char *t;
-        const char *fn;
-        size_t k;
         int fd;
 
         assert(path);
         assert(_f);
         assert(_temp_path);
 
-        t = new(char, strlen(path) + 1 + 6 + 1);
+        t = strappend(path, ".XXXXXX");
         if (!t)
                 return -ENOMEM;
 
-        fn = basename(path);
-        k = fn - path;
-        memcpy(t, path, k);
-        t[k] = '.';
-        stpcpy(stpcpy(t+k+1, fn), "XXXXXX");
-
         fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
         if (fd < 0) {
                 free(t);
@@ -4062,60 +4124,6 @@ int vt_disallocate(const char *name) {
         return 0;
 }
 
-int copy_file(const char *from, const char *to, int flags) {
-        _cleanup_close_ int fdf = -1;
-        int r, fdt;
-
-        assert(from);
-        assert(to);
-
-        fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
-        if (fdf < 0)
-                return -errno;
-
-        fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644);
-        if (fdt < 0)
-                return -errno;
-
-        for (;;) {
-                char buf[PIPE_BUF];
-                ssize_t n, k;
-
-                n = read(fdf, buf, sizeof(buf));
-                if (n < 0) {
-                        r = -errno;
-
-                        close_nointr(fdt);
-                        unlink(to);
-
-                        return r;
-                }
-
-                if (n == 0)
-                        break;
-
-                errno = 0;
-                k = loop_write(fdt, buf, n, false);
-                if (n != k) {
-                        r = k < 0 ? k : (errno ? -errno : -EIO);
-
-                        close_nointr(fdt);
-                        unlink(to);
-
-                        return r;
-                }
-        }
-
-        r = close_nointr(fdt);
-
-        if (r < 0) {
-                unlink(to);
-                return r;
-        }
-
-        return 0;
-}
-
 int symlink_atomic(const char *from, const char *to) {
         char *x;
         _cleanup_free_ char *t;
@@ -4270,7 +4278,7 @@ char* uid_to_name(uid_t uid) {
         if (p)
                 return strdup(p->pw_name);
 
-        if (asprintf(&r, "%lu", (unsigned long) uid) < 0)
+        if (asprintf(&r, UID_FMT, uid) < 0)
                 return NULL;
 
         return r;
@@ -4287,7 +4295,7 @@ char* gid_to_name(gid_t gid) {
         if (p)
                 return strdup(p->gr_name);
 
-        if (asprintf(&r, "%lu", (unsigned long) gid) < 0)
+        if (asprintf(&r, GID_FMT, gid) < 0)
                 return NULL;
 
         return r;
@@ -5432,21 +5440,27 @@ out:
 
 const char *draw_special_char(DrawSpecialChar ch) {
         static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = {
+
                 /* UTF-8 */ {
-                        [DRAW_TREE_VERT]          = "\342\224\202 ",            /* │  */
+                        [DRAW_TREE_VERTICAL]      = "\342\224\202 ",            /* │  */
                         [DRAW_TREE_BRANCH]        = "\342\224\234\342\224\200", /* ├─ */
                         [DRAW_TREE_RIGHT]         = "\342\224\224\342\224\200", /* └─ */
                         [DRAW_TREE_SPACE]         = "  ",                       /*    */
-                        [DRAW_TRIANGULAR_BULLET]  = "\342\200\243 ",            /* ‣  */
-                        [DRAW_BLACK_CIRCLE]       = "\342\227\217 ",            /* ●  */
+                        [DRAW_TRIANGULAR_BULLET]  = "\342\200\243",             /* ‣ */
+                        [DRAW_BLACK_CIRCLE]       = "\342\227\217",             /* ● */
+                        [DRAW_ARROW]              = "\342\206\222",             /* → */
+                        [DRAW_DASH]               = "\342\200\223",             /* – */
                 },
+
                 /* ASCII fallback */ {
-                        [DRAW_TREE_VERT]          = "| ",
+                        [DRAW_TREE_VERTICAL]      = "| ",
                         [DRAW_TREE_BRANCH]        = "|-",
                         [DRAW_TREE_RIGHT]         = "`-",
                         [DRAW_TREE_SPACE]         = "  ",
-                        [DRAW_TRIANGULAR_BULLET]  = "> ",
-                        [DRAW_BLACK_CIRCLE]       = "* ",
+                        [DRAW_TRIANGULAR_BULLET]  = ">",
+                        [DRAW_BLACK_CIRCLE]       = "*",
+                        [DRAW_ARROW]              = "->",
+                        [DRAW_DASH]               = "-",
                 }
         };
 
@@ -5812,8 +5826,8 @@ char *strrep(const char *s, unsigned n) {
         return r;
 }
 
-void* greedy_realloc(void **p, size_t *allocated, size_t need) {
-        size_t a;
+void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) {
+        size_t a, newalloc;
         void *q;
 
         assert(p);
@@ -5822,10 +5836,11 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need) {
         if (*allocated >= need)
                 return *p;
 
-        a = MAX(64u, need * 2);
+        newalloc = MAX(need * 2, 64u / size);
+        a = newalloc * size;
 
         /* check for overflows */
-        if (a < need)
+        if (a < size * need)
                 return NULL;
 
         q = realloc(*p, a);
@@ -5833,11 +5848,11 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need) {
                 return NULL;
 
         *p = q;
-        *allocated = a;
+        *allocated = newalloc;
         return q;
 }
 
-void* greedy_realloc0(void **p, size_t *allocated, size_t need) {
+void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size) {
         size_t prev;
         uint8_t *q;
 
@@ -5846,12 +5861,12 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need) {
 
         prev = *allocated;
 
-        q = greedy_realloc(p, allocated, need);
+        q = greedy_realloc(p, allocated, need, size);
         if (!q)
                 return NULL;
 
         if (*allocated > prev)
-                memzero(&q[prev], *allocated - prev);
+                memzero(q + prev * size, (*allocated - prev) * size);
 
         return q;
 }
@@ -6057,60 +6072,93 @@ int container_get_leader(const char *machine, pid_t *pid) {
         return 0;
 }
 
-int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *root_fd) {
-        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1;
-        const char *pidns, *mntns, *root;
-        int rfd;
+int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *root_fd) {
+        _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1;
+        int rfd = -1;
 
         assert(pid >= 0);
-        assert(pidns_fd);
-        assert(mntns_fd);
-        assert(root_fd);
 
-        mntns = procfs_file_alloca(pid, "ns/mnt");
-        mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
-        if (mntnsfd < 0)
-                return -errno;
+        if (mntns_fd) {
+                const char *mntns;
 
-        pidns = procfs_file_alloca(pid, "ns/pid");
-        pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
-        if (pidnsfd < 0)
-                return -errno;
+                mntns = procfs_file_alloca(pid, "ns/mnt");
+                mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+                if (mntnsfd < 0)
+                        return -errno;
+        }
 
-        root = procfs_file_alloca(pid, "root");
-        rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
-        if (rfd < 0)
-                return -errno;
+        if (pidns_fd) {
+                const char *pidns;
+
+                pidns = procfs_file_alloca(pid, "ns/pid");
+                pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+                if (pidnsfd < 0)
+                        return -errno;
+        }
+
+        if (netns_fd) {
+                const char *netns;
+
+                netns = procfs_file_alloca(pid, "ns/net");
+                netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+                if (netnsfd < 0)
+                        return -errno;
+        }
+
+        if (root_fd) {
+                const char *root;
+
+                root = procfs_file_alloca(pid, "root");
+                rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
+                if (rfd < 0)
+                        return -errno;
+        }
+
+        if (pidns_fd)
+                *pidns_fd = pidnsfd;
+
+        if (mntns_fd)
+                *mntns_fd = mntnsfd;
+
+        if (netns_fd)
+                *netns_fd = netnsfd;
+
+        if (root_fd)
+                *root_fd = rfd;
 
-        *pidns_fd = pidnsfd;
-        *mntns_fd = mntnsfd;
-        *root_fd = rfd;
-        pidnsfd = -1;
-        mntnsfd = -1;
+        pidnsfd = mntnsfd = netnsfd = -1;
 
         return 0;
 }
 
-int namespace_enter(int pidns_fd, int mntns_fd, int root_fd) {
-        assert(pidns_fd >= 0);
-        assert(mntns_fd >= 0);
-        assert(root_fd >= 0);
+int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
 
-        if (setns(pidns_fd, CLONE_NEWPID) < 0)
-                return -errno;
+        if (pidns_fd >= 0)
+                if (setns(pidns_fd, CLONE_NEWPID) < 0)
+                        return -errno;
 
-        if (setns(mntns_fd, CLONE_NEWNS) < 0)
-                return -errno;
+        if (mntns_fd >= 0)
+                if (setns(mntns_fd, CLONE_NEWNS) < 0)
+                        return -errno;
 
-        if (fchdir(root_fd) < 0)
-                return -errno;
+        if (netns_fd >= 0)
+                if (setns(netns_fd, CLONE_NEWNET) < 0)
+                        return -errno;
 
-        if (chroot(".") < 0)
-                return -errno;
+        if (root_fd >= 0) {
+                if (fchdir(root_fd) < 0)
+                        return -errno;
+
+                if (chroot(".") < 0)
+                        return -errno;
+        }
 
         if (setresgid(0, 0, 0) < 0)
                 return -errno;
 
+        if (setgroups(0, NULL) < 0)
+                return -errno;
+
         if (setresuid(0, 0, 0) < 0)
                 return -errno;
 
@@ -6384,3 +6432,239 @@ void hexdump(FILE *f, const void *p, size_t s) {
                 s -= 16;
         }
 }
+
+int update_reboot_param_file(const char *param) {
+        int r = 0;
+
+        if (param) {
+
+                r = write_string_file(REBOOT_PARAM_FILE, param);
+                if (r < 0)
+                        log_error("Failed to write reboot param to "
+                                  REBOOT_PARAM_FILE": %s", strerror(-r));
+        } else
+                unlink(REBOOT_PARAM_FILE);
+
+        return r;
+}
+
+int umount_recursive(const char *prefix, int flags) {
+        bool again;
+        int n = 0, r;
+
+        /* Try to umount everything recursively below a
+         * directory. Also, take care of stacked mounts, and keep
+         * unmounting them until they are gone. */
+
+        do {
+                _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+
+                again = false;
+                r = 0;
+
+                proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+                if (!proc_self_mountinfo)
+                        return -errno;
+
+                for (;;) {
+                        _cleanup_free_ char *path = NULL, *p = NULL;
+                        int k;
+
+                        k = fscanf(proc_self_mountinfo,
+                                   "%*s "       /* (1) mount id */
+                                   "%*s "       /* (2) parent id */
+                                   "%*s "       /* (3) major:minor */
+                                   "%*s "       /* (4) root */
+                                   "%ms "       /* (5) mount point */
+                                   "%*s"        /* (6) mount options */
+                                   "%*[^-]"     /* (7) optional fields */
+                                   "- "         /* (8) separator */
+                                   "%*s "       /* (9) file system type */
+                                   "%*s"        /* (10) mount source */
+                                   "%*s"        /* (11) mount options 2 */
+                                   "%*[^\n]",   /* some rubbish at the end */
+                                   &path);
+                        if (k != 1) {
+                                if (k == EOF)
+                                        break;
+
+                                continue;
+                        }
+
+                        p = cunescape(path);
+                        if (!p)
+                                return -ENOMEM;
+
+                        if (!path_startswith(p, prefix))
+                                continue;
+
+                        if (umount2(p, flags) < 0) {
+                                r = -errno;
+                                continue;
+                        }
+
+                        again = true;
+                        n++;
+
+                        break;
+                }
+
+        } while (again);
+
+        return r ? r : n;
+}
+
+int bind_remount_recursive(const char *prefix, bool ro) {
+        _cleanup_set_free_free_ Set *done = NULL;
+        _cleanup_free_ char *cleaned = NULL;
+        int r;
+
+        /* Recursively remount a directory (and all its submounts)
+         * read-only or read-write. If the directory is already
+         * mounted, we reuse the mount and simply mark it
+         * MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
+         * operation). If it isn't we first make it one. Afterwards we
+         * apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to all
+         * submounts we can access, too. When mounts are stacked on
+         * the same mount point we only care for each individual
+         * "top-level" mount on each point, as we cannot
+         * influence/access the underlying mounts anyway. We do not
+         * have any effect on future submounts that might get
+         * propagated, they migt be writable. This includes future
+         * submounts that have been triggered via autofs. */
+
+        cleaned = strdup(prefix);
+        if (!cleaned)
+                return -ENOMEM;
+
+        path_kill_slashes(cleaned);
+
+        done = set_new(string_hash_func, string_compare_func);
+        if (!done)
+                return -ENOMEM;
+
+        for (;;) {
+                _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
+                _cleanup_set_free_free_ Set *todo = NULL;
+                bool top_autofs = false;
+                char *x;
+
+                todo = set_new(string_hash_func, string_compare_func);
+                if (!todo)
+                        return -ENOMEM;
+
+                proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
+                if (!proc_self_mountinfo)
+                        return -errno;
+
+                for (;;) {
+                        _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
+                        int k;
+
+                        k = fscanf(proc_self_mountinfo,
+                                   "%*s "       /* (1) mount id */
+                                   "%*s "       /* (2) parent id */
+                                   "%*s "       /* (3) major:minor */
+                                   "%*s "       /* (4) root */
+                                   "%ms "       /* (5) mount point */
+                                   "%*s"        /* (6) mount options (superblock) */
+                                   "%*[^-]"     /* (7) optional fields */
+                                   "- "         /* (8) separator */
+                                   "%ms "       /* (9) file system type */
+                                   "%*s"        /* (10) mount source */
+                                   "%*s"        /* (11) mount options (bind mount) */
+                                   "%*[^\n]",   /* some rubbish at the end */
+                                   &path,
+                                   &type);
+                        if (k != 2) {
+                                if (k == EOF)
+                                        break;
+
+                                continue;
+                        }
+
+                        p = cunescape(path);
+                        if (!p)
+                                return -ENOMEM;
+
+                        /* Let's ignore autofs mounts.  If they aren't
+                         * triggered yet, we want to avoid triggering
+                         * them, as we don't make any guarantees for
+                         * future submounts anyway.  If they are
+                         * already triggered, then we will find
+                         * another entry for this. */
+                        if (streq(type, "autofs")) {
+                                top_autofs = top_autofs || path_equal(cleaned, p);
+                                continue;
+                        }
+
+                        if (path_startswith(p, cleaned) &&
+                            !set_contains(done, p)) {
+
+                                r = set_consume(todo, p);
+                                p = NULL;
+
+                                if (r == -EEXIST)
+                                        continue;
+                                if (r < 0)
+                                        return r;
+                        }
+                }
+
+                /* If we have no submounts to process anymore and if
+                 * the root is either already done, or an autofs, we
+                 * are done */
+                if (set_isempty(todo) &&
+                    (top_autofs || set_contains(done, cleaned)))
+                        return 0;
+
+                if (!set_contains(done, cleaned) &&
+                    !set_contains(todo, cleaned)) {
+                        /* The prefix directory itself is not yet a
+                         * mount, make it one. */
+                        if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
+                                return -errno;
+
+                        if (mount(NULL, prefix, NULL, MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
+                                return -errno;
+
+                        x = strdup(cleaned);
+                        if (!x)
+                                return -ENOMEM;
+
+                        r = set_consume(done, x);
+                        if (r < 0)
+                                return r;
+                }
+
+                while ((x = set_steal_first(todo))) {
+
+                        r = set_consume(done, x);
+                        if (r == -EEXIST)
+                                continue;
+                        if (r < 0)
+                                return r;
+
+                        if (mount(NULL, x, NULL, MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) {
+
+                                /* Deal with mount points that are
+                                 * obstructed by a later mount */
+
+                                if (errno != ENOENT)
+                                        return -errno;
+                        }
+
+                }
+        }
+}
+
+int fflush_and_check(FILE *f) {
+
+        errno = 0;
+        fflush(f);
+
+        if (ferror(f))
+                return errno ? -errno : -EIO;
+
+        return 0;
+}