]>
git.saurik.com Git - apt.git/blob - apt-pkg/cdrom.cc
   5 #pragma implementation "apt-pkg/cdrom.h" 
   7 #include<apt-pkg/init.h> 
   8 #include<apt-pkg/error.h> 
   9 #include<apt-pkg/cdromutl.h> 
  10 #include<apt-pkg/strutl.h> 
  11 #include<apt-pkg/cdrom.h> 
  24 #include "indexcopy.h" 
  28 // FindPackages - Find the package files on the CDROM                   /*{{{*/ 
  29 // --------------------------------------------------------------------- 
  30 /* We look over the cdrom for package files. This is a recursive 
  31    search that short circuits when it his a package file in the dir. 
  32    This speeds it up greatly as the majority of the size is in the 
  34 bool pkgCdrom::FindPackages(string CD
,vector
<string
> &List
, 
  35                             vector
<string
> &SList
, vector
<string
> &SigList
, 
  36                             string 
&InfoDir
, pkgCdromStatus 
*log
, 
  39    static ino_t Inodes
[9]; 
  41    // if we have a look we "pulse" now 
  48    if (CD
[CD
.length()-1] != '/') 
  51    if (chdir(CD
.c_str()) != 0) 
  52       return _error
->Errno("chdir","Unable to change to %s",CD
.c_str()); 
  54    // Look for a .disk subdirectory 
  56    if (stat(".disk",&Buf
) == 0) 
  58       if (InfoDir
.empty() == true) 
  59          InfoDir 
= CD 
+ ".disk/"; 
  62    // Don't look into directories that have been marked to ingore. 
  63    if (stat(".aptignr",&Buf
) == 0) 
  67    /* Check _first_ for a signature file as apt-cdrom assumes that all files 
  68       under a Packages/Source file are in control of that file and stops  
  71    if (stat("Release.gpg",&Buf
) == 0) 
  73       SigList
.push_back(CD
); 
  75    /* Aha! We found some package files. We assume that everything under  
  76       this dir is controlled by those package files so we don't look down 
  78    if (stat("Packages",&Buf
) == 0 || stat("Packages.gz",&Buf
) == 0) 
  82       // Continue down if thorough is given 
  83       if (_config
->FindB("APT::CDROM::Thorough",false) == false) 
  86    if (stat("Sources.gz",&Buf
) == 0 || stat("Sources",&Buf
) == 0) 
  90       // Continue down if thorough is given 
  91       if (_config
->FindB("APT::CDROM::Thorough",false) == false) 
  95    DIR *D 
= opendir("."); 
  97       return _error
->Errno("opendir","Unable to read %s",CD
.c_str()); 
  99    // Run over the directory 
 100    for (struct dirent 
*Dir 
= readdir(D
); Dir 
!= 0; Dir 
= readdir(D
)) 
 103       if (strcmp(Dir
->d_name
,".") == 0 || 
 104           strcmp(Dir
->d_name
,"..") == 0 || 
 105           //strcmp(Dir->d_name,"source") == 0 || 
 106           strcmp(Dir
->d_name
,".disk") == 0 || 
 107           strcmp(Dir
->d_name
,"experimental") == 0 || 
 108           strcmp(Dir
->d_name
,"binary-all") == 0 || 
 109           strcmp(Dir
->d_name
,"debian-installer") == 0) 
 112       // See if the name is a sub directory 
 114       if (stat(Dir
->d_name
,&Buf
) != 0) 
 117       if (S_ISDIR(Buf
.st_mode
) == 0) 
 121       for (I 
= 0; I 
!= Depth
; I
++) 
 122          if (Inodes
[I
] == Buf
.st_ino
) 
 127       // Store the inodes weve seen 
 128       Inodes
[Depth
] = Buf
.st_ino
; 
 131       if (FindPackages(CD 
+ Dir
->d_name
,List
,SList
,SigList
,InfoDir
,log
,Depth
+1) == false) 
 134       if (chdir(CD
.c_str()) != 0) 
 135          return _error
->Errno("chdir","Unable to change to %s",CD
.c_str()); 
 140    return !_error
->PendingError(); 
 143 // Score - We compute a 'score' for a path                              /*{{{*/ 
 144 // --------------------------------------------------------------------- 
 145 /* Paths are scored based on how close they come to what I consider 
 146    normal. That is ones that have 'dist' 'stable' 'testing' will score 
 147    higher than ones without. */ 
 148 int pkgCdrom::Score(string Path
) 
 151    if (Path
.find("stable/") != string::npos
) 
 153    if (Path
.find("/binary-") != string::npos
) 
 155    if (Path
.find("testing/") != string::npos
) 
 157    if (Path
.find("unstable/") != string::npos
) 
 159    if (Path
.find("/dists/") != string::npos
) 
 161    if (Path
.find("/main/") != string::npos
) 
 163    if (Path
.find("/contrib/") != string::npos
) 
 165    if (Path
.find("/non-free/") != string::npos
) 
 167    if (Path
.find("/non-US/") != string::npos
) 
 169    if (Path
.find("/source/") != string::npos
) 
 171    if (Path
.find("/debian/") != string::npos
) 
 174    // check for symlinks in the patch leading to the actual file 
 175    // a symlink gets a big penalty 
 177    string statPath 
