xref: /freebsd/contrib/sendmail/libsmdb/smndbm.c (revision 2a58b312b62f908ec92311d1bd8536dbaeb8e55b)
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>
11 SM_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 
24 struct smdb_dbm_database_struct
25 {
26 	DBM	*smndbm_dbm;
27 	int	smndbm_lock_fd;
28 	bool	smndbm_cursor_in_use;
29 };
30 typedef struct smdb_dbm_database_struct SMDB_DBM_DATABASE;
31 
32 struct smdb_dbm_cursor_struct
33 {
34 	SMDB_DBM_DATABASE	*smndbmc_db;
35 	datum			smndbmc_current_key;
36 };
37 typedef 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 
53 int
54 smdb_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 
73 SMDB_DBM_DATABASE *
74 smdbm_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 
89 int
90 smdbm_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 
106 int
107 smdbm_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 
137 int
138 smdbm_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 
151 int
152 smdbm_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 
160 int
161 smdbm_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 
194 int
195 smdbm_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 
238 int
239 smndbm_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 
269 int
270 smdbm_sync(database, flags)
271 	SMDB_DATABASE *database;
272 	unsigned int flags;
273 {
274 	return SMDBE_UNSUPPORTED;
275 }
276 
277 int
278 smdbm_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 
294 int
295 smdbm_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 
321 int
322 smdbm_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 
382 int
383 smdbm_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 
425 int
426 smdbm_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 
487 int
488 smdb_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