1 /*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996, 1997, 1998
5 * Sleepycat Software. All rights reserved.
6 */
7 /*
8 * Copyright (c) 1990, 1993
9 * Margo Seltzer. All rights reserved.
10 */
11 /*
12 * Copyright (c) 1990, 1993
13 * The Regents of the University of California. All rights reserved.
14 *
15 * This code is derived from software contributed to Berkeley by
16 * Margo Seltzer.
17 *
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
20 * are met:
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
26 * 3. All advertising materials mentioning features or use of this software
27 * must display the following acknowledgement:
28 * This product includes software developed by the University of
29 * California, Berkeley and its contributors.
30 * 4. Neither the name of the University nor the names of its contributors
31 * may be used to endorse or promote products derived from this software
32 * without specific prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * SUCH DAMAGE.
45 */
46
47 #include "config.h"
48
49 #ifndef lint
50 static const char sccsid[] = "@(#)dbm.c 10.23 (Sleepycat) 11/22/98";
51 #endif /* not lint */
52
53 #ifndef NO_SYSTEM_INCLUDES
54 #include <sys/types.h>
55
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <string.h>
59 #endif
60
61 #define DB_DBM_HSEARCH 1
62 #include "db_int.h"
63
64 #include "db_page.h"
65 #include "hash.h"
66
67 /*
68 *
69 * This package provides dbm and ndbm compatible interfaces to DB.
70 *
71 * The DBM routines, which call the NDBM routines.
72 */
73 static DBM *__cur_db;
74
75 static void __db_no_open __P((void));
76
77 int
__db_dbm_init(file)78 __db_dbm_init(file)
79 char *file;
80 {
81 if (__cur_db != NULL)
82 (void)dbm_close(__cur_db);
83 if ((__cur_db =
84 dbm_open(file, O_CREAT | O_RDWR, __db_omode("rw----"))) != NULL)
85 return (0);
86 if ((__cur_db = dbm_open(file, O_RDONLY, 0)) != NULL)
87 return (0);
88 return (-1);
89 }
90
91 int
__db_dbm_close()92 __db_dbm_close()
93 {
94 if (__cur_db != NULL) {
95 dbm_close(__cur_db);
96 __cur_db = NULL;
97 }
98 return (0);
99 }
100
101 datum
__db_dbm_fetch(key)102 __db_dbm_fetch(key)
103 datum key;
104 {
105 datum item;
106
107 if (__cur_db == NULL) {
108 __db_no_open();
109 item.dptr = 0;
110 return (item);
111 }
112 return (dbm_fetch(__cur_db, key));
113 }
114
115 datum
__db_dbm_firstkey()116 __db_dbm_firstkey()
117 {
118 datum item;
119
120 if (__cur_db == NULL) {
121 __db_no_open();
122 item.dptr = 0;
123 return (item);
124 }
125 return (dbm_firstkey(__cur_db));
126 }
127
128 datum
__db_dbm_nextkey(key)129 __db_dbm_nextkey(key)
130 datum key;
131 {
132 datum item;
133
134 COMPQUIET(key.dsize, 0);
135
136 if (__cur_db == NULL) {
137 __db_no_open();
138 item.dptr = 0;
139 return (item);
140 }
141 return (dbm_nextkey(__cur_db));
142 }
143
144 int
__db_dbm_delete(key)145 __db_dbm_delete(key)
146 datum key;
147 {
148 if (__cur_db == NULL) {
149 __db_no_open();
150 return (-1);
151 }
152 return (dbm_delete(__cur_db, key));
153 }
154
155 int
__db_dbm_store(key,dat)156 __db_dbm_store(key, dat)
157 datum key, dat;
158 {
159 if (__cur_db == NULL) {
160 __db_no_open();
161 return (-1);
162 }
163 return (dbm_store(__cur_db, key, dat, DBM_REPLACE));
164 }
165
166 static void
__db_no_open()167 __db_no_open()
168 {
169 (void)fprintf(stderr, "dbm: no open database.\n");
170 }
171
172 /*
173 * This package provides dbm and ndbm compatible interfaces to DB.
174 *
175 * The NDBM routines, which call the DB routines.
176 */
177 /*
178 * Returns:
179 * *DBM on success
180 * NULL on failure
181 */
182 DBM *
__db_ndbm_open(file,oflags,mode)183 __db_ndbm_open(file, oflags, mode)
184 const char *file;
185 int oflags, mode;
186 {
187 DB *dbp;
188 DBC *dbc;
189 DB_INFO dbinfo;
190 int sv_errno;
191 char path[MAXPATHLEN];
192
193 memset(&dbinfo, 0, sizeof(dbinfo));
194 dbinfo.db_pagesize = 4096;
195 dbinfo.h_ffactor = 40;
196 dbinfo.h_nelem = 1;
197
198 /*
199 * XXX
200 * Don't use sprintf(3)/snprintf(3) -- the former is dangerous, and
201 * the latter isn't standard, and we're manipulating strings handed
202 * us by the application.
203 */
204 if (strlen(file) + strlen(DBM_SUFFIX) + 1 > sizeof(path)) {
205 errno = ENAMETOOLONG;
206 return (NULL);
207 }
208 (void)strcpy(path, file);
209 (void)strcat(path, DBM_SUFFIX);
210 if ((errno = db_open(path,
211 DB_HASH, __db_oflags(oflags), mode, NULL, &dbinfo, &dbp)) != 0)
212 return (NULL);
213
214 if ((errno = dbp->cursor(dbp, NULL, &dbc, 0)) != 0) {
215 sv_errno = errno;
216 (void)dbp->close(dbp, 0);
217 errno = sv_errno;
218 return (NULL);
219 }
220
221 return ((DBM *)dbc);
222 }
223
224 /*
225 * Returns:
226 * Nothing.
227 */
228 void
__db_ndbm_close(dbm)229 __db_ndbm_close(dbm)
230 DBM *dbm;
231 {
232 DBC *dbc;
233
234 dbc = (DBC *)dbm;
235
236 (void)dbc->dbp->close(dbc->dbp, 0);
237 }
238
239 /*
240 * Returns:
241 * DATUM on success
242 * NULL on failure
243 */
244 datum
__db_ndbm_fetch(dbm,key)245 __db_ndbm_fetch(dbm, key)
246 DBM *dbm;
247 datum key;
248 {
249 DBC *dbc;
250 DBT _key, _data;
251 datum data;
252 int ret;
253
254 dbc = (DBC *)dbm;
255
256 memset(&_key, 0, sizeof(DBT));
257 memset(&_data, 0, sizeof(DBT));
258 _key.size = key.dsize;
259 _key.data = key.dptr;
260
261 /*
262 * Note that we can't simply use the dbc we have to do a c_get/SET,
263 * because that cursor is the one used for sequential iteration and
264 * it has to remain stable in the face of intervening gets and puts.
265 */
266 if ((ret = dbc->dbp->get(dbc->dbp, NULL, &_key, &_data, 0)) == 0) {
267 data.dptr = _data.data;
268 data.dsize = _data.size;
269 } else {
270 data.dptr = NULL;
271 data.dsize = 0;
272 if (ret == DB_NOTFOUND)
273 errno = ENOENT;
274 else {
275 errno = ret;
276 F_SET(dbc->dbp, DB_DBM_ERROR);
277 }
278 }
279 return (data);
280 }
281
282 /*
283 * Returns:
284 * DATUM on success
285 * NULL on failure
286 */
287 datum
__db_ndbm_firstkey(dbm)288 __db_ndbm_firstkey(dbm)
289 DBM *dbm;
290 {
291 DBC *dbc;
292 DBT _key, _data;
293 datum key;
294 int ret;
295
296 dbc = (DBC *)dbm;
297
298 memset(&_key, 0, sizeof(DBT));
299 memset(&_data, 0, sizeof(DBT));
300
301 if ((ret = dbc->c_get(dbc, &_key, &_data, DB_FIRST)) == 0) {
302 key.dptr = _key.data;
303 key.dsize = _key.size;
304 } else {
305 key.dptr = NULL;
306 key.dsize = 0;
307 if (ret == DB_NOTFOUND)
308 errno = ENOENT;
309 else {
310 errno = ret;
311 F_SET(dbc->dbp, DB_DBM_ERROR);
312 }
313 }
314 return (key);
315 }
316
317 /*
318 * Returns:
319 * DATUM on success
320 * NULL on failure
321 */
322 datum
__db_ndbm_nextkey(dbm)323 __db_ndbm_nextkey(dbm)
324 DBM *dbm;
325 {
326 DBC *dbc;
327 DBT _key, _data;
328 datum key;
329 int ret;
330
331 dbc = (DBC *)dbm;
332
333 memset(&_key, 0, sizeof(DBT));
334 memset(&_data, 0, sizeof(DBT));
335
336 if ((ret = dbc->c_get(dbc, &_key, &_data, DB_NEXT)) == 0) {
337 key.dptr = _key.data;
338 key.dsize = _key.size;
339 } else {
340 key.dptr = NULL;
341 key.dsize = 0;
342 if (ret == DB_NOTFOUND)
343 errno = ENOENT;
344 else {
345 errno = ret;
346 F_SET(dbc->dbp, DB_DBM_ERROR);
347 }
348 }
349 return (key);
350 }
351
352 /*
353 * Returns:
354 * 0 on success
355 * <0 failure
356 */
357 int
__db_ndbm_delete(dbm,key)358 __db_ndbm_delete(dbm, key)
359 DBM *dbm;
360 datum key;
361 {
362 DBC *dbc;
363 DBT _key;
364 int ret;
365
366 dbc = (DBC *)dbm;
367
368 memset(&_key, 0, sizeof(DBT));
369 _key.data = key.dptr;
370 _key.size = key.dsize;
371
372 if ((ret = dbc->dbp->del(dbc->dbp, NULL, &_key, 0)) == 0)
373 return (0);
374
375 if (ret == DB_NOTFOUND)
376 errno = ENOENT;
377 else {
378 errno = ret;
379 F_SET(dbc->dbp, DB_DBM_ERROR);
380 }
381 return (-1);
382 }
383
384 /*
385 * Returns:
386 * 0 on success
387 * <0 failure
388 * 1 if DBM_INSERT and entry exists
389 */
390 int
__db_ndbm_store(dbm,key,data,flags)391 __db_ndbm_store(dbm, key, data, flags)
392 DBM *dbm;
393 datum key, data;
394 int flags;
395 {
396 DBC *dbc;
397 DBT _key, _data;
398 int ret;
399
400 dbc = (DBC *)dbm;
401
402 memset(&_key, 0, sizeof(DBT));
403 _key.data = key.dptr;
404 _key.size = key.dsize;
405
406 memset(&_data, 0, sizeof(DBT));
407 _data.data = data.dptr;
408 _data.size = data.dsize;
409
410 if ((ret = dbc->dbp->put(dbc->dbp, NULL,
411 &_key, &_data, flags == DBM_INSERT ? DB_NOOVERWRITE : 0)) == 0)
412 return (0);
413
414 if (ret == DB_KEYEXIST)
415 return (1);
416
417 errno = ret;
418 F_SET(dbc->dbp, DB_DBM_ERROR);
419 return (-1);
420 }
421
422 int
__db_ndbm_error(dbm)423 __db_ndbm_error(dbm)
424 DBM *dbm;
425 {
426 DBC *dbc;
427
428 dbc = (DBC *)dbm;
429
430 return (F_ISSET(dbc->dbp, DB_DBM_ERROR));
431 }
432
433 int
__db_ndbm_clearerr(dbm)434 __db_ndbm_clearerr(dbm)
435 DBM *dbm;
436 {
437 DBC *dbc;
438
439 dbc = (DBC *)dbm;
440
441 F_CLR(dbc->dbp, DB_DBM_ERROR);
442 return (0);
443 }
444
445 /*
446 * Returns:
447 * 1 if read-only
448 * 0 if not read-only
449 */
450 int
__db_ndbm_rdonly(dbm)451 __db_ndbm_rdonly(dbm)
452 DBM *dbm;
453 {
454 DBC *dbc;
455
456 dbc = (DBC *)dbm;
457
458 return (F_ISSET(dbc->dbp, DB_AM_RDONLY) ? 1 : 0);
459 }
460
461 /*
462 * XXX
463 * We only have a single file descriptor that we can return, not two. Return
464 * the same one for both files. Hopefully, the user is using it for locking
465 * and picked one to use at random.
466 */
467 int
__db_ndbm_dirfno(dbm)468 __db_ndbm_dirfno(dbm)
469 DBM *dbm;
470 {
471 return (dbm_pagfno(dbm));
472 }
473
474 int
__db_ndbm_pagfno(dbm)475 __db_ndbm_pagfno(dbm)
476 DBM *dbm;
477 {
478 DBC *dbc;
479 int fd;
480
481 dbc = (DBC *)dbm;
482
483 (void)dbc->dbp->fd(dbc->dbp, &fd);
484 return (fd);
485 }
486