]> git.saurik.com Git - apt.git/blob - apt-pkg/indexcopy.cc
e38fe3e45942e96d820d5a2b14f99a7446a360d4
[apt.git] / apt-pkg / indexcopy.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: indexcopy.cc,v 1.10 2002/03/26 07:38:58 jgg Exp $
4 /* ######################################################################
5
6 Index Copying - Aid for copying and verifying the index files
7
8 This class helps apt-cache reconstruct a damaged index files.
9
10 ##################################################################### */
11 /*}}}*/
12 // Include Files /*{{{*/
13 #include<config.h>
14
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/progress.h>
17 #include <apt-pkg/strutl.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/aptconfiguration.h>
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/tagfile.h>
22 #include <apt-pkg/indexrecords.h>
23 #include <apt-pkg/md5.h>
24 #include <apt-pkg/cdrom.h>
25
26 #include <iostream>
27 #include <sstream>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34
35 #include "indexcopy.h"
36 #include <apti18n.h>
37 /*}}}*/
38
39 using namespace std;
40
41 // DecompressFile - wrapper for decompressing compressed files /*{{{*/
42 // ---------------------------------------------------------------------
43 /* */
44 bool DecompressFile(string Filename, int *fd, off_t *FileSize)
45 {
46 struct stat Buf;
47 *fd = -1;
48
49 std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
50 std::vector<APT::Configuration::Compressor>::const_iterator UnCompress;
51 std::string file = std::string(Filename).append(UnCompress->Extension);
52 for (UnCompress = compressor.begin(); UnCompress != compressor.end(); ++UnCompress)
53 {
54 if (stat(file.c_str(), &Buf) == 0)
55 break;
56 }
57
58 if (UnCompress == compressor.end())
59 return _error->Errno("decompressor", "Unable to parse file");
60
61 *FileSize = Buf.st_size;
62
63 // Create a data pipe
64 int Pipe[2] = {-1,-1};
65 if (pipe(Pipe) != 0)
66 return _error->Errno("pipe",_("Failed to create subprocess IPC"));
67 for (int J = 0; J != 2; J++)
68 SetCloseExec(Pipe[J],true);
69
70 *fd = Pipe[1];
71
72 // The child..
73 pid_t Pid = ExecFork();
74 if (Pid == 0)
75 {
76 dup2(Pipe[1],STDOUT_FILENO);
77 SetCloseExec(STDOUT_FILENO, false);
78
79 std::vector<char const*> Args;
80 Args.push_back(UnCompress->Binary.c_str());
81 for (std::vector<std::string>::const_iterator a = UnCompress->UncompressArgs.begin();
82 a != UnCompress->UncompressArgs.end(); ++a)
83 Args.push_back(a->c_str());
84 Args.push_back("--stdout");
85 Args.push_back(file.c_str());
86 Args.push_back(NULL);
87
88 execvp(Args[0],(char **)&Args[0]);
89 cerr << _("Failed to exec compressor ") << Args[0] << endl;
90 _exit(100);
91 }
92
93 // Wait for decompress to finish
94 if (ExecWait(Pid, UnCompress->Binary.c_str(), false) == false)
95 return false;
96
97 return true;
98 }
99 /*}}}*/
100 // IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
101 // ---------------------------------------------------------------------
102 /* */
103 bool IndexCopy::CopyPackages(string CDROM,string Name,vector<string> &List,
104 pkgCdromStatus *log)
105 {
106 OpProgress *Progress = NULL;
107 if (List.empty() == true)
108 return true;
109
110 if(log)
111 Progress = log->GetOpProgress();
112
113 bool NoStat = _config->FindB("APT::CDROM::Fast",false);
114 bool Debug = _config->FindB("Debug::aptcdrom",false);
115
116 // Prepare the progress indicator
117 off_t TotalSize = 0;
118 std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
119 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
120 {
121 struct stat Buf;
122 bool found = false;
123 std::string file = std::string(*I).append(GetFileName());
124 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
125 c != compressor.end(); ++c)
126 {
127 if (stat(std::string(file + c->Extension).c_str(), &Buf) != 0)
128 continue;
129 found = true;
130 break;
131 }
132
133 if (found == false)
134 return _error->Errno("stat", "Stat failed for %s", file.c_str());
135 TotalSize += Buf.st_size;
136 }
137
138 off_t CurrentSize = 0;
139 unsigned int NotFound = 0;
140 unsigned int WrongSize = 0;
141 unsigned int Packages = 0;
142 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
143 {
144 string OrigPath = string(*I,CDROM.length());
145 off_t FileSize = 0;
146
147 // Open the package file
148 FileFd Pkg;
149 if (RealFileExists(*I + GetFileName()) == true)
150 {
151 Pkg.Open(*I + GetFileName(),FileFd::ReadOnly);
152 FileSize = Pkg.Size();
153 }
154 else
155 {
156 int fd;
157 if (!DecompressFile(string(*I + GetFileName()), &fd, &FileSize))
158 return _error->Errno("decompress","Decompress failed for %s",
159 string(*I + GetFileName()).c_str());
160 Pkg.Fd(dup(fd));
161 Pkg.Seek(0);
162 }
163
164 pkgTagFile Parser(&Pkg);
165 if (_error->PendingError() == true)
166 return false;
167
168 // Open the output file
169 char S[400];
170 snprintf(S,sizeof(S),"cdrom:[%s]/%s%s",Name.c_str(),
171 (*I).c_str() + CDROM.length(),GetFileName());
172 string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
173 TargetF += URItoFileName(S);
174 FileFd Target;
175 if (_config->FindB("APT::CDROM::NoAct",false) == true)
176 {
177 TargetF = "/dev/null";
178 Target.Open(TargetF,FileFd::WriteExists);
179 } else {
180 Target.Open(TargetF,FileFd::WriteAtomic);
181 }
182 FILE *TargetFl = fdopen(dup(Target.Fd()),"w");
183 if (_error->PendingError() == true)
184 return false;
185 if (TargetFl == 0)
186 return _error->Errno("fdopen","Failed to reopen fd");
187
188 // Setup the progress meter
189 if(Progress)
190 Progress->OverallProgress(CurrentSize,TotalSize,FileSize,
191 string("Reading ") + Type() + " Indexes");
192
193 // Parse
194 if(Progress)
195 Progress->SubProgress(Pkg.Size());
196 pkgTagSection Section;
197 this->Section = &Section;
198 string Prefix;
199 unsigned long Hits = 0;
200 unsigned long Chop = 0;
201 while (Parser.Step(Section) == true)
202 {
203 if(Progress)
204 Progress->Progress(Parser.Offset());
205 string File;
206 unsigned long long Size;
207 if (GetFile(File,Size) == false)
208 {
209 fclose(TargetFl);
210 return false;
211 }
212
213 if (Chop != 0)
214 File = OrigPath + ChopDirs(File,Chop);
215
216 // See if the file exists
217 bool Mangled = false;
218 if (NoStat == false || Hits < 10)
219 {
220 // Attempt to fix broken structure
221 if (Hits == 0)
222 {
223 if (ReconstructPrefix(Prefix,OrigPath,CDROM,File) == false &&
224 ReconstructChop(Chop,*I,File) == false)
225 {
226 if (Debug == true)
227 clog << "Missed: " << File << endl;
228 NotFound++;
229 continue;
230 }
231 if (Chop != 0)
232 File = OrigPath + ChopDirs(File,Chop);
233 }
234
235 // Get the size
236 struct stat Buf;
237 if (stat(string(CDROM + Prefix + File).c_str(),&Buf) != 0 ||
238 Buf.st_size == 0)
239 {
240 // Attempt to fix busted symlink support for one instance
241 string OrigFile = File;
242 string::size_type Start = File.find("binary-");
243 string::size_type End = File.find("/",Start+3);
244 if (Start != string::npos && End != string::npos)
245 {
246 File.replace(Start,End-Start,"binary-all");
247 Mangled = true;
248 }
249
250 if (Mangled == false ||
251 stat(string(CDROM + Prefix + File).c_str(),&Buf) != 0)
252 {
253 if (Debug == true)
254 clog << "Missed(2): " << OrigFile << endl;
255 NotFound++;
256 continue;
257 }
258 }
259
260 // Size match
261 if ((unsigned long long)Buf.st_size != Size)
262 {
263 if (Debug == true)
264 clog << "Wrong Size: " << File << endl;
265 WrongSize++;
266 continue;
267 }
268 }
269
270 Packages++;
271 Hits++;
272
273 if (RewriteEntry(TargetFl,File) == false)
274 {
275 fclose(TargetFl);
276 return false;
277 }
278 }
279 fclose(TargetFl);
280
281 if (Debug == true)
282 cout << " Processed by using Prefix '" << Prefix << "' and chop " << Chop << endl;
283
284 if (_config->FindB("APT::CDROM::NoAct",false) == false)
285 {
286 // Move out of the partial directory
287 Target.Close();
288 string FinalF = _config->FindDir("Dir::State::lists");
289 FinalF += URItoFileName(S);
290 if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
291 return _error->Errno("rename","Failed to rename");
292 }
293
294 /* Mangle the source to be in the proper notation with
295 prefix dist [component] */
296 *I = string(*I,Prefix.length());
297 ConvertToSourceList(CDROM,*I);
298 *I = Prefix + ' ' + *I;
299
300 CurrentSize += FileSize;
301 }
302 if(Progress)
303 Progress->Done();
304
305 // Some stats
306 if(log) {
307 stringstream msg;
308 if(NotFound == 0 && WrongSize == 0)
309 ioprintf(msg, _("Wrote %i records.\n"), Packages);
310 else if (NotFound != 0 && WrongSize == 0)
311 ioprintf(msg, _("Wrote %i records with %i missing files.\n"),
312 Packages, NotFound);
313 else if (NotFound == 0 && WrongSize != 0)
314 ioprintf(msg, _("Wrote %i records with %i mismatched files\n"),
315 Packages, WrongSize);
316 if (NotFound != 0 && WrongSize != 0)
317 ioprintf(msg, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages, NotFound, WrongSize);
318 }
319
320 if (Packages == 0)
321 _error->Warning("No valid records were found.");
322
323 if (NotFound + WrongSize > 10)
324 _error->Warning("A lot of entries were discarded, something may be wrong.\n");
325
326
327 return true;
328 }
329 /*}}}*/
330 // IndexCopy::ChopDirs - Chop off the leading directory components /*{{{*/
331 // ---------------------------------------------------------------------
332 /* */
333 string IndexCopy::ChopDirs(string Path,unsigned int Depth)
334 {
335 string::size_type I = 0;
336 do
337 {
338 I = Path.find('/',I+1);
339 Depth--;
340 }
341 while (I != string::npos && Depth != 0);
342
343 if (I == string::npos)
344 return string();
345
346 return string(Path,I+1);
347 }
348 /*}}}*/
349 // IndexCopy::ReconstructPrefix - Fix strange prefixing /*{{{*/
350 // ---------------------------------------------------------------------
351 /* This prepends dir components from the path to the package files to
352 the path to the deb until it is found */
353 bool IndexCopy::ReconstructPrefix(string &Prefix,string OrigPath,string CD,
354 string File)
355 {
356 bool Debug = _config->FindB("Debug::aptcdrom",false);
357 unsigned int Depth = 1;
358 string MyPrefix = Prefix;
359 while (1)
360 {
361 struct stat Buf;
362 if (stat(string(CD + MyPrefix + File).c_str(),&Buf) != 0)
363 {
364 if (Debug == true)
365 cout << "Failed, " << CD + MyPrefix + File << endl;
366 if (GrabFirst(OrigPath,MyPrefix,Depth++) == true)
367 continue;
368
369 return false;
370 }
371 else
372 {
373 Prefix = MyPrefix;
374 return true;
375 }
376 }
377 return false;
378 }
379 /*}}}*/
380 // IndexCopy::ReconstructChop - Fixes bad source paths /*{{{*/
381 // ---------------------------------------------------------------------
382 /* This removes path components from the filename and prepends the location
383 of the package files until a file is found */
384 bool IndexCopy::ReconstructChop(unsigned long &Chop,string Dir,string File)
385 {
386 // Attempt to reconstruct the filename
387 unsigned long Depth = 0;
388 while (1)
389 {
390 struct stat Buf;
391 if (stat(string(Dir + File).c_str(),&Buf) != 0)
392 {
393 File = ChopDirs(File,1);
394 Depth++;
395 if (File.empty() == false)
396 continue;
397 return false;
398 }
399 else
400 {
401 Chop = Depth;
402 return true;
403 }
404 }
405 return false;
406 }
407 /*}}}*/
408 // IndexCopy::ConvertToSourceList - Convert a Path to a sourcelist /*{{{*/
409 // ---------------------------------------------------------------------
410 /* We look for things in dists/ notation and convert them to
411 <dist> <component> form otherwise it is left alone. This also strips
412 the CD path.
413
414 This implements a regex sort of like:
415 (.*)/dists/([^/]*)/(.*)/binary-*
416 ^ ^ ^- Component
417 | |-------- Distribution
418 |------------------- Path
419
420 It was deciced to use only a single word for dist (rather than say
421 unstable/non-us) to increase the chance that each CD gets a single
422 line in sources.list.
423 */
424 void IndexCopy::ConvertToSourceList(string CD,string &Path)
425 {
426 char S[300];
427 snprintf(S,sizeof(S),"binary-%s",_config->Find("Apt::Architecture").c_str());
428
429 // Strip the cdrom base path
430 Path = string(Path,CD.length());
431 if (Path.empty() == true)
432 Path = "/";
433
434 // Too short to be a dists/ type
435 if (Path.length() < strlen("dists/"))
436 return;
437
438 // Not a dists type.
439 if (stringcmp(Path.c_str(),Path.c_str()+strlen("dists/"),"dists/") != 0)
440 return;
441
442 // Isolate the dist
443 string::size_type Slash = strlen("dists/");
444 string::size_type Slash2 = Path.find('/',Slash + 1);
445 if (Slash2 == string::npos || Slash2 + 2 >= Path.length())
446 return;
447 string Dist = string(Path,Slash,Slash2 - Slash);
448
449 // Isolate the component
450 Slash = Slash2;
451 for (unsigned I = 0; I != 10; I++)
452 {
453 Slash = Path.find('/',Slash+1);
454 if (Slash == string::npos || Slash + 2 >= Path.length())
455 return;
456 string Comp = string(Path,Slash2+1,Slash - Slash2-1);
457
458 // Verify the trailing binary- bit
459 string::size_type BinSlash = Path.find('/',Slash + 1);
460 if (Slash == string::npos)
461 return;
462 string Binary = string(Path,Slash+1,BinSlash - Slash-1);
463
464 if (Binary != S && Binary != "source")
465 continue;
466
467 Path = Dist + ' ' + Comp;
468 return;
469 }
470 }
471 /*}}}*/
472 // IndexCopy::GrabFirst - Return the first Depth path components /*{{{*/
473 // ---------------------------------------------------------------------
474 /* */
475 bool IndexCopy::GrabFirst(string Path,string &To,unsigned int Depth)
476 {
477 string::size_type I = 0;
478 do
479 {
480 I = Path.find('/',I+1);
481 Depth--;
482 }
483 while (I != string::npos && Depth != 0);
484
485 if (I == string::npos)
486 return false;
487
488 To = string(Path,0,I+1);
489 return true;
490 }
491 /*}}}*/
492 // PackageCopy::GetFile - Get the file information from the section /*{{{*/
493 // ---------------------------------------------------------------------
494 /* */
495 bool PackageCopy::GetFile(string &File,unsigned long long &Size)
496 {
497 File = Section->FindS("Filename");
498 Size = Section->FindI("Size");
499 if (File.empty() || Size == 0)
500 return _error->Error("Cannot find filename or size tag");
501 return true;
502 }
503 /*}}}*/
504 // PackageCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
505 // ---------------------------------------------------------------------
506 /* */
507 bool PackageCopy::RewriteEntry(FILE *Target,string File)
508 {
509 TFRewriteData Changes[] = {{"Filename",File.c_str()},
510 {}};
511
512 if (TFRewrite(Target,*Section,TFRewritePackageOrder,Changes) == false)
513 return false;
514 fputc('\n',Target);
515 return true;
516 }
517 /*}}}*/
518 // SourceCopy::GetFile - Get the file information from the section /*{{{*/
519 // ---------------------------------------------------------------------
520 /* */
521 bool SourceCopy::GetFile(string &File,unsigned long long &Size)
522 {
523 string Files = Section->FindS("Files");
524 if (Files.empty() == true)
525 return false;
526
527 // Stash the / terminated directory prefix
528 string Base = Section->FindS("Directory");
529 if (Base.empty() == false && Base[Base.length()-1] != '/')
530 Base += '/';
531
532 // Read the first file triplet
533 const char *C = Files.c_str();
534 string sSize;
535 string MD5Hash;
536
537 // Parse each of the elements
538 if (ParseQuoteWord(C,MD5Hash) == false ||
539 ParseQuoteWord(C,sSize) == false ||
540 ParseQuoteWord(C,File) == false)
541 return _error->Error("Error parsing file record");
542
543 // Parse the size and append the directory
544 Size = strtoull(sSize.c_str(), NULL, 10);
545 File = Base + File;
546 return true;
547 }
548 /*}}}*/
549 // SourceCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
550 // ---------------------------------------------------------------------
551 /* */
552 bool SourceCopy::RewriteEntry(FILE *Target,string File)
553 {
554 string Dir(File,0,File.rfind('/'));
555 TFRewriteData Changes[] = {{"Directory",Dir.c_str()},
556 {}};
557
558 if (TFRewrite(Target,*Section,TFRewriteSourceOrder,Changes) == false)
559 return false;
560 fputc('\n',Target);
561 return true;
562 }
563 /*}}}*/
564 // SigVerify::Verify - Verify a files md5sum against its metaindex /*{{{*/
565 // ---------------------------------------------------------------------
566 /* */
567 bool SigVerify::Verify(string prefix, string file, indexRecords *MetaIndex)
568 {
569 const indexRecords::checkSum *Record = MetaIndex->Lookup(file);
570
571 // we skip non-existing files in the verifcation to support a cdrom
572 // with no Packages file (just a Package.gz), see LP: #255545
573 // (non-existing files are not considered a error)
574 if(!RealFileExists(prefix+file))
575 {
576 _error->Warning(_("Skipping nonexistent file %s"), string(prefix+file).c_str());
577 return true;
578 }
579
580 if (!Record)
581 {
582 _error->Warning(_("Can't find authentication record for: %s"), file.c_str());
583 return false;
584 }
585
586 if (!Record->Hash.VerifyFile(prefix+file))
587 {
588 _error->Warning(_("Hash mismatch for: %s"),file.c_str());
589 return false;
590 }
591
592 if(_config->FindB("Debug::aptcdrom",false))
593 {
594 cout << "File: " << prefix+file << endl;
595 cout << "Expected Hash " << Record->Hash.toStr() << endl;
596 }
597
598 return true;
599 }
600 /*}}}*/
601 bool SigVerify::CopyMetaIndex(string CDROM, string CDName, /*{{{*/
602 string prefix, string file)
603 {
604 char S[400];
605 snprintf(S,sizeof(S),"cdrom:[%s]/%s%s",CDName.c_str(),
606 (prefix).c_str() + CDROM.length(),file.c_str());
607 string TargetF = _config->FindDir("Dir::State::lists");
608 TargetF += URItoFileName(S);
609
610 FileFd Target;
611 FileFd Rel;
612 Target.Open(TargetF,FileFd::WriteAtomic);
613 Rel.Open(prefix + file,FileFd::ReadOnly);
614 if (_error->PendingError() == true)
615 return false;
616 if (CopyFile(Rel,Target) == false)
617 return false;
618
619 return true;
620 }
621 /*}}}*/
622 bool SigVerify::CopyAndVerify(string CDROM,string Name,vector<string> &SigList, /*{{{*/
623 vector<string> PkgList,vector<string> SrcList)
624 {
625 if (SigList.empty() == true)
626 return true;
627
628 bool Debug = _config->FindB("Debug::aptcdrom",false);
629
630 // Read all Release files
631 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
632 {
633 if(Debug)
634 cout << "Signature verify for: " << *I << endl;
635
636 indexRecords *MetaIndex = new indexRecords;
637 string prefix = *I;
638
639 string const releasegpg = *I+"Release.gpg";
640 string const release = *I+"Release";
641
642 // a Release.gpg without a Release should never happen
643 if(RealFileExists(release) == false)
644 {
645 delete MetaIndex;
646 continue;
647 }
648
649 pid_t pid = ExecFork();
650 if(pid < 0) {
651 _error->Error("Fork failed");
652 return false;
653 }
654 if(pid == 0)
655 RunGPGV(release, releasegpg);
656
657 if(!ExecWait(pid, "gpgv")) {
658 _error->Warning("Signature verification failed for: %s",
659 releasegpg.c_str());
660 // something went wrong, don't copy the Release.gpg
661 // FIXME: delete any existing gpg file?
662 continue;
663 }
664
665 // Open the Release file and add it to the MetaIndex
666 if(!MetaIndex->Load(release))
667 {
668 _error->Error("%s",MetaIndex->ErrorText.c_str());
669 return false;
670 }
671
672 // go over the Indexfiles and see if they verify
673 // if so, remove them from our copy of the lists
674 vector<string> keys = MetaIndex->MetaKeys();
675 for (vector<string>::iterator I = keys.begin(); I != keys.end(); ++I)
676 {
677 if(!Verify(prefix,*I, MetaIndex)) {
678 // something went wrong, don't copy the Release.gpg
679 // FIXME: delete any existing gpg file?
680 _error->Discard();
681 continue;
682 }
683 }
684
685 // we need a fresh one for the Release.gpg
686 delete MetaIndex;
687
688 // everything was fine, copy the Release and Release.gpg file
689 CopyMetaIndex(CDROM, Name, prefix, "Release");
690 CopyMetaIndex(CDROM, Name, prefix, "Release.gpg");
691 }
692
693 return true;
694 }
695 /*}}}*/
696 // SigVerify::RunGPGV - returns the command needed for verify /*{{{*/
697 // ---------------------------------------------------------------------
698 /* Generating the commandline for calling gpgv is somehow complicated as
699 we need to add multiple keyrings and user supplied options. Also, as
700 the cdrom code currently can not use the gpgv method we have two places
701 these need to be done - so the place for this method is wrong but better
702 than code duplication… */
703 bool SigVerify::RunGPGV(std::string const &File, std::string const &FileGPG,
704 int const &statusfd, int fd[2])
705 {
706 if (File == FileGPG)
707 {
708 #define SIGMSG "-----BEGIN PGP SIGNED MESSAGE-----\n"
709 char buffer[sizeof(SIGMSG)];
710 FILE* gpg = fopen(File.c_str(), "r");
711 if (gpg == NULL)
712 return _error->Errno("RunGPGV", _("Could not open file %s"), File.c_str());
713 char const * const test = fgets(buffer, sizeof(buffer), gpg);
714 fclose(gpg);
715 if (test == NULL || strcmp(buffer, SIGMSG) != 0)
716 return _error->Error(_("File %s doesn't start with a clearsigned message"), File.c_str());
717 #undef SIGMSG
718 }
719
720
721 string const gpgvpath = _config->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
722 // FIXME: remove support for deprecated APT::GPGV setting
723 string const trustedFile = _config->Find("APT::GPGV::TrustedKeyring", _config->FindFile("Dir::Etc::Trusted"));
724 string const trustedPath = _config->FindDir("Dir::Etc::TrustedParts");
725
726 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
727
728 if (Debug == true)
729 {
730 std::clog << "gpgv path: " << gpgvpath << std::endl;
731 std::clog << "Keyring file: " << trustedFile << std::endl;
732 std::clog << "Keyring path: " << trustedPath << std::endl;
733 }
734
735 std::vector<string> keyrings;
736 if (DirectoryExists(trustedPath))
737 keyrings = GetListOfFilesInDir(trustedPath, "gpg", false, true);
738 if (RealFileExists(trustedFile) == true)
739 keyrings.push_back(trustedFile);
740
741 std::vector<const char *> Args;
742 Args.reserve(30);
743
744 if (keyrings.empty() == true)
745 {
746 // TRANSLATOR: %s is the trusted keyring parts directory
747 return _error->Error(_("No keyring installed in %s."),
748 _config->FindDir("Dir::Etc::TrustedParts").c_str());
749 }
750
751 Args.push_back(gpgvpath.c_str());
752 Args.push_back("--ignore-time-conflict");
753
754 if (statusfd != -1)
755 {
756 Args.push_back("--status-fd");
757 char fd[10];
758 snprintf(fd, sizeof(fd), "%i", statusfd);
759 Args.push_back(fd);
760 }
761
762 for (vector<string>::const_iterator K = keyrings.begin();
763 K != keyrings.end(); ++K)
764 {
765 Args.push_back("--keyring");
766 Args.push_back(K->c_str());
767 }
768
769 Configuration::Item const *Opts;
770 Opts = _config->Tree("Acquire::gpgv::Options");
771 if (Opts != 0)
772 {
773 Opts = Opts->Child;
774 for (; Opts != 0; Opts = Opts->Next)
775 {
776 if (Opts->Value.empty() == true)
777 continue;
778 Args.push_back(Opts->Value.c_str());
779 }
780 }
781
782 Args.push_back(FileGPG.c_str());
783 if (FileGPG != File)
784 Args.push_back(File.c_str());
785 Args.push_back(NULL);
786
787 if (Debug == true)
788 {
789 std::clog << "Preparing to exec: " << gpgvpath;
790 for (std::vector<const char *>::const_iterator a = Args.begin(); *a != NULL; ++a)
791 std::clog << " " << *a;
792 std::clog << std::endl;
793 }
794
795 if (statusfd != -1)
796 {
797 int const nullfd = open("/dev/null", O_RDONLY);
798 close(fd[0]);
799 // Redirect output to /dev/null; we read from the status fd
800 dup2(nullfd, STDOUT_FILENO);
801 dup2(nullfd, STDERR_FILENO);
802 // Redirect the pipe to the status fd (3)
803 dup2(fd[1], statusfd);
804
805 putenv((char *)"LANG=");
806 putenv((char *)"LC_ALL=");
807 putenv((char *)"LC_MESSAGES=");
808 }
809
810 execvp(gpgvpath.c_str(), (char **) &Args[0]);
811 return true;
812 }
813 /*}}}*/
814 bool TranslationsCopy::CopyTranslations(string CDROM,string Name, /*{{{*/
815 vector<string> &List, pkgCdromStatus *log)
816 {
817 OpProgress *Progress = NULL;
818 if (List.empty() == true)
819 return true;
820
821 if(log)
822 Progress = log->GetOpProgress();
823
824 bool Debug = _config->FindB("Debug::aptcdrom",false);
825
826 // Prepare the progress indicator
827 off_t TotalSize = 0;
828 std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
829 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
830 {
831 struct stat Buf;
832 bool found = false;
833 std::string file = *I;
834 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
835 c != compressor.end(); ++c)
836 {
837 if (stat(std::string(file + c->Extension).c_str(), &Buf) != 0)
838 continue;
839 found = true;
840 break;
841 }
842
843 if (found == false)
844 return _error->Errno("stat", "Stat failed for %s", file.c_str());
845 TotalSize += Buf.st_size;
846 }
847
848 off_t CurrentSize = 0;
849 unsigned int NotFound = 0;
850 unsigned int WrongSize = 0;
851 unsigned int Packages = 0;
852 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
853 {
854 string OrigPath = string(*I,CDROM.length());
855 off_t FileSize = 0;
856
857 // Open the package file
858 FileFd Pkg;
859 if (RealFileExists(*I) == true)
860 {
861 Pkg.Open(*I,FileFd::ReadOnly);
862 FileSize = Pkg.Size();
863 }
864 else
865 {
866 int fd;
867 if (!DecompressFile(*I, &fd, &FileSize))
868 return _error->Errno("decompress","Decompress failed for %s", (*I).c_str());
869 Pkg.Fd(dup(fd));
870 Pkg.Seek(0);
871 }
872 pkgTagFile Parser(&Pkg);
873 if (_error->PendingError() == true)
874 return false;
875
876 // Open the output file
877 char S[400];
878 snprintf(S,sizeof(S),"cdrom:[%s]/%s",Name.c_str(),
879 (*I).c_str() + CDROM.length());
880 string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
881 TargetF += URItoFileName(S);
882 if (_config->FindB("APT::CDROM::NoAct",false) == true)
883 TargetF = "/dev/null";
884 FileFd Target(TargetF,FileFd::WriteAtomic);
885 FILE *TargetFl = fdopen(dup(Target.Fd()),"w");
886 if (_error->PendingError() == true)
887 return false;
888 if (TargetFl == 0)
889 return _error->Errno("fdopen","Failed to reopen fd");
890
891 // Setup the progress meter
892 if(Progress)
893 Progress->OverallProgress(CurrentSize,TotalSize,FileSize,
894 string("Reading Translation Indexes"));
895
896 // Parse
897 if(Progress)
898 Progress->SubProgress(Pkg.Size());
899 pkgTagSection Section;
900 this->Section = &Section;
901 string Prefix;
902 unsigned long Hits = 0;
903 while (Parser.Step(Section) == true)
904 {
905 if(Progress)
906 Progress->Progress(Parser.Offset());
907
908 const char *Start;
909 const char *Stop;
910 Section.GetSection(Start,Stop);
911 fwrite(Start,Stop-Start, 1, TargetFl);
912 fputc('\n',TargetFl);
913
914 Packages++;
915 Hits++;
916 }
917 fclose(TargetFl);
918
919 if (Debug == true)
920 cout << " Processed by using Prefix '" << Prefix << "' and chop " << endl;
921
922 if (_config->FindB("APT::CDROM::NoAct",false) == false)
923 {
924 // Move out of the partial directory
925 Target.Close();
926 string FinalF = _config->FindDir("Dir::State::lists");
927 FinalF += URItoFileName(S);
928 if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
929 return _error->Errno("rename","Failed to rename");
930 }
931
932
933 CurrentSize += FileSize;
934 }
935 if(Progress)
936 Progress->Done();
937
938 // Some stats
939 if(log) {
940 stringstream msg;
941 if(NotFound == 0 && WrongSize == 0)
942 ioprintf(msg, _("Wrote %i records.\n"), Packages);
943 else if (NotFound != 0 && WrongSize == 0)
944 ioprintf(msg, _("Wrote %i records with %i missing files.\n"),
945 Packages, NotFound);
946 else if (NotFound == 0 && WrongSize != 0)
947 ioprintf(msg, _("Wrote %i records with %i mismatched files\n"),
948 Packages, WrongSize);
949 if (NotFound != 0 && WrongSize != 0)
950 ioprintf(msg, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages, NotFound, WrongSize);
951 }
952
953 if (Packages == 0)
954 _error->Warning("No valid records were found.");
955
956 if (NotFound + WrongSize > 10)
957 _error->Warning("A lot of entries were discarded, something may be wrong.\n");
958
959
960 return true;
961 }
962 /*}}}*/