tdb2: Move min size constant out where summary.c can see it.
[ccan] / ccan / tdb2 / free.c
1  /* 
2    Trivial Database 2: free list/block handling
3    Copyright (C) Rusty Russell 2010
4    
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 3 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "private.h"
19 #include <ccan/likely/likely.h>
20 #include <time.h>
21 #include <assert.h>
22 #include <limits.h>
23
24 static unsigned fls64(uint64_t val)
25 {
26 #if HAVE_BUILTIN_CLZL
27         if (val <= ULONG_MAX) {
28                 /* This is significantly faster! */
29                 return val ? sizeof(long) * CHAR_BIT - __builtin_clzl(val) : 0;
30         } else {
31 #endif
32         uint64_t r = 64;
33
34         if (!val)
35                 return 0;
36         if (!(val & 0xffffffff00000000ull)) {
37                 val <<= 32;
38                 r -= 32;
39         }
40         if (!(val & 0xffff000000000000ull)) {
41                 val <<= 16;
42                 r -= 16;
43         }
44         if (!(val & 0xff00000000000000ull)) {
45                 val <<= 8;
46                 r -= 8;
47         }
48         if (!(val & 0xf000000000000000ull)) {
49                 val <<= 4;
50                 r -= 4;
51         }
52         if (!(val & 0xc000000000000000ull)) {
53                 val <<= 2;
54                 r -= 2;
55         }
56         if (!(val & 0x8000000000000000ull)) {
57                 val <<= 1;
58                 r -= 1;
59         }
60         return r;
61 #if HAVE_BUILTIN_CLZL
62         }
63 #endif
64 }
65
66 /* In which bucket would we find a particular record size? (ignoring header) */
67 unsigned int size_to_bucket(unsigned int zone_bits, tdb_len_t data_len)
68 {
69         unsigned int bucket;
70
71         /* We can't have records smaller than this. */
72         assert(data_len >= TDB_MIN_DATA_LEN);
73
74         /* Ignoring the header... */
75         if (data_len - TDB_MIN_DATA_LEN <= 64) {
76                 /* 0 in bucket 0, 8 in bucket 1... 64 in bucket 8. */
77                 bucket = (data_len - TDB_MIN_DATA_LEN) / 8;
78         } else {
79                 /* After that we go power of 2. */
80                 bucket = fls64(data_len - TDB_MIN_DATA_LEN) + 2;
81         }
82
83         if (unlikely(bucket > BUCKETS_FOR_ZONE(zone_bits)))
84                 bucket = BUCKETS_FOR_ZONE(zone_bits);
85         return bucket;
86 }
87
88 /* Subtract 1-byte tailer and header.  Then round up to next power of 2. */
89 static unsigned max_zone_bits(struct tdb_context *tdb)
90 {
91         return fls64(tdb->map_size-1-sizeof(struct tdb_header)-1) + 1;
92 }
93
94 /* Start by using a random zone to spread the load: returns the offset. */
95 static uint64_t random_zone(struct tdb_context *tdb)
96 {
97         struct free_zone_header zhdr;
98         tdb_off_t off = sizeof(struct tdb_header);
99         tdb_len_t half_bits;
100         uint64_t randbits = 0;
101         unsigned int i;
102
103         for (i = 0; i < 64; i += fls64(RAND_MAX)) 
104                 randbits ^= ((uint64_t)random()) << i;
105
106         /* FIXME: Does this work?  Test! */
107         half_bits = max_zone_bits(tdb) - 1;
108         do {
109                 /* Pick left or right side (not outside file) */
110                 if ((randbits & 1)
111                     && !tdb->methods->oob(tdb, off + (1ULL << half_bits)
112                                           + sizeof(zhdr), true)) {
113                         off += 1ULL << half_bits;
114                 }
115                 randbits >>= 1;
116
117                 if (tdb_read_convert(tdb, off, &zhdr, sizeof(zhdr)) == -1) 
118                         return TDB_OFF_ERR;
119
120                 if (zhdr.zone_bits == half_bits)
121                         return off;
122
123                 half_bits--;
124         } while (half_bits >= INITIAL_ZONE_BITS);
125
126         tdb->ecode = TDB_ERR_CORRUPT;
127         tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
128                  "random_zone: zone at %llu smaller than %u bits?",
129                  (long long)off, INITIAL_ZONE_BITS);
130         return TDB_OFF_ERR;
131 }
132
133 int tdb_zone_init(struct tdb_context *tdb)
134 {
135         tdb->zone_off = random_zone(tdb);
136         if (tdb->zone_off == TDB_OFF_ERR)
137                 return -1;
138         if (tdb_read_convert(tdb, tdb->zone_off,
139                              &tdb->zhdr, sizeof(tdb->zhdr)) == -1) 
140                 return -1;
141         return 0;
142 }
143
144 /* Where's the header, given a zone size of 1 << zone_bits? */
145 static tdb_off_t zone_off(tdb_off_t off, unsigned int zone_bits)
146 {
147         off -= sizeof(struct tdb_header);
148         return (off & ~((1ULL << zone_bits) - 1)) + sizeof(struct tdb_header);
149 }
150
151 /* Offset of a given bucket. */
152 /* FIXME: bucket can be "unsigned" everywhere, or even uint8/16. */
153 tdb_off_t bucket_off(tdb_off_t zone_off, tdb_off_t bucket)
154 {
155         return zone_off
156                 + sizeof(struct free_zone_header)
157                 + bucket * sizeof(tdb_off_t);
158 }
159
160 /* Returns free_buckets + 1, or list number to search. */
161 static tdb_off_t find_free_head(struct tdb_context *tdb, tdb_off_t bucket)
162 {
163         /* Speculatively search for a non-zero bucket. */
164         return tdb_find_nonzero_off(tdb, bucket_off(tdb->zone_off, 0),
165                                     bucket,
166                                     BUCKETS_FOR_ZONE(tdb->zhdr.zone_bits) + 1);
167 }
168
169 /* Remove from free bucket. */
170 static int remove_from_list(struct tdb_context *tdb,
171                             tdb_off_t b_off, struct tdb_free_record *r)
172 {
173         tdb_off_t off;
174
175         /* Front of list? */
176         if (r->prev == 0) {
177                 off = b_off;
178         } else {
179                 off = r->prev + offsetof(struct tdb_free_record, next);
180         }
181         /* r->prev->next = r->next */
182         if (tdb_write_off(tdb, off, r->next)) {
183                 return -1;
184         }
185
186         if (r->next != 0) {
187                 off = r->next + offsetof(struct tdb_free_record, prev);
188                 /* r->next->prev = r->prev */
189                 if (tdb_write_off(tdb, off, r->prev)) {
190                         return -1;
191                 }
192         }
193         return 0;
194 }
195
196 /* Enqueue in this free bucket. */
197 static int enqueue_in_free(struct tdb_context *tdb,
198                            tdb_off_t b_off,
199                            tdb_off_t off,
200                            struct tdb_free_record *new)
201 {
202         new->prev = 0;
203         /* new->next = head. */
204         new->next = tdb_read_off(tdb, b_off);
205         if (new->next == TDB_OFF_ERR)
206                 return -1;
207
208         if (new->next) {
209                 /* next->prev = new. */
210                 if (tdb_write_off(tdb, new->next
211                                   + offsetof(struct tdb_free_record, prev),
212                                   off) != 0)
213                         return -1;
214         }
215         /* head = new */
216         if (tdb_write_off(tdb, b_off, off) != 0)
217                 return -1;
218
219         return tdb_write_convert(tdb, off, new, sizeof(*new));
220 }
221
222 /* List need not be locked. */
223 int add_free_record(struct tdb_context *tdb,
224                     unsigned int zone_bits,
225                     tdb_off_t off, tdb_len_t len_with_header)
226 {
227         struct tdb_free_record new;
228         tdb_off_t b_off;
229         int ret;
230
231         assert(len_with_header >= sizeof(new));
232         assert(zone_bits < (1 << 6));
233
234         new.magic_and_meta = TDB_FREE_MAGIC | zone_bits;
235         new.data_len = len_with_header - sizeof(struct tdb_used_record);
236
237         b_off = bucket_off(zone_off(off, zone_bits),
238                            size_to_bucket(zone_bits, new.data_len));
239         if (tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) != 0)
240                 return -1;
241
242         ret = enqueue_in_free(tdb, b_off, off, &new);
243         tdb_unlock_free_bucket(tdb, b_off);
244         return ret;
245 }
246
247 /* If we have enough left over to be useful, split that off. */
248 static int to_used_record(struct tdb_context *tdb,
249                           unsigned int zone_bits,
250                           tdb_off_t off,
251                           tdb_len_t needed,
252                           tdb_len_t total_len,
253                           tdb_len_t *actual)
254 {
255         struct tdb_used_record used;
256         tdb_len_t leftover;
257
258         leftover = total_len - needed;
259         if (leftover < sizeof(struct tdb_free_record))
260                 leftover = 0;
261
262         *actual = total_len - leftover;
263
264         if (leftover) {
265                 if (add_free_record(tdb, zone_bits,
266                                     off + sizeof(used) + *actual,
267                                     total_len - needed))
268                         return -1;
269         }
270         return 0;
271 }
272
273 /* Note: we unlock the current bucket if we coalesce or fail. */
274 static int coalesce(struct tdb_context *tdb,
275                     tdb_off_t zone_off, unsigned zone_bits,
276                     tdb_off_t off, tdb_off_t b_off, tdb_len_t data_len)
277 {
278         struct tdb_free_record pad, *r;
279         tdb_off_t end = off + sizeof(struct tdb_used_record) + data_len;
280
281         while (end < (zone_off + (1ULL << zone_bits))) {
282                 tdb_off_t nb_off;
283
284                 /* FIXME: do tdb_get here and below really win? */
285                 r = tdb_get(tdb, end, &pad, sizeof(pad));
286                 if (!r)
287                         goto err;
288
289                 if (frec_magic(r) != TDB_FREE_MAGIC)
290                         break;
291
292                 nb_off = bucket_off(zone_off,
293                                     size_to_bucket(zone_bits, r->data_len));
294
295                 /* We may be violating lock order here, so best effort. */
296                 if (tdb_lock_free_bucket(tdb, nb_off, TDB_LOCK_NOWAIT) == -1)
297                         break;
298
299                 /* Now we have lock, re-check. */
300                 r = tdb_get(tdb, end, &pad, sizeof(pad));
301                 if (!r) {
302                         tdb_unlock_free_bucket(tdb, nb_off);
303                         goto err;
304                 }
305
306                 if (unlikely(frec_magic(r) != TDB_FREE_MAGIC)) {
307                         tdb_unlock_free_bucket(tdb, nb_off);
308                         break;
309                 }
310
311                 if (remove_from_list(tdb, nb_off, r) == -1) {
312                         tdb_unlock_free_bucket(tdb, nb_off);
313                         goto err;
314                 }
315
316                 end += sizeof(struct tdb_used_record) + r->data_len;
317                 tdb_unlock_free_bucket(tdb, nb_off);
318         }
319
320         /* Didn't find any adjacent free? */
321         if (end == off + sizeof(struct tdb_used_record) + data_len)
322                 return 0;
323
324         /* OK, expand record */
325         r = tdb_get(tdb, off, &pad, sizeof(pad));
326         if (!r)
327                 goto err;
328
329         if (r->data_len != data_len) {
330                 tdb->ecode = TDB_ERR_CORRUPT;
331                 tdb->log(tdb, TDB_DEBUG_FATAL, tdb->log_priv,
332                          "coalesce: expected data len %llu not %llu\n",
333                          (long long)data_len, (long long)r->data_len);
334                 goto err;
335         }
336
337         if (remove_from_list(tdb, b_off, r) == -1)
338                 goto err;
339
340         /* We have to drop this to avoid deadlocks. */
341         tdb_unlock_free_bucket(tdb, b_off);
342
343         if (add_free_record(tdb, zone_bits, off, end - off) == -1)
344                 return -1;
345         return 1;
346
347 err:
348         /* To unify error paths, we *always* unlock bucket on error. */
349         tdb_unlock_free_bucket(tdb, b_off);
350         return -1;
351 }
352
353 /* We need size bytes to put our key and data in. */
354 static tdb_off_t lock_and_alloc(struct tdb_context *tdb,
355                                 tdb_off_t zone_off,
356                                 unsigned zone_bits,
357                                 tdb_off_t bucket,
358                                 size_t size,
359                                 tdb_len_t *actual)
360 {
361         tdb_off_t off, b_off,best_off;
362         struct tdb_free_record pad, best = { 0 }, *r;
363         double multiplier;
364
365 again:
366         b_off = bucket_off(zone_off, bucket);
367
368         /* FIXME: Try non-blocking wait first, to measure contention.
369          * If we're contented, try switching zones, and don't enlarge zone
370          * next time (we want more zones). */
371         /* Lock this bucket. */
372         if (tdb_lock_free_bucket(tdb, b_off, TDB_LOCK_WAIT) == -1) {
373                 return TDB_OFF_ERR;
374         }
375
376         best.data_len = -1ULL;
377         best_off = 0;
378         /* FIXME: Start with larger multiplier if we're growing. */
379         multiplier = 1.0;
380
381         /* Walk the list to see if any are large enough, getting less fussy
382          * as we go. */
383         off = tdb_read_off(tdb, b_off);
384         if (unlikely(off == TDB_OFF_ERR))
385                 goto unlock_err;
386
387         while (off) {
388                 /* FIXME: Does tdb_get win anything here? */
389                 r = tdb_get(tdb, off, &pad, sizeof(*r));
390                 if (!r)
391                         goto unlock_err;
392
393                 if (frec_magic(r) != TDB_FREE_MAGIC) {
394                         tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
395                                  "lock_and_alloc: %llu non-free 0x%llx\n",
396                                  (long long)off, (long long)r->magic_and_meta);
397                         goto unlock_err;
398                 }
399
400                 if (r->data_len >= size && r->data_len < best.data_len) {
401                         best_off = off;
402                         best = *r;
403                 }
404
405                 if (best.data_len < size * multiplier && best_off)
406                         goto use_best;
407
408                 multiplier *= 1.01;
409
410                 /* Since we're going slow anyway, try coalescing here. */
411                 switch (coalesce(tdb, zone_off, zone_bits, off, b_off,
412                                  r->data_len)) {
413                 case -1:
414                         /* This has already unlocked on error. */
415                         return -1;
416                 case 1:
417                         /* This has unlocked list, restart. */
418                         goto again;
419                 }
420                 off = r->next;
421         }
422
423         /* If we found anything at all, use it. */
424         if (best_off) {
425         use_best:
426                 /* We're happy with this size: take it. */
427                 if (remove_from_list(tdb, b_off, &best) != 0)
428                         goto unlock_err;
429                 tdb_unlock_free_bucket(tdb, b_off);
430
431                 if (to_used_record(tdb, zone_bits, best_off, size,
432                                    best.data_len, actual)) {
433                         return -1;
434                 }
435                 return best_off;
436         }
437
438         tdb_unlock_free_bucket(tdb, b_off);
439         return 0;
440
441 unlock_err:
442         tdb_unlock_free_bucket(tdb, b_off);
443         return TDB_OFF_ERR;
444 }
445
446 static bool next_zone(struct tdb_context *tdb)
447 {
448         tdb_off_t next = tdb->zone_off + (1ULL << tdb->zhdr.zone_bits);
449
450         /* We must have a header. */
451         if (tdb->methods->oob(tdb, next + sizeof(tdb->zhdr), true))
452                 return false;
453
454         tdb->zone_off = next;
455         return tdb_read_convert(tdb, next, &tdb->zhdr, sizeof(tdb->zhdr)) == 0;
456 }
457
458 /* Offset returned is within current zone (which it may alter). */
459 static tdb_off_t get_free(struct tdb_context *tdb, size_t size,
460                           tdb_len_t *actual)
461 {
462         tdb_off_t start_zone = tdb->zone_off, off;
463         bool wrapped = false;
464
465         /* FIXME: If we don't get a hit in the first bucket we want,
466          * try changing zones for next time.  That should help wear
467          * zones evenly, so we don't need to search all of them before
468          * expanding. */
469         while (!wrapped || tdb->zone_off != start_zone) {
470                 tdb_off_t b;
471
472                 /* Shortcut for really huge allocations... */
473                 if ((size >> tdb->zhdr.zone_bits) != 0)
474                         continue;
475
476                 /* Start at exact size bucket, and search up... */
477                 b = size_to_bucket(tdb->zhdr.zone_bits, size);
478                 for (b = find_free_head(tdb, b);
479                      b <= BUCKETS_FOR_ZONE(tdb->zhdr.zone_bits);
480                      b += find_free_head(tdb, b + 1)) {
481                         /* Try getting one from list. */
482                         off = lock_and_alloc(tdb, tdb->zone_off,
483                                              tdb->zhdr.zone_bits,
484                                              b, size, actual);
485                         if (off == TDB_OFF_ERR)
486                                 return TDB_OFF_ERR;
487                         if (off != 0)
488                                 return off;
489                         /* Didn't work.  Try next bucket. */
490                 }
491
492                 /* Didn't work, try next zone, if it exists. */
493                 if (!next_zone(tdb)) {
494                         wrapped = true;
495                         tdb->zone_off = sizeof(struct tdb_header);
496                         if (tdb_read_convert(tdb, tdb->zone_off,
497                                              &tdb->zhdr, sizeof(tdb->zhdr))) {
498                                 return TDB_OFF_ERR;
499                         }
500                 }
501         }
502         return 0;
503 }
504
505 int set_header(struct tdb_context *tdb,
506                struct tdb_used_record *rec,
507                uint64_t keylen, uint64_t datalen,
508                uint64_t actuallen, uint64_t hash,
509                unsigned int zone_bits)
510 {
511         uint64_t keybits = (fls64(keylen) + 1) / 2;
512
513         /* Use bottom bits of hash, so it's independent of hash table size. */
514         rec->magic_and_meta
515                 = zone_bits
516                 | ((hash & ((1 << 5)-1)) << 6)
517                 | ((actuallen - (keylen + datalen)) << 11)
518                 | (keybits << 43)
519                 | (TDB_MAGIC << 48);
520         rec->key_and_data_len = (keylen | (datalen << (keybits*2)));
521
522         /* Encoding can fail on big values. */
523         if (rec_key_length(rec) != keylen
524             || rec_data_length(rec) != datalen
525             || rec_extra_padding(rec) != actuallen - (keylen + datalen)) {
526                 tdb->ecode = TDB_ERR_IO;
527                 tdb->log(tdb, TDB_DEBUG_ERROR, tdb->log_priv,
528                          "Could not encode k=%llu,d=%llu,a=%llu\n",
529                          (long long)keylen, (long long)datalen,
530                          (long long)actuallen);
531                 return -1;
532         }
533         return 0;
534 }
535
536 static bool zones_happy(struct tdb_context *tdb)
537 {
538         /* FIXME: look at distribution of zones. */
539         return true;
540 }
541
542 /* Assume we want buckets up to the comfort factor. */
543 static tdb_len_t overhead(unsigned int zone_bits)
544 {
545         return sizeof(struct free_zone_header)
546                 + (BUCKETS_FOR_ZONE(zone_bits) + 1) * sizeof(tdb_off_t);
547 }
548
549 /* Expand the database (by adding a zone). */
550 static int tdb_expand(struct tdb_context *tdb, tdb_len_t size)
551 {
552         uint64_t old_size;
553         tdb_off_t off;
554         uint8_t zone_bits;
555         unsigned int num_buckets;
556         tdb_len_t wanted;
557         struct free_zone_header zhdr;
558         bool enlarge_zone;
559
560         /* We need room for the record header too. */
561         wanted = sizeof(struct tdb_used_record) + size;
562
563         /* Only one person can expand file at a time. */
564         if (tdb_lock_expand(tdb, F_WRLCK) != 0)
565                 return -1;
566
567         /* Someone else may have expanded the file, so retry. */
568         old_size = tdb->map_size;
569         tdb->methods->oob(tdb, tdb->map_size + 1, true);
570         if (tdb->map_size != old_size)
571                 goto success;
572
573         /* FIXME: Tailer is a bogus optimization, remove it. */
574         /* zone bits tailer char is protected by EXPAND lock. */
575         if (tdb->methods->read(tdb, old_size - 1, &zone_bits, 1) == -1)
576                 goto fail;
577
578         /* If zones aren't working well, add larger zone if possible. */
579         enlarge_zone = !zones_happy(tdb);
580
581         /* New zone can be between zone_bits or larger if we're on the right
582          * boundary. */
583         for (;;) {
584                 /* Does this fit the allocation comfortably? */
585                 if ((1ULL << zone_bits) >= overhead(zone_bits) + wanted) {
586                         /* Only let enlarge_zone enlarge us once. */
587                         if (!enlarge_zone)
588                                 break;
589                         enlarge_zone = false;
590                 }
591                 if ((old_size - 1 - sizeof(struct tdb_header))
592                     & (1 << zone_bits))
593                         break;
594                 zone_bits++;
595         }
596
597         zhdr.zone_bits = zone_bits;
598         num_buckets = BUCKETS_FOR_ZONE(zone_bits);
599
600         /* FIXME: I don't think we need to expand to full zone, do we? */
601         if (tdb->methods->expand_file(tdb, 1ULL << zone_bits) == -1)
602                 goto fail;
603
604         /* Write new tailer. */
605         if (tdb->methods->write(tdb, tdb->map_size - 1, &zone_bits, 1) == -1)
606                 goto fail;
607
608         /* Write new zone header (just before old tailer). */
609         off = old_size - 1;
610         if (tdb_write_convert(tdb, off, &zhdr, sizeof(zhdr)) == -1)
611                 goto fail;
612
613         /* Now write empty buckets. */
614         off += sizeof(zhdr);
615         if (zero_out(tdb, off, (num_buckets+1) * sizeof(tdb_off_t)) == -1)
616                 goto fail;
617         off += (num_buckets+1) * sizeof(tdb_off_t);
618
619         /* Now add the rest as our free record. */
620         if (add_free_record(tdb, zone_bits, off, tdb->map_size-1-off) == -1)
621                 goto fail;
622
623         /* Try allocating from this zone now. */
624         tdb->zone_off = old_size - 1;
625         tdb->zhdr = zhdr;
626
627 success:
628         tdb_unlock_expand(tdb, F_WRLCK);
629         return 0;
630
631 fail:
632         tdb_unlock_expand(tdb, F_WRLCK);
633         return -1;
634 }
635
636 static tdb_len_t adjust_size(size_t keylen, size_t datalen, bool growing)
637 {
638         tdb_len_t size = keylen + datalen;
639
640         if (size < TDB_MIN_DATA_LEN)
641                 size = TDB_MIN_DATA_LEN;
642
643         /* Overallocate if this is coming from an enlarging store. */
644         if (growing)
645                 size += datalen / 2;
646
647         /* Round to next uint64_t boundary. */
648         return (size + (sizeof(uint64_t) - 1ULL)) & ~(sizeof(uint64_t) - 1ULL);
649 }
650
651 /* This won't fail: it will expand the database if it has to. */
652 tdb_off_t alloc(struct tdb_context *tdb, size_t keylen, size_t datalen,
653                 uint64_t hash, bool growing)
654 {
655         tdb_off_t off;
656         tdb_len_t size, actual;
657         struct tdb_used_record rec;
658
659         /* We can't hold pointers during this: we could unmap! */
660         assert(!tdb->direct_access);
661
662         size = adjust_size(keylen, datalen, growing);
663
664 again:
665         off = get_free(tdb, size, &actual);
666         if (unlikely(off == TDB_OFF_ERR))
667                 return off;
668
669         if (unlikely(off == 0)) {
670                 if (tdb_expand(tdb, size) == -1)
671                         return TDB_OFF_ERR;
672                 goto again;
673         }
674
675         /* Some supergiant values can't be encoded. */
676         /* FIXME: Check before, and limit actual in get_free. */
677         if (set_header(tdb, &rec, keylen, datalen, actual, hash,
678                        tdb->zhdr.zone_bits) != 0) {
679                 add_free_record(tdb, tdb->zhdr.zone_bits, off,
680                                 sizeof(rec) + actual);
681                 return TDB_OFF_ERR;
682         }
683
684         if (tdb_write_convert(tdb, off, &rec, sizeof(rec)) != 0)
685                 return TDB_OFF_ERR;
686         
687         return off;
688 }