]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
cachefile: Only set members that were initialized successfully
[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 // store file size of the download to ensure the fetcher gives
2614 // accurate progress reporting
2615 FileSize = GetExpectedHashes().FileSize();
2616
2617 if (CurrentCompressionExtension == "uncompressed")
2618 {
2619 Desc.URI = URI;
2620 }
2621 else if (CurrentCompressionExtension == "by-hash")
2622 {
2623 NextCompressionExtension(CurrentCompressionExtension, CompressionExtensions, true);
2624 if(unlikely(TransactionManager->MetaIndexParser == NULL || CurrentCompressionExtension.empty()))
2625 return;
2626 if (CurrentCompressionExtension != "uncompressed")
2627 {
2628 Desc.URI = URI + '.' + CurrentCompressionExtension;
2629 DestFile = DestFile + '.' + CurrentCompressionExtension;
2630 }
2631
2632 HashStringList const Hashes = GetExpectedHashes();
2633 HashString const * const TargetHash = Hashes.find(NULL);
2634 if (unlikely(TargetHash == nullptr))
2635 return;
2636 std::string const ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
2637 size_t const trailing_slash = Desc.URI.find_last_of("/");
2638 if (unlikely(trailing_slash == std::string::npos))
2639 return;
2640 Desc.URI = Desc.URI.replace(
2641 trailing_slash,
2642 Desc.URI.substr(trailing_slash+1).size()+1,
2643 ByHash);
2644 }
2645 else if (unlikely(CurrentCompressionExtension.empty()))
2646 return;
2647 else
2648 {
2649 Desc.URI = URI + '.' + CurrentCompressionExtension;
2650 DestFile = DestFile + '.' + CurrentCompressionExtension;
2651 }
2652
2653
2654 Desc.Description = URIDesc;
2655 Desc.Owner = this;
2656 Desc.ShortDesc = ShortDesc;
2657
2658 QueueURI(Desc);
2659 }
2660 /*}}}*/
2661 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2662 // ---------------------------------------------------------------------
2663 /* The only header we use is the last-modified header. */
2664 string pkgAcqIndex::Custom600Headers() const
2665 {
2666
2667 string msg = "\nIndex-File: true";
2668
2669 if (TransactionManager->LastMetaIndexParser == NULL)
2670 {
2671 std::string const Final = GetFinalFilename();
2672
2673 struct stat Buf;
2674 if (stat(Final.c_str(),&Buf) == 0)
2675 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
2676 }
2677
2678 if(Target.IsOptional)
2679 msg += "\nFail-Ignore: true";
2680
2681 return msg;
2682 }
2683 /*}}}*/
2684 // AcqIndex::Failed - getting the indexfile failed /*{{{*/
2685 void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
2686 {
2687 Item::Failed(Message,Cnf);
2688
2689 // authorisation matches will not be fixed by other compression types
2690 if (Status != StatAuthError)
2691 {
2692 if (CompressionExtensions.empty() == false)
2693 {
2694 Init(Target.URI, Desc.Description, Desc.ShortDesc);
2695 Status = StatIdle;
2696 return;
2697 }
2698 }
2699
2700 if(Target.IsOptional && GetExpectedHashes().empty() && Stage == STAGE_DOWNLOAD)
2701 Status = StatDone;
2702 else
2703 TransactionManager->AbortTransaction();
2704 }
2705 /*}}}*/
2706 // AcqIndex::Done - Finished a fetch /*{{{*/
2707 // ---------------------------------------------------------------------
2708 /* This goes through a number of states.. On the initial fetch the
2709 method could possibly return an alternate filename which points
2710 to the uncompressed version of the file. If this is so the file
2711 is copied into the partial directory. In all other cases the file
2712 is decompressed with a compressed uri. */
2713 void pkgAcqIndex::Done(string const &Message,
2714 HashStringList const &Hashes,
2715 pkgAcquire::MethodConfig const * const Cfg)
2716 {
2717 Item::Done(Message,Hashes,Cfg);
2718
2719 switch(Stage)
2720 {
2721 case STAGE_DOWNLOAD:
2722 StageDownloadDone(Message);
2723 break;
2724 case STAGE_DECOMPRESS_AND_VERIFY:
2725 StageDecompressDone();
2726 break;
2727 }
2728 }
2729 /*}}}*/
2730 // AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
2731 void pkgAcqIndex::StageDownloadDone(string const &Message)
2732 {
2733 Local = true;
2734 Complete = true;
2735
2736 std::string const AltFilename = LookupTag(Message,"Alt-Filename");
2737 std::string Filename = LookupTag(Message,"Filename");
2738
2739 // we need to verify the file against the current Release file again
2740 // on if-modfied-since hit to avoid a stale attack against us
2741 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2742 {
2743 // copy FinalFile into partial/ so that we check the hash again
2744 string const FinalFile = GetExistingFilename(GetFinalFileNameFromURI(Target.URI));
2745 if (symlink(FinalFile.c_str(), DestFile.c_str()) != 0)
2746 _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking final file %s back to %s failed", FinalFile.c_str(), DestFile.c_str());
2747 else
2748 {
2749 EraseFileName = DestFile;
2750 Filename = DestFile;
2751 }
2752 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2753 Desc.URI = "store:" + Filename;
2754 QueueURI(Desc);
2755 SetActiveSubprocess(::URI(Desc.URI).Access);
2756 return;
2757 }
2758 // methods like file:// give us an alternative (uncompressed) file
2759 else if (Target.KeepCompressed == false && AltFilename.empty() == false)
2760 {
2761 if (CurrentCompressionExtension != "uncompressed")
2762 DestFile.erase(DestFile.length() - (CurrentCompressionExtension.length() + 1));
2763 Filename = AltFilename;
2764 }
2765 // Methods like e.g. "file:" will give us a (compressed) FileName that is
2766 // not the "DestFile" we set, in this case we uncompress from the local file
2767 else if (Filename != DestFile && RealFileExists(DestFile) == false)
2768 {
2769 // symlinking ensures that the filename can be used for compression detection
2770 // that is e.g. needed for by-hash which has no extension over file
2771 if (symlink(Filename.c_str(),DestFile.c_str()) != 0)
2772 _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking file %s to %s failed", Filename.c_str(), DestFile.c_str());
2773 else
2774 {
2775 EraseFileName = DestFile;
2776 Filename = DestFile;
2777 }
2778 }
2779
2780 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2781 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2782 if (Filename != DestFile && flExtension(Filename) == flExtension(DestFile))
2783 Desc.URI = "copy:" + Filename;
2784 else
2785 Desc.URI = "store:" + Filename;
2786 if (DestFile == Filename)
2787 {
2788 if (CurrentCompressionExtension == "uncompressed")
2789 return StageDecompressDone();
2790 DestFile = "/dev/null";
2791 }
2792
2793 if (EraseFileName.empty())
2794 EraseFileName = Filename;
2795
2796 // queue uri for the next stage
2797 QueueURI(Desc);
2798 SetActiveSubprocess(::URI(Desc.URI).Access);
2799 }
2800 /*}}}*/
2801 // AcqIndex::StageDecompressDone - Final verification /*{{{*/
2802 void pkgAcqIndex::StageDecompressDone()
2803 {
2804 if (DestFile == "/dev/null")
2805 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2806
2807 // Done, queue for rename on transaction finished
2808 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
2809 }
2810 /*}}}*/
2811 pkgAcqIndex::~pkgAcqIndex() {}
2812
2813
2814 // AcqArchive::AcqArchive - Constructor /*{{{*/
2815 // ---------------------------------------------------------------------
2816 /* This just sets up the initial fetch environment and queues the first
2817 possibilitiy */
2818 pkgAcqArchive::pkgAcqArchive(pkgAcquire * const Owner,pkgSourceList * const Sources,
2819 pkgRecords * const Recs,pkgCache::VerIterator const &Version,
2820 string &StoreFilename) :
2821 Item(Owner), d(NULL), LocalSource(false), Version(Version), Sources(Sources), Recs(Recs),
2822 StoreFilename(StoreFilename), Vf(Version.FileList()),
2823 Trusted(false)
2824 {
2825 Retries = _config->FindI("Acquire::Retries",0);
2826
2827 if (Version.Arch() == 0)
2828 {
2829 _error->Error(_("I wasn't able to locate a file for the %s package. "
2830 "This might mean you need to manually fix this package. "
2831 "(due to missing arch)"),
2832 Version.ParentPkg().FullName().c_str());
2833 return;
2834 }
2835
2836 /* We need to find a filename to determine the extension. We make the
2837 assumption here that all the available sources for this version share
2838 the same extension.. */
2839 // Skip not source sources, they do not have file fields.
2840 for (; Vf.end() == false; ++Vf)
2841 {
2842 if (Vf.File().Flagged(pkgCache::Flag::NotSource))
2843 continue;
2844 break;
2845 }
2846
2847 // Does not really matter here.. we are going to fail out below
2848 if (Vf.end() != true)
2849 {
2850 // If this fails to get a file name we will bomb out below.
2851 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2852 if (_error->PendingError() == true)
2853 return;
2854
2855 // Generate the final file name as: package_version_arch.foo
2856 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2857 QuoteString(Version.VerStr(),"_:") + '_' +
2858 QuoteString(Version.Arch(),"_:.") +
2859 "." + flExtension(Parse.FileName());
2860 }
2861
2862 // check if we have one trusted source for the package. if so, switch
2863 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2864 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2865 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2866 bool seenUntrusted = false;
2867 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
2868 {
2869 pkgIndexFile *Index;
2870 if (Sources->FindIndex(i.File(),Index) == false)
2871 continue;
2872
2873 if (debugAuth == true)
2874 std::cerr << "Checking index: " << Index->Describe()
2875 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2876
2877 if (Index->IsTrusted() == true)
2878 {
2879 Trusted = true;
2880 if (allowUnauth == false)
2881 break;
2882 }
2883 else
2884 seenUntrusted = true;
2885 }
2886
2887 // "allow-unauthenticated" restores apts old fetching behaviour
2888 // that means that e.g. unauthenticated file:// uris are higher
2889 // priority than authenticated http:// uris
2890 if (allowUnauth == true && seenUntrusted == true)
2891 Trusted = false;
2892
2893 // Select a source
2894 if (QueueNext() == false && _error->PendingError() == false)
2895 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2896 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2897 }
2898 /*}}}*/
2899 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
2900 // ---------------------------------------------------------------------
2901 /* This queues the next available file version for download. It checks if
2902 the archive is already available in the cache and stashs the MD5 for
2903 checking later. */
2904 bool pkgAcqArchive::QueueNext()
2905 {
2906 for (; Vf.end() == false; ++Vf)
2907 {
2908 pkgCache::PkgFileIterator const PkgF = Vf.File();
2909 // Ignore not source sources
2910 if (PkgF.Flagged(pkgCache::Flag::NotSource))
2911 continue;
2912
2913 // Try to cross match against the source list
2914 pkgIndexFile *Index;
2915 if (Sources->FindIndex(PkgF, Index) == false)
2916 continue;
2917 LocalSource = PkgF.Flagged(pkgCache::Flag::LocalSource);
2918
2919 // only try to get a trusted package from another source if that source
2920 // is also trusted
2921 if(Trusted && !Index->IsTrusted())
2922 continue;
2923
2924 // Grab the text package record
2925 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2926 if (_error->PendingError() == true)
2927 return false;
2928
2929 string PkgFile = Parse.FileName();
2930 ExpectedHashes = Parse.Hashes();
2931
2932 if (PkgFile.empty() == true)
2933 return _error->Error(_("The package index files are corrupted. No Filename: "
2934 "field for package %s."),
2935 Version.ParentPkg().Name());
2936
2937 Desc.URI = Index->ArchiveURI(PkgFile);
2938 Desc.Description = Index->ArchiveInfo(Version);
2939 Desc.Owner = this;
2940 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2941
2942 // See if we already have the file. (Legacy filenames)
2943 FileSize = Version->Size;
2944 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2945 struct stat Buf;
2946 if (stat(FinalFile.c_str(),&Buf) == 0)
2947 {
2948 // Make sure the size matches
2949 if ((unsigned long long)Buf.st_size == Version->Size)
2950 {
2951 Complete = true;
2952 Local = true;
2953 Status = StatDone;
2954 StoreFilename = DestFile = FinalFile;
2955 return true;
2956 }
2957
2958 /* Hmm, we have a file and its size does not match, this means it is
2959 an old style mismatched arch */
2960 RemoveFile("pkgAcqArchive::QueueNext", FinalFile);
2961 }
2962
2963 // Check it again using the new style output filenames
2964 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2965 if (stat(FinalFile.c_str(),&Buf) == 0)
2966 {
2967 // Make sure the size matches
2968 if ((unsigned long long)Buf.st_size == Version->Size)
2969 {
2970 Complete = true;
2971 Local = true;
2972 Status = StatDone;
2973 StoreFilename = DestFile = FinalFile;
2974 return true;
2975 }
2976
2977 /* Hmm, we have a file and its size does not match, this shouldn't
2978 happen.. */
2979 RemoveFile("pkgAcqArchive::QueueNext", FinalFile);
2980 }
2981
2982 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
2983
2984 // Check the destination file
2985 if (stat(DestFile.c_str(),&Buf) == 0)
2986 {
2987 // Hmm, the partial file is too big, erase it
2988 if ((unsigned long long)Buf.st_size > Version->Size)
2989 RemoveFile("pkgAcqArchive::QueueNext", DestFile);
2990 else
2991 PartialSize = Buf.st_size;
2992 }
2993
2994 // Disables download of archives - useful if no real installation follows,
2995 // e.g. if we are just interested in proposed installation order
2996 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2997 {
2998 Complete = true;
2999 Local = true;
3000 Status = StatDone;
3001 StoreFilename = DestFile = FinalFile;
3002 return true;
3003 }
3004
3005 // Create the item
3006 Local = false;
3007 QueueURI(Desc);
3008
3009 ++Vf;
3010 return true;
3011 }
3012 return false;
3013 }
3014 /*}}}*/
3015 // AcqArchive::Done - Finished fetching /*{{{*/
3016 // ---------------------------------------------------------------------
3017 /* */
3018 void pkgAcqArchive::Done(string const &Message, HashStringList const &Hashes,
3019 pkgAcquire::MethodConfig const * const Cfg)
3020 {
3021 Item::Done(Message, Hashes, Cfg);
3022
3023 // Grab the output filename
3024 std::string const FileName = LookupTag(Message,"Filename");
3025 if (DestFile != FileName && RealFileExists(DestFile) == false)
3026 {
3027 StoreFilename = DestFile = FileName;
3028 Local = true;
3029 Complete = true;
3030 return;
3031 }
3032
3033 // Done, move it into position
3034 string const FinalFile = GetFinalFilename();
3035 Rename(DestFile,FinalFile);
3036 StoreFilename = DestFile = FinalFile;
3037 Complete = true;
3038 }
3039 /*}}}*/
3040 // AcqArchive::Failed - Failure handler /*{{{*/
3041 // ---------------------------------------------------------------------
3042 /* Here we try other sources */
3043 void pkgAcqArchive::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
3044 {
3045 Item::Failed(Message,Cnf);
3046
3047 /* We don't really want to retry on failed media swaps, this prevents
3048 that. An interesting observation is that permanent failures are not
3049 recorded. */
3050 if (Cnf->Removable == true &&
3051 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3052 {
3053 // Vf = Version.FileList();
3054 while (Vf.end() == false) ++Vf;
3055 StoreFilename = string();
3056 return;
3057 }
3058
3059 Status = StatIdle;
3060 if (QueueNext() == false)
3061 {
3062 // This is the retry counter
3063 if (Retries != 0 &&
3064 Cnf->LocalOnly == false &&
3065 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3066 {
3067 Retries--;
3068 Vf = Version.FileList();
3069 if (QueueNext() == true)
3070 return;
3071 }
3072
3073 StoreFilename = string();
3074 Status = StatError;
3075 }
3076 }
3077 /*}}}*/
3078 APT_PURE bool pkgAcqArchive::IsTrusted() const /*{{{*/
3079 {
3080 return Trusted;
3081 }
3082 /*}}}*/
3083 void pkgAcqArchive::Finished() /*{{{*/
3084 {
3085 if (Status == pkgAcquire::Item::StatDone &&
3086 Complete == true)
3087 return;
3088 StoreFilename = string();
3089 }
3090 /*}}}*/
3091 std::string pkgAcqArchive::DescURI() const /*{{{*/
3092 {
3093 return Desc.URI;
3094 }
3095 /*}}}*/
3096 std::string pkgAcqArchive::ShortDesc() const /*{{{*/
3097 {
3098 return Desc.ShortDesc;
3099 }
3100 /*}}}*/
3101 pkgAcqArchive::~pkgAcqArchive() {}
3102
3103 // AcqChangelog::pkgAcqChangelog - Constructors /*{{{*/
3104 class pkgAcqChangelog::Private
3105 {
3106 public:
3107 std::string FinalFile;
3108 };
3109 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::VerIterator const &Ver,
3110 std::string const &DestDir, std::string const &DestFilename) :
3111 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(Ver.SourcePkgName()), SrcVersion(Ver.SourceVerStr())
3112 {
3113 Desc.URI = URI(Ver);
3114 Init(DestDir, DestFilename);
3115 }
3116 // some parameters are char* here as they come likely from char* interfaces – which can also return NULL
3117 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::RlsFileIterator const &RlsFile,
3118 char const * const Component, char const * const SrcName, char const * const SrcVersion,
3119 const string &DestDir, const string &DestFilename) :
3120 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
3121 {
3122 Desc.URI = URI(RlsFile, Component, SrcName, SrcVersion);
3123 Init(DestDir, DestFilename);
3124 }
3125 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner,
3126 std::string const &URI, char const * const SrcName, char const * const SrcVersion,
3127 const string &DestDir, const string &DestFilename) :
3128 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
3129 {
3130 Desc.URI = URI;
3131 Init(DestDir, DestFilename);
3132 }
3133 void pkgAcqChangelog::Init(std::string const &DestDir, std::string const &DestFilename)
3134 {
3135 if (Desc.URI.empty())
3136 {
3137 Status = StatError;
3138 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3139 strprintf(ErrorText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
3140 // Let the error message print something sensible rather than "Failed to fetch /"
3141 if (DestFilename.empty())
3142 DestFile = SrcName + ".changelog";
3143 else
3144 DestFile = DestFilename;
3145 Desc.URI = "changelog:/" + DestFile;
3146 return;
3147 }
3148
3149 std::string DestFileName;
3150 if (DestFilename.empty())
3151 DestFileName = flCombine(DestFile, SrcName + ".changelog");
3152 else
3153 DestFileName = flCombine(DestFile, DestFilename);
3154
3155 std::string const SandboxUser = _config->Find("APT::Sandbox::User");
3156 std::string const systemTemp = GetTempDir(SandboxUser);
3157 char tmpname[1000];
3158 snprintf(tmpname, sizeof(tmpname), "%s/apt-changelog-XXXXXX", systemTemp.c_str());
3159 if (NULL == mkdtemp(tmpname))
3160 {
3161 _error->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName.c_str(), SrcVersion.c_str());
3162 Status = StatError;
3163 return;
3164 }
3165 TemporaryDirectory = tmpname;
3166
3167 ChangeOwnerAndPermissionOfFile("Item::QueueURI", TemporaryDirectory.c_str(),
3168 SandboxUser.c_str(), "root", 0700);
3169
3170 DestFile = flCombine(TemporaryDirectory, DestFileName);
3171 if (DestDir.empty() == false)
3172 {
3173 d->FinalFile = flCombine(DestDir, DestFileName);
3174 if (RealFileExists(d->FinalFile))
3175 {
3176 FileFd file1, file2;
3177 if (file1.Open(DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive) &&
3178 file2.Open(d->FinalFile, FileFd::ReadOnly) && CopyFile(file2, file1))
3179 {
3180 struct timeval times[2];
3181 times[0].tv_sec = times[1].tv_sec = file2.ModificationTime();
3182 times[0].tv_usec = times[1].tv_usec = 0;
3183 utimes(DestFile.c_str(), times);
3184 }
3185 }
3186 }
3187
3188 Desc.ShortDesc = "Changelog";
3189 strprintf(Desc.Description, "%s %s %s Changelog", URI::SiteOnly(Desc.URI).c_str(), SrcName.c_str(), SrcVersion.c_str());
3190 Desc.Owner = this;
3191 QueueURI(Desc);
3192 }
3193 /*}}}*/
3194 std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/
3195 {
3196 std::string const confOnline = "Acquire::Changelogs::AlwaysOnline";
3197 bool AlwaysOnline = _config->FindB(confOnline, false);
3198 if (AlwaysOnline == false)
3199 for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
3200 {
3201 pkgCache::PkgFileIterator const PF = VF.File();
3202 if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
3203 continue;
3204 pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
3205 if (RF->Origin != 0 && _config->FindB(confOnline + "::Origin::" + RF.Origin(), false))
3206 {
3207 AlwaysOnline = true;
3208 break;
3209 }
3210 }
3211 if (AlwaysOnline == false)
3212 {
3213 pkgCache::PkgIterator const Pkg = Ver.ParentPkg();
3214 if (Pkg->CurrentVer != 0 && Pkg.CurrentVer() == Ver)
3215 {
3216 std::string const basename = std::string("/usr/share/doc/") + Pkg.Name() + "/changelog";
3217 std::string const debianname = basename + ".Debian";
3218 if (FileExists(debianname))
3219 return "copy://" + debianname;
3220 else if (FileExists(debianname + ".gz"))
3221 return "gzip://" + debianname + ".gz";
3222 else if (FileExists(basename))
3223 return "copy://" + basename;
3224 else if (FileExists(basename + ".gz"))
3225 return "gzip://" + basename + ".gz";
3226 }
3227 }
3228
3229 char const * const SrcName = Ver.SourcePkgName();
3230 char const * const SrcVersion = Ver.SourceVerStr();
3231 // find the first source for this version which promises a changelog
3232 for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
3233 {
3234 pkgCache::PkgFileIterator const PF = VF.File();
3235 if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
3236 continue;
3237 pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
3238 std::string const uri = URI(RF, PF.Component(), SrcName, SrcVersion);
3239 if (uri.empty())
3240 continue;
3241 return uri;
3242 }
3243 return "";
3244 }
3245 std::string pkgAcqChangelog::URITemplate(pkgCache::RlsFileIterator const &Rls)
3246 {
3247 if (Rls.end() == true || (Rls->Label == 0 && Rls->Origin == 0))
3248 return "";
3249 std::string const serverConfig = "Acquire::Changelogs::URI";
3250 std::string server;
3251 #define APT_EMPTY_SERVER \
3252 if (server.empty() == false) \
3253 { \
3254 if (server != "no") \
3255 return server; \
3256 return ""; \
3257 }
3258 #define APT_CHECK_SERVER(X, Y) \
3259 if (Rls->X != 0) \
3260 { \
3261 std::string const specialServerConfig = serverConfig + "::" + Y + #X + "::" + Rls.X(); \
3262 server = _config->Find(specialServerConfig); \
3263 APT_EMPTY_SERVER \
3264 }
3265 // this way e.g. Debian-Security can fallback to Debian
3266 APT_CHECK_SERVER(Label, "Override::")
3267 APT_CHECK_SERVER(Origin, "Override::")
3268
3269 if (RealFileExists(Rls.FileName()))
3270 {
3271 _error->PushToStack();
3272 FileFd rf;
3273 /* This can be costly. A caller wanting to get millions of URIs might
3274 want to do this on its own once and use Override settings.
3275 We don't do this here as Origin/Label are not as unique as they
3276 should be so this could produce request order-dependent anomalies */
3277 if (OpenMaybeClearSignedFile(Rls.FileName(), rf) == true)
3278 {
3279 pkgTagFile TagFile(&rf, rf.Size());
3280 pkgTagSection Section;
3281 if (TagFile.Step(Section) == true)
3282 server = Section.FindS("Changelogs");
3283 }
3284 _error->RevertToStack();
3285 APT_EMPTY_SERVER
3286 }
3287
3288 APT_CHECK_SERVER(Label, "")
3289 APT_CHECK_SERVER(Origin, "")
3290 #undef APT_CHECK_SERVER
3291 #undef APT_EMPTY_SERVER
3292 return "";
3293 }
3294 std::string pkgAcqChangelog::URI(pkgCache::RlsFileIterator const &Rls,
3295 char const * const Component, char const * const SrcName,
3296 char const * const SrcVersion)
3297 {
3298 return URI(URITemplate(Rls), Component, SrcName, SrcVersion);
3299 }
3300 std::string pkgAcqChangelog::URI(std::string const &Template,
3301 char const * const Component, char const * const SrcName,
3302 char const * const SrcVersion)
3303 {
3304 if (Template.find("@CHANGEPATH@") == std::string::npos)
3305 return "";
3306
3307 // the path is: COMPONENT/SRC/SRCNAME/SRCNAME_SRCVER, e.g. main/a/apt/1.1 or contrib/liba/libapt/2.0
3308 std::string Src = SrcName;
3309 std::string path = APT::String::Startswith(SrcName, "lib") ? Src.substr(0, 4) : Src.substr(0,1);
3310 path.append("/").append(Src).append("/");
3311 path.append(Src).append("_").append(StripEpoch(SrcVersion));
3312 // we omit component for releases without one (= flat-style repositories)
3313 if (Component != NULL && strlen(Component) != 0)
3314 path = std::string(Component) + "/" + path;
3315
3316 return SubstVar(Template, "@CHANGEPATH@", path);
3317 }
3318 /*}}}*/
3319 // AcqChangelog::Failed - Failure handler /*{{{*/
3320 void pkgAcqChangelog::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
3321 {
3322 Item::Failed(Message,Cnf);
3323
3324 std::string errText;
3325 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3326 strprintf(errText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
3327
3328 // Error is probably something techy like 404 Not Found
3329 if (ErrorText.empty())
3330 ErrorText = errText;
3331 else
3332 ErrorText = errText + " (" + ErrorText + ")";
3333 }
3334 /*}}}*/
3335 // AcqChangelog::Done - Item downloaded OK /*{{{*/
3336 void pkgAcqChangelog::Done(string const &Message,HashStringList const &CalcHashes,
3337 pkgAcquire::MethodConfig const * const Cnf)
3338 {
3339 Item::Done(Message,CalcHashes,Cnf);
3340 if (d->FinalFile.empty() == false)
3341 {
3342 if (RemoveFile("pkgAcqChangelog::Done", d->FinalFile) == false ||
3343 Rename(DestFile, d->FinalFile) == false)
3344 Status = StatError;
3345 }
3346
3347 Complete = true;
3348 }
3349 /*}}}*/
3350 pkgAcqChangelog::~pkgAcqChangelog() /*{{{*/
3351 {
3352 if (TemporaryDirectory.empty() == false)
3353 {
3354 RemoveFile("~pkgAcqChangelog", DestFile);
3355 rmdir(TemporaryDirectory.c_str());
3356 }
3357 delete d;
3358 }
3359 /*}}}*/
3360
3361 // AcqFile::pkgAcqFile - Constructor /*{{{*/
3362 pkgAcqFile::pkgAcqFile(pkgAcquire * const Owner,string const &URI, HashStringList const &Hashes,
3363 unsigned long long const Size,string const &Dsc,string const &ShortDesc,
3364 const string &DestDir, const string &DestFilename,
3365 bool const IsIndexFile) :
3366 Item(Owner), d(NULL), IsIndexFile(IsIndexFile), ExpectedHashes(Hashes)
3367 {
3368 Retries = _config->FindI("Acquire::Retries",0);
3369
3370 if(!DestFilename.empty())
3371 DestFile = DestFilename;
3372 else if(!DestDir.empty())
3373 DestFile = DestDir + "/" + flNotDir(URI);
3374 else
3375 DestFile = flNotDir(URI);
3376
3377 // Create the item
3378 Desc.URI = URI;
3379 Desc.Description = Dsc;
3380 Desc.Owner = this;
3381
3382 // Set the short description to the archive component
3383 Desc.ShortDesc = ShortDesc;
3384
3385 // Get the transfer sizes
3386 FileSize = Size;
3387 struct stat Buf;
3388 if (stat(DestFile.c_str(),&Buf) == 0)
3389 {
3390 // Hmm, the partial file is too big, erase it
3391 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
3392 RemoveFile("pkgAcqFile", DestFile);
3393 else
3394 PartialSize = Buf.st_size;
3395 }
3396
3397 QueueURI(Desc);
3398 }
3399 /*}}}*/
3400 // AcqFile::Done - Item downloaded OK /*{{{*/
3401 void pkgAcqFile::Done(string const &Message,HashStringList const &CalcHashes,
3402 pkgAcquire::MethodConfig const * const Cnf)
3403 {
3404 Item::Done(Message,CalcHashes,Cnf);
3405
3406 std::string const FileName = LookupTag(Message,"Filename");
3407 Complete = true;
3408
3409 // The files timestamp matches
3410 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
3411 return;
3412
3413 // We have to copy it into place
3414 if (RealFileExists(DestFile.c_str()) == false)
3415 {
3416 Local = true;
3417 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
3418 Cnf->Removable == true)
3419 {
3420 Desc.URI = "copy:" + FileName;
3421 QueueURI(Desc);
3422 return;
3423 }
3424
3425 // Erase the file if it is a symlink so we can overwrite it
3426 struct stat St;
3427 if (lstat(DestFile.c_str(),&St) == 0)
3428 {
3429 if (S_ISLNK(St.st_mode) != 0)
3430 RemoveFile("pkgAcqFile::Done", DestFile);
3431 }
3432
3433 // Symlink the file
3434 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
3435 {
3436 _error->PushToStack();
3437 _error->Errno("pkgAcqFile::Done", "Symlinking file %s failed", DestFile.c_str());
3438 std::stringstream msg;
3439 _error->DumpErrors(msg, GlobalError::DEBUG, false);
3440 _error->RevertToStack();
3441 ErrorText = msg.str();
3442 Status = StatError;
3443 Complete = false;
3444 }
3445 }
3446 }
3447 /*}}}*/
3448 // AcqFile::Failed - Failure handler /*{{{*/
3449 // ---------------------------------------------------------------------
3450 /* Here we try other sources */
3451 void pkgAcqFile::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
3452 {
3453 Item::Failed(Message,Cnf);
3454
3455 // This is the retry counter
3456 if (Retries != 0 &&
3457 Cnf->LocalOnly == false &&
3458 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3459 {
3460 --Retries;
3461 QueueURI(Desc);
3462 Status = StatIdle;
3463 return;
3464 }
3465
3466 }
3467 /*}}}*/
3468 string pkgAcqFile::Custom600Headers() const /*{{{*/
3469 {
3470 if (IsIndexFile)
3471 return "\nIndex-File: true";
3472 return "";
3473 }
3474 /*}}}*/
3475 pkgAcqFile::~pkgAcqFile() {}