X-Git-Url: https://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ftdb%2Ftdb.c;h=d43418033e7fcc32ea5648b4ac5c2672568e26b8;hp=50ff24a6e6ca4a44ca84a9b97c7ae3b37d440474;hb=85a33135890965218010b79c89e4d3f4905727e3;hpb=41391dd9a73e1aa8bb8193cf6335542c703f20e9 diff --git a/ccan/tdb/tdb.c b/ccan/tdb/tdb.c index 50ff24a6..d4341803 100644 --- a/ccan/tdb/tdb.c +++ b/ccan/tdb/tdb.c @@ -41,8 +41,6 @@ void tdb_increment_seqnum_nonblock(struct tdb_context *tdb) return; } - tdb_trace(tdb, "tdb_increment_seqnum_nonblock"); - /* we ignore errors from this, as we have no sane way of dealing with them. */ @@ -61,13 +59,14 @@ static void tdb_increment_seqnum(struct tdb_context *tdb) return; } - if (tdb_brlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, F_SETLKW, 1, 1) != 0) { + if (tdb_brlock(tdb, F_WRLCK, TDB_SEQNUM_OFS, 1, + TDB_LOCK_WAIT|TDB_LOCK_PROBE) != 0) { return; } tdb_increment_seqnum_nonblock(tdb); - tdb_brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1, 1); + tdb_brunlock(tdb, F_WRLCK, TDB_SEQNUM_OFS, 1); } static int tdb_key_compare(TDB_DATA key, TDB_DATA data, void *private_data) @@ -98,9 +97,16 @@ static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, NULL) == 0) { return rec_ptr; } + /* detect tight infinite loop */ + if (rec_ptr == r->next) { + tdb->ecode = TDB_ERR_CORRUPT; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_find: loop detected.\n")); + return 0; + } rec_ptr = r->next; } - return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); + tdb->ecode = TDB_ERR_NOEXIST; + return 0; } /* As tdb_find, but if you succeed, keep the lock */ @@ -155,7 +161,7 @@ static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, * then the TDB_DATA will have zero length but * a non-zero pointer */ -static TDB_DATA do_tdb_fetch(struct tdb_context *tdb, TDB_DATA key) +static TDB_DATA _tdb_fetch(struct tdb_context *tdb, TDB_DATA key) { tdb_off_t rec_ptr; struct list_struct rec; @@ -176,7 +182,7 @@ static TDB_DATA do_tdb_fetch(struct tdb_context *tdb, TDB_DATA key) TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key) { - TDB_DATA ret = do_tdb_fetch(tdb, key); + TDB_DATA ret = _tdb_fetch(tdb, key); tdb_trace_1rec_retrec(tdb, "tdb_fetch", key, ret); return ret; @@ -214,7 +220,8 @@ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) { tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, -TDB_ERR_NOEXIST); - return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); + tdb->ecode = TDB_ERR_NOEXIST; + return 0; } tdb_trace_1rec_ret(tdb, "tdb_parse_record", key, 0); @@ -440,8 +447,8 @@ static tdb_off_t tdb_find_dead(struct tdb_context *tdb, uint32_t hash, return 0; } -static int _tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, - int flag, uint32_t hash) +static int _tdb_store(struct tdb_context *tdb, TDB_DATA key, + TDB_DATA dbuf, int flag, uint32_t hash) { struct list_struct rec; tdb_off_t rec_ptr; @@ -607,7 +614,7 @@ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf) if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) return -1; - dbuf = do_tdb_fetch(tdb, key); + dbuf = _tdb_fetch(tdb, key); if (dbuf.dptr == NULL) { dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize); @@ -843,6 +850,98 @@ failed: return -1; } + +struct traverse_state { + bool error; + struct tdb_context *dest_db; +}; + +/* + traverse function for repacking + */ +static int repack_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *private) +{ + struct traverse_state *state = (struct traverse_state *)private; + if (tdb_store(state->dest_db, key, data, TDB_INSERT) != 0) { + state->error = true; + return -1; + } + return 0; +} + +/* + repack a tdb + */ +int tdb_repack(struct tdb_context *tdb) +{ + struct tdb_context *tmp_db; + struct traverse_state state; + + tdb_trace(tdb, "tdb_repack"); + + if (tdb_transaction_start(tdb) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to start transaction\n")); + return -1; + } + + tmp_db = tdb_open("tmpdb", tdb_hash_size(tdb), TDB_INTERNAL, O_RDWR|O_CREAT, 0); + if (tmp_db == NULL) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to create tmp_db\n")); + tdb_transaction_cancel(tdb); + return -1; + } + + state.error = false; + state.dest_db = tmp_db; + + if (tdb_traverse_read(tdb, repack_traverse, &state) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to traverse copying out\n")); + tdb_transaction_cancel(tdb); + tdb_close(tmp_db); + return -1; + } + + if (state.error) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Error during traversal\n")); + tdb_transaction_cancel(tdb); + tdb_close(tmp_db); + return -1; + } + + if (tdb_wipe_all(tdb) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to wipe database\n")); + tdb_transaction_cancel(tdb); + tdb_close(tmp_db); + return -1; + } + + state.error = false; + state.dest_db = tdb; + + if (tdb_traverse_read(tmp_db, repack_traverse, &state) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to traverse copying back\n")); + tdb_transaction_cancel(tdb); + tdb_close(tmp_db); + return -1; + } + + if (state.error) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Error during second traversal\n")); + tdb_transaction_cancel(tdb); + tdb_close(tmp_db); + return -1; + } + + tdb_close(tmp_db); + + if (tdb_transaction_commit(tdb) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_repack: Failed to commit\n")); + return -1; + } + + return 0; +} + #ifdef TDB_TRACE static void tdb_trace_write(struct tdb_context *tdb, const char *str) { @@ -876,7 +975,7 @@ static void tdb_trace_end_ret(struct tdb_context *tdb, int ret) static void tdb_trace_record(struct tdb_context *tdb, TDB_DATA rec) { - char msg[20]; + char msg[20 + rec.dsize*2], *p; unsigned int i; /* We differentiate zero-length records from non-existent ones. */ @@ -884,12 +983,13 @@ static void tdb_trace_record(struct tdb_context *tdb, TDB_DATA rec) tdb_trace_write(tdb, " NULL"); return; } - sprintf(msg, " %zu:", rec.dsize); + + p = msg; + p += sprintf(p, " %zu:", rec.dsize); + for (i = 0; i < rec.dsize; i++) + p += sprintf(p, "%02x", rec.dptr[i]); + tdb_trace_write(tdb, msg); - for (i = 0; i < rec.dsize; i++) { - sprintf(msg, "%02x", rec.dptr[i]); - tdb_trace_write(tdb, msg); - } } void tdb_trace(struct tdb_context *tdb, const char *op) @@ -899,6 +999,16 @@ void tdb_trace(struct tdb_context *tdb, const char *op) tdb_trace_end(tdb); } +void tdb_trace_seqnum(struct tdb_context *tdb, uint32_t seqnum, const char *op) +{ + char msg[sizeof(tdb_off_t) * 4]; + + sprintf(msg, "%u ", seqnum); + tdb_trace_write(tdb, msg); + tdb_trace_write(tdb, op); + tdb_trace_end(tdb); +} + void tdb_trace_open(struct tdb_context *tdb, const char *op, unsigned hash_size, unsigned tdb_flags, unsigned open_flags) {