From: Michael Tremer Date: Tue, 31 Dec 2024 18:22:52 +0000 (+0000) Subject: file: Fix script interpreters again X-Git-Tag: 0.9.30~617 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=52ae221e3c5d2e936fa0173b1af922c0fdff7ef4;p=pakfire.git file: Fix script interpreters again Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/build.c b/src/libpakfire/build.c index 56a2ad2c6..0c0f6c2d2 100644 --- a/src/libpakfire/build.c +++ b/src/libpakfire/build.c @@ -1222,6 +1222,30 @@ static int pakfire_build_post_check_broken_symlinks( PAKFIRE_BUILD_ERROR_IF_NOT_EMPTY); } +static int pakfire_build_fix_script_interpreter( + struct pakfire_ctx* ctx, struct pakfire_file* file, void* data) { + // Only run this for regular files + switch (pakfire_file_get_type(file)) { + case S_IFREG: + break; + + // Ignore anything that isn't a symlink + default: + return 0; + } + + // The file must be executable + if (!pakfire_file_is_executable(file)) + return 0; + + // Do not run this on ELF files + else if (pakfire_file_is_elf(file)) + return 0; + + // Fix the interpreter + return pakfire_file_fix_interpreter(file); +} + /* File Issues */ @@ -1264,6 +1288,12 @@ static int pakfire_build_run_post_build_checks(struct pakfire_build* build) { if (r) goto ERROR; + // Fix script interpreters + r = pakfire_filelist_walk(filelist, + pakfire_build_fix_script_interpreter, NULL, 0, NULL); + if (r) + goto ERROR; + ERROR: if (filelist) pakfire_filelist_unref(filelist); diff --git a/src/libpakfire/file.c b/src/libpakfire/file.c index f390fb785..1d079db2c 100644 --- a/src/libpakfire/file.c +++ b/src/libpakfire/file.c @@ -1793,6 +1793,43 @@ PAKFIRE_EXPORT int pakfire_file_matches(struct pakfire_file* file, const char* p return pakfire_path_match(pattern, path); } +static int pakfire_file_fconsume(struct pakfire_file* file, FILE* src) { + FILE* dst = NULL; + int fd = -EBADF; + int r; + + // Use sendfile if we have a file descriptor + fd = fileno(src); + if (fd >= 0) + return pakfire_file_consume(file, fd); + + // Rewind the file + r = pakfire_rewind(src); + if (r < 0) { + ERROR(file->ctx, "Could not rewind file descriptor: %m\n"); + r = -errno; + goto ERROR; + } + + // Open the file for writing + dst = pakfire_file_fopen(file, "w"); + if (!dst) + goto ERROR; + + // Copy the content + r = pakfire_copy(file->ctx, src, dst); + if (r < 0) { + ERROR(file->ctx, "Could not consume to %s: %m\n", pakfire_file_get_path(file)); + goto ERROR; + } + +ERROR: + if (dst) + fclose(dst); + + return r; +} + int pakfire_file_consume(struct pakfire_file* file, int srcfd) { ssize_t bytes_written = 0; struct stat st = {}; @@ -1831,3 +1868,152 @@ ERROR: return r; } + +int pakfire_file_fix_interpreter(struct pakfire_file* file) { + const char* interpreter = NULL; + char* buffer = NULL; + FILE* f = NULL; + FILE* b = NULL; + size_t l = 0; + int r; + + char* line = NULL; + size_t length = 0; + char* p = NULL; + + char command[PATH_MAX]; + const char* args = NULL; + + const char* path = pakfire_file_get_path(file); + + // Create a place where we can write all the data to + b = open_memstream(&buffer, &l); + if (!b) { + ERROR(file->ctx, "Could not open a memory buffer: %m\n"); + r = -errno; + goto ERROR; + } + + // Open the file + f = pakfire_file_fopen(file, "r"); + if (!f) { + ERROR(file->ctx, "Could not open %s: %m\n", path); + r = -errno; + goto ERROR; + } + + for (unsigned int lineno = 0;; lineno++) { + r = getline(&line, &length, f); + if (r < 0) { + // We finished reading when we reach the end + if (feof(f)) + break; + + ERROR(file->ctx, "Could not read line from %s: %m\n", path); + r = -errno; + goto ERROR; + } + + switch (lineno) { + case 0: + // Check if the first line starts with #! + if (!pakfire_string_startswith(line, "#!")) { + r = 0; + goto ERROR; + } + + // Remove the trailing newline + pakfire_string_rstrip(line); + + // Start at the beginning of the line (after shebang) + p = line + 2; + + // Consume any whitespace + while (*p && isspace(*p)) + p++; + + // Consume the old interpreter + while (*p && !isspace(*p)) + p++; + + // Consume any whitespace + while (*p && isspace(*p)) + p++; + + // The actual interpreter begins here + interpreter = p; + + // Find the end of the interpreter + while (*p && !isspace(*p)) + p++; + + // Terminate the interpreter + *p++ = '\0'; + + // Any arguments begin here (if we didn't consume the entire string, yet) + if (*p) + args = p; + + DEBUG(file->ctx, "%s: Found command %s (%s)\n", path, interpreter, args); + + // Find the real path + r = pakfire_which(file->pakfire, command, interpreter); + if (r < 0) { + ERROR(file->ctx, "%s: Could not resolve %s: %m\n", path, interpreter); + goto ERROR; + } + + // If we could not resolve the command, this file has an invalid interpreter + if (!*command) { + ERROR(file->ctx, "%s: Could not find path for command %s\n", path, interpreter); + r = -ENOENT; + goto ERROR; + } + + // Write the new first line to the buffer + r = fprintf(b, "#!%s", command); + if (r < 0) + goto ERROR; + + // Append arguments if any + if (args) { + r = fprintf(b, " %s", args); + if (r < 0) + goto ERROR; + } + + // Terminate the line + r = fprintf(b, "\n"); + if (r < 0) + goto ERROR; + + // Process the next line + break; + + default: + // Append the line to the buffer + r = fprintf(b, "%s", line); + if (r < 0) + goto ERROR; + + break; + } + } + + // Write back the new content + r = pakfire_file_fconsume(file, b); + if (r < 0) + goto ERROR; + +ERROR: + if (b) + fclose(b); + if (buffer) + free(buffer); + if (line) + free(line); + if (f) + fclose(f); + + return r; +} diff --git a/src/libpakfire/include/pakfire/file.h b/src/libpakfire/include/pakfire/file.h index 4325c0138..d5bdfbe50 100644 --- a/src/libpakfire/include/pakfire/file.h +++ b/src/libpakfire/include/pakfire/file.h @@ -181,5 +181,7 @@ int pakfire_file_verify(struct pakfire_file* file, int* status); int pakfire_file_consume(struct pakfire_file* file, int srcfd); +int pakfire_file_fix_interpreter(struct pakfire_file* file); + #endif /* PAKFIRE_PRIVATE */ #endif /* PAKFIRE_FILE_H */