|
|
e660d2 |
From 63f9932642bcd7c617faf8b38f4976945e694883 Mon Sep 17 00:00:00 2001
|
|
|
e660d2 |
From: Matthew Almond <malmond@fb.com>
|
|
|
e660d2 |
Date: Mon, 21 Sep 2020 15:20:07 -0700
|
|
|
e660d2 |
Subject: [PATCH 1/2] Add support for rpm2extents transcoder
|
|
|
e660d2 |
|
|
|
e660d2 |
Two related parts:
|
|
|
e660d2 |
|
|
|
e660d2 |
1. If `LIBREPO_TRANSCODE_RPMS` environment is set to a program (with parameters) then downloads are piped through it.
|
|
|
e660d2 |
2. Transcoded RPMS by definition will not have the same bits on disk as downloaded. This is inherent. The transcoder is tasked with measuring the bits that enter stdin and storing a copy of the digest(s) seen in the footer. `librepo` can then use these stored digests instead if the environment variable is set.
|
|
|
e660d2 |
|
|
|
e660d2 |
This is part of changes described in https://fedoraproject.org/wiki/Changes/RPMCoW
|
|
|
e660d2 |
---
|
|
|
e660d2 |
librepo/checksum.c | 111 ++++++++++++++++++++++++++++++++-
|
|
|
e660d2 |
librepo/downloader.c | 145 ++++++++++++++++++++++++++++++++++++++++++-
|
|
|
e660d2 |
librepo/rcodes.h | 2 +
|
|
|
e660d2 |
3 files changed, 254 insertions(+), 4 deletions(-)
|
|
|
e660d2 |
|
|
|
e660d2 |
diff --git a/librepo/checksum.c b/librepo/checksum.c
|
|
|
e660d2 |
index 47e59467..0f27815e 100644
|
|
|
e660d2 |
--- a/librepo/checksum.c
|
|
|
e660d2 |
+++ b/librepo/checksum.c
|
|
|
e660d2 |
@@ -39,6 +39,9 @@
|
|
|
e660d2 |
#define BUFFER_SIZE 2048
|
|
|
e660d2 |
#define MAX_CHECKSUM_NAME_LEN 7
|
|
|
e660d2 |
|
|
|
e660d2 |
+/* magic value at end of file (64 bits) that indicates this is a transcoded rpm */
|
|
|
e660d2 |
+#define MAGIC 3472329499408095051
|
|
|
e660d2 |
+
|
|
|
e660d2 |
LrChecksumType
|
|
|
e660d2 |
lr_checksum_type(const char *type)
|
|
|
e660d2 |
{
|
|
|
e660d2 |
@@ -102,6 +105,100 @@ lr_checksum_type_to_str(LrChecksumType type)
|
|
|
e660d2 |
return NULL;
|
|
|
e660d2 |
}
|
|
|
e660d2 |
|
|
|
e660d2 |
+char *
|
|
|
e660d2 |
+lr_checksum_cow_fd(LrChecksumType type, int fd, GError **err)
|
|
|
e660d2 |
+{
|
|
|
e660d2 |
+ struct __attribute__ ((__packed__)) csum_offset_magic {
|
|
|
e660d2 |
+ off64_t csum_offset;
|
|
|
e660d2 |
+ uint64_t magic;
|
|
|
e660d2 |
+ };
|
|
|
e660d2 |
+ struct __attribute__ ((__packed__)) orig_size_algos_len {
|
|
|
e660d2 |
+ ssize_t orig_size;
|
|
|
e660d2 |
+ uint32_t algos_len;
|
|
|
e660d2 |
+ };
|
|
|
e660d2 |
+ struct __attribute__ ((__packed__)) algo_len_digest_len {
|
|
|
e660d2 |
+ uint32_t algo_len;
|
|
|
e660d2 |
+ uint32_t digest_len;
|
|
|
e660d2 |
+ };
|
|
|
e660d2 |
+
|
|
|
e660d2 |
+ struct csum_offset_magic csum_offset_magic;
|
|
|
e660d2 |
+ struct orig_size_algos_len orig_size_algos_len;
|
|
|
e660d2 |
+ struct algo_len_digest_len algo_len_digest_len;
|
|
|
e660d2 |
+ char *algo, *checksum;
|
|
|
e660d2 |
+ unsigned char *digest;
|
|
|
e660d2 |
+ size_t len = sizeof(csum_offset_magic);
|
|
|
e660d2 |
+
|
|
|
e660d2 |
+ if (g_getenv("LIBREPO_TRANSCODE_RPMS") == NULL) {
|
|
|
e660d2 |
+ g_debug("Transcoding not enabled, skipping path");
|
|
|
e660d2 |
+ return NULL;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ if (lseek(fd, -len, SEEK_END) == -1) {
|
|
|
e660d2 |
+ g_warning("seek for transcode failed, probably too small");
|
|
|
e660d2 |
+ return NULL;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ if (read(fd, &csum_offset_magic, len) != len) {
|
|
|
e660d2 |
+ g_set_error(err, LR_CHECKSUM_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "Cannot read csum_offset, magic. size = %lu", len);
|
|
|
e660d2 |
+ return NULL;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ if (csum_offset_magic.magic != MAGIC) {
|
|
|
e660d2 |
+ g_debug("Not transcoded");
|
|
|
e660d2 |
+ return NULL;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ g_debug("Is transcoded");
|
|
|
e660d2 |
+ if (lseek(fd, csum_offset_magic.csum_offset, SEEK_SET) == -1) {
|
|
|
e660d2 |
+ g_set_error(err, LR_CHECKSUM_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "seek for transcode csum_offset failed");
|
|
|
e660d2 |
+ return NULL;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ len = sizeof(orig_size_algos_len);
|
|
|
e660d2 |
+ if (read(fd, &orig_size_algos_len, len) != len) {
|
|
|
e660d2 |
+ g_set_error(err, LR_CHECKSUM_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "Cannot read orig_size_algos_len");
|
|
|
e660d2 |
+ return NULL;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ while (orig_size_algos_len.algos_len > 0) {
|
|
|
e660d2 |
+ len = sizeof(algo_len_digest_len);
|
|
|
e660d2 |
+ if (read(fd, &algo_len_digest_len, len) != len) {
|
|
|
e660d2 |
+ g_set_error(err, LR_CHECKSUM_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "Cannot read algo_len_digest_len");
|
|
|
e660d2 |
+ return NULL;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+
|
|
|
e660d2 |
+ len = algo_len_digest_len.algo_len;
|
|
|
e660d2 |
+ algo = lr_malloc0(len + 1);
|
|
|
e660d2 |
+ if (read(fd, algo, len) != len) {
|
|
|
e660d2 |
+ g_set_error(err, LR_CHECKSUM_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "Cannot read algo");
|
|
|
e660d2 |
+ lr_free(algo);
|
|
|
e660d2 |
+ return NULL;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ len = algo_len_digest_len.digest_len;
|
|
|
e660d2 |
+ digest = lr_malloc0(len);
|
|
|
e660d2 |
+ if (read(fd, digest, len) != len) {
|
|
|
e660d2 |
+ g_set_error(err, LR_CHECKSUM_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "Cannot read digest");
|
|
|
e660d2 |
+ lr_free(algo);
|
|
|
e660d2 |
+ lr_free(digest);
|
|
|
e660d2 |
+ return NULL;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ if (lr_checksum_type(algo) == type) {
|
|
|
e660d2 |
+ /* found it, do the same as lr_checksum_fd does */
|
|
|
e660d2 |
+ checksum = lr_malloc0(sizeof(char) * (len * 2 + 1));
|
|
|
e660d2 |
+ for (size_t x = 0; x < len; x++) {
|
|
|
e660d2 |
+ sprintf(checksum+(x*2), "%02x", digest[x]);
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ lr_free(algo);
|
|
|
e660d2 |
+ lr_free(digest);
|
|
|
e660d2 |
+ return checksum;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ lr_free(algo);
|
|
|
e660d2 |
+ lr_free(digest);
|
|
|
e660d2 |
+ orig_size_algos_len.algos_len--;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ return NULL;
|
|
|
e660d2 |
+}
|
|
|
e660d2 |
+
|
|
|
e660d2 |
char *
|
|
|
e660d2 |
lr_checksum_fd(LrChecksumType type, int fd, GError **err)
|
|
|
e660d2 |
{
|
|
|
e660d2 |
@@ -257,9 +354,17 @@ lr_checksum_fd_compare(LrChecksumType type,
|
|
|
e660d2 |
}
|
|
|
e660d2 |
}
|
|
|
e660d2 |
|
|
|
3b8b8a |
- char *checksum = lr_checksum_fd(type, fd, err);
|
|
|
e660d2 |
- if (!checksum)
|
|
|
e660d2 |
- return FALSE;
|
|
|
3b8b8a |
+ char *checksum = lr_checksum_cow_fd(type, fd, err);
|
|
|
e660d2 |
+ if (checksum) {
|
|
|
e660d2 |
+ // if checksum is found in CoW package, do not cache it in xattr
|
|
|
e660d2 |
+ // because looking this up is nearly constant time (cheap) but
|
|
|
e660d2 |
+ // is not valid when CoW is not enabled in RPM.
|
|
|
e660d2 |
+ caching = FALSE;
|
|
|
e660d2 |
+ } else {
|
|
|
e660d2 |
+ checksum = lr_checksum_fd(type, fd, err);
|
|
|
e660d2 |
+ if (!checksum)
|
|
|
e660d2 |
+ return FALSE;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
|
|
|
e660d2 |
*matches = (strcmp(expected, checksum)) ? FALSE : TRUE;
|
|
|
e660d2 |
|
|
|
e660d2 |
diff --git a/librepo/downloader.c b/librepo/downloader.c
|
|
|
e660d2 |
index c5278fbc..5326eb7a 100644
|
|
|
e660d2 |
--- a/librepo/downloader.c
|
|
|
e660d2 |
+++ b/librepo/downloader.c
|
|
|
e660d2 |
@@ -32,6 +32,7 @@
|
|
|
e660d2 |
#include <sys/types.h>
|
|
|
e660d2 |
#include <sys/stat.h>
|
|
|
e660d2 |
#include <sys/time.h>
|
|
|
e660d2 |
+#include <sys/wait.h>
|
|
|
e660d2 |
#include <sys/xattr.h>
|
|
|
e660d2 |
#include <fcntl.h>
|
|
|
e660d2 |
#include <curl/curl.h>
|
|
|
e660d2 |
@@ -151,6 +152,10 @@ typedef struct {
|
|
|
e660d2 |
FILE *f; /*!<
|
|
|
e660d2 |
fdopened file descriptor from LrDownloadTarget and used
|
|
|
e660d2 |
in curl_handle. */
|
|
|
e660d2 |
+ FILE *writef; /*!<
|
|
|
e660d2 |
+ the fd to write data to. Could be a subprocess. */
|
|
|
e660d2 |
+ pid_t pid; /*!<
|
|
|
e660d2 |
+ the pid of a transcoder. */
|
|
|
e660d2 |
char errorbuffer[CURL_ERROR_SIZE]; /*!<
|
|
|
e660d2 |
Error buffer used in curl handle */
|
|
|
e660d2 |
GSList *tried_mirrors; /*!<
|
|
|
e660d2 |
@@ -614,7 +619,7 @@ lr_writecb(char *ptr, size_t size, size_t nmemb, void *userdata)
|
|
|
e660d2 |
if (range_start <= 0 && range_end <= 0) {
|
|
|
e660d2 |
// Write everything curl give to you
|
|
|
e660d2 |
target->writecb_recieved += all;
|
|
|
e660d2 |
- return fwrite(ptr, size, nmemb, target->f);
|
|
|
e660d2 |
+ return fwrite(ptr, size, nmemb, target->writef);
|
|
|
e660d2 |
}
|
|
|
e660d2 |
|
|
|
e660d2 |
/* Deal with situation when user wants only specific byte range of the
|
|
|
e660d2 |
@@ -1428,6 +1433,136 @@ open_target_file(LrTarget *target, GError **err)
|
|
|
e660d2 |
return f;
|
|
|
e660d2 |
}
|
|
|
e660d2 |
|
|
|
e660d2 |
+/** Maybe transcode the file
|
|
|
e660d2 |
+ */
|
|
|
e660d2 |
+void
|
|
|
e660d2 |
+maybe_transcode(LrTarget *target, GError **err)
|
|
|
e660d2 |
+{
|
|
|
e660d2 |
+ const char *e = g_getenv("LIBREPO_TRANSCODE_RPMS");
|
|
|
e660d2 |
+ int transcoder_stdin[2], fd;
|
|
|
e660d2 |
+ pid_t pid;
|
|
|
e660d2 |
+ FILE *out;
|
|
|
e660d2 |
+ _cleanup_strv_free_ gchar **args = NULL;
|
|
|
e660d2 |
+ target->writef = NULL;
|
|
|
e660d2 |
+ if (!e) {
|
|
|
e660d2 |
+ g_debug("Not transcoding");
|
|
|
e660d2 |
+ target->writef = target->f;
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ if (g_str_has_suffix(target->target->path, ".rpm") == FALSE) {
|
|
|
e660d2 |
+ g_debug("Not transcoding %s due to name", target->target->path);
|
|
|
e660d2 |
+ target->writef = target->f;
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ g_debug("Transcoding %s", target->target->path);
|
|
|
e660d2 |
+ args = g_strsplit(e, " ", -1);
|
|
|
e660d2 |
+ if (args[0] == NULL) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "transcode env empty");
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ if (pipe(transcoder_stdin) != 0) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "input pipe creation failed: %s",
|
|
|
e660d2 |
+ g_strerror(errno));
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ /** librepo collects the 'write' ends of the pipes. We must mark these as
|
|
|
e660d2 |
+ * FD_CLOEXEC so a second download/transcode does not inherit them and
|
|
|
e660d2 |
+ * hold them open, as it'll prevent an EOF and cause a deadlock.
|
|
|
e660d2 |
+ */
|
|
|
e660d2 |
+ if (fcntl(transcoder_stdin[1], F_SETFD, FD_CLOEXEC) != 0) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "input pipe write close-on-fork failed: %s",
|
|
|
e660d2 |
+ g_strerror(errno));
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ pid = fork();
|
|
|
e660d2 |
+ if (pid == -1) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "fork failed: %s",
|
|
|
e660d2 |
+ g_strerror(errno));
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ if (pid == 0) {
|
|
|
e660d2 |
+ /* child */
|
|
|
e660d2 |
+ if (dup2(transcoder_stdin[0], STDIN_FILENO) == -1) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "dup2 of stdin failed: %s",
|
|
|
e660d2 |
+ g_strerror(errno));
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ close(transcoder_stdin[0]);
|
|
|
e660d2 |
+ close(transcoder_stdin[1]);
|
|
|
e660d2 |
+ fd = fileno(target->f);
|
|
|
e660d2 |
+ if (fd == -1) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "fileno for target failed");
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ if (dup2(fd, STDOUT_FILENO) == -1) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "dup2 of stdout failed: %s",
|
|
|
e660d2 |
+ g_strerror(errno));
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ if (execv(args[0], args) == -1) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "execv failed: %s", g_strerror(errno));
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ /* we never get here, but appease static analysis */
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ } else {
|
|
|
e660d2 |
+ /* parent */
|
|
|
e660d2 |
+ close(transcoder_stdin[0]);
|
|
|
e660d2 |
+ out = fdopen(transcoder_stdin[1], "w");
|
|
|
e660d2 |
+ if (out == NULL) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "fdopen failed: %s",
|
|
|
e660d2 |
+ g_strerror(errno));
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ target->pid = pid;
|
|
|
e660d2 |
+ target->writef = out;
|
|
|
e660d2 |
+ /* resuming a transcode is not yet implemented */
|
|
|
e660d2 |
+ target->resume = FALSE;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+}
|
|
|
e660d2 |
+
|
|
|
e660d2 |
+void
|
|
|
e660d2 |
+cleanup_transcode(LrTarget *target, GError **err)
|
|
|
e660d2 |
+{
|
|
|
e660d2 |
+ int wstatus, trc;
|
|
|
e660d2 |
+ if (!target->writef) {
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ if (target->writef == target->f) {
|
|
|
e660d2 |
+ return;
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ fclose(target->writef);
|
|
|
e660d2 |
+ if(waitpid(target->pid, &wstatus, 0) == -1) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "transcode waitpid failed: %s", g_strerror(errno));
|
|
|
e660d2 |
+ } else if (WIFEXITED(wstatus)) {
|
|
|
e660d2 |
+ trc = WEXITSTATUS(wstatus);
|
|
|
e660d2 |
+ if (trc != 0) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "transcode process non-zero exit code %d", trc);
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ } else if (WIFSIGNALED(wstatus)) {
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "transcode process was terminated with a signal: %d",
|
|
|
e660d2 |
+ WTERMSIG(wstatus));
|
|
|
e660d2 |
+ } else {
|
|
|
e660d2 |
+ /* don't think this can happen, but covering all bases */
|
|
|
e660d2 |
+ g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ "transcode unhandled circumstance in waitpid");
|
|
|
e660d2 |
+ }
|
|
|
e660d2 |
+ target->writef = NULL;
|
|
|
e660d2 |
+ /* pid is only valid if writef is not NULL */
|
|
|
e660d2 |
+ /* target->pid = -1; */
|
|
|
e660d2 |
+}
|
|
|
e660d2 |
+
|
|
|
e660d2 |
/** Prepare next transfer
|
|
|
e660d2 |
*/
|
|
|
e660d2 |
static gboolean
|
|
|
e660d2 |
@@ -1509,6 +1644,9 @@ prepare_next_transfer(LrDownload *dd, gboolean *candidatefound, GError **err)
|
|
|
e660d2 |
target->f = open_target_file(target, err);
|
|
|
e660d2 |
if (!target->f)
|
|
|
e660d2 |
goto fail;
|
|
|
e660d2 |
+ maybe_transcode(target, err);
|
|
|
e660d2 |
+ if (!target->writef)
|
|
|
e660d2 |
+ goto fail;
|
|
|
e660d2 |
target->writecb_recieved = 0;
|
|
|
e660d2 |
target->writecb_required_range_written = FALSE;
|
|
|
e660d2 |
|
|
|
e660d2 |
@@ -1684,6 +1822,7 @@ prepare_next_transfer(LrDownload *dd, gboolean *candidatefound, GError **err)
|
|
|
e660d2 |
curl_easy_cleanup(target->curl_handle);
|
|
|
e660d2 |
target->curl_handle = NULL;
|
|
|
e660d2 |
}
|
|
|
e660d2 |
+ cleanup_transcode(target, err);
|
|
|
e660d2 |
if (target->f != NULL) {
|
|
|
e660d2 |
fclose(target->f);
|
|
|
e660d2 |
target->f = NULL;
|
|
|
e660d2 |
@@ -2254,6 +2393,8 @@ check_transfer_statuses(LrDownload *dd, GError **err)
|
|
|
e660d2 |
if (transfer_err) // Transfer was unsuccessful
|
|
|
e660d2 |
goto transfer_error;
|
|
|
e660d2 |
|
|
|
e660d2 |
+ cleanup_transcode(target, err);
|
|
|
e660d2 |
+
|
|
|
e660d2 |
//
|
|
|
e660d2 |
// Checksum checking
|
|
|
e660d2 |
//
|
|
|
e660d2 |
@@ -2348,6 +2489,7 @@ check_transfer_statuses(LrDownload *dd, GError **err)
|
|
|
e660d2 |
target->curl_handle = NULL;
|
|
|
e660d2 |
g_free(target->headercb_interrupt_reason);
|
|
|
e660d2 |
target->headercb_interrupt_reason = NULL;
|
|
|
e660d2 |
+ cleanup_transcode(target, err);
|
|
|
e660d2 |
fclose(target->f);
|
|
|
e660d2 |
target->f = NULL;
|
|
|
e660d2 |
if (target->curl_rqheaders) {
|
|
|
e660d2 |
@@ -2751,6 +2893,7 @@ lr_download(GSList *targets,
|
|
|
e660d2 |
curl_multi_remove_handle(dd.multi_handle, target->curl_handle);
|
|
|
e660d2 |
curl_easy_cleanup(target->curl_handle);
|
|
|
e660d2 |
target->curl_handle = NULL;
|
|
|
e660d2 |
+ cleanup_transcode(target, err);
|
|
|
e660d2 |
fclose(target->f);
|
|
|
e660d2 |
target->f = NULL;
|
|
|
e660d2 |
g_free(target->headercb_interrupt_reason);
|
|
|
e660d2 |
diff --git a/librepo/rcodes.h b/librepo/rcodes.h
|
|
|
e660d2 |
index dcbeb413..fd65bd60 100644
|
|
|
e660d2 |
--- a/librepo/rcodes.h
|
|
|
e660d2 |
+++ b/librepo/rcodes.h
|
|
|
e660d2 |
@@ -125,6 +125,8 @@ typedef enum {
|
|
|
e660d2 |
key/group not found, ...) */
|
|
|
e660d2 |
LRE_ZCK, /*!<
|
|
|
e660d2 |
(41) Zchunk error (error reading zchunk file, ...) */
|
|
|
e660d2 |
+ LRE_TRANSCODE, /*!<
|
|
|
e660d2 |
+ (42) Transcode error (env empty, ...) */
|
|
|
e660d2 |
LRE_UNKNOWNERROR, /*!<
|
|
|
e660d2 |
(xx) unknown error - sentinel of error codes enum */
|
|
|
e660d2 |
} LrRc; /*!< Return codes */
|
|
|
e660d2 |
|
|
|
e660d2 |
From cef644335d1c5068cd70cb93d2688b97663b3a8b Mon Sep 17 00:00:00 2001
|
|
|
e660d2 |
From: Matthew Almond <malmond@fb.com>
|
|
|
e660d2 |
Date: Fri, 29 Jan 2021 23:14:28 -0800
|
|
|
e660d2 |
Subject: [PATCH 2/2] Fix hard download error on 404 etc
|
|
|
e660d2 |
|
|
|
e660d2 |
I've observed errors when some mirrors are not completely synced. The
|
|
|
e660d2 |
library tries to download a file, but gets a 404 error. This means we
|
|
|
e660d2 |
need to retry with another mirror, not crash out. This was crashing
|
|
|
e660d2 |
because setting `err` in `clean_transcode()` was firing the assert at
|
|
|
e660d2 |
the start of `truncate_transfer_file()`. Note this failure mode was most
|
|
|
e660d2 |
common with 404's, but any transfer error could likely have turned
|
|
|
e660d2 |
fatal, for invalid reasons.
|
|
|
e660d2 |
|
|
|
e660d2 |
We use `cleanup_transcode()` in two contexts.
|
|
|
e660d2 |
|
|
|
e660d2 |
1. Within `check_transfer_statuses()`. The first call here happens
|
|
|
e660d2 |
during a normal download after `check_finished_transfer_status()`.
|
|
|
e660d2 |
The cleanup checks for errors, and any here will be flagged as a
|
|
|
e660d2 |
`transfer_err` (not a general, err).
|
|
|
e660d2 |
2. In 3 other places where an error has already occurred. We need to
|
|
|
e660d2 |
wait for the program to exit (and it should stop due to a SIGPIPE
|
|
|
e660d2 |
or short read from stdin), but we don't need to set an error because
|
|
|
e660d2 |
something already is handling one. It doesn't matter that the
|
|
|
e660d2 |
transcoder crashed because we're not going to use the output anyway,
|
|
|
e660d2 |
and we are likely to retry.
|
|
|
e660d2 |
---
|
|
|
e660d2 |
librepo/downloader.c | 24 ++++++++++++++----------
|
|
|
e660d2 |
1 file changed, 14 insertions(+), 10 deletions(-)
|
|
|
e660d2 |
|
|
|
e660d2 |
diff --git a/librepo/downloader.c b/librepo/downloader.c
|
|
|
e660d2 |
index 5326eb7a..39567a19 100644
|
|
|
e660d2 |
--- a/librepo/downloader.c
|
|
|
e660d2 |
+++ b/librepo/downloader.c
|
|
|
e660d2 |
@@ -1530,8 +1530,12 @@ maybe_transcode(LrTarget *target, GError **err)
|
|
|
e660d2 |
}
|
|
|
e660d2 |
|
|
|
e660d2 |
void
|
|
|
e660d2 |
-cleanup_transcode(LrTarget *target, GError **err)
|
|
|
e660d2 |
+cleanup_transcode(LrTarget *target, GError **transfer_err)
|
|
|
e660d2 |
{
|
|
|
e660d2 |
+ /** transfer_err can be NULL if we're using this to clean up a failed
|
|
|
e660d2 |
+ * transfer. In that circumstance g_set_error does nothing which is fine,
|
|
|
e660d2 |
+ * we don't need to pile on a second failure reason.
|
|
|
e660d2 |
+ */
|
|
|
e660d2 |
int wstatus, trc;
|
|
|
e660d2 |
if (!target->writef) {
|
|
|
e660d2 |
return;
|
|
|
e660d2 |
@@ -1541,21 +1545,21 @@ cleanup_transcode(LrTarget *target, GError **err)
|
|
|
e660d2 |
}
|
|
|
e660d2 |
fclose(target->writef);
|
|
|
e660d2 |
if(waitpid(target->pid, &wstatus, 0) == -1) {
|
|
|
e660d2 |
- g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ g_set_error(transfer_err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
"transcode waitpid failed: %s", g_strerror(errno));
|
|
|
e660d2 |
} else if (WIFEXITED(wstatus)) {
|
|
|
e660d2 |
trc = WEXITSTATUS(wstatus);
|
|
|
e660d2 |
if (trc != 0) {
|
|
|
e660d2 |
- g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ g_set_error(transfer_err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
"transcode process non-zero exit code %d", trc);
|
|
|
e660d2 |
}
|
|
|
e660d2 |
} else if (WIFSIGNALED(wstatus)) {
|
|
|
e660d2 |
- g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ g_set_error(transfer_err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
"transcode process was terminated with a signal: %d",
|
|
|
e660d2 |
WTERMSIG(wstatus));
|
|
|
e660d2 |
} else {
|
|
|
e660d2 |
/* don't think this can happen, but covering all bases */
|
|
|
e660d2 |
- g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
+ g_set_error(transfer_err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE,
|
|
|
e660d2 |
"transcode unhandled circumstance in waitpid");
|
|
|
e660d2 |
}
|
|
|
e660d2 |
target->writef = NULL;
|
|
|
e660d2 |
@@ -1822,7 +1826,7 @@ prepare_next_transfer(LrDownload *dd, gboolean *candidatefound, GError **err)
|
|
|
e660d2 |
curl_easy_cleanup(target->curl_handle);
|
|
|
e660d2 |
target->curl_handle = NULL;
|
|
|
e660d2 |
}
|
|
|
e660d2 |
- cleanup_transcode(target, err);
|
|
|
e660d2 |
+ cleanup_transcode(target, NULL);
|
|
|
e660d2 |
if (target->f != NULL) {
|
|
|
e660d2 |
fclose(target->f);
|
|
|
e660d2 |
target->f = NULL;
|
|
|
e660d2 |
@@ -2390,11 +2394,11 @@ check_transfer_statuses(LrDownload *dd, GError **err)
|
|
|
e660d2 |
if (!ret) // Error
|
|
|
e660d2 |
return FALSE;
|
|
|
e660d2 |
|
|
|
e660d2 |
+ cleanup_transcode(target, &transfer_err);
|
|
|
e660d2 |
+
|
|
|
e660d2 |
if (transfer_err) // Transfer was unsuccessful
|
|
|
e660d2 |
goto transfer_error;
|
|
|
e660d2 |
|
|
|
e660d2 |
- cleanup_transcode(target, err);
|
|
|
e660d2 |
-
|
|
|
e660d2 |
//
|
|
|
e660d2 |
// Checksum checking
|
|
|
e660d2 |
//
|
|
|
e660d2 |
@@ -2489,7 +2493,7 @@ check_transfer_statuses(LrDownload *dd, GError **err)
|
|
|
e660d2 |
target->curl_handle = NULL;
|
|
|
e660d2 |
g_free(target->headercb_interrupt_reason);
|
|
|
e660d2 |
target->headercb_interrupt_reason = NULL;
|
|
|
e660d2 |
- cleanup_transcode(target, err);
|
|
|
e660d2 |
+ cleanup_transcode(target, NULL);
|
|
|
e660d2 |
fclose(target->f);
|
|
|
e660d2 |
target->f = NULL;
|
|
|
e660d2 |
if (target->curl_rqheaders) {
|
|
|
e660d2 |
@@ -2893,7 +2897,7 @@ lr_download(GSList *targets,
|
|
|
e660d2 |
curl_multi_remove_handle(dd.multi_handle, target->curl_handle);
|
|
|
e660d2 |
curl_easy_cleanup(target->curl_handle);
|
|
|
e660d2 |
target->curl_handle = NULL;
|
|
|
e660d2 |
- cleanup_transcode(target, err);
|
|
|
e660d2 |
+ cleanup_transcode(target, NULL);
|
|
|
e660d2 |
fclose(target->f);
|
|
|
e660d2 |
target->f = NULL;
|
|
|
e660d2 |
g_free(target->headercb_interrupt_reason);
|