Fix lots of warnings about implicit type conversions
[jigdo.git] / src / cachefile.cc
1 /* $Id: cachefile.cc,v 1.8 2005/07/21 11:31:43 atterer Exp $ -*- C++ -*-
2   __   _
3   |_) /|  Copyright (C) 2001-2003  |  richard@
4   | \/¯|  Richard Atterer          |  atterer.org
5   ¯ '` ¯
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License, version 2. See
8   the file COPYING for details.
9
10   Cache with MD5 sums of file contents - used by JigdoCache in scan.hh
11
12 */
13
14 #include <config.h>
15
16 #include <cachefile.hh>
17 #include <compat.hh>
18 #if HAVE_LIBDB
19
20 #if DEBUG
21 #  include <iostream>
22 #endif
23 #include <new>
24 #include <time.h> /* time() */
25
26 #include <debug.hh>
27 #include <log.hh>
28 #include <serialize.hh>
29 //______________________________________________________________________
30
31 DEBUG_UNIT("cachefile")
32
33 CacheFile::CacheFile(const char* dbName) {
34   memset(&data, 0, sizeof(DBT));
35
36   int e = db_create(&db, 0, 0); // No env/flags
37   if (e != 0) throw DbError(e);
38
39   // Cache of 0GB+4MB, one contiguous chunk
40   db->set_cachesize(db, 0, 4*1024*1024, 1);
41
42   // Use a btree, create database file if not yet present
43   e = compat_dbOpen(db, dbName, "jigdo filecache v0", DB_BTREE, DB_CREATE,
44                     0666);
45   if (e != 0) {
46     // Re-close, in case it is necessary
47     db->close(db, 0);
48     if (e != DB_OLD_VERSION && e != DB_RUNRECOVERY)
49       throw DbError(e);
50     /* If the DB file is old or corrupted, just regenerate it from
51        scratch, otherwise throw error. */
52     debug("Cache file corrupt, recreating it");
53     if (compat_dbOpen(db, dbName, "jigdo filecache v0", DB_BTREE,
54                       DB_CREATE | DB_TRUNCATE, 0666) != 0)
55       throw DbError(e);
56   }
57
58   data.flags |= DB_DBT_REALLOC;
59 }
60 //______________________________________________________________________
61
62 namespace {
63
64   /** Local struct: Wrapper which calls close() for any DBC cursor at end of
65       scope */
66   struct AutoCursor {
67     AutoCursor() : c(0) { }
68     ~AutoCursor() { close(); }
69     int close() {
70       if (c == 0) return 0;
71       int r = c->c_close(c);
72       c = 0;
73       return r;
74     }
75     int get(DBT *key, DBT *data, u_int32_t flags) {
76       return c->c_get(c, key, data, flags);
77     }
78     int put(DBT *key, DBT *data, u_int32_t flags) {
79       return c->c_put(c, key, data, flags);
80     }
81     int del(u_int32_t flags) {
82       return c->c_del(c, flags);
83     }
84     DBC* c;
85   };
86
87 }
88 //________________________________________
89
90 Status CacheFile::find(const byte*& resultData, size_t& resultSize,
91                        const string& fileName, uint64 fileSize, time_t mtime) {
92   DBT key; memset(&key, 0, sizeof(DBT));
93   key.data = const_cast<char*>(fileName.c_str());
94   key.size = (u_int32_t)fileName.size(); // filename size is safely
95                                          // less than 32 bits!
96
97   AutoCursor cursor;
98   // Cursor with no transaction id, no flags
99   if (db->cursor(db, 0, &cursor.c, 0) != 0) return FAILED;
100
101   if (cursor.get(&key, &data, DB_SET) == DB_NOTFOUND
102       || data.data == 0) return FAILED;
103
104   // Check whether mtime and size matches
105   Paranoid(data.size >= USER_DATA);
106   byte* d = static_cast<byte*>(data.data);
107   Paranoid(d != 0);
108   time_t cacheMtime;
109   unserialize4(cacheMtime, d + MTIME);
110   if (cacheMtime != mtime) return FAILED;
111   uint64 cacheFileSize;
112   unserialize6(cacheFileSize, d + SIZE);
113   if (cacheFileSize != fileSize) return FAILED;
114
115   // Match - update access time
116   time_t now = time(0);
117   Paranoid(now != static_cast<time_t>(-1));
118   serialize4(now, d + ACCESS);
119   DBT partial; memset(&partial, 0, sizeof(DBT));
120   partial.data = d + ACCESS;
121   partial.size = 4;
122   partial.flags |= DB_DBT_PARTIAL;
123   partial.doff = ACCESS;
124   partial.dlen = 4;
125   //cerr << "CacheFile lookup successfull for "<<fileName<<endl;
126   cursor.put(&key, &partial, DB_CURRENT);
127
128   resultData = d + USER_DATA;
129   resultSize = data.size - USER_DATA;
130   return OK;
131 }
132 //________________________________________
133
134 Status CacheFile::findName(const byte*& resultData, size_t& resultSize,
135     const string& fileName, off_t& resultFileSize,
136     time_t& resultMtime) {
137   DBT key; memset(&key, 0, sizeof(DBT));
138   key.data = const_cast<char*>(fileName.c_str());
139   key.size = (u_int32_t)fileName.size(); // filename size is safely
140                                          // less than 32 bits!
141
142   AutoCursor cursor;
143   // Cursor with no transaction id, no flags
144   if (db->cursor(db, 0, &cursor.c, 0) != 0) return FAILED;
145
146   if (cursor.get(&key, &data, DB_SET) == DB_NOTFOUND
147       || data.data == 0) return FAILED;
148
149   // get mtime and size
150   Paranoid(data.size >= USER_DATA);
151   byte* d = static_cast<byte*>(data.data);
152   Paranoid(d != 0);
153   time_t cacheMtime;
154   unserialize4(cacheMtime, d + MTIME);
155   resultMtime = cacheMtime;
156   uint64 cacheFileSize;
157   unserialize6(cacheFileSize, d + SIZE);
158   resultFileSize = cacheFileSize;
159
160   // Match - update access time
161   time_t now = time(0);
162   Paranoid(now != static_cast<time_t>(-1));
163   serialize4(now, d + ACCESS);
164   DBT partial; memset(&partial, 0, sizeof(DBT));
165   partial.data = d + ACCESS;
166   partial.size = 4;
167   partial.flags |= DB_DBT_PARTIAL;
168   partial.doff = ACCESS;
169   partial.dlen = 4;
170   //cerr << "CacheFile lookup successfull for "<<fileName<<endl;
171   cursor.put(&key, &partial, DB_CURRENT);
172
173   resultData = d + USER_DATA;
174   resultSize = data.size - USER_DATA;
175   return OK;
176 }
177 //______________________________________________________________________
178
179 void CacheFile::expire(time_t t) {
180   DBT key; memset(&key, 0, sizeof(DBT));
181   DBT data; memset(&data, 0, sizeof(DBT));
182   AutoCursor cursor;
183   // Cursor with no transaction id, no flags
184   if (db->cursor(db, 0, &cursor.c, 0) != 0) return;
185
186   int status;
187   while ((status = cursor.get(&key, &data, DB_NEXT)) == 0) {
188     time_t lastAccess = 0;
189     // If data.data == 0, expire entry by leaving lastAccess at 0
190     if (data.data != 0)
191       unserialize4(lastAccess, static_cast<byte*>(data.data) + ACCESS);
192     // Same as 'if (lastAccess<t)', but deals with wraparound:
193     if (static_cast<signed>(t - lastAccess) > 0) {
194       debug("Cache: expiring %1",
195             string(static_cast<char*>(key.data), key.size));
196       cursor.del(0);
197     }
198   }
199   if (status != DB_NOTFOUND)
200     throw DbError(status);
201 }
202 //______________________________________________________________________
203
204 /* Prepare for an insertion of data, by allocating a sufficient amount
205    of memory and returning a pointer to it. */
206 byte* CacheFile::insert_prepare(size_t inSize) {
207   // Allocate enough memory for the new entry
208   void* tmp = realloc(data.data, USER_DATA + inSize);
209   if (tmp == 0) throw bad_alloc();
210   data.data = tmp;
211   data.size = (u_int32_t)(USER_DATA + inSize);
212   return static_cast<byte*>(tmp) + USER_DATA;
213 }
214
215 /* ASSUMES THAT insert_prepare() HAS JUST BEEN CALLED and that the
216    data had been copied to the memory region it returned. This
217    function commits the data to the db. */
218 void CacheFile::insert_perform(const string& fileName, time_t mtime,
219                                uint64 fileSize) {
220   byte* buf = static_cast<byte*>(data.data);
221
222   // Write our data members
223   time_t now = time(0);
224   serialize4(now, buf + ACCESS);
225   serialize4(mtime, buf + MTIME);
226   serialize6(fileSize, buf + SIZE);
227
228   // Insert in database
229   DBT key; memset(&key, 0, sizeof(DBT));
230   key.data = const_cast<char*>(fileName.c_str());
231   key.size = (u_int32_t)fileName.size(); // filename size is safely
232                                          // less than 32 bits!
233   db->put(db, 0, &key, &data, 0); // No transaction, overwrite
234
235 //   cerr << "CacheFile write `"<<fileName<<'\'';
236 //   for (size_t i = USER_DATA; i < data.get_size(); ++i)
237 //     cerr << ' '<< hex << (int)(buf[i]);
238 //   cerr << endl;
239 }
240 //______________________________________________________________________
241
242 #endif /* HAVE_LIBDB */