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