1 // -*- mode: cpp; mode: fold -*-
3 // $Id: acquire-item.cc,v 1.46.2.9 2004/01/16 18:51:11 mdz Exp $
4 /* ######################################################################
6 Acquire Item - Item to acquire
8 Each item can download to exactly one file at a time. This means you
9 cannot create an item that fetches two uri's to two files at the same
10 time. The pkgAcqIndex class creates a second class upon instantiation
11 to fetch the other index files because of this.
13 ##################################################################### */
15 // Include Files /*{{{*/
18 #include <apt-pkg/acquire-item.h>
19 #include <apt-pkg/configuration.h>
20 #include <apt-pkg/aptconfiguration.h>
21 #include <apt-pkg/sourcelist.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/strutl.h>
24 #include <apt-pkg/fileutl.h>
25 #include <apt-pkg/tagfile.h>
26 #include <apt-pkg/metaindex.h>
27 #include <apt-pkg/acquire.h>
28 #include <apt-pkg/hashes.h>
29 #include <apt-pkg/indexfile.h>
30 #include <apt-pkg/pkgcache.h>
31 #include <apt-pkg/cacheiterators.h>
32 #include <apt-pkg/pkgrecords.h>
33 #include <apt-pkg/gpgv.h>
55 static void printHashSumComparison(std::string
const &URI
, HashStringList
const &Expected
, HashStringList
const &Actual
) /*{{{*/
57 if (_config
->FindB("Debug::Acquire::HashSumMismatch", false) == false)
59 std::cerr
<< std::endl
<< URI
<< ":" << std::endl
<< " Expected Hash: " << std::endl
;
60 for (HashStringList::const_iterator hs
= Expected
.begin(); hs
!= Expected
.end(); ++hs
)
61 std::cerr
<< "\t- " << hs
->toStr() << std::endl
;
62 std::cerr
<< " Actual Hash: " << std::endl
;
63 for (HashStringList::const_iterator hs
= Actual
.begin(); hs
!= Actual
.end(); ++hs
)
64 std::cerr
<< "\t- " << hs
->toStr() << std::endl
;
67 static std::string
GetPartialFileName(std::string
const &file
) /*{{{*/
69 std::string DestFile
= _config
->FindDir("Dir::State::lists") + "partial/";
74 static std::string
GetPartialFileNameFromURI(std::string
const &uri
) /*{{{*/
76 return GetPartialFileName(URItoFileName(uri
));
79 static std::string
GetFinalFileNameFromURI(std::string
const &uri
) /*{{{*/
81 return _config
->FindDir("Dir::State::lists") + URItoFileName(uri
);
84 static std::string
GetKeepCompressedFileName(std::string file
, IndexTarget
const &Target
)/*{{{*/
86 if (Target
.KeepCompressed
== false)
89 std::string
const KeepCompressedAs
= Target
.Option(IndexTarget::KEEPCOMPRESSEDAS
);
90 if (KeepCompressedAs
.empty() == false)
92 std::string
const ext
= KeepCompressedAs
.substr(0, KeepCompressedAs
.find(' '));
93 if (ext
!= "uncompressed")
94 file
.append(".").append(ext
);
99 static std::string
GetMergeDiffsPatchFileName(std::string
const &Final
, std::string
const &Patch
)/*{{{*/
101 // rred expects the patch as $FinalFile.ed.$patchname.gz
102 return Final
+ ".ed." + Patch
+ ".gz";
105 static std::string
GetDiffsPatchFileName(std::string
const &Final
) /*{{{*/
107 // rred expects the patch as $FinalFile.ed
108 return Final
+ ".ed";
111 static std::string
GetExistingFilename(std::string
const &File
) /*{{{*/
113 if (RealFileExists(File
))
115 for (auto const &type
: APT::Configuration::getCompressorExtensions())
117 std::string
const Final
= File
+ type
;
118 if (RealFileExists(Final
))
124 static std::string
GetDiffIndexFileName(std::string
const &Name
) /*{{{*/
126 return Name
+ ".diff/Index";
129 static std::string
GetDiffIndexURI(IndexTarget
const &Target
) /*{{{*/
131 return Target
.URI
+ ".diff/Index";
135 static void ReportMirrorFailureToCentral(pkgAcquire::Item
const &I
, std::string
const &FailCode
, std::string
const &Details
)/*{{{*/
137 // we only act if a mirror was used at all
138 if(I
.UsedMirror
.empty())
141 std::cerr
<< "\nReportMirrorFailure: "
143 << " Uri: " << DescURI()
145 << FailCode
<< std::endl
;
147 string
const report
= _config
->Find("Methods::Mirror::ProblemReporting",
148 "/usr/lib/apt/apt-report-mirror-failure");
149 if(!FileExists(report
))
152 std::vector
<char const*> const Args
= {
154 I
.UsedMirror
.c_str(),
161 pid_t pid
= ExecFork();
164 _error
->Error("ReportMirrorFailure Fork failed");
169 execvp(Args
[0], (char**)Args
.data());
170 std::cerr
<< "Could not exec " << Args
[0] << std::endl
;
173 if(!ExecWait(pid
, "report-mirror-failure"))
174 _error
->Warning("Couldn't report problem to '%s'", report
.c_str());
178 static APT_NONNULL(2) bool MessageInsecureRepository(bool const isError
, char const * const msg
, std::string
const &repo
)/*{{{*/
181 strprintf(m
, msg
, repo
.c_str());
184 _error
->Error("%s", m
.c_str());
185 _error
->Notice("%s", _("Updating from such a repository can't be done securely, and is therefore disabled by default."));
189 _error
->Warning("%s", m
.c_str());
190 _error
->Notice("%s", _("Data from such a repository can't be authenticated and is therefore potentially dangerous to use."));
192 _error
->Notice("%s", _("See apt-secure(8) manpage for repository creation and user configuration details."));
196 // AllowInsecureRepositories /*{{{*/
197 enum class InsecureType
{ UNSIGNED
, WEAK
, NORELEASE
};
198 static bool TargetIsAllowedToBe(IndexTarget
const &Target
, InsecureType
const type
)
200 if (_config
->FindB("Acquire::AllowInsecureRepositories"))
203 if (Target
.OptionBool(IndexTarget::ALLOW_INSECURE
))
208 case InsecureType::UNSIGNED
: break;
209 case InsecureType::NORELEASE
: break;
210 case InsecureType::WEAK
:
211 if (_config
->FindB("Acquire::AllowWeakRepositories"))
213 if (Target
.OptionBool(IndexTarget::ALLOW_WEAK
))
219 static bool APT_NONNULL(3, 4, 5) AllowInsecureRepositories(InsecureType
const msg
, std::string
const &repo
,
220 metaIndex
const * const MetaIndexParser
, pkgAcqMetaClearSig
* const TransactionManager
, pkgAcquire::Item
* const I
)
222 // we skip weak downgrades as its unlikely that a repository gets really weaker –
223 // its more realistic that apt got pickier in a newer version
224 if (msg
!= InsecureType::WEAK
)
226 std::string
const FinalInRelease
= TransactionManager
->GetFinalFilename();
227 std::string
const FinalReleasegpg
= FinalInRelease
.substr(0, FinalInRelease
.length() - strlen("InRelease")) + "Release.gpg";
228 if (RealFileExists(FinalReleasegpg
) || RealFileExists(FinalInRelease
))
230 char const * msgstr
= nullptr;
233 case InsecureType::UNSIGNED
: msgstr
= _("The repository '%s' is no longer signed."); break;
234 case InsecureType::NORELEASE
: msgstr
= _("The repository '%s' does no longer have a Release file."); break;
235 case InsecureType::WEAK
: /* unreachable */ break;
237 if (_config
->FindB("Acquire::AllowDowngradeToInsecureRepositories") ||
238 TransactionManager
->Target
.OptionBool(IndexTarget::ALLOW_DOWNGRADE_TO_INSECURE
))
240 // meh, the users wants to take risks (we still mark the packages
241 // from this repository as unauthenticated)
242 _error
->Warning(msgstr
, repo
.c_str());
243 _error
->Warning(_("This is normally not allowed, but the option "
244 "Acquire::AllowDowngradeToInsecureRepositories was "
245 "given to override it."));
247 MessageInsecureRepository(true, msgstr
, repo
);
248 TransactionManager
->AbortTransaction();
249 I
->Status
= pkgAcquire::Item::StatError
;
255 if(MetaIndexParser
->GetTrusted() == metaIndex::TRI_YES
)
258 char const * msgstr
= nullptr;
261 case InsecureType::UNSIGNED
: msgstr
= _("The repository '%s' is not signed."); break;
262 case InsecureType::NORELEASE
: msgstr
= _("The repository '%s' does not have a Release file."); break;
263 case InsecureType::WEAK
: msgstr
= _("The repository '%s' provides only weak security information."); break;
266 if (TargetIsAllowedToBe(TransactionManager
->Target
, msg
) == true)
268 MessageInsecureRepository(false, msgstr
, repo
);
272 MessageInsecureRepository(true, msgstr
, repo
);
273 TransactionManager
->AbortTransaction();
274 I
->Status
= pkgAcquire::Item::StatError
;
278 static HashStringList
GetExpectedHashesFromFor(metaIndex
* const Parser
, std::string
const &MetaKey
)/*{{{*/
281 return HashStringList();
282 metaIndex::checkSum
* const R
= Parser
->Lookup(MetaKey
);
284 return HashStringList();
289 // all ::HashesRequired and ::GetExpectedHashes implementations /*{{{*/
290 /* ::GetExpectedHashes is abstract and has to be implemented by all subclasses.
291 It is best to implement it as broadly as possible, while ::HashesRequired defaults
292 to true and should be as restrictive as possible for false cases. Note that if
293 a hash is returned by ::GetExpectedHashes it must match. Only if it doesn't
294 ::HashesRequired is called to evaluate if its okay to have no hashes. */
295 APT_CONST
bool pkgAcqTransactionItem::HashesRequired() const
297 /* signed repositories obviously have a parser and good hashes.
298 unsigned repositories, too, as even if we can't trust them for security,
299 we can at least trust them for integrity of the download itself.
300 Only repositories without a Release file can (obviously) not have
301 hashes – and they are very uncommon and strongly discouraged */
302 if (TransactionManager
->MetaIndexParser
->GetLoadedSuccessfully() != metaIndex::TRI_YES
)
304 if (TargetIsAllowedToBe(Target
, InsecureType::WEAK
))
306 /* If we allow weak hashes, we check that we have some (weak) and then
307 declare hashes not needed. That will tip us in the right direction
308 as if hashes exist, they will be used, even if not required */
309 auto const hsl
= GetExpectedHashes();
312 if (hsl
.empty() == false)
317 HashStringList
pkgAcqTransactionItem::GetExpectedHashes() const
319 return GetExpectedHashesFor(GetMetaKey());
322 APT_CONST
bool pkgAcqMetaBase::HashesRequired() const
324 // Release and co have no hashes 'by design'.
327 HashStringList
pkgAcqMetaBase::GetExpectedHashes() const
329 return HashStringList();
332 APT_CONST
bool pkgAcqIndexDiffs::HashesRequired() const
334 /* We can't check hashes of rred result as we don't know what the
335 hash of the file will be. We just know the hash of the patch(es),
336 the hash of the file they will apply on and the hash of the resulting
338 if (State
== StateFetchDiff
)
342 HashStringList
pkgAcqIndexDiffs::GetExpectedHashes() const
344 if (State
== StateFetchDiff
)
345 return available_patches
[0].download_hashes
;
346 return HashStringList();
349 APT_CONST
bool pkgAcqIndexMergeDiffs::HashesRequired() const
351 /* @see #pkgAcqIndexDiffs::HashesRequired, with the difference that
352 we can check the rred result after all patches are applied as
353 we know the expected result rather than potentially apply more patches */
354 if (State
== StateFetchDiff
)
356 return State
== StateApplyDiff
;
358 HashStringList
pkgAcqIndexMergeDiffs::GetExpectedHashes() const
360 if (State
== StateFetchDiff
)
361 return patch
.download_hashes
;
362 else if (State
== StateApplyDiff
)
363 return GetExpectedHashesFor(Target
.MetaKey
);
364 return HashStringList();
367 APT_CONST
bool pkgAcqArchive::HashesRequired() const
369 return LocalSource
== false;
371 HashStringList
pkgAcqArchive::GetExpectedHashes() const
373 // figured out while parsing the records
374 return ExpectedHashes
;
377 APT_CONST
bool pkgAcqFile::HashesRequired() const
379 // supplied as parameter at creation time, so the caller decides
380 return ExpectedHashes
.usable();
382 HashStringList
pkgAcqFile::GetExpectedHashes() const
384 return ExpectedHashes
;
387 // Acquire::Item::QueueURI and specialisations from child classes /*{{{*/
388 bool pkgAcquire::Item::QueueURI(pkgAcquire::ItemDesc
&Item
)
390 Owner
->Enqueue(Item
);
393 /* The idea here is that an item isn't queued if it exists on disk and the
394 transition manager was a hit as this means that the files it contains
395 the checksums for can't be updated either (or they are and we are asking
396 for a hashsum mismatch to happen which helps nobody) */
397 bool pkgAcqTransactionItem::QueueURI(pkgAcquire::ItemDesc
&Item
)
399 if (TransactionManager
->State
!= TransactionStarted
)
401 if (_config
->FindB("Debug::Acquire::Transaction", false))
402 std::clog
<< "Skip " << Target
.URI
<< " as transaction was already dealt with!" << std::endl
;
405 std::string
const FinalFile
= GetFinalFilename();
406 if (TransactionManager
->IMSHit
== true && FileExists(FinalFile
) == true)
408 PartialFile
= DestFile
= FinalFile
;
412 // If we got the InRelease file via a mirror, pick all indexes directly from this mirror, too
413 if (TransactionManager
->BaseURI
.empty() == false &&
414 URI::SiteOnly(Item
.URI
) != URI::SiteOnly(TransactionManager
->BaseURI
))
416 // this ensures we rewrite only once and only the first step
417 auto const OldBaseURI
= Target
.Option(IndexTarget::BASE_URI
);
418 if (OldBaseURI
.empty() == false && APT::String::Startswith(Item
.URI
, OldBaseURI
))
420 auto const ExtraPath
= Item
.URI
.substr(OldBaseURI
.length());
421 Item
.URI
= flCombine(TransactionManager
->BaseURI
, ExtraPath
);
422 UsedMirror
= TransactionManager
->UsedMirror
;
423 if (Item
.Description
.find(" ") != string::npos
)
424 Item
.Description
.replace(0, Item
.Description
.find(" "), UsedMirror
);
427 return pkgAcquire::Item::QueueURI(Item
);
429 /* The transition manager InRelease itself (or its older sisters-in-law
430 Release & Release.gpg) is always queued as this allows us to rerun gpgv
431 on it to verify that we aren't stalled with old files */
432 bool pkgAcqMetaBase::QueueURI(pkgAcquire::ItemDesc
&Item
)
434 return pkgAcquire::Item::QueueURI(Item
);
436 /* the Diff/Index needs to queue also the up-to-date complete index file
437 to ensure that the list cleaner isn't eating it */
438 bool pkgAcqDiffIndex::QueueURI(pkgAcquire::ItemDesc
&Item
)
440 if (pkgAcqTransactionItem::QueueURI(Item
) == true)
446 // Acquire::Item::GetFinalFilename and specialisations for child classes /*{{{*/
447 std::string
pkgAcquire::Item::GetFinalFilename() const
449 // Beware: Desc.URI is modified by redirections
450 return GetFinalFileNameFromURI(Desc
.URI
);
452 std::string
pkgAcqDiffIndex::GetFinalFilename() const
454 return GetFinalFileNameFromURI(GetDiffIndexURI(Target
));
456 std::string
pkgAcqIndex::GetFinalFilename() const
458 std::string
const FinalFile
= GetFinalFileNameFromURI(Target
.URI
);
459 return GetKeepCompressedFileName(FinalFile
, Target
);
461 std::string
pkgAcqMetaSig::GetFinalFilename() const
463 return GetFinalFileNameFromURI(Target
.URI
);
465 std::string
pkgAcqBaseIndex::GetFinalFilename() const
467 return GetFinalFileNameFromURI(Target
.URI
);
469 std::string
pkgAcqMetaBase::GetFinalFilename() const
471 return GetFinalFileNameFromURI(Target
.URI
);
473 std::string
pkgAcqArchive::GetFinalFilename() const
475 return _config
->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename
);
478 // pkgAcqTransactionItem::GetMetaKey and specialisations for child classes /*{{{*/
479 std::string
pkgAcqTransactionItem::GetMetaKey() const
481 return Target
.MetaKey
;
483 std::string
pkgAcqIndex::GetMetaKey() const
485 if (Stage
== STAGE_DECOMPRESS_AND_VERIFY
|| CurrentCompressionExtension
== "uncompressed")
486 return Target
.MetaKey
;
487 return Target
.MetaKey
+ "." + CurrentCompressionExtension
;
489 std::string
pkgAcqDiffIndex::GetMetaKey() const
491 return GetDiffIndexFileName(Target
.MetaKey
);
494 //pkgAcqTransactionItem::TransactionState and specialisations for child classes /*{{{*/
495 bool pkgAcqTransactionItem::TransactionState(TransactionStates
const state
)
497 bool const Debug
= _config
->FindB("Debug::Acquire::Transaction", false);
500 case TransactionStarted
: _error
->Fatal("Item %s changed to invalid transaction start state!", Target
.URI
.c_str()); break;
501 case TransactionAbort
:
503 std::clog
<< " Cancel: " << DestFile
<< std::endl
;
504 if (Status
== pkgAcquire::Item::StatIdle
)
506 Status
= pkgAcquire::Item::StatDone
;
510 case TransactionCommit
:
511 if(PartialFile
.empty() == false)
513 bool sameFile
= (PartialFile
== DestFile
);
514 // we use symlinks on IMS-Hit to avoid copies
515 if (RealFileExists(DestFile
))
518 if (lstat(PartialFile
.c_str(), &Buf
) != -1)
520 if (S_ISLNK(Buf
.st_mode
) && Buf
.st_size
> 0)
522 char partial
[Buf
.st_size
+ 1];
523 ssize_t
const sp
= readlink(PartialFile
.c_str(), partial
, Buf
.st_size
);
525 _error
->Errno("pkgAcqTransactionItem::TransactionState-sp", _("Failed to readlink %s"), PartialFile
.c_str());
529 sameFile
= (DestFile
== partial
);
534 _error
->Errno("pkgAcqTransactionItem::TransactionState-stat", _("Failed to stat %s"), PartialFile
.c_str());
536 if (sameFile
== false)
538 // ensure that even without lists-cleanup all compressions are nuked
539 std::string FinalFile
= GetFinalFileNameFromURI(Target
.URI
);
540 if (FileExists(FinalFile
))
543 std::clog
<< "rm " << FinalFile
<< " # " << DescURI() << std::endl
;
544 if (RemoveFile("TransactionStates-Cleanup", FinalFile
) == false)
547 for (auto const &ext
: APT::Configuration::getCompressorExtensions())
549 auto const Final
= FinalFile
+ ext
;
550 if (FileExists(Final
))
553 std::clog
<< "rm " << Final
<< " # " << DescURI() << std::endl
;
554 if (RemoveFile("TransactionStates-Cleanup", Final
) == false)
559 std::clog
<< "mv " << PartialFile
<< " -> "<< DestFile
<< " # " << DescURI() << std::endl
;
560 if (Rename(PartialFile
, DestFile
) == false)
563 else if(Debug
== true)
564 std::clog
<< "keep " << PartialFile
<< " # " << DescURI() << std::endl
;
568 std::clog
<< "rm " << DestFile
<< " # " << DescURI() << std::endl
;
569 if (RemoveFile("TransItem::TransactionCommit", DestFile
) == false)
576 bool pkgAcqMetaBase::TransactionState(TransactionStates
const state
)
578 // Do not remove InRelease on IMSHit of Release.gpg [yes, this is very edgecasey]
579 if (TransactionManager
->IMSHit
== false)
580 return pkgAcqTransactionItem::TransactionState(state
);
583 bool pkgAcqIndex::TransactionState(TransactionStates
const state
)
585 if (pkgAcqTransactionItem::TransactionState(state
) == false)
590 case TransactionStarted
: _error
->Fatal("AcqIndex %s changed to invalid transaction start state!", Target
.URI
.c_str()); break;
591 case TransactionAbort
:
592 if (Stage
== STAGE_DECOMPRESS_AND_VERIFY
)
594 // keep the compressed file, but drop the decompressed
595 EraseFileName
.clear();
596 if (PartialFile
.empty() == false && flExtension(PartialFile
) != CurrentCompressionExtension
)
597 RemoveFile("TransactionAbort", PartialFile
);
600 case TransactionCommit
:
601 if (EraseFileName
.empty() == false)
602 RemoveFile("AcqIndex::TransactionCommit", EraseFileName
);
607 bool pkgAcqDiffIndex::TransactionState(TransactionStates
const state
)
609 if (pkgAcqTransactionItem::TransactionState(state
) == false)
614 case TransactionStarted
: _error
->Fatal("Item %s changed to invalid transaction start state!", Target
.URI
.c_str()); break;
615 case TransactionCommit
:
617 case TransactionAbort
:
618 std::string
const Partial
= GetPartialFileNameFromURI(Target
.URI
);
619 RemoveFile("TransactionAbort", Partial
);
627 class APT_HIDDEN NoActionItem
: public pkgAcquire::Item
/*{{{*/
628 /* The sole purpose of this class is having an item which does nothing to
629 reach its done state to prevent cleanup deleting the mentioned file.
630 Handy in cases in which we know we have the file already, like IMS-Hits. */
632 IndexTarget
const Target
;
634 virtual std::string
DescURI() const APT_OVERRIDE
{return Target
.URI
;};
635 virtual HashStringList
GetExpectedHashes() const APT_OVERRIDE
{return HashStringList();};
637 NoActionItem(pkgAcquire
* const Owner
, IndexTarget
const &Target
) :
638 pkgAcquire::Item(Owner
), Target(Target
)
641 DestFile
= GetFinalFileNameFromURI(Target
.URI
);
643 NoActionItem(pkgAcquire
* const Owner
, IndexTarget
const &Target
, std::string
const &FinalFile
) :
644 pkgAcquire::Item(Owner
), Target(Target
)
647 DestFile
= FinalFile
;
651 class APT_HIDDEN CleanupItem
: public pkgAcqTransactionItem
/*{{{*/
652 /* This class ensures that a file which was configured but isn't downloaded
653 for various reasons isn't kept in an old version in the lists directory.
654 In a way its the reverse of NoActionItem as it helps with removing files
655 even if the lists-cleanup is deactivated. */
658 virtual std::string
DescURI() const APT_OVERRIDE
{return Target
.URI
;};
659 virtual HashStringList
GetExpectedHashes() const APT_OVERRIDE
{return HashStringList();};
661 CleanupItem(pkgAcquire
* const Owner
, pkgAcqMetaClearSig
* const TransactionManager
, IndexTarget
const &Target
) :
662 pkgAcqTransactionItem(Owner
, TransactionManager
, Target
)
665 DestFile
= GetFinalFileNameFromURI(Target
.URI
);
667 bool TransactionState(TransactionStates
const state
) APT_OVERRIDE
671 case TransactionStarted
:
673 case TransactionAbort
:
675 case TransactionCommit
:
676 if (_config
->FindB("Debug::Acquire::Transaction", false) == true)
677 std::clog
<< "rm " << DestFile
<< " # " << DescURI() << std::endl
;
678 if (RemoveFile("TransItem::TransactionCommit", DestFile
) == false)
687 // Acquire::Item::Item - Constructor /*{{{*/
688 class pkgAcquire::Item::Private
691 std::vector
<std::string
> PastRedirections
;
693 APT_IGNORE_DEPRECATED_PUSH
694 pkgAcquire::Item::Item(pkgAcquire
* const owner
) :
695 FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false), Local(false),
696 QueueCounter(0), ExpectedAdditionalItems(0), Owner(owner
), d(new Private())
701 APT_IGNORE_DEPRECATED_POP
703 // Acquire::Item::~Item - Destructor /*{{{*/
704 pkgAcquire::Item::~Item()
710 std::string
pkgAcquire::Item::Custom600Headers() const /*{{{*/
712 return std::string();
715 std::string
pkgAcquire::Item::ShortDesc() const /*{{{*/
720 APT_CONST
void pkgAcquire::Item::Finished() /*{{{*/
724 APT_PURE pkgAcquire
* pkgAcquire::Item::GetOwner() const /*{{{*/
729 APT_CONST
pkgAcquire::ItemDesc
&pkgAcquire::Item::GetItemDesc() /*{{{*/
734 APT_CONST
bool pkgAcquire::Item::IsTrusted() const /*{{{*/
739 // Acquire::Item::Failed - Item failed to download /*{{{*/
740 // ---------------------------------------------------------------------
741 /* We return to an idle state if there are still other queues that could
743 void pkgAcquire::Item::Failed(string
const &Message
,pkgAcquire::MethodConfig
const * const Cnf
)
745 if (QueueCounter
<= 1)
747 /* This indicates that the file is not available right now but might
748 be sometime later. If we do a retry cycle then this should be
750 if (Cnf
!= NULL
&& Cnf
->LocalOnly
== true &&
751 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
767 case StatTransientNetworkError
:
774 string
const FailReason
= LookupTag(Message
, "FailReason");
775 enum { MAXIMUM_SIZE_EXCEEDED
, HASHSUM_MISMATCH
, WEAK_HASHSUMS
, REDIRECTION_LOOP
, OTHER
} failreason
= OTHER
;
776 if ( FailReason
== "MaximumSizeExceeded")
777 failreason
= MAXIMUM_SIZE_EXCEEDED
;
778 else if ( FailReason
== "WeakHashSums")
779 failreason
= WEAK_HASHSUMS
;
780 else if (FailReason
== "RedirectionLoop")
781 failreason
= REDIRECTION_LOOP
;
782 else if (Status
== StatAuthError
)
783 failreason
= HASHSUM_MISMATCH
;
785 if(ErrorText
.empty())
787 std::ostringstream out
;
790 case HASHSUM_MISMATCH
:
791 out
<< _("Hash Sum mismatch") << std::endl
;
794 out
<< _("Insufficient information available to perform this download securely") << std::endl
;
796 case REDIRECTION_LOOP
:
797 out
<< "Redirection loop encountered" << std::endl
;
799 case MAXIMUM_SIZE_EXCEEDED
:
800 out
<< LookupTag(Message
, "Message") << std::endl
;
803 out
<< LookupTag(Message
, "Message");
807 if (Status
== StatAuthError
)
809 auto const ExpectedHashes
= GetExpectedHashes();
810 if (ExpectedHashes
.empty() == false)
812 out
<< "Hashes of expected file:" << std::endl
;
813 for (auto const &hs
: ExpectedHashes
)
815 out
<< " - " << hs
.toStr();
816 if (hs
.usable() == false)
821 if (failreason
== HASHSUM_MISMATCH
)
823 out
<< "Hashes of received file:" << std::endl
;
824 for (char const * const * type
= HashString::SupportedHashes(); *type
!= NULL
; ++type
)
826 std::string
const tagname
= std::string(*type
) + "-Hash";
827 std::string
const hashsum
= LookupTag(Message
, tagname
.c_str());
828 if (hashsum
.empty() == false)
830 auto const hs
= HashString(*type
, hashsum
);
831 out
<< " - " << hs
.toStr();
832 if (hs
.usable() == false)
837 out
<< "Last modification reported: " << LookupTag(Message
, "Last-Modified", "<none>") << std::endl
;
840 ErrorText
= out
.str();
845 case MAXIMUM_SIZE_EXCEEDED
: RenameOnError(MaximumSizeExceeded
); break;
846 case HASHSUM_MISMATCH
: RenameOnError(HashSumMismatch
); break;
847 case WEAK_HASHSUMS
: break;
848 case REDIRECTION_LOOP
: break;
852 if (FailReason
.empty() == false)
853 ReportMirrorFailureToCentral(*this, FailReason
, ErrorText
);
855 ReportMirrorFailureToCentral(*this, ErrorText
, ErrorText
);
857 if (QueueCounter
> 1)
861 // Acquire::Item::Start - Item has begun to download /*{{{*/
862 // ---------------------------------------------------------------------
863 /* Stash status and the file size. Note that setting Complete means
864 sub-phases of the acquire process such as decompresion are operating */
865 void pkgAcquire::Item::Start(string
const &/*Message*/, unsigned long long const Size
)
867 Status
= StatFetching
;
869 if (FileSize
== 0 && Complete
== false)
873 // Acquire::Item::VerifyDone - check if Item was downloaded OK /*{{{*/
874 /* Note that hash-verification is 'hardcoded' in acquire-worker and has
875 * already passed if this method is called. */
876 bool pkgAcquire::Item::VerifyDone(std::string
const &Message
,
877 pkgAcquire::MethodConfig
const * const /*Cnf*/)
879 std::string
const FileName
= LookupTag(Message
,"Filename");
880 if (FileName
.empty() == true)
883 ErrorText
= "Method gave a blank filename";
890 // Acquire::Item::Done - Item downloaded OK /*{{{*/
891 void pkgAcquire::Item::Done(string
const &/*Message*/, HashStringList
const &Hashes
,
892 pkgAcquire::MethodConfig
const * const /*Cnf*/)
894 // We just downloaded something..
897 unsigned long long const downloadedSize
= Hashes
.FileSize();
898 if (downloadedSize
!= 0)
900 FileSize
= downloadedSize
;
904 ErrorText
= string();
905 Owner
->Dequeue(this);
908 // Acquire::Item::Rename - Rename a file /*{{{*/
909 // ---------------------------------------------------------------------
910 /* This helper function is used by a lot of item methods as their final
912 bool pkgAcquire::Item::Rename(string
const &From
,string
const &To
)
914 if (From
== To
|| rename(From
.c_str(),To
.c_str()) == 0)
918 strprintf(S
, _("rename failed, %s (%s -> %s)."), strerror(errno
),
919 From
.c_str(),To
.c_str());
921 if (ErrorText
.empty())
924 ErrorText
= ErrorText
+ ": " + S
;
928 void pkgAcquire::Item::Dequeue() /*{{{*/
930 Owner
->Dequeue(this);
933 bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState
const error
)/*{{{*/
935 if (RealFileExists(DestFile
))
936 Rename(DestFile
, DestFile
+ ".FAILED");
941 case HashSumMismatch
:
942 errtext
= _("Hash Sum mismatch");
945 errtext
= _("Size mismatch");
946 Status
= StatAuthError
;
949 errtext
= _("Invalid file format");
951 // do not report as usually its not the mirrors fault, but Portal/Proxy
954 errtext
= _("Signature error");
958 strprintf(errtext
, _("Clearsigned file isn't valid, got '%s' (does the network require authentication?)"), "NOSPLIT");
959 Status
= StatAuthError
;
961 case MaximumSizeExceeded
:
962 // the method is expected to report a good error for this
965 // no handling here, done by callers
968 if (ErrorText
.empty())
973 void pkgAcquire::Item::SetActiveSubprocess(const std::string
&subprocess
)/*{{{*/
975 ActiveSubprocess
= subprocess
;
976 APT_IGNORE_DEPRECATED(Mode
= ActiveSubprocess
.c_str();)
979 // Acquire::Item::ReportMirrorFailure /*{{{*/
980 void pkgAcquire::Item::ReportMirrorFailure(std::string
const &FailCode
)
982 ReportMirrorFailureToCentral(*this, FailCode
, FailCode
);
985 std::string
pkgAcquire::Item::HashSum() const /*{{{*/
987 HashStringList
const hashes
= GetExpectedHashes();
988 HashString
const * const hs
= hashes
.find(NULL
);
989 return hs
!= NULL
? hs
->toStr() : "";
992 bool pkgAcquire::Item::IsRedirectionLoop(std::string
const &NewURI
) /*{{{*/
994 // store can fail due to permission errors and the item will "loop" then
995 if (APT::String::Startswith(NewURI
, "store:"))
997 if (d
->PastRedirections
.empty())
999 d
->PastRedirections
.push_back(NewURI
);
1002 auto const LastURI
= std::prev(d
->PastRedirections
.end());
1003 // redirections to the same file are a way of restarting/resheduling,
1004 // individual methods will have to make sure that they aren't looping this way
1005 if (*LastURI
== NewURI
)
1007 if (std::find(d
->PastRedirections
.begin(), LastURI
, NewURI
) != LastURI
)
1009 d
->PastRedirections
.push_back(NewURI
);
1014 pkgAcqTransactionItem::pkgAcqTransactionItem(pkgAcquire
* const Owner
, /*{{{*/
1015 pkgAcqMetaClearSig
* const transactionManager
, IndexTarget
const &target
) :
1016 pkgAcquire::Item(Owner
), d(NULL
), Target(target
), TransactionManager(transactionManager
)
1018 if (TransactionManager
!= this)
1019 TransactionManager
->Add(this);
1022 pkgAcqTransactionItem::~pkgAcqTransactionItem() /*{{{*/
1026 HashStringList
pkgAcqTransactionItem::GetExpectedHashesFor(std::string
const &MetaKey
) const /*{{{*/
1028 return GetExpectedHashesFromFor(TransactionManager
->MetaIndexParser
, MetaKey
);
1032 static void LoadLastMetaIndexParser(pkgAcqMetaClearSig
* const TransactionManager
, std::string
const &FinalRelease
, std::string
const &FinalInRelease
)/*{{{*/
1034 if (TransactionManager
->IMSHit
== true)
1036 if (RealFileExists(FinalInRelease
) || RealFileExists(FinalRelease
))
1038 TransactionManager
->LastMetaIndexParser
= TransactionManager
->MetaIndexParser
->UnloadedClone();
1039 if (TransactionManager
->LastMetaIndexParser
!= NULL
)
1041 _error
->PushToStack();
1042 if (RealFileExists(FinalInRelease
))
1043 TransactionManager
->LastMetaIndexParser
->Load(FinalInRelease
, NULL
);
1045 TransactionManager
->LastMetaIndexParser
->Load(FinalRelease
, NULL
);
1046 // its unlikely to happen, but if what we have is bad ignore it
1047 if (_error
->PendingError())
1049 delete TransactionManager
->LastMetaIndexParser
;
1050 TransactionManager
->LastMetaIndexParser
= NULL
;
1052 _error
->RevertToStack();
1058 // AcqMetaBase - Constructor /*{{{*/
1059 pkgAcqMetaBase::pkgAcqMetaBase(pkgAcquire
* const Owner
,
1060 pkgAcqMetaClearSig
* const TransactionManager
,
1061 IndexTarget
const &DataTarget
)
1062 : pkgAcqTransactionItem(Owner
, TransactionManager
, DataTarget
), d(NULL
),
1063 AuthPass(false), IMSHit(false), State(TransactionStarted
)
1067 // AcqMetaBase::Add - Add a item to the current Transaction /*{{{*/
1068 void pkgAcqMetaBase::Add(pkgAcqTransactionItem
* const I
)
1070 Transaction
.push_back(I
);
1073 // AcqMetaBase::AbortTransaction - Abort the current Transaction /*{{{*/
1074 void pkgAcqMetaBase::AbortTransaction()
1076 if(_config
->FindB("Debug::Acquire::Transaction", false) == true)
1077 std::clog
<< "AbortTransaction: " << TransactionManager
<< std::endl
;
1079 switch (TransactionManager
->State
)
1081 case TransactionStarted
: break;
1082 case TransactionAbort
: _error
->Fatal("Transaction %s was already aborted and is aborted again", TransactionManager
->Target
.URI
.c_str()); return;
1083 case TransactionCommit
: _error
->Fatal("Transaction %s was already aborted and is now committed", TransactionManager
->Target
.URI
.c_str()); return;
1085 TransactionManager
->State
= TransactionAbort
;
1087 // ensure the toplevel is in error state too
1088 for (std::vector
<pkgAcqTransactionItem
*>::iterator I
= Transaction
.begin();
1089 I
!= Transaction
.end(); ++I
)
1091 if ((*I
)->Status
!= pkgAcquire::Item::StatFetching
)
1093 (*I
)->TransactionState(TransactionAbort
);
1095 Transaction
.clear();
1098 // AcqMetaBase::TransactionHasError - Check for errors in Transaction /*{{{*/
1099 APT_PURE
bool pkgAcqMetaBase::TransactionHasError() const
1101 for (std::vector
<pkgAcqTransactionItem
*>::const_iterator I
= Transaction
.begin();
1102 I
!= Transaction
.end(); ++I
)
1104 switch((*I
)->Status
) {
1105 case StatDone
: break;
1106 case StatIdle
: break;
1107 case StatAuthError
: return true;
1108 case StatError
: return true;
1109 case StatTransientNetworkError
: return true;
1110 case StatFetching
: break;
1116 // AcqMetaBase::CommitTransaction - Commit a transaction /*{{{*/
1117 void pkgAcqMetaBase::CommitTransaction()
1119 if(_config
->FindB("Debug::Acquire::Transaction", false) == true)
1120 std::clog
<< "CommitTransaction: " << this << std::endl
;
1122 switch (TransactionManager
->State
)
1124 case TransactionStarted
: break;
1125 case TransactionAbort
: _error
->Fatal("Transaction %s was already committed and is now aborted", TransactionManager
->Target
.URI
.c_str()); return;
1126 case TransactionCommit
: _error
->Fatal("Transaction %s was already committed and is again committed", TransactionManager
->Target
.URI
.c_str()); return;
1128 TransactionManager
->State
= TransactionCommit
;
1130 // move new files into place *and* remove files that are not
1131 // part of the transaction but are still on disk
1132 for (std::vector
<pkgAcqTransactionItem
*>::iterator I
= Transaction
.begin();
1133 I
!= Transaction
.end(); ++I
)
1135 (*I
)->TransactionState(TransactionCommit
);
1137 Transaction
.clear();
1140 // AcqMetaBase::TransactionStageCopy - Stage a file for copying /*{{{*/
1141 void pkgAcqMetaBase::TransactionStageCopy(pkgAcqTransactionItem
* const I
,
1142 const std::string
&From
,
1143 const std::string
&To
)
1145 I
->PartialFile
= From
;
1149 // AcqMetaBase::TransactionStageRemoval - Stage a file for removal /*{{{*/
1150 void pkgAcqMetaBase::TransactionStageRemoval(pkgAcqTransactionItem
* const I
,
1151 const std::string
&FinalFile
)
1153 I
->PartialFile
= "";
1154 I
->DestFile
= FinalFile
;
1157 // AcqMetaBase::GenerateAuthWarning - Check gpg authentication error /*{{{*/
1158 /* This method is called from ::Failed handlers. If it returns true,
1159 no fallback to other files or modi is performed */
1160 bool pkgAcqMetaBase::CheckStopAuthentication(pkgAcquire::Item
* const I
, const std::string
&Message
)
1162 string
const Final
= I
->GetFinalFilename();
1163 std::string
const GPGError
= LookupTag(Message
, "Message");
1164 if (FileExists(Final
))
1166 I
->Status
= StatTransientNetworkError
;
1167 _error
->Warning(_("An error occurred during the signature verification. "
1168 "The repository is not updated and the previous index files will be used. "
1169 "GPG error: %s: %s"),
1170 Desc
.Description
.c_str(),
1172 RunScripts("APT::Update::Auth-Failure");
1174 } else if (LookupTag(Message
,"Message").find("NODATA") != string::npos
) {
1175 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
1176 _error
->Error(_("GPG error: %s: %s"),
1177 Desc
.Description
.c_str(),
1179 I
->Status
= StatAuthError
;
1182 _error
->Warning(_("GPG error: %s: %s"),
1183 Desc
.Description
.c_str(),
1186 // gpgv method failed
1187 ReportMirrorFailureToCentral(*this, "GPGFailure", GPGError
);
1191 // AcqMetaBase::Custom600Headers - Get header for AcqMetaBase /*{{{*/
1192 // ---------------------------------------------------------------------
1193 string
pkgAcqMetaBase::Custom600Headers() const
1195 std::string Header
= "\nIndex-File: true";
1196 std::string MaximumSize
;
1197 strprintf(MaximumSize
, "\nMaximum-Size: %i",
1198 _config
->FindI("Acquire::MaxReleaseFileSize", 10*1000*1000));
1199 Header
+= MaximumSize
;
1201 string
const FinalFile
= GetFinalFilename();
1203 if (stat(FinalFile
.c_str(),&Buf
) == 0)
1204 Header
+= "\nLast-Modified: " + TimeRFC1123(Buf
.st_mtime
, false);
1209 // AcqMetaBase::QueueForSignatureVerify /*{{{*/
1210 void pkgAcqMetaBase::QueueForSignatureVerify(pkgAcqTransactionItem
* const I
, std::string
const &File
, std::string
const &Signature
)
1213 I
->Desc
.URI
= "gpgv:" + Signature
;
1216 I
->SetActiveSubprocess("gpgv");
1219 // AcqMetaBase::CheckDownloadDone /*{{{*/
1220 bool pkgAcqMetaBase::CheckDownloadDone(pkgAcqTransactionItem
* const I
, const std::string
&Message
, HashStringList
const &Hashes
) const
1222 // We have just finished downloading a Release file (it is not
1225 // Save the final base URI we got this Release file from
1226 if (I
->UsedMirror
.empty() == false && _config
->FindB("Acquire::SameMirrorForAllIndexes", true))
1228 if (APT::String::Endswith(I
->Desc
.URI
, "InRelease"))
1229 TransactionManager
->BaseURI
= I
->Desc
.URI
.substr(0, I
->Desc
.URI
.length() - strlen("InRelease"));
1230 else if (APT::String::Endswith(I
->Desc
.URI
, "Release"))
1231 TransactionManager
->BaseURI
= I
->Desc
.URI
.substr(0, I
->Desc
.URI
.length() - strlen("Release"));
1234 std::string
const FileName
= LookupTag(Message
,"Filename");
1235 if (FileName
!= I
->DestFile
&& RealFileExists(I
->DestFile
) == false)
1238 I
->Desc
.URI
= "copy:" + FileName
;
1239 I
->QueueURI(I
->Desc
);
1243 // make sure to verify against the right file on I-M-S hit
1244 bool IMSHit
= StringToBool(LookupTag(Message
,"IMS-Hit"), false);
1245 if (IMSHit
== false && Hashes
.usable())
1247 // detect IMS-Hits servers haven't detected by Hash comparison
1248 std::string
const FinalFile
= I
->GetFinalFilename();
1249 if (RealFileExists(FinalFile
) && Hashes
.VerifyFile(FinalFile
) == true)
1252 RemoveFile("CheckDownloadDone", I
->DestFile
);
1258 // for simplicity, the transaction manager is always InRelease
1259 // even if it doesn't exist.
1260 TransactionManager
->IMSHit
= true;
1261 I
->PartialFile
= I
->DestFile
= I
->GetFinalFilename();
1264 // set Item to complete as the remaining work is all local (verify etc)
1270 bool pkgAcqMetaBase::CheckAuthDone(string
const &Message
) /*{{{*/
1272 // At this point, the gpgv method has succeeded, so there is a
1273 // valid signature from a key in the trusted keyring. We
1274 // perform additional verification of its contents, and use them
1275 // to verify the indexes we are about to download
1276 if (_config
->FindB("Debug::pkgAcquire::Auth", false))
1277 std::cerr
<< "Signature verification succeeded: " << DestFile
<< std::endl
;
1279 if (TransactionManager
->IMSHit
== false)
1281 // open the last (In)Release if we have it
1282 std::string
const FinalFile
= GetFinalFilename();
1283 std::string FinalRelease
;
1284 std::string FinalInRelease
;
1285 if (APT::String::Endswith(FinalFile
, "InRelease"))
1287 FinalInRelease
= FinalFile
;
1288 FinalRelease
= FinalFile
.substr(0, FinalFile
.length() - strlen("InRelease")) + "Release";
1292 FinalInRelease
= FinalFile
.substr(0, FinalFile
.length() - strlen("Release")) + "InRelease";
1293 FinalRelease
= FinalFile
;
1295 LoadLastMetaIndexParser(TransactionManager
, FinalRelease
, FinalInRelease
);
1298 bool const GoodAuth
= TransactionManager
->MetaIndexParser
->Load(DestFile
, &ErrorText
);
1299 if (GoodAuth
== false && AllowInsecureRepositories(InsecureType::WEAK
, Target
.Description
, TransactionManager
->MetaIndexParser
, TransactionManager
, this) == false)
1301 Status
= StatAuthError
;
1305 if (!VerifyVendor(Message
))
1307 Status
= StatAuthError
;
1311 // Download further indexes with verification
1312 TransactionManager
->QueueIndexes(GoodAuth
);
1317 void pkgAcqMetaClearSig::QueueIndexes(bool const verify
) /*{{{*/
1319 // at this point the real Items are loaded in the fetcher
1320 ExpectedAdditionalItems
= 0;
1322 std::set
<std::string
> targetsSeen
;
1323 bool const hasReleaseFile
= TransactionManager
->MetaIndexParser
!= NULL
;
1324 bool const metaBaseSupportsByHash
= hasReleaseFile
&& TransactionManager
->MetaIndexParser
->GetSupportsAcquireByHash();
1325 bool hasHashes
= true;
1326 auto IndexTargets
= TransactionManager
->MetaIndexParser
->GetIndexTargets();
1327 if (hasReleaseFile
&& verify
== false)
1328 hasHashes
= std::any_of(IndexTargets
.begin(), IndexTargets
.end(),
1329 [&](IndexTarget
const &Target
) { return TransactionManager
->MetaIndexParser
->Exists(Target
.MetaKey
); });
1330 for (auto&& Target
: IndexTargets
)
1332 // if we have seen a target which is created-by a target this one here is declared a
1333 // fallback to, we skip acquiring the fallback (but we make sure we clean up)
1334 if (targetsSeen
.find(Target
.Option(IndexTarget::FALLBACK_OF
)) != targetsSeen
.end())
1336 targetsSeen
.emplace(Target
.Option(IndexTarget::CREATED_BY
));
1337 new CleanupItem(Owner
, TransactionManager
, Target
);
1340 // all is an implementation detail. Users shouldn't use this as arch
1341 // We need this support trickery here as e.g. Debian has binary-all files already,
1342 // but arch:all packages are still in the arch:any files, so we would waste precious
1343 // download time, bandwidth and diskspace for nothing, BUT Debian doesn't feature all
1344 // in the set of supported architectures, so we can filter based on this property rather
1345 // than invent an entirely new flag we would need to carry for all of eternity.
1346 if (hasReleaseFile
&& Target
.Option(IndexTarget::ARCHITECTURE
) == "all")
1348 if (TransactionManager
->MetaIndexParser
->IsArchitectureAllSupportedFor(Target
) == false)
1350 new CleanupItem(Owner
, TransactionManager
, Target
);
1355 bool trypdiff
= Target
.OptionBool(IndexTarget::PDIFFS
);
1356 if (hasReleaseFile
== true)
1358 if (TransactionManager
->MetaIndexParser
->Exists(Target
.MetaKey
) == false)
1360 // optional targets that we do not have in the Release file are skipped
1361 if (hasHashes
== true && Target
.IsOptional
)
1363 new CleanupItem(Owner
, TransactionManager
, Target
);
1367 std::string
const &arch
= Target
.Option(IndexTarget::ARCHITECTURE
);
1368 if (arch
.empty() == false)
1370 if (TransactionManager
->MetaIndexParser
->IsArchitectureSupported(arch
) == false)
1372 new CleanupItem(Owner
, TransactionManager
, Target
);
1373 _error
->Notice(_("Skipping acquire of configured file '%s' as repository '%s' doesn't support architecture '%s'"),
1374 Target
.MetaKey
.c_str(), TransactionManager
->Target
.Description
.c_str(), arch
.c_str());
1377 // if the architecture is officially supported but currently no packages for it available,
1378 // ignore silently as this is pretty much the same as just shipping an empty file.
1379 // if we don't know which architectures are supported, we do NOT ignore it to notify user about this
1380 if (hasHashes
== true && TransactionManager
->MetaIndexParser
->IsArchitectureSupported("*undefined*") == false)
1382 new CleanupItem(Owner
, TransactionManager
, Target
);
1387 if (hasHashes
== true)
1389 Status
= StatAuthError
;
1390 strprintf(ErrorText
, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), Target
.MetaKey
.c_str());
1395 new pkgAcqIndex(Owner
, TransactionManager
, Target
);
1401 auto const hashes
= GetExpectedHashesFor(Target
.MetaKey
);
1402 if (hashes
.empty() == false)
1404 if (hashes
.usable() == false && TargetIsAllowedToBe(TransactionManager
->Target
, InsecureType::WEAK
) == false)
1406 new CleanupItem(Owner
, TransactionManager
, Target
);
1407 _error
->Warning(_("Skipping acquire of configured file '%s' as repository '%s' provides only weak security information for it"),
1408 Target
.MetaKey
.c_str(), TransactionManager
->Target
.Description
.c_str());
1411 // empty files are skipped as acquiring the very small compressed files is a waste of time
1412 else if (hashes
.FileSize() == 0)
1414 new CleanupItem(Owner
, TransactionManager
, Target
);
1415 targetsSeen
.emplace(Target
.Option(IndexTarget::CREATED_BY
));
1421 // autoselect the compression method
1422 std::vector
<std::string
> types
= VectorizeString(Target
.Option(IndexTarget::COMPRESSIONTYPES
), ' ');
1423 types
.erase(std::remove_if(types
.begin(), types
.end(), [&](std::string
const &t
) {
1424 if (t
== "uncompressed")
1425 return TransactionManager
->MetaIndexParser
->Exists(Target
.MetaKey
) == false;
1426 std::string
const MetaKey
= Target
.MetaKey
+ "." + t
;
1427 return TransactionManager
->MetaIndexParser
->Exists(MetaKey
) == false;
1429 if (types
.empty() == false)
1431 std::ostringstream os
;
1432 // add the special compressiontype byhash first if supported
1433 std::string
const useByHashConf
= Target
.Option(IndexTarget::BY_HASH
);
1434 bool useByHash
= false;
1435 if(useByHashConf
== "force")
1438 useByHash
= StringToBool(useByHashConf
) == true && metaBaseSupportsByHash
;
1439 if (useByHash
== true)
1441 std::copy(types
.begin(), types
.end()-1, std::ostream_iterator
<std::string
>(os
, " "));
1442 os
<< *types
.rbegin();
1443 Target
.Options
["COMPRESSIONTYPES"] = os
.str();
1446 Target
.Options
["COMPRESSIONTYPES"].clear();
1448 std::string filename
= GetExistingFilename(GetFinalFileNameFromURI(Target
.URI
));
1449 if (filename
.empty() == false)
1451 // if the Release file is a hit and we have an index it must be the current one
1452 if (TransactionManager
->IMSHit
== true)
1454 else if (TransactionManager
->LastMetaIndexParser
!= NULL
)
1456 // see if the file changed since the last Release file
1457 // we use the uncompressed files as we might compress differently compared to the server,
1458 // so the hashes might not match, even if they contain the same data.
1459 HashStringList
const newFile
= GetExpectedHashesFromFor(TransactionManager
->MetaIndexParser
, Target
.MetaKey
);
1460 HashStringList
const oldFile
= GetExpectedHashesFromFor(TransactionManager
->LastMetaIndexParser
, Target
.MetaKey
);
1461 if (newFile
!= oldFile
)
1468 trypdiff
= false; // no file to patch
1470 if (filename
.empty() == false)
1472 new NoActionItem(Owner
, Target
, filename
);
1473 std::string
const idxfilename
= GetFinalFileNameFromURI(GetDiffIndexURI(Target
));
1474 if (FileExists(idxfilename
))
1475 new NoActionItem(Owner
, Target
, idxfilename
);
1476 targetsSeen
.emplace(Target
.Option(IndexTarget::CREATED_BY
));
1480 // check if we have patches available
1481 trypdiff
&= TransactionManager
->MetaIndexParser
->Exists(GetDiffIndexFileName(Target
.MetaKey
));
1485 // if we have no file to patch, no point in trying
1486 trypdiff
&= (GetExistingFilename(GetFinalFileNameFromURI(Target
.URI
)).empty() == false);
1489 // no point in patching from local sources
1492 std::string
const proto
= Target
.URI
.substr(0, strlen("file:/"));
1493 if (proto
== "file:/" || proto
== "copy:/" || proto
== "cdrom:")
1497 // Queue the Index file (Packages, Sources, Translation-$foo, …)
1498 targetsSeen
.emplace(Target
.Option(IndexTarget::CREATED_BY
));
1500 new pkgAcqDiffIndex(Owner
, TransactionManager
, Target
);
1502 new pkgAcqIndex(Owner
, TransactionManager
, Target
);
1506 bool pkgAcqMetaBase::VerifyVendor(string
const &) /*{{{*/
1508 string Transformed
= TransactionManager
->MetaIndexParser
->GetExpectedDist();
1510 if (Transformed
== "../project/experimental")
1512 Transformed
= "experimental";
1515 auto pos
= Transformed
.rfind('/');
1516 if (pos
!= string::npos
)
1518 Transformed
= Transformed
.substr(0, pos
);
1521 if (Transformed
== ".")
1526 if (TransactionManager
->MetaIndexParser
->GetValidUntil() > 0)
1528 time_t const invalid_since
= time(NULL
) - TransactionManager
->MetaIndexParser
->GetValidUntil();
1529 if (invalid_since
> 0)
1533 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1534 // the time since then the file is invalid - formatted in the same way as in
1535 // the download progress display (e.g. 7d 3h 42min 1s)
1536 _("Release file for %s is expired (invalid since %s). "
1537 "Updates for this repository will not be applied."),
1538 Target
.URI
.c_str(), TimeToStr(invalid_since
).c_str());
1539 if (ErrorText
.empty())
1541 return _error
->Error("%s", errmsg
.c_str());
1545 /* Did we get a file older than what we have? This is a last minute IMS hit and doubles
1546 as a prevention of downgrading us to older (still valid) files */
1547 if (TransactionManager
->IMSHit
== false && TransactionManager
->LastMetaIndexParser
!= NULL
&&
1548 TransactionManager
->LastMetaIndexParser
->GetDate() > TransactionManager
->MetaIndexParser
->GetDate())
1550 TransactionManager
->IMSHit
= true;
1551 RemoveFile("VerifyVendor", DestFile
);
1552 PartialFile
= DestFile
= GetFinalFilename();
1553 // load the 'old' file in the 'new' one instead of flipping pointers as
1554 // the new one isn't owned by us, while the old one is so cleanup would be confused.
1555 TransactionManager
->MetaIndexParser
->swapLoad(TransactionManager
->LastMetaIndexParser
);
1556 delete TransactionManager
->LastMetaIndexParser
;
1557 TransactionManager
->LastMetaIndexParser
= NULL
;
1560 if (_config
->FindB("Debug::pkgAcquire::Auth", false))
1562 std::cerr
<< "Got Codename: " << TransactionManager
->MetaIndexParser
->GetCodename() << std::endl
;
1563 std::cerr
<< "Expecting Dist: " << TransactionManager
->MetaIndexParser
->GetExpectedDist() << std::endl
;
1564 std::cerr
<< "Transformed Dist: " << Transformed
<< std::endl
;
1567 if (TransactionManager
->MetaIndexParser
->CheckDist(Transformed
) == false)
1569 // This might become fatal one day
1570 // Status = StatAuthError;
1571 // ErrorText = "Conflicting distribution; expected "
1572 // + MetaIndexParser->GetExpectedDist() + " but got "
1573 // + MetaIndexParser->GetCodename();
1575 if (!Transformed
.empty())
1577 _error
->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1578 Desc
.Description
.c_str(),
1579 Transformed
.c_str(),
1580 TransactionManager
->MetaIndexParser
->GetCodename().c_str());
1587 pkgAcqMetaBase::~pkgAcqMetaBase()
1591 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire
* const Owner
, /*{{{*/
1592 IndexTarget
const &ClearsignedTarget
,
1593 IndexTarget
const &DetachedDataTarget
, IndexTarget
const &DetachedSigTarget
,
1594 metaIndex
* const MetaIndexParser
) :
1595 pkgAcqMetaIndex(Owner
, this, ClearsignedTarget
, DetachedSigTarget
),
1596 d(NULL
), DetachedDataTarget(DetachedDataTarget
),
1597 MetaIndexParser(MetaIndexParser
), LastMetaIndexParser(NULL
)
1599 // index targets + (worst case:) Release/Release.gpg
1600 ExpectedAdditionalItems
= std::numeric_limits
<decltype(ExpectedAdditionalItems
)>::max();
1601 TransactionManager
->Add(this);
1604 pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
1606 if (LastMetaIndexParser
!= NULL
)
1607 delete LastMetaIndexParser
;
1610 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1611 string
pkgAcqMetaClearSig::Custom600Headers() const
1613 string Header
= pkgAcqMetaBase::Custom600Headers();
1614 Header
+= "\nFail-Ignore: true";
1615 std::string
const key
= TransactionManager
->MetaIndexParser
->GetSignedBy();
1616 if (key
.empty() == false)
1617 Header
+= "\nSigned-By: " + key
;
1622 void pkgAcqMetaClearSig::Finished() /*{{{*/
1624 if(_config
->FindB("Debug::Acquire::Transaction", false) == true)
1625 std::clog
<< "Finished: " << DestFile
<<std::endl
;
1626 if(TransactionManager
->State
== TransactionStarted
&&
1627 TransactionManager
->TransactionHasError() == false)
1628 TransactionManager
->CommitTransaction();
1631 bool pkgAcqMetaClearSig::VerifyDone(std::string
const &Message
, /*{{{*/
1632 pkgAcquire::MethodConfig
const * const Cnf
)
1634 Item::VerifyDone(Message
, Cnf
);
1636 if (FileExists(DestFile
) && !StartsWithGPGClearTextSignature(DestFile
))
1637 return RenameOnError(NotClearsigned
);
1642 // pkgAcqMetaClearSig::Done - We got a file /*{{{*/
1643 void pkgAcqMetaClearSig::Done(std::string
const &Message
,
1644 HashStringList
const &Hashes
,
1645 pkgAcquire::MethodConfig
const * const Cnf
)
1647 Item::Done(Message
, Hashes
, Cnf
);
1649 if(AuthPass
== false)
1651 if(CheckDownloadDone(this, Message
, Hashes
) == true)
1652 QueueForSignatureVerify(this, DestFile
, DestFile
);
1655 else if(CheckAuthDone(Message
) == true)
1657 if (TransactionManager
->IMSHit
== false)
1658 TransactionManager
->TransactionStageCopy(this, DestFile
, GetFinalFilename());
1659 else if (RealFileExists(GetFinalFilename()) == false)
1661 // We got an InRelease file IMSHit, but we haven't one, which means
1662 // we had a valid Release/Release.gpg combo stepping in, which we have
1663 // to 'acquire' now to ensure list cleanup isn't removing them
1664 new NoActionItem(Owner
, DetachedDataTarget
);
1665 new NoActionItem(Owner
, DetachedSigTarget
);
1668 else if (Status
!= StatAuthError
)
1670 string
const FinalFile
= GetFinalFileNameFromURI(DetachedDataTarget
.URI
);
1671 string
const OldFile
= GetFinalFilename();
1672 if (TransactionManager
->IMSHit
== false)
1673 TransactionManager
->TransactionStageCopy(this, DestFile
, FinalFile
);
1674 else if (RealFileExists(OldFile
) == false)
1675 new NoActionItem(Owner
, DetachedDataTarget
);
1677 TransactionManager
->TransactionStageCopy(this, OldFile
, FinalFile
);
1681 void pkgAcqMetaClearSig::Failed(string
const &Message
,pkgAcquire::MethodConfig
const * const Cnf
) /*{{{*/
1683 Item::Failed(Message
, Cnf
);
1685 if (AuthPass
== false)
1687 if (Status
== StatAuthError
|| Status
== StatTransientNetworkError
)
1689 // if we expected a ClearTextSignature (InRelease) but got a network
1690 // error or got a file, but it wasn't valid, we end up here (see VerifyDone).
1691 // As these is usually called by web-portals we do not try Release/Release.gpg
1692 // as this is gonna fail anyway and instead abort our try (LP#346386)
1693 TransactionManager
->AbortTransaction();
1697 // Queue the 'old' InRelease file for removal if we try Release.gpg
1698 // as otherwise the file will stay around and gives a false-auth
1699 // impression (CVE-2012-0214)
1700 TransactionManager
->TransactionStageRemoval(this, GetFinalFilename());
1703 new pkgAcqMetaIndex(Owner
, TransactionManager
, DetachedDataTarget
, DetachedSigTarget
);
1707 if(CheckStopAuthentication(this, Message
))
1710 if(AllowInsecureRepositories(InsecureType::UNSIGNED
, Target
.Description
, TransactionManager
->MetaIndexParser
, TransactionManager
, this) == true)
1714 /* InRelease files become Release files, otherwise
1715 * they would be considered as trusted later on */
1716 string
const FinalRelease
= GetFinalFileNameFromURI(DetachedDataTarget
.URI
);
1717 string
const PartialRelease
= GetPartialFileNameFromURI(DetachedDataTarget
.URI
);
1718 string
const FinalReleasegpg
= GetFinalFileNameFromURI(DetachedSigTarget
.URI
);
1719 string
const FinalInRelease
= GetFinalFilename();
1720 Rename(DestFile
, PartialRelease
);
1721 TransactionManager
->TransactionStageCopy(this, PartialRelease
, FinalRelease
);
1722 LoadLastMetaIndexParser(TransactionManager
, FinalRelease
, FinalInRelease
);
1724 // we parse the indexes here because at this point the user wanted
1725 // a repository that may potentially harm him
1726 if (TransactionManager
->MetaIndexParser
->Load(PartialRelease
, &ErrorText
) == false || VerifyVendor(Message
) == false)
1727 /* expired Release files are still a problem you need extra force for */;
1729 TransactionManager
->QueueIndexes(true);
1735 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire
* const Owner
, /*{{{*/
1736 pkgAcqMetaClearSig
* const TransactionManager
,
1737 IndexTarget
const &DataTarget
,
1738 IndexTarget
const &DetachedSigTarget
) :
1739 pkgAcqMetaBase(Owner
, TransactionManager
, DataTarget
), d(NULL
),
1740 DetachedSigTarget(DetachedSigTarget
)
1742 if(_config
->FindB("Debug::Acquire::Transaction", false) == true)
1743 std::clog
<< "New pkgAcqMetaIndex with TransactionManager "
1744 << this->TransactionManager
<< std::endl
;
1746 DestFile
= GetPartialFileNameFromURI(DataTarget
.URI
);
1749 Desc
.Description
= DataTarget
.Description
;
1751 Desc
.ShortDesc
= DataTarget
.ShortDesc
;
1752 Desc
.URI
= DataTarget
.URI
;
1756 void pkgAcqMetaIndex::Done(string
const &Message
, /*{{{*/
1757 HashStringList
const &Hashes
,
1758 pkgAcquire::MethodConfig
const * const Cfg
)
1760 Item::Done(Message
,Hashes
,Cfg
);
1762 if(CheckDownloadDone(this, Message
, Hashes
))
1764 // we have a Release file, now download the Signature, all further
1765 // verify/queue for additional downloads will be done in the
1766 // pkgAcqMetaSig::Done() code
1767 new pkgAcqMetaSig(Owner
, TransactionManager
, DetachedSigTarget
, this);
1771 // pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
1772 void pkgAcqMetaIndex::Failed(string
const &Message
,
1773 pkgAcquire::MethodConfig
const * const Cnf
)
1775 pkgAcquire::Item::Failed(Message
, Cnf
);
1778 // No Release file was present so fall
1779 // back to queueing Packages files without verification
1780 // only allow going further if the user explicitly wants it
1781 if(AllowInsecureRepositories(InsecureType::NORELEASE
, Target
.Description
, TransactionManager
->MetaIndexParser
, TransactionManager
, this) == true)
1783 // ensure old Release files are removed
1784 TransactionManager
->TransactionStageRemoval(this, GetFinalFilename());
1786 // queue without any kind of hashsum support
1787 TransactionManager
->QueueIndexes(false);
1791 std::string
pkgAcqMetaIndex::DescURI() const /*{{{*/
1796 pkgAcqMetaIndex::~pkgAcqMetaIndex() {}
1798 // AcqMetaSig::AcqMetaSig - Constructor /*{{{*/
1799 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire
* const Owner
,
1800 pkgAcqMetaClearSig
* const TransactionManager
,
1801 IndexTarget
const &Target
,
1802 pkgAcqMetaIndex
* const MetaIndex
) :
1803 pkgAcqTransactionItem(Owner
, TransactionManager
, Target
), d(NULL
), MetaIndex(MetaIndex
)
1805 DestFile
= GetPartialFileNameFromURI(Target
.URI
);
1807 // remove any partial downloaded sig-file in partial/.
1808 // it may confuse proxies and is too small to warrant a
1809 // partial download anyway
1810 RemoveFile("pkgAcqMetaSig", DestFile
);
1812 // set the TransactionManager
1813 if(_config
->FindB("Debug::Acquire::Transaction", false) == true)
1814 std::clog
<< "New pkgAcqMetaSig with TransactionManager "
1815 << TransactionManager
<< std::endl
;
1818 Desc
.Description
= Target
.Description
;
1820 Desc
.ShortDesc
= Target
.ShortDesc
;
1821 Desc
.URI
= Target
.URI
;
1823 // If we got a hit for Release, we will get one for Release.gpg too (or obscure errors),
1824 // so we skip the download step and go instantly to verification
1825 if (TransactionManager
->IMSHit
== true && RealFileExists(GetFinalFilename()))
1829 PartialFile
= DestFile
= GetFinalFilename();
1830 MetaIndexFileSignature
= DestFile
;
1831 MetaIndex
->QueueForSignatureVerify(this, MetaIndex
->DestFile
, DestFile
);
1837 pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1841 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1842 std::string
pkgAcqMetaSig::Custom600Headers() const
1844 std::string Header
= pkgAcqTransactionItem::Custom600Headers();
1845 std::string
const key
= TransactionManager
->MetaIndexParser
->GetSignedBy();
1846 if (key
.empty() == false)
1847 Header
+= "\nSigned-By: " + key
;
1851 // AcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
1852 void pkgAcqMetaSig::Done(string
const &Message
, HashStringList
const &Hashes
,
1853 pkgAcquire::MethodConfig
const * const Cfg
)
1855 if (MetaIndexFileSignature
.empty() == false)
1857 DestFile
= MetaIndexFileSignature
;
1858 MetaIndexFileSignature
.clear();
1860 Item::Done(Message
, Hashes
, Cfg
);
1862 if(MetaIndex
->AuthPass
== false)
1864 if(MetaIndex
->CheckDownloadDone(this, Message
, Hashes
) == true)
1866 // destfile will be modified to point to MetaIndexFile for the
1867 // gpgv method, so we need to save it here
1868 MetaIndexFileSignature
= DestFile
;
1869 MetaIndex
->QueueForSignatureVerify(this, MetaIndex
->DestFile
, DestFile
);
1873 else if(MetaIndex
->CheckAuthDone(Message
) == true)
1875 if (TransactionManager
->IMSHit
== false)
1877 TransactionManager
->TransactionStageCopy(this, DestFile
, GetFinalFilename());
1878 TransactionManager
->TransactionStageCopy(MetaIndex
, MetaIndex
->DestFile
, MetaIndex
->GetFinalFilename());
1881 else if (MetaIndex
->Status
!= StatAuthError
)
1883 std::string
const FinalFile
= MetaIndex
->GetFinalFilename();
1884 if (TransactionManager
->IMSHit
== false)
1885 TransactionManager
->TransactionStageCopy(MetaIndex
, MetaIndex
->DestFile
, FinalFile
);
1887 TransactionManager
->TransactionStageCopy(MetaIndex
, FinalFile
, FinalFile
);
1891 void pkgAcqMetaSig::Failed(string
const &Message
,pkgAcquire::MethodConfig
const * const Cnf
)/*{{{*/
1893 Item::Failed(Message
,Cnf
);
1895 // check if we need to fail at this point
1896 if (MetaIndex
->AuthPass
== true && MetaIndex
->CheckStopAuthentication(this, Message
))
1899 // ensures that a Release.gpg file in the lists/ is removed by the transaction
1900 TransactionManager
->TransactionStageRemoval(this, DestFile
);
1902 // only allow going further if the user explicitly wants it
1903 if (AllowInsecureRepositories(InsecureType::UNSIGNED
, MetaIndex
->Target
.Description
, TransactionManager
->MetaIndexParser
, TransactionManager
, this) == true)
1905 string
const FinalRelease
= MetaIndex
->GetFinalFilename();
1906 string
const FinalInRelease
= TransactionManager
->GetFinalFilename();
1907 LoadLastMetaIndexParser(TransactionManager
, FinalRelease
, FinalInRelease
);
1909 // we parse the indexes here because at this point the user wanted
1910 // a repository that may potentially harm him
1911 bool const GoodLoad
= TransactionManager
->MetaIndexParser
->Load(MetaIndex
->DestFile
, &ErrorText
);
1912 if (MetaIndex
->VerifyVendor(Message
) == false)
1913 /* expired Release files are still a problem you need extra force for */;
1915 TransactionManager
->QueueIndexes(GoodLoad
);
1917 TransactionManager
->TransactionStageCopy(MetaIndex
, MetaIndex
->DestFile
, FinalRelease
);
1919 else if (TransactionManager
->IMSHit
== false)
1920 Rename(MetaIndex
->DestFile
, MetaIndex
->DestFile
+ ".FAILED");
1922 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1923 if (Cnf
->LocalOnly
== true ||
1924 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == false)
1933 // AcqBaseIndex - Constructor /*{{{*/
1934 pkgAcqBaseIndex::pkgAcqBaseIndex(pkgAcquire
* const Owner
,
1935 pkgAcqMetaClearSig
* const TransactionManager
,
1936 IndexTarget
const &Target
)
1937 : pkgAcqTransactionItem(Owner
, TransactionManager
, Target
), d(NULL
)
1941 void pkgAcqBaseIndex::Failed(std::string
const &Message
,pkgAcquire::MethodConfig
const * const Cnf
)/*{{{*/
1943 pkgAcquire::Item::Failed(Message
, Cnf
);
1944 if (Status
!= StatAuthError
)
1947 ErrorText
.append("Release file created at: ");
1948 auto const timespec
= TransactionManager
->MetaIndexParser
->GetDate();
1950 ErrorText
.append("<unknown>");
1952 ErrorText
.append(TimeRFC1123(timespec
, true));
1953 ErrorText
.append("\n");
1956 pkgAcqBaseIndex::~pkgAcqBaseIndex() {}
1958 // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
1959 // ---------------------------------------------------------------------
1960 /* Get the DiffIndex file first and see if there are patches available
1961 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
1962 * patches. If anything goes wrong in that process, it will fall back to
1963 * the original packages file
1965 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire
* const Owner
,
1966 pkgAcqMetaClearSig
* const TransactionManager
,
1967 IndexTarget
const &Target
)
1968 : pkgAcqBaseIndex(Owner
, TransactionManager
, Target
), d(NULL
), diffs(NULL
)
1970 // FIXME: Magic number as an upper bound on pdiffs we will reasonably acquire
1971 ExpectedAdditionalItems
= 40;
1973 Debug
= _config
->FindB("Debug::pkgAcquire::Diffs",false);
1976 Desc
.Description
= GetDiffIndexFileName(Target
.Description
);
1977 Desc
.ShortDesc
= Target
.ShortDesc
;
1978 Desc
.URI
= GetDiffIndexURI(Target
);
1980 DestFile
= GetPartialFileNameFromURI(Desc
.URI
);
1983 std::clog
<< "pkgAcqDiffIndex: " << Desc
.URI
<< std::endl
;
1988 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
1989 // ---------------------------------------------------------------------
1990 /* The only header we use is the last-modified header. */
1991 string
pkgAcqDiffIndex::Custom600Headers() const
1993 if (TransactionManager
->LastMetaIndexParser
!= NULL
)
1994 return "\nIndex-File: true";
1996 string
const Final
= GetFinalFilename();
1999 std::clog
<< "Custom600Header-IMS: " << Final
<< std::endl
;
2002 if (stat(Final
.c_str(),&Buf
) != 0)
2003 return "\nIndex-File: true";
2005 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf
.st_mtime
, false);
2008 void pkgAcqDiffIndex::QueueOnIMSHit() const /*{{{*/
2010 // list cleanup needs to know that this file as well as the already
2011 // present index is ours, so we create an empty diff to save it for us
2012 new pkgAcqIndexDiffs(Owner
, TransactionManager
, Target
);
2015 static bool RemoveFileForBootstrapLinking(bool const Debug
, std::string
const &For
, std::string
const &Boot
)/*{{{*/
2017 if (FileExists(Boot
) && RemoveFile("Bootstrap-linking", Boot
) == false)
2020 std::clog
<< "Bootstrap-linking for patching " << For
2021 << " by removing stale " << Boot
<< " failed!" << std::endl
;
2027 bool pkgAcqDiffIndex::ParseDiffIndex(string
const &IndexDiffFile
) /*{{{*/
2029 ExpectedAdditionalItems
= 0;
2030 // failing here is fine: our caller will take care of trying to
2031 // get the complete file if patching fails
2033 std::clog
<< "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
2036 FileFd
Fd(IndexDiffFile
,FileFd::ReadOnly
);
2038 if (Fd
.IsOpen() == false || Fd
.Failed())
2042 if(unlikely(TF
.Step(Tags
) == false))
2045 HashStringList ServerHashes
;
2046 unsigned long long ServerSize
= 0;
2048 auto const &posix
= std::locale("C.UTF-8");
2049 for (char const * const * type
= HashString::SupportedHashes(); *type
!= NULL
; ++type
)
2051 std::string tagname
= *type
;
2052 tagname
.append("-Current");
2053 std::string
const tmp
= Tags
.FindS(tagname
.c_str());
2054 if (tmp
.empty() == true)
2058 unsigned long long size
;
2059 std::stringstream
ss(tmp
);
2062 if (unlikely(hash
.empty() == true))
2064 if (unlikely(ServerSize
!= 0 && ServerSize
!= size
))
2066 ServerHashes
.push_back(HashString(*type
, hash
));
2070 if (ServerHashes
.usable() == false)
2073 std::clog
<< "pkgAcqDiffIndex: " << IndexDiffFile
<< ": Did not find a good hashsum in the index" << std::endl
;
2077 std::string
const CurrentPackagesFile
= GetFinalFileNameFromURI(Target
.URI
);
2078 HashStringList
const TargetFileHashes
= GetExpectedHashesFor(Target
.MetaKey
);
2079 if (TargetFileHashes
.usable() == false || ServerHashes
!= TargetFileHashes
)
2083 std::clog
<< "pkgAcqDiffIndex: " << IndexDiffFile
<< ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl
;
2084 printHashSumComparison(CurrentPackagesFile
, ServerHashes
, TargetFileHashes
);
2089 HashStringList LocalHashes
;
2090 // try avoiding calculating the hash here as this is costly
2091 if (TransactionManager
->LastMetaIndexParser
!= NULL
)
2092 LocalHashes
= GetExpectedHashesFromFor(TransactionManager
->LastMetaIndexParser
, Target
.MetaKey
);
2093 if (LocalHashes
.usable() == false)
2095 FileFd
fd(CurrentPackagesFile
, FileFd::ReadOnly
, FileFd::Auto
);
2096 Hashes
LocalHashesCalc(ServerHashes
);
2097 LocalHashesCalc
.AddFD(fd
);
2098 LocalHashes
= LocalHashesCalc
.GetHashStringList();
2101 if (ServerHashes
== LocalHashes
)
2103 // we have the same sha1 as the server so we are done here
2105 std::clog
<< "pkgAcqDiffIndex: Package file " << CurrentPackagesFile
<< " is up-to-date" << std::endl
;
2111 std::clog
<< "Server-Current: " << ServerHashes
.find(NULL
)->toStr() << " and we start at "
2112 << CurrentPackagesFile
<< " " << LocalHashes
.FileSize() << " " << LocalHashes
.find(NULL
)->toStr() << std::endl
;
2114 // historically, older hashes have more info than newer ones, so start
2115 // collecting with older ones first to avoid implementing complicated
2116 // information merging techniques… a failure is after all always
2117 // recoverable with a complete file and hashes aren't changed that often.
2118 std::vector
<char const *> types
;
2119 for (char const * const * type
= HashString::SupportedHashes(); *type
!= NULL
; ++type
)
2120 types
.push_back(*type
);
2122 // parse all of (provided) history
2123 vector
<DiffInfo
> available_patches
;
2124 bool firstAcceptedHashes
= true;
2125 for (auto type
= types
.crbegin(); type
!= types
.crend(); ++type
)
2127 if (LocalHashes
.find(*type
) == NULL
)
2130 std::string tagname
= *type
;
2131 tagname
.append("-History");
2132 std::string
const tmp
= Tags
.FindS(tagname
.c_str());
2133 if (tmp
.empty() == true)
2136 string hash
, filename
;
2137 unsigned long long size
;
2138 std::stringstream
ss(tmp
);
2141 while (ss
>> hash
>> size
>> filename
)
2143 if (unlikely(hash
.empty() == true || filename
.empty() == true))
2146 // see if we have a record for this file already
2147 std::vector
<DiffInfo
>::iterator cur
= available_patches
.begin();
2148 for (; cur
!= available_patches
.end(); ++cur
)
2150 if (cur
->file
!= filename
)
2152 cur
->result_hashes
.push_back(HashString(*type
, hash
));
2155 if (cur
!= available_patches
.end())
2157 if (firstAcceptedHashes
== true)
2160 next
.file
= filename
;
2161 next
.result_hashes
.push_back(HashString(*type
, hash
));
2162 next
.result_hashes
.FileSize(size
);
2163 available_patches
.push_back(next
);
2168 std::clog
<< "pkgAcqDiffIndex: " << IndexDiffFile
<< ": File " << filename
2169 << " wasn't in the list for the first parsed hash! (history)" << std::endl
;
2173 firstAcceptedHashes
= false;
2176 if (unlikely(available_patches
.empty() == true))
2179 std::clog
<< "pkgAcqDiffIndex: " << IndexDiffFile
<< ": "
2180 << "Couldn't find any patches for the patch series." << std::endl
;
2184 for (auto type
= types
.crbegin(); type
!= types
.crend(); ++type
)
2186 if (LocalHashes
.find(*type
) == NULL
)
2189 std::string tagname
= *type
;
2190 tagname
.append("-Patches");
2191 std::string
const tmp
= Tags
.FindS(tagname
.c_str());
2192 if (tmp
.empty() == true)
2195 string hash
, filename
;
2196 unsigned long long size
;
2197 std::stringstream
ss(tmp
);
2200 while (ss
>> hash
>> size
>> filename
)
2202 if (unlikely(hash
.empty() == true || filename
.empty() == true))
2205 // see if we have a record for this file already
2206 std::vector
<DiffInfo
>::iterator cur
= available_patches
.begin();
2207 for (; cur
!= available_patches
.end(); ++cur
)
2209 if (cur
->file
!= filename
)
2211 if (cur
->patch_hashes
.empty())
2212 cur
->patch_hashes
.FileSize(size
);
2213 cur
->patch_hashes
.push_back(HashString(*type
, hash
));
2216 if (cur
!= available_patches
.end())
2219 std::clog
<< "pkgAcqDiffIndex: " << IndexDiffFile
<< ": File " << filename
2220 << " wasn't in the list for the first parsed hash! (patches)" << std::endl
;
2225 for (auto type
= types
.crbegin(); type
!= types
.crend(); ++type
)
2227 std::string tagname
= *type
;
2228 tagname
.append("-Download");
2229 std::string
const tmp
= Tags
.FindS(tagname
.c_str());
2230 if (tmp
.empty() == true)
2233 string hash
, filename
;
2234 unsigned long long size
;
2235 std::stringstream
ss(tmp
);
2238 // FIXME: all of pdiff supports only .gz compressed patches
2239 while (ss
>> hash
>> size
>> filename
)
2241 if (unlikely(hash
.empty() == true || filename
.empty() == true))
2243 if (unlikely(APT::String::Endswith(filename
, ".gz") == false))
2245 filename
.erase(filename
.length() - 3);
2247 // see if we have a record for this file already
2248 std::vector
<DiffInfo
>::iterator cur
= available_patches
.begin();
2249 for (; cur
!= available_patches
.end(); ++cur
)
2251 if (cur
->file
!= filename
)
2253 if (cur
->download_hashes
.empty())
2254 cur
->download_hashes
.FileSize(size
);
2255 cur
->download_hashes
.push_back(HashString(*type
, hash
));
2258 if (cur
!= available_patches
.end())
2261 std::clog
<< "pkgAcqDiffIndex: " << IndexDiffFile
<< ": File " << filename
2262 << " wasn't in the list for the first parsed hash! (download)" << std::endl
;
2268 bool foundStart
= false;
2269 for (std::vector
<DiffInfo
>::iterator cur
= available_patches
.begin();
2270 cur
!= available_patches
.end(); ++cur
)
2272 if (LocalHashes
!= cur
->result_hashes
)
2275 available_patches
.erase(available_patches
.begin(), cur
);
2280 if (foundStart
== false || unlikely(available_patches
.empty() == true))
2283 std::clog
<< "pkgAcqDiffIndex: " << IndexDiffFile
<< ": "
2284 << "Couldn't find the start of the patch series." << std::endl
;
2288 for (auto const &patch
: available_patches
)
2289 if (patch
.result_hashes
.usable() == false ||
2290 patch
.patch_hashes
.usable() == false ||
2291 patch
.download_hashes
.usable() == false)
2294 std::clog
<< "pkgAcqDiffIndex: " << IndexDiffFile
<< ": provides no usable hashes for " << patch
.file
2295 << " so fallback to complete download" << std::endl
;
2299 // patching with too many files is rather slow compared to a fast download
2300 unsigned long const fileLimit
= _config
->FindI("Acquire::PDiffs::FileLimit", 0);
2301 if (fileLimit
!= 0 && fileLimit
< available_patches
.size())
2304 std::clog
<< "Need " << available_patches
.size() << " diffs (Limit is " << fileLimit
2305 << ") so fallback to complete download" << std::endl
;
2309 // calculate the size of all patches we have to get
2310 unsigned short const sizeLimitPercent
= _config
->FindI("Acquire::PDiffs::SizeLimit", 100);
2311 if (sizeLimitPercent
> 0)
2313 unsigned long long downloadSize
= std::accumulate(available_patches
.begin(),
2314 available_patches
.end(), 0llu, [](unsigned long long const T
, DiffInfo
const &I
) {
2315 return T
+ I
.download_hashes
.FileSize();
2317 if (downloadSize
!= 0)
2319 unsigned long long downloadSizeIdx
= 0;
2320 auto const types
= VectorizeString(Target
.Option(IndexTarget::COMPRESSIONTYPES
), ' ');
2321 for (auto const &t
: types
)
2323 std::string MetaKey
= Target
.MetaKey
;
2324 if (t
!= "uncompressed")
2326 HashStringList
const hsl
= GetExpectedHashesFor(MetaKey
);
2327 if (unlikely(hsl
.usable() == false))
2329 downloadSizeIdx
= hsl
.FileSize();
2332 unsigned long long const sizeLimit
= downloadSizeIdx
* sizeLimitPercent
;
2333 if ((sizeLimit
/100) < downloadSize
)
2336 std::clog
<< "Need " << downloadSize
<< " compressed bytes (Limit is " << (sizeLimit
/100) << ", "
2337 << "original is " << downloadSizeIdx
<< ") so fallback to complete download" << std::endl
;
2343 // we have something, queue the diffs
2344 string::size_type
const last_space
= Description
.rfind(" ");
2345 if(last_space
!= string::npos
)
2346 Description
.erase(last_space
, Description
.size()-last_space
);
2348 /* decide if we should download patches one by one or in one go:
2349 The first is good if the server merges patches, but many don't so client
2350 based merging can be attempt in which case the second is better.
2351 "bad things" will happen if patches are merged on the server,
2352 but client side merging is attempt as well */
2353 bool pdiff_merge
= _config
->FindB("Acquire::PDiffs::Merge", true);
2354 if (pdiff_merge
== true)
2356 // reprepro adds this flag if it has merged patches on the server
2357 std::string
const precedence
= Tags
.FindS("X-Patch-Precedence");
2358 pdiff_merge
= (precedence
!= "merged");
2363 std::string
const Final
= GetExistingFilename(CurrentPackagesFile
);
2364 if (unlikely(Final
.empty())) // because we wouldn't be called in such a case
2366 std::string
const PartialFile
= GetPartialFileNameFromURI(Target
.URI
);
2367 std::string
const PatchedFile
= GetKeepCompressedFileName(PartialFile
+ "-patched", Target
);
2368 if (RemoveFileForBootstrapLinking(Debug
, CurrentPackagesFile
, PartialFile
) == false ||
2369 RemoveFileForBootstrapLinking(Debug
, CurrentPackagesFile
, PatchedFile
) == false)
2371 for (auto const &ext
: APT::Configuration::getCompressorExtensions())
2373 if (RemoveFileForBootstrapLinking(Debug
, CurrentPackagesFile
, PartialFile
+ ext
) == false ||
2374 RemoveFileForBootstrapLinking(Debug
, CurrentPackagesFile
, PatchedFile
+ ext
) == false)
2377 std::string
const Ext
= Final
.substr(CurrentPackagesFile
.length());
2378 std::string
const Partial
= PartialFile
+ Ext
;
2379 if (symlink(Final
.c_str(), Partial
.c_str()) != 0)
2382 std::clog
<< "Bootstrap-linking for patching " << CurrentPackagesFile
2383 << " by linking " << Final
<< " to " << Partial
<< " failed!" << std::endl
;
2388 if (pdiff_merge
== false)
2389 new pkgAcqIndexDiffs(Owner
, TransactionManager
, Target
, available_patches
);
2392 diffs
= new std::vector
<pkgAcqIndexMergeDiffs
*>(available_patches
.size());
2393 for(size_t i
= 0; i
< available_patches
.size(); ++i
)
2394 (*diffs
)[i
] = new pkgAcqIndexMergeDiffs(Owner
, TransactionManager
,
2396 available_patches
[i
],
2406 void pkgAcqDiffIndex::Failed(string
const &Message
,pkgAcquire::MethodConfig
const * const Cnf
)/*{{{*/
2408 pkgAcqBaseIndex::Failed(Message
,Cnf
);
2410 ExpectedAdditionalItems
= 0;
2413 std::clog
<< "pkgAcqDiffIndex failed: " << Desc
.URI
<< " with " << Message
<< std::endl
2414 << "Falling back to normal index file acquire" << std::endl
;
2416 new pkgAcqIndex(Owner
, TransactionManager
, Target
);
2419 void pkgAcqDiffIndex::Done(string
const &Message
,HashStringList
const &Hashes
, /*{{{*/
2420 pkgAcquire::MethodConfig
const * const Cnf
)
2423 std::clog
<< "pkgAcqDiffIndex::Done(): " << Desc
.URI
<< std::endl
;
2425 Item::Done(Message
, Hashes
, Cnf
);
2427 string
const FinalFile
= GetFinalFilename();
2428 if(StringToBool(LookupTag(Message
,"IMS-Hit"),false))
2429 DestFile
= FinalFile
;
2431 if(ParseDiffIndex(DestFile
) == false)
2433 Failed("Message: Couldn't parse pdiff index", Cnf
);
2434 // queue for final move - this should happen even if we fail
2435 // while parsing (e.g. on sizelimit) and download the complete file.
2436 TransactionManager
->TransactionStageCopy(this, DestFile
, FinalFile
);
2440 TransactionManager
->TransactionStageCopy(this, DestFile
, FinalFile
);
2449 pkgAcqDiffIndex::~pkgAcqDiffIndex()
2455 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
2456 // ---------------------------------------------------------------------
2457 /* The package diff is added to the queue. one object is constructed
2458 * for each diff and the index
2460 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire
* const Owner
,
2461 pkgAcqMetaClearSig
* const TransactionManager
,
2462 IndexTarget
const &Target
,
2463 vector
<DiffInfo
> const &diffs
)
2464 : pkgAcqBaseIndex(Owner
, TransactionManager
, Target
), d(NULL
),
2465 available_patches(diffs
)
2467 DestFile
= GetKeepCompressedFileName(GetPartialFileNameFromURI(Target
.URI
), Target
);
2469 Debug
= _config
->FindB("Debug::pkgAcquire::Diffs",false);
2472 Description
= Target
.Description
;
2473 Desc
.ShortDesc
= Target
.ShortDesc
;
2475 if(available_patches
.empty() == true)
2477 // we are done (yeah!), check hashes against the final file
2478 DestFile
= GetKeepCompressedFileName(GetFinalFileNameFromURI(Target
.URI
), Target
);
2483 State
= StateFetchDiff
;
2488 void pkgAcqIndexDiffs::Failed(string
const &Message
,pkgAcquire::MethodConfig
const * const Cnf
)/*{{{*/
2490 pkgAcqBaseIndex::Failed(Message
,Cnf
);
2493 DestFile
= GetKeepCompressedFileName(GetPartialFileNameFromURI(Target
.URI
), Target
);
2495 std::clog
<< "pkgAcqIndexDiffs failed: " << Desc
.URI
<< " with " << Message
<< std::endl
2496 << "Falling back to normal index file acquire " << std::endl
;
2497 RenameOnError(PDiffError
);
2498 std::string
const patchname
= GetDiffsPatchFileName(DestFile
);
2499 if (RealFileExists(patchname
))
2500 Rename(patchname
, patchname
+ ".FAILED");
2501 std::string
const UnpatchedFile
= GetExistingFilename(GetPartialFileNameFromURI(Target
.URI
));
2502 if (UnpatchedFile
.empty() == false && FileExists(UnpatchedFile
))
2503 Rename(UnpatchedFile
, UnpatchedFile
+ ".FAILED");
2504 new pkgAcqIndex(Owner
, TransactionManager
, Target
);
2508 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
2509 void pkgAcqIndexDiffs::Finish(bool allDone
)
2512 std::clog
<< "pkgAcqIndexDiffs::Finish(): "
2514 << Desc
.URI
<< std::endl
;
2516 // we restore the original name, this is required, otherwise
2517 // the file will be cleaned
2520 std::string
const Final
= GetKeepCompressedFileName(GetFinalFilename(), Target
);
2521 TransactionManager
->TransactionStageCopy(this, DestFile
, Final
);
2523 // this is for the "real" finish
2528 std::clog
<< "\n\nallDone: " << DestFile
<< "\n" << std::endl
;
2535 std::clog
<< "Finishing: " << Desc
.URI
<< std::endl
;
2542 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
2544 // calc sha1 of the just patched file
2545 std::string
const PartialFile
= GetExistingFilename(GetPartialFileNameFromURI(Target
.URI
));
2546 if(unlikely(PartialFile
.empty()))
2548 Failed("Message: The file " + GetPartialFileNameFromURI(Target
.URI
) + " isn't available", NULL
);
2552 FileFd
fd(PartialFile
, FileFd::ReadOnly
, FileFd::Extension
);
2553 Hashes LocalHashesCalc
;
2554 LocalHashesCalc
.AddFD(fd
);
2555 HashStringList
const LocalHashes
= LocalHashesCalc
.GetHashStringList();
2558 std::clog
<< "QueueNextDiff: " << PartialFile
<< " (" << LocalHashes
.find(NULL
)->toStr() << ")" << std::endl
;
2560 HashStringList
const TargetFileHashes
= GetExpectedHashesFor(Target
.MetaKey
);
2561 if (unlikely(LocalHashes
.usable() == false || TargetFileHashes
.usable() == false))
2563 Failed("Local/Expected hashes are not usable for " + PartialFile
, NULL
);
2567 // final file reached before all patches are applied
2568 if(LocalHashes
== TargetFileHashes
)
2574 // remove all patches until the next matching patch is found
2575 // this requires the Index file to be ordered
2576 available_patches
.erase(available_patches
.begin(),
2577 std::find_if(available_patches
.begin(), available_patches
.end(), [&](DiffInfo
const &I
) {
2578 return I
.result_hashes
== LocalHashes
;
2581 // error checking and falling back if no patch was found
2582 if(available_patches
.empty() == true)
2584 Failed("No patches left to reach target for " + PartialFile
, NULL
);
2588 // queue the right diff
2589 Desc
.URI
= Target
.URI
+ ".diff/" + available_patches
[0].file
+ ".gz";
2590 Desc
.Description
= Description
+ " " + available_patches
[0].file
+ string(".pdiff");
2591 DestFile
= GetKeepCompressedFileName(GetPartialFileNameFromURI(Target
.URI
+ ".diff/" + available_patches
[0].file
), Target
);
2594 std::clog
<< "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc
.URI
<< std::endl
;
2601 void pkgAcqIndexDiffs::Done(string
const &Message
, HashStringList
const &Hashes
, /*{{{*/
2602 pkgAcquire::MethodConfig
const * const Cnf
)
2605 std::clog
<< "pkgAcqIndexDiffs::Done(): " << Desc
.URI
<< std::endl
;
2607 Item::Done(Message
, Hashes
, Cnf
);
2609 std::string
const UncompressedUnpatchedFile
= GetPartialFileNameFromURI(Target
.URI
);
2610 std::string
const UnpatchedFile
= GetExistingFilename(UncompressedUnpatchedFile
);
2611 std::string
const PatchFile
= GetDiffsPatchFileName(UnpatchedFile
);
2612 std::string
const PatchedFile
= GetKeepCompressedFileName(UncompressedUnpatchedFile
, Target
);
2616 // success in downloading a diff, enter ApplyDiff state
2617 case StateFetchDiff
:
2618 Rename(DestFile
, PatchFile
);
2619 DestFile
= GetKeepCompressedFileName(UncompressedUnpatchedFile
+ "-patched", Target
);
2621 std::clog
<< "Sending to rred method: " << UnpatchedFile
<< std::endl
;
2622 State
= StateApplyDiff
;
2624 Desc
.URI
= "rred:" + UnpatchedFile
;
2626 SetActiveSubprocess("rred");
2628 // success in download/apply a diff, queue next (if needed)
2629 case StateApplyDiff
:
2630 // remove the just applied patch and base file
2631 available_patches
.erase(available_patches
.begin());
2632 RemoveFile("pkgAcqIndexDiffs::Done", PatchFile
);
2633 RemoveFile("pkgAcqIndexDiffs::Done", UnpatchedFile
);
2635 std::clog
<< "Moving patched file in place: " << std::endl
2636 << DestFile
<< " -> " << PatchedFile
<< std::endl
;
2637 Rename(DestFile
, PatchedFile
);
2639 // see if there is more to download
2640 if(available_patches
.empty() == false)
2642 new pkgAcqIndexDiffs(Owner
, TransactionManager
, Target
, available_patches
);
2645 DestFile
= PatchedFile
;
2652 std::string
pkgAcqIndexDiffs::Custom600Headers() const /*{{{*/
2654 if(State
!= StateApplyDiff
)
2655 return pkgAcqBaseIndex::Custom600Headers();
2656 std::ostringstream patchhashes
;
2657 for (auto && hs
: available_patches
[0].result_hashes
)
2658 patchhashes
<< "\nStart-" << hs
.HashType() << "-Hash: " << hs
.HashValue();
2659 for (auto && hs
: available_patches
[0].patch_hashes
)
2660 patchhashes
<< "\nPatch-0-" << hs
.HashType() << "-Hash: " << hs
.HashValue();
2661 patchhashes
<< pkgAcqBaseIndex::Custom600Headers();
2662 return patchhashes
.str();
2665 pkgAcqIndexDiffs::~pkgAcqIndexDiffs() {}
2667 // AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
2668 pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire
* const Owner
,
2669 pkgAcqMetaClearSig
* const TransactionManager
,
2670 IndexTarget
const &Target
,
2671 DiffInfo
const &patch
,
2672 std::vector
<pkgAcqIndexMergeDiffs
*> const * const allPatches
)
2673 : pkgAcqBaseIndex(Owner
, TransactionManager
, Target
), d(NULL
),
2674 patch(patch
), allPatches(allPatches
), State(StateFetchDiff
)
2676 Debug
= _config
->FindB("Debug::pkgAcquire::Diffs",false);
2679 Description
= Target
.Description
;
2680 Desc
.ShortDesc
= Target
.ShortDesc
;
2681 Desc
.URI
= Target
.URI
+ ".diff/" + patch
.file
+ ".gz";
2682 Desc
.Description
= Description
+ " " + patch
.file
+ ".pdiff";
2683 DestFile
= GetPartialFileNameFromURI(Desc
.URI
);
2686 std::clog
<< "pkgAcqIndexMergeDiffs: " << Desc
.URI
<< std::endl
;
2691 void pkgAcqIndexMergeDiffs::Failed(string
const &Message
,pkgAcquire::MethodConfig
const * const Cnf
)/*{{{*/
2694 std::clog
<< "pkgAcqIndexMergeDiffs failed: " << Desc
.URI
<< " with " << Message
<< std::endl
;
2696 pkgAcqBaseIndex::Failed(Message
,Cnf
);
2699 // check if we are the first to fail, otherwise we are done here
2700 State
= StateDoneDiff
;
2701 for (std::vector
<pkgAcqIndexMergeDiffs
*>::const_iterator I
= allPatches
->begin();
2702 I
!= allPatches
->end(); ++I
)
2703 if ((*I
)->State
== StateErrorDiff
)
2705 State
= StateErrorDiff
;
2709 // first failure means we should fallback
2710 State
= StateErrorDiff
;
2712 std::clog
<< "Falling back to normal index file acquire" << std::endl
;
2713 RenameOnError(PDiffError
);
2714 if (RealFileExists(DestFile
))
2715 Rename(DestFile
, DestFile
+ ".FAILED");
2716 std::string
const UnpatchedFile
= GetExistingFilename(GetPartialFileNameFromURI(Target
.URI
));
2717 if (UnpatchedFile
.empty() == false && FileExists(UnpatchedFile
))
2718 Rename(UnpatchedFile
, UnpatchedFile
+ ".FAILED");
2720 new pkgAcqIndex(Owner
, TransactionManager
, Target
);
2723 void pkgAcqIndexMergeDiffs::Done(string
const &Message
, HashStringList
const &Hashes
, /*{{{*/
2724 pkgAcquire::MethodConfig
const * const Cnf
)
2727 std::clog
<< "pkgAcqIndexMergeDiffs::Done(): " << Desc
.URI
<< std::endl
;
2729 Item::Done(Message
, Hashes
, Cnf
);
2731 if (std::any_of(allPatches
->begin(), allPatches
->end(),
2732 [](pkgAcqIndexMergeDiffs
const * const P
) { return P
->State
== StateErrorDiff
; }))
2735 std::clog
<< "Another patch failed already, no point in processing this one." << std::endl
;
2736 State
= StateErrorDiff
;
2740 std::string
const UncompressedUnpatchedFile
= GetPartialFileNameFromURI(Target
.URI
);
2741 std::string
const UnpatchedFile
= GetExistingFilename(UncompressedUnpatchedFile
);
2742 if (UnpatchedFile
.empty())
2744 _error
->Fatal("Unpatched file %s doesn't exist (anymore)!", UncompressedUnpatchedFile
.c_str());
2745 State
= StateErrorDiff
;
2748 std::string
const PatchFile
= GetMergeDiffsPatchFileName(UnpatchedFile
, patch
.file
);
2749 std::string
const PatchedFile
= GetKeepCompressedFileName(UncompressedUnpatchedFile
, Target
);
2753 case StateFetchDiff
:
2754 Rename(DestFile
, PatchFile
);
2756 // check if this is the last completed diff
2757 State
= StateDoneDiff
;
2758 for (std::vector
<pkgAcqIndexMergeDiffs
*>::const_iterator I
= allPatches
->begin();
2759 I
!= allPatches
->end(); ++I
)
2760 if ((*I
)->State
!= StateDoneDiff
)
2763 std::clog
<< "Not the last done diff in the batch: " << Desc
.URI
<< std::endl
;
2766 // this is the last completed diff, so we are ready to apply now
2767 DestFile
= GetKeepCompressedFileName(UncompressedUnpatchedFile
+ "-patched", Target
);
2769 std::clog
<< "Sending to rred method: " << UnpatchedFile
<< std::endl
;
2770 State
= StateApplyDiff
;
2772 Desc
.URI
= "rred:" + UnpatchedFile
;
2774 SetActiveSubprocess("rred");
2776 case StateApplyDiff
:
2777 // success in download & apply all diffs, finialize and clean up
2779 std::clog
<< "Queue patched file in place: " << std::endl
2780 << DestFile
<< " -> " << PatchedFile
<< std::endl
;
2782 // queue for copy by the transaction manager
2783 TransactionManager
->TransactionStageCopy(this, DestFile
, GetKeepCompressedFileName(GetFinalFilename(), Target
));
2785 // ensure the ed's are gone regardless of list-cleanup
2786 for (std::vector
<pkgAcqIndexMergeDiffs
*>::const_iterator I
= allPatches
->begin();
2787 I
!= allPatches
->end(); ++I
)
2788 RemoveFile("pkgAcqIndexMergeDiffs::Done", GetMergeDiffsPatchFileName(UnpatchedFile
, (*I
)->patch
.file
));
2789 RemoveFile("pkgAcqIndexMergeDiffs::Done", UnpatchedFile
);
2794 std::clog
<< "allDone: " << DestFile
<< "\n" << std::endl
;
2796 case StateDoneDiff
: _error
->Fatal("Done called for %s which is in an invalid Done state", PatchFile
.c_str()); break;
2797 case StateErrorDiff
: _error
->Fatal("Done called for %s which is in an invalid Error state", PatchFile
.c_str()); break;
2801 std::string
pkgAcqIndexMergeDiffs::Custom600Headers() const /*{{{*/
2803 if(State
!= StateApplyDiff
)
2804 return pkgAcqBaseIndex::Custom600Headers();
2805 std::ostringstream patchhashes
;
2806 unsigned int seen_patches
= 0;
2807 for (auto && hs
: (*allPatches
)[0]->patch
.result_hashes
)
2808 patchhashes
<< "\nStart-" << hs
.HashType() << "-Hash: " << hs
.HashValue();
2809 for (std::vector
<pkgAcqIndexMergeDiffs
*>::const_iterator I
= allPatches
->begin();
2810 I
!= allPatches
->end(); ++I
)
2812 HashStringList
const ExpectedHashes
= (*I
)->patch
.patch_hashes
;
2813 for (HashStringList::const_iterator hs
= ExpectedHashes
.begin(); hs
!= ExpectedHashes
.end(); ++hs
)
2814 patchhashes
<< "\nPatch-" << std::to_string(seen_patches
) << "-" << hs
->HashType() << "-Hash: " << hs
->HashValue();
2817 patchhashes
<< pkgAcqBaseIndex::Custom600Headers();
2818 return patchhashes
.str();
2821 pkgAcqIndexMergeDiffs::~pkgAcqIndexMergeDiffs() {}
2823 // AcqIndex::AcqIndex - Constructor /*{{{*/
2824 pkgAcqIndex::pkgAcqIndex(pkgAcquire
* const Owner
,
2825 pkgAcqMetaClearSig
* const TransactionManager
,
2826 IndexTarget
const &Target
)
2827 : pkgAcqBaseIndex(Owner
, TransactionManager
, Target
), d(NULL
), Stage(STAGE_DOWNLOAD
),
2828 CompressionExtensions(Target
.Option(IndexTarget::COMPRESSIONTYPES
))
2830 Init(Target
.URI
, Target
.Description
, Target
.ShortDesc
);
2832 if(_config
->FindB("Debug::Acquire::Transaction", false) == true)
2833 std::clog
<< "New pkgIndex with TransactionManager "
2834 << TransactionManager
<< std::endl
;
2837 // AcqIndex::Init - defered Constructor /*{{{*/
2838 static void NextCompressionExtension(std::string
&CurrentCompressionExtension
, std::string
&CompressionExtensions
, bool const preview
)
2840 size_t const nextExt
= CompressionExtensions
.find(' ');
2841 if (nextExt
== std::string::npos
)
2843 CurrentCompressionExtension
= CompressionExtensions
;
2844 if (preview
== false)
2845 CompressionExtensions
.clear();
2849 CurrentCompressionExtension
= CompressionExtensions
.substr(0, nextExt
);
2850 if (preview
== false)
2851 CompressionExtensions
= CompressionExtensions
.substr(nextExt
+1);
2854 void pkgAcqIndex::Init(string
const &URI
, string
const &URIDesc
,
2855 string
const &ShortDesc
)
2857 Stage
= STAGE_DOWNLOAD
;
2859 DestFile
= GetPartialFileNameFromURI(URI
);
2860 NextCompressionExtension(CurrentCompressionExtension
, CompressionExtensions
, false);
2862 // store file size of the download to ensure the fetcher gives
2863 // accurate progress reporting
2864 FileSize
= GetExpectedHashes().FileSize();
2866 if (CurrentCompressionExtension
== "uncompressed")
2870 else if (CurrentCompressionExtension
== "by-hash")
2872 NextCompressionExtension(CurrentCompressionExtension
, CompressionExtensions
, true);
2873 if(unlikely(CurrentCompressionExtension
.empty()))
2875 if (CurrentCompressionExtension
!= "uncompressed")
2877 Desc
.URI
= URI
+ '.' + CurrentCompressionExtension
;
2878 DestFile
= DestFile
+ '.' + CurrentCompressionExtension
;
2881 HashStringList
const Hashes
= GetExpectedHashes();
2882 HashString
const * const TargetHash
= Hashes
.find(NULL
);
2883 if (unlikely(TargetHash
== nullptr))
2885 std::string
const ByHash
= "/by-hash/" + TargetHash
->HashType() + "/" + TargetHash
->HashValue();
2886 size_t const trailing_slash
= Desc
.URI
.find_last_of("/");
2887 if (unlikely(trailing_slash
== std::string::npos
))
2889 Desc
.URI
= Desc
.URI
.replace(
2891 Desc
.URI
.substr(trailing_slash
+1).size()+1,
2894 else if (unlikely(CurrentCompressionExtension
.empty()))
2898 Desc
.URI
= URI
+ '.' + CurrentCompressionExtension
;
2899 DestFile
= DestFile
+ '.' + CurrentCompressionExtension
;
2903 Desc
.Description
= URIDesc
;
2905 Desc
.ShortDesc
= ShortDesc
;
2910 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2911 // ---------------------------------------------------------------------
2912 /* The only header we use is the last-modified header. */
2913 string
pkgAcqIndex::Custom600Headers() const
2916 string msg
= "\nIndex-File: true";
2918 if (TransactionManager
->LastMetaIndexParser
== NULL
)
2920 std::string
const Final
= GetFinalFilename();
2923 if (stat(Final
.c_str(),&Buf
) == 0)
2924 msg
+= "\nLast-Modified: " + TimeRFC1123(Buf
.st_mtime
, false);
2927 if(Target
.IsOptional
)
2928 msg
+= "\nFail-Ignore: true";
2933 // AcqIndex::Failed - getting the indexfile failed /*{{{*/
2934 void pkgAcqIndex::Failed(string
const &Message
,pkgAcquire::MethodConfig
const * const Cnf
)
2936 pkgAcqBaseIndex::Failed(Message
,Cnf
);
2938 // authorisation matches will not be fixed by other compression types
2939 if (Status
!= StatAuthError
)
2941 if (CompressionExtensions
.empty() == false)
2943 Init(Target
.URI
, Desc
.Description
, Desc
.ShortDesc
);
2949 if(Target
.IsOptional
&& GetExpectedHashes().empty() && Stage
== STAGE_DOWNLOAD
)
2952 TransactionManager
->AbortTransaction();
2955 // AcqIndex::Done - Finished a fetch /*{{{*/
2956 // ---------------------------------------------------------------------
2957 /* This goes through a number of states.. On the initial fetch the
2958 method could possibly return an alternate filename which points
2959 to the uncompressed version of the file. If this is so the file
2960 is copied into the partial directory. In all other cases the file
2961 is decompressed with a compressed uri. */
2962 void pkgAcqIndex::Done(string
const &Message
,
2963 HashStringList
const &Hashes
,
2964 pkgAcquire::MethodConfig
const * const Cfg
)
2966 Item::Done(Message
,Hashes
,Cfg
);
2970 case STAGE_DOWNLOAD
:
2971 StageDownloadDone(Message
);
2973 case STAGE_DECOMPRESS_AND_VERIFY
:
2974 StageDecompressDone();
2979 // AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
2980 void pkgAcqIndex::StageDownloadDone(string
const &Message
)
2985 std::string
const AltFilename
= LookupTag(Message
,"Alt-Filename");
2986 std::string Filename
= LookupTag(Message
,"Filename");
2988 // we need to verify the file against the current Release file again
2989 // on if-modfied-since hit to avoid a stale attack against us
2990 if(StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true)
2992 // copy FinalFile into partial/ so that we check the hash again
2993 string
const FinalFile
= GetExistingFilename(GetFinalFileNameFromURI(Target
.URI
));
2994 if (symlink(FinalFile
.c_str(), DestFile
.c_str()) != 0)
2995 _error
->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking final file %s back to %s failed", FinalFile
.c_str(), DestFile
.c_str());
2998 EraseFileName
= DestFile
;
2999 Filename
= DestFile
;
3001 Stage
= STAGE_DECOMPRESS_AND_VERIFY
;
3002 Desc
.URI
= "store:" + Filename
;
3004 SetActiveSubprocess(::URI(Desc
.URI
).Access
);
3007 // methods like file:// give us an alternative (uncompressed) file
3008 else if (Target
.KeepCompressed
== false && AltFilename
.empty() == false)
3010 Filename
= AltFilename
;
3011 EraseFileName
.clear();
3013 // Methods like e.g. "file:" will give us a (compressed) FileName that is
3014 // not the "DestFile" we set, in this case we uncompress from the local file
3015 else if (Filename
!= DestFile
&& RealFileExists(DestFile
) == false)
3017 // symlinking ensures that the filename can be used for compression detection
3018 // that is e.g. needed for by-hash which has no extension over file
3019 if (symlink(Filename
.c_str(),DestFile
.c_str()) != 0)
3020 _error
->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking file %s to %s failed", Filename
.c_str(), DestFile
.c_str());
3023 EraseFileName
= DestFile
;
3024 Filename
= DestFile
;
3028 Stage
= STAGE_DECOMPRESS_AND_VERIFY
;
3029 DestFile
= GetKeepCompressedFileName(GetPartialFileNameFromURI(Target
.URI
), Target
);
3030 if (Filename
!= DestFile
&& flExtension(Filename
) == flExtension(DestFile
))
3031 Desc
.URI
= "copy:" + Filename
;
3033 Desc
.URI
= "store:" + Filename
;
3034 if (DestFile
== Filename
)
3036 if (CurrentCompressionExtension
== "uncompressed")
3037 return StageDecompressDone();
3038 DestFile
= "/dev/null";
3041 if (EraseFileName
.empty() && Filename
!= AltFilename
)
3042 EraseFileName
= Filename
;
3044 // queue uri for the next stage
3046 SetActiveSubprocess(::URI(Desc
.URI
).Access
);
3049 // AcqIndex::StageDecompressDone - Final verification /*{{{*/
3050 void pkgAcqIndex::StageDecompressDone()
3052 if (DestFile
== "/dev/null")
3053 DestFile
= GetKeepCompressedFileName(GetPartialFileNameFromURI(Target
.URI
), Target
);
3055 // Done, queue for rename on transaction finished
3056 TransactionManager
->TransactionStageCopy(this, DestFile
, GetFinalFilename());
3059 pkgAcqIndex::~pkgAcqIndex() {}
3062 // AcqArchive::AcqArchive - Constructor /*{{{*/
3063 // ---------------------------------------------------------------------
3064 /* This just sets up the initial fetch environment and queues the first
3066 pkgAcqArchive::pkgAcqArchive(pkgAcquire
* const Owner
,pkgSourceList
* const Sources
,
3067 pkgRecords
* const Recs
,pkgCache::VerIterator
const &Version
,
3068 string
&StoreFilename
) :
3069 Item(Owner
), d(NULL
), LocalSource(false), Version(Version
), Sources(Sources
), Recs(Recs
),
3070 StoreFilename(StoreFilename
), Vf(Version
.FileList()),
3073 Retries
= _config
->FindI("Acquire::Retries",0);
3075 if (Version
.Arch() == 0)
3077 _error
->Error(_("I wasn't able to locate a file for the %s package. "
3078 "This might mean you need to manually fix this package. "
3079 "(due to missing arch)"),
3080 Version
.ParentPkg().FullName().c_str());
3084 /* We need to find a filename to determine the extension. We make the
3085 assumption here that all the available sources for this version share
3086 the same extension.. */
3087 // Skip not source sources, they do not have file fields.
3088 for (; Vf
.end() == false; ++Vf
)
3090 if (Vf
.File().Flagged(pkgCache::Flag::NotSource
))
3095 // Does not really matter here.. we are going to fail out below
3096 if (Vf
.end() != true)
3098 // If this fails to get a file name we will bomb out below.
3099 pkgRecords::Parser
&Parse
= Recs
->Lookup(Vf
);
3100 if (_error
->PendingError() == true)
3103 // Generate the final file name as: package_version_arch.foo
3104 StoreFilename
= QuoteString(Version
.ParentPkg().Name(),"_:") + '_' +
3105 QuoteString(Version
.VerStr(),"_:") + '_' +
3106 QuoteString(Version
.Arch(),"_:.") +
3107 "." + flExtension(Parse
.FileName());
3110 // check if we have one trusted source for the package. if so, switch
3111 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
3112 bool const allowUnauth
= _config
->FindB("APT::Get::AllowUnauthenticated", false);
3113 bool const debugAuth
= _config
->FindB("Debug::pkgAcquire::Auth", false);
3114 bool seenUntrusted
= false;
3115 for (pkgCache::VerFileIterator i
= Version
.FileList(); i
.end() == false; ++i
)
3117 pkgIndexFile
*Index
;
3118 if (Sources
->FindIndex(i
.File(),Index
) == false)
3121 if (debugAuth
== true)
3122 std::cerr
<< "Checking index: " << Index
->Describe()
3123 << "(Trusted=" << Index
->IsTrusted() << ")" << std::endl
;
3125 if (Index
->IsTrusted() == true)
3128 if (allowUnauth
== false)
3132 seenUntrusted
= true;
3135 // "allow-unauthenticated" restores apts old fetching behaviour
3136 // that means that e.g. unauthenticated file:// uris are higher
3137 // priority than authenticated http:// uris
3138 if (allowUnauth
== true && seenUntrusted
== true)
3142 if (QueueNext() == false && _error
->PendingError() == false)
3143 _error
->Error(_("Can't find a source to download version '%s' of '%s'"),
3144 Version
.VerStr(), Version
.ParentPkg().FullName(false).c_str());
3147 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
3148 // ---------------------------------------------------------------------
3149 /* This queues the next available file version for download. It checks if
3150 the archive is already available in the cache and stashs the MD5 for
3152 bool pkgAcqArchive::QueueNext()
3154 for (; Vf
.end() == false; ++Vf
)
3156 pkgCache::PkgFileIterator
const PkgF
= Vf
.File();
3157 // Ignore not source sources
3158 if (PkgF
.Flagged(pkgCache::Flag::NotSource
))
3161 // Try to cross match against the source list
3162 pkgIndexFile
*Index
;
3163 if (Sources
->FindIndex(PkgF
, Index
) == false)
3165 LocalSource
= PkgF
.Flagged(pkgCache::Flag::LocalSource
);
3167 // only try to get a trusted package from another source if that source
3169 if(Trusted
&& !Index
->IsTrusted())
3172 // Grab the text package record
3173 pkgRecords::Parser
&Parse
= Recs
->Lookup(Vf
);
3174 if (_error
->PendingError() == true)
3177 string PkgFile
= Parse
.FileName();
3178 ExpectedHashes
= Parse
.Hashes();
3180 if (PkgFile
.empty() == true)
3181 return _error
->Error(_("The package index files are corrupted. No Filename: "
3182 "field for package %s."),
3183 Version
.ParentPkg().Name());
3185 Desc
.URI
= Index
->ArchiveURI(PkgFile
);
3186 Desc
.Description
= Index
->ArchiveInfo(Version
);
3188 Desc
.ShortDesc
= Version
.ParentPkg().FullName(true);
3190 // See if we already have the file. (Legacy filenames)
3191 FileSize
= Version
->Size
;
3192 string FinalFile
= _config
->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile
);
3194 if (stat(FinalFile
.c_str(),&Buf
) == 0)
3196 // Make sure the size matches
3197 if ((unsigned long long)Buf
.st_size
== Version
->Size
)
3202 StoreFilename
= DestFile
= FinalFile
;
3206 /* Hmm, we have a file and its size does not match, this means it is
3207 an old style mismatched arch */
3208 RemoveFile("pkgAcqArchive::QueueNext", FinalFile
);
3211 // Check it again using the new style output filenames
3212 FinalFile
= _config
->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename
);
3213 if (stat(FinalFile
.c_str(),&Buf
) == 0)
3215 // Make sure the size matches
3216 if ((unsigned long long)Buf
.st_size
== Version
->Size
)
3221 StoreFilename
= DestFile
= FinalFile
;
3225 /* Hmm, we have a file and its size does not match, this shouldn't
3227 RemoveFile("pkgAcqArchive::QueueNext", FinalFile
);
3230 DestFile
= _config
->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename
);
3232 // Check the destination file
3233 if (stat(DestFile
.c_str(),&Buf
) == 0)
3235 // Hmm, the partial file is too big, erase it
3236 if ((unsigned long long)Buf
.st_size
> Version
->Size
)
3237 RemoveFile("pkgAcqArchive::QueueNext", DestFile
);
3239 PartialSize
= Buf
.st_size
;
3242 // Disables download of archives - useful if no real installation follows,
3243 // e.g. if we are just interested in proposed installation order
3244 if (_config
->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
3249 StoreFilename
= DestFile
= FinalFile
;
3263 // AcqArchive::Done - Finished fetching /*{{{*/
3264 // ---------------------------------------------------------------------
3266 void pkgAcqArchive::Done(string
const &Message
, HashStringList
const &Hashes
,
3267 pkgAcquire::MethodConfig
const * const Cfg
)
3269 Item::Done(Message
, Hashes
, Cfg
);
3271 // Grab the output filename
3272 std::string
const FileName
= LookupTag(Message
,"Filename");
3273 if (DestFile
!= FileName
&& RealFileExists(DestFile
) == false)
3275 StoreFilename
= DestFile
= FileName
;
3281 // Done, move it into position
3282 string
const FinalFile
= GetFinalFilename();
3283 Rename(DestFile
,FinalFile
);
3284 StoreFilename
= DestFile
= FinalFile
;
3288 // AcqArchive::Failed - Failure handler /*{{{*/
3289 // ---------------------------------------------------------------------
3290 /* Here we try other sources */
3291 void pkgAcqArchive::Failed(string
const &Message
,pkgAcquire::MethodConfig
const * const Cnf
)
3293 Item::Failed(Message
,Cnf
);
3295 /* We don't really want to retry on failed media swaps, this prevents
3296 that. An interesting observation is that permanent failures are not
3298 if (Cnf
->Removable
== true &&
3299 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
3301 // Vf = Version.FileList();
3302 while (Vf
.end() == false) ++Vf
;
3303 StoreFilename
= string();
3308 if (QueueNext() == false)
3310 // This is the retry counter
3312 Cnf
->LocalOnly
== false &&
3313 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
3316 Vf
= Version
.FileList();
3317 if (QueueNext() == true)
3321 StoreFilename
= string();
3326 APT_PURE
bool pkgAcqArchive::IsTrusted() const /*{{{*/
3331 void pkgAcqArchive::Finished() /*{{{*/
3333 if (Status
== pkgAcquire::Item::StatDone
&&
3336 StoreFilename
= string();
3339 std::string
pkgAcqArchive::DescURI() const /*{{{*/
3344 std::string
pkgAcqArchive::ShortDesc() const /*{{{*/
3346 return Desc
.ShortDesc
;
3349 pkgAcqArchive::~pkgAcqArchive() {}
3351 // AcqChangelog::pkgAcqChangelog - Constructors /*{{{*/
3352 class pkgAcqChangelog::Private
3355 std::string FinalFile
;
3357 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire
* const Owner
, pkgCache::VerIterator
const &Ver
,
3358 std::string
const &DestDir
, std::string
const &DestFilename
) :
3359 pkgAcquire::Item(Owner
), d(new pkgAcqChangelog::Private()), SrcName(Ver
.SourcePkgName()), SrcVersion(Ver
.SourceVerStr())
3361 Desc
.URI
= URI(Ver
);
3362 Init(DestDir
, DestFilename
);
3364 // some parameters are char* here as they come likely from char* interfaces – which can also return NULL
3365 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire
* const Owner
, pkgCache::RlsFileIterator
const &RlsFile
,
3366 char const * const Component
, char const * const SrcName
, char const * const SrcVersion
,
3367 const string
&DestDir
, const string
&DestFilename
) :
3368 pkgAcquire::Item(Owner
), d(new pkgAcqChangelog::Private()), SrcName(SrcName
), SrcVersion(SrcVersion
)
3370 Desc
.URI
= URI(RlsFile
, Component
, SrcName
, SrcVersion
);
3371 Init(DestDir
, DestFilename
);
3373 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire
* const Owner
,
3374 std::string
const &URI
, char const * const SrcName
, char const * const SrcVersion
,
3375 const string
&DestDir
, const string
&DestFilename
) :
3376 pkgAcquire::Item(Owner
), d(new pkgAcqChangelog::Private()), SrcName(SrcName
), SrcVersion(SrcVersion
)
3379 Init(DestDir
, DestFilename
);
3381 void pkgAcqChangelog::Init(std::string
const &DestDir
, std::string
const &DestFilename
)
3383 if (Desc
.URI
.empty())
3386 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3387 strprintf(ErrorText
, _("Changelog unavailable for %s=%s"), SrcName
.c_str(), SrcVersion
.c_str());
3388 // Let the error message print something sensible rather than "Failed to fetch /"
3389 if (DestFilename
.empty())
3390 DestFile
= SrcName
+ ".changelog";
3392 DestFile
= DestFilename
;
3393 Desc
.URI
= "changelog:/" + DestFile
;
3397 std::string DestFileName
;
3398 if (DestFilename
.empty())
3399 DestFileName
= flCombine(DestFile
, SrcName
+ ".changelog");
3401 DestFileName
= flCombine(DestFile
, DestFilename
);
3403 std::string
const SandboxUser
= _config
->Find("APT::Sandbox::User");
3404 std::string
const systemTemp
= GetTempDir(SandboxUser
);
3406 snprintf(tmpname
, sizeof(tmpname
), "%s/apt-changelog-XXXXXX", systemTemp
.c_str());
3407 if (NULL
== mkdtemp(tmpname
))
3409 _error
->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName
.c_str(), SrcVersion
.c_str());
3413 TemporaryDirectory
= tmpname
;
3415 ChangeOwnerAndPermissionOfFile("Item::QueueURI", TemporaryDirectory
.c_str(),
3416 SandboxUser
.c_str(), "root", 0700);
3418 DestFile
= flCombine(TemporaryDirectory
, DestFileName
);
3419 if (DestDir
.empty() == false)
3421 d
->FinalFile
= flCombine(DestDir
, DestFileName
);
3422 if (RealFileExists(d
->FinalFile
))
3424 FileFd file1
, file2
;
3425 if (file1
.Open(DestFile
, FileFd::WriteOnly
| FileFd::Create
| FileFd::Exclusive
) &&
3426 file2
.Open(d
->FinalFile
, FileFd::ReadOnly
) && CopyFile(file2
, file1
))
3428 struct timeval times
[2];
3429 times
[0].tv_sec
= times
[1].tv_sec
= file2
.ModificationTime();
3430 times
[0].tv_usec
= times
[1].tv_usec
= 0;
3431 utimes(DestFile
.c_str(), times
);
3436 Desc
.ShortDesc
= "Changelog";
3437 strprintf(Desc
.Description
, "%s %s %s Changelog", URI::SiteOnly(Desc
.URI
).c_str(), SrcName
.c_str(), SrcVersion
.c_str());
3442 std::string
pkgAcqChangelog::URI(pkgCache::VerIterator
const &Ver
) /*{{{*/
3444 std::string
const confOnline
= "Acquire::Changelogs::AlwaysOnline";
3445 bool AlwaysOnline
= _config
->FindB(confOnline
, false);
3446 if (AlwaysOnline
== false)
3447 for (pkgCache::VerFileIterator VF
= Ver
.FileList(); VF
.end() == false; ++VF
)
3449 pkgCache::PkgFileIterator
const PF
= VF
.File();
3450 if (PF
.Flagged(pkgCache::Flag::NotSource
) || PF
->Release
== 0)
3452 pkgCache::RlsFileIterator
const RF
= PF
.ReleaseFile();
3453 if (RF
->Origin
!= 0 && _config
->FindB(confOnline
+ "::Origin::" + RF
.Origin(), false))
3455 AlwaysOnline
= true;
3459 if (AlwaysOnline
== false)
3461 pkgCache::PkgIterator
const Pkg
= Ver
.ParentPkg();
3462 if (Pkg
->CurrentVer
!= 0 && Pkg
.CurrentVer() == Ver
)
3464 std::string
const basename
= std::string("/usr/share/doc/") + Pkg
.Name() + "/changelog";
3465 std::string
const debianname
= basename
+ ".Debian";
3466 if (FileExists(debianname
))
3467 return "copy://" + debianname
;
3468 else if (FileExists(debianname
+ ".gz"))
3469 return "gzip://" + debianname
+ ".gz";
3470 else if (FileExists(basename
))
3471 return "copy://" + basename
;
3472 else if (FileExists(basename
+ ".gz"))
3473 return "gzip://" + basename
+ ".gz";
3477 char const * const SrcName
= Ver
.SourcePkgName();
3478 char const * const SrcVersion
= Ver
.SourceVerStr();
3479 // find the first source for this version which promises a changelog
3480 for (pkgCache::VerFileIterator VF
= Ver
.FileList(); VF
.end() == false; ++VF
)
3482 pkgCache::PkgFileIterator
const PF
= VF
.File();
3483 if (PF
.Flagged(pkgCache::Flag::NotSource
) || PF
->Release
== 0)
3485 pkgCache::RlsFileIterator
const RF
= PF
.ReleaseFile();
3486 std::string
const uri
= URI(RF
, PF
.Component(), SrcName
, SrcVersion
);
3493 std::string
pkgAcqChangelog::URITemplate(pkgCache::RlsFileIterator
const &Rls
)
3495 if (Rls
.end() == true || (Rls
->Label
== 0 && Rls
->Origin
== 0))
3497 std::string
const serverConfig
= "Acquire::Changelogs::URI";
3499 #define APT_EMPTY_SERVER \
3500 if (server.empty() == false) \
3502 if (server != "no") \
3506 #define APT_CHECK_SERVER(X, Y) \
3509 std::string const specialServerConfig = serverConfig + "::" + Y + #X + "::" + Rls.X(); \
3510 server = _config->Find(specialServerConfig); \
3513 // this way e.g. Debian-Security can fallback to Debian
3514 APT_CHECK_SERVER(Label
, "Override::")
3515 APT_CHECK_SERVER(Origin
, "Override::")
3517 if (RealFileExists(Rls
.FileName()))
3519 _error
->PushToStack();
3521 /* This can be costly. A caller wanting to get millions of URIs might
3522 want to do this on its own once and use Override settings.
3523 We don't do this here as Origin/Label are not as unique as they
3524 should be so this could produce request order-dependent anomalies */
3525 if (OpenMaybeClearSignedFile(Rls
.FileName(), rf
) == true)
3527 pkgTagFile
TagFile(&rf
, rf
.Size());
3528 pkgTagSection Section
;
3529 if (TagFile
.Step(Section
) == true)
3530 server
= Section
.FindS("Changelogs");
3532 _error
->RevertToStack();
3536 APT_CHECK_SERVER(Label
, "")
3537 APT_CHECK_SERVER(Origin
, "")
3538 #undef APT_CHECK_SERVER
3539 #undef APT_EMPTY_SERVER
3542 std::string
pkgAcqChangelog::URI(pkgCache::RlsFileIterator
const &Rls
,
3543 char const * const Component
, char const * const SrcName
,
3544 char const * const SrcVersion
)
3546 return URI(URITemplate(Rls
), Component
, SrcName
, SrcVersion
);
3548 std::string
pkgAcqChangelog::URI(std::string
const &Template
,
3549 char const * const Component
, char const * const SrcName
,
3550 char const * const SrcVersion
)
3552 if (Template
.find("@CHANGEPATH@") == std::string::npos
)
3555 // the path is: COMPONENT/SRC/SRCNAME/SRCNAME_SRCVER, e.g. main/a/apt/1.1 or contrib/liba/libapt/2.0
3556 std::string Src
= SrcName
;
3557 std::string path
= APT::String::Startswith(SrcName
, "lib") ? Src
.substr(0, 4) : Src
.substr(0,1);
3558 path
.append("/").append(Src
).append("/");
3559 path
.append(Src
).append("_").append(StripEpoch(SrcVersion
));
3560 // we omit component for releases without one (= flat-style repositories)
3561 if (Component
!= NULL
&& strlen(Component
) != 0)
3562 path
= std::string(Component
) + "/" + path
;
3564 return SubstVar(Template
, "@CHANGEPATH@", path
);
3567 // AcqChangelog::Failed - Failure handler /*{{{*/
3568 void pkgAcqChangelog::Failed(string
const &Message
, pkgAcquire::MethodConfig
const * const Cnf
)
3570 Item::Failed(Message
,Cnf
);
3572 std::string errText
;
3573 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3574 strprintf(errText
, _("Changelog unavailable for %s=%s"), SrcName
.c_str(), SrcVersion
.c_str());
3576 // Error is probably something techy like 404 Not Found
3577 if (ErrorText
.empty())
3578 ErrorText
= errText
;
3580 ErrorText
= errText
+ " (" + ErrorText
+ ")";
3583 // AcqChangelog::Done - Item downloaded OK /*{{{*/
3584 void pkgAcqChangelog::Done(string
const &Message
,HashStringList
const &CalcHashes
,
3585 pkgAcquire::MethodConfig
const * const Cnf
)
3587 Item::Done(Message
,CalcHashes
,Cnf
);
3588 if (d
->FinalFile
.empty() == false)
3590 if (RemoveFile("pkgAcqChangelog::Done", d
->FinalFile
) == false ||
3591 Rename(DestFile
, d
->FinalFile
) == false)
3598 pkgAcqChangelog::~pkgAcqChangelog() /*{{{*/
3600 if (TemporaryDirectory
.empty() == false)
3602 RemoveFile("~pkgAcqChangelog", DestFile
);
3603 rmdir(TemporaryDirectory
.c_str());
3609 // AcqFile::pkgAcqFile - Constructor /*{{{*/
3610 pkgAcqFile::pkgAcqFile(pkgAcquire
* const Owner
,string
const &URI
, HashStringList
const &Hashes
,
3611 unsigned long long const Size
,string
const &Dsc
,string
const &ShortDesc
,
3612 const string
&DestDir
, const string
&DestFilename
,
3613 bool const IsIndexFile
) :
3614 Item(Owner
), d(NULL
), IsIndexFile(IsIndexFile
), ExpectedHashes(Hashes
)
3616 Retries
= _config
->FindI("Acquire::Retries",0);
3618 if(!DestFilename
.empty())
3619 DestFile
= DestFilename
;
3620 else if(!DestDir
.empty())
3621 DestFile
= DestDir
+ "/" + flNotDir(URI
);
3623 DestFile
= flNotDir(URI
);
3627 Desc
.Description
= Dsc
;
3630 // Set the short description to the archive component
3631 Desc
.ShortDesc
= ShortDesc
;
3633 // Get the transfer sizes
3636 if (stat(DestFile
.c_str(),&Buf
) == 0)
3638 // Hmm, the partial file is too big, erase it
3639 if ((Size
> 0) && (unsigned long long)Buf
.st_size
> Size
)
3640 RemoveFile("pkgAcqFile", DestFile
);
3642 PartialSize
= Buf
.st_size
;
3648 // AcqFile::Done - Item downloaded OK /*{{{*/
3649 void pkgAcqFile::Done(string
const &Message
,HashStringList
const &CalcHashes
,
3650 pkgAcquire::MethodConfig
const * const Cnf
)
3652 Item::Done(Message
,CalcHashes
,Cnf
);
3654 std::string
const FileName
= LookupTag(Message
,"Filename");
3657 // The files timestamp matches
3658 if (StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true)
3661 // We have to copy it into place
3662 if (RealFileExists(DestFile
.c_str()) == false)
3665 if (_config
->FindB("Acquire::Source-Symlinks",true) == false ||
3666 Cnf
->Removable
== true)
3668 Desc
.URI
= "copy:" + FileName
;
3673 // Erase the file if it is a symlink so we can overwrite it
3675 if (lstat(DestFile
.c_str(),&St
) == 0)
3677 if (S_ISLNK(St
.st_mode
) != 0)
3678 RemoveFile("pkgAcqFile::Done", DestFile
);
3682 if (symlink(FileName
.c_str(),DestFile
.c_str()) != 0)
3684 _error
->PushToStack();
3685 _error
->Errno("pkgAcqFile::Done", "Symlinking file %s failed", DestFile
.c_str());
3686 std::stringstream msg
;
3687 _error
->DumpErrors(msg
, GlobalError::DEBUG
, false);
3688 _error
->RevertToStack();
3689 ErrorText
= msg
.str();
3696 // AcqFile::Failed - Failure handler /*{{{*/
3697 // ---------------------------------------------------------------------
3698 /* Here we try other sources */
3699 void pkgAcqFile::Failed(string
const &Message
, pkgAcquire::MethodConfig
const * const Cnf
)
3701 Item::Failed(Message
,Cnf
);
3703 // This is the retry counter
3705 Cnf
->LocalOnly
== false &&
3706 StringToBool(LookupTag(Message
,"Transient-Failure"),false) == true)
3716 string
pkgAcqFile::Custom600Headers() const /*{{{*/
3719 return "\nIndex-File: true";
3723 pkgAcqFile::~pkgAcqFile() {}