]>
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          if (NoStat 
== false || Hits 
< 10) 
 146             // Attempt to fix broken structure 
 149                if (ReconstructPrefix(Prefix
,OrigPath
,CDROM
,File
) == false && 
 150                    ReconstructChop(Chop
,*I
,File
) == false) 
 153                      clog 
<< "Missed: " << File 
<< endl
; 
 158                   File 
= OrigPath 
+ ChopDirs(File
,Chop
); 
 163             if (stat(string(CDROM 
+ Prefix 
+ File
).c_str(),&Buf
) != 0 ||  
 166                bool Mangled 
= false; 
 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
) 
 353    // Strip the cdrom base path 
 354    Path 
= string(Path
,CD
.length()); 
 355    if (Path
.empty() == true) 
 358    // Too short to be a dists/ type 
 359    if (Path
.length() < strlen("dists/")) 
 363    if (stringcmp(Path
.c_str(),Path
.c_str()+strlen("dists/"),"dists/") != 0) 
 367    string::size_type Slash 
= strlen("dists/"); 
 368    string::size_type Slash2 
= Path
.find('/',Slash 
+ 1); 
 369    if (Slash2 
== string::npos 
|| Slash2 
+ 2 >= Path
.length()) 
 371    string Dist 
= string(Path
,Slash
,Slash2 
- Slash
); 
 373    // Isolate the component 
 375    for (unsigned I 
= 0; I 
!= 10; I
++) 
 377       Slash 
= Path
.find('/',Slash
+1); 
 378       if (Slash 
== string::npos 
|| Slash 
+ 2 >= Path
.length()) 
 380       string Comp 
= string(Path
,Slash2
+1,Slash 
- Slash2
-1); 
 382       // Verify the trailing binary- bit 
 383       string::size_type BinSlash 
= Path
.find('/',Slash 
+ 1); 
 384       if (Slash 
== string::npos
) 
 386       string Binary 
= string(Path
,Slash
+1,BinSlash 
- Slash
-1); 
 388       if (strncmp(Binary
.c_str(), "binary-", strlen("binary-")) == 0) 
 390          Binary
.erase(0, strlen("binary-")); 
 391          if (APT::Configuration::checkArchitecture(Binary
) == false) 
 394       else if (Binary 
!= "source") 
 397       Path 
= Dist 
+ ' ' + Comp
; 
 402 // IndexCopy::GrabFirst - Return the first Depth path components        /*{{{*/ 
 403 // --------------------------------------------------------------------- 
 405 bool IndexCopy::GrabFirst(string Path
,string 
&To
,unsigned int Depth
) 
 407    string::size_type I 
= 0; 
 410       I 
= Path
.find('/',I
+1); 
 413    while (I 
!= string::npos 
&& Depth 
!= 0); 
 415    if (I 
== string::npos
) 
 418    To 
= string(Path
,0,I
+1); 
 422 // PackageCopy::GetFile - Get the file information from the section     /*{{{*/ 
 423 // --------------------------------------------------------------------- 
 425 bool PackageCopy::GetFile(string 
&File
,unsigned long long &Size
) 
 427    File 
= Section
->FindS("Filename"); 
 428    Size 
= Section
->FindI("Size"); 
 429    if (File
.empty() || Size 
== 0) 
 430       return _error
->Error("Cannot find filename or size tag"); 
 434 // PackageCopy::RewriteEntry - Rewrite the entry with a new filename    /*{{{*/ 
 435 // --------------------------------------------------------------------- 
 437 bool PackageCopy::RewriteEntry(FILE *Target
,string File
) 
 439    TFRewriteData Changes
