xref: /freebsd/contrib/sendmail/libsmdb/smndbm.c (revision b9f654b163bce26de79705e77b872427c9f2afa1)
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_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 	if (dbm_cursor == NULL)
442 		return SMDBE_MALLOC;
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 	{
450 		free(dbm_cursor);
451 		return SMDBE_MALLOC;
452 	}
453 
454 	cur->smdbc_impl = dbm_cursor;
455 	cur->smdbc_close = smdbm_cursor_close;
456 	cur->smdbc_del = smdbm_cursor_del;
457 	cur->smdbc_get = smdbm_cursor_get;
458 	cur->smdbc_put = smdbm_cursor_put;
459 	*cursor = cur;
460 
461 	return SMDBE_OK;
462 }
463 /*
464 **  SMDB_NDBM_OPEN -- Opens a ndbm database.
465 **
466 **	Parameters:
467 **		database -- An unallocated database pointer to a pointer.
468 **		db_name -- The name of the database without extension.
469 **		mode -- File permisions on a created database.
470 **		mode_mask -- Mode bits that much match on an opened database.
471 **		sff -- Flags to safefile.
472 **		type -- The type of database to open.
473 **			Only SMDB_NDBM is supported.
474 **		user_info -- Information on the user to use for file
475 **			    permissions.
476 **		db_params -- No params are supported.
477 **
478 **	Returns:
479 **		SMDBE_OK -- Success, otherwise errno:
480 **		SMDBE_MALLOC -- Cannot allocate memory.
481 **		SMDBE_UNSUPPORTED -- The type is not supported.
482 **		SMDBE_GDBM_IS_BAD -- We have detected GDBM and we don't
483 **				    like it.
484 **		SMDBE_BAD_OPEN -- dbm_open failed and errno was not set.
485 **		Anything else: errno
486 */
487 
488 int
489 smdb_ndbm_open(database, db_name, mode, mode_mask, sff, type, user_info,
490 	       db_params)
491 	SMDB_DATABASE **database;
492 	char *db_name;
493 	int mode;
494 	int mode_mask;
495 	long sff;
496 	SMDB_DBTYPE type;
497 	SMDB_USER_INFO *user_info;
498 	SMDB_DBPARAMS *db_params;
499 {
500 	bool lockcreated = false;
501 	int result;
502 	int lock_fd;
503 	SMDB_DATABASE *smdb_db;
504 	SMDB_DBM_DATABASE *db;
505 	DBM *dbm = NULL;
506 	struct stat dir_stat_info;
507 	struct stat pag_stat_info;
508 
509 	result = SMDBE_OK;
510 	*database = NULL;
511 
512 	if (type == NULL)
513 		return SMDBE_UNKNOWN_DB_TYPE;
514 
515 	result = smdb_setup_file(db_name, SMNDB_DIR_FILE_EXTENSION, mode_mask,
516 				 sff, user_info, &dir_stat_info);
517 	if (result != SMDBE_OK)
518 		return result;
519 
520 	result = smdb_setup_file(db_name, SMNDB_PAG_FILE_EXTENSION, mode_mask,
521 				 sff, user_info, &pag_stat_info);
522 	if (result != SMDBE_OK)
523 		return result;
524 
525 	if ((dir_stat_info.st_mode == ST_MODE_NOFILE ||
526 	     pag_stat_info.st_mode == ST_MODE_NOFILE) &&
527 	    bitset(mode, O_CREAT))
528 		lockcreated = true;
529 
530 	lock_fd = -1;
531 	result = smdb_lock_file(&lock_fd, db_name, mode, sff,
532 				SMNDB_DIR_FILE_EXTENSION);
533 	if (result != SMDBE_OK)
534 		return result;
535 
536 	if (lockcreated)
537 	{
538 		int pag_fd;
539 
540 		/* Need to pre-open the .pag file as well with O_EXCL */
541 		result = smdb_lock_file(&pag_fd, db_name, mode, sff,
542 					SMNDB_PAG_FILE_EXTENSION);
543 		if (result != SMDBE_OK)
544 		{
545 			(void) close(lock_fd);
546 			return result;
547 		}
548 		(void) close(pag_fd);
549 
550 		mode |= O_TRUNC;
551 		mode &= ~(O_CREAT|O_EXCL);
552 	}
553 
554 	smdb_db = smdb_malloc_database();
555 	if (smdb_db == NULL)
556 		result = SMDBE_MALLOC;
557 
558 	db = smdbm_malloc_database();
559 	if (db == NULL)
560 		result = SMDBE_MALLOC;
561 
562 	/* Try to open database */
563 	if (result == SMDBE_OK)
564 	{
565 		db->smndbm_lock_fd = lock_fd;
566 
567 		errno = 0;
568 		dbm = dbm_open(db_name, mode, DBMMODE);
569 		if (dbm == NULL)
570 		{
571 			if (errno == 0)
572 				result = SMDBE_BAD_OPEN;
573 			else
574 				result = errno;
575 		}
576 		db->smndbm_dbm = dbm;
577 	}
578 
579 	/* Check for GDBM */
580 	if (result == SMDBE_OK)
581 	{
582 		if (dbm_dirfno(dbm) == dbm_pagfno(dbm))
583 			result = SMDBE_GDBM_IS_BAD;
584 	}
585 
586 	/* Check for filechanged */
587 	if (result == SMDBE_OK)
588 	{
589 		result = smdb_filechanged(db_name, SMNDB_DIR_FILE_EXTENSION,
590 					  dbm_dirfno(dbm), &dir_stat_info);
591 		if (result == SMDBE_OK)
592 		{
593 			result = smdb_filechanged(db_name,
594 						  SMNDB_PAG_FILE_EXTENSION,
595 						  dbm_pagfno(dbm),
596 						  &pag_stat_info);
597 		}
598 	}
599 
600 	/* XXX Got to get fchown stuff in here */
601 
602 	/* Setup driver if everything is ok */
603 	if (result == SMDBE_OK)
604 	{
605 		*database = smdb_db;
606 
607 		smdb_db->smdb_close = smdbm_close;
608 		smdb_db->smdb_del = smdbm_del;
609 		smdb_db->smdb_fd = smdbm_fd;
610 		smdb_db->smdb_lockfd = smdbm_lockfd;
611 		smdb_db->smdb_get = smdbm_get;
612 		smdb_db->smdb_put = smdbm_put;
613 		smdb_db->smdb_set_owner = smndbm_set_owner;
614 		smdb_db->smdb_sync = smdbm_sync;
615 		smdb_db->smdb_cursor = smdbm_cursor;
616 
617 		smdb_db->smdb_impl = db;
618 
619 		return SMDBE_OK;
620 	}
621 
622 	/* If we're here, something bad happened, clean up */
623 	if (dbm != NULL)
624 		dbm_close(dbm);
625 
626 	smdb_unlock_file(db->smndbm_lock_fd);
627 	free(db);
628 	smdb_free_database(smdb_db);
629 
630 	return result;
631 }
632 #endif /* NDBM */
633