]> git.saurik.com Git - apt.git/blame - apt-inst/deb/dpkgdb.cc
Add INTLLIBS to various parts, so netbsd-i386 works.
[apt.git] / apt-inst / deb / dpkgdb.cc
CommitLineData
b2e465d6
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
584e4558 3// $Id: dpkgdb.cc,v 1.6 2002/11/11 06:55:50 doogie Exp $
b2e465d6
AL
4/* ######################################################################
5
6 DPKGv1 Database Implemenation
7
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.
12
13 ##################################################################### */
14 /*}}}*/
15// Include Files /*{{{*/
16#ifdef __GNUG__
17#pragma implementation "apt-pkg/dpkgdb.h"
18#endif
19
20#include <apt-pkg/dpkgdb.h>
21#include <apt-pkg/configuration.h>
22#include <apt-pkg/error.h>
23#include <apt-pkg/progress.h>
24#include <apt-pkg/tagfile.h>
25#include <apt-pkg/strutl.h>
26
27#include <stdio.h>
28#include <errno.h>
29#include <sys/stat.h>
30#include <sys/mman.h>
31#include <fcntl.h>
32#include <unistd.h>
18255546 33#include <ctype.h>
90f057fd 34#include <iostream>
b2e465d6 35 /*}}}*/
584e4558 36using namespace std;
b2e465d6
AL
37
38// EraseDir - Erase A Directory /*{{{*/
39// ---------------------------------------------------------------------
40/* This is necessary to create a new empty sub directory. The caller should
41 invoke mkdir after this with the proper permissions and check for
42 error. Maybe stick this in fileutils */
43static bool EraseDir(const char *Dir)
44{
45 // First we try a simple RM
46 if (rmdir(Dir) == 0 ||
47 errno == ENOENT)
48 return true;
49
50 // A file? Easy enough..
51 if (errno == ENOTDIR)
52 {
53 if (unlink(Dir) != 0)
54 return _error->Errno("unlink","Failed to remove %s",Dir);
55 return true;
56 }
57
58 // Should not happen
59 if (errno != ENOTEMPTY)
60 return _error->Errno("rmdir","Failed to remove %s",Dir);
61
62 // Purge it using rm
63 int Pid = ExecFork();
64
65 // Spawn the subprocess
66 if (Pid == 0)
67 {
68 execlp(_config->Find("Dir::Bin::rm","/bin/rm").c_str(),
69 "rm","-rf","--",Dir,0);
70 _exit(100);
71 }
72 return ExecWait(Pid,_config->Find("dir::bin::rm","/bin/rm").c_str());
73}
74 /*}}}*/
75// DpkgDB::debDpkgDB - Constructor /*{{{*/
76// ---------------------------------------------------------------------
77/* */
78debDpkgDB::debDpkgDB() : CacheMap(0), FileMap(0)
79{
80 AdminDir = flNotFile(_config->Find("Dir::State::status"));
81 DiverInode = 0;
82 DiverTime = 0;
83}
84 /*}}}*/
85// DpkgDB::~debDpkgDB - Destructor /*{{{*/
86// ---------------------------------------------------------------------
87/* */
88debDpkgDB::~debDpkgDB()
89{
90 delete Cache;
91 Cache = 0;
92 delete CacheMap;
93 CacheMap = 0;
94
95 delete FList;
96 FList = 0;
97 delete FileMap;
98 FileMap = 0;
99}
100 /*}}}*/
101// DpkgDB::InitMetaTmp - Get the temp dir for meta information /*{{{*/
102// ---------------------------------------------------------------------
103/* This creats+empties the meta temporary directory /var/lib/dpkg/tmp.ci
104 Only one package at a time can be using the returned meta directory. */
105bool debDpkgDB::InitMetaTmp(string &Dir)
106{
107 string Tmp = AdminDir + "tmp.ci/";
108 if (EraseDir(Tmp.c_str()) == false)
109 return _error->Error("Unable to create %s",Tmp.c_str());
110 if (mkdir(Tmp.c_str(),0755) != 0)
111 return _error->Errno("mkdir","Unable to create %s",Tmp.c_str());
112
113 // Verify it is on the same filesystem as the main info directory
114 dev_t Dev;
115 struct stat St;
116 if (stat((AdminDir + "info").c_str(),&St) != 0)
117 return _error->Errno("stat","Failed to stat %sinfo",AdminDir.c_str());
118 Dev = St.st_dev;
119 if (stat(Tmp.c_str(),&St) != 0)
120 return _error->Errno("stat","Failed to stat %s",Tmp.c_str());
121 if (Dev != St.st_dev)
122 return _error->Error("The info and temp directories need to be on the same filesystem");
123
124 // Done
125 Dir = Tmp;
126 return true;
127}
128 /*}}}*/
129// DpkgDB::ReadyPkgCache - Prepare the cache with the current status /*{{{*/
130// ---------------------------------------------------------------------
131/* This reads in the status file into an empty cache. This really needs
132 to be somehow unified with the high level APT notion of the Database
133 directory, but there is no clear way on how to do that yet. */
134bool debDpkgDB::ReadyPkgCache(OpProgress &Progress)
135{
136 if (Cache != 0)
137 {
138 Progress.OverallProgress(1,1,1,"Reading Package Lists");
139 return true;
140 }
141
142 if (CacheMap != 0)
143 {
144 delete CacheMap;
145 CacheMap = 0;
146 }
147
148 if (pkgMakeOnlyStatusCache(Progress,&CacheMap) == false)
149 return false;
150 Cache->DropProgress();
151
152 return true;
153}
154 /*}}}*/
155// DpkgDB::ReadFList - Read the File Listings in /*{{{*/
156// ---------------------------------------------------------------------
157/* This reads the file listing in from the state directory. This is a
158 performance critical routine, as it needs to parse about 50k lines of
159 text spread over a hundred or more files. For an initial cold start
160 most of the time is spent in reading file inodes and so on, not
161 actually parsing. */
162bool debDpkgDB::ReadFList(OpProgress &Progress)
163{
164 // Count the number of packages we need to read information for
165 unsigned long Total = 0;
166 pkgCache &Cache = this->Cache->GetCache();
167 for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
168 {
169 // Only not installed packages have no files.
170 if (I->CurrentState == pkgCache::State::NotInstalled)
171 continue;
172 Total++;
173 }
174
175 /* Switch into the admin dir, this prevents useless lookups for the
176 path components */
177 string Cwd = SafeGetCWD();
178 if (chdir((AdminDir + "info/").c_str()) != 0)
179 return _error->Errno("chdir","Failed to change to the admin dir %sinfo",AdminDir.c_str());
180
181 // Allocate a buffer. Anything larger than this buffer will be mmaped
182 unsigned long BufSize = 32*1024;
183 char *Buffer = new char[BufSize];
184
185 // Begin Loading them
186 unsigned long Count = 0;
187 char Name[300];
188 for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
189 {
190 /* Only not installed packages have no files. ConfFile packages have
191 file lists but we don't want to read them in */
192 if (I->CurrentState == pkgCache::State::NotInstalled ||
193 I->CurrentState == pkgCache::State::ConfigFiles)
194 continue;
195
196 // Fetch a package handle to associate with the file
197 pkgFLCache::PkgIterator FlPkg = FList->GetPkg(I.Name(),0,true);
198 if (FlPkg.end() == true)
199 {
200 _error->Error("Internal Error getting a Package Name");
201 break;
202 }
203
204 Progress.OverallProgress(Count,Total,1,"Reading File Listing");
205
206 // Open the list file
207 snprintf(Name,sizeof(Name),"%s.list",I.Name());
208 int Fd = open(Name,O_RDONLY);
209
210 /* Okay this is very strange and bad.. Best thing is to bail and
211 instruct the user to look into it. */
212 struct stat Stat;
213 if (Fd == -1 || fstat(Fd,&Stat) != 0)
214 {
215 _error->Errno("open","Failed to open the list file '%sinfo/%s'. If you "
216 "cannot restore this file then make it empty "
217 "and immediately re-install the same version of the package!",
218 AdminDir.c_str(),Name);
219 break;
220 }
221
222 // Set File to be a memory buffer containing the whole file
223 char *File;
224 if ((unsigned)Stat.st_size < BufSize)
225 {
226 if (read(Fd,Buffer,Stat.st_size) != Stat.st_size)
227 {
228 _error->Errno("read","Failed reading the list file %sinfo/%s",
229 AdminDir.c_str(),Name);
230 close(Fd);
231 break;
232 }
233 File = Buffer;
234 }
235 else
236 {
237 // Use mmap
238 File = (char *)mmap(0,Stat.st_size,PROT_READ,MAP_PRIVATE,Fd,0);
239 if (File == (char *)(-1))
240 {
241 _error->Errno("mmap","Failed reading the list file %sinfo/%s",
242 AdminDir.c_str(),Name);
243 close(Fd);
244 break;
245 }
246 }
247
248 // Parse it
249 const char *Start = File;
250 const char *End = File;
251 const char *Finish = File + Stat.st_size;
252 for (; End < Finish; End++)
253 {
254 // Not an end of line
255 if (*End != '\n' && End + 1 < Finish)
256 continue;
257
258 // Skip blank lines
259 if (End - Start > 1)
260 {
261 pkgFLCache::NodeIterator Node = FList->GetNode(Start,End,
262 FlPkg.Offset(),true,false);
263 if (Node.end() == true)
264 {
265 _error->Error("Internal Error getting a Node");
266 break;
267 }
268 }
269
270 // Skip past the end of line
271 for (; *End == '\n' && End < Finish; End++);
272 Start = End;
273 }
274
275 close(Fd);
276 if ((unsigned)Stat.st_size >= BufSize)
277 munmap((caddr_t)File,Stat.st_size);
278
279 // Failed
280 if (End < Finish)
281 break;
282
283 Count++;
284 }
285
286 delete [] Buffer;
287 if (chdir(Cwd.c_str()) != 0)
288 chdir("/");
289
290 return !_error->PendingError();
291}
292 /*}}}*/
293// DpkgDB::ReadDiversions - Load the diversions file /*{{{*/
294// ---------------------------------------------------------------------
295/* Read the diversion file in from disk. This is usually invoked by
296 LoadChanges before performing an operation that uses the FLCache. */
297bool debDpkgDB::ReadDiversions()
298{
299 struct stat Stat;
300 if (stat((AdminDir + "diversions").c_str(),&Stat) != 0)
301 return true;
302
303 if (_error->PendingError() == true)
304 return false;
305
306 FILE *Fd = fopen((AdminDir + "diversions").c_str(),"r");
307 if (Fd == 0)
308 return _error->Errno("fopen","Failed to open the diversions file %sdiversions",AdminDir.c_str());
309
310 FList->BeginDiverLoad();
311 while (1)
312 {
313 char From[300];
314 char To[300];
315 char Package[100];
316
317 // Read the three lines in
318 if (fgets(From,sizeof(From),Fd) == 0)
319 break;
320 if (fgets(To,sizeof(To),Fd) == 0 ||
321 fgets(Package,sizeof(Package),Fd) == 0)
322 {
323 _error->Error("The diversion file is corrupted");
324 break;
325 }
326
327 // Strip the \ns
328 unsigned long Len = strlen(From);
329 if (Len < 2 || From[Len-1] != '\n')
330 _error->Error("Invalid line in the diversion file: %s",From);
331 else
332 From[Len-1] = 0;
333 Len = strlen(To);
334 if (Len < 2 || To[Len-1] != '\n')
335 _error->Error("Invalid line in the diversion file: %s",To);
336 else
337 To[Len-1] = 0;
338 Len = strlen(Package);
339 if (Len < 2 || Package[Len-1] != '\n')
340 _error->Error("Invalid line in the diversion file: %s",Package);
341 else
342 Package[Len-1] = 0;
343
344 // Make sure the lines were parsed OK
345 if (_error->PendingError() == true)
346 break;
347
348 // Fetch a package
349 if (strcmp(Package,":") == 0)
350 Package[0] = 0;
351 pkgFLCache::PkgIterator FlPkg = FList->GetPkg(Package,0,true);
352 if (FlPkg.end() == true)
353 {
354 _error->Error("Internal Error getting a Package Name");
355 break;
356 }
357
358 // Install the diversion
359 if (FList->AddDiversion(FlPkg,From,To) == false)
360 {
361 _error->Error("Internal Error adding a diversion");
362 break;
363 }
364 }
365 if (_error->PendingError() == false)
366 FList->FinishDiverLoad();
367
368 DiverInode = Stat.st_ino;
369 DiverTime = Stat.st_mtime;
370
371 fclose(Fd);
372 return !_error->PendingError();
373}
374 /*}}}*/
375// DpkgDB::ReadFileList - Read the file listing /*{{{*/
376// ---------------------------------------------------------------------
377/* Read in the file listing. The file listing is created from three
378 sources, *.list, Conffile sections and the Diversion table. */
379bool debDpkgDB::ReadyFileList(OpProgress &Progress)
380{
381 if (Cache == 0)
382 return _error->Error("The pkg cache must be initialize first");
383 if (FList != 0)
384 {
385 Progress.OverallProgress(1,1,1,"Reading File List");
386 return true;
387 }
388
389 // Create the cache and read in the file listing
390 FileMap = new DynamicMMap(MMap::Public);
391 FList = new pkgFLCache(*FileMap);
392 if (_error->PendingError() == true ||
393 ReadFList(Progress) == false ||
394 ReadConfFiles() == false ||
395 ReadDiversions() == false)
396 {
397 delete FList;
398 delete FileMap;
399 FileMap = 0;
400 FList = 0;
401 return false;
402 }
403
404 cout << "Node: " << FList->HeaderP->NodeCount << ',' << FList->HeaderP->UniqNodes << endl;
405 cout << "Dir: " << FList->HeaderP->DirCount << endl;
406 cout << "Package: " << FList->HeaderP->PackageCount << endl;
407 cout << "HashSize: " << FList->HeaderP->HashSize << endl;
408 cout << "Size: " << FileMap->Size() << endl;
409 cout << endl;
410
411 return true;
412}
413 /*}}}*/
414// DpkgDB::ReadConfFiles - Read the conf file sections from the s-file /*{{{*/
415// ---------------------------------------------------------------------
416/* Reading the conf files is done by reparsing the status file. This is
417 actually rather fast so it is no big deal. */
418bool debDpkgDB::ReadConfFiles()
419{
420 FileFd File(_config->FindFile("Dir::State::status"),FileFd::ReadOnly);
421 pkgTagFile Tags(&File);
422 if (_error->PendingError() == true)
423 return false;
424
425 pkgTagSection Section;
426 while (1)
427 {
428 // Skip to the next section
429 unsigned long Offset = Tags.Offset();
430 if (Tags.Step(Section) == false)
431 break;
432
433 // Parse the line
434 const char *Start;
435 const char *Stop;
436 if (Section.Find("Conffiles",Start,Stop) == false)
437 continue;
438
439 const char *PkgStart;
440 const char *PkgEnd;
441 if (Section.Find("Package",PkgStart,PkgEnd) == false)
442 return _error->Error("Failed to find a Package: Header, offset %lu",Offset);
443
444 // Snag a package record for it
445 pkgFLCache::PkgIterator FlPkg = FList->GetPkg(PkgStart,PkgEnd,true);
446 if (FlPkg.end() == true)
447 return _error->Error("Internal Error getting a Package Name");
448
449 // Parse the conf file lines
450 while (1)
451 {
452 for (; isspace(*Start) != 0 && Start < Stop; Start++);
453 if (Start == Stop)
454 break;
455
456 // Split it into words
457 const char *End = Start;
458 for (; isspace(*End) == 0 && End < Stop; End++);
459 const char *StartMd5 = End;
460 for (; isspace(*StartMd5) != 0 && StartMd5 < Stop; StartMd5++);
461 const char *EndMd5 = StartMd5;
462 for (; isspace(*EndMd5) == 0 && EndMd5 < Stop; EndMd5++);
463 if (StartMd5 == EndMd5 || Start == End)
464 return _error->Error("Bad ConfFile section in the status file. Offset %lu",Offset);
465
466 // Insert a new entry
467 unsigned char MD5[16];
9deebc6a 468 if (Hex2Num(string(StartMd5,EndMd5-StartMd5),MD5,16) == false)
b2e465d6 469 return _error->Error("Error parsing MD5. Offset %lu",Offset);
9deebc6a 470
b2e465d6
AL
471 if (FList->AddConfFile(Start,End,FlPkg,MD5) == false)
472 return false;
473 Start = EndMd5;
474 }
475 }
476
477 return true;
478}
479 /*}}}*/
480// DpkgDB::LoadChanges - Read in any changed state files /*{{{*/
481// ---------------------------------------------------------------------
482/* The only file in the dpkg system that can change while packages are
483 unpacking is the diversions file. */
484bool debDpkgDB::LoadChanges()
485{
486 struct stat Stat;
487 if (stat((AdminDir + "diversions").c_str(),&Stat) != 0)
488 return true;
489 if (DiverInode == Stat.st_ino && DiverTime == Stat.st_mtime)
490 return true;
491 return ReadDiversions();
492}
493 /*}}}*/