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