]> git.ipfire.org Git - pakfire.git/commitdiff
file: Fix script interpreters again
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 31 Dec 2024 18:22:52 +0000 (18:22 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 31 Dec 2024 18:22:52 +0000 (18:22 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/build.c
src/libpakfire/file.c
src/libpakfire/include/pakfire/file.h

index 56a2ad2c6c7021e0df066b182949bbe6ea62f725..0c0f6c2d27aed286f767bf63349837d1ce1cd581 100644 (file)
@@ -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);
index f390fb785b53bf09f03eb82e13fd9cfeca048f13..1d079db2cf491da14971e82a1573cdd056082cee 100644 (file)
@@ -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;
+}
index 4325c01385392a1d06b09919abdbaf40be48f8da..d5bdfbe50c7593a7bbfe252ae17e27adaac48ec0 100644 (file)
@@ -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 */