= flNotFile(Path
); 
 178    string cdromPath 
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/"); 
 179    while(statPath 
!= cdromPath 
&& statPath 
!= "./") { 
 180       statPath
.resize(statPath
.size()-1);  // remove the trailing '/' 
 181       if (lstat(statPath
.c_str(),&Buf
) == 0) { 
 182         if(S_ISLNK(Buf
.st_mode
)) { 
 187       statPath 
= flNotFile(statPath
); // descent 
 194 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/         /*{{{*/ 
 195 // --------------------------------------------------------------------- 
 196 /* Here we drop everything that is not this machines arch */ 
 197 bool pkgCdrom::DropBinaryArch(vector
<string
> &List
) 
 200    snprintf(S
,sizeof(S
),"/binary-%s/", 
 201             _config
->Find("Apt::Architecture").c_str()); 
 203    for (unsigned int I 
= 0; I 
< List
.size(); I
++) 
 205       const char *Str 
= List
[I
].c_str(); 
 208       if ((Res 
= strstr(Str
,"/binary-")) == 0) 
 212       if (strlen(Res
) < strlen(S
)) 
 214          List
.erase(List
.begin() + I
); 
 219       // See if it is our arch 
 220       if (stringcmp(Res
,Res 
+ strlen(S
),S
) == 0) 
 224       List
.erase(List
.begin() + I
); 
 232 // DropRepeats - Drop repeated files resulting from symlinks            /*{{{*/ 
 233 // --------------------------------------------------------------------- 
 234 /* Here we go and stat every file that we found and strip dup inodes. */ 
 235 bool pkgCdrom::DropRepeats(vector
<string
> &List
,const char *Name
) 
 237    // Get a list of all the inodes 
 238    ino_t 
*Inodes 
= new ino_t
[List
.size()]; 
 239    for (unsigned int I 
= 0; I 
!= List
.size(); I
++) 
 242       if (stat((List
[I
] + Name
).c_str(),&Buf
) != 0 && 
 243           stat((List
[I
] + Name 
+ ".gz").c_str(),&Buf
) != 0) 
 244          _error
->Errno("stat","Failed to stat %s%s",List
[I
].c_str(), 
 246       Inodes
[I
] = Buf
.st_ino
; 
 249    if (_error
->PendingError() == true) 
 253    for (unsigned int I 
= 0; I 
!= List
.size(); I
++) 
 255       for (unsigned int J 
= I
+1; J 
< List
.size(); J
++) 
 258          if (Inodes
[J
] != Inodes
[I
]) 
 261          // We score the two paths.. and erase one 
 262          int ScoreA 
= Score(List
[I
]); 
 263          int ScoreB 
= Score(List
[J
]); 
 274    // Wipe erased entries 
 275    for (unsigned int I 
= 0; I 
< List
.size();) 
 277       if (List
[I
].empty() == false) 
 280          List
.erase(List
.begin()+I
); 
 287 // ReduceSourceList - Takes the path list and reduces it                /*{{{*/ 
 288 // --------------------------------------------------------------------- 
 289 /* This takes the list of source list expressed entires and collects 
 290    similar ones to form a single entry for each dist */ 
 291 void pkgCdrom::ReduceSourcelist(string CD
,vector
<string
> &List
) 
 293    sort(List
.begin(),List
.end()); 
 295    // Collect similar entries 
 296    for (vector
<string
>::iterator I 
= List
.begin(); I 
!= List
.end(); I
++) 
 299       string::size_type Space 
= (*I
).find(' '); 
 300       if (Space 
== string::npos
) 
 302       string::size_type SSpace 
= (*I
).find(' ',Space 
+ 1); 
 303       if (SSpace 
== string::npos
) 
 306       string Word1 
= string(*I
,Space
,SSpace
-Space
); 
 307       string Prefix 
= string(*I
,0,Space
); 
 308       for (vector
<string
>::iterator J 
= List
.begin(); J 
!= I
; J
++) 
 311          string::size_type Space2 
= (*J
).find(' '); 
 312          if (Space2 
== string::npos
) 
 314          string::size_type SSpace2 
= (*J
).find(' ',Space2 
+ 1); 
 315          if (SSpace2 
== string::npos
) 
 318          if (string(*J
,0,Space2
) != Prefix
) 
 320          if (string(*J
,Space2
,SSpace2
-Space2
) != Word1
) 
 323          *J 
+= string(*I
,SSpace
); 
 328    // Wipe erased entries 
 329    for (unsigned int I 
= 0; I 
< List
.size();) 
 331       if (List
[I
].empty() == false) 
 334          List
.erase(List
.begin()+I
); 
 338 // WriteDatabase - Write the CDROM Database file                        /*{{{*/ 
 339 // --------------------------------------------------------------------- 
 340 /* We rewrite the configuration class associated with the cdrom database. */ 
 341 bool pkgCdrom::WriteDatabase(Configuration 
&Cnf
) 
 343    string DFile 
= _config
->FindFile("Dir::State::cdroms"); 
 344    string NewFile 
= DFile 
+ ".new"; 
 346    unlink(NewFile
.c_str()); 
 347    ofstream 
Out(NewFile
.c_str()); 
 349       return _error
->Errno("ofstream::ofstream", 
 350                            "Failed to open %s.new",DFile
.c_str()); 
 352    /* Write out all of the configuration directives by walking the 
 353       configuration tree */ 
 354    const Configuration::Item 
*Top 
= Cnf
.Tree(0); 
 357       // Print the config entry 
 358       if (Top
->Value
.empty() == false) 
 359          Out 
<<  Top
->FullTag() + " \"" << Top
->Value 
<< "\";" << endl
; 
 367       while (Top 
!= 0 && Top
->Next 
== 0) 
 375    rename(DFile
.c_str(),string(DFile 
+ '~').c_str()); 
 376    if (rename(NewFile
.c_str(),DFile
.c_str()) != 0) 
 377       return _error
->Errno("rename","Failed to rename %s.new to %s", 
 378                            DFile
.c_str(),DFile
.c_str()); 
 383 // WriteSourceList - Write an updated sourcelist                        /*{{{*/ 
 384 // --------------------------------------------------------------------- 
 385 /* This reads the old source list and copies it into the new one. It  
 386    appends the new CDROM entires just after the first block of comments. 
 387    This places them first in the file. It also removes any old entries 
 388    that were the same. */ 
 389 bool pkgCdrom::WriteSourceList(string Name
,vector
<string
> &List
,bool Source
) 
 391    if (List
.size() == 0) 
 394    string File 
= _config
->FindFile("Dir::Etc::sourcelist"); 
 396    // Open the stream for reading 
 397    ifstream 
F((FileExists(File
)?File
.c_str():"/dev/null"), 
 400       return _error
->Errno("ifstream::ifstream","Opening %s",File
.c_str()); 
 402    string NewFile 
= File 
+ ".new"; 
 403    unlink(NewFile
.c_str()); 
 404    ofstream 
Out(NewFile
.c_str()); 
 406       return _error
->Errno("ofstream::ofstream", 
 407                            "Failed to open %s.new",File
.c_str()); 
 409    // Create a short uri without the path 
 410    string ShortURI 
= "cdrom:[" + Name 
+ "]/";    
 411    string ShortURI2 
= "cdrom:" + Name 
+ "/";     // For Compatibility 
 422    while (F
.eof() == false) 
 424       F
.getline(Buffer
,sizeof(Buffer
)); 
 426       if (F
.fail() && !F
.eof()) 
 427          return _error
->Error(_("Line %u too long in source list %s."), 
 428                               CurLine
,File
.c_str()); 
 429       _strtabexpand(Buffer
,sizeof(Buffer
)); 
 433       if (Buffer
[0] == '#' || Buffer
[0] == 0) 
 435          Out 
<< Buffer 
<< endl
; 
 441          for (vector
<string
>::iterator I 
= List
.begin(); I 
!= List
.end(); I
++) 
 443             string::size_type Space 
= (*I
).find(' '); 
 444             if (Space 
== string::npos
) 
 445                return _error
->Error("Internal error"); 
 446             Out 
<< Type 
<< " cdrom:[" << Name 
<< "]/" << string(*I
,0,Space
) << 
 447                " " << string(*I
,Space
+1) << endl
; 
 455       const char *C 
= Buffer
; 
 456       if (ParseQuoteWord(C
,cType
) == false || 
 457           ParseQuoteWord(C
,URI
) == false) 
 459          Out 
<< Buffer 
<< endl
; 
 463       // Emit lines like this one 
 464       if (cType 
!= Type 
|| (string(URI
,0,ShortURI
.length()) != ShortURI 
&& 
 465           string(URI
,0,ShortURI
.length()) != ShortURI2
)) 
 467          Out 
<< Buffer 
<< endl
; 
 472    // Just in case the file was empty 
 475       for (vector
<string
>::iterator I 
= List
.begin(); I 
!= List
.end(); I
++) 
 477          string::size_type Space 
= (*I
).find(' '); 
 478          if (Space 
== string::npos
) 
 479             return _error
->Error("Internal error"); 
 481          Out 
<< "deb cdrom:[" << Name 
<< "]/" << string(*I
,0,Space
) <<  
 482             " " << string(*I
,Space
+1) << endl
; 
 488    rename(File
.c_str(),string(File 
+ '~').c_str()); 
 489    if (rename(NewFile
.c_str(),File
.c_str()) != 0) 
 490       return _error
->Errno("rename","Failed to rename %s.new to %s", 
 491                            File
.c_str(),File
.c_str()); 
 497 bool pkgCdrom::Ident(string 
&ident
, pkgCdromStatus 
*log
) 
 502    string CDROM 
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/"); 
 504       CDROM
= SafeGetCWD() + '/' + CDROM
; 
 508       ioprintf(msg
, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"), 
 510       log
->Update(msg
.str()); 
 512    if (MountCdrom(CDROM
) == false) 
 513       return _error
->Error("Failed to mount the cdrom."); 
 515    // Hash the CD to get an ID 
 517       log
->Update(_("Identifying.. ")); 
 520    if (IdentCdrom(CDROM
,ident
) == false) 
 527    ioprintf(msg
, "[%s]\n",ident
.c_str()); 
 528    log
->Update(msg
.str()); 
 532    Configuration Database
; 
 533    string DFile 
= _config
->FindFile("Dir::State::cdroms"); 
 534    if (FileExists(DFile
) == true) 
 536       if (ReadConfigFile(Database
,DFile
) == false) 
 537          return _error
->Error("Unable to read the cdrom database %s", 
 542       ioprintf(msg
, _("Stored label: %s \n"), 
 543                Database
.Find("CD::"+ident
).c_str()); 
 544       log
->Update(msg
.str()); 
 550 bool pkgCdrom::Add(pkgCdromStatus 
*log
) 
 555    string CDROM 
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/"); 
 557       CDROM
= SafeGetCWD() + '/' + CDROM
; 
 560       log
->SetTotal(STEP_LAST
); 
 562       ioprintf(msg
, _("Using CD-ROM mount point %s\n"), CDROM
.c_str()); 
 563       log
->Update(msg
.str(), STEP_PREPARE
); 
 567    Configuration Database
; 
 568    string DFile 
= _config
->FindFile("Dir::State::cdroms"); 
 569    if (FileExists(DFile
) == true) 
 571       if (ReadConfigFile(Database
,DFile
) == false)  
 572          return _error
->Error("Unable to read the cdrom database %s", 
 576    // Unmount the CD and get the user to put in the one they want 
 577    if (_config
->FindB("APT::CDROM::NoMount",false) == false) 
 580          log
->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT
); 
 584          log
->Update(_("Waiting for disc...\n"), STEP_WAIT
); 
 585          if(!log
->ChangeCdrom()) { 
 591       // Mount the new CDROM 
 592       log
->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT
); 
 593       if (MountCdrom(CDROM
) == false) 
 594          return _error
->Error("Failed to mount the cdrom."); 
 597    // Hash the CD to get an ID 
 599       log
->Update(_("Identifying.. "), STEP_IDENT
); 
 601    if (IdentCdrom(CDROM
,ID
) == false) 
 607       log
->Update("["+ID
+"]\n"); 
 610       log
->Update(_("Scanning disc for index files..\n"),STEP_SCAN
); 
 612    // Get the CD structure 
 614    vector
<string
> SourceList
; 
 615    vector
<string
> SigList
; 
 616    string StartDir 
= SafeGetCWD(); 
 618    if (FindPackages(CDROM
,List
,SourceList
, SigList
,InfoDir
,log
) == false) 
 624    chdir(StartDir
.c_str()); 
 626    if (_config
->FindB("Debug::aptcdrom",false) == true) 
 628       cout 
<< "I found (binary):" << endl
; 
 629       for (vector
<string
>::iterator I 
= List
.begin(); I 
!= List
.end(); I
++) 
 631       cout 
<< "I found (source):" << endl
; 
 632       for (vector
<string
>::iterator I 
= SourceList
.begin(); I 
!= SourceList
.end(); I
++) 
 634       cout 
<< "I found (Signatures):" << endl
; 
 635       for (vector
<string
>::iterator I 
= SigList
.begin(); I 
!= SigList
.end(); I
++) 
 639    //log->Update(_("Cleaning package lists..."), STEP_CLEAN); 
 642    DropBinaryArch(List
); 
 643    DropRepeats(List
,"Packages"); 
 644    DropRepeats(SourceList
,"Sources"); 
 645    DropRepeats(SigList
,"Release.gpg"); 
 648       ioprintf(msg
, _("Found %i package indexes, %i source indexes and " 
 650                List
.size(), SourceList
.size(), SigList
.size()); 
 651       log
->Update(msg
.str(), STEP_SCAN
); 
 654    if (List
.size() == 0 && SourceList
.size() == 0)  
 656       if (_config
->FindB("APT::CDROM::NoMount",false) == false)  
 658       return _error
->Error("Unable to locate any package files, perhaps this is not a Debian Disc"); 
 661    // Check if the CD is in the database 
 663    if (Database
.Exists("CD::" + ID
) == false || 
 664        _config
->FindB("APT::CDROM::Rename",false) == true) 
 666       // Try to use the CDs label if at all possible 
 667       if (InfoDir
.empty() == false && 
 668           FileExists(InfoDir 
+ "/info") == true) 
 670          ifstream 
F(string(InfoDir 
+ "/info").c_str()); 
 674          if (Name
.empty() == false) 
 676             // Escape special characters 
 677             string::iterator J 
= Name
.begin(); 
 678             for (; J 
!= Name
.end(); J
++) 
 679                if (*J 
== '"' || *J 
== ']' || *J 
== '[') 
 684                ioprintf(msg
, _("Found label '%s'\n"), Name
.c_str()); 
 685                log
->Update(msg
.str()); 
 687             Database
.Set("CD::" + ID 
+ "::Label",Name
); 
 691       if (_config
->FindB("APT::CDROM::Rename",false) == true || 
 692           Name
.empty() == true) 
 696             if (_config
->FindB("APT::CDROM::NoMount",false) == false)  
 698             return _error
->Error("No disc name found and no way to ask for it"); 
 702             if(!log
->AskCdromName(Name
)) { 
 706             cout 
<< "Name: '" << Name 
<< "'" << endl
; 
 708             if (Name
.empty() == false && 
 709                 Name
.find('"') == string::npos 
&& 
 710                 Name
.find('[') == string::npos 
&& 
 711                 Name
.find(']') == string::npos
) 
 713             log
->Update(_("That is not a valid name, try again.\n")); 
 718       Name 
= Database
.Find("CD::" + ID
); 
 720    // Escape special characters 
 721    string::iterator J 
= Name
.begin(); 
 722    for (; J 
!= Name
.end(); J
++) 
 723       if (*J 
== '"' || *J 
== ']' || *J 
== '[') 
 726    Database
.Set("CD::" + ID
,Name
); 
 729       ioprintf(msg
, _("This disc is called: \n'%s'\n"), Name
.c_str()); 
 730       log
->Update(msg
.str()); 
 733    log
->Update(_("Copying package lists..."), STEP_COPY
); 
 734    // take care of the signatures and copy them if they are ok 
 735    // (we do this before PackageCopy as it modifies "List" and "SourceList") 
 736    SigVerify SignVerify
; 
 737    SignVerify
.CopyAndVerify(CDROM
, Name
, SigList
, List
, SourceList
); 
 739    // Copy the package files to the state directory 
 742    if (Copy
.CopyPackages(CDROM
,Name
,List
, log
) == false || 
 743        SrcCopy
.CopyPackages(CDROM
,Name
,SourceList
, log
) == false) 
 746    // reduce the List so that it takes less space in sources.list 
 747    ReduceSourcelist(CDROM
,List
); 
 748    ReduceSourcelist(CDROM
,SourceList
); 
 750    // Write the database and sourcelist 
 751    if (_config
->FindB("APT::cdrom::NoAct",false) == false) 
 753       if (WriteDatabase(Database
) == false) 
 757          log
->Update(_("Writing new source list\n"), STEP_WRITE
); 
 759       if (WriteSourceList(Name
,List
,false) == false || 
 760           WriteSourceList(Name
,SourceList
,true) == false) 
 764    // Print the sourcelist entries 
 766       log
->Update(_("Source list entries for this disc are:\n")); 
 768    for (vector
<string
>::iterator I 
= List
.begin(); I 
!= List
.end(); I
++) 
 770       string::size_type Space 
= (*I
).find(' '); 
 771       if (Space 
== string::npos
) 
 773          if (_config
->FindB("APT::CDROM::NoMount",false) == false)  
 775          return _error
->Error("Internal error"); 
 780          msg 
<< "deb cdrom:[" << Name 
<< "]/" << string(*I
,0,Space
) <<  
 781             " " << string(*I
,Space
+1) << endl
; 
 782          log
->Update(msg
.str()); 
 786    for (vector
<string
>::iterator I 
= SourceList
.begin(); I 
!= SourceList
.end(); I
++) 
 788       string::size_type Space 
= (*I
).find(' '); 
 789       if (Space 
== string::npos
) 
 791          if (_config
->FindB("APT::CDROM::NoMount",false) == false)  
 793          return _error
->Error("Internal error"); 
 798          msg 
<< "deb-src cdrom:[" << Name 
<< "]/" << string(*I
,0,Space
) <<  
 799             " " << string(*I
,Space
+1) << endl
; 
 800          log
->Update(msg
.str()); 
 806    // Unmount and finish 
 807    if (_config
->FindB("APT::CDROM::NoMount",false) == false) { 
 808       log
->Update(_("Unmounting CD-ROM...\n"), STEP_LAST
);