int transaction_brlock | ( | struct tdb_context * | tdb, | |
tdb_off_t | offset, | |||
int | rw_type, | |||
int | lck_type, | |||
int | probe, | |||
size_t | len | |||
) |
static int transaction_read | ( | struct tdb_context * | tdb, | |
tdb_off_t | off, | |||
void * | buf, | |||
tdb_len_t | len, | |||
int | cv | |||
) | [static] |
transaction.c の 138 行で定義されています。
参照先 tdb_transaction_el::data・tdb_context::ecode・tdb_transaction::elements_last・tdb_transaction::io_methods・tdb_transaction_el::length・tdb_transaction_el::offset・tdb_transaction_el::prev・tdb・tdb_convert()・TDB_DEBUG_FATAL・TDB_ERR_IO・tdb_methods::tdb_read・tdb_context::transaction・tdb_transaction::transaction_error.
00140 { 00141 struct tdb_transaction_el *el; 00142 00143 /* we need to walk the list backwards to get the most recent data */ 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 /* an overlapping read - needs to be split into up to 00155 2 reads and a memcpy */ 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 /* its not in the transaction elements - do a real read */ 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 }
static int transaction_write | ( | struct tdb_context * | tdb, | |
tdb_off_t | off, | |||
const void * | buf, | |||
tdb_len_t | len | |||
) | [static] |
transaction.c の 199 行で定義されています。
参照先 tdb_transaction_el::data・tdb_context::ecode・tdb_transaction::elements・tdb_transaction::elements_last・tdb_transaction::hash_heads・tdb_transaction_el::length・tdb_transaction_el::offset・tdb_transaction::old_map_size・tdb_transaction_el::prev・tdb・TDB_DEBUG_FATAL・TDB_ERR_IO・TDB_ERR_OOM・tdb_context::transaction・tdb_transaction::transaction_error.
参照元 tdb_transaction_start()・transaction_expand_file().
00201 { 00202 struct tdb_transaction_el *el, *best_el=NULL; 00203 00204 if (len == 0) { 00205 return 0; 00206 } 00207 00208 /* if the write is to a hash head, then update the transaction 00209 hash heads */ 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 /* first see if we can replace an existing entry */ 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 /* an overlapping write - needs to be split into up to 00232 2 writes and a memcpy */ 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 /* see if we can append the new entry to an existing entry */ 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 /* add a new entry at the end of the list */ 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 }
static void transaction_next_hash_chain | ( | struct tdb_context * | tdb, | |
u32 * | chain | |||
) | [static] |
transaction.c の 323 行で定義されています。
参照先 tdb_transaction::hash_heads・tdb_header::hash_size・tdb_context::header・tdb・tdb_context::transaction.
00324 { 00325 u32 h = *chain; 00326 for (;h < tdb->header.hash_size;h++) { 00327 /* the +1 takes account of the freelist */ 00328 if (0 != tdb->transaction->hash_heads[h+1]) { 00329 break; 00330 } 00331 } 00332 (*chain) = h; 00333 }
static int transaction_oob | ( | struct tdb_context * | tdb, | |
tdb_off_t | len, | |||
int | probe | |||
) | [static] |
transaction.c の 338 行で定義されています。
参照先 TDB_ERR_IO.
00339 { 00340 if (len <= tdb->map_size) { 00341 return 0; 00342 } 00343 return TDB_ERRCODE(TDB_ERR_IO, -1); 00344 }
static int transaction_expand_file | ( | struct tdb_context * | tdb, | |
tdb_off_t | size, | |||
tdb_off_t | addition | |||
) | [static] |
transaction.c の 349 行で定義されています。
参照先 tdb・transaction_write().
00351 { 00352 /* add a write to the transaction elements, so subsequent 00353 reads see the zero data */ 00354 if (transaction_write(tdb, size, NULL, addition) != 0) { 00355 return -1; 00356 } 00357 00358 return 0; 00359 }
int tdb_transaction_start | ( | struct tdb_context * | tdb | ) |
transaction.c の 384 行で定義されています。
参照先 tdb_lock_type::count・tdb_context::ecode・tdb_context::flags・tdb_context::global_lock・tdb_transaction::hash_heads・tdb_header::hash_size・tdb_context::header・tdb_transaction::io_methods・tdb_context::map_size・tdb_context::methods・tdb_transaction::nesting・tdb_traverse_lock::next・tdb_context::num_locks・tdb_transaction::old_map_size・tdb_context::read_only・tdb・tdb_brlock()・TDB_DEBUG_ERROR・TDB_DEBUG_FATAL・TDB_DEBUG_TRACE・TDB_ERR_EINVAL・TDB_ERR_IO・TDB_ERR_LOCK・TDB_ERR_OOM・tdb_methods::tdb_oob・tdb_methods::tdb_read・tdb_context::transaction・transaction_methods・transaction_write()・tdb_context::traverse_read・tdb_context::travlocks.
参照元 addrec_db()・init_registry_data()・tdb_trans_delete()・tdb_trans_store().
00385 { 00386 /* some sanity checks */ 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 /* cope with nested tdb_transaction_start() calls */ 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 /* the caller must not have any locks when starting a 00403 transaction as otherwise we'll be screwed by lack 00404 of nested locks in posix */ 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 /* you cannot use transactions inside a traverse (although you can use 00412 traverse inside a transaction) as otherwise you can end up with 00413 deadlock */ 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 /* get the transaction write lock. This is a blocking lock. As 00427 discussed with Volker, there are a number of ways we could 00428 make this async, which we will probably do in the future */ 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 /* get a read lock from the freelist to the end of file. This 00437 is upgraded to a write lock during the commit */ 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 /* setup a copy of the hash table heads so the hash scan in 00445 traverse can be fast */ 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 /* make sure we know about any file expansions already done by 00460 anyone else */ 00461 tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1); 00462 tdb->transaction->old_map_size = tdb->map_size; 00463 00464 /* finally hook the io methods, replacing them with 00465 transaction specific methods */ 00466 tdb->transaction->io_methods = tdb->methods; 00467 tdb->methods = &transaction_methods; 00468 00469 /* by calling this transaction write here, we ensure that we don't grow the 00470 transaction linked list due to hash table updates */ 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 }
int tdb_transaction_cancel | ( | struct tdb_context * | tdb | ) |
transaction.c の 492 行で定義されています。
参照先 tdb_lock_type::count・tdb_transaction_el::data・tdb_transaction::elements・tdb_context::global_lock・tdb_transaction::hash_heads・tdb_header::hash_size・tdb_context::header・tdb_transaction::io_methods・tdb_lock_type::list・tdb_context::lockrecs・tdb_context::map_size・tdb_context::methods・tdb_transaction::nesting・tdb_transaction_el::next・tdb_context::num_lockrecs・tdb_context::num_locks・tdb_transaction::old_map_size・tdb・tdb_brlock()・TDB_DEBUG_ERROR・tdb_context::transaction・tdb_transaction::transaction_error.
参照元 addrec_db()・init_registry_data()・tdb_close()・tdb_trans_delete()・tdb_trans_store()・tdb_transaction_commit().
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 /* free all the transaction elements */ 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 /* remove any global lock created during the transaction */ 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 /* remove any locks created during the transaction */ 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 /* restore the normal io methods */ 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 }
static int transaction_sync | ( | struct tdb_context * | tdb, | |
tdb_off_t | offset, | |||
tdb_len_t | length | |||
) | [static] |
transaction.c の 547 行で定義されています。
参照先 tdb_context::ecode・errno・tdb_context::fd・tdb_context::map_ptr・tdb_context::page_size・strerror()・tdb・TDB_DEBUG_FATAL・TDB_ERR_IO.
参照元 tdb_transaction_commit()・tdb_transaction_recover()・transaction_setup_recovery().
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 }
static tdb_len_t tdb_recovery_size | ( | struct tdb_context * | tdb | ) | [static] |
transaction.c の 573 行で定義されています。
参照先 tdb_transaction::elements・tdb_transaction_el::length・tdb_transaction_el::next・tdb_transaction_el::offset・tdb_transaction::old_map_size・tdb・tdb_context::transaction.
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 }
static int tdb_recovery_allocate | ( | struct tdb_context * | tdb, | |
tdb_len_t * | recovery_size, | |||
tdb_off_t * | recovery_offset, | |||
tdb_len_t * | recovery_max_size | |||
) | [static] |
transaction.c の 593 行で定義されています。
参照先 tdb_transaction::io_methods・tdb_context::map_size・methods・tdb_transaction::old_map_size・tdb_context::page_size・list_struct::rec_len・tdb・TDB_DEBUG_FATAL・tdb_free()・tdb_ofs_read()・tdb_recovery_size()・tdb_context::transaction.
参照元 transaction_setup_recovery().
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 /* it fits in the existing area */ 00619 *recovery_max_size = rec.rec_len; 00620 *recovery_offset = recovery_head; 00621 return 0; 00622 } 00623 00624 /* we need to free up the old recovery area, then allocate a 00625 new one at the end of the file. Note that we cannot use 00626 tdb_allocate() to allocate the new one as that might return 00627 us an area that is being currently used (as of the start of 00628 the transaction) */ 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 /* the tdb_free() call might have increased the recovery size */ 00637 *recovery_size = tdb_recovery_size(tdb); 00638 00639 /* round up to a multiple of page size */ 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 /* remap the file (if using mmap) */ 00652 methods->tdb_oob(tdb, tdb->map_size + 1, 1); 00653 00654 /* we have to reset the old map size so that we don't try to expand the file 00655 again in the transaction commit, which would destroy the recovery area */ 00656 tdb->transaction->old_map_size = tdb->map_size; 00657 00658 /* write the recovery header offset and sync - we can sync without a race here 00659 as the magic ptr in the recovery record has not been set */ 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 }
static int transaction_setup_recovery | ( | struct tdb_context * | tdb, | |
tdb_off_t * | magic_offset | |||
) | [static] |
transaction.c の 674 行で定義されています。
参照先 tdb_transaction_el::data・tdb_context::ecode・tdb_transaction::elements・tdb_transaction::io_methods・tdb_transaction_el::length・list_struct::magic・methods・tdb_transaction_el::next・tdb_transaction_el::offset・tdb_transaction::old_map_size・tdb・tdb_convert()・TDB_DEBUG_FATAL・TDB_ERR_CORRUPT・TDB_ERR_IO・TDB_ERR_OOM・tdb_recovery_allocate()・tdb_context::transaction・transaction_sync().
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 check that the recovery area has enough space 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 /* build the recovery data into a single blob to allow us to do a single 00710 large write, which should be more efficient */ 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 /* the recovery area contains the old data, not the 00728 new data, so we have to call the original tdb_read 00729 method to get it */ 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 /* and the tailer */ 00739 tailer = sizeof(*rec) + recovery_max_size; 00740 memcpy(p, &tailer, 4); 00741 CONVERT(p); 00742 00743 /* write the recovery data to the recovery area */ 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 /* as we don't have ordered writes, we have to sync the recovery 00752 data before we update the magic to indicate that the recovery 00753 data is present */ 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 /* ensure the recovery magic marker is on disk */ 00773 if (transaction_sync(tdb, *magic_offset, sizeof(magic)) == -1) { 00774 return -1; 00775 } 00776 00777 return 0; 00778 }
int tdb_transaction_commit | ( | struct tdb_context * | tdb | ) |
transaction.c の 783 行で定義されています。
参照先 tdb_lock_type::count・tdb_transaction_el::data・tdb_context::ecode・tdb_transaction::elements・tdb_context::flags・tdb_context::global_lock・tdb_transaction::io_methods・tdb_transaction_el::length・tdb_context::map_size・methods・tdb_context::methods・tdb_context::name・tdb_transaction::nesting・tdb_transaction_el::next・tdb_context::num_locks・tdb_transaction_el::offset・tdb_transaction::old_map_size・tdb・tdb_brlock()・tdb_brlock_upgrade()・TDB_DEBUG_ERROR・TDB_DEBUG_FATAL・TDB_ERR_IO・TDB_ERR_LOCK・tdb_transaction_cancel()・tdb_transaction_recover()・tdb_context::transaction・tdb_transaction::transaction_error・transaction_setup_recovery()・transaction_sync().
参照元 addrec_db()・init_registry_data()・tdb_trans_delete()・tdb_trans_store().
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 /* check for a null transaction */ 00807 if (tdb->transaction->elements == NULL) { 00808 tdb_transaction_cancel(tdb); 00809 return 0; 00810 } 00811 00812 methods = tdb->transaction->io_methods; 00813 00814 /* if there are any locks pending then the caller has not 00815 nested their locks properly, so fail the transaction */ 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 /* upgrade the main transaction lock region to a write lock */ 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 /* get the global lock - this prevents new users attaching to the database 00832 during the commit */ 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 /* write the recovery data to the end of the file */ 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 /* expand the file to the new size if needed */ 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 /* perform all the writes */ 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 /* we've overwritten part of the data and 00873 possibly expanded the file, so we need to 00874 run the crash recovery code */ 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 /* ensure the new data is on disk */ 00891 if (transaction_sync(tdb, 0, tdb->map_size) == -1) { 00892 return -1; 00893 } 00894 00895 /* remove the recovery marker */ 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 /* ensure the recovery marker has been removed on disk */ 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 TODO: maybe write to some dummy hdr field, or write to magic 00911 offset without mmap, before the last sync, instead of the 00912 utime() call 00913 */ 00914 00915 /* on some systems (like Linux 2.6.x) changes via mmap/msync 00916 don't change the mtime of the file, this means the file may 00917 not be backed up (as tdb rounding to block sizes means that 00918 file size changes are quite rare too). The following forces 00919 mtime changes when a transaction completes */ 00920 #ifdef HAVE_UTIME 00921 utime(tdb->name, NULL); 00922 #endif 00923 00924 /* use a transaction cancel to free memory and remove the 00925 transaction locks */ 00926 tdb_transaction_cancel(tdb); 00927 return 0; 00928 }
int tdb_transaction_recover | ( | struct tdb_context * | tdb | ) |
transaction.c の 936 行で定義されています。
参照先 tdb_transaction_el::data・tdb_context::ecode・tdb_context::fd・len・list_struct::magic・tdb_context::map_size・tdb_context::methods・tdb_context::read_only・tdb・tdb_convert()・TDB_DEBUG_FATAL・TDB_DEBUG_TRACE・TDB_ERR_CORRUPT・TDB_ERR_IO・TDB_ERR_OOM・tdb_mmap()・tdb_munmap()・tdb_ofs_read()・tdb_ofs_write()・tdb_methods::tdb_read・tdb_methods::tdb_write・transaction_sync().
参照元 tdb_open_ex()・tdb_transaction_commit().
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 /* find the recovery area */ 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 /* we have never allocated a recovery record */ 00952 return 0; 00953 } 00954 00955 /* read the recovery record */ 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 /* there is no valid recovery data */ 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 /* read the full recovery data */ 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 /* recover the file data */ 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 /* if the recovery area is after the recovered eof then remove it */ 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 /* remove the recovery magic */ 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 /* reduce the file size to the old size */ 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 /* all done */ 01055 return 0; 01056 }
struct tdb_methods transaction_methods [static] |
初期値:
{ transaction_read, transaction_write, transaction_next_hash_chain, transaction_oob, transaction_expand_file, transaction_brlock }
transaction.c の 370 行で定義されています。