1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 2013 Oracle and/or its affiliates.  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$
42  */
43 
44 #include "db_config.h"
45 
46 #include "db_int.h"
47 
48 /*
49  *
50  * This package provides dbm and ndbm compatible interfaces to DB.
51  *
52  * EXTERN: #if DB_DBM_HSEARCH != 0
53  * EXTERN: int	 __db_ndbm_clearerr __P((DBM *));
54  * EXTERN: void	 __db_ndbm_close __P((DBM *));
55  * EXTERN: int	 __db_ndbm_delete __P((DBM *, datum));
56  * EXTERN: int	 __db_ndbm_dirfno __P((DBM *));
57  * EXTERN: int	 __db_ndbm_error __P((DBM *));
58  * EXTERN: datum __db_ndbm_fetch __P((DBM *, datum));
59  * EXTERN: datum __db_ndbm_firstkey __P((DBM *));
60  * EXTERN: datum __db_ndbm_nextkey __P((DBM *));
61  * EXTERN: DBM	*__db_ndbm_open __P((const char *, int, int));
62  * EXTERN: int	 __db_ndbm_pagfno __P((DBM *));
63  * EXTERN: int	 __db_ndbm_rdonly __P((DBM *));
64  * EXTERN: int	 __db_ndbm_store __P((DBM *, datum, datum, int));
65  *
66  * EXTERN: int	 __db_dbm_close __P((void));
67  * EXTERN: int	 __db_dbm_delete __P((datum));
68  * EXTERN: datum __db_dbm_fetch __P((datum));
69  * EXTERN: datum __db_dbm_firstkey __P((void));
70  * EXTERN: int	 __db_dbm_init __P((char *));
71  * EXTERN: datum __db_dbm_nextkey __P((datum));
72  * EXTERN: int	 __db_dbm_store __P((datum, datum));
73  * EXTERN: #endif
74  */
75 
76 /*
77  * The DBM routines, which call the NDBM routines.
78  */
79 static DBM *__cur_db;
80 
81 static void __db_no_open __P((void));
82 
83 int
__db_dbm_init(file)84 __db_dbm_init(file)
85 	char *file;
86 {
87 	if (__cur_db != NULL)
88 		dbm_close(__cur_db);
89 	if ((__cur_db = dbm_open(file, O_CREAT | O_RDWR, DB_MODE_600)) != NULL)
90 		return (0);
91 	if ((__cur_db = dbm_open(file, O_RDONLY, 0)) != NULL)
92 		return (0);
93 	return (-1);
94 }
95 
96 int
__db_dbm_close()97 __db_dbm_close()
98 {
99 	if (__cur_db != NULL) {
100 		dbm_close(__cur_db);
101 		__cur_db = NULL;
102 	}
103 	return (0);
104 }
105 
106 datum
__db_dbm_fetch(key)107 __db_dbm_fetch(key)
108 	datum key;
109 {
110 	datum item;
111 
112 	if (__cur_db == NULL) {
113 		__db_no_open();
114 		item.dptr = NULL;
115 		item.dsize = 0;
116 		return (item);
117 	}
118 	return (dbm_fetch(__cur_db, key));
119 }
120 
121 datum
__db_dbm_firstkey()122 __db_dbm_firstkey()
123 {
124 	datum item;
125 
126 	if (__cur_db == NULL) {
127 		__db_no_open();
128 		item.dptr = NULL;
129 		item.dsize = 0;
130 		return (item);
131 	}
132 	return (dbm_firstkey(__cur_db));
133 }
134 
135 datum
__db_dbm_nextkey(key)136 __db_dbm_nextkey(key)
137 	datum key;
138 {
139 	datum item;
140 
141 	COMPQUIET(key.dsize, 0);
142 
143 	if (__cur_db == NULL) {
144 		__db_no_open();
145 		item.dptr = NULL;
146 		item.dsize = 0;
147 		return (item);
148 	}
149 	return (dbm_nextkey(__cur_db));
150 }
151 
152 int
__db_dbm_delete(key)153 __db_dbm_delete(key)
154 	datum key;
155 {
156 	if (__cur_db == NULL) {
157 		__db_no_open();
158 		return (-1);
159 	}
160 	return (dbm_delete(__cur_db, key));
161 }
162 
163 int
__db_dbm_store(key,dat)164 __db_dbm_store(key, dat)
165 	datum key, dat;
166 {
167 	if (__cur_db == NULL) {
168 		__db_no_open();
169 		return (-1);
170 	}
171 	return (dbm_store(__cur_db, key, dat, DBM_REPLACE));
172 }
173 
174 static void
__db_no_open()175 __db_no_open()
176 {
177 	(void)fprintf(stderr, DB_STR_A("5126",
178 	    "dbm: no open database.\n", "\n"));
179 }
180 
181 /*
182  * This package provides dbm and ndbm compatible interfaces to DB.
183  *
184  * The NDBM routines, which call the DB routines.
185  */
186 /*
187  * Returns:
188  *	*DBM on success
189  *	 NULL on failure
190  */
191 DBM *
__db_ndbm_open(file,oflags,mode)192 __db_ndbm_open(file, oflags, mode)
193 	const char *file;
194 	int oflags, mode;
195 {
196 	DB *dbp;
197 	DBC *dbc;
198 	int ret;
199 	char path[DB_MAXPATHLEN];
200 
201 	/*
202 	 * !!!
203 	 * Don't use sprintf(3)/snprintf(3) -- the former is dangerous, and
204 	 * the latter isn't standard, and we're manipulating strings handed
205 	 * us by the application.
206 	 */
207 	if (strlen(file) + strlen(DBM_SUFFIX) + 1 > sizeof(path)) {
208 		__os_set_errno(ENAMETOOLONG);
209 		return (NULL);
210 	}
211 	(void)strcpy(path, file);
212 	(void)strcat(path, DBM_SUFFIX);
213 	if ((ret = db_create(&dbp, NULL, 0)) != 0) {
214 		__os_set_errno(ret);
215 		return (NULL);
216 	}
217 
218 	/*
219 	 * !!!
220 	 * The historic ndbm library corrected for opening O_WRONLY.
221 	 */
222 	if (oflags & O_WRONLY) {
223 		oflags &= ~O_WRONLY;
224 		oflags |= O_RDWR;
225 	}
226 
227 	if ((ret = dbp->set_pagesize(dbp, 4096)) != 0 ||
228 	    (ret = dbp->set_h_ffactor(dbp, 40)) != 0 ||
229 	    (ret = dbp->set_h_nelem(dbp, 1)) != 0 ||
230 	    (ret = dbp->open(dbp, NULL,
231 	    path, NULL, DB_HASH, __db_openflags(oflags), mode)) != 0) {
232 		__os_set_errno(ret);
233 		return (NULL);
234 	}
235 
236 	if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0) {
237 		(void)dbp->close(dbp, 0);
238 		__os_set_errno(ret);
239 		return (NULL);
240 	}
241 
242 	return ((DBM *)dbc);
243 }
244 
245 /*
246  * Returns:
247  *	Nothing.
248  */
249 void
__db_ndbm_close(dbm)250 __db_ndbm_close(dbm)
251 	DBM *dbm;
252 {
253 	DBC *dbc;
254 
255 	dbc = (DBC *)dbm;
256 
257 	(void)dbc->dbp->close(dbc->dbp, 0);
258 }
259 
260 /*
261  * Returns:
262  *	DATUM on success
263  *	NULL on failure
264  */
265 datum
__db_ndbm_fetch(dbm,key)266 __db_ndbm_fetch(dbm, key)
267 	DBM *dbm;
268 	datum key;
269 {
270 	DBC *dbc;
271 	DBT _key, _data;
272 	datum data;
273 	int ret;
274 
275 	dbc = (DBC *)dbm;
276 
277 	DB_INIT_DBT(_key, key.dptr, key.dsize);
278 	memset(&_data, 0, sizeof(DBT));
279 
280 	/*
281 	 * Note that we can't simply use the dbc we have to do a get/SET,
282 	 * because that cursor is the one used for sequential iteration and
283 	 * it has to remain stable in the face of intervening gets and puts.
284 	 */
285 	if ((ret = dbc->dbp->get(dbc->dbp, NULL, &_key, &_data, 0)) == 0) {
286 		data.dptr = _data.data;
287 		data.dsize = (int)_data.size;
288 	} else {
289 		data.dptr = NULL;
290 		data.dsize = 0;
291 		if (ret == DB_NOTFOUND)
292 			__os_set_errno(ENOENT);
293 		else {
294 			__os_set_errno(ret);
295 			F_SET(dbc->dbp, DB_AM_DBM_ERROR);
296 		}
297 	}
298 	return (data);
299 }
300 
301 /*
302  * Returns:
303  *	DATUM on success
304  *	NULL on failure
305  */
306 datum
__db_ndbm_firstkey(dbm)307 __db_ndbm_firstkey(dbm)
308 	DBM *dbm;
309 {
310 	DBC *dbc;
311 	DBT _key, _data;
312 	datum key;
313 	int ret;
314 
315 	dbc = (DBC *)dbm;
316 
317 	memset(&_key, 0, sizeof(DBT));
318 	memset(&_data, 0, sizeof(DBT));
319 
320 	if ((ret = dbc->get(dbc, &_key, &_data, DB_FIRST)) == 0) {
321 		key.dptr = _key.data;
322 		key.dsize = (int)_key.size;
323 	} else {
324 		key.dptr = NULL;
325 		key.dsize = 0;
326 		if (ret == DB_NOTFOUND)
327 			__os_set_errno(ENOENT);
328 		else {
329 			__os_set_errno(ret);
330 			F_SET(dbc->dbp, DB_AM_DBM_ERROR);
331 		}
332 	}
333 	return (key);
334 }
335 
336 /*
337  * Returns:
338  *	DATUM on success
339  *	NULL on failure
340  */
341 datum
__db_ndbm_nextkey(dbm)342 __db_ndbm_nextkey(dbm)
343 	DBM *dbm;
344 {
345 	DBC *dbc;
346 	DBT _key, _data;
347 	datum key;
348 	int ret;
349 
350 	dbc = (DBC *)dbm;
351 
352 	memset(&_key, 0, sizeof(DBT));
353 	memset(&_data, 0, sizeof(DBT));
354 
355 	if ((ret = dbc->get(dbc, &_key, &_data, DB_NEXT)) == 0) {
356 		key.dptr = _key.data;
357 		key.dsize = (int)_key.size;
358 	} else {
359 		key.dptr = NULL;
360 		key.dsize = 0;
361 		if (ret == DB_NOTFOUND)
362 			__os_set_errno(ENOENT);
363 		else {
364 			__os_set_errno(ret);
365 			F_SET(dbc->dbp, DB_AM_DBM_ERROR);
366 		}
367 	}
368 	return (key);
369 }
370 
371 /*
372  * Returns:
373  *	 0 on success
374  *	<0 failure
375  */
376 int
__db_ndbm_delete(dbm,key)377 __db_ndbm_delete(dbm, key)
378 	DBM *dbm;
379 	datum key;
380 {
381 	DBC *dbc;
382 	DBT _key;
383 	int ret;
384 
385 	dbc = (DBC *)dbm;
386 
387 	DB_INIT_DBT(_key, key.dptr, key.dsize);
388 
389 	if ((ret = dbc->dbp->del(dbc->dbp, NULL, &_key, 0)) == 0)
390 		return (0);
391 
392 	if (ret == DB_NOTFOUND)
393 		__os_set_errno(ENOENT);
394 	else {
395 		__os_set_errno(ret);
396 		F_SET(dbc->dbp, DB_AM_DBM_ERROR);
397 	}
398 	return (-1);
399 }
400 
401 /*
402  * Returns:
403  *	 0 on success
404  *	<0 failure
405  *	 1 if DBM_INSERT and entry exists
406  */
407 int
__db_ndbm_store(dbm,key,data,flags)408 __db_ndbm_store(dbm, key, data, flags)
409 	DBM *dbm;
410 	datum key, data;
411 	int flags;
412 {
413 	DBC *dbc;
414 	DBT _key, _data;
415 	int ret;
416 
417 	dbc = (DBC *)dbm;
418 
419 	DB_INIT_DBT(_key, key.dptr, key.dsize);
420 	DB_INIT_DBT(_data, data.dptr, data.dsize);
421 
422 	if ((ret = dbc->dbp->put(dbc->dbp, NULL,
423 	    &_key, &_data, flags == DBM_INSERT ? DB_NOOVERWRITE : 0)) == 0)
424 		return (0);
425 
426 	if (ret == DB_KEYEXIST)
427 		return (1);
428 
429 	__os_set_errno(ret);
430 	F_SET(dbc->dbp, DB_AM_DBM_ERROR);
431 	return (-1);
432 }
433 
434 int
__db_ndbm_error(dbm)435 __db_ndbm_error(dbm)
436 	DBM *dbm;
437 {
438 	DBC *dbc;
439 
440 	dbc = (DBC *)dbm;
441 
442 	return (F_ISSET(dbc->dbp, DB_AM_DBM_ERROR));
443 }
444 
445 int
__db_ndbm_clearerr(dbm)446 __db_ndbm_clearerr(dbm)
447 	DBM *dbm;
448 {
449 	DBC *dbc;
450 
451 	dbc = (DBC *)dbm;
452 
453 	F_CLR(dbc->dbp, DB_AM_DBM_ERROR);
454 	return (0);
455 }
456 
457 /*
458  * Returns:
459  *	1 if read-only
460  *	0 if not read-only
461  */
462 int
__db_ndbm_rdonly(dbm)463 __db_ndbm_rdonly(dbm)
464 	DBM *dbm;
465 {
466 	DBC *dbc;
467 
468 	dbc = (DBC *)dbm;
469 
470 	return (F_ISSET(dbc->dbp, DB_AM_RDONLY) ? 1 : 0);
471 }
472 
473 /*
474  * XXX
475  * We only have a single file descriptor that we can return, not two.  Return
476  * the same one for both files.  Hopefully, the user is using it for locking
477  * and picked one to use at random.
478  */
479 int
__db_ndbm_dirfno(dbm)480 __db_ndbm_dirfno(dbm)
481 	DBM *dbm;
482 {
483 	return (dbm_pagfno(dbm));
484 }
485 
486 int
__db_ndbm_pagfno(dbm)487 __db_ndbm_pagfno(dbm)
488 	DBM *dbm;
489 {
490 	DBC *dbc;
491 	int fd;
492 
493 	dbc = (DBC *)dbm;
494 
495 	(void)dbc->dbp->fd(dbc->dbp, &fd);
496 	return (fd);
497 }
498