1/*
2**  Copyright (c) 2018 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: smcdb.c,v 8.55 2013-11-22 20:51:49 ca Exp $")
12
13#include <fcntl.h>
14#include <stdlib.h>
15#include <unistd.h>
16
17#include <sendmail/sendmail.h>
18#include <libsmdb/smdb.h>
19
20#if CDB
21#include <assert.h>
22#include <cdb.h>
23
24typedef struct cdb	cdb_map_T, *cdb_map_P;
25typedef struct cdb_make	cdb_make_T, *cdb_make_P;
26typedef union sm_cdbs_U sm_cdbs_T, *sm_cdbs_P;
27union sm_cdbs_U
28{
29	cdb_map_T	 cdbs_cdb_rd;
30	cdb_make_T	 cdbs_cdb_wr;
31};
32
33struct smdb_cdb_database
34{
35	sm_cdbs_T	cdbmap_map;
36	int		cdbmap_fd;
37	int		smcdb_lock_fd;
38	bool		cdbmap_create;
39	unsigned	smcdb_pos;
40	int		smcdb_n;
41};
42typedef struct smdb_cdb_database SMDB_CDB_DATABASE;
43
44/* static int smdb_type_to_cdb_type __P((SMDB_DBTYPE type)); */
45static int cdb_error_to_smdb __P((int error));
46static SMDB_CDB_DATABASE * smcdb_malloc_database __P((void));
47static int smcdb_close __P((SMDB_DATABASE *database));
48static int smcdb_del __P((SMDB_DATABASE *database, SMDB_DBENT *key, unsigned int flags));
49static int smcdb_fd __P((SMDB_DATABASE *database, int *fd));
50static int smcdb_lockfd __P((SMDB_DATABASE *database));
51static int smcdb_get __P((SMDB_DATABASE *database, SMDB_DBENT *key, SMDB_DBENT *data, unsigned int flags));
52static int smcdb_put __P((SMDB_DATABASE *database, SMDB_DBENT *key, SMDB_DBENT *data, unsigned int flags));
53static int smcdb_set_owner __P((SMDB_DATABASE *database, uid_t uid, gid_t gid));
54static int smcdb_sync __P((SMDB_DATABASE *database, unsigned int flags));
55static int smcdb_cursor_close __P((SMDB_CURSOR *cursor));
56static int smcdb_cursor_del __P((SMDB_CURSOR *cursor, SMDB_FLAG flags));
57static int smcdb_cursor_get __P((SMDB_CURSOR *cursor, SMDB_DBENT *key, SMDB_DBENT *value, SMDB_FLAG flags));
58static int smcdb_cursor_put __P((SMDB_CURSOR *cursor, SMDB_DBENT *key, SMDB_DBENT *value, SMDB_FLAG flags));
59static int smcdb_cursor __P((SMDB_DATABASE *database, SMDB_CURSOR **cursor, SMDB_FLAG flags));
60
61/*
62**  SMDB_TYPE_TO_CDB_TYPE -- Translates smdb database type to cdb type.
63**
64**	Parameters:
65**		type -- The type to translate.
66**
67**	Returns:
68**		The CDB type that corresponsds to the passed in SMDB type.
69**		Returns -1 if there is no equivalent type.
70**
71*/
72
73#if 0
74static int
75smdb_type_to_cdb_type(type)
76	SMDB_DBTYPE type;
77{
78	return 0;	/* XXX */
79}
80#endif
81
82/*
83**  CDB_ERROR_TO_SMDB -- Translates cdb errors to smdbe errors
84**
85**	Parameters:
86**		error -- The error to translate.
87**
88**	Returns:
89**		The SMDBE error corresponding to the cdb error.
90**		If we don't have a corresponding error, it returns error.
91**
92*/
93
94static int
95cdb_error_to_smdb(error)
96	int error;
97{
98	int result;
99
100	switch (error)
101	{
102		case 0:
103			result = SMDBE_OK;
104			break;
105
106		default:
107			result = error;
108	}
109	return result;
110}
111
112SMDB_CDB_DATABASE *
113smcdb_malloc_database()
114{
115	SMDB_CDB_DATABASE *cdb;
116
117	cdb = (SMDB_CDB_DATABASE *) malloc(sizeof(SMDB_CDB_DATABASE));
118	if (cdb != NULL)
119		cdb->smcdb_lock_fd = -1;
120
121	return cdb;
122}
123
124static int
125smcdb_close(database)
126	SMDB_DATABASE *database;
127{
128	int result, fd;
129	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
130
131	if (NULL == sm_cdbmap)
132		return -1;
133	result = 0;
134	if (sm_cdbmap->cdbmap_create)
135		result = cdb_make_finish(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr);
136
137	fd = sm_cdbmap->cdbmap_fd;
138	if (fd >= 0)
139	{
140		close(fd);
141		sm_cdbmap->cdbmap_fd = -1;
142	}
143
144	free(sm_cdbmap);
145	database->smdb_impl = NULL;
146
147	return result;
148}
149
150static int
151smcdb_del(database, key, flags)
152	SMDB_DATABASE *database;
153	SMDB_DBENT *key;
154	unsigned int flags;
155{
156	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
157
158	assert(sm_cdbmap != NULL);
159	return -1;
160}
161
162static int
163smcdb_fd(database, fd)
164	SMDB_DATABASE *database;
165	int *fd;
166{
167	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
168	return sm_cdbmap->cdbmap_fd;
169}
170
171static int
172smcdb_lockfd(database)
173	SMDB_DATABASE *database;
174{
175	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
176
177	return sm_cdbmap->smcdb_lock_fd;
178}
179
180/*
181**  allocate/free: who does it: caller or callee?
182**  If this code does it: the "last" entry will leak.
183*/
184
185#define DBEALLOC(dbe, l)	\
186	do	\
187	{	\
188		if ((dbe)->size > 0 && l > (dbe)->size)	\
189		{	\
190			free((dbe)->data);	\
191			(dbe)->size = 0;	\
192		}	\
193		if ((dbe)->size == 0)	\
194		{	\
195			(dbe)->data = malloc(l);	\
196			if ((dbe)->data == NULL)	\
197				return SMDBE_MALLOC;	\
198			(dbe)->size = l;	\
199		}	\
200		if (l > (dbe)->size)	\
201			return SMDBE_MALLOC;	/* XXX bogus */	\
202	} while (0)
203
204
205static int
206smcdb_get(database, key, data, flags)
207	SMDB_DATABASE *database;
208	SMDB_DBENT *key;
209	SMDB_DBENT *data;
210	unsigned int flags;
211{
212	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
213	size_t l;
214	int ret;
215
216	ret = SM_SUCCESS;
217
218	if (NULL == sm_cdbmap )
219		return -1;
220	/* SM_ASSERT(!sm_cdbmap->cdbmap_create); */
221
222	/* need to lock access? single threaded access! */
223	ret = cdb_find(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
224			key->data, key->size);
225	if (ret > 0)
226	{
227		l = cdb_datalen(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
228		DBEALLOC(data, l);
229		ret = cdb_read(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
230				data->data, l,
231				cdb_datapos(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd));
232		if (ret < 0)
233			ret = -1;
234		else
235		{
236			data->size = l;
237			ret = SM_SUCCESS;
238		}
239	}
240	else
241		ret = -1;
242
243	return ret;
244}
245
246static int
247smcdb_put(database, key, data, flags)
248	SMDB_DATABASE *database;
249	SMDB_DBENT *key;
250	SMDB_DBENT *data;
251	unsigned int flags;
252{
253	int r, cdb_flags;
254	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
255
256	assert(sm_cdbmap != NULL);
257	if (bitset(SMDBF_NO_OVERWRITE, flags))
258		cdb_flags = CDB_PUT_INSERT;
259	else
260		cdb_flags = CDB_PUT_REPLACE;
261
262	r = cdb_make_put(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr,
263			key->data, key->size, data->data, data->size,
264			cdb_flags);
265	if (r > 0)
266	{
267		if (bitset(SMDBF_NO_OVERWRITE, flags))
268			return SMDBE_DUPLICATE;
269		else
270			return SMDBE_OK;
271	}
272	return r;
273}
274
275
276static int
277smcdb_set_owner(database, uid, gid)
278	SMDB_DATABASE *database;
279	uid_t uid;
280	gid_t gid;
281{
282# if HASFCHOWN
283	int fd;
284	int result;
285	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
286
287	assert(sm_cdbmap != NULL);
288	fd = sm_cdbmap->cdbmap_fd;
289	if (fd >= 0)
290	{
291		result = fchown(fd, uid, gid);
292		if (result < 0)
293			return errno;
294	}
295# endif /* HASFCHOWN */
296
297	return SMDBE_OK;
298}
299
300static int
301smcdb_sync(database, flags)
302	SMDB_DATABASE *database;
303	unsigned int flags;
304{
305	return 0;
306}
307
308static int
309smcdb_cursor_close(cursor)
310	SMDB_CURSOR *cursor;
311{
312	int ret;
313
314	ret = SMDBE_OK;
315	if (cursor != NULL)
316		free(cursor);
317	return ret;
318}
319
320static int
321smcdb_cursor_del(cursor, flags)
322	SMDB_CURSOR *cursor;
323	SMDB_FLAG flags;
324{
325	return -1;
326}
327
328static int
329smcdb_cursor_get(cursor, key, value, flags)
330	SMDB_CURSOR *cursor;
331	SMDB_DBENT *key;
332	SMDB_DBENT *value;
333	SMDB_FLAG flags;
334{
335	SMDB_CDB_DATABASE *sm_cdbmap;
336	size_t l;
337	int ret;
338
339	ret = SMDBE_OK;
340	sm_cdbmap = cursor->smdbc_impl;
341	ret = cdb_seqnext(&sm_cdbmap->smcdb_pos, &sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
342	if (ret == 0)
343		return SMDBE_LAST_ENTRY;
344	if (ret < 0)
345		return SMDBE_IO_ERROR;
346
347	l = cdb_keylen(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
348	DBEALLOC(key, l);
349
350	ret = cdb_read(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
351			key->data, l,
352			cdb_keypos(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd));
353	if (ret < 0)
354		return SMDBE_IO_ERROR;
355	key->size = l;
356
357	l = cdb_datalen(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
358
359	DBEALLOC(value, l);
360	ret = cdb_read(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
361			value->data, l,
362			cdb_datapos(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd));
363	if (ret < 0)
364		return SMDBE_IO_ERROR;
365	value->size = l;
366
367	return SMDBE_OK;
368}
369
370static int
371smcdb_cursor_put(cursor, key, value, flags)
372	SMDB_CURSOR *cursor;
373	SMDB_DBENT *key;
374	SMDB_DBENT *value;
375	SMDB_FLAG flags;
376{
377	return -1;
378}
379
380static int
381smcdb_cursor(database, cursor, flags)
382	SMDB_DATABASE *database;
383	SMDB_CURSOR **cursor;
384	SMDB_FLAG flags;
385{
386	int result;
387	SMDB_CDB_DATABASE *sm_cdbmap;
388
389	result = SMDBE_OK;
390	*cursor = (SMDB_CURSOR *) malloc(sizeof(SMDB_CURSOR));
391	if (*cursor == NULL)
392		return SMDBE_MALLOC;
393
394	sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
395	(*cursor)->smdbc_close = smcdb_cursor_close;
396	(*cursor)->smdbc_del = smcdb_cursor_del;
397	(*cursor)->smdbc_get = smcdb_cursor_get;
398	(*cursor)->smdbc_put = smcdb_cursor_put;
399	(*cursor)->smdbc_impl = sm_cdbmap;
400
401	cdb_seqinit(&sm_cdbmap->smcdb_pos, &sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
402
403	return result;
404}
405
406/*
407**  SMDB_DB_OPEN -- Opens a db database.
408**
409**	Parameters:
410**		database -- An unallocated database pointer to a pointer.
411**		db_name -- The name of the database without extension.
412**		mode -- File permisions for a created database.
413**		mode_mask -- Mode bits that must match on an opened database.
414**		sff -- Flags for safefile.
415**		type -- The type of database to open
416**			See smdb_type_to_cdb_type for valid types.
417**		user_info -- User information for file permissions.
418**		db_params --
419**			An SMDB_DBPARAMS struct including params. These
420**			are processed according to the type of the
421**			database. Currently supported params (only for
422**			HASH type) are:
423**			   num_elements
424**			   cache_size
425**
426**	Returns:
427**		SMDBE_OK -- Success, other errno:
428**		SMDBE_MALLOC -- Cannot allocate memory.
429**		SMDBE_BAD_OPEN -- db_open didn't return an error, but
430**				 somehow the DB pointer is NULL.
431**		Anything else: translated error from cdb
432*/
433
434int
435smdb_cdb_open(database, db_name, mode, mode_mask, sff, type, user_info, db_params)
436	SMDB_DATABASE **database;
437	char *db_name;
438	int mode;
439	int mode_mask;
440	long sff;
441	SMDB_DBTYPE type;
442	SMDB_USER_INFO *user_info;
443	SMDB_DBPARAMS *db_params;
444{
445	bool lockcreated = false;
446	int result;
447	int lock_fd;
448	int db_fd;
449	SMDB_DATABASE *smdb_db;
450	SMDB_CDB_DATABASE *sm_cdbmap;
451	struct stat stat_info;
452	char db_file_name[MAXPATHLEN];
453
454	*database = NULL;
455	result = smdb_add_extension(db_file_name, sizeof db_file_name,
456				    db_name, SMCDB_FILE_EXTENSION);
457	if (result != SMDBE_OK)
458		return result;
459
460	result = smdb_setup_file(db_name, SMCDB_FILE_EXTENSION,
461				 mode_mask, sff, user_info, &stat_info);
462	if (result != SMDBE_OK)
463		return result;
464
465	lock_fd = -1;
466
467	if (stat_info.st_mode == ST_MODE_NOFILE &&
468	    bitset(mode, O_CREAT))
469		lockcreated = true;
470
471	result = smdb_lock_file(&lock_fd, db_name, mode, sff,
472				SMCDB_FILE_EXTENSION);
473	if (result != SMDBE_OK)
474		return result;
475
476	if (lockcreated)
477	{
478		mode |= O_TRUNC;
479		mode &= ~(O_CREAT|O_EXCL);
480	}
481
482	smdb_db = smdb_malloc_database();
483	sm_cdbmap = smcdb_malloc_database();
484	if (sm_cdbmap == NULL || smdb_db == NULL)
485	{
486		smdb_unlock_file(lock_fd);
487		smdb_free_database(smdb_db);	/* ok to be NULL */
488		if (sm_cdbmap != NULL)
489			free(sm_cdbmap);
490		return SMDBE_MALLOC;
491	}
492
493	sm_cdbmap->smcdb_lock_fd = lock_fd;
494
495#if 0
496	db = NULL;
497	db_flags = 0;
498	if (bitset(O_CREAT, mode))
499		db_flags |= DB_CREATE;
500	if (bitset(O_TRUNC, mode))
501		db_flags |= DB_TRUNCATE;
502	if (mode == O_RDONLY)
503		db_flags |= DB_RDONLY;
504	SM_DB_FLAG_ADD(db_flags);
505#endif
506
507	result = -1; /* smdb_db_open_internal(db_file_name, db_type, db_flags, db_params, &db); */
508	db_fd = open(db_file_name, mode, DBMMODE);
509	if (db_fd == -1)
510	{
511		result = SMDBE_BAD_OPEN;
512		goto error;
513	}
514
515	sm_cdbmap->cdbmap_create = (mode != O_RDONLY);
516	if (mode == O_RDONLY)
517		result = cdb_init(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd, db_fd);
518	else
519		result = cdb_make_start(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr, db_fd);
520	if (result != 0)
521	{
522		result = SMDBE_BAD_OPEN;
523		goto error;
524	}
525
526	if (result == 0)
527		result = SMDBE_OK;
528	else
529	{
530		/* Try and narrow down on the problem */
531		if (result != 0)
532			result = cdb_error_to_smdb(result);
533		else
534			result = SMDBE_BAD_OPEN;
535	}
536
537	if (result == SMDBE_OK)
538		result = smdb_filechanged(db_name, SMCDB_FILE_EXTENSION, db_fd,
539					  &stat_info);
540
541	if (result == SMDBE_OK)
542	{
543		/* Everything is ok. Setup driver */
544		/* smdb_db->smcdb_db = sm_cdbmap; */
545
546		smdb_db->smdb_close = smcdb_close;
547		smdb_db->smdb_del = smcdb_del;
548		smdb_db->smdb_fd = smcdb_fd;
549		smdb_db->smdb_lockfd = smcdb_lockfd;
550		smdb_db->smdb_get = smcdb_get;
551		smdb_db->smdb_put = smcdb_put;
552		smdb_db->smdb_set_owner = smcdb_set_owner;
553		smdb_db->smdb_sync = smcdb_sync;
554		smdb_db->smdb_cursor = smcdb_cursor;
555		smdb_db->smdb_impl = sm_cdbmap;
556
557		*database = smdb_db;
558
559		return SMDBE_OK;
560	}
561
562  error:
563	if (sm_cdbmap != NULL)
564	{
565		/* close */
566	}
567
568	smdb_unlock_file(sm_cdbmap->smcdb_lock_fd);
569	free(sm_cdbmap);
570	smdb_free_database(smdb_db);
571
572	return result;
573}
574
575#endif /* CDB */
576