]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/acquire-item.cc
allow pdiff bootstrap from all supported compressors
[apt.git] / apt-pkg / acquire-item.cc
index 6444a21b33d7d2146b401bec13458c742dadaaba..82ccaff64ebf59adfadb2c9ce48ccd9014560a1c 100644 (file)
@@ -123,28 +123,17 @@ static std::string GetDiffsPatchFileName(std::string const &Final)        /*{{{*/
    return Final + ".ed";
 }
                                                                        /*}}}*/
-static bool BootstrapPDiffWith(std::string const &PartialFile, std::string const &FinalFile, IndexTarget const &Target)/*{{{*/
+static std::string GetExistingFilename(std::string const &File)                /*{{{*/
 {
-   // patching needs to be bootstrapped with the 'old' version
-   std::vector<std::string> types = VectorizeString(Target.Option(IndexTarget::COMPRESSIONTYPES), ' ');
-   auto typeItr = types.cbegin();
-   for (; typeItr != types.cend(); ++typeItr)
+   if (RealFileExists(File))
+      return File;
+   for (auto const &type : APT::Configuration::getCompressorExtensions())
    {
-      std::string Final = FinalFile;
-      if (*typeItr != "uncompressed")
-        Final.append(".").append(*typeItr);
-      if (RealFileExists(Final) == false)
-        continue;
-      std::string Partial = PartialFile;
-      if (*typeItr != "uncompressed")
-        Partial.append(".").append(*typeItr);
-      if (FileExists(Partial.c_str()) == true)
-        return true;
-      if (symlink(Final.c_str(), Partial.c_str()) != 0)
-        return false;
-      break;
+      std::string const Final = File + type;
+      if (RealFileExists(Final))
+        return Final;
    }
-   return typeItr != types.cend();
+   return "";
 }
                                                                        /*}}}*/
 
@@ -153,12 +142,12 @@ static bool MessageInsecureRepository(bool const isError, std::string const &msg
    if (isError)
    {
       _error->Error("%s", msg.c_str());
-      _error->Notice("%s", _("Updating such a repository securily is impossible and therefore disabled by default."));
+      _error->Notice("%s", _("Updating from such a repository can't be done securely, and is therefore disabled by default."));
    }
    else
    {
       _error->Warning("%s", msg.c_str());
-      _error->Notice("%s", _("Data from such a repository can not be authenticated and is therefore potentially dangerous to use."));
+      _error->Notice("%s", _("Data from such a repository can't be authenticated and is therefore potentially dangerous to use."));
    }
    _error->Notice("%s", _("See apt-secure(8) manpage for repository creation and user configuration details."));
    return false;
@@ -388,16 +377,43 @@ bool pkgAcqTransactionItem::TransactionState(TransactionStates const state)
         }
         break;
       case TransactionCommit:
-        if(PartialFile != "")
+        if(PartialFile.empty() == false)
         {
-           if(Debug == true)
-              std::clog << "mv " << PartialFile << " -> "<< DestFile << " # " << DescURI() << std::endl;
+           if (PartialFile != DestFile)
+           {
+              // ensure that even without lists-cleanup all compressions are nuked
+              std::string FinalFile = GetFinalFileNameFromURI(Target.URI);
+              if (FileExists(FinalFile))
+              {
+                 if(Debug == true)
+                    std::clog << "rm " << FinalFile << " # " << DescURI() << std::endl;
+                 if (RemoveFile("TransactionStates-Cleanup", FinalFile) == false)
+                    return false;
+              }
+              for (auto const &ext: APT::Configuration::getCompressorExtensions())
+              {
+                 auto const Final = FinalFile + ext;
+                 if (FileExists(Final))
+                 {
+                    if(Debug == true)
+                       std::clog << "rm " << Final << " # " << DescURI() << std::endl;
+                    if (RemoveFile("TransactionStates-Cleanup", Final) == false)
+                       return false;
+                 }
+              }
+              if(Debug == true)
+                 std::clog << "mv " << PartialFile << " -> "<< DestFile << " # " << DescURI() << std::endl;
+              if (Rename(PartialFile, DestFile) == false)
+                 return false;
+           }
+           else if(Debug == true)
+              std::clog << "keep " << PartialFile << " # " << DescURI() << std::endl;
 
-           Rename(PartialFile, DestFile);
         } else {
            if(Debug == true)
               std::clog << "rm " << DestFile << " # " << DescURI() << std::endl;
-           RemoveFile("TransactionCommit", DestFile);
+           if (RemoveFile("TransactionCommit", DestFile) == false)
+              return false;
         }
         break;
    }
