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