summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Tridgell <tridge@samba.org>2006-04-12 04:42:40 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 14:04:03 -0500
commit289911bb4e7980bf42cc87305d3f94477c5f2b75 (patch)
treedc9c146fc478a8e2ec1995b0a344cff893def0fd
parent9cf41988ff6cf0647ec4850f25415ba66845fd70 (diff)
downloadsamba-289911bb4e7980bf42cc87305d3f94477c5f2b75.tar.gz
samba-289911bb4e7980bf42cc87305d3f94477c5f2b75.tar.bz2
samba-289911bb4e7980bf42cc87305d3f94477c5f2b75.zip
r15048: started on the server side implementation of oplocks. The code is not
functional yet, I'm committing so it doesn't diverge too much from other peoples work. It is disabled by default. (This used to be commit ba0b8a218dfe1ef6cdf7de724fb30650301369dd)
-rw-r--r--source4/lib/messaging/messaging.h1
-rw-r--r--source4/librpc/idl/opendb.idl1
-rw-r--r--source4/ntvfs/common/opendb.c105
-rw-r--r--source4/ntvfs/posix/pvfs_open.c51
-rw-r--r--source4/ntvfs/posix/vfs_posix.c4
5 files changed, 122 insertions, 40 deletions
diff --git a/source4/lib/messaging/messaging.h b/source4/lib/messaging/messaging.h
index de65eb9ffe..86f5db2c17 100644
--- a/source4/lib/messaging/messaging.h
+++ b/source4/lib/messaging/messaging.h
@@ -32,5 +32,6 @@ struct messaging_context;
#define MSG_PVFS_RETRY_OPEN 5
#define MSG_IRPC 6
#define MSG_PVFS_NOTIFY 7
+#define MSG_NTVFS_OPLOCK_BREAK 8
#endif
diff --git a/source4/librpc/idl/opendb.idl b/source4/librpc/idl/opendb.idl
index 3754d233f6..601c143bf2 100644
--- a/source4/librpc/idl/opendb.idl
+++ b/source4/librpc/idl/opendb.idl
@@ -21,6 +21,7 @@ interface opendb
/* we need a per-entry delete on close, as well as a per-file
one, to cope with strange semantics on open */
bool8 delete_on_close;
+ uint32 oplock_level;
} opendb_entry;
typedef struct {
diff --git a/source4/ntvfs/common/opendb.c b/source4/ntvfs/common/opendb.c
index 81a13857ab..fc981d5af6 100644
--- a/source4/ntvfs/common/opendb.c
+++ b/source4/ntvfs/common/opendb.c
@@ -47,11 +47,12 @@
#include "lib/messaging/irpc.h"
#include "librpc/gen_ndr/ndr_opendb.h"
#include "smb.h"
+#include "ntvfs/ntvfs.h"
struct odb_context {
struct tdb_wrap *w;
- uint32_t server;
- struct messaging_context *messaging_ctx;
+ struct ntvfs_context *ntvfs_ctx;
+ BOOL oplocks;
};
/*
@@ -68,8 +69,8 @@ struct odb_lock {
talloc_free(). We need the messaging_ctx to allow for pending open
notifications.
*/
-_PUBLIC_ struct odb_context *odb_init(TALLOC_CTX *mem_ctx, uint32_t server,
- struct messaging_context *messaging_ctx)
+_PUBLIC_ struct odb_context *odb_init(TALLOC_CTX *mem_ctx,
+ struct ntvfs_context *ntvfs_ctx)
{
char *path;
struct odb_context *odb;
@@ -89,8 +90,10 @@ _PUBLIC_ struct odb_context *odb_init(TALLOC_CTX *mem_ctx, uint32_t server,
return NULL;
}
- odb->server = server;
- odb->messaging_ctx = messaging_ctx;
+ odb->ntvfs_ctx = ntvfs_ctx;
+
+ /* leave oplocks disabled by default until the code is working */
+ odb->oplocks = lp_parm_bool(-1, "opendb", "oplocks", False);
return odb;
}
@@ -249,6 +252,16 @@ static NTSTATUS odb_push_record(struct odb_lock *lck, struct opendb_file *file)
return NT_STATUS_OK;
}
+/*
+ send an oplock break to a client
+*/
+static NTSTATUS odb_oplock_break_send(struct odb_context *odb, struct opendb_entry *e)
+{
+ /* tell the server handling this open file about the need to send the client
+ a break */
+ return messaging_send_ptr(odb->ntvfs_ctx->msg_ctx, e->server,
+ MSG_NTVFS_OPLOCK_BREAK, e->file_handle);
+}
/*
register an open file in the open files database. This implements the share_access
@@ -260,7 +273,8 @@ static NTSTATUS odb_push_record(struct odb_lock *lck, struct opendb_file *file)
_PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle,
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, BOOL delete_on_close,
- const char *path)
+ const char *path,
+ uint32_t oplock_level, uint32_t *oplock_granted)
{
struct odb_context *odb = lck->odb;
struct opendb_entry e;
@@ -268,6 +282,10 @@ _PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle,
struct opendb_file file;
NTSTATUS status;
+ if (odb->oplocks == False) {
+ oplock_level = OPLOCK_NONE;
+ }
+
status = odb_pull_record(lck, &file);
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
/* initialise a blank structure */
@@ -277,27 +295,70 @@ _PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle,
NT_STATUS_NOT_OK_RETURN(status);
}
-
- if (file.delete_on_close ||
- (file.num_entries != 0 && delete_on_close)) {
- /* while delete on close is set, no new opens are allowed */
- return NT_STATUS_DELETE_PENDING;
- }
-
/* see if it conflicts */
- e.server = odb->server;
+ e.server = odb->ntvfs_ctx->server_id;
e.file_handle = file_handle;
e.stream_id = stream_id;
e.share_access = share_access;
e.access_mask = access_mask;
e.delete_on_close = delete_on_close;
+ e.oplock_level = OPLOCK_NONE;
+ /* see if anyone has an oplock, which we need to break */
+ for (i=0;i<file.num_entries;i++) {
+ if (file.entries[i].oplock_level == OPLOCK_BATCH) {
+ /* a batch oplock caches close calls, which
+ means the client application might have
+ already closed the file. We have to allow
+ this close to propogate by sending a oplock
+ break request and suspending this call
+ until the break is acknowledged or the file
+ is closed */
+ odb_oplock_break_send(odb, &file.entries[i]);
+ return NT_STATUS_OPLOCK_NOT_GRANTED;
+ }
+ }
+
+ if (file.delete_on_close ||
+ (file.num_entries != 0 && delete_on_close)) {
+ /* while delete on close is set, no new opens are allowed */
+ return NT_STATUS_DELETE_PENDING;
+ }
+
+ /* check for sharing violations */
for (i=0;i<file.num_entries;i++) {
status = share_conflict(&file.entries[i], &e);
NT_STATUS_NOT_OK_RETURN(status);
}
- /* it doesn't, so add it to the end */
+ /* we now know the open could succeed, but we need to check
+ for any exclusive oplocks. We can't grant a second open
+ till these are broken. Note that we check for batch oplocks
+ before checking for sharing violations, and check for
+ exclusive oplocks afterwards. */
+ for (i=0;i<file.num_entries;i++) {
+ if (file.entries[i].oplock_level == OPLOCK_EXCLUSIVE) {
+ odb_oplock_break_send(odb, &file.entries[i]);
+ return NT_STATUS_OPLOCK_NOT_GRANTED;
+ }
+ }
+
+ /*
+ possibly grant an exclusive or batch oplock if this is the only client
+ with the file open. We don't yet grant levelII oplocks.
+ */
+ if (oplock_granted != NULL) {
+ if ((oplock_level == OPLOCK_BATCH ||
+ oplock_level == OPLOCK_EXCLUSIVE) &&
+ file.num_entries == 0) {
+ (*oplock_granted) = oplock_level;
+ } else {
+ (*oplock_granted) = OPLOCK_NONE;
+ }
+ e.oplock_level = (*oplock_granted);
+ }
+
+ /* it doesn't conflict, so add it to the end */
file.entries = talloc_realloc(lck, file.entries, struct opendb_entry,
file.num_entries+1);
NT_STATUS_HAVE_NO_MEMORY(file.entries);
@@ -325,7 +386,7 @@ _PUBLIC_ NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
file.num_pending+1);
NT_STATUS_HAVE_NO_MEMORY(file.pending);
- file.pending[file.num_pending].server = odb->server;
+ file.pending[file.num_pending].server = odb->ntvfs_ctx->server_id;
file.pending[file.num_pending].notify_ptr = private;
file.num_pending++;
@@ -350,7 +411,7 @@ _PUBLIC_ NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle)
/* find the entry, and delete it */
for (i=0;i<file.num_entries;i++) {
if (file_handle == file.entries[i].file_handle &&
- odb->server == file.entries[i].server) {
+ odb->ntvfs_ctx->server_id == file.entries[i].server) {
if (file.entries[i].delete_on_close) {
file.delete_on_close = True;
}
@@ -369,7 +430,7 @@ _PUBLIC_ NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle)
/* send any pending notifications, removing them once sent */
for (i=0;i<file.num_pending;i++) {
- messaging_send_ptr(odb->messaging_ctx, file.pending[i].server,
+ messaging_send_ptr(odb->ntvfs_ctx->msg_ctx, file.pending[i].server,
MSG_PVFS_RETRY_OPEN,
file.pending[i].notify_ptr);
}
@@ -397,7 +458,7 @@ _PUBLIC_ NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
/* find the entry, and delete it */
for (i=0;i<file.num_pending;i++) {
if (private == file.pending[i].notify_ptr &&
- odb->server == file.pending[i].server) {
+ odb->ntvfs_ctx->server_id == file.pending[i].server) {
if (i < file.num_pending-1) {
memmove(file.pending+i, file.pending+i+1,
(file.num_pending - (i+1)) *
@@ -525,7 +586,7 @@ _PUBLIC_ NTSTATUS odb_can_open(struct odb_lock *lck,
return NT_STATUS_DELETE_PENDING;
}
- e.server = odb->server;
+ e.server = odb->ntvfs_ctx->server_id;
e.file_handle = NULL;
e.stream_id = 0;
e.share_access = share_access;
@@ -536,7 +597,7 @@ _PUBLIC_ NTSTATUS odb_can_open(struct odb_lock *lck,
if (!NT_STATUS_IS_OK(status)) {
/* note that we discard the error code
here. We do this as unless we are actually
- doing an open (which comes via a sdifferent
+ doing an open (which comes via a different
function), we need to return a sharing
violation */
return NT_STATUS_SHARING_VIOLATION;
diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c
index 5afb538db7..0a2cab8747 100644
--- a/source4/ntvfs/posix/pvfs_open.c
+++ b/source4/ntvfs/posix/pvfs_open.c
@@ -313,7 +313,8 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
/* see if we are allowed to open at the same time as existing opens */
status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
- share_access, access_mask, del_on_close, name->full_name);
+ share_access, access_mask, del_on_close,
+ name->full_name, OPLOCK_NONE, NULL);
if (!NT_STATUS_IS_OK(status)) {
idr_remove(pvfs->files.idtree, f->fnum);
@@ -369,7 +370,8 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
}
status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
- share_access, access_mask, del_on_close, name->full_name);
+ share_access, access_mask, del_on_close,
+ name->full_name, OPLOCK_NONE, NULL);
if (!NT_STATUS_IS_OK(status)) {
goto cleanup_delete;
@@ -563,6 +565,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
uint32_t attrib;
BOOL del_on_close;
struct pvfs_filename *parent;
+ uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
(create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
@@ -678,8 +681,17 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
del_on_close = False;
}
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_level = OPLOCK_NONE;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
+ oplock_level = OPLOCK_BATCH;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
+ oplock_level = OPLOCK_EXCLUSIVE;
+ }
+
status = odb_open_file(lck, f->handle, name->stream_id,
- share_access, access_mask, del_on_close, name->full_name);
+ share_access, access_mask, del_on_close,
+ name->full_name, oplock_level, &oplock_granted);
talloc_free(lck);
if (!NT_STATUS_IS_OK(status)) {
/* bad news, we must have hit a race - we don't delete the file
@@ -690,6 +702,10 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
return status;
}
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_granted = OPLOCK_BATCH;
+ }
+
f->fnum = fnum;
f->session_info = req->session_info;
f->smbpid = req->smbpid;
@@ -718,12 +734,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
talloc_set_destructor(f, pvfs_fnum_destructor);
talloc_set_destructor(f->handle, pvfs_handle_destructor);
-
- if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
- io->generic.out.oplock_level = OPLOCK_BATCH;
- } else {
- io->generic.out.oplock_level = OPLOCK_NONE;
- }
+ io->generic.out.oplock_level = oplock_granted;
io->generic.out.file.fnum = f->fnum;
io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
io->generic.out.create_time = name->dos.create_time;
@@ -994,6 +1005,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
uint32_t share_access;
uint32_t access_mask;
BOOL stream_existed, stream_truncate=False;
+ uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
/* use the generic mapping code to avoid implementing all the
different open calls. */
@@ -1174,9 +1186,18 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
talloc_set_destructor(f, pvfs_fnum_destructor);
talloc_set_destructor(f->handle, pvfs_handle_destructor);
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_level = OPLOCK_NONE;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
+ oplock_level = OPLOCK_BATCH;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
+ oplock_level = OPLOCK_EXCLUSIVE;
+ }
+
/* see if we are allowed to open at the same time as existing opens */
status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
- share_access, access_mask, False, name->full_name);
+ share_access, access_mask, False, name->full_name,
+ oplock_level, &oplock_granted);
/* on a sharing violation we need to retry when the file is closed by
the other user, or after 1 second */
@@ -1190,6 +1211,10 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
return status;
}
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_granted = OPLOCK_BATCH;
+ }
+
f->handle->have_opendb_entry = True;
if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
@@ -1252,11 +1277,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
talloc_free(lck);
- if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
- io->generic.out.oplock_level = OPLOCK_BATCH;
- } else {
- io->generic.out.oplock_level = OPLOCK_NONE;
- }
+ io->generic.out.oplock_level = oplock_granted;
io->generic.out.file.fnum = f->fnum;
io->generic.out.create_action = stream_existed?
NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c
index c4d4d11c04..eddc49c919 100644
--- a/source4/ntvfs/posix/vfs_posix.c
+++ b/source4/ntvfs/posix/vfs_posix.c
@@ -178,9 +178,7 @@ static NTSTATUS pvfs_connect(struct ntvfs_module_context *ntvfs,
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- pvfs->odb_context = odb_init(pvfs,
- pvfs->ntvfs->ctx->server_id,
- pvfs->ntvfs->ctx->msg_ctx);
+ pvfs->odb_context = odb_init(pvfs, pvfs->ntvfs->ctx);
if (pvfs->odb_context == NULL) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}