#include <stdio.h>
#include <ctime>
#include <sstream>
+#include <numeric>
#include <apti18n.h>
/*}}}*/
using namespace std;
-static void printHashSumComparision(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/
+static void printHashSumComparison(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/
{
if (_config->FindB("Debug::Acquire::HashSumMismatch", false) == false)
return;
return "";
}
/*}}}*/
+static std::string GetDiffIndexFileName(std::string const &Name) /*{{{*/
+{
+ return Name + ".diff/Index";
+}
+ /*}}}*/
+static std::string GetDiffIndexURI(IndexTarget const &Target) /*{{{*/
+{
+ return Target.URI + ".diff/Index";
+}
+ /*}}}*/
+
+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<char const*> 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)/*{{{*/
{
APT_CONST bool pkgAcqIndexDiffs::HashesRequired() const
{
- /* We don't always have the diff of the downloaded pdiff file.
- What we have for sure is hashes for the uncompressed file,
- but rred uncompresses them on the fly while parsing, so not handled here.
- Hashes are (also) checked while searching for (next) patch to apply. */
+ /* We can't check hashes of rred result as we don't know what the
+ hash of the file will be. We just know the hash of the patch(es),
+ the hash of the file they will apply on and the hash of the resulting
+ file. */
if (State == StateFetchDiff)
- return available_patches[0].download_hashes.empty() == false;
+ return true;
return false;
}
HashStringList pkgAcqIndexDiffs::GetExpectedHashes() const
we can check the rred result after all patches are applied as
we know the expected result rather than potentially apply more patches */
if (State == StateFetchDiff)
- return patch.download_hashes.empty() == false;
+ return true;
return State == StateApplyDiff;
}
HashStringList pkgAcqIndexMergeDiffs::GetExpectedHashes() const
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)
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 (OldBaseURI.empty() == false && 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
// Acquire::Item::GetFinalFilename and specialisations for child classes /*{{{*/
std::string pkgAcquire::Item::GetFinalFilename() const
{
+ // Beware: Desc.URI is modified by redirections
return GetFinalFileNameFromURI(Desc.URI);
}
std::string pkgAcqDiffIndex::GetFinalFilename() const
{
- // the logic we inherent from pkgAcqBaseIndex isn't what we need here
- return pkgAcquire::Item::GetFinalFilename();
+ return GetFinalFileNameFromURI(GetDiffIndexURI(Target));
}
std::string pkgAcqIndex::GetFinalFilename() const
{
}
std::string pkgAcqDiffIndex::GetMetaKey() const
{
- return Target.MetaKey + ".diff/Index";
+ return GetDiffIndexFileName(Target.MetaKey);
}
/*}}}*/
//pkgAcqTransactionItem::TransactionState and specialisations for child classes /*{{{*/
bool const Debug = _config->FindB("Debug::Acquire::Transaction", false);
switch(state)
{
+ case TransactionStarted: _error->Fatal("Item %s changed to invalid transaction start state!", Target.URI.c_str()); break;
case TransactionAbort:
if(Debug == true)
std::clog << " Cancel: " << DestFile << std::endl;
} 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;
switch (state)
{
+ case TransactionStarted: _error->Fatal("AcqIndex %s changed to invalid transaction start state!", Target.URI.c_str()); break;
case TransactionAbort:
if (Stage == STAGE_DECOMPRESS_AND_VERIFY)
{
break;
case TransactionCommit:
if (EraseFileName.empty() == false)
- RemoveFile("TransactionCommit", EraseFileName);
+ RemoveFile("AcqIndex::TransactionCommit", EraseFileName);
break;
}
return true;
switch (state)
{
+ case TransactionStarted: _error->Fatal("Item %s changed to invalid transaction start state!", Target.URI.c_str()); break;
case TransactionCommit:
break;
case TransactionAbort:
}
};
/*}}}*/
+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
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
}
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", "<none>") << 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;
{
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");
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
}
/*}}}*/
// 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<char const*> 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 /*{{{*/
}
/*}}}*/
+static void LoadLastMetaIndexParser(pkgAcqMetaClearSig * const TransactionManager, std::string const &FinalRelease, std::string const &FinalInRelease)/*{{{*/
+{
+ if (TransactionManager->IMSHit == true)
+ return;
+ if (RealFileExists(FinalInRelease) || RealFileExists(FinalRelease))
+ {
+ TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
+ if (TransactionManager->LastMetaIndexParser != NULL)
+ {
+ _error->PushToStack();
+ if (RealFileExists(FinalInRelease))
+ TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
+ else
+ TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
+ // its unlikely to happen, but if what we have is bad ignore it
+ if (_error->PendingError())
+ {
+ delete TransactionManager->LastMetaIndexParser;
+ TransactionManager->LastMetaIndexParser = NULL;
+ }
+ _error->RevertToStack();
+ }
+ }
+}
+ /*}}}*/
+
// AcqMetaBase - Constructor /*{{{*/
pkgAcqMetaBase::pkgAcqMetaBase(pkgAcquire * const Owner,
pkgAcqMetaClearSig * const TransactionManager,
IndexTarget const &DataTarget)
: pkgAcqTransactionItem(Owner, TransactionManager, DataTarget), d(NULL),
IndexTargets(IndexTargets),
- AuthPass(false), IMSHit(false)
+ AuthPass(false), IMSHit(false), State(TransactionStarted)
{
}
/*}}}*/
if(_config->FindB("Debug::Acquire::Transaction", false) == true)
std::clog << "AbortTransaction: " << TransactionManager << std::endl;
+ switch (TransactionManager->State)
+ {
+ case TransactionStarted: break;
+ case TransactionAbort: _error->Fatal("Transaction %s was already aborted and is aborted again", TransactionManager->Target.URI.c_str()); return;
+ case TransactionCommit: _error->Fatal("Transaction %s was already aborted and is now commited", TransactionManager->Target.URI.c_str()); return;
+ }
+ TransactionManager->State = TransactionAbort;
+
// ensure the toplevel is in error state too
for (std::vector<pkgAcqTransactionItem*>::iterator I = Transaction.begin();
I != Transaction.end(); ++I)
{
+ if ((*I)->Status != pkgAcquire::Item::StatFetching)
+ Owner->Dequeue(*I);
(*I)->TransactionState(TransactionAbort);
}
Transaction.clear();
if(_config->FindB("Debug::Acquire::Transaction", false) == true)
std::clog << "CommitTransaction: " << this << std::endl;
+ switch (TransactionManager->State)
+ {
+ case TransactionStarted: break;
+ case TransactionAbort: _error->Fatal("Transaction %s was already commited and is now aborted", TransactionManager->Target.URI.c_str()); return;
+ case TransactionCommit: _error->Fatal("Transaction %s was already commited and is again commited", TransactionManager->Target.URI.c_str()); return;
+ }
+ TransactionManager->State = TransactionCommit;
+
// move new files into place *and* remove files that are not
// part of the transaction but are still on disk
for (std::vector<pkgAcqTransactionItem*>::iterator I = Transaction.begin();
}
/*}}}*/
// 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;
}
/*}}}*/
// 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 if (APT::String::Endswith(I->Desc.URI, "Release"))
+ 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)
{
FinalInRelease = FinalFile.substr(0, FinalFile.length() - strlen("Release")) + "InRelease";
FinalRelease = FinalFile;
}
- if (RealFileExists(FinalInRelease) || RealFileExists(FinalRelease))
- {
- TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
- if (TransactionManager->LastMetaIndexParser != NULL)
- {
- _error->PushToStack();
- if (RealFileExists(FinalInRelease))
- TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
- else
- TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
- // its unlikely to happen, but if what we have is bad ignore it
- if (_error->PendingError())
- {
- delete TransactionManager->LastMetaIndexParser;
- TransactionManager->LastMetaIndexParser = NULL;
- }
- _error->RevertToStack();
- }
- }
+ LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
}
if (TransactionManager->MetaIndexParser->Load(DestFile, &ErrorText) == false)
// 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);
{
// 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;
// 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;
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;
+ }
}
}
if (filename.empty() == false)
{
new NoActionItem(Owner, *Target, filename);
- std::string const idxfilename = GetFinalFileNameFromURI(Target->URI + ".diff/Index");
+ std::string const idxfilename = GetFinalFileNameFromURI(GetDiffIndexURI(*Target));
if (FileExists(idxfilename))
new NoActionItem(Owner, *Target, idxfilename);
continue;
}
// check if we have patches available
- trypdiff &= TransactionManager->MetaIndexParser->Exists(Target->MetaKey + ".diff/Index");
+ trypdiff &= TransactionManager->MetaIndexParser->Exists(GetDiffIndexFileName(Target->MetaKey));
}
else
{
}
}
/*}}}*/
-bool pkgAcqMetaBase::VerifyVendor(string const &Message) /*{{{*/
+bool pkgAcqMetaBase::VerifyVendor(string const &) /*{{{*/
{
- string::size_type pos;
-
- // check for missing sigs (that where not fatal because otherwise we had
- // bombed earlier)
- string missingkeys;
- string msg = _("There is no public key available for the "
- "following key IDs:\n");
- pos = Message.find("NO_PUBKEY ");
- if (pos != std::string::npos)
- {
- string::size_type start = pos+strlen("NO_PUBKEY ");
- string Fingerprint = Message.substr(start, Message.find("\n")-start);
- missingkeys += (Fingerprint);
- }
- if(!missingkeys.empty())
- _error->Warning("%s", (msg + missingkeys).c_str());
-
string Transformed = TransactionManager->MetaIndexParser->GetExpectedDist();
if (Transformed == "../project/experimental")
Transformed = "experimental";
}
- pos = Transformed.rfind('/');
+ auto pos = Transformed.rfind('/');
if (pos != string::npos)
{
Transformed = Transformed.substr(0, pos);
return Header;
}
/*}}}*/
+void pkgAcqMetaClearSig::Finished() /*{{{*/
+{
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "Finished: " << DestFile <<std::endl;
+ if(TransactionManager != NULL && TransactionManager->State == TransactionStarted &&
+ TransactionManager->TransactionHasError() == false)
+ TransactionManager->CommitTransaction();
+}
+ /*}}}*/
bool pkgAcqMetaClearSig::VerifyDone(std::string const &Message, /*{{{*/
pkgAcquire::MethodConfig const * const Cnf)
{
string const FinalInRelease = GetFinalFilename();
Rename(DestFile, PartialRelease);
TransactionManager->TransactionStageCopy(this, PartialRelease, FinalRelease);
-
- if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
- {
- // open the last Release if we have it
- if (TransactionManager->IMSHit == false)
- {
- TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
- if (TransactionManager->LastMetaIndexParser != NULL)
- {
- _error->PushToStack();
- if (RealFileExists(FinalInRelease))
- TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
- else
- TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
- // its unlikely to happen, but if what we have is bad ignore it
- if (_error->PendingError())
- {
- delete TransactionManager->LastMetaIndexParser;
- TransactionManager->LastMetaIndexParser = NULL;
- }
- _error->RevertToStack();
- }
- }
- }
+ LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
// we parse the indexes here because at this point the user wanted
// a repository that may potentially harm him
}
}
/*}}}*/
-void pkgAcqMetaIndex::Finished() /*{{{*/
-{
- if(_config->FindB("Debug::Acquire::Transaction", false) == true)
- std::clog << "Finished: " << DestFile <<std::endl;
- if(TransactionManager != NULL &&
- TransactionManager->TransactionHasError() == false)
- TransactionManager->CommitTransaction();
-}
- /*}}}*/
std::string pkgAcqMetaIndex::DescURI() const /*{{{*/
{
return Target.URI;
// 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))
- {
- // open the last Release if we have it
- if (TransactionManager->IMSHit == false)
- {
- TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
- if (TransactionManager->LastMetaIndexParser != NULL)
- {
- _error->PushToStack();
- if (RealFileExists(FinalInRelease))
- TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
- else
- TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
- // its unlikely to happen, but if what we have is bad ignore it
- if (_error->PendingError())
- {
- delete TransactionManager->LastMetaIndexParser;
- TransactionManager->LastMetaIndexParser = NULL;
- }
- _error->RevertToStack();
- }
- }
- }
+ LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
// we parse the indexes here because at this point the user wanted
// a repository that may potentially harm him
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("<unknown>");
+ else
+ ErrorText.append(TimeRFC1123(timespec));
+ ErrorText.append("\n");
}
/*}}}*/
pkgAcqBaseIndex::~pkgAcqBaseIndex() {}
Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
Desc.Owner = this;
- Desc.Description = Target.Description + ".diff/Index";
+ Desc.Description = GetDiffIndexFileName(Target.Description);
Desc.ShortDesc = Target.ShortDesc;
- Desc.URI = Target.URI + ".diff/Index";
+ Desc.URI = GetDiffIndexURI(Target);
DestFile = GetPartialFileNameFromURI(Desc.URI);
if (Debug == true)
{
std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl;
- printHashSumComparision(CurrentPackagesFile, ServerHashes, TargetFileHashes);
+ printHashSumComparison(CurrentPackagesFile, ServerHashes, TargetFileHashes);
}
return false;
}
return false;
}
+ for (auto const &patch: available_patches)
+ if (patch.result_hashes.usable() == false ||
+ patch.patch_hashes.usable() == false ||
+ patch.download_hashes.usable() == false)
+ {
+ if (Debug)
+ std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": provides no usable hashes for " << patch.file
+ << " so fallback to complete download" << std::endl;
+ return false;
+ }
+
// patching with too many files is rather slow compared to a fast download
unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
if (fileLimit != 0 && fileLimit < available_patches.size())
}
// calculate the size of all patches we have to get
- // note that all sizes are uncompressed, while we download compressed files
- unsigned long long patchesSize = 0;
- for (std::vector<DiffInfo>::const_iterator cur = available_patches.begin();
- cur != available_patches.end(); ++cur)
- patchesSize += cur->patch_hashes.FileSize();
- unsigned long long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
- if (sizeLimit > 0 && (sizeLimit/100) < patchesSize)
- {
- if (Debug)
- std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
- << ") so fallback to complete download" << std::endl;
- return false;
+ unsigned short const sizeLimitPercent = _config->FindI("Acquire::PDiffs::SizeLimit", 100);
+ if (sizeLimitPercent > 0 && TransactionManager->MetaIndexParser != nullptr)
+ {
+ unsigned long long downloadSize = std::accumulate(available_patches.begin(),
+ available_patches.end(), 0llu, [](unsigned long long const T, DiffInfo const &I) {
+ return T + I.download_hashes.FileSize();
+ });
+ if (downloadSize != 0)
+ {
+ unsigned long long downloadSizeIdx = 0;
+ auto const types = VectorizeString(Target.Option(IndexTarget::COMPRESSIONTYPES), ' ');
+ for (auto const &t : types)
+ {
+ std::string MetaKey = Target.MetaKey;
+ if (t != "uncompressed")
+ MetaKey += '.' + t;
+ HashStringList const hsl = GetExpectedHashesFor(MetaKey);
+ if (unlikely(hsl.usable() == false))
+ continue;
+ downloadSizeIdx = hsl.FileSize();
+ break;
+ }
+ unsigned long long const sizeLimit = downloadSizeIdx * sizeLimitPercent;
+ if ((sizeLimit/100) < downloadSize)
+ {
+ if (Debug)
+ std::clog << "Need " << downloadSize << " compressed bytes (Limit is " << (sizeLimit/100) << ", "
+ << "original is " << downloadSizeIdx << ") so fallback to complete download" << std::endl;
+ return false;
+ }
+ }
}
// we have something, queue the diffs
// clean the plate
{
+ 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 PartialFile = GetPartialFileNameFromURI(Target.URI);
- std::vector<std::string> exts = APT::Configuration::getCompressorExtensions();
- for (auto const &ext : exts)
+ if (FileExists(PartialFile) && RemoveFile("Bootstrap-linking", PartialFile) == false)
+ {
+ if (Debug)
+ std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile
+ << " by removing stale " << PartialFile << " failed!" << std::endl;
+ return false;
+ }
+ for (auto const &ext : APT::Configuration::getCompressorExtensions())
{
std::string const Partial = PartialFile + ext;
- if (FileExists(Partial))
- RemoveFile("PDiffs-Bootstrap", Partial);
+ if (FileExists(Partial) && RemoveFile("Bootstrap-linking", Partial) == false)
+ {
+ if (Debug)
+ std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile
+ << " by removing stale " << Partial << " failed!" << std::endl;
+ return false;
+ }
}
- 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;
+ if (Debug)
+ std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile
+ << " by linking " << Final << " to " << Partial << " failed!" << std::endl;
return false;
}
}
/*}}}*/
void pkgAcqDiffIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
{
- Item::Failed(Message,Cnf);
+ pkgAcqBaseIndex::Failed(Message,Cnf);
Status = StatDone;
if(Debug)
/*}}}*/
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);
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
for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
I != allPatches->end(); ++I)
if ((*I)->State == StateErrorDiff)
+ {
+ State = StateErrorDiff;
return;
+ }
// first failure means we should fallback
State = StateErrorDiff;
if (Debug)
std::clog << "Falling back to normal index file acquire" << std::endl;
RenameOnError(PDiffError);
- std::string const patchname = GetPartialFileNameFromURI(Desc.URI);
- if (RealFileExists(patchname))
- Rename(patchname, patchname + ".FAILED");
+ if (RealFileExists(DestFile))
+ Rename(DestFile, DestFile + ".FAILED");
std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile))
Rename(UnpatchedFile, UnpatchedFile + ".FAILED");
Item::Done(Message, Hashes, Cnf);
+ if (std::any_of(allPatches->begin(), allPatches->end(),
+ [](pkgAcqIndexMergeDiffs const * const P) { return P->State == StateErrorDiff; }))
+ {
+ if(Debug)
+ std::clog << "Another patch failed already, no point in processing this one." << std::endl;
+ State = StateErrorDiff;
+ return;
+ }
+
std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI);
std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile);
+ if (UnpatchedFile.empty())
+ {
+ _error->Fatal("Unpatched file %s doesn't exist (anymore)!", UncompressedUnpatchedFile.c_str());
+ State = StateErrorDiff;
+ return;
+ }
std::string const PatchFile = GetMergeDiffsPatchFileName(UnpatchedFile, patch.file);
std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target);
DestFile = GetPartialFileNameFromURI(URI);
NextCompressionExtension(CurrentCompressionExtension, CompressionExtensions, false);
+ // store file size of the download to ensure the fetcher gives
+ // accurate progress reporting
+ FileSize = GetExpectedHashes().FileSize();
+
if (CurrentCompressionExtension == "uncompressed")
{
Desc.URI = URI;
// 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)
// 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
DestFile = "/dev/null";
}
- if (EraseFileName.empty())
+ if (EraseFileName.empty() && Filename != AltFilename)
EraseFileName = Filename;
// queue uri for the next stage
pkgAcqArchive::~pkgAcqArchive() {}
// AcqChangelog::pkgAcqChangelog - Constructors /*{{{*/
+class pkgAcqChangelog::Private
+{
+ public:
+ std::string FinalFile;
+};
pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::VerIterator const &Ver,
std::string const &DestDir, std::string const &DestFilename) :
- pkgAcquire::Item(Owner), d(NULL), SrcName(Ver.SourcePkgName()), SrcVersion(Ver.SourceVerStr())
+ pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(Ver.SourcePkgName()), SrcVersion(Ver.SourceVerStr())
{
Desc.URI = URI(Ver);
Init(DestDir, DestFilename);
pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::RlsFileIterator const &RlsFile,
char const * const Component, char const * const SrcName, char const * const SrcVersion,
const string &DestDir, const string &DestFilename) :
- pkgAcquire::Item(Owner), d(NULL), SrcName(SrcName), SrcVersion(SrcVersion)
+ pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
{
Desc.URI = URI(RlsFile, Component, SrcName, SrcVersion);
Init(DestDir, DestFilename);
pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner,
std::string const &URI, char const * const SrcName, char const * const SrcVersion,
const string &DestDir, const string &DestFilename) :
- pkgAcquire::Item(Owner), d(NULL), SrcName(SrcName), SrcVersion(SrcVersion)
+ pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
{
Desc.URI = URI;
Init(DestDir, DestFilename);
return;
}
- if (DestDir.empty())
+ std::string DestFileName;
+ if (DestFilename.empty())
+ DestFileName = flCombine(DestFile, SrcName + ".changelog");
+ else
+ DestFileName = flCombine(DestFile, DestFilename);
+
+ std::string const SandboxUser = _config->Find("APT::Sandbox::User");
+ std::string const systemTemp = GetTempDir(SandboxUser);
+ char tmpname[1000];
+ snprintf(tmpname, sizeof(tmpname), "%s/apt-changelog-XXXXXX", systemTemp.c_str());
+ if (NULL == mkdtemp(tmpname))
+ {
+ _error->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName.c_str(), SrcVersion.c_str());
+ Status = StatError;
+ return;
+ }
+ TemporaryDirectory = tmpname;
+
+ ChangeOwnerAndPermissionOfFile("Item::QueueURI", TemporaryDirectory.c_str(),
+ SandboxUser.c_str(), "root", 0700);
+
+ DestFile = flCombine(TemporaryDirectory, DestFileName);
+ if (DestDir.empty() == false)
{
- std::string const SandboxUser = _config->Find("APT::Sandbox::User");
- std::string const systemTemp = GetTempDir(SandboxUser);
- char tmpname[100];
- snprintf(tmpname, sizeof(tmpname), "%s/apt-changelog-XXXXXX", systemTemp.c_str());
- if (NULL == mkdtemp(tmpname))
+ d->FinalFile = flCombine(DestDir, DestFileName);
+ if (RealFileExists(d->FinalFile))
{
- _error->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName.c_str(), SrcVersion.c_str());
- Status = StatError;
- return;
+ FileFd file1, file2;
+ if (file1.Open(DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive) &&
+ file2.Open(d->FinalFile, FileFd::ReadOnly) && CopyFile(file2, file1))
+ {
+ struct timeval times[2];
+ times[0].tv_sec = times[1].tv_sec = file2.ModificationTime();
+ times[0].tv_usec = times[1].tv_usec = 0;
+ utimes(DestFile.c_str(), times);
+ }
}
- DestFile = TemporaryDirectory = tmpname;
-
- ChangeOwnerAndPermissionOfFile("Item::QueueURI", DestFile.c_str(),
- SandboxUser.c_str(), "root", 0700);
}
- else
- DestFile = DestDir;
-
- if (DestFilename.empty())
- DestFile = flCombine(DestFile, SrcName + ".changelog");
- else
- DestFile = flCombine(DestFile, DestFilename);
Desc.ShortDesc = "Changelog";
strprintf(Desc.Description, "%s %s %s Changelog", URI::SiteOnly(Desc.URI).c_str(), SrcName.c_str(), SrcVersion.c_str());
/*}}}*/
std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/
{
+ std::string const confOnline = "Acquire::Changelogs::AlwaysOnline";
+ bool AlwaysOnline = _config->FindB(confOnline, false);
+ if (AlwaysOnline == false)
+ for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
+ {
+ pkgCache::PkgFileIterator const PF = VF.File();
+ if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
+ continue;
+ pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
+ if (RF->Origin != 0 && _config->FindB(confOnline + "::Origin::" + RF.Origin(), false))
+ {
+ AlwaysOnline = true;
+ break;
+ }
+ }
+ if (AlwaysOnline == false)
+ {
+ pkgCache::PkgIterator const Pkg = Ver.ParentPkg();
+ if (Pkg->CurrentVer != 0 && Pkg.CurrentVer() == Ver)
+ {
+ std::string const basename = std::string("/usr/share/doc/") + Pkg.Name() + "/changelog";
+ std::string const debianname = basename + ".Debian";
+ if (FileExists(debianname))
+ return "copy://" + debianname;
+ else if (FileExists(debianname + ".gz"))
+ return "gzip://" + debianname + ".gz";
+ else if (FileExists(basename))
+ return "copy://" + basename;
+ else if (FileExists(basename + ".gz"))
+ return "gzip://" + basename + ".gz";
+ }
+ }
+
char const * const SrcName = Ver.SourcePkgName();
char const * const SrcVersion = Ver.SourceVerStr();
- pkgCache::PkgFileIterator PkgFile;
// find the first source for this version which promises a changelog
for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
{
pkgCache::PkgFileIterator const PF = VF.File();
if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
continue;
- PkgFile = PF;
pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
std::string const uri = URI(RF, PF.Component(), SrcName, SrcVersion);
if (uri.empty())
ErrorText = errText;
else
ErrorText = errText + " (" + ErrorText + ")";
- return;
}
/*}}}*/
// AcqChangelog::Done - Item downloaded OK /*{{{*/
pkgAcquire::MethodConfig const * const Cnf)
{
Item::Done(Message,CalcHashes,Cnf);
+ if (d->FinalFile.empty() == false)
+ {
+ if (RemoveFile("pkgAcqChangelog::Done", d->FinalFile) == false ||
+ Rename(DestFile, d->FinalFile) == false)
+ Status = StatError;
+ }
Complete = true;
}
RemoveFile("~pkgAcqChangelog", DestFile);
rmdir(TemporaryDirectory.c_str());
}
+ delete d;
}
/*}}}*/