@@ -422,7 +438,7 @@ bool pkgAcqIndex::TransactionState(TransactionStates const state)
         {
            // keep the compressed file, but drop the decompressed
            EraseFileName.clear();
-           if (PartialFile.empty() == false && flExtension(PartialFile) == "decomp")
+           if (PartialFile.empty() == false && flExtension(PartialFile) != CurrentCompressionExtension)
               RemoveFile("TransactionAbort", PartialFile);
         }
         break;
@@ -1042,9 +1058,13 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify)                     /*{{{*/
       // download time, bandwidth and diskspace for nothing, BUT Debian doesn't feature all
       // in the set of supported architectures, so we can filter based on this property rather
       // than invent an entirely new flag we would need to carry for all of eternity.
-      if (Target->Option(IndexTarget::ARCHITECTURE) == "all" &&
-           TransactionManager->MetaIndexParser->IsArchitectureSupported("all") == false)
-        continue;
+      if (Target->Option(IndexTarget::ARCHITECTURE) == "all")
+      {
+        if (TransactionManager->MetaIndexParser->IsArchitectureSupported("all") == false)
+           continue;
+        if (TransactionManager->MetaIndexParser->IsArchitectureAllSupportedFor(*Target) == false)
+           continue;
+      }
 
       bool trypdiff = Target->OptionBool(IndexTarget::PDIFFS);
       if (verify == true)
@@ -1075,6 +1095,16 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify)                     /*{{{*/
            strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), Target->MetaKey.c_str());
            return;
         }
+        else
+        {
+           auto const hashes = GetExpectedHashesFor(Target->MetaKey);
+           if (hashes.usable() == false && hashes.empty() == false)
+           {
+              _error->Warning(_("Skipping acquire of configured file '%s' as repository '%s' provides only weak security information for it"),
+                       Target->MetaKey.c_str(), TransactionManager->Target.Description.c_str());
+              continue;
+           }
+        }
 
         // autoselect the compression method
         std::vector<std::string> types = VectorizeString(Target->Option(IndexTarget::COMPRESSIONTYPES), ' ');
@@ -1103,19 +1133,7 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify)                     /*{{{*/
         else
            Target->Options["COMPRESSIONTYPES"].clear();
 
-        std::string filename = GetFinalFileNameFromURI(Target->URI);
-        if (RealFileExists(filename) == false)
-        {
-           if (Target->KeepCompressed)
-           {
-              filename = GetKeepCompressedFileName(filename, *Target);
-              if (RealFileExists(filename) == false)
-                 filename.clear();
-           }
-           else
-              filename.clear();
-        }
-
+        std::string filename = GetExistingFilename(GetFinalFileNameFromURI(Target->URI));
         if (filename.empty() == false)
         {
            // if the Release file is a hit and we have an index it must be the current one
@@ -1152,19 +1170,7 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify)                     /*{{{*/
       else
       {
         // if we have no file to patch, no point in trying
-        std::string filename = GetFinalFileNameFromURI(Target->URI);
-        if (RealFileExists(filename) == false)
-        {
-           if (Target->KeepCompressed)
-           {
-              filename = GetKeepCompressedFileName(filename, *Target);
-              if (RealFileExists(filename) == false)
-                 filename.clear();
-           }
-           else
-              filename.clear();
-        }
-        trypdiff &= (filename.empty() == false);
+        trypdiff &= (GetExistingFilename(GetFinalFileNameFromURI(Target->URI)).empty() == false);
       }
 
       // no point in patching from local sources
