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