]> git.ozlabs.org Git - ccan/blob - ccan/tdb/transaction.c
7e8ac34dd5bc7e93a9a20cd9ac7e127716d3e504
[ccan] / ccan / tdb / transaction.c
1  /* 
2    Unix SMB/CIFS implementation.
3
4    trivial database library
5
6    Copyright (C) Andrew Tridgell              2005
7
8      ** NOTE! The following LGPL license applies to the tdb
9      ** library. This does NOT imply that all of Samba is released
10      ** under the LGPL
11    
12    This library is free software; you can redistribute it and/or
13    modify it under the terms of the GNU Lesser General Public
14    License as published by the Free Software Foundation; either
15    version 3 of the License, or (at your option) any later version.
16
17    This library is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    Lesser General Public License for more details.
21
22    You should have received a copy of the GNU Lesser General Public
23    License along with this library; if not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "tdb_private.h"
27
28 /*
29   transaction design:
30
31   - only allow a single transaction at a time per database. This makes
32     using the transaction API simpler, as otherwise the caller would
33     have to cope with temporary failures in transactions that conflict
34     with other current transactions
35
36   - keep the transaction recovery information in the same file as the
37     database, using a special 'transaction recovery' record pointed at
38     by the header. This removes the need for extra journal files as
39     used by some other databases
40
41   - dynamically allocated the transaction recover record, re-using it
42     for subsequent transactions. If a larger record is needed then
43     tdb_free() the old record to place it on the normal tdb freelist
44     before allocating the new record
45
46   - during transactions, keep a linked list of writes all that have
47     been performed by intercepting all tdb_write() calls. The hooked
48     transaction versions of tdb_read() and tdb_write() check this
49     linked list and try to use the elements of the list in preference
50     to the real database.
51
52   - don't allow any locks to be held when a transaction starts,
53     otherwise we can end up with deadlock (plus lack of lock nesting
54     in posix locks would mean the lock is lost)
55
56   - if the caller gains a lock during the transaction but doesn't
57     release it then fail the commit
58
59   - allow for nested calls to tdb_transaction_start(), re-using the
60     existing transaction record. If the inner transaction is cancelled
61     then a subsequent commit will fail
62  
63   - keep a mirrored copy of the tdb hash chain heads to allow for the
64     fast hash heads scan on traverse, updating the mirrored copy in
65     the transaction version of tdb_write
66
67   - allow callers to mix transaction and non-transaction use of tdb,
68     although once a transaction is started then an exclusive lock is
69     gained until the transaction is committed or cancelled
70
71   - the commit stategy involves first saving away all modified data
72     into a linearised buffer in the transaction recovery area, then
73     marking the transaction recovery area with a magic value to
74     indicate a valid recovery record. In total 4 fsync/msync calls are
75     needed per commit to prevent race conditions. It might be possible
76     to reduce this to 3 or even 2 with some more work.
77
78   - check for a valid recovery record on open of the tdb, while the
79     global lock is held. Automatically recover from the transaction
80     recovery area if needed, then continue with the open as
81     usual. This allows for smooth crash recovery with no administrator
82     intervention.
83
84   - if TDB_NOSYNC is passed to flags in tdb_open then transactions are
85     still available, but no transaction recovery area is used and no
86     fsync/msync calls are made.
87
88   - if TDB_ALLOW_NESTING is passed to flags in tdb open, or added using
89     tdb_add_flags() transaction is enabled.
90     The default is that transaction nesting is not allowed and an attempt
91     to create a nested transaction will fail with TDB_ERR_NESTING.
92
93     Beware. when transactions are nested a transaction successfully
94     completed with tdb_transaction_commit() can be silently unrolled later.
95 */
96
97
98 /*
99   hold the context of any current transaction
100 */
101 struct tdb_transaction {
102         /* we keep a mirrored copy of the tdb hash heads here so
103            tdb_next_hash_chain() can operate efficiently */
104         uint32_t *hash_heads;
105
106         /* the original io methods - used to do IOs to the real db */
107         const struct tdb_methods *io_methods;
108
109         /* the list of transaction blocks. When a block is first
110            written to, it gets created in this list */
111         uint8_t **blocks;
112         uint32_t num_blocks;
113         uint32_t block_size;      /* bytes in each block */
114         uint32_t last_block_size; /* number of valid bytes in the last block */
115
116         /* non-zero when an internal transaction error has
117            occurred. All write operations will then fail until the
118            transaction is ended */
119         int transaction_error;
120
121         /* when inside a transaction we need to keep track of any
122            nested tdb_transaction_start() calls, as these are allowed,
123            but don't create a new transaction */
124         int nesting;
125
126         /* set when a prepare has already occurred */
127         bool prepared;
128         tdb_off_t magic_offset;
129
130         /* old file size before transaction */
131         tdb_len_t old_map_size;
132
133         /* we should re-pack on commit */
134         bool need_repack;
135 };
136
137
138 /*
139   read while in a transaction. We need to check first if the data is in our list
140   of transaction elements, then if not do a real read
141 */
142 static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf, 
143                             tdb_len_t len, int cv)
144 {
145         uint32_t blk;
146
147         /* Only a commit is allowed on a prepared transaction */
148         if (tdb->transaction->prepared) {
149                 tdb->ecode = TDB_ERR_EINVAL;
150                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_read: transaction already prepared, read not allowed\n"));
151                 tdb->transaction->transaction_error = 1;
152                 return -1;
153         }
154
155         /* break it down into block sized ops */
156         while (len + (off % tdb->transaction->block_size) > tdb->transaction->block_size) {
157                 tdb_len_t len2 = tdb->transaction->block_size - (off % tdb->transaction->block_size);
158                 if (transaction_read(tdb, off, buf, len2, cv) != 0) {
159                         return -1;
160                 }
161                 len -= len2;
162                 off += len2;
163                 buf = (void *)(len2 + (char *)buf);
164         }
165
166         if (len == 0) {
167                 return 0;
168         }
169
170         blk = off / tdb->transaction->block_size;
171
172         /* see if we have it in the block list */
173         if (tdb->transaction->num_blocks <= blk ||
174             tdb->transaction->blocks[blk] == NULL) {
175                 /* nope, do a real read */
176                 if (tdb->transaction->io_methods->tdb_read(tdb, off, buf, len, cv) != 0) {
177                         goto fail;
178                 }
179                 return 0;
180         }
181
182         /* it is in the block list. Now check for the last block */
183         if (blk == tdb->transaction->num_blocks-1) {
184                 if (len > tdb->transaction->last_block_size) {
185                         goto fail;
186                 }
187         }
188         
189         /* now copy it out of this block */
190         memcpy(buf, tdb->transaction->blocks[blk] + (off % tdb->transaction->block_size), len);
191         if (cv) {
192                 tdb_convert(buf, len);
193         }
194         return 0;
195
196 fail:
197         TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_read: failed at off=%d len=%d\n", off, len));
198         tdb->ecode = TDB_ERR_IO;
199         tdb->transaction->transaction_error = 1;
200         return -1;
201 }
202
203
204 /*
205   write while in a transaction
206 */
207 static int transaction_write(struct tdb_context *tdb, tdb_off_t off, 
208                              const void *buf, tdb_len_t len)
209 {
210         uint32_t blk;
211
212         /* Only a commit is allowed on a prepared transaction */
213         if (tdb->transaction->prepared) {
214                 tdb->ecode = TDB_ERR_EINVAL;
215                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: transaction already prepared, write not allowed\n"));
216                 tdb->transaction->transaction_error = 1;
217                 return -1;
218         }
219
220         /* if the write is to a hash head, then update the transaction
221            hash heads */
222         if (len == sizeof(tdb_off_t) && off >= FREELIST_TOP &&
223             off < FREELIST_TOP+TDB_HASHTABLE_SIZE(tdb)) {
224                 uint32_t chain = (off-FREELIST_TOP) / sizeof(tdb_off_t);
225                 memcpy(&tdb->transaction->hash_heads[chain], buf, len);
226         }
227
228         /* break it up into block sized chunks */
229         while (len + (off % tdb->transaction->block_size) > tdb->transaction->block_size) {
230                 tdb_len_t len2 = tdb->transaction->block_size - (off % tdb->transaction->block_size);
231                 if (transaction_write(tdb, off, buf, len2) != 0) {
232                         return -1;
233                 }
234                 len -= len2;
235                 off += len2;
236                 if (buf != NULL) {
237                         buf = (const void *)(len2 + (const char *)buf);
238                 }
239         }
240
241         if (len == 0) {
242                 return 0;
243         }
244
245         blk = off / tdb->transaction->block_size;
246         off = off % tdb->transaction->block_size;
247
248         if (tdb->transaction->num_blocks <= blk) {
249                 uint8_t **new_blocks;
250                 /* expand the blocks array */
251                 if (tdb->transaction->blocks == NULL) {
252                         new_blocks = (uint8_t **)malloc(
253                                 (blk+1)*sizeof(uint8_t *));
254                 } else {
255                         new_blocks = (uint8_t **)realloc(
256                                 tdb->transaction->blocks,
257                                 (blk+1)*sizeof(uint8_t *));
258                 }
259                 if (new_blocks == NULL) {
260                         tdb->ecode = TDB_ERR_OOM;
261                         goto fail;
262                 }
263                 memset(&new_blocks[tdb->transaction->num_blocks], 0, 
264                        (1+(blk - tdb->transaction->num_blocks))*sizeof(uint8_t *));
265                 tdb->transaction->blocks = new_blocks;
266                 tdb->transaction->num_blocks = blk+1;
267                 tdb->transaction->last_block_size = 0;
268         }
269
270         /* allocate and fill a block? */
271         if (tdb->transaction->blocks[blk] == NULL) {
272                 tdb->transaction->blocks[blk] = (uint8_t *)calloc(tdb->transaction->block_size, 1);
273                 if (tdb->transaction->blocks[blk] == NULL) {
274                         tdb->ecode = TDB_ERR_OOM;
275                         tdb->transaction->transaction_error = 1;
276                         return -1;                      
277                 }
278                 if (tdb->transaction->old_map_size > blk * tdb->transaction->block_size) {
279                         tdb_len_t len2 = tdb->transaction->block_size;
280                         if (len2 + (blk * tdb->transaction->block_size) > tdb->transaction->old_map_size) {
281                                 len2 = tdb->transaction->old_map_size - (blk * tdb->transaction->block_size);
282                         }
283                         if (tdb->transaction->io_methods->tdb_read(tdb, blk * tdb->transaction->block_size, 
284                                                                    tdb->transaction->blocks[blk], 
285                                                                    len2, 0) != 0) {
286                                 SAFE_FREE(tdb->transaction->blocks[blk]);                               
287                                 tdb->ecode = TDB_ERR_IO;
288                                 goto fail;
289                         }
290                         if (blk == tdb->transaction->num_blocks-1) {
291                                 tdb->transaction->last_block_size = len2;
292                         }                       
293                 }
294         }
295         
296         /* overwrite part of an existing block */
297         if (buf == NULL) {
298                 memset(tdb->transaction->blocks[blk] + off, 0, len);
299         } else {
300                 memcpy(tdb->transaction->blocks[blk] + off, buf, len);
301         }
302         if (blk == tdb->transaction->num_blocks-1) {
303                 if (len + off > tdb->transaction->last_block_size) {
304                         tdb->transaction->last_block_size = len + off;
305                 }
306         }
307
308         return 0;
309
310 fail:
311         TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: failed at off=%d len=%d\n", 
312                  (blk*tdb->transaction->block_size) + off, len));
313         tdb->transaction->transaction_error = 1;
314         return -1;
315 }
316
317
318 /*
319   write while in a transaction - this varient never expands the transaction blocks, it only
320   updates existing blocks. This means it cannot change the recovery size
321 */
322 static int transaction_write_existing(struct tdb_context *tdb, tdb_off_t off, 
323                                       const void *buf, tdb_len_t len)
324 {
325         uint32_t blk;
326
327         /* break it up into block sized chunks */
328         while (len + (off % tdb->transaction->block_size) > tdb->transaction->block_size) {
329                 tdb_len_t len2 = tdb->transaction->block_size - (off % tdb->transaction->block_size);
330                 if (transaction_write_existing(tdb, off, buf, len2) != 0) {
331                         return -1;
332                 }
333                 len -= len2;
334                 off += len2;
335                 if (buf != NULL) {
336                         buf = (const void *)(len2 + (const char *)buf);
337                 }
338         }
339
340         if (len == 0) {
341                 return 0;
342         }
343
344         blk = off / tdb->transaction->block_size;
345         off = off % tdb->transaction->block_size;
346
347         if (tdb->transaction->num_blocks <= blk ||
348             tdb->transaction->blocks[blk] == NULL) {
349                 return 0;
350         }
351
352         if (blk == tdb->transaction->num_blocks-1 &&
353             off + len > tdb->transaction->last_block_size) {
354                 if (off >= tdb->transaction->last_block_size) {
355                         return 0;
356                 }
357                 len = tdb->transaction->last_block_size - off;
358         }
359
360         /* overwrite part of an existing block */
361         memcpy(tdb->transaction->blocks[blk] + off, buf, len);
362
363         return 0;
364 }
365
366
367 /*
368   accelerated hash chain head search, using the cached hash heads
369 */
370 static void transaction_next_hash_chain(struct tdb_context *tdb, uint32_t *chain)
371 {
372         uint32_t h = *chain;
373         for (;h < tdb->header.hash_size;h++) {
374                 /* the +1 takes account of the freelist */
375                 if (0 != tdb->transaction->hash_heads[h+1]) {
376                         break;
377                 }
378         }
379         (*chain) = h;
380 }
381
382 /*
383   out of bounds check during a transaction
384 */
385 static int transaction_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
386 {
387         if (len <= tdb->map_size) {
388                 return 0;
389         }
390         tdb->ecode = TDB_ERR_IO;
391         return -1;
392 }
393
394 /*
395   transaction version of tdb_expand().
396 */
397 static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size, 
398                                    tdb_off_t addition)
399 {
400         /* add a write to the transaction elements, so subsequent
401            reads see the zero data */
402         if (transaction_write(tdb, size, NULL, addition) != 0) {
403                 return -1;
404         }
405
406         tdb->transaction->need_repack = true;
407
408         return 0;
409 }
410
411 /*
412   brlock during a transaction - ignore them
413 */
414 static int transaction_brlock(struct tdb_context *tdb, tdb_off_t offset, 
415                               int rw_type, int lck_type, int probe, size_t len)
416 {
417         return 0;
418 }
419
420 static const struct tdb_methods transaction_methods = {
421         transaction_read,
422         transaction_write,
423         transaction_next_hash_chain,
424         transaction_oob,
425         transaction_expand_file,
426         transaction_brlock
427 };
428
429 /*
430   sync to disk
431 */
432 static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length)
433 {       
434         if (tdb->flags & TDB_NOSYNC) {
435                 return 0;
436         }
437
438         if (fsync(tdb->fd) != 0) {
439                 tdb->ecode = TDB_ERR_IO;
440                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n"));
441                 return -1;
442         }
443 #ifdef MS_SYNC
444         if (tdb->map_ptr) {
445                 tdb_off_t moffset = offset & ~(tdb->page_size-1);
446                 if (msync(moffset + (char *)tdb->map_ptr, 
447                           length + (offset - moffset), MS_SYNC) != 0) {
448                         tdb->ecode = TDB_ERR_IO;
449                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: msync failed - %s\n",
450                                  strerror(errno)));
451                         return -1;
452                 }
453         }
454 #endif
455         return 0;
456 }
457
458 int _tdb_transaction_cancel(struct tdb_context *tdb)
459 {
460         int i, ret = 0;
461
462         if (tdb->transaction == NULL) {
463                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_cancel: no transaction\n"));
464                 return -1;
465         }
466
467         if (tdb->transaction->nesting != 0) {
468                 tdb->transaction->transaction_error = 1;
469                 tdb->transaction->nesting--;
470                 return 0;
471         }               
472
473         tdb->map_size = tdb->transaction->old_map_size;
474
475         /* free all the transaction blocks */
476         for (i=0;i<tdb->transaction->num_blocks;i++) {
477                 if (tdb->transaction->blocks[i] != NULL) {
478                         free(tdb->transaction->blocks[i]);
479                 }
480         }
481         SAFE_FREE(tdb->transaction->blocks);
482
483         if (tdb->transaction->magic_offset) {
484                 const struct tdb_methods *methods = tdb->transaction->io_methods;
485                 uint32_t zero = 0;
486
487                 /* remove the recovery marker */
488                 if (methods->tdb_write(tdb, tdb->transaction->magic_offset, &zero, 4) == -1 ||
489                 transaction_sync(tdb, tdb->transaction->magic_offset, 4) == -1) {
490                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_cancel: failed to remove recovery magic\n"));
491                         ret = -1;
492                 }
493         }
494
495         /* remove any global lock created during the transaction */
496         if (tdb->global_lock.count != 0) {
497                 tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 4*tdb->header.hash_size);
498                 tdb->global_lock.count = 0;
499         }
500
501         /* remove any locks created during the transaction */
502         if (tdb->num_locks != 0) {
503                 for (i=0;i<tdb->num_lockrecs;i++) {
504                         tdb_brlock(tdb,FREELIST_TOP+4*tdb->lockrecs[i].list,
505                                    F_UNLCK,F_SETLKW, 0, 1);
506                 }
507                 tdb->num_locks = 0;
508                 tdb->num_lockrecs = 0;
509                 SAFE_FREE(tdb->lockrecs);
510         }
511
512         /* restore the normal io methods */
513         tdb->methods = tdb->transaction->io_methods;
514
515         tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0);
516         tdb_transaction_unlock(tdb);
517         SAFE_FREE(tdb->transaction->hash_heads);
518         SAFE_FREE(tdb->transaction);
519         
520         return ret;
521 }
522
523 /*
524   start a tdb transaction. No token is returned, as only a single
525   transaction is allowed to be pending per tdb_context
526 */
527 int tdb_transaction_start(struct tdb_context *tdb)
528 {
529         /* some sanity checks */
530         if (tdb->read_only || (tdb->flags & TDB_INTERNAL) || tdb->traverse_read) {
531                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction on a read-only or internal db\n"));
532                 tdb->ecode = TDB_ERR_EINVAL;
533                 return -1;
534         }
535
536         /* cope with nested tdb_transaction_start() calls */
537         if (tdb->transaction != NULL) {
538                 if (!(tdb->flags & TDB_ALLOW_NESTING)) {
539                         tdb->ecode = TDB_ERR_NESTING;
540                         return -1;
541                 }
542                 tdb_trace(tdb, "tdb_transaction_start");
543                 tdb->transaction->nesting++;
544                 TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_start: nesting %d\n", 
545                          tdb->transaction->nesting));
546                 return 0;
547         }
548
549         if (tdb->num_locks != 0 || tdb->global_lock.count) {
550                 /* the caller must not have any locks when starting a
551                    transaction as otherwise we'll be screwed by lack
552                    of nested locks in posix */
553                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction with locks held\n"));
554                 tdb->ecode = TDB_ERR_LOCK;
555                 return -1;
556         }
557
558         if (tdb->travlocks.next != NULL) {
559                 /* you cannot use transactions inside a traverse (although you can use
560                    traverse inside a transaction) as otherwise you can end up with
561                    deadlock */
562                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction within a traverse\n"));
563                 tdb->ecode = TDB_ERR_LOCK;
564                 return -1;
565         }
566
567         tdb->transaction = (struct tdb_transaction *)
568                 calloc(sizeof(struct tdb_transaction), 1);
569         if (tdb->transaction == NULL) {
570                 tdb->ecode = TDB_ERR_OOM;
571                 return -1;
572         }
573
574         /* a page at a time seems like a reasonable compromise between compactness and efficiency */
575         tdb->transaction->block_size = tdb->page_size;
576
577         /* get the transaction write lock. This is a blocking lock. As
578            discussed with Volker, there are a number of ways we could
579            make this async, which we will probably do in the future */
580         if (tdb_transaction_lock(tdb, F_WRLCK) == -1) {
581                 SAFE_FREE(tdb->transaction->blocks);
582                 SAFE_FREE(tdb->transaction);
583                 return -1;
584         }
585         
586         /* get a read lock from the freelist to the end of file. This
587            is upgraded to a write lock during the commit */
588         if (tdb_brlock(tdb, FREELIST_TOP, F_RDLCK, F_SETLKW, 0, 0) == -1) {
589                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to get hash locks\n"));
590                 tdb->ecode = TDB_ERR_LOCK;
591                 goto fail;
592         }
593
594         /* setup a copy of the hash table heads so the hash scan in
595            traverse can be fast */
596         tdb->transaction->hash_heads = (uint32_t *)
597                 calloc(tdb->header.hash_size+1, sizeof(uint32_t));
598         if (tdb->transaction->hash_heads == NULL) {
599                 tdb->ecode = TDB_ERR_OOM;
600                 goto fail;
601         }
602         if (tdb->methods->tdb_read(tdb, FREELIST_TOP, tdb->transaction->hash_heads,
603                                    TDB_HASHTABLE_SIZE(tdb), 0) != 0) {
604                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to read hash heads\n"));
605                 tdb->ecode = TDB_ERR_IO;
606                 goto fail;
607         }
608
609         /* make sure we know about any file expansions already done by
610            anyone else */
611         tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
612         tdb->transaction->old_map_size = tdb->map_size;
613
614         /* finally hook the io methods, replacing them with
615            transaction specific methods */
616         tdb->transaction->io_methods = tdb->methods;
617         tdb->methods = &transaction_methods;
618
619         /* Trace at the end, so we get sequence number correct. */
620         tdb_trace(tdb, "tdb_transaction_start");
621         return 0;
622         
623 fail:
624         tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0);
625         tdb_transaction_unlock(tdb);
626         SAFE_FREE(tdb->transaction->blocks);
627         SAFE_FREE(tdb->transaction->hash_heads);
628         SAFE_FREE(tdb->transaction);
629         return -1;
630 }
631
632
633 /*
634   cancel the current transaction
635 */
636 int tdb_transaction_cancel(struct tdb_context *tdb)
637 {       
638         tdb_trace(tdb, "tdb_transaction_cancel");
639         return _tdb_transaction_cancel(tdb);
640 }
641
642 /*
643   work out how much space the linearised recovery data will consume
644 */
645 static tdb_len_t tdb_recovery_size(struct tdb_context *tdb)
646 {
647         tdb_len_t recovery_size = 0;
648         int i;
649
650         recovery_size = sizeof(uint32_t);
651         for (i=0;i<tdb->transaction->num_blocks;i++) {
652                 if (i * tdb->transaction->block_size >= tdb->transaction->old_map_size) {
653                         break;
654                 }
655                 if (tdb->transaction->blocks[i] == NULL) {
656                         continue;
657                 }
658                 recovery_size += 2*sizeof(tdb_off_t);
659                 if (i == tdb->transaction->num_blocks-1) {
660                         recovery_size += tdb->transaction->last_block_size;
661                 } else {
662                         recovery_size += tdb->transaction->block_size;
663                 }
664         }       
665
666         return recovery_size;
667 }
668
669 /*
670   allocate the recovery area, or use an existing recovery area if it is
671   large enough
672 */
673 static int tdb_recovery_allocate(struct tdb_context *tdb, 
674                                  tdb_len_t *recovery_size,
675                                  tdb_off_t *recovery_offset,
676                                  tdb_len_t *recovery_max_size)
677 {
678         struct list_struct rec;
679         const struct tdb_methods *methods = tdb->transaction->io_methods;
680         tdb_off_t recovery_head;
681
682         if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
683                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery head\n"));
684                 return -1;
685         }
686
687         rec.rec_len = 0;
688
689         if (recovery_head != 0 && 
690             methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {
691                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n"));
692                 return -1;
693         }
694
695         *recovery_size = tdb_recovery_size(tdb);
696
697         if (recovery_head != 0 && *recovery_size <= rec.rec_len) {
698                 /* it fits in the existing area */
699                 *recovery_max_size = rec.rec_len;
700                 *recovery_offset = recovery_head;
701                 return 0;
702         }
703
704         /* we need to free up the old recovery area, then allocate a
705            new one at the end of the file. Note that we cannot use
706            tdb_allocate() to allocate the new one as that might return
707            us an area that is being currently used (as of the start of
708            the transaction) */
709         if (recovery_head != 0) {
710                 if (tdb_free(tdb, recovery_head, &rec) == -1) {
711                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to free previous recovery area\n"));
712                         return -1;
713                 }
714         }
715
716         /* the tdb_free() call might have increased the recovery size */
717         *recovery_size = tdb_recovery_size(tdb);
718
719         /* round up to a multiple of page size */
720         *recovery_max_size = TDB_ALIGN(sizeof(rec) + *recovery_size, tdb->page_size) - sizeof(rec);
721         *recovery_offset = tdb->map_size;
722         recovery_head = *recovery_offset;
723
724         if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, 
725                                      (tdb->map_size - tdb->transaction->old_map_size) +
726                                      sizeof(rec) + *recovery_max_size) == -1) {
727                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to create recovery area\n"));
728                 return -1;
729         }
730
731         /* remap the file (if using mmap) */
732         methods->tdb_oob(tdb, tdb->map_size + 1, 1);
733
734         /* we have to reset the old map size so that we don't try to expand the file
735            again in the transaction commit, which would destroy the recovery area */
736         tdb->transaction->old_map_size = tdb->map_size;
737
738         /* write the recovery header offset and sync - we can sync without a race here
739            as the magic ptr in the recovery record has not been set */
740         CONVERT(recovery_head);
741         if (methods->tdb_write(tdb, TDB_RECOVERY_HEAD, 
742                                &recovery_head, sizeof(tdb_off_t)) == -1) {
743                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n"));
744                 return -1;
745         }
746         if (transaction_write_existing(tdb, TDB_RECOVERY_HEAD, &recovery_head, sizeof(tdb_off_t)) == -1) {
747                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n"));
748                 return -1;
749         }
750
751         return 0;
752 }
753
754
755 /*
756   setup the recovery data that will be used on a crash during commit
757 */
758 static int transaction_setup_recovery(struct tdb_context *tdb, 
759                                       tdb_off_t *magic_offset)
760 {
761         tdb_len_t recovery_size;
762         unsigned char *data, *p;
763         const struct tdb_methods *methods = tdb->transaction->io_methods;
764         struct list_struct *rec;
765         tdb_off_t recovery_offset, recovery_max_size;
766         tdb_off_t old_map_size = tdb->transaction->old_map_size;
767         uint32_t magic, tailer;
768         int i;
769
770         /*
771           check that the recovery area has enough space
772         */
773         if (tdb_recovery_allocate(tdb, &recovery_size, 
774                                   &recovery_offset, &recovery_max_size) == -1) {
775                 return -1;
776         }
777
778         data = (unsigned char *)malloc(recovery_size + sizeof(*rec));
779         if (data == NULL) {
780                 tdb->ecode = TDB_ERR_OOM;
781                 return -1;
782         }
783
784         rec = (struct list_struct *)data;
785         memset(rec, 0, sizeof(*rec));
786
787         rec->magic    = 0;
788         rec->data_len = recovery_size;
789         rec->rec_len  = recovery_max_size;
790         rec->key_len  = old_map_size;
791         CONVERT(rec);
792
793         /* build the recovery data into a single blob to allow us to do a single
794            large write, which should be more efficient */
795         p = data + sizeof(*rec);
796         for (i=0;i<tdb->transaction->num_blocks;i++) {
797                 tdb_off_t offset;
798                 tdb_len_t length;
799
800                 if (tdb->transaction->blocks[i] == NULL) {
801                         continue;
802                 }
803
804                 offset = i * tdb->transaction->block_size;
805                 length = tdb->transaction->block_size;
806                 if (i == tdb->transaction->num_blocks-1) {
807                         length = tdb->transaction->last_block_size;
808                 }
809                 
810                 if (offset >= old_map_size) {
811                         continue;
812                 }
813                 if (offset + length > tdb->transaction->old_map_size) {
814                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: transaction data over new region boundary\n"));
815                         free(data);
816                         tdb->ecode = TDB_ERR_CORRUPT;
817                         return -1;
818                 }
819                 memcpy(p, &offset, 4);
820                 memcpy(p+4, &length, 4);
821                 if (DOCONV()) {
822                         tdb_convert(p, 8);
823                 }
824                 /* the recovery area contains the old data, not the
825                    new data, so we have to call the original tdb_read
826                    method to get it */
827                 if (methods->tdb_read(tdb, offset, p + 8, length, 0) != 0) {
828                         free(data);
829                         tdb->ecode = TDB_ERR_IO;
830                         return -1;
831                 }
832                 p += 8 + length;
833         }
834
835         /* and the tailer */
836         tailer = sizeof(*rec) + recovery_max_size;
837         memcpy(p, &tailer, 4);
838         CONVERT(p);
839
840         /* write the recovery data to the recovery area */
841         if (methods->tdb_write(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) {
842                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery data\n"));
843                 free(data);
844                 tdb->ecode = TDB_ERR_IO;
845                 return -1;
846         }
847         if (transaction_write_existing(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) {
848                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write secondary recovery data\n"));
849                 free(data);
850                 tdb->ecode = TDB_ERR_IO;
851                 return -1;
852         }
853
854         /* as we don't have ordered writes, we have to sync the recovery
855            data before we update the magic to indicate that the recovery
856            data is present */
857         if (transaction_sync(tdb, recovery_offset, sizeof(*rec) + recovery_size) == -1) {
858                 free(data);
859                 return -1;
860         }
861
862         free(data);
863
864         magic = TDB_RECOVERY_MAGIC;
865         CONVERT(magic);
866
867         *magic_offset = recovery_offset + offsetof(struct list_struct, magic);
868
869         if (methods->tdb_write(tdb, *magic_offset, &magic, sizeof(magic)) == -1) {
870                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery magic\n"));
871                 tdb->ecode = TDB_ERR_IO;
872                 return -1;
873         }
874         if (transaction_write_existing(tdb, *magic_offset, &magic, sizeof(magic)) == -1) {
875                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write secondary recovery magic\n"));
876                 tdb->ecode = TDB_ERR_IO;
877                 return -1;
878         }
879
880         /* ensure the recovery magic marker is on disk */
881         if (transaction_sync(tdb, *magic_offset, sizeof(magic)) == -1) {
882                 return -1;
883         }
884
885         return 0;
886 }
887
888 static int _tdb_transaction_prepare_commit(struct tdb_context *tdb)
889 {       
890         const struct tdb_methods *methods;
891
892         if (tdb->transaction == NULL) {
893                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: no transaction\n"));
894                 return -1;
895         }
896
897         if (tdb->transaction->prepared) {
898                 tdb->ecode = TDB_ERR_EINVAL;
899                 _tdb_transaction_cancel(tdb);
900                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: transaction already prepared\n"));
901                 return -1;
902         }
903
904         if (tdb->transaction->transaction_error) {
905                 tdb->ecode = TDB_ERR_IO;
906                 _tdb_transaction_cancel(tdb);
907                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: transaction error pending\n"));
908                 return -1;
909         }
910
911
912         if (tdb->transaction->nesting != 0) {
913                 tdb->transaction->nesting--;
914                 return 0;
915         }               
916
917 #ifdef TDB_TRACE
918         /* store seqnum now, before reading becomes illegal. */
919         tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &tdb->transaction_prepare_seqnum);
920 #endif
921
922         /* check for a null transaction */
923         if (tdb->transaction->blocks == NULL) {
924                 return 0;
925         }
926
927         methods = tdb->transaction->io_methods;
928         
929         /* if there are any locks pending then the caller has not
930            nested their locks properly, so fail the transaction */
931         if (tdb->num_locks || tdb->global_lock.count) {
932                 tdb->ecode = TDB_ERR_LOCK;
933                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: locks pending on commit\n"));
934                 _tdb_transaction_cancel(tdb);
935                 return -1;
936         }
937
938         /* upgrade the main transaction lock region to a write lock */
939         if (tdb_brlock_upgrade(tdb, FREELIST_TOP, 0) == -1) {
940                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to upgrade hash locks\n"));
941                 tdb->ecode = TDB_ERR_LOCK;
942                 _tdb_transaction_cancel(tdb);
943                 return -1;
944         }
945
946         /* get the global lock - this prevents new users attaching to the database
947            during the commit */
948         if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) {
949                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_prepare_commit: failed to get global lock\n"));
950                 tdb->ecode = TDB_ERR_LOCK;
951                 _tdb_transaction_cancel(tdb);
952                 return -1;
953         }
954
955         if (!(tdb->flags & TDB_NOSYNC)) {
956                 /* write the recovery data to the end of the file */
957                 if (transaction_setup_recovery(tdb, &tdb->transaction->magic_offset) == -1) {
958                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: failed to setup recovery data\n"));
959                         tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
960                         _tdb_transaction_cancel(tdb);
961                         return -1;
962                 }
963         }
964
965         tdb->transaction->prepared = true;
966
967         /* expand the file to the new size if needed */
968         if (tdb->map_size != tdb->transaction->old_map_size) {
969                 if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, 
970                                              tdb->map_size - 
971                                              tdb->transaction->old_map_size) == -1) {
972                         tdb->ecode = TDB_ERR_IO;
973                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_prepare_commit: expansion failed\n"));
974                         tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
975                         _tdb_transaction_cancel(tdb);
976                         return -1;
977                 }
978                 tdb->map_size = tdb->transaction->old_map_size;
979                 methods->tdb_oob(tdb, tdb->map_size + 1, 1);
980         }
981
982         /* Keep the global lock until the actual commit */
983
984         return 0;
985 }
986
987 /*
988    prepare to commit the current transaction
989 */
990 int tdb_transaction_prepare_commit(struct tdb_context *tdb)
991 {       
992         tdb_trace(tdb, "tdb_transaction_prepare_commit");
993         return _tdb_transaction_prepare_commit(tdb);
994 }
995
996 /*
997   commit the current transaction
998 */
999 int tdb_transaction_commit(struct tdb_context *tdb)
1000 {       
1001         const struct tdb_methods *methods;
1002         int i;
1003         bool need_repack;
1004
1005         if (tdb->transaction == NULL) {
1006                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n"));
1007                 return -1;
1008         }
1009
1010         /* If we've prepared, can't read seqnum. */
1011         if (tdb->transaction->prepared) {
1012                 tdb_trace_seqnum(tdb, tdb->transaction_prepare_seqnum,
1013                                  "tdb_transaction_commit");
1014         } else {
1015                 tdb_trace(tdb, "tdb_transaction_commit");
1016         }
1017
1018         if (tdb->transaction->transaction_error) {
1019                 tdb->ecode = TDB_ERR_IO;
1020                 tdb_transaction_cancel(tdb);
1021                 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: transaction error pending\n"));
1022                 return -1;
1023         }
1024
1025
1026         if (tdb->transaction->nesting != 0) {
1027                 tdb->transaction->nesting--;
1028                 return 0;
1029         }
1030
1031         /* check for a null transaction */
1032         if (tdb->transaction->blocks == NULL) {
1033                 _tdb_transaction_cancel(tdb);
1034                 return 0;
1035         }
1036
1037         if (!tdb->transaction->prepared) {
1038                 int ret = _tdb_transaction_prepare_commit(tdb);
1039                 if (ret)
1040                         return ret;
1041         }
1042
1043         methods = tdb->transaction->io_methods;
1044
1045         /* perform all the writes */
1046         for (i=0;i<tdb->transaction->num_blocks;i++) {
1047                 tdb_off_t offset;
1048                 tdb_len_t length;
1049
1050                 if (tdb->transaction->blocks[i] == NULL) {
1051                         continue;
1052                 }
1053
1054                 offset = i * tdb->transaction->block_size;
1055                 length = tdb->transaction->block_size;
1056                 if (i == tdb->transaction->num_blocks-1) {
1057                         length = tdb->transaction->last_block_size;
1058                 }
1059
1060                 if (methods->tdb_write(tdb, offset, tdb->transaction->blocks[i], length) == -1) {
1061                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed during commit\n"));
1062                         
1063                         /* we've overwritten part of the data and
1064                            possibly expanded the file, so we need to
1065                            run the crash recovery code */
1066                         tdb->methods = methods;
1067                         tdb_transaction_recover(tdb); 
1068
1069                         _tdb_transaction_cancel(tdb);
1070                         tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
1071
1072                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed\n"));
1073                         return -1;
1074                 }
1075                 SAFE_FREE(tdb->transaction->blocks[i]);
1076         } 
1077
1078         SAFE_FREE(tdb->transaction->blocks);
1079         tdb->transaction->num_blocks = 0;
1080
1081         /* ensure the new data is on disk */
1082         if (transaction_sync(tdb, 0, tdb->map_size) == -1) {
1083                 return -1;
1084         }
1085
1086         tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
1087
1088         /*
1089           TODO: maybe write to some dummy hdr field, or write to magic
1090           offset without mmap, before the last sync, instead of the
1091           utime() call
1092         */
1093
1094         /* on some systems (like Linux 2.6.x) changes via mmap/msync
1095            don't change the mtime of the file, this means the file may
1096            not be backed up (as tdb rounding to block sizes means that
1097            file size changes are quite rare too). The following forces
1098            mtime changes when a transaction completes */
1099 #if HAVE_UTIME
1100         utime(tdb->name, NULL);
1101 #endif
1102
1103         need_repack = tdb->transaction->need_repack;
1104
1105         /* use a transaction cancel to free memory and remove the
1106            transaction locks */
1107         _tdb_transaction_cancel(tdb);
1108
1109         if (need_repack) {
1110                 return tdb_repack(tdb);
1111         }
1112
1113         return 0;
1114 }
1115
1116
1117 /*
1118   recover from an aborted transaction. Must be called with exclusive
1119   database write access already established (including the global
1120   lock to prevent new processes attaching)
1121 */
1122 int tdb_transaction_recover(struct tdb_context *tdb)
1123 {
1124         tdb_off_t recovery_head, recovery_eof;
1125         unsigned char *data, *p;
1126         uint32_t zero = 0;
1127         struct list_struct rec;
1128
1129         /* find the recovery area */
1130         if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
1131                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery head\n"));
1132                 tdb->ecode = TDB_ERR_IO;
1133                 return -1;
1134         }
1135
1136         if (recovery_head == 0) {
1137                 /* we have never allocated a recovery record */
1138                 return 0;
1139         }
1140
1141         /* read the recovery record */
1142         if (tdb->methods->tdb_read(tdb, recovery_head, &rec, 
1143                                    sizeof(rec), DOCONV()) == -1) {
1144                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery record\n"));           
1145                 tdb->ecode = TDB_ERR_IO;
1146                 return -1;
1147         }
1148
1149         if (rec.magic != TDB_RECOVERY_MAGIC) {
1150                 /* there is no valid recovery data */
1151                 return 0;
1152         }
1153
1154         if (tdb->read_only) {
1155                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: attempt to recover read only database\n"));
1156                 tdb->ecode = TDB_ERR_CORRUPT;
1157                 return -1;
1158         }
1159
1160         recovery_eof = rec.key_len;
1161
1162         data = (unsigned char *)malloc(rec.data_len);
1163         if (data == NULL) {
1164                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to allocate recovery data\n"));         
1165                 tdb->ecode = TDB_ERR_OOM;
1166                 return -1;
1167         }
1168
1169         /* read the full recovery data */
1170         if (tdb->methods->tdb_read(tdb, recovery_head + sizeof(rec), data,
1171                                    rec.data_len, 0) == -1) {
1172                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery data\n"));             
1173                 tdb->ecode = TDB_ERR_IO;
1174                 return -1;
1175         }
1176
1177         /* recover the file data */
1178         p = data;
1179         while (p+8 < data + rec.data_len) {
1180                 uint32_t ofs, len;
1181                 if (DOCONV()) {
1182                         tdb_convert(p, 8);
1183                 }
1184                 memcpy(&ofs, p, 4);
1185                 memcpy(&len, p+4, 4);
1186
1187                 if (tdb->methods->tdb_write(tdb, ofs, p+8, len) == -1) {
1188                         free(data);
1189                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to recover %d bytes at offset %d\n", len, ofs));
1190                         tdb->ecode = TDB_ERR_IO;
1191                         return -1;
1192                 }
1193                 p += 8 + len;
1194         }
1195
1196         free(data);
1197
1198         if (transaction_sync(tdb, 0, tdb->map_size) == -1) {
1199                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync recovery\n"));
1200                 tdb->ecode = TDB_ERR_IO;
1201                 return -1;
1202         }
1203
1204         /* if the recovery area is after the recovered eof then remove it */
1205         if (recovery_eof <= recovery_head) {
1206                 if (tdb_ofs_write(tdb, TDB_RECOVERY_HEAD, &zero) == -1) {
1207                         TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery head\n"));
1208                         tdb->ecode = TDB_ERR_IO;
1209                         return -1;                      
1210                 }
1211         }
1212
1213         /* remove the recovery magic */
1214         if (tdb_ofs_write(tdb, recovery_head + offsetof(struct list_struct, magic), 
1215                           &zero) == -1) {
1216                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery magic\n"));
1217                 tdb->ecode = TDB_ERR_IO;
1218                 return -1;                      
1219         }
1220         
1221         /* reduce the file size to the old size */
1222         tdb_munmap(tdb);
1223         if (ftruncate(tdb->fd, recovery_eof) != 0) {
1224                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to reduce to recovery size\n"));
1225                 tdb->ecode = TDB_ERR_IO;
1226                 return -1;                      
1227         }
1228         tdb->map_size = recovery_eof;
1229         tdb_mmap(tdb);
1230
1231         if (transaction_sync(tdb, 0, recovery_eof) == -1) {
1232                 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync2 recovery\n"));
1233                 tdb->ecode = TDB_ERR_IO;
1234                 return -1;
1235         }
1236
1237         TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_recover: recovered %d byte database\n", 
1238                  recovery_eof));
1239
1240         /* all done */
1241         return 0;
1242 }