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