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