00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "tdb_private.h"
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091 int transaction_brlock(struct tdb_context *tdb, tdb_off_t offset,
00092 int rw_type, int lck_type, int probe, size_t len);
00093
00094 struct tdb_transaction_el {
00095 struct tdb_transaction_el *next, *prev;
00096 tdb_off_t offset;
00097 tdb_len_t length;
00098 unsigned char *data;
00099 };
00100
00101
00102
00103
00104 struct tdb_transaction {
00105
00106
00107 u32 *hash_heads;
00108
00109
00110 const struct tdb_methods *io_methods;
00111
00112
00113
00114
00115
00116
00117 struct tdb_transaction_el *elements, *elements_last;
00118
00119
00120
00121
00122 int transaction_error;
00123
00124
00125
00126
00127 int nesting;
00128
00129
00130 tdb_len_t old_map_size;
00131 };
00132
00133
00134
00135
00136
00137
00138 static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf,
00139 tdb_len_t len, int cv)
00140 {
00141 struct tdb_transaction_el *el;
00142
00143
00144 for (el=tdb->transaction->elements_last;el;el=el->prev) {
00145 tdb_len_t partial;
00146
00147 if (off+len <= el->offset) {
00148 continue;
00149 }
00150 if (off >= el->offset + el->length) {
00151 continue;
00152 }
00153
00154
00155
00156 if (off < el->offset) {
00157 partial = el->offset - off;
00158 if (transaction_read(tdb, off, buf, partial, cv) != 0) {
00159 goto fail;
00160 }
00161 len -= partial;
00162 off += partial;
00163 buf = (void *)(partial + (char *)buf);
00164 }
00165 if (off + len <= el->offset + el->length) {
00166 partial = len;
00167 } else {
00168 partial = el->offset + el->length - off;
00169 }
00170 memcpy(buf, el->data + (off - el->offset), partial);
00171 if (cv) {
00172 tdb_convert(buf, len);
00173 }
00174 len -= partial;
00175 off += partial;
00176 buf = (void *)(partial + (char *)buf);
00177
00178 if (len != 0 && transaction_read(tdb, off, buf, len, cv) != 0) {
00179 goto fail;
00180 }
00181
00182 return 0;
00183 }
00184
00185
00186 return tdb->transaction->io_methods->tdb_read(tdb, off, buf, len, cv);
00187
00188 fail:
00189 TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_read: failed at off=%d len=%d\n", off, len));
00190 tdb->ecode = TDB_ERR_IO;
00191 tdb->transaction->transaction_error = 1;
00192 return -1;
00193 }
00194
00195
00196
00197
00198
00199 static int transaction_write(struct tdb_context *tdb, tdb_off_t off,
00200 const void *buf, tdb_len_t len)
00201 {
00202 struct tdb_transaction_el *el, *best_el=NULL;
00203
00204 if (len == 0) {
00205 return 0;
00206 }
00207
00208
00209
00210 if (len == sizeof(tdb_off_t) && off >= FREELIST_TOP &&
00211 off < FREELIST_TOP+TDB_HASHTABLE_SIZE(tdb)) {
00212 u32 chain = (off-FREELIST_TOP) / sizeof(tdb_off_t);
00213 memcpy(&tdb->transaction->hash_heads[chain], buf, len);
00214 }
00215
00216
00217 for (el=tdb->transaction->elements_last;el;el=el->prev) {
00218 tdb_len_t partial;
00219
00220 if (best_el == NULL && off == el->offset+el->length) {
00221 best_el = el;
00222 }
00223
00224 if (off+len <= el->offset) {
00225 continue;
00226 }
00227 if (off >= el->offset + el->length) {
00228 continue;
00229 }
00230
00231
00232
00233 if (off < el->offset) {
00234 partial = el->offset - off;
00235 if (transaction_write(tdb, off, buf, partial) != 0) {
00236 goto fail;
00237 }
00238 len -= partial;
00239 off += partial;
00240 buf = (const void *)(partial + (const char *)buf);
00241 }
00242 if (off + len <= el->offset + el->length) {
00243 partial = len;
00244 } else {
00245 partial = el->offset + el->length - off;
00246 }
00247 memcpy(el->data + (off - el->offset), buf, partial);
00248 len -= partial;
00249 off += partial;
00250 buf = (const void *)(partial + (const char *)buf);
00251
00252 if (len != 0 && transaction_write(tdb, off, buf, len) != 0) {
00253 goto fail;
00254 }
00255
00256 return 0;
00257 }
00258
00259
00260 if (best_el && best_el->offset + best_el->length == off &&
00261 (off+len < tdb->transaction->old_map_size ||
00262 off > tdb->transaction->old_map_size)) {
00263 unsigned char *data = best_el->data;
00264 el = best_el;
00265 el->data = (unsigned char *)realloc(el->data,
00266 el->length + len);
00267 if (el->data == NULL) {
00268 tdb->ecode = TDB_ERR_OOM;
00269 tdb->transaction->transaction_error = 1;
00270 el->data = data;
00271 return -1;
00272 }
00273 if (buf) {
00274 memcpy(el->data + el->length, buf, len);
00275 } else {
00276 memset(el->data + el->length, TDB_PAD_BYTE, len);
00277 }
00278 el->length += len;
00279 return 0;
00280 }
00281
00282
00283 el = (struct tdb_transaction_el *)malloc(sizeof(*el));
00284 if (el == NULL) {
00285 tdb->ecode = TDB_ERR_OOM;
00286 tdb->transaction->transaction_error = 1;
00287 return -1;
00288 }
00289 el->next = NULL;
00290 el->prev = tdb->transaction->elements_last;
00291 el->offset = off;
00292 el->length = len;
00293 el->data = (unsigned char *)malloc(len);
00294 if (el->data == NULL) {
00295 free(el);
00296 tdb->ecode = TDB_ERR_OOM;
00297 tdb->transaction->transaction_error = 1;
00298 return -1;
00299 }
00300 if (buf) {
00301 memcpy(el->data, buf, len);
00302 } else {
00303 memset(el->data, TDB_PAD_BYTE, len);
00304 }
00305 if (el->prev) {
00306 el->prev->next = el;
00307 } else {
00308 tdb->transaction->elements = el;
00309 }
00310 tdb->transaction->elements_last = el;
00311 return 0;
00312
00313 fail:
00314 TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: failed at off=%d len=%d\n", off, len));
00315 tdb->ecode = TDB_ERR_IO;
00316 tdb->transaction->transaction_error = 1;
00317 return -1;
00318 }
00319
00320
00321
00322
00323 static void transaction_next_hash_chain(struct tdb_context *tdb, u32 *chain)
00324 {
00325 u32 h = *chain;
00326 for (;h < tdb->header.hash_size;h++) {
00327
00328 if (0 != tdb->transaction->hash_heads[h+1]) {
00329 break;
00330 }
00331 }
00332 (*chain) = h;
00333 }
00334
00335
00336
00337
00338 static int transaction_oob(struct tdb_context *tdb, tdb_off_t len, int probe)
00339 {
00340 if (len <= tdb->map_size) {
00341 return 0;
00342 }
00343 return TDB_ERRCODE(TDB_ERR_IO, -1);
00344 }
00345
00346
00347
00348
00349 static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size,
00350 tdb_off_t addition)
00351 {
00352
00353
00354 if (transaction_write(tdb, size, NULL, addition) != 0) {
00355 return -1;
00356 }
00357
00358 return 0;
00359 }
00360
00361
00362
00363
00364 int transaction_brlock(struct tdb_context *tdb, tdb_off_t offset,
00365 int rw_type, int lck_type, int probe, size_t len)
00366 {
00367 return 0;
00368 }
00369
00370 static const struct tdb_methods transaction_methods = {
00371 transaction_read,
00372 transaction_write,
00373 transaction_next_hash_chain,
00374 transaction_oob,
00375 transaction_expand_file,
00376 transaction_brlock
00377 };
00378
00379
00380
00381
00382
00383
00384 int tdb_transaction_start(struct tdb_context *tdb)
00385 {
00386
00387 if (tdb->read_only || (tdb->flags & TDB_INTERNAL) || tdb->traverse_read) {
00388 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction on a read-only or internal db\n"));
00389 tdb->ecode = TDB_ERR_EINVAL;
00390 return -1;
00391 }
00392
00393
00394 if (tdb->transaction != NULL) {
00395 tdb->transaction->nesting++;
00396 TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_start: nesting %d\n",
00397 tdb->transaction->nesting));
00398 return 0;
00399 }
00400
00401 if (tdb->num_locks != 0 || tdb->global_lock.count) {
00402
00403
00404
00405 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction with locks held\n"));
00406 tdb->ecode = TDB_ERR_LOCK;
00407 return -1;
00408 }
00409
00410 if (tdb->travlocks.next != NULL) {
00411
00412
00413
00414 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction within a traverse\n"));
00415 tdb->ecode = TDB_ERR_LOCK;
00416 return -1;
00417 }
00418
00419 tdb->transaction = (struct tdb_transaction *)
00420 calloc(sizeof(struct tdb_transaction), 1);
00421 if (tdb->transaction == NULL) {
00422 tdb->ecode = TDB_ERR_OOM;
00423 return -1;
00424 }
00425
00426
00427
00428
00429 if (tdb_brlock(tdb, TRANSACTION_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) {
00430 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to get transaction lock\n"));
00431 tdb->ecode = TDB_ERR_LOCK;
00432 SAFE_FREE(tdb->transaction);
00433 return -1;
00434 }
00435
00436
00437
00438 if (tdb_brlock(tdb, FREELIST_TOP, F_RDLCK, F_SETLKW, 0, 0) == -1) {
00439 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to get hash locks\n"));
00440 tdb->ecode = TDB_ERR_LOCK;
00441 goto fail;
00442 }
00443
00444
00445
00446 tdb->transaction->hash_heads = (u32 *)
00447 calloc(tdb->header.hash_size+1, sizeof(u32));
00448 if (tdb->transaction->hash_heads == NULL) {
00449 tdb->ecode = TDB_ERR_OOM;
00450 goto fail;
00451 }
00452 if (tdb->methods->tdb_read(tdb, FREELIST_TOP, tdb->transaction->hash_heads,
00453 TDB_HASHTABLE_SIZE(tdb), 0) != 0) {
00454 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to read hash heads\n"));
00455 tdb->ecode = TDB_ERR_IO;
00456 goto fail;
00457 }
00458
00459
00460
00461 tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1);
00462 tdb->transaction->old_map_size = tdb->map_size;
00463
00464
00465
00466 tdb->transaction->io_methods = tdb->methods;
00467 tdb->methods = &transaction_methods;
00468
00469
00470
00471 if (transaction_write(tdb, FREELIST_TOP, tdb->transaction->hash_heads,
00472 TDB_HASHTABLE_SIZE(tdb)) != 0) {
00473 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to prime hash table\n"));
00474 tdb->ecode = TDB_ERR_IO;
00475 goto fail;
00476 }
00477
00478 return 0;
00479
00480 fail:
00481 tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0);
00482 tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1);
00483 SAFE_FREE(tdb->transaction->hash_heads);
00484 SAFE_FREE(tdb->transaction);
00485 return -1;
00486 }
00487
00488
00489
00490
00491
00492 int tdb_transaction_cancel(struct tdb_context *tdb)
00493 {
00494 if (tdb->transaction == NULL) {
00495 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_cancel: no transaction\n"));
00496 return -1;
00497 }
00498
00499 if (tdb->transaction->nesting != 0) {
00500 tdb->transaction->transaction_error = 1;
00501 tdb->transaction->nesting--;
00502 return 0;
00503 }
00504
00505 tdb->map_size = tdb->transaction->old_map_size;
00506
00507
00508 while (tdb->transaction->elements) {
00509 struct tdb_transaction_el *el = tdb->transaction->elements;
00510 tdb->transaction->elements = el->next;
00511 free(el->data);
00512 free(el);
00513 }
00514
00515
00516 if (tdb->global_lock.count != 0) {
00517 tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 4*tdb->header.hash_size);
00518 tdb->global_lock.count = 0;
00519 }
00520
00521
00522 if (tdb->num_locks != 0) {
00523 int i;
00524 for (i=0;i<tdb->num_lockrecs;i++) {
00525 tdb_brlock(tdb,FREELIST_TOP+4*tdb->lockrecs[i].list,
00526 F_UNLCK,F_SETLKW, 0, 1);
00527 }
00528 tdb->num_locks = 0;
00529 tdb->num_lockrecs = 0;
00530 SAFE_FREE(tdb->lockrecs);
00531 }
00532
00533
00534 tdb->methods = tdb->transaction->io_methods;
00535
00536 tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0);
00537 tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1);
00538 SAFE_FREE(tdb->transaction->hash_heads);
00539 SAFE_FREE(tdb->transaction);
00540
00541 return 0;
00542 }
00543
00544
00545
00546
00547 static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length)
00548 {
00549 if (fsync(tdb->fd) != 0) {
00550 tdb->ecode = TDB_ERR_IO;
00551 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n"));
00552 return -1;
00553 }
00554 #ifdef MS_SYNC
00555 if (tdb->map_ptr) {
00556 tdb_off_t moffset = offset & ~(tdb->page_size-1);
00557 if (msync(moffset + (char *)tdb->map_ptr,
00558 length + (offset - moffset), MS_SYNC) != 0) {
00559 tdb->ecode = TDB_ERR_IO;
00560 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: msync failed - %s\n",
00561 strerror(errno)));
00562 return -1;
00563 }
00564 }
00565 #endif
00566 return 0;
00567 }
00568
00569
00570
00571
00572
00573 static tdb_len_t tdb_recovery_size(struct tdb_context *tdb)
00574 {
00575 struct tdb_transaction_el *el;
00576 tdb_len_t recovery_size = 0;
00577
00578 recovery_size = sizeof(u32);
00579 for (el=tdb->transaction->elements;el;el=el->next) {
00580 if (el->offset >= tdb->transaction->old_map_size) {
00581 continue;
00582 }
00583 recovery_size += 2*sizeof(tdb_off_t) + el->length;
00584 }
00585
00586 return recovery_size;
00587 }
00588
00589
00590
00591
00592
00593 static int tdb_recovery_allocate(struct tdb_context *tdb,
00594 tdb_len_t *recovery_size,
00595 tdb_off_t *recovery_offset,
00596 tdb_len_t *recovery_max_size)
00597 {
00598 struct list_struct rec;
00599 const struct tdb_methods *methods = tdb->transaction->io_methods;
00600 tdb_off_t recovery_head;
00601
00602 if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
00603 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery head\n"));
00604 return -1;
00605 }
00606
00607 rec.rec_len = 0;
00608
00609 if (recovery_head != 0 &&
00610 methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) {
00611 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n"));
00612 return -1;
00613 }
00614
00615 *recovery_size = tdb_recovery_size(tdb);
00616
00617 if (recovery_head != 0 && *recovery_size <= rec.rec_len) {
00618
00619 *recovery_max_size = rec.rec_len;
00620 *recovery_offset = recovery_head;
00621 return 0;
00622 }
00623
00624
00625
00626
00627
00628
00629 if (recovery_head != 0) {
00630 if (tdb_free(tdb, recovery_head, &rec) == -1) {
00631 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to free previous recovery area\n"));
00632 return -1;
00633 }
00634 }
00635
00636
00637 *recovery_size = tdb_recovery_size(tdb);
00638
00639
00640 *recovery_max_size = TDB_ALIGN(sizeof(rec) + *recovery_size, tdb->page_size) - sizeof(rec);
00641 *recovery_offset = tdb->map_size;
00642 recovery_head = *recovery_offset;
00643
00644 if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size,
00645 (tdb->map_size - tdb->transaction->old_map_size) +
00646 sizeof(rec) + *recovery_max_size) == -1) {
00647 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to create recovery area\n"));
00648 return -1;
00649 }
00650
00651
00652 methods->tdb_oob(tdb, tdb->map_size + 1, 1);
00653
00654
00655
00656 tdb->transaction->old_map_size = tdb->map_size;
00657
00658
00659
00660 CONVERT(recovery_head);
00661 if (methods->tdb_write(tdb, TDB_RECOVERY_HEAD,
00662 &recovery_head, sizeof(tdb_off_t)) == -1) {
00663 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n"));
00664 return -1;
00665 }
00666
00667 return 0;
00668 }
00669
00670
00671
00672
00673
00674 static int transaction_setup_recovery(struct tdb_context *tdb,
00675 tdb_off_t *magic_offset)
00676 {
00677 struct tdb_transaction_el *el;
00678 tdb_len_t recovery_size;
00679 unsigned char *data, *p;
00680 const struct tdb_methods *methods = tdb->transaction->io_methods;
00681 struct list_struct *rec;
00682 tdb_off_t recovery_offset, recovery_max_size;
00683 tdb_off_t old_map_size = tdb->transaction->old_map_size;
00684 u32 magic, tailer;
00685
00686
00687
00688
00689 if (tdb_recovery_allocate(tdb, &recovery_size,
00690 &recovery_offset, &recovery_max_size) == -1) {
00691 return -1;
00692 }
00693
00694 data = (unsigned char *)malloc(recovery_size + sizeof(*rec));
00695 if (data == NULL) {
00696 tdb->ecode = TDB_ERR_OOM;
00697 return -1;
00698 }
00699
00700 rec = (struct list_struct *)data;
00701 memset(rec, 0, sizeof(*rec));
00702
00703 rec->magic = 0;
00704 rec->data_len = recovery_size;
00705 rec->rec_len = recovery_max_size;
00706 rec->key_len = old_map_size;
00707 CONVERT(rec);
00708
00709
00710
00711 p = data + sizeof(*rec);
00712 for (el=tdb->transaction->elements;el;el=el->next) {
00713 if (el->offset >= old_map_size) {
00714 continue;
00715 }
00716 if (el->offset + el->length > tdb->transaction->old_map_size) {
00717 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: transaction data over new region boundary\n"));
00718 free(data);
00719 tdb->ecode = TDB_ERR_CORRUPT;
00720 return -1;
00721 }
00722 memcpy(p, &el->offset, 4);
00723 memcpy(p+4, &el->length, 4);
00724 if (DOCONV()) {
00725 tdb_convert(p, 8);
00726 }
00727
00728
00729
00730 if (methods->tdb_read(tdb, el->offset, p + 8, el->length, 0) != 0) {
00731 free(data);
00732 tdb->ecode = TDB_ERR_IO;
00733 return -1;
00734 }
00735 p += 8 + el->length;
00736 }
00737
00738
00739 tailer = sizeof(*rec) + recovery_max_size;
00740 memcpy(p, &tailer, 4);
00741 CONVERT(p);
00742
00743
00744 if (methods->tdb_write(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) {
00745 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery data\n"));
00746 free(data);
00747 tdb->ecode = TDB_ERR_IO;
00748 return -1;
00749 }
00750
00751
00752
00753
00754 if (transaction_sync(tdb, recovery_offset, sizeof(*rec) + recovery_size) == -1) {
00755 free(data);
00756 return -1;
00757 }
00758
00759 free(data);
00760
00761 magic = TDB_RECOVERY_MAGIC;
00762 CONVERT(magic);
00763
00764 *magic_offset = recovery_offset + offsetof(struct list_struct, magic);
00765
00766 if (methods->tdb_write(tdb, *magic_offset, &magic, sizeof(magic)) == -1) {
00767 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery magic\n"));
00768 tdb->ecode = TDB_ERR_IO;
00769 return -1;
00770 }
00771
00772
00773 if (transaction_sync(tdb, *magic_offset, sizeof(magic)) == -1) {
00774 return -1;
00775 }
00776
00777 return 0;
00778 }
00779
00780
00781
00782
00783 int tdb_transaction_commit(struct tdb_context *tdb)
00784 {
00785 const struct tdb_methods *methods;
00786 tdb_off_t magic_offset = 0;
00787 u32 zero = 0;
00788
00789 if (tdb->transaction == NULL) {
00790 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n"));
00791 return -1;
00792 }
00793
00794 if (tdb->transaction->transaction_error) {
00795 tdb->ecode = TDB_ERR_IO;
00796 tdb_transaction_cancel(tdb);
00797 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: transaction error pending\n"));
00798 return -1;
00799 }
00800
00801 if (tdb->transaction->nesting != 0) {
00802 tdb->transaction->nesting--;
00803 return 0;
00804 }
00805
00806
00807 if (tdb->transaction->elements == NULL) {
00808 tdb_transaction_cancel(tdb);
00809 return 0;
00810 }
00811
00812 methods = tdb->transaction->io_methods;
00813
00814
00815
00816 if (tdb->num_locks || tdb->global_lock.count) {
00817 tdb->ecode = TDB_ERR_LOCK;
00818 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: locks pending on commit\n"));
00819 tdb_transaction_cancel(tdb);
00820 return -1;
00821 }
00822
00823
00824 if (tdb_brlock_upgrade(tdb, FREELIST_TOP, 0) == -1) {
00825 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to upgrade hash locks\n"));
00826 tdb->ecode = TDB_ERR_LOCK;
00827 tdb_transaction_cancel(tdb);
00828 return -1;
00829 }
00830
00831
00832
00833 if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) {
00834 TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: failed to get global lock\n"));
00835 tdb->ecode = TDB_ERR_LOCK;
00836 tdb_transaction_cancel(tdb);
00837 return -1;
00838 }
00839
00840 if (!(tdb->flags & TDB_NOSYNC)) {
00841
00842 if (transaction_setup_recovery(tdb, &magic_offset) == -1) {
00843 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to setup recovery data\n"));
00844 tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
00845 tdb_transaction_cancel(tdb);
00846 return -1;
00847 }
00848 }
00849
00850
00851 if (tdb->map_size != tdb->transaction->old_map_size) {
00852 if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size,
00853 tdb->map_size -
00854 tdb->transaction->old_map_size) == -1) {
00855 tdb->ecode = TDB_ERR_IO;
00856 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: expansion failed\n"));
00857 tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
00858 tdb_transaction_cancel(tdb);
00859 return -1;
00860 }
00861 tdb->map_size = tdb->transaction->old_map_size;
00862 methods->tdb_oob(tdb, tdb->map_size + 1, 1);
00863 }
00864
00865
00866 while (tdb->transaction->elements) {
00867 struct tdb_transaction_el *el = tdb->transaction->elements;
00868
00869 if (methods->tdb_write(tdb, el->offset, el->data, el->length) == -1) {
00870 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed during commit\n"));
00871
00872
00873
00874
00875 tdb->methods = methods;
00876 tdb_transaction_recover(tdb);
00877
00878 tdb_transaction_cancel(tdb);
00879 tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
00880
00881 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed\n"));
00882 return -1;
00883 }
00884 tdb->transaction->elements = el->next;
00885 free(el->data);
00886 free(el);
00887 }
00888
00889 if (!(tdb->flags & TDB_NOSYNC)) {
00890
00891 if (transaction_sync(tdb, 0, tdb->map_size) == -1) {
00892 return -1;
00893 }
00894
00895
00896 if (methods->tdb_write(tdb, magic_offset, &zero, 4) == -1) {
00897 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to remove recovery magic\n"));
00898 return -1;
00899 }
00900
00901
00902 if (transaction_sync(tdb, magic_offset, 4) == -1) {
00903 return -1;
00904 }
00905 }
00906
00907 tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1);
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920 #ifdef HAVE_UTIME
00921 utime(tdb->name, NULL);
00922 #endif
00923
00924
00925
00926 tdb_transaction_cancel(tdb);
00927 return 0;
00928 }
00929
00930
00931
00932
00933
00934
00935
00936 int tdb_transaction_recover(struct tdb_context *tdb)
00937 {
00938 tdb_off_t recovery_head, recovery_eof;
00939 unsigned char *data, *p;
00940 u32 zero = 0;
00941 struct list_struct rec;
00942
00943
00944 if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) {
00945 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery head\n"));
00946 tdb->ecode = TDB_ERR_IO;
00947 return -1;
00948 }
00949
00950 if (recovery_head == 0) {
00951
00952 return 0;
00953 }
00954
00955
00956 if (tdb->methods->tdb_read(tdb, recovery_head, &rec,
00957 sizeof(rec), DOCONV()) == -1) {
00958 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery record\n"));
00959 tdb->ecode = TDB_ERR_IO;
00960 return -1;
00961 }
00962
00963 if (rec.magic != TDB_RECOVERY_MAGIC) {
00964
00965 return 0;
00966 }
00967
00968 if (tdb->read_only) {
00969 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: attempt to recover read only database\n"));
00970 tdb->ecode = TDB_ERR_CORRUPT;
00971 return -1;
00972 }
00973
00974 recovery_eof = rec.key_len;
00975
00976 data = (unsigned char *)malloc(rec.data_len);
00977 if (data == NULL) {
00978 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to allocate recovery data\n"));
00979 tdb->ecode = TDB_ERR_OOM;
00980 return -1;
00981 }
00982
00983
00984 if (tdb->methods->tdb_read(tdb, recovery_head + sizeof(rec), data,
00985 rec.data_len, 0) == -1) {
00986 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery data\n"));
00987 tdb->ecode = TDB_ERR_IO;
00988 return -1;
00989 }
00990
00991
00992 p = data;
00993 while (p+8 < data + rec.data_len) {
00994 u32 ofs, len;
00995 if (DOCONV()) {
00996 tdb_convert(p, 8);
00997 }
00998 memcpy(&ofs, p, 4);
00999 memcpy(&len, p+4, 4);
01000
01001 if (tdb->methods->tdb_write(tdb, ofs, p+8, len) == -1) {
01002 free(data);
01003 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to recover %d bytes at offset %d\n", len, ofs));
01004 tdb->ecode = TDB_ERR_IO;
01005 return -1;
01006 }
01007 p += 8 + len;
01008 }
01009
01010 free(data);
01011
01012 if (transaction_sync(tdb, 0, tdb->map_size) == -1) {
01013 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync recovery\n"));
01014 tdb->ecode = TDB_ERR_IO;
01015 return -1;
01016 }
01017
01018
01019 if (recovery_eof <= recovery_head) {
01020 if (tdb_ofs_write(tdb, TDB_RECOVERY_HEAD, &zero) == -1) {
01021 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery head\n"));
01022 tdb->ecode = TDB_ERR_IO;
01023 return -1;
01024 }
01025 }
01026
01027
01028 if (tdb_ofs_write(tdb, recovery_head + offsetof(struct list_struct, magic),
01029 &zero) == -1) {
01030 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery magic\n"));
01031 tdb->ecode = TDB_ERR_IO;
01032 return -1;
01033 }
01034
01035
01036 tdb_munmap(tdb);
01037 if (ftruncate(tdb->fd, recovery_eof) != 0) {
01038 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to reduce to recovery size\n"));
01039 tdb->ecode = TDB_ERR_IO;
01040 return -1;
01041 }
01042 tdb->map_size = recovery_eof;
01043 tdb_mmap(tdb);
01044
01045 if (transaction_sync(tdb, 0, recovery_eof) == -1) {
01046 TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync2 recovery\n"));
01047 tdb->ecode = TDB_ERR_IO;
01048 return -1;
01049 }
01050
01051 TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_recover: recovered %d byte database\n",
01052 recovery_eof));
01053
01054
01055 return 0;
01056 }