xref: /freebsd/contrib/sendmail/libsmdb/smndbm.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
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.1 2000/08/24 17:08:00 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 
128 	errno = 0;
129 	result = dbm_delete(dbm, key->dbm);
130 	if (result != 0)
131 	{
132 		int save_errno = errno;
133 
134 		if (dbm_error(dbm))
135 			return SMDBE_IO_ERROR;
136 
137 		if (save_errno != 0)
138 			return save_errno;
139 
140 		return SMDBE_NOT_FOUND;
141 	}
142 	return SMDBE_OK;
143 }
144 
145 int
146 smdbm_fd(database, fd)
147 	SMDB_DATABASE *database;
148 	int *fd;
149 {
150 	DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
151 
152 	*fd = dbm_dirfno(dbm);
153 	if (*fd <= 0)
154 		return EINVAL;
155 
156 	return SMDBE_OK;
157 }
158 
159 int
160 smdbm_lockfd(database)
161 	SMDB_DATABASE *database;
162 {
163 	SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
164 
165 	return db->smndbm_lock_fd;
166 }
167 
168 int
169 smdbm_get(database, key, data, flags)
170 	SMDB_DATABASE *database;
171 	SMDB_DBENT *key;
172 	SMDB_DBENT *data;
173 	u_int flags;
174 {
175 	DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
176 
177 	errno = 0;
178 	data->dbm = dbm_fetch(dbm, key->dbm);
179 	if (data->dbm.dptr == NULL)
180 	{
181 		int save_errno = errno;
182 
183 		if (dbm_error(dbm))
184 			return SMDBE_IO_ERROR;
185 
186 		if (save_errno != 0)
187 			return save_errno;
188 
189 		return SMDBE_NOT_FOUND;
190 	}
191 
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 	u_int flags;
201 {
202 	int result;
203 	int save_errno;
204 	DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
205 
206 	errno = 0;
207 	result = dbm_store(dbm, key->dbm, data->dbm,
208 			   smdb_put_flags_to_ndbm_flags(flags));
209 	switch (result)
210 	{
211 	  case 1:
212 		return SMDBE_DUPLICATE;
213 
214 	  case 0:
215 		return SMDBE_OK;
216 
217 	  default:
218 		save_errno = errno;
219 
220 		if (dbm_error(dbm))
221 			return SMDBE_IO_ERROR;
222 
223 		if (save_errno != 0)
224 			return save_errno;
225 
226 		return SMDBE_IO_ERROR;
227 	}
228 	/* NOTREACHED */
229 }
230 
231 int
232 smndbm_set_owner(database, uid, gid)
233 	SMDB_DATABASE *database;
234 	uid_t uid;
235 	gid_t gid;
236 {
237 # if HASFCHOWN
238 	int fd;
239 	int result;
240 	DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
241 
242 	fd = dbm_dirfno(dbm);
243 	if (fd <= 0)
244 		return EINVAL;
245 
246 	result = fchown(fd, uid, gid);
247 	if (result < 0)
248 		return errno;
249 
250 	fd = dbm_pagfno(dbm);
251 	if (fd <= 0)
252 		return EINVAL;
253 
254 	result = fchown(fd, uid, gid);
255 	if (result < 0)
256 		return errno;
257 # endif /* HASFCHOWN */
258 
259 	return SMDBE_OK;
260 }
261 
262 int
263 smdbm_sync(database, flags)
264 	SMDB_DATABASE *database;
265 	u_int flags;
266 {
267 	return SMDBE_UNSUPPORTED;
268 }
269 
270 int
271 smdbm_cursor_close(cursor)
272 	SMDB_CURSOR *cursor;
273 {
274 	SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
275 	SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
276 
277 	if (!db->smndbm_cursor_in_use)
278 		return SMDBE_NOT_A_VALID_CURSOR;
279 
280 	db->smndbm_cursor_in_use = FALSE;
281 	free(dbm_cursor);
282 	free(cursor);
283 
284 	return SMDBE_OK;
285 }
286 
287 int
288 smdbm_cursor_del(cursor, flags)
289 	SMDB_CURSOR *cursor;
290 	u_int flags;
291 {
292 	int result;
293 	SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
294 	SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
295 	DBM *dbm = db->smndbm_dbm;
296 
297 	errno = 0;
298 	result = dbm_delete(dbm, dbm_cursor->smndbmc_current_key);
299 	if (result != 0)
300 	{
301 		int save_errno = errno;
302 
303 		if (dbm_error(dbm))
304 			return SMDBE_IO_ERROR;
305 
306 		if (save_errno != 0)
307 			return save_errno;
308 
309 		return SMDBE_NOT_FOUND;
310 	}
311 	return SMDBE_OK;
312 }
313 
314 int
315 smdbm_cursor_get(cursor, key, value, flags)
316 	SMDB_CURSOR *cursor;
317 	SMDB_DBENT *key;
318 	SMDB_DBENT *value;
319 	SMDB_FLAG flags;
320 {
321 	SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
322 	SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
323 	DBM *dbm = db->smndbm_dbm;
324 
325 	if (flags == SMDB_CURSOR_GET_RANGE)
326 		return SMDBE_UNSUPPORTED;
327 
328 	if (dbm_cursor->smndbmc_current_key.dptr == NULL)
329 	{
330 		dbm_cursor->smndbmc_current_key = dbm_firstkey(dbm);
331 		if (dbm_cursor->smndbmc_current_key.dptr == NULL)
332 		{
333 			if (dbm_error(dbm))
334 				return SMDBE_IO_ERROR;
335 			return SMDBE_LAST_ENTRY;
336 		}
337 	}
338 	else
339 	{
340 		dbm_cursor->smndbmc_current_key = dbm_nextkey(dbm);
341 		if (dbm_cursor->smndbmc_current_key.dptr == NULL)
342 		{
343 			if (dbm_error(dbm))
344 				return SMDBE_IO_ERROR;
345 			return SMDBE_LAST_ENTRY;
346 		}
347 	}
348 
349 	errno = 0;
350 	value->dbm = dbm_fetch(dbm, dbm_cursor->smndbmc_current_key);
351 	if (value->dbm.dptr == NULL)
352 	{
353 		int save_errno = errno;
354 
355 		if (dbm_error(dbm))
356 			return SMDBE_IO_ERROR;
357 
358 		if (save_errno != 0)
359 			return save_errno;
360 
361 		return SMDBE_NOT_FOUND;
362 	}
363 	key->dbm = dbm_cursor->smndbmc_current_key;
364 
365 	return SMDBE_OK;
366 }
367 
368 int
369 smdbm_cursor_put(cursor, key, value, flags)
370 	SMDB_CURSOR *cursor;
371 	SMDB_DBENT *key;
372 	SMDB_DBENT *value;
373 	SMDB_FLAG flags;
374 {
375 	int result;
376 	int save_errno;
377 	SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
378 	SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
379 	DBM *dbm = db->smndbm_dbm;
380 
381 	errno = 0;
382 	result = dbm_store(dbm, dbm_cursor->smndbmc_current_key, value->dbm,
383 			   smdb_put_flags_to_ndbm_flags(flags));
384 	switch (result)
385 	{
386 	  case 1:
387 		return SMDBE_DUPLICATE;
388 
389 	  case 0:
390 		return SMDBE_OK;
391 
392 	  default:
393 		save_errno = errno;
394 
395 		if (dbm_error(dbm))
396 			return SMDBE_IO_ERROR;
397 
398 		if (save_errno != 0)
399 			return save_errno;
400 
401 		return SMDBE_IO_ERROR;
402 	}
403 	/* NOTREACHED */
404 }
405 
406 int
407 smdbm_cursor(database, cursor, flags)
408 	SMDB_DATABASE *database;
409 	SMDB_CURSOR **cursor;
410 	SMDB_FLAG flags;
411 {
412 	SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
413 	SMDB_CURSOR *cur;
414 	SMDB_DBM_CURSOR *dbm_cursor;
415 
416 	if (db->smndbm_cursor_in_use)
417 		return SMDBE_ONLY_SUPPORTS_ONE_CURSOR;
418 
419 	db->smndbm_cursor_in_use = TRUE;
420 	dbm_cursor = (SMDB_DBM_CURSOR *) malloc(sizeof(SMDB_DBM_CURSOR));
421 	dbm_cursor->smndbmc_db = db;
422 	dbm_cursor->smndbmc_current_key.dptr = NULL;
423 	dbm_cursor->smndbmc_current_key.dsize = 0;
424 
425 	cur = (SMDB_CURSOR*) malloc(sizeof(SMDB_CURSOR));
426 	if (cur == NULL)
427 		return SMDBE_MALLOC;
428 
429 	cur->smdbc_impl = dbm_cursor;
430 	cur->smdbc_close = smdbm_cursor_close;
431 	cur->smdbc_del = smdbm_cursor_del;
432 	cur->smdbc_get = smdbm_cursor_get;
433 	cur->smdbc_put = smdbm_cursor_put;
434 	*cursor = cur;
435 
436 	return SMDBE_OK;
437 }
438 
439 /*
440 **  SMDB_NDBM_OPEN -- Opens a ndbm database.
441 **
442 **	Parameters:
443 **		database -- An unallocated database pointer to a pointer.
444 **		db_name -- The name of the database without extension.
445 **		mode -- File permisions on a created database.
446 **		mode_mask -- Mode bits that much match on an opened database.
447 **		sff -- Flags to safefile.
448 **		type -- The type of database to open.
449 **			Only SMDB_NDBM is supported.
450 **		user_info -- Information on the user to use for file
451 **			    permissions.
452 **		db_params --
453 **			No params are supported.
454 **
455 **	Returns:
456 **		SMDBE_OK -- Success, otherwise errno:
457 **		SMDBE_MALLOC -- Cannot allocate memory.
458 **		SMDBE_UNSUPPORTED -- The type is not supported.
459 **		SMDBE_GDBM_IS_BAD -- We have detected GDBM and we don't
460 **				    like it.
461 **		SMDBE_BAD_OPEN -- dbm_open failed and errno was not set.
462 **		Anything else: errno
463 */
464 
465 int
466 smdb_ndbm_open(database, db_name, mode, mode_mask, sff, type, user_info,
467 	       db_params)
468 	SMDB_DATABASE **database;
469 	char *db_name;
470 	int mode;
471 	int mode_mask;
472 	long sff;
473 	SMDB_DBTYPE type;
474 	SMDB_USER_INFO *user_info;
475 	SMDB_DBPARAMS *db_params;
476 {
477 	int result;
478 	int lock_fd;
479 	SMDB_DATABASE *smdb_db;
480 	SMDB_DBM_DATABASE *db;
481 	DBM *dbm = NULL;
482 	struct stat dir_stat_info;
483 	struct stat pag_stat_info;
484 
485 	result = SMDBE_OK;
486 	*database = NULL;
487 
488 	if (type == NULL)
489 		return SMDBE_UNKNOWN_DB_TYPE;
490 
491 	result = smdb_setup_file(db_name, SMNDB_DIR_FILE_EXTENSION, mode_mask,
492 				 sff, user_info, &dir_stat_info);
493 	if (result != SMDBE_OK)
494 		return result;
495 
496 	result = smdb_setup_file(db_name, SMNDB_PAG_FILE_EXTENSION, mode_mask,
497 				 sff, user_info, &pag_stat_info);
498 	if (result != SMDBE_OK)
499 		return result;
500 
501 	lock_fd = -1;
502 # if O_EXLOCK
503 	mode |= O_EXLOCK;
504 # else /* O_EXLOCK */
505 	result = smdb_lock_file(&lock_fd, db_name, mode, sff,
506 				SMNDB_DIR_FILE_EXTENSION);
507 	if (result != SMDBE_OK)
508 		return result;
509 # endif /* O_EXLOCK */
510 
511 	smdb_db = smdb_malloc_database();
512 	if (smdb_db == NULL)
513 		result = SMDBE_MALLOC;
514 
515 	db = smdbm_malloc_database();
516 	if (db == NULL)
517 		result = SMDBE_MALLOC;
518 
519 	/* Try to open database */
520 	if (result == SMDBE_OK)
521 	{
522 		db->smndbm_lock_fd = lock_fd;
523 
524 		errno = 0;
525 		dbm = dbm_open(db_name, mode, 0644);
526 		if (dbm == NULL)
527 		{
528 			if (errno == 0)
529 				result = SMDBE_BAD_OPEN;
530 			else
531 				result = errno;
532 		}
533 		db->smndbm_dbm = dbm;
534 	}
535 
536 	/* Check for GDBM */
537 	if (result == SMDBE_OK)
538 	{
539 		if (dbm_dirfno(dbm) == dbm_pagfno(dbm))
540 			result = SMDBE_GDBM_IS_BAD;
541 	}
542 
543 	/* Check for filechanged */
544 	if (result == SMDBE_OK)
545 	{
546 		result = smdb_filechanged(db_name, SMNDB_DIR_FILE_EXTENSION,
547 					  dbm_dirfno(dbm), &dir_stat_info);
548 		if (result == SMDBE_OK)
549 		{
550 			result = smdb_filechanged(db_name,
551 						  SMNDB_PAG_FILE_EXTENSION,
552 						  dbm_pagfno(dbm),
553 						  &pag_stat_info);
554 		}
555 	}
556 
557 	/* XXX Got to get fchown stuff in here */
558 
559 	/* Setup driver if everything is ok */
560 	if (result == SMDBE_OK)
561 	{
562 		*database = smdb_db;
563 
564 		smdb_db->smdb_close = smdbm_close;
565 		smdb_db->smdb_del = smdbm_del;
566 		smdb_db->smdb_fd = smdbm_fd;
567 		smdb_db->smdb_lockfd = smdbm_lockfd;
568 		smdb_db->smdb_get = smdbm_get;
569 		smdb_db->smdb_put = smdbm_put;
570 		smdb_db->smdb_set_owner = smndbm_set_owner;
571 		smdb_db->smdb_sync = smdbm_sync;
572 		smdb_db->smdb_cursor = smdbm_cursor;
573 
574 		smdb_db->smdb_impl = db;
575 
576 		return SMDBE_OK;
577 	}
578 
579 	/* If we're here, something bad happened, clean up */
580 	if (dbm != NULL)
581 		dbm_close(dbm);
582 
583 	smdb_unlock_file(db->smndbm_lock_fd);
584 	free(db);
585 	smdb_free_database(smdb_db);
586 
587 	return result;
588 }
589 #endif /* NDBM */
590