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