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