]>
git.saurik.com Git - apt.git/blob - apt-inst/deb/dpkgdb.cc
   1 // -*- mode: cpp; mode: fold -*- 
   3 // $Id: dpkgdb.cc,v 1.7.2.1 2004/01/16 18:58:50 mdz Exp $ 
   4 /* ###################################################################### 
   6    DPKGv1 Database Implemenation 
   8    This class provides parsers and other implementations for the DPKGv1 
   9    database. It reads the diversion file, the list files and the status 
  10    file to build both the list of currently installed files and the  
  11    currently installed package list. 
  13    ##################################################################### */ 
  15 // Include Files                                                        /*{{{*/ 
  16 #include <apt-pkg/dpkgdb.h> 
  17 #include <apt-pkg/configuration.h> 
  18 #include <apt-pkg/error.h> 
  19 #include <apt-pkg/progress.h> 
  20 #include <apt-pkg/tagfile.h> 
  21 #include <apt-pkg/strutl.h> 
  35 // EraseDir - Erase A Directory                                         /*{{{*/ 
  36 // --------------------------------------------------------------------- 
  37 /* This is necessary to create a new empty sub directory. The caller should 
  38    invoke mkdir after this with the proper permissions and check for  
  39    error. Maybe stick this in fileutils */ 
  40 static bool EraseDir(const char *Dir
) 
  42    // First we try a simple RM 
  43    if (rmdir(Dir
) == 0 || 
  47    // A file? Easy enough.. 
  51          return _error
->Errno("unlink",_("Failed to remove %s"),Dir
); 
  56    if (errno 
!= ENOTEMPTY
) 
  57       return _error
->Errno("rmdir",_("Failed to remove %s"),Dir
); 
  60    pid_t Pid 
= ExecFork(); 
  62    // Spawn the subprocess 
  65       execlp(_config
->Find("Dir::Bin::rm","/bin/rm").c_str(), 
  66              "rm","-rf","--",Dir
,(char *)NULL
); 
  69    return ExecWait(Pid
,_config
->Find("dir::bin::rm","/bin/rm").c_str()); 
  72 // DpkgDB::debDpkgDB - Constructor                                      /*{{{*/ 
  73 // --------------------------------------------------------------------- 
  75 debDpkgDB::debDpkgDB() : CacheMap(0), FileMap(0) 
  77    AdminDir 
= flNotFile(_config
->Find("Dir::State::status"));    
  82 // DpkgDB::~debDpkgDB - Destructor                                      /*{{{*/ 
  83 // --------------------------------------------------------------------- 
  85 debDpkgDB::~debDpkgDB() 
  98 // DpkgDB::InitMetaTmp - Get the temp dir for meta information          /*{{{*/ 
  99 // --------------------------------------------------------------------- 
 100 /* This creats+empties the meta temporary directory /var/lib/dpkg/tmp.ci 
 101    Only one package at a time can be using the returned meta directory. */ 
 102 bool debDpkgDB::InitMetaTmp(string 
&Dir
) 
 104    string Tmp 
= AdminDir 
+ "tmp.ci/"; 
 105    if (EraseDir(Tmp
.c_str()) == false) 
 106       return _error
->Error(_("Unable to create %s"),Tmp
.c_str()); 
 107    if (mkdir(Tmp
.c_str(),0755) != 0) 
 108       return _error
->Errno("mkdir",_("Unable to create %s"),Tmp
.c_str()); 
 110    // Verify it is on the same filesystem as the main info directory 
 113    if (stat((AdminDir 
+ "info").c_str(),&St
) != 0) 
 114       return _error
->Errno("stat",_("Failed to stat %sinfo"),AdminDir
.c_str()); 
 116    if (stat(Tmp
.c_str(),&St
) != 0) 
 117       return _error
->Errno("stat",_("Failed to stat %s"),Tmp
.c_str()); 
 118    if (Dev 
!= St
.st_dev
) 
 119       return _error
->Error(_("The info and temp directories need to be on the same filesystem")); 
 126 // DpkgDB::ReadyPkgCache - Prepare the cache with the current status    /*{{{*/ 
 127 // --------------------------------------------------------------------- 
 128 /* This reads in the status file into an empty cache. This really needs  
 129    to be somehow unified with the high level APT notion of the Database 
 130    directory, but there is no clear way on how to do that yet. */ 
 131 bool debDpkgDB::ReadyPkgCache(OpProgress 
&Progress
) 
 135       Progress
.OverallProgress(1,1,1,_("Reading package lists"));       
 145    if (pkgMakeOnlyStatusCache(Progress
,&CacheMap
) == false) 
 147    Cache
->DropProgress(); 
 152 // DpkgDB::ReadFList - Read the File Listings in                        /*{{{*/ 
 153 // --------------------------------------------------------------------- 
 154 /* This reads the file listing in from the state directory. This is a  
 155    performance critical routine, as it needs to parse about 50k lines of 
 156    text spread over a hundred or more files. For an initial cold start 
 157    most of the time is spent in reading file inodes and so on, not  
 159 bool debDpkgDB::ReadFList(OpProgress 
&Progress
) 
 161    // Count the number of packages we need to read information for 
 162    unsigned long Total 
= 0; 
 163    pkgCache 
&Cache 
= this->Cache
->GetCache(); 
 164    for (pkgCache::PkgIterator I 
= Cache
.PkgBegin(); I
.end() == false; I
++) 
 166       // Only not installed packages have no files. 
 167       if (I
->CurrentState 
== pkgCache::State::NotInstalled
) 
 172    /* Switch into the admin dir, this prevents useless lookups for the  
 174    string Cwd 
= SafeGetCWD(); 
 175    if (chdir((AdminDir 
+ "info/").c_str()) != 0) 
 176       return _error
->Errno("chdir",_("Failed to change to the admin dir %sinfo"),AdminDir
.c_str()); 
 178    // Allocate a buffer. Anything larger than this buffer will be mmaped 
 179    unsigned long BufSize 
= 32*1024; 
 180    char *Buffer 
= new char[BufSize
]; 
 182    // Begin Loading them 
 183    unsigned long Count 
= 0; 
 185    for (pkgCache::PkgIterator I 
= Cache
.PkgBegin(); I
.end() == false; I
++) 
 187       /* Only not installed packages have no files. ConfFile packages have 
 188          file lists but we don't want to read them in */ 
 189       if (I
->CurrentState 
== pkgCache::State::NotInstalled 
|| 
 190           I
->CurrentState 
== pkgCache::State::ConfigFiles
) 
 193       // Fetch a package handle to associate with the file 
 194       pkgFLCache::PkgIterator FlPkg 
= FList
->GetPkg(I
.Name(),0,true); 
 195       if (FlPkg
.end() == true) 
 197          _error
->Error(_("Internal error getting a package name")); 
 201       Progress
.OverallProgress(Count
,Total
,1,_("Reading file listing")); 
 203       // Open the list file 
 204       snprintf(Name
,sizeof(Name
),"%s.list",I
.Name()); 
 205       int Fd 
= open(Name
,O_RDONLY
); 
 207       /* Okay this is very strange and bad.. Best thing is to bail and 
 208          instruct the user to look into it. */ 
 210       if (Fd 
== -1 || fstat(Fd
,&Stat
) != 0) 
 212          _error
->Errno("open",_("Failed to open the list file '%sinfo/%s'. If you " 
 213                        "cannot restore this file then make it empty " 
 214                        "and immediately re-install the same version of the package!"), 
 215                        AdminDir
.c_str(),Name
); 
 219       // Set File to be a memory buffer containing the whole file 
 221       if ((unsigned)Stat
.st_size 
< BufSize
) 
 223          if (read(Fd
,Buffer
,Stat
.st_size
) != Stat
.st_size
) 
 225             _error
->Errno("read",_("Failed reading the list file %sinfo/%s"), 
 226                           AdminDir
.c_str(),Name
); 
 235          File 
= (char *)mmap(0,Stat
.st_size
,PROT_READ
,MAP_PRIVATE
,Fd
,0); 
 236          if (File 
== (char *)(-1)) 
 238             _error
->Errno("mmap",_("Failed reading the list file %sinfo/%s"), 
 239                           AdminDir
.c_str(),Name
); 
 246       const char *Start 
= File
; 
 247       const char *End 
= File
; 
 248       const char *Finish 
= File 
+ Stat
.st_size
; 
 249       for (; End 
< Finish
; End
++) 
 251          // Not an end of line 
 252          if (*End 
!= '\n' && End 
+ 1 < Finish
) 
 258             pkgFLCache::NodeIterator Node 
= FList
->GetNode(Start
,End
, 
 259                                               FlPkg
.Offset(),true,false); 
 260             if (Node
.end() == true) 
 262                _error
->Error(_("Internal error getting a node")); 
 267          // Skip past the end of line 
 268          for (; *End 
== '\n' && End 
< Finish
; End
++); 
 273       if ((unsigned)Stat
.st_size 
>= BufSize
) 
 274          munmap((caddr_t
)File
,Stat
.st_size
); 
 284    if (chdir(Cwd
.c_str()) != 0) 
 287    return !_error
->PendingError(); 
 290 // DpkgDB::ReadDiversions - Load the diversions file                    /*{{{*/ 
 291 // --------------------------------------------------------------------- 
 292 /* Read the diversion file in from disk. This is usually invoked by  
 293    LoadChanges before performing an operation that uses the FLCache. */ 
 294 bool debDpkgDB::ReadDiversions() 
 297    if (stat((AdminDir 
+ "diversions").c_str(),&Stat
) != 0) 
 300    if (_error
->PendingError() == true) 
 303    FILE *Fd 
= fopen((AdminDir 
+ "diversions").c_str(),"r"); 
 305       return _error
->Errno("fopen",_("Failed to open the diversions file %sdiversions"),AdminDir
.c_str()); 
 307    FList
->BeginDiverLoad(); 
 314       // Read the three lines in 
 315       if (fgets(From
,sizeof(From
),Fd
) == 0) 
 317       if (fgets(To
,sizeof(To
),Fd
) == 0 || 
 318           fgets(Package
,sizeof(Package
),Fd
) == 0) 
 320          _error
->Error(_("The diversion file is corrupted")); 
 325       unsigned long Len 
= strlen(From
); 
 326       if (Len 
< 2 || From
[Len
-1] != '\n') 
 327          _error
->Error(_("Invalid line in the diversion file: %s"),From
); 
 331       if (Len 
< 2 || To
[Len
-1] != '\n') 
 332          _error
->Error(_("Invalid line in the diversion file: %s"),To
); 
 335       Len 
= strlen(Package
); 
 336       if (Len 
< 2 || Package
[Len
-1] != '\n') 
 337          _error
->Error(_("Invalid line in the diversion file: %s"),Package
); 
 341       // Make sure the lines were parsed OK 
 342       if (_error
->PendingError() == true) 
 346       if (strcmp(Package
,":") == 0) 
 348       pkgFLCache::PkgIterator FlPkg 
= FList
->GetPkg(Package
,0,true); 
 349       if (FlPkg
.end() == true) 
 351          _error
->Error(_("Internal error getting a package name")); 
 355       // Install the diversion 
 356       if (FList
->AddDiversion(FlPkg
,From
,To
) == false) 
 358          _error
->Error(_("Internal error adding a diversion")); 
 362    if (_error
->PendingError() == false) 
 363       FList
->FinishDiverLoad(); 
 365    DiverInode 
= Stat
.st_ino
; 
 366    DiverTime 
= Stat
.st_mtime
; 
 369    return !_error
->PendingError(); 
 372 // DpkgDB::ReadFileList - Read the file listing                         /*{{{*/ 
 373 // --------------------------------------------------------------------- 
 374 /* Read in the file listing. The file listing is created from three 
 375    sources, *.list, Conffile sections and the Diversion table. */ 
 376 bool debDpkgDB::ReadyFileList(OpProgress 
&Progress
) 
 379       return _error
->Error(_("The pkg cache must be initialized first")); 
 382       Progress
.OverallProgress(1,1,1,_("Reading file listing")); 
 386    // Create the cache and read in the file listing 
 387    FileMap 
= new DynamicMMap(MMap::Public
); 
 388    FList 
= new pkgFLCache(*FileMap
); 
 389    if (_error
->PendingError() == true ||  
 390        ReadFList(Progress
) == false || 
 391        ReadConfFiles() == false ||  
 392        ReadDiversions() == false) 
 401    cout 
<< "Node: " << FList
->HeaderP
->NodeCount 
<< ',' << FList
->HeaderP
->UniqNodes 
<< endl
; 
 402    cout 
<< "Dir: " << FList
->HeaderP
->DirCount 
<< endl
; 
 403    cout 
<< "Package: " << FList
->HeaderP
->PackageCount 
<< endl
; 
 404    cout 
<< "HashSize: " << FList
->HeaderP
->HashSize 
<< endl
; 
 405    cout 
<< "Size: " << FileMap
->Size() << endl
; 
 411 // DpkgDB::ReadConfFiles - Read the conf file sections from the s-file  /*{{{*/ 
 412 // --------------------------------------------------------------------- 
 413 /* Reading the conf files is done by reparsing the status file. This is 
 414    actually rather fast so it is no big deal. */ 
 415 bool debDpkgDB::ReadConfFiles() 
 417    FileFd 
File(_config
->FindFile("Dir::State::status"),FileFd::ReadOnly
); 
 418    pkgTagFile 
Tags(&File
); 
 419    if (_error
->PendingError() == true) 
 422    pkgTagSection Section
;    
 425       // Skip to the next section 
 426       unsigned long Offset 
= Tags
.Offset(); 
 427       if (Tags
.Step(Section
) == false) 
 433       if (Section
.Find("Conffiles",Start
,Stop
) == false) 
 436       const char *PkgStart
; 
 438       if (Section
.Find("Package",PkgStart
,PkgEnd
) == false) 
 439          return _error
->Error(_("Failed to find a Package: header, offset %lu"),Offset
); 
 441       // Snag a package record for it 
 442       pkgFLCache::PkgIterator FlPkg 
= FList
->GetPkg(PkgStart
,PkgEnd
,true); 
 443       if (FlPkg
.end() == true) 
 444          return _error
->Error(_("Internal error getting a package name")); 
 446       // Parse the conf file lines 
 449          for (; isspace(*Start
) != 0 && Start 
< Stop
; Start
++); 
 453          // Split it into words 
 454          const char *End 
= Start
; 
 455          for (; isspace(*End
) == 0 && End 
< Stop
; End
++); 
 456          const char *StartMd5 
= End
; 
 457          for (; isspace(*StartMd5
) != 0 && StartMd5 
< Stop
; StartMd5
++); 
 458          const char *EndMd5 
= StartMd5
; 
 459          for (; isspace(*EndMd5
) == 0 && EndMd5 
< Stop
; EndMd5
++); 
 460          if (StartMd5 
== EndMd5 
|| Start 
== End
) 
 461             return _error
->Error(_("Bad ConfFile section in the status file. Offset %lu"),Offset
); 
 463          // Insert a new entry 
 464          unsigned char MD5
[16]; 
 465          if (Hex2Num(string(StartMd5
,EndMd5
-StartMd5
),MD5
,16) == false) 
 466             return _error
->Error(_("Error parsing MD5. Offset %lu"),Offset
); 
 468          if (FList
->AddConfFile(Start
,End
,FlPkg
,MD5
) == false) 
 477 // DpkgDB::LoadChanges - Read in any changed state files                /*{{{*/ 
 478 // --------------------------------------------------------------------- 
 479 /* The only file in the dpkg system that can change while packages are 
 480    unpacking is the diversions file. */ 
 481 bool debDpkgDB::LoadChanges() 
 484    if (stat((AdminDir 
+ "diversions").c_str(),&Stat
) != 0) 
 486    if (DiverInode 
== Stat
.st_ino 
&& DiverTime 
== Stat
.st_mtime
) 
 488    return ReadDiversions();