From: Michael Tremer Date: Mon, 17 May 2021 23:30:03 +0000 (+0000) Subject: arch: Use qemu emulator for foreign arches X-Git-Tag: 0.9.28~1285^2~133 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a29c7190ebf458611fa183a4d1f9dad80c5ca5ed;p=pakfire.git arch: Use qemu emulator for foreign arches 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 --- diff --git a/src/libpakfire/arch.c b/src/libpakfire/arch.c index 2e58845d9..f3d8922fb 100644 --- a/src/libpakfire/arch.c +++ b/src/libpakfire/arch.c @@ -19,8 +19,12 @@ #############################################################################*/ #include +#include +#include +#include #include #include +#include #include #include #include @@ -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; +} diff --git a/src/libpakfire/include/pakfire/arch.h b/src/libpakfire/include/pakfire/arch.h index fa08e3b63..3f93af298 100644 --- a/src/libpakfire/include/pakfire/arch.h +++ b/src/libpakfire/include/pakfire/arch.h @@ -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 */ diff --git a/src/libpakfire/include/pakfire/util.h b/src/libpakfire/include/pakfire/util.h index bed30a622..61573f160 100644 --- a/src/libpakfire/include/pakfire/util.h +++ b/src/libpakfire/include/pakfire/util.h @@ -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); diff --git a/src/libpakfire/pakfire.c b/src/libpakfire/pakfire.c index 2e902ea3d..38fc1a600 100644 --- a/src/libpakfire/pakfire.c +++ b/src/libpakfire/pakfire.c @@ -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); diff --git a/src/libpakfire/util.c b/src/libpakfire/util.c index 2dea9b1a8..9dd14d6a9 100644 --- a/src/libpakfire/util.c +++ b/src/libpakfire/util.c @@ -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);