xref: /freebsd/contrib/sendmail/libsmdb/smdb2.c (revision 9f23cbd6cae82fd77edfad7173432fa8dccd0a95)
1 /*
2 ** Copyright (c) 1999-2003, 2009 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>
11 SM_RCSID("@(#)$Id: smdb2.c,v 8.83 2013-11-22 20:51:49 ca Exp $")
12 
13 #include <fcntl.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 
17 
18 #include <sendmail/sendmail.h>
19 #include <libsmdb/smdb.h>
20 
21 #if (DB_VERSION_MAJOR >= 2)
22 
23 struct smdb_db2_database
24 {
25 	DB	*smdb2_db;
26 	int	smdb2_lock_fd;
27 };
28 typedef struct smdb_db2_database SMDB_DB2_DATABASE;
29 
30 /*
31 **  SMDB_TYPE_TO_DB2_TYPE -- Translates smdb database type to db2 type.
32 **
33 **	Parameters:
34 **		type -- The type to translate.
35 **
36 **	Returns:
37 **		The DB2 type that corresponsds to the passed in SMDB type.
38 **		Returns -1 if there is no equivalent type.
39 **
40 */
41 
42 static DBTYPE
43 smdb_type_to_db2_type(type)
44 	SMDB_DBTYPE type;
45 {
46 	if (type == SMDB_TYPE_DEFAULT)
47 		return DB_HASH;
48 
49 	if (SMDB_IS_TYPE_HASH(type))
50 		return DB_HASH;
51 
52 	if (SMDB_IS_TYPE_BTREE(type))
53 		return DB_BTREE;
54 
55 	return DB_UNKNOWN;
56 }
57 /*
58 **  DB2_ERROR_TO_SMDB -- Translates db2 errors to smdbe errors
59 **
60 **	Parameters:
61 **		error -- The error to translate.
62 **
63 **	Returns:
64 **		The SMDBE error corresponding to the db2 error.
65 **		If we don't have a corresponding error, it returns errno.
66 **
67 */
68 
69 static int
70 db2_error_to_smdb(error)
71 	int error;
72 {
73 	int result;
74 
75 	switch (error)
76 	{
77 # ifdef DB_INCOMPLETE
78 		case DB_INCOMPLETE:
79 			result = SMDBE_INCOMPLETE;
80 			break;
81 # endif
82 
83 # ifdef DB_NOTFOUND
84 		case DB_NOTFOUND:
85 			result = SMDBE_NOT_FOUND;
86 			break;
87 # endif
88 
89 # ifdef DB_KEYEMPTY
90 		case DB_KEYEMPTY:
91 			result = SMDBE_KEY_EMPTY;
92 			break;
93 # endif
94 
95 # ifdef DB_KEYEXIST
96 		case DB_KEYEXIST:
97 			result = SMDBE_KEY_EXIST;
98 			break;
99 # endif
100 
101 # ifdef DB_LOCK_DEADLOCK
102 		case DB_LOCK_DEADLOCK:
103 			result = SMDBE_LOCK_DEADLOCK;
104 			break;
105 # endif
106 
107 # ifdef DB_LOCK_NOTGRANTED
108 		case DB_LOCK_NOTGRANTED:
109 			result = SMDBE_LOCK_NOT_GRANTED;
110 			break;
111 # endif
112 
113 # ifdef DB_LOCK_NOTHELD
114 		case DB_LOCK_NOTHELD:
115 			result = SMDBE_LOCK_NOT_HELD;
116 			break;
117 # endif
118 
119 # ifdef DB_RUNRECOVERY
120 		case DB_RUNRECOVERY:
121 			result = SMDBE_RUN_RECOVERY;
122 			break;
123 # endif
124 
125 # ifdef DB_OLD_VERSION
126 		case DB_OLD_VERSION:
127 			result = SMDBE_OLD_VERSION;
128 			break;
129 # endif
130 
131 		case 0:
132 			result = SMDBE_OK;
133 			break;
134 
135 		default:
136 			result = error;
137 	}
138 	return result;
139 }
140 /*
141 **  SMDB_PUT_FLAGS_TO_DB2_FLAGS -- Translates smdb put flags to db2 put flags.
142 **
143 **	Parameters:
144 **		flags -- The flags to translate.
145 **
146 **	Returns:
147 **		The db2 flags that are equivalent to the smdb flags.
148 **
149 **	Notes:
150 **		Any invalid flags are ignored.
151 **
152 */
153 
154 static unsigned int
155 smdb_put_flags_to_db2_flags(flags)
156 	SMDB_FLAG flags;
157 {
158 	int return_flags;
159 
160 	return_flags = 0;
161 
162 	if (bitset(SMDBF_NO_OVERWRITE, flags))
163 		return_flags |= DB_NOOVERWRITE;
164 
165 	return return_flags;
166 }
167 /*
168 **  SMDB_CURSOR_GET_FLAGS_TO_DB2 -- Translates smdb cursor get flags to db2
169 **	getflags.
170 **
171 **	Parameters:
172 **		flags -- The flags to translate.
173 **
174 **	Returns:
175 **		The db2 flags that are equivalent to the smdb flags.
176 **
177 **	Notes:
178 **		-1 is returned if flag is unknown.
179 **
180 */
181 
182 static int
183 smdb_cursor_get_flags_to_db2(flags)
184 	SMDB_FLAG flags;
185 {
186 	switch (flags)
187 	{
188 		case SMDB_CURSOR_GET_FIRST:
189 			return DB_FIRST;
190 
191 		case SMDB_CURSOR_GET_LAST:
192 			return DB_LAST;
193 
194 		case SMDB_CURSOR_GET_NEXT:
195 			return DB_NEXT;
196 
197 		case SMDB_CURSOR_GET_RANGE:
198 			return DB_SET_RANGE;
199 
200 		default:
201 			return -1;
202 	}
203 }
204 
205 /*
206 **  Except for smdb_db_open, the rest of these functions correspond to the
207 **  interface laid out in smdb.h.
208 */
209 
210 static SMDB_DB2_DATABASE *
211 smdb2_malloc_database()
212 {
213 	SMDB_DB2_DATABASE *db2;
214 
215 	db2 = (SMDB_DB2_DATABASE *) malloc(sizeof(SMDB_DB2_DATABASE));
216 	if (db2 != NULL)
217 		db2->smdb2_lock_fd = -1;
218 
219 	return db2;
220 }
221 
222 static int
223 smdb2_close(database)
224 	SMDB_DATABASE *database;
225 {
226 	int result;
227 	SMDB_DB2_DATABASE *db2 = (SMDB_DB2_DATABASE *) database->smdb_impl;
228 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
229 
230 	result = db2_error_to_smdb(db->close(db, 0));
231 	if (db2->smdb2_lock_fd != -1)
232 		close(db2->smdb2_lock_fd);
233 
234 	free(db2);
235 	database->smdb_impl = NULL;
236 
237 	return result;
238 }
239 
240 static int
241 smdb2_del(database, key, flags)
242 	SMDB_DATABASE *database;
243 	SMDB_DBENT *key;
244 	unsigned int flags;
245 {
246 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
247 	DBT dbkey;
248 
249 	(void) memset(&dbkey, '\0', sizeof dbkey);
250 	dbkey.data = key->data;
251 	dbkey.size = key->size;
252 	return db2_error_to_smdb(db->del(db, NULL, &dbkey, flags));
253 }
254 
255 static int
256 smdb2_fd(database, fd)
257 	SMDB_DATABASE *database;
258 	int *fd;
259 {
260 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
261 
262 	return db2_error_to_smdb(db->fd(db, fd));
263 }
264 
265 static int
266 smdb2_lockfd(database)
267 	SMDB_DATABASE *database;
268 {
269 	SMDB_DB2_DATABASE *db2 = (SMDB_DB2_DATABASE *) database->smdb_impl;
270 
271 	return db2->smdb2_lock_fd;
272 }
273 
274 static int
275 smdb2_get(database, key, data, flags)
276 	SMDB_DATABASE *database;
277 	SMDB_DBENT *key;
278 	SMDB_DBENT *data;
279 	unsigned int flags;
280 {
281 	int result;
282 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
283 	DBT dbkey, dbdata;
284 
285 	(void) memset(&dbdata, '\0', sizeof dbdata);
286 	(void) memset(&dbkey, '\0', sizeof dbkey);
287 	dbkey.data = key->data;
288 	dbkey.size = key->size;
289 
290 	result = db->get(db, NULL, &dbkey, &dbdata, flags);
291 	data->data = dbdata.data;
292 	data->size = dbdata.size;
293 	return db2_error_to_smdb(result);
294 }
295 
296 static int
297 smdb2_put(database, key, data, flags)
298 	SMDB_DATABASE *database;
299 	SMDB_DBENT *key;
300 	SMDB_DBENT *data;
301 	unsigned int flags;
302 {
303 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
304 	DBT dbkey, dbdata;
305 
306 	(void) memset(&dbdata, '\0', sizeof dbdata);
307 	(void) memset(&dbkey, '\0', sizeof dbkey);
308 	dbkey.data = key->data;
309 	dbkey.size = key->size;
310 	dbdata.data = data->data;
311 	dbdata.size = data->size;
312 
313 	return db2_error_to_smdb(db->put(db, NULL, &dbkey, &dbdata,
314 					 smdb_put_flags_to_db2_flags(flags)));
315 }
316 
317 
318 static int
319 smdb2_set_owner(database, uid, gid)
320 	SMDB_DATABASE *database;
321 	uid_t uid;
322 	gid_t gid;
323 {
324 # if HASFCHOWN
325 	int fd;
326 	int result;
327 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
328 
329 	result = db->fd(db, &fd);
330 	if (result != 0)
331 		return result;
332 
333 	result = fchown(fd, uid, gid);
334 	if (result < 0)
335 		return errno;
336 # endif /* HASFCHOWN */
337 
338 	return SMDBE_OK;
339 }
340 
341 static int
342 smdb2_sync(database, flags)
343 	SMDB_DATABASE *database;
344 	unsigned int flags;
345 {
346 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
347 
348 	return db2_error_to_smdb(db->sync(db, flags));
349 }
350 
351 static int
352 smdb2_cursor_close(cursor)
353 	SMDB_CURSOR *cursor;
354 {
355 	int ret;
356 	DBC *dbc = (DBC *) cursor->smdbc_impl;
357 
358 	ret = db2_error_to_smdb(dbc->c_close(dbc));
359 	free(cursor);
360 	return ret;
361 }
362 
363 static int
364 smdb2_cursor_del(cursor, flags)
365 	SMDB_CURSOR *cursor;
366 	SMDB_FLAG flags;
367 {
368 	DBC *dbc = (DBC *) cursor->smdbc_impl;
369 
370 	return db2_error_to_smdb(dbc->c_del(dbc, 0));
371 }
372 
373 static int
374 smdb2_cursor_get(cursor, key, value, flags)
375 	SMDB_CURSOR *cursor;
376 	SMDB_DBENT *key;
377 	SMDB_DBENT *value;
378 	SMDB_FLAG flags;
379 {
380 	int db2_flags;
381 	int result;
382 	DBC *dbc = (DBC *) cursor->smdbc_impl;
383 	DBT dbkey, dbdata;
384 
385 	(void) memset(&dbdata, '\0', sizeof dbdata);
386 	(void) memset(&dbkey, '\0', sizeof dbkey);
387 
388 	db2_flags = smdb_cursor_get_flags_to_db2(flags);
389 	result = dbc->c_get(dbc, &dbkey, &dbdata, db2_flags);
390 	if (result == DB_NOTFOUND)
391 		return SMDBE_LAST_ENTRY;
392 	key->data = dbkey.data;
393 	key->size = dbkey.size;
394 	value->data = dbdata.data;
395 	value->size = dbdata.size;
396 	return db2_error_to_smdb(result);
397 }
398 
399 static int
400 smdb2_cursor_put(cursor, key, value, flags)
401 	SMDB_CURSOR *cursor;
402 	SMDB_DBENT *key;
403 	SMDB_DBENT *value;
404 	SMDB_FLAG flags;
405 {
406 	DBC *dbc = (DBC *) cursor->smdbc_impl;
407 	DBT dbkey, dbdata;
408 
409 	(void) memset(&dbdata, '\0', sizeof dbdata);
410 	(void) memset(&dbkey, '\0', sizeof dbkey);
411 	dbkey.data = key->data;
412 	dbkey.size = key->size;
413 	dbdata.data = value->data;
414 	dbdata.size = value->size;
415 
416 	return db2_error_to_smdb(dbc->c_put(dbc, &dbkey, &dbdata, 0));
417 }
418 
419 static int
420 smdb2_cursor(database, cursor, flags)
421 	SMDB_DATABASE *database;
422 	SMDB_CURSOR **cursor;
423 	SMDB_FLAG flags;
424 {
425 	int result;
426 	DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db;
427 	DBC *db2_cursor;
428 
429 # if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6
430 	result = db->cursor(db, NULL, &db2_cursor, 0);
431 # else
432 	result = db->cursor(db, NULL, &db2_cursor);
433 # endif
434 	if (result != 0)
435 		return db2_error_to_smdb(result);
436 
437 	*cursor = (SMDB_CURSOR *) malloc(sizeof(SMDB_CURSOR));
438 	if (*cursor == NULL)
439 		return SMDBE_MALLOC;
440 
441 	(*cursor)->smdbc_close = smdb2_cursor_close;
442 	(*cursor)->smdbc_del = smdb2_cursor_del;
443 	(*cursor)->smdbc_get = smdb2_cursor_get;
444 	(*cursor)->smdbc_put = smdb2_cursor_put;
445 	(*cursor)->smdbc_impl = db2_cursor;
446 
447 	return SMDBE_OK;
448 }
449 
450 # if DB_VERSION_MAJOR == 2
451 static int
452 smdb_db_open_internal(db_name, db_type, db_flags, db_params, db)
453 	char *db_name;
454 	DBTYPE db_type;
455 	int db_flags;
456 	SMDB_DBPARAMS *db_params;
457 	DB **db;
458 {
459 	void *params;
460 	DB_INFO db_info;
461 
462 	params = NULL;
463 	(void) memset(&db_info, '\0', sizeof db_info);
464 	if (db_params != NULL)
465 	{
466 		db_info.db_cachesize = db_params->smdbp_cache_size;
467 		if (db_type == DB_HASH)
468 			db_info.h_nelem = db_params->smdbp_num_elements;
469 		if (db_params->smdbp_allow_dup)
470 			db_info.flags |= DB_DUP;
471 		params = &db_info;
472 	}
473 	return db_open(db_name, db_type, db_flags, DBMMODE, NULL, params, db);
474 }
475 # endif /* DB_VERSION_MAJOR == 2 */
476 
477 # if DB_VERSION_MAJOR > 2
478 
479 static void
480 db_err_cb(
481 #  if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
482 	dbenv,
483 #  endif
484 	errpfx, msg)
485 #  if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3
486 	const DB_ENV *dbenv;
487 	const char *errpfx;
488 	const char *msg;
489 #  else
490 	const char *errpfx;
491 	char *msg;
492 #  endif
493 {
494 	/* do not print/log any errors... */
495 	return;
496 }
497 
498 static int
499 smdb_db_open_internal(db_name, db_type, db_flags, db_params, db)
500 	char *db_name;
501 	DBTYPE db_type;
502 	int db_flags;
503 	SMDB_DBPARAMS *db_params;
504 	DB **db;
505 {
506 	int result;
507 
508 	result = db_create(db, NULL, 0);
509 	if (result != 0 || *db == NULL)
510 		return result;
511 
512 	(*db)->set_errcall(*db, db_err_cb);
513 	if (db_params != NULL)
514 	{
515 		result = (*db)->set_cachesize(*db, 0,
516 					      db_params->smdbp_cache_size, 0);
517 		if (result != 0)
518 			goto error;
519 		if (db_type == DB_HASH)
520 		{
521 			result = (*db)->set_h_nelem(*db, db_params->smdbp_num_elements);
522 			if (result != 0)
523 				goto error;
524 		}
525 		if (db_params->smdbp_allow_dup)
526 		{
527 			result = (*db)->set_flags(*db, DB_DUP);
528 			if (result != 0)
529 				goto error;
530 		}
531 	}
532 
533 	result = (*db)->open(*db,
534 			     DBTXN	/* transaction for DB 4.1 */
535 			     db_name, NULL, db_type, db_flags, DBMMODE);
536   error:
537 	if (result != 0)
538 	{
539 		(void) (*db)->close(*db, 0);
540 		*db = NULL;
541 	}
542 	return db2_error_to_smdb(result);
543 }
544 # endif /* DB_VERSION_MAJOR > 2 */
545 
546 /*
547 **  SMDB_DB_OPEN -- Opens a db database.
548 **
549 **	Parameters:
550 **		database -- An unallocated database pointer to a pointer.
551 **		db_name -- The name of the database without extension.
552 **		mode -- File permisions for a created database.
553 **		mode_mask -- Mode bits that must match on an opened database.
554 **		sff -- Flags for safefile.
555 **		type -- The type of database to open
556 **			See smdb_type_to_db2_type for valid types.
557 **		user_info -- User information for file permissions.
558 **		db_params --
559 **			An SMDB_DBPARAMS struct including params. These
560 **			are processed according to the type of the
561 **			database. Currently supported params (only for
562 **			HASH type) are:
563 **			   num_elements
564 **			   cache_size
565 **
566 **	Returns:
567 **		SMDBE_OK -- Success, other errno:
568 **		SMDBE_MALLOC -- Cannot allocate memory.
569 **		SMDBE_BAD_OPEN -- db_open didn't return an error, but
570 **				 somehow the DB pointer is NULL.
571 **		Anything else: translated error from db2
572 */
573 
574 int
575 smdb_db_open(database, db_name, mode, mode_mask, sff, type, user_info, db_params)
576 	SMDB_DATABASE **database;
577 	char *db_name;
578 	int mode;
579 	int mode_mask;
580 	long sff;
581 	SMDB_DBTYPE type;
582 	SMDB_USER_INFO *user_info;
583 	SMDB_DBPARAMS *db_params;
584 {
585 	bool lockcreated = false;
586 	int result;
587 	int db_flags;
588 	int lock_fd;
589 	int db_fd;
590 	int major_v, minor_v, patch_v;
591 	SMDB_DATABASE *smdb_db;
592 	SMDB_DB2_DATABASE *db2;
593 	DB *db;
594 	DBTYPE db_type;
595 	struct stat stat_info;
596 	char db_file_name[MAXPATHLEN];
597 
598 	(void) db_version(&major_v, &minor_v, &patch_v);
599 	if (major_v != DB_VERSION_MAJOR || minor_v != DB_VERSION_MINOR)
600 		return SMDBE_VERSION_MISMATCH;
601 
602 	*database = NULL;
603 
604 	result = smdb_add_extension(db_file_name, sizeof db_file_name,
605 				    db_name, SMDB2_FILE_EXTENSION);
606 	if (result != SMDBE_OK)
607 		return result;
608 
609 	result = smdb_setup_file(db_name, SMDB2_FILE_EXTENSION,
610 				 mode_mask, sff, user_info, &stat_info);
611 	if (result != SMDBE_OK)
612 		return result;
613 
614 	lock_fd = -1;
615 
616 	if (stat_info.st_mode == ST_MODE_NOFILE &&
617 	    bitset(mode, O_CREAT))
618 		lockcreated = true;
619 
620 	result = smdb_lock_file(&lock_fd, db_name, mode, sff,
621 				SMDB2_FILE_EXTENSION);
622 	if (result != SMDBE_OK)
623 		return result;
624 
625 	if (lockcreated)
626 	{
627 		mode |= O_TRUNC;
628 		mode &= ~(O_CREAT|O_EXCL);
629 	}
630 
631 	smdb_db = smdb_malloc_database();
632 	db2 = smdb2_malloc_database();
633 	if (db2 == NULL || smdb_db == NULL)
634 	{
635 		smdb_unlock_file(lock_fd);
636 		smdb_free_database(smdb_db);	/* ok to be NULL */
637 		if (db2 != NULL)
638 			free(db2);
639 		return SMDBE_MALLOC;
640 	}
641 
642 	db2->smdb2_lock_fd = lock_fd;
643 
644 	db_type = smdb_type_to_db2_type(type);
645 
646 	db = NULL;
647 
648 	db_flags = 0;
649 	if (bitset(O_CREAT, mode))
650 		db_flags |= DB_CREATE;
651 	if (bitset(O_TRUNC, mode))
652 		db_flags |= DB_TRUNCATE;
653 	if (mode == O_RDONLY)
654 		db_flags |= DB_RDONLY;
655 	SM_DB_FLAG_ADD(db_flags);
656 
657 	result = smdb_db_open_internal(db_file_name, db_type,
658 				       db_flags, db_params, &db);
659 
660 	if (result == 0 && db != NULL)
661 	{
662 		result = db->fd(db, &db_fd);
663 		if (result == 0)
664 			result = SMDBE_OK;
665 	}
666 	else
667 	{
668 		/* Try and narrow down on the problem */
669 		if (result != 0)
670 			result = db2_error_to_smdb(result);
671 		else
672 			result = SMDBE_BAD_OPEN;
673 	}
674 
675 	if (result == SMDBE_OK)
676 		result = smdb_filechanged(db_name, SMDB2_FILE_EXTENSION, db_fd,
677 					  &stat_info);
678 
679 	if (result == SMDBE_OK)
680 	{
681 		/* Everything is ok. Setup driver */
682 		db2->smdb2_db = db;
683 
684 		smdb_db->smdb_close = smdb2_close;
685 		smdb_db->smdb_del = smdb2_del;
686 		smdb_db->smdb_fd = smdb2_fd;
687 		smdb_db->smdb_lockfd = smdb2_lockfd;
688 		smdb_db->smdb_get = smdb2_get;
689 		smdb_db->smdb_put = smdb2_put;
690 		smdb_db->smdb_set_owner = smdb2_set_owner;
691 		smdb_db->smdb_sync = smdb2_sync;
692 		smdb_db->smdb_cursor = smdb2_cursor;
693 		smdb_db->smdb_impl = db2;
694 
695 		*database = smdb_db;
696 
697 		return SMDBE_OK;
698 	}
699 
700 	if (db != NULL)
701 		db->close(db, 0);
702 
703 	smdb_unlock_file(db2->smdb2_lock_fd);
704 	free(db2);
705 	smdb_free_database(smdb_db);
706 
707 	return result;
708 }
709 
710 #endif /* (DB_VERSION_MAJOR >= 2) */
711