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