]> git.ipfire.org Git - pakfire.git/commitdiff
libpakfire: Automatically mount virtual environment
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 21 Mar 2021 14:27:32 +0000 (14:27 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 21 Mar 2021 14:27:32 +0000 (14:27 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/pakfire.c

index 98edd75f832b605d0bb7ef7ac996f52488b73d35..074acc960374898166a1c7e13a3f9abb7e4db323 100644 (file)
@@ -25,6 +25,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/mount.h>
+#include <sys/queue.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
 #include <pakfire/types.h>
 #include <pakfire/util.h>
 
+struct mountpoint {
+       STAILQ_ENTRY(mountpoint) nodes;
+       char path[PATH_MAX];
+};
+
 struct _Pakfire {
        char path[PATH_MAX];
        char cache_path[PATH_MAX];
@@ -68,9 +74,205 @@ struct _Pakfire {
        int activated;
        int nrefs;
 
+       STAILQ_HEAD(mountpoints, mountpoint) mountpoints;
        int destroy_on_free;
 };
 
+static const struct pakfire_mountpoint {
+       const char* source;
+       const char* target;
+       const char* fstype;
+       int flags;
+       const char* options;
+} mountpoints[] = {
+       { "pakfire_proc",  "proc",         "proc",  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL },
+
+       // Bind mount /proc/sys as read-only with the following exceptions:
+       //  * /proc/sys/net
+       { "/proc/sys",     "proc/sys",     NULL,    MS_BIND, NULL },
+       { "/proc/sys/net", "proc/sys/net", NULL,    MS_BIND, NULL },
+       { "/proc/sys",     "proc/sys",     NULL,    MS_BIND|MS_RDONLY|MS_REMOUNT, NULL },
+
+       // Bind mount /sys as read-only
+       { "/sys",          "sys",          NULL,    MS_BIND, NULL },
+       { "/sys",          "sys",          NULL,    MS_BIND|MS_RDONLY|MS_REMOUNT, NULL },
+
+       // Create a new /dev
+       { "pakfire_dev",   "dev",          "tmpfs", MS_NOSUID|MS_NOEXEC,
+               "mode=755,size=4m,nr_inodes=64k" },
+       { "/dev/pts",      "dev/pts",      NULL,    MS_BIND, NULL },
+
+       // Create a new /run
+       { "pakfire_tmpfs", "run",          "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+               "mode=755,size=4m,nr_inodes=1k" },
+
+       // Create a new /tmp
+       { "pakfire_tmpfs", "tmp",          "tmpfs", MS_NOSUID|MS_NODEV,
+               "mode=755" },
+
+       // The end
+       { NULL },
+};
+
+static int __mount(Pakfire pakfire, const char* source, const char* target,
+               const char* filesystemtype, unsigned long mountflags, const void* data) {
+       int r = mount(source, target, filesystemtype, mountflags, data);
+       if (r)
+               return r;
+
+       // Store mountpoint so that we can umount it later
+       struct mountpoint* mp = calloc(1, sizeof(*mp));
+       if (!mp)
+               return 1;
+
+       snprintf(mp->path, sizeof(mp->path) - 1, "%s", target);
+       STAILQ_INSERT_HEAD(&pakfire->mountpoints, mp, nodes);
+
+       return 0;
+}
+
+static int pakfire_mount(Pakfire pakfire) {
+       int r;
+
+       for (const struct pakfire_mountpoint* mp = mountpoints; mp->source; mp++) {
+               DEBUG(pakfire, "Mounting /%s\n", mp->target);
+
+               char* target = pakfire_path_join(pakfire->path, mp->target);
+
+RETRY:
+               // Perform mount()
+               r = __mount(pakfire, mp->source, target, mp->fstype, mp->flags, mp->options);
+               if (r) {
+                       // If the target directory does not exist, we will create it
+                       if (errno == ENOENT) {
+                               r = pakfire_mkdir(pakfire, target, S_IRUSR|S_IWUSR|S_IXUSR);
+                               if (r) {
+                                       ERROR(pakfire, "Could not create %s\n", target);
+                                       free(target);
+                                       return r;
+                               }
+
+                               goto RETRY;
+                       }
+
+                       ERROR(pakfire, "Could not mount /%s: %s\n", mp->target, strerror(errno));
+                       free(target);
+                       return r;
+               }
+
+               free(target);
+       }
+
+       return 0;
+}
+
+static int pakfire_umount(Pakfire pakfire) {
+       struct mountpoint* mp;
+       int r;
+       int flags = 0;
+
+       while (!STAILQ_EMPTY(&pakfire->mountpoints)) {
+               // Take the first element from the list
+               mp = STAILQ_FIRST(&pakfire->mountpoints);
+
+               // Remove first element
+               STAILQ_REMOVE_HEAD(&pakfire->mountpoints, nodes);
+
+               DEBUG(pakfire, "Umounting %s\n", mp->path);
+
+               // Reset flags
+               flags = 0;
+
+               // Perform umount
+RETRY:
+               r = umount2(mp->path, flags);
+
+               if (r) {
+                       // Attempt a lazy umount when busy
+                       if (errno == EBUSY) {
+                               flags |= MNT_DETACH;
+                               goto RETRY;
+                       }
+
+                       ERROR(pakfire, "Could not umount %s: %s\n", mp->path, strerror(errno));
+               }
+
+               free(mp);
+       }
+
+       return 0;
+}
+
+static const struct pakfire_devnode {
+       const char* path;
+       int major;
+       int minor;
+       mode_t mode;
+} devnodes[] = {
+       { "/dev/null",      1,  3, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
+       { "/dev/zero",      1,  5, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
+       { "/dev/full",      1,  7, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
+       { "/dev/random",    1,  8, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, },
+       { "/dev/urandom",   1,  9, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, },
+       { "/dev/kmsg",      1, 11, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, },
+       { "/dev/tty",       5,  0, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
+       { "/dev/console",   5,  1, S_IFCHR|S_IRUSR|S_IWUSR, },
+       { "/dev/ptmx",      5,  2, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
+       { "/dev/rtc0",    252,  0, S_IFCHR|S_IRUSR|S_IWUSR, },
+       { NULL },
+};
+
+static const struct pakfire_symlink {
+       const char* target;
+       const char* path;
+} symlinks[] = {
+       { "/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 },
+};
+
+static int pakfire_populate_dev(Pakfire pakfire) {
+       // Create device nodes
+       for (const struct pakfire_devnode* devnode = devnodes; devnode->path; devnode++) {
+               DEBUG(pakfire, "Creating device node %s\n", devnode->path);
+
+               char* path = pakfire_make_path(pakfire, devnode->path);
+               dev_t dev = makedev(devnode->major, devnode->minor);
+
+               int r = mknod(path, devnode->mode, dev);
+               if (r) {
+                       ERROR(pakfire, "Could not create %s: %s\n", devnode->path, strerror(errno));
+                       free(path);
+
+                       return r;
+               }
+
+               free(path);
+       }
+
+       // Create symlinks
+       for (const struct pakfire_symlink* s = symlinks; s->target; s++) {
+               DEBUG(pakfire, "Creating symlink %s -> %s\n", s->path, s->target);
+
+               char* path = pakfire_make_path(pakfire, s->path);
+
+               int r = symlink(s->target, path);
+               if (r) {
+                       ERROR(pakfire, "Could not create symlink %s: %s\n", s->path, strerror(errno));
+                       free(path);
+
+                       return r;
+               }
+
+               free(path);
+       }
+
+       return 0;
+}
+
 static int log_priority(const char* priority) {
        char* end;
 
@@ -138,12 +340,12 @@ ERROR:
 static void pakfire_free(Pakfire pakfire) {
        DEBUG(pakfire, "Releasing Pakfire at %p\n", pakfire);
 
+       // umount everything
+       pakfire_umount(pakfire);
+
        if (pakfire->destroy_on_free && *pakfire->path) {
                DEBUG(pakfire, "Destroying %s\n", pakfire->path);
 
-               // Umount the temporary file system
-               umount(pakfire->path);
-
                // Destroy the temporary directory
                pakfire_rmtree(pakfire->path, 0);
        }
@@ -220,7 +422,7 @@ PAKFIRE_EXPORT int pakfire_create(Pakfire* pakfire, const char* path, const char
                p->destroy_on_free = 1;
 
                // Mount tmpfs
-               r = mount("pakfire_root", path, "tmpfs", 0, NULL);
+               r = __mount(p, "pakfire_root", path, "tmpfs", 0, NULL);
                if (r)
                        goto ERROR;
        }
@@ -250,6 +452,16 @@ PAKFIRE_EXPORT int pakfire_create(Pakfire* pakfire, const char* path, const char
        if (r)
                goto ERROR;
 
+       // Mount filesystems
+       r = pakfire_mount(p);
+       if (r)
+               goto ERROR;
+
+       // Populate /dev
+       r = pakfire_populate_dev(p);
+       if (r)
+               goto ERROR;
+
        // Make sure that our private directory exists
        char* private_dir = pakfire_make_path(p, PAKFIRE_PRIVATE_DIR);
        r = pakfire_mkdir(p, private_dir, 0);
@@ -319,227 +531,19 @@ PAKFIRE_EXPORT int pakfire_bind(Pakfire pakfire, const char* src, const char* ds
                goto ERROR;
 
        // Perform mount
-       r = mount(src, mountpoint, NULL, flags|MS_BIND, NULL);
+       r = __mount(pakfire, src, mountpoint, NULL, flags|MS_BIND, NULL);
 
 ERROR:
        free(mountpoint);
        return r;
 }
 
-static const struct pakfire_mountpoint {
-       const char* source;
-       const char* target;
-       const char* fstype;
-       int flags;
-       const char* options;
-} mountpoints[] = {
-       { "pakfire_proc",  "proc",         "proc",  MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL },
-
-       // Bind mount /proc/sys as read-only with the following exceptions:
-       //  * /proc/sys/net
-       { "/proc/sys",     "proc/sys",     NULL,    MS_BIND, NULL },
-       { "/proc/sys/net", "proc/sys/net", NULL,    MS_BIND, NULL },
-       { "/proc/sys",     "proc/sys",     NULL,    MS_BIND|MS_RDONLY|MS_REMOUNT, NULL },
-
-       // Bind mount /sys as read-only
-       { "/sys",          "sys",          NULL,    MS_BIND, NULL },
-       { "/sys",          "sys",          NULL,    MS_BIND|MS_RDONLY|MS_REMOUNT, NULL },
-
-       // Create a new /dev
-       { "pakfire_dev",   "dev",          "tmpfs", MS_NOSUID|MS_NOEXEC,
-               "mode=755,size=4m,nr_inodes=64k" },
-       { "/dev/pts",      "dev/pts",      NULL,    MS_BIND, NULL },
-
-       // Create a new /run
-       { "pakfire_tmpfs", "run",          "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV,
-               "mode=755,size=4m,nr_inodes=1k" },
-
-       // Create a new /tmp
-       { "pakfire_tmpfs", "tmp",          "tmpfs", MS_NOSUID|MS_NODEV,
-               "mode=755" },
-
-       // The end
-       { NULL },
-};
-
-static int pakfire_mount(Pakfire pakfire) {
-       int r;
-
-       for (const struct pakfire_mountpoint* mp = mountpoints; mp->source; mp++) {
-               DEBUG(pakfire, "Mounting /%s\n", mp->target);
-
-               char* target = pakfire_path_join(pakfire->path, mp->target);
-
-RETRY:
-               // Perform mount()
-               r = mount(mp->source, target, mp->fstype, mp->flags, mp->options);
-               if (r) {
-                       // If the target directory does not exist, we will create it
-                       if (errno == ENOENT) {
-                               r = pakfire_mkdir(pakfire, target, S_IRUSR|S_IWUSR|S_IXUSR);
-                               if (r) {
-                                       ERROR(pakfire, "Could not create %s\n", target);
-                                       free(target);
-                                       return r;
-                               }
-
-                               goto RETRY;
-                       }
-
-                       ERROR(pakfire, "Could not mount /%s: %s\n", mp->target, strerror(errno));
-                       free(target);
-                       return r;
-               }
-
-               free(target);
-       }
-
-       return 0;
-}
-
-static int pakfire_umount(Pakfire pakfire) {
-       // Offset to the last element in mountpoints
-       const size_t s = (sizeof(mountpoints) / sizeof(*mountpoints)) - 1;
-       char* target = NULL;
-       int r = 0;
-
-       // Walk through the array backwards
-       for (const struct pakfire_mountpoint* mp = mountpoints + s; mp-- != mountpoints;) {
-               DEBUG(pakfire, "Umounting /%s\n", mp->target);
-               int flags = 0;
-
-               target = pakfire_path_join(pakfire->path, mp->target);
-
-RETRY:
-               // Perform umount
-               r = umount2(target, flags);
-
-               if (r) {
-                       // Skip anything that wasn't mounted
-                       if (errno == EINVAL)
-                               continue;
-
-                       // Attempt a lazy umount when busy
-                       else if (errno == EBUSY) {
-                               flags |= MNT_DETACH;
-                               goto RETRY;
-                       }
-
-                       ERROR(pakfire, "Could not umount %s: %s\n", mp->target, strerror(errno));
-                       goto ERROR;
-               }
-       }
-
-ERROR:
-       if (target)
-               free(target);
-
-       return r;
-}
-
-static const struct pakfire_devnode {
-       const char* path;
-       int major;
-       int minor;
-       mode_t mode;
-} devnodes[] = {
-       { "/dev/null",      1,  3, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
-       { "/dev/zero",      1,  5, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
-       { "/dev/full",      1,  7, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
-       { "/dev/random",    1,  8, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, },
-       { "/dev/urandom",   1,  9, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, },
-       { "/dev/kmsg",      1, 11, S_IFCHR|S_IRUSR|S_IRGRP|S_IROTH, },
-       { "/dev/tty",       5,  0, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
-       { "/dev/console",   5,  1, S_IFCHR|S_IRUSR|S_IWUSR, },
-       { "/dev/ptmx",      5,  2, S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH, },
-       { "/dev/rtc0",    252,  0, S_IFCHR|S_IRUSR|S_IWUSR, },
-       { NULL },
-};
-
-static const struct pakfire_symlink {
-       const char* target;
-       const char* path;
-} symlinks[] = {
-       { "/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 },
-};
-
-static int pakfire_populate_dev(Pakfire pakfire) {
-       // Create device nodes
-       for (const struct pakfire_devnode* devnode = devnodes; devnode->path; devnode++) {
-               DEBUG(pakfire, "Creating device node %s\n", devnode->path);
-
-               char* path = pakfire_make_path(pakfire, devnode->path);
-               dev_t dev = makedev(devnode->major, devnode->minor);
-
-               int r = mknod(path, devnode->mode, dev);
-               if (r) {
-                       ERROR(pakfire, "Could not create %s: %s\n", devnode->path, strerror(errno));
-                       free(path);
-
-                       return r;
-               }
-
-               free(path);
-       }
-
-       // Create symlinks
-       for (const struct pakfire_symlink* s = symlinks; s->target; s++) {
-               DEBUG(pakfire, "Creating symlink %s -> %s\n", s->path, s->target);
-
-               char* path = pakfire_make_path(pakfire, s->path);
-
-               int r = symlink(s->target, path);
-               if (r) {
-                       ERROR(pakfire, "Could not create symlink %s: %s\n", s->path, strerror(errno));
-                       free(path);
-
-                       return r;
-               }
-
-               free(path);
-       }
-
-       return 0;
-}
-
 PAKFIRE_EXPORT int pakfire_activate(Pakfire pakfire) {
-       if (strcmp(pakfire->path, "/") == 0)
-               return 0;
-
-       // Do nothing if this already activated
-       if (pakfire->activated++)
-               return 0;
-
-       // Mount filesystems
-       int r = pakfire_mount(pakfire);
-       if (r) {
-               pakfire_umount(pakfire);
-
-               return r;
-       }
-
-       // Populate /dev
-       r = pakfire_populate_dev(pakfire);
-       if (r)
-               return r;
-
        return 0;
 }
 
 PAKFIRE_EXPORT int pakfire_deactivate(Pakfire pakfire) {
-       if (strcmp(pakfire->path, "/") == 0)
-               return 0;
-
-       // Do nothing if there are some activations left
-       if (--pakfire->activated > 0)
-               return 0;
-
-       return pakfire_umount(pakfire);
+       return 0;
 }
 
 PAKFIRE_EXPORT const char* pakfire_get_arch(Pakfire pakfire) {