1  /*
2    Unix SMB/CIFS implementation.
3 
4    trivial database library
5 
6    Copyright (C) Andrew Tridgell              1999-2005
7    Copyright (C) Paul `Rusty' Russell		   2000
8    Copyright (C) Jeremy Allison			   2000-2003
9 
10      ** NOTE! The following LGPL license applies to the tdb
11      ** library. This does NOT imply that all of Samba is released
12      ** under the LGPL
13 
14    This library is free software; you can redistribute it and/or
15    modify it under the terms of the GNU Lesser General Public
16    License as published by the Free Software Foundation; either
17    version 3 of the License, or (at your option) any later version.
18 
19    This library is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22    Lesser General Public License for more details.
23 
24    You should have received a copy of the GNU Lesser General Public
25    License along with this library; if not, see <http://www.gnu.org/licenses/>.
26 */
27 
28 
29 #include "tdb_private.h"
30 
31 /*
32  * We prepend the mutex area, so fixup offsets. See mutex.c for details.
33  * tdb->hdr_ofs is 0 or header.mutex_size.
34  *
35  * Note: that we only have the 4GB limit of tdb_off_t for
36  * tdb->map_size. The file size on disk can be 4GB + tdb->hdr_ofs!
37  */
38 
tdb_adjust_offset(struct tdb_context * tdb,off_t * off)39 static bool tdb_adjust_offset(struct tdb_context *tdb, off_t *off)
40 {
41 	off_t tmp = tdb->hdr_ofs + *off;
42 
43 	if ((tmp < tdb->hdr_ofs) || (tmp < *off)) {
44 		errno = EIO;
45 		return false;
46 	}
47 
48 	*off = tmp;
49 	return true;
50 }
51 
tdb_pwrite(struct tdb_context * tdb,const void * buf,size_t count,off_t offset)52 static ssize_t tdb_pwrite(struct tdb_context *tdb, const void *buf,
53 			  size_t count, off_t offset)
54 {
55 	ssize_t ret;
56 
57 	if (!tdb_adjust_offset(tdb, &offset)) {
58 		return -1;
59 	}
60 
61 	do {
62 		ret = pwrite(tdb->fd, buf, count, offset);
63 	} while ((ret == -1) && (errno == EINTR));
64 
65 	return ret;
66 }
67 
tdb_pread(struct tdb_context * tdb,void * buf,size_t count,off_t offset)68 static ssize_t tdb_pread(struct tdb_context *tdb, void *buf,
69 			 size_t count, off_t offset)
70 {
71 	ssize_t ret;
72 
73 	if (!tdb_adjust_offset(tdb, &offset)) {
74 		return -1;
75 	}
76 
77 	do {
78 		ret = pread(tdb->fd, buf, count, offset);
79 	} while ((ret == -1) && (errno == EINTR));
80 
81 	return ret;
82 }
83 
tdb_ftruncate(struct tdb_context * tdb,off_t length)84 static int tdb_ftruncate(struct tdb_context *tdb, off_t length)
85 {
86 	ssize_t ret;
87 
88 	if (!tdb_adjust_offset(tdb, &length)) {
89 		return -1;
90 	}
91 
92 	do {
93 		ret = ftruncate(tdb->fd, length);
94 	} while ((ret == -1) && (errno == EINTR));
95 
96 	return ret;
97 }
98 
99 #ifdef HAVE_POSIX_FALLOCATE
tdb_posix_fallocate(struct tdb_context * tdb,off_t offset,off_t len)100 static int tdb_posix_fallocate(struct tdb_context *tdb, off_t offset,
101 			       off_t len)
102 {
103 	ssize_t ret;
104 
105 	if (!tdb_adjust_offset(tdb, &offset)) {
106 		return -1;
107 	}
108 
109 	do {
110 		ret = posix_fallocate(tdb->fd, offset, len);
111 	} while ((ret == -1) && (errno == EINTR));
112 
113 	return ret;
114 }
115 #endif
116 
tdb_fstat(struct tdb_context * tdb,struct stat * buf)117 static int tdb_fstat(struct tdb_context *tdb, struct stat *buf)
118 {
119 	int ret;
120 
121 	ret = fstat(tdb->fd, buf);
122 	if (ret == -1) {
123 		return -1;
124 	}
125 
126 	if (buf->st_size < tdb->hdr_ofs) {
127 		errno = EIO;
128 		return -1;
129 	}
130 	buf->st_size -= tdb->hdr_ofs;
131 
132 	return ret;
133 }
134 
135 /* check for an out of bounds access - if it is out of bounds then
136    see if the database has been expanded by someone else and expand
137    if necessary
138 */
tdb_oob(struct tdb_context * tdb,tdb_off_t off,tdb_len_t len,int probe)139 static int tdb_oob(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len,
140 		   int probe)
141 {
142 	struct stat st;
143 	if (len + off < len) {
144 		if (!probe) {
145 			/* Ensure ecode is set for log fn. */
146 			tdb->ecode = TDB_ERR_IO;
147 			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob off %u len %u wrap\n",
148 				 off, len));
149 		}
150 		return -1;
151 	}
152 
153 	if (off + len <= tdb->map_size)
154 		return 0;
155 	if (tdb->flags & TDB_INTERNAL) {
156 		if (!probe) {
157 			/* Ensure ecode is set for log fn. */
158 			tdb->ecode = TDB_ERR_IO;
159 			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %u beyond internal malloc size %u\n",
160 				 (int)(off + len), (int)tdb->map_size));
161 		}
162 		return -1;
163 	}
164 
165 	if (tdb_fstat(tdb, &st) == -1) {
166 		tdb->ecode = TDB_ERR_IO;
167 		return -1;
168 	}
169 
170 	/* Beware >4G files! */
171 	if ((tdb_off_t)st.st_size != st.st_size) {
172 		/* Ensure ecode is set for log fn. */
173 		tdb->ecode = TDB_ERR_IO;
174 		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_oob len %llu too large!\n",
175 			 (long long)st.st_size));
176 		return -1;
177 	}
178 
179 	/* Unmap, update size, remap.  We do this unconditionally, to handle
180 	 * the unusual case where the db is truncated.
181 	 *
182 	 * This can happen to a child using tdb_reopen_all(true) on a
183 	 * TDB_CLEAR_IF_FIRST tdb whose parent crashes: the next
184 	 * opener will truncate the database. */
185 	if (tdb_munmap(tdb) == -1) {
186 		tdb->ecode = TDB_ERR_IO;
187 		return -1;
188 	}
189 	tdb->map_size = st.st_size;
190 	if (tdb_mmap(tdb) != 0) {
191 		return -1;
192 	}
193 
194 	if (st.st_size < (size_t)off + len) {
195 		if (!probe) {
196 			/* Ensure ecode is set for log fn. */
197 			tdb->ecode = TDB_ERR_IO;
198 			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %u beyond eof at %u\n",
199 				 (int)(off + len), (int)st.st_size));
200 		}
201 		return -1;
202 	}
203 	return 0;
204 }
205 
206 /* write a lump of data at a specified offset */
tdb_write(struct tdb_context * tdb,tdb_off_t off,const void * buf,tdb_len_t len)207 static int tdb_write(struct tdb_context *tdb, tdb_off_t off,
208 		     const void *buf, tdb_len_t len)
209 {
210 	if (len == 0) {
211 		return 0;
212 	}
213 
214 	if (tdb->read_only || tdb->traverse_read) {
215 		tdb->ecode = TDB_ERR_RDONLY;
216 		return -1;
217 	}
218 
219 	if (tdb->methods->tdb_oob(tdb, off, len, 0) != 0)
220 		return -1;
221 
222 	if (tdb->map_ptr) {
223 		memcpy(off + (char *)tdb->map_ptr, buf, len);
224 	} else {
225 #ifdef HAVE_INCOHERENT_MMAP
226 		tdb->ecode = TDB_ERR_IO;
227 		return -1;
228 #else
229 		ssize_t written;
230 
231 		written = tdb_pwrite(tdb, buf, len, off);
232 
233 		if ((written != (ssize_t)len) && (written != -1)) {
234 			/* try once more */
235 			tdb->ecode = TDB_ERR_IO;
236 			TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: wrote only "
237 				 "%zi of %u bytes at %u, trying once more\n",
238 				 written, len, off));
239 			written = tdb_pwrite(tdb, (const char *)buf+written,
240 					     len-written, off+written);
241 		}
242 		if (written == -1) {
243 			/* Ensure ecode is set for log fn. */
244 			tdb->ecode = TDB_ERR_IO;
245 			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %u "
246 				 "len=%u (%s)\n", off, len, strerror(errno)));
247 			return -1;
248 		} else if (written != (ssize_t)len) {
249 			tdb->ecode = TDB_ERR_IO;
250 			TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_write: failed to "
251 				 "write %u bytes at %u in two attempts\n",
252 				 len, off));
253 			return -1;
254 		}
255 #endif
256 	}
257 	return 0;
258 }
259 
260 /* Endian conversion: we only ever deal with 4 byte quantities */
tdb_convert(void * buf,uint32_t size)261 void *tdb_convert(void *buf, uint32_t size)
262 {
263 	uint32_t i, *p = (uint32_t *)buf;
264 	for (i = 0; i < size / 4; i++)
265 		p[i] = TDB_BYTEREV(p[i]);
266 	return buf;
267 }
268 
269 
270 /* read a lump of data at a specified offset, maybe convert */
tdb_read(struct tdb_context * tdb,tdb_off_t off,void * buf,tdb_len_t len,int cv)271 static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
272 		    tdb_len_t len, int cv)
273 {
274 	if (tdb->methods->tdb_oob(tdb, off, len, 0) != 0) {
275 		return -1;
276 	}
277 
278 	if (tdb->map_ptr) {
279 		memcpy(buf, off + (char *)tdb->map_ptr, len);
280 	} else {
281 #ifdef HAVE_INCOHERENT_MMAP
282 		tdb->ecode = TDB_ERR_IO;
283 		return -1;
284 #else
285 		ssize_t ret;
286 
287 		ret = tdb_pread(tdb, buf, len, off);
288 		if (ret != (ssize_t)len) {
289 			/* Ensure ecode is set for log fn. */
290 			tdb->ecode = TDB_ERR_IO;
291 			TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %u "
292 				 "len=%u ret=%zi (%s) map_size=%u\n",
293 				 off, len, ret, strerror(errno),
294 				 tdb->map_size));
295 			return -1;
296 		}
297 #endif
298 	}
299 	if (cv) {
300 		tdb_convert(buf, len);
301 	}
302 	return 0;
303 }
304 
305 
306 
307 /*
308   do an unlocked scan of the hash table heads to find the next non-zero head. The value
309   will then be confirmed with the lock held
310 */
tdb_next_hash_chain(struct tdb_context * tdb,uint32_t * chain)311 static void tdb_next_hash_chain(struct tdb_context *tdb, uint32_t *chain)
312 {
313 	uint32_t h = *chain;
314 	if (tdb->map_ptr) {
315 		for (;h < tdb->hash_size;h++) {
316 			if (0 != *(uint32_t *)(TDB_HASH_TOP(h) + (unsigned char *)tdb->map_ptr)) {
317 				break;
318 			}
319 		}
320 	} else {
321 		uint32_t off=0;
322 		for (;h < tdb->hash_size;h++) {
323 			if (tdb_ofs_read(tdb, TDB_HASH_TOP(h), &off) != 0 || off != 0) {
324 				break;
325 			}
326 		}
327 	}
328 	(*chain) = h;
329 }
330 
331 
tdb_munmap(struct tdb_context * tdb)332 int tdb_munmap(struct tdb_context *tdb)
333 {
334 	if (tdb->flags & TDB_INTERNAL)
335 		return 0;
336 
337 #ifdef HAVE_MMAP
338 	if (tdb->map_ptr) {
339 		int ret;
340 
341 		ret = munmap(tdb->map_ptr, tdb->map_size);
342 		if (ret != 0)
343 			return ret;
344 	}
345 #endif
346 	tdb->map_ptr = NULL;
347 	return 0;
348 }
349 
350 /* If mmap isn't coherent, *everyone* must always mmap. */
should_mmap(const struct tdb_context * tdb)351 static bool should_mmap(const struct tdb_context *tdb)
352 {
353 #ifdef HAVE_INCOHERENT_MMAP
354 	return true;
355 #else
356 	return !(tdb->flags & TDB_NOMMAP);
357 #endif
358 }
359 
tdb_mmap(struct tdb_context * tdb)360 int tdb_mmap(struct tdb_context *tdb)
361 {
362 	if (tdb->flags & TDB_INTERNAL)
363 		return 0;
364 
365 #ifdef HAVE_MMAP
366 	if (should_mmap(tdb)) {
367 		tdb->map_ptr = mmap(NULL, tdb->map_size,
368 				    PROT_READ|(tdb->read_only? 0:PROT_WRITE),
369 				    MAP_SHARED|MAP_FILE, tdb->fd,
370 				    tdb->hdr_ofs);
371 
372 		/*
373 		 * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
374 		 */
375 
376 		if (tdb->map_ptr == MAP_FAILED) {
377 			tdb->map_ptr = NULL;
378 			TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %u (%s)\n",
379 				 tdb->map_size, strerror(errno)));
380 #ifdef HAVE_INCOHERENT_MMAP
381 			tdb->ecode = TDB_ERR_IO;
382 			return -1;
383 #endif
384 		}
385 	} else {
386 		tdb->map_ptr = NULL;
387 	}
388 #else
389 	tdb->map_ptr = NULL;
390 #endif
391 	return 0;
392 }
393 
394 /* expand a file.  we prefer to use ftruncate, as that is what posix
395   says to use for mmap expansion */
tdb_expand_file(struct tdb_context * tdb,tdb_off_t size,tdb_off_t addition)396 static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition)
397 {
398 	char buf[8192];
399 	tdb_off_t new_size;
400 	int ret;
401 
402 	if (tdb->read_only || tdb->traverse_read) {
403 		tdb->ecode = TDB_ERR_RDONLY;
404 		return -1;
405 	}
406 
407 	if (!tdb_add_off_t(size, addition, &new_size)) {
408 		tdb->ecode = TDB_ERR_OOM;
409 		TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write "
410 			"overflow detected current size[%u] addition[%u]!\n",
411 			(unsigned)size, (unsigned)addition));
412 		errno = ENOSPC;
413 		return -1;
414 	}
415 
416 #ifdef HAVE_POSIX_FALLOCATE
417 	ret = tdb_posix_fallocate(tdb, size, addition);
418 	if (ret == 0) {
419 		return 0;
420 	}
421 	if (ret == ENOSPC) {
422 		/*
423 		 * The Linux glibc (at least as of 2.24) fallback if
424 		 * the file system does not support fallocate does not
425 		 * reset the file size back to where it was. Also, to
426 		 * me it is unclear from the posix spec of
427 		 * posix_fallocate whether this is allowed or
428 		 * not. Better be safe than sorry and "goto fail" but
429 		 * "return -1" here, leaving the EOF pointer too
430 		 * large.
431 		 */
432 		goto fail;
433 	}
434 
435 	/*
436 	 * Retry the "old" way. Possibly unnecessary, but looking at
437 	 * our configure script there seem to be weird failure modes
438 	 * for posix_fallocate. See commit 3264a98ff16de, which
439 	 * probably refers to
440 	 * https://sourceware.org/bugzilla/show_bug.cgi?id=1083.
441 	 */
442 #endif
443 
444 	ret = tdb_ftruncate(tdb, new_size);
445 	if (ret == -1) {
446 		char b = 0;
447 		ssize_t written = tdb_pwrite(tdb, &b, 1, new_size - 1);
448 		if (written == 0) {
449 			/* try once more, potentially revealing errno */
450 			written = tdb_pwrite(tdb, &b, 1, new_size - 1);
451 		}
452 		if (written == 0) {
453 			/* again - give up, guessing errno */
454 			errno = ENOSPC;
455 		}
456 		if (written != 1) {
457 			tdb->ecode = TDB_ERR_OOM;
458 			TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %u failed (%s)\n",
459 				 (unsigned)new_size, strerror(errno)));
460 			return -1;
461 		}
462 	}
463 
464 	/* now fill the file with something. This ensures that the
465 	   file isn't sparse, which would be very bad if we ran out of
466 	   disk. This must be done with write, not via mmap */
467 	memset(buf, TDB_PAD_BYTE, sizeof(buf));
468 	while (addition) {
469 		size_t n = addition>sizeof(buf)?sizeof(buf):addition;
470 		ssize_t written = tdb_pwrite(tdb, buf, n, size);
471 		if (written == 0) {
472 			/* prevent infinite loops: try _once_ more */
473 			written = tdb_pwrite(tdb, buf, n, size);
474 		}
475 		if (written == 0) {
476 			/* give up, trying to provide a useful errno */
477 			tdb->ecode = TDB_ERR_OOM;
478 			TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write "
479 				"returned 0 twice: giving up!\n"));
480 			errno = ENOSPC;
481 			goto fail;
482 		}
483 		if (written == -1) {
484 			tdb->ecode = TDB_ERR_OOM;
485 			TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of "
486 				 "%u bytes failed (%s)\n", (int)n,
487 				 strerror(errno)));
488 			goto fail;
489 		}
490 		if (written != n) {
491 			TDB_LOG((tdb, TDB_DEBUG_WARNING, "expand_file: wrote "
492 				 "only %zu of %zi bytes - retrying\n", written,
493 				 n));
494 		}
495 		addition -= written;
496 		size += written;
497 	}
498 	return 0;
499 
500 fail:
501 	{
502 		int err = errno;
503 
504 		/*
505 		 * We're holding the freelist lock or are inside a
506 		 * transaction. Cutting the file is safe, the space we
507 		 * tried to allocate can't have been used anywhere in
508 		 * the meantime.
509 		 */
510 
511 		ret = tdb_ftruncate(tdb, size);
512 		if (ret == -1) {
513 			TDB_LOG((tdb, TDB_DEBUG_WARNING, "expand_file: "
514 				 "retruncate to %ju failed\n",
515 				 (uintmax_t)size));
516 		}
517 		errno = err;
518 	}
519 
520 	return -1;
521 }
522 
523 
524 /* You need 'size', this tells you how much you should expand by. */
tdb_expand_adjust(tdb_off_t map_size,tdb_off_t size,int page_size)525 tdb_off_t tdb_expand_adjust(tdb_off_t map_size, tdb_off_t size, int page_size)
526 {
527 	tdb_off_t new_size, top_size, increment;
528 	tdb_off_t max_size = UINT32_MAX - map_size;
529 
530 	if (size > max_size) {
531 		/*
532 		 * We can't round up anymore, just give back
533 		 * what we're asked for.
534 		 *
535 		 * The caller has to take care of the ENOSPC handling.
536 		 */
537 		return size;
538 	}
539 
540 	/* limit size in order to avoid using up huge amounts of memory for
541 	 * in memory tdbs if an oddball huge record creeps in */
542 	if (size > 100 * 1024) {
543 		increment = size * 2;
544 	} else {
545 		increment = size * 100;
546 	}
547 	if (increment < size) {
548 		goto overflow;
549 	}
550 
551 	if (!tdb_add_off_t(map_size, increment, &top_size)) {
552 		goto overflow;
553 	}
554 
555 	/* always make room for at least top_size more records, and at
556 	   least 25% more space. if the DB is smaller than 100MiB,
557 	   otherwise grow it by 10% only. */
558 	if (map_size > 100 * 1024 * 1024) {
559 		new_size = map_size * 1.10;
560 	} else {
561 		new_size = map_size * 1.25;
562 	}
563 	if (new_size < map_size) {
564 		goto overflow;
565 	}
566 
567 	/* Round the database up to a multiple of the page size */
568 	new_size = MAX(top_size, new_size);
569 
570 	if (new_size + page_size < new_size) {
571 		/* There's a "+" in TDB_ALIGN that might overflow... */
572 		goto overflow;
573 	}
574 
575 	return TDB_ALIGN(new_size, page_size) - map_size;
576 
577 overflow:
578 	/*
579 	 * Somewhere in between we went over 4GB. Make one big jump to
580 	 * exactly 4GB database size.
581 	 */
582 	return max_size;
583 }
584 
585 /* expand the database at least size bytes by expanding the underlying
586    file and doing the mmap again if necessary */
tdb_expand(struct tdb_context * tdb,tdb_off_t size)587 int tdb_expand(struct tdb_context *tdb, tdb_off_t size)
588 {
589 	struct tdb_record rec;
590 	tdb_off_t offset;
591 	tdb_off_t new_size;
592 
593 	if (tdb_lock(tdb, -1, F_WRLCK) == -1) {
594 		TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n"));
595 		return -1;
596 	}
597 
598 	/* must know about any previous expansions by another process */
599 	tdb->methods->tdb_oob(tdb, tdb->map_size, 1, 1);
600 
601 	/*
602 	 * Note: that we don't care about tdb->hdr_ofs != 0 here
603 	 *
604 	 * The 4GB limitation is just related to tdb->map_size
605 	 * and the offset calculation in the records.
606 	 *
607 	 * The file on disk can be up to 4GB + tdb->hdr_ofs
608 	 */
609 	size = tdb_expand_adjust(tdb->map_size, size, tdb->page_size);
610 
611 	if (!tdb_add_off_t(tdb->map_size, size, &new_size)) {
612 		tdb->ecode = TDB_ERR_OOM;
613 		TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_expand "
614 			"overflow detected current map_size[%u] size[%u]!\n",
615 			(unsigned)tdb->map_size, (unsigned)size));
616 		goto fail;
617 	}
618 
619 	/* form a new freelist record */
620 	offset = tdb->map_size;
621 	memset(&rec,'\0',sizeof(rec));
622 	rec.rec_len = size - sizeof(rec);
623 
624 	if (tdb->flags & TDB_INTERNAL) {
625 		char *new_map_ptr;
626 
627 		new_map_ptr = (char *)realloc(tdb->map_ptr, new_size);
628 		if (!new_map_ptr) {
629 			tdb->ecode = TDB_ERR_OOM;
630 			goto fail;
631 		}
632 		tdb->map_ptr = new_map_ptr;
633 		tdb->map_size = new_size;
634 	} else {
635 		int ret;
636 
637 		/*
638 		 * expand the file itself
639 		 */
640 		ret = tdb->methods->tdb_expand_file(tdb, tdb->map_size, size);
641 		if (ret != 0) {
642 			goto fail;
643 		}
644 
645 		/* Explicitly remap: if we're in a transaction, this won't
646 		 * happen automatically! */
647 		tdb_munmap(tdb);
648 		tdb->map_size = new_size;
649 		if (tdb_mmap(tdb) != 0) {
650 			goto fail;
651 		}
652 	}
653 
654 	/* link it into the free list */
655 	if (tdb_free(tdb, offset, &rec) == -1)
656 		goto fail;
657 
658 	tdb_unlock(tdb, -1, F_WRLCK);
659 	return 0;
660  fail:
661 	tdb_unlock(tdb, -1, F_WRLCK);
662 	return -1;
663 }
664 
665 /* read/write a tdb_off_t */
tdb_ofs_read(struct tdb_context * tdb,tdb_off_t offset,tdb_off_t * d)666 int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
667 {
668 	return tdb->methods->tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV());
669 }
670 
tdb_ofs_write(struct tdb_context * tdb,tdb_off_t offset,tdb_off_t * d)671 int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d)
672 {
673 	tdb_off_t off = *d;
674 	return tdb->methods->tdb_write(tdb, offset, CONVERT(off), sizeof(*d));
675 }
676 
677 
678 /* read a lump of data, allocating the space for it */
tdb_alloc_read(struct tdb_context * tdb,tdb_off_t offset,tdb_len_t len)679 unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len)
680 {
681 	unsigned char *buf;
682 
683 	/* some systems don't like zero length malloc */
684 
685 	if (!(buf = (unsigned char *)malloc(len ? len : 1))) {
686 		/* Ensure ecode is set for log fn. */
687 		tdb->ecode = TDB_ERR_OOM;
688 		TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%u (%s)\n",
689 			   len, strerror(errno)));
690 		return NULL;
691 	}
692 	if (tdb->methods->tdb_read(tdb, offset, buf, len, 0) == -1) {
693 		SAFE_FREE(buf);
694 		return NULL;
695 	}
696 	return buf;
697 }
698 
699 /* Give a piece of tdb data to a parser */
700 
tdb_parse_data(struct tdb_context * tdb,TDB_DATA key,tdb_off_t offset,tdb_len_t len,int (* parser)(TDB_DATA key,TDB_DATA data,void * private_data),void * private_data)701 int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key,
702 		   tdb_off_t offset, tdb_len_t len,
703 		   int (*parser)(TDB_DATA key, TDB_DATA data,
704 				 void *private_data),
705 		   void *private_data)
706 {
707 	TDB_DATA data;
708 	int result;
709 
710 	data.dsize = len;
711 
712 	if ((tdb->transaction == NULL) && (tdb->map_ptr != NULL)) {
713 		/*
714 		 * Optimize by avoiding the malloc/memcpy/free, point the
715 		 * parser directly at the mmap area.
716 		 */
717 		if (tdb->methods->tdb_oob(tdb, offset, len, 0) != 0) {
718 			return -1;
719 		}
720 		data.dptr = offset + (unsigned char *)tdb->map_ptr;
721 		return parser(key, data, private_data);
722 	}
723 
724 	if (!(data.dptr = tdb_alloc_read(tdb, offset, len))) {
725 		return -1;
726 	}
727 
728 	result = parser(key, data, private_data);
729 	free(data.dptr);
730 	return result;
731 }
732 
733 /* read/write a record */
tdb_rec_read(struct tdb_context * tdb,tdb_off_t offset,struct tdb_record * rec)734 int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
735 {
736 	int ret;
737 	tdb_len_t overall_len;
738 
739 	if (tdb->methods->tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1)
740 		return -1;
741 	if (TDB_BAD_MAGIC(rec)) {
742 		/* Ensure ecode is set for log fn. */
743 		tdb->ecode = TDB_ERR_CORRUPT;
744 		TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%u\n", rec->magic, offset));
745 		return -1;
746 	}
747 
748 	overall_len = rec->key_len + rec->data_len;
749 	if (overall_len < rec->data_len) {
750 		/* overflow */
751 		return -1;
752 	}
753 
754 	if (overall_len > rec->rec_len) {
755 		/* invalid record */
756 		return -1;
757 	}
758 
759 	ret = tdb->methods->tdb_oob(tdb, offset, rec->key_len, 1);
760 	if (ret == -1) {
761 		return -1;
762 	}
763 	ret = tdb->methods->tdb_oob(tdb, offset, rec->data_len, 1);
764 	if (ret == -1) {
765 		return -1;
766 	}
767 	ret = tdb->methods->tdb_oob(tdb, offset, rec->rec_len, 1);
768 	if (ret == -1) {
769 		return -1;
770 	}
771 
772 	return tdb->methods->tdb_oob(tdb, rec->next, sizeof(*rec), 0);
773 }
774 
tdb_rec_write(struct tdb_context * tdb,tdb_off_t offset,struct tdb_record * rec)775 int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct tdb_record *rec)
776 {
777 	struct tdb_record r = *rec;
778 	return tdb->methods->tdb_write(tdb, offset, CONVERT(r), sizeof(r));
779 }
780 
781 static const struct tdb_methods io_methods = {
782 	tdb_read,
783 	tdb_write,
784 	tdb_next_hash_chain,
785 	tdb_oob,
786 	tdb_expand_file,
787 };
788 
789 /*
790   initialise the default methods table
791 */
tdb_io_init(struct tdb_context * tdb)792 void tdb_io_init(struct tdb_context *tdb)
793 {
794 	tdb->methods = &io_methods;
795 }
796