]> git.ipfire.org Git - people/ms/pakfire.git/blobdiff - src/libpakfire/mount.c
mount: Use the parse helpers to log /proc/self/mounts
[people/ms/pakfire.git] / src / libpakfire / mount.c
index a1ee7b875dec57ffb38d32e15c7a96af796158a8..1f248468bc21732ea0a9086cdbd543fc1a1a884c 100644 (file)
 #include <stddef.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
+#include <sys/sysmacros.h>
 #include <sys/types.h>
 
 // libmount
 #include <libmount/libmount.h>
 
+#include <pakfire/arch.h>
 #include <pakfire/logging.h>
 #include <pakfire/pakfire.h>
+#include <pakfire/parse.h>
+#include <pakfire/path.h>
 #include <pakfire/mount.h>
+#include <pakfire/string.h>
 #include <pakfire/util.h>
 
 static const struct pakfire_mountpoint {
@@ -39,52 +44,159 @@ static const struct pakfire_mountpoint {
        const char* fstype;
        int flags;
        const char* options;
-       enum pakfire_mount_flags mount_flags;
 } mountpoints[] = {
-       // The root filesystem is a tmpfs
-       { "pakfire_root",  "",             "tmpfs", 0, NULL, 0 },
+       // Mount a new instance of /proc
+       { "pakfire_proc",        "proc",               "proc",
+               MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, },
+
+       // Make /proc/sys read-only (except /proc/sys/net)
+       { "/proc/sys",           "proc/sys",           "bind",   MS_BIND|MS_REC, NULL, },
+       { "/proc/sys/net",       "proc/sys/net",       "bind",   MS_BIND|MS_REC, NULL, },
+       { "/proc/sys",           "proc/sys",           "bind",
+               MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
+
+       // Deny write access to /proc/sysrq-trigger (can be used to restart the host)
+       { "/proc/sysrq-trigger", "proc/sysrq-trigger", "bind",   MS_BIND|MS_REC, NULL, },
+       { "/proc/sysrq-trigger", "proc/sysrq-trigger", "bind",
+               MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
+
+       // Make /proc/irq read-only
+       { "/proc/irq",           "proc/irq",           "bind",   MS_BIND|MS_REC, NULL, },
+       { "/proc/irq",           "proc/irq",           "bind",
+               MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
+
+       // Make /proc/bus read-only
+       { "/proc/bus",           "proc/bus",           "bind",   MS_BIND|MS_REC, NULL, },
+       { "/proc/bus",           "proc/bus",           "bind",
+               MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
+
+       // Bind-Mount /sys ready-only
+       { "/sys",                "sys",                "bind",   MS_BIND|MS_REC, NULL, },
+       { "/sys",                "sys",                "bind",
+               MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, NULL, },
 
-       { "pakfire_proc",  "proc",         "proc",  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL,
-               MOUNT_IN_NEW_NS },
-
-       // Bind mount /proc/sys as read-only with the following exceptions:
-       //  * /proc/sys/net
-       { "/proc/sys",     "proc/sys",     "bind",  MS_BIND, NULL,
-               MOUNT_IN_NEW_NS },
-       { "/proc/sys/net", "proc/sys/net", "bind",  MS_BIND, NULL,
-               MOUNT_IN_NEW_NS },
-       { "/proc/sys",     "proc/sys",     "bind",  MS_BIND|MS_RDONLY|MS_REMOUNT, NULL,
-               MOUNT_IN_NEW_NS },
+       // Create a new /dev
+       { "pakfire_dev",         "dev",                "tmpfs",  MS_NOSUID|MS_NOEXEC,
+               "mode=0755,size=4m,nr_inodes=64k", },
+       { "pakfire_dev_pts",     "dev/pts",            "devpts", MS_NOSUID|MS_NOEXEC,
+               "newinstance,ptmxmode=0666,mode=620", },
 
-       // Bind mount /sys as read-only
-       { "/sys",          "sys",          "bind",  MS_BIND, NULL, 0 },
-       { "/sys",          "sys",          "bind",  MS_BIND|MS_RDONLY|MS_REMOUNT, NULL, 0 },
+       // Create a new /dev/shm
+       { "pakfire_dev_shm",     "dev/shm",            "tmpfs",
+               MS_NOSUID|MS_NODEV|MS_STRICTATIME, "mode=1777,size=1024m", },
 
-       // Create a new /dev
-       { "pakfire_dev",   "dev",          "tmpfs", MS_NOSUID|MS_NOEXEC,
-               "mode=755,size=4m,nr_inodes=64k", 0 },
-       { "/dev/pts",      "dev/pts",      "bind",  MS_BIND, NULL, 0 },
+       // Mount /dev/mqueue
+       { "mqueue",               "dev/mqueue",        "mqueue",
+               MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL },
 
        // Create a new /run
-       { "pakfire_tmpfs", "run",          "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV,
-               "mode=755,size=4m,nr_inodes=1k", 0 },
+       { "pakfire_run",          "run",               "tmpfs",  MS_NOSUID|MS_NOEXEC|MS_NODEV,
+               "mode=755,size=256m,nr_inodes=1k", },
 
        // Create a new /tmp
-       { "pakfire_tmpfs", "tmp",          "tmpfs", MS_NOSUID|MS_NODEV,
-               "mode=755", 0 },
+       { "pakfire_tmp",          "tmp",               "tmpfs",
+               MS_NOSUID|MS_NODEV|MS_STRICTATIME, "mode=1777,size=4096m", },
 
        // The end
        { NULL },
 };
 
+static const struct pakfire_devnode {
+       const char* path;
+       int major;
+       int minor;
+       mode_t mode;
+       int flags;
+} devnodes[] = {
+       { "/dev/null",      1,  3, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
+       { "/dev/zero",      1,  5, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
+       { "/dev/full",      1,  7, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
+       { "/dev/random",    1,  8, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
+       { "/dev/urandom",   1,  9, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
+       { "/dev/kmsg",      1, 11, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, 0 },
+       { "/dev/tty",       5,  0, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, 0 },
+       { "/dev/console",   5,  1, S_IFCHR|S_IRUSR|S_IWUSR, 0 },
+       { "/dev/rtc0",    252,  0, S_IFCHR|S_IRUSR|S_IWUSR, 0 },
+
+       // Loop Devices
+       { "/dev/loop-control", 10, 237, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+       { "/dev/loop0",         7,   0, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+       { "/dev/loop1",         7,   1, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+       { "/dev/loop2",         7,   2, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+       { "/dev/loop3",         7,   3, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+       { "/dev/loop4",         7,   4, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+       { "/dev/loop5",         7,   5, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+       { "/dev/loop6",         7,   6, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+       { "/dev/loop7",         7,   7, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, PAKFIRE_MOUNT_LOOP_DEVICES },
+
+       { NULL },
+};
+
+static const struct pakfire_symlink {
+       const char* target;
+       const char* path;
+} symlinks[] = {
+       { "/dev/pts/ptmx",   "/dev/ptmx", },
+       { "/proc/self/fd",   "/dev/fd", },
+       { "/proc/self/fd/0", "/dev/stdin" },
+       { "/proc/self/fd/1", "/dev/stdout" },
+       { "/proc/self/fd/2", "/dev/stderr" },
+       { "/proc/kcore",     "/dev/core" },
+       { NULL },
+};
+
+int pakfire_mount_change_propagation(struct pakfire_ctx* ctx, const char* path, int propagation) {
+       CTX_DEBUG(ctx, "Changing mount propagation on %s\n", path);
+
+       int r = mount(NULL, path, NULL, propagation|MS_REC, NULL);
+       if (r)
+               CTX_ERROR(ctx, "Failed to change mount propagation on %s: %m\n", path);
+
+       return r;
+}
+
+static int pakfire_mount_is_mountpoint(struct pakfire* pakfire, const char* path) {
+       // XXX THIS STILL NEEDS TO BE IMPLEMENTED
+       return 1;
+}
+
+int pakfire_mount_make_mounpoint(struct pakfire* pakfire, const char* path) {
+       int r;
+
+       // Check if path already is a mountpoint
+       r = pakfire_mount_is_mountpoint(pakfire, path);
+       switch (r) {
+               // Already is a mountpoint
+               case 0:
+                       return 0;
+
+               // Is not a mountpoint
+               case 1:
+                       break;
+
+               default:
+                       ERROR(pakfire, "Could not determine whether %s is a mountpoint: %m\n", path);
+                       return r;
+       }
+
+       // Bind-mount to self
+       r = mount(path, path, NULL, MS_BIND|MS_REC, NULL);
+       if (r) {
+               ERROR(pakfire, "Could not make %s a mountpoint: %m\n", path);
+               return r;
+       }
+
+       return 0;
+}
+
 /*
        Easy way to iterate through all mountpoints
 */
 static int pakfire_mount_foreach(struct pakfire* pakfire, int direction,
                int (*callback)(struct pakfire* pakfire, struct libmnt_fs* fs, const void* data),
-               const char* option, const void* data) {
+               const void* data) {
        const char* root = pakfire_get_path(pakfire);
-       int r = 1;
+       int r = 0;
 
        struct libmnt_iter* iterator = NULL;
        struct libmnt_table* tab = NULL;
@@ -111,12 +223,6 @@ static int pakfire_mount_foreach(struct pakfire* pakfire, int direction,
                if (!pakfire_string_startswith(target, root))
                        continue;
 
-               // Filter by option
-               if (option) {
-                       if (!mnt_fs_match_options(fs, option))
-                               continue;
-               }
-
                // Call the callback for each relevant mountpoint
                r = callback(pakfire, fs, data);
                if (r)
@@ -135,24 +241,9 @@ ERROR:
        return r;
 }
 
-static int __pakfire_is_mountpoint(struct pakfire* pakfire,
-               struct libmnt_fs* fs, const void* data) {
-       const char* path = (const char*)data;
-
-       return mnt_fs_streq_target(fs, path);
-}
-
-int pakfire_is_mountpoint(struct pakfire* pakfire, const char* path) {
-       return pakfire_mount_foreach(pakfire, MNT_ITER_FORWARD,
-               __pakfire_is_mountpoint, NULL, path);
-}
-
-int pakfire_mount(struct pakfire* pakfire, const char* source, const char* target,
+static int pakfire_mount(struct pakfire* pakfire, const char* source, const char* target,
                const char* fstype, unsigned long mflags, const void* data) {
        const char* options = (const char*)data;
-       int r;
-
-       char error[1024];
 
        // Check for some basic inputs
        if (!source || !target) {
@@ -160,200 +251,267 @@ int pakfire_mount(struct pakfire* pakfire, const char* source, const char* targe
                return 1;
        }
 
-       DEBUG(pakfire, "Mounting %s from %s (%s - %s)\n",
-               target, source, fstype, options);
-
-#ifdef PAKFIRE_DEBUG
-       // Enable debugging for libmount
-       mnt_init_debug(0xffff);
-#endif
-
-       // Allocate a new mount context
-       struct libmnt_context* ctx = mnt_new_context();
-       if (!ctx)
-               return 1;
+       DEBUG(pakfire, "Mounting %s from %s (%s - %s)\n", target, source, fstype, options);
 
-       // Set source
-       r = mnt_context_set_source(ctx, source);
+       // Perform mount()
+       int r = mount(source, target, fstype, mflags, data);
        if (r) {
-               ERROR(pakfire, "Could not set source: %m\n");
-               goto ERROR;
+               ERROR(pakfire, "Could not mount %s: %m\n", target);
        }
 
-       // Set target
-       r = mnt_context_set_target(ctx, target);
-       if (r) {
-               ERROR(pakfire, "Could not set target: %m\n");
-               goto ERROR;
-       }
+       return r;
+}
 
-       // Set filesystem type
-       if (fstype) {
-               r = mnt_context_set_fstype(ctx, fstype);
-               if (r) {
-                       ERROR(pakfire, "Could not set filesystem type: %m\n");
-                       goto ERROR;
-               }
-       }
+static int __pakfire_mount_print(struct pakfire* pakfire,
+               struct libmnt_fs* fs, const void* data) {
+       DEBUG(pakfire,
+               "  %s %s %s %s\n",
+               mnt_fs_get_source(fs),
+               mnt_fs_get_target(fs),
+               mnt_fs_get_fstype(fs),
+               mnt_fs_get_fs_options(fs)
+       );
 
-       // Set mount flags
-       if (mflags) {
-               r = mnt_context_set_mflags(ctx, mflags);
-               if (r) {
-                       ERROR(pakfire, "Could not set mount flags: %m\n");
-                       goto ERROR;
-               }
-       }
+       return 0;
+}
 
-       // Set options
-       if (options) {
-               r = mnt_context_set_options(ctx, options);
-               if (r) {
-                       ERROR(pakfire, "Could not set mount options: %m\n");
-                       goto ERROR;
-               }
-       }
+static int __pakfire_mount_list(char* line, size_t length, void* data) {
+       struct pakfire_ctx* ctx = data;
 
-       // Perform mount operation
-       r = mnt_context_mount(ctx);
-       if (r) {
-               // Fetch the error message
-               mnt_context_get_excode(ctx, r, error, sizeof(error));
+       // Send the line to the logger
+       CTX_DEBUG(ctx, "  %.*s", (int)length, line);
 
-               ERROR(pakfire, "Mount unsuccessful: %s\n", error);
-               goto ERROR;
-       }
+       return 0;
+}
 
-ERROR:
-       if (ctx)
-               mnt_free_context(ctx);
+int pakfire_mount_list(struct pakfire_ctx* ctx) {
+       CTX_DEBUG(ctx, "Mountpoints:\n");
 
-       return r;
+       return pakfire_parse_file("/proc/self/mounts", __pakfire_mount_list, ctx);
 }
 
-static int pakfire_umount(struct pakfire* pakfire, const char* path, int flags) {
-       int r;
+static int pakfire_populate_dev(struct pakfire* pakfire, int flags) {
+       char path[PATH_MAX];
 
-       DEBUG(pakfire, "Umounting %s\n", path);
+       // Create device nodes
+       for (const struct pakfire_devnode* devnode = devnodes; devnode->path; devnode++) {
+               DEBUG(pakfire, "Creating device node %s\n", devnode->path);
 
-RETRY:
-       // Perform umount
-       r = umount2(path, flags);
-       if (r) {
-               // Attempt a lazy umount when busy
-               if (errno == EBUSY) {
-                       flags |= MNT_DETACH;
-                       goto RETRY;
+               // Check if flags match
+               if (devnode->flags && !(flags & devnode->flags))
+                       continue;
+
+               int r = pakfire_path(pakfire, path, "%s", devnode->path);
+               if (r)
+                       return r;
+
+               dev_t dev = makedev(devnode->major, devnode->minor);
+
+               r = mknod(path, devnode->mode, dev);
+
+               // Continue if mknod was successful
+               if (r == 0)
+                       continue;
+
+               // If we could not create the device node because of permission issues,
+               // it might be likely that we are running in a user namespace where creating
+               // device nodes is not permitted. Try bind-mounting them.
+               if (errno == EPERM)
+                       goto MOUNT;
+
+               // Otherwise log an error and end
+               ERROR(pakfire, "Could not create %s: %m\n", devnode->path);
+               return r;
+
+MOUNT:
+               // Create an empty file
+               r = pakfire_touch(path, 0444);
+               if (r) {
+                       ERROR(pakfire, "Could not create %s: %m\n", path);
+                       return r;
                }
 
-               ERROR(pakfire, "Could not umount %s: %m\n", path);
+               // Create a bind-mount over the file
+               r = pakfire_mount(pakfire, devnode->path, path, "bind", MS_BIND, NULL);
+               if (r)
+                       return r;
        }
 
-       return r;
-}
+       // Create symlinks
+       for (const struct pakfire_symlink* s = symlinks; s->target; s++) {
+               DEBUG(pakfire, "Creating symlink %s -> %s\n", s->path, s->target);
 
-static int __pakfire_mount_print(struct pakfire* pakfire,
-               struct libmnt_fs* fs, const void* data) {
-       DEBUG(pakfire,
-               "  %s %s %s %s\n",
-               mnt_fs_get_source(fs),
-               mnt_fs_get_target(fs),
-               mnt_fs_get_fstype(fs),
-               mnt_fs_get_fs_options(fs)
-       );
+               int r = pakfire_path(pakfire, path, "%s", s->path);
+               if (r)
+                       return r;
+
+               r = symlink(s->target, path);
+               if (r) {
+                       ERROR(pakfire, "Could not create symlink %s: %m\n", s->path);
+                       return r;
+               }
+       }
 
        return 0;
 }
 
-static int pakfire_mount_list(struct pakfire* pakfire) {
-       DEBUG(pakfire, "Current mountpoints:\n");
+static int pakfire_mount_interpreter(struct pakfire* pakfire) {
+       char target[PATH_MAX];
+
+       // Fetch the target architecture
+       const char* arch = pakfire_get_effective_arch(pakfire);
 
-       return pakfire_mount_foreach(pakfire, MNT_ITER_FORWARD,
-               __pakfire_mount_print, NULL, NULL);
+       // Can we emulate this architecture?
+       char* interpreter = pakfire_arch_find_interpreter(arch);
+
+       // No interpreter required
+       if (!interpreter)
+               return 0;
+
+       DEBUG(pakfire, "Mounting interpreter %s for %s\n", interpreter, arch);
+
+       // Where to mount this?
+       int r = pakfire_path(pakfire, target, "%s", interpreter);
+       if (r)
+               return r;
+
+       // Create directory
+       r = pakfire_mkparentdir(target, 0755);
+       if (r)
+               return r;
+
+       // Create an empty file
+       FILE* f = fopen(target, "w");
+       if (!f)
+               return 1;
+       fclose(f);
+
+       r = pakfire_mount(pakfire, interpreter, target, NULL, MS_BIND|MS_RDONLY, NULL);
+       if (r)
+               ERROR(pakfire, "Could not mount interpreter %s to %s: %m\n", interpreter, target);
+
+       return r;
 }
 
-int pakfire_mount_all(struct pakfire* pakfire, const enum pakfire_mount_flags mount_flags) {
+int pakfire_mount_all(struct pakfire* pakfire, int flags) {
        char target[PATH_MAX];
-       char options[PATH_MAX];
        int r;
 
        // Fetch Pakfire's root directory
        const char* root = pakfire_get_path(pakfire);
 
-       // Are we being called from a new namespace?
-       const unsigned int in_new_ns = (mount_flags & MOUNT_IN_NEW_NS);
-
        for (const struct pakfire_mountpoint* mp = mountpoints; mp->source; mp++) {
-               if (!in_new_ns && (mp->mount_flags & MOUNT_IN_NEW_NS))
-                       continue;
-
-               if (in_new_ns && !(mp->mount_flags & MOUNT_IN_NEW_NS))
-                       continue;
-
                // Figure out where to mount
-               r = pakfire_path_join(target, root, mp->target);
-               if (r < 0)
+               r = pakfire_path_append(target, root, mp->target);
+               if (r)
                        return r;
 
-               // Append namespace mount option
-               if (in_new_ns) {
-                       if (mp->options)
-                               pakfire_string_format(options, "%s,%s",
-                                       mp->options, PAKFIRE_MOUNT_OPT_NAMESPACED);
-                       else
-                               pakfire_string_set(options, PAKFIRE_MOUNT_OPT_NAMESPACED);
-               } else {
-                       if (mp->options)
-                               pakfire_string_set(options, mp->options);
-                       else
-                               pakfire_string_set(options, "");
+               // Create target if it doesn't exist
+               if (!pakfire_path_exists(target)) {
+                       r = pakfire_mkdir(target, 0755);
+                       if (r) {
+                               ERROR(pakfire, "Could not create %s: %m\n", target);
+                               return r;
+                       }
                }
 
-               // Create target
-               pakfire_mkdir(target, 0);
-
-RETRY:
                // Perform mount()
-               r = pakfire_mount(pakfire, mp->source, target, mp->fstype, mp->flags, options);
-               if (r) {
-                       // If the target directory does not exist, we will create it
-                       if (errno == ENOENT) {
-                               r = pakfire_mkdir(target, S_IRUSR|S_IWUSR|S_IXUSR);
-                               if (r) {
-                                       ERROR(pakfire, "Could not create %s\n", target);
-                                       return r;
-                               }
-
-                               goto RETRY;
-                       }
-
-                       ERROR(pakfire, "Could not mount /%s: %m\n", mp->target);
+               r = pakfire_mount(pakfire, mp->source, target, mp->fstype, mp->flags, mp->options);
+               if (r)
                        return r;
-               }
        }
 
-       // List everything that is mounted
-       pakfire_mount_list(pakfire);
+       // Populate /dev
+       r = pakfire_populate_dev(pakfire, flags);
+       if (r)
+               return r;
+
+       // Mount the interpreter (if needed)
+       r = pakfire_mount_interpreter(pakfire);
+       if (r)
+               return r;
 
        return 0;
 }
 
-static int __pakfire_umount(struct pakfire* pakfire,
-               struct libmnt_fs* fs, const void* data) {
-       const char* target = mnt_fs_get_target(fs);
-       if (!target)
-               return 1;
+int pakfire_make_ramdisk(struct pakfire* pakfire, char* path, const char* args) {
+       int r;
+
+       // Create a new temporary directory
+       char* p = pakfire_mkdtemp(path);
+       if (!p)
+               return -errno;
+
+       // Mount the ramdisk
+       r = pakfire_mount(pakfire, "pakfire_ramdisk", p, "tmpfs", 0, args);
+       if (r) {
+               ERROR_ERRNO(pakfire, r, "Could not mount ramdisk at %s (%s): %m\n", p, args);
+               return r;
+       }
+
+       DEBUG(pakfire, "Ramdisk mounted at %s (%s)\n", p, args);
 
-       return pakfire_umount(pakfire, target, 0);
+       return 0;
 }
 
-/*
-       umounts everything that hasn't been umounted, yet
-*/
-int pakfire_umount_all(struct pakfire* pakfire, const char* option) {
-       // List everything that is mounted
-       pakfire_mount_list(pakfire);
+int pakfire_bind(struct pakfire* pakfire, const char* src, const char* dst, int flags) {
+       struct stat st;
+       char mountpoint[PATH_MAX];
+
+       if (!dst)
+               dst = src;
+
+       int r = pakfire_path(pakfire, mountpoint, "%s", dst);
+       if (r)
+               return r;
+
+       DEBUG(pakfire, "Bind-mounting %s to %s\n", src, mountpoint);
+
+       r = stat(src, &st);
+       if (r < 0) {
+               ERROR(pakfire, "Could not stat %s: %m\n", src);
+               return 1;
+       }
+
+       // Make sure the mountpoint exists
+       switch (st.st_mode & S_IFMT) {
+               case S_IFDIR:
+                       r = pakfire_mkdir(mountpoint, st.st_mode);
+                       if (r && errno != EEXIST)
+                               return r;
+                       break;
+
+               case S_IFREG:
+               case S_IFLNK:
+                       // Make parent directory
+                       r = pakfire_mkparentdir(mountpoint, 0755);
+                       if (r)
+                               return r;
+
+                       // Create a file
+                       FILE* f = fopen(mountpoint, "w");
+                       if (!f)
+                               return 1;
+                       fclose(f);
+                       break;
+
+               default:
+                       errno = ENOTSUP;
+                       return 1;
+       }
+
+       // The Linux kernel seems to be quite funny when trying to bind-mount something
+       // as read-only and requires us to mount the source first, and then remount it
+       // again using MS_RDONLY.
+       if (flags & MS_RDONLY) {
+               r = pakfire_mount(pakfire, src, mountpoint, "bind", MS_BIND|MS_REC, NULL);
+               if (r)
+                       return r;
+
+               // Add the remount flag
+               flags |= MS_REMOUNT;
+       }
 
-       return pakfire_mount_foreach(pakfire, MNT_ITER_BACKWARD,
-               __pakfire_umount, option, NULL);
+       // Perform mount
+       return pakfire_mount(pakfire, src, mountpoint, "bind", flags|MS_BIND|MS_REC, NULL);
 }