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_CDB_OPEN -- Opens a cdb 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 permissions 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 -- unused
419**
420**	Returns:
421**		SMDBE_OK -- Success, other errno:
422**		SMDBE_MALLOC -- Cannot allocate memory.
423**		SMDBE_BAD_OPEN -- various (OS) errors.
424**		Anything else: translated error from cdb
425*/
426
427int
428smdb_cdb_open(database, db_name, mode, mode_mask, sff, type, user_info, db_params)
429	SMDB_DATABASE **database;
430	char *db_name;
431	int mode;
432	int mode_mask;
433	long sff;
434	SMDB_DBTYPE type;
435	SMDB_USER_INFO *user_info;
436	SMDB_DBPARAMS *db_params;
437{
438	bool lockcreated = false;
439	int result;
440	int lock_fd;
441	int db_fd;
442	SMDB_DATABASE *smdb_db;
443	SMDB_CDB_DATABASE *sm_cdbmap;
444	struct stat stat_info;
445	char db_file_name[MAXPATHLEN];
446
447	*database = NULL;
448	result = smdb_add_extension(db_file_name, sizeof db_file_name,
449				    db_name, SMCDB_FILE_EXTENSION);
450	if (result != SMDBE_OK)
451		return result;
452
453	result = smdb_setup_file(db_name, SMCDB_FILE_EXTENSION,
454				 mode_mask, sff, user_info, &stat_info);
455	if (result != SMDBE_OK)
456		return result;
457
458	lock_fd = -1;
459
460	if (stat_info.st_mode == ST_MODE_NOFILE &&
461	    bitset(mode, O_CREAT))
462		lockcreated = true;
463
464	result = smdb_lock_file(&lock_fd, db_name, mode, sff,
465				SMCDB_FILE_EXTENSION);
466	if (result != SMDBE_OK)
467		return result;
468
469	if (lockcreated)
470	{
471		mode |= O_TRUNC;
472		mode &= ~(O_CREAT|O_EXCL);
473	}
474
475	smdb_db = smdb_malloc_database();
476	sm_cdbmap = smcdb_malloc_database();
477	if (sm_cdbmap == NULL || smdb_db == NULL)
478	{
479		smdb_unlock_file(lock_fd);
480		smdb_free_database(smdb_db);	/* ok to be NULL */
481		if (sm_cdbmap != NULL)
482			free(sm_cdbmap);
483		return SMDBE_MALLOC;
484	}
485
486	sm_cdbmap->smcdb_lock_fd = lock_fd;
487
488# if 0
489	db = NULL;
490	db_flags = 0;
491	if (bitset(O_CREAT, mode))
492		db_flags |= DB_CREATE;
493	if (bitset(O_TRUNC, mode))
494		db_flags |= DB_TRUNCATE;
495	if (mode == O_RDONLY)
496		db_flags |= DB_RDONLY;
497	SM_DB_FLAG_ADD(db_flags);
498# endif
499
500	result = -1; /* smdb_db_open_internal(db_file_name, db_type, db_flags, db_params, &db); */
501	db_fd = open(db_file_name, mode, DBMMODE);
502	if (db_fd == -1)
503	{
504		result = SMDBE_BAD_OPEN;
505		goto error;
506	}
507
508	sm_cdbmap->cdbmap_create = (mode != O_RDONLY);
509	if (mode == O_RDONLY)
510		result = cdb_init(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd, db_fd);
511	else
512		result = cdb_make_start(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr, db_fd);
513	if (result != 0)
514	{
515		result = SMDBE_BAD_OPEN;
516		goto error;
517	}
518
519	if (result == 0)
520		result = SMDBE_OK;
521	else
522	{
523		/* Try and narrow down on the problem */
524		if (result != 0)
525			result = cdb_error_to_smdb(result);
526		else
527			result = SMDBE_BAD_OPEN;
528	}
529
530	if (result == SMDBE_OK)
531		result = smdb_filechanged(db_name, SMCDB_FILE_EXTENSION, db_fd,
532					  &stat_info);
533
534	if (result == SMDBE_OK)
535	{
536		/* Everything is ok. Setup driver */
537		/* smdb_db->smcdb_db = sm_cdbmap; */
538
539		smdb_db->smdb_close = smcdb_close;
540		smdb_db->smdb_del = smcdb_del;
541		smdb_db->smdb_fd = smcdb_fd;
542		smdb_db->smdb_lockfd = smcdb_lockfd;
543		smdb_db->smdb_get = smcdb_get;
544		smdb_db->smdb_put = smcdb_put;
545		smdb_db->smdb_set_owner = smcdb_set_owner;
546		smdb_db->smdb_sync = smcdb_sync;
547		smdb_db->smdb_cursor = smcdb_cursor;
548		smdb_db->smdb_impl = sm_cdbmap;
549
550		*database = smdb_db;
551
552		return SMDBE_OK;
553	}
554
555  error:
556	if (sm_cdbmap != NULL)
557	{
558		/* close */
559	}
560
561	smdb_unlock_file(sm_cdbmap->smcdb_lock_fd);
562	free(sm_cdbmap);
563	smdb_free_database(smdb_db);
564
565	return result;
566}
567
568#endif /* CDB */
569