tdb: Harden tdb_rec_read
[samba.git] / lib / tdb / common / io.c
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
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
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
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
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 #if HAVE_POSIX_FALLOCATE
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
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 */
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 */
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 */
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 */
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 */
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
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. */
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
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 */
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 #if 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. */
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 */
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 */
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
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 */
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
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 */
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
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 */
792 void tdb_io_init(struct tdb_context *tdb)
793 {
794         tdb->methods = &io_methods;
795 }