#include <apt-pkg/fileutl.h>
#include <apt-pkg/sha1.h>
#include <apt-pkg/tagfile.h>
-#include <apt-pkg/indexrecords.h>
+#include <apt-pkg/metaindex.h>
#include <apt-pkg/acquire.h>
#include <apt-pkg/hashes.h>
#include <apt-pkg/indexfile.h>
#include <apt-pkg/pkgcache.h>
#include <apt-pkg/cacheiterators.h>
#include <apt-pkg/pkgrecords.h>
+#include <apt-pkg/gpgv.h>
#include <stddef.h>
#include <stdlib.h>
using namespace std;
+static void printHashSumComparision(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/
+{
+ if (_config->FindB("Debug::Acquire::HashSumMismatch", false) == false)
+ return;
+ std::cerr << std::endl << URI << ":" << std::endl << " Expected Hash: " << std::endl;
+ for (HashStringList::const_iterator hs = Expected.begin(); hs != Expected.end(); ++hs)
+ std::cerr << "\t- " << hs->toStr() << std::endl;
+ std::cerr << " Actual Hash: " << std::endl;
+ for (HashStringList::const_iterator hs = Actual.begin(); hs != Actual.end(); ++hs)
+ std::cerr << "\t- " << hs->toStr() << std::endl;
+}
+ /*}}}*/
+static std::string GetPartialFileName(std::string const &file) /*{{{*/
+{
+ std::string DestFile = _config->FindDir("Dir::State::lists") + "partial/";
+ DestFile += file;
+ return DestFile;
+}
+ /*}}}*/
+static std::string GetPartialFileNameFromURI(std::string const &uri) /*{{{*/
+{
+ return GetPartialFileName(URItoFileName(uri));
+}
+ /*}}}*/
+static std::string GetFinalFileNameFromURI(std::string const &uri) /*{{{*/
+{
+ return _config->FindDir("Dir::State::lists") + URItoFileName(uri);
+}
+ /*}}}*/
+static std::string GetCompressedFileName(IndexTarget const &Target, std::string const &Name, std::string const &Ext) /*{{{*/
+{
+ if (Ext.empty() || Ext == "uncompressed")
+ return Name;
+
+ // do not reverify cdrom sources as apt-cdrom may rewrite the Packages
+ // file when its doing the indexcopy
+ if (Target.URI.substr(0,6) == "cdrom:")
+ return Name;
+
+ // adjust DestFile if its compressed on disk
+ if (Target.KeepCompressed == true)
+ return Name + '.' + Ext;
+ return Name;
+}
+ /*}}}*/
+static std::string GetMergeDiffsPatchFileName(std::string const &Final, std::string const &Patch)/*{{{*/
+{
+ // rred expects the patch as $FinalFile.ed.$patchname.gz
+ return Final + ".ed." + Patch + ".gz";
+}
+ /*}}}*/
+static std::string GetDiffsPatchFileName(std::string const &Final) /*{{{*/
+{
+ // rred expects the patch as $FinalFile.ed
+ return Final + ".ed";
+}
+ /*}}}*/
+
+static bool AllowInsecureRepositories(metaIndex const * const MetaIndexParser, pkgAcqMetaClearSig * const TransactionManager, pkgAcquire::Item * const I) /*{{{*/
+{
+ if(MetaIndexParser->GetTrusted() == metaIndex::TRI_YES || _config->FindB("Acquire::AllowInsecureRepositories") == true)
+ return true;
+
+ _error->Error(_("Use --allow-insecure-repositories to force the update"));
+ TransactionManager->AbortTransaction();
+ I->Status = pkgAcquire::Item::StatError;
+ return false;
+}
+ /*}}}*/
+static HashStringList GetExpectedHashesFromFor(metaIndex * const Parser, std::string const &MetaKey)/*{{{*/
+{
+ if (Parser == NULL)
+ return HashStringList();
+ metaIndex::checkSum * const R = Parser->Lookup(MetaKey);
+ if (R == NULL)
+ return HashStringList();
+ return R->Hashes;
+}
+ /*}}}*/
+
+// all ::HashesRequired and ::GetExpectedHashes implementations /*{{{*/
+/* ::GetExpectedHashes is abstract and has to be implemented by all subclasses.
+ It is best to implement it as broadly as possible, while ::HashesRequired defaults
+ to true and should be as restrictive as possible for false cases. Note that if
+ a hash is returned by ::GetExpectedHashes it must match. Only if it doesn't
+ ::HashesRequired is called to evaluate if its okay to have no hashes. */
+APT_CONST bool pkgAcqTransactionItem::HashesRequired() const
+{
+ /* signed repositories obviously have a parser and good hashes.
+ unsigned repositories, too, as even if we can't trust them for security,
+ we can at least trust them for integrity of the download itself.
+ Only repositories without a Release file can (obviously) not have
+ hashes – and they are very uncommon and strongly discouraged */
+ return TransactionManager->MetaIndexParser != NULL &&
+ TransactionManager->MetaIndexParser->GetLoadedSuccessfully() != metaIndex::TRI_UNSET;
+}
+HashStringList pkgAcqTransactionItem::GetExpectedHashes() const
+{
+ return GetExpectedHashesFor(GetMetaKey());
+}
+
+APT_CONST bool pkgAcqMetaBase::HashesRequired() const
+{
+ // Release and co have no hashes 'by design'.
+ return false;
+}
+HashStringList pkgAcqMetaBase::GetExpectedHashes() const
+{
+ return HashStringList();
+}
+
+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. */
+ if (State == StateFetchDiff)
+ return available_patches[0].download_hashes.empty() == false;
+ return false;
+}
+HashStringList pkgAcqIndexDiffs::GetExpectedHashes() const
+{
+ if (State == StateFetchDiff)
+ return available_patches[0].download_hashes;
+ return HashStringList();
+}
+
+APT_CONST bool pkgAcqIndexMergeDiffs::HashesRequired() const
+{
+ /* @see #pkgAcqIndexDiffs::HashesRequired, with the difference that
+ 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 State == StateApplyDiff;
+}
+HashStringList pkgAcqIndexMergeDiffs::GetExpectedHashes() const
+{
+ if (State == StateFetchDiff)
+ return patch.download_hashes;
+ else if (State == StateApplyDiff)
+ return GetExpectedHashesFor(Target.MetaKey);
+ return HashStringList();
+}
+
+APT_CONST bool pkgAcqArchive::HashesRequired() const
+{
+ return LocalSource == false;
+}
+HashStringList pkgAcqArchive::GetExpectedHashes() const
+{
+ // figured out while parsing the records
+ return ExpectedHashes;
+}
+
+APT_CONST bool pkgAcqFile::HashesRequired() const
+{
+ // supplied as parameter at creation time, so the caller decides
+ return ExpectedHashes.usable();
+}
+HashStringList pkgAcqFile::GetExpectedHashes() const
+{
+ return ExpectedHashes;
+}
+ /*}}}*/
+// Acquire::Item::QueueURI and specialisations from child classes /*{{{*/
+bool pkgAcquire::Item::QueueURI(pkgAcquire::ItemDesc &Item)
+{
+ Owner->Enqueue(Item);
+ return true;
+}
+/* The idea here is that an item isn't queued if it exists on disk and the
+ transition manager was a hit as this means that the files it contains
+ the checksums for can't be updated either (or they are and we are asking
+ for a hashsum mismatch to happen which helps nobody) */
+bool pkgAcqTransactionItem::QueueURI(pkgAcquire::ItemDesc &Item)
+{
+ std::string const FinalFile = GetFinalFilename();
+ if (TransactionManager != NULL && TransactionManager->IMSHit == true &&
+ FileExists(FinalFile) == true)
+ {
+ PartialFile = DestFile = FinalFile;
+ Status = StatDone;
+ return false;
+ }
+ return pkgAcquire::Item::QueueURI(Item);
+}
+/* The transition manager InRelease itself (or its older sisters-in-law
+ Release & Release.gpg) is always queued as this allows us to rerun gpgv
+ on it to verify that we aren't stalled with old files */
+bool pkgAcqMetaBase::QueueURI(pkgAcquire::ItemDesc &Item)
+{
+ return pkgAcquire::Item::QueueURI(Item);
+}
+/* the Diff/Index needs to queue also the up-to-date complete index file
+ to ensure that the list cleaner isn't eating it */
+bool pkgAcqDiffIndex::QueueURI(pkgAcquire::ItemDesc &Item)
+{
+ if (pkgAcqTransactionItem::QueueURI(Item) == true)
+ return true;
+ QueueOnIMSHit();
+ return false;
+}
+ /*}}}*/
+// Acquire::Item::GetFinalFilename and specialisations for child classes /*{{{*/
+std::string pkgAcquire::Item::GetFinalFilename() const
+{
+ 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();
+}
+std::string pkgAcqIndex::GetFinalFilename() const
+{
+ std::string const FinalFile = GetFinalFileNameFromURI(Target.URI);
+ return GetCompressedFileName(Target, FinalFile, CurrentCompressionExtension);
+}
+std::string pkgAcqMetaSig::GetFinalFilename() const
+{
+ return GetFinalFileNameFromURI(Target.URI);
+}
+std::string pkgAcqBaseIndex::GetFinalFilename() const
+{
+ return GetFinalFileNameFromURI(Target.URI);
+}
+std::string pkgAcqMetaBase::GetFinalFilename() const
+{
+ return GetFinalFileNameFromURI(Target.URI);
+}
+std::string pkgAcqArchive::GetFinalFilename() const
+{
+ return _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
+}
+ /*}}}*/
+// pkgAcqTransactionItem::GetMetaKey and specialisations for child classes /*{{{*/
+std::string pkgAcqTransactionItem::GetMetaKey() const
+{
+ return Target.MetaKey;
+}
+std::string pkgAcqIndex::GetMetaKey() const
+{
+ if (Stage == STAGE_DECOMPRESS_AND_VERIFY || CurrentCompressionExtension == "uncompressed")
+ return Target.MetaKey;
+ return Target.MetaKey + "." + CurrentCompressionExtension;
+}
+std::string pkgAcqDiffIndex::GetMetaKey() const
+{
+ return Target.MetaKey + ".diff/Index";
+}
+ /*}}}*/
+//pkgAcqTransactionItem::TransactionState and specialisations for child classes /*{{{*/
+bool pkgAcqTransactionItem::TransactionState(TransactionStates const state)
+{
+ bool const Debug = _config->FindB("Debug::Acquire::Transaction", false);
+ switch(state)
+ {
+ case TransactionAbort:
+ if(Debug == true)
+ std::clog << " Cancel: " << DestFile << std::endl;
+ if (Status == pkgAcquire::Item::StatIdle)
+ {
+ Status = pkgAcquire::Item::StatDone;
+ Dequeue();
+ }
+ break;
+ case TransactionCommit:
+ if(PartialFile != "")
+ {
+ if(Debug == true)
+ std::clog << "mv " << PartialFile << " -> "<< DestFile << " # " << DescURI() << std::endl;
+
+ Rename(PartialFile, DestFile);
+ } else {
+ if(Debug == true)
+ std::clog << "rm " << DestFile << " # " << DescURI() << std::endl;
+ unlink(DestFile.c_str());
+ }
+ break;
+ }
+ return true;
+}
+bool pkgAcqMetaBase::TransactionState(TransactionStates const state)
+{
+ // Do not remove InRelease on IMSHit of Release.gpg [yes, this is very edgecasey]
+ if (TransactionManager->IMSHit == false)
+ return pkgAcqTransactionItem::TransactionState(state);
+ return true;
+}
+bool pkgAcqIndex::TransactionState(TransactionStates const state)
+{
+ if (pkgAcqTransactionItem::TransactionState(state) == false)
+ return false;
+
+ switch (state)
+ {
+ case TransactionAbort:
+ if (Stage == STAGE_DECOMPRESS_AND_VERIFY)
+ {
+ // keep the compressed file, but drop the decompressed
+ EraseFileName.clear();
+ if (PartialFile.empty() == false && flExtension(PartialFile) == "decomp")
+ unlink(PartialFile.c_str());
+ }
+ break;
+ case TransactionCommit:
+ if (EraseFileName.empty() == false)
+ unlink(EraseFileName.c_str());
+ break;
+ }
+ return true;
+}
+bool pkgAcqDiffIndex::TransactionState(TransactionStates const state)
+{
+ if (pkgAcqTransactionItem::TransactionState(state) == false)
+ return false;
+
+ switch (state)
+ {
+ case TransactionCommit:
+ break;
+ case TransactionAbort:
+ std::string const Partial = GetPartialFileNameFromURI(Target.URI);
+ unlink(Partial.c_str());
+ break;
+ }
+
+ return true;
+}
+ /*}}}*/
+
+class APT_HIDDEN NoActionItem : public pkgAcquire::Item /*{{{*/
+/* The sole purpose of this class is having an item which does nothing to
+ reach its done state to prevent cleanup deleting the mentioned file.
+ Handy in cases in which we know we have the file already, like IMS-Hits. */
+{
+ IndexTarget const Target;
+ public:
+ virtual std::string DescURI() const APT_OVERRIDE {return Target.URI;};
+ virtual HashStringList GetExpectedHashes() const APT_OVERRIDE {return HashStringList();};
+
+ NoActionItem(pkgAcquire * const Owner, IndexTarget const &Target) :
+ pkgAcquire::Item(Owner), Target(Target)
+ {
+ Status = StatDone;
+ DestFile = GetFinalFileNameFromURI(Target.URI);
+ }
+};
+ /*}}}*/
+
// Acquire::Item::Item - Constructor /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
- PartialSize(0), Mode(0), ID(0), Complete(false),
- Local(false), QueueCounter(0),
- ExpectedAdditionalItems(0)
+APT_IGNORE_DEPRECATED_PUSH
+pkgAcquire::Item::Item(pkgAcquire * const owner) :
+ FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false), Local(false),
+ QueueCounter(0), ExpectedAdditionalItems(0), Owner(owner), d(NULL)
{
Owner->Add(this);
Status = StatIdle;
}
+APT_IGNORE_DEPRECATED_POP
/*}}}*/
// Acquire::Item::~Item - Destructor /*{{{*/
-// ---------------------------------------------------------------------
-/* */
pkgAcquire::Item::~Item()
{
Owner->Remove(this);
}
/*}}}*/
+std::string pkgAcquire::Item::Custom600Headers() const /*{{{*/
+{
+ return std::string();
+}
+ /*}}}*/
+std::string pkgAcquire::Item::ShortDesc() const /*{{{*/
+{
+ return DescURI();
+}
+ /*}}}*/
+APT_CONST void pkgAcquire::Item::Finished() /*{{{*/
+{
+}
+ /*}}}*/
+APT_PURE pkgAcquire * pkgAcquire::Item::GetOwner() const /*{{{*/
+{
+ return Owner;
+}
+ /*}}}*/
+APT_CONST pkgAcquire::ItemDesc &pkgAcquire::Item::GetItemDesc() /*{{{*/
+{
+ return Desc;
+}
+ /*}}}*/
+APT_CONST bool pkgAcquire::Item::IsTrusted() const /*{{{*/
+{
+ return false;
+}
+ /*}}}*/
// Acquire::Item::Failed - Item failed to download /*{{{*/
// ---------------------------------------------------------------------
/* We return to an idle state if there are still other queues that could
fetch this object */
-void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
+void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
{
- Status = StatIdle;
- ErrorText = LookupTag(Message,"Message");
- UsedMirror = LookupTag(Message,"UsedMirror");
+ if(ErrorText.empty())
+ ErrorText = LookupTag(Message,"Message");
if (QueueCounter <= 1)
{
/* This indicates that the file is not available right now but might
be sometime later. If we do a retry cycle then this should be
retried [CDROMs] */
- if (Cnf->LocalOnly == true &&
+ if (Cnf != NULL && Cnf->LocalOnly == true &&
StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
{
Status = StatIdle;
return;
}
- Status = StatError;
+ switch (Status)
+ {
+ case StatIdle:
+ case StatFetching:
+ case StatDone:
+ Status = StatError;
+ break;
+ case StatAuthError:
+ case StatError:
+ case StatTransientNetworkError:
+ break;
+ }
+ Complete = false;
Dequeue();
- }
+ }
+
+ string const FailReason = LookupTag(Message, "FailReason");
+ if (FailReason == "MaximumSizeExceeded")
+ RenameOnError(MaximumSizeExceeded);
+ else if (Status == StatAuthError)
+ RenameOnError(HashSumMismatch);
// report mirror failure back to LP if we actually use a mirror
- string FailReason = LookupTag(Message, "FailReason");
- if(FailReason.size() != 0)
+ if (FailReason.empty() == false)
ReportMirrorFailure(FailReason);
else
ReportMirrorFailure(ErrorText);
+
+ if (QueueCounter > 1)
+ Status = StatIdle;
}
/*}}}*/
// Acquire::Item::Start - Item has begun to download /*{{{*/
// ---------------------------------------------------------------------
-/* Stash status and the file size. Note that setting Complete means
+/* Stash status and the file size. Note that setting Complete means
sub-phases of the acquire process such as decompresion are operating */
-void pkgAcquire::Item::Start(string /*Message*/,unsigned long long Size)
+void pkgAcquire::Item::Start(string const &/*Message*/, unsigned long long const Size)
{
Status = StatFetching;
+ ErrorText.clear();
if (FileSize == 0 && Complete == false)
FileSize = Size;
}
/*}}}*/
-// Acquire::Item::Done - Item downloaded OK /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-void pkgAcquire::Item::Done(string Message,unsigned long long Size,string /*Hash*/,
- pkgAcquire::MethodConfig * /*Cnf*/)
+// Acquire::Item::VerifyDone - check if Item was downloaded OK /*{{{*/
+/* Note that hash-verification is 'hardcoded' in acquire-worker and has
+ * already passed if this method is called. */
+bool pkgAcquire::Item::VerifyDone(std::string const &Message,
+ pkgAcquire::MethodConfig const * const /*Cnf*/)
{
- // We just downloaded something..
- string FileName = LookupTag(Message,"Filename");
- UsedMirror = LookupTag(Message,"UsedMirror");
- if (Complete == false && !Local && FileName == DestFile)
+ std::string const FileName = LookupTag(Message,"Filename");
+ if (FileName.empty() == true)
{
- if (Owner->Log != 0)
- Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
+ Status = StatError;
+ ErrorText = "Method gave a blank filename";
+ return false;
}
+ return true;
+}
+ /*}}}*/
+// Acquire::Item::Done - Item downloaded OK /*{{{*/
+void pkgAcquire::Item::Done(string const &/*Message*/, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const /*Cnf*/)
+{
+ // We just downloaded something..
if (FileSize == 0)
- FileSize= Size;
+ {
+ unsigned long long const downloadedSize = Hashes.FileSize();
+ if (downloadedSize != 0)
+ {
+ FileSize = downloadedSize;
+ }
+ }
Status = StatDone;
ErrorText = string();
Owner->Dequeue(this);
// ---------------------------------------------------------------------
/* This helper function is used by a lot of item methods as their final
step */
-void pkgAcquire::Item::Rename(string From,string To)
+bool pkgAcquire::Item::Rename(string const &From,string const &To)
{
- if (rename(From.c_str(),To.c_str()) != 0)
- {
- char S[300];
- snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
- From.c_str(),To.c_str());
- Status = StatError;
+ if (From == To || rename(From.c_str(),To.c_str()) == 0)
+ return true;
+
+ std::string S;
+ strprintf(S, _("rename failed, %s (%s -> %s)."), strerror(errno),
+ From.c_str(),To.c_str());
+ Status = StatError;
+ if (ErrorText.empty())
ErrorText = S;
- }
+ else
+ ErrorText = ErrorText + ": " + S;
+ return false;
+}
+ /*}}}*/
+void pkgAcquire::Item::Dequeue() /*{{{*/
+{
+ Owner->Dequeue(this);
}
/*}}}*/
bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
{
- if(FileExists(DestFile))
+ if (RealFileExists(DestFile))
Rename(DestFile, DestFile + ".FAILED");
+ std::string errtext;
switch (error)
{
case HashSumMismatch:
- ErrorText = _("Hash Sum mismatch");
+ errtext = _("Hash Sum mismatch");
Status = StatAuthError;
ReportMirrorFailure("HashChecksumFailure");
break;
case SizeMismatch:
- ErrorText = _("Size mismatch");
+ errtext = _("Size mismatch");
Status = StatAuthError;
ReportMirrorFailure("SizeFailure");
break;
case InvalidFormat:
- ErrorText = _("Invalid file format");
+ errtext = _("Invalid file format");
Status = StatError;
// do not report as usually its not the mirrors fault, but Portal/Proxy
break;
+ case SignatureError:
+ errtext = _("Signature error");
+ Status = StatError;
+ break;
+ case NotClearsigned:
+ strprintf(errtext, _("Clearsigned file isn't valid, got '%s' (does the network require authentication?)"), "NOSPLIT");
+ Status = StatAuthError;
+ 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
+ break;
}
+ if (ErrorText.empty())
+ ErrorText = errtext;
return false;
}
/*}}}*/
+void pkgAcquire::Item::SetActiveSubprocess(const std::string &subprocess)/*{{{*/
+{
+ ActiveSubprocess = subprocess;
+ APT_IGNORE_DEPRECATED(Mode = ActiveSubprocess.c_str();)
+}
+ /*}}}*/
// Acquire::Item::ReportMirrorFailure /*{{{*/
-// ---------------------------------------------------------------------
-void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
+void pkgAcquire::Item::ReportMirrorFailure(string const &FailCode)
{
// we only act if a mirror was used at all
if(UsedMirror.empty())
<< " FailCode: "
<< FailCode << std::endl;
#endif
- const char *Args[40];
- unsigned int i = 0;
string report = _config->Find("Methods::Mirror::ProblemReporting",
"/usr/lib/apt/apt-report-mirror-failure");
if(!FileExists(report))
return;
- Args[i++] = report.c_str();
- Args[i++] = UsedMirror.c_str();
- Args[i++] = DescURI().c_str();
- Args[i++] = FailCode.c_str();
- Args[i++] = NULL;
+
+ 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)
+ if(pid < 0)
{
_error->Error("ReportMirrorFailure Fork failed");
return;
}
- else if(pid == 0)
+ else if(pid == 0)
{
- execvp(Args[0], (char**)Args);
+ execvp(Args[0], (char**)Args.data());
std::cerr << "Could not exec " << Args[0] << std::endl;
_exit(100);
}
- if(!ExecWait(pid, "report-mirror-failure"))
+ if(!ExecWait(pid, "report-mirror-failure"))
{
_error->Warning("Couldn't report problem to '%s'",
_config->Find("Methods::Mirror::ProblemReporting").c_str());
}
}
/*}}}*/
-// AcqSubIndex::AcqSubIndex - Constructor /*{{{*/
-// ---------------------------------------------------------------------
-/* Get a sub-index file based on checksums from a 'master' file and
- possibly query additional files */
-pkgAcqSubIndex::pkgAcqSubIndex(pkgAcquire *Owner, string const &URI,
- string const &URIDesc, string const &ShortDesc,
- HashString const &ExpectedHash)
- : Item(Owner), ExpectedHash(ExpectedHash)
+std::string pkgAcquire::Item::HashSum() const /*{{{*/
{
- /* XXX: Beware: Currently this class does nothing (of value) anymore ! */
- Debug = _config->FindB("Debug::pkgAcquire::SubIndex",false);
-
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
- DestFile += URItoFileName(URI);
-
- Desc.URI = URI;
- Desc.Description = URIDesc;
- Desc.Owner = this;
- Desc.ShortDesc = ShortDesc;
-
- QueueURI(Desc);
+ HashStringList const hashes = GetExpectedHashes();
+ HashString const * const hs = hashes.find(NULL);
+ return hs != NULL ? hs->toStr() : "";
+}
+ /*}}}*/
- if(Debug)
- std::clog << "pkgAcqSubIndex: " << Desc.URI << std::endl;
+pkgAcqTransactionItem::pkgAcqTransactionItem(pkgAcquire * const Owner, /*{{{*/
+ pkgAcqMetaClearSig * const transactionManager, IndexTarget const &target) :
+ pkgAcquire::Item(Owner), d(NULL), Target(target), TransactionManager(transactionManager)
+{
+ if (TransactionManager != this)
+ TransactionManager->Add(this);
}
/*}}}*/
-// AcqSubIndex::Custom600Headers - Insert custom request headers /*{{{*/
-// ---------------------------------------------------------------------
-/* The only header we use is the last-modified header. */
-string pkgAcqSubIndex::Custom600Headers()
+pkgAcqTransactionItem::~pkgAcqTransactionItem() /*{{{*/
{
- string Final = _config->FindDir("Dir::State::lists");
- Final += URItoFileName(Desc.URI);
-
- struct stat Buf;
- if (stat(Final.c_str(),&Buf) != 0)
- return "\nIndex-File: true\nFail-Ignore: true\n";
- return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
}
/*}}}*/
-void pkgAcqSubIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
+HashStringList pkgAcqTransactionItem::GetExpectedHashesFor(std::string const &MetaKey) const /*{{{*/
{
- if(Debug)
- std::clog << "pkgAcqSubIndex failed: " << Desc.URI << " with " << Message << std::endl;
-
- Complete = false;
- Status = StatDone;
- Dequeue();
+ return GetExpectedHashesFromFor(TransactionManager->MetaIndexParser, MetaKey);
+}
+ /*}}}*/
- // No good Index is provided
+// AcqMetaBase - Constructor /*{{{*/
+pkgAcqMetaBase::pkgAcqMetaBase(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ std::vector<IndexTarget> const &IndexTargets,
+ IndexTarget const &DataTarget)
+: pkgAcqTransactionItem(Owner, TransactionManager, DataTarget), d(NULL),
+ IndexTargets(IndexTargets),
+ AuthPass(false), IMSHit(false)
+{
}
/*}}}*/
-void pkgAcqSubIndex::Done(string Message,unsigned long long Size,string Md5Hash, /*{{{*/
- pkgAcquire::MethodConfig *Cnf)
+// AcqMetaBase::Add - Add a item to the current Transaction /*{{{*/
+void pkgAcqMetaBase::Add(pkgAcqTransactionItem * const I)
{
- if(Debug)
- std::clog << "pkgAcqSubIndex::Done(): " << Desc.URI << std::endl;
+ Transaction.push_back(I);
+}
+ /*}}}*/
+// AcqMetaBase::AbortTransaction - Abort the current Transaction /*{{{*/
+void pkgAcqMetaBase::AbortTransaction()
+{
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "AbortTransaction: " << TransactionManager << std::endl;
- string FileName = LookupTag(Message,"Filename");
- if (FileName.empty() == true)
+ // ensure the toplevel is in error state too
+ for (std::vector<pkgAcqTransactionItem*>::iterator I = Transaction.begin();
+ I != Transaction.end(); ++I)
{
- Status = StatError;
- ErrorText = "Method gave a blank filename";
- return;
+ (*I)->TransactionState(TransactionAbort);
}
-
- if (FileName != DestFile)
- {
- Local = true;
- Desc.URI = "copy:" + FileName;
- QueueURI(Desc);
- return;
+ Transaction.clear();
+}
+ /*}}}*/
+// AcqMetaBase::TransactionHasError - Check for errors in Transaction /*{{{*/
+APT_PURE bool pkgAcqMetaBase::TransactionHasError() const
+{
+ for (std::vector<pkgAcqTransactionItem*>::const_iterator I = Transaction.begin();
+ I != Transaction.end(); ++I)
+ {
+ switch((*I)->Status) {
+ case StatDone: break;
+ case StatIdle: break;
+ case StatAuthError: return true;
+ case StatError: return true;
+ case StatTransientNetworkError: return true;
+ case StatFetching: break;
+ }
}
+ return false;
+}
+ /*}}}*/
+// AcqMetaBase::CommitTransaction - Commit a transaction /*{{{*/
+void pkgAcqMetaBase::CommitTransaction()
+{
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "CommitTransaction: " << this << std::endl;
- Item::Done(Message,Size,Md5Hash,Cnf);
-
- string FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(Desc.URI);
-
- /* Downloaded invalid transindex => Error (LP: #346386) (Closes: #627642) */
- indexRecords SubIndexParser;
- if (FileExists(DestFile) == true && !SubIndexParser.Load(DestFile)) {
- Status = StatError;
- ErrorText = SubIndexParser.ErrorText;
- return;
+ // 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();
+ I != Transaction.end(); ++I)
+ {
+ (*I)->TransactionState(TransactionCommit);
}
-
- // success in downloading the index
- // rename the index
- if(Debug)
- std::clog << "Renaming: " << DestFile << " -> " << FinalFile << std::endl;
- Rename(DestFile,FinalFile);
- chmod(FinalFile.c_str(),0644);
- DestFile = FinalFile;
-
- if(ParseIndex(DestFile) == false)
- return Failed("", NULL);
-
- Complete = true;
- Status = StatDone;
- Dequeue();
- return;
+ Transaction.clear();
}
/*}}}*/
-bool pkgAcqSubIndex::ParseIndex(string const &IndexFile) /*{{{*/
+// AcqMetaBase::TransactionStageCopy - Stage a file for copying /*{{{*/
+void pkgAcqMetaBase::TransactionStageCopy(pkgAcqTransactionItem * const I,
+ const std::string &From,
+ const std::string &To)
{
- indexRecords SubIndexParser;
- if (FileExists(IndexFile) == false || SubIndexParser.Load(IndexFile) == false)
- return false;
- // so something with the downloaded index
- return true;
+ I->PartialFile = From;
+ I->DestFile = To;
}
/*}}}*/
-// AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
-// ---------------------------------------------------------------------
-/* Get the DiffIndex file first and see if there are patches available
- * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
- * patches. If anything goes wrong in that process, it will fall back to
- * the original packages file
- */
-pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
- IndexTarget const *Target,
- HashString ExpectedHash,
- indexRecords *MetaIndexParser)
- : Item(Owner), ExpectedHash(ExpectedHash), Target(Target),
- MetaIndexParser(MetaIndexParser)
-
+// AcqMetaBase::TransactionStageRemoval - Stage a file for removal /*{{{*/
+void pkgAcqMetaBase::TransactionStageRemoval(pkgAcqTransactionItem * const I,
+ const std::string &FinalFile)
{
-
- Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
-
- RealURI = Target->URI;
- Desc.Owner = this;
- Desc.Description = Target->Description + "/DiffIndex";
- Desc.ShortDesc = Target->ShortDesc;
- Desc.URI = Target->URI + ".diff/Index";
-
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
- DestFile += URItoFileName(Target->URI) + string(".DiffIndex");
-
- if(Debug)
- std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
-
- // look for the current package file
- CurrentPackagesFile = _config->FindDir("Dir::State::lists");
- CurrentPackagesFile += URItoFileName(RealURI);
-
- // FIXME: this file:/ check is a hack to prevent fetching
- // from local sources. this is really silly, and
- // should be fixed cleanly as soon as possible
- if(!FileExists(CurrentPackagesFile) ||
- Desc.URI.substr(0,strlen("file:/")) == "file:/")
- {
- // we don't have a pkg file or we don't want to queue
- if(Debug)
- std::clog << "No index file, local or canceld by user" << std::endl;
- Failed("", NULL);
- return;
+ I->PartialFile = "";
+ I->DestFile = FinalFile;
+}
+ /*}}}*/
+// AcqMetaBase::GenerateAuthWarning - Check gpg authentication error /*{{{*/
+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))
+ {
+ 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());
+ 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());
+ I->Status = StatAuthError;
+ return true;
+ } else {
+ _error->Warning(_("GPG error: %s: %s"),
+ Desc.Description.c_str(),
+ LookupTag(Message,"Message").c_str());
}
-
- if(Debug)
- std::clog << "pkgAcqDiffIndex::pkgAcqDiffIndex(): "
- << CurrentPackagesFile << std::endl;
-
- QueueURI(Desc);
-
+ // gpgv method failed
+ ReportMirrorFailure("GPGFailure");
+ return false;
}
/*}}}*/
-// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
+// AcqMetaBase::Custom600Headers - Get header for AcqMetaBase /*{{{*/
// ---------------------------------------------------------------------
-/* The only header we use is the last-modified header. */
-string pkgAcqDiffIndex::Custom600Headers()
+string pkgAcqMetaBase::Custom600Headers() const
{
- string Final = _config->FindDir("Dir::State::lists");
- Final += URItoFileName(RealURI) + string(".IndexDiff");
-
- if(Debug)
- std::clog << "Custom600Header-IMS: " << Final << std::endl;
+ std::string Header = "\nIndex-File: true";
+ std::string MaximumSize;
+ strprintf(MaximumSize, "\nMaximum-Size: %i",
+ _config->FindI("Acquire::MaxReleaseFileSize", 10*1000*1000));
+ Header += MaximumSize;
+ string const FinalFile = GetFinalFilename();
struct stat Buf;
- if (stat(Final.c_str(),&Buf) != 0)
- return "\nIndex-File: true";
-
- return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
+ if (stat(FinalFile.c_str(),&Buf) == 0)
+ Header += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
+
+ return Header;
}
/*}}}*/
-bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) /*{{{*/
+// AcqMetaBase::QueueForSignatureVerify /*{{{*/
+void pkgAcqMetaBase::QueueForSignatureVerify(pkgAcqTransactionItem * const I, std::string const &File, std::string const &Signature)
{
- if(Debug)
- std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
- << std::endl;
+ AuthPass = true;
+ I->Desc.URI = "gpgv:" + Signature;
+ I->DestFile = File;
+ QueueURI(I->Desc);
+ I->SetActiveSubprocess("gpgv");
+}
+ /*}}}*/
+// AcqMetaBase::CheckDownloadDone /*{{{*/
+bool pkgAcqMetaBase::CheckDownloadDone(pkgAcqTransactionItem * const I, const std::string &Message, HashStringList const &Hashes) const
+{
+ // We have just finished downloading a Release file (it is not
+ // verified yet)
- pkgTagSection Tags;
- string ServerSha1;
- vector<DiffInfo> available_patches;
-
- FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
- pkgTagFile TF(&Fd);
- if (_error->PendingError() == true)
+ std::string const FileName = LookupTag(Message,"Filename");
+ if (FileName != I->DestFile && RealFileExists(I->DestFile) == false)
+ {
+ I->Local = true;
+ I->Desc.URI = "copy:" + FileName;
+ I->QueueURI(I->Desc);
return false;
+ }
+
+ // make sure to verify against the right file on I-M-S hit
+ bool IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"), false);
+ if (IMSHit == false && Hashes.usable())
+ {
+ // detect IMS-Hits servers haven't detected by Hash comparison
+ std::string const FinalFile = I->GetFinalFilename();
+ if (RealFileExists(FinalFile) && Hashes.VerifyFile(FinalFile) == true)
+ {
+ IMSHit = true;
+ unlink(I->DestFile.c_str());
+ }
+ }
- if(TF.Step(Tags) == true)
+ if(IMSHit == true)
{
- bool found = false;
- DiffInfo d;
- string size;
+ // for simplicity, the transaction manager is always InRelease
+ // even if it doesn't exist.
+ if (TransactionManager != NULL)
+ TransactionManager->IMSHit = true;
+ I->PartialFile = I->DestFile = I->GetFinalFilename();
+ }
- string const tmp = Tags.FindS("SHA1-Current");
- std::stringstream ss(tmp);
- ss >> ServerSha1 >> size;
- unsigned long const ServerSize = atol(size.c_str());
+ // set Item to complete as the remaining work is all local (verify etc)
+ I->Complete = true;
- FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
- SHA1Summation SHA1;
- SHA1.AddFD(fd);
- string const local_sha1 = SHA1.Result();
+ return true;
+}
+ /*}}}*/
+bool pkgAcqMetaBase::CheckAuthDone(string const &Message) /*{{{*/
+{
+ // At this point, the gpgv method has succeeded, so there is a
+ // valid signature from a key in the trusted keyring. We
+ // perform additional verification of its contents, and use them
+ // to verify the indexes we are about to download
- if(local_sha1 == ServerSha1)
+ if (TransactionManager->IMSHit == false)
+ {
+ // open the last (In)Release if we have it
+ std::string const FinalFile = GetFinalFilename();
+ std::string FinalRelease;
+ std::string FinalInRelease;
+ if (APT::String::Endswith(FinalFile, "InRelease"))
{
- // we have the same sha1 as the server so we are done here
- if(Debug)
- std::clog << "Package file is up-to-date" << std::endl;
- // list cleanup needs to know that this file as well as the already
- // present index is ours, so we create an empty diff to save it for us
- new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
- ExpectedHash, ServerSha1, available_patches);
- return true;
+ FinalInRelease = FinalFile;
+ FinalRelease = FinalFile.substr(0, FinalFile.length() - strlen("InRelease")) + "Release";
}
else
{
- if(Debug)
- std::clog << "SHA1-Current: " << ServerSha1 << " and we start at "<< fd.Name() << " " << fd.Size() << " " << local_sha1 << std::endl;
+ 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();
+ }
+ }
+ }
+
+ if (TransactionManager->MetaIndexParser->Load(DestFile, &ErrorText) == false)
+ {
+ Status = StatAuthError;
+ return false;
+ }
- // check the historie and see what patches we need
- string const history = Tags.FindS("SHA1-History");
- std::stringstream hist(history);
- while(hist >> d.sha1 >> size >> d.file)
+ if (!VerifyVendor(Message))
+ {
+ Status = StatAuthError;
+ return false;
+ }
+
+ if (_config->FindB("Debug::pkgAcquire::Auth", false))
+ std::cerr << "Signature verification succeeded: "
+ << DestFile << std::endl;
+
+ // Download further indexes with verification
+ QueueIndexes(true);
+
+ return true;
+}
+ /*}}}*/
+void pkgAcqMetaBase::QueueIndexes(bool const verify) /*{{{*/
+{
+ // at this point the real Items are loaded in the fetcher
+ ExpectedAdditionalItems = 0;
+
+ for (std::vector <IndexTarget>::const_iterator Target = IndexTargets.begin();
+ Target != IndexTargets.end();
+ ++Target)
+ {
+ bool trypdiff = _config->FindB("Acquire::PDiffs", true);
+ if (verify == true)
+ {
+ if (TransactionManager->MetaIndexParser->Exists(Target->MetaKey) == false)
{
- // read until the first match is found
- // from that point on, we probably need all diffs
- if(d.sha1 == local_sha1)
- found=true;
- else if (found == false)
+ // optional targets that we do not have in the Release file are skipped
+ if (Target->IsOptional)
continue;
- if(Debug)
- std::clog << "Need to get diff: " << d.file << std::endl;
- available_patches.push_back(d);
+ Status = StatAuthError;
+ strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), Target->MetaKey.c_str());
+ return;
}
- if (available_patches.empty() == false)
+ if (RealFileExists(GetFinalFileNameFromURI(Target->URI)))
{
- // 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())
- {
- if (Debug)
- std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
- << ") so fallback to complete download" << std::endl;
- return false;
- }
-
- // see if the patches are too big
- found = false; // it was true and it will be true again at the end
- d = *available_patches.begin();
- string const firstPatch = d.file;
- unsigned long patchesSize = 0;
- std::stringstream patches(Tags.FindS("SHA1-Patches"));
- while(patches >> d.sha1 >> size >> d.file)
+ if (TransactionManager->LastMetaIndexParser != NULL)
{
- if (firstPatch == d.file)
- found = true;
- else if (found == false)
+ HashStringList const newFile = GetExpectedHashesFromFor(TransactionManager->MetaIndexParser, Target->MetaKey);
+ HashStringList const oldFile = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target->MetaKey);
+ if (newFile == oldFile)
+ {
+ // we have the file already, no point in trying to acquire it again
+ new NoActionItem(Owner, *Target);
continue;
-
- patchesSize += atol(size.c_str());
+ }
}
- unsigned long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
- if (sizeLimit > 0 && (sizeLimit/100) < patchesSize)
+ else if (TransactionManager->IMSHit == true)
{
- if (Debug)
- std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
- << ") so fallback to complete download" << std::endl;
- return false;
+ // we have the file already, no point in trying to acquire it again
+ new NoActionItem(Owner, *Target);
+ continue;
}
}
- }
-
- // we have something, queue the next diff
- if(found)
- {
- // queue the diffs
- string::size_type const last_space = Description.rfind(" ");
- if(last_space != string::npos)
- Description.erase(last_space, Description.size()-last_space);
-
- /* decide if we should download patches one by one or in one go:
- The first is good if the server merges patches, but many don't so client
- based merging can be attempt in which case the second is better.
- "bad things" will happen if patches are merged on the server,
- but client side merging is attempt as well */
- bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
- if (pdiff_merge == true)
- {
- // reprepro adds this flag if it has merged patches on the server
- std::string const precedence = Tags.FindS("X-Patch-Precedence");
- pdiff_merge = (precedence != "merged");
- }
-
- if (pdiff_merge == false)
- new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
- ExpectedHash, ServerSha1, available_patches);
else
- {
- std::vector<pkgAcqIndexMergeDiffs*> *diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
- for(size_t i = 0; i < available_patches.size(); ++i)
- (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, RealURI, Description, Desc.ShortDesc, ExpectedHash,
- available_patches[i], diffs);
- }
+ trypdiff = false; // no file to patch
- Complete = false;
- Status = StatDone;
- Dequeue();
- return true;
+ // check if we have patches available
+ trypdiff &= TransactionManager->MetaIndexParser->Exists(Target->MetaKey + ".diff/Index");
}
- }
-
- // Nothing found, report and return false
- // Failing here is ok, if we return false later, the full
- // IndexFile is queued
- if(Debug)
- std::clog << "Can't find a patch in the index file" << std::endl;
- return false;
-}
- /*}}}*/
-void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
-{
- if(Debug)
- std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
- << "Falling back to normal index file acquire" << std::endl;
+ // if we have no file to patch, no point in trying
+ trypdiff &= RealFileExists(GetFinalFileNameFromURI(Target->URI));
- new pkgAcqIndex(Owner, Target, ExpectedHash, MetaIndexParser);
+ // no point in patching from local sources
+ if (trypdiff)
+ {
+ std::string const proto = Target->URI.substr(0, strlen("file:/"));
+ if (proto == "file:/" || proto == "copy:/" || proto == "cdrom:")
+ trypdiff = false;
+ }
- Complete = false;
- Status = StatDone;
- Dequeue();
+ // Queue the Index file (Packages, Sources, Translation-$foo, …)
+ if (trypdiff)
+ new pkgAcqDiffIndex(Owner, TransactionManager, *Target);
+ else
+ new pkgAcqIndex(Owner, TransactionManager, *Target);
+ }
}
/*}}}*/
-void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,string Md5Hash, /*{{{*/
- pkgAcquire::MethodConfig *Cnf)
+bool pkgAcqMetaBase::VerifyVendor(string const &Message) /*{{{*/
{
- if(Debug)
- std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
-
- Item::Done(Message,Size,Md5Hash,Cnf);
+ string::size_type pos;
- string FinalFile;
- FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
+ // 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());
- // success in downloading the index
- // rename the index
- FinalFile += string(".IndexDiff");
- if(Debug)
- std::clog << "Renaming: " << DestFile << " -> " << FinalFile
- << std::endl;
- Rename(DestFile,FinalFile);
- chmod(FinalFile.c_str(),0644);
- DestFile = FinalFile;
+ string Transformed = TransactionManager->MetaIndexParser->GetExpectedDist();
- if(!ParseDiffIndex(DestFile))
- return Failed("", NULL);
+ if (Transformed == "../project/experimental")
+ {
+ Transformed = "experimental";
+ }
- Complete = true;
- Status = StatDone;
- Dequeue();
- return;
-}
- /*}}}*/
-// AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
-// ---------------------------------------------------------------------
-/* The package diff is added to the queue. one object is constructed
- * for each diff and the index
- */
-pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
- string URI,string URIDesc,string ShortDesc,
- HashString ExpectedHash,
- string ServerSha1,
- vector<DiffInfo> diffs)
- : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash),
- available_patches(diffs), ServerSha1(ServerSha1)
-{
-
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
- DestFile += URItoFileName(URI);
+ pos = Transformed.rfind('/');
+ if (pos != string::npos)
+ {
+ Transformed = Transformed.substr(0, pos);
+ }
- Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
+ if (Transformed == ".")
+ {
+ Transformed = "";
+ }
- Description = URIDesc;
- Desc.Owner = this;
- Desc.ShortDesc = ShortDesc;
+ if (TransactionManager->MetaIndexParser->GetValidUntil() > 0)
+ {
+ time_t const invalid_since = time(NULL) - TransactionManager->MetaIndexParser->GetValidUntil();
+ if (invalid_since > 0)
+ {
+ std::string errmsg;
+ strprintf(errmsg,
+ // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
+ // the time since then the file is invalid - formatted in the same way as in
+ // the download progress display (e.g. 7d 3h 42min 1s)
+ _("Release file for %s is expired (invalid since %s). "
+ "Updates for this repository will not be applied."),
+ Target.URI.c_str(), TimeToStr(invalid_since).c_str());
+ if (ErrorText.empty())
+ ErrorText = errmsg;
+ return _error->Error("%s", errmsg.c_str());
+ }
+ }
- if(available_patches.empty() == true)
+ /* Did we get a file older than what we have? This is a last minute IMS hit and doubles
+ as a prevention of downgrading us to older (still valid) files */
+ if (TransactionManager->IMSHit == false && TransactionManager->LastMetaIndexParser != NULL &&
+ TransactionManager->LastMetaIndexParser->GetDate() > TransactionManager->MetaIndexParser->GetDate())
{
- // we are done (yeah!)
- Finish(true);
+ TransactionManager->IMSHit = true;
+ unlink(DestFile.c_str());
+ PartialFile = DestFile = GetFinalFilename();
+ // load the 'old' file in the 'new' one instead of flipping pointers as
+ // the new one isn't owned by us, while the old one is so cleanup would be confused.
+ TransactionManager->MetaIndexParser->swapLoad(TransactionManager->LastMetaIndexParser);
+ delete TransactionManager->LastMetaIndexParser;
+ TransactionManager->LastMetaIndexParser = NULL;
}
- else
+
+ if (_config->FindB("Debug::pkgAcquire::Auth", false))
{
- // get the next diff
- State = StateFetchDiff;
- QueueNextDiff();
+ std::cerr << "Got Codename: " << TransactionManager->MetaIndexParser->GetCodename() << std::endl;
+ std::cerr << "Expecting Dist: " << TransactionManager->MetaIndexParser->GetExpectedDist() << std::endl;
+ std::cerr << "Transformed Dist: " << Transformed << std::endl;
}
+
+ if (TransactionManager->MetaIndexParser->CheckDist(Transformed) == false)
+ {
+ // This might become fatal one day
+// Status = StatAuthError;
+// ErrorText = "Conflicting distribution; expected "
+// + MetaIndexParser->GetExpectedDist() + " but got "
+// + MetaIndexParser->GetCodename();
+// return false;
+ if (!Transformed.empty())
+ {
+ _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
+ Desc.Description.c_str(),
+ Transformed.c_str(),
+ TransactionManager->MetaIndexParser->GetCodename().c_str());
+ }
+ }
+
+ return true;
}
/*}}}*/
-void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
-{
- if(Debug)
- std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
- << "Falling back to normal index file acquire" << std::endl;
- new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc,
- ExpectedHash);
- Finish();
-}
- /*}}}*/
-// Finish - helper that cleans the item out of the fetcher queue /*{{{*/
-void pkgAcqIndexDiffs::Finish(bool allDone)
+pkgAcqMetaBase::~pkgAcqMetaBase()
{
- // we restore the original name, this is required, otherwise
- // the file will be cleaned
- if(allDone)
+}
+
+pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire * const Owner, /*{{{*/
+ IndexTarget const &ClearsignedTarget,
+ IndexTarget const &DetachedDataTarget, IndexTarget const &DetachedSigTarget,
+ std::vector<IndexTarget> const &IndexTargets,
+ metaIndex * const MetaIndexParser) :
+ pkgAcqMetaIndex(Owner, this, ClearsignedTarget, DetachedSigTarget, IndexTargets),
+ d(NULL), ClearsignedTarget(ClearsignedTarget),
+ DetachedDataTarget(DetachedDataTarget),
+ MetaIndexParser(MetaIndexParser), LastMetaIndexParser(NULL)
+{
+ // index targets + (worst case:) Release/Release.gpg
+ ExpectedAdditionalItems = IndexTargets.size() + 2;
+ TransactionManager->Add(this);
+}
+ /*}}}*/
+pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
+{
+ if (LastMetaIndexParser != NULL)
+ delete LastMetaIndexParser;
+}
+ /*}}}*/
+// pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
+string pkgAcqMetaClearSig::Custom600Headers() const
+{
+ string Header = pkgAcqMetaBase::Custom600Headers();
+ Header += "\nFail-Ignore: true";
+ std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
+ if (key.empty() == false)
+ Header += "\nSigned-By: " + key;
+
+ return Header;
+}
+ /*}}}*/
+bool pkgAcqMetaClearSig::VerifyDone(std::string const &Message,
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ Item::VerifyDone(Message, Cnf);
+
+ if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
+ return RenameOnError(NotClearsigned);
+
+ return true;
+}
+// pkgAcqMetaClearSig::Done - We got a file /*{{{*/
+void pkgAcqMetaClearSig::Done(std::string const &Message,
+ HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ Item::Done(Message, Hashes, Cnf);
+
+ if(AuthPass == false)
{
- DestFile = _config->FindDir("Dir::State::lists");
- DestFile += URItoFileName(RealURI);
+ if(CheckDownloadDone(this, Message, Hashes) == true)
+ QueueForSignatureVerify(this, DestFile, DestFile);
+ return;
+ }
+ else if(CheckAuthDone(Message) == true)
+ {
+ if (TransactionManager->IMSHit == false)
+ TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
+ else if (RealFileExists(GetFinalFilename()) == false)
+ {
+ // We got an InRelease file IMSHit, but we haven't one, which means
+ // we had a valid Release/Release.gpg combo stepping in, which we have
+ // to 'acquire' now to ensure list cleanup isn't removing them
+ new NoActionItem(Owner, DetachedDataTarget);
+ new NoActionItem(Owner, DetachedSigTarget);
+ }
+ }
+}
+ /*}}}*/
+void pkgAcqMetaClearSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf) /*{{{*/
+{
+ Item::Failed(Message, Cnf);
- if(!ExpectedHash.empty() && !ExpectedHash.VerifyFile(DestFile))
+ // we failed, we will not get additional items from this method
+ ExpectedAdditionalItems = 0;
+
+ if (AuthPass == false)
+ {
+ if (Status == StatAuthError)
{
- RenameOnError(HashSumMismatch);
- Dequeue();
+ // if we expected a ClearTextSignature (InRelease) and got a file,
+ // but it wasn't valid we end up here (see VerifyDone).
+ // As these is usually called by web-portals we do not try Release/Release.gpg
+ // as this is gonna fail anyway and instead abort our try (LP#346386)
+ TransactionManager->AbortTransaction();
return;
}
- // this is for the "real" finish
- Complete = true;
+ // Queue the 'old' InRelease file for removal if we try Release.gpg
+ // as otherwise the file will stay around and gives a false-auth
+ // impression (CVE-2012-0214)
+ TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
Status = StatDone;
- Dequeue();
- if(Debug)
- std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
- return;
+
+ new pkgAcqMetaIndex(Owner, TransactionManager, DetachedDataTarget, DetachedSigTarget, IndexTargets);
}
+ else
+ {
+ if(CheckStopAuthentication(this, Message))
+ return;
- if(Debug)
- std::clog << "Finishing: " << Desc.URI << std::endl;
- Complete = false;
- Status = StatDone;
- Dequeue();
- return;
+ _error->Warning(_("The data from '%s' is not signed. Packages "
+ "from that repository can not be authenticated."),
+ ClearsignedTarget.Description.c_str());
+
+ // 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
+ if(AllowInsecureRepositories(TransactionManager->MetaIndexParser, TransactionManager, this) == true)
+ {
+ Status = StatDone;
+
+ /* InRelease files become Release files, otherwise
+ * they would be considered as trusted later on */
+ string const FinalRelease = GetFinalFileNameFromURI(DetachedDataTarget.URI);
+ string const PartialRelease = GetPartialFileNameFromURI(DetachedDataTarget.URI);
+ string const FinalReleasegpg = GetFinalFileNameFromURI(DetachedSigTarget.URI);
+ 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();
+ }
+ }
+ }
+
+ // we parse the indexes here because at this point the user wanted
+ // a repository that may potentially harm him
+ if (TransactionManager->MetaIndexParser->Load(PartialRelease, &ErrorText) == false || VerifyVendor(Message) == false)
+ /* expired Release files are still a problem you need extra force for */;
+ else
+ QueueIndexes(true);
+ }
+ }
}
/*}}}*/
-bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
+
+pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire * const Owner, /*{{{*/
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &DataTarget,
+ IndexTarget const &DetachedSigTarget,
+ vector<IndexTarget> const &IndexTargets) :
+ pkgAcqMetaBase(Owner, TransactionManager, IndexTargets, DataTarget), d(NULL),
+ DetachedSigTarget(DetachedSigTarget)
{
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "New pkgAcqMetaIndex with TransactionManager "
+ << this->TransactionManager << std::endl;
- // calc sha1 of the just patched file
- string FinalFile = _config->FindDir("Dir::State::lists");
- FinalFile += URItoFileName(RealURI);
+ DestFile = GetPartialFileNameFromURI(DataTarget.URI);
- FileFd fd(FinalFile, FileFd::ReadOnly);
- SHA1Summation SHA1;
- SHA1.AddFD(fd);
- string local_sha1 = string(SHA1.Result());
- if(Debug)
- std::clog << "QueueNextDiff: "
- << FinalFile << " (" << local_sha1 << ")"<<std::endl;
+ // Create the item
+ Desc.Description = DataTarget.Description;
+ Desc.Owner = this;
+ Desc.ShortDesc = DataTarget.ShortDesc;
+ Desc.URI = DataTarget.URI;
- // final file reached before all patches are applied
- if(local_sha1 == ServerSha1)
- {
- Finish(true);
- return true;
- }
+ // we expect more item
+ ExpectedAdditionalItems = IndexTargets.size();
+ QueueURI(Desc);
+}
+ /*}}}*/
+void pkgAcqMetaIndex::Done(string const &Message, /*{{{*/
+ HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cfg)
+{
+ Item::Done(Message,Hashes,Cfg);
- // remove all patches until the next matching patch is found
- // this requires the Index file to be ordered
- for(vector<DiffInfo>::iterator I=available_patches.begin();
- available_patches.empty() == false &&
- I != available_patches.end() &&
- I->sha1 != local_sha1;
- ++I)
+ if(CheckDownloadDone(this, Message, Hashes))
{
- available_patches.erase(I);
+ // we have a Release file, now download the Signature, all further
+ // verify/queue for additional downloads will be done in the
+ // pkgAcqMetaSig::Done() code
+ new pkgAcqMetaSig(Owner, TransactionManager, DetachedSigTarget, this);
}
+}
+ /*}}}*/
+// pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
+void pkgAcqMetaIndex::Failed(string const &Message,
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ pkgAcquire::Item::Failed(Message, Cnf);
+ Status = StatDone;
- // error checking and falling back if no patch was found
- if(available_patches.empty() == true)
+ _error->Warning(_("The repository '%s' does not have a Release file. "
+ "This is deprecated, please contact the owner of the "
+ "repository."), Target.Description.c_str());
+
+ // No Release file was present so fall
+ // back to queueing Packages files without verification
+ // only allow going further if the users explicitely wants it
+ if(AllowInsecureRepositories(TransactionManager->MetaIndexParser, TransactionManager, this) == true)
{
- Failed("", NULL);
- return false;
+ // ensure old Release files are removed
+ TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
+
+ // queue without any kind of hashsum support
+ QueueIndexes(false);
}
+}
+ /*}}}*/
+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;
+}
+ /*}}}*/
+pkgAcqMetaIndex::~pkgAcqMetaIndex() {}
+
+// AcqMetaSig::AcqMetaSig - Constructor /*{{{*/
+pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target,
+ pkgAcqMetaIndex * const MetaIndex) :
+ pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL), MetaIndex(MetaIndex)
+{
+ DestFile = GetPartialFileNameFromURI(Target.URI);
- // queue the right diff
- Desc.URI = RealURI + ".diff/" + available_patches[0].file + ".gz";
- Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
- DestFile += URItoFileName(RealURI + ".diff/" + available_patches[0].file);
+ // remove any partial downloaded sig-file in partial/.
+ // it may confuse proxies and is too small to warrant a
+ // partial download anyway
+ unlink(DestFile.c_str());
- if(Debug)
- std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
-
- QueueURI(Desc);
+ // set the TransactionManager
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "New pkgAcqMetaSig with TransactionManager "
+ << TransactionManager << std::endl;
- return true;
+ // Create the item
+ Desc.Description = Target.Description;
+ Desc.Owner = this;
+ Desc.ShortDesc = Target.ShortDesc;
+ Desc.URI = Target.URI;
+
+ // If we got a hit for Release, we will get one for Release.gpg too (or obscure errors),
+ // so we skip the download step and go instantly to verification
+ if (TransactionManager->IMSHit == true && RealFileExists(GetFinalFilename()))
+ {
+ Complete = true;
+ Status = StatDone;
+ PartialFile = DestFile = GetFinalFilename();
+ MetaIndexFileSignature = DestFile;
+ MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
+ }
+ else
+ QueueURI(Desc);
}
/*}}}*/
-void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size,string Md5Hash, /*{{{*/
- pkgAcquire::MethodConfig *Cnf)
+pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
{
- if(Debug)
- std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
-
- Item::Done(Message,Size,Md5Hash,Cnf);
-
- string FinalFile;
- FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
+}
+ /*}}}*/
+// pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
+std::string pkgAcqMetaSig::Custom600Headers() const
+{
+ std::string Header = pkgAcqTransactionItem::Custom600Headers();
+ std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
+ if (key.empty() == false)
+ Header += "\nSigned-By: " + key;
+ return Header;
+}
+ /*}}}*/
+// AcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
+void pkgAcqMetaSig::Done(string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cfg)
+{
+ if (MetaIndexFileSignature.empty() == false)
+ {
+ DestFile = MetaIndexFileSignature;
+ MetaIndexFileSignature.clear();
+ }
+ Item::Done(Message, Hashes, Cfg);
- // success in downloading a diff, enter ApplyDiff state
- if(State == StateFetchDiff)
+ if(MetaIndex->AuthPass == false)
{
+ if(MetaIndex->CheckDownloadDone(this, Message, Hashes) == true)
+ {
+ // destfile will be modified to point to MetaIndexFile for the
+ // gpgv method, so we need to save it here
+ MetaIndexFileSignature = DestFile;
+ MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
+ }
+ return;
+ }
+ else if(MetaIndex->CheckAuthDone(Message) == true)
+ {
+ if (TransactionManager->IMSHit == false)
+ {
+ TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
+ TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
+ }
+ }
+}
+ /*}}}*/
+void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
+{
+ Item::Failed(Message,Cnf);
- // rred excepts the patch as $FinalFile.ed
- Rename(DestFile,FinalFile+".ed");
+ // check if we need to fail at this point
+ if (MetaIndex->AuthPass == true && MetaIndex->CheckStopAuthentication(this, Message))
+ return;
- if(Debug)
- std::clog << "Sending to rred method: " << FinalFile << std::endl;
+ string const FinalRelease = MetaIndex->GetFinalFilename();
+ string const FinalReleasegpg = GetFinalFilename();
+ string const FinalInRelease = TransactionManager->GetFinalFilename();
- State = StateApplyDiff;
- Local = true;
- Desc.URI = "rred:" + FinalFile;
- QueueURI(Desc);
- Mode = "rred";
- return;
- }
+ if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
+ {
+ std::string downgrade_msg;
+ strprintf(downgrade_msg, _("The repository '%s' is no longer signed."),
+ MetaIndex->Target.Description.c_str());
+ if(_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
+ {
+ // meh, the users wants to take risks (we still mark the packages
+ // from this repository as unauthenticated)
+ _error->Warning("%s", downgrade_msg.c_str());
+ _error->Warning(_("This is normally not allowed, but the option "
+ "Acquire::AllowDowngradeToInsecureRepositories was "
+ "given to override it."));
+ Status = StatDone;
+ } else {
+ _error->Error("%s", downgrade_msg.c_str());
+ if (TransactionManager->IMSHit == false)
+ Rename(MetaIndex->DestFile, MetaIndex->DestFile + ".FAILED");
+ Item::Failed("Message: " + downgrade_msg, Cnf);
+ TransactionManager->AbortTransaction();
+ return;
+ }
+ }
+ else
+ _error->Warning(_("The data from '%s' is not signed. Packages "
+ "from that repository can not be authenticated."),
+ MetaIndex->Target.Description.c_str());
+ // ensures that a Release.gpg file in the lists/ is removed by the transaction
+ TransactionManager->TransactionStageRemoval(this, DestFile);
- // success in download/apply a diff, queue next (if needed)
- if(State == StateApplyDiff)
+ // only allow going further if the users explicitely wants it
+ if(AllowInsecureRepositories(TransactionManager->MetaIndexParser, TransactionManager, this) == true)
{
- // remove the just applied patch
- available_patches.erase(available_patches.begin());
- unlink((FinalFile + ".ed").c_str());
-
- // move into place
- if(Debug)
+ if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
{
- std::clog << "Moving patched file in place: " << std::endl
- << DestFile << " -> " << FinalFile << std::endl;
+ // 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();
+ }
+ }
}
- Rename(DestFile,FinalFile);
- chmod(FinalFile.c_str(),0644);
- // see if there is more to download
- if(available_patches.empty() == false) {
- new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
- ExpectedHash, ServerSha1, available_patches);
- return Finish();
- } else
- return Finish(true);
+ // we parse the indexes here because at this point the user wanted
+ // a repository that may potentially harm him
+ if (TransactionManager->MetaIndexParser->Load(MetaIndex->DestFile, &ErrorText) == false || MetaIndex->VerifyVendor(Message) == false)
+ /* expired Release files are still a problem you need extra force for */;
+ else
+ MetaIndex->QueueIndexes(true);
+
+ TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
+ }
+
+ // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
+ if (Cnf->LocalOnly == true ||
+ StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
+ {
+ // Ignore this
+ Status = StatDone;
}
}
/*}}}*/
-// AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
-pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire *Owner,
- string const &URI, string const &URIDesc,
- string const &ShortDesc, HashString const &ExpectedHash,
- DiffInfo const &patch,
- std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
- : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash),
- patch(patch),allPatches(allPatches), State(StateFetchDiff)
-{
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
- DestFile += URItoFileName(URI);
+// AcqBaseIndex - Constructor /*{{{*/
+pkgAcqBaseIndex::pkgAcqBaseIndex(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target)
+: pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL)
+{
+}
+ /*}}}*/
+pkgAcqBaseIndex::~pkgAcqBaseIndex() {}
+
+// AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* Get the DiffIndex file first and see if there are patches available
+ * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
+ * patches. If anything goes wrong in that process, it will fall back to
+ * the original packages file
+ */
+pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target)
+ : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), diffs(NULL)
+{
Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
- Description = URIDesc;
Desc.Owner = this;
- Desc.ShortDesc = ShortDesc;
+ Desc.Description = Target.Description + ".diff/Index";
+ Desc.ShortDesc = Target.ShortDesc;
+ Desc.URI = Target.URI + ".diff/Index";
- Desc.URI = RealURI + ".diff/" + patch.file + ".gz";
- Desc.Description = Description + " " + patch.file + string(".pdiff");
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
- DestFile += URItoFileName(RealURI + ".diff/" + patch.file);
+ DestFile = GetPartialFileNameFromURI(Desc.URI);
if(Debug)
- std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
+ std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
QueueURI(Desc);
}
/*}}}*/
-void pkgAcqIndexMergeDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
+// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
+// ---------------------------------------------------------------------
+/* The only header we use is the last-modified header. */
+string pkgAcqDiffIndex::Custom600Headers() const
{
- if(Debug)
- std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
- Complete = false;
- Status = StatDone;
- Dequeue();
+ string const Final = GetFinalFilename();
- // check if we are the first to fail, otherwise we are done here
- State = StateDoneDiff;
- for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
- I != allPatches->end(); ++I)
- if ((*I)->State == StateErrorDiff)
- return;
+ if(Debug)
+ std::clog << "Custom600Header-IMS: " << Final << std::endl;
- // first failure means we should fallback
- State = StateErrorDiff;
- std::clog << "Falling back to normal index file acquire" << std::endl;
- new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc,
- ExpectedHash);
+ struct stat Buf;
+ if (stat(Final.c_str(),&Buf) != 0)
+ return "\nIndex-File: true";
+
+ return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
}
/*}}}*/
-void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,string Md5Hash, /*{{{*/
- pkgAcquire::MethodConfig *Cnf)
+void pkgAcqDiffIndex::QueueOnIMSHit() const /*{{{*/
{
+ // list cleanup needs to know that this file as well as the already
+ // present index is ours, so we create an empty diff to save it for us
+ new pkgAcqIndexDiffs(Owner, TransactionManager, Target);
+}
+ /*}}}*/
+bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/
+{
+ // failing here is fine: our caller will take care of trying to
+ // get the complete file if patching fails
if(Debug)
- std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
-
- Item::Done(Message,Size,Md5Hash,Cnf);
-
- string const FinalFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
-
- if (State == StateFetchDiff)
- {
- // rred expects the patch as $FinalFile.ed.$patchname.gz
- Rename(DestFile, FinalFile + ".ed." + patch.file + ".gz");
+ std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
+ << 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;
- }
+ FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
+ pkgTagFile TF(&Fd);
+ if (_error->PendingError() == true)
+ return false;
- // this is the last completed diff, so we are ready to apply now
- State = StateApplyDiff;
+ pkgTagSection Tags;
+ if(unlikely(TF.Step(Tags) == false))
+ return false;
- if(Debug)
- std::clog << "Sending to rred method: " << FinalFile << std::endl;
+ HashStringList ServerHashes;
+ unsigned long long ServerSize = 0;
- Local = true;
- Desc.URI = "rred:" + FinalFile;
- QueueURI(Desc);
- Mode = "rred";
- return;
- }
- // success in download/apply all diffs, clean up
- else if (State == StateApplyDiff)
+ for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
{
- // see if we really got the expected file
- if(!ExpectedHash.empty() && !ExpectedHash.VerifyFile(DestFile))
- {
- RenameOnError(HashSumMismatch);
- return;
- }
+ std::string tagname = *type;
+ tagname.append("-Current");
+ std::string const tmp = Tags.FindS(tagname.c_str());
+ if (tmp.empty() == true)
+ continue;
- // move the result 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);
+ string hash;
+ unsigned long long size;
+ std::stringstream ss(tmp);
+ ss >> hash >> size;
+ if (unlikely(hash.empty() == true))
+ continue;
+ if (unlikely(ServerSize != 0 && ServerSize != size))
+ continue;
+ ServerHashes.push_back(HashString(*type, hash));
+ ServerSize = size;
+ }
- // otherwise lists cleanup will eat the file
- DestFile = FinalFile;
+ if (ServerHashes.usable() == false)
+ {
+ if (Debug == true)
+ std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Did not find a good hashsum in the index" << std::endl;
+ return false;
+ }
- // 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 CurrentPackagesFile = GetFinalFileNameFromURI(Target.URI);
+ HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
+ if (TargetFileHashes.usable() == false || ServerHashes != TargetFileHashes)
+ {
+ if (Debug == true)
{
- std::string patch = FinalFile + ".ed." + (*I)->patch.file + ".gz";
- unlink(patch.c_str());
+ std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl;
+ printHashSumComparision(CurrentPackagesFile, ServerHashes, TargetFileHashes);
}
-
- // all set and done
- Complete = true;
- if(Debug)
- std::clog << "allDone: " << DestFile << "\n" << std::endl;
- }
-}
- /*}}}*/
-// AcqIndex::AcqIndex - Constructor /*{{{*/
-// ---------------------------------------------------------------------
-/* The package file is added to the queue and a second class is
- instantiated to fetch the revision file */
-pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
- string URI,string URIDesc,string ShortDesc,
- HashString ExpectedHash, string comprExt)
- : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash), Target(0),
- MetaIndexParser(0)
-{
- if(comprExt.empty() == true)
- {
- // autoselect the compression method
- std::vector<std::string> types = APT::Configuration::getCompressionTypes();
- for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
- comprExt.append(*t).append(" ");
- if (comprExt.empty() == false)
- comprExt.erase(comprExt.end()-1);
+ return false;
}
- CompressionExtension = comprExt;
- Verify = true;
-
- Init(URI, URIDesc, ShortDesc);
-}
-pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, IndexTarget const *Target,
- HashString const &ExpectedHash, indexRecords *MetaIndexParser)
- : Item(Owner), RealURI(Target->URI), ExpectedHash(ExpectedHash)
-{
- // autoselect the compression method
- std::vector<std::string> types = APT::Configuration::getCompressionTypes();
- CompressionExtension = "";
- if (ExpectedHash.empty() == false)
+ HashStringList LocalHashes;
+ // try avoiding calculating the hash here as this is costly
+ if (TransactionManager->LastMetaIndexParser != NULL)
+ LocalHashes = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target.MetaKey);
+ if (LocalHashes.usable() == false)
{
- for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
- if (*t == "uncompressed" || MetaIndexParser->Exists(string(Target->MetaKey).append(".").append(*t)) == true)
- CompressionExtension.append(*t).append(" ");
+ FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
+ Hashes LocalHashesCalc(ServerHashes);
+ LocalHashesCalc.AddFD(fd);
+ LocalHashes = LocalHashesCalc.GetHashStringList();
}
- else
+
+ if (ServerHashes == LocalHashes)
{
- for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
- CompressionExtension.append(*t).append(" ");
+ // we have the same sha1 as the server so we are done here
+ if(Debug)
+ std::clog << "pkgAcqDiffIndex: Package file " << CurrentPackagesFile << " is up-to-date" << std::endl;
+ QueueOnIMSHit();
+ return true;
}
- if (CompressionExtension.empty() == false)
- CompressionExtension.erase(CompressionExtension.end()-1);
- // only verify non-optional targets, see acquire-item.h for a FIXME
- // to make this more flexible
- if (Target->IsOptional())
- Verify = false;
- else
- Verify = true;
+ if(Debug)
+ std::clog << "Server-Current: " << ServerHashes.find(NULL)->toStr() << " and we start at "
+ << CurrentPackagesFile << " " << LocalHashes.FileSize() << " " << LocalHashes.find(NULL)->toStr() << std::endl;
- // we need this in Init()
- this->Target = Target;
- this->MetaIndexParser = MetaIndexParser;
+ // parse all of (provided) history
+ vector<DiffInfo> available_patches;
+ bool firstAcceptedHashes = true;
+ for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+ {
+ if (LocalHashes.find(*type) == NULL)
+ continue;
- Init(Target->URI, Target->Description, Target->ShortDesc);
-}
- /*}}}*/
-// AcqIndex::Init - defered Constructor /*{{{*/
-void pkgAcqIndex::Init(string const &URI, string const &URIDesc, string const &ShortDesc) {
- Decompression = false;
- Erase = false;
+ std::string tagname = *type;
+ tagname.append("-History");
+ std::string const tmp = Tags.FindS(tagname.c_str());
+ if (tmp.empty() == true)
+ continue;
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
- DestFile += URItoFileName(URI);
+ string hash, filename;
+ unsigned long long size;
+ std::stringstream ss(tmp);
- std::string const comprExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
- std::string MetaKey;
- if (comprExt == "uncompressed")
- {
- Desc.URI = URI;
- if(Target)
- MetaKey = string(Target->MetaKey);
+ while (ss >> hash >> size >> filename)
+ {
+ if (unlikely(hash.empty() == true || filename.empty() == true))
+ continue;
+
+ // see if we have a record for this file already
+ std::vector<DiffInfo>::iterator cur = available_patches.begin();
+ for (; cur != available_patches.end(); ++cur)
+ {
+ if (cur->file != filename)
+ continue;
+ cur->result_hashes.push_back(HashString(*type, hash));
+ break;
+ }
+ if (cur != available_patches.end())
+ continue;
+ if (firstAcceptedHashes == true)
+ {
+ DiffInfo next;
+ next.file = filename;
+ next.result_hashes.push_back(HashString(*type, hash));
+ next.result_hashes.FileSize(size);
+ available_patches.push_back(next);
+ }
+ else
+ {
+ if (Debug == true)
+ std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
+ << " wasn't in the list for the first parsed hash! (history)" << std::endl;
+ break;
+ }
+ }
+ firstAcceptedHashes = false;
}
- else
+
+ if (unlikely(available_patches.empty() == true))
{
- Desc.URI = URI + '.' + comprExt;
- if(Target)
- MetaKey = string(Target->MetaKey) + '.' + comprExt;
+ if (Debug)
+ std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
+ << "Couldn't find any patches for the patch series." << std::endl;
+ return false;
}
- // load the filesize
- if(MetaIndexParser)
+ for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
{
- indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
- if(Record)
- FileSize = Record->Size;
- }
+ if (LocalHashes.find(*type) == NULL)
+ continue;
- Desc.Description = URIDesc;
- Desc.Owner = this;
- Desc.ShortDesc = ShortDesc;
+ std::string tagname = *type;
+ tagname.append("-Patches");
+ std::string const tmp = Tags.FindS(tagname.c_str());
+ if (tmp.empty() == true)
+ continue;
- QueueURI(Desc);
-}
- /*}}}*/
-// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
-// ---------------------------------------------------------------------
-/* The only header we use is the last-modified header. */
-string pkgAcqIndex::Custom600Headers()
-{
- string Final = _config->FindDir("Dir::State::lists");
- Final += URItoFileName(RealURI);
- if (_config->FindB("Acquire::GzipIndexes",false))
- Final += ".gz";
-
- string msg = "\nIndex-File: true";
- // FIXME: this really should use "IndexTarget::IsOptional()" but that
- // seems to be difficult without breaking ABI
- if (ShortDesc().find("Translation") != 0)
- msg += "\nFail-Ignore: true";
- struct stat Buf;
- if (stat(Final.c_str(),&Buf) == 0)
- msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
+ string hash, filename;
+ unsigned long long size;
+ std::stringstream ss(tmp);
- return msg;
-}
- /*}}}*/
-void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
-{
- size_t const nextExt = CompressionExtension.find(' ');
- if (nextExt != std::string::npos)
- {
- CompressionExtension = CompressionExtension.substr(nextExt+1);
- Init(RealURI, Desc.Description, Desc.ShortDesc);
- return;
+ while (ss >> hash >> size >> filename)
+ {
+ if (unlikely(hash.empty() == true || filename.empty() == true))
+ continue;
+
+ // see if we have a record for this file already
+ std::vector<DiffInfo>::iterator cur = available_patches.begin();
+ for (; cur != available_patches.end(); ++cur)
+ {
+ if (cur->file != filename)
+ continue;
+ if (cur->patch_hashes.empty())
+ cur->patch_hashes.FileSize(size);
+ cur->patch_hashes.push_back(HashString(*type, hash));
+ break;
+ }
+ if (cur != available_patches.end())
+ continue;
+ if (Debug == true)
+ std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
+ << " wasn't in the list for the first parsed hash! (patches)" << std::endl;
+ break;
+ }
}
- // on decompression failure, remove bad versions in partial/
- if (Decompression && Erase) {
- string s = _config->FindDir("Dir::State::lists") + "partial/";
- s.append(URItoFileName(RealURI));
- unlink(s.c_str());
- }
+ for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+ {
+ std::string tagname = *type;
+ tagname.append("-Download");
+ std::string const tmp = Tags.FindS(tagname.c_str());
+ if (tmp.empty() == true)
+ continue;
- Item::Failed(Message,Cnf);
-}
- /*}}}*/
-// AcqIndex::Done - Finished a fetch /*{{{*/
-// ---------------------------------------------------------------------
-/* This goes through a number of states.. On the initial fetch the
- method could possibly return an alternate filename which points
- to the uncompressed version of the file. If this is so the file
- is copied into the partial directory. In all other cases the file
- is decompressed with a gzip uri. */
-void pkgAcqIndex::Done(string Message,unsigned long long Size,string Hash,
- pkgAcquire::MethodConfig *Cfg)
-{
- Item::Done(Message,Size,Hash,Cfg);
+ string hash, filename;
+ unsigned long long size;
+ std::stringstream ss(tmp);
- if (Decompression == true)
- {
- if (_config->FindB("Debug::pkgAcquire::Auth", false))
+ // FIXME: all of pdiff supports only .gz compressed patches
+ while (ss >> hash >> size >> filename)
{
- std::cerr << std::endl << RealURI << ": Computed Hash: " << Hash;
- std::cerr << " Expected Hash: " << ExpectedHash.toStr() << std::endl;
- }
+ if (unlikely(hash.empty() == true || filename.empty() == true))
+ continue;
+ if (unlikely(APT::String::Endswith(filename, ".gz") == false))
+ continue;
+ filename.erase(filename.length() - 3);
- if (!ExpectedHash.empty() && ExpectedHash.toStr() != Hash)
- {
- RenameOnError(HashSumMismatch);
- return;
+ // see if we have a record for this file already
+ std::vector<DiffInfo>::iterator cur = available_patches.begin();
+ for (; cur != available_patches.end(); ++cur)
+ {
+ if (cur->file != filename)
+ continue;
+ if (cur->download_hashes.empty())
+ cur->download_hashes.FileSize(size);
+ cur->download_hashes.push_back(HashString(*type, hash));
+ break;
+ }
+ if (cur != available_patches.end())
+ continue;
+ if (Debug == true)
+ std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
+ << " wasn't in the list for the first parsed hash! (download)" << std::endl;
+ break;
}
+ }
- /* Verify the index file for correctness (all indexes must
- * have a Package field) (LP: #346386) (Closes: #627642) */
- if (Verify == true)
- {
- FileFd fd(DestFile, FileFd::ReadOnly);
- // Only test for correctness if the file is not empty (empty is ok)
- if (fd.FileSize() > 0)
- {
- pkgTagSection sec;
- pkgTagFile tag(&fd);
- // all our current indexes have a field 'Package' in each section
- if (_error->PendingError() == true || tag.Step(sec) == false || sec.Exists("Package") == false)
- {
- RenameOnError(InvalidFormat);
- return;
- }
- }
- }
-
- // Done, move it into position
- string FinalFile = _config->FindDir("Dir::State::lists");
- FinalFile += URItoFileName(RealURI);
- Rename(DestFile,FinalFile);
- chmod(FinalFile.c_str(),0644);
+ bool foundStart = false;
+ for (std::vector<DiffInfo>::iterator cur = available_patches.begin();
+ cur != available_patches.end(); ++cur)
+ {
+ if (LocalHashes != cur->result_hashes)
+ continue;
- /* We restore the original name to DestFile so that the clean operation
- will work OK */
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
- DestFile += URItoFileName(RealURI);
-
- // Remove the compressed version.
- if (Erase == true)
- unlink(DestFile.c_str());
+ available_patches.erase(available_patches.begin(), cur);
+ foundStart = true;
+ break;
+ }
- return;
+ if (foundStart == false || unlikely(available_patches.empty() == true))
+ {
+ if (Debug)
+ std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
+ << "Couldn't find the start of the patch series." << std::endl;
+ return false;
}
- Erase = false;
- Complete = true;
-
- // Handle the unzipd case
- string FileName = LookupTag(Message,"Alt-Filename");
- if (FileName.empty() == 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())
{
- // The files timestamp matches
- if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
- return;
- Decompression = true;
- Local = true;
- DestFile += ".decomp";
- Desc.URI = "copy:" + FileName;
- QueueURI(Desc);
- Mode = "copy";
- return;
+ if (Debug)
+ std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
+ << ") so fallback to complete download" << std::endl;
+ return false;
}
- FileName = LookupTag(Message,"Filename");
- if (FileName.empty() == true)
+ // 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)
{
- Status = StatError;
- ErrorText = "Method gave a blank filename";
+ if (Debug)
+ std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
+ << ") so fallback to complete download" << std::endl;
+ return false;
}
- std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
+ // we have something, queue the diffs
+ string::size_type const last_space = Description.rfind(" ");
+ if(last_space != string::npos)
+ Description.erase(last_space, Description.size()-last_space);
- // The files timestamp matches
- if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) {
- if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
- // Update DestFile for .gz suffix so that the clean operation keeps it
- DestFile += ".gz";
- return;
- }
+ /* decide if we should download patches one by one or in one go:
+ The first is good if the server merges patches, but many don't so client
+ based merging can be attempt in which case the second is better.
+ "bad things" will happen if patches are merged on the server,
+ but client side merging is attempt as well */
+ bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
+ if (pdiff_merge == true)
+ {
+ // reprepro adds this flag if it has merged patches on the server
+ std::string const precedence = Tags.FindS("X-Patch-Precedence");
+ pdiff_merge = (precedence != "merged");
+ }
- if (FileName == DestFile)
- Erase = true;
+ if (pdiff_merge == false)
+ new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
else
- Local = true;
-
- string decompProg;
-
- // If we enable compressed indexes and already have gzip, keep it
- if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz" && !Local) {
- string FinalFile = _config->FindDir("Dir::State::lists");
- FinalFile += URItoFileName(RealURI) + ".gz";
- Rename(DestFile,FinalFile);
- chmod(FinalFile.c_str(),0644);
-
- // Update DestFile for .gz suffix so that the clean operation keeps it
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
- DestFile += URItoFileName(RealURI) + ".gz";
- return;
- }
-
- // get the binary name for your used compression type
- decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
- if(decompProg.empty() == false);
- else if(compExt == "uncompressed")
- decompProg = "copy";
- else {
- _error->Error("Unsupported extension: %s", compExt.c_str());
- return;
+ {
+ diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
+ for(size_t i = 0; i < available_patches.size(); ++i)
+ (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, TransactionManager,
+ Target,
+ available_patches[i],
+ diffs);
}
- Decompression = true;
- DestFile += ".decomp";
- Desc.URI = decompProg + ":" + FileName;
- QueueURI(Desc);
-
- // FIXME: this points to a c++ string that goes out of scope
- Mode = decompProg.c_str();
-}
- /*}}}*/
-// AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
-// ---------------------------------------------------------------------
-/* The Translation file is added to the queue */
-pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
- string URI,string URIDesc,string ShortDesc)
- : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashString(), "")
-{
-}
-pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const *Target,
- HashString const &ExpectedHash, indexRecords *MetaIndexParser)
- : pkgAcqIndex(Owner, Target, ExpectedHash, MetaIndexParser)
-{
- // load the filesize
- indexRecords::checkSum *Record = MetaIndexParser->Lookup(string(Target->MetaKey));
- if(Record)
- FileSize = Record->Size;
-}
- /*}}}*/
-// AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/
-// ---------------------------------------------------------------------
-string pkgAcqIndexTrans::Custom600Headers()
-{
- string Final = _config->FindDir("Dir::State::lists");
- Final += URItoFileName(RealURI);
-
- struct stat Buf;
- if (stat(Final.c_str(),&Buf) != 0)
- return "\nFail-Ignore: true\nIndex-File: true";
- return "\nFail-Ignore: true\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
+ Complete = false;
+ Status = StatDone;
+ Dequeue();
+ return true;
}
/*}}}*/
-// AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
+void pkgAcqDiffIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
{
- size_t const nextExt = CompressionExtension.find(' ');
- if (nextExt != std::string::npos)
- {
- CompressionExtension = CompressionExtension.substr(nextExt+1);
- Init(RealURI, Desc.Description, Desc.ShortDesc);
- Status = StatIdle;
- return;
- }
+ Item::Failed(Message,Cnf);
+ Status = StatDone;
- if (Cnf->LocalOnly == true ||
- StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
- {
- // Ignore this
- Status = StatDone;
- Complete = false;
- Dequeue();
- return;
- }
+ if(Debug)
+ std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
+ << "Falling back to normal index file acquire" << std::endl;
- Item::Failed(Message,Cnf);
+ new pkgAcqIndex(Owner, TransactionManager, Target);
}
/*}}}*/
-pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
- string URI,string URIDesc,string ShortDesc,
- string MetaIndexURI, string MetaIndexURIDesc,
- string MetaIndexShortDesc,
- const vector<IndexTarget*>* IndexTargets,
- indexRecords* MetaIndexParser) :
- Item(Owner), RealURI(URI), MetaIndexURI(MetaIndexURI),
- MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
- MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
+void pkgAcqDiffIndex::Done(string const &Message,HashStringList const &Hashes, /*{{{*/
+ pkgAcquire::MethodConfig const * const Cnf)
{
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
- DestFile += URItoFileName(URI);
+ if(Debug)
+ std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
- // remove any partial downloaded sig-file in partial/.
- // it may confuse proxies and is too small to warrant a
- // partial download anyway
- unlink(DestFile.c_str());
+ Item::Done(Message, Hashes, Cnf);
- // Create the item
- Desc.Description = URIDesc;
- Desc.Owner = this;
- Desc.ShortDesc = ShortDesc;
- Desc.URI = URI;
-
- string Final = _config->FindDir("Dir::State::lists");
- Final += URItoFileName(RealURI);
- if (RealFileExists(Final) == true)
+ string const FinalFile = GetFinalFilename();
+ if(StringToBool(LookupTag(Message,"IMS-Hit"),false))
+ DestFile = FinalFile;
+
+ if(ParseDiffIndex(DestFile) == false)
{
- // File was already in place. It needs to be re-downloaded/verified
- // because Release might have changed, we do give it a different
- // name than DestFile because otherwise the http method will
- // send If-Range requests and there are too many broken servers
- // out there that do not understand them
- LastGoodSig = DestFile+".reverify";
- Rename(Final,LastGoodSig);
+ Failed("Message: Couldn't parse pdiff index", Cnf);
+ // queue for final move - this should happen even if we fail
+ // while parsing (e.g. on sizelimit) and download the complete file.
+ TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
+ return;
}
- // we expect the indextargets + one additional Release file
- ExpectedAdditionalItems = IndexTargets->size() + 1;
+ TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
- QueueURI(Desc);
+ Complete = true;
+ Status = StatDone;
+ Dequeue();
+
+ return;
}
/*}}}*/
-pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
+pkgAcqDiffIndex::~pkgAcqDiffIndex()
{
- // if the file was never queued undo file-changes done in the constructor
- if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false &&
- LastGoodSig.empty() == false)
- {
- string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
- if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
- Rename(LastGoodSig, Final);
- }
-
+ if (diffs != NULL)
+ delete diffs;
}
- /*}}}*/
-// pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
+
+// AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
// ---------------------------------------------------------------------
-/* The only header we use is the last-modified header. */
-string pkgAcqMetaSig::Custom600Headers()
+/* The package diff is added to the queue. one object is constructed
+ * for each diff and the index
+ */
+pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target,
+ vector<DiffInfo> const &diffs)
+ : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
+ available_patches(diffs)
{
- struct stat Buf;
- if (stat(LastGoodSig.c_str(),&Buf) != 0)
- return "\nIndex-File: true";
+ DestFile = GetPartialFileNameFromURI(Target.URI);
- return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
-}
+ Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
-void pkgAcqMetaSig::Done(string Message,unsigned long long Size,string MD5,
- pkgAcquire::MethodConfig *Cfg)
-{
- Item::Done(Message,Size,MD5,Cfg);
+ Desc.Owner = this;
+ Description = Target.Description;
+ Desc.ShortDesc = Target.ShortDesc;
- string FileName = LookupTag(Message,"Filename");
- if (FileName.empty() == true)
+ if(available_patches.empty() == true)
{
- Status = StatError;
- ErrorText = "Method gave a blank filename";
- return;
+ // we are done (yeah!), check hashes against the final file
+ DestFile = GetFinalFileNameFromURI(Target.URI);
+ Finish(true);
}
-
- if (FileName != DestFile)
+ else
{
- // We have to copy it into place
- Local = true;
- Desc.URI = "copy:" + FileName;
- QueueURI(Desc);
- return;
- }
-
- Complete = true;
-
- // at this point pkgAcqMetaIndex takes over
- ExpectedAdditionalItems = 0;
-
- // put the last known good file back on i-m-s hit (it will
- // be re-verified again)
- // Else do nothing, we have the new file in DestFile then
- if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
- Rename(LastGoodSig, DestFile);
-
- // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
- new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc,
- MetaIndexShortDesc, DestFile, IndexTargets,
- MetaIndexParser);
+ // patching needs to be bootstrapped with the 'old' version
+ std::string const PartialFile = GetPartialFileNameFromURI(Target.URI);
+ if (RealFileExists(PartialFile) == false)
+ {
+ if (symlink(GetFinalFilename().c_str(), PartialFile.c_str()) != 0)
+ {
+ Failed("Link creation of " + PartialFile + " to " + GetFinalFilename() + " failed", NULL);
+ return;
+ }
+ }
+ // get the next diff
+ State = StateFetchDiff;
+ QueueNextDiff();
+ }
}
/*}}}*/
-void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
+void pkgAcqIndexDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
{
- string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
+ Item::Failed(Message,Cnf);
+ Status = StatDone;
- // at this point pkgAcqMetaIndex takes over
- ExpectedAdditionalItems = 0;
+ if(Debug)
+ std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
+ << "Falling back to normal index file acquire" << std::endl;
+ DestFile = GetPartialFileNameFromURI(Target.URI);
+ RenameOnError(PDiffError);
+ std::string const patchname = GetDiffsPatchFileName(DestFile);
+ if (RealFileExists(patchname))
+ rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
+ new pkgAcqIndex(Owner, TransactionManager, Target);
+ Finish();
+}
+ /*}}}*/
+// Finish - helper that cleans the item out of the fetcher queue /*{{{*/
+void pkgAcqIndexDiffs::Finish(bool allDone)
+{
+ if(Debug)
+ std::clog << "pkgAcqIndexDiffs::Finish(): "
+ << allDone << " "
+ << Desc.URI << std::endl;
- // if we get a network error we fail gracefully
- if(Status == StatTransientNetworkError)
+ // we restore the original name, this is required, otherwise
+ // the file will be cleaned
+ if(allDone)
{
- Item::Failed(Message,Cnf);
- // move the sigfile back on transient network failures
- if(FileExists(LastGoodSig))
- Rename(LastGoodSig,Final);
-
- // set the status back to , Item::Failed likes to reset it
- Status = pkgAcquire::Item::StatTransientNetworkError;
- return;
- }
+ TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
- // Delete any existing sigfile when the acquire failed
- unlink(Final.c_str());
-
- // queue a pkgAcqMetaIndex with no sigfile
- new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
- "", IndexTargets, MetaIndexParser);
-
- if (Cnf->LocalOnly == true ||
- StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
- {
- // Ignore this
+ // this is for the "real" finish
+ Complete = true;
Status = StatDone;
- Complete = false;
Dequeue();
+ if(Debug)
+ std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
return;
}
-
- Item::Failed(Message,Cnf);
+
+ if(Debug)
+ std::clog << "Finishing: " << Desc.URI << std::endl;
+ Complete = false;
+ Status = StatDone;
+ Dequeue();
+ return;
}
/*}}}*/
-pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
- string URI,string URIDesc,string ShortDesc,
- string SigFile,
- const vector<struct IndexTarget*>* IndexTargets,
- indexRecords* MetaIndexParser) :
- Item(Owner), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
- MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
+bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
{
- DestFile = _config->FindDir("Dir::State::lists") + "partial/";
- DestFile += URItoFileName(URI);
-
- // Create the item
- Desc.Description = URIDesc;
- Desc.Owner = this;
- Desc.ShortDesc = ShortDesc;
- Desc.URI = URI;
+ // calc sha1 of the just patched file
+ std::string const FinalFile = GetPartialFileNameFromURI(Target.URI);
- // we expect more item
- ExpectedAdditionalItems = IndexTargets->size();
+ if(!FileExists(FinalFile))
+ {
+ Failed("Message: No FinalFile " + FinalFile + " available", NULL);
+ return false;
+ }
- QueueURI(Desc);
-}
- /*}}}*/
-// pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
-// ---------------------------------------------------------------------
-/* The only header we use is the last-modified header. */
-string pkgAcqMetaIndex::Custom600Headers()
-{
- string Final = _config->FindDir("Dir::State::lists");
- Final += URItoFileName(RealURI);
-
- struct stat Buf;
- if (stat(Final.c_str(),&Buf) != 0)
- return "\nIndex-File: true";
-
- return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
-}
- /*}}}*/
-void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,string Hash, /*{{{*/
- pkgAcquire::MethodConfig *Cfg)
-{
- Item::Done(Message,Size,Hash,Cfg);
+ FileFd fd(FinalFile, FileFd::ReadOnly);
+ Hashes LocalHashesCalc;
+ LocalHashesCalc.AddFD(fd);
+ HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
- // MetaIndexes are done in two passes: one to download the
- // metaindex with an appropriate method, and a second to verify it
- // with the gpgv method
+ if(Debug)
+ std::clog << "QueueNextDiff: " << FinalFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
- if (AuthPass == true)
+ HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
+ if (unlikely(LocalHashes.usable() == false || TargetFileHashes.usable() == false))
{
- AuthDone(Message);
-
- // all cool, move Release file into place
- Complete = true;
+ Failed("Local/Expected hashes are not usable", NULL);
+ return false;
}
- else
+
+
+ // final file reached before all patches are applied
+ if(LocalHashes == TargetFileHashes)
{
- RetrievalDone(Message);
- if (!Complete)
- // Still more retrieving to do
- return;
+ Finish(true);
+ return true;
+ }
- if (SigFile == "")
- {
- // There was no signature file, so we are finished. Download
- // the indexes and do only hashsum verification if possible
- MetaIndexParser->Load(DestFile);
- QueueIndexes(false);
- }
- else
- {
- // There was a signature file, so pass it to gpgv for
- // verification
-
- if (_config->FindB("Debug::pkgAcquire::Auth", false))
- std::cerr << "Metaindex acquired, queueing gpg verification ("
- << SigFile << "," << DestFile << ")\n";
- AuthPass = true;
- Desc.URI = "gpgv:" + SigFile;
- QueueURI(Desc);
- Mode = "gpgv";
- return;
- }
+ // remove all patches until the next matching patch is found
+ // this requires the Index file to be ordered
+ for(vector<DiffInfo>::iterator I = available_patches.begin();
+ available_patches.empty() == false &&
+ I != available_patches.end() &&
+ I->result_hashes != LocalHashes;
+ ++I)
+ {
+ available_patches.erase(I);
}
- if (Complete == true)
+ // error checking and falling back if no patch was found
+ if(available_patches.empty() == true)
{
- string FinalFile = _config->FindDir("Dir::State::lists");
- FinalFile += URItoFileName(RealURI);
- if (SigFile == DestFile)
- SigFile = FinalFile;
- Rename(DestFile,FinalFile);
- chmod(FinalFile.c_str(),0644);
- DestFile = FinalFile;
+ Failed("No patches left to reach target", NULL);
+ return false;
}
+
+ // queue the right diff
+ Desc.URI = Target.URI + ".diff/" + available_patches[0].file + ".gz";
+ Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
+ DestFile = GetPartialFileNameFromURI(Target.URI + ".diff/" + available_patches[0].file);
+
+ if(Debug)
+ std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
+
+ QueueURI(Desc);
+
+ return true;
}
/*}}}*/
-void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
+void pkgAcqIndexDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
+ pkgAcquire::MethodConfig const * const Cnf)
{
- // We have just finished downloading a Release file (it is not
- // verified yet)
+ if(Debug)
+ std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
- string FileName = LookupTag(Message,"Filename");
- if (FileName.empty() == true)
- {
- Status = StatError;
- ErrorText = "Method gave a blank filename";
- return;
- }
+ Item::Done(Message, Hashes, Cnf);
+
+ std::string const FinalFile = GetPartialFileNameFromURI(Target.URI);
+ std::string const PatchFile = GetDiffsPatchFileName(FinalFile);
- if (FileName != DestFile)
+ // 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 = "copy:" + FileName;
+ Desc.URI = "rred:" + FinalFile;
QueueURI(Desc);
+ SetActiveSubprocess("rred");
return;
}
- // make sure to verify against the right file on I-M-S hit
- IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
- if(IMSHit)
+ // success in download/apply a diff, queue next (if needed)
+ if(State == StateApplyDiff)
{
- string FinalFile = _config->FindDir("Dir::State::lists");
- FinalFile += URItoFileName(RealURI);
- if (SigFile == DestFile)
+ // remove the just applied patch
+ available_patches.erase(available_patches.begin());
+ unlink(PatchFile.c_str());
+
+ // move into place
+ if(Debug)
{
- SigFile = FinalFile;
- // constructor of pkgAcqMetaClearSig moved it out of the way,
- // now move it back in on IMS hit for the 'old' file
- string const OldClearSig = DestFile + ".reverify";
- if (RealFileExists(OldClearSig) == true)
- Rename(OldClearSig, FinalFile);
+ std::clog << "Moving patched file in place: " << std::endl
+ << DestFile << " -> " << FinalFile << std::endl;
}
- DestFile = FinalFile;
+ 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);
}
- Complete = true;
}
/*}}}*/
-void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
+std::string pkgAcqIndexDiffs::Custom600Headers() const /*{{{*/
{
- // At this point, the gpgv method has succeeded, so there is a
- // valid signature from a key in the trusted keyring. We
- // perform additional verification of its contents, and use them
- // to verify the indexes we are about to download
+ if(State != StateApplyDiff)
+ return pkgAcqBaseIndex::Custom600Headers();
+ std::ostringstream patchhashes;
+ HashStringList const ExpectedHashes = available_patches[0].patch_hashes;
+ for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
+ patchhashes << "\nPatch-0-" << hs->HashType() << "-Hash: " << hs->HashValue();
+ patchhashes << pkgAcqBaseIndex::Custom600Headers();
+ return patchhashes.str();
+}
+ /*}}}*/
+pkgAcqIndexDiffs::~pkgAcqIndexDiffs() {}
- if (!MetaIndexParser->Load(DestFile))
- {
- Status = StatAuthError;
- ErrorText = MetaIndexParser->ErrorText;
- return;
- }
+// AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
+pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target,
+ DiffInfo const &patch,
+ std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
+ : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
+ patch(patch), allPatches(allPatches), State(StateFetchDiff)
+{
+ Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
- if (!VerifyVendor(Message))
- {
- return;
- }
+ Desc.Owner = this;
+ Description = Target.Description;
+ Desc.ShortDesc = Target.ShortDesc;
- if (_config->FindB("Debug::pkgAcquire::Auth", false))
- std::cerr << "Signature verification succeeded: "
- << DestFile << std::endl;
+ Desc.URI = Target.URI + ".diff/" + patch.file + ".gz";
+ Desc.Description = Description + " " + patch.file + string(".pdiff");
- // Download further indexes with verification
- QueueIndexes(true);
+ DestFile = GetPartialFileNameFromURI(Target.URI + ".diff/" + patch.file);
- // is it a clearsigned MetaIndex file?
- if (DestFile == SigFile)
- return;
+ if(Debug)
+ std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
- // Done, move signature file into position
- string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
- URItoFileName(RealURI) + ".gpg";
- Rename(SigFile,VerifiedSigFile);
- chmod(VerifiedSigFile.c_str(),0644);
+ QueueURI(Desc);
}
/*}}}*/
-void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/
+void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
{
-#if 0
- /* Reject invalid, existing Release files (LP: #346386) (Closes: #627642)
- * FIXME: Disabled; it breaks unsigned repositories without hashes */
- if (!verify && FileExists(DestFile) && !MetaIndexParser->Load(DestFile))
- {
- Status = StatError;
- ErrorText = MetaIndexParser->ErrorText;
- return;
- }
-#endif
- bool transInRelease = false;
- {
- std::vector<std::string> const keys = MetaIndexParser->MetaKeys();
- for (std::vector<std::string>::const_iterator k = keys.begin(); k != keys.end(); ++k)
- // FIXME: Feels wrong to check for hardcoded string here, but what should we do else…
- if (k->find("Translation-") != std::string::npos)
- {
- transInRelease = true;
- break;
- }
- }
+ if(Debug)
+ std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
- // at this point the real Items are loaded in the fetcher
- ExpectedAdditionalItems = 0;
+ Item::Failed(Message,Cnf);
+ Status = StatDone;
- for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
- Target != IndexTargets->end();
- ++Target)
+ // check if we are the first to fail, otherwise we are done here
+ State = StateDoneDiff;
+ for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
+ I != allPatches->end(); ++I)
+ if ((*I)->State == StateErrorDiff)
+ return;
+
+ // first failure means we should fallback
+ State = StateErrorDiff;
+ if (Debug)
+ std::clog << "Falling back to normal index file acquire" << std::endl;
+ DestFile = GetPartialFileNameFromURI(Target.URI);
+ RenameOnError(PDiffError);
+ std::string const patchname = GetMergeDiffsPatchFileName(DestFile, patch.file);
+ if (RealFileExists(patchname))
+ rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
+ new pkgAcqIndex(Owner, TransactionManager, Target);
+}
+ /*}}}*/
+void pkgAcqIndexMergeDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ if(Debug)
+ std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
+
+ Item::Done(Message, Hashes, Cnf);
+
+ string const FinalFile = GetPartialFileNameFromURI(Target.URI);
+ if (State == StateFetchDiff)
{
- HashString ExpectedIndexHash;
- indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
- bool compressedAvailable = false;
- if (Record == NULL)
- {
- if ((*Target)->IsOptional() == true)
- {
- std::vector<std::string> types = APT::Configuration::getCompressionTypes();
- for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
- if (MetaIndexParser->Exists((*Target)->MetaKey + "." + *t) == true)
- {
- compressedAvailable = true;
- break;
- }
- }
- else if (verify == true)
+ 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)
{
- Status = StatAuthError;
- strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
+ if(Debug)
+ std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
return;
}
- }
- else
+
+ // this is the last completed diff, so we are ready to apply now
+ State = StateApplyDiff;
+
+ // patching needs to be bootstrapped with the 'old' version
+ if (symlink(GetFinalFilename().c_str(), FinalFile.c_str()) != 0)
{
- ExpectedIndexHash = Record->Hash;
- if (_config->FindB("Debug::pkgAcquire::Auth", false))
- {
- std::cerr << "Queueing: " << (*Target)->URI << std::endl;
- std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl;
- std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
- }
- if (verify == true && ExpectedIndexHash.empty() == true && (*Target)->IsOptional() == false)
- {
- Status = StatAuthError;
- strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
- return;
- }
+ Failed("Link creation of " + FinalFile + " to " + GetFinalFilename() + " failed", NULL);
+ return;
}
- if ((*Target)->IsOptional() == true)
+ 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)
+ {
+ // move the result into place
+ std::string const Final = GetFinalFilename();
+ 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)
{
- if ((*Target)->IsSubIndex() == true)
- new pkgAcqSubIndex(Owner, (*Target)->URI, (*Target)->Description,
- (*Target)->ShortDesc, ExpectedIndexHash);
- else if (transInRelease == false || Record != NULL || compressedAvailable == true)
- {
- if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
- MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
- new pkgAcqDiffIndex(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
- else
- new pkgAcqIndexTrans(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
- }
- continue;
+ std::string const PartialFile = GetPartialFileNameFromURI(Target.URI);
+ std::string const patch = GetMergeDiffsPatchFileName(PartialFile, (*I)->patch.file);
+ unlink(patch.c_str());
}
+ unlink(FinalFile.c_str());
- /* Queue Packages file (either diff or full packages files, depending
- on the users option) - we also check if the PDiff Index file is listed
- in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
- instead, but passing the required info to it is to much hassle */
- if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
- MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
- new pkgAcqDiffIndex(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
- else
- new pkgAcqIndex(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
+ // all set and done
+ Complete = true;
+ if(Debug)
+ std::clog << "allDone: " << DestFile << "\n" << std::endl;
}
}
/*}}}*/
-bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
+std::string pkgAcqIndexMergeDiffs::Custom600Headers() 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)
+ if(State != StateApplyDiff)
+ return pkgAcqBaseIndex::Custom600Headers();
+ std::ostringstream patchhashes;
+ unsigned int seen_patches = 0;
+ for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
+ I != allPatches->end(); ++I)
{
- string::size_type start = pos+strlen("NO_PUBKEY ");
- string Fingerprint = Message.substr(start, Message.find("\n")-start);
- missingkeys += (Fingerprint);
+ HashStringList const ExpectedHashes = (*I)->patch.patch_hashes;
+ for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
+ patchhashes << "\nPatch-" << seen_patches << "-" << hs->HashType() << "-Hash: " << hs->HashValue();
+ ++seen_patches;
}
- if(!missingkeys.empty())
- _error->Warning("%s", (msg + missingkeys).c_str());
+ patchhashes << pkgAcqBaseIndex::Custom600Headers();
+ return patchhashes.str();
+}
+ /*}}}*/
+pkgAcqIndexMergeDiffs::~pkgAcqIndexMergeDiffs() {}
- string Transformed = MetaIndexParser->GetExpectedDist();
+// AcqIndex::AcqIndex - Constructor /*{{{*/
+pkgAcqIndex::pkgAcqIndex(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target)
+ : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), Stage(STAGE_DOWNLOAD)
+{
+ // autoselect the compression method
+ AutoSelectCompression();
+ Init(Target.URI, Target.Description, Target.ShortDesc);
- if (Transformed == "../project/experimental")
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "New pkgIndex with TransactionManager "
+ << TransactionManager << std::endl;
+}
+ /*}}}*/
+// AcqIndex::AutoSelectCompression - Select compression /*{{{*/
+void pkgAcqIndex::AutoSelectCompression()
+{
+ std::vector<std::string> types = APT::Configuration::getCompressionTypes();
+ CompressionExtensions = "";
+ if (TransactionManager->MetaIndexParser != NULL && TransactionManager->MetaIndexParser->Exists(Target.MetaKey))
{
- Transformed = "experimental";
+ for (std::vector<std::string>::const_iterator t = types.begin();
+ t != types.end(); ++t)
+ {
+ std::string CompressedMetaKey = string(Target.MetaKey).append(".").append(*t);
+ if (*t == "uncompressed" ||
+ TransactionManager->MetaIndexParser->Exists(CompressedMetaKey) == true)
+ CompressionExtensions.append(*t).append(" ");
+ }
}
-
- pos = Transformed.rfind('/');
- if (pos != string::npos)
+ else
{
- Transformed = Transformed.substr(0, pos);
+ for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
+ CompressionExtensions.append(*t).append(" ");
}
+ if (CompressionExtensions.empty() == false)
+ CompressionExtensions.erase(CompressionExtensions.end()-1);
+}
+ /*}}}*/
+// AcqIndex::Init - defered Constructor /*{{{*/
+void pkgAcqIndex::Init(string const &URI, string const &URIDesc,
+ string const &ShortDesc)
+{
+ Stage = STAGE_DOWNLOAD;
- if (Transformed == ".")
- {
- Transformed = "";
- }
+ DestFile = GetPartialFileNameFromURI(URI);
- if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
- MetaIndexParser->GetValidUntil() > 0) {
- time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
- if (invalid_since > 0)
- // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
- // the time since then the file is invalid - formated in the same way as in
- // the download progress display (e.g. 7d 3h 42min 1s)
- return _error->Error(
- _("Release file for %s is expired (invalid since %s). "
- "Updates for this repository will not be applied."),
- RealURI.c_str(), TimeToStr(invalid_since).c_str());
+ size_t const nextExt = CompressionExtensions.find(' ');
+ if (nextExt == std::string::npos)
+ {
+ CurrentCompressionExtension = CompressionExtensions;
+ CompressionExtensions.clear();
}
-
- if (_config->FindB("Debug::pkgAcquire::Auth", false))
+ else
{
- std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
- std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
- std::cerr << "Transformed Dist: " << Transformed << std::endl;
+ CurrentCompressionExtension = CompressionExtensions.substr(0, nextExt);
+ CompressionExtensions = CompressionExtensions.substr(nextExt+1);
}
- if (MetaIndexParser->CheckDist(Transformed) == false)
+ if (CurrentCompressionExtension == "uncompressed")
{
- // This might become fatal one day
-// Status = StatAuthError;
-// ErrorText = "Conflicting distribution; expected "
-// + MetaIndexParser->GetExpectedDist() + " but got "
-// + MetaIndexParser->GetDist();
-// return false;
- if (!Transformed.empty())
- {
- _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
- Desc.Description.c_str(),
- Transformed.c_str(),
- MetaIndexParser->GetDist().c_str());
- }
+ Desc.URI = URI;
+ }
+ else if (unlikely(CurrentCompressionExtension.empty()))
+ return;
+ else
+ {
+ Desc.URI = URI + '.' + CurrentCompressionExtension;
+ DestFile = DestFile + '.' + CurrentCompressionExtension;
}
- return true;
+ if(TransactionManager->MetaIndexParser != NULL)
+ InitByHashIfNeeded();
+
+ Desc.Description = URIDesc;
+ Desc.Owner = this;
+ Desc.ShortDesc = ShortDesc;
+
+ QueueURI(Desc);
}
/*}}}*/
-// pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)
+// AcqIndex::AdjustForByHash - modify URI for by-hash support /*{{{*/
+void pkgAcqIndex::InitByHashIfNeeded()
{
- if (AuthPass == true)
- {
- // gpgv method failed, if we have a good signature
- string LastGoodSigFile = _config->FindDir("Dir::State::lists").append("partial/").append(URItoFileName(RealURI));
- if (DestFile != SigFile)
- LastGoodSigFile.append(".gpg");
- LastGoodSigFile.append(".reverify");
-
- if(FileExists(LastGoodSigFile))
+ // TODO:
+ // - (maybe?) add support for by-hash into the sources.list as flag
+ // - make apt-ftparchive generate the hashes (and expire?)
+ std::string HostKnob = "APT::Acquire::" + ::URI(Desc.URI).Host + "::By-Hash";
+ if(_config->FindB("APT::Acquire::By-Hash", false) == true ||
+ _config->FindB(HostKnob, false) == true ||
+ TransactionManager->MetaIndexParser->GetSupportsAcquireByHash())
+ {
+ HashStringList const Hashes = GetExpectedHashes();
+ if(Hashes.usable())
{
- string VerifiedSigFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
- if (DestFile != SigFile)
- VerifiedSigFile.append(".gpg");
- Rename(LastGoodSigFile, VerifiedSigFile);
- 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\n"),
- Desc.Description.c_str(),
- LookupTag(Message,"Message").c_str());
- RunScripts("APT::Update::Auth-Failure");
- return;
- } 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());
- return;
+ // FIXME: should we really use the best hash here? or a fixed one?
+ HashString const * const TargetHash = Hashes.find("");
+ std::string const ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
+ size_t const trailing_slash = Desc.URI.find_last_of("/");
+ Desc.URI = Desc.URI.replace(
+ trailing_slash,
+ Desc.URI.substr(trailing_slash+1).size()+1,
+ ByHash);
} else {
- _error->Warning(_("GPG error: %s: %s"),
- Desc.Description.c_str(),
- LookupTag(Message,"Message").c_str());
+ _error->Warning(
+ "Fetching ByHash requested but can not find record for %s",
+ GetMetaKey().c_str());
}
- // gpgv method failed
- ReportMirrorFailure("GPGFailure");
- }
-
- /* Always move the meta index, even if gpgv failed. This ensures
- * that PackageFile objects are correctly filled in */
- if (FileExists(DestFile)) {
- string FinalFile = _config->FindDir("Dir::State::lists");
- FinalFile += URItoFileName(RealURI);
- /* InRelease files become Release files, otherwise
- * they would be considered as trusted later on */
- if (SigFile == DestFile) {
- RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
- "Release");
- FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
- "Release");
- SigFile = FinalFile;
- }
- Rename(DestFile,FinalFile);
- chmod(FinalFile.c_str(),0644);
-
- DestFile = FinalFile;
}
-
- // No Release file was present, or verification failed, so fall
- // back to queueing Packages files without verification
- QueueIndexes(false);
}
/*}}}*/
-pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
- string const &URI, string const &URIDesc, string const &ShortDesc,
- string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
- string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
- const vector<struct IndexTarget*>* IndexTargets,
- indexRecords* MetaIndexParser) :
- pkgAcqMetaIndex(Owner, URI, URIDesc, ShortDesc, "", IndexTargets, MetaIndexParser),
- MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
- MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
+// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
+// ---------------------------------------------------------------------
+/* The only header we use is the last-modified header. */
+string pkgAcqIndex::Custom600Headers() const
{
- SigFile = DestFile;
+ string Final = GetFinalFilename();
- // index targets + (worst case:) Release/Release.gpg
- ExpectedAdditionalItems = IndexTargets->size() + 2;
+ string msg = "\nIndex-File: true";
+ struct stat Buf;
+ if (stat(Final.c_str(),&Buf) == 0)
+ msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
+ if(Target.IsOptional)
+ msg += "\nFail-Ignore: true";
- // keep the old InRelease around in case of transistent network errors
- string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
- if (RealFileExists(Final) == true)
- {
- string const LastGoodSig = DestFile + ".reverify";
- Rename(Final,LastGoodSig);
- }
+ return msg;
}
/*}}}*/
-pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
+// AcqIndex::Failed - getting the indexfile failed /*{{{*/
+void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
{
- // if the file was never queued undo file-changes done in the constructor
- if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false)
+ Item::Failed(Message,Cnf);
+
+ // authorisation matches will not be fixed by other compression types
+ if (Status != StatAuthError)
{
- string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
- string const LastGoodSig = DestFile + ".reverify";
- if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
- Rename(LastGoodSig, Final);
+ if (CompressionExtensions.empty() == false)
+ {
+ Init(Target.URI, Desc.Description, Desc.ShortDesc);
+ Status = StatIdle;
+ return;
+ }
}
+
+ if(Target.IsOptional && GetExpectedHashes().empty() && Stage == STAGE_DOWNLOAD)
+ Status = StatDone;
+ else
+ TransactionManager->AbortTransaction();
}
/*}}}*/
-// pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
+// AcqIndex::ReverifyAfterIMS - Reverify index after an ims-hit /*{{{*/
+void pkgAcqIndex::ReverifyAfterIMS()
+{
+ // update destfile to *not* include the compression extension when doing
+ // a reverify (as its uncompressed on disk already)
+ DestFile = GetCompressedFileName(Target, GetPartialFileNameFromURI(Target.URI), CurrentCompressionExtension);
+
+ // copy FinalFile into partial/ so that we check the hash again
+ string FinalFile = GetFinalFilename();
+ Stage = STAGE_DECOMPRESS_AND_VERIFY;
+ Desc.URI = "copy:" + FinalFile;
+ QueueURI(Desc);
+}
+ /*}}}*/
+// AcqIndex::Done - Finished a fetch /*{{{*/
// ---------------------------------------------------------------------
-// FIXME: this can go away once the InRelease file is used widely
-string pkgAcqMetaClearSig::Custom600Headers()
+/* This goes through a number of states.. On the initial fetch the
+ method could possibly return an alternate filename which points
+ to the uncompressed version of the file. If this is so the file
+ is copied into the partial directory. In all other cases the file
+ is decompressed with a compressed uri. */
+void pkgAcqIndex::Done(string const &Message,
+ HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cfg)
{
- string Final = _config->FindDir("Dir::State::lists");
- Final += URItoFileName(RealURI);
+ Item::Done(Message,Hashes,Cfg);
- struct stat Buf;
- if (stat(Final.c_str(),&Buf) != 0)
+ switch(Stage)
{
- Final = DestFile + ".reverify";
- if (stat(Final.c_str(),&Buf) != 0)
- return "\nIndex-File: true\nFail-Ignore: true\n";
+ case STAGE_DOWNLOAD:
+ StageDownloadDone(Message, Hashes, Cfg);
+ break;
+ case STAGE_DECOMPRESS_AND_VERIFY:
+ StageDecompressDone(Message, Hashes, Cfg);
+ break;
}
-
- return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
}
/*}}}*/
-void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
+// AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
+void pkgAcqIndex::StageDownloadDone(string const &Message, HashStringList const &,
+ pkgAcquire::MethodConfig const * const)
{
- // we failed, we will not get additional items from this method
- ExpectedAdditionalItems = 0;
+ Complete = true;
- if (AuthPass == false)
+ // Handle the unzipd case
+ std::string FileName = LookupTag(Message,"Alt-Filename");
+ if (FileName.empty() == false)
{
- // Remove the 'old' InRelease file if we try Release.gpg now as otherwise
- // the file will stay around and gives a false-auth impression (CVE-2012-0214)
- string FinalFile = _config->FindDir("Dir::State::lists");
- FinalFile.append(URItoFileName(RealURI));
- if (FileExists(FinalFile))
- unlink(FinalFile.c_str());
+ Stage = STAGE_DECOMPRESS_AND_VERIFY;
+ Local = true;
+ DestFile += ".decomp";
+ Desc.URI = "copy:" + FileName;
+ QueueURI(Desc);
+ SetActiveSubprocess("copy");
+ return;
+ }
+ FileName = LookupTag(Message,"Filename");
- new pkgAcqMetaSig(Owner,
- MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
- MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
- IndexTargets, MetaIndexParser);
- if (Cnf->LocalOnly == true ||
- StringToBool(LookupTag(Message, "Transient-Failure"), false) == false)
- Dequeue();
+ // 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
+ if (FileName != DestFile && RealFileExists(DestFile) == false)
+ Local = true;
+ else
+ EraseFileName = FileName;
+
+ // we need to verify the file against the current Release file again
+ // on if-modfied-since hit to avoid a stale attack against us
+ if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
+ {
+ // The files timestamp matches, reverify by copy into partial/
+ EraseFileName = "";
+ ReverifyAfterIMS();
+ return;
}
+
+ // If we want compressed indexes, just copy in place for hash verification
+ if (Target.KeepCompressed == true)
+ {
+ DestFile = GetPartialFileNameFromURI(Target.URI + '.' + CurrentCompressionExtension);
+ EraseFileName = "";
+ Stage = STAGE_DECOMPRESS_AND_VERIFY;
+ Desc.URI = "copy:" + FileName;
+ QueueURI(Desc);
+ SetActiveSubprocess("copy");
+ return;
+ }
+
+ // get the binary name for your used compression type
+ string decompProg;
+ if(CurrentCompressionExtension == "uncompressed")
+ decompProg = "copy";
else
- pkgAcqMetaIndex::Failed(Message, Cnf);
+ decompProg = _config->Find(string("Acquire::CompressionTypes::").append(CurrentCompressionExtension),"");
+ if(decompProg.empty() == true)
+ {
+ _error->Error("Unsupported extension: %s", CurrentCompressionExtension.c_str());
+ return;
+ }
+
+ // queue uri for the next stage
+ Stage = STAGE_DECOMPRESS_AND_VERIFY;
+ DestFile += ".decomp";
+ Desc.URI = decompProg + ":" + FileName;
+ QueueURI(Desc);
+ SetActiveSubprocess(decompProg);
+}
+ /*}}}*/
+// AcqIndex::StageDecompressDone - Final verification /*{{{*/
+void pkgAcqIndex::StageDecompressDone(string const &,
+ HashStringList const &,
+ pkgAcquire::MethodConfig const * const)
+{
+ // Done, queue for rename on transaction finished
+ TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
+ return;
}
/*}}}*/
+pkgAcqIndex::~pkgAcqIndex() {}
+
+
// AcqArchive::AcqArchive - Constructor /*{{{*/
// ---------------------------------------------------------------------
/* This just sets up the initial fetch environment and queues the first
possibilitiy */
-pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
- pkgRecords *Recs,pkgCache::VerIterator const &Version,
+pkgAcqArchive::pkgAcqArchive(pkgAcquire * const Owner,pkgSourceList * const Sources,
+ pkgRecords * const Recs,pkgCache::VerIterator const &Version,
string &StoreFilename) :
- Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
- StoreFilename(StoreFilename), Vf(Version.FileList()),
+ Item(Owner), d(NULL), LocalSource(false), Version(Version), Sources(Sources), Recs(Recs),
+ StoreFilename(StoreFilename), Vf(Version.FileList()),
Trusted(false)
{
Retries = _config->FindI("Acquire::Retries",0);
// Skip not source sources, they do not have file fields.
for (; Vf.end() == false; ++Vf)
{
- if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
+ if (Vf.File().Flagged(pkgCache::Flag::NotSource))
continue;
break;
}
checking later. */
bool pkgAcqArchive::QueueNext()
{
- string const ForceHash = _config->Find("Acquire::ForceHash");
for (; Vf.end() == false; ++Vf)
{
+ pkgCache::PkgFileIterator const PkgF = Vf.File();
// Ignore not source sources
- if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
+ if (PkgF.Flagged(pkgCache::Flag::NotSource))
continue;
// Try to cross match against the source list
pkgIndexFile *Index;
- if (Sources->FindIndex(Vf.File(),Index) == false)
+ if (Sources->FindIndex(PkgF, Index) == false)
continue;
-
+ LocalSource = PkgF.Flagged(pkgCache::Flag::LocalSource);
+
// only try to get a trusted package from another source if that source
// is also trusted
if(Trusted && !Index->IsTrusted())
pkgRecords::Parser &Parse = Recs->Lookup(Vf);
if (_error->PendingError() == true)
return false;
-
+
string PkgFile = Parse.FileName();
- if (ForceHash.empty() == false)
- {
- if(stringcasecmp(ForceHash, "sha512") == 0)
- ExpectedHash = HashString("SHA512", Parse.SHA512Hash());
- else if(stringcasecmp(ForceHash, "sha256") == 0)
- ExpectedHash = HashString("SHA256", Parse.SHA256Hash());
- else if (stringcasecmp(ForceHash, "sha1") == 0)
- ExpectedHash = HashString("SHA1", Parse.SHA1Hash());
- else
- ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
- }
- else
- {
- string Hash;
- if ((Hash = Parse.SHA512Hash()).empty() == false)
- ExpectedHash = HashString("SHA512", Hash);
- else if ((Hash = Parse.SHA256Hash()).empty() == false)
- ExpectedHash = HashString("SHA256", Hash);
- else if ((Hash = Parse.SHA1Hash()).empty() == false)
- ExpectedHash = HashString("SHA1", Hash);
- else
- ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
- }
+ ExpectedHashes = Parse.Hashes();
+
if (PkgFile.empty() == true)
return _error->Error(_("The package index files are corrupted. No Filename: "
"field for package %s."),
// AcqArchive::Done - Finished fetching /*{{{*/
// ---------------------------------------------------------------------
/* */
-void pkgAcqArchive::Done(string Message,unsigned long long Size,string CalcHash,
- pkgAcquire::MethodConfig *Cfg)
+void pkgAcqArchive::Done(string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cfg)
{
- Item::Done(Message,Size,CalcHash,Cfg);
-
- // Check the size
- if (Size != Version->Size)
- {
- RenameOnError(SizeMismatch);
- return;
- }
-
- // Check the hash
- if(ExpectedHash.toStr() != CalcHash)
- {
- RenameOnError(HashSumMismatch);
- return;
- }
+ Item::Done(Message, Hashes, Cfg);
// Grab the output filename
- string FileName = LookupTag(Message,"Filename");
- if (FileName.empty() == true)
- {
- Status = StatError;
- ErrorText = "Method gave a blank filename";
- return;
- }
-
- Complete = true;
-
- // Reference filename
- if (FileName != DestFile)
+ std::string const FileName = LookupTag(Message,"Filename");
+ if (DestFile != FileName && RealFileExists(DestFile) == false)
{
StoreFilename = DestFile = FileName;
Local = true;
+ Complete = true;
return;
}
-
+
// Done, move it into position
- string FinalFile = _config->FindDir("Dir::Cache::Archives");
- FinalFile += flNotDir(StoreFilename);
+ string const FinalFile = GetFinalFilename();
Rename(DestFile,FinalFile);
-
StoreFilename = DestFile = FinalFile;
Complete = true;
}
// AcqArchive::Failed - Failure handler /*{{{*/
// ---------------------------------------------------------------------
/* Here we try other sources */
-void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
+void pkgAcqArchive::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
{
- ErrorText = LookupTag(Message,"Message");
-
- /* We don't really want to retry on failed media swaps, this prevents
+ Item::Failed(Message,Cnf);
+
+ /* We don't really want to retry on failed media swaps, this prevents
that. An interesting observation is that permanent failures are not
recorded. */
- if (Cnf->Removable == true &&
+ if (Cnf->Removable == true &&
StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
{
// Vf = Version.FileList();
while (Vf.end() == false) ++Vf;
StoreFilename = string();
- Item::Failed(Message,Cnf);
return;
}
-
+
+ Status = StatIdle;
if (QueueNext() == false)
{
// This is the retry counter
if (QueueNext() == true)
return;
}
-
+
StoreFilename = string();
- Item::Failed(Message,Cnf);
+ Status = StatError;
}
}
/*}}}*/
-// AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
-// ---------------------------------------------------------------------
-APT_PURE bool pkgAcqArchive::IsTrusted()
+APT_PURE bool pkgAcqArchive::IsTrusted() const /*{{{*/
{
return Trusted;
}
/*}}}*/
-// AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-void pkgAcqArchive::Finished()
+void pkgAcqArchive::Finished() /*{{{*/
{
if (Status == pkgAcquire::Item::StatDone &&
Complete == true)
StoreFilename = string();
}
/*}}}*/
+std::string pkgAcqArchive::DescURI() const /*{{{*/
+{
+ return Desc.URI;
+}
+ /*}}}*/
+std::string pkgAcqArchive::ShortDesc() const /*{{{*/
+{
+ return Desc.ShortDesc;
+}
+ /*}}}*/
+pkgAcqArchive::~pkgAcqArchive() {}
+
+// AcqChangelog::pkgAcqChangelog - Constructors /*{{{*/
+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())
+{
+ Desc.URI = URI(Ver);
+ Init(DestDir, DestFilename);
+}
+// some parameters are char* here as they come likely from char* interfaces – which can also return NULL
+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)
+{
+ 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)
+{
+ Desc.URI = URI;
+ Init(DestDir, DestFilename);
+}
+void pkgAcqChangelog::Init(std::string const &DestDir, std::string const &DestFilename)
+{
+ if (Desc.URI.empty())
+ {
+ Status = StatError;
+ // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
+ strprintf(ErrorText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
+ // Let the error message print something sensible rather than "Failed to fetch /"
+ if (DestFilename.empty())
+ DestFile = SrcName + ".changelog";
+ else
+ DestFile = DestFilename;
+ Desc.URI = "changelog:/" + DestFile;
+ return;
+ }
+
+ if (DestDir.empty())
+ {
+ std::string const systemTemp = GetTempDir();
+ char tmpname[100];
+ 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;
+ }
+ DestFile = TemporaryDirectory = tmpname;
+ }
+ 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());
+ Desc.Owner = this;
+ QueueURI(Desc);
+}
+ /*}}}*/
+std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/
+{
+ 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())
+ continue;
+ return uri;
+ }
+ return "";
+}
+std::string pkgAcqChangelog::URITemplate(pkgCache::RlsFileIterator const &Rls)
+{
+ if (Rls.end() == true || (Rls->Label == 0 && Rls->Origin == 0))
+ return "";
+ std::string const serverConfig = "Acquire::Changelogs::URI";
+ std::string server;
+#define APT_EMPTY_SERVER \
+ if (server.empty() == false) \
+ { \
+ if (server != "no") \
+ return server; \
+ return ""; \
+ }
+#define APT_CHECK_SERVER(X, Y) \
+ if (Rls->X != 0) \
+ { \
+ std::string const specialServerConfig = serverConfig + "::" + Y + #X + "::" + Rls.X(); \
+ server = _config->Find(specialServerConfig); \
+ APT_EMPTY_SERVER \
+ }
+ // this way e.g. Debian-Security can fallback to Debian
+ APT_CHECK_SERVER(Label, "Override::")
+ APT_CHECK_SERVER(Origin, "Override::")
+
+ if (RealFileExists(Rls.FileName()))
+ {
+ _error->PushToStack();
+ FileFd rf;
+ /* This can be costly. A caller wanting to get millions of URIs might
+ want to do this on its own once and use Override settings.
+ We don't do this here as Origin/Label are not as unique as they
+ should be so this could produce request order-dependent anomalies */
+ if (OpenMaybeClearSignedFile(Rls.FileName(), rf) == true)
+ {
+ pkgTagFile TagFile(&rf, rf.Size());
+ pkgTagSection Section;
+ if (TagFile.Step(Section) == true)
+ server = Section.FindS("Changelogs");
+ }
+ _error->RevertToStack();
+ APT_EMPTY_SERVER
+ }
+
+ APT_CHECK_SERVER(Label, "")
+ APT_CHECK_SERVER(Origin, "")
+#undef APT_CHECK_SERVER
+#undef APT_EMPTY_SERVER
+ return "";
+}
+std::string pkgAcqChangelog::URI(pkgCache::RlsFileIterator const &Rls,
+ char const * const Component, char const * const SrcName,
+ char const * const SrcVersion)
+{
+ return URI(URITemplate(Rls), Component, SrcName, SrcVersion);
+}
+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)
+ return "";
+
+ // the path is: COMPONENT/SRC/SRCNAME/SRCNAME_SRCVER, e.g. main/a/apt/1.1 or contrib/liba/libapt/2.0
+ std::string Src = SrcName;
+ std::string path = APT::String::Startswith(SrcName, "lib") ? Src.substr(0, 4) : Src.substr(0,1);
+ path.append("/").append(Src).append("/");
+ path.append(Src).append("_").append(StripEpoch(SrcVersion));
+ // we omit component for releases without one (= flat-style repositories)
+ if (Component != NULL && strlen(Component) != 0)
+ path = std::string(Component) + "/" + path;
+
+ return SubstVar(Template, "CHANGEPATH", path);
+}
+ /*}}}*/
+// AcqChangelog::Failed - Failure handler /*{{{*/
+void pkgAcqChangelog::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
+{
+ Item::Failed(Message,Cnf);
+
+ std::string errText;
+ // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
+ strprintf(errText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
+
+ // Error is probably something techy like 404 Not Found
+ if (ErrorText.empty())
+ ErrorText = errText;
+ else
+ ErrorText = errText + " (" + ErrorText + ")";
+ return;
+}
+ /*}}}*/
+// AcqChangelog::Done - Item downloaded OK /*{{{*/
+void pkgAcqChangelog::Done(string const &Message,HashStringList const &CalcHashes,
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ Item::Done(Message,CalcHashes,Cnf);
+
+ Complete = true;
+}
+ /*}}}*/
+pkgAcqChangelog::~pkgAcqChangelog() /*{{{*/
+{
+ if (TemporaryDirectory.empty() == false)
+ {
+ unlink(DestFile.c_str());
+ rmdir(TemporaryDirectory.c_str());
+ }
+}
+ /*}}}*/
+
// AcqFile::pkgAcqFile - Constructor /*{{{*/
-// ---------------------------------------------------------------------
-/* The file is added to the queue */
-pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash,
- unsigned long long Size,string Dsc,string ShortDesc,
+pkgAcqFile::pkgAcqFile(pkgAcquire * const Owner,string const &URI, HashStringList const &Hashes,
+ unsigned long long const Size,string const &Dsc,string const &ShortDesc,
const string &DestDir, const string &DestFilename,
- bool IsIndexFile) :
- Item(Owner), ExpectedHash(Hash), IsIndexFile(IsIndexFile)
+ bool const IsIndexFile) :
+ Item(Owner), d(NULL), IsIndexFile(IsIndexFile), ExpectedHashes(Hashes)
{
Retries = _config->FindI("Acquire::Retries",0);
-
+
if(!DestFilename.empty())
DestFile = DestFilename;
else if(!DestDir.empty())
// Set the short description to the archive component
Desc.ShortDesc = ShortDesc;
-
+
// Get the transfer sizes
FileSize = Size;
struct stat Buf;
}
/*}}}*/
// AcqFile::Done - Item downloaded OK /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-void pkgAcqFile::Done(string Message,unsigned long long Size,string CalcHash,
- pkgAcquire::MethodConfig *Cnf)
+void pkgAcqFile::Done(string const &Message,HashStringList const &CalcHashes,
+ pkgAcquire::MethodConfig const * const Cnf)
{
- Item::Done(Message,Size,CalcHash,Cnf);
-
- // Check the hash
- if(!ExpectedHash.empty() && ExpectedHash.toStr() != CalcHash)
- {
- RenameOnError(HashSumMismatch);
- return;
- }
-
- string FileName = LookupTag(Message,"Filename");
- if (FileName.empty() == true)
- {
- Status = StatError;
- ErrorText = "Method gave a blank filename";
- return;
- }
+ Item::Done(Message,CalcHashes,Cnf);
+ std::string const FileName = LookupTag(Message,"Filename");
Complete = true;
-
+
// The files timestamp matches
if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
return;
-
+
// We have to copy it into place
- if (FileName != DestFile)
+ if (RealFileExists(DestFile.c_str()) == false)
{
Local = true;
if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
QueueURI(Desc);
return;
}
-
+
// Erase the file if it is a symlink so we can overwrite it
struct stat St;
if (lstat(DestFile.c_str(),&St) == 0)
if (S_ISLNK(St.st_mode) != 0)
unlink(DestFile.c_str());
}
-
+
// Symlink the file
if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
{
- ErrorText = "Link to " + DestFile + " failure ";
+ _error->PushToStack();
+ _error->Errno("pkgAcqFile::Done", "Symlinking file %s failed", DestFile.c_str());
+ std::stringstream msg;
+ _error->DumpErrors(msg);
+ _error->RevertToStack();
+ ErrorText = msg.str();
Status = StatError;
Complete = false;
- }
+ }
}
}
/*}}}*/
// AcqFile::Failed - Failure handler /*{{{*/
// ---------------------------------------------------------------------
/* Here we try other sources */
-void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
+void pkgAcqFile::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
{
- ErrorText = LookupTag(Message,"Message");
-
+ Item::Failed(Message,Cnf);
+
// This is the retry counter
if (Retries != 0 &&
Cnf->LocalOnly == false &&
StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
{
- Retries--;
+ --Retries;
QueueURI(Desc);
+ Status = StatIdle;
return;
}
-
- Item::Failed(Message,Cnf);
+
}
/*}}}*/
-// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
-// ---------------------------------------------------------------------
-/* The only header we use is the last-modified header. */
-string pkgAcqFile::Custom600Headers()
+string pkgAcqFile::Custom600Headers() const /*{{{*/
{
if (IsIndexFile)
return "\nIndex-File: true";
return "";
}
/*}}}*/
+pkgAcqFile::~pkgAcqFile() {}