tdb/common/transaction.c

ソースコードを見る。

データ構造

struct  tdb_transaction_el
struct  tdb_transaction

関数

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 int transaction_write (struct tdb_context *tdb, tdb_off_t off, const void *buf, tdb_len_t len)
static void transaction_next_hash_chain (struct tdb_context *tdb, u32 *chain)
static int transaction_oob (struct tdb_context *tdb, tdb_off_t len, int probe)
static int transaction_expand_file (struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition)
int tdb_transaction_start (struct tdb_context *tdb)
int tdb_transaction_cancel (struct tdb_context *tdb)
static int transaction_sync (struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length)
static tdb_len_t tdb_recovery_size (struct tdb_context *tdb)
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 int transaction_setup_recovery (struct tdb_context *tdb, tdb_off_t *magic_offset)
int tdb_transaction_commit (struct tdb_context *tdb)
int tdb_transaction_recover (struct tdb_context *tdb)

変数

static struct tdb_methods transaction_methods


関数

int transaction_brlock ( struct tdb_context tdb,
tdb_off_t  offset,
int  rw_type,
int  lck_type,
int  probe,
size_t  len 
)

transaction.c364 行で定義されています。

00366 {
00367         return 0;
00368 }

static int transaction_read ( struct tdb_context tdb,
tdb_off_t  off,
void *  buf,
tdb_len_t  len,
int  cv 
) [static]

transaction.c138 行で定義されています。

参照先 tdb_transaction_el::datatdb_context::ecodetdb_transaction::elements_lasttdb_transaction::io_methodstdb_transaction_el::lengthtdb_transaction_el::offsettdb_transaction_el::prevtdbtdb_convert()TDB_DEBUG_FATALTDB_ERR_IOtdb_methods::tdb_readtdb_context::transactiontdb_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.c199 行で定義されています。

参照先 tdb_transaction_el::datatdb_context::ecodetdb_transaction::elementstdb_transaction::elements_lasttdb_transaction::hash_headstdb_transaction_el::lengthtdb_transaction_el::offsettdb_transaction::old_map_sizetdb_transaction_el::prevtdbTDB_DEBUG_FATALTDB_ERR_IOTDB_ERR_OOMtdb_context::transactiontdb_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.c323 行で定義されています。

参照先 tdb_transaction::hash_headstdb_header::hash_sizetdb_context::headertdbtdb_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.c338 行で定義されています。

参照先 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.c349 行で定義されています。

参照先 tdbtransaction_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.c384 行で定義されています。

参照先 tdb_lock_type::counttdb_context::ecodetdb_context::flagstdb_context::global_locktdb_transaction::hash_headstdb_header::hash_sizetdb_context::headertdb_transaction::io_methodstdb_context::map_sizetdb_context::methodstdb_transaction::nestingtdb_traverse_lock::nexttdb_context::num_lockstdb_transaction::old_map_sizetdb_context::read_onlytdbtdb_brlock()TDB_DEBUG_ERRORTDB_DEBUG_FATALTDB_DEBUG_TRACETDB_ERR_EINVALTDB_ERR_IOTDB_ERR_LOCKTDB_ERR_OOMtdb_methods::tdb_oobtdb_methods::tdb_readtdb_context::transactiontransaction_methodstransaction_write()tdb_context::traverse_readtdb_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.c492 行で定義されています。

参照先 tdb_lock_type::counttdb_transaction_el::datatdb_transaction::elementstdb_context::global_locktdb_transaction::hash_headstdb_header::hash_sizetdb_context::headertdb_transaction::io_methodstdb_lock_type::listtdb_context::lockrecstdb_context::map_sizetdb_context::methodstdb_transaction::nestingtdb_transaction_el::nexttdb_context::num_lockrecstdb_context::num_lockstdb_transaction::old_map_sizetdbtdb_brlock()TDB_DEBUG_ERRORtdb_context::transactiontdb_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.c547 行で定義されています。

参照先 tdb_context::ecodeerrnotdb_context::fdtdb_context::map_ptrtdb_context::page_sizestrerror()tdbTDB_DEBUG_FATALTDB_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.c573 行で定義されています。

参照先 tdb_transaction::elementstdb_transaction_el::lengthtdb_transaction_el::nexttdb_transaction_el::offsettdb_transaction::old_map_sizetdbtdb_context::transaction.

参照元 tdb_recovery_allocate().

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.c593 行で定義されています。

参照先 tdb_transaction::io_methodstdb_context::map_sizemethodstdb_transaction::old_map_sizetdb_context::page_sizelist_struct::rec_lentdbTDB_DEBUG_FATALtdb_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.c674 行で定義されています。

参照先 tdb_transaction_el::datatdb_context::ecodetdb_transaction::elementstdb_transaction::io_methodstdb_transaction_el::lengthlist_struct::magicmethodstdb_transaction_el::nexttdb_transaction_el::offsettdb_transaction::old_map_sizetdbtdb_convert()TDB_DEBUG_FATALTDB_ERR_CORRUPTTDB_ERR_IOTDB_ERR_OOMtdb_recovery_allocate()tdb_context::transactiontransaction_sync().

参照元 tdb_transaction_commit().

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.c783 行で定義されています。

参照先 tdb_lock_type::counttdb_transaction_el::datatdb_context::ecodetdb_transaction::elementstdb_context::flagstdb_context::global_locktdb_transaction::io_methodstdb_transaction_el::lengthtdb_context::map_sizemethodstdb_context::methodstdb_context::nametdb_transaction::nestingtdb_transaction_el::nexttdb_context::num_lockstdb_transaction_el::offsettdb_transaction::old_map_sizetdbtdb_brlock()tdb_brlock_upgrade()TDB_DEBUG_ERRORTDB_DEBUG_FATALTDB_ERR_IOTDB_ERR_LOCKtdb_transaction_cancel()tdb_transaction_recover()tdb_context::transactiontdb_transaction::transaction_errortransaction_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.c936 行で定義されています。

参照先 tdb_transaction_el::datatdb_context::ecodetdb_context::fdlenlist_struct::magictdb_context::map_sizetdb_context::methodstdb_context::read_onlytdbtdb_convert()TDB_DEBUG_FATALTDB_DEBUG_TRACETDB_ERR_CORRUPTTDB_ERR_IOTDB_ERR_OOMtdb_mmap()tdb_munmap()tdb_ofs_read()tdb_ofs_write()tdb_methods::tdb_readtdb_methods::tdb_writetransaction_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.c370 行で定義されています。

参照元 tdb_transaction_start().


Sambaに対してSat Aug 29 21:24:30 2009に生成されました。  doxygen 1.4.7