1/*
2** Copyright (c) 1999-2002 Sendmail, 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#pragma ident	"%Z%%M%	%I%	%E% SMI"
11
12#include <sm/gen.h>
13SM_RCSID("@(#)$Id: smndbm.c,v 8.52 2002/05/21 22:30:30 gshapiro Exp $")
14
15#include <fcntl.h>
16#include <stdlib.h>
17#include <unistd.h>
18
19#include <sendmail/sendmail.h>
20#include <libsmdb/smdb.h>
21
22#ifdef NDBM
23
24# define SMNDB_DIR_FILE_EXTENSION "dir"
25# define SMNDB_PAG_FILE_EXTENSION "pag"
26
27struct smdb_dbm_database_struct
28{
29	DBM	*smndbm_dbm;
30	int	smndbm_lock_fd;
31	bool	smndbm_cursor_in_use;
32};
33typedef struct smdb_dbm_database_struct SMDB_DBM_DATABASE;
34
35struct smdb_dbm_cursor_struct
36{
37	SMDB_DBM_DATABASE	*smndbmc_db;
38	datum			smndbmc_current_key;
39};
40typedef struct smdb_dbm_cursor_struct SMDB_DBM_CURSOR;
41
42/*
43**  SMDB_PUT_FLAGS_TO_NDBM_FLAGS -- Translates smdb put flags to ndbm put flags.
44**
45**	Parameters:
46**		flags -- The flags to translate.
47**
48**	Returns:
49**		The ndbm flags that are equivalent to the smdb flags.
50**
51**	Notes:
52**		Any invalid flags are ignored.
53**
54*/
55
56int
57smdb_put_flags_to_ndbm_flags(flags)
58	SMDB_FLAG flags;
59{
60	int return_flags;
61
62	return_flags = 0;
63	if (bitset(SMDBF_NO_OVERWRITE, flags))
64		return_flags = DBM_INSERT;
65	else
66		return_flags = DBM_REPLACE;
67
68	return return_flags;
69}
70
71/*
72**  Except for smdb_ndbm_open, the rest of these function correspond to the
73**  interface laid out in smdb.h.
74*/
75
76SMDB_DBM_DATABASE *
77smdbm_malloc_database()
78{
79	SMDB_DBM_DATABASE *db;
80
81	db = (SMDB_DBM_DATABASE *) malloc(sizeof(SMDB_DBM_DATABASE));
82	if (db != NULL)
83	{
84		db->smndbm_dbm = NULL;
85		db->smndbm_lock_fd = -1;
86		db->smndbm_cursor_in_use = false;
87	}
88
89	return db;
90}
91
92int
93smdbm_close(database)
94	SMDB_DATABASE *database;
95{
96	SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
97	DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
98
99	dbm_close(dbm);
100	if (db->smndbm_lock_fd != -1)
101		close(db->smndbm_lock_fd);
102
103	free(db);
104	database->smdb_impl = NULL;
105
106	return SMDBE_OK;
107}
108
109int
110smdbm_del(database, key, flags)
111	SMDB_DATABASE *database;
112	SMDB_DBENT *key;
113	unsigned int flags;
114{
115	int result;
116	DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
117	datum dbkey;
118
119	(void) memset(&dbkey, '\0', sizeof dbkey);
120	dbkey.dptr = key->data;
121	dbkey.dsize = key->size;
122
123	errno = 0;
124	result = dbm_delete(dbm, dbkey);
125	if (result != 0)
126	{
127		int save_errno = errno;
128
129		if (dbm_error(dbm))
130			return SMDBE_IO_ERROR;
131
132		if (save_errno != 0)
133			return save_errno;
134
135		return SMDBE_NOT_FOUND;
136	}
137	return SMDBE_OK;
138}
139
140int
141smdbm_fd(database, fd)
142	SMDB_DATABASE *database;
143	int *fd;
144{
145	DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
146
147	*fd = dbm_dirfno(dbm);
148	if (*fd <= 0)
149		return EINVAL;
150
151	return SMDBE_OK;
152}
153
154int
155smdbm_lockfd(database)
156	SMDB_DATABASE *database;
157{
158	SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
159
160	return db->smndbm_lock_fd;
161}
162
163int
164smdbm_get(database, key, data, flags)
165	SMDB_DATABASE *database;
166	SMDB_DBENT *key;
167	SMDB_DBENT *data;
168	unsigned int flags;
169{
170	DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
171	datum dbkey, dbdata;
172
173	(void) memset(&dbkey, '\0', sizeof dbkey);
174	(void) memset(&dbdata, '\0', sizeof dbdata);
175	dbkey.dptr = key->data;
176	dbkey.dsize = key->size;
177
178	errno = 0;
179	dbdata = dbm_fetch(dbm, dbkey);
180	if (dbdata.dptr == NULL)
181	{
182		int save_errno = errno;
183
184		if (dbm_error(dbm))
185			return SMDBE_IO_ERROR;
186
187		if (save_errno != 0)
188			return save_errno;
189
190		return SMDBE_NOT_FOUND;
191	}
192	data->data = dbdata.dptr;
193	data->size = dbdata.dsize;
194	return SMDBE_OK;
195}
196
197int
198smdbm_put(database, key, data, flags)
199	SMDB_DATABASE *database;
200	SMDB_DBENT *key;
201	SMDB_DBENT *data;
202	unsigned int flags;
203{
204	int result;
205	int save_errno;
206	DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
207	datum dbkey, dbdata;
208
209	(void) memset(&dbkey, '\0', sizeof dbkey);
210	(void) memset(&dbdata, '\0', sizeof dbdata);
211	dbkey.dptr = key->data;
212	dbkey.dsize = key->size;
213	dbdata.dptr = data->data;
214	dbdata.dsize = data->size;
215
216	errno = 0;
217	result = dbm_store(dbm, dbkey, dbdata,
218			   smdb_put_flags_to_ndbm_flags(flags));
219	switch (result)
220	{
221	  case 1:
222		return SMDBE_DUPLICATE;
223
224	  case 0:
225		return SMDBE_OK;
226
227	  default:
228		save_errno = errno;
229
230		if (dbm_error(dbm))
231			return SMDBE_IO_ERROR;
232
233		if (save_errno != 0)
234			return save_errno;
235
236		return SMDBE_IO_ERROR;
237	}
238	/* NOTREACHED */
239}
240
241int
242smndbm_set_owner(database, uid, gid)
243	SMDB_DATABASE *database;
244	uid_t uid;
245	gid_t gid;
246{
247# if HASFCHOWN
248	int fd;
249	int result;
250	DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
251
252	fd = dbm_dirfno(dbm);
253	if (fd <= 0)
254		return EINVAL;
255
256	result = fchown(fd, uid, gid);
257	if (result < 0)
258		return errno;
259
260	fd = dbm_pagfno(dbm);
261	if (fd <= 0)
262		return EINVAL;
263
264	result = fchown(fd, uid, gid);
265	if (result < 0)
266		return errno;
267# endif /* HASFCHOWN */
268
269	return SMDBE_OK;
270}
271
272int
273smdbm_sync(database, flags)
274	SMDB_DATABASE *database;
275	unsigned int flags;
276{
277	return SMDBE_UNSUPPORTED;
278}
279
280int
281smdbm_cursor_close(cursor)
282	SMDB_CURSOR *cursor;
283{
284	SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
285	SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
286
287	if (!db->smndbm_cursor_in_use)
288		return SMDBE_NOT_A_VALID_CURSOR;
289
290	db->smndbm_cursor_in_use = false;
291	free(dbm_cursor);
292	free(cursor);
293
294	return SMDBE_OK;
295}
296
297int
298smdbm_cursor_del(cursor, flags)
299	SMDB_CURSOR *cursor;
300	unsigned int flags;
301{
302	int result;
303	SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
304	SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
305	DBM *dbm = db->smndbm_dbm;
306
307	errno = 0;
308	result = dbm_delete(dbm, dbm_cursor->smndbmc_current_key);
309	if (result != 0)
310	{
311		int save_errno = errno;
312
313		if (dbm_error(dbm))
314			return SMDBE_IO_ERROR;
315
316		if (save_errno != 0)
317			return save_errno;
318
319		return SMDBE_NOT_FOUND;
320	}
321	return SMDBE_OK;
322}
323
324int
325smdbm_cursor_get(cursor, key, value, flags)
326	SMDB_CURSOR *cursor;
327	SMDB_DBENT *key;
328	SMDB_DBENT *value;
329	SMDB_FLAG flags;
330{
331	SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
332	SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
333	DBM *dbm = db->smndbm_dbm;
334	datum dbkey, dbdata;
335
336	(void) memset(&dbkey, '\0', sizeof dbkey);
337	(void) memset(&dbdata, '\0', sizeof dbdata);
338
339	if (flags == SMDB_CURSOR_GET_RANGE)
340		return SMDBE_UNSUPPORTED;
341
342	if (dbm_cursor->smndbmc_current_key.dptr == NULL)
343	{
344		dbm_cursor->smndbmc_current_key = dbm_firstkey(dbm);
345		if (dbm_cursor->smndbmc_current_key.dptr == NULL)
346		{
347			if (dbm_error(dbm))
348				return SMDBE_IO_ERROR;
349			return SMDBE_LAST_ENTRY;
350		}
351	}
352	else
353	{
354		dbm_cursor->smndbmc_current_key = dbm_nextkey(dbm);
355		if (dbm_cursor->smndbmc_current_key.dptr == NULL)
356		{
357			if (dbm_error(dbm))
358				return SMDBE_IO_ERROR;
359			return SMDBE_LAST_ENTRY;
360		}
361	}
362
363	errno = 0;
364	dbdata = dbm_fetch(dbm, dbm_cursor->smndbmc_current_key);
365	if (dbdata.dptr == NULL)
366	{
367		int save_errno = errno;
368
369		if (dbm_error(dbm))
370			return SMDBE_IO_ERROR;
371
372		if (save_errno != 0)
373			return save_errno;
374
375		return SMDBE_NOT_FOUND;
376	}
377	value->data = dbdata.dptr;
378	value->size = dbdata.dsize;
379	key->data = dbm_cursor->smndbmc_current_key.dptr;
380	key->size = dbm_cursor->smndbmc_current_key.dsize;
381
382	return SMDBE_OK;
383}
384
385int
386smdbm_cursor_put(cursor, key, value, flags)
387	SMDB_CURSOR *cursor;
388	SMDB_DBENT *key;
389	SMDB_DBENT *value;
390	SMDB_FLAG flags;
391{
392	int result;
393	int save_errno;
394	SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
395	SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
396	DBM *dbm = db->smndbm_dbm;
397	datum dbdata;
398
399	(void) memset(&dbdata, '\0', sizeof dbdata);
400	dbdata.dptr = value->data;
401	dbdata.dsize = value->size;
402
403	errno = 0;
404	result = dbm_store(dbm, dbm_cursor->smndbmc_current_key, dbdata,
405			   smdb_put_flags_to_ndbm_flags(flags));
406	switch (result)
407	{
408	  case 1:
409		return SMDBE_DUPLICATE;
410
411	  case 0:
412		return SMDBE_OK;
413
414	  default:
415		save_errno = errno;
416
417		if (dbm_error(dbm))
418			return SMDBE_IO_ERROR;
419
420		if (save_errno != 0)
421			return save_errno;
422
423		return SMDBE_IO_ERROR;
424	}
425	/* NOTREACHED */
426}
427
428int
429smdbm_cursor(database, cursor, flags)
430	SMDB_DATABASE *database;
431	SMDB_CURSOR **cursor;
432	SMDB_FLAG flags;
433{
434	SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
435	SMDB_CURSOR *cur;
436	SMDB_DBM_CURSOR *dbm_cursor;
437
438	if (db->smndbm_cursor_in_use)
439		return SMDBE_ONLY_SUPPORTS_ONE_CURSOR;
440
441	db->smndbm_cursor_in_use = true;
442	dbm_cursor = (SMDB_DBM_CURSOR *) malloc(sizeof(SMDB_DBM_CURSOR));
443	dbm_cursor->smndbmc_db = db;
444	dbm_cursor->smndbmc_current_key.dptr = NULL;
445	dbm_cursor->smndbmc_current_key.dsize = 0;
446
447	cur = (SMDB_CURSOR*) malloc(sizeof(SMDB_CURSOR));
448	if (cur == NULL)
449		return SMDBE_MALLOC;
450
451	cur->smdbc_impl = dbm_cursor;
452	cur->smdbc_close = smdbm_cursor_close;
453	cur->smdbc_del = smdbm_cursor_del;
454	cur->smdbc_get = smdbm_cursor_get;
455	cur->smdbc_put = smdbm_cursor_put;
456	*cursor = cur;
457
458	return SMDBE_OK;
459}
460/*
461**  SMDB_NDBM_OPEN -- Opens a ndbm database.
462**
463**	Parameters:
464**		database -- An unallocated database pointer to a pointer.
465**		db_name -- The name of the database without extension.
466**		mode -- File permisions on a created database.
467**		mode_mask -- Mode bits that much match on an opened database.
468**		sff -- Flags to safefile.
469**		type -- The type of database to open.
470**			Only SMDB_NDBM is supported.
471**		user_info -- Information on the user to use for file
472**			    permissions.
473**		db_params -- No params are supported.
474**
475**	Returns:
476**		SMDBE_OK -- Success, otherwise errno:
477**		SMDBE_MALLOC -- Cannot allocate memory.
478**		SMDBE_UNSUPPORTED -- The type is not supported.
479**		SMDBE_GDBM_IS_BAD -- We have detected GDBM and we don't
480**				    like it.
481**		SMDBE_BAD_OPEN -- dbm_open failed and errno was not set.
482**		Anything else: errno
483*/
484
485int
486smdb_ndbm_open(database, db_name, mode, mode_mask, sff, type, user_info,
487	       db_params)
488	SMDB_DATABASE **database;
489	char *db_name;
490	int mode;
491	int mode_mask;
492	long sff;
493	SMDB_DBTYPE type;
494	SMDB_USER_INFO *user_info;
495	SMDB_DBPARAMS *db_params;
496{
497	bool lockcreated = false;
498	int result;
499	int lock_fd;
500	SMDB_DATABASE *smdb_db;
501	SMDB_DBM_DATABASE *db;
502	DBM *dbm = NULL;
503	struct stat dir_stat_info;
504	struct stat pag_stat_info;
505
506	result = SMDBE_OK;
507	*database = NULL;
508
509	if (type == NULL)
510		return SMDBE_UNKNOWN_DB_TYPE;
511
512	result = smdb_setup_file(db_name, SMNDB_DIR_FILE_EXTENSION, mode_mask,
513				 sff, user_info, &dir_stat_info);
514	if (result != SMDBE_OK)
515		return result;
516
517	result = smdb_setup_file(db_name, SMNDB_PAG_FILE_EXTENSION, mode_mask,
518				 sff, user_info, &pag_stat_info);
519	if (result != SMDBE_OK)
520		return result;
521
522	if ((dir_stat_info.st_mode == ST_MODE_NOFILE ||
523	     pag_stat_info.st_mode == ST_MODE_NOFILE) &&
524	    bitset(mode, O_CREAT))
525		lockcreated = true;
526
527	lock_fd = -1;
528	result = smdb_lock_file(&lock_fd, db_name, mode, sff,
529				SMNDB_DIR_FILE_EXTENSION);
530	if (result != SMDBE_OK)
531		return result;
532
533	if (lockcreated)
534	{
535		int pag_fd;
536
537		/* Need to pre-open the .pag file as well with O_EXCL */
538		result = smdb_lock_file(&pag_fd, db_name, mode, sff,
539					SMNDB_PAG_FILE_EXTENSION);
540		if (result != SMDBE_OK)
541		{
542			(void) close(lock_fd);
543			return result;
544		}
545		(void) close(pag_fd);
546
547		mode |= O_TRUNC;
548		mode &= ~(O_CREAT|O_EXCL);
549	}
550
551	smdb_db = smdb_malloc_database();
552	if (smdb_db == NULL)
553		result = SMDBE_MALLOC;
554
555	db = smdbm_malloc_database();
556	if (db == NULL)
557		result = SMDBE_MALLOC;
558
559	/* Try to open database */
560	if (result == SMDBE_OK)
561	{
562		db->smndbm_lock_fd = lock_fd;
563
564		errno = 0;
565		dbm = dbm_open(db_name, mode, DBMMODE);
566		if (dbm == NULL)
567		{
568			if (errno == 0)
569				result = SMDBE_BAD_OPEN;
570			else
571				result = errno;
572		}
573		db->smndbm_dbm = dbm;
574	}
575
576	/* Check for GDBM */
577	if (result == SMDBE_OK)
578	{
579		if (dbm_dirfno(dbm) == dbm_pagfno(dbm))
580			result = SMDBE_GDBM_IS_BAD;
581	}
582
583	/* Check for filechanged */
584	if (result == SMDBE_OK)
585	{
586		result = smdb_filechanged(db_name, SMNDB_DIR_FILE_EXTENSION,
587					  dbm_dirfno(dbm), &dir_stat_info);
588		if (result == SMDBE_OK)
589		{
590			result = smdb_filechanged(db_name,
591						  SMNDB_PAG_FILE_EXTENSION,
592						  dbm_pagfno(dbm),
593						  &pag_stat_info);
594		}
595	}
596
597	/* XXX Got to get fchown stuff in here */
598
599	/* Setup driver if everything is ok */
600	if (result == SMDBE_OK)
601	{
602		*database = smdb_db;
603
604		smdb_db->smdb_close = smdbm_close;
605		smdb_db->smdb_del = smdbm_del;
606		smdb_db->smdb_fd = smdbm_fd;
607		smdb_db->smdb_lockfd = smdbm_lockfd;
608		smdb_db->smdb_get = smdbm_get;
609		smdb_db->smdb_put = smdbm_put;
610		smdb_db->smdb_set_owner = smndbm_set_owner;
611		smdb_db->smdb_sync = smdbm_sync;
612		smdb_db->smdb_cursor = smdbm_cursor;
613
614		smdb_db->smdb_impl = db;
615
616		return SMDBE_OK;
617	}
618
619	/* If we're here, something bad happened, clean up */
620	if (dbm != NULL)
621		dbm_close(dbm);
622
623	smdb_unlock_file(db->smndbm_lock_fd);
624	free(db);
625	smdb_free_database(smdb_db);
626
627	return result;
628}
629#endif /* NDBM */
630