smdb2.c revision 363466
1/*
2** Copyright (c) 1999-2003, 2009 Proofpoint, Inc. and its suppliers.
3**	All rights reserved.
4**
5** By using this file, you agree to the terms and conditions set
6** forth in the LICENSE file which can be found at the top level of
7** the sendmail distribution.
8*/
9
10#include <sm/gen.h>
11SM_RCSID("@(#)$Id: smdb2.c,v 8.83 2013-11-22 20:51:49 ca Exp $")
12
13#include <fcntl.h>
14#include <stdlib.h>
15#include <unistd.h>
16
17
18#include <sendmail/sendmail.h>
19#include <libsmdb/smdb.h>
20
21#if (DB_VERSION_MAJOR >= 2)
22
23struct smdb_db2_database
24{
25	DB	*smdb2_db;
26	int	smdb2_lock_fd;
27};
28typedef struct smdb_db2_database SMDB_DB2_DATABASE;
29
30/*
31**  SMDB_TYPE_TO_DB2_TYPE -- Translates smdb database type to db2 type.
32**
33**	Parameters:
34**		type -- The type to translate.
35**
36**	Returns:
37**		The DB2 type that corresponsds to the passed in SMDB type.
38**		Returns -1 if there is no equivalent type.
39**
40*/
41
42static DBTYPE
43smdb_type_to_db2_type(type)
44	SMDB_DBTYPE type;
45{
46	if (type == SMDB_TYPE_DEFAULT)
47		return DB_HASH;
48
49	if (SMDB_IS_TYPE_HASH(type))
50		return DB_HASH;
51
52	if (SMDB_IS_TYPE_BTREE(type))
53		return DB_BTREE;
54
55	return DB_UNKNOWN;
56}
57/*
58**  DB2_ERROR_TO_SMDB -- Translates db2 errors to smdbe errors
59**
60**	Parameters:
61**		error -- The error to translate.
62**
63**	Returns:
64**		The SMDBE error corresponding to the db2 error.
65**		If we don't have a corresponding error, it returns errno.
66**
67*/
68
69static int
70db2_error_to_smdb(error)
71	int error;
72{
73	int result;
74
75	switch (error)
76	{
77# ifdef DB_INCOMPLETE
78		case DB_INCOMPLETE:
79			result = SMDBE_INCOMPLETE;
80			break;
81# endif
82
83# ifdef DB_NOTFOUND
84		case DB_NOTFOUND:
85			result = SMDBE_NOT_FOUND;
86			break;
87# endif
88
89# ifdef DB_KEYEMPTY
90		case DB_KEYEMPTY:
91			result = SMDBE_KEY_EMPTY;
92			break;
93# endif
94
95# ifdef DB_KEYEXIST
96		case DB_KEYEXIST:
97			result = SMDBE_KEY_EXIST;
98			break;
99# endif
100
101# ifdef DB_LOCK_DEADLOCK
102		case DB_LOCK_DEADLOCK:
103			result = SMDBE_LOCK_DEADLOCK;
104			break;
105# endif
106
107# ifdef DB_LOCK_NOTGRANTED
108		case DB_LOCK_NOTGRANTED:
109			result = SMDBE_LOCK_NOT_GRANTED;
110			break;
111# endif
112
113# ifdef DB_LOCK_NOTHELD
114		case DB_LOCK_NOTHELD:
115			result = SMDBE_LOCK_NOT_HELD;
116			break;
117# endif
118
119# ifdef DB_RUNRECOVERY
120		case DB_RUNRECOVERY:
121			result = SMDBE_RUN_RECOVERY;
122			break;
123# endif
124
125# ifdef DB_OLD_VERSION
126		case DB_OLD_VERSION:
127			result = SMDBE_OLD_VERSION;
128			break;
129# endif
130
131		case 0:
132			result = SMDBE_OK;
133			break;
134
135		default:
136			result = error;
137	}
138	return result;
139}
140/*
141**  SMDB_PUT_FLAGS_TO_DB2_FLAGS -- Translates smdb put flags to db2 put flags.
142**
143**	Parameters:
144**		flags -- The flags to translate.
145**
146**	Returns:
147**		The db2 flags that are equivalent to the smdb flags.
148**
149**	Notes:
150**		Any invalid flags are ignored.
151**
152*/
153
154static unsigned int
155smdb_put_flags_to_db2_flags(flags)
156	SMDB_FLAG flags;
157{
158	int return_flags;
159
160	return_flags = 0;
161
162	if (bitset(SMDBF_NO_OVERWRITE, flags))
163		return_flags |= DB_NOOVERWRITE;
164
165	return return_flags;
166}
167/*
168**  SMDB_CURSOR_GET_FLAGS_TO_DB2 -- Translates smdb cursor get flags to db2
169**	getflags.
170**
171**	Parameters:
172**		flags -- The flags to translate.
173**
174**	Returns:
175**		The db2 flags that are equivalent to the smdb flags.
176**
177**	Notes:
178**		-1 is returned if flag is unknown.
179**
180*/
181
182static int
183smdb_cursor_get_flags_to_db2(flags)
184	SMDB_FLAG flags;
185{
186	switch (flags)
187	{
188		case SMDB_CURSOR_GET_FIRST:
189			return DB_FIRST;
190
191		case SMDB_CURSOR_GET_LAST:
192			return DB_LAST;
193
194		case SMDB_CURSOR_GET_NEXT:
195			return DB_NEXT;
196
197		case SMDB_CURSOR_GET_RANGE:
198			return DB_SET_RANGE;
199
200		default:
201			return -1;
202	}
203}
204
205/*
206**  Except for smdb_db_open, the rest of these functions correspond to the
207**  interface laid out in smdb.h.
208*/
209
210static SMDB_DB2_DATABASE *
211smdb2_malloc_database()
212{
213	SMDB_DB2_DATABASE *db2;
214
215	db2 = (SMDB_DB2_DATABASE *) malloc(sizeof(SMDB_DB2_DATABASE));
216	if (db2 != NULL)
217		db2->smdb2_lock_fd = -1;
218
219	return db2;
220}
221
222static int
223smdb2_close(database)
224	SMDB_DATABASE *database;
225{
226	int result;
227	SMDB_DB2_DATABASE *db2 = (SMDB_DB2_DATABASE *) database->smdb_impl;
228	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
229
230	result = db2_error_to_smdb(db->close(db, 0));
231	if (db2->smdb2_lock_fd != -1)
232		close(db2->smdb2_lock_fd);
233
234	free(db2);
235	database->smdb_impl = NULL;
236
237	return result;
238}
239
240static int
241smdb2_del(database, key, flags)
242	SMDB_DATABASE *database;
243	SMDB_DBENT *key;
244	unsigned int flags;
245{
246	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
247	DBT dbkey;
248
249	(void) memset(&dbkey, '\0', sizeof dbkey);
250	dbkey.data = key->data;
251	dbkey.size = key->size;
252	return db2_error_to_smdb(db->del(db, NULL, &dbkey, flags));
253}
254
255static int
256smdb2_fd(database, fd)
257	SMDB_DATABASE *database;
258	int *fd;
259{
260	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
261
262	return db2_error_to_smdb(db->fd(db, fd));
263}
264
265static int
266smdb2_lockfd(database)
267	SMDB_DATABASE *database;
268{
269	SMDB_DB2_DATABASE *db2 = (SMDB_DB2_DATABASE *) database->smdb_impl;
270
271	return db2->smdb2_lock_fd;
272}
273
274static int
275smdb2_get(database, key, data, flags)
276	SMDB_DATABASE *database;
277	SMDB_DBENT *key;
278	SMDB_DBENT *data;
279	unsigned int flags;
280{
281	int result;
282	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
283	DBT dbkey, dbdata;
284
285	(void) memset(&dbdata, '\0', sizeof dbdata);
286	(void) memset(&dbkey, '\0', sizeof dbkey);
287	dbkey.data = key->data;
288	dbkey.size = key->size;
289
290	result = db->get(db, NULL, &dbkey, &dbdata, flags);
291	data->data = dbdata.data;
292	data->size = dbdata.size;
293	return db2_error_to_smdb(result);
294}
295
296static int
297smdb2_put(database, key, data, flags)
298	SMDB_DATABASE *database;
299	SMDB_DBENT *key;
300	SMDB_DBENT *data;
301	unsigned int flags;
302{
303	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
304	DBT dbkey, dbdata;
305
306	(void) memset(&dbdata, '\0', sizeof dbdata);
307	(void) memset(&dbkey, '\0', sizeof dbkey);
308	dbkey.data = key->data;
309	dbkey.size = key->size;
310	dbdata.data = data->data;
311	dbdata.size = data->size;
312
313	return db2_error_to_smdb(db->put(db, NULL, &dbkey, &dbdata,
314					 smdb_put_flags_to_db2_flags(flags)));
315}
316
317
318static int
319smdb2_set_owner(database, uid, gid)
320	SMDB_DATABASE *database;
321	uid_t uid;
322	gid_t gid;
323{
324# if HASFCHOWN
325	int fd;
326	int result;
327	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
328
329	result = db->fd(db, &fd);
330	if (result != 0)
331		return result;
332
333	result = fchown(fd, uid, gid);
334	if (result < 0)
335		return errno;
336# endif /* HASFCHOWN */
337
338	return SMDBE_OK;
339}
340
341static int
342smdb2_sync(database, flags)
343	SMDB_DATABASE *database;
344	unsigned int flags;
345{
346	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
347
348	return db2_error_to_smdb(db->sync(db, flags));
349}
350
351static int
352smdb2_cursor_close(cursor)
353	SMDB_CURSOR *cursor;
354{
355	int ret;
356	DBC *dbc = (DBC *) cursor->smdbc_impl;
357
358	ret = db2_error_to_smdb(dbc->c_close(dbc));
359	free(cursor);
360	return ret;
361}
362
363static int
364smdb2_cursor_del(cursor, flags)
365	SMDB_CURSOR *cursor;
366	SMDB_FLAG flags;
367{
368	DBC *dbc = (DBC *) cursor->smdbc_impl;
369
370	return db2_error_to_smdb(dbc->c_del(dbc, 0));
371}
372
373static int
374smdb2_cursor_get(cursor, key, value, flags)
375	SMDB_CURSOR *cursor;
376	SMDB_DBENT *key;
377	SMDB_DBENT *value;
378	SMDB_FLAG flags;
379{
380	int db2_flags;
381	int result;
382	DBC *dbc = (DBC *) cursor->smdbc_impl;
383	DBT dbkey, dbdata;
384
385	(void) memset(&dbdata, '\0', sizeof dbdata);
386	(void) memset(&dbkey, '\0', sizeof dbkey);
387
388	db2_flags = smdb_cursor_get_flags_to_db2(flags);
389	result = dbc->c_get(dbc, &dbkey, &dbdata, db2_flags);
390	if (result == DB_NOTFOUND)
391		return SMDBE_LAST_ENTRY;
392	key->data = dbkey.data;
393	key->size = dbkey.size;
394	value->data = dbdata.data;
395	value->size = dbdata.size;
396	return db2_error_to_smdb(result);
397}
398
399static int
400smdb2_cursor_put(cursor, key, value, flags)
401	SMDB_CURSOR *cursor;
402	SMDB_DBENT *key;
403	SMDB_DBENT *value;
404	SMDB_FLAG flags;
405{
406	DBC *dbc = (DBC *) cursor->smdbc_impl;
407	DBT dbkey, dbdata;
408
409	(void) memset(&dbdata, '\0', sizeof dbdata);
410	(void) memset(&dbkey, '\0', sizeof dbkey);
411	dbkey.data = key->data;
412	dbkey.size = key->size;
413	dbdata.data = value->data;
414	dbdata.size = value->size;
415
416	return db2_error_to_smdb(dbc->c_put(dbc, &dbkey, &dbdata, 0));
417}
418
419static int
420smdb2_cursor(database, cursor, flags)
421	SMDB_DATABASE *database;
422	SMDB_CURSOR **cursor;
423	SMDB_FLAG flags;
424{
425	int result;
426	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
427	DBC *db2_cursor;
428
429# if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6
430	result = db->cursor(db, NULL, &db2_cursor, 0);
431# else
432	result = db->cursor(db, NULL, &db2_cursor);
433# endif
434	if (result != 0)
435		return db2_error_to_smdb(result);
436
437	*cursor = (SMDB_CURSOR *) malloc(sizeof(SMDB_CURSOR));
438	if (*cursor == NULL)
439		return SMDBE_MALLOC;
440
441	(*cursor)->smdbc_close = smdb2_cursor_close;
442	(*cursor)->smdbc_del = smdb2_cursor_del;
443	(*cursor)->smdbc_get = smdb2_cursor_get;
444	(*cursor)->smdbc_put = smdb2_cursor_put;
445	(*cursor)->smdbc_impl = db2_cursor;
446
447	return SMDBE_OK;
448}
449
450# if DB_VERSION_MAJOR == 2
451static int
452smdb_db_open_internal(db_name, db_type, db_flags, db_params, db)
453	char *db_name;
454	DBTYPE db_type;
455	int db_flags;
456	SMDB_DBPARAMS *db_params;
457	DB **db;
458{
459	void *params;
460	DB_INFO db_info;
461
462	params = NULL;
463	(void) memset(&db_info, '\0', sizeof db_info);
464	if (db_params != NULL)
465	{
466		db_info.db_cachesize = db_params->smdbp_cache_size;
467		if (db_type == DB_HASH)
468			db_info.h_nelem = db_params->smdbp_num_elements;
469		if (db_params->smdbp_allow_dup)
470			db_info.flags |= DB_DUP;
471		params = &db_info;
472	}
473	return db_open(db_name, db_type, db_flags, DBMMODE, NULL, params, db);
474}
475# endif /* DB_VERSION_MAJOR == 2 */
476
477# if DB_VERSION_MAJOR > 2
478
479static void
480db_err_cb(
481#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
482	dbenv,
483#endif
484	errpfx, msg)
485#if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
486	const DB_ENV *dbenv;
487	const char *errpfx;
488	const char *msg;
489#else
490	const char *errpfx;
491	char *msg;
492#endif
493{
494	/* do not print/log any errors... */
495	return;
496}
497
498static int
499smdb_db_open_internal(db_name, db_type, db_flags, db_params, db)
500	char *db_name;
501	DBTYPE db_type;
502	int db_flags;
503	SMDB_DBPARAMS *db_params;
504	DB **db;
505{
506	int result;
507
508	result = db_create(db, NULL, 0);
509	if (result != 0 || *db == NULL)
510		return result;
511
512	(*db)->set_errcall(*db, db_err_cb);
513	if (db_params != NULL)
514	{
515		result = (*db)->set_cachesize(*db, 0,
516					      db_params->smdbp_cache_size, 0);
517		if (result != 0)
518			goto error;
519		if (db_type == DB_HASH)
520		{
521			result = (*db)->set_h_nelem(*db, db_params->smdbp_num_elements);
522			if (result != 0)
523				goto error;
524		}
525		if (db_params->smdbp_allow_dup)
526		{
527			result = (*db)->set_flags(*db, DB_DUP);
528			if (result != 0)
529				goto error;
530		}
531	}
532
533	result = (*db)->open(*db,
534			     DBTXN	/* transaction for DB 4.1 */
535			     db_name, NULL, db_type, db_flags, DBMMODE);
536  error:
537	if (result != 0)
538	{
539		(void) (*db)->close(*db, 0);
540		*db = NULL;
541	}
542	return db2_error_to_smdb(result);
543}
544# endif /* DB_VERSION_MAJOR > 2 */
545
546/*
547**  SMDB_DB_OPEN -- Opens a db database.
548**
549**	Parameters:
550**		database -- An unallocated database pointer to a pointer.
551**		db_name -- The name of the database without extension.
552**		mode -- File permisions for a created database.
553**		mode_mask -- Mode bits that must match on an opened database.
554**		sff -- Flags for safefile.
555**		type -- The type of database to open
556**			See smdb_type_to_db2_type for valid types.
557**		user_info -- User information for file permissions.
558**		db_params --
559**			An SMDB_DBPARAMS struct including params. These
560**			are processed according to the type of the
561**			database. Currently supported params (only for
562**			HASH type) are:
563**			   num_elements
564**			   cache_size
565**
566**	Returns:
567**		SMDBE_OK -- Success, other errno:
568**		SMDBE_MALLOC -- Cannot allocate memory.
569**		SMDBE_BAD_OPEN -- db_open didn't return an error, but
570**				 somehow the DB pointer is NULL.
571**		Anything else: translated error from db2
572*/
573
574int
575smdb_db_open(database, db_name, mode, mode_mask, sff, type, user_info, db_params)
576	SMDB_DATABASE **database;
577	char *db_name;
578	int mode;
579	int mode_mask;
580	long sff;
581	SMDB_DBTYPE type;
582	SMDB_USER_INFO *user_info;
583	SMDB_DBPARAMS *db_params;
584{
585	bool lockcreated = false;
586	int result;
587	int db_flags;
588	int lock_fd;
589	int db_fd;
590	int major_v, minor_v, patch_v;
591	SMDB_DATABASE *smdb_db;
592	SMDB_DB2_DATABASE *db2;
593	DB *db;
594	DBTYPE db_type;
595	struct stat stat_info;
596	char db_file_name[MAXPATHLEN];
597
598	(void) db_version(&major_v, &minor_v, &patch_v);
599	if (major_v != DB_VERSION_MAJOR || minor_v != DB_VERSION_MINOR)
600		return SMDBE_VERSION_MISMATCH;
601
602	*database = NULL;
603
604	result = smdb_add_extension(db_file_name, sizeof db_file_name,
605				    db_name, SMDB2_FILE_EXTENSION);
606	if (result != SMDBE_OK)
607		return result;
608
609	result = smdb_setup_file(db_name, SMDB2_FILE_EXTENSION,
610				 mode_mask, sff, user_info, &stat_info);
611	if (result != SMDBE_OK)
612		return result;
613
614	lock_fd = -1;
615
616	if (stat_info.st_mode == ST_MODE_NOFILE &&
617	    bitset(mode, O_CREAT))
618		lockcreated = true;
619
620	result = smdb_lock_file(&lock_fd, db_name, mode, sff,
621				SMDB2_FILE_EXTENSION);
622	if (result != SMDBE_OK)
623		return result;
624
625	if (lockcreated)
626	{
627		mode |= O_TRUNC;
628		mode &= ~(O_CREAT|O_EXCL);
629	}
630
631	smdb_db = smdb_malloc_database();
632	db2 = smdb2_malloc_database();
633	if (db2 == NULL || smdb_db == NULL)
634	{
635		smdb_unlock_file(lock_fd);
636		smdb_free_database(smdb_db);	/* ok to be NULL */
637		if (db2 != NULL)
638			free(db2);
639		return SMDBE_MALLOC;
640	}
641
642	db2->smdb2_lock_fd = lock_fd;
643
644	db_type = smdb_type_to_db2_type(type);
645
646	db = NULL;
647
648	db_flags = 0;
649	if (bitset(O_CREAT, mode))
650		db_flags |= DB_CREATE;
651	if (bitset(O_TRUNC, mode))
652		db_flags |= DB_TRUNCATE;
653	if (mode == O_RDONLY)
654		db_flags |= DB_RDONLY;
655	SM_DB_FLAG_ADD(db_flags);
656
657	result = smdb_db_open_internal(db_file_name, db_type,
658				       db_flags, db_params, &db);
659
660	if (result == 0 && db != NULL)
661	{
662		result = db->fd(db, &db_fd);
663		if (result == 0)
664			result = SMDBE_OK;
665	}
666	else
667	{
668		/* Try and narrow down on the problem */
669		if (result != 0)
670			result = db2_error_to_smdb(result);
671		else
672			result = SMDBE_BAD_OPEN;
673	}
674
675	if (result == SMDBE_OK)
676		result = smdb_filechanged(db_name, SMDB2_FILE_EXTENSION, db_fd,
677					  &stat_info);
678
679	if (result == SMDBE_OK)
680	{
681		/* Everything is ok. Setup driver */
682		db2->smdb2_db = db;
683
684		smdb_db->smdb_close = smdb2_close;
685		smdb_db->smdb_del = smdb2_del;
686		smdb_db->smdb_fd = smdb2_fd;
687		smdb_db->smdb_lockfd = smdb2_lockfd;
688		smdb_db->smdb_get = smdb2_get;
689		smdb_db->smdb_put = smdb2_put;
690		smdb_db->smdb_set_owner = smdb2_set_owner;
691		smdb_db->smdb_sync = smdb2_sync;
692		smdb_db->smdb_cursor = smdb2_cursor;
693		smdb_db->smdb_impl = db2;
694
695		*database = smdb_db;
696
697		return SMDBE_OK;
698	}
699
700	if (db != NULL)
701		db->close(db, 0);
702
703	smdb_unlock_file(db2->smdb2_lock_fd);
704	free(db2);
705	smdb_free_database(smdb_db);
706
707	return result;
708}
709
710#endif /* (DB_VERSION_MAJOR >= 2) */
711