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