diff options
Diffstat (limited to 'source3/locking/brlock.c')
-rw-r--r-- | source3/locking/brlock.c | 73 |
1 files changed, 56 insertions, 17 deletions
diff --git a/source3/locking/brlock.c b/source3/locking/brlock.c index 4c34bd7dfd..61144eb59a 100644 --- a/source3/locking/brlock.c +++ b/source3/locking/brlock.c @@ -92,6 +92,28 @@ static BOOL brl_same_context(struct lock_context *ctx1, } /**************************************************************************** + See if lck1 and lck2 overlap. +****************************************************************************/ + +static BOOL brl_overlap(struct lock_struct *lck1, + struct lock_struct *lck2) +{ + /* this extra check is not redundent - it copes with locks + that go beyond the end of 64 bit file space */ + if (lck1->size != 0 && + lck1->start == lck2->start && + lck1->size == lck2->size) { + return True; + } + + if (lck1->start >= (lck2->start+lck2->size) || + lck2->start >= (lck1->start+lck1->size)) { + return False; + } + return True; +} + +/**************************************************************************** See if lock2 can be added when lock1 is in place. ****************************************************************************/ @@ -115,7 +137,7 @@ static BOOL brl_conflict(struct lock_struct *lck1, return False; } - return True; + return brl_overlap(lck1, lck2); } #if ZERO_ZERO @@ -175,9 +197,36 @@ static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck lck2->start >= (lck1->start + lck1->size)) return False; - return True; + return brl_overlap(lck1, lck2); } +/**************************************************************************** + Amazingly enough, w2k3 "remembers" whether the last lock failure + is the same as this one and changes its error code. I wonder if any + app depends on this ? +****************************************************************************/ + +static NTSTATUS brl_lock_failed(struct lock_struct *lock) +{ + static struct lock_struct last_lock_failure; + + if (brl_same_context(&lock->context, &last_lock_failure.context) && + lock->fnum == last_lock_failure.fnum && + lock->start == last_lock_failure.start && + lock->size == last_lock_failure.size) { + return NT_STATUS_FILE_LOCK_CONFLICT; + } + last_lock_failure = *lock; + if (lock->start >= 0xEF000000 && + (lock->start >> 63) == 0) { + /* amazing the little things you learn with a test + suite. Locks beyond this offset (as a 64 bit + number!) always generate the conflict error code, + unless the top bit is set */ + return NT_STATUS_FILE_LOCK_CONFLICT; + } + return NT_STATUS_LOCK_NOT_GRANTED; +} #if DONT_DO_THIS /* doing this traversal could kill solaris machines under high load (tridge) */ @@ -313,8 +362,6 @@ NTSTATUS brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum, struct lock_struct lock, *locks; char *tp; NTSTATUS status = NT_STATUS_OK; - static int last_failed = -1; - static br_off last_failed_start; *my_lock_ctx = False; kbuf = locking_key(dev,ino); @@ -344,7 +391,7 @@ NTSTATUS brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum, count = dbuf.dsize / sizeof(*locks); for (i=0; i<count; i++) { if (brl_conflict(&locks[i], &lock)) { - status = NT_STATUS_LOCK_NOT_GRANTED; + status = brl_lock_failed(&lock);; /* Did we block ourselves ? */ if (brl_same_context(&locks[i].context, &lock.context)) *my_lock_ctx = True; @@ -375,24 +422,16 @@ NTSTATUS brl_lock(SMB_DEV_T dev, SMB_INO_T ino, int fnum, qsort(dbuf.dptr, dbuf.dsize/sizeof(lock), sizeof(lock), lock_compare); #endif - tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); + if (tdb_store(tdb, kbuf, dbuf, TDB_REPLACE) != 0) { + status = NT_STATUS_INTERNAL_DB_CORRUPTION; + goto fail; + } SAFE_FREE(dbuf.dptr); tdb_chainunlock(tdb, kbuf); return NT_STATUS_OK; fail: - /* this is a nasty hack to try to simulate the lock result cache code in w2k. - It isn't completely accurate as I haven't yet worked out the correct - semantics (tridge) - */ - if (last_failed == fnum && - last_failed_start == start && - NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED)) { - status = NT_STATUS_FILE_LOCK_CONFLICT; - } - last_failed = fnum; - last_failed_start = start; SAFE_FREE(dbuf.dptr); tdb_chainunlock(tdb, kbuf); |