]>
git.saurik.com Git - apt.git/blob - apt-pkg/indexcopy.cc
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 // IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
42 // ---------------------------------------------------------------------
44 bool IndexCopy::CopyPackages(string CDROM
,string Name
,vector
<string
> &List
,
47 OpProgress
*Progress
= NULL
;
48 if (List
.empty() == true)
52 Progress
= log
->GetOpProgress();
54 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
55 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
57 // Prepare the progress indicator
59 std::vector
<APT::Configuration::Compressor
> const compressor
= APT::Configuration::getCompressors();
60 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
64 std::string file
= std::string(*I
).append(GetFileName());
65 for (std::vector
<APT::Configuration::Compressor
>::const_iterator c
= compressor
.begin();
66 c
!= compressor
.end(); ++c
)
68 if (stat(std::string(file
+ c
->Extension
).c_str(), &Buf
) != 0)
75 return _error
->Errno("stat", "Stat failed for %s", file
.c_str());
76 TotalSize
+= Buf
.st_size
;
79 off_t CurrentSize
= 0;
80 unsigned int NotFound
= 0;
81 unsigned int WrongSize
= 0;
82 unsigned int Packages
= 0;
83 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
85 string OrigPath
= string(*I
,CDROM
.length());
87 // Open the package file
88 FileFd
Pkg(*I
+ GetFileName(), FileFd::ReadOnly
, FileFd::Auto
);
89 off_t
const FileSize
= Pkg
.Size();
91 pkgTagFile
Parser(&Pkg
);
92 if (_error
->PendingError() == true)
95 // Open the output file
97 snprintf(S
,sizeof(S
),"cdrom:[%s]/%s%s",Name
.c_str(),
98 (*I
).c_str() + CDROM
.length(),GetFileName());
99 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
100 TargetF
+= URItoFileName(S
);
102 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
104 TargetF
= "/dev/null";
105 Target
.Open(TargetF
,FileFd::WriteExists
);
107 Target
.Open(TargetF
,FileFd::WriteAtomic
);
109 FILE *TargetFl
= fdopen(dup(Target
.Fd()),"w");
110 if (_error
->PendingError() == true)
113 return _error
->Errno("fdopen","Failed to reopen fd");
115 // Setup the progress meter
117 Progress
->OverallProgress(CurrentSize
,TotalSize
,FileSize
,
118 string("Reading ") + Type() + " Indexes");
122 Progress
->SubProgress(Pkg
.Size());
123 pkgTagSection Section
;
124 this->Section
= &Section
;
126 unsigned long Hits
= 0;
127 unsigned long Chop
= 0;
128 while (Parser
.Step(Section
) == true)
131 Progress
->Progress(Parser
.Offset());
133 unsigned long long Size
;
134 if (GetFile(File
,Size
) == false)
141 File
= OrigPath
+ ChopDirs(File
,Chop
);
143 // See if the file exists
144 bool Mangled
= false;
145 if (NoStat
== false || Hits
< 10)
147 // Attempt to fix broken structure
150 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
151 ReconstructChop(Chop
,*I
,File
) == false)
154 clog
<< "Missed: " << File
<< endl
;
159 File
= OrigPath
+ ChopDirs(File
,Chop
);
164 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0 ||
167 // Attempt to fix busted symlink support for one instance
168 string OrigFile
= File
;
169 string::size_type Start
= File
.find("binary-");
170 string::size_type End
= File
.find("/",Start
+3);
171 if (Start
!= string::npos
&& End
!= string::npos
)
173 File
.replace(Start
,End
-Start
,"binary-all");
177 if (Mangled
== false ||
178 stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
181 clog
<< "Missed(2): " << OrigFile
<< endl
;
188 if ((unsigned long long)Buf
.st_size
!= Size
)
191 clog
<< "Wrong Size: " << File
<< endl
;
200 if (RewriteEntry(TargetFl
,File
) == false)
209 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
211 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
213 // Move out of the partial directory
215 string FinalF
= _config
->FindDir("Dir::State::lists");
216 FinalF
+= URItoFileName(S
);
217 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
218 return _error
->Errno("rename","Failed to rename");
221 /* Mangle the source to be in the proper notation with
222 prefix dist [component] */
223 *I
= string(*I
,Prefix
.length());
224 ConvertToSourceList(CDROM
,*I
);
225 *I
= Prefix
+ ' ' + *I
;
227 CurrentSize
+= FileSize
;
235 if(NotFound
== 0 && WrongSize
== 0)
236 ioprintf(msg
, _("Wrote %i records.\n"), Packages
);
237 else if (NotFound
!= 0 && WrongSize
== 0)
238 ioprintf(msg
, _("Wrote %i records with %i missing files.\n"),
240 else if (NotFound
== 0 && WrongSize
!= 0)
241 ioprintf(msg
, _("Wrote %i records with %i mismatched files\n"),
242 Packages
, WrongSize
);
243 if (NotFound
!= 0 && WrongSize
!= 0)
244 ioprintf(msg
, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages
, NotFound
, WrongSize
);
248 _error
->Warning("No valid records were found.");
250 if (NotFound
+ WrongSize
> 10)
251 _error
->Warning("A lot of entries were discarded, something may be wrong.\n");
257 // IndexCopy::ChopDirs - Chop off the leading directory components /*{{{*/
258 // ---------------------------------------------------------------------
260 string
IndexCopy::ChopDirs(string Path
,unsigned int Depth
)
262 string::size_type I
= 0;
265 I
= Path
.find('/',I
+1);
268 while (I
!= string::npos
&& Depth
!= 0);
270 if (I
== string::npos
)
273 return string(Path
,I
+1);
276 // IndexCopy::ReconstructPrefix - Fix strange prefixing /*{{{*/
277 // ---------------------------------------------------------------------
278 /* This prepends dir components from the path to the package files to
279 the path to the deb until it is found */
280 bool IndexCopy::ReconstructPrefix(string
&Prefix
,string OrigPath
,string CD
,
283 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
284 unsigned int Depth
= 1;
285 string MyPrefix
= Prefix
;
289 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
292 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
293 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
307 // IndexCopy::ReconstructChop - Fixes bad source paths /*{{{*/
308 // ---------------------------------------------------------------------
309 /* This removes path components from the filename and prepends the location
310 of the package files until a file is found */
311 bool IndexCopy::ReconstructChop(unsigned long &Chop
,string Dir
,string File
)
313 // Attempt to reconstruct the filename
314 unsigned long Depth
= 0;
318 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
320 File
= ChopDirs(File
,1);
322 if (File
.empty() == false)
335 // IndexCopy::ConvertToSourceList - Convert a Path to a sourcelist /*{{{*/
336 // ---------------------------------------------------------------------
337 /* We look for things in dists/ notation and convert them to
338 <dist> <component> form otherwise it is left alone. This also strips
341 This implements a regex sort of like:
342 (.*)/dists/([^/]*)/(.*)/binary-*
344 | |-------- Distribution
345 |------------------- Path
347 It was deciced to use only a single word for dist (rather than say
348 unstable/non-us) to increase the chance that each CD gets a single
349 line in sources.list.
351 void IndexCopy::ConvertToSourceList(string CD
,string
&Path
)
354 snprintf(S
,sizeof(S
),"binary-%s",_config
->Find("Apt::Architecture").c_str());
356 // Strip the cdrom base path
357 Path
= string(Path
,CD
.length());
358 if (Path
.empty() == true)
361 // Too short to be a dists/ type
362 if (Path
.length() < strlen("dists/"))
366 if (stringcmp(Path
.c_str(),Path
.c_str()+strlen("dists/"),"dists/") != 0)
370 string::size_type Slash
= strlen("dists/");
371 string::size_type Slash2
= Path
.find('/',Slash
+ 1);
372 if (Slash2
== string::npos
|| Slash2
+ 2 >= Path
.length())
374 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
376 // Isolate the component
378 for (unsigned I
= 0; I
!= 10; I
++)
380 Slash
= Path
.find('/',Slash
+1);
381 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
383 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
385 // Verify the trailing binary- bit
386 string::size_type BinSlash
= Path
.find('/',Slash
+ 1);
387 if (Slash
== string::npos
)
389 string Binary
= string(Path
,Slash
+1,BinSlash
- Slash
-1);
391 if (Binary
!= S
&& Binary
!= "source")
394 Path
= Dist
+ ' ' + Comp
;
399 // IndexCopy::GrabFirst - Return the first Depth path components /*{{{*/
400 // ---------------------------------------------------------------------
402 bool IndexCopy::GrabFirst(string Path
,string
&To
,unsigned int Depth
)
404 string::size_type I
= 0;
407 I
= Path
.find('/',I
+1);
410 while (I
!= string::npos
&& Depth
!= 0);
412 if (I
== string::npos
)
415 To
= string(Path
,0,I
+1);
419 // PackageCopy::GetFile - Get the file information from the section /*{{{*/
420 // ---------------------------------------------------------------------
422 bool PackageCopy::GetFile(string
&File
,unsigned long long &Size
)
424 File
= Section
->FindS("Filename");
425 Size
= Section
->FindI("Size");
426 if (File
.empty() || Size
== 0)
427 return _error
->Error("Cannot find filename or size tag");
431 // PackageCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
432 // ---------------------------------------------------------------------
434 bool PackageCopy::RewriteEntry(FILE *Target
,string File
)
436 TFRewriteData Changes
[] = {{"Filename",File
.c_str()},
439 if (TFRewrite(Target
,*Section
,TFRewritePackageOrder
,Changes
) == false)
445 // SourceCopy::GetFile - Get the file information from the section /*{{{*/
446 // ---------------------------------------------------------------------
448 bool SourceCopy::GetFile(string
&File
,unsigned long long &Size
)
450 string Files
= Section
->FindS("Files");
451 if (Files
.empty() == true)
454 // Stash the / terminated directory prefix
455 string Base
= Section
->FindS("Directory");
456 if (Base
.empty() == false && Base
[Base
.length()-1] != '/')
459 // Read the first file triplet
460 const char *C
= Files
.c_str();
464 // Parse each of the elements
465 if (ParseQuoteWord(C
,MD5Hash
) == false ||
466 ParseQuoteWord(C
,sSize
) == false ||
467 ParseQuoteWord(C
,File
) == false)
468 return _error
->Error("Error parsing file record");
470 // Parse the size and append the directory
471 Size
= strtoull(sSize
.c_str(), NULL
, 10);
476 // SourceCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
477 // ---------------------------------------------------------------------
479 bool SourceCopy::RewriteEntry(FILE *Target
,string File
)
481 string
Dir(File
,0,File
.rfind('/'));
482 TFRewriteData Changes
[] = {{"Directory",Dir
.c_str()},
485 if (TFRewrite(Target
,*Section
,TFRewriteSourceOrder
,Changes
) == false)
491 // SigVerify::Verify - Verify a files md5sum against its metaindex /*{{{*/
492 // ---------------------------------------------------------------------
494 bool SigVerify::Verify(string prefix
, string file
, indexRecords
*MetaIndex
)
496 const indexRecords::checkSum
*Record
= MetaIndex
->Lookup(file
);
498 // we skip non-existing files in the verifcation to support a cdrom
499 // with no Packages file (just a Package.gz), see LP: #255545
500 // (non-existing files are not considered a error)
501 if(!RealFileExists(prefix
+file
))
503 _error
->Warning(_("Skipping nonexistent file %s"), string(prefix
+file
).c_str());
509 _error
->Warning(_("Can't find authentication record for: %s"), file
.c_str());
513 if (!Record
->Hash
.VerifyFile(prefix
+file
))
515 _error
->Warning(_("Hash mismatch for: %s"),file
.c_str());
519 if(_config
->FindB("Debug::aptcdrom",false))
521 cout
<< "File: " << prefix
+file
<< endl
;
522 cout
<< "Expected Hash " << Record
->Hash
.toStr() << endl
;
528 bool SigVerify::CopyMetaIndex(string CDROM
, string CDName
, /*{{{*/
529 string prefix
, string file
)
532 snprintf(S
,sizeof(S
),"cdrom:[%s]/%s%s",CDName
.c_str(),
533 (prefix
).c_str() + CDROM
.length(),file
.c_str());
534 string TargetF
= _config
->FindDir("Dir::State::lists");
535 TargetF
+= URItoFileName(S
);
539 Target
.Open(TargetF
,FileFd::WriteAtomic
);
540 Rel
.Open(prefix
+ file
,FileFd::ReadOnly
);
541 if (_error
->PendingError() == true)
543 if (CopyFile(Rel
,Target
) == false)
549 bool SigVerify::CopyAndVerify(string CDROM
,string Name
,vector
<string
> &SigList
, /*{{{*/
550 vector
<string
> PkgList
,vector
<string
> SrcList
)
552 if (SigList
.empty() == true)
555 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
557 // Read all Release files
558 for (vector
<string
>::iterator I
= SigList
.begin(); I
!= SigList
.end(); ++I
)
561 cout
<< "Signature verify for: " << *I
<< endl
;
563 indexRecords
*MetaIndex
= new indexRecords
;
566 string
const releasegpg
= *I
+"Release.gpg";
567 string
const release
= *I
+"Release";
568 string
const inrelease
= *I
+"InRelease";
569 bool useInRelease
= true;
571 // a Release.gpg without a Release should never happen
572 if (RealFileExists(inrelease
) == true)
574 else if(RealFileExists(release
) == false || RealFileExists(releasegpg
) == false)
580 useInRelease
= false;
582 pid_t pid
= ExecFork();
584 _error
->Error("Fork failed");
589 if (useInRelease
== true)
590 RunGPGV(inrelease
, inrelease
);
592 RunGPGV(release
, releasegpg
);
595 if(!ExecWait(pid
, "gpgv")) {
596 _error
->Warning("Signature verification failed for: %s",
597 (useInRelease
? inrelease
.c_str() : releasegpg
.c_str()));
598 // something went wrong, don't copy the Release.gpg
599 // FIXME: delete any existing gpg file?
603 // Open the Release file and add it to the MetaIndex
604 if(!MetaIndex
->Load(release
))
606 _error
->Error("%s",MetaIndex
->ErrorText
.c_str());
610 // go over the Indexfiles and see if they verify
611 // if so, remove them from our copy of the lists
612 vector
<string
> keys
= MetaIndex
->MetaKeys();
613 for (vector
<string
>::iterator I
= keys
.begin(); I
!= keys
.end(); ++I
)
615 if(!Verify(prefix
,*I
, MetaIndex
)) {
616 // something went wrong, don't copy the Release.gpg
617 // FIXME: delete any existing gpg file?
623 // we need a fresh one for the Release.gpg
626 // everything was fine, copy the Release and Release.gpg file
627 if (useInRelease
== true)
628 CopyMetaIndex(CDROM
, Name
, prefix
, "InRelease");
631 CopyMetaIndex(CDROM
, Name
, prefix
, "Release");
632 CopyMetaIndex(CDROM
, Name
, prefix
, "Release.gpg");
639 // SigVerify::RunGPGV - returns the command needed for verify /*{{{*/
640 // ---------------------------------------------------------------------
641 /* Generating the commandline for calling gpgv is somehow complicated as
642 we need to add multiple keyrings and user supplied options. Also, as
643 the cdrom code currently can not use the gpgv method we have two places
644 these need to be done - so the place for this method is wrong but better
645 than code duplication… */
646 bool SigVerify::RunGPGV(std::string
const &File
, std::string
const &FileGPG
,
647 int const &statusfd
, int fd
[2])
651 #define SIGMSG "-----BEGIN PGP SIGNED MESSAGE-----\n"
652 char buffer
[sizeof(SIGMSG
)];
653 FILE* gpg
= fopen(File
.c_str(), "r");
655 return _error
->Errno("RunGPGV", _("Could not open file %s"), File
.c_str());
656 char const * const test
= fgets(buffer
, sizeof(buffer
), gpg
);
658 if (test
== NULL
|| strcmp(buffer
, SIGMSG
) != 0)
659 return _error
->Error(_("File %s doesn't start with a clearsigned message"), File
.c_str());
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
->Find("APT::GPGV::TrustedKeyring", _config
->FindFile("Dir::Etc::Trusted"));
667 string
const trustedPath
= _config
->FindDir("Dir::Etc::TrustedParts");
669 bool const Debug
= _config
->FindB("Debug::Acquire::gpgv", false);
673 std::clog
<< "gpgv path: " << gpgvpath
<< std::endl
;
674 std::clog
<< "Keyring file: " << trustedFile
<< std::endl
;
675 std::clog
<< "Keyring path: " << trustedPath
<< std::endl
;
678 std::vector
<string
> keyrings
;
679 if (DirectoryExists(trustedPath
))
680 keyrings
= GetListOfFilesInDir(trustedPath
, "gpg", false, true);
681 if (RealFileExists(trustedFile
) == true)
682 keyrings
.push_back(trustedFile
);
684 std::vector
<const char *> Args
;
687 if (keyrings
.empty() == true)
689 // TRANSLATOR: %s is the trusted keyring parts directory
690 return _error
->Error(_("No keyring installed in %s."),
691 _config
->FindDir("Dir::Etc::TrustedParts").c_str());
694 Args
.push_back(gpgvpath
.c_str());
695 Args
.push_back("--ignore-time-conflict");
699 Args
.push_back("--status-fd");
701 snprintf(fd
, sizeof(fd
), "%i", statusfd
);
705 for (vector
<string
>::const_iterator K
= keyrings
.begin();
706 K
!= keyrings
.end(); ++K
)
708 Args
.push_back("--keyring");
709 Args
.push_back(K
->c_str());
712 Configuration::Item
const *Opts
;
713 Opts
= _config
->Tree("Acquire::gpgv::Options");
717 for (; Opts
!= 0; Opts
= Opts
->Next
)
719 if (Opts
->Value
.empty() == true)
721 Args
.push_back(Opts
->Value
.c_str());
725 Args
.push_back(FileGPG
.c_str());
727 Args
.push_back(File
.c_str());
728 Args
.push_back(NULL
);
732 std::clog
<< "Preparing to exec: " << gpgvpath
;
733 for (std::vector
<const char *>::const_iterator a
= Args
.begin(); *a
!= NULL
; ++a
)
734 std::clog
<< " " << *a
;
735 std::clog
<< std::endl
;
740 int const nullfd
= open("/dev/null", O_RDONLY
);
742 // Redirect output to /dev/null; we read from the status fd
743 dup2(nullfd
, STDOUT_FILENO
);
744 dup2(nullfd
, STDERR_FILENO
);
745 // Redirect the pipe to the status fd (3)
746 dup2(fd
[1], statusfd
);
748 putenv((char *)"LANG=");
749 putenv((char *)"LC_ALL=");
750 putenv((char *)"LC_MESSAGES=");
753 execvp(gpgvpath
.c_str(), (char **) &Args
[0]);
757 bool TranslationsCopy::CopyTranslations(string CDROM
,string Name
, /*{{{*/
758 vector
<string
> &List
, pkgCdromStatus
*log
)
760 OpProgress
*Progress
= NULL
;
761 if (List
.empty() == true)
765 Progress
= log
->GetOpProgress();
767 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
769 // Prepare the progress indicator
771 std::vector
<APT::Configuration::Compressor
> const compressor
= APT::Configuration::getCompressors();
772 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
776 std::string file
= *I
;
777 for (std::vector
<APT::Configuration::Compressor
>::const_iterator c
= compressor
.begin();
778 c
!= compressor
.end(); ++c
)
780 if (stat(std::string(file
+ c
->Extension
).c_str(), &Buf
) != 0)
787 return _error
->Errno("stat", "Stat failed for %s", file
.c_str());
788 TotalSize
+= Buf
.st_size
;
791 off_t CurrentSize
= 0;
792 unsigned int NotFound
= 0;
793 unsigned int WrongSize
= 0;
794 unsigned int Packages
= 0;
795 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
797 string OrigPath
= string(*I
,CDROM
.length());
799 // Open the package file
800 FileFd
Pkg(*I
, FileFd::ReadOnly
, FileFd::Auto
);
801 off_t
const FileSize
= Pkg
.Size();
803 pkgTagFile
Parser(&Pkg
);
804 if (_error
->PendingError() == true)
807 // Open the output file
809 snprintf(S
,sizeof(S
),"cdrom:[%s]/%s",Name
.c_str(),
810 (*I
).c_str() + CDROM
.length());
811 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
812 TargetF
+= URItoFileName(S
);
814 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
816 TargetF
= "/dev/null";
817 Target
.Open(TargetF
,FileFd::WriteExists
);
819 Target
.Open(TargetF
,FileFd::WriteAtomic
);
821 FILE *TargetFl
= fdopen(dup(Target
.Fd()),"w");
822 if (_error
->PendingError() == true)
825 return _error
->Errno("fdopen","Failed to reopen fd");
827 // Setup the progress meter
829 Progress
->OverallProgress(CurrentSize
,TotalSize
,FileSize
,
830 string("Reading Translation Indexes"));
834 Progress
->SubProgress(Pkg
.Size());
835 pkgTagSection Section
;
836 this->Section
= &Section
;
838 unsigned long Hits
= 0;
839 while (Parser
.Step(Section
) == true)
842 Progress
->Progress(Parser
.Offset());
846 Section
.GetSection(Start
,Stop
);
847 fwrite(Start
,Stop
-Start
, 1, TargetFl
);
848 fputc('\n',TargetFl
);
856 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << endl
;
858 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
860 // Move out of the partial directory
862 string FinalF
= _config
->FindDir("Dir::State::lists");
863 FinalF
+= URItoFileName(S
);
864 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
865 return _error
->Errno("rename","Failed to rename");
869 CurrentSize
+= FileSize
;
877 if(NotFound
== 0 && WrongSize
== 0)
878 ioprintf(msg
, _("Wrote %i records.\n"), Packages
);
879 else if (NotFound
!= 0 && WrongSize
== 0)
880 ioprintf(msg
, _("Wrote %i records with %i missing files.\n"),
882 else if (NotFound
== 0 && WrongSize
!= 0)
883 ioprintf(msg
, _("Wrote %i records with %i mismatched files\n"),
884 Packages
, WrongSize
);
885 if (NotFound
!= 0 && WrongSize
!= 0)
886 ioprintf(msg
, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages
, NotFound
, WrongSize
);
890 _error
->Warning("No valid records were found.");
892 if (NotFound
+ WrongSize
> 10)
893 _error
->Warning("A lot of entries were discarded, something may be wrong.\n");