]> git.saurik.com Git - apt.git/blame - ftparchive/cachedb.cc
* Enable apt-ftparchive to generate Release files
[apt.git] / ftparchive / cachedb.cc
CommitLineData
b2e465d6
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
dc738e7a 3// $Id: cachedb.cc,v 1.6 2003/02/10 07:34:41 doogie Exp $
b2e465d6
AL
4/* ######################################################################
5
6 CacheDB
7
8 Simple uniform interface to a cache database.
9
10 ##################################################################### */
11 /*}}}*/
12// Include Files /*{{{*/
13#ifdef __GNUG__
14#pragma implementation "cachedb.h"
15#endif
16
17#include "cachedb.h"
18
dc738e7a 19#include <apti18n.h>
b2e465d6
AL
20#include <apt-pkg/error.h>
21#include <apt-pkg/md5.h>
22#include <apt-pkg/strutl.h>
23#include <apt-pkg/configuration.h>
24
25#include <netinet/in.h> // htonl, etc
26 /*}}}*/
27
28// CacheDB::ReadyDB - Ready the DB2 /*{{{*/
29// ---------------------------------------------------------------------
30/* This opens the DB2 file for caching package information */
31bool CacheDB::ReadyDB(string DB)
32{
33 ReadOnly = _config->FindB("APT::FTPArchive::ReadOnlyDB",false);
34
35 // Close the old DB
36 if (Dbp != 0)
37 Dbp->close(Dbp,0);
38
39 /* Check if the DB was disabled while running and deal with a
40 corrupted DB */
41 if (DBFailed() == true)
42 {
dc738e7a 43 _error->Warning(_("DB was corrupted, file renamed to %s.old"),DBFile.c_str());
b2e465d6
AL
44 rename(DBFile.c_str(),(DBFile+".old").c_str());
45 }
46
47 DBLoaded = false;
48 Dbp = 0;
49 DBFile = string();
50
51 if (DB.empty())
52 return true;
53
54 if ((errno = db_open(DB.c_str(),DB_HASH,
55 (ReadOnly?DB_RDONLY:DB_CREATE),
56 0644,0,0,&Dbp)) != 0)
57 {
58 Dbp = 0;
dc738e7a 59 return _error->Errno("db_open",_("Unable to open DB2 file %s"),DB.c_str());
b2e465d6
AL
60 }
61
62 DBFile = DB;
63 DBLoaded = true;
64 return true;
65}
66 /*}}}*/
67// CacheDB::SetFile - Select a file to be working with /*{{{*/
68// ---------------------------------------------------------------------
69/* All future actions will be performed against this file */
70bool CacheDB::SetFile(string FileName,struct stat St,FileFd *Fd)
71{
72 delete DebFile;
73 DebFile = 0;
74 this->FileName = FileName;
75 this->Fd = Fd;
76 this->FileStat = St;
77 FileStat = St;
78 memset(&CurStat,0,sizeof(CurStat));
79
80 Stats.Bytes += St.st_size;
81 Stats.Packages++;
82
83 if (DBLoaded == false)
84 return true;
85
86 InitQuery("st");
87
88 // Ensure alignment of the returned structure
89 Data.data = &CurStat;
90 Data.ulen = sizeof(CurStat);
91 Data.flags = DB_DBT_USERMEM;
92 // Lookup the stat info and confirm the file is unchanged
93 if (Get() == true)
94 {
c53766c7 95 if (CurStat.mtime != htonl(St.st_mtime))
b2e465d6 96 {
c53766c7 97 CurStat.mtime = htonl(St.st_mtime);
b2e465d6 98 CurStat.Flags = 0;
dc738e7a 99 _error->Warning(_("File date has changed %s"),FileName.c_str());
b2e465d6
AL
100 }
101 }
102 else
103 {
c53766c7 104 CurStat.mtime = htonl(St.st_mtime);
b2e465d6
AL
105 CurStat.Flags = 0;
106 }
107 CurStat.Flags = ntohl(CurStat.Flags);
108 OldStat = CurStat;
109 return true;
110}
111 /*}}}*/
112// CacheDB::LoadControl - Load Control information /*{{{*/
113// ---------------------------------------------------------------------
114/* */
115bool CacheDB::LoadControl()
116{
117 // Try to read the control information out of the DB.
118 if ((CurStat.Flags & FlControl) == FlControl)
119 {
120 // Lookup the control information
121 InitQuery("cl");
122 if (Get() == true && Control.TakeControl(Data.data,Data.size) == true)
123 return true;
124 CurStat.Flags &= ~FlControl;
125 }
126
127 // Create a deb instance to read the archive
128 if (DebFile == 0)
129 {
130 DebFile = new debDebFile(*Fd);
131 if (_error->PendingError() == true)
132 return false;
133 }
134
135 Stats.Misses++;
136 if (Control.Read(*DebFile) == false)
137 return false;
138
139 if (Control.Control == 0)
dc738e7a 140 return _error->Error(_("Archive has no control record"));
b2e465d6
AL
141
142 // Write back the control information
143 InitQuery("cl");
144 if (Put(Control.Control,Control.Length) == true)
145 CurStat.Flags |= FlControl;
146 return true;
147}
148 /*}}}*/
149// CacheDB::LoadContents - Load the File Listing /*{{{*/
150// ---------------------------------------------------------------------
151/* */
152bool CacheDB::LoadContents(bool GenOnly)
153{
154 // Try to read the control information out of the DB.
155 if ((CurStat.Flags & FlContents) == FlContents)
156 {
157 if (GenOnly == true)
158 return true;
159
160 // Lookup the contents information
161 InitQuery("cn");
162 if (Get() == true)
163 {
164 if (Contents.TakeContents(Data.data,Data.size) == true)
165 return true;
166 }
167
168 CurStat.Flags &= ~FlContents;
169 }
170
171 // Create a deb instance to read the archive
172 if (DebFile == 0)
173 {
174 DebFile = new debDebFile(*Fd);
175 if (_error->PendingError() == true)
176 return false;
177 }
178
179 if (Contents.Read(*DebFile) == false)
180 return false;
181
182 // Write back the control information
183 InitQuery("cn");
184 if (Put(Contents.Data,Contents.CurSize) == true)
185 CurStat.Flags |= FlContents;
186 return true;
187}
188 /*}}}*/
189// CacheDB::GetMD5 - Get the MD5 hash /*{{{*/
190// ---------------------------------------------------------------------
191/* */
192bool CacheDB::GetMD5(string &MD5Res,bool GenOnly)
193{
194 // Try to read the control information out of the DB.
195 if ((CurStat.Flags & FlMD5) == FlMD5)
196 {
197 if (GenOnly == true)
198 return true;
199
200 InitQuery("m5");
201 if (Get() == true)
202 {
203 MD5Res = string((char *)Data.data,Data.size);
204 return true;
205 }
206 CurStat.Flags &= ~FlMD5;
207 }
208
209 Stats.MD5Bytes += FileStat.st_size;
210
211 MD5Summation MD5;
212 if (Fd->Seek(0) == false || MD5.AddFD(Fd->Fd(),FileStat.st_size) == false)
213 return false;
214
215 MD5Res = MD5.Result();
216 InitQuery("m5");
812f4169 217 if (Put(MD5Res.c_str(),MD5Res.length()) == true)
b2e465d6
AL
218 CurStat.Flags |= FlMD5;
219 return true;
220}
221 /*}}}*/
222// CacheDB::Finish - Write back the cache structure /*{{{*/
223// ---------------------------------------------------------------------
224/* */
225bool CacheDB::Finish()
226{
227 // Optimize away some writes.
228 if (CurStat.Flags == OldStat.Flags &&
9bfe66dc 229 CurStat.mtime == OldStat.mtime)
b2e465d6
AL
230 return true;
231
232 // Write the stat information
233 CurStat.Flags = htonl(CurStat.Flags);
234 InitQuery("st");
235 Put(&CurStat,sizeof(CurStat));
236 CurStat.Flags = ntohl(CurStat.Flags);
237 return true;
238}
239 /*}}}*/
240// CacheDB::Clean - Clean the Database /*{{{*/
241// ---------------------------------------------------------------------
242/* Tidy the database by removing files that no longer exist at all. */
243bool CacheDB::Clean()
244{
245 if (DBLoaded == false)
246 return true;
247
248 /* I'm not sure what VERSION_MINOR should be here.. 2.4.14 certainly
249 needs the lower one and 2.7.7 needs the upper.. */
250#if DB_VERSION_MAJOR >= 2 && DB_VERSION_MINOR >= 7
251 DBC *Cursor;
252 if ((errno = Dbp->cursor(Dbp,0,&Cursor,0)) != 0)
dc738e7a 253 return _error->Error(_("Unable to get a cursor"));
b2e465d6
AL
254#else
255 DBC *Cursor;
256 if ((errno = Dbp->cursor(Dbp,0,&Cursor)) != 0)
dc738e7a 257 return _error->Error(_("Unable to get a cursor"));
b2e465d6
AL
258#endif
259
260 DBT Key;
261 DBT Data;
262 memset(&Key,0,sizeof(Key));
263 memset(&Data,0,sizeof(Data));
264 while ((errno = Cursor->c_get(Cursor,&Key,&Data,DB_NEXT)) == 0)
265 {
266 const char *Colon = (char *)Key.data;
267 for (; Colon != (char *)Key.data+Key.size && *Colon != ':'; Colon++);
268 if ((char *)Key.data+Key.size - Colon > 2)
269 {
270 if (stringcmp((char *)Key.data,Colon,"st") == 0 ||
271 stringcmp((char *)Key.data,Colon,"cn") == 0 ||
272 stringcmp((char *)Key.data,Colon,"m5") == 0 ||
273 stringcmp((char *)Key.data,Colon,"cl") == 0)
274 {
275 if (FileExists(string(Colon+1,(const char *)Key.data+Key.size)) == true)
276 continue;
277 }
278 }
279
280 Cursor->c_del(Cursor,0);
281 }
282
283 return true;
284}
285 /*}}}*/