From: Michael Tremer Date: Tue, 12 Sep 2023 17:17:07 +0000 (+0000) Subject: file: Rewrite fixing interpreters X-Git-Tag: 0.9.29~7 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=94a53bda9207da717f16bb279611f1255aa46243;p=pakfire.git file: Rewrite fixing interpreters The old version had some weaknesses in finding any arguments that came after the interpreter which are being fixed here. This version should also be easier to read. Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/file.c b/src/libpakfire/file.c index 3358ebdfa..7a994c48f 100644 --- a/src/libpakfire/file.c +++ b/src/libpakfire/file.c @@ -2418,129 +2418,147 @@ ERROR: static int pakfire_file_fix_interpreter(struct pakfire_file* file, const char* interpreter) { FILE* f = NULL; - char path[PATH_MAX]; int r; - char* data = NULL; + char* buffer = NULL; + char* line = NULL; size_t length = 0; + char* p = NULL; - DEBUG(file->pakfire, "%s: Fixing interpreter %s\n", - pakfire_file_get_path(file), interpreter); + char command[PATH_MAX]; + const char* args = NULL; + + const char* path = pakfire_file_get_path(file); + + DEBUG(file->pakfire, "%s: Fixing interpreter %s\n", path, interpreter); // Open the file f = pakfire_file_open(file); if (!f) { - ERROR(file->pakfire, "Could not open %s: %m\n", pakfire_file_get_path(file)); - r = 1; + ERROR(file->pakfire, "Could not open %s: %m\n", path); + r = -errno; goto ERROR; } - // Read the entire content into the buffer - r = pakfire_read_file_into_buffer(f, &data, &length); - if (r) { - ERROR(file->pakfire, "Could not read file into memory: %m\n"); - 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; - // Find the end of the first line - char* eol = memchr(data, '\n', length); - if (!eol) { - DEBUG(file->pakfire, "Could not find the end of the first line\n"); - errno = EINVAL; - r = 0; - goto ERROR; - } + ERROR(file->pakfire, "Could not read line from %s: %m\n", path); + goto ERROR; + } - // Terminate the first line - *eol = '\0'; + switch (lineno) { + case 0: + // Check if the first line starts with #! + if (!pakfire_string_startswith(line, "#!")) { + r = -EINVAL; + goto ERROR; + } - // Pointer to the beginning of the first line - char* p = data; + // Remove the trailing newline + pakfire_remove_trailing_newline(line); - // Pointer to the beginning of the rest of the script - const char* rest = eol + 1; + // Start at the beginning of the line (after shebang) + p = line + 2; - DEBUG(file->pakfire, "First line: %s\n", p); + // Consume any whitespace + while (*p && isspace(*p)) + p++; - // Read shebang - if (pakfire_string_startswith(p, "#!")) { - p += 2; - } else { - ERROR(file->pakfire, "%s: Could not find shebang\n", pakfire_file_get_path(file)); - errno = EINVAL; - r = 1; - goto ERROR; - } + // Consume the old interpreter + while (*p && !isspace(*p)) + p++; - // Consume any whitespace - while (isspace(*p)) - p++; + // Consume any whitespace + while (*p && isspace(*p)) + p++; - // Consume the old interpreter - while (!isspace(*p)) - p++; + // The actual interpreter begins here + interpreter = p; - // Consume any whitespace - while (isspace(*p)) - p++; + // Find the end of the interpreter + while (*p && !isspace(*p)) + p++; - // The command begins here - const char* command = p; + // Terminate the interpreter + *p++ = '\0'; - // Find the end of the command - while (!isspace(*p)) - p++; + // Any arguments begin here (if we didn't consume the entire string, yet) + if (*p) + args = p; - // Terminate the command - *p++ = '\0'; + ERROR(file->pakfire, "%s: Found command %s (%s)\n", path, interpreter, args); - DEBUG(file->pakfire, "%s: Found command: %s\n", pakfire_file_get_path(file), command); + // Find the real path + r = pakfire_which(file->pakfire, command, interpreter); + if (r) { + ERROR(file->pakfire, "%s: Could not resolve %s: %m\n", path, interpreter); + goto ERROR; + } - // Find the absolute path to the command - r = pakfire_which(file->pakfire, path, command); - if (r) { - DEBUG(file->pakfire, "%s: Could not resolve command %s: %m\n", - pakfire_file_get_path(file), command); - goto ERROR; - } + // If we could not resolve the command, this file has an invalid interpreter + if (!*command) { + ERROR(file->pakfire, "%s: Could not find path for command %s\n", path, interpreter); - // If we could not resolve the command, this file has an invalid interpreter - if (!*path) { - file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER; - r = 0; - goto ERROR; - } + file->issues |= PAKFIRE_FILE_INVALID_INTERPRETER; + r = 0; + goto ERROR; + } - // Go back to the beginning of the file - rewind(f); + // Write the new first line to the buffer + r = asprintf(&buffer, "#!%s", command); + if (r < 0) + goto ERROR; - // Write shebang - r = fprintf(f, (p >= rest) ? "#!%s\n" : "#!%s %s\n", path, p); - if (r < 0) { - ERROR(file->pakfire, "%s: Could not write back first line: %m\n", - pakfire_file_get_path(file)); - r = 1; - goto ERROR; + // Append arguments if any + if (args) { + r = asprintf(&buffer, "%s %s", buffer, args); + if (r < 0) + goto ERROR; + } + + // Terminate the line + r = asprintf(&buffer, "%s\n", buffer); + if (r < 0) + goto ERROR; + + // Process the next line + break; + + default: + // Append the line to the buffer + r = asprintf(&buffer, "%s%s", buffer, line); + if (r < 0) + goto ERROR; + break; + } } - // Determine the length of the payload - length -= rest - data; + // Go back to the beginning of the file + rewind(f); - // Write the rest - size_t bytes_written = fwrite(rest, 1, length, f); - if (bytes_written < length) { - ERROR(file->pakfire, "%s: Could not write the payload: %m\n", - pakfire_file_get_path(file)); - r = 1; - goto ERROR; + // Write back the buffer + if (buffer) { + size_t bytes_written = fwrite(buffer, 1, strlen(buffer), f); + if (bytes_written < length) { + ERROR(file->pakfire, "%s: Could not write the payload: %m\n", path); + r = -errno; + goto ERROR; + } } // Success! r = 0; ERROR: - if (data) - free(data); + if (buffer) + free(buffer); + if (line) + free(line); if (f) fclose(f);