Commit b87e1449 authored by Rusty Russell's avatar Rusty Russell

tdb2: add TDB_RDONLY flag, allow setting/unsetting it.

You can only unset it if the TDB was originally opened O_RDWR.

Also, cleaned up error handling in tdb_allrecord_lock() so we only get
one log message on a r/o database.
parent 91436a25
...@@ -200,7 +200,7 @@ enum TDB_ERROR zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len) ...@@ -200,7 +200,7 @@ enum TDB_ERROR zero_out(struct tdb_context *tdb, tdb_off_t off, tdb_len_t len)
void *p = tdb->methods->direct(tdb, off, len, true); void *p = tdb->methods->direct(tdb, off, len, true);
enum TDB_ERROR ecode = TDB_SUCCESS; enum TDB_ERROR ecode = TDB_SUCCESS;
assert(!tdb->read_only); assert(!(tdb->flags & TDB_RDONLY));
if (TDB_PTR_IS_ERR(p)) { if (TDB_PTR_IS_ERR(p)) {
return TDB_PTR_ERR(p); return TDB_PTR_ERR(p);
} }
...@@ -248,7 +248,7 @@ static enum TDB_ERROR tdb_write(struct tdb_context *tdb, tdb_off_t off, ...@@ -248,7 +248,7 @@ static enum TDB_ERROR tdb_write(struct tdb_context *tdb, tdb_off_t off,
{ {
enum TDB_ERROR ecode; enum TDB_ERROR ecode;
if (tdb->read_only) { if (tdb->flags & TDB_RDONLY) {
return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR, return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
"Write to read-only database"); "Write to read-only database");
} }
...@@ -337,7 +337,7 @@ enum TDB_ERROR tdb_read_convert(struct tdb_context *tdb, tdb_off_t off, ...@@ -337,7 +337,7 @@ enum TDB_ERROR tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
enum TDB_ERROR tdb_write_off(struct tdb_context *tdb, enum TDB_ERROR tdb_write_off(struct tdb_context *tdb,
tdb_off_t off, tdb_off_t val) tdb_off_t off, tdb_off_t val)
{ {
if (tdb->read_only) { if (tdb->flags & TDB_RDONLY) {
return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR, return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
"Write to read-only database"); "Write to read-only database");
} }
...@@ -416,7 +416,7 @@ static enum TDB_ERROR tdb_expand_file(struct tdb_context *tdb, ...@@ -416,7 +416,7 @@ static enum TDB_ERROR tdb_expand_file(struct tdb_context *tdb,
char buf[8192]; char buf[8192];
enum TDB_ERROR ecode; enum TDB_ERROR ecode;
if (tdb->read_only) { if (tdb->flags & TDB_RDONLY) {
return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR, return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
"Expand on read-only database"); "Expand on read-only database");
} }
...@@ -488,7 +488,7 @@ void *tdb_access_write(struct tdb_context *tdb, ...@@ -488,7 +488,7 @@ void *tdb_access_write(struct tdb_context *tdb,
{ {
void *ret = NULL; void *ret = NULL;
if (tdb->read_only) { if (tdb->flags & TDB_RDONLY) {
tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR, tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
"Write to read-only database"); "Write to read-only database");
return TDB_ERR_PTR(TDB_ERR_RDONLY); return TDB_ERR_PTR(TDB_ERR_RDONLY);
......
...@@ -194,7 +194,7 @@ static enum TDB_ERROR tdb_brlock(struct tdb_context *tdb, ...@@ -194,7 +194,7 @@ static enum TDB_ERROR tdb_brlock(struct tdb_context *tdb,
return TDB_SUCCESS; return TDB_SUCCESS;
} }
if (rw_type == F_WRLCK && tdb->read_only) { if (rw_type == F_WRLCK && (tdb->flags & TDB_RDONLY)) {
return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR, return tdb_logerr(tdb, TDB_ERR_RDONLY, TDB_LOG_USE_ERROR,
"Write lock attempted on read-only database"); "Write lock attempted on read-only database");
} }
...@@ -508,8 +508,9 @@ static enum TDB_ERROR tdb_lock_gradual(struct tdb_context *tdb, ...@@ -508,8 +508,9 @@ static enum TDB_ERROR tdb_lock_gradual(struct tdb_context *tdb,
} }
/* First we try non-blocking. */ /* First we try non-blocking. */
if (tdb_brlock(tdb, ltype, off, len, nb_flags) == TDB_SUCCESS) { ecode = tdb_brlock(tdb, ltype, off, len, nb_flags);
return TDB_SUCCESS; if (ecode != TDB_ERR_LOCK) {
return ecode;
} }
/* Try locking first half, then second. */ /* Try locking first half, then second. */
......
...@@ -402,7 +402,8 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags, ...@@ -402,7 +402,8 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
} }
if (tdb_flags & ~(TDB_INTERNAL | TDB_NOLOCK | TDB_NOMMAP | TDB_CONVERT if (tdb_flags & ~(TDB_INTERNAL | TDB_NOLOCK | TDB_NOMMAP | TDB_CONVERT
| TDB_NOSYNC | TDB_SEQNUM | TDB_ALLOW_NESTING)) { | TDB_NOSYNC | TDB_SEQNUM | TDB_ALLOW_NESTING
| TDB_RDONLY)) {
ecode = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR, ecode = tdb_logerr(tdb, TDB_ERR_EINVAL, TDB_LOG_USE_ERROR,
"tdb_open: unknown flags %u", tdb_flags); "tdb_open: unknown flags %u", tdb_flags);
goto fail; goto fail;
...@@ -416,10 +417,16 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags, ...@@ -416,10 +417,16 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
} }
if ((open_flags & O_ACCMODE) == O_RDONLY) { if ((open_flags & O_ACCMODE) == O_RDONLY) {
tdb->read_only = true;
openlock = F_RDLCK; openlock = F_RDLCK;
tdb->flags |= TDB_RDONLY;
} else { } else {
tdb->read_only = false; if (tdb_flags & TDB_RDONLY) {
ecode = tdb_logerr(tdb, TDB_ERR_EINVAL,
TDB_LOG_USE_ERROR,
"tdb_open: can't use TDB_RDONLY"
" without O_RDONLY");
goto fail;
}
openlock = F_WRLCK; openlock = F_WRLCK;
} }
......
...@@ -331,9 +331,6 @@ struct tdb_context { ...@@ -331,9 +331,6 @@ struct tdb_context {
/* Are we accessing directly? (debugging check). */ /* Are we accessing directly? (debugging check). */
int direct_access; int direct_access;
/* Operating read-only? (Opened O_RDONLY, or in traverse_read) */
bool read_only;
/* Open flags passed to tdb_open. */ /* Open flags passed to tdb_open. */
int open_flags; int open_flags;
......
...@@ -324,6 +324,29 @@ unsigned int tdb_get_flags(struct tdb_context *tdb) ...@@ -324,6 +324,29 @@ unsigned int tdb_get_flags(struct tdb_context *tdb)
return tdb->flags; return tdb->flags;
} }
static bool readonly_changable(struct tdb_context *tdb, const char *caller)
{
if (tdb->transaction) {
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) void tdb_add_flag(struct tdb_context *tdb, unsigned flag)
{ {
if (tdb->flags & TDB_INTERNAL) { if (tdb->flags & TDB_INTERNAL) {
...@@ -349,6 +372,10 @@ void tdb_add_flag(struct tdb_context *tdb, unsigned flag) ...@@ -349,6 +372,10 @@ void tdb_add_flag(struct tdb_context *tdb, unsigned flag)
case TDB_ALLOW_NESTING: case TDB_ALLOW_NESTING:
tdb->flags |= TDB_ALLOW_NESTING; tdb->flags |= TDB_ALLOW_NESTING;
break; break;
case TDB_RDONLY:
if (readonly_changable(tdb, "tdb_add_flag"))
tdb->flags |= TDB_RDONLY;
break;
default: default:
tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL, tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
TDB_LOG_USE_ERROR, TDB_LOG_USE_ERROR,
...@@ -382,6 +409,18 @@ void tdb_remove_flag(struct tdb_context *tdb, unsigned flag) ...@@ -382,6 +409,18 @@ void tdb_remove_flag(struct tdb_context *tdb, unsigned flag)
case TDB_ALLOW_NESTING: case TDB_ALLOW_NESTING:
tdb->flags &= ~TDB_ALLOW_NESTING; tdb->flags &= ~TDB_ALLOW_NESTING;
break; 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: default:
tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL, tdb->last_error = tdb_logerr(tdb, TDB_ERR_EINVAL,
TDB_LOG_USE_ERROR, TDB_LOG_USE_ERROR,
......
...@@ -85,6 +85,7 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags, ...@@ -85,6 +85,7 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
#define TDB_NOSYNC 64 /* don't use synchronous transactions */ #define TDB_NOSYNC 64 /* don't use synchronous transactions */
#define TDB_SEQNUM 128 /* maintain a sequence number */ #define TDB_SEQNUM 128 /* maintain a sequence number */
#define TDB_ALLOW_NESTING 256 /* fake nested transactions */ #define TDB_ALLOW_NESTING 256 /* fake nested transactions */
#define TDB_RDONLY 512 /* implied by O_RDONLY */
/** /**
* tdb_close - close and free a tdb. * tdb_close - close and free a tdb.
......
#include <ccan/tdb2/tdb.c>
#include <ccan/tdb2/open.c>
#include <ccan/tdb2/free.c>
#include <ccan/tdb2/lock.c>
#include <ccan/tdb2/io.c>
#include <ccan/tdb2/hash.c>
#include <ccan/tdb2/check.c>
#include <ccan/tdb2/transaction.c>
#include <ccan/tdb2/traverse.c>
#include <ccan/tap/tap.h>
#include "logging.h"
int main(int argc, char *argv[])
{
unsigned int i;
struct tdb_context *tdb;
struct tdb_data key = tdb_mkdata("key", 3);
struct tdb_data data = tdb_mkdata("data", 4);
int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
plan_tests(sizeof(flags) / sizeof(flags[0]) * 48);
for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
/* RW -> R0 */
tdb = tdb_open("run-92-get-set-readonly.tdb", flags[i],
O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
ok1(tdb);
ok1(!(tdb_get_flags(tdb) & TDB_RDONLY));
ok1(tdb_store(tdb, key, data, TDB_INSERT) == TDB_SUCCESS);
tdb_add_flag(tdb, TDB_RDONLY);
ok1(tdb_get_flags(tdb) & TDB_RDONLY);
/* Can't store, append, delete. */
ok1(tdb_store(tdb, key, data, TDB_MODIFY) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 1);
ok1(tdb_append(tdb, key, data) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 2);
ok1(tdb_delete(tdb, key) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 3);
/* Can't start a transaction, or any write lock. */
ok1(tdb_transaction_start(tdb) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 4);
ok1(tdb_chainlock(tdb, key) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 5);
ok1(tdb_lockall(tdb) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 6);
ok1(tdb_wipe_all(tdb) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 7);
/* Back to RW. */
tdb_remove_flag(tdb, TDB_RDONLY);
ok1(!(tdb_get_flags(tdb) & TDB_RDONLY));
ok1(tdb_store(tdb, key, data, TDB_MODIFY) == TDB_SUCCESS);
ok1(tdb_append(tdb, key, data) == TDB_SUCCESS);
ok1(tdb_delete(tdb, key) == TDB_SUCCESS);
ok1(tdb_transaction_start(tdb) == TDB_SUCCESS);
ok1(tdb_store(tdb, key, data, TDB_INSERT) == TDB_SUCCESS);
ok1(tdb_transaction_commit(tdb) == TDB_SUCCESS);
ok1(tdb_chainlock(tdb, key) == TDB_SUCCESS);
tdb_chainunlock(tdb, key);
ok1(tdb_lockall(tdb) == TDB_SUCCESS);
tdb_unlockall(tdb);
ok1(tdb_wipe_all(tdb) == TDB_SUCCESS);
ok1(tap_log_messages == 7);
tdb_close(tdb);
/* R0 -> RW */
tdb = tdb_open("run-92-get-set-readonly.tdb", flags[i],
O_RDONLY, 0600, &tap_log_attr);
ok1(tdb);
ok1(tdb_get_flags(tdb) & TDB_RDONLY);
/* Can't store, append, delete. */
ok1(tdb_store(tdb, key, data, TDB_INSERT) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 8);
ok1(tdb_append(tdb, key, data) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 9);
ok1(tdb_delete(tdb, key) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 10);
/* Can't start a transaction, or any write lock. */
ok1(tdb_transaction_start(tdb) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 11);
ok1(tdb_chainlock(tdb, key) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 12);
ok1(tdb_lockall(tdb) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 13);
ok1(tdb_wipe_all(tdb) == TDB_ERR_RDONLY);
ok1(tap_log_messages == 14);
/* Can't remove TDB_RDONLY since we opened with O_RDONLY */
tdb_remove_flag(tdb, TDB_RDONLY);
ok1(tap_log_messages == 15);
ok1(tdb_get_flags(tdb) & TDB_RDONLY);
tdb_close(tdb);
ok1(tap_log_messages == 15);
tap_log_messages = 0;
}
return exit_status();
}
...@@ -532,7 +532,7 @@ enum TDB_ERROR tdb_transaction_start(struct tdb_context *tdb) ...@@ -532,7 +532,7 @@ enum TDB_ERROR tdb_transaction_start(struct tdb_context *tdb)
" internal tdb"); " internal tdb");
} }
if (tdb->read_only) { if (tdb->flags & TDB_RDONLY) {
return tdb->last_error = tdb_logerr(tdb, TDB_ERR_RDONLY, return tdb->last_error = tdb_logerr(tdb, TDB_ERR_RDONLY,
TDB_LOG_USE_ERROR, TDB_LOG_USE_ERROR,
"tdb_transaction_start:" "tdb_transaction_start:"
...@@ -1196,7 +1196,7 @@ enum TDB_ERROR tdb_transaction_recover(struct tdb_context *tdb) ...@@ -1196,7 +1196,7 @@ enum TDB_ERROR tdb_transaction_recover(struct tdb_context *tdb)
return TDB_SUCCESS; return TDB_SUCCESS;
} }
if (tdb->read_only) { if (tdb->flags & TDB_RDONLY) {
return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR, return tdb_logerr(tdb, TDB_ERR_CORRUPT, TDB_LOG_ERROR,
"tdb_transaction_recover:" "tdb_transaction_recover:"
" attempt to recover read only database"); " attempt to recover read only database");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment