]>
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>
24 #include <apt-pkg/fileutl.h>
25 #include <apt-pkg/filelist.h>
39 // EraseDir - Erase A Directory /*{{{*/
40 // ---------------------------------------------------------------------
41 /* This is necessary to create a new empty sub directory. The caller should
42 invoke mkdir after this with the proper permissions and check for
43 error. Maybe stick this in fileutils */
44 static bool EraseDir(const char *Dir
)
46 // First we try a simple RM
47 if (rmdir(Dir
) == 0 ||
51 // A file? Easy enough..
55 return _error
->Errno("unlink",_("Failed to remove %s"),Dir
);
60 if (errno
!= ENOTEMPTY
)
61 return _error
->Errno("rmdir",_("Failed to remove %s"),Dir
);
64 pid_t Pid
= ExecFork();
66 // Spawn the subprocess
69 execlp(_config
->Find("Dir::Bin::rm","/bin/rm").c_str(),
70 "rm","-rf","--",Dir
,(char *)NULL
);
73 return ExecWait(Pid
,_config
->Find("dir::bin::rm","/bin/rm").c_str());
76 // DpkgDB::debDpkgDB - Constructor /*{{{*/
77 // ---------------------------------------------------------------------
79 debDpkgDB::debDpkgDB() : CacheMap(0), FileMap(0)
81 AdminDir
= flNotFile(_config
->Find("Dir::State::status"));
86 // DpkgDB::~debDpkgDB - Destructor /*{{{*/
87 // ---------------------------------------------------------------------
89 debDpkgDB::~debDpkgDB()
102 // DpkgDB::InitMetaTmp - Get the temp dir for meta information /*{{{*/
103 // ---------------------------------------------------------------------
104 /* This creats+empties the meta temporary directory /var/lib/dpkg/tmp.ci
105 Only one package at a time can be using the returned meta directory. */
106 bool debDpkgDB::InitMetaTmp(string
&Dir
)
108 string Tmp
= AdminDir
+ "tmp.ci/";
109 if (EraseDir(Tmp
.c_str()) == false)
110 return _error
->Error(_("Unable to create %s"),Tmp
.c_str());
111 if (mkdir(Tmp
.c_str(),0755) != 0)
112 return _error
->Errno("mkdir",_("Unable to create %s"),Tmp
.c_str());
114 // Verify it is on the same filesystem as the main info directory
117 if (stat((AdminDir
+ "info").c_str(),&St
) != 0)
118 return _error
->Errno("stat",_("Failed to stat %sinfo"),AdminDir
.c_str());
120 if (stat(Tmp
.c_str(),&St
) != 0)
121 return _error
->Errno("stat",_("Failed to stat %s"),Tmp
.c_str());
122 if (Dev
!= St
.st_dev
)
123 return _error
->Error(_("The info and temp directories need to be on the same filesystem"));
130 // DpkgDB::ReadyPkgCache - Prepare the cache with the current status /*{{{*/
131 // ---------------------------------------------------------------------
132 /* This reads in the status file into an empty cache. This really needs
133 to be somehow unified with the high level APT notion of the Database
134 directory, but there is no clear way on how to do that yet. */
135 bool debDpkgDB::ReadyPkgCache(OpProgress
&Progress
)
139 Progress
.OverallProgress(1,1,1,_("Reading package lists"));
149 if (pkgCacheGenerator::MakeOnlyStatusCache(&Progress
,&CacheMap
) == false)
151 Cache
->DropProgress();
156 // DpkgDB::ReadFList - Read the File Listings in /*{{{*/
157 // ---------------------------------------------------------------------
158 /* This reads the file listing in from the state directory. This is a
159 performance critical routine, as it needs to parse about 50k lines of
160 text spread over a hundred or more files. For an initial cold start
161 most of the time is spent in reading file inodes and so on, not
163 bool debDpkgDB::ReadFList(OpProgress
&Progress
)
165 // Count the number of packages we need to read information for
166 unsigned long Total
= 0;
167 pkgCache
&Cache
= this->Cache
->GetCache();
168 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; I
++)
170 // Only not installed packages have no files.
171 if (I
->CurrentState
== pkgCache::State::NotInstalled
)
176 /* Switch into the admin dir, this prevents useless lookups for the
178 string Cwd
= SafeGetCWD();
179 if (chdir((AdminDir
+ "info/").c_str()) != 0)
180 return _error
->Errno("chdir",_("Failed to change to the admin dir %sinfo"),AdminDir
.c_str());
182 // Allocate a buffer. Anything larger than this buffer will be mmaped
183 unsigned long BufSize
= 32*1024;
184 char *Buffer
= new char[BufSize
];
186 // Begin Loading them
187 unsigned long Count
= 0;
189 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; I
++)
191 /* Only not installed packages have no files. ConfFile packages have
192 file lists but we don't want to read them in */
193 if (I
->CurrentState
== pkgCache::State::NotInstalled
||
194 I
->CurrentState
== pkgCache::State::ConfigFiles
)
197 // Fetch a package handle to associate with the file
198 pkgFLCache::PkgIterator FlPkg
= FList
->GetPkg(I
.Name(),0,true);
199 if (FlPkg
.end() == true)
201 _error
->Error(_("Internal error getting a package name"));
205 Progress
.OverallProgress(Count
,Total
,1,_("Reading file listing"));
207 // Open the list file
208 snprintf(Name
,sizeof(Name
),"%s.list",I
.Name());
209 int Fd
= open(Name
,O_RDONLY
);
211 /* Okay this is very strange and bad.. Best thing is to bail and
212 instruct the user to look into it. */
214 if (Fd
== -1 || fstat(Fd
,&Stat
) != 0)
216 _error
->Errno("open",_("Failed to open the list file '%sinfo/%s'. If you "
217 "cannot restore this file then make it empty "
218 "and immediately re-install the same version of the package!"),
219 AdminDir
.c_str(),Name
);
223 // Set File to be a memory buffer containing the whole file
225 if ((unsigned)Stat
.st_size
< BufSize
)
227 if (read(Fd
,Buffer
,Stat
.st_size
) != Stat
.st_size
)
229 _error
->Errno("read",_("Failed reading the list file %sinfo/%s"),
230 AdminDir
.c_str(),Name
);
239 File
= (char *)mmap(0,Stat
.st_size
,PROT_READ
,MAP_PRIVATE
,Fd
,0);
240 if (File
== (char *)(-1))
242 _error
->Errno("mmap",_("Failed reading the list file %sinfo/%s"),
243 AdminDir
.c_str(),Name
);
250 const char *Start
= File
;
251 const char *End
= File
;
252 const char *Finish
= File
+ Stat
.st_size
;
253 for (; End
< Finish
; End
++)
255 // Not an end of line
256 if (*End
!= '\n' && End
+ 1 < Finish
)
262 pkgFLCache::NodeIterator Node
= FList
->GetNode(Start
,End
,
263 FlPkg
.Offset(),true,false);
264 if (Node
.end() == true)
266 _error
->Error(_("Internal error getting a node"));
271 // Skip past the end of line
272 for (; *End
== '\n' && End
< Finish
; End
++);
277 if ((unsigned)Stat
.st_size
>= BufSize
)
278 munmap((caddr_t
)File
,Stat
.st_size
);
288 if (chdir(Cwd
.c_str()) != 0)
291 return !_error
->PendingError();
294 // DpkgDB::ReadDiversions - Load the diversions file /*{{{*/
295 // ---------------------------------------------------------------------
296 /* Read the diversion file in from disk. This is usually invoked by
297 LoadChanges before performing an operation that uses the FLCache. */
298 bool debDpkgDB::ReadDiversions()
301 if (stat((AdminDir
+ "diversions").c_str(),&Stat
) != 0)
304 if (_error
->PendingError() == true)
307 FILE *Fd
= fopen((AdminDir
+ "diversions").c_str(),"r");
309 return _error
->Errno("fopen",_("Failed to open the diversions file %sdiversions"),AdminDir
.c_str());
311 FList
->BeginDiverLoad();
318 // Read the three lines in
319 if (fgets(From
,sizeof(From
),Fd
) == 0)
321 if (fgets(To
,sizeof(To
),Fd
) == 0 ||
322 fgets(Package
,sizeof(Package
),Fd
) == 0)
324 _error
->Error(_("The diversion file is corrupted"));
329 unsigned long Len
= strlen(From
);
330 if (Len
< 2 || From
[Len
-1] != '\n')
331 _error
->Error(_("Invalid line in the diversion file: %s"),From
);
335 if (Len
< 2 || To
[Len
-1] != '\n')
336 _error
->Error(_("Invalid line in the diversion file: %s"),To
);
339 Len
= strlen(Package
);
340 if (Len
< 2 || Package
[Len
-1] != '\n')
341 _error
->Error(_("Invalid line in the diversion file: %s"),Package
);
345 // Make sure the lines were parsed OK
346 if (_error
->PendingError() == true)
350 if (strcmp(Package
,":") == 0)
352 pkgFLCache::PkgIterator FlPkg
= FList
->GetPkg(Package
,0,true);
353 if (FlPkg
.end() == true)
355 _error
->Error(_("Internal error getting a package name"));
359 // Install the diversion
360 if (FList
->AddDiversion(FlPkg
,From
,To
) == false)
362 _error
->Error(_("Internal error adding a diversion"));
366 if (_error
->PendingError() == false)
367 FList
->FinishDiverLoad();
369 DiverInode
= Stat
.st_ino
;
370 DiverTime
= Stat
.st_mtime
;
373 return !_error
->PendingError();
376 // DpkgDB::ReadFileList - Read the file listing /*{{{*/
377 // ---------------------------------------------------------------------
378 /* Read in the file listing. The file listing is created from three
379 sources, *.list, Conffile sections and the Diversion table. */
380 bool debDpkgDB::ReadyFileList(OpProgress
&Progress
)
383 return _error
->Error(_("The pkg cache must be initialized first"));
386 Progress
.OverallProgress(1,1,1,_("Reading file listing"));
390 // Create the cache and read in the file listing
391 FileMap
= new DynamicMMap(MMap::Public
);
392 FList
= new pkgFLCache(*FileMap
);
393 if (_error
->PendingError() == true ||
394 ReadFList(Progress
) == false ||
395 ReadConfFiles() == false ||
396 ReadDiversions() == false)
405 cout
<< "Node: " << FList
->HeaderP
->NodeCount
<< ',' << FList
->HeaderP
->UniqNodes
<< endl
;
406 cout
<< "Dir: " << FList
->HeaderP
->DirCount
<< endl
;
407 cout
<< "Package: " << FList
->HeaderP
->PackageCount
<< endl
;
408 cout
<< "HashSize: " << FList
->HeaderP
->HashSize
<< endl
;
409 cout
<< "Size: " << FileMap
->Size() << endl
;
415 // DpkgDB::ReadConfFiles - Read the conf file sections from the s-file /*{{{*/
416 // ---------------------------------------------------------------------
417 /* Reading the conf files is done by reparsing the status file. This is
418 actually rather fast so it is no big deal. */
419 bool debDpkgDB::ReadConfFiles()
421 FileFd
File(_config
->FindFile("Dir::State::status"),FileFd::ReadOnly
);
422 pkgTagFile
Tags(&File
);
423 if (_error
->PendingError() == true)
426 pkgTagSection Section
;
429 // Skip to the next section
430 unsigned long Offset
= Tags
.Offset();
431 if (Tags
.Step(Section
) == false)
437 if (Section
.Find("Conffiles",Start
,Stop
) == false)
440 const char *PkgStart
;
442 if (Section
.Find("Package",PkgStart
,PkgEnd
) == false)
443 return _error
->Error(_("Failed to find a Package: header, offset %lu"),Offset
);
445 // Snag a package record for it
446 pkgFLCache::PkgIterator FlPkg
= FList
->GetPkg(PkgStart
,PkgEnd
,true);
447 if (FlPkg
.end() == true)
448 return _error
->Error(_("Internal error getting a package name"));
450 // Parse the conf file lines
453 for (; isspace(*Start
) != 0 && Start
< Stop
; Start
++);
457 // Split it into words
458 const char *End
= Start
;
459 for (; isspace(*End
) == 0 && End
< Stop
; End
++);
460 const char *StartMd5
= End
;
461 for (; isspace(*StartMd5
) != 0 && StartMd5
< Stop
; StartMd5
++);
462 const char *EndMd5
= StartMd5
;
463 for (; isspace(*EndMd5
) == 0 && EndMd5
< Stop
; EndMd5
++);
464 if (StartMd5
== EndMd5
|| Start
== End
)
465 return _error
->Error(_("Bad ConfFile section in the status file. Offset %lu"),Offset
);
467 // Insert a new entry
468 unsigned char MD5
[16];
469 if (Hex2Num(string(StartMd5
,EndMd5
-StartMd5
),MD5
,16) == false)
470 return _error
->Error(_("Error parsing MD5. Offset %lu"),Offset
);
472 if (FList
->AddConfFile(Start
,End
,FlPkg
,MD5
) == false)
481 // DpkgDB::LoadChanges - Read in any changed state files /*{{{*/
482 // ---------------------------------------------------------------------
483 /* The only file in the dpkg system that can change while packages are
484 unpacking is the diversions file. */
485 bool debDpkgDB::LoadChanges()
488 if (stat((AdminDir
+ "diversions").c_str(),&Stat
) != 0)
490 if (DiverInode
== Stat
.st_ino
&& DiverTime
== Stat
.st_mtime
)
492 return ReadDiversions();