#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];
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;
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);
}
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;
}
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);
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) {