--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2022 Pakfire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#include <errno.h>
+#include <linux/limits.h>
+#include <mntent.h>
+#include <stddef.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+// libmount
+#include <libmount/libmount.h>
+
+#include <pakfire/logging.h>
+#include <pakfire/pakfire.h>
+#include <pakfire/mount.h>
+#include <pakfire/util.h>
+
+static const struct pakfire_mountpoint {
+ const char* source;
+ const char* target;
+ 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 },
+
+ { "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 },
+
+ // 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
+ { "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 },
+
+ // Create a new /run
+ { "pakfire_tmpfs", "run", "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ "mode=755,size=4m,nr_inodes=1k", 0 },
+
+ // Create a new /tmp
+ { "pakfire_tmpfs", "tmp", "tmpfs", MS_NOSUID|MS_NODEV,
+ "mode=755", 0 },
+
+ // The end
+ { NULL },
+};
+
+/*
+ Easy way to iterate through all mountpoints
+*/
+static int pakfire_mount_foreach(struct pakfire* pakfire,
+ int (*callback)(struct pakfire* pakfire, struct mntent* mnt, const void* data),
+ const char* option, const void* data) {
+ const char* root = pakfire_get_path(pakfire);
+ int r = 1;
+
+ FILE* f = setmntent("/proc/mounts", "r");
+ if (!f) {
+ ERROR(pakfire, "Could not open /proc/mounts: %m\n");
+ return r;
+ }
+
+ for (;;) {
+ struct mntent* mnt = getmntent(f);
+ if (!mnt)
+ break;
+
+ // Ignore any mointpoints that don't belong to us
+ if (!pakfire_string_startswith(mnt->mnt_dir, root))
+ continue;
+
+ // Filter by option
+ if (option) {
+ if (hasmntopt(mnt, option))
+ continue;
+ }
+
+ // Call the callback for each relevant mountpoint
+ r = callback(pakfire, mnt, data);
+ if (r)
+ break;
+ }
+
+ // Tidy up
+ endmntent(f);
+
+ return r;
+}
+
+static int __pakfire_is_mountpoint(struct pakfire* pakfire,
+ struct mntent* mnt, const void* data) {
+ const char* path = (const char*)data;
+
+ // Return if path matches
+ return (strcmp(mnt->mnt_dir, path) == 0);
+}
+
+int pakfire_is_mountpoint(struct pakfire* pakfire, const char* path) {
+ return pakfire_mount_foreach(pakfire, __pakfire_is_mountpoint, NULL, path);
+}
+
+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) {
+ errno = EINVAL;
+ 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;
+
+ // Disable any mount helpers
+ r = mnt_context_disable_helpers(ctx, 1);
+ if (r) {
+ ERROR(pakfire, "Could not disable mount helpers: %m\n");
+ goto ERROR;
+ }
+
+ // Set source
+ r = mnt_context_set_source(ctx, source);
+ if (r) {
+ ERROR(pakfire, "Could not set source: %m\n");
+ goto ERROR;
+ }
+
+ // Set target
+ r = mnt_context_set_target(ctx, target);
+ if (r) {
+ ERROR(pakfire, "Could not set target: %m\n");
+ goto ERROR;
+ }
+
+ // 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;
+ }
+ }
+
+ // 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;
+ }
+ }
+
+ // Set options
+ if (options) {
+ r = mnt_context_set_options(ctx, options);
+ if (r) {
+ ERROR(pakfire, "Could not set mount options: %m\n");
+ goto ERROR;
+ }
+ }
+
+ // Perform mount operation
+ r = mnt_context_mount(ctx);
+ if (r) {
+ // Fetch the error message
+ mnt_context_get_excode(ctx, r, error, sizeof(error));
+
+ ERROR(pakfire, "Mount unsuccessful: %s\n", error);
+ goto ERROR;
+ }
+
+ERROR:
+ if (ctx)
+ mnt_free_context(ctx);
+
+ return r;
+}
+
+static int pakfire_umount(struct pakfire* pakfire, const char* path, int flags) {
+ int r;
+
+ DEBUG(pakfire, "Umounting %s\n", path);
+
+RETRY:
+ // Perform umount
+ r = umount2(path, flags);
+ if (r) {
+ // Attempt a lazy umount when busy
+ if (errno == EBUSY) {
+ flags |= MNT_DETACH;
+ goto RETRY;
+ }
+
+ ERROR(pakfire, "Could not umount %s: %m\n", path);
+ }
+
+ return r;
+}
+
+static int __pakfire_mount_print(struct pakfire* pakfire,
+ struct mntent* mnt, const void* data) {
+ //DEBUG(pakfire,
+ printf(
+ "%s %s %s %s %d %d\n",
+ mnt->mnt_fsname,
+ mnt->mnt_dir,
+ mnt->mnt_type,
+ mnt->mnt_opts,
+ mnt->mnt_freq,
+ mnt->mnt_passno
+ );
+
+ return 0;
+}
+
+static int pakfire_mount_list(struct pakfire* pakfire) {
+ return 0;
+
+ DEBUG(pakfire, "Current mountpoints:\n");
+
+ return pakfire_mount_foreach(pakfire, __pakfire_mount_print, NULL, NULL);
+}
+
+int pakfire_mount_all(struct pakfire* pakfire, const enum pakfire_mount_flags mount_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)
+ 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
+ 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);
+ return r;
+ }
+ }
+
+ // List everything that is mounted
+ pakfire_mount_list(pakfire);
+
+ return 0;
+}
+
+static int __pakfire_umount(struct pakfire* pakfire,
+ struct mntent* mnt, const void* data) {
+ return pakfire_umount(pakfire, mnt->mnt_dir, 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);
+
+ return pakfire_mount_foreach(pakfire, __pakfire_umount, option, NULL);
+}
#include <stdlib.h>
#include <sys/file.h>
#include <sys/mount.h>
-#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <pakfire/db.h>
#include <pakfire/keystore.h>
#include <pakfire/logging.h>
+#include <pakfire/mount.h>
#include <pakfire/package.h>
#include <pakfire/packagelist.h>
#include <pakfire/pakfire.h>
#define LOCK_PATH PAKFIRE_PRIVATE_DIR "/.lock"
#define CCACHE_DIR "/var/cache/ccache"
-struct mountpoint {
- STAILQ_ENTRY(mountpoint) nodes;
- char path[PATH_MAX];
-};
-
struct pakfire {
int nrefs;
struct pakfire_config* config;
- STAILQ_HEAD(mountpoints, mountpoint) mountpoints;
-
struct pakfire_distro {
char pretty_name[256];
char name[64];
return (strcmp(pakfire->path, "/") == 0);
}
-static const struct pakfire_mountpoint {
- const char* source;
- const char* target;
- const char* fstype;
- int flags;
- const char* options;
-} mountpoints[] = {
- { "pakfire_root", "", "tmpfs", 0, NULL },
-
- { "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(struct 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;
-
- // Skip if the mountpoint is already on the list
- if (pakfire_is_mountpoint(pakfire, target))
- return 0;
-
- // If not, add the mountpoint to the list so that we can umount it later
- struct mountpoint* mp = calloc(1, sizeof(*mp));
- if (!mp)
- return 1;
-
- pakfire_string_set(mp->path, target);
-
- STAILQ_INSERT_HEAD(&pakfire->mountpoints, mp, nodes);
-
- return 0;
-}
-
-static int pakfire_mount(struct pakfire* pakfire) {
- char target[PATH_MAX];
- int r;
-
- // Remount / as private
- r = mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL);
- if (r) {
- ERROR(pakfire, "Could not re-mount / as private: %m\n");
- return r;
- }
-
- for (const struct pakfire_mountpoint* mp = mountpoints; mp->source; mp++) {
- // Skip mounting root when a directory has been passed
- if (!*mp->target && !pakfire->mount_tmpfs)
- continue;
-
- DEBUG(pakfire, "Mounting /%s\n", mp->target);
-
- r = pakfire_path_join(target, pakfire->path, mp->target);
- if (r < 0)
- return r;
-
-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(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);
- return r;
- }
- }
-
- return 0;
-}
-
-static int pakfire_umount(struct 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: %m\n", mp->path);
- }
-
- free(mp);
- }
-
- return 0;
-}
-
-int pakfire_is_mountpoint(struct pakfire* pakfire, const char* path) {
- struct mountpoint* mp;
-
- // Check if path is on this list
- STAILQ_FOREACH(mp, &pakfire->mountpoints, nodes) {
- if (strcmp(mp->path, path) == 0)
- return 1;
- }
-
- return 0;
-}
-
static const struct pakfire_devnode {
const char* path;
int major;
return 1;
fclose(f);
- r = __mount(pakfire, interpreter, target, NULL, MS_BIND|MS_RDONLY, NULL);
+ 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);
pakfire_release_lock(pakfire);
// umount everything
- pakfire_umount(pakfire);
+ pakfire_umount_all(pakfire, 0);
if (pakfire->destroy_on_free && *pakfire->path) {
DEBUG(pakfire, "Destroying %s\n", pakfire->path);
}
// Mount filesystems
- r = pakfire_mount(p);
+ r = pakfire_mount_all(p, 0);
if (r)
goto ERROR;
if (r < 0)
return 1;
- DEBUG(pakfire, "Mounting %s to %s\n", src, mountpoint);
+ DEBUG(pakfire, "Bind-mounting %s to %s\n", src, mountpoint);
r = stat(src, &st);
if (r < 0) {
}
// Perform mount
- return __mount(pakfire, src, mountpoint, NULL, flags|MS_BIND, NULL);
+ return pakfire_mount(pakfire, src, mountpoint, NULL, flags|MS_BIND, NULL);
}
gpgme_ctx_t pakfire_get_gpgctx(struct pakfire* pakfire) {