]>
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 /*{{{*/
18 #include <apt-pkg/dpkgdb.h>
19 #include <apt-pkg/configuration.h>
20 #include <apt-pkg/error.h>
21 #include <apt-pkg/progress.h>
22 #include <apt-pkg/tagfile.h>
23 #include <apt-pkg/strutl.h>
37 // EraseDir - Erase A Directory /*{{{*/
38 // ---------------------------------------------------------------------
39 /* This is necessary to create a new empty sub directory. The caller should
40 invoke mkdir after this with the proper permissions and check for
41 error. Maybe stick this in fileutils */
42 static bool EraseDir(const char *Dir
)
44 // First we try a simple RM
45 if (rmdir(Dir
) == 0 ||
49 // A file? Easy enough..
53 return _error
->Errno("unlink",_("Failed to remove %s"),Dir
);
58 if (errno
!= ENOTEMPTY
)
59 return _error
->Errno("rmdir",_("Failed to remove %s"),Dir
);
62 pid_t Pid
= ExecFork();
64 // Spawn the subprocess
67 execlp(_config
->Find("Dir::Bin::rm","/bin/rm").c_str(),
68 "rm","-rf","--",Dir
,(char *)NULL
);
71 return ExecWait(Pid
,_config
->Find("dir::bin::rm","/bin/rm").c_str());
74 // DpkgDB::debDpkgDB - Constructor /*{{{*/
75 // ---------------------------------------------------------------------
77 debDpkgDB::debDpkgDB() : CacheMap(0), FileMap(0)
79 AdminDir
= flNotFile(_config
->Find("Dir::State::status"));
84 // DpkgDB::~debDpkgDB - Destructor /*{{{*/
85 // ---------------------------------------------------------------------
87 debDpkgDB::~debDpkgDB()
100 // DpkgDB::InitMetaTmp - Get the temp dir for meta information /*{{{*/
101 // ---------------------------------------------------------------------
102 /* This creats+empties the meta temporary directory /var/lib/dpkg/tmp.ci
103 Only one package at a time can be using the returned meta directory. */
104 bool debDpkgDB::InitMetaTmp(string
&Dir
)
106 string Tmp
= AdminDir
+ "tmp.ci/";
107 if (EraseDir(Tmp
.c_str()) == false)
108 return _error
->Error(_("Unable to create %s"),Tmp
.c_str());
109 if (mkdir(Tmp
.c_str(),0755) != 0)
110 return _error
->Errno("mkdir",_("Unable to create %s"),Tmp
.c_str());
112 // Verify it is on the same filesystem as the main info directory
115 if (stat((AdminDir
+ "info").c_str(),&St
) != 0)
116 return _error
->Errno("stat",_("Failed to stat %sinfo"),AdminDir
.c_str());
118 if (stat(Tmp
.c_str(),&St
) != 0)
119 return _error
->Errno("stat",_("Failed to stat %s"),Tmp
.c_str());
120 if (Dev
!= St
.st_dev
)
121 return _error
->Error(_("The info and temp directories need to be on the same filesystem"));
128 // DpkgDB::ReadyPkgCache - Prepare the cache with the current status /*{{{*/
129 // ---------------------------------------------------------------------
130 /* This reads in the status file into an empty cache. This really needs
131 to be somehow unified with the high level APT notion of the Database
132 directory, but there is no clear way on how to do that yet. */
133 bool debDpkgDB::ReadyPkgCache(OpProgress
&Progress
)
137 Progress
.OverallProgress(1,1,1,_("Reading package lists"));
147 if (pkgCacheGenerator::MakeOnlyStatusCache(&Progress
,&CacheMap
) == false)
149 Cache
->DropProgress();
154 // DpkgDB::ReadFList - Read the File Listings in /*{{{*/
155 // ---------------------------------------------------------------------
156 /* This reads the file listing in from the state directory. This is a
157 performance critical routine, as it needs to parse about 50k lines of
158 text spread over a hundred or more files. For an initial cold start
159 most of the time is spent in reading file inodes and so on, not
161 bool debDpkgDB::ReadFList(OpProgress
&Progress
)
163 // Count the number of packages we need to read information for
164 unsigned long Total
= 0;
165 pkgCache
&Cache
= this->Cache
->GetCache();
166 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; I
++)
168 // Only not installed packages have no files.
169 if (I
->CurrentState
== pkgCache::State::NotInstalled
)
174 /* Switch into the admin dir, this prevents useless lookups for the
176 string Cwd
= SafeGetCWD();
177 if (chdir((AdminDir
+ "info/").c_str()) != 0)
178 return _error
->Errno("chdir",_("Failed to change to the admin dir %sinfo"),AdminDir
.c_str());
180 // Allocate a buffer. Anything larger than this buffer will be mmaped
181 unsigned long BufSize
= 32*1024;
182 char *Buffer
= new char[BufSize
];
184 // Begin Loading them
185 unsigned long Count
= 0;
187 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; I
++)
189 /* Only not installed packages have no files. ConfFile packages have
190 file lists but we don't want to read them in */
191 if (I
->CurrentState
== pkgCache::State::NotInstalled
||
192 I
->CurrentState
== pkgCache::State::ConfigFiles
)
195 // Fetch a package handle to associate with the file
196 pkgFLCache::PkgIterator FlPkg
= FList
->GetPkg(I
.Name(),0,true);
197 if (FlPkg
.end() == true)
199 _error
->Error(_("Internal error getting a package name"));
203 Progress
.OverallProgress(Count
,Total
,1,_("Reading file listing"));
205 // Open the list file
206 snprintf(Name
,sizeof(Name
),"%s.list",I
.Name());
207 int Fd
= open(Name
,O_RDONLY
);
209 /* Okay this is very strange and bad.. Best thing is to bail and
210 instruct the user to look into it. */
212 if (Fd
== -1 || fstat(Fd
,&Stat
) != 0)
214 _error
->Errno("open",_("Failed to open the list file '%sinfo/%s'. If you "
215 "cannot restore this file then make it empty "
216 "and immediately re-install the same version of the package!"),
217 AdminDir
.c_str(),Name
);
221 // Set File to be a memory buffer containing the whole file
223 if ((unsigned)Stat
.st_size
< BufSize
)
225 if (read(Fd
,Buffer
,Stat
.st_size
) != Stat
.st_size
)
227 _error
->Errno("read",_("Failed reading the list file %sinfo/%s"),
228 AdminDir
.c_str(),Name
);
237 File
= (char *)mmap(0,Stat
.st_size
,PROT_READ
,MAP_PRIVATE
,Fd
,0);
238 if (File
== (char *)(-1))
240 _error
->Errno("mmap",_("Failed reading the list file %sinfo/%s"),
241 AdminDir
.c_str(),Name
);
248 const char *Start
= File
;
249 const char *End
= File
;
250 const char *Finish
= File
+ Stat
.st_size
;
251 for (; End
< Finish
; End
++)
253 // Not an end of line
254 if (*End
!= '\n' && End
+ 1 < Finish
)
260 pkgFLCache::NodeIterator Node
= FList
->GetNode(Start
,End
,
261 FlPkg
.Offset(),true,false);
262 if (Node
.end() == true)
264 _error
->Error(_("Internal error getting a node"));
269 // Skip past the end of line
270 for (; *End
== '\n' && End
< Finish
; End
++);
275 if ((unsigned)Stat
.st_size
>= BufSize
)
276 munmap((caddr_t
)File
,Stat
.st_size
);
286 if (chdir(Cwd
.c_str()) != 0)
289 return !_error
->PendingError();
292 // DpkgDB::ReadDiversions - Load the diversions file /*{{{*/
293 // ---------------------------------------------------------------------
294 /* Read the diversion file in from disk. This is usually invoked by
295 LoadChanges before performing an operation that uses the FLCache. */
296 bool debDpkgDB::ReadDiversions()
299 if (stat((AdminDir
+ "diversions").c_str(),&Stat
) != 0)
302 if (_error
->PendingError() == true)
305 FILE *Fd
= fopen((AdminDir
+ "diversions").c_str(),"r");
307 return _error
->Errno("fopen",_("Failed to open the diversions file %sdiversions"),AdminDir
.c_str());
309 FList
->BeginDiverLoad();
316 // Read the three lines in
317 if (fgets(From
,sizeof(From
),Fd
) == 0)
319 if (fgets(To
,sizeof(To
),Fd
) == 0 ||
320 fgets(Package
,sizeof(Package
),Fd
) == 0)
322 _error
->Error(_("The diversion file is corrupted"));
327 unsigned long Len
= strlen(From
);
328 if (Len
< 2 || From
[Len
-1] != '\n')
329 _error
->Error(_("Invalid line in the diversion file: %s"),From
);
333 if (Len
< 2 || To
[Len
-1] != '\n')
334 _error
->Error(_("Invalid line in the diversion file: %s"),To
);
337 Len
= strlen(Package
);
338 if (Len
< 2 || Package
[Len
-1] != '\n')
339 _error
->Error(_("Invalid line in the diversion file: %s"),Package
);
343 // Make sure the lines were parsed OK
344 if (_error
->PendingError() == true)
348 if (strcmp(Package
,":") == 0)
350 pkgFLCache::PkgIterator FlPkg
= FList
->GetPkg(Package
,0,true);
351 if (FlPkg
.end() == true)
353 _error
->Error(_("Internal error getting a package name"));
357 // Install the diversion
358 if (FList
->AddDiversion(FlPkg
,From
,To
) == false)
360 _error
->Error(_("Internal error adding a diversion"));
364 if (_error
->PendingError() == false)
365 FList
->FinishDiverLoad();
367 DiverInode
= Stat
.st_ino
;
368 DiverTime
= Stat
.st_mtime
;
371 return !_error
->PendingError();
374 // DpkgDB::ReadFileList - Read the file listing /*{{{*/
375 // ---------------------------------------------------------------------
376 /* Read in the file listing. The file listing is created from three
377 sources, *.list, Conffile sections and the Diversion table. */
378 bool debDpkgDB::ReadyFileList(OpProgress
&Progress
)
381 return _error
->Error(_("The pkg cache must be initialized first"));
384 Progress
.OverallProgress(1,1,1,_("Reading file listing"));
388 // Create the cache and read in the file listing
389 FileMap
= new DynamicMMap(MMap::Public
);
390 FList
= new pkgFLCache(*FileMap
);
391 if (_error
->PendingError() == true ||
392 ReadFList(Progress
) == false ||
393 ReadConfFiles() == false ||
394 ReadDiversions() == false)
403 cout
<< "Node: " << FList
->HeaderP
->NodeCount
<< ',' << FList
->HeaderP
->UniqNodes
<< endl
;
404 cout
<< "Dir: " << FList
->HeaderP
->DirCount
<< endl
;
405 cout
<< "Package: " << FList
->HeaderP
->PackageCount
<< endl
;
406 cout
<< "HashSize: " << FList
->HeaderP
->HashSize
<< endl
;
407 cout
<< "Size: " << FileMap
->Size() << endl
;
413 // DpkgDB::ReadConfFiles - Read the conf file sections from the s-file /*{{{*/
414 // ---------------------------------------------------------------------
415 /* Reading the conf files is done by reparsing the status file. This is
416 actually rather fast so it is no big deal. */
417 bool debDpkgDB::ReadConfFiles()
419 FileFd
File(_config
->FindFile("Dir::State::status"),FileFd::ReadOnly
);
420 pkgTagFile
Tags(&File
);
421 if (_error
->PendingError() == true)
424 pkgTagSection Section
;
427 // Skip to the next section
428 unsigned long Offset
= Tags
.Offset();
429 if (Tags
.Step(Section
) == false)
435 if (Section
.Find("Conffiles",Start
,Stop
) == false)
438 const char *PkgStart
;
440 if (Section
.Find("Package",PkgStart
,PkgEnd
) == false)
441 return _error
->Error(_("Failed to find a Package: header, offset %lu"),Offset
);
443 // Snag a package record for it
444 pkgFLCache::PkgIterator FlPkg
= FList
->GetPkg(PkgStart
,PkgEnd
,true);
445 if (FlPkg
.end() == true)
446 return _error
->Error(_("Internal error getting a package name"));
448 // Parse the conf file lines
451 for (; isspace(*Start
) != 0 && Start
< Stop
; Start
++);
455 // Split it into words
456 const char *End
= Start
;
457 for (; isspace(*End
) == 0 && End
< Stop
; End
++);
458 const char *StartMd5
= End
;
459 for (; isspace(*StartMd5
) != 0 && StartMd5
< Stop
; StartMd5
++);
460 const char *EndMd5
= StartMd5
;
461 for (; isspace(*EndMd5
) == 0 && EndMd5
< Stop
; EndMd5
++);
462 if (StartMd5
== EndMd5
|| Start
== End
)
463 return _error
->Error(_("Bad ConfFile section in the status file. Offset %lu"),Offset
);
465 // Insert a new entry
466 unsigned char MD5
[16];
467 if (Hex2Num(string(StartMd5
,EndMd5
-StartMd5
),MD5
,16) == false)
468 return _error
->Error(_("Error parsing MD5. Offset %lu"),Offset
);
470 if (FList
->AddConfFile(Start
,End
,FlPkg
,MD5
) == false)
479 // DpkgDB::LoadChanges - Read in any changed state files /*{{{*/
480 // ---------------------------------------------------------------------
481 /* The only file in the dpkg system that can change while packages are
482 unpacking is the diversions file. */
483 bool debDpkgDB::LoadChanges()
486 if (stat((AdminDir
+ "diversions").c_str(),&Stat
) != 0)
488 if (DiverInode
== Stat
.st_ino
&& DiverTime
== Stat
.st_mtime
)
490 return ReadDiversions();