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