]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/indexcopy.cc
* apt-pkg/indexcopy.cc:
[apt.git] / apt-pkg / indexcopy.cc
... / ...
CommitLineData
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
39using namespace std;
40
41// IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
42// ---------------------------------------------------------------------
43/* */
44bool 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/* */
260string 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 */
280bool 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 */
311bool 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 */
351void 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/* */
405bool 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/* */
425bool 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/* */
437bool 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/* */
451bool 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/* */
482bool 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/* */
497bool SigVerify::Verify(string prefix, string file, indexRecords *MetaIndex)
498{
499 const indexRecords::checkSum *Record = MetaIndex->Lookup(file);
500
501 // we skip non-existing files in the verifcation to support a cdrom
502 // with no Packages file (just a Package.gz), see LP: #255545
503 // (non-existing files are not considered a error)
504 if(!RealFileExists(prefix+file))
505 {
506 _error->Warning(_("Skipping nonexistent file %s"), string(prefix+file).c_str());
507 return true;
508 }
509
510 if (!Record)
511 {
512 _error->Warning(_("Can't find authentication record for: %s"), file.c_str());
513 return false;
514 }
515
516 if (!Record->Hash.VerifyFile(prefix+file))
517 {
518 _error->Warning(_("Hash mismatch for: %s"),file.c_str());
519 return false;
520 }
521
522 if(_config->FindB("Debug::aptcdrom",false))
523 {
524 cout << "File: " << prefix+file << endl;
525 cout << "Expected Hash " << Record->Hash.toStr() << endl;
526 }
527
528 return true;
529}
530 /*}}}*/
531bool SigVerify::CopyMetaIndex(string CDROM, string CDName, /*{{{*/
532 string prefix, string file)
533{
534 char S[400];
535 snprintf(S,sizeof(S),"cdrom:[%s]/%s%s",CDName.c_str(),
536 (prefix).c_str() + CDROM.length(),file.c_str());
537 string TargetF = _config->FindDir("Dir::State::lists");
538 TargetF += URItoFileName(S);
539
540 FileFd Target;
541 FileFd Rel;
542 Target.Open(TargetF,FileFd::WriteAtomic);
543 Rel.Open(prefix + file,FileFd::ReadOnly);
544 if (_error->PendingError() == true)
545 return false;
546 if (CopyFile(Rel,Target) == false)
547 return false;
548
549 return true;
550}
551 /*}}}*/
552bool SigVerify::CopyAndVerify(string CDROM,string Name,vector<string> &SigList, /*{{{*/
553 vector<string> PkgList,vector<string> SrcList)
554{
555 if (SigList.empty() == true)
556 return true;
557
558 bool Debug = _config->FindB("Debug::aptcdrom",false);
559
560 // Read all Release files
561 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); ++I)
562 {
563 if(Debug)
564 cout << "Signature verify for: " << *I << endl;
565
566 indexRecords *MetaIndex = new indexRecords;
567 string prefix = *I;
568
569 string const releasegpg = *I+"Release.gpg";
570 string const release = *I+"Release";
571 string const inrelease = *I+"InRelease";
572 bool useInRelease = true;
573
574 // a Release.gpg without a Release should never happen
575 if (RealFileExists(inrelease) == true)
576 ;
577 else if(RealFileExists(release) == false || RealFileExists(releasegpg) == false)
578 {
579 delete MetaIndex;
580 continue;
581 }
582 else
583 useInRelease = false;
584
585 pid_t pid = ExecFork();
586 if(pid < 0) {
587 _error->Error("Fork failed");
588 return false;
589 }
590 if(pid == 0)
591 {
592 if (useInRelease == true)
593 RunGPGV(inrelease, inrelease);
594 else
595 RunGPGV(release, releasegpg);
596 }
597
598 if(!ExecWait(pid, "gpgv")) {
599 _error->Warning("Signature verification failed for: %s",
600 (useInRelease ? inrelease.c_str() : releasegpg.c_str()));
601 // something went wrong, don't copy the Release.gpg
602 // FIXME: delete any existing gpg file?
603 continue;
604 }
605
606 // Open the Release file and add it to the MetaIndex
607 if(!MetaIndex->Load(release))
608 {
609 _error->Error("%s",MetaIndex->ErrorText.c_str());
610 return false;
611 }
612
613 // go over the Indexfiles and see if they verify
614 // if so, remove them from our copy of the lists
615 vector<string> keys = MetaIndex->MetaKeys();
616 for (vector<string>::iterator I = keys.begin(); I != keys.end(); ++I)
617 {
618 if(!Verify(prefix,*I, MetaIndex)) {
619 // something went wrong, don't copy the Release.gpg
620 // FIXME: delete any existing gpg file?
621 _error->Discard();
622 continue;
623 }
624 }
625
626 // we need a fresh one for the Release.gpg
627 delete MetaIndex;
628
629 // everything was fine, copy the Release and Release.gpg file
630 if (useInRelease == true)
631 CopyMetaIndex(CDROM, Name, prefix, "InRelease");
632 else
633 {
634 CopyMetaIndex(CDROM, Name, prefix, "Release");
635 CopyMetaIndex(CDROM, Name, prefix, "Release.gpg");
636 }
637 }
638
639 return true;
640}
641 /*}}}*/
642// SigVerify::RunGPGV - returns the command needed for verify /*{{{*/
643// ---------------------------------------------------------------------
644/* Generating the commandline for calling gpgv is somehow complicated as
645 we need to add multiple keyrings and user supplied options. Also, as
646 the cdrom code currently can not use the gpgv method we have two places
647 these need to be done - so the place for this method is wrong but better
648 than code duplication… */
649bool SigVerify::RunGPGV(std::string const &File, std::string const &FileGPG,
650 int const &statusfd, int fd[2])
651{
652 if (File == FileGPG)
653 {
654 #define SIGMSG "-----BEGIN PGP SIGNED MESSAGE-----\n"
655 char buffer[sizeof(SIGMSG)];
656 FILE* gpg = fopen(File.c_str(), "r");
657 if (gpg == NULL)
658 return _error->Errno("RunGPGV", _("Could not open file %s"), File.c_str());
659 char const * const test = fgets(buffer, sizeof(buffer), gpg);
660 fclose(gpg);
661 if (test == NULL || strcmp(buffer, SIGMSG) != 0)
662 return _error->Error(_("File %s doesn't start with a clearsigned message"), File.c_str());
663 #undef SIGMSG
664 }
665
666
667 string const gpgvpath = _config->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
668 // FIXME: remove support for deprecated APT::GPGV setting
669 string const trustedFile = _config->Find("APT::GPGV::TrustedKeyring", _config->FindFile("Dir::Etc::Trusted"));
670 string const trustedPath = _config->FindDir("Dir::Etc::TrustedParts");
671
672 bool const Debug = _config->FindB("Debug::Acquire::gpgv", false);
673
674 if (Debug == true)
675 {
676 std::clog << "gpgv path: " << gpgvpath << std::endl;
677 std::clog << "Keyring file: " << trustedFile << std::endl;
678 std::clog << "Keyring path: " << trustedPath << std::endl;
679 }
680
681 std::vector<string> keyrings;
682 if (DirectoryExists(trustedPath))
683 keyrings = GetListOfFilesInDir(trustedPath, "gpg", false, true);
684 if (RealFileExists(trustedFile) == true)
685 keyrings.push_back(trustedFile);
686
687 std::vector<const char *> Args;
688 Args.reserve(30);
689
690 if (keyrings.empty() == true)
691 {
692 // TRANSLATOR: %s is the trusted keyring parts directory
693 return _error->Error(_("No keyring installed in %s."),
694 _config->FindDir("Dir::Etc::TrustedParts").c_str());
695 }
696
697 Args.push_back(gpgvpath.c_str());
698 Args.push_back("--ignore-time-conflict");
699
700 if (statusfd != -1)
701 {
702 Args.push_back("--status-fd");
703 char fd[10];
704 snprintf(fd, sizeof(fd), "%i", statusfd);
705 Args.push_back(fd);
706 }
707
708 for (vector<string>::const_iterator K = keyrings.begin();
709 K != keyrings.end(); ++K)
710 {
711 Args.push_back("--keyring");
712 Args.push_back(K->c_str());
713 }
714
715 Configuration::Item const *Opts;
716 Opts = _config->Tree("Acquire::gpgv::Options");
717 if (Opts != 0)
718 {
719 Opts = Opts->Child;
720 for (; Opts != 0; Opts = Opts->Next)
721 {
722 if (Opts->Value.empty() == true)
723 continue;
724 Args.push_back(Opts->Value.c_str());
725 }
726 }
727
728 Args.push_back(FileGPG.c_str());
729 if (FileGPG != File)
730 Args.push_back(File.c_str());
731 Args.push_back(NULL);
732
733 if (Debug == true)
734 {
735 std::clog << "Preparing to exec: " << gpgvpath;
736 for (std::vector<const char *>::const_iterator a = Args.begin(); *a != NULL; ++a)
737 std::clog << " " << *a;
738 std::clog << std::endl;
739 }
740
741 if (statusfd != -1)
742 {
743 int const nullfd = open("/dev/null", O_RDONLY);
744 close(fd[0]);
745 // Redirect output to /dev/null; we read from the status fd
746 dup2(nullfd, STDOUT_FILENO);
747 dup2(nullfd, STDERR_FILENO);
748 // Redirect the pipe to the status fd (3)
749 dup2(fd[1], statusfd);
750
751 putenv((char *)"LANG=");
752 putenv((char *)"LC_ALL=");
753 putenv((char *)"LC_MESSAGES=");
754 }
755
756 execvp(gpgvpath.c_str(), (char **) &Args[0]);
757 return true;
758}
759 /*}}}*/
760bool TranslationsCopy::CopyTranslations(string CDROM,string Name, /*{{{*/
761 vector<string> &List, pkgCdromStatus *log)
762{
763 OpProgress *Progress = NULL;
764 if (List.empty() == true)
765 return true;
766
767 if(log)
768 Progress = log->GetOpProgress();
769
770 bool Debug = _config->FindB("Debug::aptcdrom",false);
771
772 // Prepare the progress indicator
773 off_t TotalSize = 0;
774 std::vector<APT::Configuration::Compressor> const compressor = APT::Configuration::getCompressors();
775 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
776 {
777 struct stat Buf;
778 bool found = false;
779 std::string file = *I;
780 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
781 c != compressor.end(); ++c)
782 {
783 if (stat(std::string(file + c->Extension).c_str(), &Buf) != 0)
784 continue;
785 found = true;
786 break;
787 }
788
789 if (found == false)
790 return _error->Errno("stat", "Stat failed for %s", file.c_str());
791 TotalSize += Buf.st_size;
792 }
793
794 off_t CurrentSize = 0;
795 unsigned int NotFound = 0;
796 unsigned int WrongSize = 0;
797 unsigned int Packages = 0;
798 for (vector<string>::iterator I = List.begin(); I != List.end(); ++I)
799 {
800 string OrigPath = string(*I,CDROM.length());
801
802 // Open the package file
803 FileFd Pkg(*I, FileFd::ReadOnly, FileFd::Auto);
804 off_t const FileSize = Pkg.Size();
805
806 pkgTagFile Parser(&Pkg);
807 if (_error->PendingError() == true)
808 return false;
809
810 // Open the output file
811 char S[400];
812 snprintf(S,sizeof(S),"cdrom:[%s]/%s",Name.c_str(),
813 (*I).c_str() + CDROM.length());
814 string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
815 TargetF += URItoFileName(S);
816 FileFd Target;
817 if (_config->FindB("APT::CDROM::NoAct",false) == true)
818 {
819 TargetF = "/dev/null";
820 Target.Open(TargetF,FileFd::WriteExists);
821 } else {
822 Target.Open(TargetF,FileFd::WriteAtomic);
823 }
824 FILE *TargetFl = fdopen(dup(Target.Fd()),"w");
825 if (_error->PendingError() == true)
826 return false;
827 if (TargetFl == 0)
828 return _error->Errno("fdopen","Failed to reopen fd");
829
830 // Setup the progress meter
831 if(Progress)
832 Progress->OverallProgress(CurrentSize,TotalSize,FileSize,
833 string("Reading Translation Indexes"));
834
835 // Parse
836 if(Progress)
837 Progress->SubProgress(Pkg.Size());
838 pkgTagSection Section;
839 this->Section = &Section;
840 string Prefix;
841 unsigned long Hits = 0;
842 while (Parser.Step(Section) == true)
843 {
844 if(Progress)
845 Progress->Progress(Parser.Offset());
846
847 const char *Start;
848 const char *Stop;
849 Section.GetSection(Start,Stop);
850 fwrite(Start,Stop-Start, 1, TargetFl);
851 fputc('\n',TargetFl);
852
853 Packages++;
854 Hits++;
855 }
856 fclose(TargetFl);
857
858 if (Debug == true)
859 cout << " Processed by using Prefix '" << Prefix << "' and chop " << endl;
860
861 if (_config->FindB("APT::CDROM::NoAct",false) == false)
862 {
863 // Move out of the partial directory
864 Target.Close();
865 string FinalF = _config->FindDir("Dir::State::lists");
866 FinalF += URItoFileName(S);
867 if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
868 return _error->Errno("rename","Failed to rename");
869 }
870
871
872 CurrentSize += FileSize;
873 }
874 if(Progress)
875 Progress->Done();
876
877 // Some stats
878 if(log) {
879 stringstream msg;
880 if(NotFound == 0 && WrongSize == 0)
881 ioprintf(msg, _("Wrote %i records.\n"), Packages);
882 else if (NotFound != 0 && WrongSize == 0)
883 ioprintf(msg, _("Wrote %i records with %i missing files.\n"),
884 Packages, NotFound);
885 else if (NotFound == 0 && WrongSize != 0)
886 ioprintf(msg, _("Wrote %i records with %i mismatched files\n"),
887 Packages, WrongSize);
888 if (NotFound != 0 && WrongSize != 0)
889 ioprintf(msg, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages, NotFound, WrongSize);
890 }
891
892 if (Packages == 0)
893 _error->Warning("No valid records were found.");
894
895 if (NotFound + WrongSize > 10)
896 _error->Warning("A lot of entries were discarded, something may be wrong.\n");
897
898
899 return true;
900}
901 /*}}}*/