]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
ensure outdated files are dropped without lists-cleanup
[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 &Message) /*{{{*/
1300 {
1301 string::size_type pos;
1302
1303 // check for missing sigs (that where not fatal because otherwise we had
1304 // bombed earlier)
1305 string missingkeys;
1306 string msg = _("There is no public key available for the "
1307 "following key IDs:\n");
1308 pos = Message.find("NO_PUBKEY ");
1309 if (pos != std::string::npos)
1310 {
1311 string::size_type start = pos+strlen("NO_PUBKEY ");
1312 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1313 missingkeys += (Fingerprint);
1314 }
1315 if(!missingkeys.empty())
1316 _error->Warning("%s", (msg + missingkeys).c_str());
1317
1318 string Transformed = TransactionManager->MetaIndexParser->GetExpectedDist();
1319
1320 if (Transformed == "../project/experimental")
1321 {
1322 Transformed = "experimental";
1323 }
1324
1325 pos = Transformed.rfind('/');
1326 if (pos != string::npos)
1327 {
1328 Transformed = Transformed.substr(0, pos);
1329 }
1330
1331 if (Transformed == ".")
1332 {
1333 Transformed = "";
1334 }
1335
1336 if (TransactionManager->MetaIndexParser->GetValidUntil() > 0)
1337 {
1338 time_t const invalid_since = time(NULL) - TransactionManager->MetaIndexParser->GetValidUntil();
1339 if (invalid_since > 0)
1340 {
1341 std::string errmsg;
1342 strprintf(errmsg,
1343 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1344 // the time since then the file is invalid - formatted in the same way as in
1345 // the download progress display (e.g. 7d 3h 42min 1s)
1346 _("Release file for %s is expired (invalid since %s). "
1347 "Updates for this repository will not be applied."),
1348 Target.URI.c_str(), TimeToStr(invalid_since).c_str());
1349 if (ErrorText.empty())
1350 ErrorText = errmsg;
1351 return _error->Error("%s", errmsg.c_str());
1352 }
1353 }
1354
1355 /* Did we get a file older than what we have? This is a last minute IMS hit and doubles
1356 as a prevention of downgrading us to older (still valid) files */
1357 if (TransactionManager->IMSHit == false && TransactionManager->LastMetaIndexParser != NULL &&
1358 TransactionManager->LastMetaIndexParser->GetDate() > TransactionManager->MetaIndexParser->GetDate())
1359 {
1360 TransactionManager->IMSHit = true;
1361 RemoveFile("VerifyVendor", DestFile);
1362 PartialFile = DestFile = GetFinalFilename();
1363 // load the 'old' file in the 'new' one instead of flipping pointers as
1364 // the new one isn't owned by us, while the old one is so cleanup would be confused.
1365 TransactionManager->MetaIndexParser->swapLoad(TransactionManager->LastMetaIndexParser);
1366 delete TransactionManager->LastMetaIndexParser;
1367 TransactionManager->LastMetaIndexParser = NULL;
1368 }
1369
1370 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1371 {
1372 std::cerr << "Got Codename: " << TransactionManager->MetaIndexParser->GetCodename() << std::endl;
1373 std::cerr << "Expecting Dist: " << TransactionManager->MetaIndexParser->GetExpectedDist() << std::endl;
1374 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1375 }
1376
1377 if (TransactionManager->MetaIndexParser->CheckDist(Transformed) == false)
1378 {
1379 // This might become fatal one day
1380 // Status = StatAuthError;
1381 // ErrorText = "Conflicting distribution; expected "
1382 // + MetaIndexParser->GetExpectedDist() + " but got "
1383 // + MetaIndexParser->GetCodename();
1384 // return false;
1385 if (!Transformed.empty())
1386 {
1387 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1388 Desc.Description.c_str(),
1389 Transformed.c_str(),
1390 TransactionManager->MetaIndexParser->GetCodename().c_str());
1391 }
1392 }
1393
1394 return true;
1395 }
1396 /*}}}*/
1397 pkgAcqMetaBase::~pkgAcqMetaBase()
1398 {
1399 }
1400
1401 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire * const Owner, /*{{{*/
1402 IndexTarget const &ClearsignedTarget,
1403 IndexTarget const &DetachedDataTarget, IndexTarget const &DetachedSigTarget,
1404 std::vector<IndexTarget> const &IndexTargets,
1405 metaIndex * const MetaIndexParser) :
1406 pkgAcqMetaIndex(Owner, this, ClearsignedTarget, DetachedSigTarget, IndexTargets),
1407 d(NULL), ClearsignedTarget(ClearsignedTarget),
1408 DetachedDataTarget(DetachedDataTarget),
1409 MetaIndexParser(MetaIndexParser), LastMetaIndexParser(NULL)
1410 {
1411 // index targets + (worst case:) Release/Release.gpg
1412 ExpectedAdditionalItems = IndexTargets.size() + 2;
1413 TransactionManager->Add(this);
1414 }
1415 /*}}}*/
1416 pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
1417 {
1418 if (LastMetaIndexParser != NULL)
1419 delete LastMetaIndexParser;
1420 }
1421 /*}}}*/
1422 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1423 string pkgAcqMetaClearSig::Custom600Headers() const
1424 {
1425 string Header = pkgAcqMetaBase::Custom600Headers();
1426 Header += "\nFail-Ignore: true";
1427 std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
1428 if (key.empty() == false)
1429 Header += "\nSigned-By: " + key;
1430
1431 return Header;
1432 }
1433 /*}}}*/
1434 void pkgAcqMetaClearSig::Finished() /*{{{*/
1435 {
1436 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1437 std::clog << "Finished: " << DestFile <<std::endl;
1438 if(TransactionManager != NULL && TransactionManager->State == TransactionStarted &&
1439 TransactionManager->TransactionHasError() == false)
1440 TransactionManager->CommitTransaction();
1441 }
1442 /*}}}*/
1443 bool pkgAcqMetaClearSig::VerifyDone(std::string const &Message, /*{{{*/
1444 pkgAcquire::MethodConfig const * const Cnf)
1445 {
1446 Item::VerifyDone(Message, Cnf);
1447
1448 if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
1449 return RenameOnError(NotClearsigned);
1450
1451 return true;
1452 }
1453 /*}}}*/
1454 // pkgAcqMetaClearSig::Done - We got a file /*{{{*/
1455 void pkgAcqMetaClearSig::Done(std::string const &Message,
1456 HashStringList const &Hashes,
1457 pkgAcquire::MethodConfig const * const Cnf)
1458 {
1459 Item::Done(Message, Hashes, Cnf);
1460
1461 if(AuthPass == false)
1462 {
1463 if(CheckDownloadDone(this, Message, Hashes) == true)
1464 QueueForSignatureVerify(this, DestFile, DestFile);
1465 return;
1466 }
1467 else if(CheckAuthDone(Message) == true)
1468 {
1469 if (TransactionManager->IMSHit == false)
1470 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1471 else if (RealFileExists(GetFinalFilename()) == false)
1472 {
1473 // We got an InRelease file IMSHit, but we haven't one, which means
1474 // we had a valid Release/Release.gpg combo stepping in, which we have
1475 // to 'acquire' now to ensure list cleanup isn't removing them
1476 new NoActionItem(Owner, DetachedDataTarget);
1477 new NoActionItem(Owner, DetachedSigTarget);
1478 }
1479 }
1480 }
1481 /*}}}*/
1482 void pkgAcqMetaClearSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf) /*{{{*/
1483 {
1484 Item::Failed(Message, Cnf);
1485
1486 // we failed, we will not get additional items from this method
1487 ExpectedAdditionalItems = 0;
1488
1489 if (AuthPass == false)
1490 {
1491 if (Status == StatAuthError || Status == StatTransientNetworkError)
1492 {
1493 // if we expected a ClearTextSignature (InRelease) but got a network
1494 // error or got a file, but it wasn't valid, we end up here (see VerifyDone).
1495 // As these is usually called by web-portals we do not try Release/Release.gpg
1496 // as this is gonna fail anyway and instead abort our try (LP#346386)
1497 TransactionManager->AbortTransaction();
1498 return;
1499 }
1500
1501 // Queue the 'old' InRelease file for removal if we try Release.gpg
1502 // as otherwise the file will stay around and gives a false-auth
1503 // impression (CVE-2012-0214)
1504 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
1505 Status = StatDone;
1506
1507 new pkgAcqMetaIndex(Owner, TransactionManager, DetachedDataTarget, DetachedSigTarget, IndexTargets);
1508 }
1509 else
1510 {
1511 if(CheckStopAuthentication(this, Message))
1512 return;
1513
1514 // No Release file was present, or verification failed, so fall
1515 // back to queueing Packages files without verification
1516 // only allow going further if the user explicitly wants it
1517 if(AllowInsecureRepositories(_("The repository '%s' is not signed."), ClearsignedTarget.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1518 {
1519 Status = StatDone;
1520
1521 /* InRelease files become Release files, otherwise
1522 * they would be considered as trusted later on */
1523 string const FinalRelease = GetFinalFileNameFromURI(DetachedDataTarget.URI);
1524 string const PartialRelease = GetPartialFileNameFromURI(DetachedDataTarget.URI);
1525 string const FinalReleasegpg = GetFinalFileNameFromURI(DetachedSigTarget.URI);
1526 string const FinalInRelease = GetFinalFilename();
1527 Rename(DestFile, PartialRelease);
1528 TransactionManager->TransactionStageCopy(this, PartialRelease, FinalRelease);
1529 LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
1530
1531 // we parse the indexes here because at this point the user wanted
1532 // a repository that may potentially harm him
1533 if (TransactionManager->MetaIndexParser->Load(PartialRelease, &ErrorText) == false || VerifyVendor(Message) == false)
1534 /* expired Release files are still a problem you need extra force for */;
1535 else
1536 QueueIndexes(true);
1537 }
1538 }
1539 }
1540 /*}}}*/
1541
1542 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire * const Owner, /*{{{*/
1543 pkgAcqMetaClearSig * const TransactionManager,
1544 IndexTarget const &DataTarget,
1545 IndexTarget const &DetachedSigTarget,
1546 vector<IndexTarget> const &IndexTargets) :
1547 pkgAcqMetaBase(Owner, TransactionManager, IndexTargets, DataTarget), d(NULL),
1548 DetachedSigTarget(DetachedSigTarget)
1549 {
1550 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1551 std::clog << "New pkgAcqMetaIndex with TransactionManager "
1552 << this->TransactionManager << std::endl;
1553
1554 DestFile = GetPartialFileNameFromURI(DataTarget.URI);
1555
1556 // Create the item
1557 Desc.Description = DataTarget.Description;
1558 Desc.Owner = this;
1559 Desc.ShortDesc = DataTarget.ShortDesc;
1560 Desc.URI = DataTarget.URI;
1561
1562 // we expect more item
1563 ExpectedAdditionalItems = IndexTargets.size();
1564 QueueURI(Desc);
1565 }
1566 /*}}}*/
1567 void pkgAcqMetaIndex::Done(string const &Message, /*{{{*/
1568 HashStringList const &Hashes,
1569 pkgAcquire::MethodConfig const * const Cfg)
1570 {
1571 Item::Done(Message,Hashes,Cfg);
1572
1573 if(CheckDownloadDone(this, Message, Hashes))
1574 {
1575 // we have a Release file, now download the Signature, all further
1576 // verify/queue for additional downloads will be done in the
1577 // pkgAcqMetaSig::Done() code
1578 new pkgAcqMetaSig(Owner, TransactionManager, DetachedSigTarget, this);
1579 }
1580 }
1581 /*}}}*/
1582 // pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
1583 void pkgAcqMetaIndex::Failed(string const &Message,
1584 pkgAcquire::MethodConfig const * const Cnf)
1585 {
1586 pkgAcquire::Item::Failed(Message, Cnf);
1587 Status = StatDone;
1588
1589 // No Release file was present so fall
1590 // back to queueing Packages files without verification
1591 // only allow going further if the user explicitly wants it
1592 if(AllowInsecureRepositories(_("The repository '%s' does not have a Release file."), Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1593 {
1594 // ensure old Release files are removed
1595 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
1596
1597 // queue without any kind of hashsum support
1598 QueueIndexes(false);
1599 }
1600 }
1601 /*}}}*/
1602 std::string pkgAcqMetaIndex::DescURI() const /*{{{*/
1603 {
1604 return Target.URI;
1605 }
1606 /*}}}*/
1607 pkgAcqMetaIndex::~pkgAcqMetaIndex() {}
1608
1609 // AcqMetaSig::AcqMetaSig - Constructor /*{{{*/
1610 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire * const Owner,
1611 pkgAcqMetaClearSig * const TransactionManager,
1612 IndexTarget const &Target,
1613 pkgAcqMetaIndex * const MetaIndex) :
1614 pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL), MetaIndex(MetaIndex)
1615 {
1616 DestFile = GetPartialFileNameFromURI(Target.URI);
1617
1618 // remove any partial downloaded sig-file in partial/.
1619 // it may confuse proxies and is too small to warrant a
1620 // partial download anyway
1621 RemoveFile("pkgAcqMetaSig", DestFile);
1622
1623 // set the TransactionManager
1624 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1625 std::clog << "New pkgAcqMetaSig with TransactionManager "
1626 << TransactionManager << std::endl;
1627
1628 // Create the item
1629 Desc.Description = Target.Description;
1630 Desc.Owner = this;
1631 Desc.ShortDesc = Target.ShortDesc;
1632 Desc.URI = Target.URI;
1633
1634 // If we got a hit for Release, we will get one for Release.gpg too (or obscure errors),
1635 // so we skip the download step and go instantly to verification
1636 if (TransactionManager->IMSHit == true && RealFileExists(GetFinalFilename()))
1637 {
1638 Complete = true;
1639 Status = StatDone;
1640 PartialFile = DestFile = GetFinalFilename();
1641 MetaIndexFileSignature = DestFile;
1642 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1643 }
1644 else
1645 QueueURI(Desc);
1646 }
1647 /*}}}*/
1648 pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1649 {
1650 }
1651 /*}}}*/
1652 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1653 std::string pkgAcqMetaSig::Custom600Headers() const
1654 {
1655 std::string Header = pkgAcqTransactionItem::Custom600Headers();
1656 std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
1657 if (key.empty() == false)
1658 Header += "\nSigned-By: " + key;
1659 return Header;
1660 }
1661 /*}}}*/
1662 // AcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
1663 void pkgAcqMetaSig::Done(string const &Message, HashStringList const &Hashes,
1664 pkgAcquire::MethodConfig const * const Cfg)
1665 {
1666 if (MetaIndexFileSignature.empty() == false)
1667 {
1668 DestFile = MetaIndexFileSignature;
1669 MetaIndexFileSignature.clear();
1670 }
1671 Item::Done(Message, Hashes, Cfg);
1672
1673 if(MetaIndex->AuthPass == false)
1674 {
1675 if(MetaIndex->CheckDownloadDone(this, Message, Hashes) == true)
1676 {
1677 // destfile will be modified to point to MetaIndexFile for the
1678 // gpgv method, so we need to save it here
1679 MetaIndexFileSignature = DestFile;
1680 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1681 }
1682 return;
1683 }
1684 else if(MetaIndex->CheckAuthDone(Message) == true)
1685 {
1686 if (TransactionManager->IMSHit == false)
1687 {
1688 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1689 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
1690 }
1691 }
1692 }
1693 /*}}}*/
1694 void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
1695 {
1696 Item::Failed(Message,Cnf);
1697
1698 // check if we need to fail at this point
1699 if (MetaIndex->AuthPass == true && MetaIndex->CheckStopAuthentication(this, Message))
1700 return;
1701
1702 string const FinalRelease = MetaIndex->GetFinalFilename();
1703 string const FinalReleasegpg = GetFinalFilename();
1704 string const FinalInRelease = TransactionManager->GetFinalFilename();
1705
1706 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
1707 {
1708 std::string downgrade_msg;
1709 strprintf(downgrade_msg, _("The repository '%s' is no longer signed."),
1710 MetaIndex->Target.Description.c_str());
1711 if(_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
1712 {
1713 // meh, the users wants to take risks (we still mark the packages
1714 // from this repository as unauthenticated)
1715 _error->Warning("%s", downgrade_msg.c_str());
1716 _error->Warning(_("This is normally not allowed, but the option "
1717 "Acquire::AllowDowngradeToInsecureRepositories was "
1718 "given to override it."));
1719 Status = StatDone;
1720 } else {
1721 MessageInsecureRepository(true, downgrade_msg);
1722 if (TransactionManager->IMSHit == false)
1723 Rename(MetaIndex->DestFile, MetaIndex->DestFile + ".FAILED");
1724 Item::Failed("Message: " + downgrade_msg, Cnf);
1725 TransactionManager->AbortTransaction();
1726 return;
1727 }
1728 }
1729
1730 // ensures that a Release.gpg file in the lists/ is removed by the transaction
1731 TransactionManager->TransactionStageRemoval(this, DestFile);
1732
1733 // only allow going further if the user explicitly wants it
1734 if (AllowInsecureRepositories(_("The repository '%s' is not signed."), MetaIndex->Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1735 {
1736 LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
1737
1738 // we parse the indexes here because at this point the user wanted
1739 // a repository that may potentially harm him
1740 bool const GoodLoad = TransactionManager->MetaIndexParser->Load(MetaIndex->DestFile, &ErrorText);
1741 if (MetaIndex->VerifyVendor(Message) == false)
1742 /* expired Release files are still a problem you need extra force for */;
1743 else
1744 MetaIndex->QueueIndexes(GoodLoad);
1745
1746 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
1747 }
1748
1749 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1750 if (Cnf->LocalOnly == true ||
1751 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1752 {
1753 // Ignore this
1754 Status = StatDone;
1755 }
1756 }
1757 /*}}}*/
1758
1759
1760 // AcqBaseIndex - Constructor /*{{{*/
1761 pkgAcqBaseIndex::pkgAcqBaseIndex(pkgAcquire * const Owner,
1762 pkgAcqMetaClearSig * const TransactionManager,
1763 IndexTarget const &Target)
1764 : pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL)
1765 {
1766 }
1767 /*}}}*/
1768 pkgAcqBaseIndex::~pkgAcqBaseIndex() {}
1769
1770 // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
1771 // ---------------------------------------------------------------------
1772 /* Get the DiffIndex file first and see if there are patches available
1773 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
1774 * patches. If anything goes wrong in that process, it will fall back to
1775 * the original packages file
1776 */
1777 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire * const Owner,
1778 pkgAcqMetaClearSig * const TransactionManager,
1779 IndexTarget const &Target)
1780 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), diffs(NULL)
1781 {
1782 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
1783
1784 Desc.Owner = this;
1785 Desc.Description = GetDiffIndexFileName(Target.Description);
1786 Desc.ShortDesc = Target.ShortDesc;
1787 Desc.URI = GetDiffIndexURI(Target);
1788
1789 DestFile = GetPartialFileNameFromURI(Desc.URI);
1790
1791 if(Debug)
1792 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
1793
1794 QueueURI(Desc);
1795 }
1796 /*}}}*/
1797 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
1798 // ---------------------------------------------------------------------
1799 /* The only header we use is the last-modified header. */
1800 string pkgAcqDiffIndex::Custom600Headers() const
1801 {
1802 if (TransactionManager->LastMetaIndexParser != NULL)
1803 return "\nIndex-File: true";
1804
1805 string const Final = GetFinalFilename();
1806
1807 if(Debug)
1808 std::clog << "Custom600Header-IMS: " << Final << std::endl;
1809
1810 struct stat Buf;
1811 if (stat(Final.c_str(),&Buf) != 0)
1812 return "\nIndex-File: true";
1813
1814 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1815 }
1816 /*}}}*/
1817 void pkgAcqDiffIndex::QueueOnIMSHit() const /*{{{*/
1818 {
1819 // list cleanup needs to know that this file as well as the already
1820 // present index is ours, so we create an empty diff to save it for us
1821 new pkgAcqIndexDiffs(Owner, TransactionManager, Target);
1822 }
1823 /*}}}*/
1824 bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/
1825 {
1826 // failing here is fine: our caller will take care of trying to
1827 // get the complete file if patching fails
1828 if(Debug)
1829 std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
1830 << std::endl;
1831
1832 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
1833 pkgTagFile TF(&Fd);
1834 if (Fd.IsOpen() == false || Fd.Failed())
1835 return false;
1836
1837 pkgTagSection Tags;
1838 if(unlikely(TF.Step(Tags) == false))
1839 return false;
1840
1841 HashStringList ServerHashes;
1842 unsigned long long ServerSize = 0;
1843
1844 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1845 {
1846 std::string tagname = *type;
1847 tagname.append("-Current");
1848 std::string const tmp = Tags.FindS(tagname.c_str());
1849 if (tmp.empty() == true)
1850 continue;
1851
1852 string hash;
1853 unsigned long long size;
1854 std::stringstream ss(tmp);
1855 ss >> hash >> size;
1856 if (unlikely(hash.empty() == true))
1857 continue;
1858 if (unlikely(ServerSize != 0 && ServerSize != size))
1859 continue;
1860 ServerHashes.push_back(HashString(*type, hash));
1861 ServerSize = size;
1862 }
1863
1864 if (ServerHashes.usable() == false)
1865 {
1866 if (Debug == true)
1867 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Did not find a good hashsum in the index" << std::endl;
1868 return false;
1869 }
1870
1871 std::string const CurrentPackagesFile = GetFinalFileNameFromURI(Target.URI);
1872 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
1873 if (TargetFileHashes.usable() == false || ServerHashes != TargetFileHashes)
1874 {
1875 if (Debug == true)
1876 {
1877 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl;
1878 printHashSumComparison(CurrentPackagesFile, ServerHashes, TargetFileHashes);
1879 }
1880 return false;
1881 }
1882
1883 HashStringList LocalHashes;
1884 // try avoiding calculating the hash here as this is costly
1885 if (TransactionManager->LastMetaIndexParser != NULL)
1886 LocalHashes = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target.MetaKey);
1887 if (LocalHashes.usable() == false)
1888 {
1889 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly, FileFd::Auto);
1890 Hashes LocalHashesCalc(ServerHashes);
1891 LocalHashesCalc.AddFD(fd);
1892 LocalHashes = LocalHashesCalc.GetHashStringList();
1893 }
1894
1895 if (ServerHashes == LocalHashes)
1896 {
1897 // we have the same sha1 as the server so we are done here
1898 if(Debug)
1899 std::clog << "pkgAcqDiffIndex: Package file " << CurrentPackagesFile << " is up-to-date" << std::endl;
1900 QueueOnIMSHit();
1901 return true;
1902 }
1903
1904 if(Debug)
1905 std::clog << "Server-Current: " << ServerHashes.find(NULL)->toStr() << " and we start at "
1906 << CurrentPackagesFile << " " << LocalHashes.FileSize() << " " << LocalHashes.find(NULL)->toStr() << std::endl;
1907
1908 // historically, older hashes have more info than newer ones, so start
1909 // collecting with older ones first to avoid implementing complicated
1910 // information merging techniques… a failure is after all always
1911 // recoverable with a complete file and hashes aren't changed that often.
1912 std::vector<char const *> types;
1913 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1914 types.push_back(*type);
1915
1916 // parse all of (provided) history
1917 vector<DiffInfo> available_patches;
1918 bool firstAcceptedHashes = true;
1919 for (auto type = types.crbegin(); type != types.crend(); ++type)
1920 {
1921 if (LocalHashes.find(*type) == NULL)
1922 continue;
1923
1924 std::string tagname = *type;
1925 tagname.append("-History");
1926 std::string const tmp = Tags.FindS(tagname.c_str());
1927 if (tmp.empty() == true)
1928 continue;
1929
1930 string hash, filename;
1931 unsigned long long size;
1932 std::stringstream ss(tmp);
1933
1934 while (ss >> hash >> size >> filename)
1935 {
1936 if (unlikely(hash.empty() == true || filename.empty() == true))
1937 continue;
1938
1939 // see if we have a record for this file already
1940 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1941 for (; cur != available_patches.end(); ++cur)
1942 {
1943 if (cur->file != filename)
1944 continue;
1945 cur->result_hashes.push_back(HashString(*type, hash));
1946 break;
1947 }
1948 if (cur != available_patches.end())
1949 continue;
1950 if (firstAcceptedHashes == true)
1951 {
1952 DiffInfo next;
1953 next.file = filename;
1954 next.result_hashes.push_back(HashString(*type, hash));
1955 next.result_hashes.FileSize(size);
1956 available_patches.push_back(next);
1957 }
1958 else
1959 {
1960 if (Debug == true)
1961 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
1962 << " wasn't in the list for the first parsed hash! (history)" << std::endl;
1963 break;
1964 }
1965 }
1966 firstAcceptedHashes = false;
1967 }
1968
1969 if (unlikely(available_patches.empty() == true))
1970 {
1971 if (Debug)
1972 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
1973 << "Couldn't find any patches for the patch series." << std::endl;
1974 return false;
1975 }
1976
1977 for (auto type = types.crbegin(); type != types.crend(); ++type)
1978 {
1979 if (LocalHashes.find(*type) == NULL)
1980 continue;
1981
1982 std::string tagname = *type;
1983 tagname.append("-Patches");
1984 std::string const tmp = Tags.FindS(tagname.c_str());
1985 if (tmp.empty() == true)
1986 continue;
1987
1988 string hash, filename;
1989 unsigned long long size;
1990 std::stringstream ss(tmp);
1991
1992 while (ss >> hash >> size >> filename)
1993 {
1994 if (unlikely(hash.empty() == true || filename.empty() == true))
1995 continue;
1996
1997 // see if we have a record for this file already
1998 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1999 for (; cur != available_patches.end(); ++cur)
2000 {
2001 if (cur->file != filename)
2002 continue;
2003 if (cur->patch_hashes.empty())
2004 cur->patch_hashes.FileSize(size);
2005 cur->patch_hashes.push_back(HashString(*type, hash));
2006 break;
2007 }
2008 if (cur != available_patches.end())
2009 continue;
2010 if (Debug == true)
2011 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
2012 << " wasn't in the list for the first parsed hash! (patches)" << std::endl;
2013 break;
2014 }
2015 }
2016
2017 for (auto type = types.crbegin(); type != types.crend(); ++type)
2018 {
2019 std::string tagname = *type;
2020 tagname.append("-Download");
2021 std::string const tmp = Tags.FindS(tagname.c_str());
2022 if (tmp.empty() == true)
2023 continue;
2024
2025 string hash, filename;
2026 unsigned long long size;
2027 std::stringstream ss(tmp);
2028
2029 // FIXME: all of pdiff supports only .gz compressed patches
2030 while (ss >> hash >> size >> filename)
2031 {
2032 if (unlikely(hash.empty() == true || filename.empty() == true))
2033 continue;
2034 if (unlikely(APT::String::Endswith(filename, ".gz") == false))
2035 continue;
2036 filename.erase(filename.length() - 3);
2037
2038 // see if we have a record for this file already
2039 std::vector<DiffInfo>::iterator cur = available_patches.begin();
2040 for (; cur != available_patches.end(); ++cur)
2041 {
2042 if (cur->file != filename)
2043 continue;
2044 if (cur->download_hashes.empty())
2045 cur->download_hashes.FileSize(size);
2046 cur->download_hashes.push_back(HashString(*type, hash));
2047 break;
2048 }
2049 if (cur != available_patches.end())
2050 continue;
2051 if (Debug == true)
2052 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
2053 << " wasn't in the list for the first parsed hash! (download)" << std::endl;
2054 break;
2055 }
2056 }
2057
2058
2059 bool foundStart = false;
2060 for (std::vector<DiffInfo>::iterator cur = available_patches.begin();
2061 cur != available_patches.end(); ++cur)
2062 {
2063 if (LocalHashes != cur->result_hashes)
2064 continue;
2065
2066 available_patches.erase(available_patches.begin(), cur);
2067 foundStart = true;
2068 break;
2069 }
2070
2071 if (foundStart == false || unlikely(available_patches.empty() == true))
2072 {
2073 if (Debug)
2074 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
2075 << "Couldn't find the start of the patch series." << std::endl;
2076 return false;
2077 }
2078
2079 for (auto const &patch: available_patches)
2080 if (patch.result_hashes.usable() == false ||
2081 patch.patch_hashes.usable() == false ||
2082 patch.download_hashes.usable() == false)
2083 {
2084 if (Debug)
2085 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": provides no usable hashes for " << patch.file
2086 << " so fallback to complete download" << std::endl;
2087 return false;
2088 }
2089
2090 // patching with too many files is rather slow compared to a fast download
2091 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
2092 if (fileLimit != 0 && fileLimit < available_patches.size())
2093 {
2094 if (Debug)
2095 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
2096 << ") so fallback to complete download" << std::endl;
2097 return false;
2098 }
2099
2100 // calculate the size of all patches we have to get
2101 unsigned short const sizeLimitPercent = _config->FindI("Acquire::PDiffs::SizeLimit", 100);
2102 if (sizeLimitPercent > 0 && TransactionManager->MetaIndexParser != nullptr)
2103 {
2104 unsigned long long downloadSize = std::accumulate(available_patches.begin(),
2105 available_patches.end(), 0llu, [](unsigned long long const T, DiffInfo const &I) {
2106 return T + I.download_hashes.FileSize();
2107 });
2108 if (downloadSize != 0)
2109 {
2110 unsigned long long downloadSizeIdx = 0;
2111 auto const types = VectorizeString(Target.Option(IndexTarget::COMPRESSIONTYPES), ' ');
2112 for (auto const &t : types)
2113 {
2114 std::string MetaKey = Target.MetaKey;
2115 if (t != "uncompressed")
2116 MetaKey += '.' + t;
2117 HashStringList const hsl = GetExpectedHashesFor(MetaKey);
2118 if (unlikely(hsl.usable() == false))
2119 continue;
2120 downloadSizeIdx = hsl.FileSize();
2121 break;
2122 }
2123 unsigned long long const sizeLimit = downloadSizeIdx * sizeLimitPercent;
2124 if ((sizeLimit/100) < downloadSize)
2125 {
2126 if (Debug)
2127 std::clog << "Need " << downloadSize << " compressed bytes (Limit is " << (sizeLimit/100) << ", "
2128 << "original is " << downloadSizeIdx << ") so fallback to complete download" << std::endl;
2129 return false;
2130 }
2131 }
2132 }
2133
2134 // we have something, queue the diffs
2135 string::size_type const last_space = Description.rfind(" ");
2136 if(last_space != string::npos)
2137 Description.erase(last_space, Description.size()-last_space);
2138
2139 /* decide if we should download patches one by one or in one go:
2140 The first is good if the server merges patches, but many don't so client
2141 based merging can be attempt in which case the second is better.
2142 "bad things" will happen if patches are merged on the server,
2143 but client side merging is attempt as well */
2144 bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
2145 if (pdiff_merge == true)
2146 {
2147 // reprepro adds this flag if it has merged patches on the server
2148 std::string const precedence = Tags.FindS("X-Patch-Precedence");
2149 pdiff_merge = (precedence != "merged");
2150 }
2151
2152 // clean the plate
2153 {
2154 std::string const Final = GetExistingFilename(CurrentPackagesFile);
2155 if (unlikely(Final.empty())) // because we wouldn't be called in such a case
2156 return false;
2157 std::string const PartialFile = GetPartialFileNameFromURI(Target.URI);
2158 if (FileExists(PartialFile) && RemoveFile("Bootstrap-linking", PartialFile) == false)
2159 {
2160 if (Debug)
2161 std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile
2162 << " by removing stale " << PartialFile << " failed!" << std::endl;
2163 return false;
2164 }
2165 for (auto const &ext : APT::Configuration::getCompressorExtensions())
2166 {
2167 std::string const Partial = PartialFile + ext;
2168 if (FileExists(Partial) && RemoveFile("Bootstrap-linking", Partial) == false)
2169 {
2170 if (Debug)
2171 std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile
2172 << " by removing stale " << Partial << " failed!" << std::endl;
2173 return false;
2174 }
2175 }
2176 std::string const Ext = Final.substr(CurrentPackagesFile.length());
2177 std::string const Partial = PartialFile + Ext;
2178 if (symlink(Final.c_str(), Partial.c_str()) != 0)
2179 {
2180 if (Debug)
2181 std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile
2182 << " by linking " << Final << " to " << Partial << " failed!" << std::endl;
2183 return false;
2184 }
2185 }
2186
2187 if (pdiff_merge == false)
2188 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
2189 else
2190 {
2191 diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
2192 for(size_t i = 0; i < available_patches.size(); ++i)
2193 (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, TransactionManager,
2194 Target,
2195 available_patches[i],
2196 diffs);
2197 }
2198
2199 Complete = false;
2200 Status = StatDone;
2201 Dequeue();
2202 return true;
2203 }
2204 /*}}}*/
2205 void pkgAcqDiffIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2206 {
2207 Item::Failed(Message,Cnf);
2208 Status = StatDone;
2209
2210 if(Debug)
2211 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
2212 << "Falling back to normal index file acquire" << std::endl;
2213
2214 new pkgAcqIndex(Owner, TransactionManager, Target);
2215 }
2216 /*}}}*/
2217 void pkgAcqDiffIndex::Done(string const &Message,HashStringList const &Hashes, /*{{{*/
2218 pkgAcquire::MethodConfig const * const Cnf)
2219 {
2220 if(Debug)
2221 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
2222
2223 Item::Done(Message, Hashes, Cnf);
2224
2225 string const FinalFile = GetFinalFilename();
2226 if(StringToBool(LookupTag(Message,"IMS-Hit"),false))
2227 DestFile = FinalFile;
2228
2229 if(ParseDiffIndex(DestFile) == false)
2230 {
2231 Failed("Message: Couldn't parse pdiff index", Cnf);
2232 // queue for final move - this should happen even if we fail
2233 // while parsing (e.g. on sizelimit) and download the complete file.
2234 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2235 return;
2236 }
2237
2238 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2239
2240 Complete = true;
2241 Status = StatDone;
2242 Dequeue();
2243
2244 return;
2245 }
2246 /*}}}*/
2247 pkgAcqDiffIndex::~pkgAcqDiffIndex()
2248 {
2249 if (diffs != NULL)
2250 delete diffs;
2251 }
2252
2253 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
2254 // ---------------------------------------------------------------------
2255 /* The package diff is added to the queue. one object is constructed
2256 * for each diff and the index
2257 */
2258 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire * const Owner,
2259 pkgAcqMetaClearSig * const TransactionManager,
2260 IndexTarget const &Target,
2261 vector<DiffInfo> const &diffs)
2262 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
2263 available_patches(diffs)
2264 {
2265 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2266
2267 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
2268
2269 Desc.Owner = this;
2270 Description = Target.Description;
2271 Desc.ShortDesc = Target.ShortDesc;
2272
2273 if(available_patches.empty() == true)
2274 {
2275 // we are done (yeah!), check hashes against the final file
2276 DestFile = GetKeepCompressedFileName(GetFinalFileNameFromURI(Target.URI), Target);
2277 Finish(true);
2278 }
2279 else
2280 {
2281 State = StateFetchDiff;
2282 QueueNextDiff();
2283 }
2284 }
2285 /*}}}*/
2286 void pkgAcqIndexDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2287 {
2288 Item::Failed(Message,Cnf);
2289 Status = StatDone;
2290
2291 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2292 if(Debug)
2293 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
2294 << "Falling back to normal index file acquire " << std::endl;
2295 RenameOnError(PDiffError);
2296 std::string const patchname = GetDiffsPatchFileName(DestFile);
2297 if (RealFileExists(patchname))
2298 Rename(patchname, patchname + ".FAILED");
2299 std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
2300 if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile))
2301 Rename(UnpatchedFile, UnpatchedFile + ".FAILED");
2302 new pkgAcqIndex(Owner, TransactionManager, Target);
2303 Finish();
2304 }
2305 /*}}}*/
2306 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
2307 void pkgAcqIndexDiffs::Finish(bool allDone)
2308 {
2309 if(Debug)
2310 std::clog << "pkgAcqIndexDiffs::Finish(): "
2311 << allDone << " "
2312 << Desc.URI << std::endl;
2313
2314 // we restore the original name, this is required, otherwise
2315 // the file will be cleaned
2316 if(allDone)
2317 {
2318 std::string const Final = GetKeepCompressedFileName(GetFinalFilename(), Target);
2319 TransactionManager->TransactionStageCopy(this, DestFile, Final);
2320
2321 // this is for the "real" finish
2322 Complete = true;
2323 Status = StatDone;
2324 Dequeue();
2325 if(Debug)
2326 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
2327 return;
2328 }
2329 else
2330 DestFile.clear();
2331
2332 if(Debug)
2333 std::clog << "Finishing: " << Desc.URI << std::endl;
2334 Complete = false;
2335 Status = StatDone;
2336 Dequeue();
2337 return;
2338 }
2339 /*}}}*/
2340 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
2341 {
2342 // calc sha1 of the just patched file
2343 std::string const PartialFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
2344 if(unlikely(PartialFile.empty()))
2345 {
2346 Failed("Message: The file " + GetPartialFileNameFromURI(Target.URI) + " isn't available", NULL);
2347 return false;
2348 }
2349
2350 FileFd fd(PartialFile, FileFd::ReadOnly, FileFd::Extension);
2351 Hashes LocalHashesCalc;
2352 LocalHashesCalc.AddFD(fd);
2353 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
2354
2355 if(Debug)
2356 std::clog << "QueueNextDiff: " << PartialFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
2357
2358 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
2359 if (unlikely(LocalHashes.usable() == false || TargetFileHashes.usable() == false))
2360 {
2361 Failed("Local/Expected hashes are not usable for " + PartialFile, NULL);
2362 return false;
2363 }
2364
2365 // final file reached before all patches are applied
2366 if(LocalHashes == TargetFileHashes)
2367 {
2368 Finish(true);
2369 return true;
2370 }
2371
2372 // remove all patches until the next matching patch is found
2373 // this requires the Index file to be ordered
2374 available_patches.erase(available_patches.begin(),
2375 std::find_if(available_patches.begin(), available_patches.end(), [&](DiffInfo const &I) {
2376 return I.result_hashes == LocalHashes;
2377 }));
2378
2379 // error checking and falling back if no patch was found
2380 if(available_patches.empty() == true)
2381 {
2382 Failed("No patches left to reach target for " + PartialFile, NULL);
2383 return false;
2384 }
2385
2386 // queue the right diff
2387 Desc.URI = Target.URI + ".diff/" + available_patches[0].file + ".gz";
2388 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
2389 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI + ".diff/" + available_patches[0].file), Target);
2390
2391 if(Debug)
2392 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
2393
2394 QueueURI(Desc);
2395
2396 return true;
2397 }
2398 /*}}}*/
2399 void pkgAcqIndexDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2400 pkgAcquire::MethodConfig const * const Cnf)
2401 {
2402 if (Debug)
2403 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
2404
2405 Item::Done(Message, Hashes, Cnf);
2406
2407 std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI);
2408 std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile);
2409 std::string const PatchFile = GetDiffsPatchFileName(UnpatchedFile);
2410 std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target);
2411
2412 switch (State)
2413 {
2414 // success in downloading a diff, enter ApplyDiff state
2415 case StateFetchDiff:
2416 Rename(DestFile, PatchFile);
2417 DestFile = GetKeepCompressedFileName(UncompressedUnpatchedFile + "-patched", Target);
2418 if(Debug)
2419 std::clog << "Sending to rred method: " << UnpatchedFile << std::endl;
2420 State = StateApplyDiff;
2421 Local = true;
2422 Desc.URI = "rred:" + UnpatchedFile;
2423 QueueURI(Desc);
2424 SetActiveSubprocess("rred");
2425 return;
2426 // success in download/apply a diff, queue next (if needed)
2427 case StateApplyDiff:
2428 // remove the just applied patch and base file
2429 available_patches.erase(available_patches.begin());
2430 RemoveFile("pkgAcqIndexDiffs::Done", PatchFile);
2431 RemoveFile("pkgAcqIndexDiffs::Done", UnpatchedFile);
2432 if(Debug)
2433 std::clog << "Moving patched file in place: " << std::endl
2434 << DestFile << " -> " << PatchedFile << std::endl;
2435 Rename(DestFile, PatchedFile);
2436
2437 // see if there is more to download
2438 if(available_patches.empty() == false)
2439 {
2440 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
2441 Finish();
2442 } else {
2443 DestFile = PatchedFile;
2444 Finish(true);
2445 }
2446 return;
2447 }
2448 }
2449 /*}}}*/
2450 std::string pkgAcqIndexDiffs::Custom600Headers() const /*{{{*/
2451 {
2452 if(State != StateApplyDiff)
2453 return pkgAcqBaseIndex::Custom600Headers();
2454 std::ostringstream patchhashes;
2455 HashStringList const ExpectedHashes = available_patches[0].patch_hashes;
2456 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
2457 patchhashes << "\nPatch-0-" << hs->HashType() << "-Hash: " << hs->HashValue();
2458 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2459 return patchhashes.str();
2460 }
2461 /*}}}*/
2462 pkgAcqIndexDiffs::~pkgAcqIndexDiffs() {}
2463
2464 // AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
2465 pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire * const Owner,
2466 pkgAcqMetaClearSig * const TransactionManager,
2467 IndexTarget const &Target,
2468 DiffInfo const &patch,
2469 std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
2470 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
2471 patch(patch), allPatches(allPatches), State(StateFetchDiff)
2472 {
2473 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
2474
2475 Desc.Owner = this;
2476 Description = Target.Description;
2477 Desc.ShortDesc = Target.ShortDesc;
2478 Desc.URI = Target.URI + ".diff/" + patch.file + ".gz";
2479 Desc.Description = Description + " " + patch.file + ".pdiff";
2480 DestFile = GetPartialFileNameFromURI(Desc.URI);
2481
2482 if(Debug)
2483 std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
2484
2485 QueueURI(Desc);
2486 }
2487 /*}}}*/
2488 void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2489 {
2490 if(Debug)
2491 std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
2492
2493 Item::Failed(Message,Cnf);
2494 Status = StatDone;
2495
2496 // check if we are the first to fail, otherwise we are done here
2497 State = StateDoneDiff;
2498 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2499 I != allPatches->end(); ++I)
2500 if ((*I)->State == StateErrorDiff)
2501 {
2502 State = StateErrorDiff;
2503 return;
2504 }
2505
2506 // first failure means we should fallback
2507 State = StateErrorDiff;
2508 if (Debug)
2509 std::clog << "Falling back to normal index file acquire" << std::endl;
2510 RenameOnError(PDiffError);
2511 if (RealFileExists(DestFile))
2512 Rename(DestFile, DestFile + ".FAILED");
2513 std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
2514 if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile))
2515 Rename(UnpatchedFile, UnpatchedFile + ".FAILED");
2516 DestFile.clear();
2517 new pkgAcqIndex(Owner, TransactionManager, Target);
2518 }
2519 /*}}}*/
2520 void pkgAcqIndexMergeDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2521 pkgAcquire::MethodConfig const * const Cnf)
2522 {
2523 if(Debug)
2524 std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
2525
2526 Item::Done(Message, Hashes, Cnf);
2527
2528 if (std::any_of(allPatches->begin(), allPatches->end(),
2529 [](pkgAcqIndexMergeDiffs const * const P) { return P->State == StateErrorDiff; }))
2530 {
2531 if(Debug)
2532 std::clog << "Another patch failed already, no point in processing this one." << std::endl;
2533 State = StateErrorDiff;
2534 return;
2535 }
2536
2537 std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI);
2538 std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile);
2539 if (UnpatchedFile.empty())
2540 {
2541 _error->Fatal("Unpatched file %s doesn't exist (anymore)!", UncompressedUnpatchedFile.c_str());
2542 State = StateErrorDiff;
2543 return;
2544 }
2545 std::string const PatchFile = GetMergeDiffsPatchFileName(UnpatchedFile, patch.file);
2546 std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target);
2547
2548 switch (State)
2549 {
2550 case StateFetchDiff:
2551 Rename(DestFile, PatchFile);
2552
2553 // check if this is the last completed diff
2554 State = StateDoneDiff;
2555 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2556 I != allPatches->end(); ++I)
2557 if ((*I)->State != StateDoneDiff)
2558 {
2559 if(Debug)
2560 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
2561 return;
2562 }
2563 // this is the last completed diff, so we are ready to apply now
2564 DestFile = GetKeepCompressedFileName(UncompressedUnpatchedFile + "-patched", Target);
2565 if(Debug)
2566 std::clog << "Sending to rred method: " << UnpatchedFile << std::endl;
2567 State = StateApplyDiff;
2568 Local = true;
2569 Desc.URI = "rred:" + UnpatchedFile;
2570 QueueURI(Desc);
2571 SetActiveSubprocess("rred");
2572 return;
2573 case StateApplyDiff:
2574 // success in download & apply all diffs, finialize and clean up
2575 if(Debug)
2576 std::clog << "Queue patched file in place: " << std::endl
2577 << DestFile << " -> " << PatchedFile << std::endl;
2578
2579 // queue for copy by the transaction manager
2580 TransactionManager->TransactionStageCopy(this, DestFile, GetKeepCompressedFileName(GetFinalFilename(), Target));
2581
2582 // ensure the ed's are gone regardless of list-cleanup
2583 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2584 I != allPatches->end(); ++I)
2585 RemoveFile("pkgAcqIndexMergeDiffs::Done", GetMergeDiffsPatchFileName(UnpatchedFile, (*I)->patch.file));
2586 RemoveFile("pkgAcqIndexMergeDiffs::Done", UnpatchedFile);
2587
2588 // all set and done
2589 Complete = true;
2590 if(Debug)
2591 std::clog << "allDone: " << DestFile << "\n" << std::endl;
2592 return;
2593 case StateDoneDiff: _error->Fatal("Done called for %s which is in an invalid Done state", PatchFile.c_str()); break;
2594 case StateErrorDiff: _error->Fatal("Done called for %s which is in an invalid Error state", PatchFile.c_str()); break;
2595 }
2596 }
2597 /*}}}*/
2598 std::string pkgAcqIndexMergeDiffs::Custom600Headers() const /*{{{*/
2599 {
2600 if(State != StateApplyDiff)
2601 return pkgAcqBaseIndex::Custom600Headers();
2602 std::ostringstream patchhashes;
2603 unsigned int seen_patches = 0;
2604 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2605 I != allPatches->end(); ++I)
2606 {
2607 HashStringList const ExpectedHashes = (*I)->patch.patch_hashes;
2608 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
2609 patchhashes << "\nPatch-" << seen_patches << "-" << hs->HashType() << "-Hash: " << hs->HashValue();
2610 ++seen_patches;
2611 }
2612 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2613 return patchhashes.str();
2614 }
2615 /*}}}*/
2616 pkgAcqIndexMergeDiffs::~pkgAcqIndexMergeDiffs() {}
2617
2618 // AcqIndex::AcqIndex - Constructor /*{{{*/
2619 pkgAcqIndex::pkgAcqIndex(pkgAcquire * const Owner,
2620 pkgAcqMetaClearSig * const TransactionManager,
2621 IndexTarget const &Target)
2622 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), Stage(STAGE_DOWNLOAD),
2623 CompressionExtensions(Target.Option(IndexTarget::COMPRESSIONTYPES))
2624 {
2625 Init(Target.URI, Target.Description, Target.ShortDesc);
2626
2627 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
2628 std::clog << "New pkgIndex with TransactionManager "
2629 << TransactionManager << std::endl;
2630 }
2631 /*}}}*/
2632 // AcqIndex::Init - defered Constructor /*{{{*/
2633 static void NextCompressionExtension(std::string &CurrentCompressionExtension, std::string &CompressionExtensions, bool const preview)
2634 {
2635 size_t const nextExt = CompressionExtensions.find(' ');
2636 if (nextExt == std::string::npos)
2637 {
2638 CurrentCompressionExtension = CompressionExtensions;
2639 if (preview == false)
2640 CompressionExtensions.clear();
2641 }
2642 else
2643 {
2644 CurrentCompressionExtension = CompressionExtensions.substr(0, nextExt);
2645 if (preview == false)
2646 CompressionExtensions = CompressionExtensions.substr(nextExt+1);
2647 }
2648 }
2649 void pkgAcqIndex::Init(string const &URI, string const &URIDesc,
2650 string const &ShortDesc)
2651 {
2652 Stage = STAGE_DOWNLOAD;
2653
2654 DestFile = GetPartialFileNameFromURI(URI);
2655 NextCompressionExtension(CurrentCompressionExtension, CompressionExtensions, false);
2656
2657 // store file size of the download to ensure the fetcher gives
2658 // accurate progress reporting
2659 FileSize = GetExpectedHashes().FileSize();
2660
2661 if (CurrentCompressionExtension == "uncompressed")
2662 {
2663 Desc.URI = URI;
2664 }
2665 else if (CurrentCompressionExtension == "by-hash")
2666 {
2667 NextCompressionExtension(CurrentCompressionExtension, CompressionExtensions, true);
2668 if(unlikely(TransactionManager->MetaIndexParser == NULL || CurrentCompressionExtension.empty()))
2669 return;
2670 if (CurrentCompressionExtension != "uncompressed")
2671 {
2672 Desc.URI = URI + '.' + CurrentCompressionExtension;
2673 DestFile = DestFile + '.' + CurrentCompressionExtension;
2674 }
2675
2676 HashStringList const Hashes = GetExpectedHashes();
2677 HashString const * const TargetHash = Hashes.find(NULL);
2678 if (unlikely(TargetHash == nullptr))
2679 return;
2680 std::string const ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
2681 size_t const trailing_slash = Desc.URI.find_last_of("/");
2682 if (unlikely(trailing_slash == std::string::npos))
2683 return;
2684 Desc.URI = Desc.URI.replace(
2685 trailing_slash,
2686 Desc.URI.substr(trailing_slash+1).size()+1,
2687 ByHash);
2688 }
2689 else if (unlikely(CurrentCompressionExtension.empty()))
2690 return;
2691 else
2692 {
2693 Desc.URI = URI + '.' + CurrentCompressionExtension;
2694 DestFile = DestFile + '.' + CurrentCompressionExtension;
2695 }
2696
2697
2698 Desc.Description = URIDesc;
2699 Desc.Owner = this;
2700 Desc.ShortDesc = ShortDesc;
2701
2702 QueueURI(Desc);
2703 }
2704 /*}}}*/
2705 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2706 // ---------------------------------------------------------------------
2707 /* The only header we use is the last-modified header. */
2708 string pkgAcqIndex::Custom600Headers() const
2709 {
2710
2711 string msg = "\nIndex-File: true";
2712
2713 if (TransactionManager->LastMetaIndexParser == NULL)
2714 {
2715 std::string const Final = GetFinalFilename();
2716
2717 struct stat Buf;
2718 if (stat(Final.c_str(),&Buf) == 0)
2719 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
2720 }
2721
2722 if(Target.IsOptional)
2723 msg += "\nFail-Ignore: true";
2724
2725 return msg;
2726 }
2727 /*}}}*/
2728 // AcqIndex::Failed - getting the indexfile failed /*{{{*/
2729 void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
2730 {
2731 Item::Failed(Message,Cnf);
2732
2733 // authorisation matches will not be fixed by other compression types
2734 if (Status != StatAuthError)
2735 {
2736 if (CompressionExtensions.empty() == false)
2737 {
2738 Init(Target.URI, Desc.Description, Desc.ShortDesc);
2739 Status = StatIdle;
2740 return;
2741 }
2742 }
2743
2744 if(Target.IsOptional && GetExpectedHashes().empty() && Stage == STAGE_DOWNLOAD)
2745 Status = StatDone;
2746 else
2747 TransactionManager->AbortTransaction();
2748 }
2749 /*}}}*/
2750 // AcqIndex::Done - Finished a fetch /*{{{*/
2751 // ---------------------------------------------------------------------
2752 /* This goes through a number of states.. On the initial fetch the
2753 method could possibly return an alternate filename which points
2754 to the uncompressed version of the file. If this is so the file
2755 is copied into the partial directory. In all other cases the file
2756 is decompressed with a compressed uri. */
2757 void pkgAcqIndex::Done(string const &Message,
2758 HashStringList const &Hashes,
2759 pkgAcquire::MethodConfig const * const Cfg)
2760 {
2761 Item::Done(Message,Hashes,Cfg);
2762
2763 switch(Stage)
2764 {
2765 case STAGE_DOWNLOAD:
2766 StageDownloadDone(Message);
2767 break;
2768 case STAGE_DECOMPRESS_AND_VERIFY:
2769 StageDecompressDone();
2770 break;
2771 }
2772 }
2773 /*}}}*/
2774 // AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
2775 void pkgAcqIndex::StageDownloadDone(string const &Message)
2776 {
2777 Local = true;
2778 Complete = true;
2779
2780 std::string const AltFilename = LookupTag(Message,"Alt-Filename");
2781 std::string Filename = LookupTag(Message,"Filename");
2782
2783 // we need to verify the file against the current Release file again
2784 // on if-modfied-since hit to avoid a stale attack against us
2785 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2786 {
2787 // copy FinalFile into partial/ so that we check the hash again
2788 string const FinalFile = GetExistingFilename(GetFinalFileNameFromURI(Target.URI));
2789 if (symlink(FinalFile.c_str(), DestFile.c_str()) != 0)
2790 _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking final file %s back to %s failed", FinalFile.c_str(), DestFile.c_str());
2791 else
2792 {
2793 EraseFileName = DestFile;
2794 Filename = DestFile;
2795 }
2796 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2797 Desc.URI = "store:" + Filename;
2798 QueueURI(Desc);
2799 SetActiveSubprocess(::URI(Desc.URI).Access);
2800 return;
2801 }
2802 // methods like file:// give us an alternative (uncompressed) file
2803 else if (Target.KeepCompressed == false && AltFilename.empty() == false)
2804 {
2805 Filename = AltFilename;
2806 EraseFileName.clear();
2807 }
2808 // Methods like e.g. "file:" will give us a (compressed) FileName that is
2809 // not the "DestFile" we set, in this case we uncompress from the local file
2810 else if (Filename != DestFile && RealFileExists(DestFile) == false)
2811 {
2812 // symlinking ensures that the filename can be used for compression detection
2813 // that is e.g. needed for by-hash which has no extension over file
2814 if (symlink(Filename.c_str(),DestFile.c_str()) != 0)
2815 _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking file %s to %s failed", Filename.c_str(), DestFile.c_str());
2816 else
2817 {
2818 EraseFileName = DestFile;
2819 Filename = DestFile;
2820 }
2821 }
2822
2823 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2824 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2825 if (Filename != DestFile && flExtension(Filename) == flExtension(DestFile))
2826 Desc.URI = "copy:" + Filename;
2827 else
2828 Desc.URI = "store:" + Filename;
2829 if (DestFile == Filename)
2830 {
2831 if (CurrentCompressionExtension == "uncompressed")
2832 return StageDecompressDone();
2833 DestFile = "/dev/null";
2834 }
2835
2836 if (EraseFileName.empty() && Filename != AltFilename)
2837 EraseFileName = Filename;
2838
2839 // queue uri for the next stage
2840 QueueURI(Desc);
2841 SetActiveSubprocess(::URI(Desc.URI).Access);
2842 }
2843 /*}}}*/
2844 // AcqIndex::StageDecompressDone - Final verification /*{{{*/
2845 void pkgAcqIndex::StageDecompressDone()
2846 {
2847 if (DestFile == "/dev/null")
2848 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2849
2850 // Done, queue for rename on transaction finished
2851 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
2852 }
2853 /*}}}*/
2854 pkgAcqIndex::~pkgAcqIndex() {}
2855
2856
2857 // AcqArchive::AcqArchive - Constructor /*{{{*/
2858 // ---------------------------------------------------------------------
2859 /* This just sets up the initial fetch environment and queues the first
2860 possibilitiy */
2861 pkgAcqArchive::pkgAcqArchive(pkgAcquire * const Owner,pkgSourceList * const Sources,
2862 pkgRecords * const Recs,pkgCache::VerIterator const &Version,
2863 string &StoreFilename) :
2864 Item(Owner), d(NULL), LocalSource(false), Version(Version), Sources(Sources), Recs(Recs),
2865 StoreFilename(StoreFilename), Vf(Version.FileList()),
2866 Trusted(false)
2867 {
2868 Retries = _config->FindI("Acquire::Retries",0);
2869
2870 if (Version.Arch() == 0)
2871 {
2872 _error->Error(_("I wasn't able to locate a file for the %s package. "
2873 "This might mean you need to manually fix this package. "
2874 "(due to missing arch)"),
2875 Version.ParentPkg().FullName().c_str());
2876 return;
2877 }
2878
2879 /* We need to find a filename to determine the extension. We make the
2880 assumption here that all the available sources for this version share
2881 the same extension.. */
2882 // Skip not source sources, they do not have file fields.
2883 for (; Vf.end() == false; ++Vf)
2884 {
2885 if (Vf.File().Flagged(pkgCache::Flag::NotSource))
2886 continue;
2887 break;
2888 }
2889
2890 // Does not really matter here.. we are going to fail out below
2891 if (Vf.end() != true)
2892 {
2893 // If this fails to get a file name we will bomb out below.
2894 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2895 if (_error->PendingError() == true)
2896 return;
2897
2898 // Generate the final file name as: package_version_arch.foo
2899 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2900 QuoteString(Version.VerStr(),"_:") + '_' +
2901 QuoteString(Version.Arch(),"_:.") +
2902 "." + flExtension(Parse.FileName());
2903 }
2904
2905 // check if we have one trusted source for the package. if so, switch
2906 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2907 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2908 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2909 bool seenUntrusted = false;
2910 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
2911 {
2912 pkgIndexFile *Index;
2913 if (Sources->FindIndex(i.File(),Index) == false)
2914 continue;
2915
2916 if (debugAuth == true)
2917 std::cerr << "Checking index: " << Index->Describe()
2918 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2919
2920 if (Index->IsTrusted() == true)
2921 {
2922 Trusted = true;
2923 if (allowUnauth == false)
2924 break;
2925 }
2926 else
2927 seenUntrusted = true;
2928 }
2929
2930 // "allow-unauthenticated" restores apts old fetching behaviour
2931 // that means that e.g. unauthenticated file:// uris are higher
2932 // priority than authenticated http:// uris
2933 if (allowUnauth == true && seenUntrusted == true)
2934 Trusted = false;
2935
2936 // Select a source
2937 if (QueueNext() == false && _error->PendingError() == false)
2938 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2939 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2940 }
2941 /*}}}*/
2942 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
2943 // ---------------------------------------------------------------------
2944 /* This queues the next available file version for download. It checks if
2945 the archive is already available in the cache and stashs the MD5 for
2946 checking later. */
2947 bool pkgAcqArchive::QueueNext()
2948 {
2949 for (; Vf.end() == false; ++Vf)
2950 {
2951 pkgCache::PkgFileIterator const PkgF = Vf.File();
2952 // Ignore not source sources
2953 if (PkgF.Flagged(pkgCache::Flag::NotSource))
2954 continue;
2955
2956 // Try to cross match against the source list
2957 pkgIndexFile *Index;
2958 if (Sources->FindIndex(PkgF, Index) == false)
2959 continue;
2960 LocalSource = PkgF.Flagged(pkgCache::Flag::LocalSource);
2961
2962 // only try to get a trusted package from another source if that source
2963 // is also trusted
2964 if(Trusted && !Index->IsTrusted())
2965 continue;
2966
2967 // Grab the text package record
2968 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2969 if (_error->PendingError() == true)
2970 return false;
2971
2972 string PkgFile = Parse.FileName();
2973 ExpectedHashes = Parse.Hashes();
2974
2975 if (PkgFile.empty() == true)
2976 return _error->Error(_("The package index files are corrupted. No Filename: "
2977 "field for package %s."),
2978 Version.ParentPkg().Name());
2979
2980 Desc.URI = Index->ArchiveURI(PkgFile);
2981 Desc.Description = Index->ArchiveInfo(Version);
2982 Desc.Owner = this;
2983 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2984
2985 // See if we already have the file. (Legacy filenames)
2986 FileSize = Version->Size;
2987 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2988 struct stat Buf;
2989 if (stat(FinalFile.c_str(),&Buf) == 0)
2990 {
2991 // Make sure the size matches
2992 if ((unsigned long long)Buf.st_size == Version->Size)
2993 {
2994 Complete = true;
2995 Local = true;
2996 Status = StatDone;
2997 StoreFilename = DestFile = FinalFile;
2998 return true;
2999 }
3000
3001 /* Hmm, we have a file and its size does not match, this means it is
3002 an old style mismatched arch */
3003 RemoveFile("pkgAcqArchive::QueueNext", FinalFile);
3004 }
3005
3006 // Check it again using the new style output filenames
3007 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
3008 if (stat(FinalFile.c_str(),&Buf) == 0)
3009 {
3010 // Make sure the size matches
3011 if ((unsigned long long)Buf.st_size == Version->Size)
3012 {
3013 Complete = true;
3014 Local = true;
3015 Status = StatDone;
3016 StoreFilename = DestFile = FinalFile;
3017 return true;
3018 }
3019
3020 /* Hmm, we have a file and its size does not match, this shouldn't
3021 happen.. */
3022 RemoveFile("pkgAcqArchive::QueueNext", FinalFile);
3023 }
3024
3025 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
3026
3027 // Check the destination file
3028 if (stat(DestFile.c_str(),&Buf) == 0)
3029 {
3030 // Hmm, the partial file is too big, erase it
3031 if ((unsigned long long)Buf.st_size > Version->Size)
3032 RemoveFile("pkgAcqArchive::QueueNext", DestFile);
3033 else
3034 PartialSize = Buf.st_size;
3035 }
3036
3037 // Disables download of archives - useful if no real installation follows,
3038 // e.g. if we are just interested in proposed installation order
3039 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
3040 {
3041 Complete = true;
3042 Local = true;
3043 Status = StatDone;
3044 StoreFilename = DestFile = FinalFile;
3045 return true;
3046 }
3047
3048 // Create the item
3049 Local = false;
3050 QueueURI(Desc);
3051
3052 ++Vf;
3053 return true;
3054 }
3055 return false;
3056 }
3057 /*}}}*/
3058 // AcqArchive::Done - Finished fetching /*{{{*/
3059 // ---------------------------------------------------------------------
3060 /* */
3061 void pkgAcqArchive::Done(string const &Message, HashStringList const &Hashes,
3062 pkgAcquire::MethodConfig const * const Cfg)
3063 {
3064 Item::Done(Message, Hashes, Cfg);
3065
3066 // Grab the output filename
3067 std::string const FileName = LookupTag(Message,"Filename");
3068 if (DestFile != FileName && RealFileExists(DestFile) == false)
3069 {
3070 StoreFilename = DestFile = FileName;
3071 Local = true;
3072 Complete = true;
3073 return;
3074 }
3075
3076 // Done, move it into position
3077 string const FinalFile = GetFinalFilename();
3078 Rename(DestFile,FinalFile);
3079 StoreFilename = DestFile = FinalFile;
3080 Complete = true;
3081 }
3082 /*}}}*/
3083 // AcqArchive::Failed - Failure handler /*{{{*/
3084 // ---------------------------------------------------------------------
3085 /* Here we try other sources */
3086 void pkgAcqArchive::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
3087 {
3088 Item::Failed(Message,Cnf);
3089
3090 /* We don't really want to retry on failed media swaps, this prevents
3091 that. An interesting observation is that permanent failures are not
3092 recorded. */
3093 if (Cnf->Removable == true &&
3094 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3095 {
3096 // Vf = Version.FileList();
3097 while (Vf.end() == false) ++Vf;
3098 StoreFilename = string();
3099 return;
3100 }
3101
3102 Status = StatIdle;
3103 if (QueueNext() == false)
3104 {
3105 // This is the retry counter
3106 if (Retries != 0 &&
3107 Cnf->LocalOnly == false &&
3108 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3109 {
3110 Retries--;
3111 Vf = Version.FileList();
3112 if (QueueNext() == true)
3113 return;
3114 }
3115
3116 StoreFilename = string();
3117 Status = StatError;
3118 }
3119 }
3120 /*}}}*/
3121 APT_PURE bool pkgAcqArchive::IsTrusted() const /*{{{*/
3122 {
3123 return Trusted;
3124 }
3125 /*}}}*/
3126 void pkgAcqArchive::Finished() /*{{{*/
3127 {
3128 if (Status == pkgAcquire::Item::StatDone &&
3129 Complete == true)
3130 return;
3131 StoreFilename = string();
3132 }
3133 /*}}}*/
3134 std::string pkgAcqArchive::DescURI() const /*{{{*/
3135 {
3136 return Desc.URI;
3137 }
3138 /*}}}*/
3139 std::string pkgAcqArchive::ShortDesc() const /*{{{*/
3140 {
3141 return Desc.ShortDesc;
3142 }
3143 /*}}}*/
3144 pkgAcqArchive::~pkgAcqArchive() {}
3145
3146 // AcqChangelog::pkgAcqChangelog - Constructors /*{{{*/
3147 class pkgAcqChangelog::Private
3148 {
3149 public:
3150 std::string FinalFile;
3151 };
3152 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::VerIterator const &Ver,
3153 std::string const &DestDir, std::string const &DestFilename) :
3154 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(Ver.SourcePkgName()), SrcVersion(Ver.SourceVerStr())
3155 {
3156 Desc.URI = URI(Ver);
3157 Init(DestDir, DestFilename);
3158 }
3159 // some parameters are char* here as they come likely from char* interfaces – which can also return NULL
3160 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::RlsFileIterator const &RlsFile,
3161 char const * const Component, char const * const SrcName, char const * const SrcVersion,
3162 const string &DestDir, const string &DestFilename) :
3163 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
3164 {
3165 Desc.URI = URI(RlsFile, Component, SrcName, SrcVersion);
3166 Init(DestDir, DestFilename);
3167 }
3168 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner,
3169 std::string const &URI, char const * const SrcName, char const * const SrcVersion,
3170 const string &DestDir, const string &DestFilename) :
3171 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
3172 {
3173 Desc.URI = URI;
3174 Init(DestDir, DestFilename);
3175 }
3176 void pkgAcqChangelog::Init(std::string const &DestDir, std::string const &DestFilename)
3177 {
3178 if (Desc.URI.empty())
3179 {
3180 Status = StatError;
3181 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3182 strprintf(ErrorText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
3183 // Let the error message print something sensible rather than "Failed to fetch /"
3184 if (DestFilename.empty())
3185 DestFile = SrcName + ".changelog";
3186 else
3187 DestFile = DestFilename;
3188 Desc.URI = "changelog:/" + DestFile;
3189 return;
3190 }
3191
3192 std::string DestFileName;
3193 if (DestFilename.empty())
3194 DestFileName = flCombine(DestFile, SrcName + ".changelog");
3195 else
3196 DestFileName = flCombine(DestFile, DestFilename);
3197
3198 std::string const SandboxUser = _config->Find("APT::Sandbox::User");
3199 std::string const systemTemp = GetTempDir(SandboxUser);
3200 char tmpname[1000];
3201 snprintf(tmpname, sizeof(tmpname), "%s/apt-changelog-XXXXXX", systemTemp.c_str());
3202 if (NULL == mkdtemp(tmpname))
3203 {
3204 _error->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName.c_str(), SrcVersion.c_str());
3205 Status = StatError;
3206 return;
3207 }
3208 TemporaryDirectory = tmpname;
3209
3210 ChangeOwnerAndPermissionOfFile("Item::QueueURI", TemporaryDirectory.c_str(),
3211 SandboxUser.c_str(), "root", 0700);
3212
3213 DestFile = flCombine(TemporaryDirectory, DestFileName);
3214 if (DestDir.empty() == false)
3215 {
3216 d->FinalFile = flCombine(DestDir, DestFileName);
3217 if (RealFileExists(d->FinalFile))
3218 {
3219 FileFd file1, file2;
3220 if (file1.Open(DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive) &&
3221 file2.Open(d->FinalFile, FileFd::ReadOnly) && CopyFile(file2, file1))
3222 {
3223 struct timeval times[2];
3224 times[0].tv_sec = times[1].tv_sec = file2.ModificationTime();
3225 times[0].tv_usec = times[1].tv_usec = 0;
3226 utimes(DestFile.c_str(), times);
3227 }
3228 }
3229 }
3230
3231 Desc.ShortDesc = "Changelog";
3232 strprintf(Desc.Description, "%s %s %s Changelog", URI::SiteOnly(Desc.URI).c_str(), SrcName.c_str(), SrcVersion.c_str());
3233 Desc.Owner = this;
3234 QueueURI(Desc);
3235 }
3236 /*}}}*/
3237 std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/
3238 {
3239 std::string const confOnline = "Acquire::Changelogs::AlwaysOnline";
3240 bool AlwaysOnline = _config->FindB(confOnline, false);
3241 if (AlwaysOnline == false)
3242 for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
3243 {
3244 pkgCache::PkgFileIterator const PF = VF.File();
3245 if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
3246 continue;
3247 pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
3248 if (RF->Origin != 0 && _config->FindB(confOnline + "::Origin::" + RF.Origin(), false))
3249 {
3250 AlwaysOnline = true;
3251 break;
3252 }
3253 }
3254 if (AlwaysOnline == false)
3255 {
3256 pkgCache::PkgIterator const Pkg = Ver.ParentPkg();
3257 if (Pkg->CurrentVer != 0 && Pkg.CurrentVer() == Ver)
3258 {
3259 std::string const basename = std::string("/usr/share/doc/") + Pkg.Name() + "/changelog";
3260 std::string const debianname = basename + ".Debian";
3261 if (FileExists(debianname))
3262 return "copy://" + debianname;
3263 else if (FileExists(debianname + ".gz"))
3264 return "gzip://" + debianname + ".gz";
3265 else if (FileExists(basename))
3266 return "copy://" + basename;
3267 else if (FileExists(basename + ".gz"))
3268 return "gzip://" + basename + ".gz";
3269 }
3270 }
3271
3272 char const * const SrcName = Ver.SourcePkgName();
3273 char const * const SrcVersion = Ver.SourceVerStr();
3274 // find the first source for this version which promises a changelog
3275 for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
3276 {
3277 pkgCache::PkgFileIterator const PF = VF.File();
3278 if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
3279 continue;
3280 pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
3281 std::string const uri = URI(RF, PF.Component(), SrcName, SrcVersion);
3282 if (uri.empty())
3283 continue;
3284 return uri;
3285 }
3286 return "";
3287 }
3288 std::string pkgAcqChangelog::URITemplate(pkgCache::RlsFileIterator const &Rls)
3289 {
3290 if (Rls.end() == true || (Rls->Label == 0 && Rls->Origin == 0))
3291 return "";
3292 std::string const serverConfig = "Acquire::Changelogs::URI";
3293 std::string server;
3294 #define APT_EMPTY_SERVER \
3295 if (server.empty() == false) \
3296 { \
3297 if (server != "no") \
3298 return server; \
3299 return ""; \
3300 }
3301 #define APT_CHECK_SERVER(X, Y) \
3302 if (Rls->X != 0) \
3303 { \
3304 std::string const specialServerConfig = serverConfig + "::" + Y + #X + "::" + Rls.X(); \
3305 server = _config->Find(specialServerConfig); \
3306 APT_EMPTY_SERVER \
3307 }
3308 // this way e.g. Debian-Security can fallback to Debian
3309 APT_CHECK_SERVER(Label, "Override::")
3310 APT_CHECK_SERVER(Origin, "Override::")
3311
3312 if (RealFileExists(Rls.FileName()))
3313 {
3314 _error->PushToStack();
3315 FileFd rf;
3316 /* This can be costly. A caller wanting to get millions of URIs might
3317 want to do this on its own once and use Override settings.
3318 We don't do this here as Origin/Label are not as unique as they
3319 should be so this could produce request order-dependent anomalies */
3320 if (OpenMaybeClearSignedFile(Rls.FileName(), rf) == true)
3321 {
3322 pkgTagFile TagFile(&rf, rf.Size());
3323 pkgTagSection Section;
3324 if (TagFile.Step(Section) == true)
3325 server = Section.FindS("Changelogs");
3326 }
3327 _error->RevertToStack();
3328 APT_EMPTY_SERVER
3329 }
3330
3331 APT_CHECK_SERVER(Label, "")
3332 APT_CHECK_SERVER(Origin, "")
3333 #undef APT_CHECK_SERVER
3334 #undef APT_EMPTY_SERVER
3335 return "";
3336 }
3337 std::string pkgAcqChangelog::URI(pkgCache::RlsFileIterator const &Rls,
3338 char const * const Component, char const * const SrcName,
3339 char const * const SrcVersion)
3340 {
3341 return URI(URITemplate(Rls), Component, SrcName, SrcVersion);
3342 }
3343 std::string pkgAcqChangelog::URI(std::string const &Template,
3344 char const * const Component, char const * const SrcName,
3345 char const * const SrcVersion)
3346 {
3347 if (Template.find("@CHANGEPATH@") == std::string::npos)
3348 return "";
3349
3350 // the path is: COMPONENT/SRC/SRCNAME/SRCNAME_SRCVER, e.g. main/a/apt/1.1 or contrib/liba/libapt/2.0
3351 std::string Src = SrcName;
3352 std::string path = APT::String::Startswith(SrcName, "lib") ? Src.substr(0, 4) : Src.substr(0,1);
3353 path.append("/").append(Src).append("/");
3354 path.append(Src).append("_").append(StripEpoch(SrcVersion));
3355 // we omit component for releases without one (= flat-style repositories)
3356 if (Component != NULL && strlen(Component) != 0)
3357 path = std::string(Component) + "/" + path;
3358
3359 return SubstVar(Template, "@CHANGEPATH@", path);
3360 }
3361 /*}}}*/
3362 // AcqChangelog::Failed - Failure handler /*{{{*/
3363 void pkgAcqChangelog::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
3364 {
3365 Item::Failed(Message,Cnf);
3366
3367 std::string errText;
3368 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3369 strprintf(errText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
3370
3371 // Error is probably something techy like 404 Not Found
3372 if (ErrorText.empty())
3373 ErrorText = errText;
3374 else
3375 ErrorText = errText + " (" + ErrorText + ")";
3376 }
3377 /*}}}*/
3378 // AcqChangelog::Done - Item downloaded OK /*{{{*/
3379 void pkgAcqChangelog::Done(string const &Message,HashStringList const &CalcHashes,
3380 pkgAcquire::MethodConfig const * const Cnf)
3381 {
3382 Item::Done(Message,CalcHashes,Cnf);
3383 if (d->FinalFile.empty() == false)
3384 {
3385 if (RemoveFile("pkgAcqChangelog::Done", d->FinalFile) == false ||
3386 Rename(DestFile, d->FinalFile) == false)
3387 Status = StatError;
3388 }
3389
3390 Complete = true;
3391 }
3392 /*}}}*/
3393 pkgAcqChangelog::~pkgAcqChangelog() /*{{{*/
3394 {
3395 if (TemporaryDirectory.empty() == false)
3396 {
3397 RemoveFile("~pkgAcqChangelog", DestFile);
3398 rmdir(TemporaryDirectory.c_str());
3399 }
3400 delete d;
3401 }
3402 /*}}}*/
3403
3404 // AcqFile::pkgAcqFile - Constructor /*{{{*/
3405 pkgAcqFile::pkgAcqFile(pkgAcquire * const Owner,string const &URI, HashStringList const &Hashes,
3406 unsigned long long const Size,string const &Dsc,string const &ShortDesc,
3407 const string &DestDir, const string &DestFilename,
3408 bool const IsIndexFile) :
3409 Item(Owner), d(NULL), IsIndexFile(IsIndexFile), ExpectedHashes(Hashes)
3410 {
3411 Retries = _config->FindI("Acquire::Retries",0);
3412
3413 if(!DestFilename.empty())
3414 DestFile = DestFilename;
3415 else if(!DestDir.empty())
3416 DestFile = DestDir + "/" + flNotDir(URI);
3417 else
3418 DestFile = flNotDir(URI);
3419
3420 // Create the item
3421 Desc.URI = URI;
3422 Desc.Description = Dsc;
3423 Desc.Owner = this;
3424
3425 // Set the short description to the archive component
3426 Desc.ShortDesc = ShortDesc;
3427
3428 // Get the transfer sizes
3429 FileSize = Size;
3430 struct stat Buf;
3431 if (stat(DestFile.c_str(),&Buf) == 0)
3432 {
3433 // Hmm, the partial file is too big, erase it
3434 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
3435 RemoveFile("pkgAcqFile", DestFile);
3436 else
3437 PartialSize = Buf.st_size;
3438 }
3439
3440 QueueURI(Desc);
3441 }
3442 /*}}}*/
3443 // AcqFile::Done - Item downloaded OK /*{{{*/
3444 void pkgAcqFile::Done(string const &Message,HashStringList const &CalcHashes,
3445 pkgAcquire::MethodConfig const * const Cnf)
3446 {
3447 Item::Done(Message,CalcHashes,Cnf);
3448
3449 std::string const FileName = LookupTag(Message,"Filename");
3450 Complete = true;
3451
3452 // The files timestamp matches
3453 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
3454 return;
3455
3456 // We have to copy it into place
3457 if (RealFileExists(DestFile.c_str()) == false)
3458 {
3459 Local = true;
3460 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
3461 Cnf->Removable == true)
3462 {
3463 Desc.URI = "copy:" + FileName;
3464 QueueURI(Desc);
3465 return;
3466 }
3467
3468 // Erase the file if it is a symlink so we can overwrite it
3469 struct stat St;
3470 if (lstat(DestFile.c_str(),&St) == 0)
3471 {
3472 if (S_ISLNK(St.st_mode) != 0)
3473 RemoveFile("pkgAcqFile::Done", DestFile);
3474 }
3475
3476 // Symlink the file
3477 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
3478 {
3479 _error->PushToStack();
3480 _error->Errno("pkgAcqFile::Done", "Symlinking file %s failed", DestFile.c_str());
3481 std::stringstream msg;
3482 _error->DumpErrors(msg, GlobalError::DEBUG, false);
3483 _error->RevertToStack();
3484 ErrorText = msg.str();
3485 Status = StatError;
3486 Complete = false;
3487 }
3488 }
3489 }
3490 /*}}}*/
3491 // AcqFile::Failed - Failure handler /*{{{*/
3492 // ---------------------------------------------------------------------
3493 /* Here we try other sources */
3494 void pkgAcqFile::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
3495 {
3496 Item::Failed(Message,Cnf);
3497
3498 // This is the retry counter
3499 if (Retries != 0 &&
3500 Cnf->LocalOnly == false &&
3501 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3502 {
3503 --Retries;
3504 QueueURI(Desc);
3505 Status = StatIdle;
3506 return;
3507 }
3508
3509 }
3510 /*}}}*/
3511 string pkgAcqFile::Custom600Headers() const /*{{{*/
3512 {
3513 if (IsIndexFile)
3514 return "\nIndex-File: true";
3515 return "";
3516 }
3517 /*}}}*/
3518 pkgAcqFile::~pkgAcqFile() {}