1 /* $Id: cachefile.cc,v 1.8 2005/07/21 11:31:43 atterer Exp $ -*- C++ -*-
3 |_) /| Copyright (C) 2001-2003 | richard@
4 | \/¯| Richard Atterer | atterer.org
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.
10 Cache with MD5 sums of file contents - used by JigdoCache in scan.hh
16 #include <cachefile.hh>
24 #include <time.h> /* time() */
28 #include <serialize.hh>
29 //______________________________________________________________________
31 DEBUG_UNIT("cachefile")
33 CacheFile::CacheFile(const char* dbName) {
34 memset(&data, 0, sizeof(DBT));
36 int e = db_create(&db, 0, 0); // No env/flags
37 if (e != 0) throw DbError(e);
39 // Cache of 0GB+4MB, one contiguous chunk
40 db->set_cachesize(db, 0, 4*1024*1024, 1);
42 // Use a btree, create database file if not yet present
43 e = compat_dbOpen(db, dbName, "jigdo filecache v0", DB_BTREE, DB_CREATE,
46 // Re-close, in case it is necessary
48 if (e != DB_OLD_VERSION && e != DB_RUNRECOVERY)
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)
58 data.flags |= DB_DBT_REALLOC;
60 //______________________________________________________________________
64 /** Local struct: Wrapper which calls close() for any DBC cursor at end of
67 AutoCursor() : c(0) { }
68 ~AutoCursor() { close(); }
71 int r = c->c_close(c);
75 int get(DBT *key, DBT *data, u_int32_t flags) {
76 return c->c_get(c, key, data, flags);
78 int put(DBT *key, DBT *data, u_int32_t flags) {
79 return c->c_put(c, key, data, flags);
81 int del(u_int32_t flags) {
82 return c->c_del(c, flags);
88 //________________________________________
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
98 // Cursor with no transaction id, no flags
99 if (db->cursor(db, 0, &cursor.c, 0) != 0) return FAILED;
101 if (cursor.get(&key, &data, DB_SET) == DB_NOTFOUND
102 || data.data == 0) return FAILED;
104 // Check whether mtime and size matches
105 Paranoid(data.size >= USER_DATA);
106 byte* d = static_cast<byte*>(data.data);
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;
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;
122 partial.flags |= DB_DBT_PARTIAL;
123 partial.doff = ACCESS;
125 //cerr << "CacheFile lookup successfull for "<<fileName<<endl;
126 cursor.put(&key, &partial, DB_CURRENT);
128 resultData = d + USER_DATA;
129 resultSize = data.size - USER_DATA;
132 //________________________________________
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!
143 // Cursor with no transaction id, no flags
144 if (db->cursor(db, 0, &cursor.c, 0) != 0) return FAILED;
146 if (cursor.get(&key, &data, DB_SET) == DB_NOTFOUND
147 || data.data == 0) return FAILED;
149 // get mtime and size
150 Paranoid(data.size >= USER_DATA);
151 byte* d = static_cast<byte*>(data.data);
154 unserialize4(cacheMtime, d + MTIME);
155 resultMtime = cacheMtime;
156 uint64 cacheFileSize;
157 unserialize6(cacheFileSize, d + SIZE);
158 resultFileSize = cacheFileSize;
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;
167 partial.flags |= DB_DBT_PARTIAL;
168 partial.doff = ACCESS;
170 //cerr << "CacheFile lookup successfull for "<<fileName<<endl;
171 cursor.put(&key, &partial, DB_CURRENT);
173 resultData = d + USER_DATA;
174 resultSize = data.size - USER_DATA;
177 //______________________________________________________________________
179 void CacheFile::expire(time_t t) {
180 DBT key; memset(&key, 0, sizeof(DBT));
181 DBT data; memset(&data, 0, sizeof(DBT));
183 // Cursor with no transaction id, no flags
184 if (db->cursor(db, 0, &cursor.c, 0) != 0) return;
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
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));
199 if (status != DB_NOTFOUND)
200 throw DbError(status);
202 //______________________________________________________________________
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();
211 data.size = (u_int32_t)(USER_DATA + inSize);
212 return static_cast<byte*>(tmp) + USER_DATA;
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,
220 byte* buf = static_cast<byte*>(data.data);
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);
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
235 // cerr << "CacheFile write `"<<fileName<<'\'';
236 // for (size_t i = USER_DATA; i < data.get_size(); ++i)
237 // cerr << ' '<< hex << (int)(buf[i]);
240 //______________________________________________________________________
242 #endif /* HAVE_LIBDB */