summaryrefslogtreecommitdiff
path: root/source4/ntvfs/posix
diff options
context:
space:
mode:
Diffstat (limited to 'source4/ntvfs/posix')
-rw-r--r--source4/ntvfs/posix/config.mk1
-rw-r--r--source4/ntvfs/posix/pvfs_lock.c186
-rw-r--r--source4/ntvfs/posix/pvfs_open.c6
-rw-r--r--source4/ntvfs/posix/pvfs_wait.c128
-rw-r--r--source4/ntvfs/posix/vfs_posix.c3
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;
}