]> git.ipfire.org Git - pakfire.git/commitdiff
arch: Use qemu emulator for foreign arches
authorMichael Tremer <michael.tremer@ipfire.org>
Mon, 17 May 2021 23:30:03 +0000 (23:30 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Mon, 17 May 2021 23:30:03 +0000 (23:30 +0000)
This patch allows Pakfire to emulate any architecture that the host
system supports using binfmt_misc.

We bind-mount the interpreter into the chroot and execute everything as
normal.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/arch.c
src/libpakfire/include/pakfire/arch.h
src/libpakfire/include/pakfire/util.h
src/libpakfire/pakfire.c
src/libpakfire/util.c

index 2e58845d901734e4483398ec50512c5f2c555a9a..f3d8922fbc35a15a6bc44740f25c4fb08ab1b8bb 100644 (file)
 #############################################################################*/
 
 #include <ctype.h>
+#include <errno.h>
+#include <fts.h>
+#include <linux/limits.h>
 #include <stddef.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/personality.h>
 #include <sys/utsname.h>
@@ -35,6 +39,7 @@ struct pakfire_arch {
        const char* platform;
        const char* compatible[5];
        unsigned long personality;
+       const char magic[41];
 };
 
 static const struct pakfire_arch PAKFIRE_ARCHES[] = {
@@ -44,11 +49,13 @@ static const struct pakfire_arch PAKFIRE_ARCHES[] = {
                .platform = "x86",
                .compatible = { "i686", NULL },
                .personality = PER_LINUX,
+               .magic = "7f454c4602010100000000000000000002003e00",
        },
        {
                .name = "i686",
                .platform = "x86",
                .personality = PER_LINUX32,
+               .magic = "7f454c4601010100000000000000000002000300",
        },
 
        // ARM
@@ -56,35 +63,41 @@ static const struct pakfire_arch PAKFIRE_ARCHES[] = {
                .name = "aarch64",
                .platform = "arm",
                .personality = PER_LINUX,
+               .magic = "7f454c460201010000000000000000000200b700",
        },
        {
                .name = "armv7hl",
                .platform = "arm",
                .compatible = { "armv7l", "armv6l", "armv5tejl", "armv5tel", NULL },
                .personality = PER_LINUX32,
+               .magic = "7f454c4601010100000000000000000002002800",
        },
        {
                .name = "armv7l",
                .platform = "arm",
                .compatible = { "armv6l", "armv5tejl", "armv5tel", NULL },
                .personality = PER_LINUX32,
+               .magic = "7f454c4601010100000000000000000002002800",
        },
        {
                .name = "armv6l",
                .platform = "arm",
                .compatible = { "armv5tejl", "armv5tel", NULL },
                .personality = PER_LINUX32,
+               .magic = "7f454c4601010100000000000000000002002800",
        },
        {
                .name = "armv5tejl",
                .platform = "arm",
                .compatible = { "armv5tel", NULL },
                .personality = PER_LINUX32,
+               .magic = "7f454c4601010100000000000000000002002800",
        },
        {
                .name = "armv5tel",
                .platform = "arm",
                .personality = PER_LINUX32,
+               .magic = "7f454c4601010100000000000000000002002800",
        },
 
        // RISC-V
@@ -92,6 +105,7 @@ static const struct pakfire_arch PAKFIRE_ARCHES[] = {
                .name = "riscv64",
                .platform = "riscv",
                .personality = PER_LINUX,
+               .magic = "7f454c460201010000000000000000000200f300",
        },
 
        // The end
@@ -204,3 +218,91 @@ PAKFIRE_EXPORT int pakfire_arch_supported_by_host(const char* name) {
        // Check if those two architectures are compatible
        return pakfire_arch_is_compatible(native_arch, name);
 }
+
+static char* find_interpreter(const char* path, const char* magic) {
+       FILE* f = fopen(path, "r");
+       if (!f)
+               return NULL;
+
+       char* line = NULL;
+       size_t length = 0;
+
+       int enabled = 0;
+       int match = 0;
+       char interpreter[PATH_MAX];
+
+       while (1) {
+               ssize_t bytes_read = getline(&line, &length, f);
+               if (bytes_read < 0)
+                       break;
+
+               // Remove the newline
+               pakfire_remove_trailing_newline(line);
+
+               // Look for the "enabled" line
+               if (strcmp("enabled", line) == 0) {
+                       enabled = 1;
+
+               // Store the interpreter for later
+               } else if (pakfire_string_startswith(line, "interpreter ")) {
+                       pakfire_string_set(interpreter, line + strlen("interpreter "));
+
+               // If we found the magic, we check if it is a match
+               } else if (pakfire_string_startswith(line, "magic ")) {
+                       const char* m = line + strlen("magic ");
+
+                       if (strcmp(magic, m) == 0)
+                               match = 1;
+               }
+       }
+
+       // Free resources
+       if (line)
+               free(line);
+       fclose(f);
+
+       // Return the interpreter if it is a match
+       if (enabled && match && *interpreter)
+               return strdup(interpreter);
+
+       // Otherwise return NULL
+       return NULL;
+}
+
+char* pakfire_arch_find_interpreter(const char* name) {
+       const struct pakfire_arch* arch = pakfire_arch_find(name);
+       if (!arch)
+               return NULL;
+
+       char* interpreter = NULL;
+
+       char* paths[] = {
+               "/proc/sys/fs/binfmt_misc", NULL,
+       };
+
+       FTS* f = fts_open(paths, FTS_NOCHDIR|FTS_NOSTAT, NULL);
+       if (!f)
+               goto ERROR;
+
+       for (;;) {
+               FTSENT* fent = fts_read(f);
+               if (!fent)
+                       break;
+
+               // Only handle files
+               if (!(fent->fts_info & FTS_F))
+                       continue;
+
+               interpreter = find_interpreter(fent->fts_path, arch->magic);
+
+               // End search if we have found a match
+               if (interpreter)
+                       break;
+       }
+
+ERROR:
+       if (f)
+               fts_close(f);
+
+       return interpreter;
+}
index fa08e3b63dc7ed1eff21dcb3f4c85937bc5e6b00..3f93af298f7ac59c4f7b2c7420a02940e885cc29 100644 (file)
@@ -40,6 +40,8 @@ int __pakfire_arch_buildtarget(char* buffer, size_t length, const char* arch, co
 const char* pakfire_arch_platform(const char* name);
 int pakfire_arch_is_compatible(const char* name, const char* compatible_arch);
 
+char* pakfire_arch_find_interpreter(const char* name);
+
 #endif
 
 #endif /* PAKFIRE_ARCH_H */
index bed30a62202ccd96da38b0977cdae9da44a9a700..61573f160689e88b20401b5b9c639d7487ec9e19 100644 (file)
@@ -70,6 +70,7 @@ int __pakfire_path_join(char* dest, size_t length,
        const char* first, const char* second);
 const char* pakfire_path_relpath(const char* root, const char* path);
 
+int pakfire_mkparentdir(const char* path, mode_t mode);
 int pakfire_mkdir(const char* path, mode_t mode);
 FILE* pakfire_mktemp(char* path);
 char* pakfire_mkdtemp(char* path);
index 2e902ea3dac43b0312a92a4eec3c18977a803cd9..38fc1a600087d5252907a70d744d6a4ab79ed1f2 100644 (file)
@@ -431,6 +431,42 @@ ERROR:
        return r;
 }
 
+static int pakfire_mount_interpreter(Pakfire pakfire) {
+       char target[PATH_MAX];
+
+       // Can we emulate this architecture?
+       char* interpreter = pakfire_arch_find_interpreter(pakfire->arch);
+
+       // No interpreter required
+       if (!interpreter)
+               return 0;
+
+       DEBUG(pakfire, "Mounting interpreter %s for %s\n", interpreter, pakfire->arch);
+
+       // Where to mount this?
+       int r = pakfire_make_path(pakfire, target, interpreter);
+       if (r < 0)
+               return r;
+
+       // Create directory
+       r = pakfire_mkparentdir(target, 0);
+       if (r)
+               return r;
+
+       // Create an empty file
+       FILE* f = fopen(target, "w");
+       if (!f)
+               return 1;
+       fclose(f);
+
+       r = __mount(pakfire, interpreter, target, NULL, MS_BIND|MS_RDONLY, NULL);
+       if (r)
+               ERROR(pakfire, "Could not mount interpreter %s to %s: %s\n",
+                       interpreter, target, strerror(errno));
+
+       return r;
+}
+
 static void pakfire_free(Pakfire pakfire) {
        DEBUG(pakfire, "Releasing Pakfire at %p\n", pakfire);
 
@@ -846,6 +882,11 @@ PAKFIRE_EXPORT int pakfire_create(
        if (r)
                goto ERROR;
 
+       // Mount the interpreter (if needed)
+       r = pakfire_mount_interpreter(p);
+       if (r)
+               goto ERROR;
+
        // Make path for private files
        char private_dir[PATH_MAX];
        r = pakfire_make_path(p, private_dir, PAKFIRE_PRIVATE_DIR);
index 2dea9b1a8c16ae442b4e2d91a5cc54e92432185a..9dd14d6a96d15c53afea92936984fc1d99f89474 100644 (file)
@@ -618,7 +618,7 @@ char* pakfire_hexlify(const char* digest, const size_t length) {
        return s;
 }
 
-static int pakfire_mkparentdir(const char* path, mode_t mode) {
+int pakfire_mkparentdir(const char* path, mode_t mode) {
        int r;
 
        char* dirname = pakfire_dirname(path);