X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ftdb2%2Ftransaction.c;h=2cab4655661768a80e44593211f152dd3c73175c;hp=6e0b1669fcd9fa1e2276282e621856f1f8236c3c;hb=2b5cb9bd6be4e61870ba7a400d1a9d91c5933371;hpb=51a56b52627e635566253a3fae081c3f466b6bb6 diff --git a/ccan/tdb2/transaction.c b/ccan/tdb2/transaction.c index 6e0b1669..2cab4655 100644 --- a/ccan/tdb2/transaction.c +++ b/ccan/tdb2/transaction.c @@ -53,13 +53,13 @@ - don't allow any locks to be held when a transaction starts, otherwise we can end up with deadlock (plus lack of lock nesting - in posix locks would mean the lock is lost) + in POSIX locks would mean the lock is lost) - if the caller gains a lock during the transaction but doesn't release it then fail the commit - allow for nested calls to tdb_transaction_start(), re-using the - existing transaction record. If the inner transaction is cancelled + existing transaction record. If the inner transaction is canceled then a subsequent commit will fail - keep a mirrored copy of the tdb hash chain heads to allow for the @@ -68,7 +68,7 @@ - allow callers to mix transaction and non-transaction use of tdb, although once a transaction is started then an exclusive lock is - gained until the transaction is committed or cancelled + gained until the transaction is committed or canceled - the commit stategy involves first saving away all modified data into a linearised buffer in the transaction recovery area, then @@ -125,16 +125,18 @@ struct tdb_transaction { read while in a transaction. We need to check first if the data is in our list of transaction elements, then if not do a real read */ -static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf, - tdb_len_t len) +static enum TDB_ERROR transaction_read(struct tdb_context *tdb, tdb_off_t off, + void *buf, tdb_len_t len) { size_t blk; + enum TDB_ERROR ecode; /* break it down into block sized ops */ while (len + (off % getpagesize()) > getpagesize()) { tdb_len_t len2 = getpagesize() - (off % getpagesize()); - if (transaction_read(tdb, off, buf, len2) != 0) { - return -1; + ecode = transaction_read(tdb, off, buf, len2); + if (ecode != TDB_SUCCESS) { + return ecode; } len -= len2; off += len2; @@ -142,7 +144,7 @@ static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf, } if (len == 0) { - return 0; + return TDB_SUCCESS; } blk = off / getpagesize(); @@ -151,7 +153,8 @@ static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf, if (tdb->transaction->num_blocks <= blk || tdb->transaction->blocks[blk] == NULL) { /* nope, do a real read */ - if (tdb->transaction->io_methods->read(tdb, off, buf, len) != 0) { + ecode = tdb->transaction->io_methods->tread(tdb, off, buf, len); + if (ecode != TDB_SUCCESS) { goto fail; } return 0; @@ -160,43 +163,45 @@ static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf, /* it is in the block list. Now check for the last block */ if (blk == tdb->transaction->num_blocks-1) { if (len > tdb->transaction->last_block_size) { + ecode = TDB_ERR_IO; goto fail; } } /* now copy it out of this block */ memcpy(buf, tdb->transaction->blocks[blk] + (off % getpagesize()), len); - return 0; + return TDB_SUCCESS; fail: - tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL, - "transaction_read: failed at off=%zu len=%zu", - (size_t)off, (size_t)len); tdb->transaction->transaction_error = 1; - return -1; + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "transaction_read: failed at off=%zu len=%zu", + (size_t)off, (size_t)len); } /* write while in a transaction */ -static int transaction_write(struct tdb_context *tdb, tdb_off_t off, - const void *buf, tdb_len_t len) +static enum TDB_ERROR transaction_write(struct tdb_context *tdb, tdb_off_t off, + const void *buf, tdb_len_t len) { size_t blk; + enum TDB_ERROR ecode; /* Only a commit is allowed on a prepared transaction */ if (tdb->transaction->prepared) { - tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_DEBUG_FATAL, - "transaction_write: transaction already prepared," - " write not allowed"); + ecode = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_ERROR, + "transaction_write: transaction already" + " prepared, write not allowed"); goto fail; } /* break it up into block sized chunks */ while (len + (off % getpagesize()) > getpagesize()) { tdb_len_t len2 = getpagesize() - (off % getpagesize()); - if (transaction_write(tdb, off, buf, len2) != 0) { + ecode = transaction_write(tdb, off, buf, len2); + if (ecode != TDB_SUCCESS) { return -1; } len -= len2; @@ -207,7 +212,7 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off, } if (len == 0) { - return 0; + return TDB_SUCCESS; } blk = off / getpagesize(); @@ -225,8 +230,9 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off, (blk+1)*sizeof(uint8_t *)); } if (new_blocks == NULL) { - tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL, - "transaction_write: failed to allocate"); + ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR, + "transaction_write:" + " failed to allocate"); goto fail; } memset(&new_blocks[tdb->transaction->num_blocks], 0, @@ -240,8 +246,9 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off, if (tdb->transaction->blocks[blk] == NULL) { tdb->transaction->blocks[blk] = (uint8_t *)calloc(getpagesize(), 1); if (tdb->transaction->blocks[blk] == NULL) { - tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL, - "transaction_write: failed to allocate"); + ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR, + "transaction_write:" + " failed to allocate"); goto fail; } if (tdb->transaction->old_map_size > blk * getpagesize()) { @@ -249,13 +256,17 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off, if (len2 + (blk * getpagesize()) > tdb->transaction->old_map_size) { len2 = tdb->transaction->old_map_size - (blk * getpagesize()); } - if (tdb->transaction->io_methods->read(tdb, blk * getpagesize(), - tdb->transaction->blocks[blk], - len2) != 0) { - tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL, - "transaction_write: failed to" - " read old block: %s", - strerror(errno)); + ecode = tdb->transaction->io_methods->tread(tdb, + blk * getpagesize(), + tdb->transaction->blocks[blk], + len2); + if (ecode != TDB_SUCCESS) { + ecode = tdb_logerr(tdb, ecode, + TDB_LOG_ERROR, + "transaction_write:" + " failed to" + " read old block: %s", + strerror(errno)); SAFE_FREE(tdb->transaction->blocks[blk]); goto fail; } @@ -277,16 +288,16 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off, } } - return 0; + return TDB_SUCCESS; fail: tdb->transaction->transaction_error = 1; - return -1; + return ecode; } /* - write while in a transaction - this varient never expands the transaction blocks, it only + write while in a transaction - this variant never expands the transaction blocks, it only updates existing blocks. This means it cannot change the recovery size */ static void transaction_write_existing(struct tdb_context *tdb, tdb_off_t off, @@ -333,37 +344,40 @@ static void transaction_write_existing(struct tdb_context *tdb, tdb_off_t off, /* out of bounds check during a transaction */ -static int transaction_oob(struct tdb_context *tdb, tdb_off_t len, bool probe) +static enum TDB_ERROR transaction_oob(struct tdb_context *tdb, tdb_off_t len, + bool probe) { if (len <= tdb->map_size) { - return 0; + return TDB_SUCCESS; } - tdb->ecode = TDB_ERR_IO; if (!probe) { - tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL, + tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR, "tdb_oob len %lld beyond transaction size %lld", (long long)len, (long long)tdb->map_size); } - return -1; + return TDB_ERR_IO; } /* transaction version of tdb_expand(). */ -static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t addition) +static enum TDB_ERROR transaction_expand_file(struct tdb_context *tdb, + tdb_off_t addition) { + enum TDB_ERROR ecode; + /* add a write to the transaction elements, so subsequent reads see the zero data */ - if (transaction_write(tdb, tdb->map_size, NULL, addition) != 0) { - return -1; + ecode = transaction_write(tdb, tdb->map_size, NULL, addition); + if (ecode == TDB_SUCCESS) { + tdb->map_size += addition; } - tdb->map_size += addition; - return 0; + return ecode; } static void *transaction_direct(struct tdb_context *tdb, tdb_off_t off, - size_t len, bool write) + size_t len, bool write_mode) { size_t blk = off / getpagesize(), end_blk; @@ -371,7 +385,7 @@ static void *transaction_direct(struct tdb_context *tdb, tdb_off_t off, end_blk = (off + len - 1) / getpagesize(); /* Can only do direct if in single block and we've already copied. */ - if (write) { + if (write_mode) { if (blk != end_blk) return NULL; if (blk >= tdb->transaction->num_blocks) @@ -395,7 +409,7 @@ static void *transaction_direct(struct tdb_context *tdb, tdb_off_t off, return NULL; blk++; } - return tdb->transaction->io_methods->direct(tdb, off, len, write); + return tdb->transaction->io_methods->direct(tdb, off, len, false); } static const struct tdb_methods transaction_methods = { @@ -409,40 +423,40 @@ static const struct tdb_methods transaction_methods = { /* sync to disk */ -static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length) +static enum TDB_ERROR transaction_sync(struct tdb_context *tdb, + tdb_off_t offset, tdb_len_t length) { if (tdb->flags & TDB_NOSYNC) { - return 0; + return TDB_SUCCESS; } if (fsync(tdb->fd) != 0) { - tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL, - "tdb_transaction: fsync failed: %s", - strerror(errno)); - return -1; + return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR, + "tdb_transaction: fsync failed: %s", + strerror(errno)); } #ifdef MS_SYNC if (tdb->map_ptr) { tdb_off_t moffset = offset & ~(getpagesize()-1); if (msync(moffset + (char *)tdb->map_ptr, length + (offset - moffset), MS_SYNC) != 0) { - tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_FATAL, - "tdb_transaction: msync failed: %s", - strerror(errno)); - return -1; + return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_ERROR, + "tdb_transaction: msync failed: %s", + strerror(errno)); } } #endif - return 0; + return TDB_SUCCESS; } static void _tdb_transaction_cancel(struct tdb_context *tdb) { int i; + enum TDB_ERROR ecode; if (tdb->transaction == NULL) { - tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_DEBUG_ERROR, + tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR, "tdb_transaction_cancel: no transaction"); return; } @@ -468,11 +482,14 @@ static void _tdb_transaction_cancel(struct tdb_context *tdb) uint64_t invalid = TDB_RECOVERY_INVALID_MAGIC; /* remove the recovery marker */ - if (methods->write(tdb, tdb->transaction->magic_offset, - &invalid, sizeof(invalid)) == -1 || - transaction_sync(tdb, tdb->transaction->magic_offset, - sizeof(invalid)) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, + ecode = methods->twrite(tdb, tdb->transaction->magic_offset, + &invalid, sizeof(invalid)); + if (ecode == TDB_SUCCESS) + ecode = transaction_sync(tdb, + tdb->transaction->magic_offset, + sizeof(invalid)); + if (ecode != TDB_SUCCESS) { + tdb_logerr(tdb, ecode, TDB_LOG_ERROR, "tdb_transaction_cancel: failed to remove" " recovery magic"); } @@ -496,54 +513,54 @@ static void _tdb_transaction_cancel(struct tdb_context *tdb) start a tdb transaction. No token is returned, as only a single transaction is allowed to be pending per tdb_context */ -int tdb_transaction_start(struct tdb_context *tdb) +enum TDB_ERROR tdb_transaction_start(struct tdb_context *tdb) { + enum TDB_ERROR ecode; + /* some sanity checks */ if (tdb->read_only || (tdb->flags & TDB_INTERNAL)) { - tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_DEBUG_ERROR, - "tdb_transaction_start: cannot start a transaction" - " on a read-only or internal db"); - return -1; + return tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR, + "tdb_transaction_start: cannot start a" + " transaction on a read-only or internal db"); } /* cope with nested tdb_transaction_start() calls */ if (tdb->transaction != NULL) { - tdb_logerr(tdb, TDB_ERR_NESTING, TDB_DEBUG_ERROR, - "tdb_transaction_start:" - " already inside transaction"); - return -1; + return tdb_logerr(tdb, TDB_ERR_IO, TDB_LOG_USE_ERROR, + "tdb_transaction_start:" + " already inside transaction"); } if (tdb_has_hash_locks(tdb)) { /* the caller must not have any locks when starting a transaction as otherwise we'll be screwed by lack - of nested locks in posix */ - tdb_logerr(tdb, TDB_ERR_LOCK, TDB_DEBUG_ERROR, - "tdb_transaction_start: cannot start a transaction" - " with locks held"); - return -1; + of nested locks in POSIX */ + return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR, + "tdb_transaction_start: cannot start a" + " transaction with locks held"); } tdb->transaction = (struct tdb_transaction *) calloc(sizeof(struct tdb_transaction), 1); if (tdb->transaction == NULL) { - tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_ERROR, - "tdb_transaction_start: cannot allocate"); - return -1; + return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR, + "tdb_transaction_start: cannot allocate"); } /* get the transaction write lock. This is a blocking lock. As discussed with Volker, there are a number of ways we could make this async, which we will probably do in the future */ - if (tdb_transaction_lock(tdb, F_WRLCK) == -1) { + ecode = tdb_transaction_lock(tdb, F_WRLCK); + if (ecode != TDB_SUCCESS) { SAFE_FREE(tdb->transaction->blocks); SAFE_FREE(tdb->transaction); - return -1; + return ecode; } /* get a read lock over entire file. This is upgraded to a write lock during the commit */ - if (tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, true) == -1) { + ecode = tdb_allrecord_lock(tdb, F_RDLCK, TDB_LOCK_WAIT, true); + if (ecode != TDB_SUCCESS) { goto fail_allrecord_lock; } @@ -556,13 +573,13 @@ int tdb_transaction_start(struct tdb_context *tdb) transaction specific methods */ tdb->transaction->io_methods = tdb->methods; tdb->methods = &transaction_methods; - return 0; + return TDB_SUCCESS; fail_allrecord_lock: tdb_transaction_unlock(tdb, F_WRLCK); SAFE_FREE(tdb->transaction->blocks); SAFE_FREE(tdb->transaction); - return -1; + return ecode; } @@ -605,30 +622,30 @@ static tdb_len_t tdb_recovery_size(struct tdb_context *tdb) allocate the recovery area, or use an existing recovery area if it is large enough */ -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 enum TDB_ERROR tdb_recovery_allocate(struct tdb_context *tdb, + tdb_len_t *recovery_size, + tdb_off_t *recovery_offset, + tdb_len_t *recovery_max_size) { struct tdb_recovery_record rec; const struct tdb_methods *methods = tdb->transaction->io_methods; tdb_off_t recovery_head; size_t addition; + enum TDB_ERROR ecode; recovery_head = tdb_read_off(tdb, offsetof(struct tdb_header,recovery)); - if (recovery_head == TDB_OFF_ERR) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_recovery_allocate:" - " failed to read recovery head"); - return -1; + if (TDB_OFF_IS_ERR(recovery_head)) { + return tdb_logerr(tdb, recovery_head, TDB_LOG_ERROR, + "tdb_recovery_allocate:" + " failed to read recovery head"); } if (recovery_head != 0) { - if (methods->read(tdb, recovery_head, &rec, sizeof(rec))) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_recovery_allocate:" - " failed to read recovery record"); - return -1; + ecode = methods->tread(tdb, recovery_head, &rec, sizeof(rec)); + if (ecode != TDB_SUCCESS) { + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_recovery_allocate:" + " failed to read recovery record"); } tdb_convert(tdb, &rec, sizeof(rec)); /* ignore invalid recovery regions: can happen in crash */ @@ -644,7 +661,7 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, /* it fits in the existing area */ *recovery_max_size = rec.max_len; *recovery_offset = recovery_head; - return 0; + return TDB_SUCCESS; } /* we need to free up the old recovery area, then allocate a @@ -654,12 +671,13 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, the transaction) */ if (recovery_head != 0) { add_stat(tdb, frees, 1); - if (add_free_record(tdb, recovery_head, - sizeof(rec) + rec.max_len) != 0) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_recovery_allocate:" - " failed to free previous recovery area"); - return -1; + ecode = add_free_record(tdb, recovery_head, + sizeof(rec) + rec.max_len); + if (ecode != TDB_SUCCESS) { + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_recovery_allocate:" + " failed to free previous" + " recovery area"); } } @@ -681,11 +699,11 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, addition = (tdb->map_size - tdb->transaction->old_map_size) + sizeof(rec) + *recovery_max_size; tdb->map_size = tdb->transaction->old_map_size; - if (methods->expand_file(tdb, addition) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_recovery_allocate:" - " failed to create recovery area"); - return -1; + ecode = methods->expand_file(tdb, addition); + if (ecode != TDB_SUCCESS) { + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_recovery_allocate:" + " failed to create recovery area"); } /* we have to reset the old map size so that we don't try to @@ -696,17 +714,17 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, /* write the recovery header offset and sync - we can sync without a race here as the magic ptr in the recovery record has not been set */ tdb_convert(tdb, &recovery_head, sizeof(recovery_head)); - if (methods->write(tdb, offsetof(struct tdb_header, recovery), - &recovery_head, sizeof(tdb_off_t)) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_recovery_allocate:" - " failed to write recovery head"); - return -1; + ecode = methods->twrite(tdb, offsetof(struct tdb_header, recovery), + &recovery_head, sizeof(tdb_off_t)); + if (ecode != TDB_SUCCESS) { + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_recovery_allocate:" + " failed to write recovery head"); } transaction_write_existing(tdb, offsetof(struct tdb_header, recovery), &recovery_head, sizeof(tdb_off_t)); - return 0; + return TDB_SUCCESS; } /* Set up header for the recovery record. */ @@ -724,31 +742,34 @@ static void set_recovery_header(struct tdb_recovery_record *rec, /* setup the recovery data that will be used on a crash during commit */ -static int transaction_setup_recovery(struct tdb_context *tdb, - tdb_off_t *magic_offset) +static enum TDB_ERROR transaction_setup_recovery(struct tdb_context *tdb, + tdb_off_t *magic_offset) { - tdb_len_t recovery_size; + /* Initialized for GCC's 4.4.5 overzealous uninitialized warnings. */ + tdb_len_t recovery_size = 0; + tdb_off_t recovery_offset = 0, recovery_max_size = 0; unsigned char *data, *p; const struct tdb_methods *methods = tdb->transaction->io_methods; struct tdb_recovery_record *rec; - tdb_off_t recovery_offset, recovery_max_size; tdb_off_t old_map_size = tdb->transaction->old_map_size; uint64_t magic, tailer; int i; + enum TDB_ERROR ecode; /* check that the recovery area has enough space */ - if (tdb_recovery_allocate(tdb, &recovery_size, - &recovery_offset, &recovery_max_size) == -1) { - return -1; + ecode = tdb_recovery_allocate(tdb, &recovery_size, + &recovery_offset, &recovery_max_size); + if (ecode != TDB_SUCCESS) { + return ecode; } data = (unsigned char *)malloc(recovery_size + sizeof(*rec)); if (data == NULL) { - tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL, - "transaction_setup_recovery: cannot allocate"); - return -1; + return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR, + "transaction_setup_recovery:" + " cannot allocate"); } rec = (struct tdb_recovery_record *)data; @@ -777,11 +798,11 @@ static int transaction_setup_recovery(struct tdb_context *tdb, continue; } if (offset + length > tdb->map_size) { - tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_FATAL, - "tdb_transaction_setup_recovery:" - " transaction data over new region boundary"); free(data); - return -1; + return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "tdb_transaction_setup_recovery:" + " transaction data over new region" + " boundary"); } memcpy(p, &offset, sizeof(offset)); memcpy(p + sizeof(offset), &length, sizeof(length)); @@ -790,11 +811,12 @@ static int transaction_setup_recovery(struct tdb_context *tdb, /* the recovery area contains the old data, not the new data, so we have to call the original tdb_read method to get it */ - if (methods->read(tdb, offset, - p + sizeof(offset) + sizeof(length), - length) != 0) { + ecode = methods->tread(tdb, offset, + p + sizeof(offset) + sizeof(length), + length); + if (ecode != TDB_SUCCESS) { free(data); - return -1; + return ecode; } p += sizeof(offset) + sizeof(length) + length; } @@ -805,13 +827,13 @@ static int transaction_setup_recovery(struct tdb_context *tdb, tdb_convert(tdb, p, sizeof(tailer)); /* write the recovery data to the recovery area */ - if (methods->write(tdb, recovery_offset, data, - sizeof(*rec) + recovery_size) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_transaction_setup_recovery:" - " failed to write recovery data"); + ecode = methods->twrite(tdb, recovery_offset, data, + sizeof(*rec) + recovery_size); + if (ecode != TDB_SUCCESS) { free(data); - return -1; + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_transaction_setup_recovery:" + " failed to write recovery data"); } transaction_write_existing(tdb, recovery_offset, data, sizeof(*rec) + recovery_size); @@ -819,10 +841,11 @@ static int transaction_setup_recovery(struct tdb_context *tdb, /* as we don't have ordered writes, we have to sync the recovery data before we update the magic to indicate that the recovery data is present */ - if (transaction_sync(tdb, recovery_offset, - sizeof(*rec) + recovery_size) == -1) { + ecode = transaction_sync(tdb, recovery_offset, + sizeof(*rec) + recovery_size); + if (ecode != TDB_SUCCESS) { free(data); - return -1; + return ecode; } free(data); @@ -833,89 +856,89 @@ static int transaction_setup_recovery(struct tdb_context *tdb, *magic_offset = recovery_offset + offsetof(struct tdb_recovery_record, magic); - if (methods->write(tdb, *magic_offset, &magic, sizeof(magic)) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_transaction_setup_recovery:" - " failed to write recovery magic"); - return -1; + ecode = methods->twrite(tdb, *magic_offset, &magic, sizeof(magic)); + if (ecode != TDB_SUCCESS) { + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_transaction_setup_recovery:" + " failed to write recovery magic"); } transaction_write_existing(tdb, *magic_offset, &magic, sizeof(magic)); /* ensure the recovery magic marker is on disk */ - if (transaction_sync(tdb, *magic_offset, sizeof(magic)) == -1) { - return -1; - } - - return 0; + return transaction_sync(tdb, *magic_offset, sizeof(magic)); } -static int _tdb_transaction_prepare_commit(struct tdb_context *tdb) +static enum TDB_ERROR _tdb_transaction_prepare_commit(struct tdb_context *tdb) { const struct tdb_methods *methods; + enum TDB_ERROR ecode; if (tdb->transaction == NULL) { - tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_DEBUG_ERROR, - "tdb_transaction_prepare_commit: no transaction"); - return -1; + return tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR, + "tdb_transaction_prepare_commit:" + " no transaction"); } if (tdb->transaction->prepared) { _tdb_transaction_cancel(tdb); - tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_DEBUG_ERROR, - "tdb_transaction_prepare_commit:" - " transaction already prepared"); - return -1; + return tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR, + "tdb_transaction_prepare_commit:" + " transaction already prepared"); } if (tdb->transaction->transaction_error) { _tdb_transaction_cancel(tdb); - tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_DEBUG_ERROR, - "tdb_transaction_prepare_commit:" - " transaction error pending"); - return -1; + return tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_ERROR, + "tdb_transaction_prepare_commit:" + " transaction error pending"); } if (tdb->transaction->nesting != 0) { tdb->transaction->nesting--; - return 0; + return TDB_SUCCESS; } /* check for a null transaction */ if (tdb->transaction->blocks == NULL) { - return 0; + return TDB_SUCCESS; } methods = tdb->transaction->io_methods; /* upgrade the main transaction lock region to a write lock */ - if (tdb_allrecord_upgrade(tdb) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_ERROR, + ecode = tdb_allrecord_upgrade(tdb); + if (ecode != TDB_SUCCESS) { + tdb_logerr(tdb, ecode, TDB_LOG_ERROR, "tdb_transaction_prepare_commit:" " failed to upgrade hash locks"); _tdb_transaction_cancel(tdb); - return -1; + return ecode; } /* get the open lock - this prevents new users attaching to the database during the commit */ - if (tdb_lock_open(tdb, TDB_LOCK_WAIT|TDB_LOCK_NOCHECK) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_ERROR, - "tdb_transaction_prepare_commit:" - " failed to get open lock"); + ecode = tdb_lock_open(tdb, TDB_LOCK_WAIT|TDB_LOCK_NOCHECK); + if (ecode != TDB_SUCCESS) { + tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_transaction_prepare_commit:" + " failed to get open lock"); _tdb_transaction_cancel(tdb); - return -1; + return ecode; } /* Since we have whole db locked, we don't need the expansion lock. */ if (!(tdb->flags & TDB_NOSYNC)) { /* write the recovery data to the end of the file */ - if (transaction_setup_recovery(tdb, &tdb->transaction->magic_offset) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, + ecode = transaction_setup_recovery(tdb, + &tdb->transaction + ->magic_offset); + if (ecode != TDB_SUCCESS) { + tdb_logerr(tdb, ecode, TDB_LOG_ERROR, "tdb_transaction_prepare_commit:" " failed to setup recovery data"); _tdb_transaction_cancel(tdb); - return -1; + return ecode; } } @@ -926,24 +949,24 @@ static int _tdb_transaction_prepare_commit(struct tdb_context *tdb) tdb_len_t add = tdb->map_size - tdb->transaction->old_map_size; /* Restore original map size for tdb_expand_file */ tdb->map_size = tdb->transaction->old_map_size; - if (methods->expand_file(tdb, add) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_ERROR, + ecode = methods->expand_file(tdb, add); + if (ecode != TDB_SUCCESS) { + tdb_logerr(tdb, ecode, TDB_LOG_ERROR, "tdb_transaction_prepare_commit:" " expansion failed"); _tdb_transaction_cancel(tdb); - return -1; + return ecode; } } /* Keep the open lock until the actual commit */ - - return 0; + return TDB_SUCCESS; } /* prepare to commit the current transaction */ -int tdb_transaction_prepare_commit(struct tdb_context *tdb) +enum TDB_ERROR tdb_transaction_prepare_commit(struct tdb_context *tdb) { return _tdb_transaction_prepare_commit(tdb); } @@ -951,43 +974,34 @@ int tdb_transaction_prepare_commit(struct tdb_context *tdb) /* commit the current transaction */ -int tdb_transaction_commit(struct tdb_context *tdb) +enum TDB_ERROR tdb_transaction_commit(struct tdb_context *tdb) { const struct tdb_methods *methods; int i; + enum TDB_ERROR ecode; if (tdb->transaction == NULL) { - tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_DEBUG_ERROR, - "tdb_transaction_commit: no transaction"); - return -1; + return tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR, + "tdb_transaction_commit: no transaction"); } tdb_trace(tdb, "tdb_transaction_commit"); - if (tdb->transaction->transaction_error) { - tdb_transaction_cancel(tdb); - tdb_logerr(tdb, TDB_ERR_IO, TDB_DEBUG_ERROR, - "tdb_transaction_commit:" - " transaction error pending"); - return -1; - } - - if (tdb->transaction->nesting != 0) { tdb->transaction->nesting--; - return 0; + return TDB_SUCCESS; } /* check for a null transaction */ if (tdb->transaction->blocks == NULL) { _tdb_transaction_cancel(tdb); - return 0; + return TDB_SUCCESS; } if (!tdb->transaction->prepared) { - int ret = _tdb_transaction_prepare_commit(tdb); - if (ret) - return ret; + ecode = _tdb_transaction_prepare_commit(tdb); + if (ecode != TDB_SUCCESS) + return ecode; } methods = tdb->transaction->io_methods; @@ -1007,9 +1021,10 @@ int tdb_transaction_commit(struct tdb_context *tdb) length = tdb->transaction->last_block_size; } - if (methods->write(tdb, offset, tdb->transaction->blocks[i], - length) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, + ecode = methods->twrite(tdb, offset, + tdb->transaction->blocks[i], length); + if (ecode != TDB_SUCCESS) { + tdb_logerr(tdb, ecode, TDB_LOG_ERROR, "tdb_transaction_commit:" " write failed during commit"); @@ -1021,7 +1036,7 @@ int tdb_transaction_commit(struct tdb_context *tdb) _tdb_transaction_cancel(tdb); - return -1; + return ecode; } SAFE_FREE(tdb->transaction->blocks[i]); } @@ -1030,8 +1045,9 @@ int tdb_transaction_commit(struct tdb_context *tdb) tdb->transaction->num_blocks = 0; /* ensure the new data is on disk */ - if (transaction_sync(tdb, 0, tdb->map_size) == -1) { - return -1; + ecode = transaction_sync(tdb, 0, tdb->map_size); + if (ecode != TDB_SUCCESS) { + return ecode; } /* @@ -1053,7 +1069,7 @@ int tdb_transaction_commit(struct tdb_context *tdb) transaction locks */ _tdb_transaction_cancel(tdb); - return 0; + return TDB_SUCCESS; } @@ -1062,63 +1078,61 @@ int tdb_transaction_commit(struct tdb_context *tdb) database write access already established (including the open lock to prevent new processes attaching) */ -int tdb_transaction_recover(struct tdb_context *tdb) +enum TDB_ERROR tdb_transaction_recover(struct tdb_context *tdb) { tdb_off_t recovery_head, recovery_eof; unsigned char *data, *p; struct tdb_recovery_record rec; + enum TDB_ERROR ecode; /* find the recovery area */ recovery_head = tdb_read_off(tdb, offsetof(struct tdb_header,recovery)); - if (recovery_head == TDB_OFF_ERR) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_transaction_recover:" - " failed to read recovery head"); - return -1; + if (TDB_OFF_IS_ERR(recovery_head)) { + return tdb_logerr(tdb, recovery_head, TDB_LOG_ERROR, + "tdb_transaction_recover:" + " failed to read recovery head"); } if (recovery_head == 0) { /* we have never allocated a recovery record */ - return 0; + return TDB_SUCCESS; } /* read the recovery record */ - if (tdb_read_convert(tdb, recovery_head, &rec, sizeof(rec)) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_transaction_recover:" - " failed to read recovery record"); - return -1; + ecode = tdb_read_convert(tdb, recovery_head, &rec, sizeof(rec)); + if (ecode != TDB_SUCCESS) { + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_transaction_recover:" + " failed to read recovery record"); } if (rec.magic != TDB_RECOVERY_MAGIC) { /* there is no valid recovery data */ - return 0; + return TDB_SUCCESS; } if (tdb->read_only) { - tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_DEBUG_FATAL, - "tdb_transaction_recover:" - " attempt to recover read only database"); - return -1; + return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, + "tdb_transaction_recover:" + " attempt to recover read only database"); } recovery_eof = rec.eof; data = (unsigned char *)malloc(rec.len); if (data == NULL) { - tdb_logerr(tdb, TDB_ERR_OOM, TDB_DEBUG_FATAL, - "tdb_transaction_recover:" - " failed to allocate recovery data"); - return -1; + return tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR, + "tdb_transaction_recover:" + " failed to allocate recovery data"); } /* read the full recovery data */ - if (tdb->methods->read(tdb, recovery_head + sizeof(rec), data, - rec.len) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_transaction_recover:" - " failed to read recovery data"); - return -1; + ecode = tdb->methods->tread(tdb, recovery_head + sizeof(rec), data, + rec.len); + if (ecode != TDB_SUCCESS) { + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_transaction_recover:" + " failed to read recovery data"); } /* recover the file data */ @@ -1131,71 +1145,75 @@ int tdb_transaction_recover(struct tdb_context *tdb) memcpy(&len, p + sizeof(ofs), sizeof(len)); p += sizeof(ofs) + sizeof(len); - if (tdb->methods->write(tdb, ofs, p, len) == -1) { + ecode = tdb->methods->twrite(tdb, ofs, p, len); + if (ecode != TDB_SUCCESS) { free(data); - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_transaction_recover:" - " failed to recover %zu bytes at offset %zu", - (size_t)len, (size_t)ofs); - return -1; + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_transaction_recover:" + " failed to recover %zu bytes" + " at offset %zu", + (size_t)len, (size_t)ofs); } p += len; } free(data); - if (transaction_sync(tdb, 0, tdb->map_size) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_transaction_recover: failed to sync recovery"); - return -1; + ecode = transaction_sync(tdb, 0, tdb->map_size); + if (ecode != TDB_SUCCESS) { + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_transaction_recover:" + " failed to sync recovery"); } /* if the recovery area is after the recovered eof then remove it */ if (recovery_eof <= recovery_head) { - if (tdb_write_off(tdb, offsetof(struct tdb_header,recovery), 0) - == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_transaction_recover:" - " failed to remove recovery head"); - return -1; + ecode = tdb_write_off(tdb, offsetof(struct tdb_header, + recovery), + 0); + if (ecode != TDB_SUCCESS) { + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_transaction_recover:" + " failed to remove recovery head"); } } /* remove the recovery magic */ - if (tdb_write_off(tdb, - recovery_head - + offsetof(struct tdb_recovery_record, magic), - TDB_RECOVERY_INVALID_MAGIC) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_transaction_recover:" - " failed to remove recovery magic"); - return -1; + ecode = tdb_write_off(tdb, + recovery_head + + offsetof(struct tdb_recovery_record, magic), + TDB_RECOVERY_INVALID_MAGIC); + if (ecode != TDB_SUCCESS) { + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_transaction_recover:" + " failed to remove recovery magic"); } - if (transaction_sync(tdb, 0, recovery_eof) == -1) { - tdb_logerr(tdb, tdb->ecode, TDB_DEBUG_FATAL, - "tdb_transaction_recover: failed to sync2 recovery"); - return -1; + ecode = transaction_sync(tdb, 0, recovery_eof); + if (ecode != TDB_SUCCESS) { + return tdb_logerr(tdb, ecode, TDB_LOG_ERROR, + "tdb_transaction_recover:" + " failed to sync2 recovery"); } - tdb_logerr(tdb, TDB_SUCCESS, TDB_DEBUG_TRACE, + tdb_logerr(tdb, TDB_SUCCESS, TDB_LOG_WARNING, "tdb_transaction_recover: recovered %zu byte database", (size_t)recovery_eof); /* all done */ - return 0; + return TDB_SUCCESS; } -/* Any I/O failures we say "needs recovery". */ -bool tdb_needs_recovery(struct tdb_context *tdb) +tdb_bool_err tdb_needs_recovery(struct tdb_context *tdb) { tdb_off_t recovery_head; struct tdb_recovery_record rec; + enum TDB_ERROR ecode; /* find the recovery area */ recovery_head = tdb_read_off(tdb, offsetof(struct tdb_header,recovery)); - if (recovery_head == TDB_OFF_ERR) { - return true; + if (TDB_OFF_IS_ERR(recovery_head)) { + return recovery_head; } if (recovery_head == 0) { @@ -1204,8 +1222,9 @@ bool tdb_needs_recovery(struct tdb_context *tdb) } /* read the recovery record */ - if (tdb_read_convert(tdb, recovery_head, &rec, sizeof(rec)) == -1) { - return true; + ecode = tdb_read_convert(tdb, recovery_head, &rec, sizeof(rec)); + if (ecode != TDB_SUCCESS) { + return ecode; } return (rec.magic == TDB_RECOVERY_MAGIC);