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