summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRusty Russell <rusty@rustcorp.com.au>2011-09-14 08:12:13 +0930
committerRusty Russell <rusty@rustcorp.com.au>2011-09-14 08:12:13 +0930
commitd26908846a84bcff7860ebdb384bbff030206675 (patch)
tree6589b8a22fa0fab916600683a36fccb06e7c3282
parent85fe9cffe529fd6a8c76ca5f0e3c68c090ce25bf (diff)
downloadsamba-d26908846a84bcff7860ebdb384bbff030206675.tar.gz
samba-d26908846a84bcff7860ebdb384bbff030206675.tar.bz2
samba-d26908846a84bcff7860ebdb384bbff030206675.zip
tdb2: check lock owner in tdb1 backend.
This reports errors if we fork() while holding a lock, or misuse a tdb which we have dual-opened. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (Imported from CCAN commit bef6f1b02e95370ecb2cb44be87c82afc9cb74b2)
-rw-r--r--lib/tdb2/lock.c14
-rw-r--r--lib/tdb2/private.h6
-rw-r--r--lib/tdb2/tdb1_lock.c95
3 files changed, 88 insertions, 27 deletions
diff --git a/lib/tdb2/lock.c b/lib/tdb2/lock.c
index 7d4311c8e5..bf62d9719e 100644
--- a/lib/tdb2/lock.c
+++ b/lib/tdb2/lock.c
@@ -30,7 +30,7 @@
#include <ccan/build_assert/build_assert.h>
/* If we were threaded, we could wait for unlock, but we're not, so fail. */
-static enum TDB_ERROR owner_conflict(struct tdb_context *tdb, const char *call)
+enum TDB_ERROR owner_conflict(struct tdb_context *tdb, const char *call)
{
return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR,
"%s: lock owned by another tdb in this process.",
@@ -38,8 +38,7 @@ static enum TDB_ERROR owner_conflict(struct tdb_context *tdb, const char *call)
}
/* If we fork, we no longer really own locks. */
-static bool check_lock_pid(struct tdb_context *tdb,
- const char *call, bool log)
+bool check_lock_pid(struct tdb_context *tdb, const char *call, bool log)
{
/* No locks? No problem! */
if (tdb->file->allrecord_lock.count == 0
@@ -787,6 +786,11 @@ enum TDB_ERROR tdb_unlock_hashes(struct tdb_context *tdb,
return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
"tdb_unlock_hashes RO allrecord!");
}
+ if (tdb->file->allrecord_lock.owner != tdb) {
+ return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_USE_ERROR,
+ "tdb_unlock_hashes:"
+ " not locked by us!");
+ }
return TDB_SUCCESS;
}
@@ -817,6 +821,10 @@ enum TDB_ERROR tdb_lock_free_bucket(struct tdb_context *tdb, tdb_off_t b_off,
if (!check_lock_pid(tdb, "tdb_lock_free_bucket", true))
return TDB_ERR_LOCK;
+ if (tdb->file->allrecord_lock.owner != tdb) {
+ return owner_conflict(tdb, "tdb_lock_free_bucket");
+ }
+
if (tdb->file->allrecord_lock.ltype == F_WRLCK)
return 0;
return tdb_logerr(tdb, TDB_ERR_LOCK, TDB_LOG_ERROR,
diff --git a/lib/tdb2/private.h b/lib/tdb2/private.h
index 6e28692c34..715bd1def7 100644
--- a/lib/tdb2/private.h
+++ b/lib/tdb2/private.h
@@ -475,6 +475,12 @@ enum TDB_ERROR tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
void tdb_inc_seqnum(struct tdb_context *tdb);
/* lock.c: */
+/* Print message because another tdb owns a lock we want. */
+enum TDB_ERROR owner_conflict(struct tdb_context *tdb, const char *call);
+
+/* If we fork, we no longer really own locks. */
+bool check_lock_pid(struct tdb_context *tdb, const char *call, bool log);
+
/* Lock/unlock a range of hashes. */
enum TDB_ERROR tdb_lock_hashes(struct tdb_context *tdb,
tdb_off_t hash_lock, tdb_len_t hash_range,
diff --git a/lib/tdb2/tdb1_lock.c b/lib/tdb2/tdb1_lock.c
index 495e1d1bf6..e59874fb2f 100644
--- a/lib/tdb2/tdb1_lock.c
+++ b/lib/tdb2/tdb1_lock.c
@@ -149,34 +149,43 @@ static int tdb1_lock_list(struct tdb_context *tdb, int list, int ltype,
bool check = false;
/* a allrecord lock allows us to avoid per chain locks */
- if (tdb->file->allrecord_lock.count &&
- (ltype == tdb->file->allrecord_lock.ltype || ltype == F_RDLCK)) {
- return 0;
+ if (tdb->file->allrecord_lock.count) {
+ if (!check_lock_pid(tdb, "tdb1_lock_list", true)) {
+ tdb->last_error = TDB_ERR_LOCK;
+ return -1;
+ }
+ if (tdb->file->allrecord_lock.owner != tdb) {
+ tdb->last_error = owner_conflict(tdb, "tdb1_lock_list");
+ return -1;
+ }
+ if (ltype == tdb->file->allrecord_lock.ltype
+ || ltype == F_RDLCK) {
+ return 0;
+ }
+ tdb->last_error = tdb_logerr(tdb, TDB_ERR_LOCK,
+ TDB_LOG_USE_ERROR,
+ "tdb1_lock_list:"
+ " already have read lock");
+ return -1;
}
- if (tdb->file->allrecord_lock.count) {
- tdb->last_error = TDB_ERR_LOCK;
- ret = -1;
- } else {
- /* Only check when we grab first data lock. */
- check = !have_data_locks(tdb);
- ret = tdb1_nest_lock(tdb, lock_offset(list), ltype, waitflag);
+ /* Only check when we grab first data lock. */
+ check = !have_data_locks(tdb);
+ ret = tdb1_nest_lock(tdb, lock_offset(list), ltype, waitflag);
+
+ if (ret == 0 && check) {
+ tdb_bool_err berr = tdb1_needs_recovery(tdb);
- if (ret == 0 && check) {
- tdb_bool_err berr = tdb1_needs_recovery(tdb);
+ if (berr < 0) {
+ return -1;
+ }
+ if (berr == true) {
+ tdb1_nest_unlock(tdb, lock_offset(list), ltype);
- if (berr < 0) {
+ if (tdb1_lock_and_recover(tdb) == -1) {
return -1;
}
- if (berr == true) {
- tdb1_nest_unlock(tdb, lock_offset(list), ltype);
-
- if (tdb1_lock_and_recover(tdb) == -1) {
- return -1;
- }
- return tdb1_lock_list(tdb, list, ltype,
- waitflag);
- }
+ return tdb1_lock_list(tdb, list, ltype, waitflag);
}
}
return ret;
@@ -222,6 +231,10 @@ int tdb1_unlock(struct tdb_context *tdb, int list, int ltype)
/* a global lock allows us to avoid per chain locks */
if (tdb->file->allrecord_lock.count &&
(ltype == tdb->file->allrecord_lock.ltype || ltype == F_RDLCK)) {
+ if (tdb->file->allrecord_lock.owner != tdb) {
+ tdb->last_error = owner_conflict(tdb, "tdb1_unlock");
+ return -1;
+ }
return 0;
}
@@ -314,9 +327,9 @@ int tdb1_allrecord_lock(struct tdb_context *tdb, int ltype,
return -1;
}
- /* FIXME: Temporary cast. */
- tdb->file->allrecord_lock.owner = (void *)(struct tdb_context *)tdb;
+ tdb->file->allrecord_lock.owner = tdb;
tdb->file->allrecord_lock.count = 1;
+ tdb->file->locker = getpid();
/* If it's upgradable, it's actually exclusive so we can treat
* it as a write lock. */
tdb->file->allrecord_lock.ltype = upgradable ? F_WRLCK : ltype;
@@ -362,6 +375,11 @@ int tdb1_allrecord_unlock(struct tdb_context *tdb, int ltype)
}
if (tdb->file->allrecord_lock.count > 1) {
+ if (tdb->file->allrecord_lock.owner != tdb) {
+ tdb->last_error
+ = owner_conflict(tdb, "tdb1_allrecord_unlock");
+ return -1;
+ }
tdb->file->allrecord_lock.count--;
return 0;
}
@@ -412,6 +430,15 @@ int tdb1_chainunlock_read(struct tdb_context *tdb, TDB_DATA key)
int tdb1_lock_record(struct tdb_context *tdb, tdb1_off_t off)
{
if (tdb->file->allrecord_lock.count) {
+ if (!check_lock_pid(tdb, "tdb1_lock_record", true)) {
+ tdb->last_error = TDB_ERR_LOCK;
+ return -1;
+ }
+ if (tdb->file->allrecord_lock.owner != tdb) {
+ tdb->last_error = owner_conflict(tdb,
+ "tdb1_lock_record");
+ return -1;
+ }
return 0;
}
return off ? tdb1_brlock(tdb, F_RDLCK, off, 1, TDB_LOCK_WAIT) : 0;
@@ -429,6 +456,15 @@ int tdb1_write_lock_record(struct tdb_context *tdb, tdb1_off_t off)
if (i->off == off)
return -1;
if (tdb->file->allrecord_lock.count) {
+ if (!check_lock_pid(tdb, "tdb1_write_lock_record", true)) {
+ tdb->last_error = TDB_ERR_LOCK;
+ return -1;
+ }
+ if (tdb->file->allrecord_lock.owner != tdb) {
+ tdb->last_error
+ = owner_conflict(tdb, "tdb1_write_lock_record");
+ return -1;
+ }
if (tdb->file->allrecord_lock.ltype == F_WRLCK) {
return 0;
}
@@ -440,6 +476,12 @@ int tdb1_write_lock_record(struct tdb_context *tdb, tdb1_off_t off)
int tdb1_write_unlock_record(struct tdb_context *tdb, tdb1_off_t off)
{
if (tdb->file->allrecord_lock.count) {
+ if (tdb->file->allrecord_lock.owner != tdb) {
+ tdb->last_error
+ = owner_conflict(tdb,
+ "tdb1_write_unlock_record");
+ return -1;
+ }
return 0;
}
return tdb1_brunlock(tdb, F_WRLCK, off, 1);
@@ -452,6 +494,11 @@ int tdb1_unlock_record(struct tdb_context *tdb, tdb1_off_t off)
uint32_t count = 0;
if (tdb->file->allrecord_lock.count) {
+ if (tdb->file->allrecord_lock.owner != tdb) {
+ tdb->last_error = owner_conflict(tdb,
+ "tdb1_unlock_record");
+ return -1;
+ }
return 0;
}