From: Michael Tremer Date: Tue, 24 May 2022 14:18:12 +0000 (+0000) Subject: libpakfire: Move mount operations into a new file X-Git-Tag: 0.9.28~763 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bf5f9c2485804f989e40b378255583a975700c43;p=pakfire.git libpakfire: Move mount operations into a new file Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index da734ae48..466365e11 100644 --- a/Makefile.am +++ b/Makefile.am @@ -231,6 +231,7 @@ libpakfire_la_SOURCES = \ src/libpakfire/key.c \ src/libpakfire/keystore.c \ src/libpakfire/logging.c \ + src/libpakfire/mount.c \ src/libpakfire/package.c \ src/libpakfire/packager.c \ src/libpakfire/packagelist.c \ @@ -267,6 +268,7 @@ pkginclude_HEADERS += \ src/libpakfire/include/pakfire/key.h \ src/libpakfire/include/pakfire/keystore.h \ src/libpakfire/include/pakfire/logging.h \ + src/libpakfire/include/pakfire/mount.h \ src/libpakfire/include/pakfire/package.h \ src/libpakfire/include/pakfire/packager.h \ src/libpakfire/include/pakfire/packagelist.h \ diff --git a/src/libpakfire/execute.c b/src/libpakfire/execute.c index 082b04be9..4279b17b9 100644 --- a/src/libpakfire/execute.c +++ b/src/libpakfire/execute.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -560,6 +561,12 @@ static int pakfire_execute_fork(void* data) { // Change root (unless root is /) if (strcmp(root, "/") != 0) { + // Mount everything + r = pakfire_mount_all(pakfire, MOUNT_IN_NEW_NS); + if (r) + return r; + + // Move the old root toi here r = pakfire_string_format(oldroot, "%s/.oldroot.XXXXXX", root); if (r < 0) { ERROR(pakfire, "Could not figure out where the old root directory is going: %m\n"); @@ -871,6 +878,9 @@ ERROR: if (env.stderr[0]) close(env.stderr[0]); + // Umount everything + pakfire_umount_all(pakfire, PAKFIRE_MOUNT_OPT_NAMESPACED); + // Free environment for (unsigned int i = 0; env.envp[i]; i++) free(env.envp[i]); diff --git a/src/libpakfire/include/pakfire/mount.h b/src/libpakfire/include/pakfire/mount.h new file mode 100644 index 000000000..2f745e032 --- /dev/null +++ b/src/libpakfire/include/pakfire/mount.h @@ -0,0 +1,39 @@ +/*############################################################################# +# # +# 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 . # +# # +#############################################################################*/ + +#ifdef PAKFIRE_PRIVATE + +#include + +#include + +#define PAKFIRE_MOUNT_OPT_NAMESPACED "x-pakfire.ns" // "_pakfire_ns" + +enum pakfire_mount_flags { + MOUNT_IN_NEW_NS = (1 << 0), +}; + +int pakfire_mount(struct pakfire* pakfire, const char* source, const char* target, + const char* filesystemtype, unsigned long mountflags, const void* data); + +int pakfire_mount_all(struct pakfire* pakfire, const enum pakfire_mount_flags mount_flags); +int pakfire_umount_all(struct pakfire* pakfire, const char* option); + +#endif /* PAKFIRE_PRIVATE */ diff --git a/src/libpakfire/mount.c b/src/libpakfire/mount.c new file mode 100644 index 000000000..e8885cc5e --- /dev/null +++ b/src/libpakfire/mount.c @@ -0,0 +1,350 @@ +/*############################################################################# +# # +# 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 . # +# # +#############################################################################*/ + +#include +#include +#include +#include +#include +#include +#include + +// libmount +#include + +#include +#include +#include +#include + +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); +} diff --git a/src/libpakfire/pakfire.c b/src/libpakfire/pakfire.c index ea7bf2deb..378ab095f 100644 --- a/src/libpakfire/pakfire.c +++ b/src/libpakfire/pakfire.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -49,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -66,11 +66,6 @@ #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; @@ -104,8 +99,6 @@ struct pakfire { struct pakfire_config* config; - STAILQ_HEAD(mountpoints, mountpoint) mountpoints; - struct pakfire_distro { char pretty_name[256]; char name[64]; @@ -154,160 +147,6 @@ int pakfire_on_root(struct pakfire* pakfire) { 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; @@ -524,7 +363,7 @@ static int pakfire_mount_interpreter(struct pakfire* pakfire) { 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); @@ -557,7 +396,7 @@ static void pakfire_free(struct pakfire* pakfire) { 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); @@ -996,7 +835,7 @@ PAKFIRE_EXPORT int pakfire_create(struct pakfire** pakfire, const char* path, } // Mount filesystems - r = pakfire_mount(p); + r = pakfire_mount_all(p, 0); if (r) goto ERROR; @@ -1162,7 +1001,7 @@ PAKFIRE_EXPORT int pakfire_bind(struct pakfire* pakfire, const char* src, const 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) { @@ -1198,7 +1037,7 @@ PAKFIRE_EXPORT int pakfire_bind(struct pakfire* pakfire, const char* src, const } // 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) {