@@ -1391,7 +1397,7 @@ void pkgAcqMetaClearSig::Failed(string const &Message,pkgAcquire::MethodConfig c
 
       // No Release file was present, or verification failed, so fall
       // back to queueing Packages files without verification
-      // only allow going further if the users explicitely wants it
+      // only allow going further if the user explicitly wants it
       if(AllowInsecureRepositories(_("The repository '%s' is not signed."), ClearsignedTarget.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
       {
         Status = StatDone;
@@ -1489,7 +1495,7 @@ void pkgAcqMetaIndex::Failed(string const &Message,
 
    // No Release file was present so fall
    // back to queueing Packages files without verification
-   // only allow going further if the users explicitely wants it
+   // only allow going further if the user explicitly wants it
    if(AllowInsecureRepositories(_("The repository '%s' does not have a Release file."), Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
    {
       // ensure old Release files are removed
@@ -1640,7 +1646,7 @@ void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const
    // ensures that a Release.gpg file in the lists/ is removed by the transaction
    TransactionManager->TransactionStageRemoval(this, DestFile);
 
-   // only allow going further if the users explicitely wants it
+   // only allow going further if the user explicitly wants it
    if (AllowInsecureRepositories(_("The repository '%s' is not signed."), MetaIndex->Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
    {
       if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
@@ -1837,10 +1843,18 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile)       /*{{{*/
       std::clog << "Server-Current: " << ServerHashes.find(NULL)->toStr() << " and we start at "
         << CurrentPackagesFile << " " << LocalHashes.FileSize() << " " << LocalHashes.find(NULL)->toStr() << std::endl;
 
+   // historically, older hashes have more info than newer ones, so start
+   // collecting with older ones first to avoid implementing complicated
+   // information merging techniques… a failure is after all always
+   // recoverable with a complete file and hashes aren't changed that often.
+   std::vector<char const *> types;
+   for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+      types.push_back(*type);
+
    // parse all of (provided) history
    vector<DiffInfo> available_patches;
    bool firstAcceptedHashes = true;
-   for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+   for (auto type = types.crbegin(); type != types.crend(); ++type)
    {
       if (LocalHashes.find(*type) == NULL)
         continue;
@@ -1898,7 +1912,7 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/
       return false;
    }
 
-   for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+   for (auto type = types.crbegin(); type != types.crend(); ++type)
    {
       if (LocalHashes.find(*type) == NULL)
         continue;
@@ -1938,7 +1952,7 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/
       }
    }
 
-   for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+   for (auto type = types.crbegin(); type != types.crend(); ++type)
    {
       std::string tagname = *type;
       tagname.append("-Download");
@@ -2043,6 +2057,28 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile)        /*{{{*/
       pdiff_merge = (precedence != "merged");
    }
 
+   // clean the plate
+   {
+      std::string const PartialFile = GetPartialFileNameFromURI(Target.URI);
+      std::vector<std::string> exts = APT::Configuration::getCompressorExtensions();
+      for (auto const &ext : exts)
+      {
+        std::string const Partial = PartialFile + ext;
+        if (FileExists(Partial))
+           RemoveFile("PDiffs-Bootstrap", Partial);
+      }
+      std::string const Final = GetExistingFilename(CurrentPackagesFile);
+      if (unlikely(Final.empty())) // because we wouldn't be called in such a case
+        return false;
+      std::string const Ext = Final.substr(CurrentPackagesFile.length());
+      std::string const Partial = PartialFile + Ext;
+      if (symlink(Final.c_str(), Partial.c_str()) != 0)
+      {
+        std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile << " by linking " << Final << " to " << Partial << " failed!" << std::endl;
+        return false;
+      }
+   }
+
    if (pdiff_merge == false)
       new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
    else
@@ -2137,13 +2173,6 @@ pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire * const Owner,
    }
    else
    {
-      if (BootstrapPDiffWith(GetPartialFileNameFromURI(Target.URI), GetFinalFilename(), Target) == false)
-      {
-        Failed("Bootstrapping of " + DestFile + " failed", NULL);
-        return;
-      }
-
-      // get the next diff
       State = StateFetchDiff;
       QueueNextDiff();
    }
@@ -2161,7 +2190,10 @@ void pkgAcqIndexDiffs::Failed(string const &Message,pkgAcquire::MethodConfig con
    RenameOnError(PDiffError);
    std::string const patchname = GetDiffsPatchFileName(DestFile);
    if (RealFileExists(patchname))
-      rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
+      Rename(patchname, patchname + ".FAILED");
+   std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
+   if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile))
+      Rename(UnpatchedFile, UnpatchedFile + ".FAILED");
    new pkgAcqIndex(Owner, TransactionManager, Target);
    Finish();
 }
@@ -2178,13 +2210,7 @@ void pkgAcqIndexDiffs::Finish(bool allDone)
    // the file will be cleaned
    if(allDone)
    {
-      std::string Final = GetFinalFilename();
-      if (Target.KeepCompressed)
-      {
-        std::string const ext = flExtension(DestFile);
-        if (ext.empty() == false)
-           Final.append(".").append(ext);
-      }
+      std::string const Final = GetKeepCompressedFileName(GetFinalFilename(), Target);
       TransactionManager->TransactionStageCopy(this, DestFile, Final);
 
       // this is for the "real" finish
@@ -2209,29 +2235,28 @@ void pkgAcqIndexDiffs::Finish(bool allDone)
 bool pkgAcqIndexDiffs::QueueNextDiff()                                 /*{{{*/
 {
    // calc sha1 of the just patched file
-   std::string const FinalFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
-   if(!FileExists(FinalFile))
+   std::string const PartialFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
+   if(unlikely(PartialFile.empty()))
    {
-      Failed("Message: No FinalFile " + FinalFile + " available", NULL);
+      Failed("Message: The file " + GetPartialFileNameFromURI(Target.URI) + " isn't available", NULL);
       return false;
    }
 
-   FileFd fd(FinalFile, FileFd::ReadOnly, FileFd::Extension);
+   FileFd fd(PartialFile, FileFd::ReadOnly, FileFd::Extension);
    Hashes LocalHashesCalc;
    LocalHashesCalc.AddFD(fd);
    HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
 
    if(Debug)
-      std::clog << "QueueNextDiff: " << FinalFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
+      std::clog << "QueueNextDiff: " << PartialFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
 
    HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
    if (unlikely(LocalHashes.usable() == false || TargetFileHashes.usable() == false))
    {
-      Failed("Local/Expected hashes are not usable", NULL);
+      Failed("Local/Expected hashes are not usable for " + PartialFile, NULL);
       return false;
    }
 
-
    // final file reached before all patches are applied
    if(LocalHashes == TargetFileHashes)
    {
@@ -2249,7 +2274,7 @@ bool pkgAcqIndexDiffs::QueueNextDiff()                                    /*{{{*/
    // error checking and falling back if no patch was found
    if(available_patches.empty() == true)
    {
-      Failed("No patches left to reach target", NULL);
+      Failed("No patches left to reach target for " + PartialFile, NULL);
       return false;
    }
 
@@ -2269,55 +2294,51 @@ bool pkgAcqIndexDiffs::QueueNextDiff()                                  /*{{{*/
 void pkgAcqIndexDiffs::Done(string const &Message, HashStringList const &Hashes,       /*{{{*/
                            pkgAcquire::MethodConfig const * const Cnf)
 {
-   if(Debug)
+   if (Debug)
       std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
 
    Item::Done(Message, Hashes, Cnf);
 
-   std::string const FinalFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
-   std::string const PatchFile = GetDiffsPatchFileName(FinalFile);
-
-   // success in downloading a diff, enter ApplyDiff state
-   if(State == StateFetchDiff)
-   {
-      Rename(DestFile, PatchFile);
-
-      if(Debug)
-        std::clog << "Sending to rred method: " << FinalFile << std::endl;
-
-      State = StateApplyDiff;
-      Local = true;
-      Desc.URI = "rred:" + FinalFile;
-      QueueURI(Desc);
-      SetActiveSubprocess("rred");
-      return;
-   }
+   std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI);
+   std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile);
+   std::string const PatchFile = GetDiffsPatchFileName(UnpatchedFile);
+   std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target);
 
-   // success in download/apply a diff, queue next (if needed)
-   if(State == StateApplyDiff)
+   switch (State)
    {
-      // remove the just applied patch
-      available_patches.erase(available_patches.begin());
-      RemoveFile("pkgAcqIndexDiffs::Done", PatchFile);
-
-      // move into place
-      if(Debug)
-      {
-        std::clog << "Moving patched file in place: " << std::endl
-                  << DestFile << " -> " << FinalFile << std::endl;
-      }
-      Rename(DestFile,FinalFile);
-      chmod(FinalFile.c_str(),0644);
-
-      // see if there is more to download
-      if(available_patches.empty() == false) {
-        new pkgAcqIndexDiffs(Owner, TransactionManager, Target,
-                             available_patches);
-        return Finish();
-      } else 
-         // update
-         DestFile = FinalFile;
-        return Finish(true);
+      // success in downloading a diff, enter ApplyDiff state
+      case StateFetchDiff:
+        Rename(DestFile, PatchFile);
+        DestFile = GetKeepCompressedFileName(UncompressedUnpatchedFile + "-patched", Target);
+        if(Debug)
+           std::clog << "Sending to rred method: " << UnpatchedFile << std::endl;
+        State = StateApplyDiff;
+        Local = true;
+        Desc.URI = "rred:" + UnpatchedFile;
+        QueueURI(Desc);
+        SetActiveSubprocess("rred");
+        return;
+      // success in download/apply a diff, queue next (if needed)
+      case StateApplyDiff:
+        // remove the just applied patch and base file
+        available_patches.erase(available_patches.begin());
+        RemoveFile("pkgAcqIndexDiffs::Done", PatchFile);
+        RemoveFile("pkgAcqIndexDiffs::Done", UnpatchedFile);
+        if(Debug)
+           std::clog << "Moving patched file in place: " << std::endl
+              << DestFile << " -> " << PatchedFile << std::endl;
+        Rename(DestFile, PatchedFile);
+
+        // see if there is more to download
+        if(available_patches.empty() == false)
+        {
+           new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
+           Finish();
+        } else {
+           DestFile = PatchedFile;
+           Finish(true);
+        }
+        return;
    }
 }
                                                                        /*}}}*/
@@ -2349,11 +2370,9 @@ pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire * const Owner,
    Desc.Owner = this;
    Description = Target.Description;
    Desc.ShortDesc = Target.ShortDesc;
-
    Desc.URI = Target.URI + ".diff/" + patch.file + ".gz";
-   Desc.Description = Description + " " + patch.file + string(".pdiff");
-
-   DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI + ".diff/" + patch.file), Target);
+   Desc.Description = Description + " " + patch.file + ".pdiff";
+   DestFile = GetPartialFileNameFromURI(Desc.URI);
 
    if(Debug)
       std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
@@ -2380,13 +2399,15 @@ void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfi
    State = StateErrorDiff;
    if (Debug)
       std::clog << "Falling back to normal index file acquire" << std::endl;
-   DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
    RenameOnError(PDiffError);
-   std::string const patchname = GetMergeDiffsPatchFileName(DestFile, patch.file);
+   std::string const patchname = GetPartialFileNameFromURI(Desc.URI);
    if (RealFileExists(patchname))
-      rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
-   new pkgAcqIndex(Owner, TransactionManager, Target);
+      Rename(patchname, patchname + ".FAILED");
+   std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
+   if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile))
+      Rename(UnpatchedFile, UnpatchedFile + ".FAILED");
    DestFile.clear();
+   new pkgAcqIndex(Owner, TransactionManager, Target);
 }
                                                                        /*}}}*/
 void pkgAcqIndexMergeDiffs::Done(string const &Message, HashStringList const &Hashes,  /*{{{*/
@@ -2397,67 +2418,58 @@ void pkgAcqIndexMergeDiffs::Done(string const &Message, HashStringList const &Ha
 
    Item::Done(Message, Hashes, Cnf);
 
-   std::string const UncompressedFinalFile = GetPartialFileNameFromURI(Target.URI);
-   std::string const FinalFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
-   if (State == StateFetchDiff)
-   {
-      Rename(DestFile, GetMergeDiffsPatchFileName(FinalFile, patch.file));
-
-      // check if this is the last completed diff
-      State = StateDoneDiff;
-      for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
-           I != allPatches->end(); ++I)
-        if ((*I)->State != StateDoneDiff)
-        {
-           if(Debug)
-              std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
-           return;
-        }
-
-      // this is the last completed diff, so we are ready to apply now
-      State = StateApplyDiff;
+   std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI);
+   std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile);
+   std::string const PatchFile = GetMergeDiffsPatchFileName(UnpatchedFile, patch.file);
+   std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target);
 
