xref: /illumos-gate/usr/src/cmd/sendmail/db/dbm/dbm.c (revision 7c478bd9)
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