diff options
author | Andrew Tridgell <tridge@samba.org> | 2004-10-18 07:40:17 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 13:00:00 -0500 |
commit | d0cc571e30bf49443ac7d1b1a0b896ee72d7d9a6 (patch) | |
tree | a0c43b9894da40ab6b78cdad8a6a6964d8067fe7 /source4/ntvfs/posix | |
parent | d37acd0fe71923987a68377bf01040eaed245d9f (diff) | |
download | samba-d0cc571e30bf49443ac7d1b1a0b896ee72d7d9a6.tar.gz samba-d0cc571e30bf49443ac7d1b1a0b896ee72d7d9a6.tar.bz2 samba-d0cc571e30bf49443ac7d1b1a0b896ee72d7d9a6.zip |
r3029: implemented byte range lock timeouts.
This adds a pvfs_wait_message() routine which uses the new messaging
system, event timers and talloc destructors to give a nice generic
async event handling system with a easy to use interface. The
extensions to pvfs_lock.c are based on calls to pvfs_wait_message()
routines.
We now pass all of our smbtorture locking tests, although while
writing this code I have thought of some additonal tests that should
be added, particularly for lock cancel operations. I'll work on that
soon.
This commit also extends the smbtorture lock tests to test the rather
weird 0xEEFFFFFF locking semantics that I have discovered in
win2003. Win2003 treats the 0xEEFFFFFF boundary as special, and will
give different error codes on either side of it. Locks on both sides
are allowed, the only difference is which error code is given when a
lock is denied. Anyone like to hazard a guess as to why? It has
me stumped.
(This used to be commit 4395c0557ab175d6a8dd99df03c266325949ffa5)
Diffstat (limited to 'source4/ntvfs/posix')
-rw-r--r-- | source4/ntvfs/posix/config.mk | 1 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_lock.c | 186 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_open.c | 6 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_wait.c | 128 | ||||
-rw-r--r-- | source4/ntvfs/posix/vfs_posix.c | 3 |
5 files changed, 317 insertions, 7 deletions
diff --git a/source4/ntvfs/posix/config.mk b/source4/ntvfs/posix/config.mk index 8ca5ad7b0b..b6ba073a99 100644 --- a/source4/ntvfs/posix/config.mk +++ b/source4/ntvfs/posix/config.mk @@ -21,6 +21,7 @@ ADD_OBJ_FILES = \ ntvfs/posix/pvfs_resolve.o \ ntvfs/posix/pvfs_shortname.o \ ntvfs/posix/pvfs_lock.o \ + ntvfs/posix/pvfs_wait.o \ ntvfs/common/brlock.o # End MODULE ntvfs_posix ################################################ diff --git a/source4/ntvfs/posix/pvfs_lock.c b/source4/ntvfs/posix/pvfs_lock.c index d7aca9df8b..548c5bd82c 100644 --- a/source4/ntvfs/posix/pvfs_lock.c +++ b/source4/ntvfs/posix/pvfs_lock.c @@ -44,6 +44,153 @@ NTSTATUS pvfs_check_lock(struct pvfs_state *pvfs, offset, count, rw); } +/* this state structure holds information about a lock we are waiting on */ +struct pending_state { + struct pvfs_state *pvfs; + union smb_lock *lck; + struct pvfs_file *f; + struct smbsrv_request *req; + int pending_lock; + void *wait_handle; + time_t end_time; +}; + + +/* + a secondary attempt to setup a lock has failed - back out + the locks we did get and send an error +*/ +static void pvfs_lock_async_failed(struct pvfs_state *pvfs, + struct smbsrv_request *req, + struct pvfs_file *f, + struct smb_lock_entry *locks, + int i, + NTSTATUS status) +{ + /* undo the locks we just did */ + for (i=i-1;i>=0;i--) { + brl_unlock(pvfs->brl_context, + &f->locking_key, + locks[i].pid, + f->fnum, + locks[i].offset, + locks[i].count); + } + req->async.status = status; + req->async.send_fn(req); +} + + +/* + called when we receive a pending lock notification. It means that + either our lock timed out or somoene else has unlocked a overlapping + range, so we should try the lock again. Note that on timeout we + do retry the lock, giving it a last chance. +*/ +static void pvfs_pending_lock_continue(void *private, BOOL timed_out) +{ + struct pending_state *pending = private; + struct pvfs_state *pvfs = pending->pvfs; + struct pvfs_file *f = pending->f; + struct smbsrv_request *req = pending->req; + union smb_lock *lck = pending->lck; + struct smb_lock_entry *locks; + enum brl_type rw; + NTSTATUS status; + int i; + + locks = lck->lockx.in.locks + lck->lockx.in.ulock_cnt; + + if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) { + rw = READ_LOCK; + } else { + rw = WRITE_LOCK; + } + + status = brl_lock(pvfs->brl_context, + &f->locking_key, + req->smbpid, + f->fnum, + locks[pending->pending_lock].offset, + locks[pending->pending_lock].count, + rw, NULL); + + /* if we have failed and timed out, or succeeded, then we + don't need the pending lock any more */ + if (NT_STATUS_IS_OK(status) || timed_out) { + NTSTATUS status2; + status2 = brl_remove_pending(pvfs->brl_context, &f->locking_key, pending); + if (!NT_STATUS_IS_OK(status2)) { + DEBUG(0,("pvfs_lock: failed to remove pending lock - %s\n", nt_errstr(status2))); + } + talloc_free(pending->wait_handle); + } + + if (!NT_STATUS_IS_OK(status)) { + if (timed_out) { + /* no more chances */ + pvfs_lock_async_failed(pvfs, req, f, locks, pending->pending_lock, status); + } + /* we can try again */ + return; + } + + /* if we haven't timed out yet, then we can do more pending locks */ + if (timed_out) { + pending = NULL; + } else { + if (rw == READ_LOCK) { + rw = PENDING_READ_LOCK; + } else { + rw = PENDING_WRITE_LOCK; + } + } + + /* we've now got the pending lock. try and get the rest, which might + lead to more pending locks */ + for (i=pending->pending_lock;i<lck->lockx.in.lock_cnt;i++) { + if (pending) { + pending->pending_lock = i; + } + + status = brl_lock(pvfs->brl_context, + &f->locking_key, + req->smbpid, + f->fnum, + locks[i].offset, + locks[i].count, + rw, pending); + if (!NT_STATUS_IS_OK(status)) { + if (pending) { + /* a timed lock failed - setup a wait message to handle + the pending lock notification or a timeout */ + pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY, + pending->end_time, + pvfs_pending_lock_continue, + pending); + if (pending->wait_handle == NULL) { + pvfs_lock_async_failed(pvfs, req, f, locks, i, NT_STATUS_NO_MEMORY); + } + return; + } + pvfs_lock_async_failed(pvfs, req, f, locks, i, status); + return; + } + } + + brl_unlock(pvfs->brl_context, + &f->locking_key, + req->smbpid, + f->fnum, + lck->lock.in.offset, + lck->lock.in.count); + + /* we've managed to get all the locks. Tell the client */ + req->async.status = NT_STATUS_OK; + req->async.send_fn(req); +} + + /* lock or unlock a byte range */ @@ -55,6 +202,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; f = pvfs_find_fd(pvfs, req, lck->generic.in.fnum); if (!f) { @@ -69,7 +217,7 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, f->fnum, lck->lock.in.offset, lck->lock.in.count, - WRITE_LOCK); + WRITE_LOCK, NULL); case RAW_LOCK_UNLOCK: return brl_unlock(pvfs->brl_context, @@ -88,11 +236,25 @@ 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); + if (pending == NULL) { + return NT_STATUS_NO_MEMORY; + } + + pending->pvfs = pvfs; + pending->lck = lck; + pending->f = f; + pending->req = req; + + /* round up to the nearest second */ + pending->end_time = time(NULL) + ((lck->lockx.in.timeout+999)/1000); + } if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) { - rw = READ_LOCK; + rw = pending? PENDING_READ_LOCK : READ_LOCK; } else { - rw = WRITE_LOCK; + rw = pending? PENDING_WRITE_LOCK : WRITE_LOCK; } if (lck->lockx.in.mode & @@ -125,14 +287,30 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, for (i=0;i<lck->lockx.in.lock_cnt;i++) { NTSTATUS status; + if (pending) { + pending->pending_lock = i; + } + status = brl_lock(pvfs->brl_context, &f->locking_key, locks[i].pid, f->fnum, locks[i].offset, locks[i].count, - rw); + rw, pending); if (!NT_STATUS_IS_OK(status)) { + if (pending) { + /* a timed lock failed - setup a wait message to handle + the pending lock notification or a timeout */ + pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY, + pending->end_time, + pvfs_pending_lock_continue, + pending); + if (pending->wait_handle == NULL) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; + } /* undo the locks we just did */ for (i=i-1;i>=0;i--) { brl_unlock(pvfs->brl_context, diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index 51526461e0..5798aa782f 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -96,9 +96,11 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, flags = O_CREAT | O_TRUNC; break; case NTCREATEX_DISP_OPEN: - case NTCREATEX_DISP_OVERWRITE: flags = 0; break; + case NTCREATEX_DISP_OVERWRITE: + flags = O_TRUNC; + break; case NTCREATEX_DISP_CREATE: flags = O_CREAT | O_EXCL; break; @@ -222,7 +224,7 @@ NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs, if (!NT_STATUS_IS_OK(status)) { return status; } - + if (close(f->fd) != 0) { status = pvfs_map_errno(pvfs, errno); } else { diff --git a/source4/ntvfs/posix/pvfs_wait.c b/source4/ntvfs/posix/pvfs_wait.c new file mode 100644 index 0000000000..1d6da6aaf8 --- /dev/null +++ b/source4/ntvfs/posix/pvfs_wait.c @@ -0,0 +1,128 @@ +/* + Unix SMB/CIFS implementation. + + POSIX NTVFS backend - async request wait routines + + Copyright (C) Andrew Tridgell 2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "include/includes.h" +#include "vfs_posix.h" + +/* the context for a single wait instance */ +struct pvfs_wait { + void (*handler)(void *, BOOL); + void *private; + struct timed_event *te; + int msg_type; + void *msg_ctx; + struct event_context *ev; +}; + + +/* + receive a completion message for a wait +*/ +static void pvfs_wait_dispatch(void *msg_ctx, void *private, uint32_t msg_type, + servid_t src, DATA_BLOB *data) +{ + struct pvfs_wait *pwait = private; + + /* we need to check that this one is for us. This sender sends + the private pointer as the body of the message. This might + seem a little unusual, but as the pointer is guaranteed + unique for this server, it is a good token */ + if (data->length != sizeof(void *) || + *(void **)data->data != pwait->private) { + return; + } + + pwait->handler(pwait->private, False); +} + + +/* + receive a timeout on a message wait +*/ +static void pvfs_wait_timeout(struct event_context *ev, struct timed_event *te, time_t t) +{ + struct pvfs_wait *pwait = te->private; + pwait->handler(pwait->private, True); +} + + +/* + destroy a pending wait + */ +static int pvfs_wait_destructor(void *ptr) +{ + struct pvfs_wait *pwait = ptr; + messaging_deregister(pwait->msg_ctx, pwait->msg_type, pwait->private); + event_remove_timed(pwait->ev, pwait->te); + return 0; +} + +/* + setup a request to wait on a message of type msg_type, with a + timeout (given as an expiry time) + + the return value is a handle. To stop waiting talloc_free this + handle. +*/ +void *pvfs_wait_message(struct pvfs_state *pvfs, + struct smbsrv_request *req, + int msg_type, + time_t end_time, + void (*fn)(void *, BOOL), + void *private) +{ + struct timed_event te; + struct pvfs_wait *pwait; + + pwait = talloc_p(req, struct pvfs_wait); + if (pwait == NULL) { + return NULL; + } + + pwait->private = private; + pwait->handler = fn; + pwait->msg_ctx = pvfs->tcon->smb_conn->connection->messaging_ctx; + pwait->ev = req->tcon->smb_conn->connection->event.ctx; + pwait->msg_type = msg_type; + + /* setup a timer */ + te.next_event = end_time; + te.handler = pvfs_wait_timeout; + te.private = pwait; + pwait->te = event_add_timed(pwait->ev, &te); + + /* register with the messaging subsystem for this message + type */ + messaging_register(pwait->msg_ctx, + pwait, + msg_type, + pvfs_wait_dispatch); + + /* tell the main smb server layer that we will be replying + asynchronously */ + req->control_flags |= REQ_CONTROL_ASYNC; + + /* make sure we cleanup the timer and message handler */ + talloc_set_destructor(pwait, pvfs_wait_destructor); + + return pwait; +} diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c index 5e7a605c9f..6e6c8b4275 100644 --- a/source4/ntvfs/posix/vfs_posix.c +++ b/source4/ntvfs/posix/vfs_posix.c @@ -89,7 +89,8 @@ static NTSTATUS pvfs_connect(struct ntvfs_module_context *ntvfs, pvfs->brl_context = brl_init(pvfs, pvfs->tcon->smb_conn->connection->server_id, - pvfs->tcon->service); + pvfs->tcon->service, + pvfs->tcon->smb_conn->connection->messaging_ctx); if (pvfs->brl_context == NULL) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } |