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