xref: /freebsd/contrib/sendmail/libsmdb/smcdb.c (revision f81cdf24ba5436367377f7c8e8f51f6df2a75ca7)
1 /*
2 **  Copyright (c) 2018 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: smcdb.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 #if CDB
21 #include <assert.h>
22 #include <cdb.h>
23 
24 typedef struct cdb	cdb_map_T, *cdb_map_P;
25 typedef struct cdb_make	cdb_make_T, *cdb_make_P;
26 typedef union sm_cdbs_U sm_cdbs_T, *sm_cdbs_P;
27 union sm_cdbs_U
28 {
29 	cdb_map_T	 cdbs_cdb_rd;
30 	cdb_make_T	 cdbs_cdb_wr;
31 };
32 
33 struct smdb_cdb_database
34 {
35 	sm_cdbs_T	cdbmap_map;
36 	int		cdbmap_fd;
37 	int		smcdb_lock_fd;
38 	bool		cdbmap_create;
39 	unsigned	smcdb_pos;
40 	int		smcdb_n;
41 };
42 typedef struct smdb_cdb_database SMDB_CDB_DATABASE;
43 
44 /* static int smdb_type_to_cdb_type __P((SMDB_DBTYPE type)); */
45 static int cdb_error_to_smdb __P((int error));
46 static SMDB_CDB_DATABASE * smcdb_malloc_database __P((void));
47 static int smcdb_close __P((SMDB_DATABASE *database));
48 static int smcdb_del __P((SMDB_DATABASE *database, SMDB_DBENT *key, unsigned int flags));
49 static int smcdb_fd __P((SMDB_DATABASE *database, int *fd));
50 static int smcdb_lockfd __P((SMDB_DATABASE *database));
51 static int smcdb_get __P((SMDB_DATABASE *database, SMDB_DBENT *key, SMDB_DBENT *data, unsigned int flags));
52 static int smcdb_put __P((SMDB_DATABASE *database, SMDB_DBENT *key, SMDB_DBENT *data, unsigned int flags));
53 static int smcdb_set_owner __P((SMDB_DATABASE *database, uid_t uid, gid_t gid));
54 static int smcdb_sync __P((SMDB_DATABASE *database, unsigned int flags));
55 static int smcdb_cursor_close __P((SMDB_CURSOR *cursor));
56 static int smcdb_cursor_del __P((SMDB_CURSOR *cursor, SMDB_FLAG flags));
57 static int smcdb_cursor_get __P((SMDB_CURSOR *cursor, SMDB_DBENT *key, SMDB_DBENT *value, SMDB_FLAG flags));
58 static int smcdb_cursor_put __P((SMDB_CURSOR *cursor, SMDB_DBENT *key, SMDB_DBENT *value, SMDB_FLAG flags));
59 static int smcdb_cursor __P((SMDB_DATABASE *database, SMDB_CURSOR **cursor, SMDB_FLAG flags));
60 
61 /*
62 **  SMDB_TYPE_TO_CDB_TYPE -- Translates smdb database type to cdb type.
63 **
64 **	Parameters:
65 **		type -- The type to translate.
66 **
67 **	Returns:
68 **		The CDB type that corresponsds to the passed in SMDB type.
69 **		Returns -1 if there is no equivalent type.
70 **
71 */
72 
73 # if 0
74 static int
75 smdb_type_to_cdb_type(type)
76 	SMDB_DBTYPE type;
77 {
78 	return 0;	/* XXX */
79 }
80 # endif
81 
82 /*
83 **  CDB_ERROR_TO_SMDB -- Translates cdb errors to smdbe errors
84 **
85 **	Parameters:
86 **		error -- The error to translate.
87 **
88 **	Returns:
89 **		The SMDBE error corresponding to the cdb error.
90 **		If we don't have a corresponding error, it returns error.
91 **
92 */
93 
94 static int
95 cdb_error_to_smdb(error)
96 	int error;
97 {
98 	int result;
99 
100 	switch (error)
101 	{
102 		case 0:
103 			result = SMDBE_OK;
104 			break;
105 
106 		default:
107 			result = error;
108 	}
109 	return result;
110 }
111 
112 SMDB_CDB_DATABASE *
113 smcdb_malloc_database()
114 {
115 	SMDB_CDB_DATABASE *cdb;
116 
117 	cdb = (SMDB_CDB_DATABASE *) malloc(sizeof(SMDB_CDB_DATABASE));
118 	if (cdb != NULL)
119 		cdb->smcdb_lock_fd = -1;
120 
121 	return cdb;
122 }
123 
124 static int
125 smcdb_close(database)
126 	SMDB_DATABASE *database;
127 {
128 	int result, fd;
129 	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
130 
131 	if (NULL == sm_cdbmap)
132 		return -1;
133 	result = 0;
134 	if (sm_cdbmap->cdbmap_create)
135 		result = cdb_make_finish(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr);
136 
137 	fd = sm_cdbmap->cdbmap_fd;
138 	if (fd >= 0)
139 	{
140 		close(fd);
141 		sm_cdbmap->cdbmap_fd = -1;
142 	}
143 
144 	free(sm_cdbmap);
145 	database->smdb_impl = NULL;
146 
147 	return result;
148 }
149 
150 static int
151 smcdb_del(database, key, flags)
152 	SMDB_DATABASE *database;
153 	SMDB_DBENT *key;
154 	unsigned int flags;
155 {
156 	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
157 
158 	assert(sm_cdbmap != NULL);
159 	return -1;
160 }
161 
162 static int
163 smcdb_fd(database, fd)
164 	SMDB_DATABASE *database;
165 	int *fd;
166 {
167 	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
168 	return sm_cdbmap->cdbmap_fd;
169 }
170 
171 static int
172 smcdb_lockfd(database)
173 	SMDB_DATABASE *database;
174 {
175 	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
176 
177 	return sm_cdbmap->smcdb_lock_fd;
178 }
179 
180 /*
181 **  allocate/free: who does it: caller or callee?
182 **  If this code does it: the "last" entry will leak.
183 */
184 
185 #define DBEALLOC(dbe, l)	\
186 	do	\
187 	{	\
188 		if ((dbe)->size > 0 && l > (dbe)->size)	\
189 		{	\
190 			free((dbe)->data);	\
191 			(dbe)->size = 0;	\
192 		}	\
193 		if ((dbe)->size == 0)	\
194 		{	\
195 			(dbe)->data = malloc(l);	\
196 			if ((dbe)->data == NULL)	\
197 				return SMDBE_MALLOC;	\
198 			(dbe)->size = l;	\
199 		}	\
200 		if (l > (dbe)->size)	\
201 			return SMDBE_MALLOC;	/* XXX bogus */	\
202 	} while (0)
203 
204 
205 static int
206 smcdb_get(database, key, data, flags)
207 	SMDB_DATABASE *database;
208 	SMDB_DBENT *key;
209 	SMDB_DBENT *data;
210 	unsigned int flags;
211 {
212 	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
213 	size_t l;
214 	int ret;
215 
216 	ret = SM_SUCCESS;
217 
218 	if (NULL == sm_cdbmap )
219 		return -1;
220 	/* SM_ASSERT(!sm_cdbmap->cdbmap_create); */
221 
222 	/* need to lock access? single threaded access! */
223 	ret = cdb_find(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
224 			key->data, key->size);
225 	if (ret > 0)
226 	{
227 		l = cdb_datalen(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
228 		DBEALLOC(data, l);
229 		ret = cdb_read(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
230 				data->data, l,
231 				cdb_datapos(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd));
232 		if (ret < 0)
233 			ret = -1;
234 		else
235 		{
236 			data->size = l;
237 			ret = SM_SUCCESS;
238 		}
239 	}
240 	else
241 		ret = -1;
242 
243 	return ret;
244 }
245 
246 static int
247 smcdb_put(database, key, data, flags)
248 	SMDB_DATABASE *database;
249 	SMDB_DBENT *key;
250 	SMDB_DBENT *data;
251 	unsigned int flags;
252 {
253 	int r, cdb_flags;
254 	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
255 
256 	assert(sm_cdbmap != NULL);
257 	if (bitset(SMDBF_NO_OVERWRITE, flags))
258 		cdb_flags = CDB_PUT_INSERT;
259 	else
260 		cdb_flags = CDB_PUT_REPLACE;
261 
262 	r = cdb_make_put(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr,
263 			key->data, key->size, data->data, data->size,
264 			cdb_flags);
265 	if (r > 0)
266 	{
267 		if (bitset(SMDBF_NO_OVERWRITE, flags))
268 			return SMDBE_DUPLICATE;
269 		else
270 			return SMDBE_OK;
271 	}
272 	return r;
273 }
274 
275 
276 static int
277 smcdb_set_owner(database, uid, gid)
278 	SMDB_DATABASE *database;
279 	uid_t uid;
280 	gid_t gid;
281 {
282 # if HASFCHOWN
283 	int fd;
284 	int result;
285 	SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
286 
287 	assert(sm_cdbmap != NULL);
288 	fd = sm_cdbmap->cdbmap_fd;
289 	if (fd >= 0)
290 	{
291 		result = fchown(fd, uid, gid);
292 		if (result < 0)
293 			return errno;
294 	}
295 # endif /* HASFCHOWN */
296 
297 	return SMDBE_OK;
298 }
299 
300 static int
301 smcdb_sync(database, flags)
302 	SMDB_DATABASE *database;
303 	unsigned int flags;
304 {
305 	return 0;
306 }
307 
308 static int
309 smcdb_cursor_close(cursor)
310 	SMDB_CURSOR *cursor;
311 {
312 	int ret;
313 
314 	ret = SMDBE_OK;
315 	if (cursor != NULL)
316 		free(cursor);
317 	return ret;
318 }
319 
320 static int
321 smcdb_cursor_del(cursor, flags)
322 	SMDB_CURSOR *cursor;
323 	SMDB_FLAG flags;
324 {
325 	return -1;
326 }
327 
328 static int
329 smcdb_cursor_get(cursor, key, value, flags)
330 	SMDB_CURSOR *cursor;
331 	SMDB_DBENT *key;
332 	SMDB_DBENT *value;
333 	SMDB_FLAG flags;
334 {
335 	SMDB_CDB_DATABASE *sm_cdbmap;
336 	size_t l;
337 	int ret;
338 
339 	ret = SMDBE_OK;
340 	sm_cdbmap = cursor->smdbc_impl;
341 	ret = cdb_seqnext(&sm_cdbmap->smcdb_pos, &sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
342 	if (ret == 0)
343 		return SMDBE_LAST_ENTRY;
344 	if (ret < 0)
345 		return SMDBE_IO_ERROR;
346 
347 	l = cdb_keylen(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
348 	DBEALLOC(key, l);
349 
350 	ret = cdb_read(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
351 			key->data, l,
352 			cdb_keypos(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd));
353 	if (ret < 0)
354 		return SMDBE_IO_ERROR;
355 	key->size = l;
356 
357 	l = cdb_datalen(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
358 
359 	DBEALLOC(value, l);
360 	ret = cdb_read(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
361 			value->data, l,
362 			cdb_datapos(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd));
363 	if (ret < 0)
364 		return SMDBE_IO_ERROR;
365 	value->size = l;
366 
367 	return SMDBE_OK;
368 }
369 
370 static int
371 smcdb_cursor_put(cursor, key, value, flags)
372 	SMDB_CURSOR *cursor;
373 	SMDB_DBENT *key;
374 	SMDB_DBENT *value;
375 	SMDB_FLAG flags;
376 {
377 	return -1;
378 }
379 
380 static int
381 smcdb_cursor(database, cursor, flags)
382 	SMDB_DATABASE *database;
383 	SMDB_CURSOR **cursor;
384 	SMDB_FLAG flags;
385 {
386 	int result;
387 	SMDB_CDB_DATABASE *sm_cdbmap;
388 
389 	result = SMDBE_OK;
390 	*cursor = (SMDB_CURSOR *) malloc(sizeof(SMDB_CURSOR));
391 	if (*cursor == NULL)
392 		return SMDBE_MALLOC;
393 
394 	sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
395 	(*cursor)->smdbc_close = smcdb_cursor_close;
396 	(*cursor)->smdbc_del = smcdb_cursor_del;
397 	(*cursor)->smdbc_get = smcdb_cursor_get;
398 	(*cursor)->smdbc_put = smcdb_cursor_put;
399 	(*cursor)->smdbc_impl = sm_cdbmap;
400 
401 	cdb_seqinit(&sm_cdbmap->smcdb_pos, &sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
402 
403 	return result;
404 }
405 
406 /*
407 **  SMDB_CDB_OPEN -- Opens a cdb database.
408 **
409 **	Parameters:
410 **		database -- An unallocated database pointer to a pointer.
411 **		db_name -- The name of the database without extension.
412 **		mode -- File permissions for a created database.
413 **		mode_mask -- Mode bits that must match on an opened database.
414 **		sff -- Flags for safefile.
415 **		type -- The type of database to open
416 **			See smdb_type_to_cdb_type for valid types.
417 **		user_info -- User information for file permissions.
418 **		db_params -- unused
419 **
420 **	Returns:
421 **		SMDBE_OK -- Success, other errno:
422 **		SMDBE_MALLOC -- Cannot allocate memory.
423 **		SMDBE_BAD_OPEN -- various (OS) errors.
424 **		Anything else: translated error from cdb
425 */
426 
427 int
428 smdb_cdb_open(database, db_name, mode, mode_mask, sff, type, user_info, db_params)
429 	SMDB_DATABASE **database;
430 	char *db_name;
431 	int mode;
432 	int mode_mask;
433 	long sff;
434 	SMDB_DBTYPE type;
435 	SMDB_USER_INFO *user_info;
436 	SMDB_DBPARAMS *db_params;
437 {
438 	bool lockcreated = false;
439 	int result;
440 	int lock_fd;
441 	int db_fd;
442 	SMDB_DATABASE *smdb_db;
443 	SMDB_CDB_DATABASE *sm_cdbmap;
444 	struct stat stat_info;
445 	char db_file_name[MAXPATHLEN];
446 
447 	*database = NULL;
448 	result = smdb_add_extension(db_file_name, sizeof db_file_name,
449 				    db_name, SMCDB_FILE_EXTENSION);
450 	if (result != SMDBE_OK)
451 		return result;
452 
453 	result = smdb_setup_file(db_name, SMCDB_FILE_EXTENSION,
454 				 mode_mask, sff, user_info, &stat_info);
455 	if (result != SMDBE_OK)
456 		return result;
457 
458 	lock_fd = -1;
459 
460 	if (stat_info.st_mode == ST_MODE_NOFILE &&
461 	    bitset(mode, O_CREAT))
462 		lockcreated = true;
463 
464 	result = smdb_lock_file(&lock_fd, db_name, mode, sff,
465 				SMCDB_FILE_EXTENSION);
466 	if (result != SMDBE_OK)
467 		return result;
468 
469 	if (lockcreated)
470 	{
471 		mode |= O_TRUNC;
472 		mode &= ~(O_CREAT|O_EXCL);
473 	}
474 
475 	smdb_db = smdb_malloc_database();
476 	sm_cdbmap = smcdb_malloc_database();
477 	if (sm_cdbmap == NULL || smdb_db == NULL)
478 	{
479 		smdb_unlock_file(lock_fd);
480 		smdb_free_database(smdb_db);	/* ok to be NULL */
481 		if (sm_cdbmap != NULL)
482 			free(sm_cdbmap);
483 		return SMDBE_MALLOC;
484 	}
485 
486 	sm_cdbmap->smcdb_lock_fd = lock_fd;
487 
488 # if 0
489 	db = NULL;
490 	db_flags = 0;
491 	if (bitset(O_CREAT, mode))
492 		db_flags |= DB_CREATE;
493 	if (bitset(O_TRUNC, mode))
494 		db_flags |= DB_TRUNCATE;
495 	if (mode == O_RDONLY)
496 		db_flags |= DB_RDONLY;
497 	SM_DB_FLAG_ADD(db_flags);
498 # endif
499 
500 	result = -1; /* smdb_db_open_internal(db_file_name, db_type, db_flags, db_params, &db); */
501 	db_fd = open(db_file_name, mode, DBMMODE);
502 	if (db_fd == -1)
503 	{
504 		result = SMDBE_BAD_OPEN;
505 		goto error;
506 	}
507 
508 	sm_cdbmap->cdbmap_create = (mode != O_RDONLY);
509 	if (mode == O_RDONLY)
510 		result = cdb_init(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd, db_fd);
511 	else
512 		result = cdb_make_start(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr, db_fd);
513 	if (result != 0)
514 	{
515 		result = SMDBE_BAD_OPEN;
516 		goto error;
517 	}
518 
519 	if (result == 0)
520 		result = SMDBE_OK;
521 	else
522 	{
523 		/* Try and narrow down on the problem */
524 		if (result != 0)
525 			result = cdb_error_to_smdb(result);
526 		else
527 			result = SMDBE_BAD_OPEN;
528 	}
529 
530 	if (result == SMDBE_OK)
531 		result = smdb_filechanged(db_name, SMCDB_FILE_EXTENSION, db_fd,
532 					  &stat_info);
533 
534 	if (result == SMDBE_OK)
535 	{
536 		/* Everything is ok. Setup driver */
537 		/* smdb_db->smcdb_db = sm_cdbmap; */
538 
539 		smdb_db->smdb_close = smcdb_close;
540 		smdb_db->smdb_del = smcdb_del;
541 		smdb_db->smdb_fd = smcdb_fd;
542 		smdb_db->smdb_lockfd = smcdb_lockfd;
543 		smdb_db->smdb_get = smcdb_get;
544 		smdb_db->smdb_put = smcdb_put;
545 		smdb_db->smdb_set_owner = smcdb_set_owner;
546 		smdb_db->smdb_sync = smcdb_sync;
547 		smdb_db->smdb_cursor = smcdb_cursor;
548 		smdb_db->smdb_impl = sm_cdbmap;
549 
550 		*database = smdb_db;
551 
552 		return SMDBE_OK;
553 	}
554 
555   error:
556 	if (sm_cdbmap != NULL)
557 	{
558 		/* close */
559 	}
560 
561 	smdb_unlock_file(sm_cdbmap->smcdb_lock_fd);
562 	free(sm_cdbmap);
563 	smdb_free_database(smdb_db);
564 
565 	return result;
566 }
567 
568 #endif /* CDB */
569