X-Git-Url: http://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ftdb2%2Ftdb.c;h=53e1de624d70d7fe188f1ce9c5a23453aa84fb6f;hp=d7b5163be95eba66ac204f1be75dc239350c41f9;hb=e487983a4099b6f760056ff7182f2ff543e6da71;hpb=5c4a21ab03a428373e7659b9606facf85dcbe17b diff --git a/ccan/tdb2/tdb.c b/ccan/tdb2/tdb.c index d7b5163b..53e1de62 100644 --- a/ccan/tdb2/tdb.c +++ b/ccan/tdb2/tdb.c @@ -1,3 +1,20 @@ + /* + Trivial Database 2: fetch, store and misc routines. + Copyright (C) Rusty Russell 2010 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, see . +*/ #include "private.h" #include #include @@ -42,7 +59,7 @@ static enum TDB_ERROR replace_data(struct tdb_context *tdb, ecode = add_free_record(tdb, old_off, sizeof(struct tdb_used_record) + key.dsize + old_room, - TDB_LOCK_WAIT); + TDB_LOCK_WAIT, true); if (ecode == TDB_SUCCESS) ecode = replace_in_hash(tdb, h, new_off); } else { @@ -53,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; } @@ -77,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); @@ -97,6 +114,12 @@ 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; @@ -160,6 +183,12 @@ 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; @@ -194,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; @@ -225,6 +254,9 @@ 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; @@ -252,6 +284,10 @@ 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; @@ -270,6 +306,12 @@ 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; @@ -292,7 +334,7 @@ enum TDB_ERROR tdb_delete(struct tdb_context *tdb, struct tdb_data key) + rec_key_length(&rec) + rec_data_length(&rec) + rec_extra_padding(&rec), - TDB_LOCK_WAIT); + TDB_LOCK_WAIT, true); if (tdb->flags & TDB_SEQNUM) tdb_inc_seqnum(tdb); @@ -307,6 +349,37 @@ 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; + } + + if (tdb->file->allrecord_lock.count != 0 + || tdb->file->num_lockrecs != 0) { + tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL, + TDB_LOG_USE_ERROR, + "%s: can't change" + " TDB_RDONLY holding locks", + caller); + return false; + } + return true; +} + void tdb_add_flag(struct tdb_context *tdb, unsigned flag) { if (tdb->flags & TDB_INTERNAL) { @@ -332,6 +405,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, @@ -365,6 +442,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, @@ -414,11 +503,11 @@ enum TDB_ERROR COLD tdb_logerr(struct tdb_context *tdb, va_end(ap); if (len < 0) { - tdb->log_fn(tdb, TDB_LOG_ERROR, + tdb->log_fn(tdb, TDB_LOG_ERROR, TDB_ERR_OOM, "out of memory formatting message:", tdb->log_data); - tdb->log_fn(tdb, level, fmt, tdb->log_data); + tdb->log_fn(tdb, level, ecode, fmt, tdb->log_data); } else { - tdb->log_fn(tdb, level, message, tdb->log_data); + tdb->log_fn(tdb, level, ecode, message, tdb->log_data); free(message); } errno = saved_errno; @@ -437,6 +526,11 @@ 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; @@ -469,7 +563,20 @@ 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->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; else @@ -482,3 +589,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; +}