X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/57f16d51f4158dce1a49f6d5f5f05f057125b871..9b8034a9fd40b4d05075fda719e61f6eb4c45678:/apt-pkg/acquire-item.cc?ds=sidebyside diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc index d820756ca..317db65b8 100644 --- a/apt-pkg/acquire-item.cc +++ b/apt-pkg/acquire-item.cc @@ -132,6 +132,49 @@ static std::string GetDiffIndexURI(IndexTarget const &Target) /*{{{*/ } /*}}}*/ +static void ReportMirrorFailureToCentral(pkgAcquire::Item const &I, std::string const &FailCode, std::string const &Details)/*{{{*/ +{ + // we only act if a mirror was used at all + if(I.UsedMirror.empty()) + return; +#if 0 + std::cerr << "\nReportMirrorFailure: " + << UsedMirror + << " Uri: " << DescURI() + << " FailCode: " + << FailCode << std::endl; +#endif + string const report = _config->Find("Methods::Mirror::ProblemReporting", + "/usr/lib/apt/apt-report-mirror-failure"); + if(!FileExists(report)) + return; + + std::vector const Args = { + report.c_str(), + I.UsedMirror.c_str(), + I.DescURI().c_str(), + FailCode.c_str(), + Details.c_str(), + NULL + }; + + pid_t pid = ExecFork(); + if(pid < 0) + { + _error->Error("ReportMirrorFailure Fork failed"); + return; + } + else if(pid == 0) + { + execvp(Args[0], (char**)Args.data()); + std::cerr << "Could not exec " << Args[0] << std::endl; + _exit(100); + } + if(!ExecWait(pid, "report-mirror-failure")) + _error->Warning("Couldn't report problem to '%s'", report.c_str()); +} + /*}}}*/ + static bool MessageInsecureRepository(bool const isError, std::string const &msg)/*{{{*/ { if (isError) @@ -281,6 +324,12 @@ bool pkgAcquire::Item::QueueURI(pkgAcquire::ItemDesc &Item) for a hashsum mismatch to happen which helps nobody) */ bool pkgAcqTransactionItem::QueueURI(pkgAcquire::ItemDesc &Item) { + if (TransactionManager->State != TransactionStarted) + { + if (_config->FindB("Debug::Acquire::Transaction", false)) + std::clog << "Skip " << Target.URI << " as transaction was already dealt with!" << std::endl; + return false; + } std::string const FinalFile = GetFinalFilename(); if (TransactionManager != NULL && TransactionManager->IMSHit == true && FileExists(FinalFile) == true) @@ -289,6 +338,21 @@ bool pkgAcqTransactionItem::QueueURI(pkgAcquire::ItemDesc &Item) Status = StatDone; return false; } + // If we got the InRelease file via a mirror, pick all indexes directly from this mirror, too + if (TransactionManager != nullptr && TransactionManager->BaseURI.empty() == false && + URI::SiteOnly(Item.URI) != URI::SiteOnly(TransactionManager->BaseURI)) + { + // this ensures we rewrite only once and only the first step + auto const OldBaseURI = Target.Option(IndexTarget::BASE_URI); + if (APT::String::Startswith(Item.URI, OldBaseURI)) + { + auto const ExtraPath = Item.URI.substr(OldBaseURI.length()); + Item.URI = flCombine(TransactionManager->BaseURI, ExtraPath); + UsedMirror = TransactionManager->UsedMirror; + if (Item.Description.find(" ") != string::npos) + Item.Description.replace(0, Item.Description.find(" "), UsedMirror); + } + } return pkgAcquire::Item::QueueURI(Item); } /* The transition manager InRelease itself (or its older sisters-in-law @@ -431,7 +495,7 @@ bool pkgAcqTransactionItem::TransactionState(TransactionStates const state) } else { if(Debug == true) std::clog << "rm " << DestFile << " # " << DescURI() << std::endl; - if (RemoveFile("TransactionCommit", DestFile) == false) + if (RemoveFile("TransItem::TransactionCommit", DestFile) == false) return false; } break; @@ -464,7 +528,7 @@ bool pkgAcqIndex::TransactionState(TransactionStates const state) break; case TransactionCommit: if (EraseFileName.empty() == false) - RemoveFile("TransactionCommit", EraseFileName); + RemoveFile("AcqIndex::TransactionCommit", EraseFileName); break; } return true; @@ -513,6 +577,41 @@ class APT_HIDDEN NoActionItem : public pkgAcquire::Item /*{{{*/ } }; /*}}}*/ +class APT_HIDDEN CleanupItem : public pkgAcqTransactionItem /*{{{*/ +/* This class ensures that a file which was configured but isn't downloaded + for various reasons isn't kept in an old version in the lists directory. + In a way its the reverse of NoActionItem as it helps with removing files + even if the lists-cleanup is deactivated. */ +{ + public: + virtual std::string DescURI() const APT_OVERRIDE {return Target.URI;}; + virtual HashStringList GetExpectedHashes() const APT_OVERRIDE {return HashStringList();}; + + CleanupItem(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager, IndexTarget const &Target) : + pkgAcqTransactionItem(Owner, TransactionManager, Target) + { + Status = StatDone; + DestFile = GetFinalFileNameFromURI(Target.URI); + } + bool TransactionState(TransactionStates const state) APT_OVERRIDE + { + switch (state) + { + case TransactionStarted: + break; + case TransactionAbort: + break; + case TransactionCommit: + if (_config->FindB("Debug::Acquire::Transaction", false) == true) + std::clog << "rm " << DestFile << " # " << DescURI() << std::endl; + if (RemoveFile("TransItem::TransactionCommit", DestFile) == false) + return false; + break; + } + return true; + } +}; + /*}}}*/ // Acquire::Item::Item - Constructor /*{{{*/ APT_IGNORE_DEPRECATED_PUSH @@ -566,8 +665,6 @@ APT_CONST bool pkgAcquire::Item::IsTrusted() const /*{{{*/ fetch this object */ void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf) { - if(ErrorText.empty()) - ErrorText = LookupTag(Message,"Message"); if (QueueCounter <= 1) { /* This indicates that the file is not available right now but might @@ -598,16 +695,63 @@ void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig con } string const FailReason = LookupTag(Message, "FailReason"); - if (FailReason == "MaximumSizeExceeded") - RenameOnError(MaximumSizeExceeded); + enum { MAXIMUM_SIZE_EXCEEDED, HASHSUM_MISMATCH, OTHER } failreason = OTHER; + if ( FailReason == "MaximumSizeExceeded") + failreason = MAXIMUM_SIZE_EXCEEDED; else if (Status == StatAuthError) - RenameOnError(HashSumMismatch); + failreason = HASHSUM_MISMATCH; + + if(ErrorText.empty()) + { + if (Status == StatAuthError) + { + std::ostringstream out; + switch (failreason) + { + case HASHSUM_MISMATCH: + out << _("Hash Sum mismatch") << std::endl; + break; + case MAXIMUM_SIZE_EXCEEDED: + case OTHER: + out << LookupTag(Message, "Message") << std::endl; + break; + } + auto const ExpectedHashes = GetExpectedHashes(); + if (ExpectedHashes.empty() == false) + { + out << "Hashes of expected file:" << std::endl; + for (auto const &hs: ExpectedHashes) + out << " - " << hs.toStr() << std::endl; + } + if (failreason == HASHSUM_MISMATCH) + { + out << "Hashes of received file:" << std::endl; + for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type) + { + std::string const tagname = std::string(*type) + "-Hash"; + std::string const hashsum = LookupTag(Message, tagname.c_str()); + if (hashsum.empty() == false) + out << " - " << HashString(*type, hashsum).toStr() << std::endl; + } + out << "Last modification reported: " << LookupTag(Message, "Last-Modified", "") << std::endl; + } + ErrorText = out.str(); + } + else + ErrorText = LookupTag(Message,"Message"); + } + + switch (failreason) + { + case MAXIMUM_SIZE_EXCEEDED: RenameOnError(MaximumSizeExceeded); break; + case HASHSUM_MISMATCH: RenameOnError(HashSumMismatch); break; + case OTHER: break; + } - // report mirror failure back to LP if we actually use a mirror if (FailReason.empty() == false) - ReportMirrorFailure(FailReason); + ReportMirrorFailureToCentral(*this, FailReason, ErrorText); else - ReportMirrorFailure(ErrorText); + ReportMirrorFailureToCentral(*this, ErrorText, ErrorText); if (QueueCounter > 1) Status = StatIdle; @@ -695,13 +839,10 @@ bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const { case HashSumMismatch: errtext = _("Hash Sum mismatch"); - Status = StatAuthError; - ReportMirrorFailure("HashChecksumFailure"); break; case SizeMismatch: errtext = _("Size mismatch"); Status = StatAuthError; - ReportMirrorFailure("SizeFailure"); break; case InvalidFormat: errtext = _("Invalid file format"); @@ -718,7 +859,6 @@ bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const break; case MaximumSizeExceeded: // the method is expected to report a good error for this - Status = StatError; break; case PDiffError: // no handling here, done by callers @@ -736,47 +876,9 @@ void pkgAcquire::Item::SetActiveSubprocess(const std::string &subprocess)/*{{{*/ } /*}}}*/ // Acquire::Item::ReportMirrorFailure /*{{{*/ -void pkgAcquire::Item::ReportMirrorFailure(string const &FailCode) +void pkgAcquire::Item::ReportMirrorFailure(std::string const &FailCode) { - // we only act if a mirror was used at all - if(UsedMirror.empty()) - return; -#if 0 - std::cerr << "\nReportMirrorFailure: " - << UsedMirror - << " Uri: " << DescURI() - << " FailCode: " - << FailCode << std::endl; -#endif - string report = _config->Find("Methods::Mirror::ProblemReporting", - "/usr/lib/apt/apt-report-mirror-failure"); - if(!FileExists(report)) - return; - - std::vector Args; - Args.push_back(report.c_str()); - Args.push_back(UsedMirror.c_str()); - Args.push_back(DescURI().c_str()); - Args.push_back(FailCode.c_str()); - Args.push_back(NULL); - - pid_t pid = ExecFork(); - if(pid < 0) - { - _error->Error("ReportMirrorFailure Fork failed"); - return; - } - else if(pid == 0) - { - execvp(Args[0], (char**)Args.data()); - std::cerr << "Could not exec " << Args[0] << std::endl; - _exit(100); - } - if(!ExecWait(pid, "report-mirror-failure")) - { - _error->Warning("Couldn't report problem to '%s'", - _config->Find("Methods::Mirror::ProblemReporting").c_str()); - } + ReportMirrorFailureToCentral(*this, FailCode, FailCode); } /*}}}*/ std::string pkgAcquire::Item::HashSum() const /*{{{*/ @@ -866,6 +968,8 @@ void pkgAcqMetaBase::AbortTransaction() for (std::vector::iterator I = Transaction.begin(); I != Transaction.end(); ++I) { + if ((*I)->Status != pkgAcquire::Item::StatFetching) + Owner->Dequeue(*I); (*I)->TransactionState(TransactionAbort); } Transaction.clear(); @@ -931,37 +1035,36 @@ void pkgAcqMetaBase::TransactionStageRemoval(pkgAcqTransactionItem * const I, } /*}}}*/ // AcqMetaBase::GenerateAuthWarning - Check gpg authentication error /*{{{*/ +/* This method is called from ::Failed handlers. If it returns true, + no fallback to other files or modi is performed */ bool pkgAcqMetaBase::CheckStopAuthentication(pkgAcquire::Item * const I, const std::string &Message) { - // FIXME: this entire function can do now that we disallow going to - // a unauthenticated state and can cleanly rollback - string const Final = I->GetFinalFilename(); - if(FileExists(Final)) + std::string const GPGError = LookupTag(Message, "Message"); + if (FileExists(Final)) { I->Status = StatTransientNetworkError; - _error->Warning(_("An error occurred during the signature " - "verification. The repository is not updated " - "and the previous index files will be used. " - "GPG error: %s: %s"), - Desc.Description.c_str(), - LookupTag(Message,"Message").c_str()); + _error->Warning(_("An error occurred during the signature verification. " + "The repository is not updated and the previous index files will be used. " + "GPG error: %s: %s"), + Desc.Description.c_str(), + GPGError.c_str()); RunScripts("APT::Update::Auth-Failure"); return true; } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) { /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */ _error->Error(_("GPG error: %s: %s"), - Desc.Description.c_str(), - LookupTag(Message,"Message").c_str()); + Desc.Description.c_str(), + GPGError.c_str()); I->Status = StatAuthError; return true; } else { _error->Warning(_("GPG error: %s: %s"), - Desc.Description.c_str(), - LookupTag(Message,"Message").c_str()); + Desc.Description.c_str(), + GPGError.c_str()); } // gpgv method failed - ReportMirrorFailure("GPGFailure"); + ReportMirrorFailureToCentral(*this, "GPGFailure", GPGError); return false; } /*}}}*/ @@ -999,6 +1102,15 @@ bool pkgAcqMetaBase::CheckDownloadDone(pkgAcqTransactionItem * const I, const st // We have just finished downloading a Release file (it is not // verified yet) + // Save the final base URI we got this Release file from + if (I->UsedMirror.empty() == false && _config->FindB("Acquire::SameMirrorForAllIndexes", true)) + { + if (APT::String::Endswith(I->Desc.URI, "InRelease")) + TransactionManager->BaseURI = I->Desc.URI.substr(0, I->Desc.URI.length() - strlen("InRelease")); + else + TransactionManager->BaseURI = I->Desc.URI.substr(0, I->Desc.URI.length() - strlen("Release")); + } + std::string const FileName = LookupTag(Message,"Filename"); if (FileName != I->DestFile && RealFileExists(I->DestFile) == false) { @@ -1105,10 +1217,12 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify) /*{{{*/ // than invent an entirely new flag we would need to carry for all of eternity. if (Target->Option(IndexTarget::ARCHITECTURE) == "all") { - if (TransactionManager->MetaIndexParser->IsArchitectureSupported("all") == false) - continue; - if (TransactionManager->MetaIndexParser->IsArchitectureAllSupportedFor(*Target) == false) + if (TransactionManager->MetaIndexParser->IsArchitectureSupported("all") == false || + TransactionManager->MetaIndexParser->IsArchitectureAllSupportedFor(*Target) == false) + { + new CleanupItem(Owner, TransactionManager, *Target); continue; + } } bool trypdiff = Target->OptionBool(IndexTarget::PDIFFS); @@ -1118,13 +1232,17 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify) /*{{{*/ { // optional targets that we do not have in the Release file are skipped if (Target->IsOptional) + { + new CleanupItem(Owner, TransactionManager, *Target); continue; + } std::string const &arch = Target->Option(IndexTarget::ARCHITECTURE); if (arch.empty() == false) { if (TransactionManager->MetaIndexParser->IsArchitectureSupported(arch) == false) { + new CleanupItem(Owner, TransactionManager, *Target); _error->Notice(_("Skipping acquire of configured file '%s' as repository '%s' doesn't support architecture '%s'"), Target->MetaKey.c_str(), TransactionManager->Target.Description.c_str(), arch.c_str()); continue; @@ -1133,7 +1251,10 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify) /*{{{*/ // ignore silently as this is pretty much the same as just shipping an empty file. // if we don't know which architectures are supported, we do NOT ignore it to notify user about this if (TransactionManager->MetaIndexParser->IsArchitectureSupported("*undefined*") == false) + { + new CleanupItem(Owner, TransactionManager, *Target); continue; + } } Status = StatAuthError; @@ -1143,11 +1264,21 @@ void pkgAcqMetaBase::QueueIndexes(bool const verify) /*{{{*/ else { auto const hashes = GetExpectedHashesFor(Target->MetaKey); - if (hashes.usable() == false && hashes.empty() == false) + if (hashes.empty() == false) { - _error->Warning(_("Skipping acquire of configured file '%s' as repository '%s' provides only weak security information for it"), + if (hashes.usable() == false) + { + new CleanupItem(Owner, TransactionManager, *Target); + _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; + continue; + } + // empty files are skipped as acquiring the very small compressed files is a waste of time + else if (hashes.FileSize() == 0) + { + new CleanupItem(Owner, TransactionManager, *Target); + continue; + } } } @@ -1701,6 +1832,22 @@ pkgAcqBaseIndex::pkgAcqBaseIndex(pkgAcquire * const Owner, IndexTarget const &Target) : pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL) { +} + /*}}}*/ +void pkgAcqBaseIndex::Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/ +{ + pkgAcquire::Item::Failed(Message, Cnf); + if (TransactionManager == nullptr || TransactionManager->MetaIndexParser == nullptr || + Status != StatAuthError) + return; + + ErrorText.append("Release file created at: "); + auto const timespec = TransactionManager->MetaIndexParser->GetDate(); + if (timespec == 0) + ErrorText.append(""); + else + ErrorText.append(TimeRFC1123(timespec)); + ErrorText.append("\n"); } /*}}}*/ pkgAcqBaseIndex::~pkgAcqBaseIndex() {} @@ -2142,7 +2289,7 @@ bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/ /*}}}*/ void pkgAcqDiffIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/ { - Item::Failed(Message,Cnf); + pkgAcqBaseIndex::Failed(Message,Cnf); Status = StatDone; if(Debug) @@ -2223,7 +2370,7 @@ pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire * const Owner, /*}}}*/ void pkgAcqIndexDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/ { - Item::Failed(Message,Cnf); + pkgAcqBaseIndex::Failed(Message,Cnf); Status = StatDone; DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target); @@ -2428,7 +2575,7 @@ void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfi if(Debug) std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl; - Item::Failed(Message,Cnf); + pkgAcqBaseIndex::Failed(Message,Cnf); Status = StatDone; // check if we are the first to fail, otherwise we are done here @@ -2666,7 +2813,7 @@ string pkgAcqIndex::Custom600Headers() const // AcqIndex::Failed - getting the indexfile failed /*{{{*/ void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf) { - Item::Failed(Message,Cnf); + pkgAcqBaseIndex::Failed(Message,Cnf); // authorisation matches will not be fixed by other compression types if (Status != StatAuthError) @@ -2740,9 +2887,8 @@ void pkgAcqIndex::StageDownloadDone(string const &Message) // methods like file:// give us an alternative (uncompressed) file else if (Target.KeepCompressed == false && AltFilename.empty() == false) { - if (CurrentCompressionExtension != "uncompressed") - DestFile.erase(DestFile.length() - (CurrentCompressionExtension.length() + 1)); Filename = AltFilename; + EraseFileName.clear(); } // Methods like e.g. "file:" will give us a (compressed) FileName that is // not the "DestFile" we set, in this case we uncompress from the local file @@ -2772,7 +2918,7 @@ void pkgAcqIndex::StageDownloadDone(string const &Message) DestFile = "/dev/null"; } - if (EraseFileName.empty()) + if (EraseFileName.empty() && Filename != AltFilename) EraseFileName = Filename; // queue uri for the next stage