]>
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 (pkgCacheGenerator::MakeOnlyStatusCache(&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();