-      if (BootstrapPDiffWith(UncompressedFinalFile, GetFinalFilename(), Target) == false)
-      {
-        Failed("Bootstrapping of " + DestFile + " failed", NULL);
-        return;
-      }
-
-      if(Debug)
-        std::clog << "Sending to rred method: " << FinalFile << std::endl;
-
-      Local = true;
-      Desc.URI = "rred:" + FinalFile;
-      QueueURI(Desc);
-      SetActiveSubprocess("rred");
-      return;
-   }
-   // success in download/apply all diffs, clean up
-   else if (State == StateApplyDiff)
+   switch (State)
    {
-      // move the result into place
-      std::string const Final = GetKeepCompressedFileName(GetFinalFilename(), Target);
-      if(Debug)
-        std::clog << "Queue patched file in place: " << std::endl
-                  << DestFile << " -> " << Final << std::endl;
-
-      // queue for copy by the transaction manager
-      TransactionManager->TransactionStageCopy(this, DestFile, Final);
-
-      // ensure the ed's are gone regardless of list-cleanup
-      for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
-           I != allPatches->end(); ++I)
-      {
-        std::string const PartialFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
-        std::string const patch = GetMergeDiffsPatchFileName(PartialFile, (*I)->patch.file);
-        RemoveFile("pkgAcqIndexMergeDiffs::Done", patch);
-      }
-      RemoveFile("pkgAcqIndexMergeDiffs::Done", FinalFile);
+      case StateFetchDiff:
+        Rename(DestFile, PatchFile);
 
