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