[] = {{"Filename",File
.c_str()}, 
 442    if (TFRewrite(Target
,*Section
,TFRewritePackageOrder
,Changes
) == false) 
 448 // SourceCopy::GetFile - Get the file information from the section      /*{{{*/ 
 449 // --------------------------------------------------------------------- 
 451 bool SourceCopy::GetFile(string 
&File
,unsigned long long &Size
) 
 453    string Files 
= Section
->FindS("Files"); 
 454    if (Files
.empty() == true) 
 457    // Stash the / terminated directory prefix 
 458    string Base 
= Section
->FindS("Directory"); 
 459    if (Base
.empty() == false && Base
[Base
.length()-1] != '/') 
 462    // Read the first file triplet 
 463    const char *C 
= Files
.c_str(); 
 467    // Parse each of the elements 
 468    if (ParseQuoteWord(C
,MD5Hash
) == false || 
 469        ParseQuoteWord(C
,sSize
) == false || 
 470        ParseQuoteWord(C
,File
) == false) 
 471       return _error
->Error("Error parsing file record"); 
 473    // Parse the size and append the directory 
 474    Size 
= strtoull(sSize
.c_str(), NULL
, 10); 
 479 // SourceCopy::RewriteEntry - Rewrite the entry with a new filename     /*{{{*/ 
 480 // --------------------------------------------------------------------- 
 482 bool SourceCopy::RewriteEntry(FILE *Target
,string File
) 
 484    string 
Dir(File
,0,File
.rfind('/')); 
 485    TFRewriteData Changes
[] = {{"Directory",Dir
.c_str()}, 
 488    if (TFRewrite(Target
,*Section
,TFRewriteSourceOrder
,Changes
) == false) 
 494 // SigVerify::Verify - Verify a files md5sum against its metaindex      /*{{{*/ 
 495 // --------------------------------------------------------------------- 
 497 bool SigVerify::Verify(string prefix
, string file
, indexRecords 
*MetaIndex
) 
 499    const indexRecords::checkSum 
*Record 
= MetaIndex
->Lookup(file
); 
 500    bool const Debug 
= _config
->FindB("Debug::aptcdrom",false); 
 502    // we skip non-existing files in the verifcation of the Release file 
 503    // as non-existing files do not harm, but a warning scares people and 
 504    // makes it hard to strip unneeded files from an ISO like uncompressed 
 505    // indexes as it is done on the mirrors (see also LP: #255545 ) 
 506    if(!RealFileExists(prefix
+file
)) 
 509          cout 
<< "Skipping nonexistent in " << prefix 
<< " file " << file 
<< std::endl
; 
 515       _error
->Warning(_("Can't find authentication record for: %s"), file
.c_str()); 
 519    if (!Record
->Hash
.VerifyFile(prefix
+file
)) 
 521       _error
->Warning(_("Hash mismatch for: %s"),file
.c_str()); 
 527       cout 
<< "File: " << prefix
+file 
<< endl
; 
 528       cout 
<< "Expected Hash " << Record
->Hash
.toStr() << endl
; 
 534 bool SigVerify::CopyMetaIndex(string CDROM
, string CDName
,              /*{{{*/ 
 535                               string prefix
, string file
) 
 538       snprintf(S
,sizeof(S
),"cdrom:[%s]/%s%s",CDName
.c_str(), 
 539                (prefix
).c_str() + CDROM
.length(),file
.c_str()); 
 540       string TargetF 
= _config
->FindDir("Dir::State::lists"); 
 541       TargetF 
+= URItoFileName(S
); 
 545       Target
.Open(TargetF
,FileFd::WriteAtomic
); 
 546       Rel
.Open(prefix 
+ file
,FileFd::ReadOnly
); 
 547       if (_error
->PendingError() == true) 
 549       if (CopyFile(Rel
,Target
) == false) 
 555 bool SigVerify::CopyAndVerify(string CDROM
,string Name
,vector
<string
> &SigList
, /*{{{*/ 
 556                               vector
<string
> PkgList
,vector
<string
> SrcList
) 
 558    if (SigList
.empty() == true) 
 561    bool Debug 
= _config
->FindB("Debug::aptcdrom",false); 
 563    // Read all Release files 
 564    for (vector
<string
>::iterator I 
= SigList
.begin(); I 
!= SigList
.end(); ++I
) 
 567          cout 
<< "Signature verify for: " << *I 
<< endl
; 
 569       indexRecords 
*MetaIndex 
= new indexRecords
; 
 572       string 
const releasegpg 
= *I
+"Release.gpg"; 
 573       string 
const release 
= *I
+"Release"; 
 574       string 
const inrelease 
= *I
+"InRelease"; 
 575       bool useInRelease 
= true; 
 577       // a Release.gpg without a Release should never happen 
 578       if (RealFileExists(inrelease
) == true) 
 580       else if(RealFileExists(release
) == false || RealFileExists(releasegpg
) == false) 
 586          useInRelease 
= false; 
 588       pid_t pid 
= ExecFork(); 
 590          _error
->Error("Fork failed"); 
 595          if (useInRelease 
== true) 
 596             ExecGPGV(inrelease
, inrelease
); 
 598             ExecGPGV(release
, releasegpg
); 
 601       if(!ExecWait(pid
, "gpgv")) { 
 602          _error
->Warning("Signature verification failed for: %s", 
 603                          (useInRelease 
? inrelease
.c_str() : releasegpg
.c_str())); 
 604          // something went wrong, don't copy the Release.gpg 
 605          // FIXME: delete any existing gpg file? 
 609       // Open the Release file and add it to the MetaIndex 
 610       if(!MetaIndex
->Load(release
)) 
 612          _error
->Error("%s",MetaIndex
->ErrorText
.c_str()); 
 616       // go over the Indexfiles and see if they verify 
 617       // if so, remove them from our copy of the lists 
 618       vector
<string
> keys 
= MetaIndex
->MetaKeys(); 
 619       for (vector
<string
>::iterator I 
= keys
.begin(); I 
!= keys
.end(); ++I
) 
 621          if(!Verify(prefix
,*I
, MetaIndex
)) { 
 622             // something went wrong, don't copy the Release.gpg 
 623             // FIXME: delete any existing gpg file? 
 629       // we need a fresh one for the Release.gpg 
 632       // everything was fine, copy the Release and Release.gpg file 
 633       if (useInRelease 
== true) 
 634          CopyMetaIndex(CDROM
, Name
, prefix
, "InRelease"); 
 637          CopyMetaIndex(CDROM
, Name
, prefix
, "Release"); 
 638          CopyMetaIndex(CDROM
, Name
, prefix
, "Release.gpg"); 
 645 bool TranslationsCopy::CopyTranslations(string CDROM
,string Name
,       /*{{{*/ 
 646                                 vector
<string
> &List
, pkgCdromStatus 
*log
) 
 648    OpProgress 
*Progress 
= NULL
; 
 649    if (List
.empty() == true) 
 653       Progress 
= log
->GetOpProgress(); 
 655    bool Debug 
= _config
->FindB("Debug::aptcdrom",false); 
 657    // Prepare the progress indicator 
 659    std::vector
<APT::Configuration::Compressor
> const compressor 
= APT::Configuration::getCompressors(); 
 660    for (vector
<string
>::iterator I 
= List
.begin(); I 
!= List
.end(); ++I
) 
 664       std::string file 
= *I
; 
 665       for (std::vector
<APT::Configuration::Compressor
>::const_iterator c 
= compressor
.begin(); 
 666            c 
!= compressor
.end(); ++c
) 
 668          if (stat(std::string(file 
+ c
->Extension
).c_str(), &Buf
) != 0) 
 675          return _error
->Errno("stat", "Stat failed for %s", file
.c_str()); 
 676       TotalSize 
+= Buf
.st_size
; 
 679    off_t CurrentSize 
= 0; 
 680    unsigned int NotFound 
= 0; 
 681    unsigned int WrongSize 
= 0; 
 682    unsigned int Packages 
= 0; 
 683    for (vector
<string
>::iterator I 
= List
.begin(); I 
!= List
.end(); ++I
) 
 685       // Open the package file 
 686       FileFd 
Pkg(*I
, FileFd::ReadOnly
, FileFd::Auto
); 
 687       off_t 
const FileSize 
= Pkg
.Size(); 
 689       pkgTagFile 
Parser(&Pkg
); 
 690       if (_error
->PendingError() == true) 
 693       // Open the output file 
 695       snprintf(S
,sizeof(S
),"cdrom:[%s]/%s",Name
.c_str(), 
 696                (*I
).c_str() + CDROM
.length()); 
 697       string TargetF 
= _config
->FindDir("Dir::State::lists") + "partial/"; 
 698       TargetF 
+= URItoFileName(S
); 
 700       if (_config
->FindB("APT::CDROM::NoAct",false) == true) 
 702          TargetF 
= "/dev/null"; 
 703          Target
.Open(TargetF
,FileFd::WriteExists
); 
 705          Target
.Open(TargetF
,FileFd::WriteAtomic
); 
 707       FILE *TargetFl 
= fdopen(dup(Target
.Fd()),"w"); 
 708       if (_error
->PendingError() == true) 
 711          return _error
->Errno("fdopen","Failed to reopen fd"); 
 713       // Setup the progress meter 
 715          Progress
->OverallProgress(CurrentSize
,TotalSize
,FileSize
, 
 716                                    string("Reading Translation Indexes")); 
 720          Progress
->SubProgress(Pkg
.Size()); 
 721       pkgTagSection Section
; 
 722       this->Section 
= &Section
; 
 724       unsigned long Hits 
= 0; 
 725       while (Parser
.Step(Section
) == true) 
 728             Progress
->Progress(Parser
.Offset()); 
 732          Section
.GetSection(Start
,Stop
); 
 733          fwrite(Start
,Stop
-Start
, 1, TargetFl
); 
 734          fputc('\n',TargetFl
); 
 742          cout 
<< " Processed by using Prefix '" << Prefix 
<< "' and chop " << endl
; 
 744       if (_config
->FindB("APT::CDROM::NoAct",false) == false) 
 746          // Move out of the partial directory 
 748          string FinalF 
= _config
->FindDir("Dir::State::lists"); 
 749          FinalF 
+= URItoFileName(S
); 
 750          if (rename(TargetF
.c_str(),FinalF
.c_str()) != 0) 
 751             return _error
->Errno("rename","Failed to rename"); 
 755       CurrentSize 
+= FileSize
; 
 763       if(NotFound 
== 0 && WrongSize 
== 0) 
 764          ioprintf(msg
, _("Wrote %i records.\n"), Packages
); 
 765       else if (NotFound 
!= 0 && WrongSize 
== 0) 
 766          ioprintf(msg
, _("Wrote %i records with %i missing files.\n"),  
 768       else if (NotFound 
== 0 && WrongSize 
!= 0) 
 769          ioprintf(msg
, _("Wrote %i records with %i mismatched files\n"),  
 770                   Packages
, WrongSize
); 
 771       if (NotFound 
!= 0 && WrongSize 
!= 0) 
 772          ioprintf(msg
, _("Wrote %i records with %i missing files and %i mismatched files\n"), Packages
, NotFound
, WrongSize
); 
 776       _error
->Warning("No valid records were found."); 
 778    if (NotFound 
+ WrongSize 
> 10) 
 779       _error
->Warning("A lot of entries were discarded, something may be wrong.\n");