From: Michael Tremer Date: Sun, 21 Mar 2021 14:27:32 +0000 (+0000) Subject: libpakfire: Automatically mount virtual environment X-Git-Tag: 0.9.28~1285^2~495 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=14373022e740ab5e05181b4f4e366e609aea00a5;p=pakfire.git libpakfire: Automatically mount virtual environment Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/pakfire.c b/src/libpakfire/pakfire.c index 98edd75f8..074acc960 100644 --- a/src/libpakfire/pakfire.c +++ b/src/libpakfire/pakfire.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,11 @@ #include #include +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) {