]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
1dcbde2233078815370c85c1ee67e3f9ec99461f
[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/sha1.h>
26 #include <apt-pkg/tagfile.h>
27 #include <apt-pkg/indexrecords.h>
28 #include <apt-pkg/acquire.h>
29 #include <apt-pkg/hashes.h>
30 #include <apt-pkg/indexfile.h>
31 #include <apt-pkg/pkgcache.h>
32 #include <apt-pkg/cacheiterators.h>
33 #include <apt-pkg/pkgrecords.h>
34
35 #include <stddef.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <iostream>
39 #include <vector>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <string>
44 #include <sstream>
45 #include <stdio.h>
46 #include <ctime>
47 #include <sys/types.h>
48 #include <pwd.h>
49 #include <grp.h>
50
51 #include <apti18n.h>
52 /*}}}*/
53
54 using namespace std;
55
56 static void printHashSumComparision(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/
57 {
58 if (_config->FindB("Debug::Acquire::HashSumMismatch", false) == false)
59 return;
60 std::cerr << std::endl << URI << ":" << std::endl << " Expected Hash: " << std::endl;
61 for (HashStringList::const_iterator hs = Expected.begin(); hs != Expected.end(); ++hs)
62 std::cerr << "\t- " << hs->toStr() << std::endl;
63 std::cerr << " Actual Hash: " << std::endl;
64 for (HashStringList::const_iterator hs = Actual.begin(); hs != Actual.end(); ++hs)
65 std::cerr << "\t- " << hs->toStr() << std::endl;
66 }
67 /*}}}*/
68 static void ChangeOwnerAndPermissionOfFile(char const * const requester, char const * const file, char const * const user, char const * const group, mode_t const mode)
69 {
70 // ensure the file is owned by root and has good permissions
71 struct passwd const * const pw = getpwnam(user);
72 struct group const * const gr = getgrnam(group);
73 if (getuid() == 0) // if we aren't root, we can't chown, so don't try it
74 {
75 if (pw != NULL && gr != NULL && chown(file, pw->pw_uid, gr->gr_gid) != 0)
76 _error->WarningE(requester, "chown to %s:%s of file %s failed", user, group, file);
77 }
78 if (chmod(file, mode) != 0)
79 _error->WarningE(requester, "chmod 0%o of file %s failed", mode, file);
80 }
81 static std::string GetPartialFileName(std::string const &file)
82 {
83 std::string DestFile = _config->FindDir("Dir::State::lists") + "partial/";
84 DestFile += file;
85 return DestFile;
86 }
87 static std::string GetPartialFileNameFromURI(std::string const &uri)
88 {
89 return GetPartialFileName(URItoFileName(uri));
90 }
91
92
93 // Acquire::Item::Item - Constructor /*{{{*/
94 #if __GNUC__ >= 4
95 #pragma GCC diagnostic push
96 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
97 #endif
98 pkgAcquire::Item::Item(pkgAcquire *Owner,
99 HashStringList const &ExpectedHashes,
100 pkgAcqMetaBase *TransactionManager)
101 : Owner(Owner), FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false),
102 Local(false), QueueCounter(0), TransactionManager(TransactionManager),
103 ExpectedAdditionalItems(0), ExpectedHashes(ExpectedHashes)
104 {
105 Owner->Add(this);
106 Status = StatIdle;
107 if(TransactionManager != NULL)
108 TransactionManager->Add(this);
109 }
110 #if __GNUC__ >= 4
111 #pragma GCC diagnostic pop
112 #endif
113 /*}}}*/
114 // Acquire::Item::~Item - Destructor /*{{{*/
115 // ---------------------------------------------------------------------
116 /* */
117 pkgAcquire::Item::~Item()
118 {
119 Owner->Remove(this);
120 }
121 /*}}}*/
122 // Acquire::Item::Failed - Item failed to download /*{{{*/
123 // ---------------------------------------------------------------------
124 /* We return to an idle state if there are still other queues that could
125 fetch this object */
126 void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
127 {
128 if(ErrorText == "")
129 ErrorText = LookupTag(Message,"Message");
130 UsedMirror = LookupTag(Message,"UsedMirror");
131 if (QueueCounter <= 1)
132 {
133 /* This indicates that the file is not available right now but might
134 be sometime later. If we do a retry cycle then this should be
135 retried [CDROMs] */
136 if (Cnf != NULL && Cnf->LocalOnly == true &&
137 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
138 {
139 Status = StatIdle;
140 Dequeue();
141 return;
142 }
143
144 Status = StatError;
145 Complete = false;
146 Dequeue();
147 }
148 else
149 Status = StatIdle;
150
151 // report mirror failure back to LP if we actually use a mirror
152 string FailReason = LookupTag(Message, "FailReason");
153 if(FailReason.size() != 0)
154 ReportMirrorFailure(FailReason);
155 else
156 ReportMirrorFailure(ErrorText);
157 }
158 /*}}}*/
159 // Acquire::Item::Start - Item has begun to download /*{{{*/
160 // ---------------------------------------------------------------------
161 /* Stash status and the file size. Note that setting Complete means
162 sub-phases of the acquire process such as decompresion are operating */
163 void pkgAcquire::Item::Start(string /*Message*/,unsigned long long Size)
164 {
165 Status = StatFetching;
166 if (FileSize == 0 && Complete == false)
167 FileSize = Size;
168 }
169 /*}}}*/
170 // Acquire::Item::Done - Item downloaded OK /*{{{*/
171 // ---------------------------------------------------------------------
172 /* */
173 void pkgAcquire::Item::Done(string Message,unsigned long long Size,HashStringList const &/*Hash*/,
174 pkgAcquire::MethodConfig * /*Cnf*/)
175 {
176 // We just downloaded something..
177 string FileName = LookupTag(Message,"Filename");
178 UsedMirror = LookupTag(Message,"UsedMirror");
179 if (Complete == false && !Local && FileName == DestFile)
180 {
181 if (Owner->Log != 0)
182 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
183 }
184
185 if (FileSize == 0)
186 FileSize= Size;
187 Status = StatDone;
188 ErrorText = string();
189 Owner->Dequeue(this);
190 }
191 /*}}}*/
192 // Acquire::Item::Rename - Rename a file /*{{{*/
193 // ---------------------------------------------------------------------
194 /* This helper function is used by a lot of item methods as their final
195 step */
196 bool pkgAcquire::Item::Rename(string From,string To)
197 {
198 if (rename(From.c_str(),To.c_str()) != 0)
199 {
200 char S[300];
201 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
202 From.c_str(),To.c_str());
203 Status = StatError;
204 ErrorText += S;
205 return false;
206 }
207 return true;
208 }
209 /*}}}*/
210
211 void pkgAcquire::Item::QueueURI(ItemDesc &Item)
212 {
213 if (RealFileExists(DestFile))
214 ChangeOwnerAndPermissionOfFile("GetPartialFileName", DestFile.c_str(), "_apt", "root", 0600);
215 Owner->Enqueue(Item);
216 }
217 void pkgAcquire::Item::Dequeue()
218 {
219 Owner->Dequeue(this);
220 }
221
222 bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
223 {
224 if(FileExists(DestFile))
225 Rename(DestFile, DestFile + ".FAILED");
226
227 switch (error)
228 {
229 case HashSumMismatch:
230 ErrorText = _("Hash Sum mismatch");
231 Status = StatAuthError;
232 ReportMirrorFailure("HashChecksumFailure");
233 break;
234 case SizeMismatch:
235 ErrorText = _("Size mismatch");
236 Status = StatAuthError;
237 ReportMirrorFailure("SizeFailure");
238 break;
239 case InvalidFormat:
240 ErrorText = _("Invalid file format");
241 Status = StatError;
242 // do not report as usually its not the mirrors fault, but Portal/Proxy
243 break;
244 case SignatureError:
245 ErrorText = _("Signature error");
246 Status = StatError;
247 break;
248 case NotClearsigned:
249 ErrorText = _("Does not start with a cleartext signature");
250 Status = StatError;
251 break;
252 }
253 return false;
254 }
255 /*}}}*/
256 void pkgAcquire::Item::SetActiveSubprocess(const std::string &subprocess)/*{{{*/
257 {
258 ActiveSubprocess = subprocess;
259 #if __GNUC__ >= 4
260 #pragma GCC diagnostic push
261 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
262 #endif
263 Mode = ActiveSubprocess.c_str();
264 #if __GNUC__ >= 4
265 #pragma GCC diagnostic pop
266 #endif
267 }
268 /*}}}*/
269 // Acquire::Item::ReportMirrorFailure /*{{{*/
270 // ---------------------------------------------------------------------
271 void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
272 {
273 // we only act if a mirror was used at all
274 if(UsedMirror.empty())
275 return;
276 #if 0
277 std::cerr << "\nReportMirrorFailure: "
278 << UsedMirror
279 << " Uri: " << DescURI()
280 << " FailCode: "
281 << FailCode << std::endl;
282 #endif
283 const char *Args[40];
284 unsigned int i = 0;
285 string report = _config->Find("Methods::Mirror::ProblemReporting",
286 "/usr/lib/apt/apt-report-mirror-failure");
287 if(!FileExists(report))
288 return;
289 Args[i++] = report.c_str();
290 Args[i++] = UsedMirror.c_str();
291 Args[i++] = DescURI().c_str();
292 Args[i++] = FailCode.c_str();
293 Args[i++] = NULL;
294 pid_t pid = ExecFork();
295 if(pid < 0)
296 {
297 _error->Error("ReportMirrorFailure Fork failed");
298 return;
299 }
300 else if(pid == 0)
301 {
302 execvp(Args[0], (char**)Args);
303 std::cerr << "Could not exec " << Args[0] << std::endl;
304 _exit(100);
305 }
306 if(!ExecWait(pid, "report-mirror-failure"))
307 {
308 _error->Warning("Couldn't report problem to '%s'",
309 _config->Find("Methods::Mirror::ProblemReporting").c_str());
310 }
311 }
312 /*}}}*/
313 // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
314 // ---------------------------------------------------------------------
315 /* Get the DiffIndex file first and see if there are patches available
316 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
317 * patches. If anything goes wrong in that process, it will fall back to
318 * the original packages file
319 */
320 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
321 pkgAcqMetaBase *TransactionManager,
322 IndexTarget const * const Target,
323 HashStringList const &ExpectedHashes,
324 indexRecords *MetaIndexParser)
325 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHashes,
326 MetaIndexParser), PackagesFileReadyInPartial(false)
327 {
328
329 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
330
331 RealURI = Target->URI;
332 Desc.Owner = this;
333 Desc.Description = Target->Description + ".diff/Index";
334 Desc.ShortDesc = Target->ShortDesc;
335 Desc.URI = Target->URI + ".diff/Index";
336
337 DestFile = GetPartialFileNameFromURI(Desc.URI);
338
339 if(Debug)
340 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
341
342 // look for the current package file
343 CurrentPackagesFile = _config->FindDir("Dir::State::lists");
344 CurrentPackagesFile += URItoFileName(RealURI);
345
346 // FIXME: this file:/ check is a hack to prevent fetching
347 // from local sources. this is really silly, and
348 // should be fixed cleanly as soon as possible
349 if(!FileExists(CurrentPackagesFile) ||
350 Desc.URI.substr(0,strlen("file:/")) == "file:/")
351 {
352 // we don't have a pkg file or we don't want to queue
353 Failed("No index file, local or canceld by user", NULL);
354 return;
355 }
356
357 if(Debug)
358 std::clog << "pkgAcqDiffIndex::pkgAcqDiffIndex(): "
359 << CurrentPackagesFile << std::endl;
360
361 QueueURI(Desc);
362
363 }
364 /*}}}*/
365 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
366 // ---------------------------------------------------------------------
367 /* The only header we use is the last-modified header. */
368 string pkgAcqDiffIndex::Custom600Headers() const
369 {
370 string Final = _config->FindDir("Dir::State::lists");
371 Final += URItoFileName(Desc.URI);
372
373 if(Debug)
374 std::clog << "Custom600Header-IMS: " << Final << std::endl;
375
376 struct stat Buf;
377 if (stat(Final.c_str(),&Buf) != 0)
378 return "\nIndex-File: true";
379
380 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
381 }
382 /*}}}*/
383 bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) /*{{{*/
384 {
385 // failing here is fine: our caller will take care of trying to
386 // get the complete file if patching fails
387 if(Debug)
388 std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
389 << std::endl;
390
391 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
392 pkgTagFile TF(&Fd);
393 if (_error->PendingError() == true)
394 return false;
395
396 pkgTagSection Tags;
397 if(unlikely(TF.Step(Tags) == false))
398 return false;
399
400 HashStringList ServerHashes;
401 unsigned long long ServerSize = 0;
402
403 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
404 {
405 std::string tagname = *type;
406 tagname.append("-Current");
407 std::string const tmp = Tags.FindS(tagname.c_str());
408 if (tmp.empty() == true)
409 continue;
410
411 string hash;
412 unsigned long long size;
413 std::stringstream ss(tmp);
414 ss >> hash >> size;
415 if (unlikely(hash.empty() == true))
416 continue;
417 if (unlikely(ServerSize != 0 && ServerSize != size))
418 continue;
419 ServerHashes.push_back(HashString(*type, hash));
420 ServerSize = size;
421 }
422
423 if (ServerHashes.usable() == false)
424 {
425 if (Debug == true)
426 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Did not find a good hashsum in the index" << std::endl;
427 return false;
428 }
429
430 if (ServerHashes != HashSums())
431 {
432 if (Debug == true)
433 {
434 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl;
435 printHashSumComparision(CurrentPackagesFile, ServerHashes, HashSums());
436 }
437 return false;
438 }
439
440 if (ServerHashes.VerifyFile(CurrentPackagesFile) == true)
441 {
442 // we have the same sha1 as the server so we are done here
443 if(Debug)
444 std::clog << "pkgAcqDiffIndex: Package file " << CurrentPackagesFile << " is up-to-date" << std::endl;
445
446 // list cleanup needs to know that this file as well as the already
447 // present index is ours, so we create an empty diff to save it for us
448 new pkgAcqIndexDiffs(Owner, TransactionManager, Target,
449 ExpectedHashes, MetaIndexParser);
450 return true;
451 }
452
453 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
454 Hashes LocalHashesCalc;
455 LocalHashesCalc.AddFD(fd);
456 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
457
458 if(Debug)
459 std::clog << "Server-Current: " << ServerHashes.find(NULL)->toStr() << " and we start at "
460 << fd.Name() << " " << fd.FileSize() << " " << LocalHashes.find(NULL)->toStr() << std::endl;
461
462 // parse all of (provided) history
463 vector<DiffInfo> available_patches;
464 bool firstAcceptedHashes = true;
465 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
466 {
467 if (LocalHashes.find(*type) == NULL)
468 continue;
469
470 std::string tagname = *type;
471 tagname.append("-History");
472 std::string const tmp = Tags.FindS(tagname.c_str());
473 if (tmp.empty() == true)
474 continue;
475
476 string hash, filename;
477 unsigned long long size;
478 std::stringstream ss(tmp);
479
480 while (ss >> hash >> size >> filename)
481 {
482 if (unlikely(hash.empty() == true || filename.empty() == true))
483 continue;
484
485 // see if we have a record for this file already
486 std::vector<DiffInfo>::iterator cur = available_patches.begin();
487 for (; cur != available_patches.end(); ++cur)
488 {
489 if (cur->file != filename || unlikely(cur->result_size != size))
490 continue;
491 cur->result_hashes.push_back(HashString(*type, hash));
492 break;
493 }
494 if (cur != available_patches.end())
495 continue;
496 if (firstAcceptedHashes == true)
497 {
498 DiffInfo next;
499 next.file = filename;
500 next.result_hashes.push_back(HashString(*type, hash));
501 next.result_size = size;
502 next.patch_size = 0;
503 available_patches.push_back(next);
504 }
505 else
506 {
507 if (Debug == true)
508 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
509 << " wasn't in the list for the first parsed hash! (history)" << std::endl;
510 break;
511 }
512 }
513 firstAcceptedHashes = false;
514 }
515
516 if (unlikely(available_patches.empty() == true))
517 {
518 if (Debug)
519 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
520 << "Couldn't find any patches for the patch series." << std::endl;
521 return false;
522 }
523
524 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
525 {
526 if (LocalHashes.find(*type) == NULL)
527 continue;
528
529 std::string tagname = *type;
530 tagname.append("-Patches");
531 std::string const tmp = Tags.FindS(tagname.c_str());
532 if (tmp.empty() == true)
533 continue;
534
535 string hash, filename;
536 unsigned long long size;
537 std::stringstream ss(tmp);
538
539 while (ss >> hash >> size >> filename)
540 {
541 if (unlikely(hash.empty() == true || filename.empty() == true))
542 continue;
543
544 // see if we have a record for this file already
545 std::vector<DiffInfo>::iterator cur = available_patches.begin();
546 for (; cur != available_patches.end(); ++cur)
547 {
548 if (cur->file != filename)
549 continue;
550 if (unlikely(cur->patch_size != 0 && cur->patch_size != size))
551 continue;
552 cur->patch_hashes.push_back(HashString(*type, hash));
553 cur->patch_size = size;
554 break;
555 }
556 if (cur != available_patches.end())
557 continue;
558 if (Debug == true)
559 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
560 << " wasn't in the list for the first parsed hash! (patches)" << std::endl;
561 break;
562 }
563 }
564
565 bool foundStart = false;
566 for (std::vector<DiffInfo>::iterator cur = available_patches.begin();
567 cur != available_patches.end(); ++cur)
568 {
569 if (LocalHashes != cur->result_hashes)
570 continue;
571
572 available_patches.erase(available_patches.begin(), cur);
573 foundStart = true;
574 break;
575 }
576
577 if (foundStart == false || unlikely(available_patches.empty() == true))
578 {
579 if (Debug)
580 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
581 << "Couldn't find the start of the patch series." << std::endl;
582 return false;
583 }
584
585 // patching with too many files is rather slow compared to a fast download
586 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
587 if (fileLimit != 0 && fileLimit < available_patches.size())
588 {
589 if (Debug)
590 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
591 << ") so fallback to complete download" << std::endl;
592 return false;
593 }
594
595 // calculate the size of all patches we have to get
596 // note that all sizes are uncompressed, while we download compressed files
597 unsigned long long patchesSize = 0;
598 for (std::vector<DiffInfo>::const_iterator cur = available_patches.begin();
599 cur != available_patches.end(); ++cur)
600 patchesSize += cur->patch_size;
601 unsigned long long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
602 if (false && sizeLimit > 0 && (sizeLimit/100) < patchesSize)
603 {
604 if (Debug)
605 std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
606 << ") so fallback to complete download" << std::endl;
607 return false;
608 }
609
610 // FIXME: make this use the method
611 PackagesFileReadyInPartial = true;
612 std::string const Partial = GetPartialFileNameFromURI(RealURI);
613
614 FileFd From(CurrentPackagesFile, FileFd::ReadOnly);
615 FileFd To(Partial, FileFd::WriteEmpty);
616 if(CopyFile(From, To) == false)
617 return _error->Errno("CopyFile", "failed to copy");
618
619 if(Debug)
620 std::cerr << "Done copying " << CurrentPackagesFile
621 << " -> " << Partial
622 << std::endl;
623
624 // we have something, queue the diffs
625 string::size_type const last_space = Description.rfind(" ");
626 if(last_space != string::npos)
627 Description.erase(last_space, Description.size()-last_space);
628
629 /* decide if we should download patches one by one or in one go:
630 The first is good if the server merges patches, but many don't so client
631 based merging can be attempt in which case the second is better.
632 "bad things" will happen if patches are merged on the server,
633 but client side merging is attempt as well */
634 bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
635 if (pdiff_merge == true)
636 {
637 // reprepro adds this flag if it has merged patches on the server
638 std::string const precedence = Tags.FindS("X-Patch-Precedence");
639 pdiff_merge = (precedence != "merged");
640 }
641
642 if (pdiff_merge == false)
643 {
644 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, ExpectedHashes,
645 MetaIndexParser, available_patches);
646 }
647 else
648 {
649 std::vector<pkgAcqIndexMergeDiffs*> *diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
650 for(size_t i = 0; i < available_patches.size(); ++i)
651 (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, TransactionManager,
652 Target,
653 ExpectedHashes,
654 MetaIndexParser,
655 available_patches[i],
656 diffs);
657 }
658
659 Complete = false;
660 Status = StatDone;
661 Dequeue();
662 return true;
663 }
664 /*}}}*/
665 void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * Cnf)/*{{{*/
666 {
667 if(Debug)
668 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
669 << "Falling back to normal index file acquire" << std::endl;
670
671 new pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser);
672
673 Item::Failed(Message,Cnf);
674 Status = StatDone;
675 }
676 /*}}}*/
677 void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
678 pkgAcquire::MethodConfig *Cnf)
679 {
680 if(Debug)
681 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
682
683 Item::Done(Message, Size, Hashes, Cnf);
684
685 // verify the index target
686 if(Target && Target->MetaKey != "" && MetaIndexParser && Hashes.usable())
687 {
688 std::string IndexMetaKey = Target->MetaKey + ".diff/Index";
689 indexRecords::checkSum *Record = MetaIndexParser->Lookup(IndexMetaKey);
690 if(Record && Record->Hashes.usable() && Hashes != Record->Hashes)
691 {
692 RenameOnError(HashSumMismatch);
693 printHashSumComparision(RealURI, Record->Hashes, Hashes);
694 Failed(Message, Cnf);
695 return;
696 }
697
698 }
699
700 string FinalFile;
701 FinalFile = _config->FindDir("Dir::State::lists");
702 FinalFile += URItoFileName(Desc.URI);
703
704 if(StringToBool(LookupTag(Message,"IMS-Hit"),false))
705 DestFile = FinalFile;
706
707 if(!ParseDiffIndex(DestFile))
708 return Failed("Message: Couldn't parse pdiff index", Cnf);
709
710 // queue for final move
711 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
712
713 Complete = true;
714 Status = StatDone;
715 Dequeue();
716 return;
717 }
718 /*}}}*/
719 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
720 // ---------------------------------------------------------------------
721 /* The package diff is added to the queue. one object is constructed
722 * for each diff and the index
723 */
724 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
725 pkgAcqMetaBase *TransactionManager,
726 struct IndexTarget const * const Target,
727 HashStringList const &ExpectedHashes,
728 indexRecords *MetaIndexParser,
729 vector<DiffInfo> diffs)
730 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser),
731 available_patches(diffs)
732 {
733 DestFile = GetPartialFileNameFromURI(Target->URI);
734
735 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
736
737 RealURI = Target->URI;
738 Desc.Owner = this;
739 Description = Target->Description;
740 Desc.ShortDesc = Target->ShortDesc;
741
742 if(available_patches.empty() == true)
743 {
744 // we are done (yeah!), check hashes against the final file
745 DestFile = _config->FindDir("Dir::State::lists");
746 DestFile += URItoFileName(Target->URI);
747 Finish(true);
748 }
749 else
750 {
751 // get the next diff
752 State = StateFetchDiff;
753 QueueNextDiff();
754 }
755 }
756 /*}}}*/
757 void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
758 {
759 if(Debug)
760 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
761 << "Falling back to normal index file acquire" << std::endl;
762 new pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser);
763 Finish();
764 }
765 /*}}}*/
766 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
767 void pkgAcqIndexDiffs::Finish(bool allDone)
768 {
769 if(Debug)
770 std::clog << "pkgAcqIndexDiffs::Finish(): "
771 << allDone << " "
772 << Desc.URI << std::endl;
773
774 // we restore the original name, this is required, otherwise
775 // the file will be cleaned
776 if(allDone)
777 {
778 if(HashSums().usable() && !HashSums().VerifyFile(DestFile))
779 {
780 RenameOnError(HashSumMismatch);
781 Dequeue();
782 return;
783 }
784
785 // queue for copy
786 std::string FinalFile = _config->FindDir("Dir::State::lists");
787 FinalFile += URItoFileName(RealURI);
788 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
789
790 // this is for the "real" finish
791 Complete = true;
792 Status = StatDone;
793 Dequeue();
794 if(Debug)
795 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
796 return;
797 }
798
799 if(Debug)
800 std::clog << "Finishing: " << Desc.URI << std::endl;
801 Complete = false;
802 Status = StatDone;
803 Dequeue();
804 return;
805 }
806 /*}}}*/
807 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
808 {
809 // calc sha1 of the just patched file
810 std::string const FinalFile = GetPartialFileNameFromURI(RealURI);
811
812 if(!FileExists(FinalFile))
813 {
814 Failed("Message: No FinalFile " + FinalFile + " available", NULL);
815 return false;
816 }
817
818 FileFd fd(FinalFile, FileFd::ReadOnly);
819 Hashes LocalHashesCalc;
820 LocalHashesCalc.AddFD(fd);
821 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
822
823 if(Debug)
824 std::clog << "QueueNextDiff: " << FinalFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
825
826 if (unlikely(LocalHashes.usable() == false || ExpectedHashes.usable() == false))
827 {
828 Failed("Local/Expected hashes are not usable", NULL);
829 return false;
830 }
831
832
833 // final file reached before all patches are applied
834 if(LocalHashes == ExpectedHashes)
835 {
836 Finish(true);
837 return true;
838 }
839
840 // remove all patches until the next matching patch is found
841 // this requires the Index file to be ordered
842 for(vector<DiffInfo>::iterator I = available_patches.begin();
843 available_patches.empty() == false &&
844 I != available_patches.end() &&
845 I->result_hashes != LocalHashes;
846 ++I)
847 {
848 available_patches.erase(I);
849 }
850
851 // error checking and falling back if no patch was found
852 if(available_patches.empty() == true)
853 {
854 Failed("No patches left to reach target", NULL);
855 return false;
856 }
857
858 // queue the right diff
859 Desc.URI = RealURI + ".diff/" + available_patches[0].file + ".gz";
860 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
861 DestFile = GetPartialFileNameFromURI(RealURI + ".diff/" + available_patches[0].file);
862
863 if(Debug)
864 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
865
866 QueueURI(Desc);
867
868 return true;
869 }
870 /*}}}*/
871 void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size, HashStringList const &Hashes, /*{{{*/
872 pkgAcquire::MethodConfig *Cnf)
873 {
874 if(Debug)
875 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
876
877 Item::Done(Message, Size, Hashes, Cnf);
878
879 // FIXME: verify this download too before feeding it to rred
880 std::string const FinalFile = GetPartialFileNameFromURI(RealURI);
881
882 // success in downloading a diff, enter ApplyDiff state
883 if(State == StateFetchDiff)
884 {
885 FileFd fd(DestFile, FileFd::ReadOnly, FileFd::Gzip);
886 class Hashes LocalHashesCalc;
887 LocalHashesCalc.AddFD(fd);
888 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
889
890 if (fd.Size() != available_patches[0].patch_size ||
891 available_patches[0].patch_hashes != LocalHashes)
892 {
893 Failed("Patch has Size/Hashsum mismatch", NULL);
894 return;
895 }
896
897 // rred excepts the patch as $FinalFile.ed
898 Rename(DestFile,FinalFile+".ed");
899
900 if(Debug)
901 std::clog << "Sending to rred method: " << FinalFile << std::endl;
902
903 State = StateApplyDiff;
904 Local = true;
905 Desc.URI = "rred:" + FinalFile;
906 QueueURI(Desc);
907 SetActiveSubprocess("rred");
908 return;
909 }
910
911
912 // success in download/apply a diff, queue next (if needed)
913 if(State == StateApplyDiff)
914 {
915 // remove the just applied patch
916 available_patches.erase(available_patches.begin());
917 unlink((FinalFile + ".ed").c_str());
918
919 // move into place
920 if(Debug)
921 {
922 std::clog << "Moving patched file in place: " << std::endl
923 << DestFile << " -> " << FinalFile << std::endl;
924 }
925 Rename(DestFile,FinalFile);
926 chmod(FinalFile.c_str(),0644);
927
928 // see if there is more to download
929 if(available_patches.empty() == false) {
930 new pkgAcqIndexDiffs(Owner, TransactionManager, Target,
931 ExpectedHashes, MetaIndexParser,
932 available_patches);
933 return Finish();
934 } else
935 // update
936 DestFile = FinalFile;
937 return Finish(true);
938 }
939 }
940 /*}}}*/
941 // AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
942 pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire *Owner,
943 pkgAcqMetaBase *TransactionManager,
944 struct IndexTarget const * const Target,
945 HashStringList const &ExpectedHashes,
946 indexRecords *MetaIndexParser,
947 DiffInfo const &patch,
948 std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
949 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser),
950 patch(patch), allPatches(allPatches), State(StateFetchDiff)
951 {
952 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
953
954 RealURI = Target->URI;
955 Desc.Owner = this;
956 Description = Target->Description;
957 Desc.ShortDesc = Target->ShortDesc;
958
959 Desc.URI = RealURI + ".diff/" + patch.file + ".gz";
960 Desc.Description = Description + " " + patch.file + string(".pdiff");
961
962 DestFile = GetPartialFileNameFromURI(RealURI + ".diff/" + patch.file);
963
964 if(Debug)
965 std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
966
967 QueueURI(Desc);
968 }
969 /*}}}*/
970 void pkgAcqIndexMergeDiffs::Failed(string Message,pkgAcquire::MethodConfig * Cnf)/*{{{*/
971 {
972 if(Debug)
973 std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
974
975 Item::Failed(Message,Cnf);
976 Status = StatDone;
977
978 // check if we are the first to fail, otherwise we are done here
979 State = StateDoneDiff;
980 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
981 I != allPatches->end(); ++I)
982 if ((*I)->State == StateErrorDiff)
983 return;
984
985 // first failure means we should fallback
986 State = StateErrorDiff;
987 std::clog << "Falling back to normal index file acquire" << std::endl;
988 new pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser);
989 }
990 /*}}}*/
991 void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
992 pkgAcquire::MethodConfig *Cnf)
993 {
994 if(Debug)
995 std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
996
997 Item::Done(Message,Size,Hashes,Cnf);
998
999 // FIXME: verify download before feeding it to rred
1000 string const FinalFile = GetPartialFileNameFromURI(RealURI);
1001
1002 if (State == StateFetchDiff)
1003 {
1004 FileFd fd(DestFile, FileFd::ReadOnly, FileFd::Gzip);
1005 class Hashes LocalHashesCalc;
1006 LocalHashesCalc.AddFD(fd);
1007 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
1008
1009 if (fd.Size() != patch.patch_size || patch.patch_hashes != LocalHashes)
1010 {
1011 Failed("Patch has Size/Hashsum mismatch", NULL);
1012 return;
1013 }
1014
1015 // rred expects the patch as $FinalFile.ed.$patchname.gz
1016 Rename(DestFile, FinalFile + ".ed." + patch.file + ".gz");
1017
1018 // check if this is the last completed diff
1019 State = StateDoneDiff;
1020 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
1021 I != allPatches->end(); ++I)
1022 if ((*I)->State != StateDoneDiff)
1023 {
1024 if(Debug)
1025 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
1026 return;
1027 }
1028
1029 // this is the last completed diff, so we are ready to apply now
1030 State = StateApplyDiff;
1031
1032 if(Debug)
1033 std::clog << "Sending to rred method: " << FinalFile << std::endl;
1034
1035 Local = true;
1036 Desc.URI = "rred:" + FinalFile;
1037 QueueURI(Desc);
1038 SetActiveSubprocess("rred");
1039 return;
1040 }
1041 // success in download/apply all diffs, clean up
1042 else if (State == StateApplyDiff)
1043 {
1044 // see if we really got the expected file
1045 if(ExpectedHashes.usable() && !ExpectedHashes.VerifyFile(DestFile))
1046 {
1047 RenameOnError(HashSumMismatch);
1048 return;
1049 }
1050
1051
1052 std::string FinalFile = _config->FindDir("Dir::State::lists");
1053 FinalFile += URItoFileName(RealURI);
1054
1055 // move the result into place
1056 if(Debug)
1057 std::clog << "Queue patched file in place: " << std::endl
1058 << DestFile << " -> " << FinalFile << std::endl;
1059
1060 // queue for copy by the transaction manager
1061 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
1062
1063 // ensure the ed's are gone regardless of list-cleanup
1064 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
1065 I != allPatches->end(); ++I)
1066 {
1067 std::string const PartialFile = GetPartialFileNameFromURI(RealURI);
1068 std::string patch = PartialFile + ".ed." + (*I)->patch.file + ".gz";
1069 std::cerr << patch << std::endl;
1070 unlink(patch.c_str());
1071 }
1072
1073 // all set and done
1074 Complete = true;
1075 if(Debug)
1076 std::clog << "allDone: " << DestFile << "\n" << std::endl;
1077 }
1078 }
1079 /*}}}*/
1080 // AcqBaseIndex::VerifyHashByMetaKey - verify hash for the given metakey /*{{{*/
1081 bool pkgAcqBaseIndex::VerifyHashByMetaKey(HashStringList const &Hashes)
1082 {
1083 if(MetaKey != "" && Hashes.usable())
1084 {
1085 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1086 if(Record && Record->Hashes.usable() && Hashes != Record->Hashes)
1087 {
1088 printHashSumComparision(RealURI, Record->Hashes, Hashes);
1089 return false;
1090 }
1091 }
1092 return true;
1093 }
1094 /*}}}*/
1095 // AcqIndex::AcqIndex - Constructor /*{{{*/
1096 // ---------------------------------------------------------------------
1097 /* The package file is added to the queue and a second class is
1098 instantiated to fetch the revision file */
1099 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
1100 string URI,string URIDesc,string ShortDesc,
1101 HashStringList const &ExpectedHash)
1102 : pkgAcqBaseIndex(Owner, 0, NULL, ExpectedHash, NULL)
1103 {
1104 RealURI = URI;
1105
1106 AutoSelectCompression();
1107 Init(URI, URIDesc, ShortDesc);
1108
1109 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1110 std::clog << "New pkgIndex with TransactionManager "
1111 << TransactionManager << std::endl;
1112 }
1113 /*}}}*/
1114 // AcqIndex::AcqIndex - Constructor /*{{{*/
1115 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
1116 pkgAcqMetaBase *TransactionManager,
1117 IndexTarget const *Target,
1118 HashStringList const &ExpectedHash,
1119 indexRecords *MetaIndexParser)
1120 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHash,
1121 MetaIndexParser)
1122 {
1123 RealURI = Target->URI;
1124
1125 // autoselect the compression method
1126 AutoSelectCompression();
1127 Init(Target->URI, Target->Description, Target->ShortDesc);
1128
1129 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1130 std::clog << "New pkgIndex with TransactionManager "
1131 << TransactionManager << std::endl;
1132 }
1133 /*}}}*/
1134 // AcqIndex::AutoSelectCompression - Select compression /*{{{*/
1135 void pkgAcqIndex::AutoSelectCompression()
1136 {
1137 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
1138 CompressionExtensions = "";
1139 if (ExpectedHashes.usable())
1140 {
1141 for (std::vector<std::string>::const_iterator t = types.begin();
1142 t != types.end(); ++t)
1143 {
1144 std::string CompressedMetaKey = string(Target->MetaKey).append(".").append(*t);
1145 if (*t == "uncompressed" ||
1146 MetaIndexParser->Exists(CompressedMetaKey) == true)
1147 CompressionExtensions.append(*t).append(" ");
1148 }
1149 }
1150 else
1151 {
1152 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
1153 CompressionExtensions.append(*t).append(" ");
1154 }
1155 if (CompressionExtensions.empty() == false)
1156 CompressionExtensions.erase(CompressionExtensions.end()-1);
1157 }
1158 /*}}}*/
1159 // AcqIndex::Init - defered Constructor /*{{{*/
1160 void pkgAcqIndex::Init(string const &URI, string const &URIDesc,
1161 string const &ShortDesc)
1162 {
1163 Stage = STAGE_DOWNLOAD;
1164
1165 DestFile = GetPartialFileNameFromURI(URI);
1166
1167 CurrentCompressionExtension = CompressionExtensions.substr(0, CompressionExtensions.find(' '));
1168 if (CurrentCompressionExtension == "uncompressed")
1169 {
1170 Desc.URI = URI;
1171 if(Target)
1172 MetaKey = string(Target->MetaKey);
1173 }
1174 else
1175 {
1176 Desc.URI = URI + '.' + CurrentCompressionExtension;
1177 DestFile = DestFile + '.' + CurrentCompressionExtension;
1178 if(Target)
1179 MetaKey = string(Target->MetaKey) + '.' + CurrentCompressionExtension;
1180 }
1181
1182 // load the filesize
1183 if(MetaIndexParser)
1184 {
1185 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1186 if(Record)
1187 FileSize = Record->Size;
1188
1189 InitByHashIfNeeded(MetaKey);
1190 }
1191
1192 Desc.Description = URIDesc;
1193 Desc.Owner = this;
1194 Desc.ShortDesc = ShortDesc;
1195
1196 QueueURI(Desc);
1197 }
1198 /*}}}*/
1199 // AcqIndex::AdjustForByHash - modify URI for by-hash support /*{{{*/
1200 void pkgAcqIndex::InitByHashIfNeeded(const std::string MetaKey)
1201 {
1202 // TODO:
1203 // - (maybe?) add support for by-hash into the sources.list as flag
1204 // - make apt-ftparchive generate the hashes (and expire?)
1205 std::string HostKnob = "APT::Acquire::" + ::URI(Desc.URI).Host + "::By-Hash";
1206 if(_config->FindB("APT::Acquire::By-Hash", false) == true ||
1207 _config->FindB(HostKnob, false) == true ||
1208 MetaIndexParser->GetSupportsAcquireByHash())
1209 {
1210 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1211 if(Record)
1212 {
1213 // FIXME: should we really use the best hash here? or a fixed one?
1214 const HashString *TargetHash = Record->Hashes.find("");
1215 std::string ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
1216 size_t trailing_slash = Desc.URI.find_last_of("/");
1217 Desc.URI = Desc.URI.replace(
1218 trailing_slash,
1219 Desc.URI.substr(trailing_slash+1).size()+1,
1220 ByHash);
1221 } else {
1222 _error->Warning(
1223 "Fetching ByHash requested but can not find record for %s",
1224 MetaKey.c_str());
1225 }
1226 }
1227 }
1228 /*}}}*/
1229 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
1230 // ---------------------------------------------------------------------
1231 /* The only header we use is the last-modified header. */
1232 string pkgAcqIndex::Custom600Headers() const
1233 {
1234 string Final = GetFinalFilename();
1235
1236 string msg = "\nIndex-File: true";
1237 struct stat Buf;
1238 if (stat(Final.c_str(),&Buf) == 0)
1239 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1240
1241 return msg;
1242 }
1243 /*}}}*/
1244 // pkgAcqIndex::Failed - getting the indexfile failed /*{{{*/
1245 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1246 {
1247 size_t const nextExt = CompressionExtensions.find(' ');
1248 if (nextExt != std::string::npos)
1249 {
1250 CompressionExtensions = CompressionExtensions.substr(nextExt+1);
1251 Init(RealURI, Desc.Description, Desc.ShortDesc);
1252 return;
1253 }
1254
1255 // on decompression failure, remove bad versions in partial/
1256 if (Stage == STAGE_DECOMPRESS_AND_VERIFY)
1257 {
1258 unlink(EraseFileName.c_str());
1259 }
1260
1261 Item::Failed(Message,Cnf);
1262
1263 /// cancel the entire transaction
1264 TransactionManager->AbortTransaction();
1265 }
1266 /*}}}*/
1267 // pkgAcqIndex::GetFinalFilename - Return the full final file path /*{{{*/
1268 std::string pkgAcqIndex::GetFinalFilename() const
1269 {
1270 std::string FinalFile = _config->FindDir("Dir::State::lists");
1271 FinalFile += URItoFileName(RealURI);
1272 if (_config->FindB("Acquire::GzipIndexes",false) == true)
1273 FinalFile += '.' + CurrentCompressionExtension;
1274 return FinalFile;
1275 }
1276 /*}}}*/
1277 // AcqIndex::ReverifyAfterIMS - Reverify index after an ims-hit /*{{{*/
1278 void pkgAcqIndex::ReverifyAfterIMS()
1279 {
1280 // update destfile to *not* include the compression extension when doing
1281 // a reverify (as its uncompressed on disk already)
1282 DestFile = GetPartialFileNameFromURI(RealURI);
1283
1284 // adjust DestFile if its compressed on disk
1285 if (_config->FindB("Acquire::GzipIndexes",false) == true)
1286 DestFile += '.' + CurrentCompressionExtension;
1287
1288 // copy FinalFile into partial/ so that we check the hash again
1289 string FinalFile = GetFinalFilename();
1290 Stage = STAGE_DECOMPRESS_AND_VERIFY;
1291 Desc.URI = "copy:" + FinalFile;
1292 QueueURI(Desc);
1293 }
1294 /*}}}*/
1295 // AcqIndex::ValidateFile - Validate the content of the downloaded file /*{{{*/
1296 bool pkgAcqIndex::ValidateFile(const std::string &FileName)
1297 {
1298 // FIXME: this can go away once we only ever download stuff that
1299 // has a valid hash and we never do GET based probing
1300 // FIXME2: this also leaks debian-isms into the code and should go therefore
1301
1302 /* Always validate the index file for correctness (all indexes must
1303 * have a Package field) (LP: #346386) (Closes: #627642)
1304 */
1305 FileFd fd(FileName, FileFd::ReadOnly, FileFd::Extension);
1306 // Only test for correctness if the content of the file is not empty
1307 // (empty is ok)
1308 if (fd.Size() > 0)
1309 {
1310 pkgTagSection sec;
1311 pkgTagFile tag(&fd);
1312
1313 // all our current indexes have a field 'Package' in each section
1314 if (_error->PendingError() == true ||
1315 tag.Step(sec) == false ||
1316 sec.Exists("Package") == false)
1317 return false;
1318 }
1319 return true;
1320 }
1321 /*}}}*/
1322 // AcqIndex::Done - Finished a fetch /*{{{*/
1323 // ---------------------------------------------------------------------
1324 /* This goes through a number of states.. On the initial fetch the
1325 method could possibly return an alternate filename which points
1326 to the uncompressed version of the file. If this is so the file
1327 is copied into the partial directory. In all other cases the file
1328 is decompressed with a compressed uri. */
1329 void pkgAcqIndex::Done(string Message,
1330 unsigned long long Size,
1331 HashStringList const &Hashes,
1332 pkgAcquire::MethodConfig *Cfg)
1333 {
1334 Item::Done(Message,Size,Hashes,Cfg);
1335
1336 switch(Stage)
1337 {
1338 case STAGE_DOWNLOAD:
1339 StageDownloadDone(Message, Hashes, Cfg);
1340 break;
1341 case STAGE_DECOMPRESS_AND_VERIFY:
1342 StageDecompressDone(Message, Hashes, Cfg);
1343 break;
1344 }
1345 }
1346 /*}}}*/
1347 // AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
1348 void pkgAcqIndex::StageDownloadDone(string Message,
1349 HashStringList const &Hashes,
1350 pkgAcquire::MethodConfig *Cfg)
1351 {
1352 // First check if the calculcated Hash of the (compressed) downloaded
1353 // file matches the hash we have in the MetaIndexRecords for this file
1354 if(VerifyHashByMetaKey(Hashes) == false)
1355 {
1356 RenameOnError(HashSumMismatch);
1357 Failed(Message, Cfg);
1358 return;
1359 }
1360
1361 Complete = true;
1362
1363 // Handle the unzipd case
1364 string FileName = LookupTag(Message,"Alt-Filename");
1365 if (FileName.empty() == false)
1366 {
1367 Stage = STAGE_DECOMPRESS_AND_VERIFY;
1368 Local = true;
1369 DestFile += ".decomp";
1370 Desc.URI = "copy:" + FileName;
1371 QueueURI(Desc);
1372 SetActiveSubprocess("copy");
1373 return;
1374 }
1375
1376 FileName = LookupTag(Message,"Filename");
1377 if (FileName.empty() == true)
1378 {
1379 Status = StatError;
1380 ErrorText = "Method gave a blank filename";
1381 }
1382
1383 // Methods like e.g. "file:" will give us a (compressed) FileName that is
1384 // not the "DestFile" we set, in this case we uncompress from the local file
1385 if (FileName != DestFile)
1386 Local = true;
1387 else
1388 EraseFileName = FileName;
1389
1390 // we need to verify the file against the current Release file again
1391 // on if-modfied-since hit to avoid a stale attack against us
1392 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1393 {
1394 // do not reverify cdrom sources as apt-cdrom may rewrite the Packages
1395 // file when its doing the indexcopy
1396 if (RealURI.substr(0,6) == "cdrom:")
1397 return;
1398
1399 // The files timestamp matches, reverify by copy into partial/
1400 EraseFileName = "";
1401 ReverifyAfterIMS();
1402 return;
1403 }
1404
1405 // If we have compressed indexes enabled, queue for hash verification
1406 if (_config->FindB("Acquire::GzipIndexes",false))
1407 {
1408 DestFile = GetPartialFileNameFromURI(RealURI + '.' + CurrentCompressionExtension);
1409 EraseFileName = "";
1410 Stage = STAGE_DECOMPRESS_AND_VERIFY;
1411 Desc.URI = "copy:" + FileName;
1412 QueueURI(Desc);
1413 SetActiveSubprocess("copy");
1414 return;
1415 }
1416
1417 // get the binary name for your used compression type
1418 string decompProg;
1419 if(CurrentCompressionExtension == "uncompressed")
1420 decompProg = "copy";
1421 else
1422 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(CurrentCompressionExtension),"");
1423 if(decompProg.empty() == true)
1424 {
1425 _error->Error("Unsupported extension: %s", CurrentCompressionExtension.c_str());
1426 return;
1427 }
1428
1429 // queue uri for the next stage
1430 Stage = STAGE_DECOMPRESS_AND_VERIFY;
1431 DestFile += ".decomp";
1432 Desc.URI = decompProg + ":" + FileName;
1433 QueueURI(Desc);
1434 SetActiveSubprocess(decompProg);
1435 }
1436 /*}}}*/
1437 // pkgAcqIndex::StageDecompressDone - Final verification /*{{{*/
1438 void pkgAcqIndex::StageDecompressDone(string Message,
1439 HashStringList const &Hashes,
1440 pkgAcquire::MethodConfig *Cfg)
1441 {
1442 if (ExpectedHashes.usable() && ExpectedHashes != Hashes)
1443 {
1444 Desc.URI = RealURI;
1445 RenameOnError(HashSumMismatch);
1446 printHashSumComparision(RealURI, ExpectedHashes, Hashes);
1447 Failed(Message, Cfg);
1448 return;
1449 }
1450
1451 if(!ValidateFile(DestFile))
1452 {
1453 RenameOnError(InvalidFormat);
1454 Failed(Message, Cfg);
1455 return;
1456 }
1457
1458 // remove the compressed version of the file
1459 unlink(EraseFileName.c_str());
1460
1461 // Done, queue for rename on transaction finished
1462 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1463
1464 return;
1465 }
1466 /*}}}*/
1467 // AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
1468 // ---------------------------------------------------------------------
1469 /* The Translation file is added to the queue */
1470 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
1471 string URI,string URIDesc,string ShortDesc)
1472 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashStringList())
1473 {
1474 }
1475 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
1476 pkgAcqMetaBase *TransactionManager,
1477 IndexTarget const * const Target,
1478 HashStringList const &ExpectedHashes,
1479 indexRecords *MetaIndexParser)
1480 : pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser)
1481 {
1482 // load the filesize
1483 indexRecords::checkSum *Record = MetaIndexParser->Lookup(string(Target->MetaKey));
1484 if(Record)
1485 FileSize = Record->Size;
1486 }
1487 /*}}}*/
1488 // AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/
1489 string pkgAcqIndexTrans::Custom600Headers() const
1490 {
1491 string Final = GetFinalFilename();
1492
1493 struct stat Buf;
1494 if (stat(Final.c_str(),&Buf) != 0)
1495 return "\nFail-Ignore: true\nIndex-File: true";
1496 return "\nFail-Ignore: true\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1497 }
1498 /*}}}*/
1499 // AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
1500 void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1501 {
1502 size_t const nextExt = CompressionExtensions.find(' ');
1503 if (nextExt != std::string::npos)
1504 {
1505 CompressionExtensions = CompressionExtensions.substr(nextExt+1);
1506 Init(RealURI, Desc.Description, Desc.ShortDesc);
1507 Status = StatIdle;
1508 return;
1509 }
1510
1511 Item::Failed(Message,Cnf);
1512
1513 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1514 if (Cnf->LocalOnly == true ||
1515 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1516 {
1517 // Ignore this
1518 Status = StatDone;
1519 }
1520 }
1521 /*}}}*/
1522 // AcqMetaBase::Add - Add a item to the current Transaction /*{{{*/
1523 void pkgAcqMetaBase::Add(Item *I)
1524 {
1525 Transaction.push_back(I);
1526 }
1527 /*}}}*/
1528 // AcqMetaBase::AbortTransaction - Abort the current Transaction /*{{{*/
1529 void pkgAcqMetaBase::AbortTransaction()
1530 {
1531 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1532 std::clog << "AbortTransaction: " << TransactionManager << std::endl;
1533
1534 // ensure the toplevel is in error state too
1535 for (std::vector<Item*>::iterator I = Transaction.begin();
1536 I != Transaction.end(); ++I)
1537 {
1538 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1539 std::clog << " Cancel: " << (*I)->DestFile << std::endl;
1540 // the transaction will abort, so stop anything that is idle
1541 if ((*I)->Status == pkgAcquire::Item::StatIdle)
1542 (*I)->Status = pkgAcquire::Item::StatDone;
1543
1544 // kill files in partial
1545 std::string const PartialFile = GetPartialFileName(flNotDir((*I)->DestFile));
1546 if(FileExists(PartialFile))
1547 Rename(PartialFile, PartialFile + ".FAILED");
1548 }
1549 }
1550 /*}}}*/
1551 // AcqMetaBase::TransactionHasError - Check for errors in Transaction /*{{{*/
1552 bool pkgAcqMetaBase::TransactionHasError()
1553 {
1554 for (pkgAcquire::ItemIterator I = Transaction.begin();
1555 I != Transaction.end(); ++I)
1556 if((*I)->Status != pkgAcquire::Item::StatDone &&
1557 (*I)->Status != pkgAcquire::Item::StatIdle)
1558 return true;
1559
1560 return false;
1561 }
1562 /*}}}*/
1563 // AcqMetaBase::CommitTransaction - Commit a transaction /*{{{*/
1564 void pkgAcqMetaBase::CommitTransaction()
1565 {
1566 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1567 std::clog << "CommitTransaction: " << this << std::endl;
1568
1569 // move new files into place *and* remove files that are not
1570 // part of the transaction but are still on disk
1571 for (std::vector<Item*>::iterator I = Transaction.begin();
1572 I != Transaction.end(); ++I)
1573 {
1574 if((*I)->PartialFile != "")
1575 {
1576 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1577 std::clog << "mv " << (*I)->PartialFile << " -> "<< (*I)->DestFile << " "
1578 << (*I)->DescURI() << std::endl;
1579
1580 Rename((*I)->PartialFile, (*I)->DestFile);
1581 ChangeOwnerAndPermissionOfFile("CommitTransaction", (*I)->DestFile.c_str(), "root", "root", 0644);
1582
1583 } else {
1584 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1585 std::clog << "rm "
1586 << (*I)->DestFile
1587 << " "
1588 << (*I)->DescURI()
1589 << std::endl;
1590 unlink((*I)->DestFile.c_str());
1591 }
1592 // mark that this transaction is finished
1593 (*I)->TransactionManager = 0;
1594 }
1595 }
1596 /*}}}*/
1597 // AcqMetaBase::TransactionStageCopy - Stage a file for copying /*{{{*/
1598 void pkgAcqMetaBase::TransactionStageCopy(Item *I,
1599 const std::string &From,
1600 const std::string &To)
1601 {
1602 I->PartialFile = From;
1603 I->DestFile = To;
1604 }
1605 /*}}}*/
1606 // AcqMetaBase::TransactionStageRemoval - Sage a file for removal /*{{{*/
1607 void pkgAcqMetaBase::TransactionStageRemoval(Item *I,
1608 const std::string &FinalFile)
1609 {
1610 I->PartialFile = "";
1611 I->DestFile = FinalFile;
1612 }
1613 /*}}}*/
1614 // AcqMetaBase::GenerateAuthWarning - Check gpg authentication error /*{{{*/
1615 bool pkgAcqMetaBase::CheckStopAuthentication(const std::string &RealURI,
1616 const std::string &Message)
1617 {
1618 // FIXME: this entire function can do now that we disallow going to
1619 // a unauthenticated state and can cleanly rollback
1620
1621 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1622
1623 if(FileExists(Final))
1624 {
1625 Status = StatTransientNetworkError;
1626 _error->Warning(_("An error occurred during the signature "
1627 "verification. The repository is not updated "
1628 "and the previous index files will be used. "
1629 "GPG error: %s: %s\n"),
1630 Desc.Description.c_str(),
1631 LookupTag(Message,"Message").c_str());
1632 RunScripts("APT::Update::Auth-Failure");
1633 return true;
1634 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
1635 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
1636 _error->Error(_("GPG error: %s: %s"),
1637 Desc.Description.c_str(),
1638 LookupTag(Message,"Message").c_str());
1639 Status = StatError;
1640 return true;
1641 } else {
1642 _error->Warning(_("GPG error: %s: %s"),
1643 Desc.Description.c_str(),
1644 LookupTag(Message,"Message").c_str());
1645 }
1646 // gpgv method failed
1647 ReportMirrorFailure("GPGFailure");
1648 return false;
1649 }
1650 /*}}}*/
1651 // AcqMetaSig::AcqMetaSig - Constructor /*{{{*/
1652 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
1653 pkgAcqMetaBase *TransactionManager,
1654 string URI,string URIDesc,string ShortDesc,
1655 string MetaIndexFile,
1656 const vector<IndexTarget*>* IndexTargets,
1657 indexRecords* MetaIndexParser) :
1658 pkgAcqMetaBase(Owner, IndexTargets, MetaIndexParser,
1659 HashStringList(), TransactionManager),
1660 RealURI(URI), MetaIndexFile(MetaIndexFile), URIDesc(URIDesc),
1661 ShortDesc(ShortDesc)
1662 {
1663 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1664 DestFile += URItoFileName(RealURI);
1665
1666 // remove any partial downloaded sig-file in partial/.
1667 // it may confuse proxies and is too small to warrant a
1668 // partial download anyway
1669 unlink(DestFile.c_str());
1670
1671 // set the TransactionManager
1672 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1673 std::clog << "New pkgAcqMetaSig with TransactionManager "
1674 << TransactionManager << std::endl;
1675
1676 // Create the item
1677 Desc.Description = URIDesc;
1678 Desc.Owner = this;
1679 Desc.ShortDesc = ShortDesc;
1680 Desc.URI = URI;
1681
1682 QueueURI(Desc);
1683 }
1684 /*}}}*/
1685 pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1686 {
1687 }
1688 /*}}}*/
1689 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1690 // ---------------------------------------------------------------------
1691 string pkgAcqMetaSig::Custom600Headers() const
1692 {
1693 std::string Header = GetCustom600Headers(RealURI);
1694 return Header;
1695 }
1696 /*}}}*/
1697 // pkgAcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
1698 // ---------------------------------------------------------------------
1699 /* The only header we use is the last-modified header. */
1700 void pkgAcqMetaSig::Done(string Message,unsigned long long Size,
1701 HashStringList const &Hashes,
1702 pkgAcquire::MethodConfig *Cfg)
1703 {
1704 Item::Done(Message, Size, Hashes, Cfg);
1705
1706 if(AuthPass == false)
1707 {
1708 if(CheckDownloadDone(Message, RealURI) == true)
1709 {
1710 // destfile will be modified to point to MetaIndexFile for the
1711 // gpgv method, so we need to save it here
1712 MetaIndexFileSignature = DestFile;
1713 QueueForSignatureVerify(MetaIndexFile, MetaIndexFileSignature);
1714 }
1715 return;
1716 }
1717 else
1718 {
1719 if(CheckAuthDone(Message, RealURI) == true)
1720 {
1721 std::string FinalFile = _config->FindDir("Dir::State::lists");
1722 FinalFile += URItoFileName(RealURI);
1723 TransactionManager->TransactionStageCopy(this, MetaIndexFileSignature, FinalFile);
1724 }
1725 }
1726 }
1727 /*}}}*/
1728 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
1729 {
1730 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1731
1732 // check if we need to fail at this point
1733 if (AuthPass == true && CheckStopAuthentication(RealURI, Message))
1734 return;
1735
1736 // FIXME: meh, this is not really elegant
1737 string InReleaseURI = RealURI.replace(RealURI.rfind("Release.gpg"), 12,
1738 "InRelease");
1739 string FinalInRelease = _config->FindDir("Dir::State::lists") + URItoFileName(InReleaseURI);
1740
1741 if (RealFileExists(Final) || RealFileExists(FinalInRelease))
1742 {
1743 std::string downgrade_msg;
1744 strprintf(downgrade_msg, _("The repository '%s' is no longer signed."),
1745 URIDesc.c_str());
1746 if(_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
1747 {
1748 // meh, the users wants to take risks (we still mark the packages
1749 // from this repository as unauthenticated)
1750 _error->Warning("%s", downgrade_msg.c_str());
1751 _error->Warning(_("This is normally not allowed, but the option "
1752 "Acquire::AllowDowngradeToInsecureRepositories was "
1753 "given to override it."));
1754
1755 } else {
1756 _error->Error("%s", downgrade_msg.c_str());
1757 Rename(MetaIndexFile, MetaIndexFile+".FAILED");
1758 Item::Failed("Message: " + downgrade_msg, Cnf);
1759 TransactionManager->AbortTransaction();
1760 return;
1761 }
1762 }
1763
1764 // this ensures that any file in the lists/ dir is removed by the
1765 // transaction
1766 DestFile = GetPartialFileNameFromURI(RealURI);
1767 TransactionManager->TransactionStageRemoval(this, DestFile);
1768
1769 // only allow going further if the users explicitely wants it
1770 if(_config->FindB("Acquire::AllowInsecureRepositories") == true)
1771 {
1772 // we parse the indexes here because at this point the user wanted
1773 // a repository that may potentially harm him
1774 MetaIndexParser->Load(MetaIndexFile);
1775 QueueIndexes(true);
1776 }
1777 else
1778 {
1779 _error->Warning("Use --allow-insecure-repositories to force the update");
1780 }
1781
1782 Item::Failed(Message,Cnf);
1783
1784 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1785 if (Cnf->LocalOnly == true ||
1786 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1787 {
1788 // Ignore this
1789 Status = StatDone;
1790 }
1791 }
1792 /*}}}*/
1793 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
1794 pkgAcqMetaBase *TransactionManager,
1795 string URI,string URIDesc,string ShortDesc,
1796 string MetaIndexSigURI,string MetaIndexSigURIDesc, string MetaIndexSigShortDesc,
1797 const vector<IndexTarget*>* IndexTargets,
1798 indexRecords* MetaIndexParser) :
1799 pkgAcqMetaBase(Owner, IndexTargets, MetaIndexParser, HashStringList(),
1800 TransactionManager),
1801 RealURI(URI), URIDesc(URIDesc), ShortDesc(ShortDesc),
1802 MetaIndexSigURI(MetaIndexSigURI), MetaIndexSigURIDesc(MetaIndexSigURIDesc),
1803 MetaIndexSigShortDesc(MetaIndexSigShortDesc)
1804 {
1805 if(TransactionManager == NULL)
1806 {
1807 this->TransactionManager = this;
1808 this->TransactionManager->Add(this);
1809 }
1810
1811 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1812 std::clog << "New pkgAcqMetaIndex with TransactionManager "
1813 << this->TransactionManager << std::endl;
1814
1815
1816 Init(URIDesc, ShortDesc);
1817 }
1818 /*}}}*/
1819 // pkgAcqMetaIndex::Init - Delayed constructor /*{{{*/
1820 void pkgAcqMetaIndex::Init(std::string URIDesc, std::string ShortDesc)
1821 {
1822 DestFile = GetPartialFileNameFromURI(RealURI);
1823
1824 // Create the item
1825 Desc.Description = URIDesc;
1826 Desc.Owner = this;
1827 Desc.ShortDesc = ShortDesc;
1828 Desc.URI = RealURI;
1829
1830 // we expect more item
1831 ExpectedAdditionalItems = IndexTargets->size();
1832 QueueURI(Desc);
1833 }
1834 /*}}}*/
1835 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
1836 // ---------------------------------------------------------------------
1837 string pkgAcqMetaIndex::Custom600Headers() const
1838 {
1839 return GetCustom600Headers(RealURI);
1840 }
1841 /*}}}*/
1842 void pkgAcqMetaIndex::Done(string Message,unsigned long long Size, /*{{{*/
1843 HashStringList const &Hashes,
1844 pkgAcquire::MethodConfig *Cfg)
1845 {
1846 Item::Done(Message,Size,Hashes,Cfg);
1847
1848 if(CheckDownloadDone(Message, RealURI))
1849 {
1850 // we have a Release file, now download the Signature, all further
1851 // verify/queue for additional downloads will be done in the
1852 // pkgAcqMetaSig::Done() code
1853 std::string MetaIndexFile = DestFile;
1854 new pkgAcqMetaSig(Owner, TransactionManager,
1855 MetaIndexSigURI, MetaIndexSigURIDesc,
1856 MetaIndexSigShortDesc, MetaIndexFile, IndexTargets,
1857 MetaIndexParser);
1858
1859 string FinalFile = _config->FindDir("Dir::State::lists");
1860 FinalFile += URItoFileName(RealURI);
1861 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
1862 }
1863 }
1864 /*}}}*/
1865 bool pkgAcqMetaBase::CheckAuthDone(string Message, const string &RealURI) /*{{{*/
1866 {
1867 // At this point, the gpgv method has succeeded, so there is a
1868 // valid signature from a key in the trusted keyring. We
1869 // perform additional verification of its contents, and use them
1870 // to verify the indexes we are about to download
1871
1872 if (!MetaIndexParser->Load(DestFile))
1873 {
1874 Status = StatAuthError;
1875 ErrorText = MetaIndexParser->ErrorText;
1876 return false;
1877 }
1878
1879 if (!VerifyVendor(Message, RealURI))
1880 {
1881 return false;
1882 }
1883
1884 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1885 std::cerr << "Signature verification succeeded: "
1886 << DestFile << std::endl;
1887
1888 // Download further indexes with verification
1889 //
1890 // it would be really nice if we could simply do
1891 // if (IMSHit == false) QueueIndexes(true)
1892 // and skip the download if the Release file has not changed
1893 // - but right now the list cleaner will needs to be tricked
1894 // to not delete all our packages/source indexes in this case
1895 QueueIndexes(true);
1896
1897 return true;
1898 }
1899 /*}}}*/
1900 // pkgAcqMetaBase::GetCustom600Headers - Get header for AcqMetaBase /*{{{*/
1901 // ---------------------------------------------------------------------
1902 string pkgAcqMetaBase::GetCustom600Headers(const string &RealURI) const
1903 {
1904 std::string Header = "\nIndex-File: true";
1905 std::string MaximumSize;
1906 strprintf(MaximumSize, "\nMaximum-Size: %i",
1907 _config->FindI("Acquire::MaxReleaseFileSize", 10*1000*1000));
1908 Header += MaximumSize;
1909
1910 string FinalFile = _config->FindDir("Dir::State::lists");
1911 FinalFile += URItoFileName(RealURI);
1912
1913 struct stat Buf;
1914 if (stat(FinalFile.c_str(),&Buf) == 0)
1915 Header += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1916
1917 return Header;
1918 }
1919 /*}}}*/
1920 // pkgAcqMetaBase::QueueForSignatureVerify /*{{{*/
1921 void pkgAcqMetaBase::QueueForSignatureVerify(const std::string &MetaIndexFile,
1922 const std::string &MetaIndexFileSignature)
1923 {
1924 AuthPass = true;
1925 Desc.URI = "gpgv:" + MetaIndexFileSignature;
1926 DestFile = MetaIndexFile;
1927 QueueURI(Desc);
1928 SetActiveSubprocess("gpgv");
1929 }
1930 /*}}}*/
1931 // pkgAcqMetaBase::CheckDownloadDone /*{{{*/
1932 bool pkgAcqMetaBase::CheckDownloadDone(const std::string &Message,
1933 const std::string &RealURI)
1934 {
1935 // We have just finished downloading a Release file (it is not
1936 // verified yet)
1937
1938 string FileName = LookupTag(Message,"Filename");
1939 if (FileName.empty() == true)
1940 {
1941 Status = StatError;
1942 ErrorText = "Method gave a blank filename";
1943 return false;
1944 }
1945
1946 if (FileName != DestFile)
1947 {
1948 Local = true;
1949 Desc.URI = "copy:" + FileName;
1950 QueueURI(Desc);
1951 return false;
1952 }
1953
1954 // make sure to verify against the right file on I-M-S hit
1955 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
1956 if(IMSHit)
1957 {
1958 string FinalFile = _config->FindDir("Dir::State::lists");
1959 FinalFile += URItoFileName(RealURI);
1960 DestFile = FinalFile;
1961 }
1962
1963 // set Item to complete as the remaining work is all local (verify etc)
1964 Complete = true;
1965
1966 return true;
1967 }
1968 /*}}}*/
1969 void pkgAcqMetaBase::QueueIndexes(bool verify) /*{{{*/
1970 {
1971 bool transInRelease = false;
1972 {
1973 std::vector<std::string> const keys = MetaIndexParser->MetaKeys();
1974 for (std::vector<std::string>::const_iterator k = keys.begin(); k != keys.end(); ++k)
1975 // FIXME: Feels wrong to check for hardcoded string here, but what should we do else…
1976 if (k->find("Translation-") != std::string::npos)
1977 {
1978 transInRelease = true;
1979 break;
1980 }
1981 }
1982
1983 // at this point the real Items are loaded in the fetcher
1984 ExpectedAdditionalItems = 0;
1985 for (vector <IndexTarget*>::const_iterator Target = IndexTargets->begin();
1986 Target != IndexTargets->end();
1987 ++Target)
1988 {
1989 HashStringList ExpectedIndexHashes;
1990 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1991 bool compressedAvailable = false;
1992 if (Record == NULL)
1993 {
1994 if ((*Target)->IsOptional() == true)
1995 {
1996 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
1997 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
1998 if (MetaIndexParser->Exists((*Target)->MetaKey + "." + *t) == true)
1999 {
2000 compressedAvailable = true;
2001 break;
2002 }
2003 }
2004 else if (verify == true)
2005 {
2006 Status = StatAuthError;
2007 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
2008 return;
2009 }
2010 }
2011 else
2012 {
2013 ExpectedIndexHashes = Record->Hashes;
2014 if (_config->FindB("Debug::pkgAcquire::Auth", false))
2015 {
2016 std::cerr << "Queueing: " << (*Target)->URI << std::endl
2017 << "Expected Hash:" << std::endl;
2018 for (HashStringList::const_iterator hs = ExpectedIndexHashes.begin(); hs != ExpectedIndexHashes.end(); ++hs)
2019 std::cerr << "\t- " << hs->toStr() << std::endl;
2020 std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
2021 }
2022 if (verify == true && ExpectedIndexHashes.empty() == true && (*Target)->IsOptional() == false)
2023 {
2024 Status = StatAuthError;
2025 strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
2026 return;
2027 }
2028 }
2029
2030 if ((*Target)->IsOptional() == true)
2031 {
2032 if (transInRelease == false || Record != NULL || compressedAvailable == true)
2033 {
2034 if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
2035 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
2036 new pkgAcqDiffIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
2037 else
2038 new pkgAcqIndexTrans(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
2039 }
2040 continue;
2041 }
2042
2043 /* Queue Packages file (either diff or full packages files, depending
2044 on the users option) - we also check if the PDiff Index file is listed
2045 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
2046 instead, but passing the required info to it is to much hassle */
2047 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
2048 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
2049 new pkgAcqDiffIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
2050 else
2051 new pkgAcqIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
2052 }
2053 }
2054 /*}}}*/
2055 bool pkgAcqMetaBase::VerifyVendor(string Message, const string &RealURI)/*{{{*/
2056 {
2057 string::size_type pos;
2058
2059 // check for missing sigs (that where not fatal because otherwise we had
2060 // bombed earlier)
2061 string missingkeys;
2062 string msg = _("There is no public key available for the "
2063 "following key IDs:\n");
2064 pos = Message.find("NO_PUBKEY ");
2065 if (pos != std::string::npos)
2066 {
2067 string::size_type start = pos+strlen("NO_PUBKEY ");
2068 string Fingerprint = Message.substr(start, Message.find("\n")-start);
2069 missingkeys += (Fingerprint);
2070 }
2071 if(!missingkeys.empty())
2072 _error->Warning("%s", (msg + missingkeys).c_str());
2073
2074 string Transformed = MetaIndexParser->GetExpectedDist();
2075
2076 if (Transformed == "../project/experimental")
2077 {
2078 Transformed = "experimental";
2079 }
2080
2081 pos = Transformed.rfind('/');
2082 if (pos != string::npos)
2083 {
2084 Transformed = Transformed.substr(0, pos);
2085 }
2086
2087 if (Transformed == ".")
2088 {
2089 Transformed = "";
2090 }
2091
2092 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
2093 MetaIndexParser->GetValidUntil() > 0) {
2094 time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
2095 if (invalid_since > 0)
2096 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
2097 // the time since then the file is invalid - formated in the same way as in
2098 // the download progress display (e.g. 7d 3h 42min 1s)
2099 return _error->Error(
2100 _("Release file for %s is expired (invalid since %s). "
2101 "Updates for this repository will not be applied."),
2102 RealURI.c_str(), TimeToStr(invalid_since).c_str());
2103 }
2104
2105 if (_config->FindB("Debug::pkgAcquire::Auth", false))
2106 {
2107 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
2108 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
2109 std::cerr << "Transformed Dist: " << Transformed << std::endl;
2110 }
2111
2112 if (MetaIndexParser->CheckDist(Transformed) == false)
2113 {
2114 // This might become fatal one day
2115 // Status = StatAuthError;
2116 // ErrorText = "Conflicting distribution; expected "
2117 // + MetaIndexParser->GetExpectedDist() + " but got "
2118 // + MetaIndexParser->GetDist();
2119 // return false;
2120 if (!Transformed.empty())
2121 {
2122 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
2123 Desc.Description.c_str(),
2124 Transformed.c_str(),
2125 MetaIndexParser->GetDist().c_str());
2126 }
2127 }
2128
2129 return true;
2130 }
2131 /*}}}*/
2132 // pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
2133 void pkgAcqMetaIndex::Failed(string Message,
2134 pkgAcquire::MethodConfig * Cnf)
2135 {
2136 pkgAcquire::Item::Failed(Message, Cnf);
2137 Status = StatDone;
2138
2139 string FinalFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
2140
2141 _error->Warning(_("The repository '%s' does not have a Release file. "
2142 "This is deprecated, please contact the owner of the "
2143 "repository."), URIDesc.c_str());
2144
2145 // No Release file was present so fall
2146 // back to queueing Packages files without verification
2147 // only allow going further if the users explicitely wants it
2148 if(_config->FindB("Acquire::AllowInsecureRepositories") == true)
2149 {
2150 // Done, queue for rename on transaction finished
2151 if (FileExists(DestFile))
2152 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2153
2154 // queue without any kind of hashsum support
2155 QueueIndexes(false);
2156 } else {
2157 // warn if the repository is unsinged
2158 _error->Warning("Use --allow-insecure-repositories to force the update");
2159 TransactionManager->AbortTransaction();
2160 Status = StatError;
2161 return;
2162 }
2163 }
2164 /*}}}*/
2165 void pkgAcqMetaIndex::Finished() /*{{{*/
2166 {
2167 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
2168 std::clog << "Finished: " << DestFile <<std::endl;
2169 if(TransactionManager != NULL &&
2170 TransactionManager->TransactionHasError() == false)
2171 TransactionManager->CommitTransaction();
2172 }
2173 /*}}}*/
2174 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
2175 string const &URI, string const &URIDesc, string const &ShortDesc,
2176 string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
2177 string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
2178 const vector<IndexTarget*>* IndexTargets,
2179 indexRecords* MetaIndexParser) :
2180 pkgAcqMetaIndex(Owner, NULL, URI, URIDesc, ShortDesc, MetaSigURI, MetaSigURIDesc,MetaSigShortDesc, IndexTargets, MetaIndexParser),
2181 MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
2182 MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
2183 {
2184 // index targets + (worst case:) Release/Release.gpg
2185 ExpectedAdditionalItems = IndexTargets->size() + 2;
2186
2187 }
2188 /*}}}*/
2189 pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
2190 {
2191 }
2192 /*}}}*/
2193 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
2194 // ---------------------------------------------------------------------
2195 string pkgAcqMetaClearSig::Custom600Headers() const
2196 {
2197 string Header = GetCustom600Headers(RealURI);
2198 Header += "\nFail-Ignore: true";
2199 return Header;
2200 }
2201 /*}}}*/
2202 // pkgAcqMetaClearSig::Done - We got a file /*{{{*/
2203 // ---------------------------------------------------------------------
2204 void pkgAcqMetaClearSig::Done(std::string Message,unsigned long long /*Size*/,
2205 HashStringList const &/*Hashes*/,
2206 pkgAcquire::MethodConfig *Cnf)
2207 {
2208 // if we expect a ClearTextSignature (InRelase), ensure that
2209 // this is what we get and if not fail to queue a
2210 // Release/Release.gpg, see #346386
2211 if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
2212 {
2213 pkgAcquire::Item::Failed(Message, Cnf);
2214 RenameOnError(NotClearsigned);
2215 TransactionManager->AbortTransaction();
2216 return;
2217 }
2218
2219 if(AuthPass == false)
2220 {
2221 if(CheckDownloadDone(Message, RealURI) == true)
2222 QueueForSignatureVerify(DestFile, DestFile);
2223 return;
2224 }
2225 else
2226 {
2227 if(CheckAuthDone(Message, RealURI) == true)
2228 {
2229 string FinalFile = _config->FindDir("Dir::State::lists");
2230 FinalFile += URItoFileName(RealURI);
2231
2232 // queue for copy in place
2233 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2234 }
2235 }
2236 }
2237 /*}}}*/
2238 void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
2239 {
2240 Item::Failed(Message, Cnf);
2241
2242 // we failed, we will not get additional items from this method
2243 ExpectedAdditionalItems = 0;
2244
2245 if (AuthPass == false)
2246 {
2247 // Queue the 'old' InRelease file for removal if we try Release.gpg
2248 // as otherwise the file will stay around and gives a false-auth
2249 // impression (CVE-2012-0214)
2250 string FinalFile = _config->FindDir("Dir::State::lists");
2251 FinalFile.append(URItoFileName(RealURI));
2252 TransactionManager->TransactionStageRemoval(this, FinalFile);
2253 Status = StatDone;
2254
2255 new pkgAcqMetaIndex(Owner, TransactionManager,
2256 MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
2257 MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
2258 IndexTargets, MetaIndexParser);
2259 }
2260 else
2261 {
2262 if(CheckStopAuthentication(RealURI, Message))
2263 return;
2264
2265 _error->Warning(_("The data from '%s' is not signed. Packages "
2266 "from that repository can not be authenticated."),
2267 URIDesc.c_str());
2268
2269 // No Release file was present, or verification failed, so fall
2270 // back to queueing Packages files without verification
2271 // only allow going further if the users explicitely wants it
2272 if(_config->FindB("Acquire::AllowInsecureRepositories") == true)
2273 {
2274 Status = StatDone;
2275
2276 /* Always move the meta index, even if gpgv failed. This ensures
2277 * that PackageFile objects are correctly filled in */
2278 if (FileExists(DestFile))
2279 {
2280 string FinalFile = _config->FindDir("Dir::State::lists");
2281 FinalFile += URItoFileName(RealURI);
2282 /* InRelease files become Release files, otherwise
2283 * they would be considered as trusted later on */
2284 RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
2285 "Release");
2286 FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
2287 "Release");
2288
2289 // Done, queue for rename on transaction finished
2290 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2291 }
2292 QueueIndexes(false);
2293 } else {
2294 // warn if the repository is unsigned
2295 _error->Warning("Use --allow-insecure-repositories to force the update");
2296 TransactionManager->AbortTransaction();
2297 Status = StatError;
2298 }
2299 }
2300 }
2301 /*}}}*/
2302 // AcqArchive::AcqArchive - Constructor /*{{{*/
2303 // ---------------------------------------------------------------------
2304 /* This just sets up the initial fetch environment and queues the first
2305 possibilitiy */
2306 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
2307 pkgRecords *Recs,pkgCache::VerIterator const &Version,
2308 string &StoreFilename) :
2309 Item(Owner, HashStringList()), Version(Version), Sources(Sources), Recs(Recs),
2310 StoreFilename(StoreFilename), Vf(Version.FileList()),
2311 Trusted(false)
2312 {
2313 Retries = _config->FindI("Acquire::Retries",0);
2314
2315 if (Version.Arch() == 0)
2316 {
2317 _error->Error(_("I wasn't able to locate a file for the %s package. "
2318 "This might mean you need to manually fix this package. "
2319 "(due to missing arch)"),
2320 Version.ParentPkg().FullName().c_str());
2321 return;
2322 }
2323
2324 /* We need to find a filename to determine the extension. We make the
2325 assumption here that all the available sources for this version share
2326 the same extension.. */
2327 // Skip not source sources, they do not have file fields.
2328 for (; Vf.end() == false; ++Vf)
2329 {
2330 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2331 continue;
2332 break;
2333 }
2334
2335 // Does not really matter here.. we are going to fail out below
2336 if (Vf.end() != true)
2337 {
2338 // If this fails to get a file name we will bomb out below.
2339 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2340 if (_error->PendingError() == true)
2341 return;
2342
2343 // Generate the final file name as: package_version_arch.foo
2344 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2345 QuoteString(Version.VerStr(),"_:") + '_' +
2346 QuoteString(Version.Arch(),"_:.") +
2347 "." + flExtension(Parse.FileName());
2348 }
2349
2350 // check if we have one trusted source for the package. if so, switch
2351 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2352 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2353 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2354 bool seenUntrusted = false;
2355 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
2356 {
2357 pkgIndexFile *Index;
2358 if (Sources->FindIndex(i.File(),Index) == false)
2359 continue;
2360
2361 if (debugAuth == true)
2362 std::cerr << "Checking index: " << Index->Describe()
2363 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2364
2365 if (Index->IsTrusted() == true)
2366 {
2367 Trusted = true;
2368 if (allowUnauth == false)
2369 break;
2370 }
2371 else
2372 seenUntrusted = true;
2373 }
2374
2375 // "allow-unauthenticated" restores apts old fetching behaviour
2376 // that means that e.g. unauthenticated file:// uris are higher
2377 // priority than authenticated http:// uris
2378 if (allowUnauth == true && seenUntrusted == true)
2379 Trusted = false;
2380
2381 // Select a source
2382 if (QueueNext() == false && _error->PendingError() == false)
2383 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2384 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2385 }
2386 /*}}}*/
2387 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
2388 // ---------------------------------------------------------------------
2389 /* This queues the next available file version for download. It checks if
2390 the archive is already available in the cache and stashs the MD5 for
2391 checking later. */
2392 bool pkgAcqArchive::QueueNext()
2393 {
2394 for (; Vf.end() == false; ++Vf)
2395 {
2396 // Ignore not source sources
2397 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2398 continue;
2399
2400 // Try to cross match against the source list
2401 pkgIndexFile *Index;
2402 if (Sources->FindIndex(Vf.File(),Index) == false)
2403 continue;
2404
2405 // only try to get a trusted package from another source if that source
2406 // is also trusted
2407 if(Trusted && !Index->IsTrusted())
2408 continue;
2409
2410 // Grab the text package record
2411 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2412 if (_error->PendingError() == true)
2413 return false;
2414
2415 string PkgFile = Parse.FileName();
2416 ExpectedHashes = Parse.Hashes();
2417
2418 if (PkgFile.empty() == true)
2419 return _error->Error(_("The package index files are corrupted. No Filename: "
2420 "field for package %s."),
2421 Version.ParentPkg().Name());
2422
2423 Desc.URI = Index->ArchiveURI(PkgFile);
2424 Desc.Description = Index->ArchiveInfo(Version);
2425 Desc.Owner = this;
2426 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2427
2428 // See if we already have the file. (Legacy filenames)
2429 FileSize = Version->Size;
2430 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2431 struct stat Buf;
2432 if (stat(FinalFile.c_str(),&Buf) == 0)
2433 {
2434 // Make sure the size matches
2435 if ((unsigned long long)Buf.st_size == Version->Size)
2436 {
2437 Complete = true;
2438 Local = true;
2439 Status = StatDone;
2440 StoreFilename = DestFile = FinalFile;
2441 return true;
2442 }
2443
2444 /* Hmm, we have a file and its size does not match, this means it is
2445 an old style mismatched arch */
2446 unlink(FinalFile.c_str());
2447 }
2448
2449 // Check it again using the new style output filenames
2450 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2451 if (stat(FinalFile.c_str(),&Buf) == 0)
2452 {
2453 // Make sure the size matches
2454 if ((unsigned long long)Buf.st_size == Version->Size)
2455 {
2456 Complete = true;
2457 Local = true;
2458 Status = StatDone;
2459 StoreFilename = DestFile = FinalFile;
2460 return true;
2461 }
2462
2463 /* Hmm, we have a file and its size does not match, this shouldn't
2464 happen.. */
2465 unlink(FinalFile.c_str());
2466 }
2467
2468 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
2469
2470 // Check the destination file
2471 if (stat(DestFile.c_str(),&Buf) == 0)
2472 {
2473 // Hmm, the partial file is too big, erase it
2474 if ((unsigned long long)Buf.st_size > Version->Size)
2475 unlink(DestFile.c_str());
2476 else
2477 {
2478 PartialSize = Buf.st_size;
2479 ChangeOwnerAndPermissionOfFile("pkgAcqArchive::QueueNext", FinalFile.c_str(), "_apt", "root", 0600);
2480 }
2481 }
2482
2483 // Disables download of archives - useful if no real installation follows,
2484 // e.g. if we are just interested in proposed installation order
2485 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2486 {
2487 Complete = true;
2488 Local = true;
2489 Status = StatDone;
2490 StoreFilename = DestFile = FinalFile;
2491 return true;
2492 }
2493
2494 // Create the item
2495 Local = false;
2496 QueueURI(Desc);
2497
2498 ++Vf;
2499 return true;
2500 }
2501 return false;
2502 }
2503 /*}}}*/
2504 // AcqArchive::Done - Finished fetching /*{{{*/
2505 // ---------------------------------------------------------------------
2506 /* */
2507 void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList const &CalcHashes,
2508 pkgAcquire::MethodConfig *Cfg)
2509 {
2510 Item::Done(Message, Size, CalcHashes, Cfg);
2511
2512 // Check the size
2513 if (Size != Version->Size)
2514 {
2515 RenameOnError(SizeMismatch);
2516 return;
2517 }
2518
2519 // FIXME: could this empty() check impose *any* sort of security issue?
2520 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2521 {
2522 RenameOnError(HashSumMismatch);
2523 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2524 return;
2525 }
2526
2527 // Grab the output filename
2528 string FileName = LookupTag(Message,"Filename");
2529 if (FileName.empty() == true)
2530 {
2531 Status = StatError;
2532 ErrorText = "Method gave a blank filename";
2533 return;
2534 }
2535
2536 // Reference filename
2537 if (FileName != DestFile)
2538 {
2539 StoreFilename = DestFile = FileName;
2540 Local = true;
2541 Complete = true;
2542 return;
2543 }
2544
2545 // Done, move it into position
2546 string FinalFile = _config->FindDir("Dir::Cache::Archives");
2547 FinalFile += flNotDir(StoreFilename);
2548 Rename(DestFile,FinalFile);
2549 ChangeOwnerAndPermissionOfFile("pkgAcqArchive::Done", FinalFile.c_str(), "root", "root", 0644);
2550 StoreFilename = DestFile = FinalFile;
2551 Complete = true;
2552 }
2553 /*}}}*/
2554 // AcqArchive::Failed - Failure handler /*{{{*/
2555 // ---------------------------------------------------------------------
2556 /* Here we try other sources */
2557 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2558 {
2559 ErrorText = LookupTag(Message,"Message");
2560
2561 /* We don't really want to retry on failed media swaps, this prevents
2562 that. An interesting observation is that permanent failures are not
2563 recorded. */
2564 if (Cnf->Removable == true &&
2565 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2566 {
2567 // Vf = Version.FileList();
2568 while (Vf.end() == false) ++Vf;
2569 StoreFilename = string();
2570 Item::Failed(Message,Cnf);
2571 return;
2572 }
2573
2574 if (QueueNext() == false)
2575 {
2576 // This is the retry counter
2577 if (Retries != 0 &&
2578 Cnf->LocalOnly == false &&
2579 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2580 {
2581 Retries--;
2582 Vf = Version.FileList();
2583 if (QueueNext() == true)
2584 return;
2585 }
2586
2587 StoreFilename = string();
2588 Item::Failed(Message,Cnf);
2589 }
2590 }
2591 /*}}}*/
2592 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
2593 // ---------------------------------------------------------------------
2594 APT_PURE bool pkgAcqArchive::IsTrusted() const
2595 {
2596 return Trusted;
2597 }
2598 /*}}}*/
2599 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
2600 // ---------------------------------------------------------------------
2601 /* */
2602 void pkgAcqArchive::Finished()
2603 {
2604 if (Status == pkgAcquire::Item::StatDone &&
2605 Complete == true)
2606 return;
2607 StoreFilename = string();
2608 }
2609 /*}}}*/
2610 // AcqFile::pkgAcqFile - Constructor /*{{{*/
2611 // ---------------------------------------------------------------------
2612 /* The file is added to the queue */
2613 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI, HashStringList const &Hashes,
2614 unsigned long long Size,string Dsc,string ShortDesc,
2615 const string &DestDir, const string &DestFilename,
2616 bool IsIndexFile) :
2617 Item(Owner, Hashes), IsIndexFile(IsIndexFile)
2618 {
2619 Retries = _config->FindI("Acquire::Retries",0);
2620
2621 if(!DestFilename.empty())
2622 DestFile = DestFilename;
2623 else if(!DestDir.empty())
2624 DestFile = DestDir + "/" + flNotDir(URI);
2625 else
2626 DestFile = flNotDir(URI);
2627
2628 // Create the item
2629 Desc.URI = URI;
2630 Desc.Description = Dsc;
2631 Desc.Owner = this;
2632
2633 // Set the short description to the archive component
2634 Desc.ShortDesc = ShortDesc;
2635
2636 // Get the transfer sizes
2637 FileSize = Size;
2638 struct stat Buf;
2639 if (stat(DestFile.c_str(),&Buf) == 0)
2640 {
2641 // Hmm, the partial file is too big, erase it
2642 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
2643 unlink(DestFile.c_str());
2644 else
2645 {
2646 PartialSize = Buf.st_size;
2647 ChangeOwnerAndPermissionOfFile("pkgAcqFile", DestFile.c_str(), "_apt", "root", 0600);
2648 }
2649 }
2650
2651 QueueURI(Desc);
2652 }
2653 /*}}}*/
2654 // AcqFile::Done - Item downloaded OK /*{{{*/
2655 // ---------------------------------------------------------------------
2656 /* */
2657 void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList const &CalcHashes,
2658 pkgAcquire::MethodConfig *Cnf)
2659 {
2660 Item::Done(Message,Size,CalcHashes,Cnf);
2661
2662 // Check the hash
2663 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2664 {
2665 RenameOnError(HashSumMismatch);
2666 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2667 return;
2668 }
2669
2670 string FileName = LookupTag(Message,"Filename");
2671 if (FileName.empty() == true)
2672 {
2673 Status = StatError;
2674 ErrorText = "Method gave a blank filename";
2675 return;
2676 }
2677
2678 Complete = true;
2679
2680 // The files timestamp matches
2681 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2682 return;
2683
2684 // We have to copy it into place
2685 if (FileName != DestFile)
2686 {
2687 Local = true;
2688 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
2689 Cnf->Removable == true)
2690 {
2691 Desc.URI = "copy:" + FileName;
2692 QueueURI(Desc);
2693 return;
2694 }
2695
2696 // Erase the file if it is a symlink so we can overwrite it
2697 struct stat St;
2698 if (lstat(DestFile.c_str(),&St) == 0)
2699 {
2700 if (S_ISLNK(St.st_mode) != 0)
2701 unlink(DestFile.c_str());
2702 }
2703
2704 // Symlink the file
2705 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
2706 {
2707 ErrorText = "Link to " + DestFile + " failure ";
2708 Status = StatError;
2709 Complete = false;
2710 }
2711 }
2712 }
2713 /*}}}*/
2714 // AcqFile::Failed - Failure handler /*{{{*/
2715 // ---------------------------------------------------------------------
2716 /* Here we try other sources */
2717 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2718 {
2719 ErrorText = LookupTag(Message,"Message");
2720
2721 // This is the retry counter
2722 if (Retries != 0 &&
2723 Cnf->LocalOnly == false &&
2724 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2725 {
2726 Retries--;
2727 QueueURI(Desc);
2728 return;
2729 }
2730
2731 Item::Failed(Message,Cnf);
2732 }
2733 /*}}}*/
2734 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2735 // ---------------------------------------------------------------------
2736 /* The only header we use is the last-modified header. */
2737 string pkgAcqFile::Custom600Headers() const
2738 {
2739 if (IsIndexFile)
2740 return "\nIndex-File: true";
2741 return "";
2742 }
2743 /*}}}*/