diff options
-rw-r--r-- | source4/ntvfs/common/brlock.c | 19 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_lock.c | 59 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_open.c | 1 | ||||
-rw-r--r-- | source4/ntvfs/posix/vfs_posix.h | 4 |
4 files changed, 72 insertions, 11 deletions
diff --git a/source4/ntvfs/common/brlock.c b/source4/ntvfs/common/brlock.c index 792ee52ad5..2da32891fb 100644 --- a/source4/ntvfs/common/brlock.c +++ b/source4/ntvfs/common/brlock.c @@ -117,8 +117,16 @@ static BOOL brl_same_context(struct lock_context *ctx1, struct lock_context *ctx static BOOL brl_overlap(struct lock_struct *lck1, struct lock_struct *lck2) { - if (lck1->start >= (lck2->start + lck2->size) || - lck2->start >= (lck1->start + lck1->size)) { + /* 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; @@ -193,11 +201,12 @@ static NTSTATUS brl_lock_failed(struct brl_context *brl, struct lock_struct *loc return NT_STATUS_FILE_LOCK_CONFLICT; } brl->last_lock_failure = *lock; - if (lock->start >= 0xEF000000) { + 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. */ + 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; diff --git a/source4/ntvfs/posix/pvfs_lock.c b/source4/ntvfs/posix/pvfs_lock.c index 548c5bd82c..100fc50e55 100644 --- a/source4/ntvfs/posix/pvfs_lock.c +++ b/source4/ntvfs/posix/pvfs_lock.c @@ -45,7 +45,8 @@ NTSTATUS pvfs_check_lock(struct pvfs_state *pvfs, } /* this state structure holds information about a lock we are waiting on */ -struct pending_state { +struct pvfs_pending_lock { + struct pvfs_pending_lock *next, *prev; struct pvfs_state *pvfs; union smb_lock *lck; struct pvfs_file *f; @@ -55,7 +56,6 @@ struct pending_state { time_t end_time; }; - /* a secondary attempt to setup a lock has failed - back out the locks we did get and send an error @@ -89,7 +89,7 @@ static void pvfs_lock_async_failed(struct pvfs_state *pvfs, */ static void pvfs_pending_lock_continue(void *private, BOOL timed_out) { - struct pending_state *pending = private; + struct pvfs_pending_lock *pending = private; struct pvfs_state *pvfs = pending->pvfs; struct pvfs_file *f = pending->f; struct smbsrv_request *req = pending->req; @@ -107,6 +107,8 @@ static void pvfs_pending_lock_continue(void *private, BOOL timed_out) rw = WRITE_LOCK; } + DLIST_REMOVE(f->pending_list, pending); + status = brl_lock(pvfs->brl_context, &f->locking_key, req->smbpid, @@ -130,8 +132,10 @@ static void pvfs_pending_lock_continue(void *private, BOOL timed_out) if (timed_out) { /* no more chances */ pvfs_lock_async_failed(pvfs, req, f, locks, pending->pending_lock, status); + } else { + /* we can try again */ + DLIST_ADD(f->pending_list, pending); } - /* we can try again */ return; } @@ -170,6 +174,8 @@ static void pvfs_pending_lock_continue(void *private, BOOL timed_out) pending); if (pending->wait_handle == NULL) { pvfs_lock_async_failed(pvfs, req, f, locks, i, NT_STATUS_NO_MEMORY); + } else { + DLIST_ADD(f->pending_list, pending); } return; } @@ -192,6 +198,42 @@ static void pvfs_pending_lock_continue(void *private, BOOL timed_out) /* + cancel a set of locks +*/ +static NTSTATUS pvfs_lock_cancel(struct pvfs_state *pvfs, struct smbsrv_request *req, union smb_lock *lck, + struct pvfs_file *f) +{ + struct pvfs_pending_lock *p; + + for (p=f->pending_list;p;p=p->next) { + /* check if the lock request matches exactly - you can only cancel with exact matches */ + if (p->lck->lockx.in.ulock_cnt == lck->lockx.in.ulock_cnt && + p->lck->lockx.in.lock_cnt == lck->lockx.in.lock_cnt && + p->lck->lockx.in.fnum == lck->lockx.in.fnum && + p->lck->lockx.in.mode == (lck->lockx.in.mode & ~LOCKING_ANDX_CANCEL_LOCK)) { + int i; + + for (i=0;i<lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt;i++) { + if (p->lck->lockx.in.locks[i].pid != lck->lockx.in.locks[i].pid || + p->lck->lockx.in.locks[i].offset != lck->lockx.in.locks[i].offset || + p->lck->lockx.in.locks[i].count != lck->lockx.in.locks[i].count) { + break; + } + } + if (i < lck->lockx.in.ulock_cnt) continue; + + /* an exact match! we can cancel it, which is equivalent + to triggering the timeout early */ + pvfs_pending_lock_continue(p ,True); + return NT_STATUS_OK; + } + } + + return NT_STATUS_UNSUCCESSFUL; +} + + +/* lock or unlock a byte range */ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, @@ -202,7 +244,7 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, struct smb_lock_entry *locks; int i; enum brl_type rw; - struct pending_state *pending = NULL; + struct pvfs_pending_lock *pending = NULL; f = pvfs_find_fd(pvfs, req, lck->generic.in.fnum); if (!f) { @@ -237,7 +279,7 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, /* now the lockingX case, most common and also most complex */ if (lck->lockx.in.timeout != 0) { - pending = talloc_p(req, struct pending_state); + pending = talloc_p(req, struct pvfs_pending_lock); if (pending == NULL) { return NT_STATUS_NO_MEMORY; } @@ -257,6 +299,10 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, rw = pending? PENDING_WRITE_LOCK : WRITE_LOCK; } + if (lck->lockx.in.mode & LOCKING_ANDX_CANCEL_LOCK) { + return pvfs_lock_cancel(pvfs, req, lck, f); + } + if (lck->lockx.in.mode & (LOCKING_ANDX_OPLOCK_RELEASE | LOCKING_ANDX_CHANGE_LOCKTYPE | @@ -309,6 +355,7 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, if (pending->wait_handle == NULL) { return NT_STATUS_NO_MEMORY; } + DLIST_ADD(f->pending_list, pending); return NT_STATUS_OK; } /* undo the locks we just did */ diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index 5798aa782f..05c2bdda28 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -171,6 +171,7 @@ do_open: f->session = req->session; f->smbpid = req->smbpid; f->pvfs = pvfs; + f->pending_list = NULL; /* we must zero here to take account of padding */ ZERO_STRUCT(lock_context); diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h index e83f0479a9..6aaa43a213 100644 --- a/source4/ntvfs/posix/vfs_posix.h +++ b/source4/ntvfs/posix/vfs_posix.h @@ -123,8 +123,12 @@ struct pvfs_file { /* we need this hook back to our parent for lock destruction */ struct pvfs_state *pvfs; + + /* a list of pending locks - used for locking cancel operations */ + struct pvfs_pending_lock *pending_list; }; + struct pvfs_mangle_context { uint8_t char_flags[256]; /* |