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