]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
Merge branch 'feature/acq-trans' into feature/expected-size
[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 string FinalFile = _config->FindDir("Dir::State::lists");
1694 FinalFile += URItoFileName(RealURI);
1695
1696 struct stat Buf;
1697 if (stat(FinalFile.c_str(),&Buf) != 0)
1698 return "\nIndex-File: true";
1699
1700 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1701 }
1702 /*}}}*/
1703 // pkgAcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
1704 // ---------------------------------------------------------------------
1705 /* The only header we use is the last-modified header. */
1706 void pkgAcqMetaSig::Done(string Message,unsigned long long Size,
1707 HashStringList const &Hashes,
1708 pkgAcquire::MethodConfig *Cfg)
1709 {
1710 Item::Done(Message, Size, Hashes, Cfg);
1711
1712 if(AuthPass == false)
1713 {
1714 if(CheckDownloadDone(Message, RealURI) == true)
1715 {
1716 // destfile will be modified to point to MetaIndexFile for the
1717 // gpgv method, so we need to save it here
1718 MetaIndexFileSignature = DestFile;
1719 QueueForSignatureVerify(MetaIndexFile, MetaIndexFileSignature);
1720 }
1721 return;
1722 }
1723 else
1724 {
1725 if(CheckAuthDone(Message, RealURI) == true)
1726 {
1727 std::string FinalFile = _config->FindDir("Dir::State::lists");
1728 FinalFile += URItoFileName(RealURI);
1729 TransactionManager->TransactionStageCopy(this, MetaIndexFileSignature, FinalFile);
1730 }
1731 }
1732 }
1733 /*}}}*/
1734 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
1735 {
1736 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1737
1738 // check if we need to fail at this point
1739 if (AuthPass == true && CheckStopAuthentication(RealURI, Message))
1740 return;
1741
1742 // FIXME: meh, this is not really elegant
1743 string InReleaseURI = RealURI.replace(RealURI.rfind("Release.gpg"), 12,
1744 "InRelease");
1745 string FinalInRelease = _config->FindDir("Dir::State::lists") + URItoFileName(InReleaseURI);
1746
1747 if (RealFileExists(Final) || RealFileExists(FinalInRelease))
1748 {
1749 std::string downgrade_msg;
1750 strprintf(downgrade_msg, _("The repository '%s' is no longer signed."),
1751 URIDesc.c_str());
1752 if(_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
1753 {
1754 // meh, the users wants to take risks (we still mark the packages
1755 // from this repository as unauthenticated)
1756 _error->Warning("%s", downgrade_msg.c_str());
1757 _error->Warning(_("This is normally not allowed, but the option "
1758 "Acquire::AllowDowngradeToInsecureRepositories was "
1759 "given to override it."));
1760
1761 } else {
1762 _error->Error("%s", downgrade_msg.c_str());
1763 Rename(MetaIndexFile, MetaIndexFile+".FAILED");
1764 Item::Failed("Message: " + downgrade_msg, Cnf);
1765 TransactionManager->AbortTransaction();
1766 return;
1767 }
1768 }
1769
1770 // this ensures that any file in the lists/ dir is removed by the
1771 // transaction
1772 DestFile = GetPartialFileNameFromURI(RealURI);
1773 TransactionManager->TransactionStageRemoval(this, DestFile);
1774
1775 // only allow going further if the users explicitely wants it
1776 if(_config->FindB("Acquire::AllowInsecureRepositories") == true)
1777 {
1778 // we parse the indexes here because at this point the user wanted
1779 // a repository that may potentially harm him
1780 MetaIndexParser->Load(MetaIndexFile);
1781 QueueIndexes(true);
1782 }
1783 else
1784 {
1785 _error->Warning("Use --allow-insecure-repositories to force the update");
1786 }
1787
1788 Item::Failed(Message,Cnf);
1789
1790 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1791 if (Cnf->LocalOnly == true ||
1792 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1793 {
1794 // Ignore this
1795 Status = StatDone;
1796 }
1797 }
1798 /*}}}*/
1799 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
1800 pkgAcqMetaBase *TransactionManager,
1801 string URI,string URIDesc,string ShortDesc,
1802 string MetaIndexSigURI,string MetaIndexSigURIDesc, string MetaIndexSigShortDesc,
1803 const vector<IndexTarget*>* IndexTargets,
1804 indexRecords* MetaIndexParser) :
1805 pkgAcqMetaBase(Owner, IndexTargets, MetaIndexParser, HashStringList(),
1806 TransactionManager),
1807 RealURI(URI), URIDesc(URIDesc), ShortDesc(ShortDesc),
1808 MetaIndexSigURI(MetaIndexSigURI), MetaIndexSigURIDesc(MetaIndexSigURIDesc),
1809 MetaIndexSigShortDesc(MetaIndexSigShortDesc)
1810 {
1811 if(TransactionManager == NULL)
1812 {
1813 this->TransactionManager = this;
1814 this->TransactionManager->Add(this);
1815 }
1816
1817 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1818 std::clog << "New pkgAcqMetaIndex with TransactionManager "
1819 << this->TransactionManager << std::endl;
1820
1821
1822 Init(URIDesc, ShortDesc);
1823 }
1824 /*}}}*/
1825 // pkgAcqMetaIndex::Init - Delayed constructor /*{{{*/
1826 void pkgAcqMetaIndex::Init(std::string URIDesc, std::string ShortDesc)
1827 {
1828 DestFile = GetPartialFileNameFromURI(RealURI);
1829
1830 // Create the item
1831 Desc.Description = URIDesc;
1832 Desc.Owner = this;
1833 Desc.ShortDesc = ShortDesc;
1834 Desc.URI = RealURI;
1835
1836 // we expect more item
1837 ExpectedAdditionalItems = IndexTargets->size();
1838 QueueURI(Desc);
1839 }
1840 /*}}}*/
1841 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
1842 // ---------------------------------------------------------------------
1843 string pkgAcqMetaIndex::Custom600Headers() const
1844 {
1845 string Final = _config->FindDir("Dir::State::lists");
1846 Final += URItoFileName(RealURI);
1847
1848 struct stat Buf;
1849 if (stat(Final.c_str(),&Buf) != 0)
1850 return "\nIndex-File: true";
1851
1852 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1853 }
1854 /*}}}*/
1855 void pkgAcqMetaIndex::Done(string Message,unsigned long long Size, /*{{{*/
1856 HashStringList const &Hashes,
1857 pkgAcquire::MethodConfig *Cfg)
1858 {
1859 Item::Done(Message,Size,Hashes,Cfg);
1860
1861 if(CheckDownloadDone(Message, RealURI))
1862 {
1863 // we have a Release file, now download the Signature, all further
1864 // verify/queue for additional downloads will be done in the
1865 // pkgAcqMetaSig::Done() code
1866 std::string MetaIndexFile = DestFile;
1867 new pkgAcqMetaSig(Owner, TransactionManager,
1868 MetaIndexSigURI, MetaIndexSigURIDesc,
1869 MetaIndexSigShortDesc, MetaIndexFile, IndexTargets,
1870 MetaIndexParser);
1871
1872 string FinalFile = _config->FindDir("Dir::State::lists");
1873 FinalFile += URItoFileName(RealURI);
1874 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
1875 }
1876 }
1877 /*}}}*/
1878 bool pkgAcqMetaBase::CheckAuthDone(string Message, const string &RealURI) /*{{{*/
1879 {
1880 // At this point, the gpgv method has succeeded, so there is a
1881 // valid signature from a key in the trusted keyring. We
1882 // perform additional verification of its contents, and use them
1883 // to verify the indexes we are about to download
1884
1885 if (!MetaIndexParser->Load(DestFile))
1886 {
1887 Status = StatAuthError;
1888 ErrorText = MetaIndexParser->ErrorText;
1889 return false;
1890 }
1891
1892 if (!VerifyVendor(Message, RealURI))
1893 {
1894 return false;
1895 }
1896
1897 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1898 std::cerr << "Signature verification succeeded: "
1899 << DestFile << std::endl;
1900
1901 // Download further indexes with verification
1902 //
1903 // it would be really nice if we could simply do
1904 // if (IMSHit == false) QueueIndexes(true)
1905 // and skip the download if the Release file has not changed
1906 // - but right now the list cleaner will needs to be tricked
1907 // to not delete all our packages/source indexes in this case
1908 QueueIndexes(true);
1909
1910 return true;
1911 }
1912 /*}}}*/
1913 // pkgAcqMetaBase::QueueForSignatureVerify /*{{{*/
1914 void pkgAcqMetaBase::QueueForSignatureVerify(const std::string &MetaIndexFile,
1915 const std::string &MetaIndexFileSignature)
1916 {
1917 AuthPass = true;
1918 Desc.URI = "gpgv:" + MetaIndexFileSignature;
1919 DestFile = MetaIndexFile;
1920 QueueURI(Desc);
1921 SetActiveSubprocess("gpgv");
1922 }
1923 /*}}}*/
1924 // pkgAcqMetaBase::CheckDownloadDone /*{{{*/
1925 bool pkgAcqMetaBase::CheckDownloadDone(const std::string &Message,
1926 const std::string &RealURI)
1927 {
1928 // We have just finished downloading a Release file (it is not
1929 // verified yet)
1930
1931 string FileName = LookupTag(Message,"Filename");
1932 if (FileName.empty() == true)
1933 {
1934 Status = StatError;
1935 ErrorText = "Method gave a blank filename";
1936 return false;
1937 }
1938
1939 if (FileName != DestFile)
1940 {
1941 Local = true;
1942 Desc.URI = "copy:" + FileName;
1943 QueueURI(Desc);
1944 return false;
1945 }
1946
1947 // make sure to verify against the right file on I-M-S hit
1948 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
1949 if(IMSHit)
1950 {
1951 string FinalFile = _config->FindDir("Dir::State::lists");
1952 FinalFile += URItoFileName(RealURI);
1953 DestFile = FinalFile;
1954 }
1955
1956 // set Item to complete as the remaining work is all local (verify etc)
1957 Complete = true;
1958
1959 return true;
1960 }
1961 /*}}}*/
1962 void pkgAcqMetaBase::QueueIndexes(bool verify) /*{{{*/
1963 {
1964 bool transInRelease = false;
1965 {
1966 std::vector<std::string> const keys = MetaIndexParser->MetaKeys();
1967 for (std::vector<std::string>::const_iterator k = keys.begin(); k != keys.end(); ++k)
1968 // FIXME: Feels wrong to check for hardcoded string here, but what should we do else…
1969 if (k->find("Translation-") != std::string::npos)
1970 {
1971 transInRelease = true;
1972 break;
1973 }
1974 }
1975
1976 // at this point the real Items are loaded in the fetcher
1977 ExpectedAdditionalItems = 0;
1978 for (vector <IndexTarget*>::const_iterator Target = IndexTargets->begin();
1979 Target != IndexTargets->end();
1980 ++Target)
1981 {
1982 HashStringList ExpectedIndexHashes;
1983 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1984 bool compressedAvailable = false;
1985 if (Record == NULL)
1986 {
1987 if ((*Target)->IsOptional() == true)
1988 {
1989 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
1990 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
1991 if (MetaIndexParser->Exists((*Target)->MetaKey + "." + *t) == true)
1992 {
1993 compressedAvailable = true;
1994 break;
1995 }
1996 }
1997 else if (verify == true)
1998 {
1999 Status = StatAuthError;
2000 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
2001 return;
2002 }
2003 }
2004 else
2005 {
2006 ExpectedIndexHashes = Record->Hashes;
2007 if (_config->FindB("Debug::pkgAcquire::Auth", false))
2008 {
2009 std::cerr << "Queueing: " << (*Target)->URI << std::endl
2010 << "Expected Hash:" << std::endl;
2011 for (HashStringList::const_iterator hs = ExpectedIndexHashes.begin(); hs != ExpectedIndexHashes.end(); ++hs)
2012 std::cerr << "\t- " << hs->toStr() << std::endl;
2013 std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
2014 }
2015 if (verify == true && ExpectedIndexHashes.empty() == true && (*Target)->IsOptional() == false)
2016 {
2017 Status = StatAuthError;
2018 strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
2019 return;
2020 }
2021 }
2022
2023 if ((*Target)->IsOptional() == true)
2024 {
2025 if (transInRelease == false || Record != NULL || compressedAvailable == true)
2026 {
2027 if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
2028 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
2029 new pkgAcqDiffIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
2030 else
2031 new pkgAcqIndexTrans(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
2032 }
2033 continue;
2034 }
2035
2036 /* Queue Packages file (either diff or full packages files, depending
2037 on the users option) - we also check if the PDiff Index file is listed
2038 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
2039 instead, but passing the required info to it is to much hassle */
2040 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
2041 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
2042 new pkgAcqDiffIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
2043 else
2044 new pkgAcqIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
2045 }
2046 }
2047 /*}}}*/
2048 bool pkgAcqMetaBase::VerifyVendor(string Message, const string &RealURI)/*{{{*/
2049 {
2050 string::size_type pos;
2051
2052 // check for missing sigs (that where not fatal because otherwise we had
2053 // bombed earlier)
2054 string missingkeys;
2055 string msg = _("There is no public key available for the "
2056 "following key IDs:\n");
2057 pos = Message.find("NO_PUBKEY ");
2058 if (pos != std::string::npos)
2059 {
2060 string::size_type start = pos+strlen("NO_PUBKEY ");
2061 string Fingerprint = Message.substr(start, Message.find("\n")-start);
2062 missingkeys += (Fingerprint);
2063 }
2064 if(!missingkeys.empty())
2065 _error->Warning("%s", (msg + missingkeys).c_str());
2066
2067 string Transformed = MetaIndexParser->GetExpectedDist();
2068
2069 if (Transformed == "../project/experimental")
2070 {
2071 Transformed = "experimental";
2072 }
2073
2074 pos = Transformed.rfind('/');
2075 if (pos != string::npos)
2076 {
2077 Transformed = Transformed.substr(0, pos);
2078 }
2079
2080 if (Transformed == ".")
2081 {
2082 Transformed = "";
2083 }
2084
2085 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
2086 MetaIndexParser->GetValidUntil() > 0) {
2087 time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
2088 if (invalid_since > 0)
2089 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
2090 // the time since then the file is invalid - formated in the same way as in
2091 // the download progress display (e.g. 7d 3h 42min 1s)
2092 return _error->Error(
2093 _("Release file for %s is expired (invalid since %s). "
2094 "Updates for this repository will not be applied."),
2095 RealURI.c_str(), TimeToStr(invalid_since).c_str());
2096 }
2097
2098 if (_config->FindB("Debug::pkgAcquire::Auth", false))
2099 {
2100 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
2101 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
2102 std::cerr << "Transformed Dist: " << Transformed << std::endl;
2103 }
2104
2105 if (MetaIndexParser->CheckDist(Transformed) == false)
2106 {
2107 // This might become fatal one day
2108 // Status = StatAuthError;
2109 // ErrorText = "Conflicting distribution; expected "
2110 // + MetaIndexParser->GetExpectedDist() + " but got "
2111 // + MetaIndexParser->GetDist();
2112 // return false;
2113 if (!Transformed.empty())
2114 {
2115 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
2116 Desc.Description.c_str(),
2117 Transformed.c_str(),
2118 MetaIndexParser->GetDist().c_str());
2119 }
2120 }
2121
2122 return true;
2123 }
2124 /*}}}*/
2125 // pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
2126 void pkgAcqMetaIndex::Failed(string Message,
2127 pkgAcquire::MethodConfig * Cnf)
2128 {
2129 pkgAcquire::Item::Failed(Message, Cnf);
2130 Status = StatDone;
2131
2132 string FinalFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
2133
2134 _error->Warning(_("The repository '%s' does not have a Release file. "
2135 "This is deprecated, please contact the owner of the "
2136 "repository."), URIDesc.c_str());
2137
2138 // No Release file was present so fall
2139 // back to queueing Packages files without verification
2140 // only allow going further if the users explicitely wants it
2141 if(_config->FindB("Acquire::AllowInsecureRepositories") == true)
2142 {
2143 // Done, queue for rename on transaction finished
2144 if (FileExists(DestFile))
2145 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2146
2147 // queue without any kind of hashsum support
2148 QueueIndexes(false);
2149 } else {
2150 // warn if the repository is unsinged
2151 _error->Warning("Use --allow-insecure-repositories to force the update");
2152 TransactionManager->AbortTransaction();
2153 Status = StatError;
2154 return;
2155 }
2156 }
2157 /*}}}*/
2158 void pkgAcqMetaIndex::Finished() /*{{{*/
2159 {
2160 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
2161 std::clog << "Finished: " << DestFile <<std::endl;
2162 if(TransactionManager != NULL &&
2163 TransactionManager->TransactionHasError() == false)
2164 TransactionManager->CommitTransaction();
2165 }
2166 /*}}}*/
2167 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
2168 string const &URI, string const &URIDesc, string const &ShortDesc,
2169 string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
2170 string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
2171 const vector<IndexTarget*>* IndexTargets,
2172 indexRecords* MetaIndexParser) :
2173 pkgAcqMetaIndex(Owner, NULL, URI, URIDesc, ShortDesc, MetaSigURI, MetaSigURIDesc,MetaSigShortDesc, IndexTargets, MetaIndexParser),
2174 MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
2175 MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
2176 {
2177 // index targets + (worst case:) Release/Release.gpg
2178 ExpectedAdditionalItems = IndexTargets->size() + 2;
2179
2180 }
2181 /*}}}*/
2182 pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
2183 {
2184 }
2185 /*}}}*/
2186 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
2187 // ---------------------------------------------------------------------
2188 string pkgAcqMetaClearSig::Custom600Headers() const
2189 {
2190 string Final = _config->FindDir("Dir::State::lists");
2191 Final += URItoFileName(RealURI);
2192
2193 struct stat Buf;
2194 if (stat(Final.c_str(),&Buf) != 0)
2195 {
2196 if (stat(Final.c_str(),&Buf) != 0)
2197 return "\nIndex-File: true\nFail-Ignore: true\n";
2198 }
2199
2200 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
2201 }
2202 /*}}}*/
2203 // pkgAcqMetaClearSig::Done - We got a file /*{{{*/
2204 // ---------------------------------------------------------------------
2205 void pkgAcqMetaClearSig::Done(std::string Message,unsigned long long /*Size*/,
2206 HashStringList const &/*Hashes*/,
2207 pkgAcquire::MethodConfig *Cnf)
2208 {
2209 // if we expect a ClearTextSignature (InRelase), ensure that
2210 // this is what we get and if not fail to queue a
2211 // Release/Release.gpg, see #346386
2212 if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
2213 {
2214 pkgAcquire::Item::Failed(Message, Cnf);
2215 RenameOnError(NotClearsigned);
2216 TransactionManager->AbortTransaction();
2217 return;
2218 }
2219
2220 if(AuthPass == false)
2221 {
2222 if(CheckDownloadDone(Message, RealURI) == true)
2223 QueueForSignatureVerify(DestFile, DestFile);
2224 return;
2225 }
2226 else
2227 {
2228 if(CheckAuthDone(Message, RealURI) == true)
2229 {
2230 string FinalFile = _config->FindDir("Dir::State::lists");
2231 FinalFile += URItoFileName(RealURI);
2232
2233 // queue for copy in place
2234 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2235 }
2236 }
2237 }
2238 /*}}}*/
2239 void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
2240 {
2241 Item::Failed(Message, Cnf);
2242
2243 // we failed, we will not get additional items from this method
2244 ExpectedAdditionalItems = 0;
2245
2246 if (AuthPass == false)
2247 {
2248 // Queue the 'old' InRelease file for removal if we try Release.gpg
2249 // as otherwise the file will stay around and gives a false-auth
2250 // impression (CVE-2012-0214)
2251 string FinalFile = _config->FindDir("Dir::State::lists");
2252 FinalFile.append(URItoFileName(RealURI));
2253 TransactionManager->TransactionStageRemoval(this, FinalFile);
2254 Status = StatDone;
2255
2256 new pkgAcqMetaIndex(Owner, TransactionManager,
2257 MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
2258 MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
2259 IndexTargets, MetaIndexParser);
2260 }
2261 else
2262 {
2263 if(CheckStopAuthentication(RealURI, Message))
2264 return;
2265
2266 _error->Warning(_("The data from '%s' is not signed. Packages "
2267 "from that repository can not be authenticated."),
2268 URIDesc.c_str());
2269
2270 // No Release file was present, or verification failed, so fall
2271 // back to queueing Packages files without verification
2272 // only allow going further if the users explicitely wants it
2273 if(_config->FindB("Acquire::AllowInsecureRepositories") == true)
2274 {
2275 Status = StatDone;
2276
2277 /* Always move the meta index, even if gpgv failed. This ensures
2278 * that PackageFile objects are correctly filled in */
2279 if (FileExists(DestFile))
2280 {
2281 string FinalFile = _config->FindDir("Dir::State::lists");
2282 FinalFile += URItoFileName(RealURI);
2283 /* InRelease files become Release files, otherwise
2284 * they would be considered as trusted later on */
2285 RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
2286 "Release");
2287 FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
2288 "Release");
2289
2290 // Done, queue for rename on transaction finished
2291 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2292 }
2293 QueueIndexes(false);
2294 } else {
2295 // warn if the repository is unsigned
2296 _error->Warning("Use --allow-insecure-repositories to force the update");
2297 TransactionManager->AbortTransaction();
2298 Status = StatError;
2299 }
2300 }
2301 }
2302 /*}}}*/
2303 // AcqArchive::AcqArchive - Constructor /*{{{*/
2304 // ---------------------------------------------------------------------
2305 /* This just sets up the initial fetch environment and queues the first
2306 possibilitiy */
2307 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
2308 pkgRecords *Recs,pkgCache::VerIterator const &Version,
2309 string &StoreFilename) :
2310 Item(Owner, HashStringList()), Version(Version), Sources(Sources), Recs(Recs),
2311 StoreFilename(StoreFilename), Vf(Version.FileList()),
2312 Trusted(false)
2313 {
2314 Retries = _config->FindI("Acquire::Retries",0);
2315
2316 if (Version.Arch() == 0)
2317 {
2318 _error->Error(_("I wasn't able to locate a file for the %s package. "
2319 "This might mean you need to manually fix this package. "
2320 "(due to missing arch)"),
2321 Version.ParentPkg().FullName().c_str());
2322 return;
2323 }
2324
2325 /* We need to find a filename to determine the extension. We make the
2326 assumption here that all the available sources for this version share
2327 the same extension.. */
2328 // Skip not source sources, they do not have file fields.
2329 for (; Vf.end() == false; ++Vf)
2330 {
2331 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2332 continue;
2333 break;
2334 }
2335
2336 // Does not really matter here.. we are going to fail out below
2337 if (Vf.end() != true)
2338 {
2339 // If this fails to get a file name we will bomb out below.
2340 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2341 if (_error->PendingError() == true)
2342 return;
2343
2344 // Generate the final file name as: package_version_arch.foo
2345 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2346 QuoteString(Version.VerStr(),"_:") + '_' +
2347 QuoteString(Version.Arch(),"_:.") +
2348 "." + flExtension(Parse.FileName());
2349 }
2350
2351 // check if we have one trusted source for the package. if so, switch
2352 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2353 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2354 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2355 bool seenUntrusted = false;
2356 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
2357 {
2358 pkgIndexFile *Index;
2359 if (Sources->FindIndex(i.File(),Index) == false)
2360 continue;
2361
2362 if (debugAuth == true)
2363 std::cerr << "Checking index: " << Index->Describe()
2364 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2365
2366 if (Index->IsTrusted() == true)
2367 {
2368 Trusted = true;
2369 if (allowUnauth == false)
2370 break;
2371 }
2372 else
2373 seenUntrusted = true;
2374 }
2375
2376 // "allow-unauthenticated" restores apts old fetching behaviour
2377 // that means that e.g. unauthenticated file:// uris are higher
2378 // priority than authenticated http:// uris
2379 if (allowUnauth == true && seenUntrusted == true)
2380 Trusted = false;
2381
2382 // Select a source
2383 if (QueueNext() == false && _error->PendingError() == false)
2384 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2385 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2386 }
2387 /*}}}*/
2388 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
2389 // ---------------------------------------------------------------------
2390 /* This queues the next available file version for download. It checks if
2391 the archive is already available in the cache and stashs the MD5 for
2392 checking later. */
2393 bool pkgAcqArchive::QueueNext()
2394 {
2395 for (; Vf.end() == false; ++Vf)
2396 {
2397 // Ignore not source sources
2398 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2399 continue;
2400
2401 // Try to cross match against the source list
2402 pkgIndexFile *Index;
2403 if (Sources->FindIndex(Vf.File(),Index) == false)
2404 continue;
2405
2406 // only try to get a trusted package from another source if that source
2407 // is also trusted
2408 if(Trusted && !Index->IsTrusted())
2409 continue;
2410
2411 // Grab the text package record
2412 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2413 if (_error->PendingError() == true)
2414 return false;
2415
2416 string PkgFile = Parse.FileName();
2417 ExpectedHashes = Parse.Hashes();
2418
2419 if (PkgFile.empty() == true)
2420 return _error->Error(_("The package index files are corrupted. No Filename: "
2421 "field for package %s."),
2422 Version.ParentPkg().Name());
2423
2424 Desc.URI = Index->ArchiveURI(PkgFile);
2425 Desc.Description = Index->ArchiveInfo(Version);
2426 Desc.Owner = this;
2427 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2428
2429 // See if we already have the file. (Legacy filenames)
2430 FileSize = Version->Size;
2431 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2432 struct stat Buf;
2433 if (stat(FinalFile.c_str(),&Buf) == 0)
2434 {
2435 // Make sure the size matches
2436 if ((unsigned long long)Buf.st_size == Version->Size)
2437 {
2438 Complete = true;
2439 Local = true;
2440 Status = StatDone;
2441 StoreFilename = DestFile = FinalFile;
2442 return true;
2443 }
2444
2445 /* Hmm, we have a file and its size does not match, this means it is
2446 an old style mismatched arch */
2447 unlink(FinalFile.c_str());
2448 }
2449
2450 // Check it again using the new style output filenames
2451 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2452 if (stat(FinalFile.c_str(),&Buf) == 0)
2453 {
2454 // Make sure the size matches
2455 if ((unsigned long long)Buf.st_size == Version->Size)
2456 {
2457 Complete = true;
2458 Local = true;
2459 Status = StatDone;
2460 StoreFilename = DestFile = FinalFile;
2461 return true;
2462 }
2463
2464 /* Hmm, we have a file and its size does not match, this shouldn't
2465 happen.. */
2466 unlink(FinalFile.c_str());
2467 }
2468
2469 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
2470
2471 // Check the destination file
2472 if (stat(DestFile.c_str(),&Buf) == 0)
2473 {
2474 // Hmm, the partial file is too big, erase it
2475 if ((unsigned long long)Buf.st_size > Version->Size)
2476 unlink(DestFile.c_str());
2477 else
2478 {
2479 PartialSize = Buf.st_size;
2480 ChangeOwnerAndPermissionOfFile("pkgAcqArchive::QueueNext", FinalFile.c_str(), "_apt", "root", 0600);
2481 }
2482 }
2483
2484 // Disables download of archives - useful if no real installation follows,
2485 // e.g. if we are just interested in proposed installation order
2486 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2487 {
2488 Complete = true;
2489 Local = true;
2490 Status = StatDone;
2491 StoreFilename = DestFile = FinalFile;
2492 return true;
2493 }
2494
2495 // Create the item
2496 Local = false;
2497 QueueURI(Desc);
2498
2499 ++Vf;
2500 return true;
2501 }
2502 return false;
2503 }
2504 /*}}}*/
2505 // AcqArchive::Done - Finished fetching /*{{{*/
2506 // ---------------------------------------------------------------------
2507 /* */
2508 void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList const &CalcHashes,
2509 pkgAcquire::MethodConfig *Cfg)
2510 {
2511 Item::Done(Message, Size, CalcHashes, Cfg);
2512
2513 // Check the size
2514 if (Size != Version->Size)
2515 {
2516 RenameOnError(SizeMismatch);
2517 return;
2518 }
2519
2520 // FIXME: could this empty() check impose *any* sort of security issue?
2521 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2522 {
2523 RenameOnError(HashSumMismatch);
2524 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2525 return;
2526 }
2527
2528 // Grab the output filename
2529 string FileName = LookupTag(Message,"Filename");
2530 if (FileName.empty() == true)
2531 {
2532 Status = StatError;
2533 ErrorText = "Method gave a blank filename";
2534 return;
2535 }
2536
2537 // Reference filename
2538 if (FileName != DestFile)
2539 {
2540 StoreFilename = DestFile = FileName;
2541 Local = true;
2542 Complete = true;
2543 return;
2544 }
2545
2546 // Done, move it into position
2547 string FinalFile = _config->FindDir("Dir::Cache::Archives");
2548 FinalFile += flNotDir(StoreFilename);
2549 Rename(DestFile,FinalFile);
2550 ChangeOwnerAndPermissionOfFile("pkgAcqArchive::Done", FinalFile.c_str(), "root", "root", 0644);
2551 StoreFilename = DestFile = FinalFile;
2552 Complete = true;
2553 }
2554 /*}}}*/
2555 // AcqArchive::Failed - Failure handler /*{{{*/
2556 // ---------------------------------------------------------------------
2557 /* Here we try other sources */
2558 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2559 {
2560 ErrorText = LookupTag(Message,"Message");
2561
2562 /* We don't really want to retry on failed media swaps, this prevents
2563 that. An interesting observation is that permanent failures are not
2564 recorded. */
2565 if (Cnf->Removable == true &&
2566 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2567 {
2568 // Vf = Version.FileList();
2569 while (Vf.end() == false) ++Vf;
2570 StoreFilename = string();
2571 Item::Failed(Message,Cnf);
2572 return;
2573 }
2574
2575 if (QueueNext() == false)
2576 {
2577 // This is the retry counter
2578 if (Retries != 0 &&
2579 Cnf->LocalOnly == false &&
2580 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2581 {
2582 Retries--;
2583 Vf = Version.FileList();
2584 if (QueueNext() == true)
2585 return;
2586 }
2587
2588 StoreFilename = string();
2589 Item::Failed(Message,Cnf);
2590 }
2591 }
2592 /*}}}*/
2593 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
2594 // ---------------------------------------------------------------------
2595 APT_PURE bool pkgAcqArchive::IsTrusted() const
2596 {
2597 return Trusted;
2598 }
2599 /*}}}*/
2600 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
2601 // ---------------------------------------------------------------------
2602 /* */
2603 void pkgAcqArchive::Finished()
2604 {
2605 if (Status == pkgAcquire::Item::StatDone &&
2606 Complete == true)
2607 return;
2608 StoreFilename = string();
2609 }
2610 /*}}}*/
2611 // AcqFile::pkgAcqFile - Constructor /*{{{*/
2612 // ---------------------------------------------------------------------
2613 /* The file is added to the queue */
2614 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI, HashStringList const &Hashes,
2615 unsigned long long Size,string Dsc,string ShortDesc,
2616 const string &DestDir, const string &DestFilename,
2617 bool IsIndexFile) :
2618 Item(Owner, Hashes), IsIndexFile(IsIndexFile)
2619 {
2620 Retries = _config->FindI("Acquire::Retries",0);
2621
2622 if(!DestFilename.empty())
2623 DestFile = DestFilename;
2624 else if(!DestDir.empty())
2625 DestFile = DestDir + "/" + flNotDir(URI);
2626 else
2627 DestFile = flNotDir(URI);
2628
2629 // Create the item
2630 Desc.URI = URI;
2631 Desc.Description = Dsc;
2632 Desc.Owner = this;
2633
2634 // Set the short description to the archive component
2635 Desc.ShortDesc = ShortDesc;
2636
2637 // Get the transfer sizes
2638 FileSize = Size;
2639 struct stat Buf;
2640 if (stat(DestFile.c_str(),&Buf) == 0)
2641 {
2642 // Hmm, the partial file is too big, erase it
2643 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
2644 unlink(DestFile.c_str());
2645 else
2646 {
2647 PartialSize = Buf.st_size;
2648 ChangeOwnerAndPermissionOfFile("pkgAcqFile", DestFile.c_str(), "_apt", "root", 0600);
2649 }
2650 }
2651
2652 QueueURI(Desc);
2653 }
2654 /*}}}*/
2655 // AcqFile::Done - Item downloaded OK /*{{{*/
2656 // ---------------------------------------------------------------------
2657 /* */
2658 void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList const &CalcHashes,
2659 pkgAcquire::MethodConfig *Cnf)
2660 {
2661 Item::Done(Message,Size,CalcHashes,Cnf);
2662
2663 // Check the hash
2664 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2665 {
2666 RenameOnError(HashSumMismatch);
2667 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2668 return;
2669 }
2670
2671 string FileName = LookupTag(Message,"Filename");
2672 if (FileName.empty() == true)
2673 {
2674 Status = StatError;
2675 ErrorText = "Method gave a blank filename";
2676 return;
2677 }
2678
2679 Complete = true;
2680
2681 // The files timestamp matches
2682 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2683 return;
2684
2685 // We have to copy it into place
2686 if (FileName != DestFile)
2687 {
2688 Local = true;
2689 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
2690 Cnf->Removable == true)
2691 {
2692 Desc.URI = "copy:" + FileName;
2693 QueueURI(Desc);
2694 return;
2695 }
2696
2697 // Erase the file if it is a symlink so we can overwrite it
2698 struct stat St;
2699 if (lstat(DestFile.c_str(),&St) == 0)
2700 {
2701 if (S_ISLNK(St.st_mode) != 0)
2702 unlink(DestFile.c_str());
2703 }
2704
2705 // Symlink the file
2706 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
2707 {
2708 ErrorText = "Link to " + DestFile + " failure ";
2709 Status = StatError;
2710 Complete = false;
2711 }
2712 }
2713 }
2714 /*}}}*/
2715 // AcqFile::Failed - Failure handler /*{{{*/
2716 // ---------------------------------------------------------------------
2717 /* Here we try other sources */
2718 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2719 {
2720 ErrorText = LookupTag(Message,"Message");
2721
2722 // This is the retry counter
2723 if (Retries != 0 &&
2724 Cnf->LocalOnly == false &&
2725 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2726 {
2727 Retries--;
2728 QueueURI(Desc);
2729 return;
2730 }
2731
2732 Item::Failed(Message,Cnf);
2733 }
2734 /*}}}*/
2735 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2736 // ---------------------------------------------------------------------
2737 /* The only header we use is the last-modified header. */
2738 string pkgAcqFile::Custom600Headers() const
2739 {
2740 if (IsIndexFile)
2741 return "\nIndex-File: true";
2742 return "";
2743 }
2744 /*}}}*/