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