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