]>
git.saurik.com Git - apt-legacy.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 /*{{{*/
13 #include "indexcopy.h"
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>
37 // IndexCopy::CopyPackages - Copy the package files from the CD /*{{{*/
38 // ---------------------------------------------------------------------
40 bool IndexCopy::CopyPackages(string CDROM
,string Name
,vector
<string
> &List
,
43 OpProgress
*Progress
= NULL
;
48 Progress
= log
->GetOpProgress();
50 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
51 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
53 // Prepare the progress indicator
54 unsigned long TotalSize
= 0;
55 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
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
;
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
++)
71 string OrigPath
= string(*I
,CDROM
.length());
72 unsigned long FileSize
= 0;
74 // Open the package file
76 if (FileExists(*I
+ GetFileName()) == true)
78 Pkg
.Open(*I
+ GetFileName(),FileFd::ReadOnly
);
79 FileSize
= Pkg
.Size();
83 FileFd
From(*I
+ GetFileName() + ".gz",FileFd::ReadOnly
);
84 if (_error
->PendingError() == true)
86 FileSize
= From
.Size();
89 FILE *tmp
= tmpfile();
91 return _error
->Errno("tmpfile","Unable to create a tmp file");
92 Pkg
.Fd(dup(fileno(tmp
)));
96 pid_t Process
= fork();
98 return _error
->Errno("fork","Couldn't fork gzip");
103 dup2(From
.Fd(),STDIN_FILENO
);
104 dup2(Pkg
.Fd(),STDOUT_FILENO
);
105 SetCloseExec(STDIN_FILENO
,false);
106 SetCloseExec(STDOUT_FILENO
,false);
109 string Tmp
= _config
->Find("Dir::bin::gzip","gzip");
110 Args
[0] = Tmp
.c_str();
113 execvp(Args
[0],(char **)Args
);
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.");
123 pkgTagFile
Parser(&Pkg
);
124 if (_error
->PendingError() == true)
127 // Open the output file
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)
140 return _error
->Errno("fdopen","Failed to reopen fd");
142 // Setup the progress meter
144 Progress
->OverallProgress(CurrentSize
,TotalSize
,FileSize
,
145 string("Reading ") + Type() + " Indexes");
149 Progress
->SubProgress(Pkg
.Size());
150 pkgTagSection Section
;
151 this->Section
= &Section
;
153 unsigned long Hits
= 0;
154 unsigned long Chop
= 0;
155 while (Parser
.Step(Section
) == true)
158 Progress
->Progress(Parser
.Offset());
161 if (GetFile(File
,Size
) == false)
168 File
= OrigPath
+ ChopDirs(File
,Chop
);
170 // See if the file exists
171 bool Mangled
= false;
172 if (NoStat
== false || Hits
< 10)
174 // Attempt to fix broken structure
177 if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false &&
178 ReconstructChop(Chop
,*I
,File
) == false)
181 clog
<< "Missed: " << File
<< endl
;
186 File
= OrigPath
+ ChopDirs(File
,Chop
);
191 if (stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0 ||
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
)
200 File
.replace(Start
,End
-Start
,"binary-all");
204 if (Mangled
== false ||
205 stat(string(CDROM
+ Prefix
+ File
).c_str(),&Buf
) != 0)
208 clog
<< "Missed(2): " << OrigFile
<< endl
;
215 if ((unsigned)Buf
.st_size
!= Size
)
218 clog
<< "Wrong Size: " << File
<< endl
;
227 if (RewriteEntry(TargetFl
,File
) == false)
236 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
238 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
240 // Move out of the partial directory
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");
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
;
254 CurrentSize
+= FileSize
;
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"),
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
);
275 _error
->Warning("No valid records were found.");
277 if (NotFound
+ WrongSize
> 10)
278 _error
->Warning("A lot of entries were discarded, something may be wrong.\n");
284 // IndexCopy::ChopDirs - Chop off the leading directory components /*{{{*/
285 // ---------------------------------------------------------------------
287 string
IndexCopy::ChopDirs(string Path
,unsigned int Depth
)
289 string::size_type I
= 0;
292 I
= Path
.find('/',I
+1);
295 while (I
!= string::npos
&& Depth
!= 0);
297 if (I
== string::npos
)
300 return string(Path
,I
+1);
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
,
310 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
311 unsigned int Depth
= 1;
312 string MyPrefix
= Prefix
;
316 if (stat(string(CD
+ MyPrefix
+ File
).c_str(),&Buf
) != 0)
319 cout
<< "Failed, " << CD
+ MyPrefix
+ File
<< endl
;
320 if (GrabFirst(OrigPath
,MyPrefix
,Depth
++) == true)
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
)
340 // Attempt to reconstruct the filename
341 unsigned long Depth
= 0;
345 if (stat(string(Dir
+ File
).c_str(),&Buf
) != 0)
347 File
= ChopDirs(File
,1);
349 if (File
.empty() == false)
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
368 This implements a regex sort of like:
369 (.*)/dists/([^/]*)/(.*)/binary-*
371 | |-------- Distribution
372 |------------------- Path
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.
378 void IndexCopy::ConvertToSourceList(string CD
,string
&Path
)
381 snprintf(S
,sizeof(S
),"binary-%s",_config
->Find("Apt::Architecture").c_str());
383 // Strip the cdrom base path
384 Path
= string(Path
,CD
.length());
385 if (Path
.empty() == true)
388 // Too short to be a dists/ type
389 if (Path
.length() < strlen("dists/"))
393 if (stringcmp(Path
.c_str(),Path
.c_str()+strlen("dists/"),"dists/") != 0)
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())
401 string Dist
= string(Path
,Slash
,Slash2
- Slash
);
403 // Isolate the component
405 for (unsigned I
= 0; I
!= 10; I
++)
407 Slash
= Path
.find('/',Slash
+1);
408 if (Slash
== string::npos
|| Slash
+ 2 >= Path
.length())
410 string Comp
= string(Path
,Slash2
+1,Slash
- Slash2
-1);
412 // Verify the trailing binary- bit
413 string::size_type BinSlash
= Path
.find('/',Slash
+ 1);
414 if (Slash
== string::npos
)
416 string Binary
= string(Path
,Slash
+1,BinSlash
- Slash
-1);
418 if (Binary
!= S
&& Binary
!= "source")
421 Path
= Dist
+ ' ' + Comp
;
426 // IndexCopy::GrabFirst - Return the first Depth path components /*{{{*/
427 // ---------------------------------------------------------------------
429 bool IndexCopy::GrabFirst(string Path
,string
&To
,unsigned int Depth
)
431 string::size_type I
= 0;
434 I
= Path
.find('/',I
+1);
437 while (I
!= string::npos
&& Depth
!= 0);
439 if (I
== string::npos
)
442 To
= string(Path
,0,I
+1);
446 // PackageCopy::GetFile - Get the file information from the section /*{{{*/
447 // ---------------------------------------------------------------------
449 bool PackageCopy::GetFile(string
&File
,unsigned long &Size
)
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");
458 // PackageCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
459 // ---------------------------------------------------------------------
461 bool PackageCopy::RewriteEntry(FILE *Target
,string File
)
463 TFRewriteData Changes
[] = {{"Filename",File
.c_str()},
466 if (TFRewrite(Target
,*Section
,TFRewritePackageOrder
,Changes
) == false)
472 // SourceCopy::GetFile - Get the file information from the section /*{{{*/
473 // ---------------------------------------------------------------------
475 bool SourceCopy::GetFile(string
&File
,unsigned long &Size
)
477 string Files
= Section
->FindS("Files");
478 if (Files
.empty() == true)
481 // Stash the / terminated directory prefix
482 string Base
= Section
->FindS("Directory");
483 if (Base
.empty() == false && Base
[Base
.length()-1] != '/')
486 // Read the first file triplet
487 const char *C
= Files
.c_str();
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");
497 // Parse the size and append the directory
498 Size
= atoi(sSize
.c_str());
503 // SourceCopy::RewriteEntry - Rewrite the entry with a new filename /*{{{*/
504 // ---------------------------------------------------------------------
506 bool SourceCopy::RewriteEntry(FILE *Target
,string File
)
508 string
Dir(File
,0,File
.rfind('/'));
509 TFRewriteData Changes
[] = {{"Directory",Dir
.c_str()},
512 if (TFRewrite(Target
,*Section
,TFRewriteSourceOrder
,Changes
) == false)
518 // SigVerify::Verify - Verify a files md5sum against its metaindex /*{{{*/
519 // ---------------------------------------------------------------------
521 bool SigVerify::Verify(string prefix
, string file
, indexRecords
*MetaIndex
)
523 const indexRecords::checkSum
*Record
= MetaIndex
->Lookup(file
);
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
))
530 _error
->Warning(_("Skipping nonexistent file %s"), string(prefix
+file
).c_str());
536 _error
->Warning(_("Can't find authentication record for: %s"), file
.c_str());
540 if (!Record
->Hash
.VerifyFile(prefix
+file
))
542 _error
->Warning(_("Hash mismatch for: %s"),file
.c_str());
546 if(_config
->FindB("Debug::aptcdrom",false))
548 cout
<< "File: " << prefix
+file
<< endl
;
549 cout
<< "Expected Hash " << Record
->Hash
.toStr() << endl
;
555 bool SigVerify::CopyMetaIndex(string CDROM
, string CDName
, /*{{{*/
556 string prefix
, string file
)
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
);
566 Target
.Open(TargetF
,FileFd::WriteEmpty
);
567 Rel
.Open(prefix
+ file
,FileFd::ReadOnly
);
568 if (_error
->PendingError() == true)
570 if (CopyFile(Rel
,Target
) == false)
576 bool SigVerify::CopyAndVerify(string CDROM
,string Name
,vector
<string
> &SigList
, /*{{{*/
577 vector
<string
> PkgList
,vector
<string
> SrcList
)
579 if (SigList
.size() == 0)
582 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
584 // Read all Release files
585 for (vector
<string
>::iterator I
= SigList
.begin(); I
!= SigList
.end(); I
++)
588 cout
<< "Signature verify for: " << *I
<< endl
;
590 indexRecords
*MetaIndex
= new indexRecords
;
593 // a Release.gpg without a Release should never happen
594 if(!FileExists(*I
+"Release"))
601 // verify the gpg signature of "Release"
602 // gpg --verify "*I+Release.gpg", "*I+Release"
603 const char *Args
[400];
606 string gpgvpath
= _config
->Find("Dir::Bin::gpg", "/usr/bin/gpgv");
607 string pubringpath
= _config
->Find("Apt::GPGV::TrustedKeyring", "/etc/apt/trusted.gpg");
608 string releasegpg
= *I
+"Release.gpg";
609 string release
= *I
+"Release";
611 Args
[i
++] = gpgvpath
.c_str();
612 Args
[i
++] = "--keyring";
613 Args
[i
++] = pubringpath
.c_str();
614 Configuration::Item
const *Opts
;
615 Opts
= _config
->Tree("Acquire::gpgv::Options");
619 for (; Opts
!= 0; Opts
= Opts
->Next
)
621 if (Opts
->Value
.empty() == true)
623 Args
[i
++] = Opts
->Value
.c_str();
625 _error
->Error("Argument list from Acquire::gpgv::Options too long. Exiting.");
631 Args
[i
++] = releasegpg
.c_str();
632 Args
[i
++] = release
.c_str();
635 pid_t pid
= ExecFork();
637 _error
->Error("Fork failed");
641 execvp(gpgvpath
.c_str(), (char**)Args
);
643 if(!ExecWait(pid
, "gpgv")) {
644 _error
->Warning("Signature verification failed for: %s",
645 string(*I
+"Release.gpg").c_str());
646 // something went wrong, don't copy the Release.gpg
647 // FIXME: delete any existing gpg file?
651 // Open the Release file and add it to the MetaIndex
652 if(!MetaIndex
->Load(*I
+"Release"))
654 _error
->Error("%s",MetaIndex
->ErrorText
.c_str());
658 // go over the Indexfiles and see if they verify
659 // if so, remove them from our copy of the lists
660 vector
<string
> keys
= MetaIndex
->MetaKeys();
661 for (vector
<string
>::iterator I
= keys
.begin(); I
!= keys
.end(); I
++)
663 if(!Verify(prefix
,*I
, MetaIndex
)) {
664 // something went wrong, don't copy the Release.gpg
665 // FIXME: delete any existing gpg file?
671 // we need a fresh one for the Release.gpg
674 // everything was fine, copy the Release and Release.gpg file
675 CopyMetaIndex(CDROM
, Name
, prefix
, "Release");
676 CopyMetaIndex(CDROM
, Name
, prefix
, "Release.gpg");
682 bool TranslationsCopy::CopyTranslations(string CDROM
,string Name
, /*{{{*/
683 vector
<string
> &List
, pkgCdromStatus
*log
)
685 OpProgress
*Progress
= NULL
;
686 if (List
.size() == 0)
690 Progress
= log
->GetOpProgress();
692 bool Debug
= _config
->FindB("Debug::aptcdrom",false);
694 // Prepare the progress indicator
695 unsigned long TotalSize
= 0;
696 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
699 if (stat(string(*I
).c_str(),&Buf
) != 0 &&
700 stat(string(*I
+ ".gz").c_str(),&Buf
) != 0)
701 return _error
->Errno("stat","Stat failed for %s",
703 TotalSize
+= Buf
.st_size
;
706 unsigned long CurrentSize
= 0;
707 unsigned int NotFound
= 0;
708 unsigned int WrongSize
= 0;
709 unsigned int Packages
= 0;
710 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
712 string OrigPath
= string(*I
,CDROM
.length());
713 unsigned long FileSize
= 0;
715 // Open the package file
717 if (FileExists(*I
) == true)
719 Pkg
.Open(*I
,FileFd::ReadOnly
);
720 FileSize
= Pkg
.Size();
724 FileFd
From(*I
+ ".gz",FileFd::ReadOnly
);
725 if (_error
->PendingError() == true)
727 FileSize
= From
.Size();
730 FILE *tmp
= tmpfile();
732 return _error
->Errno("tmpfile","Unable to create a tmp file");
733 Pkg
.Fd(dup(fileno(tmp
)));
737 pid_t Process
= fork();
739 return _error
->Errno("fork","Couldn't fork gzip");
744 dup2(From
.Fd(),STDIN_FILENO
);
745 dup2(Pkg
.Fd(),STDOUT_FILENO
);
746 SetCloseExec(STDIN_FILENO
,false);
747 SetCloseExec(STDOUT_FILENO
,false);
750 string Tmp
= _config
->Find("Dir::bin::gzip","gzip");
751 Args
[0] = Tmp
.c_str();
754 execvp(Args
[0],(char **)Args
);
758 // Wait for gzip to finish
759 if (ExecWait(Process
,_config
->Find("Dir::bin::gzip","gzip").c_str(),false) == false)
760 return _error
->Error("gzip failed, perhaps the disk is full.");
764 pkgTagFile
Parser(&Pkg
);
765 if (_error
->PendingError() == true)
768 // Open the output file
770 snprintf(S
,sizeof(S
),"cdrom:[%s]/%s",Name
.c_str(),
771 (*I
).c_str() + CDROM
.length());
772 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
773 TargetF
+= URItoFileName(S
);
774 if (_config
->FindB("APT::CDROM::NoAct",false) == true)
775 TargetF
= "/dev/null";
776 FileFd
Target(TargetF
,FileFd::WriteEmpty
);
777 FILE *TargetFl
= fdopen(dup(Target
.Fd()),"w");
778 if (_error
->PendingError() == true)
781 return _error
->Errno("fdopen","Failed to reopen fd");
783 // Setup the progress meter
785 Progress
->OverallProgress(CurrentSize
,TotalSize
,FileSize
,
786 string("Reading Translation Indexes"));
790 Progress
->SubProgress(Pkg
.Size());
791 pkgTagSection Section
;
792 this->Section
= &Section
;
794 unsigned long Hits
= 0;
795 unsigned long Chop
= 0;
796 while (Parser
.Step(Section
) == true)
799 Progress
->Progress(Parser
.Offset());
803 Section
.GetSection(Start
,Stop
);
804 fwrite(Start
,Stop
-Start
, 1, TargetFl
);
805 fputc('\n',TargetFl
);
813 cout
<< " Processed by using Prefix '" << Prefix
<< "' and chop " << Chop
<< endl
;
815 if (_config
->FindB("APT::CDROM::NoAct",false) == false)
817 // Move out of the partial directory
819 string FinalF
= _config
->FindDir("Dir::State::lists");
820 FinalF
+= URItoFileName(S
);
821 if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0)
822 return _error
->Errno("rename","Failed to rename");
826 CurrentSize
+= FileSize
;
834 if(NotFound
== 0 && WrongSize
== 0)
835 ioprintf(msg
, _("Wrote %i records.\n"), Packages
);
836 else if (NotFound
!= 0 && WrongSize
== 0)
837 ioprintf(msg
, _("Wrote %i records with %i missing files.\n"),
839 else if (NotFound
== 0 && WrongSize
!= 0)
840 ioprintf(msg
, _("Wrote %i records with %i mismatched files\n"),
841 Packages
, WrongSize
);
842 if (NotFound
!= 0 && WrongSize
!= 0)
843 ioprintf(msg
, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages
, NotFound
, WrongSize
);
847 _error
->Warning("No valid records were found.");
849 if (NotFound
+ WrongSize
> 10)
850 _error
->Warning("A lot of entries were discarded, something may be wrong.\n");