-      // all set and done
-      Complete = true;
-      if(Debug)
-        std::clog << "allDone: " << DestFile << "\n" << std::endl;
+        // check if this is the last completed diff
+        State = StateDoneDiff;
+        for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
+              I != allPatches->end(); ++I)
+           if ((*I)->State != StateDoneDiff)
+           {
+              if(Debug)
+                 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
+              return;
+           }
+        // this is the last completed diff, so we are ready to apply now
+        DestFile = GetKeepCompressedFileName(UncompressedUnpatchedFile + "-patched", Target);
+        if(Debug)
+           std::clog << "Sending to rred method: " << UnpatchedFile << std::endl;
+        State = StateApplyDiff;
+        Local = true;
+        Desc.URI = "rred:" + UnpatchedFile;
+        QueueURI(Desc);
+        SetActiveSubprocess("rred");
+        return;
+      case StateApplyDiff:
+        // success in download & apply all diffs, finialize and clean up
+        if(Debug)
+           std::clog << "Queue patched file in place: " << std::endl
+              << DestFile << " -> " << PatchedFile << std::endl;
+
+        // queue for copy by the transaction manager
+        TransactionManager->TransactionStageCopy(this, DestFile, GetKeepCompressedFileName(GetFinalFilename(), Target));
+
+        // ensure the ed's are gone regardless of list-cleanup
+        for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
+              I != allPatches->end(); ++I)
+           RemoveFile("pkgAcqIndexMergeDiffs::Done", GetMergeDiffsPatchFileName(UnpatchedFile, (*I)->patch.file));
+        RemoveFile("pkgAcqIndexMergeDiffs::Done", UnpatchedFile);
+
+        // all set and done
+        Complete = true;
+        if(Debug)
+           std::clog << "allDone: " << DestFile << "\n" << std::endl;
+        return;
+      case StateDoneDiff: _error->Fatal("Done called for %s which is in an invalid Done state", PatchFile.c_str()); break;
+      case StateErrorDiff: _error->Fatal("Done called for %s which is in an invalid Error state", PatchFile.c_str()); break;
    }
 }
                                                                        /*}}}*/
