1 // -*- mode: cpp; mode: fold -*-
3 // $Id: indexcopy.cc,v 1.10 2002/03/26 07:38:58 jgg Exp $
4 /* ######################################################################
6 Index Copying - Aid for copying and verifying the index files
8 This class helps apt-cache reconstruct a damaged index files.
10 ##################################################################### */
12 // Include Files /*{{{*/
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>
30 #include <sys/types.h>
35 #include "indexcopy.h"
41 // DecompressFile - wrapper for decompressing compressed files /*{{{*/
42 // ---------------------------------------------------------------------
44 bool DecompressFile(string Filename
, int *fd
, off_t
*FileSize
)
49 std::vector
<APT::Configuration::Compressor
> const compressor
= APT::Configuration::getCompressors();
50 std::vector
<APT::Configuration::Compressor
>::const_iterator UnCompress
;
51 std::string file
= std::string(Filename
).append(UnCompress
->Extension
);
52 for (UnCompress
= compressor
.begin(); UnCompress
!= compressor
.end(); ++UnCompress
)
54 if (stat(file
.c_str(), &Buf
) == 0)
58 if (UnCompress
== compressor
.end())
59 return _error
->Errno("decompressor", "Unable to parse file");
61 *FileSize
= Buf
.st_size
;
64 int Pipe
[2] = {-1,-1};
66 return _error
->Errno("pipe",_("Failed to create subprocess IPC"));
67 for (int J
= 0; J
!= 2; J
++)
68 SetCloseExec(Pipe
[J
],true);
73 pid_t Pid
= ExecFork();
76 dup2(Pipe
[1],STDOUT_FILENO
);
77 SetCloseExec(STDOUT_FILENO
, false);
79 std::vector
<char const*> Args
;
80 Args
.push_back(UnCompress
->Binary
.c_str());
81 for (std::vector
<std::string
>::const_iterator a
= UnCompress
->UncompressArgs
.begin();
82 a
!= UnCompress
->UncompressArgs
.end(); ++a
)
83 Args
.push_back(a
->c_str());
84 Args
.push_back("--stdout");
85 Args
.push_back(file
.c_str());
88 execvp(Args
[0],(char **)&Args
[0]);
89 cerr
<< _("Failed to exec compressor ") << Args
[0] << endl
;
93 // Wait for decompress to finish
94 if (ExecWait(Pid
, UnCompress
->Binary
.c_str(), false) == false)
100 // IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
101 // ---------------------------------------------------------------------
103 bool IndexCopy::CopyPackages(string CDROM
,string Name
,vector
<string
> &List
,
106 OpProgress
*Progress
= NULL
;
107 if (List
.empty() == true)
111 Progress
= log
->GetOpProgress();
113 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
114 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
116 // Prepare the progress indicator
118 std::vector
<APT::Configuration::Compressor
> const compressor
= APT::Configuration::getCompressors();
119 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
123 std::string file
= std::string(*I
).append(GetFileName());
124 for (std::vector
<APT::Configuration::Compressor
>::const_iterator c
= compressor
.begin();
125 c
!= compressor
.end(); ++c
)
127 if (stat(std::string(file
+ c
->Extension
).c_str(), &Buf
) != 0)
134 return _error
->Errno("stat", "Stat failed for %s", file
.c_str());
135 TotalSize
+= Buf
.st_size
;
138 off_t CurrentSize
= 0;
139 unsigned int NotFound
= 0;
140 unsigned int WrongSize
= 0;
141 unsigned int Packages
= 0;
142 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
144 string OrigPath
= string(*I
,CDROM
.length());
147 // Open the package file
149 if (RealFileExists(*I
+ GetFileName()) == true)
151 Pkg
.Open(*I
+ GetFileName(),FileFd::ReadOnly
);
152 FileSize
= Pkg
.Size();
157 if (!DecompressFile(string(*I
+ GetFileName()), &fd
, &FileSize
))
158 return _error
->Errno("decompress","Decompress failed for %s",
159 string(*I
+ GetFileName()).c_str());
164 pkgTagFile
Parser(&Pkg
);
165 if (_error
->PendingError() == true)
168 // Open the output file
170 snprintf(S
,sizeof(S
),"cdrom:[%s]/%s%s",Name
.c_str(),
171 (*I
).c_str() + CDROM
.length(),GetFileName());
172 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
173 TargetF
+= URItoFileName(S
);
175 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
177 TargetF
= "/dev/null";
178 Target
.Open(TargetF
,FileFd::WriteExists
);
180 Target
.Open(TargetF
,FileFd::WriteAtomic
);
182 FILE *TargetFl
= fdopen(dup(Target
.Fd()),"w");
183 if (_error
->PendingError() == true)
186 return _error
->Errno("fdopen","Failed to reopen fd");
188 // Setup the progress meter
190 Progress
->OverallProgress(CurrentSize
,TotalSize
,FileSize
,
191 string("Reading ") + Type() + " Indexes");
195 Progress
->SubProgress(Pkg
.Size());
196 pkgTagSection Section
;
197 this->Section
= &Section
;
199 unsigned long Hits
= 0;
200 unsigned long Chop
= 0;
201 while (Parser
.Step(Section
) == true)
204 Progress
->Progress(Parser
.Offset());
206 unsigned long long Size
;
207 if (GetFile(File
,Size
) == false)
214 File
= OrigPath
+ ChopDirs(File
,Chop
);
216 // See if the file exists
217 bool Mangled
= false;
218 if (NoStat
== false || Hits
< 10)
220 // Attempt to fix broken structure
223 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
224 ReconstructChop(Chop
,*I
,File
) == false)
227 clog
<< "Missed: " << File
<< endl
;
232 File
= OrigPath
+ ChopDirs(File
,Chop
);
237 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0 ||
240 // Attempt to fix busted symlink support for one instance
241 string OrigFile
= File
;
242 string::size_type Start
= File
.find("binary-");
243 string::size_type End
= File
.find("/",Start
+3);
244 if (Start
!= string::npos
&& End
!= string::npos
)
246 File
.replace(Start
,End
-Start
,"binary-all");
250 if (Mangled
== false ||
251 stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
254 clog
<< "Missed(2): " << OrigFile
<< endl
;
261 if ((unsigned long long)Buf
.st_size
!= Size
)
264 clog
<< "Wrong Size: " << File
<< endl
;
273 if (RewriteEntry(TargetFl
,File
) == false)
282 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
284 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
286 // Move out of the partial directory
288 string FinalF
= _config
->FindDir("Dir::State::lists");
289 FinalF
+= URItoFileName(S
);
290 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
291 return _error
->Errno("rename","Failed to rename");
294 /* Mangle the source to be in the proper notation with
295 prefix dist [component] */
296 *I
= string(*I
,Prefix
.length());
297 ConvertToSourceList(CDROM
,*I
);
298 *I
= Prefix
+ ' ' + *I
;
300 CurrentSize
+= FileSize
;
308 if(NotFound
== 0 && WrongSize
== 0)
309 ioprintf(msg
, _("Wrote %i records.\n"), Packages
);
310 else if (NotFound
!= 0 && WrongSize
== 0)
311 ioprintf(msg
, _("Wrote %i records with %i missing files.\n"),
313 else if (NotFound
== 0 && WrongSize
!= 0)
314 ioprintf(msg
, _("Wrote %i records with %i mismatched files\n"),
315 Packages
, WrongSize
);
316 if (NotFound
!= 0 && WrongSize
!= 0)
317 ioprintf(msg
, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages
, NotFound
, WrongSize
);
321 _error
->Warning("No valid records were found.");
323 if (NotFound
+ WrongSize
> 10)
324 _error
->Warning("A lot of entries were discarded, something may be wrong.\n");
330 // IndexCopy::ChopDirs - Chop off the leading directory components /*{{{*/
331 // ---------------------------------------------------------------------
333 string
IndexCopy::ChopDirs(string Path
,unsigned int Depth
)
335 string::size_type I
= 0;
338 I
= Path
.find('/',I
+1);
341 while (I
!= string::npos
&& Depth
!= 0);
343 if (I
== string::npos
)
346 return string(Path
,I
+1);
349 // IndexCopy::ReconstructPrefix - Fix strange prefixing /*{{{*/
350 // ---------------------------------------------------------------------
351 /* This prepends dir components from the path to the package files to
352 the path to the deb until it is found */
353 bool IndexCopy::ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
356 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
357 unsigned int Depth
= 1;
358 string MyPrefix
= Prefix
;
362 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
365 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
366 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
380 // IndexCopy::ReconstructChop - Fixes bad source paths /*{{{*/
381 // ---------------------------------------------------------------------
382 /* This removes path components from the filename and prepends the location
383 of the package files until a file is found */
384 bool IndexCopy::ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
386 // Attempt to reconstruct the filename
387 unsigned long Depth
= 0;
391 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
393 File
= ChopDirs(File
,1);
395 if (File
.empty() == false)
408 // IndexCopy::ConvertToSourceList - Convert a Path to a sourcelist /*{{{*/
409 // ---------------------------------------------------------------------
410 /* We look for things in dists/ notation and convert them to
411 <dist> <component> form otherwise it is left alone. This also strips
414 This implements a regex sort of like:
415 (.*)/dists/([^/]*)/(.*)/binary-*
417 | |-------- Distribution
418 |------------------- Path
420 It was deciced to use only a single word for dist (rather than say
421 unstable/non-us) to increase the chance that each CD gets a single
422 line in sources.list.
424 void IndexCopy::ConvertToSourceList(string CD
,string
&Path
)
427 snprintf(S
,sizeof(S
),"binary-%s",_config
->Find("Apt::Architecture").c_str());
429 // Strip the cdrom base path
430 Path
= string(Path
,CD
.length());
431 if (Path
.empty() == true)
434 // Too short to be a dists/ type
435 if (Path
.length() < strlen("dists/"))
439 if (stringcmp(Path
.c_str(),Path
.c_str()+strlen("dists/"),"dists/") != 0)
443 string::size_type Slash
= strlen("dists/");
444 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
445 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
447 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
449 // Isolate the component
451 for (unsigned I
= 0; I
!= 10; I
++)
453 Slash
= Path
.find('/',Slash
+1);
454 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
456 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
458 // Verify the trailing binary- bit
459 string::size_type BinSlash
= Path
.find('/',Slash
+ 1);
460 if (Slash
== string::npos
)
462 string Binary
= string(Path
,Slash
+1,BinSlash
- Slash
-1);
464 if (Binary
!= S
&& Binary
!= "source")
467 Path
= Dist
+ ' ' + Comp
;
472 // IndexCopy::GrabFirst - Return the first Depth path components /*{{{*/
473 // ---------------------------------------------------------------------
475 bool IndexCopy::GrabFirst(string Path
,string
&To
,unsigned int Depth
)
477 string::size_type I
= 0;
480 I
= Path
.find('/',I
+1);
483 while (I
!= string::npos
&& Depth
!= 0);
485 if (I
== string::npos
)
488 To
= string(Path
,0,I
+1);
492 // PackageCopy::GetFile - Get the file information from the section /*{{{*/
493 // ---------------------------------------------------------------------
495 bool PackageCopy::GetFile(string
&File
,unsigned long long &Size
)
497 File
= Section
->FindS("Filename");
498 Size
= Section
->FindI("Size");
499 if (File
.empty() || Size
== 0)
500 return _error
->Error("Cannot find filename or size tag");
504 // PackageCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
505 // ---------------------------------------------------------------------
507 bool PackageCopy::RewriteEntry(FILE *Target
,string File
)
509 TFRewriteData Changes
[] = {{"Filename",File
.c_str()},
512 if (TFRewrite(Target
,*Section
,TFRewritePackageOrder
,Changes
) == false)
518 // SourceCopy::GetFile - Get the file information from the section /*{{{*/
519 // ---------------------------------------------------------------------
521 bool SourceCopy::GetFile(string
&File
,unsigned long long &Size
)
523 string Files
= Section
->FindS("Files");
524 if (Files
.empty() == true)
527 // Stash the / terminated directory prefix
528 string Base
= Section
->FindS("Directory");
529 if (Base
.empty() == false && Base
[Base
.length()-1] != '/')
532 // Read the first file triplet
533 const char *C
= Files
.c_str();
537 // Parse each of the elements
538 if (ParseQuoteWord(C
,MD5Hash
) == false ||
539 ParseQuoteWord(C
,sSize
) == false ||
540 ParseQuoteWord(C
,File
) == false)
541 return _error
->Error("Error parsing file record");
543 // Parse the size and append the directory
544 Size
= strtoull(sSize
.c_str(), NULL
, 10);
549 // SourceCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
550 // ---------------------------------------------------------------------
552 bool SourceCopy::RewriteEntry(FILE *Target
,string File
)
554 string
Dir(File
,0,File
.rfind('/'));
555 TFRewriteData Changes
[] = {{"Directory",Dir
.c_str()},
558 if (TFRewrite(Target
,*Section
,TFRewriteSourceOrder
,Changes
) == false)
564 // SigVerify::Verify - Verify a files md5sum against its metaindex /*{{{*/
565 // ---------------------------------------------------------------------
567 bool SigVerify::Verify(string prefix
, string file
, indexRecords
*MetaIndex
)
569 const indexRecords::checkSum
*Record
= MetaIndex
->Lookup(file
);
571 // we skip non-existing files in the verifcation to support a cdrom
572 // with no Packages file (just a Package.gz), see LP: #255545
573 // (non-existing files are not considered a error)
574 if(!RealFileExists(prefix
+file
))
576 _error
->Warning(_("Skipping nonexistent file %s"), string(prefix
+file
).c_str());
582 _error
->Warning(_("Can't find authentication record for: %s"), file
.c_str());
586 if (!Record
->Hash
.VerifyFile(prefix
+file
))
588 _error
->Warning(_("Hash mismatch for: %s"),file
.c_str());
592 if(_config
->FindB("Debug::aptcdrom",false))
594 cout
<< "File: " << prefix
+file
<< endl
;
595 cout
<< "Expected Hash " << Record
->Hash
.toStr() << endl
;
601 bool SigVerify::CopyMetaIndex(string CDROM
, string CDName
, /*{{{*/
602 string prefix
, string file
)
605 snprintf(S
,sizeof(S
),"cdrom:[%s]/%s%s",CDName
.c_str(),
606 (prefix
).c_str() + CDROM
.length(),file
.c_str());
607 string TargetF
= _config
->FindDir("Dir::State::lists");
608 TargetF
+= URItoFileName(S
);
612 Target
.Open(TargetF
,FileFd::WriteAtomic
);
613 Rel
.Open(prefix
+ file
,FileFd::ReadOnly
);
614 if (_error
->PendingError() == true)
616 if (CopyFile(Rel
,Target
) == false)
622 bool SigVerify::CopyAndVerify(string CDROM
,string Name
,vector
<string
> &SigList
, /*{{{*/
623 vector
<string
> PkgList
,vector
<string
> SrcList
)
625 if (SigList
.empty() == true)
628 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
630 // Read all Release files
631 for (vector
<string
>::iterator I
= SigList
.begin(); I
!= SigList
.end(); ++I
)
634 cout
<< "Signature verify for: " << *I
<< endl
;
636 indexRecords
*MetaIndex
= new indexRecords
;
639 string
const releasegpg
= *I
+"Release.gpg";
640 string
const release
= *I
+"Release";
641 string
const inrelease
= *I
+"InRelease";
642 bool useInRelease
= true;
644 // a Release.gpg without a Release should never happen
645 if (RealFileExists(inrelease
) == true)
647 else if(RealFileExists(release
) == false || RealFileExists(releasegpg
) == false)
653 useInRelease
= false;
655 pid_t pid
= ExecFork();
657 _error
->Error("Fork failed");
662 if (useInRelease
== true)
663 RunGPGV(inrelease
, inrelease
);
665 RunGPGV(release
, releasegpg
);
668 if(!ExecWait(pid
, "gpgv")) {
669 _error
->Warning("Signature verification failed for: %s",
670 (useInRelease
? inrelease
.c_str() : releasegpg
.c_str()));
671 // something went wrong, don't copy the Release.gpg
672 // FIXME: delete any existing gpg file?
676 // Open the Release file and add it to the MetaIndex
677 if(!MetaIndex
->Load(release
))
679 _error
->Error("%s",MetaIndex
->ErrorText
.c_str());
683 // go over the Indexfiles and see if they verify
684 // if so, remove them from our copy of the lists
685 vector
<string
> keys
= MetaIndex
->MetaKeys();
686 for (vector
<string
>::iterator I
= keys
.begin(); I
!= keys
.end(); ++I
)
688 if(!Verify(prefix
,*I
, MetaIndex
)) {
689 // something went wrong, don't copy the Release.gpg
690 // FIXME: delete any existing gpg file?
696 // we need a fresh one for the Release.gpg
699 // everything was fine, copy the Release and Release.gpg file
700 if (useInRelease
== true)
701 CopyMetaIndex(CDROM
, Name
, prefix
, "InRelease");
704 CopyMetaIndex(CDROM
, Name
, prefix
, "Release");
705 CopyMetaIndex(CDROM
, Name
, prefix
, "Release.gpg");
712 // SigVerify::RunGPGV - returns the command needed for verify /*{{{*/
713 // ---------------------------------------------------------------------
714 /* Generating the commandline for calling gpgv is somehow complicated as
715 we need to add multiple keyrings and user supplied options. Also, as
716 the cdrom code currently can not use the gpgv method we have two places
717 these need to be done - so the place for this method is wrong but better
718 than code duplication… */
719 bool SigVerify::RunGPGV(std::string
const &File
, std::string
const &FileGPG
,
720 int const &statusfd
, int fd
[2])
724 #define SIGMSG "-----BEGIN PGP SIGNED MESSAGE-----\n"
725 char buffer
[sizeof(SIGMSG
)];
726 FILE* gpg
= fopen(File
.c_str(), "r");
728 return _error
->Errno("RunGPGV", _("Could not open file %s"), File
.c_str());
729 char const * const test
= fgets(buffer
, sizeof(buffer
), gpg
);
731 if (test
== NULL
|| strcmp(buffer
, SIGMSG
) != 0)
732 return _error
->Error(_("File %s doesn't start with a clearsigned message"), File
.c_str());
737 string
const gpgvpath
= _config
->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
738 // FIXME: remove support for deprecated APT::GPGV setting
739 string
const trustedFile
= _config
->Find("APT::GPGV::TrustedKeyring", _config
->FindFile("Dir::Etc::Trusted"));
740 string
const trustedPath
= _config
->FindDir("Dir::Etc::TrustedParts");
742 bool const Debug
= _config
->FindB("Debug::Acquire::gpgv", false);
746 std::clog
<< "gpgv path: " << gpgvpath
<< std::endl
;
747 std::clog
<< "Keyring file: " << trustedFile
<< std::endl
;
748 std::clog
<< "Keyring path: " << trustedPath
<< std::endl
;
751 std::vector
<string
> keyrings
;
752 if (DirectoryExists(trustedPath
))
753 keyrings
= GetListOfFilesInDir(trustedPath
, "gpg", false, true);
754 if (RealFileExists(trustedFile
) == true)
755 keyrings
.push_back(trustedFile
);
757 std::vector
<const char *> Args
;
760 if (keyrings
.empty() == true)
762 // TRANSLATOR: %s is the trusted keyring parts directory
763 return _error
->Error(_("No keyring installed in %s."),
764 _config
->FindDir("Dir::Etc::TrustedParts").c_str());
767 Args
.push_back(gpgvpath
.c_str());
768 Args
.push_back("--ignore-time-conflict");
772 Args
.push_back("--status-fd");
774 snprintf(fd
, sizeof(fd
), "%i", statusfd
);
778 for (vector
<string
>::const_iterator K
= keyrings
.begin();
779 K
!= keyrings
.end(); ++K
)
781 Args
.push_back("--keyring");
782 Args
.push_back(K
->c_str());
785 Configuration::Item
const *Opts
;
786 Opts
= _config
->Tree("Acquire::gpgv::Options");
790 for (; Opts
!= 0; Opts
= Opts
->Next
)
792 if (Opts
->Value
.empty() == true)
794 Args
.push_back(Opts
->Value
.c_str());
798 Args
.push_back(FileGPG
.c_str());
800 Args
.push_back(File
.c_str());
801 Args
.push_back(NULL
);
805 std::clog
<< "Preparing to exec: " << gpgvpath
;
806 for (std::vector
<const char *>::const_iterator a
= Args
.begin(); *a
!= NULL
; ++a
)
807 std::clog
<< " " << *a
;
808 std::clog
<< std::endl
;
813 int const nullfd
= open("/dev/null", O_RDONLY
);
815 // Redirect output to /dev/null; we read from the status fd
816 dup2(nullfd
, STDOUT_FILENO
);
817 dup2(nullfd
, STDERR_FILENO
);
818 // Redirect the pipe to the status fd (3)
819 dup2(fd
[1], statusfd
);
821 putenv((char *)"LANG=");
822 putenv((char *)"LC_ALL=");
823 putenv((char *)"LC_MESSAGES=");
826 execvp(gpgvpath
.c_str(), (char **) &Args
[0]);
830 bool TranslationsCopy::CopyTranslations(string CDROM
,string Name
, /*{{{*/
831 vector
<string
> &List
, pkgCdromStatus
*log
)
833 OpProgress
*Progress
= NULL
;
834 if (List
.empty() == true)
838 Progress
= log
->GetOpProgress();
840 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
842 // Prepare the progress indicator
844 std::vector
<APT::Configuration::Compressor
> const compressor
= APT::Configuration::getCompressors();
845 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
849 std::string file
= *I
;
850 for (std::vector
<APT::Configuration::Compressor
>::const_iterator c
= compressor
.begin();
851 c
!= compressor
.end(); ++c
)
853 if (stat(std::string(file
+ c
->Extension
).c_str(), &Buf
) != 0)
860 return _error
->Errno("stat", "Stat failed for %s", file
.c_str());
861 TotalSize
+= Buf
.st_size
;
864 off_t CurrentSize
= 0;
865 unsigned int NotFound
= 0;
866 unsigned int WrongSize
= 0;
867 unsigned int Packages
= 0;
868 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
870 string OrigPath
= string(*I
,CDROM
.length());
873 // Open the package file
875 if (RealFileExists(*I
) == true)
877 Pkg
.Open(*I
,FileFd::ReadOnly
);
878 FileSize
= Pkg
.Size();
883 if (!DecompressFile(*I
, &fd
, &FileSize
))
884 return _error
->Errno("decompress","Decompress failed for %s", (*I
).c_str());
888 pkgTagFile
Parser(&Pkg
);
889 if (_error
->PendingError() == true)
892 // Open the output file
894 snprintf(S
,sizeof(S
),"cdrom:[%s]/%s",Name
.c_str(),
895 (*I
).c_str() + CDROM
.length());
896 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
897 TargetF
+= URItoFileName(S
);
898 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
899 TargetF
= "/dev/null";
900 FileFd
Target(TargetF
,FileFd::WriteAtomic
);
901 FILE *TargetFl
= fdopen(dup(Target
.Fd()),"w");
902 if (_error
->PendingError() == true)
905 return _error
->Errno("fdopen","Failed to reopen fd");
907 // Setup the progress meter
909 Progress
->OverallProgress(CurrentSize
,TotalSize
,FileSize
,
910 string("Reading Translation Indexes"));
914 Progress
->SubProgress(Pkg
.Size());
915 pkgTagSection Section
;
916 this->Section
= &Section
;
918 unsigned long Hits
= 0;
919 while (Parser
.Step(Section
) == true)
922 Progress
->Progress(Parser
.Offset());
926 Section
.GetSection(Start
,Stop
);
927 fwrite(Start
,Stop
-Start
, 1, TargetFl
);
928 fputc('\n',TargetFl
);
936 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << endl
;
938 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
940 // Move out of the partial directory
942 string FinalF
= _config
->FindDir("Dir::State::lists");
943 FinalF
+= URItoFileName(S
);
944 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
945 return _error
->Errno("rename","Failed to rename");
949 CurrentSize
+= FileSize
;
957 if(NotFound
== 0 && WrongSize
== 0)
958 ioprintf(msg
, _("Wrote %i records.\n"), Packages
);
959 else if (NotFound
!= 0 && WrongSize
== 0)
960 ioprintf(msg
, _("Wrote %i records with %i missing files.\n"),
962 else if (NotFound
== 0 && WrongSize
!= 0)
963 ioprintf(msg
, _("Wrote %i records with %i mismatched files\n"),
964 Packages
, WrongSize
);
965 if (NotFound
!= 0 && WrongSize
!= 0)
966 ioprintf(msg
, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages
, NotFound
, WrongSize
);
970 _error
->Warning("No valid records were found.");
972 if (NotFound
+ WrongSize
> 10)
973 _error
->Warning("A lot of entries were discarded, something may be wrong.\n");