X-Git-Url: https://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ftdb2%2Ftdb.c;h=62607bf1e5d8eb2f4c9bf9002aa9b30e299a9af9;hp=7484695d7ab9a8724eb178e82bf3e1a9e52a58e2;hb=24e5ddb143fb5e79112649472258f5da67cc7362;hpb=6e3d9e8a66bf8b45150500f176452ee8e9ee9927 diff --git a/ccan/tdb2/tdb.c b/ccan/tdb2/tdb.c index 7484695d..62607bf1 100644 --- a/ccan/tdb2/tdb.c +++ b/ccan/tdb2/tdb.c @@ -50,7 +50,7 @@ static enum TDB_ERROR replace_data(struct tdb_context *tdb, new_off = alloc(tdb, key.dsize, dbuf.dsize, h->h, TDB_USED_MAGIC, growing); if (TDB_OFF_IS_ERR(new_off)) { - return new_off; + return TDB_OFF_TO_ERR(new_off); } /* We didn't like the existing one: remove it. */ @@ -70,13 +70,13 @@ static enum TDB_ERROR replace_data(struct tdb_context *tdb, } new_off += sizeof(struct tdb_used_record); - ecode = tdb->methods->twrite(tdb, new_off, key.dptr, key.dsize); + ecode = tdb->tdb2.io->twrite(tdb, new_off, key.dptr, key.dsize); if (ecode != TDB_SUCCESS) { return ecode; } new_off += key.dsize; - ecode = tdb->methods->twrite(tdb, new_off, dbuf.dptr, dbuf.dsize); + ecode = tdb->tdb2.io->twrite(tdb, new_off, dbuf.dptr, dbuf.dsize); if (ecode != TDB_SUCCESS) { return ecode; } @@ -94,10 +94,10 @@ static enum TDB_ERROR update_data(struct tdb_context *tdb, { enum TDB_ERROR ecode; - ecode = tdb->methods->twrite(tdb, off, dbuf.dptr, dbuf.dsize); + ecode = tdb->tdb2.io->twrite(tdb, off, dbuf.dptr, dbuf.dsize); if (ecode == TDB_SUCCESS && extra) { /* Put a zero in; future versions may append other data. */ - ecode = tdb->methods->twrite(tdb, off + dbuf.dsize, "", 1); + ecode = tdb->tdb2.io->twrite(tdb, off + dbuf.dsize, "", 1); } if (tdb->flags & TDB_SEQNUM) tdb_inc_seqnum(tdb); @@ -114,9 +114,15 @@ enum TDB_ERROR tdb_store(struct tdb_context *tdb, struct tdb_used_record rec; enum TDB_ERROR ecode; + if (tdb->flags & TDB_VERSION1) { + if (tdb1_store(tdb, key, dbuf, flag) == -1) + return tdb->last_error; + return TDB_SUCCESS; + } + off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL); if (TDB_OFF_IS_ERR(off)) { - return tdb->last_error = off; + return tdb->last_error = TDB_OFF_TO_ERR(off); } /* Now we have lock on this hash bucket. */ @@ -177,9 +183,15 @@ enum TDB_ERROR tdb_append(struct tdb_context *tdb, struct tdb_data new_dbuf; enum TDB_ERROR ecode; + if (tdb->flags & TDB_VERSION1) { + if (tdb1_append(tdb, key, dbuf) == -1) + return tdb->last_error; + return TDB_SUCCESS; + } + off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL); if (TDB_OFF_IS_ERR(off)) { - return tdb->last_error = off; + return tdb->last_error = TDB_OFF_TO_ERR(off); } if (off) { @@ -211,7 +223,7 @@ enum TDB_ERROR tdb_append(struct tdb_context *tdb, + dbuf.dsize)); goto out; } - ecode = tdb->methods->tread(tdb, off + sizeof(rec) + key.dsize, + ecode = tdb->tdb2.io->tread(tdb, off + sizeof(rec) + key.dsize, newdata, old_dlen); if (ecode != TDB_SUCCESS) { goto out_free_newdata; @@ -242,9 +254,12 @@ enum TDB_ERROR tdb_fetch(struct tdb_context *tdb, struct tdb_data key, struct hash_info h; enum TDB_ERROR ecode; + if (tdb->flags & TDB_VERSION1) + return tdb1_fetch(tdb, key, data); + off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL); if (TDB_OFF_IS_ERR(off)) { - return tdb->last_error = off; + return tdb->last_error = TDB_OFF_TO_ERR(off); } if (!off) { @@ -269,9 +284,13 @@ bool tdb_exists(struct tdb_context *tdb, TDB_DATA key) struct tdb_used_record rec; struct hash_info h; + if (tdb->flags & TDB_VERSION1) { + return tdb1_exists(tdb, key); + } + off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL); if (TDB_OFF_IS_ERR(off)) { - tdb->last_error = off; + tdb->last_error = TDB_OFF_TO_ERR(off); return false; } tdb_unlock_hashes(tdb, h.hlock_start, h.hlock_range, F_RDLCK); @@ -287,9 +306,15 @@ enum TDB_ERROR tdb_delete(struct tdb_context *tdb, struct tdb_data key) struct hash_info h; enum TDB_ERROR ecode; + if (tdb->flags & TDB_VERSION1) { + if (tdb1_delete(tdb, key) == -1) + return tdb->last_error; + return TDB_SUCCESS; + } + off = find_and_lock(tdb, key, F_WRLCK, &h, &rec, NULL); if (TDB_OFF_IS_ERR(off)) { - return tdb->last_error = off; + return tdb->last_error = TDB_OFF_TO_ERR(off); } if (!off) { @@ -324,6 +349,27 @@ unsigned int tdb_get_flags(struct tdb_context *tdb) return tdb->flags; } +static bool inside_transaction(const struct tdb_context *tdb) +{ + if (tdb->flags & TDB_VERSION1) + return tdb->tdb1.transaction != NULL; + else + return tdb->tdb2.transaction != NULL; +} + +static bool readonly_changable(struct tdb_context *tdb, const char *caller) +{ + if (inside_transaction(tdb)) { + tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL, + TDB_LOG_USE_ERROR, + "%s: can't change" + " TDB_RDONLY inside transaction", + caller); + return false; + } + return true; +} + void tdb_add_flag(struct tdb_context *tdb, unsigned flag) { if (tdb->flags & TDB_INTERNAL) { @@ -349,6 +395,10 @@ void tdb_add_flag(struct tdb_context *tdb, unsigned flag) case TDB_ALLOW_NESTING: tdb->flags |= TDB_ALLOW_NESTING; break; + case TDB_RDONLY: + if (readonly_changable(tdb, "tdb_add_flag")) + tdb->flags |= TDB_RDONLY; + break; default: tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR, @@ -382,6 +432,18 @@ void tdb_remove_flag(struct tdb_context *tdb, unsigned flag) case TDB_ALLOW_NESTING: tdb->flags &= ~TDB_ALLOW_NESTING; break; + case TDB_RDONLY: + if ((tdb->open_flags & O_ACCMODE) == O_RDONLY) { + tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL, + TDB_LOG_USE_ERROR, + "tdb_remove_flag: can't" + " remove TDB_RDONLY on tdb" + " opened with O_RDONLY"); + break; + } + if (readonly_changable(tdb, "tdb_remove_flag")) + tdb->flags &= ~TDB_RDONLY; + break; default: tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR, @@ -393,16 +455,16 @@ void tdb_remove_flag(struct tdb_context *tdb, unsigned flag) const char *tdb_errorstr(enum TDB_ERROR ecode) { /* Gcc warns if you miss a case in the switch, so use that. */ - switch (ecode) { - case TDB_SUCCESS: return "Success"; - case TDB_ERR_CORRUPT: return "Corrupt database"; - case TDB_ERR_IO: return "IO Error"; - case TDB_ERR_LOCK: return "Locking error"; - case TDB_ERR_OOM: return "Out of memory"; - case TDB_ERR_EXISTS: return "Record exists"; - case TDB_ERR_EINVAL: return "Invalid parameter"; - case TDB_ERR_NOEXIST: return "Record does not exist"; - case TDB_ERR_RDONLY: return "write not permitted"; + switch (TDB_ERR_TO_OFF(ecode)) { + case TDB_ERR_TO_OFF(TDB_SUCCESS): return "Success"; + case TDB_ERR_TO_OFF(TDB_ERR_CORRUPT): return "Corrupt database"; + case TDB_ERR_TO_OFF(TDB_ERR_IO): return "IO Error"; + case TDB_ERR_TO_OFF(TDB_ERR_LOCK): return "Locking error"; + case TDB_ERR_TO_OFF(TDB_ERR_OOM): return "Out of memory"; + case TDB_ERR_TO_OFF(TDB_ERR_EXISTS): return "Record exists"; + case TDB_ERR_TO_OFF(TDB_ERR_EINVAL): return "Invalid parameter"; + case TDB_ERR_TO_OFF(TDB_ERR_NOEXIST): return "Record does not exist"; + case TDB_ERR_TO_OFF(TDB_ERR_RDONLY): return "write not permitted"; } return "Invalid error code"; } @@ -454,9 +516,14 @@ enum TDB_ERROR tdb_parse_record_(struct tdb_context *tdb, struct hash_info h; enum TDB_ERROR ecode; + if (tdb->flags & TDB_VERSION1) { + return tdb->last_error = tdb1_parse_record(tdb, key, parse, + data); + } + off = find_and_lock(tdb, key, F_RDLCK, &h, &rec, NULL); if (TDB_OFF_IS_ERR(off)) { - return tdb->last_error = off; + return tdb->last_error = TDB_OFF_TO_ERR(off); } if (!off) { @@ -486,9 +553,22 @@ const char *tdb_name(const struct tdb_context *tdb) int64_t tdb_get_seqnum(struct tdb_context *tdb) { - tdb_off_t off = tdb_read_off(tdb, offsetof(struct tdb_header, seqnum)); + tdb_off_t off; + + if (tdb->flags & TDB_VERSION1) { + tdb1_off_t val; + tdb->last_error = TDB_SUCCESS; + val = tdb1_get_seqnum(tdb); + + if (tdb->last_error != TDB_SUCCESS) + return TDB_ERR_TO_OFF(tdb->last_error); + else + return val; + } + + off = tdb_read_off(tdb, offsetof(struct tdb_header, seqnum)); if (TDB_OFF_IS_ERR(off)) - tdb->last_error = off; + tdb->last_error = TDB_OFF_TO_ERR(off); else tdb->last_error = TDB_SUCCESS; return off; @@ -499,3 +579,64 @@ int tdb_fd(const struct tdb_context *tdb) { return tdb->file->fd; } + +struct traverse_state { + enum TDB_ERROR error; + struct tdb_context *dest_db; +}; + +/* + traverse function for repacking + */ +static int repack_traverse(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, + struct traverse_state *state) +{ + state->error = tdb_store(state->dest_db, key, data, TDB_INSERT); + if (state->error != TDB_SUCCESS) { + return -1; + } + return 0; +} + +enum TDB_ERROR tdb_repack(struct tdb_context *tdb) +{ + struct tdb_context *tmp_db; + struct traverse_state state; + + state.error = tdb_transaction_start(tdb); + if (state.error != TDB_SUCCESS) { + return state.error; + } + + tmp_db = tdb_open("tmpdb", TDB_INTERNAL, O_RDWR|O_CREAT, 0, NULL); + if (tmp_db == NULL) { + state.error = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR, + __location__ + " Failed to create tmp_db"); + tdb_transaction_cancel(tdb); + return tdb->last_error = state.error; + } + + state.dest_db = tmp_db; + if (tdb_traverse(tdb, repack_traverse, &state) < 0) { + goto fail; + } + + state.error = tdb_wipe_all(tdb); + if (state.error != TDB_SUCCESS) { + goto fail; + } + + state.dest_db = tdb; + if (tdb_traverse(tmp_db, repack_traverse, &state) < 0) { + goto fail; + } + + tdb_close(tmp_db); + return tdb_transaction_commit(tdb); + +fail: + tdb_transaction_cancel(tdb); + tdb_close(tmp_db); + return state.error; +}