@@ -2659,7 +2671,8 @@ void pkgAcqIndex::StageDownloadDone(string const &Message, HashStringList const
    {
       Stage = STAGE_DECOMPRESS_AND_VERIFY;
       Local = true;
-      DestFile += ".decomp";
+      if (CurrentCompressionExtension != "uncompressed")
+        DestFile.erase(DestFile.length() - (CurrentCompressionExtension.length() + 1));
       Desc.URI = "copy:" + FileName;
       QueueURI(Desc);
       SetActiveSubprocess("copy");
@@ -2681,6 +2694,18 @@ void pkgAcqIndex::StageDownloadDone(string const &Message, HashStringList const
         SetActiveSubprocess("copy");
         return;
       }
+      else
+      {
+        // symlinking ensures that the filename can be used for compression detection
+        // that is e.g. needed for by-hash over file
+        if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
+           _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking file %s to %s failed", FileName.c_str(), DestFile.c_str());
+        else
+        {
+           EraseFileName = DestFile;
+           FileName = DestFile;
+        }
+      }
    }
    else
       EraseFileName = FileName;
@@ -2695,25 +2720,19 @@ void pkgAcqIndex::StageDownloadDone(string const &Message, HashStringList const
       return;
    }
 
-   // get the binary name for your used compression type
-   string decompProg;
-   if(CurrentCompressionExtension == "uncompressed")
-      decompProg = "copy";
-   else
-      decompProg = _config->Find(string("Acquire::CompressionTypes::").append(CurrentCompressionExtension),"");
-   if(decompProg.empty() == true)
-   {
-      _error->Error("Unsupported extension: %s", CurrentCompressionExtension.c_str());
-      return;
-   }
-
+   string decompProg = "store";
    if (Target.KeepCompressed == true)
    {
       DestFile = "/dev/null";
       EraseFileName.clear();
    }
    else
-      DestFile += ".decomp";
+   {
+      if (CurrentCompressionExtension == "uncompressed")
+        decompProg = "copy";
+      else
+        DestFile.erase(DestFile.length() - (CurrentCompressionExtension.length() + 1));
+   }
 
    // queue uri for the next stage
    Stage = STAGE_DECOMPRESS_AND_VERIFY;
@@ -3178,7 +3197,7 @@ std::string pkgAcqChangelog::URI(std::string const &Template,
         char const * const Component, char const * const SrcName,
         char const * const SrcVersion)
 {
-   if (Template.find("CHANGEPATH") == std::string::npos)
+   if (Template.find("@CHANGEPATH@") == std::string::npos)
       return "";
 
    // the path is: COMPONENT/SRC/SRCNAME/SRCNAME_SRCVER, e.g. main/a/apt/1.1 or contrib/liba/libapt/2.0
@@ -3190,7 +3209,7 @@ std::string pkgAcqChangelog::URI(std::string const &Template,
    if (Component != NULL && strlen(Component) != 0)
       path = std::string(Component) + "/" + path;
 
-   return SubstVar(Template, "CHANGEPATH", path);
+   return SubstVar(Template, "@CHANGEPATH@", path);
 }
                                                                        /*}}}*/
 // AcqChangelog::Failed - Failure handler                              /*{{{*/