merged from the 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/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 // Strip the cdrom base path
354 Path = string(Path,CD.length());
355 if (Path.empty() == true)
356 Path = "/";
357
358 // Too short to be a dists/ type
359 if (Path.length() < strlen("dists/"))
360 return;
361
362 // Not a dists type.
363 if (stringcmp(Path.c_str(),Path.c_str()+strlen("dists/"),"dists/") != 0)
364 return;
365
366 // Isolate the dist
367 string::size_type Slash = strlen("dists/");
368 string::size_type Slash2 = Path.find('/',Slash + 1);
369 if (Slash2 == string::npos || Slash2 + 2 >= Path.length())
370 return;
371 string Dist = string(Path,Slash,Slash2 - Slash);
372
373 // Isolate the component
374 Slash = Slash2;
375 for (unsigned I = 0; I != 10; I++)
376 {
377 Slash = Path.find('/',Slash+1);
378 if (Slash == string::npos || Slash + 2 >= Path.length())
379 return;
380 string Comp = string(Path,Slash2+1,Slash - Slash2-1);
381
382 // Verify the trailing binary- bit
383 string::size_type BinSlash = Path.find('/',Slash + 1);
384 if (Slash == string::npos)
385 return;
386 string Binary = string(Path,Slash+1,BinSlash - Slash-1);
387
388 if (strncmp(Binary.c_str(), "binary-", strlen("binary-")) == 0)
389 {
390 Binary.erase(0, strlen("binary-"));
391 if (APT::Configuration::checkArchitecture(Binary) == false)
392 continue;
393 }
394 else if (Binary != "source")
395 continue;
396
397 Path = Dist + ' ' + Comp;
398 return;
399 }
400 }
401 /*}}}*/
402 // IndexCopy::GrabFirst - Return the first Depth path components /*{{{*/
403 // ---------------------------------------------------------------------
404 /* */
405 bool IndexCopy::GrabFirst(string Path,string &To,unsigned int Depth)
406 {
407 string::size_type I = 0;
408 do
409 {
410 I = Path.find('/',I+1);
411 Depth--;
412 }
413 while (I != string::npos && Depth != 0);
414
415 if (I == string::npos)
416 return false;
417
418 To = string(Path,0,I+1);
419 return true;
420 }
421 /*}}}*/
422 // PackageCopy::GetFile - Get the file information from the section /*{{{*/
423 // ---------------------------------------------------------------------
424 /* */
425 bool PackageCopy::GetFile(string &File,unsigned long long &Size)
426 {
427 File = Section->FindS("Filename");
428 Size = Section->FindI("Size");
429 if (File.empty() || Size == 0)
430 return _error->Error("Cannot find filename or size tag");
431 return true;
432 }
433 /*}}}*/
434 // PackageCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
435 // ---------------------------------------------------------------------
436 /* */
437 bool PackageCopy::RewriteEntry(FILE *Target,string File)
438 {
439 TFRewriteData Changes[] = {{"Filename",File.c_str()},
440 {}};
441
442 if (TFRewrite(Target,*Section,TFRewritePackageOrder,Changes) == false)
443 return false;
444 fputc('\n',Target);
445 return true;
446 }
447 /*}}}*/
448 // SourceCopy::GetFile - Get the file information from the section /*{{{*/
449 // ---------------------------------------------------------------------
450 /* */
451 bool SourceCopy::GetFile(string &File,unsigned long long &Size)
452 {
453 string Files = Section->FindS("Files");
454 if (Files.empty() == true)
455 return false;
456
457 // Stash the / terminated directory prefix
458 string Base = Section->FindS("Directory");
459 if (Base.empty() == false && Base[Base.length()-1] != '/')
460 Base += '/';
461
462 // Read the first file triplet
463 const char *C = Files.c_str();
464 string sSize;
465 string MD5Hash;
466
467 // Parse each of the elements
468 if (ParseQuoteWord(C,MD5Hash) == false ||
469 ParseQuoteWord(C,sSize) == false ||
470 ParseQuoteWord(C,File) == false)
471 return _error->Error("Error parsing file record");
472
473 // Parse the size and append the directory
474 Size = strtoull(sSize.c_str(), NULL, 10);
475 File = Base + File;
476 return true;
477 }
478 /*}}}*/
479 // SourceCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
480 // ---------------------------------------------------------------------
481 /* */
482 bool SourceCopy::RewriteEntry(FILE *Target,string File)
483 {
484 string Dir(File,0,File.rfind('/'));
485 TFRewriteData Changes[] = {{"Directory",Dir.c_str()},
486 {}};
487
488 if (TFRewrite(Target,*Section,TFRewriteSourceOrder,Changes) == false)
489 return false;
490 fputc('\n',Target);
491 return true;
492 }
493 /*}}}*/
494 // SigVerify::Verify - Verify a files md5sum against its metaindex /*{{{*/
495 // ---------------------------------------------------------------------
496 /* */
497 bool SigVerify::Verify(string prefix, string file, indexRecords *MetaIndex)
498 {
499 const indexRecords::checkSum *Record = MetaIndex->Lookup(file);
500 bool const Debug = _config->FindB("Debug::aptcdrom",false);
501
502 // we skip non-existing files in the verifcation of the Release file
503 // as non-existing files do not harm, but a warning scares people and
504 // makes it hard to strip unneeded files from an ISO like uncompressed
505 // indexes as it is done on the mirrors (see also LP: #255545 )
506 if(!RealFileExists(prefix+file))
507 {
508 if (Debug == true)
509 cout << "Skipping nonexistent in " << prefix << " file " << file << std::endl;
510 return true;
511 }
512
513 if (!Record)
514 {
515 _error->Warning(_("Can't find authentication record for: %s"), file.c_str());
516 return false;
517 }
518
519 if (!Record->Hash.VerifyFile(prefix+file))
520 {
521 _error->Warning(_("Hash mismatch for: %s"),file.c_str());
522 return false;
523 }
524
525 if(Debug == true)
526 {
527 cout << "File: " << prefix+file << endl;
528 cout << "Expected Hash " << Record->Hash.toStr() << endl;
529 }
530
531 return true;
532 }
533 /*}}}*/
534 bool SigVerify::CopyMetaIndex(string CDROM, string CDName, /*{{{*/
535 string prefix, string file)
536 {
537 char S[400];
538 snprintf(S,sizeof(S),"cdrom:[%s]/%s%s",CDName.c_str(),
539 (prefix).c_str() + CDROM.length(),file.c_str());
540 string TargetF = _config->FindDir("Dir::State::lists");
541 TargetF += URItoFileName(S);
542
543 FileFd Target;
544 FileFd Rel;
545 Target.Open(TargetF,FileFd::WriteAtomic);
546 Rel.Open(prefix + file,FileFd::ReadOnly);
547 if (_error->PendingError() == true)
548 return false;
549 if (CopyFile(Rel,Target) == false)
550 return false;
551
552 return true;
553 }
554 /*}}}*/
555 bool SigVerify::CopyAndVerify(string CDROM,string Name,vector<string> &SigList, /*{{{*/
556 vector<string> PkgList,vector<string> SrcList)
557 {
558 if (SigList.empty() == true)
559 return true;
560
561 bool Debug = _config->FindB("Debug::aptcdrom",false);
562
563 // Read all Release files
564 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
565 {
566 if(Debug)
567 cout << "Signature verify for: " << *I << endl;
568
569 indexRecords *MetaIndex = new indexRecords;
570 string prefix = *I;
571
572 string const releasegpg = *I+"Release.gpg";
573 string const release = *I+"Release";
574 string const inrelease = *I+"InRelease";
575 bool useInRelease = true;
576
577 // a Release.gpg without a Release should never happen
578 if (RealFileExists(inrelease) == true)
579 ;
580 else if(RealFileExists(release) == false || RealFileExists(releasegpg) == false)
581 {
582 delete MetaIndex;
583 continue;
584 }
585 else
586 useInRelease = false;
587
588 pid_t pid = ExecFork();
589 if(pid < 0) {
590 _error->Error("Fork failed");
591 return false;
592 }
593 if(pid == 0)
594 {
595 if (useInRelease == true)
596 RunGPGV(inrelease, inrelease);
597 else
598 RunGPGV(release, releasegpg);
599 }
600
601 if(!ExecWait(pid, "gpgv")) {
602 _error->Warning("Signature verification failed for: %s",
603 (useInRelease ? inrelease.c_str() : releasegpg.c_str()));
604 // something went wrong, don't copy the Release.gpg
605 // FIXME: delete any existing gpg file?
606 continue;
607 }
608
609 // Open the Release file and add it to the MetaIndex
610 if(!MetaIndex->Load(release))
611 {
612 _error->Error("%s",MetaIndex->ErrorText.c_str());
613 return false;
614 }
615
616 // go over the Indexfiles and see if they verify
617 // if so, remove them from our copy of the lists
618 vector<string> keys = MetaIndex->MetaKeys();
619 for (vector<string>::iterator I = keys.begin(); I != keys.end(); ++I)
620 {
621 if(!Verify(prefix,*I, MetaIndex)) {
622 // something went wrong, don't copy the Release.gpg
623 // FIXME: delete any existing gpg file?
624 _error->Discard();
625 continue;
626 }
627 }
628
629 // we need a fresh one for the Release.gpg
630 delete MetaIndex;
631
632 // everything was fine, copy the Release and Release.gpg file
633 if (useInRelease == true)
634 CopyMetaIndex(CDROM, Name, prefix, "InRelease");
635 else
636 {
637 CopyMetaIndex(CDROM, Name, prefix, "Release");
638 CopyMetaIndex(CDROM, Name, prefix, "Release.gpg");
639 }
640 }
641
642 return true;
643 }
644 /*}}}*/
645 // SigVerify::RunGPGV - returns the command needed for verify /*{{{*/
646 // ---------------------------------------------------------------------
647 /* Generating the commandline for calling gpgv is somehow complicated as
648 we need to add multiple keyrings and user supplied options. Also, as
649 the cdrom code currently can not use the gpgv method we have two places
650 these need to be done - so the place for this method is wrong but better
651 than code duplication… */
652 bool SigVerify::RunGPGV(std::string const &File, std::string const &FileGPG,
653 int const &statusfd, int fd[2])
654 {
655 if (File == FileGPG)
656 {
657 #define SIGMSG "-----BEGIN PGP SIGNED MESSAGE-----\n"
658 char buffer[sizeof(SIGMSG)];
659 FILE* gpg = fopen(File.c_str(), "r");
660 if (gpg == NULL)
661 return _error->Errno("RunGPGV", _("Could not open file %s"), File.c_str());
662 char const * const test = fgets(buffer, sizeof(buffer), gpg);
663 fclose(gpg);
664 if (test == NULL || strcmp(buffer, SIGMSG) != 0)
665 return _error->Error(_("File %s doesn't start with a clearsigned message"), File.c_str());
666 #undef SIGMSG
667 }
668
669
670 string const gpgvpath = _config->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
671 // FIXME: remove support for deprecated APT::GPGV setting
672 string const trustedFile = _config->Find("APT::GPGV::TrustedKeyring", _config->FindFile("Dir::Etc::Trusted"));
673 string const trustedPath = _config->FindDir("Dir::Etc::TrustedParts");
674
675 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
676
677 if (Debug == true)
678 {
679 std::clog << "gpgv path: " << gpgvpath << std::endl;
680 std::clog << "Keyring file: " << trustedFile << std::endl;
681 std::clog << "Keyring path: " << trustedPath << std::endl;
682 }
683
684 std::vector<string> keyrings;
685 if (DirectoryExists(trustedPath))
686 keyrings = GetListOfFilesInDir(trustedPath, "gpg", false, true);
687 if (RealFileExists(trustedFile) == true)
688 keyrings.push_back(trustedFile);
689
690 std::vector<const char *> Args;
691 Args.reserve(30);
692
693 if (keyrings.empty() == true)
694 {
695 // TRANSLATOR: %s is the trusted keyring parts directory
696 return _error->Error(_("No keyring installed in %s."),
697 _config->FindDir("Dir::Etc::TrustedParts").c_str());
698 }
699
700 Args.push_back(gpgvpath.c_str());
701 Args.push_back("--ignore-time-conflict");
702
703 if (statusfd != -1)
704 {
705 Args.push_back("--status-fd");
706 char fd[10];
707 snprintf(fd, sizeof(fd), "%i", statusfd);
708 Args.push_back(fd);
709 }
710
711 for (vector<string>::const_iterator K = keyrings.begin();
712 K != keyrings.end(); ++K)
713 {
714 Args.push_back("--keyring");
715 Args.push_back(K->c_str());
716 }
717
718 Configuration::Item const *Opts;
719 Opts = _config->Tree("Acquire::gpgv::Options");
720 if (Opts != 0)
721 {
722 Opts = Opts->Child;
723 for (; Opts != 0; Opts = Opts->Next)
724 {
725 if (Opts->Value.empty() == true)
726 continue;
727 Args.push_back(Opts->Value.c_str());
728 }
729 }
730
731 Args.push_back(FileGPG.c_str());
732 if (FileGPG != File)
733 Args.push_back(File.c_str());
734 Args.push_back(NULL);
735
736 if (Debug == true)
737 {
738 std::clog << "Preparing to exec: " << gpgvpath;
739 for (std::vector<const char *>::const_iterator a = Args.begin(); *a != NULL; ++a)
740 std::clog << " " << *a;
741 std::clog << std::endl;
742 }
743
744 if (statusfd != -1)
745 {
746 int const nullfd = open("/dev/null", O_RDONLY);
747 close(fd[0]);
748 // Redirect output to /dev/null; we read from the status fd
749 dup2(nullfd, STDOUT_FILENO);
750 dup2(nullfd, STDERR_FILENO);
751 // Redirect the pipe to the status fd (3)
752 dup2(fd[1], statusfd);
753
754 putenv((char *)"LANG=");
755 putenv((char *)"LC_ALL=");
756 putenv((char *)"LC_MESSAGES=");
757 }
758
759 execvp(gpgvpath.c_str(), (char **) &Args[0]);
760 return true;
761 }
762 /*}}}*/
763 bool TranslationsCopy::CopyTranslations(string CDROM,string Name, /*{{{*/
764 vector<string> &List, pkgCdromStatus *log)
765 {
766 OpProgress *Progress = NULL;
767 if (List.empty() == true)
768 return true;
769
770 if(log)
771 Progress = log->GetOpProgress();
772
773 bool Debug = _config->FindB("Debug::aptcdrom",false);
774
775 // Prepare the progress indicator
776 off_t TotalSize = 0;
777 std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
778 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
779 {
780 struct stat Buf;
781 bool found = false;
782 std::string file = *I;
783 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
784 c != compressor.end(); ++c)
785 {
786 if (stat(std::string(file + c->Extension).c_str(), &Buf) != 0)
787 continue;
788 found = true;
789 break;
790 }
791
792 if (found == false)
793 return _error->Errno("stat", "Stat failed for %s", file.c_str());
794 TotalSize += Buf.st_size;
795 }
796
797 off_t CurrentSize = 0;
798 unsigned int NotFound = 0;
799 unsigned int WrongSize = 0;
800 unsigned int Packages = 0;
801 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
802 {
803 string OrigPath = string(*I,CDROM.length());
804
805 // Open the package file
806 FileFd Pkg(*I, FileFd::ReadOnly, FileFd::Auto);
807 off_t const FileSize = Pkg.Size();
808
809 pkgTagFile Parser(&Pkg);
810 if (_error->PendingError() == true)
811 return false;
812
813 // Open the output file
814 char S[400];
815 snprintf(S,sizeof(S),"cdrom:[%s]/%s",Name.c_str(),
816 (*I).c_str() + CDROM.length());
817 string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
818 TargetF += URItoFileName(S);
819 FileFd Target;
820 if (_config->FindB("APT::CDROM::NoAct",false) == true)
821 {
822 TargetF = "/dev/null";
823 Target.Open(TargetF,FileFd::WriteExists);
824 } else {
825 Target.Open(TargetF,FileFd::WriteAtomic);
826 }
827 FILE *TargetFl = fdopen(dup(Target.Fd()),"w");
828 if (_error->PendingError() == true)
829 return false;
830 if (TargetFl == 0)
831 return _error->Errno("fdopen","Failed to reopen fd");
832
833 // Setup the progress meter
834 if(Progress)
835 Progress->OverallProgress(CurrentSize,TotalSize,FileSize,
836 string("Reading Translation Indexes"));
837
838 // Parse
839 if(Progress)
840 Progress->SubProgress(Pkg.Size());
841 pkgTagSection Section;
842 this->Section = &Section;
843 string Prefix;
844 unsigned long Hits = 0;
845 while (Parser.Step(Section) == true)
846 {
847 if(Progress)
848 Progress->Progress(Parser.Offset());
849
850 const char *Start;
851 const char *Stop;
852 Section.GetSection(Start,Stop);
853 fwrite(Start,Stop-Start, 1, TargetFl);
854 fputc('\n',TargetFl);
855
856 Packages++;
857 Hits++;
858 }
859 fclose(TargetFl);
860
861 if (Debug == true)
862 cout << " Processed by using Prefix '" << Prefix << "' and chop " << endl;
863
864 if (_config->FindB("APT::CDROM::NoAct",false) == false)
865 {
866 // Move out of the partial directory
867 Target.Close();
868 string FinalF = _config->FindDir("Dir::State::lists");
869 FinalF += URItoFileName(S);
870 if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
871 return _error->Errno("rename","Failed to rename");
872 }
873
874
875 CurrentSize += FileSize;
876 }
877 if(Progress)
878 Progress->Done();
879
880 // Some stats
881 if(log) {
882 stringstream msg;
883 if(NotFound == 0 && WrongSize == 0)
884 ioprintf(msg, _("Wrote %i records.\n"), Packages);
885 else if (NotFound != 0 && WrongSize == 0)
886 ioprintf(msg, _("Wrote %i records with %i missing files.\n"),
887 Packages, NotFound);
888 else if (NotFound == 0 && WrongSize != 0)
889 ioprintf(msg, _("Wrote %i records with %i mismatched files\n"),
890 Packages, WrongSize);
891 if (NotFound != 0 && WrongSize != 0)
892 ioprintf(msg, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages, NotFound, WrongSize);
893 }
894
895 if (Packages == 0)
896 _error->Warning("No valid records were found.");
897
898 if (NotFound + WrongSize > 10)
899 _error->Warning("A lot of entries were discarded, something may be wrong.\n");
900
901
902 return true;
903 }
904 /*}}}*/