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