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