1 /*
2  * Make our db functions a wrapper for GDBM.
3  *
4  * Copyright 2007 Andrew Wood, distributed under the Artistic License.
5  */
6 
7 #include "config.h"
8 #include "database.h"
9 
10 #ifdef USING_GDBM
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #ifdef HAVE_FCNTL
18 #include <fcntl.h>
19 #endif
20 #include <unistd.h>
21 #include <gdbm.h>
22 
23 struct qdbint_s {
24 	GDBM_FILE dbf;
25 #ifndef HAVE_GDBM_FDESC
26 	int fd;
27 #endif					 /* !HAVE_GDBM_FDESC */
28 };
29 
30 
31 /*
32  * Return a file descriptor for the given database, or -1 on error.
33  */
qdb_gdbm_fd(qdbint_t db)34 int qdb_gdbm_fd(qdbint_t db)
35 {
36 	if (db == NULL)
37 		return -1;
38 	if (db->dbf == NULL)
39 		return -1;
40 #ifdef HAVE_GDBM_FDESC
41 	return gdbm_fdesc(db->dbf);
42 #else				/* !HAVE_GDBM_FDESC */
43 	return db->fd;
44 #endif				/* HAVE_GDBM_FDESC */
45 }
46 
47 
48 /*
49  * Return nonzero if the given file is of this database type.
50  */
qdb_gdbm_identify(const char * file)51 int qdb_gdbm_identify(const char *file)
52 {
53 	FILE *fptr;
54 	unsigned char buf[8];		 /* RATS: ignore (checked) */
55 
56 	if (file == NULL)
57 		return 0;
58 	if (strncasecmp(file, "gdbm:", 5) == 0)
59 		return 1;
60 
61 	fptr = fopen(file, "rb");
62 	if (fptr == NULL)
63 		return 0;
64 	if (fread(buf, 4, 1, fptr) < 1) {
65 		fclose(fptr);
66 		return 0;
67 	}
68 
69 	fclose(fptr);
70 
71 	buf[4] = 0;
72 
73 	if ((buf[0] == 0x13)
74 	    && (buf[1] == 0x57)
75 	    && (buf[2] == 0x9a)
76 	    && (buf[3] == 0xce)
77 	    )
78 		return 1;
79 
80 	if ((buf[3] == 0x13)
81 	    && (buf[2] == 0x57)
82 	    && (buf[1] == 0x9a)
83 	    && (buf[0] == 0xce)
84 	    )
85 		return 1;
86 
87 	if (strncmp((char *) buf, "GDBM", 4) == 0)
88 		return 1;
89 
90 	return 0;
91 }
92 
93 
94 /*
95  * Open the given database in the given way (new database, read-only, or
96  * read-write); return a qdbint_t or NULL on error.
97  */
qdb_gdbm_open(const char * file,qdb_open_t method)98 qdbint_t qdb_gdbm_open(const char *file, qdb_open_t method)
99 {
100 	GDBM_FILE dbf = NULL;
101 	qdbint_t db;
102 	int tries;
103 
104 	if (strncasecmp(file, "gdbm:", 5) == 0)
105 		file += 5;
106 
107 	for (tries = 0; tries < 60 && dbf == NULL; tries++) {
108 		switch (method) {
109 		case QDB_NEW:
110 			dbf =
111 			    gdbm_open((char *) file, 512, GDBM_NEWDB,
112 				      S_IRUSR | S_IWUSR, NULL);
113 			break;
114 		case QDB_READONLY:
115 			dbf =
116 			    gdbm_open((char *) file, 512, GDBM_READER,
117 				      S_IRUSR, NULL);
118 			break;
119 		case QDB_READWRITE:
120 			dbf =
121 			    gdbm_open((char *) file, 512, GDBM_WRCREAT,
122 				      S_IRUSR | S_IWUSR, NULL);
123 			break;
124 		default:
125 			break;
126 		}
127 
128 		if (dbf != NULL)
129 			break;
130 
131 		switch (gdbm_errno) {
132 		case GDBM_CANT_BE_READER:
133 		case GDBM_CANT_BE_WRITER:
134 			sleep(1);
135 			break;
136 		default:
137 			return NULL;
138 		}
139 	}
140 
141 	if (dbf == NULL)
142 		return NULL;
143 
144 	db = calloc(1, sizeof(*db));
145 	if (db == NULL) {
146 		gdbm_close(dbf);
147 		return NULL;
148 	}
149 #ifndef HAVE_GDBM_FDESC
150 	db->fd = open(file, O_RDONLY);
151 #endif				/* !HAVE_GDBM_FDESC */
152 
153 	db->dbf = dbf;
154 	return db;
155 }
156 
157 
158 /*
159  * Close the given database.
160  */
qdb_gdbm_close(qdbint_t db)161 void qdb_gdbm_close(qdbint_t db)
162 {
163 	if (db == NULL)
164 		return;
165 #ifndef HAVE_GDBM_FDESC
166 	if (db->fd >= 0)
167 		close(db->fd);
168 #endif				/* !HAVE_GDBM_FDESC */
169 	gdbm_close(db->dbf);
170 	free(db);
171 }
172 
173 
174 /*
175  * Fetch a value from the database. The datum returned needs its val.data
176  * free()ing after use. If val.data is NULL, no value was found for the
177  * given key.
178  */
qdb_gdbm_fetch(qdbint_t db,qdb_datum key)179 qdb_datum qdb_gdbm_fetch(qdbint_t db, qdb_datum key)
180 {
181 	datum gkey, gval;
182 	qdb_datum val;
183 
184 	if (db == NULL) {
185 		val.data = NULL;
186 		val.size = 0;
187 		return val;
188 	}
189 
190 	if (key.data == NULL) {
191 		val.data = NULL;
192 		val.size = 0;
193 		return val;
194 	}
195 
196 	gkey.dptr = (char *) (key.data);
197 	gkey.dsize = key.size;
198 
199 	gval = gdbm_fetch(db->dbf, gkey);
200 
201 	val.data = (unsigned char *) (gval.dptr);
202 	val.size = gval.dsize;
203 
204 	return val;
205 }
206 
207 
208 /*
209  * Store the given key with the given value into the database, replacing any
210  * existing value for that key. Returns nonzero on error.
211  */
qdb_gdbm_store(qdbint_t db,qdb_datum key,qdb_datum val)212 int qdb_gdbm_store(qdbint_t db, qdb_datum key, qdb_datum val)
213 {
214 	datum gkey, gval;
215 
216 	if (db == NULL)
217 		return 1;
218 
219 	if ((key.data == NULL) || (val.data == NULL))
220 		return 1;
221 
222 	gkey.dptr = (char *) (key.data);
223 	gkey.dsize = key.size;
224 	gval.dptr = (char *) (val.data);
225 	gval.dsize = val.size;
226 
227 	return gdbm_store(db->dbf, gkey, gval, GDBM_REPLACE);
228 }
229 
230 
231 /*
232  * Delete the given key from the database. Returns nonzero on error.
233  */
qdb_gdbm_delete(qdbint_t db,qdb_datum key)234 int qdb_gdbm_delete(qdbint_t db, qdb_datum key)
235 {
236 	datum gkey;
237 
238 	if (db == NULL)
239 		return 1;
240 
241 	if (key.data == NULL)
242 		return 1;
243 
244 	gkey.dptr = (char *) (key.data);
245 	gkey.dsize = key.size;
246 
247 	return gdbm_delete(db->dbf, gkey);
248 }
249 
250 
251 /*
252  * Return the "first" key in the database, suitable for using with repeated
253  * calls to qdb_nextkey() to walk through every key in the database.
254  */
qdb_gdbm_firstkey(qdbint_t db)255 qdb_datum qdb_gdbm_firstkey(qdbint_t db)
256 {
257 	datum gkey;
258 	qdb_datum key;
259 
260 	gkey = gdbm_firstkey(db->dbf);
261 
262 	key.data = (unsigned char *) (gkey.dptr);
263 	key.size = gkey.dsize;
264 
265 	return key;
266 }
267 
268 
269 /*
270  * Return the "next" key in the database, or key.data=NULL when all keys
271  * have been returned.
272  */
qdb_gdbm_nextkey(qdbint_t db,qdb_datum key)273 qdb_datum qdb_gdbm_nextkey(qdbint_t db, qdb_datum key)
274 {
275 	datum gkey, newgkey;
276 	qdb_datum newkey;
277 
278 	gkey.dptr = (char *) (key.data);
279 	gkey.dsize = key.size;
280 
281 	newgkey = gdbm_nextkey(db->dbf, gkey);
282 
283 	newkey.data = (unsigned char *) (newgkey.dptr);
284 	newkey.size = newgkey.dsize;
285 
286 	return newkey;
287 }
288 
289 
290 /*
291  * Reorganise the database for better efficiency.
292  */
qdb_gdbm_optimise(qdbint_t db)293 void qdb_gdbm_optimise(qdbint_t db)
294 {
295 	gdbm_reorganize(db->dbf);
296 }
297 
298 
299 /*
300  * Return a string describing the last database error to occur.
301  */
qdb_gdbm_error(void)302 char *qdb_gdbm_error(void)
303 {
304 	return (char *) gdbm_strerror(gdbm_errno);
305 }
306 
307 
308 #endif				/* USING_GDBM */
309 
310 /* EOF */
311