]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
set the correct item FileSize in by-hash case
[apt.git] / apt-pkg / acquire-item.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-item.cc,v 1.46.2.9 2004/01/16 18:51:11 mdz Exp $
4 /* ######################################################################
5
6 Acquire Item - Item to acquire
7
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.
12
13 ##################################################################### */
14 /*}}}*/
15 // Include Files /*{{{*/
16 #include <config.h>
17
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>
34
35 #include <algorithm>
36 #include <stddef.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <iostream>
40 #include <vector>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <errno.h>
44 #include <string>
45 #include <stdio.h>
46 #include <ctime>
47 #include <sstream>
48 #include <numeric>
49
50 #include <apti18n.h>
51 /*}}}*/
52
53 using namespace std;
54
55 static void printHashSumComparison(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/
56 {
57 if (_config->FindB("Debug::Acquire::HashSumMismatch", false) == false)
58 return;
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;
65 }
66 /*}}}*/
67 static std::string GetPartialFileName(std::string const &file) /*{{{*/
68 {
69 std::string DestFile = _config->FindDir("Dir::State::lists") + "partial/";
70 DestFile += file;
71 return DestFile;
72 }
73 /*}}}*/
74 static std::string GetPartialFileNameFromURI(std::string const &uri) /*{{{*/
75 {
76 return GetPartialFileName(URItoFileName(uri));
77 }
78 /*}}}*/
79 static std::string GetFinalFileNameFromURI(std::string const &uri) /*{{{*/
80 {
81 return _config->FindDir("Dir::State::lists") + URItoFileName(uri);
82 }
83 /*}}}*/
84 static std::string GetKeepCompressedFileName(std::string file, IndexTarget const &Target)/*{{{*/
85 {
86 if (Target.KeepCompressed == false)
87 return file;
88
89 std::string const KeepCompressedAs = Target.Option(IndexTarget::KEEPCOMPRESSEDAS);
90 if (KeepCompressedAs.empty() == false)
91 {
92 std::string const ext = KeepCompressedAs.substr(0, KeepCompressedAs.find(' '));
93 if (ext != "uncompressed")
94 file.append(".").append(ext);
95 }
96 return file;
97 }
98 /*}}}*/
99 static std::string GetMergeDiffsPatchFileName(std::string const &Final, std::string const &Patch)/*{{{*/
100 {
101 // rred expects the patch as $FinalFile.ed.$patchname.gz
102 return Final + ".ed." + Patch + ".gz";
103 }
104 /*}}}*/
105 static std::string GetDiffsPatchFileName(std::string const &Final) /*{{{*/
106 {
107 // rred expects the patch as $FinalFile.ed
108 return Final + ".ed";
109 }
110 /*}}}*/
111 static std::string GetExistingFilename(std::string const &File) /*{{{*/
112 {
113 if (RealFileExists(File))
114 return File;
115 for (auto const &type : APT::Configuration::getCompressorExtensions())
116 {
117 std::string const Final = File + type;
118 if (RealFileExists(Final))
119 return Final;
120 }
121 return "";
122 }
123 /*}}}*/
124 static std::string GetDiffIndexFileName(std::string const &Name) /*{{{*/
125 {
126 return Name + ".diff/Index";
127 }
128 /*}}}*/
129 static std::string GetDiffIndexURI(IndexTarget const &Target) /*{{{*/
130 {
131 return Target.URI + ".diff/Index";
132 }
133 /*}}}*/
134
135 static bool MessageInsecureRepository(bool const isError, std::string const &msg)/*{{{*/
136 {
137 if (isError)
138 {
139 _error->Error("%s", msg.c_str());
140 _error->Notice("%s", _("Updating from such a repository can't be done securely, and is therefore disabled by default."));
141 }
142 else
143 {
144 _error->Warning("%s", msg.c_str());
145 _error->Notice("%s", _("Data from such a repository can't be authenticated and is therefore potentially dangerous to use."));
146 }
147 _error->Notice("%s", _("See apt-secure(8) manpage for repository creation and user configuration details."));
148 return false;
149 }
150 static bool MessageInsecureRepository(bool const isError, char const * const msg, std::string const &repo)
151 {
152 std::string m;
153 strprintf(m, msg, repo.c_str());
154 return MessageInsecureRepository(isError, m);
155 }
156 /*}}}*/
157 static bool AllowInsecureRepositories(char const * const msg, std::string const &repo,/*{{{*/
158 metaIndex const * const MetaIndexParser, pkgAcqMetaClearSig * const TransactionManager, pkgAcquire::Item * const I)
159 {
160 if(MetaIndexParser->GetTrusted() == metaIndex::TRI_YES)
161 return true;
162
163 if (_config->FindB("Acquire::AllowInsecureRepositories") == true)
164 {
165 MessageInsecureRepository(false, msg, repo);
166 return true;
167 }
168
169 MessageInsecureRepository(true, msg, repo);
170 TransactionManager->AbortTransaction();
171 I->Status = pkgAcquire::Item::StatError;
172 return false;
173 }
174 /*}}}*/
175 static HashStringList GetExpectedHashesFromFor(metaIndex * const Parser, std::string const &MetaKey)/*{{{*/
176 {
177 if (Parser == NULL)
178 return HashStringList();
179 metaIndex::checkSum * const R = Parser->Lookup(MetaKey);
180 if (R == NULL)
181 return HashStringList();
182 return R->Hashes;
183 }
184 /*}}}*/
185
186 // all ::HashesRequired and ::GetExpectedHashes implementations /*{{{*/
187 /* ::GetExpectedHashes is abstract and has to be implemented by all subclasses.
188 It is best to implement it as broadly as possible, while ::HashesRequired defaults
189 to true and should be as restrictive as possible for false cases. Note that if
190 a hash is returned by ::GetExpectedHashes it must match. Only if it doesn't
191 ::HashesRequired is called to evaluate if its okay to have no hashes. */
192 APT_CONST bool pkgAcqTransactionItem::HashesRequired() const
193 {
194 /* signed repositories obviously have a parser and good hashes.
195 unsigned repositories, too, as even if we can't trust them for security,
196 we can at least trust them for integrity of the download itself.
197 Only repositories without a Release file can (obviously) not have
198 hashes – and they are very uncommon and strongly discouraged */
199 return TransactionManager->MetaIndexParser != NULL &&
200 TransactionManager->MetaIndexParser->GetLoadedSuccessfully() == metaIndex::TRI_YES;
201 }
202 HashStringList pkgAcqTransactionItem::GetExpectedHashes() const
203 {
204 return GetExpectedHashesFor(GetMetaKey());
205 }
206
207 APT_CONST bool pkgAcqMetaBase::HashesRequired() const
208 {
209 // Release and co have no hashes 'by design'.
210 return false;
211 }
212 HashStringList pkgAcqMetaBase::GetExpectedHashes() const
213 {
214 return HashStringList();
215 }
216
217 APT_CONST bool pkgAcqIndexDiffs::HashesRequired() const
218 {
219 /* We can't check hashes of rred result as we don't know what the
220 hash of the file will be. We just know the hash of the patch(es),
221 the hash of the file they will apply on and the hash of the resulting
222 file. */
223 if (State == StateFetchDiff)
224 return true;
225 return false;
226 }
227 HashStringList pkgAcqIndexDiffs::GetExpectedHashes() const
228 {
229 if (State == StateFetchDiff)
230 return available_patches[0].download_hashes;
231 return HashStringList();
232 }
233
234 APT_CONST bool pkgAcqIndexMergeDiffs::HashesRequired() const
235 {
236 /* @see #pkgAcqIndexDiffs::HashesRequired, with the difference that
237 we can check the rred result after all patches are applied as
238 we know the expected result rather than potentially apply more patches */
239 if (State == StateFetchDiff)
240 return true;
241 return State == StateApplyDiff;
242 }
243 HashStringList pkgAcqIndexMergeDiffs::GetExpectedHashes() const
244 {
245 if (State == StateFetchDiff)
246 return patch.download_hashes;
247 else if (State == StateApplyDiff)
248 return GetExpectedHashesFor(Target.MetaKey);
249 return HashStringList();
250 }
251
252 APT_CONST bool pkgAcqArchive::HashesRequired() const
253 {
254 return LocalSource == false;
255 }
256 HashStringList pkgAcqArchive::GetExpectedHashes() const
257 {
258 // figured out while parsing the records
259 return ExpectedHashes;
260 }
261
262 APT_CONST bool pkgAcqFile::HashesRequired() const
263 {
264 // supplied as parameter at creation time, so the caller decides
265 return ExpectedHashes.usable();
266 }
267 HashStringList pkgAcqFile::GetExpectedHashes() const
268 {
269 return ExpectedHashes;
270 }
271 /*}}}*/
272 // Acquire::Item::QueueURI and specialisations from child classes /*{{{*/
273 bool pkgAcquire::Item::QueueURI(pkgAcquire::ItemDesc &Item)
274 {
275 Owner->Enqueue(Item);
276 return true;
277 }
278 /* The idea here is that an item isn't queued if it exists on disk and the
279 transition manager was a hit as this means that the files it contains
280 the checksums for can't be updated either (or they are and we are asking
281 for a hashsum mismatch to happen which helps nobody) */
282 bool pkgAcqTransactionItem::QueueURI(pkgAcquire::ItemDesc &Item)
283 {
284 if (TransactionManager->State != TransactionStarted)
285 {
286 if (_config->FindB("Debug::Acquire::Transaction", false))
287 std::clog << "Skip " << Target.URI << " as transaction was already dealt with!" << std::endl;
288 return false;
289 }
290 std::string const FinalFile = GetFinalFilename();
291 if (TransactionManager != NULL && TransactionManager->IMSHit == true &&
292 FileExists(FinalFile) == true)
293 {
294 PartialFile = DestFile = FinalFile;
295 Status = StatDone;
296 return false;
297 }
298 return pkgAcquire::Item::QueueURI(Item);
299 }
300 /* The transition manager InRelease itself (or its older sisters-in-law
301 Release & Release.gpg) is always queued as this allows us to rerun gpgv
302 on it to verify that we aren't stalled with old files */
303 bool pkgAcqMetaBase::QueueURI(pkgAcquire::ItemDesc &Item)
304 {
305 return pkgAcquire::Item::QueueURI(Item);
306 }
307 /* the Diff/Index needs to queue also the up-to-date complete index file
308 to ensure that the list cleaner isn't eating it */
309 bool pkgAcqDiffIndex::QueueURI(pkgAcquire::ItemDesc &Item)
310 {
311 if (pkgAcqTransactionItem::QueueURI(Item) == true)
312 return true;
313 QueueOnIMSHit();
314 return false;
315 }
316 /*}}}*/
317 // Acquire::Item::GetFinalFilename and specialisations for child classes /*{{{*/
318 std::string pkgAcquire::Item::GetFinalFilename() const
319 {
320 // Beware: Desc.URI is modified by redirections
321 return GetFinalFileNameFromURI(Desc.URI);
322 }
323 std::string pkgAcqDiffIndex::GetFinalFilename() const
324 {
325 return GetFinalFileNameFromURI(GetDiffIndexURI(Target));
326 }
327 std::string pkgAcqIndex::GetFinalFilename() const
328 {
329 std::string const FinalFile = GetFinalFileNameFromURI(Target.URI);
330 return GetKeepCompressedFileName(FinalFile, Target);
331 }
332 std::string pkgAcqMetaSig::GetFinalFilename() const
333 {
334 return GetFinalFileNameFromURI(Target.URI);
335 }
336 std::string pkgAcqBaseIndex::GetFinalFilename() const
337 {
338 return GetFinalFileNameFromURI(Target.URI);
339 }
340 std::string pkgAcqMetaBase::GetFinalFilename() const
341 {
342 return GetFinalFileNameFromURI(Target.URI);
343 }
344 std::string pkgAcqArchive::GetFinalFilename() const
345 {
346 return _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
347 }
348 /*}}}*/
349 // pkgAcqTransactionItem::GetMetaKey and specialisations for child classes /*{{{*/
350 std::string pkgAcqTransactionItem::GetMetaKey() const
351 {
352 return Target.MetaKey;
353 }
354 std::string pkgAcqIndex::GetMetaKey() const
355 {
356 if (Stage == STAGE_DECOMPRESS_AND_VERIFY || CurrentCompressionExtension == "uncompressed")
357 return Target.MetaKey;
358 return Target.MetaKey + "." + CurrentCompressionExtension;
359 }
360 std::string pkgAcqDiffIndex::GetMetaKey() const
361 {
362 return GetDiffIndexFileName(Target.MetaKey);
363 }
364 /*}}}*/
365 //pkgAcqTransactionItem::TransactionState and specialisations for child classes /*{{{*/
366 bool pkgAcqTransactionItem::TransactionState(TransactionStates const state)
367 {
368 bool const Debug = _config->FindB("Debug::Acquire::Transaction", false);
369 switch(state)
370 {
371 case TransactionStarted: _error->Fatal("Item %s changed to invalid transaction start state!", Target.URI.c_str()); break;
372 case TransactionAbort:
373 if(Debug == true)
374 std::clog << " Cancel: " << DestFile << std::endl;
375 if (Status == pkgAcquire::Item::StatIdle)
376 {
377 Status = pkgAcquire::Item::StatDone;
378 Dequeue();
379 }
380 break;
381 case TransactionCommit:
382 if(PartialFile.empty() == false)
383 {
384 bool sameFile = (PartialFile == DestFile);
385 // we use symlinks on IMS-Hit to avoid copies
386 if (RealFileExists(DestFile))
387 {
388 struct stat Buf;
389 if (lstat(PartialFile.c_str(), &Buf) != -1)
390 {
391 if (S_ISLNK(Buf.st_mode) && Buf.st_size > 0)
392 {
393 char partial[Buf.st_size + 1];
394 ssize_t const sp = readlink(PartialFile.c_str(), partial, Buf.st_size);
395 if (sp == -1)
396 _error->Errno("pkgAcqTransactionItem::TransactionState-sp", _("Failed to readlink %s"), PartialFile.c_str());
397 else
398 {
399 partial[sp] = '\0';
400 sameFile = (DestFile == partial);
401 }
402 }
403 }
404 else
405 _error->Errno("pkgAcqTransactionItem::TransactionState-stat", _("Failed to stat %s"), PartialFile.c_str());
406 }
407 if (sameFile == false)
408 {
409 // ensure that even without lists-cleanup all compressions are nuked
410 std::string FinalFile = GetFinalFileNameFromURI(Target.URI);
411 if (FileExists(FinalFile))
412 {
413 if(Debug == true)
414 std::clog << "rm " << FinalFile << " # " << DescURI() << std::endl;
415 if (RemoveFile("TransactionStates-Cleanup", FinalFile) == false)
416 return false;
417 }
418 for (auto const &ext: APT::Configuration::getCompressorExtensions())
419 {
420 auto const Final = FinalFile + ext;
421 if (FileExists(Final))
422 {
423 if(Debug == true)
424 std::clog << "rm " << Final << " # " << DescURI() << std::endl;
425 if (RemoveFile("TransactionStates-Cleanup", Final) == false)
426 return false;
427 }
428 }
429 if(Debug == true)
430 std::clog << "mv " << PartialFile << " -> "<< DestFile << " # " << DescURI() << std::endl;
431 if (Rename(PartialFile, DestFile) == false)
432 return false;
433 }
434 else if(Debug == true)
435 std::clog << "keep " << PartialFile << " # " << DescURI() << std::endl;
436
437 } else {
438 if(Debug == true)
439 std::clog << "rm " << DestFile << " # " << DescURI() << std::endl;
440 if (RemoveFile("TransItem::TransactionCommit", DestFile) == false)
441 return false;
442 }
443 break;
444 }
445 return true;
446 }
447 bool pkgAcqMetaBase::TransactionState(TransactionStates const state)
448 {
449 // Do not remove InRelease on IMSHit of Release.gpg [yes, this is very edgecasey]
450 if (TransactionManager->IMSHit == false)
451 return pkgAcqTransactionItem::TransactionState(state);
452 return true;
453 }
454 bool pkgAcqIndex::TransactionState(TransactionStates const state)
455 {
456 if (pkgAcqTransactionItem::TransactionState(state) == false)
457 return false;
458
459 switch (state)
460 {
461 case TransactionStarted: _error->Fatal("AcqIndex %s changed to invalid transaction start state!", Target.URI.c_str()); break;
462 case TransactionAbort:
463 if (Stage == STAGE_DECOMPRESS_AND_VERIFY)
464 {
465 // keep the compressed file, but drop the decompressed
466 EraseFileName.clear();
467 if (PartialFile.empty() == false && flExtension(PartialFile) != CurrentCompressionExtension)
468 RemoveFile("TransactionAbort", PartialFile);
469 }
470 break;
471 case TransactionCommit:
472 if (EraseFileName.empty() == false)
473 RemoveFile("AcqIndex::TransactionCommit", EraseFileName);
474 break;
475 }
476 return true;
477 }
478 bool pkgAcqDiffIndex::TransactionState(TransactionStates const state)
479 {
480 if (pkgAcqTransactionItem::TransactionState(state) == false)
481 return false;
482
483 switch (state)
484 {
485 case TransactionStarted: _error->Fatal("Item %s changed to invalid transaction start state!", Target.URI.c_str()); break;
486 case TransactionCommit:
487 break;
488 case TransactionAbort:
489 std::string const Partial = GetPartialFileNameFromURI(Target.URI);
490 RemoveFile("TransactionAbort", Partial);
491 break;
492 }
493
494 return true;
495 }
496 /*}}}*/
497
498 class APT_HIDDEN NoActionItem : public pkgAcquire::Item /*{{{*/
499 /* The sole purpose of this class is having an item which does nothing to
500 reach its done state to prevent cleanup deleting the mentioned file.
501 Handy in cases in which we know we have the file already, like IMS-Hits. */
502 {
503 IndexTarget const Target;
504 public:
505 virtual std::string DescURI() const APT_OVERRIDE {return Target.URI;};
506 virtual HashStringList GetExpectedHashes() const APT_OVERRIDE {return HashStringList();};
507
508 NoActionItem(pkgAcquire * const Owner, IndexTarget const &Target) :
509 pkgAcquire::Item(Owner), Target(Target)
510 {
511 Status = StatDone;
512 DestFile = GetFinalFileNameFromURI(Target.URI);
513 }
514 NoActionItem(pkgAcquire * const Owner, IndexTarget const &Target, std::string const &FinalFile) :
515 pkgAcquire::Item(Owner), Target(Target)
516 {
517 Status = StatDone;
518 DestFile = FinalFile;
519 }
520 };
521 /*}}}*/
522 class APT_HIDDEN CleanupItem : public pkgAcqTransactionItem /*{{{*/
523 /* This class ensures that a file which was configured but isn't downloaded
524 for various reasons isn't kept in an old version in the lists directory.
525 In a way its the reverse of NoActionItem as it helps with removing files
526 even if the lists-cleanup is deactivated. */
527 {
528 public:
529 virtual std::string DescURI() const APT_OVERRIDE {return Target.URI;};
530 virtual HashStringList GetExpectedHashes() const APT_OVERRIDE {return HashStringList();};
531
532 CleanupItem(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager, IndexTarget const &Target) :
533 pkgAcqTransactionItem(Owner, TransactionManager, Target)
534 {
535 Status = StatDone;
536 DestFile = GetFinalFileNameFromURI(Target.URI);
537 }
538 bool TransactionState(TransactionStates const state) APT_OVERRIDE
539 {
540 switch (state)
541 {
542 case TransactionStarted:
543 break;
544 case TransactionAbort:
545 break;
546 case TransactionCommit:
547 if (_config->FindB("Debug::Acquire::Transaction", false) == true)
548 std::clog << "rm " << DestFile << " # " << DescURI() << std::endl;
549 if (RemoveFile("TransItem::TransactionCommit", DestFile) == false)
550 return false;
551 break;
552 }
553 return true;
554 }
555 };
556 /*}}}*/
557
558 // Acquire::Item::Item - Constructor /*{{{*/
559 APT_IGNORE_DEPRECATED_PUSH
560 pkgAcquire::Item::Item(pkgAcquire * const owner) :
561 FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false), Local(false),
562 QueueCounter(0), ExpectedAdditionalItems(0), Owner(owner), d(NULL)
563 {
564 Owner->Add(this);
565 Status = StatIdle;
566 }
567 APT_IGNORE_DEPRECATED_POP
568 /*}}}*/
569 // Acquire::Item::~Item - Destructor /*{{{*/
570 pkgAcquire::Item::~Item()
571 {
572 Owner->Remove(this);
573 }
574 /*}}}*/
575 std::string pkgAcquire::Item::Custom600Headers() const /*{{{*/
576 {
577 return std::string();
578 }
579 /*}}}*/
580 std::string pkgAcquire::Item::ShortDesc() const /*{{{*/
581 {
582 return DescURI();
583 }
584 /*}}}*/
585 APT_CONST void pkgAcquire::Item::Finished() /*{{{*/
586 {
587 }
588 /*}}}*/
589 APT_PURE pkgAcquire * pkgAcquire::Item::GetOwner() const /*{{{*/
590 {
591 return Owner;
592 }
593 /*}}}*/
594 APT_CONST pkgAcquire::ItemDesc &pkgAcquire::Item::GetItemDesc() /*{{{*/
595 {
596 return Desc;
597 }
598 /*}}}*/
599 APT_CONST bool pkgAcquire::Item::IsTrusted() const /*{{{*/
600 {
601 return false;
602 }
603 /*}}}*/
604 // Acquire::Item::Failed - Item failed to download /*{{{*/
605 // ---------------------------------------------------------------------
606 /* We return to an idle state if there are still other queues that could
607 fetch this object */
608 void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
609 {
610 if(ErrorText.empty())
611 ErrorText = LookupTag(Message,"Message");
612 if (QueueCounter <= 1)
613 {
614 /* This indicates that the file is not available right now but might
615 be sometime later. If we do a retry cycle then this should be
616 retried [CDROMs] */
617 if (Cnf != NULL && Cnf->LocalOnly == true &&
618 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
619 {
620 Status = StatIdle;
621 Dequeue();
622 return;
623 }
624
625 switch (Status)
626 {
627 case StatIdle:
628 case StatFetching:
629 case StatDone:
630 Status = StatError;
631 break;
632 case StatAuthError:
633 case StatError:
634 case StatTransientNetworkError:
635 break;
636 }
637 Complete = false;
638 Dequeue();
639 }
640
641 string const FailReason = LookupTag(Message, "FailReason");
642 if (FailReason == "MaximumSizeExceeded")
643 RenameOnError(MaximumSizeExceeded);
644 else if (Status == StatAuthError)
645 RenameOnError(HashSumMismatch);
646
647 // report mirror failure back to LP if we actually use a mirror
648 if (FailReason.empty() == false)
649 ReportMirrorFailure(FailReason);
650 else
651 ReportMirrorFailure(ErrorText);
652
653 if (QueueCounter > 1)
654 Status = StatIdle;
655 }
656 /*}}}*/
657 // Acquire::Item::Start - Item has begun to download /*{{{*/
658 // ---------------------------------------------------------------------
659 /* Stash status and the file size. Note that setting Complete means
660 sub-phases of the acquire process such as decompresion are operating */
661 void pkgAcquire::Item::Start(string const &/*Message*/, unsigned long long const Size)
662 {
663 Status = StatFetching;
664 ErrorText.clear();
665 if (FileSize == 0 && Complete == false)
666 FileSize = Size;
667 }
668 /*}}}*/
669 // Acquire::Item::VerifyDone - check if Item was downloaded OK /*{{{*/
670 /* Note that hash-verification is 'hardcoded' in acquire-worker and has
671 * already passed if this method is called. */
672 bool pkgAcquire::Item::VerifyDone(std::string const &Message,
673 pkgAcquire::MethodConfig const * const /*Cnf*/)
674 {
675 std::string const FileName = LookupTag(Message,"Filename");
676 if (FileName.empty() == true)
677 {
678 Status = StatError;
679 ErrorText = "Method gave a blank filename";
680 return false;
681 }
682
683 return true;
684 }
685 /*}}}*/
686 // Acquire::Item::Done - Item downloaded OK /*{{{*/
687 void pkgAcquire::Item::Done(string const &/*Message*/, HashStringList const &Hashes,
688 pkgAcquire::MethodConfig const * const /*Cnf*/)
689 {
690 // We just downloaded something..
691 if (FileSize == 0)
692 {
693 unsigned long long const downloadedSize = Hashes.FileSize();
694 if (downloadedSize != 0)
695 {
696 FileSize = downloadedSize;
697 }
698 }
699 Status = StatDone;
700 ErrorText = string();
701 Owner->Dequeue(this);
702 }
703 /*}}}*/
704 // Acquire::Item::Rename - Rename a file /*{{{*/
705 // ---------------------------------------------------------------------
706 /* This helper function is used by a lot of item methods as their final
707 step */
708 bool pkgAcquire::Item::Rename(string const &From,string const &To)
709 {
710 if (From == To || rename(From.c_str(),To.c_str()) == 0)
711 return true;
712
713 std::string S;
714 strprintf(S, _("rename failed, %s (%s -> %s)."), strerror(errno),
715 From.c_str(),To.c_str());
716 Status = StatError;
717 if (ErrorText.empty())
718 ErrorText = S;
719 else
720 ErrorText = ErrorText + ": " + S;
721 return false;
722 }
723 /*}}}*/
724 void pkgAcquire::Item::Dequeue() /*{{{*/
725 {
726 Owner->Dequeue(this);
727 }
728 /*}}}*/
729 bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
730 {
731 if (RealFileExists(DestFile))
732 Rename(DestFile, DestFile + ".FAILED");
733
734 std::string errtext;
735 switch (error)
736 {
737 case HashSumMismatch:
738 errtext = _("Hash Sum mismatch");
739 Status = StatAuthError;
740 ReportMirrorFailure("HashChecksumFailure");
741 break;
742 case SizeMismatch:
743 errtext = _("Size mismatch");
744 Status = StatAuthError;
745 ReportMirrorFailure("SizeFailure");
746 break;
747 case InvalidFormat:
748 errtext = _("Invalid file format");
749 Status = StatError;
750 // do not report as usually its not the mirrors fault, but Portal/Proxy
751 break;
752 case SignatureError:
753 errtext = _("Signature error");
754 Status = StatError;
755 break;
756 case NotClearsigned:
757 strprintf(errtext, _("Clearsigned file isn't valid, got '%s' (does the network require authentication?)"), "NOSPLIT");
758 Status = StatAuthError;
759 break;
760 case MaximumSizeExceeded:
761 // the method is expected to report a good error for this
762 Status = StatError;
763 break;
764 case PDiffError:
765 // no handling here, done by callers
766 break;
767 }
768 if (ErrorText.empty())
769 ErrorText = errtext;
770 return false;
771 }
772 /*}}}*/
773 void pkgAcquire::Item::SetActiveSubprocess(const std::string &subprocess)/*{{{*/
774 {
775 ActiveSubprocess = subprocess;
776 APT_IGNORE_DEPRECATED(Mode = ActiveSubprocess.c_str();)
777 }
778 /*}}}*/
779 // Acquire::Item::ReportMirrorFailure /*{{{*/
780 void pkgAcquire::Item::ReportMirrorFailure(string const &FailCode)
781 {
782 // we only act if a mirror was used at all
783 if(UsedMirror.empty())
784 return;
785 #if 0
786 std::cerr << "\nReportMirrorFailure: "
787 << UsedMirror
788 << " Uri: " << DescURI()
789 << " FailCode: "
790 << FailCode << std::endl;
791 #endif
792 string report = _config->Find("Methods::Mirror::ProblemReporting",
793 "/usr/lib/apt/apt-report-mirror-failure");
794 if(!FileExists(report))
795 return;
796
797 std::vector<char const*> Args;
798 Args.push_back(report.c_str());
799 Args.push_back(UsedMirror.c_str());
800 Args.push_back(DescURI().c_str());
801 Args.push_back(FailCode.c_str());
802 Args.push_back(NULL);
803
804 pid_t pid = ExecFork();
805 if(pid < 0)
806 {
807 _error->Error("ReportMirrorFailure Fork failed");
808 return;
809 }
810 else if(pid == 0)
811 {
812 execvp(Args[0], (char**)Args.data());
813 std::cerr << "Could not exec " << Args[0] << std::endl;
814 _exit(100);
815 }
816 if(!ExecWait(pid, "report-mirror-failure"))
817 {
818 _error->Warning("Couldn't report problem to '%s'",
819 _config->Find("Methods::Mirror::ProblemReporting").c_str());
820 }
821 }
822 /*}}}*/
823 std::string pkgAcquire::Item::HashSum() const /*{{{*/
824 {
825 HashStringList const hashes = GetExpectedHashes();
826 HashString const * const hs = hashes.find(NULL);
827 return hs != NULL ? hs->toStr() : "";
828 }
829 /*}}}*/
830
831 pkgAcqTransactionItem::pkgAcqTransactionItem(pkgAcquire * const Owner, /*{{{*/
832 pkgAcqMetaClearSig * const transactionManager, IndexTarget const &target) :
833 pkgAcquire::Item(Owner), d(NULL), Target(target), TransactionManager(transactionManager)
834 {
835 if (TransactionManager != this)
836 TransactionManager->Add(this);
837 }
838 /*}}}*/
839 pkgAcqTransactionItem::~pkgAcqTransactionItem() /*{{{*/
840 {
841 }
842 /*}}}*/
843 HashStringList pkgAcqTransactionItem::GetExpectedHashesFor(std::string const &MetaKey) const /*{{{*/
844 {
845 return GetExpectedHashesFromFor(TransactionManager->MetaIndexParser, MetaKey);
846 }
847 /*}}}*/
848
849 static void LoadLastMetaIndexParser(pkgAcqMetaClearSig * const TransactionManager, std::string const &FinalRelease, std::string const &FinalInRelease)/*{{{*/
850 {
851 if (TransactionManager->IMSHit == true)
852 return;
853 if (RealFileExists(FinalInRelease) || RealFileExists(FinalRelease))
854 {
855 TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
856 if (TransactionManager->LastMetaIndexParser != NULL)
857 {
858 _error->PushToStack();
859 if (RealFileExists(FinalInRelease))
860 TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
861 else
862 TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
863 // its unlikely to happen, but if what we have is bad ignore it
864 if (_error->PendingError())
865 {
866 delete TransactionManager->LastMetaIndexParser;
867 TransactionManager->LastMetaIndexParser = NULL;
868 }
869 _error->RevertToStack();
870 }
871 }
872 }
873 /*}}}*/
874
875 // AcqMetaBase - Constructor /*{{{*/
876 pkgAcqMetaBase::pkgAcqMetaBase(pkgAcquire * const Owner,
877 pkgAcqMetaClearSig * const TransactionManager,
878 std::vector<IndexTarget> const &IndexTargets,
879 IndexTarget const &DataTarget)
880 : pkgAcqTransactionItem(Owner, TransactionManager, DataTarget), d(NULL),
881 IndexTargets(IndexTargets),
882 AuthPass(false), IMSHit(false), State(TransactionStarted)
883 {
884 }
885 /*}}}*/
886 // AcqMetaBase::Add - Add a item to the current Transaction /*{{{*/
887 void pkgAcqMetaBase::Add(pkgAcqTransactionItem * const I)
888 {
889 Transaction.push_back(I);
890 }
891 /*}}}*/
892 // AcqMetaBase::AbortTransaction - Abort the current Transaction /*{{{*/
893 void pkgAcqMetaBase::AbortTransaction()
894 {
895 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
896 std::clog << "AbortTransaction: " << TransactionManager << std::endl;
897
898 switch (TransactionManager->State)
899 {
900 case TransactionStarted: break;
901 case TransactionAbort: _error->Fatal("Transaction %s was already aborted and is aborted again", TransactionManager->Target.URI.c_str()); return;
902 case TransactionCommit: _error->Fatal("Transaction %s was already aborted and is now commited", TransactionManager->Target.URI.c_str()); return;
903 }
904 TransactionManager->State = TransactionAbort;
905
906 // ensure the toplevel is in error state too
907 for (std::vector<pkgAcqTransactionItem*>::iterator I = Transaction.begin();
908 I != Transaction.end(); ++I)
909 {
910 if ((*I)->Status != pkgAcquire::Item::StatFetching)
911 Owner->Dequeue(*I);
912 (*I)->TransactionState(TransactionAbort);
913 }
914 Transaction.clear();
915 }
916 /*}}}*/
917 // AcqMetaBase::TransactionHasError - Check for errors in Transaction /*{{{*/
918 APT_PURE bool pkgAcqMetaBase::TransactionHasError() const
919 {
920 for (std::vector<pkgAcqTransactionItem*>::const_iterator I = Transaction.begin();
921 I != Transaction.end(); ++I)
922 {
923 switch((*I)->Status) {
924 case StatDone: break;
925 case StatIdle: break;
926 case StatAuthError: return true;
927 case StatError: return true;
928 case StatTransientNetworkError: return true;
929 case StatFetching: break;
930 }
931 }
932 return false;
933 }
934 /*}}}*/
935 // AcqMetaBase::CommitTransaction - Commit a transaction /*{{{*/
936 void pkgAcqMetaBase::CommitTransaction()
937 {
938 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
939 std::clog << "CommitTransaction: " << this << std::endl;
940
941 switch (TransactionManager->State)
942 {
943 case TransactionStarted: break;
944 case TransactionAbort: _error->Fatal("Transaction %s was already commited and is now aborted", TransactionManager->Target.URI.c_str()); return;
945 case TransactionCommit: _error->Fatal("Transaction %s was already commited and is again commited", TransactionManager->Target.URI.c_str()); return;
946 }
947 TransactionManager->State = TransactionCommit;
948
949 // move new files into place *and* remove files that are not
950 // part of the transaction but are still on disk
951 for (std::vector<pkgAcqTransactionItem*>::iterator I = Transaction.begin();
952 I != Transaction.end(); ++I)
953 {
954 (*I)->TransactionState(TransactionCommit);
955 }
956 Transaction.clear();
957 }
958 /*}}}*/
959 // AcqMetaBase::TransactionStageCopy - Stage a file for copying /*{{{*/
960 void pkgAcqMetaBase::TransactionStageCopy(pkgAcqTransactionItem * const I,
961 const std::string &From,
962 const std::string &To)
963 {
964 I->PartialFile = From;
965 I->DestFile = To;
966 }
967 /*}}}*/
968 // AcqMetaBase::TransactionStageRemoval - Stage a file for removal /*{{{*/
969 void pkgAcqMetaBase::TransactionStageRemoval(pkgAcqTransactionItem * const I,
970 const std::string &FinalFile)
971 {
972 I->PartialFile = "";
973 I->DestFile = FinalFile;
974 }
975 /*}}}*/
976 // AcqMetaBase::GenerateAuthWarning - Check gpg authentication error /*{{{*/
977 bool pkgAcqMetaBase::CheckStopAuthentication(pkgAcquire::Item * const I, const std::string &Message)
978 {
979 // FIXME: this entire function can do now that we disallow going to
980 // a unauthenticated state and can cleanly rollback
981
982 string const Final = I->GetFinalFilename();
983 if(FileExists(Final))
984 {
985 I->Status = StatTransientNetworkError;
986 _error->Warning(_("An error occurred during the signature "
987 "verification. The repository is not updated "
988 "and the previous index files will be used. "
989 "GPG error: %s: %s"),
990 Desc.Description.c_str(),
991 LookupTag(Message,"Message").c_str());
992 RunScripts("APT::Update::Auth-Failure");
993 return true;
994 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
995 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
996 _error->Error(_("GPG error: %s: %s"),
997 Desc.Description.c_str(),
998 LookupTag(Message,"Message").c_str());
999 I->Status = StatAuthError;
1000 return true;
1001 } else {
1002 _error->Warning(_("GPG error: %s: %s"),
1003 Desc.Description.c_str(),
1004 LookupTag(Message,"Message").c_str());
1005 }
1006 // gpgv method failed
1007 ReportMirrorFailure("GPGFailure");
1008 return false;
1009 }
1010 /*}}}*/
1011 // AcqMetaBase::Custom600Headers - Get header for AcqMetaBase /*{{{*/
1012 // ---------------------------------------------------------------------
1013 string pkgAcqMetaBase::Custom600Headers() const
1014 {
1015 std::string Header = "\nIndex-File: true";
1016 std::string MaximumSize;
1017 strprintf(MaximumSize, "\nMaximum-Size: %i",
1018 _config->FindI("Acquire::MaxReleaseFileSize", 10*1000*1000));
1019 Header += MaximumSize;
1020
1021 string const FinalFile = GetFinalFilename();
1022 struct stat Buf;
1023 if (stat(FinalFile.c_str(),&Buf) == 0)
1024 Header += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1025
1026 return Header;
1027 }
1028 /*}}}*/
1029 // AcqMetaBase::QueueForSignatureVerify /*{{{*/
1030 void pkgAcqMetaBase::QueueForSignatureVerify(pkgAcqTransactionItem * const I, std::string const &File, std::string const &Signature)
1031 {
1032 AuthPass = true;
1033 I->Desc.URI = "gpgv:" + Signature;
1034 I->DestFile = File;
1035 QueueURI(I->Desc);
1036 I->SetActiveSubprocess("gpgv");
1037 }
1038 /*}}}*/
1039 // AcqMetaBase::CheckDownloadDone /*{{{*/
1040 bool pkgAcqMetaBase::CheckDownloadDone(pkgAcqTransactionItem * const I, const std::string &Message, HashStringList const &Hashes) const
1041 {
1042 // We have just finished downloading a Release file (it is not
1043 // verified yet)
1044
1045 std::string const FileName = LookupTag(Message,"Filename");
1046 if (FileName != I->DestFile && RealFileExists(I->DestFile) == false)
1047 {
1048 I->Local = true;
1049 I->Desc.URI = "copy:" + FileName;
1050 I->QueueURI(I->Desc);
1051 return false;
1052 }
1053
1054 // make sure to verify against the right file on I-M-S hit
1055 bool IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"), false);
1056 if (IMSHit == false && Hashes.usable())
1057 {
1058 // detect IMS-Hits servers haven't detected by Hash comparison
1059 std::string const FinalFile = I->GetFinalFilename();
1060 if (RealFileExists(FinalFile) && Hashes.VerifyFile(FinalFile) == true)
1061 {
1062 IMSHit = true;
1063 RemoveFile("CheckDownloadDone", I->DestFile);
1064 }
1065 }
1066
1067 if(IMSHit == true)
1068 {
1069 // for simplicity, the transaction manager is always InRelease
1070 // even if it doesn't exist.
1071 if (TransactionManager != NULL)
1072 TransactionManager->IMSHit = true;
1073 I->PartialFile = I->DestFile = I->GetFinalFilename();
1074 }
1075
1076 // set Item to complete as the remaining work is all local (verify etc)
1077 I->Complete = true;
1078
1079 return true;
1080 }
1081 /*}}}*/
1082 bool pkgAcqMetaBase::CheckAuthDone(string const &Message) /*{{{*/
1083 {
1084 // At this point, the gpgv method has succeeded, so there is a
1085 // valid signature from a key in the trusted keyring. We
1086 // perform additional verification of its contents, and use them
1087 // to verify the indexes we are about to download
1088
1089 if (TransactionManager->IMSHit == false)
1090 {
1091 // open the last (In)Release if we have it
1092 std::string const FinalFile = GetFinalFilename();
1093 std::string FinalRelease;
1094 std::string FinalInRelease;
1095 if (APT::String::Endswith(FinalFile, "InRelease"))
1096 {
1097 FinalInRelease = FinalFile;
1098 FinalRelease = FinalFile.substr(0, FinalFile.length() - strlen("InRelease")) + "Release";
1099 }
1100 else
1101 {
1102 FinalInRelease = FinalFile.substr(0, FinalFile.length() - strlen("Release")) + "InRelease";
1103 FinalRelease = FinalFile;
1104 }
1105 LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
1106 }
1107
1108 if (TransactionManager->MetaIndexParser->Load(DestFile, &ErrorText) == false)
1109 {
1110 Status = StatAuthError;
1111 return false;
1112 }
1113
1114 if (!VerifyVendor(Message))
1115 {
1116 Status = StatAuthError;
1117 return false;
1118 }
1119
1120 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1121 std::cerr << "Signature verification succeeded: "
1122 << DestFile << std::endl;
1123
1124 // Download further indexes with verification
1125 QueueIndexes(true);
1126
1127 return true;
1128 }
1129 /*}}}*/
1130 void pkgAcqMetaBase::QueueIndexes(bool const verify) /*{{{*/
1131 {
1132 // at this point the real Items are loaded in the fetcher
1133 ExpectedAdditionalItems = 0;
1134
1135 bool metaBaseSupportsByHash = false;
1136 if (TransactionManager != NULL && TransactionManager->MetaIndexParser != NULL)
1137 metaBaseSupportsByHash = TransactionManager->MetaIndexParser->GetSupportsAcquireByHash();
1138
1139 for (std::vector <IndexTarget>::iterator Target = IndexTargets.begin();
1140 Target != IndexTargets.end();
1141 ++Target)
1142 {
1143 // all is an implementation detail. Users shouldn't use this as arch
1144 // We need this support trickery here as e.g. Debian has binary-all files already,
1145 // but arch:all packages are still in the arch:any files, so we would waste precious
1146 // download time, bandwidth and diskspace for nothing, BUT Debian doesn't feature all
1147 // in the set of supported architectures, so we can filter based on this property rather
1148 // than invent an entirely new flag we would need to carry for all of eternity.
1149 if (Target->Option(IndexTarget::ARCHITECTURE) == "all")
1150 {
1151 if (TransactionManager->MetaIndexParser->IsArchitectureSupported("all") == false ||
1152 TransactionManager->MetaIndexParser->IsArchitectureAllSupportedFor(*Target) == false)
1153 {
1154 new CleanupItem(Owner, TransactionManager, *Target);
1155 continue;
1156 }
1157 }
1158
1159 bool trypdiff = Target->OptionBool(IndexTarget::PDIFFS);
1160 if (verify == true)
1161 {
1162 if (TransactionManager->MetaIndexParser->Exists(Target->MetaKey) == false)
1163 {
1164 // optional targets that we do not have in the Release file are skipped
1165 if (Target->IsOptional)
1166 {
1167 new CleanupItem(Owner, TransactionManager, *Target);
1168 continue;
1169 }
1170
1171 std::string const &arch = Target->Option(IndexTarget::ARCHITECTURE);
1172 if (arch.empty() == false)
1173 {
1174 if (TransactionManager->MetaIndexParser->IsArchitectureSupported(arch) == false)
1175 {
1176 new CleanupItem(Owner, TransactionManager, *Target);
1177 _error->Notice(_("Skipping acquire of configured file '%s' as repository '%s' doesn't support architecture '%s'"),
1178 Target->MetaKey.c_str(), TransactionManager->Target.Description.c_str(), arch.c_str());
1179 continue;
1180 }
1181 // if the architecture is officially supported but currently no packages for it available,
1182 // ignore silently as this is pretty much the same as just shipping an empty file.
1183 // if we don't know which architectures are supported, we do NOT ignore it to notify user about this
1184 if (TransactionManager->MetaIndexParser->IsArchitectureSupported("*undefined*") == false)
1185 {
1186 new CleanupItem(Owner, TransactionManager, *Target);
1187 continue;
1188 }
1189 }
1190
1191 Status = StatAuthError;
1192 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), Target->MetaKey.c_str());
1193 return;
1194 }
1195 else
1196 {
1197 auto const hashes = GetExpectedHashesFor(Target->MetaKey);
1198 if (hashes.empty() == false)
1199 {
1200 if (hashes.usable() == false)
1201 {
1202 new CleanupItem(Owner, TransactionManager, *Target);
1203 _error->Warning(_("Skipping acquire of configured file '%s' as repository '%s' provides only weak security information for it"),
1204 Target->MetaKey.c_str(), TransactionManager->Target.Description.c_str());
1205 continue;
1206 }
1207 // empty files are skipped as acquiring the very small compressed files is a waste of time
1208 else if (hashes.FileSize() == 0)
1209 {
1210 new CleanupItem(Owner, TransactionManager, *Target);
1211 continue;
1212 }
1213 }
1214 }
1215
1216 // autoselect the compression method
1217 std::vector<std::string> types = VectorizeString(Target->Option(IndexTarget::COMPRESSIONTYPES), ' ');
1218 types.erase(std::remove_if(types.begin(), types.end(), [&](std::string const &t) {
1219 if (t == "uncompressed")
1220 return TransactionManager->MetaIndexParser->Exists(Target->MetaKey) == false;
1221 std::string const MetaKey = Target->MetaKey + "." + t;
1222 return TransactionManager->MetaIndexParser->Exists(MetaKey) == false;
1223 }), types.end());
1224 if (types.empty() == false)
1225 {
1226 std::ostringstream os;
1227 // add the special compressiontype byhash first if supported
1228 std::string const useByHashConf = Target->Option(IndexTarget::BY_HASH);
1229 bool useByHash = false;
1230 if(useByHashConf == "force")
1231 useByHash = true;
1232 else
1233 useByHash = StringToBool(useByHashConf) == true && metaBaseSupportsByHash;
1234 if (useByHash == true)
1235 os << "by-hash ";
1236 std::copy(types.begin(), types.end()-1, std::ostream_iterator<std::string>(os, " "));
1237 os << *types.rbegin();
1238 Target->Options["COMPRESSIONTYPES"] = os.str();
1239 }
1240 else
1241 Target->Options["COMPRESSIONTYPES"].clear();
1242
1243 std::string filename = GetExistingFilename(GetFinalFileNameFromURI(Target->URI));
1244 if (filename.empty() == false)
1245 {
1246 // if the Release file is a hit and we have an index it must be the current one
1247 if (TransactionManager->IMSHit == true)
1248 ;
1249 else if (TransactionManager->LastMetaIndexParser != NULL)
1250 {
1251 // see if the file changed since the last Release file
1252 // we use the uncompressed files as we might compress differently compared to the server,
1253 // so the hashes might not match, even if they contain the same data.
1254 HashStringList const newFile = GetExpectedHashesFromFor(TransactionManager->MetaIndexParser, Target->MetaKey);
1255 HashStringList const oldFile = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target->MetaKey);
1256 if (newFile != oldFile)
1257 filename.clear();
1258 }
1259 else
1260 filename.clear();
1261 }
1262 else
1263 trypdiff = false; // no file to patch
1264
1265 if (filename.empty() == false)
1266 {
1267 new NoActionItem(Owner, *Target, filename);
1268 std::string const idxfilename = GetFinalFileNameFromURI(GetDiffIndexURI(*Target));
1269 if (FileExists(idxfilename))
1270 new NoActionItem(Owner, *Target, idxfilename);
1271 continue;
1272 }
1273
1274 // check if we have patches available
1275 trypdiff &= TransactionManager->MetaIndexParser->Exists(GetDiffIndexFileName(Target->MetaKey));
1276 }
1277 else
1278 {
1279 // if we have no file to patch, no point in trying
1280 trypdiff &= (GetExistingFilename(GetFinalFileNameFromURI(Target->URI)).empty() == false);
1281 }
1282
1283 // no point in patching from local sources
1284 if (trypdiff)
1285 {
1286 std::string const proto = Target->URI.substr(0, strlen("file:/"));
1287 if (proto == "file:/" || proto == "copy:/" || proto == "cdrom:")
1288 trypdiff = false;
1289 }
1290
1291 // Queue the Index file (Packages, Sources, Translation-$foo, …)
1292 if (trypdiff)
1293 new pkgAcqDiffIndex(Owner, TransactionManager, *Target);
1294 else
1295 new pkgAcqIndex(Owner, TransactionManager, *Target);
1296 }
1297 }
1298 /*}}}*/
1299 bool pkgAcqMetaBase::VerifyVendor(string const &) /*{{{*/
1300 {
1301 string Transformed = TransactionManager->MetaIndexParser->GetExpectedDist();
1302
1303 if (Transformed == "../project/experimental")
1304 {
1305 Transformed = "experimental";
1306 }
1307
1308 auto pos = Transformed.rfind('/');
1309 if (pos != string::npos)
1310 {
1311 Transformed = Transformed.substr(0, pos);
1312 }
1313
1314 if (Transformed == ".")
1315 {
1316 Transformed = "";
1317 }
1318
1319 if (TransactionManager->MetaIndexParser->GetValidUntil() > 0)
1320 {
1321 time_t const invalid_since = time(NULL) - TransactionManager->MetaIndexParser->GetValidUntil();
1322 if (invalid_since > 0)
1323 {
1324 std::string errmsg;
1325 strprintf(errmsg,
1326 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1327 // the time since then the file is invalid - formatted in the same way as in
1328 // the download progress display (e.g. 7d 3h 42min 1s)
1329 _("Release file for %s is expired (invalid since %s). "
1330 "Updates for this repository will not be applied."),
1331 Target.URI.c_str(), TimeToStr(invalid_since).c_str());
1332 if (ErrorText.empty())
1333 ErrorText = errmsg;
1334 return _error->Error("%s", errmsg.c_str());
1335 }
1336 }
1337
1338 /* Did we get a file older than what we have? This is a last minute IMS hit and doubles
1339 as a prevention of downgrading us to older (still valid) files */
1340 if (TransactionManager->IMSHit == false && TransactionManager->LastMetaIndexParser != NULL &&
1341 TransactionManager->LastMetaIndexParser->GetDate() > TransactionManager->MetaIndexParser->GetDate())
1342 {
1343 TransactionManager->IMSHit = true;
1344 RemoveFile("VerifyVendor", DestFile);
1345 PartialFile = DestFile = GetFinalFilename();
1346 // load the 'old' file in the 'new' one instead of flipping pointers as
1347 // the new one isn't owned by us, while the old one is so cleanup would be confused.
1348 TransactionManager->MetaIndexParser->swapLoad(TransactionManager->LastMetaIndexParser);
1349 delete TransactionManager->LastMetaIndexParser;
1350 TransactionManager->LastMetaIndexParser = NULL;
1351 }
1352
1353 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1354 {
1355 std::cerr << "Got Codename: " << TransactionManager->MetaIndexParser->GetCodename() << std::endl;
1356 std::cerr << "Expecting Dist: " << TransactionManager->MetaIndexParser->GetExpectedDist() << std::endl;
1357 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1358 }
1359
1360 if (TransactionManager->MetaIndexParser->CheckDist(Transformed) == false)
1361 {
1362 // This might become fatal one day
1363 // Status = StatAuthError;
1364 // ErrorText = "Conflicting distribution; expected "
1365 // + MetaIndexParser->GetExpectedDist() + " but got "
1366 // + MetaIndexParser->GetCodename();
1367 // return false;
1368 if (!Transformed.empty())
1369 {
1370 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1371 Desc.Description.c_str(),
1372 Transformed.c_str(),
1373 TransactionManager->MetaIndexParser->GetCodename().c_str());
1374 }
1375 }
1376
1377 return true;
1378 }
1379 /*}}}*/
1380 pkgAcqMetaBase::~pkgAcqMetaBase()
1381 {
1382 }
1383
1384 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire * const Owner, /*{{{*/
1385 IndexTarget const &ClearsignedTarget,
1386 IndexTarget const &DetachedDataTarget, IndexTarget const &DetachedSigTarget,
1387 std::vector<IndexTarget> const &IndexTargets,
1388 metaIndex * const MetaIndexParser) :
1389 pkgAcqMetaIndex(Owner, this, ClearsignedTarget, DetachedSigTarget, IndexTargets),
1390 d(NULL), ClearsignedTarget(ClearsignedTarget),
1391 DetachedDataTarget(DetachedDataTarget),
1392 MetaIndexParser(MetaIndexParser), LastMetaIndexParser(NULL)
1393 {
1394 // index targets + (worst case:) Release/Release.gpg
1395 ExpectedAdditionalItems = IndexTargets.size() + 2;
1396 TransactionManager->Add(this);
1397 }
1398 /*}}}*/
1399 pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
1400 {
1401 if (LastMetaIndexParser != NULL)
1402 delete LastMetaIndexParser;
1403 }
1404 /*}}}*/
1405 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1406 string pkgAcqMetaClearSig::Custom600Headers() const
1407 {
1408 string Header = pkgAcqMetaBase::Custom600Headers();
1409 Header += "\nFail-Ignore: true";
1410 std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
1411 if (key.empty() == false)
1412 Header += "\nSigned-By: " + key;
1413
1414 return Header;
1415 }
1416 /*}}}*/
1417 void pkgAcqMetaClearSig::Finished() /*{{{*/
1418 {
1419 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1420 std::clog << "Finished: " << DestFile <<std::endl;
1421 if(TransactionManager != NULL && TransactionManager->State == TransactionStarted &&
1422 TransactionManager->TransactionHasError() == false)
1423 TransactionManager->CommitTransaction();
1424 }
1425 /*}}}*/
1426 bool pkgAcqMetaClearSig::VerifyDone(std::string const &Message, /*{{{*/
1427 pkgAcquire::MethodConfig const * const Cnf)
1428 {
1429 Item::VerifyDone(Message, Cnf);
1430
1431 if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
1432 return RenameOnError(NotClearsigned);
1433
1434 return true;
1435 }
1436 /*}}}*/
1437 // pkgAcqMetaClearSig::Done - We got a file /*{{{*/
1438 void pkgAcqMetaClearSig::Done(std::string const &Message,
1439 HashStringList const &Hashes,
1440 pkgAcquire::MethodConfig const * const Cnf)
1441 {
1442 Item::Done(Message, Hashes, Cnf);
1443
1444 if(AuthPass == false)
1445 {
1446 if(CheckDownloadDone(this, Message, Hashes) == true)
1447 QueueForSignatureVerify(this, DestFile, DestFile);
1448 return;
1449 }
1450 else if(CheckAuthDone(Message) == true)
1451 {
1452 if (TransactionManager->IMSHit == false)
1453 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1454 else if (RealFileExists(GetFinalFilename()) == false)
1455 {
1456 // We got an InRelease file IMSHit, but we haven't one, which means
1457 // we had a valid Release/Release.gpg combo stepping in, which we have
1458 // to 'acquire' now to ensure list cleanup isn't removing them
1459 new NoActionItem(Owner, DetachedDataTarget);
1460 new NoActionItem(Owner, DetachedSigTarget);
1461 }
1462 }
1463 }
1464 /*}}}*/
1465 void pkgAcqMetaClearSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf) /*{{{*/
1466 {
1467 Item::Failed(Message, Cnf);
1468
1469 // we failed, we will not get additional items from this method
1470 ExpectedAdditionalItems = 0;
1471
1472 if (AuthPass == false)
1473 {
1474 if (Status == StatAuthError || Status == StatTransientNetworkError)
1475 {
1476 // if we expected a ClearTextSignature (InRelease) but got a network
1477 // error or got a file, but it wasn't valid, we end up here (see VerifyDone).
1478 // As these is usually called by web-portals we do not try Release/Release.gpg
1479 // as this is gonna fail anyway and instead abort our try (LP#346386)
1480 TransactionManager->AbortTransaction();
1481 return;
1482 }
1483
1484 // Queue the 'old' InRelease file for removal if we try Release.gpg
1485 // as otherwise the file will stay around and gives a false-auth
1486 // impression (CVE-2012-0214)
1487 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
1488 Status = StatDone;
1489
1490 new pkgAcqMetaIndex(Owner, TransactionManager, DetachedDataTarget, DetachedSigTarget, IndexTargets);
1491 }
1492 else
1493 {
1494 if(CheckStopAuthentication(this, Message))
1495 return;
1496
1497 // No Release file was present, or verification failed, so fall
1498 // back to queueing Packages files without verification
1499 // only allow going further if the user explicitly wants it
1500 if(AllowInsecureRepositories(_("The repository '%s' is not signed."), ClearsignedTarget.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1501 {
1502 Status = StatDone;
1503
1504 /* InRelease files become Release files, otherwise
1505 * they would be considered as trusted later on */
1506 string const FinalRelease = GetFinalFileNameFromURI(DetachedDataTarget.URI);
1507 string const PartialRelease = GetPartialFileNameFromURI(DetachedDataTarget.URI);
1508 string const FinalReleasegpg = GetFinalFileNameFromURI(DetachedSigTarget.URI);
1509 string const FinalInRelease = GetFinalFilename();
1510 Rename(DestFile, PartialRelease);
1511 TransactionManager->TransactionStageCopy(this, PartialRelease, FinalRelease);
1512 LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
1513
1514 // we parse the indexes here because at this point the user wanted
1515 // a repository that may potentially harm him
1516 if (TransactionManager->MetaIndexParser->Load(PartialRelease, &ErrorText) == false || VerifyVendor(Message) == false)
1517 /* expired Release files are still a problem you need extra force for */;
1518 else
1519 QueueIndexes(true);
1520 }
1521 }
1522 }
1523 /*}}}*/
1524
1525 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire * const Owner, /*{{{*/
1526 pkgAcqMetaClearSig * const TransactionManager,
1527 IndexTarget const &DataTarget,
1528 IndexTarget const &DetachedSigTarget,
1529 vector<IndexTarget> const &IndexTargets) :
1530 pkgAcqMetaBase(Owner, TransactionManager, IndexTargets, DataTarget), d(NULL),
1531 DetachedSigTarget(DetachedSigTarget)
1532 {
1533 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1534 std::clog << "New pkgAcqMetaIndex with TransactionManager "
1535 << this->TransactionManager << std::endl;
1536
1537 DestFile = GetPartialFileNameFromURI(DataTarget.URI);
1538
1539 // Create the item
1540 Desc.Description = DataTarget.Description;
1541 Desc.Owner = this;
1542 Desc.ShortDesc = DataTarget.ShortDesc;
1543 Desc.URI = DataTarget.URI;
1544
1545 // we expect more item
1546 ExpectedAdditionalItems = IndexTargets.size();
1547 QueueURI(Desc);
1548 }
1549 /*}}}*/
1550 void pkgAcqMetaIndex::Done(string const &Message, /*{{{*/
1551 HashStringList const &Hashes,
1552 pkgAcquire::MethodConfig const * const Cfg)
1553 {
1554 Item::Done(Message,Hashes,Cfg);
1555
1556 if(CheckDownloadDone(this, Message, Hashes))
1557 {
1558 // we have a Release file, now download the Signature, all further
1559 // verify/queue for additional downloads will be done in the
1560 // pkgAcqMetaSig::Done() code
1561 new pkgAcqMetaSig(Owner, TransactionManager, DetachedSigTarget, this);
1562 }
1563 }
1564 /*}}}*/
1565 // pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
1566 void pkgAcqMetaIndex::Failed(string const &Message,
1567 pkgAcquire::MethodConfig const * const Cnf)
1568 {
1569 pkgAcquire::Item::Failed(Message, Cnf);
1570 Status = StatDone;
1571
1572 // No Release file was present so fall
1573 // back to queueing Packages files without verification
1574 // only allow going further if the user explicitly wants it
1575 if(AllowInsecureRepositories(_("The repository '%s' does not have a Release file."), Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1576 {
1577 // ensure old Release files are removed
1578 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
1579
1580 // queue without any kind of hashsum support
1581 QueueIndexes(false);
1582 }
1583 }
1584 /*}}}*/
1585 std::string pkgAcqMetaIndex::DescURI() const /*{{{*/
1586 {
1587 return Target.URI;
1588 }
1589 /*}}}*/
1590 pkgAcqMetaIndex::~pkgAcqMetaIndex() {}
1591
1592 // AcqMetaSig::AcqMetaSig - Constructor /*{{{*/
1593 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire * const Owner,
1594 pkgAcqMetaClearSig * const TransactionManager,
1595 IndexTarget const &Target,
1596 pkgAcqMetaIndex * const MetaIndex) :
1597 pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL), MetaIndex(MetaIndex)
1598 {
1599 DestFile = GetPartialFileNameFromURI(Target.URI);
1600
1601 // remove any partial downloaded sig-file in partial/.
1602 // it may confuse proxies and is too small to warrant a
1603 // partial download anyway
1604 RemoveFile("pkgAcqMetaSig", DestFile);
1605
1606 // set the TransactionManager
1607 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1608 std::clog << "New pkgAcqMetaSig with TransactionManager "
1609 << TransactionManager << std::endl;
1610
1611 // Create the item
1612 Desc.Description = Target.Description;
1613 Desc.Owner = this;
1614 Desc.ShortDesc = Target.ShortDesc;
1615 Desc.URI = Target.URI;
1616
1617 // If we got a hit for Release, we will get one for Release.gpg too (or obscure errors),
1618 // so we skip the download step and go instantly to verification
1619 if (TransactionManager->IMSHit == true && RealFileExists(GetFinalFilename()))
1620 {
1621 Complete = true;
1622 Status = StatDone;
1623 PartialFile = DestFile = GetFinalFilename();
1624 MetaIndexFileSignature = DestFile;
1625 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1626 }
1627 else
1628 QueueURI(Desc);
1629 }
1630 /*}}}*/
1631 pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1632 {
1633 }
1634 /*}}}*/
1635 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1636 std::string pkgAcqMetaSig::Custom600Headers() const
1637 {
1638 std::string Header = pkgAcqTransactionItem::Custom600Headers();
1639 std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
1640 if (key.empty() == false)
1641 Header += "\nSigned-By: " + key;
1642 return Header;
1643 }
1644 /*}}}*/
1645 // AcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
1646 void pkgAcqMetaSig::Done(string const &Message, HashStringList const &Hashes,
1647 pkgAcquire::MethodConfig const * const Cfg)
1648 {
1649 if (MetaIndexFileSignature.empty() == false)
1650 {
1651 DestFile = MetaIndexFileSignature;
1652 MetaIndexFileSignature.clear();
1653 }
1654 Item::Done(Message, Hashes, Cfg);
1655
1656 if(MetaIndex->AuthPass == false)
1657 {
1658 if(MetaIndex->CheckDownloadDone(this, Message, Hashes) == true)
1659 {
1660 // destfile will be modified to point to MetaIndexFile for the
1661 // gpgv method, so we need to save it here
1662 MetaIndexFileSignature = DestFile;
1663 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1664 }
1665 return;
1666 }
1667 else if(MetaIndex->CheckAuthDone(Message) == true)
1668 {
1669 if (TransactionManager->IMSHit == false)
1670 {
1671 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1672 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
1673 }
1674 }
1675 }
1676 /*}}}*/
1677 void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
1678 {
1679 Item::Failed(Message,Cnf);
1680
1681 // check if we need to fail at this point
1682 if (MetaIndex->AuthPass == true && MetaIndex->CheckStopAuthentication(this, Message))
1683 return;
1684
1685 string const FinalRelease = MetaIndex->GetFinalFilename();
1686 string const FinalReleasegpg = GetFinalFilename();
1687 string const FinalInRelease = TransactionManager->GetFinalFilename();
1688
1689 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
1690 {
1691 std::string downgrade_msg;
1692 strprintf(downgrade_msg, _("The repository '%s' is no longer signed."),
1693 MetaIndex->Target.Description.c_str());
1694 if(_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
1695 {
1696 // meh, the users wants to take risks (we still mark the packages
1697 // from this repository as unauthenticated)
1698 _error->Warning("%s", downgrade_msg.c_str());
1699 _error->Warning(_("This is normally not allowed, but the option "
1700 "Acquire::AllowDowngradeToInsecureRepositories was "
1701 "given to override it."));
1702 Status = StatDone;
1703 } else {
1704 MessageInsecureRepository(true, downgrade_msg);
1705 if (TransactionManager->IMSHit == false)
1706 Rename(MetaIndex->DestFile, MetaIndex->DestFile + ".FAILED");
1707 Item::Failed("Message: " + downgrade_msg, Cnf);
1708 TransactionManager->AbortTransaction();
1709 return;
1710 }
1711 }
1712
1713 // ensures that a Release.gpg file in the lists/ is removed by the transaction
1714 TransactionManager->TransactionStageRemoval(this, DestFile);
1715
1716 // only allow going further if the user explicitly wants it
1717 if (AllowInsecureRepositories(_("The repository '%s' is not signed."), MetaIndex->Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1718 {
1719 LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
1720
1721 // we parse the indexes here because at this point the user wanted
1722 // a repository that may potentially harm him
1723 bool const GoodLoad = TransactionManager->MetaIndexParser->Load(MetaIndex->DestFile, &ErrorText);
1724 if (MetaIndex->VerifyVendor(Message) == false)
1725 /* expired Release files are still a problem you need extra force for */;
1726 else
1727 MetaIndex->QueueIndexes(GoodLoad);
1728
1729 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
1730 }
1731
1732 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1733 if (Cnf->LocalOnly == true ||
1734 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1735 {
1736 // Ignore this
1737 Status = StatDone;
1738 }
1739 }
1740 /*}}}*/
1741
1742
1743 // AcqBaseIndex - Constructor /*{{{*/
1744 pkgAcqBaseIndex::pkgAcqBaseIndex(pkgAcquire * const Owner,
1745 pkgAcqMetaClearSig * const TransactionManager,
1746 IndexTarget const &Target)
1747 : pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL)
1748 {
1749 }
1750 /*}}}*/
1751 pkgAcqBaseIndex::~pkgAcqBaseIndex() {}
1752
1753 // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
1754 // ---------------------------------------------------------------------
1755 /* Get the DiffIndex file first and see if there are patches available
1756 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
1757 * patches. If anything goes wrong in that process, it will fall back to
1758 * the original packages file
1759 */
1760 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire * const Owner,
1761 pkgAcqMetaClearSig * const TransactionManager,
1762 IndexTarget const &Target)
1763 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), diffs(NULL)
1764 {
1765 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
1766
1767 Desc.Owner = this;
1768 Desc.Description = GetDiffIndexFileName(Target.Description);
1769 Desc.ShortDesc = Target.ShortDesc;
1770 Desc.URI = GetDiffIndexURI(Target);
1771
1772 DestFile = GetPartialFileNameFromURI(Desc.URI);
1773
1774 if(Debug)
1775 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
1776
1777 QueueURI(Desc);
1778 }
1779 /*}}}*/
1780 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
1781 // ---------------------------------------------------------------------
1782 /* The only header we use is the last-modified header. */
1783 string pkgAcqDiffIndex::Custom600Headers() const
1784 {
1785 if (TransactionManager->LastMetaIndexParser != NULL)
1786 return "\nIndex-File: true";
1787
1788 string const Final = GetFinalFilename();
1789
1790 if(Debug)
1791 std::clog << "Custom600Header-IMS: " << Final << std::endl;
1792
1793 struct stat Buf;
1794 if (stat(Final.c_str(),&Buf) != 0)
1795 return "\nIndex-File: true";
1796
1797 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1798 }
1799 /*}}}*/
1800 void pkgAcqDiffIndex::QueueOnIMSHit() const /*{{{*/
1801 {
1802 // list cleanup needs to know that this file as well as the already
1803 // present index is ours, so we create an empty diff to save it for us
1804 new pkgAcqIndexDiffs(Owner, TransactionManager, Target);
1805 }
1806 /*}}}*/
1807 static bool RemoveFileForBootstrapLinking(bool const Debug, std::string const &For, std::string const &Boot)/*{{{*/
1808 {
1809 if (FileExists(Boot) && RemoveFile("Bootstrap-linking", Boot) == false)
1810 {
1811 if (Debug)
1812 std::clog << "Bootstrap-linking for patching " << For
1813 << " by removing stale " << Boot << " failed!" << std::endl;
1814 return false;
1815 }
1816 return true;
1817 }
1818 /*}}}*/
1819 bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/
1820 {
1821 // failing here is fine: our caller will take care of trying to
1822 // get the complete file if patching fails
1823 if(Debug)
1824 std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
1825 << std::endl;
1826
1827 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
1828 pkgTagFile TF(&Fd);
1829 if (Fd.IsOpen() == false || Fd.Failed())
1830 return false;
1831
1832 pkgTagSection Tags;
1833 if(unlikely(TF.Step(Tags) == false))
1834 return false;
1835
1836 HashStringList ServerHashes;
1837 unsigned long long ServerSize = 0;
1838
1839 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1840 {
1841 std::string tagname = *type;
1842 tagname.append("-Current");
1843 std::string const tmp = Tags.FindS(tagname.c_str());
1844 if (tmp.empty() == true)
1845 continue;
1846
1847 string hash;
1848 unsigned long long size;
1849 std::stringstream ss(tmp);
1850 ss >> hash >> size;
1851 if (unlikely(hash.empty() == true))
1852 continue;
1853 if (unlikely(ServerSize != 0 && ServerSize != size))
1854 continue;
1855 ServerHashes.push_back(HashString(*type, hash));
1856 ServerSize = size;
1857 }
1858
1859 if (ServerHashes.usable() == false)
1860 {
1861 if (Debug == true)
1862 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Did not find a good hashsum in the index" << std::endl;
1863 return false;
1864 }
1865
1866 std::string const CurrentPackagesFile = GetFinalFileNameFromURI(Target.URI);
1867 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
1868 if (TargetFileHashes.usable() == false || ServerHashes != TargetFileHashes)
1869 {
1870 if (Debug == true)
1871 {
1872 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl;
1873 printHashSumComparison(CurrentPackagesFile, ServerHashes, TargetFileHashes);
1874 }
1875 return false;
1876 }
1877
1878 HashStringList LocalHashes;
1879 // try avoiding calculating the hash here as this is costly
1880 if (TransactionManager->LastMetaIndexParser != NULL)
1881 LocalHashes = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target.MetaKey);
1882 if (LocalHashes.usable() == false)
1883 {
1884 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly, FileFd::Auto);
1885 Hashes LocalHashesCalc(ServerHashes);
1886 LocalHashesCalc.AddFD(fd);
1887 LocalHashes = LocalHashesCalc.GetHashStringList();
1888 }
1889
1890 if (ServerHashes == LocalHashes)
1891 {
1892 // we have the same sha1 as the server so we are done here
1893 if(Debug)
1894 std::clog << "pkgAcqDiffIndex: Package file " << CurrentPackagesFile << " is up-to-date" << std::endl;
1895 QueueOnIMSHit();
1896 return true;
1897 }
1898
1899 if(Debug)
1900 std::clog << "Server-Current: " << ServerHashes.find(NULL)->toStr() << " and we start at "
1901 << CurrentPackagesFile << " " << LocalHashes.FileSize() << " " << LocalHashes.find(NULL)->toStr() << std::endl;
1902
1903 // historically, older hashes have more info than newer ones, so start
1904 // collecting with older ones first to avoid implementing complicated
1905 // information merging techniques… a failure is after all always
1906 // recoverable with a complete file and hashes aren't changed that often.
1907 std::vector<char const *> types;
1908 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1909 types.push_back(*type);
1910
1911 // parse all of (provided) history
1912 vector<DiffInfo> available_patches;
1913 bool firstAcceptedHashes = true;
1914 for (auto type = types.crbegin(); type != types.crend(); ++type)
1915 {
1916 if (LocalHashes.find(*type) == NULL)
1917 continue;
1918
1919 std::string tagname = *type;
1920 tagname.append("-History");
1921 std::string const tmp = Tags.FindS(tagname.c_str());
1922 if (tmp.empty() == true)
1923 continue;
1924
1925 string hash, filename;
1926 unsigned long long size;
1927 std::stringstream ss(tmp);
1928
1929 while (ss >> hash >> size >> filename)
1930 {
1931 if (unlikely(hash.empty() == true || filename.empty() == true))
1932 continue;
1933
1934 // see if we have a record for this file already
1935 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1936 for (; cur != available_patches.end(); ++cur)
1937 {
1938 if (cur->file != filename)
1939 continue;
1940 cur->result_hashes.push_back(HashString(*type, hash));
1941 break;
1942 }
1943 if (cur != available_patches.end())
1944 continue;
1945 if (firstAcceptedHashes == true)
1946 {
1947 DiffInfo next;
1948 next.file = filename;
1949 next.result_hashes.push_back(HashString(*type, hash));
1950 next.result_hashes.FileSize(size);
1951 available_patches.push_back(next);
1952 }
1953 else
1954 {
1955 if (Debug == true)
1956 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
1957 << " wasn't in the list for the first parsed hash! (history)" << std::endl;
1958 break;
1959 }
1960 }
1961 firstAcceptedHashes = false;
1962 }
1963
1964 if (unlikely(available_patches.empty() == true))
1965 {
1966 if (Debug)
1967 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
1968 << "Couldn't find any patches for the patch series." << std::endl;
1969 return false;
1970 }
1971
1972 for (auto type = types.crbegin(); type != types.crend(); ++type)
1973 {
1974 if (LocalHashes.find(*type) == NULL)
1975 continue;
1976
1977 std::string tagname = *type;
1978 tagname.append("-Patches");
1979 std::string const tmp = Tags.FindS(tagname.c_str());
1980 if (tmp.empty() == true)
1981 continue;
1982
1983 string hash, filename;
1984 unsigned long long size;
1985 std::stringstream ss(tmp);
1986
1987 while (ss >> hash >> size >> filename)
1988 {
1989 if (unlikely(hash.empty() == true || filename.empty() == true))
1990 continue;
1991
1992 // see if we have a record for this file already
1993 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1994 for (; cur != available_patches.end(); ++cur)
1995 {
1996 if (cur->file != filename)
1997 continue;
1998 if (cur->patch_hashes.empty())
1999 cur->patch_hashes.FileSize(size);
2000 cur->patch_hashes.push_back(HashString(*type, hash));
2001 break;
2002 }
2003 if (cur != available_patches.end())
2004 continue;
2005 if (Debug == true)
2006 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
2007 << " wasn't in the list for the first parsed hash! (patches)" << std::endl;
2008 break;
2009 }
2010 }
2011
2012 for (auto type = types.crbegin(); type != types.crend(); ++type)
2013 {
2014 std::string tagname = *type;
2015 tagname.append("-Download");
2016 std::string const tmp = Tags.FindS(tagname.c_str());
2017 if (tmp.empty() == true)
2018 continue;
2019
2020 string hash, filename;
2021 unsigned long long size;
2022 std::stringstream ss(tmp);
2023
2024 // FIXME: all of pdiff supports only .gz compressed patches
2025 while (ss >> hash >> size >> filename)
2026 {
2027 if (unlikely(hash.empty() == true || filename.empty() == true))
2028 continue;
2029 if (unlikely(APT::String::Endswith(filename, ".gz") == false))
2030 continue;
2031 filename.erase(filename.length() - 3);
2032
2033 // see if we have a record for this file already
2034 std::vector<DiffInfo>::iterator cur = available_patches.begin();
2035 for (; cur != available_patches.end(); ++cur)
2036 {
2037 if (cur->file != filename)
2038 continue;
2039 if (cur->download_hashes.empty())
2040 cur->download_hashes.FileSize(size);
2041 cur->download_hashes.push_back(HashString(*type, hash));
2042 break;
2043 }
2044 if (cur != available_patches.end())
2045 continue;
2046 if (Debug == true)
2047 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
2048 << " wasn't in the list for the first parsed hash! (download)" << std::endl;
2049 break;
2050 }
2051 }
2052
2053
2054 bool foundStart = false;
2055 for (std::vector<DiffInfo>::iterator cur = available_patches.begin();
2056 cur != available_patches.end(); ++cur)
2057 {
2058 if (LocalHashes != cur->result_hashes)
2059 continue;
2060
2061 available_patches.erase(available_patches.begin(), cur);
2062 foundStart = true;
2063 break;
2064 }
2065
2066 if (foundStart == false || unlikely(available_patches.empty() == true))
2067 {
2068 if (Debug)
2069 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
2070 << "Couldn't find the start of the patch series." << std::endl;
2071 return false;
2072 }
2073
2074 for (auto const &patch: available_patches)
2075 if (patch.result_hashes.usable() == false ||
2076 patch.patch_hashes.usable() == false ||
2077 patch.download_hashes.usable() == false)
2078 {
2079 if (Debug)
2080 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": provides no usable hashes for " << patch.file
2081 << " so fallback to complete download" << std::endl;
2082 return false;
2083 }
2084
2085 // patching with too many files is rather slow compared to a fast download
2086 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
2087 if (fileLimit != 0 && fileLimit < available_patches.size())
2088 {
2089 if (Debug)
2090 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
2091 << ") so fallback to complete download" << std::endl;
2092 return false;
2093 }
2094
2095 // calculate the size of all patches we have to get
2096 unsigned short const sizeLimitPercent = _config->FindI("Acquire::PDiffs::SizeLimit", 100);
2097 if (sizeLimitPercent > 0 && TransactionManager->MetaIndexParser != nullptr)
2098 {
2099 unsigned long long downloadSize = std::accumulate(available_patches.begin(),
2100 available_patches.end(), 0llu, [](unsigned long long const T, DiffInfo const &I) {
2101 return T + I.download_hashes.FileSize();
2102 });
2103 if (downloadSize != 0)
2104 {
2105 unsigned long long downloadSizeIdx = 0;
2106 auto const types = VectorizeString(Target.Option(IndexTarget::COMPRESSIONTYPES), ' ');
2107 for (auto const &t : types)
2108 {
2109 std::string MetaKey = Target.MetaKey;
2110 if (t != "uncompressed")
2111 MetaKey += '.' + t;
2112 HashStringList const hsl = GetExpectedHashesFor(MetaKey);
2113 if (unlikely(hsl.usable() == false))
2114 continue;
2115 downloadSizeIdx = hsl.FileSize();
2116 break;
2117 }
2118 unsigned long long const sizeLimit = downloadSizeIdx * sizeLimitPercent;
2119 if ((sizeLimit/100) < downloadSize)
2120 {
2121 if (Debug)
2122 std::clog << "Need " << downloadSize << " compressed bytes (Limit is " << (sizeLimit/100) << ", "
2123 << "original is " << downloadSizeIdx << ") so fallback to complete download" << std::endl;
2124 return false;
2125 }
2126 }
2127 }
2128
2129 // we have something, queue the diffs
2130 string::size_type const last_space = Description.rfind(" ");
2131 if(last_space != string::npos)
2132 Description.erase(last_space, Description.size()-last_space);
2133
2134 /* decide if we should download patches one by one or in one go:
2135 The first is good if the server merges patches, but many don't so client
2136 based merging can be attempt in which case the second is better.
2137 "bad things" will happen if patches are merged on the server,
2138 but client side merging is attempt as well */
2139 bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
2140 if (pdiff_merge == true)
2141 {
2142 // reprepro adds this flag if it has merged patches on the server
2143 std::string const precedence = Tags.FindS("X-Patch-Precedence");
2144 pdiff_merge = (precedence != "merged");
2145 }
2146
2147 // clean the plate
2148 {
2149 std::string const Final = GetExistingFilename(CurrentPackagesFile);
2150 if (unlikely(Final.empty())) // because we wouldn't be called in such a case
2151 return false;
2152 std::string const PartialFile = GetPartialFileNameFromURI(Target.URI);
2153 std::string const PatchedFile = GetKeepCompressedFileName(PartialFile + "-patched", Target);
2154 if (RemoveFileForBootstrapLinking(Debug, CurrentPackagesFile, PartialFile) == false ||
2155 RemoveFileForBootstrapLinking(Debug, CurrentPackagesFile, PatchedFile) == false)
2156 return false;
2157 for (auto const &ext : APT::Configuration::getCompressorExtensions())
2158 {
2159 if (RemoveFileForBootstrapLinking(Debug, CurrentPackagesFile, PartialFile + ext) == false ||
2160 RemoveFileForBootstrapLinking(Debug, CurrentPackagesFile, PatchedFile + ext) == false)
2161 return false;
2162 }
2163 std::string const Ext = Final.substr(CurrentPackagesFile.length());
2164 std::string const Partial = PartialFile + Ext;
2165 if (symlink(Final.c_str(), Partial.c_str()) != 0)
2166 {
2167 if (Debug)
2168 std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile
2169 << " by linking " << Final << " to " << Partial << " failed!" << std::endl;
2170 return false;
2171 }
2172 }
2173
2174 if (pdiff_merge == false)
2175 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
2176 else
2177 {
2178 diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
2179 for(size_t i = 0; i < available_patches.size(); ++i)
2180 (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, TransactionManager,
2181 Target,
2182 available_patches[i],
2183 diffs);
2184 }
2185
2186 Complete = false;
2187 Status = StatDone;
2188 Dequeue();
2189 return true;
2190 }
2191 /*}}}*/
2192 void pkgAcqDiffIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2193 {
2194 Item::Failed(Message,Cnf);
2195 Status = StatDone;
2196
2197 if(Debug)
2198 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
2199 << "Falling back to normal index file acquire" << std::endl;
2200
2201 new pkgAcqIndex(Owner, TransactionManager, Target);
2202 }
2203 /*}}}*/
2204 void pkgAcqDiffIndex::Done(string const &Message,HashStringList const &Hashes, /*{{{*/
2205 pkgAcquire::MethodConfig const * const Cnf)
2206 {
2207 if(Debug)
2208 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
2209
2210 Item::Done(Message, Hashes, Cnf);
2211
2212 string const FinalFile = GetFinalFilename();
2213 if(StringToBool(LookupTag(Message,"IMS-Hit"),false))
2214 DestFile = FinalFile;
2215
2216 if(ParseDiffIndex(DestFile) == false)
2217 {
2218 Failed("Message: Couldn't parse pdiff index", Cnf);
2219 // queue for final move - this should happen even if we fail
2220 // while parsing (e.g. on sizelimit) and download the complete file.
2221 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2222 return;
2223 }
2224
2225 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2226
2227 Complete = true;
2228 Status = StatDone;
2229 Dequeue();
2230
2231 return;
2232 }
2233 /*}}}*/
2234 pkgAcqDiffIndex::~pkgAcqDiffIndex()
2235 {
2236 if (diffs != NULL)
2237 delete diffs;
2238 }
2239
2240 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
2241 // ---------------------------------------------------------------------
2242 /* The package diff is added to the queue. one object is constructed
2243 * for each diff and the index
2244 */
2245 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire * const Owner,
2246 pkgAcqMetaClearSig * const TransactionManager,
2247 IndexTarget const &Target,
2248 vector<DiffInfo> const &diffs)
2249 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
2250 available_patches(diffs)
2251 {
2252 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2253
2254 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
2255
2256 Desc.Owner = this;
2257 Description = Target.Description;
2258 Desc.ShortDesc = Target.ShortDesc;
2259
2260 if(available_patches.empty() == true)
2261 {
2262 // we are done (yeah!), check hashes against the final file
2263 DestFile = GetKeepCompressedFileName(GetFinalFileNameFromURI(Target.URI), Target);
2264 Finish(true);
2265 }
2266 else
2267 {
2268 State = StateFetchDiff;
2269 QueueNextDiff();
2270 }
2271 }
2272 /*}}}*/
2273 void pkgAcqIndexDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2274 {
2275 Item::Failed(Message,Cnf);
2276 Status = StatDone;
2277
2278 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2279 if(Debug)
2280 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
2281 << "Falling back to normal index file acquire " << std::endl;
2282 RenameOnError(PDiffError);
2283 std::string const patchname = GetDiffsPatchFileName(DestFile);
2284 if (RealFileExists(patchname))
2285 Rename(patchname, patchname + ".FAILED");
2286 std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
2287 if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile))
2288 Rename(UnpatchedFile, UnpatchedFile + ".FAILED");
2289 new pkgAcqIndex(Owner, TransactionManager, Target);
2290 Finish();
2291 }
2292 /*}}}*/
2293 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
2294 void pkgAcqIndexDiffs::Finish(bool allDone)
2295 {
2296 if(Debug)
2297 std::clog << "pkgAcqIndexDiffs::Finish(): "
2298 << allDone << " "
2299 << Desc.URI << std::endl;
2300
2301 // we restore the original name, this is required, otherwise
2302 // the file will be cleaned
2303 if(allDone)
2304 {
2305 std::string const Final = GetKeepCompressedFileName(GetFinalFilename(), Target);
2306 TransactionManager->TransactionStageCopy(this, DestFile, Final);
2307
2308 // this is for the "real" finish
2309 Complete = true;
2310 Status = StatDone;
2311 Dequeue();
2312 if(Debug)
2313 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
2314 return;
2315 }
2316 else
2317 DestFile.clear();
2318
2319 if(Debug)
2320 std::clog << "Finishing: " << Desc.URI << std::endl;
2321 Complete = false;
2322 Status = StatDone;
2323 Dequeue();
2324 return;
2325 }
2326 /*}}}*/
2327 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
2328 {
2329 // calc sha1 of the just patched file
2330 std::string const PartialFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
2331 if(unlikely(PartialFile.empty()))
2332 {
2333 Failed("Message: The file " + GetPartialFileNameFromURI(Target.URI) + " isn't available", NULL);
2334 return false;
2335 }
2336
2337 FileFd fd(PartialFile, FileFd::ReadOnly, FileFd::Extension);
2338 Hashes LocalHashesCalc;
2339 LocalHashesCalc.AddFD(fd);
2340 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
2341
2342 if(Debug)
2343 std::clog << "QueueNextDiff: " << PartialFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
2344
2345 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
2346 if (unlikely(LocalHashes.usable() == false || TargetFileHashes.usable() == false))
2347 {
2348 Failed("Local/Expected hashes are not usable for " + PartialFile, NULL);
2349 return false;
2350 }
2351
2352 // final file reached before all patches are applied
2353 if(LocalHashes == TargetFileHashes)
2354 {
2355 Finish(true);
2356 return true;
2357 }
2358
2359 // remove all patches until the next matching patch is found
2360 // this requires the Index file to be ordered
2361 available_patches.erase(available_patches.begin(),
2362 std::find_if(available_patches.begin(), available_patches.end(), [&](DiffInfo const &I) {
2363 return I.result_hashes == LocalHashes;
2364 }));
2365
2366 // error checking and falling back if no patch was found
2367 if(available_patches.empty() == true)
2368 {
2369 Failed("No patches left to reach target for " + PartialFile, NULL);
2370 return false;
2371 }
2372
2373 // queue the right diff
2374 Desc.URI = Target.URI + ".diff/" + available_patches[0].file + ".gz";
2375 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
2376 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI + ".diff/" + available_patches[0].file), Target);
2377
2378 if(Debug)
2379 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
2380
2381 QueueURI(Desc);
2382
2383 return true;
2384 }
2385 /*}}}*/
2386 void pkgAcqIndexDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2387 pkgAcquire::MethodConfig const * const Cnf)
2388 {
2389 if (Debug)
2390 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
2391
2392 Item::Done(Message, Hashes, Cnf);
2393
2394 std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI);
2395 std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile);
2396 std::string const PatchFile = GetDiffsPatchFileName(UnpatchedFile);
2397 std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target);
2398
2399 switch (State)
2400 {
2401 // success in downloading a diff, enter ApplyDiff state
2402 case StateFetchDiff:
2403 Rename(DestFile, PatchFile);
2404 DestFile = GetKeepCompressedFileName(UncompressedUnpatchedFile + "-patched", Target);
2405 if(Debug)
2406 std::clog << "Sending to rred method: " << UnpatchedFile << std::endl;
2407 State = StateApplyDiff;
2408 Local = true;
2409 Desc.URI = "rred:" + UnpatchedFile;
2410 QueueURI(Desc);
2411 SetActiveSubprocess("rred");
2412 return;
2413 // success in download/apply a diff, queue next (if needed)
2414 case StateApplyDiff:
2415 // remove the just applied patch and base file
2416 available_patches.erase(available_patches.begin());
2417 RemoveFile("pkgAcqIndexDiffs::Done", PatchFile);
2418 RemoveFile("pkgAcqIndexDiffs::Done", UnpatchedFile);
2419 if(Debug)
2420 std::clog << "Moving patched file in place: " << std::endl
2421 << DestFile << " -> " << PatchedFile << std::endl;
2422 Rename(DestFile, PatchedFile);
2423
2424 // see if there is more to download
2425 if(available_patches.empty() == false)
2426 {
2427 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
2428 Finish();
2429 } else {
2430 DestFile = PatchedFile;
2431 Finish(true);
2432 }
2433 return;
2434 }
2435 }
2436 /*}}}*/
2437 std::string pkgAcqIndexDiffs::Custom600Headers() const /*{{{*/
2438 {
2439 if(State != StateApplyDiff)
2440 return pkgAcqBaseIndex::Custom600Headers();
2441 std::ostringstream patchhashes;
2442 for (auto && hs : available_patches[0].result_hashes)
2443 patchhashes << "\nStart-" << hs.HashType() << "-Hash: " << hs.HashValue();
2444 for (auto && hs : available_patches[0].patch_hashes)
2445 patchhashes << "\nPatch-0-" << hs.HashType() << "-Hash: " << hs.HashValue();
2446 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2447 return patchhashes.str();
2448 }
2449 /*}}}*/
2450 pkgAcqIndexDiffs::~pkgAcqIndexDiffs() {}
2451
2452 // AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
2453 pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire * const Owner,
2454 pkgAcqMetaClearSig * const TransactionManager,
2455 IndexTarget const &Target,
2456 DiffInfo const &patch,
2457 std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
2458 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
2459 patch(patch), allPatches(allPatches), State(StateFetchDiff)
2460 {
2461 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
2462
2463 Desc.Owner = this;
2464 Description = Target.Description;
2465 Desc.ShortDesc = Target.ShortDesc;
2466 Desc.URI = Target.URI + ".diff/" + patch.file + ".gz";
2467 Desc.Description = Description + " " + patch.file + ".pdiff";
2468 DestFile = GetPartialFileNameFromURI(Desc.URI);
2469
2470 if(Debug)
2471 std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
2472
2473 QueueURI(Desc);
2474 }
2475 /*}}}*/
2476 void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2477 {
2478 if(Debug)
2479 std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
2480
2481 Item::Failed(Message,Cnf);
2482 Status = StatDone;
2483
2484 // check if we are the first to fail, otherwise we are done here
2485 State = StateDoneDiff;
2486 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2487 I != allPatches->end(); ++I)
2488 if ((*I)->State == StateErrorDiff)
2489 {
2490 State = StateErrorDiff;
2491 return;
2492 }
2493
2494 // first failure means we should fallback
2495 State = StateErrorDiff;
2496 if (Debug)
2497 std::clog << "Falling back to normal index file acquire" << std::endl;
2498 RenameOnError(PDiffError);
2499 if (RealFileExists(DestFile))
2500 Rename(DestFile, DestFile + ".FAILED");
2501 std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
2502 if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile))
2503 Rename(UnpatchedFile, UnpatchedFile + ".FAILED");
2504 DestFile.clear();
2505 new pkgAcqIndex(Owner, TransactionManager, Target);
2506 }
2507 /*}}}*/
2508 void pkgAcqIndexMergeDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2509 pkgAcquire::MethodConfig const * const Cnf)
2510 {
2511 if(Debug)
2512 std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
2513
2514 Item::Done(Message, Hashes, Cnf);
2515
2516 if (std::any_of(allPatches->begin(), allPatches->end(),
2517 [](pkgAcqIndexMergeDiffs const * const P) { return P->State == StateErrorDiff; }))
2518 {
2519 if(Debug)
2520 std::clog << "Another patch failed already, no point in processing this one." << std::endl;
2521 State = StateErrorDiff;
2522 return;
2523 }
2524
2525 std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI);
2526 std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile);
2527 if (UnpatchedFile.empty())
2528 {
2529 _error->Fatal("Unpatched file %s doesn't exist (anymore)!", UncompressedUnpatchedFile.c_str());
2530 State = StateErrorDiff;
2531 return;
2532 }
2533 std::string const PatchFile = GetMergeDiffsPatchFileName(UnpatchedFile, patch.file);
2534 std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target);
2535
2536 switch (State)
2537 {
2538 case StateFetchDiff:
2539 Rename(DestFile, PatchFile);
2540
2541 // check if this is the last completed diff
2542 State = StateDoneDiff;
2543 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2544 I != allPatches->end(); ++I)
2545 if ((*I)->State != StateDoneDiff)
2546 {
2547 if(Debug)
2548 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
2549 return;
2550 }
2551 // this is the last completed diff, so we are ready to apply now
2552 DestFile = GetKeepCompressedFileName(UncompressedUnpatchedFile + "-patched", Target);
2553 if(Debug)
2554 std::clog << "Sending to rred method: " << UnpatchedFile << std::endl;
2555 State = StateApplyDiff;
2556 Local = true;
2557 Desc.URI = "rred:" + UnpatchedFile;
2558 QueueURI(Desc);
2559 SetActiveSubprocess("rred");
2560 return;
2561 case StateApplyDiff:
2562 // success in download & apply all diffs, finialize and clean up
2563 if(Debug)
2564 std::clog << "Queue patched file in place: " << std::endl
2565 << DestFile << " -> " << PatchedFile << std::endl;
2566
2567 // queue for copy by the transaction manager
2568 TransactionManager->TransactionStageCopy(this, DestFile, GetKeepCompressedFileName(GetFinalFilename(), Target));
2569
2570 // ensure the ed's are gone regardless of list-cleanup
2571 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2572 I != allPatches->end(); ++I)
2573 RemoveFile("pkgAcqIndexMergeDiffs::Done", GetMergeDiffsPatchFileName(UnpatchedFile, (*I)->patch.file));
2574 RemoveFile("pkgAcqIndexMergeDiffs::Done", UnpatchedFile);
2575
2576 // all set and done
2577 Complete = true;
2578 if(Debug)
2579 std::clog << "allDone: " << DestFile << "\n" << std::endl;
2580 return;
2581 case StateDoneDiff: _error->Fatal("Done called for %s which is in an invalid Done state", PatchFile.c_str()); break;
2582 case StateErrorDiff: _error->Fatal("Done called for %s which is in an invalid Error state", PatchFile.c_str()); break;
2583 }
2584 }
2585 /*}}}*/
2586 std::string pkgAcqIndexMergeDiffs::Custom600Headers() const /*{{{*/
2587 {
2588 if(State != StateApplyDiff)
2589 return pkgAcqBaseIndex::Custom600Headers();
2590 std::ostringstream patchhashes;
2591 unsigned int seen_patches = 0;
2592 for (auto && hs : (*allPatches)[0]->patch.result_hashes)
2593 patchhashes << "\nStart-" << hs.HashType() << "-Hash: " << hs.HashValue();
2594 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2595 I != allPatches->end(); ++I)
2596 {
2597 HashStringList const ExpectedHashes = (*I)->patch.patch_hashes;
2598 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
2599 patchhashes << "\nPatch-" << seen_patches << "-" << hs->HashType() << "-Hash: " << hs->HashValue();
2600 ++seen_patches;
2601 }
2602 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2603 return patchhashes.str();
2604 }
2605 /*}}}*/
2606 pkgAcqIndexMergeDiffs::~pkgAcqIndexMergeDiffs() {}
2607
2608 // AcqIndex::AcqIndex - Constructor /*{{{*/
2609 pkgAcqIndex::pkgAcqIndex(pkgAcquire * const Owner,
2610 pkgAcqMetaClearSig * const TransactionManager,
2611 IndexTarget const &Target)
2612 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), Stage(STAGE_DOWNLOAD),
2613 CompressionExtensions(Target.Option(IndexTarget::COMPRESSIONTYPES))
2614 {
2615 Init(Target.URI, Target.Description, Target.ShortDesc);
2616
2617 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
2618 std::clog << "New pkgIndex with TransactionManager "
2619 << TransactionManager << std::endl;
2620 }
2621 /*}}}*/
2622 // AcqIndex::Init - defered Constructor /*{{{*/
2623 static void NextCompressionExtension(std::string &CurrentCompressionExtension, std::string &CompressionExtensions, bool const preview)
2624 {
2625 size_t const nextExt = CompressionExtensions.find(' ');
2626 if (nextExt == std::string::npos)
2627 {
2628 CurrentCompressionExtension = CompressionExtensions;
2629 if (preview == false)
2630 CompressionExtensions.clear();
2631 }
2632 else
2633 {
2634 CurrentCompressionExtension = CompressionExtensions.substr(0, nextExt);
2635 if (preview == false)
2636 CompressionExtensions = CompressionExtensions.substr(nextExt+1);
2637 }
2638 }
2639 void pkgAcqIndex::Init(string const &URI, string const &URIDesc,
2640 string const &ShortDesc)
2641 {
2642 Stage = STAGE_DOWNLOAD;
2643
2644 DestFile = GetPartialFileNameFromURI(URI);
2645 NextCompressionExtension(CurrentCompressionExtension, CompressionExtensions, false);
2646
2647 if (CurrentCompressionExtension == "uncompressed")
2648 {
2649 Desc.URI = URI;
2650 }
2651 else if (CurrentCompressionExtension == "by-hash")
2652 {
2653 NextCompressionExtension(CurrentCompressionExtension, CompressionExtensions, true);
2654 if(unlikely(TransactionManager->MetaIndexParser == NULL || CurrentCompressionExtension.empty()))
2655 return;
2656 if (CurrentCompressionExtension != "uncompressed")
2657 {
2658 Desc.URI = URI + '.' + CurrentCompressionExtension;
2659 DestFile = DestFile + '.' + CurrentCompressionExtension;
2660 }
2661
2662 HashStringList const Hashes = GetExpectedHashes();
2663 HashString const * const TargetHash = Hashes.find(NULL);
2664 if (unlikely(TargetHash == nullptr))
2665 return;
2666 std::string const ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
2667 size_t const trailing_slash = Desc.URI.find_last_of("/");
2668 if (unlikely(trailing_slash == std::string::npos))
2669 return;
2670 Desc.URI = Desc.URI.replace(
2671 trailing_slash,
2672 Desc.URI.substr(trailing_slash+1).size()+1,
2673 ByHash);
2674 }
2675 else if (unlikely(CurrentCompressionExtension.empty()))
2676 return;
2677 else
2678 {
2679 Desc.URI = URI + '.' + CurrentCompressionExtension;
2680 DestFile = DestFile + '.' + CurrentCompressionExtension;
2681 }
2682
2683 // store file size of the download to ensure the fetcher gives
2684 // accurate progress reporting
2685 FileSize = GetExpectedHashes().FileSize();
2686
2687 Desc.Description = URIDesc;
2688 Desc.Owner = this;
2689 Desc.ShortDesc = ShortDesc;
2690
2691 QueueURI(Desc);
2692 }
2693 /*}}}*/
2694 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2695 // ---------------------------------------------------------------------
2696 /* The only header we use is the last-modified header. */
2697 string pkgAcqIndex::Custom600Headers() const
2698 {
2699
2700 string msg = "\nIndex-File: true";
2701
2702 if (TransactionManager->LastMetaIndexParser == NULL)
2703 {
2704 std::string const Final = GetFinalFilename();
2705
2706 struct stat Buf;
2707 if (stat(Final.c_str(),&Buf) == 0)
2708 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
2709 }
2710
2711 if(Target.IsOptional)
2712 msg += "\nFail-Ignore: true";
2713
2714 return msg;
2715 }
2716 /*}}}*/
2717 // AcqIndex::Failed - getting the indexfile failed /*{{{*/
2718 void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
2719 {
2720 Item::Failed(Message,Cnf);
2721
2722 // authorisation matches will not be fixed by other compression types
2723 if (Status != StatAuthError)
2724 {
2725 if (CompressionExtensions.empty() == false)
2726 {
2727 Init(Target.URI, Desc.Description, Desc.ShortDesc);
2728 Status = StatIdle;
2729 return;
2730 }
2731 }
2732
2733 if(Target.IsOptional && GetExpectedHashes().empty() && Stage == STAGE_DOWNLOAD)
2734 Status = StatDone;
2735 else
2736 TransactionManager->AbortTransaction();
2737 }
2738 /*}}}*/
2739 // AcqIndex::Done - Finished a fetch /*{{{*/
2740 // ---------------------------------------------------------------------
2741 /* This goes through a number of states.. On the initial fetch the
2742 method could possibly return an alternate filename which points
2743 to the uncompressed version of the file. If this is so the file
2744 is copied into the partial directory. In all other cases the file
2745 is decompressed with a compressed uri. */
2746 void pkgAcqIndex::Done(string const &Message,
2747 HashStringList const &Hashes,
2748 pkgAcquire::MethodConfig const * const Cfg)
2749 {
2750 Item::Done(Message,Hashes,Cfg);
2751
2752 switch(Stage)
2753 {
2754 case STAGE_DOWNLOAD:
2755 StageDownloadDone(Message);
2756 break;
2757 case STAGE_DECOMPRESS_AND_VERIFY:
2758 StageDecompressDone();
2759 break;
2760 }
2761 }
2762 /*}}}*/
2763 // AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
2764 void pkgAcqIndex::StageDownloadDone(string const &Message)
2765 {
2766 Local = true;
2767 Complete = true;
2768
2769 std::string const AltFilename = LookupTag(Message,"Alt-Filename");
2770 std::string Filename = LookupTag(Message,"Filename");
2771
2772 // we need to verify the file against the current Release file again
2773 // on if-modfied-since hit to avoid a stale attack against us
2774 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2775 {
2776 // copy FinalFile into partial/ so that we check the hash again
2777 string const FinalFile = GetExistingFilename(GetFinalFileNameFromURI(Target.URI));
2778 if (symlink(FinalFile.c_str(), DestFile.c_str()) != 0)
2779 _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking final file %s back to %s failed", FinalFile.c_str(), DestFile.c_str());
2780 else
2781 {
2782 EraseFileName = DestFile;
2783 Filename = DestFile;
2784 }
2785 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2786 Desc.URI = "store:" + Filename;
2787 QueueURI(Desc);
2788 SetActiveSubprocess(::URI(Desc.URI).Access);
2789 return;
2790 }
2791 // methods like file:// give us an alternative (uncompressed) file
2792 else if (Target.KeepCompressed == false && AltFilename.empty() == false)
2793 {
2794 Filename = AltFilename;
2795 EraseFileName.clear();
2796 }
2797 // Methods like e.g. "file:" will give us a (compressed) FileName that is
2798 // not the "DestFile" we set, in this case we uncompress from the local file
2799 else if (Filename != DestFile && RealFileExists(DestFile) == false)
2800 {
2801 // symlinking ensures that the filename can be used for compression detection
2802 // that is e.g. needed for by-hash which has no extension over file
2803 if (symlink(Filename.c_str(),DestFile.c_str()) != 0)
2804 _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking file %s to %s failed", Filename.c_str(), DestFile.c_str());
2805 else
2806 {
2807 EraseFileName = DestFile;
2808 Filename = DestFile;
2809 }
2810 }
2811
2812 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2813 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2814 if (Filename != DestFile && flExtension(Filename) == flExtension(DestFile))
2815 Desc.URI = "copy:" + Filename;
2816 else
2817 Desc.URI = "store:" + Filename;
2818 if (DestFile == Filename)
2819 {
2820 if (CurrentCompressionExtension == "uncompressed")
2821 return StageDecompressDone();
2822 DestFile = "/dev/null";
2823 }
2824
2825 if (EraseFileName.empty() && Filename != AltFilename)
2826 EraseFileName = Filename;
2827
2828 // queue uri for the next stage
2829 QueueURI(Desc);
2830 SetActiveSubprocess(::URI(Desc.URI).Access);
2831 }
2832 /*}}}*/
2833 // AcqIndex::StageDecompressDone - Final verification /*{{{*/
2834 void pkgAcqIndex::StageDecompressDone()
2835 {
2836 if (DestFile == "/dev/null")
2837 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2838
2839 // Done, queue for rename on transaction finished
2840 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
2841 }
2842 /*}}}*/
2843 pkgAcqIndex::~pkgAcqIndex() {}
2844
2845
2846 // AcqArchive::AcqArchive - Constructor /*{{{*/
2847 // ---------------------------------------------------------------------
2848 /* This just sets up the initial fetch environment and queues the first
2849 possibilitiy */
2850 pkgAcqArchive::pkgAcqArchive(pkgAcquire * const Owner,pkgSourceList * const Sources,
2851 pkgRecords * const Recs,pkgCache::VerIterator const &Version,
2852 string &StoreFilename) :
2853 Item(Owner), d(NULL), LocalSource(false), Version(Version), Sources(Sources), Recs(Recs),
2854 StoreFilename(StoreFilename), Vf(Version.FileList()),
2855 Trusted(false)
2856 {
2857 Retries = _config->FindI("Acquire::Retries",0);
2858
2859 if (Version.Arch() == 0)
2860 {
2861 _error->Error(_("I wasn't able to locate a file for the %s package. "
2862 "This might mean you need to manually fix this package. "
2863 "(due to missing arch)"),
2864 Version.ParentPkg().FullName().c_str());
2865 return;
2866 }
2867
2868 /* We need to find a filename to determine the extension. We make the
2869 assumption here that all the available sources for this version share
2870 the same extension.. */
2871 // Skip not source sources, they do not have file fields.
2872 for (; Vf.end() == false; ++Vf)
2873 {
2874 if (Vf.File().Flagged(pkgCache::Flag::NotSource))
2875 continue;
2876 break;
2877 }
2878
2879 // Does not really matter here.. we are going to fail out below
2880 if (Vf.end() != true)
2881 {
2882 // If this fails to get a file name we will bomb out below.
2883 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2884 if (_error->PendingError() == true)
2885 return;
2886
2887 // Generate the final file name as: package_version_arch.foo
2888 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2889 QuoteString(Version.VerStr(),"_:") + '_' +
2890 QuoteString(Version.Arch(),"_:.") +
2891 "." + flExtension(Parse.FileName());
2892 }
2893
2894 // check if we have one trusted source for the package. if so, switch
2895 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2896 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2897 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2898 bool seenUntrusted = false;
2899 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
2900 {
2901 pkgIndexFile *Index;
2902 if (Sources->FindIndex(i.File(),Index) == false)
2903 continue;
2904
2905 if (debugAuth == true)
2906 std::cerr << "Checking index: " << Index->Describe()
2907 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2908
2909 if (Index->IsTrusted() == true)
2910 {
2911 Trusted = true;
2912 if (allowUnauth == false)
2913 break;
2914 }
2915 else
2916 seenUntrusted = true;
2917 }
2918
2919 // "allow-unauthenticated" restores apts old fetching behaviour
2920 // that means that e.g. unauthenticated file:// uris are higher
2921 // priority than authenticated http:// uris
2922 if (allowUnauth == true && seenUntrusted == true)
2923 Trusted = false;
2924
2925 // Select a source
2926 if (QueueNext() == false && _error->PendingError() == false)
2927 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2928 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2929 }
2930 /*}}}*/
2931 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
2932 // ---------------------------------------------------------------------
2933 /* This queues the next available file version for download. It checks if
2934 the archive is already available in the cache and stashs the MD5 for
2935 checking later. */
2936 bool pkgAcqArchive::QueueNext()
2937 {
2938 for (; Vf.end() == false; ++Vf)
2939 {
2940 pkgCache::PkgFileIterator const PkgF = Vf.File();
2941 // Ignore not source sources
2942 if (PkgF.Flagged(pkgCache::Flag::NotSource))
2943 continue;
2944
2945 // Try to cross match against the source list
2946 pkgIndexFile *Index;
2947 if (Sources->FindIndex(PkgF, Index) == false)
2948 continue;
2949 LocalSource = PkgF.Flagged(pkgCache::Flag::LocalSource);
2950
2951 // only try to get a trusted package from another source if that source
2952 // is also trusted
2953 if(Trusted && !Index->IsTrusted())
2954 continue;
2955
2956 // Grab the text package record
2957 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2958 if (_error->PendingError() == true)
2959 return false;
2960
2961 string PkgFile = Parse.FileName();
2962 ExpectedHashes = Parse.Hashes();
2963
2964 if (PkgFile.empty() == true)
2965 return _error->Error(_("The package index files are corrupted. No Filename: "
2966 "field for package %s."),
2967 Version.ParentPkg().Name());
2968
2969 Desc.URI = Index->ArchiveURI(PkgFile);
2970 Desc.Description = Index->ArchiveInfo(Version);
2971 Desc.Owner = this;
2972 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2973
2974 // See if we already have the file. (Legacy filenames)
2975 FileSize = Version->Size;
2976 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2977 struct stat Buf;
2978 if (stat(FinalFile.c_str(),&Buf) == 0)
2979 {
2980 // Make sure the size matches
2981 if ((unsigned long long)Buf.st_size == Version->Size)
2982 {
2983 Complete = true;
2984 Local = true;
2985 Status = StatDone;
2986 StoreFilename = DestFile = FinalFile;
2987 return true;
2988 }
2989
2990 /* Hmm, we have a file and its size does not match, this means it is
2991 an old style mismatched arch */
2992 RemoveFile("pkgAcqArchive::QueueNext", FinalFile);
2993 }
2994
2995 // Check it again using the new style output filenames
2996 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2997 if (stat(FinalFile.c_str(),&Buf) == 0)
2998 {
2999 // Make sure the size matches
3000 if ((unsigned long long)Buf.st_size == Version->Size)
3001 {
3002 Complete = true;
3003 Local = true;
3004 Status = StatDone;
3005 StoreFilename = DestFile = FinalFile;
3006 return true;
3007 }
3008
3009 /* Hmm, we have a file and its size does not match, this shouldn't
3010 happen.. */
3011 RemoveFile("pkgAcqArchive::QueueNext", FinalFile);
3012 }
3013
3014 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
3015
3016 // Check the destination file
3017 if (stat(DestFile.c_str(),&Buf) == 0)
3018 {
3019 // Hmm, the partial file is too big, erase it
3020 if ((unsigned long long)Buf.st_size > Version->Size)
3021 RemoveFile("pkgAcqArchive::QueueNext", DestFile);
3022 else
3023 PartialSize = Buf.st_size;
3024 }
3025
3026 // Disables download of archives - useful if no real installation follows,
3027 // e.g. if we are just interested in proposed installation order
3028 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
3029 {
3030 Complete = true;
3031 Local = true;
3032 Status = StatDone;
3033 StoreFilename = DestFile = FinalFile;
3034 return true;
3035 }
3036
3037 // Create the item
3038 Local = false;
3039 QueueURI(Desc);
3040
3041 ++Vf;
3042 return true;
3043 }
3044 return false;
3045 }
3046 /*}}}*/
3047 // AcqArchive::Done - Finished fetching /*{{{*/
3048 // ---------------------------------------------------------------------
3049 /* */
3050 void pkgAcqArchive::Done(string const &Message, HashStringList const &Hashes,
3051 pkgAcquire::MethodConfig const * const Cfg)
3052 {
3053 Item::Done(Message, Hashes, Cfg);
3054
3055 // Grab the output filename
3056 std::string const FileName = LookupTag(Message,"Filename");
3057 if (DestFile != FileName && RealFileExists(DestFile) == false)
3058 {
3059 StoreFilename = DestFile = FileName;
3060 Local = true;
3061 Complete = true;
3062 return;
3063 }
3064
3065 // Done, move it into position
3066 string const FinalFile = GetFinalFilename();
3067 Rename(DestFile,FinalFile);
3068 StoreFilename = DestFile = FinalFile;
3069 Complete = true;
3070 }
3071 /*}}}*/
3072 // AcqArchive::Failed - Failure handler /*{{{*/
3073 // ---------------------------------------------------------------------
3074 /* Here we try other sources */
3075 void pkgAcqArchive::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
3076 {
3077 Item::Failed(Message,Cnf);
3078
3079 /* We don't really want to retry on failed media swaps, this prevents
3080 that. An interesting observation is that permanent failures are not
3081 recorded. */
3082 if (Cnf->Removable == true &&
3083 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3084 {
3085 // Vf = Version.FileList();
3086 while (Vf.end() == false) ++Vf;
3087 StoreFilename = string();
3088 return;
3089 }
3090
3091 Status = StatIdle;
3092 if (QueueNext() == false)
3093 {
3094 // This is the retry counter
3095 if (Retries != 0 &&
3096 Cnf->LocalOnly == false &&
3097 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3098 {
3099 Retries--;
3100 Vf = Version.FileList();
3101 if (QueueNext() == true)
3102 return;
3103 }
3104
3105 StoreFilename = string();
3106 Status = StatError;
3107 }
3108 }
3109 /*}}}*/
3110 APT_PURE bool pkgAcqArchive::IsTrusted() const /*{{{*/
3111 {
3112 return Trusted;
3113 }
3114 /*}}}*/
3115 void pkgAcqArchive::Finished() /*{{{*/
3116 {
3117 if (Status == pkgAcquire::Item::StatDone &&
3118 Complete == true)
3119 return;
3120 StoreFilename = string();
3121 }
3122 /*}}}*/
3123 std::string pkgAcqArchive::DescURI() const /*{{{*/
3124 {
3125 return Desc.URI;
3126 }
3127 /*}}}*/
3128 std::string pkgAcqArchive::ShortDesc() const /*{{{*/
3129 {
3130 return Desc.ShortDesc;
3131 }
3132 /*}}}*/
3133 pkgAcqArchive::~pkgAcqArchive() {}
3134
3135 // AcqChangelog::pkgAcqChangelog - Constructors /*{{{*/
3136 class pkgAcqChangelog::Private
3137 {
3138 public:
3139 std::string FinalFile;
3140 };
3141 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::VerIterator const &Ver,
3142 std::string const &DestDir, std::string const &DestFilename) :
3143 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(Ver.SourcePkgName()), SrcVersion(Ver.SourceVerStr())
3144 {
3145 Desc.URI = URI(Ver);
3146 Init(DestDir, DestFilename);
3147 }
3148 // some parameters are char* here as they come likely from char* interfaces – which can also return NULL
3149 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::RlsFileIterator const &RlsFile,
3150 char const * const Component, char const * const SrcName, char const * const SrcVersion,
3151 const string &DestDir, const string &DestFilename) :
3152 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
3153 {
3154 Desc.URI = URI(RlsFile, Component, SrcName, SrcVersion);
3155 Init(DestDir, DestFilename);
3156 }
3157 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner,
3158 std::string const &URI, char const * const SrcName, char const * const SrcVersion,
3159 const string &DestDir, const string &DestFilename) :
3160 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
3161 {
3162 Desc.URI = URI;
3163 Init(DestDir, DestFilename);
3164 }
3165 void pkgAcqChangelog::Init(std::string const &DestDir, std::string const &DestFilename)
3166 {
3167 if (Desc.URI.empty())
3168 {
3169 Status = StatError;
3170 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3171 strprintf(ErrorText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
3172 // Let the error message print something sensible rather than "Failed to fetch /"
3173 if (DestFilename.empty())
3174 DestFile = SrcName + ".changelog";
3175 else
3176 DestFile = DestFilename;
3177 Desc.URI = "changelog:/" + DestFile;
3178 return;
3179 }
3180
3181 std::string DestFileName;
3182 if (DestFilename.empty())
3183 DestFileName = flCombine(DestFile, SrcName + ".changelog");
3184 else
3185 DestFileName = flCombine(DestFile, DestFilename);
3186
3187 std::string const SandboxUser = _config->Find("APT::Sandbox::User");
3188 std::string const systemTemp = GetTempDir(SandboxUser);
3189 char tmpname[1000];
3190 snprintf(tmpname, sizeof(tmpname), "%s/apt-changelog-XXXXXX", systemTemp.c_str());
3191 if (NULL == mkdtemp(tmpname))
3192 {
3193 _error->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName.c_str(), SrcVersion.c_str());
3194 Status = StatError;
3195 return;
3196 }
3197 TemporaryDirectory = tmpname;
3198
3199 ChangeOwnerAndPermissionOfFile("Item::QueueURI", TemporaryDirectory.c_str(),
3200 SandboxUser.c_str(), "root", 0700);
3201
3202 DestFile = flCombine(TemporaryDirectory, DestFileName);
3203 if (DestDir.empty() == false)
3204 {
3205 d->FinalFile = flCombine(DestDir, DestFileName);
3206 if (RealFileExists(d->FinalFile))
3207 {
3208 FileFd file1, file2;
3209 if (file1.Open(DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive) &&
3210 file2.Open(d->FinalFile, FileFd::ReadOnly) && CopyFile(file2, file1))
3211 {
3212 struct timeval times[2];
3213 times[0].tv_sec = times[1].tv_sec = file2.ModificationTime();
3214 times[0].tv_usec = times[1].tv_usec = 0;
3215 utimes(DestFile.c_str(), times);
3216 }
3217 }
3218 }
3219
3220 Desc.ShortDesc = "Changelog";
3221 strprintf(Desc.Description, "%s %s %s Changelog", URI::SiteOnly(Desc.URI).c_str(), SrcName.c_str(), SrcVersion.c_str());
3222 Desc.Owner = this;
3223 QueueURI(Desc);
3224 }
3225 /*}}}*/
3226 std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/
3227 {
3228 std::string const confOnline = "Acquire::Changelogs::AlwaysOnline";
3229 bool AlwaysOnline = _config->FindB(confOnline, false);
3230 if (AlwaysOnline == false)
3231 for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
3232 {
3233 pkgCache::PkgFileIterator const PF = VF.File();
3234 if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
3235 continue;
3236 pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
3237 if (RF->Origin != 0 && _config->FindB(confOnline + "::Origin::" + RF.Origin(), false))
3238 {
3239 AlwaysOnline = true;
3240 break;
3241 }
3242 }
3243 if (AlwaysOnline == false)
3244 {
3245 pkgCache::PkgIterator const Pkg = Ver.ParentPkg();
3246 if (Pkg->CurrentVer != 0 && Pkg.CurrentVer() == Ver)
3247 {
3248 std::string const basename = std::string("/usr/share/doc/") + Pkg.Name() + "/changelog";
3249 std::string const debianname = basename + ".Debian";
3250 if (FileExists(debianname))
3251 return "copy://" + debianname;
3252 else if (FileExists(debianname + ".gz"))
3253 return "gzip://" + debianname + ".gz";
3254 else if (FileExists(basename))
3255 return "copy://" + basename;
3256 else if (FileExists(basename + ".gz"))
3257 return "gzip://" + basename + ".gz";
3258 }
3259 }
3260
3261 char const * const SrcName = Ver.SourcePkgName();
3262 char const * const SrcVersion = Ver.SourceVerStr();
3263 // find the first source for this version which promises a changelog
3264 for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
3265 {
3266 pkgCache::PkgFileIterator const PF = VF.File();
3267 if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
3268 continue;
3269 pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
3270 std::string const uri = URI(RF, PF.Component(), SrcName, SrcVersion);
3271 if (uri.empty())
3272 continue;
3273 return uri;
3274 }
3275 return "";
3276 }
3277 std::string pkgAcqChangelog::URITemplate(pkgCache::RlsFileIterator const &Rls)
3278 {
3279 if (Rls.end() == true || (Rls->Label == 0 && Rls->Origin == 0))
3280 return "";
3281 std::string const serverConfig = "Acquire::Changelogs::URI";
3282 std::string server;
3283 #define APT_EMPTY_SERVER \
3284 if (server.empty() == false) \
3285 { \
3286 if (server != "no") \
3287 return server; \
3288 return ""; \
3289 }
3290 #define APT_CHECK_SERVER(X, Y) \
3291 if (Rls->X != 0) \
3292 { \
3293 std::string const specialServerConfig = serverConfig + "::" + Y + #X + "::" + Rls.X(); \
3294 server = _config->Find(specialServerConfig); \
3295 APT_EMPTY_SERVER \
3296 }
3297 // this way e.g. Debian-Security can fallback to Debian
3298 APT_CHECK_SERVER(Label, "Override::")
3299 APT_CHECK_SERVER(Origin, "Override::")
3300
3301 if (RealFileExists(Rls.FileName()))
3302 {
3303 _error->PushToStack();
3304 FileFd rf;
3305 /* This can be costly. A caller wanting to get millions of URIs might
3306 want to do this on its own once and use Override settings.
3307 We don't do this here as Origin/Label are not as unique as they
3308 should be so this could produce request order-dependent anomalies */
3309 if (OpenMaybeClearSignedFile(Rls.FileName(), rf) == true)
3310 {
3311 pkgTagFile TagFile(&rf, rf.Size());
3312 pkgTagSection Section;
3313 if (TagFile.Step(Section) == true)
3314 server = Section.FindS("Changelogs");
3315 }
3316 _error->RevertToStack();
3317 APT_EMPTY_SERVER
3318 }
3319
3320 APT_CHECK_SERVER(Label, "")
3321 APT_CHECK_SERVER(Origin, "")
3322 #undef APT_CHECK_SERVER
3323 #undef APT_EMPTY_SERVER
3324 return "";
3325 }
3326 std::string pkgAcqChangelog::URI(pkgCache::RlsFileIterator const &Rls,
3327 char const * const Component, char const * const SrcName,
3328 char const * const SrcVersion)
3329 {
3330 return URI(URITemplate(Rls), Component, SrcName, SrcVersion);
3331 }
3332 std::string pkgAcqChangelog::URI(std::string const &Template,
3333 char const * const Component, char const * const SrcName,
3334 char const * const SrcVersion)
3335 {
3336 if (Template.find("@CHANGEPATH@") == std::string::npos)
3337 return "";
3338
3339 // the path is: COMPONENT/SRC/SRCNAME/SRCNAME_SRCVER, e.g. main/a/apt/1.1 or contrib/liba/libapt/2.0
3340 std::string Src = SrcName;
3341 std::string path = APT::String::Startswith(SrcName, "lib") ? Src.substr(0, 4) : Src.substr(0,1);
3342 path.append("/").append(Src).append("/");
3343 path.append(Src).append("_").append(StripEpoch(SrcVersion));
3344 // we omit component for releases without one (= flat-style repositories)
3345 if (Component != NULL && strlen(Component) != 0)
3346 path = std::string(Component) + "/" + path;
3347
3348 return SubstVar(Template, "@CHANGEPATH@", path);
3349 }
3350 /*}}}*/
3351 // AcqChangelog::Failed - Failure handler /*{{{*/
3352 void pkgAcqChangelog::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
3353 {
3354 Item::Failed(Message,Cnf);
3355
3356 std::string errText;
3357 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3358 strprintf(errText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
3359
3360 // Error is probably something techy like 404 Not Found
3361 if (ErrorText.empty())
3362 ErrorText = errText;
3363 else
3364 ErrorText = errText + " (" + ErrorText + ")";
3365 }
3366 /*}}}*/
3367 // AcqChangelog::Done - Item downloaded OK /*{{{*/
3368 void pkgAcqChangelog::Done(string const &Message,HashStringList const &CalcHashes,
3369 pkgAcquire::MethodConfig const * const Cnf)
3370 {
3371 Item::Done(Message,CalcHashes,Cnf);
3372 if (d->FinalFile.empty() == false)
3373 {
3374 if (RemoveFile("pkgAcqChangelog::Done", d->FinalFile) == false ||
3375 Rename(DestFile, d->FinalFile) == false)
3376 Status = StatError;
3377 }
3378
3379 Complete = true;
3380 }
3381 /*}}}*/
3382 pkgAcqChangelog::~pkgAcqChangelog() /*{{{*/
3383 {
3384 if (TemporaryDirectory.empty() == false)
3385 {
3386 RemoveFile("~pkgAcqChangelog", DestFile);
3387 rmdir(TemporaryDirectory.c_str());
3388 }
3389 delete d;
3390 }
3391 /*}}}*/
3392
3393 // AcqFile::pkgAcqFile - Constructor /*{{{*/
3394 pkgAcqFile::pkgAcqFile(pkgAcquire * const Owner,string const &URI, HashStringList const &Hashes,
3395 unsigned long long const Size,string const &Dsc,string const &ShortDesc,
3396 const string &DestDir, const string &DestFilename,
3397 bool const IsIndexFile) :
3398 Item(Owner), d(NULL), IsIndexFile(IsIndexFile), ExpectedHashes(Hashes)
3399 {
3400 Retries = _config->FindI("Acquire::Retries",0);
3401
3402 if(!DestFilename.empty())
3403 DestFile = DestFilename;
3404 else if(!DestDir.empty())
3405 DestFile = DestDir + "/" + flNotDir(URI);
3406 else
3407 DestFile = flNotDir(URI);
3408
3409 // Create the item
3410 Desc.URI = URI;
3411 Desc.Description = Dsc;
3412 Desc.Owner = this;
3413
3414 // Set the short description to the archive component
3415 Desc.ShortDesc = ShortDesc;
3416
3417 // Get the transfer sizes
3418 FileSize = Size;
3419 struct stat Buf;
3420 if (stat(DestFile.c_str(),&Buf) == 0)
3421 {
3422 // Hmm, the partial file is too big, erase it
3423 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
3424 RemoveFile("pkgAcqFile", DestFile);
3425 else
3426 PartialSize = Buf.st_size;
3427 }
3428
3429 QueueURI(Desc);
3430 }
3431 /*}}}*/
3432 // AcqFile::Done - Item downloaded OK /*{{{*/
3433 void pkgAcqFile::Done(string const &Message,HashStringList const &CalcHashes,
3434 pkgAcquire::MethodConfig const * const Cnf)
3435 {
3436 Item::Done(Message,CalcHashes,Cnf);
3437
3438 std::string const FileName = LookupTag(Message,"Filename");
3439 Complete = true;
3440
3441 // The files timestamp matches
3442 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
3443 return;
3444
3445 // We have to copy it into place
3446 if (RealFileExists(DestFile.c_str()) == false)
3447 {
3448 Local = true;
3449 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
3450 Cnf->Removable == true)
3451 {
3452 Desc.URI = "copy:" + FileName;
3453 QueueURI(Desc);
3454 return;
3455 }
3456
3457 // Erase the file if it is a symlink so we can overwrite it
3458 struct stat St;
3459 if (lstat(DestFile.c_str(),&St) == 0)
3460 {
3461 if (S_ISLNK(St.st_mode) != 0)
3462 RemoveFile("pkgAcqFile::Done", DestFile);
3463 }
3464
3465 // Symlink the file
3466 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
3467 {
3468 _error->PushToStack();
3469 _error->Errno("pkgAcqFile::Done", "Symlinking file %s failed", DestFile.c_str());
3470 std::stringstream msg;
3471 _error->DumpErrors(msg, GlobalError::DEBUG, false);
3472 _error->RevertToStack();
3473 ErrorText = msg.str();
3474 Status = StatError;
3475 Complete = false;
3476 }
3477 }
3478 }
3479 /*}}}*/
3480 // AcqFile::Failed - Failure handler /*{{{*/
3481 // ---------------------------------------------------------------------
3482 /* Here we try other sources */
3483 void pkgAcqFile::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
3484 {
3485 Item::Failed(Message,Cnf);
3486
3487 // This is the retry counter
3488 if (Retries != 0 &&
3489 Cnf->LocalOnly == false &&
3490 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3491 {
3492 --Retries;
3493 QueueURI(Desc);
3494 Status = StatIdle;
3495 return;
3496 }
3497
3498 }
3499 /*}}}*/
3500 string pkgAcqFile::Custom600Headers() const /*{{{*/
3501 {
3502 if (IsIndexFile)
3503 return "\nIndex-File: true";
3504 return "";
3505 }
3506 /*}}}*/
3507 pkgAcqFile::~pkgAcqFile() {}