summaryrefslogtreecommitdiff
path: root/source4/ntvfs
diff options
context:
space:
mode:
Diffstat (limited to 'source4/ntvfs')
-rw-r--r--source4/ntvfs/common/opendb.c4
-rw-r--r--source4/ntvfs/common/opendb.h1
-rw-r--r--source4/ntvfs/common/opendb_tdb.c32
-rw-r--r--source4/ntvfs/ntvfs.h16
-rw-r--r--source4/ntvfs/ntvfs_base.c2
-rw-r--r--source4/ntvfs/ntvfs_util.c1
-rw-r--r--source4/ntvfs/posix/pvfs_open.c31
-rw-r--r--source4/ntvfs/posix/pvfs_oplock.c178
8 files changed, 183 insertions, 82 deletions
diff --git a/source4/ntvfs/common/opendb.c b/source4/ntvfs/common/opendb.c
index 6c1a9c070a..a7e5458aaf 100644
--- a/source4/ntvfs/common/opendb.c
+++ b/source4/ntvfs/common/opendb.c
@@ -98,11 +98,13 @@ _PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck,
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
uint32_t open_disposition, bool break_to_none,
+ bool allow_level_II_oplock,
uint32_t oplock_level, uint32_t *oplock_granted)
{
return ops->odb_open_file(lck, file_handle, path, stream_id, share_access,
access_mask, delete_on_close, open_disposition,
- break_to_none, oplock_level, oplock_granted);
+ break_to_none, allow_level_II_oplock,
+ oplock_level, oplock_granted);
}
diff --git a/source4/ntvfs/common/opendb.h b/source4/ntvfs/common/opendb.h
index 69a7f718ba..1c7f815dea 100644
--- a/source4/ntvfs/common/opendb.h
+++ b/source4/ntvfs/common/opendb.h
@@ -30,6 +30,7 @@ struct opendb_ops {
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
uint32_t open_disposition, bool break_to_none,
+ bool allow_level_II_oplock,
uint32_t oplock_level, uint32_t *oplock_granted);
NTSTATUS (*odb_open_file_pending)(struct odb_lock *lck, void *private);
NTSTATUS (*odb_close_file)(struct odb_lock *lck, void *file_handle,
diff --git a/source4/ntvfs/common/opendb_tdb.c b/source4/ntvfs/common/opendb_tdb.c
index 47b35f594c..0736af3d1e 100644
--- a/source4/ntvfs/common/opendb_tdb.c
+++ b/source4/ntvfs/common/opendb_tdb.c
@@ -344,7 +344,8 @@ static NTSTATUS odb_tdb_open_can_internal(struct odb_context *odb,
break request and suspending this call
until the break is acknowledged or the file
is closed */
- if (break_to_none) {
+ if (break_to_none ||
+ !file->entries[i].allow_level_II_oplock) {
oplock_return = OPLOCK_BREAK_TO_NONE;
}
odb_oplock_break_send(odb, &file->entries[i],
@@ -391,7 +392,8 @@ static NTSTATUS odb_tdb_open_can_internal(struct odb_context *odb,
* send an oplock break to the holder of the
* oplock and tell caller to retry later
*/
- if (break_to_none) {
+ if (break_to_none ||
+ !file->entries[i].allow_level_II_oplock) {
oplock_return = OPLOCK_BREAK_TO_NONE;
}
odb_oplock_break_send(odb, &file->entries[i],
@@ -418,6 +420,7 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck,
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
uint32_t open_disposition, bool break_to_none,
+ bool allow_level_II_oplock,
uint32_t oplock_level, uint32_t *oplock_granted)
{
struct odb_context *odb = lck->odb;
@@ -447,13 +450,14 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck,
NT_STATUS_NOT_OK_RETURN(status);
/* see if it conflicts */
- 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;
+ 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.allow_level_II_oplock = allow_level_II_oplock;
+ e.oplock_level = OPLOCK_NONE;
/*
possibly grant an exclusive, batch or level2 oplock
@@ -466,17 +470,23 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck,
if (file.num_entries == 0) {
e.oplock_level = OPLOCK_EXCLUSIVE;
*oplock_granted = EXCLUSIVE_OPLOCK_RETURN;
- } else {
+ } else if (allow_level_II_oplock) {
e.oplock_level = OPLOCK_LEVEL_II;
*oplock_granted = LEVEL_II_OPLOCK_RETURN;
+ } else {
+ e.oplock_level = OPLOCK_NONE;
+ *oplock_granted = NO_OPLOCK_RETURN;
}
} else if (oplock_level == OPLOCK_BATCH) {
if (file.num_entries == 0) {
e.oplock_level = OPLOCK_BATCH;
*oplock_granted = BATCH_OPLOCK_RETURN;
- } else {
+ } else if (allow_level_II_oplock) {
e.oplock_level = OPLOCK_LEVEL_II;
*oplock_granted = LEVEL_II_OPLOCK_RETURN;
+ } else {
+ e.oplock_level = OPLOCK_NONE;
+ *oplock_granted = NO_OPLOCK_RETURN;
}
} else if (oplock_level == OPLOCK_LEVEL_II) {
e.oplock_level = OPLOCK_LEVEL_II;
diff --git a/source4/ntvfs/ntvfs.h b/source4/ntvfs/ntvfs.h
index a708dbff51..7a2edc7e2c 100644
--- a/source4/ntvfs/ntvfs.h
+++ b/source4/ntvfs/ntvfs.h
@@ -181,6 +181,14 @@ struct ntvfs_context {
enum protocol_types protocol;
+ /*
+ * client capabilities
+ * this field doesn't use protocol specific
+ * values!
+ */
+#define NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS 0x0000000000000001LLU
+ uint64_t client_caps;
+
/*
* linked list of module contexts
*/
@@ -257,6 +265,14 @@ struct ntvfs_request {
/* the smb pid is needed for locking contexts */
uint16_t smbpid;
+ /*
+ * client capabilities
+ * this field doesn't use protocol specific
+ * values!
+ * see NTVFS_CLIENT_CAP_*
+ */
+ uint64_t client_caps;
+
/* some statictics for the management tools */
struct {
/* the system time when the request arrived */
diff --git a/source4/ntvfs/ntvfs_base.c b/source4/ntvfs/ntvfs_base.c
index f5a24f23a0..35becabcf9 100644
--- a/source4/ntvfs/ntvfs_base.c
+++ b/source4/ntvfs/ntvfs_base.c
@@ -153,6 +153,7 @@ _PUBLIC_ bool ntvfs_interface_differs(const struct ntvfs_critical_sizes *const i
*/
NTSTATUS ntvfs_init_connection(TALLOC_CTX *mem_ctx, struct share_config *scfg, enum ntvfs_type type,
enum protocol_types protocol,
+ uint64_t ntvfs_client_caps,
struct event_context *ev, struct messaging_context *msg,
struct loadparm_context *lp_ctx,
struct server_id server_id, struct ntvfs_context **_ctx)
@@ -168,6 +169,7 @@ NTSTATUS ntvfs_init_connection(TALLOC_CTX *mem_ctx, struct share_config *scfg, e
ctx = talloc_zero(mem_ctx, struct ntvfs_context);
NT_STATUS_HAVE_NO_MEMORY(ctx);
ctx->protocol = protocol;
+ ctx->client_caps = ntvfs_client_caps;
ctx->type = type;
ctx->config = talloc_steal(ctx, scfg);
ctx->event_ctx = ev;
diff --git a/source4/ntvfs/ntvfs_util.c b/source4/ntvfs/ntvfs_util.c
index 7432ac2c13..ebe8008edd 100644
--- a/source4/ntvfs/ntvfs_util.c
+++ b/source4/ntvfs/ntvfs_util.c
@@ -42,6 +42,7 @@ _PUBLIC_ struct ntvfs_request *ntvfs_request_create(struct ntvfs_context *ctx, T
req->async_states = NULL;
req->session_info = session_info;
req->smbpid = smbpid;
+ req->client_caps = ctx->client_caps;
req->statistics.request_time = request_time;
async = talloc(req, struct ntvfs_async_state);
diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c
index adf4c1ac18..47b44b9634 100644
--- a/source4/ntvfs/posix/pvfs_open.c
+++ b/source4/ntvfs/posix/pvfs_open.c
@@ -293,7 +293,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
io->generic.in.open_disposition,
- false, OPLOCK_NONE, NULL);
+ false, false, OPLOCK_NONE, NULL);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);
@@ -347,7 +347,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
io->generic.in.open_disposition,
- false, OPLOCK_NONE, NULL);
+ false, false, OPLOCK_NONE, NULL);
if (!NT_STATUS_IS_OK(status)) {
goto cleanup_delete;
@@ -544,6 +544,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
bool del_on_close;
struct pvfs_filename *parent;
uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
+ bool allow_level_II_oplock = false;
if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
(create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
@@ -658,10 +659,15 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
oplock_level = OPLOCK_EXCLUSIVE;
}
+ if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
+ allow_level_II_oplock = true;
+ }
+
status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
io->generic.in.open_disposition,
- false, oplock_level, &oplock_granted);
+ false, allow_level_II_oplock,
+ 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
@@ -696,21 +702,20 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
DLIST_ADD(pvfs->files.list, f);
+ /* setup a destructor to avoid file descriptor leaks on
+ abnormal termination */
+ talloc_set_destructor(f, pvfs_fnum_destructor);
+ talloc_set_destructor(f->handle, pvfs_handle_destructor);
+
if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
oplock_granted = OPLOCK_BATCH;
} else if (oplock_granted != OPLOCK_NONE) {
status = pvfs_setup_oplock(f, oplock_granted);
if (!NT_STATUS_IS_OK(status)) {
- talloc_free(lck);
return status;
}
}
- /* setup a destructor to avoid file descriptor leaks on
- abnormal termination */
- talloc_set_destructor(f, pvfs_fnum_destructor);
- talloc_set_destructor(f->handle, pvfs_handle_destructor);
-
io->generic.out.oplock_level = oplock_granted;
io->generic.out.file.ntvfs = f->ntvfs;
io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
@@ -1048,6 +1053,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
bool del_on_close;
bool stream_existed, stream_truncate=false;
uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
+ bool allow_level_II_oplock = false;
/* use the generic mapping code to avoid implementing all the
different open calls. */
@@ -1242,11 +1248,16 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
oplock_level = OPLOCK_EXCLUSIVE;
}
+ if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
+ allow_level_II_oplock = true;
+ }
+
/* see if we are allowed to open at the same time as existing opens */
status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
io->generic.in.open_disposition,
- false, oplock_level, &oplock_granted);
+ false, allow_level_II_oplock,
+ oplock_level, &oplock_granted);
/*
* on a sharing violation we need to retry when the file is closed by
diff --git a/source4/ntvfs/posix/pvfs_oplock.c b/source4/ntvfs/posix/pvfs_oplock.c
index cf30ddbc59..dfa3697af7 100644
--- a/source4/ntvfs/posix/pvfs_oplock.c
+++ b/source4/ntvfs/posix/pvfs_oplock.c
@@ -22,6 +22,7 @@
#include "includes.h"
#include "lib/messaging/messaging.h"
#include "lib/messaging/irpc.h"
+#include "system/time.h"
#include "vfs_posix.h"
@@ -29,9 +30,63 @@ struct pvfs_oplock {
struct pvfs_file_handle *handle;
struct pvfs_file *file;
uint32_t level;
+ struct timeval break_to_level_II;
+ struct timeval break_to_none;
struct messaging_context *msg_ctx;
};
+static NTSTATUS pvfs_oplock_release_internal(struct pvfs_file_handle *h,
+ uint8_t oplock_break)
+{
+ struct odb_lock *olck;
+ NTSTATUS status;
+
+ if (h->fd == -1) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (!h->have_opendb_entry) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (!h->oplock) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (olck == NULL) {
+ DEBUG(0,("Unable to lock opendb for oplock update\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (oplock_break == OPLOCK_BREAK_TO_NONE) {
+ h->oplock->level = OPLOCK_NONE;
+ } else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
+ h->oplock->level = OPLOCK_LEVEL_II;
+ } else {
+ /* fallback to level II in case of a invalid value */
+ DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
+ h->oplock->level = OPLOCK_LEVEL_II;
+ }
+ status = odb_update_oplock(olck, h, h->oplock->level);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ talloc_free(olck);
+ return status;
+ }
+
+ talloc_free(olck);
+
+ /* after a break to none, we no longer have an oplock attached */
+ if (h->oplock->level == OPLOCK_NONE) {
+ talloc_free(h->oplock);
+ h->oplock = NULL;
+ }
+
+ return NT_STATUS_OK;
+}
+
/*
receive oplock breaks and forward them to the client
*/
@@ -41,13 +96,65 @@ static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level)
struct pvfs_file *f = opl->file;
struct pvfs_file_handle *h = opl->handle;
struct pvfs_state *pvfs = h->pvfs;
+ struct timeval cur = timeval_current();
+ struct timeval *last = NULL;
+ struct timeval end;
+
+ switch (level) {
+ case OPLOCK_BREAK_TO_LEVEL_II:
+ last = &opl->break_to_level_II;
+ break;
+ case OPLOCK_BREAK_TO_NONE:
+ last = &opl->break_to_none;
+ break;
+ }
+
+ if (!last) {
+ DEBUG(0,("%s: got unexpected level[0x%02X]\n",
+ __FUNCTION__, level));
+ return;
+ }
+
+ if (timeval_is_zero(last)) {
+ /*
+ * this is the first break we for this level
+ * remember the time
+ */
+ *last = cur;
- DEBUG(10,("pvfs_oplock_break: sending oplock break level %d for '%s' %p\n",
- level, h->name->original_name, h));
- status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
+ DEBUG(0,("%s: sending oplock break level %d for '%s' %p\n",
+ __FUNCTION__, level, h->name->original_name, h));
+ status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s: sending oplock break failed: %s\n",
+ __FUNCTION__, nt_errstr(status)));
+ }
+ return;
+ }
+
+ end = timeval_add(last, pvfs->oplock_break_timeout, 0);
+
+ if (timeval_compare(&cur, &end) < 0) {
+ /*
+ * If it's not expired just ignore the break
+ * as we already sent the break request to the client
+ */
+ DEBUG(0,("%s: do not resend oplock break level %d for '%s' %p\n",
+ __FUNCTION__, level, h->name->original_name, h));
+ return;
+ }
+
+ /*
+ * If the client did not send a release within the
+ * oplock break timeout time frame we auto release
+ * the oplock
+ */
+ DEBUG(0,("%s: auto release oplock level %d for '%s' %p\n",
+ __FUNCTION__, level, h->name->original_name, h));
+ status = pvfs_oplock_release_internal(h, level);
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,("pvfs_oplock_break: sending oplock break failed: %s\n",
- nt_errstr(status)));
+ DEBUG(0,("%s: failed to auto release the oplock[0x%02X]: %s\n",
+ __FUNCTION__, level, nt_errstr(status)));
}
}
@@ -113,7 +220,7 @@ NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
return NT_STATUS_OK;
}
- opl = talloc(f->handle, struct pvfs_oplock);
+ opl = talloc_zero(f->handle, struct pvfs_oplock);
NT_STATUS_HAVE_NO_MEMORY(opl);
opl->handle = f->handle;
@@ -140,8 +247,6 @@ NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_file *f;
- struct pvfs_file_handle *h;
- struct odb_lock *olck;
uint8_t oplock_break;
NTSTATUS status;
@@ -150,52 +255,15 @@ NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
return NT_STATUS_INVALID_HANDLE;
}
- h = f->handle;
-
- if (h->fd == -1) {
- return NT_STATUS_FILE_IS_A_DIRECTORY;
- }
-
- if (!h->have_opendb_entry) {
- return NT_STATUS_FOOBAR;
- }
-
- if (!h->oplock) {
- return NT_STATUS_FOOBAR;
- }
-
- olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
- if (olck == NULL) {
- DEBUG(0,("Unable to lock opendb for oplock update\n"));
- return NT_STATUS_FOOBAR;
- }
-
oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
- if (oplock_break == OPLOCK_BREAK_TO_NONE) {
- h->oplock->level = OPLOCK_NONE;
- } else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
- h->oplock->level = OPLOCK_LEVEL_II;
- } else {
- /* fallback to level II in case of a invalid value */
- DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
- h->oplock->level = OPLOCK_LEVEL_II;
- }
- status = odb_update_oplock(olck, h, h->oplock->level);
+
+ status = pvfs_oplock_release_internal(f->handle, oplock_break);
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
- h->name->full_name, nt_errstr(status)));
- talloc_free(olck);
+ DEBUG(0,("%s: failed to release the oplock[0x%02X]: %s\n",
+ __FUNCTION__, oplock_break, nt_errstr(status)));
return status;
}
- talloc_free(olck);
-
- /* after a break to none, we no longer have an oplock attached */
- if (h->oplock->level == OPLOCK_NONE) {
- talloc_free(h->oplock);
- h->oplock = NULL;
- }
-
return NT_STATUS_OK;
}
@@ -205,7 +273,7 @@ NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
struct odb_lock *olck;
NTSTATUS status;
- if (h->oplock && h->oplock->level == OPLOCK_EXCLUSIVE) {
+ if (h->oplock && h->oplock->level != OPLOCK_LEVEL_II) {
return NT_STATUS_OK;
}
@@ -215,16 +283,6 @@ NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
return NT_STATUS_FOOBAR;
}
- if (h->oplock && h->oplock->level == OPLOCK_BATCH) {
- status = odb_update_oplock(olck, h, OPLOCK_LEVEL_II);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
- h->name->full_name, nt_errstr(status)));
- talloc_free(olck);
- return status;
- }
- }
-
status = odb_break_oplocks(olck);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",