ntdb: special accessor functions for read/write of an offset.
[kai/samba-autobuild/.git] / lib / ntdb / 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    Copyright (C) Rusty Russell                     2010
10
11      ** NOTE! The following LGPL license applies to the ntdb
12      ** library. This does NOT imply that all of Samba is released
13      ** under the LGPL
14
15    This library is free software; you can redistribute it and/or
16    modify it under the terms of the GNU Lesser General Public
17    License as published by the Free Software Foundation; either
18    version 3 of the License, or (at your option) any later version.
19
20    This library is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23    Lesser General Public License for more details.
24
25    You should have received a copy of the GNU Lesser General Public
26    License along with this library; if not, see <http://www.gnu.org/licenses/>.
27 */
28 #include "private.h"
29 #include <assert.h>
30 #include <ccan/likely/likely.h>
31
32 void ntdb_munmap(struct ntdb_file *file)
33 {
34         if (file->fd == -1)
35                 return;
36
37         if (file->map_ptr) {
38                 munmap(file->map_ptr, file->map_size);
39                 file->map_ptr = NULL;
40         }
41 }
42
43 enum NTDB_ERROR ntdb_mmap(struct ntdb_context *ntdb)
44 {
45         int mmap_flags;
46
47         if (ntdb->flags & NTDB_INTERNAL)
48                 return NTDB_SUCCESS;
49
50 #ifndef HAVE_INCOHERENT_MMAP
51         if (ntdb->flags & NTDB_NOMMAP)
52                 return NTDB_SUCCESS;
53 #endif
54
55         if ((ntdb->open_flags & O_ACCMODE) == O_RDONLY)
56                 mmap_flags = PROT_READ;
57         else
58                 mmap_flags = PROT_READ | PROT_WRITE;
59
60         /* size_t can be smaller than off_t. */
61         if ((size_t)ntdb->file->map_size == ntdb->file->map_size) {
62                 ntdb->file->map_ptr = mmap(NULL, ntdb->file->map_size,
63                                           mmap_flags,
64                                           MAP_SHARED, ntdb->file->fd, 0);
65         } else
66                 ntdb->file->map_ptr = MAP_FAILED;
67
68         /*
69          * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!!
70          */
71         if (ntdb->file->map_ptr == MAP_FAILED) {
72                 ntdb->file->map_ptr = NULL;
73 #ifdef HAVE_INCOHERENT_MMAP
74                 /* Incoherent mmap means everyone must mmap! */
75                 return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
76                                   "ntdb_mmap failed for size %lld (%s)",
77                                   (long long)ntdb->file->map_size,
78                                   strerror(errno));
79 #else
80                 ntdb_logerr(ntdb, NTDB_SUCCESS, NTDB_LOG_WARNING,
81                            "ntdb_mmap failed for size %lld (%s)",
82                            (long long)ntdb->file->map_size, strerror(errno));
83 #endif
84         }
85         return NTDB_SUCCESS;
86 }
87
88 /* check for an out of bounds access - if it is out of bounds then
89    see if the database has been expanded by someone else and expand
90    if necessary
91    note that "len" is the minimum length needed for the db.
92
93    If probe is true, len being too large isn't a failure.
94 */
95 static enum NTDB_ERROR ntdb_normal_oob(struct ntdb_context *ntdb,
96                                        ntdb_off_t off, ntdb_len_t len,
97                                        bool probe)
98 {
99         struct stat st;
100         enum NTDB_ERROR ecode;
101
102         /* We can't hold pointers during this: we could unmap! */
103         assert(!ntdb->direct_access
104                || (ntdb->flags & NTDB_NOLOCK)
105                || ntdb_has_expansion_lock(ntdb));
106
107         if (len + off < len) {
108                 if (probe)
109                         return NTDB_SUCCESS;
110
111                 return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
112                                   "ntdb_oob off %llu len %llu wrap\n",
113                                   (long long)off, (long long)len);
114         }
115
116         if (ntdb->flags & NTDB_INTERNAL) {
117                 if (probe)
118                         return NTDB_SUCCESS;
119
120                 ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
121                            "ntdb_oob len %lld beyond internal"
122                            " alloc size %lld",
123                            (long long)(off + len),
124                            (long long)ntdb->file->map_size);
125                 return NTDB_ERR_IO;
126         }
127
128         ecode = ntdb_lock_expand(ntdb, F_RDLCK);
129         if (ecode != NTDB_SUCCESS) {
130                 return ecode;
131         }
132
133         if (fstat(ntdb->file->fd, &st) != 0) {
134                 ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
135                            "Failed to fstat file: %s", strerror(errno));
136                 ntdb_unlock_expand(ntdb, F_RDLCK);
137                 return NTDB_ERR_IO;
138         }
139
140         ntdb_unlock_expand(ntdb, F_RDLCK);
141
142         if (st.st_size < off + len) {
143                 if (probe)
144                         return NTDB_SUCCESS;
145
146                 ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
147                            "ntdb_oob len %llu beyond eof at %llu",
148                            (long long)(off + len), (long long)st.st_size);
149                 return NTDB_ERR_IO;
150         }
151
152         /* Unmap, update size, remap */
153         ntdb_munmap(ntdb->file);
154
155         ntdb->file->map_size = st.st_size;
156         return ntdb_mmap(ntdb);
157 }
158
159 /* Endian conversion: we only ever deal with 8 byte quantities */
160 void *ntdb_convert(const struct ntdb_context *ntdb, void *buf, ntdb_len_t size)
161 {
162         assert(size % 8 == 0);
163         if (unlikely((ntdb->flags & NTDB_CONVERT)) && buf) {
164                 uint64_t i, *p = (uint64_t *)buf;
165                 for (i = 0; i < size / 8; i++)
166                         p[i] = bswap_64(p[i]);
167         }
168         return buf;
169 }
170
171 /* Return first non-zero offset in offset array, or end, or -ve error. */
172 /* FIXME: Return the off? */
173 uint64_t ntdb_find_nonzero_off(struct ntdb_context *ntdb,
174                               ntdb_off_t base, uint64_t start, uint64_t end)
175 {
176         uint64_t i;
177         const uint64_t *val;
178
179         /* Zero vs non-zero is the same unconverted: minor optimization. */
180         val = ntdb_access_read(ntdb, base + start * sizeof(ntdb_off_t),
181                               (end - start) * sizeof(ntdb_off_t), false);
182         if (NTDB_PTR_IS_ERR(val)) {
183                 return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(val));
184         }
185
186         for (i = 0; i < (end - start); i++) {
187                 if (val[i])
188                         break;
189         }
190         ntdb_access_release(ntdb, val);
191         return start + i;
192 }
193
194 /* Return first zero offset in num offset array, or num, or -ve error. */
195 uint64_t ntdb_find_zero_off(struct ntdb_context *ntdb, ntdb_off_t off,
196                            uint64_t num)
197 {
198         uint64_t i;
199         const uint64_t *val;
200
201         /* Zero vs non-zero is the same unconverted: minor optimization. */
202         val = ntdb_access_read(ntdb, off, num * sizeof(ntdb_off_t), false);
203         if (NTDB_PTR_IS_ERR(val)) {
204                 return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(val));
205         }
206
207         for (i = 0; i < num; i++) {
208                 if (!val[i])
209                         break;
210         }
211         ntdb_access_release(ntdb, val);
212         return i;
213 }
214
215 enum NTDB_ERROR zero_out(struct ntdb_context *ntdb, ntdb_off_t off, ntdb_len_t len)
216 {
217         char buf[8192] = { 0 };
218         void *p = ntdb->io->direct(ntdb, off, len, true);
219         enum NTDB_ERROR ecode = NTDB_SUCCESS;
220
221         assert(!(ntdb->flags & NTDB_RDONLY));
222         if (NTDB_PTR_IS_ERR(p)) {
223                 return NTDB_PTR_ERR(p);
224         }
225         if (p) {
226                 memset(p, 0, len);
227                 return ecode;
228         }
229         while (len) {
230                 unsigned todo = len < sizeof(buf) ? len : sizeof(buf);
231                 ecode = ntdb->io->twrite(ntdb, off, buf, todo);
232                 if (ecode != NTDB_SUCCESS) {
233                         break;
234                 }
235                 len -= todo;
236                 off += todo;
237         }
238         return ecode;
239 }
240
241 /* write a lump of data at a specified offset */
242 static enum NTDB_ERROR ntdb_write(struct ntdb_context *ntdb, ntdb_off_t off,
243                                 const void *buf, ntdb_len_t len)
244 {
245         enum NTDB_ERROR ecode;
246
247         if (ntdb->flags & NTDB_RDONLY) {
248                 return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
249                                   "Write to read-only database");
250         }
251
252         ecode = ntdb_oob(ntdb, off, len, false);
253         if (ecode != NTDB_SUCCESS) {
254                 return ecode;
255         }
256
257         if (ntdb->file->map_ptr) {
258                 memcpy(off + (char *)ntdb->file->map_ptr, buf, len);
259         } else {
260 #ifdef HAVE_INCOHERENT_MMAP
261                 return NTDB_ERR_IO;
262 #else
263                 ssize_t ret;
264                 ret = pwrite(ntdb->file->fd, buf, len, off);
265                 if (ret != len) {
266                         /* This shouldn't happen: we avoid sparse files. */
267                         if (ret >= 0)
268                                 errno = ENOSPC;
269
270                         return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
271                                           "ntdb_write: %zi at %zu len=%zu (%s)",
272                                           ret, (size_t)off, (size_t)len,
273                                           strerror(errno));
274                 }
275 #endif
276         }
277         return NTDB_SUCCESS;
278 }
279
280 /* read a lump of data at a specified offset */
281 static enum NTDB_ERROR ntdb_read(struct ntdb_context *ntdb, ntdb_off_t off,
282                                void *buf, ntdb_len_t len)
283 {
284         enum NTDB_ERROR ecode;
285
286         ecode = ntdb_oob(ntdb, off, len, false);
287         if (ecode != NTDB_SUCCESS) {
288                 return ecode;
289         }
290
291         if (ntdb->file->map_ptr) {
292                 memcpy(buf, off + (char *)ntdb->file->map_ptr, len);
293         } else {
294 #ifdef HAVE_INCOHERENT_MMAP
295                 return NTDB_ERR_IO;
296 #else
297                 ssize_t r = pread(ntdb->file->fd, buf, len, off);
298                 if (r != len) {
299                         return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
300                                           "ntdb_read failed with %zi at %zu "
301                                           "len=%zu (%s) map_size=%zu",
302                                           r, (size_t)off, (size_t)len,
303                                           strerror(errno),
304                                           (size_t)ntdb->file->map_size);
305                 }
306 #endif
307         }
308         return NTDB_SUCCESS;
309 }
310
311 enum NTDB_ERROR ntdb_write_convert(struct ntdb_context *ntdb, ntdb_off_t off,
312                                  const void *rec, size_t len)
313 {
314         enum NTDB_ERROR ecode;
315
316         if (unlikely((ntdb->flags & NTDB_CONVERT))) {
317                 void *conv = ntdb->alloc_fn(ntdb, len, ntdb->alloc_data);
318                 if (!conv) {
319                         return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
320                                           "ntdb_write: no memory converting"
321                                           " %zu bytes", len);
322                 }
323                 memcpy(conv, rec, len);
324                 ecode = ntdb->io->twrite(ntdb, off,
325                                          ntdb_convert(ntdb, conv, len), len);
326                 ntdb->free_fn(conv, ntdb->alloc_data);
327         } else {
328                 ecode = ntdb->io->twrite(ntdb, off, rec, len);
329         }
330         return ecode;
331 }
332
333 enum NTDB_ERROR ntdb_read_convert(struct ntdb_context *ntdb, ntdb_off_t off,
334                                 void *rec, size_t len)
335 {
336         enum NTDB_ERROR ecode = ntdb->io->tread(ntdb, off, rec, len);
337         ntdb_convert(ntdb, rec, len);
338         return ecode;
339 }
340
341 static void *_ntdb_alloc_read(struct ntdb_context *ntdb, ntdb_off_t offset,
342                              ntdb_len_t len, unsigned int prefix)
343 {
344         unsigned char *buf;
345         enum NTDB_ERROR ecode;
346
347         /* some systems don't like zero length malloc */
348         buf = ntdb->alloc_fn(ntdb, prefix + len ? prefix + len : 1,
349                           ntdb->alloc_data);
350         if (!buf) {
351                 ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_USE_ERROR,
352                            "ntdb_alloc_read alloc failed len=%zu",
353                            (size_t)(prefix + len));
354                 return NTDB_ERR_PTR(NTDB_ERR_OOM);
355         } else {
356                 ecode = ntdb->io->tread(ntdb, offset, buf+prefix, len);
357                 if (unlikely(ecode != NTDB_SUCCESS)) {
358                         ntdb->free_fn(buf, ntdb->alloc_data);
359                         return NTDB_ERR_PTR(ecode);
360                 }
361         }
362         return buf;
363 }
364
365 /* read a lump of data, allocating the space for it */
366 void *ntdb_alloc_read(struct ntdb_context *ntdb, ntdb_off_t offset, ntdb_len_t len)
367 {
368         return _ntdb_alloc_read(ntdb, offset, len, 0);
369 }
370
371 static enum NTDB_ERROR fill(struct ntdb_context *ntdb,
372                            const void *buf, size_t size,
373                            ntdb_off_t off, ntdb_len_t len)
374 {
375         while (len) {
376                 size_t n = len > size ? size : len;
377                 ssize_t ret = pwrite(ntdb->file->fd, buf, n, off);
378                 if (ret != n) {
379                         if (ret >= 0)
380                                 errno = ENOSPC;
381
382                         return ntdb_logerr(ntdb, NTDB_ERR_IO, NTDB_LOG_ERROR,
383                                           "fill failed:"
384                                           " %zi at %zu len=%zu (%s)",
385                                           ret, (size_t)off, (size_t)len,
386                                           strerror(errno));
387                 }
388                 len -= n;
389                 off += n;
390         }
391         return NTDB_SUCCESS;
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 enum NTDB_ERROR ntdb_expand_file(struct ntdb_context *ntdb,
397                                       ntdb_len_t addition)
398 {
399         char buf[8192];
400         enum NTDB_ERROR ecode;
401
402         assert((ntdb->file->map_size + addition) % NTDB_PGSIZE == 0);
403         if (ntdb->flags & NTDB_RDONLY) {
404                 return ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
405                                   "Expand on read-only database");
406         }
407
408         if (ntdb->flags & NTDB_INTERNAL) {
409                 char *new = ntdb->expand_fn(ntdb->file->map_ptr,
410                                         ntdb->file->map_size + addition,
411                                         ntdb->alloc_data);
412                 if (!new) {
413                         return ntdb_logerr(ntdb, NTDB_ERR_OOM, NTDB_LOG_ERROR,
414                                           "No memory to expand database");
415                 }
416                 ntdb->file->map_ptr = new;
417                 ntdb->file->map_size += addition;
418                 return NTDB_SUCCESS;
419         } else {
420                 /* Unmap before trying to write; old NTDB claimed OpenBSD had
421                  * problem with this otherwise. */
422                 ntdb_munmap(ntdb->file);
423
424                 /* If this fails, we try to fill anyway. */
425                 if (ftruncate(ntdb->file->fd, ntdb->file->map_size + addition))
426                         ;
427
428                 /* now fill the file with something. This ensures that the
429                    file isn't sparse, which would be very bad if we ran out of
430                    disk. This must be done with write, not via mmap */
431                 memset(buf, 0x43, sizeof(buf));
432                 ecode = fill(ntdb, buf, sizeof(buf), ntdb->file->map_size,
433                              addition);
434                 if (ecode != NTDB_SUCCESS)
435                         return ecode;
436                 ntdb->file->map_size += addition;
437                 return ntdb_mmap(ntdb);
438         }
439 }
440
441 const void *ntdb_access_read(struct ntdb_context *ntdb,
442                             ntdb_off_t off, ntdb_len_t len, bool convert)
443 {
444         void *ret = NULL;
445
446         if (likely(!(ntdb->flags & NTDB_CONVERT))) {
447                 ret = ntdb->io->direct(ntdb, off, len, false);
448
449                 if (NTDB_PTR_IS_ERR(ret)) {
450                         return ret;
451                 }
452         }
453         if (!ret) {
454                 struct ntdb_access_hdr *hdr;
455                 hdr = _ntdb_alloc_read(ntdb, off, len, sizeof(*hdr));
456                 if (NTDB_PTR_IS_ERR(hdr)) {
457                         return hdr;
458                 }
459                 hdr->next = ntdb->access;
460                 ntdb->access = hdr;
461                 ret = hdr + 1;
462                 if (convert) {
463                         ntdb_convert(ntdb, (void *)ret, len);
464                 }
465         } else
466                 ntdb->direct_access++;
467
468         return ret;
469 }
470
471 void *ntdb_access_write(struct ntdb_context *ntdb,
472                        ntdb_off_t off, ntdb_len_t len, bool convert)
473 {
474         void *ret = NULL;
475
476         if (ntdb->flags & NTDB_RDONLY) {
477                 ntdb_logerr(ntdb, NTDB_ERR_RDONLY, NTDB_LOG_USE_ERROR,
478                            "Write to read-only database");
479                 return NTDB_ERR_PTR(NTDB_ERR_RDONLY);
480         }
481
482         if (likely(!(ntdb->flags & NTDB_CONVERT))) {
483                 ret = ntdb->io->direct(ntdb, off, len, true);
484
485                 if (NTDB_PTR_IS_ERR(ret)) {
486                         return ret;
487                 }
488         }
489
490         if (!ret) {
491                 struct ntdb_access_hdr *hdr;
492                 hdr = _ntdb_alloc_read(ntdb, off, len, sizeof(*hdr));
493                 if (NTDB_PTR_IS_ERR(hdr)) {
494                         return hdr;
495                 }
496                 hdr->next = ntdb->access;
497                 ntdb->access = hdr;
498                 hdr->off = off;
499                 hdr->len = len;
500                 hdr->convert = convert;
501                 ret = hdr + 1;
502                 if (convert)
503                         ntdb_convert(ntdb, (void *)ret, len);
504         } else
505                 ntdb->direct_access++;
506
507         return ret;
508 }
509
510 static struct ntdb_access_hdr **find_hdr(struct ntdb_context *ntdb, const void *p)
511 {
512         struct ntdb_access_hdr **hp;
513
514         for (hp = &ntdb->access; *hp; hp = &(*hp)->next) {
515                 if (*hp + 1 == p)
516                         return hp;
517         }
518         return NULL;
519 }
520
521 void ntdb_access_release(struct ntdb_context *ntdb, const void *p)
522 {
523         struct ntdb_access_hdr *hdr, **hp = find_hdr(ntdb, p);
524
525         if (hp) {
526                 hdr = *hp;
527                 *hp = hdr->next;
528                 ntdb->free_fn(hdr, ntdb->alloc_data);
529         } else
530                 ntdb->direct_access--;
531 }
532
533 enum NTDB_ERROR ntdb_access_commit(struct ntdb_context *ntdb, void *p)
534 {
535         struct ntdb_access_hdr *hdr, **hp = find_hdr(ntdb, p);
536         enum NTDB_ERROR ecode;
537
538         if (hp) {
539                 hdr = *hp;
540                 if (hdr->convert)
541                         ecode = ntdb_write_convert(ntdb, hdr->off, p, hdr->len);
542                 else
543                         ecode = ntdb_write(ntdb, hdr->off, p, hdr->len);
544                 *hp = hdr->next;
545                 ntdb->free_fn(hdr, ntdb->alloc_data);
546         } else {
547                 ntdb->direct_access--;
548                 ecode = NTDB_SUCCESS;
549         }
550
551         return ecode;
552 }
553
554 static void *ntdb_direct(struct ntdb_context *ntdb, ntdb_off_t off, size_t len,
555                         bool write_mode)
556 {
557         enum NTDB_ERROR ecode;
558
559         if (unlikely(!ntdb->file->map_ptr))
560                 return NULL;
561
562         ecode = ntdb_oob(ntdb, off, len, false);
563         if (unlikely(ecode != NTDB_SUCCESS))
564                 return NTDB_ERR_PTR(ecode);
565         return (char *)ntdb->file->map_ptr + off;
566 }
567
568 static ntdb_off_t ntdb_read_normal_off(struct ntdb_context *ntdb,
569                                        ntdb_off_t off)
570 {
571         ntdb_off_t ret;
572         enum NTDB_ERROR ecode;
573         ntdb_off_t *p;
574
575         p = ntdb_direct(ntdb, off, sizeof(*p), false);
576         if (NTDB_PTR_IS_ERR(p)) {
577                 return NTDB_ERR_TO_OFF(NTDB_PTR_ERR(p));
578         }
579         if (likely(p)) {
580                 return *p;
581         }
582
583         ecode = ntdb_read(ntdb, off, &ret, sizeof(ret));
584         if (ecode != NTDB_SUCCESS) {
585                 return NTDB_ERR_TO_OFF(ecode);
586         }
587         return ret;
588 }
589
590 static ntdb_off_t ntdb_read_convert_off(struct ntdb_context *ntdb,
591                                         ntdb_off_t off)
592 {
593         ntdb_off_t ret;
594         enum NTDB_ERROR ecode;
595
596         ecode = ntdb_read_convert(ntdb, off, &ret, sizeof(ret));
597         if (ecode != NTDB_SUCCESS) {
598                 return NTDB_ERR_TO_OFF(ecode);
599         }
600         return ret;
601 }
602
603 static enum NTDB_ERROR ntdb_write_normal_off(struct ntdb_context *ntdb,
604                                              ntdb_off_t off, ntdb_off_t val)
605 {
606         ntdb_off_t *p;
607
608         p = ntdb_direct(ntdb, off, sizeof(*p), true);
609         if (NTDB_PTR_IS_ERR(p)) {
610                 return NTDB_PTR_ERR(p);
611         }
612         if (likely(p)) {
613                 *p = val;
614                 return NTDB_SUCCESS;
615         }
616         return ntdb_write(ntdb, off, &val, sizeof(val));
617 }
618
619 static enum NTDB_ERROR ntdb_write_convert_off(struct ntdb_context *ntdb,
620                                               ntdb_off_t off, ntdb_off_t val)
621 {
622         return ntdb_write_convert(ntdb, off, &val, sizeof(val));
623 }
624
625 void ntdb_inc_seqnum(struct ntdb_context *ntdb)
626 {
627         ntdb_off_t seq;
628
629         if (likely(!(ntdb->flags & NTDB_CONVERT))) {
630                 int64_t *direct;
631
632                 direct = ntdb->io->direct(ntdb,
633                                          offsetof(struct ntdb_header, seqnum),
634                                          sizeof(*direct), true);
635                 if (likely(direct)) {
636                         /* Don't let it go negative, even briefly */
637                         if (unlikely((*direct) + 1) < 0)
638                                 *direct = 0;
639                         (*direct)++;
640                         return;
641                 }
642         }
643
644         seq = ntdb_read_off(ntdb, offsetof(struct ntdb_header, seqnum));
645         if (!NTDB_OFF_IS_ERR(seq)) {
646                 seq++;
647                 if (unlikely((int64_t)seq < 0))
648                         seq = 0;
649                 ntdb_write_off(ntdb, offsetof(struct ntdb_header, seqnum), seq);
650         }
651 }
652
653 static const struct ntdb_methods io_methods = {
654         ntdb_read,
655         ntdb_write,
656         ntdb_normal_oob,
657         ntdb_expand_file,
658         ntdb_direct,
659         ntdb_read_normal_off,
660         ntdb_write_normal_off,
661 };
662
663 static const struct ntdb_methods io_convert_methods = {
664         ntdb_read,
665         ntdb_write,
666         ntdb_normal_oob,
667         ntdb_expand_file,
668         ntdb_direct,
669         ntdb_read_convert_off,
670         ntdb_write_convert_off,
671 };
672
673 /*
674   initialise the default methods table
675 */
676 void ntdb_io_init(struct ntdb_context *ntdb)
677 {
678         if (ntdb->flags & NTDB_CONVERT)
679                 ntdb->io = &io_convert_methods;
680         else
681                 ntdb->io = &io_methods;
682 }