1/*
2   Unix SMB/CIFS implementation.
3   tdb utility functions
4   Copyright (C) Andrew Tridgell   1992-1998
5   Copyright (C) Rafal Szczesniak  2002
6   Copyright (C) Michael Adam      2007
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "includes.h"
23#undef malloc
24#undef realloc
25#undef calloc
26#undef strdup
27
28/* these are little tdb utility functions that are meant to make
29   dealing with a tdb database a little less cumbersome in Samba */
30
31static SIG_ATOMIC_T gotalarm;
32
33/***************************************************************
34 Signal function to tell us we timed out.
35****************************************************************/
36
37static void gotalarm_sig(void)
38{
39	gotalarm = 1;
40}
41
42/****************************************************************************
43 Lock a chain with timeout (in seconds).
44****************************************************************************/
45
46static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout, int rw_type)
47{
48	/* Allow tdb_chainlock to be interrupted by an alarm. */
49	int ret;
50	gotalarm = 0;
51
52	if (timeout) {
53		CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
54		tdb_setalarm_sigptr(tdb, &gotalarm);
55		alarm(timeout);
56	}
57
58	if (rw_type == F_RDLCK)
59		ret = tdb_chainlock_read(tdb, key);
60	else
61		ret = tdb_chainlock(tdb, key);
62
63	if (timeout) {
64		alarm(0);
65		tdb_setalarm_sigptr(tdb, NULL);
66		CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
67		if (gotalarm && (ret == -1)) {
68			DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n",
69				timeout, key.dptr, tdb_name(tdb)));
70			/* TODO: If we time out waiting for a lock, it might
71			 * be nice to use F_GETLK to get the pid of the
72			 * process currently holding the lock and print that
73			 * as part of the debugging message. -- mbp */
74			return -1;
75		}
76	}
77
78	return ret;
79}
80
81/****************************************************************************
82 Write lock a chain. Return -1 if timeout or lock failed.
83****************************************************************************/
84
85int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout)
86{
87	return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK);
88}
89
90int tdb_lock_bystring_with_timeout(TDB_CONTEXT *tdb, const char *keyval,
91				   int timeout)
92{
93	TDB_DATA key = string_term_tdb_data(keyval);
94
95	return tdb_chainlock_with_timeout(tdb, key, timeout);
96}
97
98/****************************************************************************
99 Read lock a chain by string. Return -1 if timeout or lock failed.
100****************************************************************************/
101
102int tdb_read_lock_bystring_with_timeout(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout)
103{
104	TDB_DATA key = string_term_tdb_data(keyval);
105
106	return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_RDLCK);
107}
108
109
110
111
112int tdb_trans_store_bystring(TDB_CONTEXT *tdb, const char *keystr,
113			     TDB_DATA data, int flags)
114{
115	TDB_DATA key = string_term_tdb_data(keystr);
116
117	return tdb_trans_store(tdb, key, data, flags);
118}
119
120/****************************************************************************
121 Useful pair of routines for packing/unpacking data consisting of
122 integers and strings.
123****************************************************************************/
124
125static size_t tdb_pack_va(uint8 *buf, int bufsize, const char *fmt, va_list ap)
126{
127	uint8 bt;
128	uint16 w;
129	uint32 d;
130	int i;
131	void *p;
132	int len;
133	char *s;
134	char c;
135	uint8 *buf0 = buf;
136	const char *fmt0 = fmt;
137	int bufsize0 = bufsize;
138
139	while (*fmt) {
140		switch ((c = *fmt++)) {
141		case 'b': /* unsigned 8-bit integer */
142			len = 1;
143			bt = (uint8)va_arg(ap, int);
144			if (bufsize && bufsize >= len)
145				SSVAL(buf, 0, bt);
146			break;
147		case 'w': /* unsigned 16-bit integer */
148			len = 2;
149			w = (uint16)va_arg(ap, int);
150			if (bufsize && bufsize >= len)
151				SSVAL(buf, 0, w);
152			break;
153		case 'd': /* signed 32-bit integer (standard int in most systems) */
154			len = 4;
155			d = va_arg(ap, uint32);
156			if (bufsize && bufsize >= len)
157				SIVAL(buf, 0, d);
158			break;
159		case 'p': /* pointer */
160			len = 4;
161			p = va_arg(ap, void *);
162			d = p?1:0;
163			if (bufsize && bufsize >= len)
164				SIVAL(buf, 0, d);
165			break;
166		case 'P': /* null-terminated string */
167			s = va_arg(ap,char *);
168			w = strlen(s);
169			len = w + 1;
170			if (bufsize && bufsize >= len)
171				memcpy(buf, s, len);
172			break;
173		case 'f': /* null-terminated string */
174			s = va_arg(ap,char *);
175			w = strlen(s);
176			len = w + 1;
177			if (bufsize && bufsize >= len)
178				memcpy(buf, s, len);
179			break;
180		case 'B': /* fixed-length string */
181			i = va_arg(ap, int);
182			s = va_arg(ap, char *);
183			len = 4+i;
184			if (bufsize && bufsize >= len) {
185				SIVAL(buf, 0, i);
186				memcpy(buf+4, s, i);
187			}
188			break;
189		default:
190			DEBUG(0,("Unknown tdb_pack format %c in %s\n",
191				 c, fmt));
192			len = 0;
193			break;
194		}
195
196		buf += len;
197		if (bufsize)
198			bufsize -= len;
199		if (bufsize < 0)
200			bufsize = 0;
201	}
202
203	DEBUG(18,("tdb_pack_va(%s, %d) -> %d\n",
204		 fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
205
206	return PTR_DIFF(buf, buf0);
207}
208
209size_t tdb_pack(uint8 *buf, int bufsize, const char *fmt, ...)
210{
211	va_list ap;
212	size_t result;
213
214	va_start(ap, fmt);
215	result = tdb_pack_va(buf, bufsize, fmt, ap);
216	va_end(ap);
217	return result;
218}
219
220bool tdb_pack_append(TALLOC_CTX *mem_ctx, uint8 **buf, size_t *len,
221		     const char *fmt, ...)
222{
223	va_list ap;
224	size_t len1, len2;
225
226	va_start(ap, fmt);
227	len1 = tdb_pack_va(NULL, 0, fmt, ap);
228	va_end(ap);
229
230	if (mem_ctx != NULL) {
231		*buf = TALLOC_REALLOC_ARRAY(mem_ctx, *buf, uint8,
232					    (*len) + len1);
233	} else {
234		*buf = SMB_REALLOC_ARRAY(*buf, uint8, (*len) + len1);
235	}
236
237	if (*buf == NULL) {
238		return False;
239	}
240
241	va_start(ap, fmt);
242	len2 = tdb_pack_va((*buf)+(*len), len1, fmt, ap);
243	va_end(ap);
244
245	if (len1 != len2) {
246		return False;
247	}
248
249	*len += len2;
250
251	return True;
252}
253
254/****************************************************************************
255 Useful pair of routines for packing/unpacking data consisting of
256 integers and strings.
257****************************************************************************/
258
259int tdb_unpack(const uint8 *buf, int bufsize, const char *fmt, ...)
260{
261	va_list ap;
262	uint8 *bt;
263	uint16 *w;
264	uint32 *d;
265	int len;
266	int *i;
267	void **p;
268	char *s, **b, **ps;
269	char c;
270	const uint8 *buf0 = buf;
271	const char *fmt0 = fmt;
272	int bufsize0 = bufsize;
273
274	va_start(ap, fmt);
275
276	while (*fmt) {
277		switch ((c=*fmt++)) {
278		case 'b': /* unsigned 8-bit integer */
279			len = 1;
280			bt = va_arg(ap, uint8 *);
281			if (bufsize < len)
282				goto no_space;
283			*bt = SVAL(buf, 0);
284			break;
285		case 'w': /* unsigned 16-bit integer */
286			len = 2;
287			w = va_arg(ap, uint16 *);
288			if (bufsize < len)
289				goto no_space;
290			*w = SVAL(buf, 0);
291			break;
292		case 'd': /* signed 32-bit integer (standard int in most systems) */
293			len = 4;
294			d = va_arg(ap, uint32 *);
295			if (bufsize < len)
296				goto no_space;
297			*d = IVAL(buf, 0);
298			break;
299		case 'p': /* pointer */
300			len = 4;
301			p = va_arg(ap, void **);
302			if (bufsize < len)
303				goto no_space;
304			/*
305			 * This isn't a real pointer - only a token (1 or 0)
306			 * to mark the fact a pointer is present.
307			 */
308
309			*p = (void *)(IVAL(buf, 0) ? (void *)1 : NULL);
310			break;
311		case 'P': /* null-terminated string */
312			/* Return malloc'ed string. */
313			ps = va_arg(ap,char **);
314			len = strlen((const char *)buf) + 1;
315			*ps = SMB_STRDUP((const char *)buf);
316			break;
317		case 'f': /* null-terminated string */
318			s = va_arg(ap,char *);
319			len = strlen((const char *)buf) + 1;
320			if (bufsize < len || len > sizeof(fstring))
321				goto no_space;
322			memcpy(s, buf, len);
323			break;
324		case 'B': /* fixed-length string */
325			i = va_arg(ap, int *);
326			b = va_arg(ap, char **);
327			len = 4;
328			if (bufsize < len)
329				goto no_space;
330			*i = IVAL(buf, 0);
331			if (! *i) {
332				*b = NULL;
333				break;
334			}
335			len += *i;
336			if (bufsize < len)
337				goto no_space;
338			*b = (char *)SMB_MALLOC(*i);
339			if (! *b)
340				goto no_space;
341			memcpy(*b, buf+4, *i);
342			break;
343		default:
344			DEBUG(0,("Unknown tdb_unpack format %c in %s\n",
345				 c, fmt));
346
347			len = 0;
348			break;
349		}
350
351		buf += len;
352		bufsize -= len;
353	}
354
355	va_end(ap);
356
357	DEBUG(18,("tdb_unpack(%s, %d) -> %d\n",
358		 fmt0, bufsize0, (int)PTR_DIFF(buf, buf0)));
359
360	return PTR_DIFF(buf, buf0);
361
362 no_space:
363	va_end(ap);
364	return -1;
365}
366
367
368/****************************************************************************
369 Log tdb messages via DEBUG().
370****************************************************************************/
371
372static void tdb_log(TDB_CONTEXT *tdb, enum tdb_debug_level level, const char *format, ...)
373{
374	va_list ap;
375	char *ptr = NULL;
376	int ret;
377
378	va_start(ap, format);
379	ret = vasprintf(&ptr, format, ap);
380	va_end(ap);
381
382	if ((ret == -1) || !*ptr)
383		return;
384
385	DEBUG((int)level, ("tdb(%s): %s", tdb_name(tdb) ? tdb_name(tdb) : "unnamed", ptr));
386	SAFE_FREE(ptr);
387}
388
389/****************************************************************************
390 Like tdb_open() but also setup a logging function that redirects to
391 the samba DEBUG() system.
392****************************************************************************/
393
394TDB_CONTEXT *tdb_open_log(const char *name, int hash_size, int tdb_flags,
395			  int open_flags, mode_t mode)
396{
397	TDB_CONTEXT *tdb;
398	struct tdb_logging_context log_ctx;
399
400	if (!lp_use_mmap())
401		tdb_flags |= TDB_NOMMAP;
402
403	log_ctx.log_fn = tdb_log;
404	log_ctx.log_private = NULL;
405
406	if ((hash_size == 0) && (name != NULL)) {
407		const char *base = strrchr_m(name, '/');
408		if (base != NULL) {
409			base += 1;
410		}
411		else {
412			base = name;
413		}
414		hash_size = lp_parm_int(-1, "tdb_hashsize", base, 0);
415	}
416
417	tdb = tdb_open_ex(name, hash_size, tdb_flags,
418			  open_flags, mode, &log_ctx, NULL);
419	if (!tdb)
420		return NULL;
421
422	return tdb;
423}
424
425/****************************************************************************
426 tdb_store, wrapped in a transaction. This way we make sure that a process
427 that dies within writing does not leave a corrupt tdb behind.
428****************************************************************************/
429
430int tdb_trans_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf,
431		    int flag)
432{
433	int res;
434
435	if ((res = tdb_transaction_start(tdb)) != 0) {
436		DEBUG(5, ("tdb_transaction_start failed\n"));
437		return res;
438	}
439
440	if ((res = tdb_store(tdb, key, dbuf, flag)) != 0) {
441		DEBUG(10, ("tdb_store failed\n"));
442		if (tdb_transaction_cancel(tdb) != 0) {
443			smb_panic("Cancelling transaction failed");
444		}
445		return res;
446	}
447
448	if ((res = tdb_transaction_commit(tdb)) != 0) {
449		DEBUG(5, ("tdb_transaction_commit failed\n"));
450	}
451
452	return res;
453}
454
455/****************************************************************************
456 tdb_delete, wrapped in a transaction. This way we make sure that a process
457 that dies within deleting does not leave a corrupt tdb behind.
458****************************************************************************/
459
460int tdb_trans_delete(struct tdb_context *tdb, TDB_DATA key)
461{
462	int res;
463
464	if ((res = tdb_transaction_start(tdb)) != 0) {
465		DEBUG(5, ("tdb_transaction_start failed\n"));
466		return res;
467	}
468
469	if ((res = tdb_delete(tdb, key)) != 0) {
470		DEBUG(10, ("tdb_delete failed\n"));
471		if (tdb_transaction_cancel(tdb) != 0) {
472			smb_panic("Cancelling transaction failed");
473		}
474		return res;
475	}
476
477	if ((res = tdb_transaction_commit(tdb)) != 0) {
478		DEBUG(5, ("tdb_transaction_commit failed\n"));
479	}
480
481	return res;
482}
483
484/*
485 Log tdb messages via DEBUG().
486*/
487static void tdb_wrap_log(TDB_CONTEXT *tdb, enum tdb_debug_level level,
488			 const char *format, ...) PRINTF_ATTRIBUTE(3,4);
489
490static void tdb_wrap_log(TDB_CONTEXT *tdb, enum tdb_debug_level level,
491			 const char *format, ...)
492{
493	va_list ap;
494	char *ptr = NULL;
495	int debuglevel = 0;
496	int ret;
497
498	switch (level) {
499	case TDB_DEBUG_FATAL:
500		debuglevel = 0;
501		break;
502	case TDB_DEBUG_ERROR:
503		debuglevel = 1;
504		break;
505	case TDB_DEBUG_WARNING:
506		debuglevel = 2;
507		break;
508	case TDB_DEBUG_TRACE:
509		debuglevel = 5;
510		break;
511	default:
512		debuglevel = 0;
513	}
514
515	va_start(ap, format);
516	ret = vasprintf(&ptr, format, ap);
517	va_end(ap);
518
519	if (ret != -1) {
520		const char *name = tdb_name(tdb);
521		DEBUG(debuglevel, ("tdb(%s): %s", name ? name : "unnamed", ptr));
522		free(ptr);
523	}
524}
525
526static struct tdb_wrap *tdb_list;
527
528/* destroy the last connection to a tdb */
529static int tdb_wrap_destructor(struct tdb_wrap *w)
530{
531	tdb_close(w->tdb);
532	DLIST_REMOVE(tdb_list, w);
533	return 0;
534}
535
536/*
537  wrapped connection to a tdb database
538  to close just talloc_free() the tdb_wrap pointer
539 */
540struct tdb_wrap *tdb_wrap_open(TALLOC_CTX *mem_ctx,
541			       const char *name, int hash_size, int tdb_flags,
542			       int open_flags, mode_t mode)
543{
544	struct tdb_wrap *w;
545	struct tdb_logging_context log_ctx;
546	log_ctx.log_fn = tdb_wrap_log;
547
548	if (!lp_use_mmap())
549		tdb_flags |= TDB_NOMMAP;
550
551	for (w=tdb_list;w;w=w->next) {
552		if (strcmp(name, w->name) == 0) {
553			/*
554			 * Yes, talloc_reference is exactly what we want
555			 * here. Otherwise we would have to implement our own
556			 * reference counting.
557			 */
558			return talloc_reference(mem_ctx, w);
559		}
560	}
561
562	w = talloc(mem_ctx, struct tdb_wrap);
563	if (w == NULL) {
564		return NULL;
565	}
566
567	if (!(w->name = talloc_strdup(w, name))) {
568		talloc_free(w);
569		return NULL;
570	}
571
572	if ((hash_size == 0) && (name != NULL)) {
573		const char *base = strrchr_m(name, '/');
574		if (base != NULL) {
575			base += 1;
576		}
577		else {
578			base = name;
579		}
580		hash_size = lp_parm_int(-1, "tdb_hashsize", base, 0);
581	}
582
583	w->tdb = tdb_open_ex(name, hash_size, tdb_flags,
584			     open_flags, mode, &log_ctx, NULL);
585	if (w->tdb == NULL) {
586		talloc_free(w);
587		return NULL;
588	}
589
590	talloc_set_destructor(w, tdb_wrap_destructor);
591
592	DLIST_ADD(tdb_list, w);
593
594	return w;
595}
596
597NTSTATUS map_nt_error_from_tdb(enum TDB_ERROR err)
598{
599	struct { enum TDB_ERROR err; NTSTATUS status; }	map[] =
600		{ { TDB_SUCCESS,	NT_STATUS_OK },
601		  { TDB_ERR_CORRUPT,	NT_STATUS_INTERNAL_DB_CORRUPTION },
602		  { TDB_ERR_IO,		NT_STATUS_UNEXPECTED_IO_ERROR },
603		  { TDB_ERR_OOM,	NT_STATUS_NO_MEMORY },
604		  { TDB_ERR_EXISTS,	NT_STATUS_OBJECT_NAME_COLLISION },
605
606		  /*
607		   * TDB_ERR_LOCK is very broad, we could for example
608		   * distinguish between fcntl locks and invalid lock
609		   * sequences. So NT_STATUS_FILE_LOCK_CONFLICT is a
610		   * compromise.
611		   */
612		  { TDB_ERR_LOCK,	NT_STATUS_FILE_LOCK_CONFLICT },
613		  /*
614		   * The next two ones in the enum are not actually used
615		   */
616		  { TDB_ERR_NOLOCK,	NT_STATUS_FILE_LOCK_CONFLICT },
617		  { TDB_ERR_LOCK_TIMEOUT, NT_STATUS_FILE_LOCK_CONFLICT },
618		  { TDB_ERR_NOEXIST,	NT_STATUS_NOT_FOUND },
619		  { TDB_ERR_EINVAL,	NT_STATUS_INVALID_PARAMETER },
620		  { TDB_ERR_RDONLY,	NT_STATUS_ACCESS_DENIED }
621		};
622
623	int i;
624
625	for (i=0; i < sizeof(map) / sizeof(map[0]); i++) {
626		if (err == map[i].err) {
627			return map[i].status;
628		}
629	}
630
631	return NT_STATUS_INTERNAL_ERROR;
632}
633
634int tdb_data_cmp(TDB_DATA t1, TDB_DATA t2)
635{
636	int ret;
637	if (t1.dptr == NULL && t2.dptr != NULL) {
638		return -1;
639	}
640	if (t1.dptr != NULL && t2.dptr == NULL) {
641		return 1;
642	}
643	if (t1.dptr == t2.dptr) {
644		return t1.dsize - t2.dsize;
645	}
646	ret = memcmp(t1.dptr, t2.dptr, MIN(t1.dsize, t2.dsize));
647	if (ret == 0) {
648		return t1.dsize - t2.dsize;
649	}
650	return ret;
651}
652