summaryrefslogtreecommitdiff
path: root/source4
diff options
context:
space:
mode:
Diffstat (limited to 'source4')
-rw-r--r--source4/lib/ldb_wrap.c2
-rw-r--r--source4/ntvfs/posix/pvfs_oplock.c178
-rwxr-xr-xsource4/selftest/samba4_tests.sh2
-rw-r--r--source4/torture/raw/oplock.c188
4 files changed, 308 insertions, 62 deletions
diff --git a/source4/lib/ldb_wrap.c b/source4/lib/ldb_wrap.c
index 63049b06fc..71ba37b479 100644
--- a/source4/lib/ldb_wrap.c
+++ b/source4/lib/ldb_wrap.c
@@ -63,7 +63,7 @@ static void ldb_wrap_debug(void *context, enum ldb_debug_level level,
};
vasprintf(&s, fmt, ap);
if (!s) return;
- DEBUG(level, ("ldb: %s\n", s));
+ DEBUG(samba_level, ("ldb: %s\n", s));
free(s);
}
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",
diff --git a/source4/selftest/samba4_tests.sh b/source4/selftest/samba4_tests.sh
index bea4173555..8102095958 100755
--- a/source4/selftest/samba4_tests.sh
+++ b/source4/selftest/samba4_tests.sh
@@ -217,7 +217,7 @@ done
plantest "rpc.echo on ncacn_np over smb2" dc $smb4torture ncacn_np:"\$SERVER[smb2]" -U"\$USERNAME"%"\$PASSWORD" -W \$DOMAIN RPC-ECHO "$*"
# Tests against the NTVFS POSIX backend
-NTVFSARGS="--option=torture:sharedelay=100000"
+NTVFSARGS="--option=torture:sharedelay=100000 --option=torture:oplocktimeout=3"
smb2=`$smb4torture --list | grep "^SMB2-" | xargs`
raw=`$smb4torture --list | grep "^RAW-" | xargs`
base=`$smb4torture --list | grep "^BASE-" | xargs`
diff --git a/source4/torture/raw/oplock.c b/source4/torture/raw/oplock.c
index 7ac88c0996..8a7489c84e 100644
--- a/source4/torture/raw/oplock.c
+++ b/source4/torture/raw/oplock.c
@@ -32,6 +32,13 @@
ret = false; \
}} while (0)
+#define CHECK_RANGE(v, min, max) do { \
+ if ((v) < (min) || (v) > (max)) { \
+ torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got %d - should be between %d and %d\n", \
+ __location__, #v, (int)v, (int)min, (int)max); \
+ ret = false; \
+ }} while (0)
+
#define CHECK_STRMATCH(v, correct) do { \
if (!v || strstr((v),(correct)) == NULL) { \
torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got '%s' - should be '%s'\n", \
@@ -92,6 +99,21 @@ static bool oplock_handler_ack_to_none(struct smbcli_transport *transport,
return smbcli_oplock_ack(tree, fnum, OPLOCK_BREAK_TO_NONE);
}
+/*
+ a handler function for oplock break requests. Let it timeout
+*/
+static bool oplock_handler_timeout(struct smbcli_transport *transport,
+ uint16_t tid, uint16_t fnum,
+ uint8_t level, void *private)
+{
+ break_info.fnum = fnum;
+ break_info.level = level;
+ break_info.count++;
+
+ printf("Let oplock break timeout\n");
+ return true;
+}
+
static void oplock_handler_close_recv(struct smbcli_request *req)
{
NTSTATUS status;
@@ -2204,6 +2226,170 @@ done:
return ret;
}
+static bool test_raw_oplock_batch21(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch21.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ struct smb_echo e;
+ uint16_t fnum=0;
+ char c = 0;
+ ssize_t wr;
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_levelII, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ /*
+ with a batch oplock we get a break
+ */
+ torture_comment(tctx, "open with batch oplock\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "writing should not generate a break\n");
+ wr = smbcli_write(cli1->tree, fnum, 0, &c, 0, 1);
+ CHECK_VAL(wr, 1);
+ CHECK_STATUS(tctx, smbcli_nt_error(cli1->tree), NT_STATUS_OK);
+
+ ZERO_STRUCT(e);
+ e.in.repeat_count = 1;
+ status = smb_raw_echo(cli1->transport, &e);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+
+ CHECK_VAL(break_info.count, 0);
+
+ smbcli_close(cli1->tree, fnum);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
+static bool test_raw_oplock_batch22(struct torture_context *tctx, struct smbcli_state *cli1, struct smbcli_state *cli2)
+{
+ const char *fname = BASEDIR "\\test_batch22.dat";
+ NTSTATUS status;
+ bool ret = true;
+ union smb_open io;
+ union smb_unlink unl;
+ uint16_t fnum=0, fnum2=0;
+ char c = 0;
+ struct timeval tv;
+ int timeout = torture_setting_int(tctx, "oplocktimeout", 30);
+ int te;
+ ssize_t wr;
+
+ if (torture_setting_bool(tctx, "samba3", false)) {
+ torture_skip(tctx, "BACHT22 disabled against samba3\n");
+ }
+
+ if (!torture_setup_dir(cli1, BASEDIR)) {
+ return false;
+ }
+
+ /* cleanup */
+ smbcli_unlink(cli1->tree, fname);
+
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_levelII, cli1->tree);
+
+ /*
+ base ntcreatex parms
+ */
+ io.generic.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = fname;
+
+ /*
+ with a batch oplock we get a break
+ */
+ torture_comment(tctx, "open with batch oplock\n");
+ ZERO_STRUCT(break_info);
+ io.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|
+ NTCREATEX_SHARE_ACCESS_WRITE|
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+ CHECK_VAL(io.ntcreatex.out.oplock_level, BATCH_OPLOCK_RETURN);
+
+ torture_comment(tctx, "a 2nd open shoud not succeed after the oplock break timeout\n");
+ tv = timeval_current();
+ smbcli_oplock_handler(cli1->transport, oplock_handler_timeout, cli1->tree);
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_SHARING_VIOLATION);
+ te = (int)timeval_elapsed(&tv);
+ CHECK_RANGE(te, timeout - 1, timeout + 15);
+
+ CHECK_VAL(break_info.count, 1);
+ CHECK_VAL(break_info.fnum, fnum);
+ CHECK_VAL(break_info.level, OPLOCK_BREAK_TO_LEVEL_II);
+ CHECK_VAL(break_info.failures, 0);
+ ZERO_STRUCT(break_info);
+
+ torture_comment(tctx, "a 2nd open shoud succeed after the oplock release without break\n");
+ tv = timeval_current();
+ smbcli_oplock_handler(cli1->transport, oplock_handler_ack_to_levelII, cli1->tree);
+ status = smb_raw_open(cli1->tree, tctx, &io);
+ CHECK_STATUS(tctx, status, NT_STATUS_OK);
+ CHECK_VAL(io.ntcreatex.out.oplock_level, LEVEL_II_OPLOCK_RETURN);
+ te = (int)timeval_elapsed(&tv);
+ /* it should come in without delay */
+ CHECK_RANGE(te+1, 0, timeout);
+ fnum2 = io.ntcreatex.out.file.fnum;
+
+ CHECK_VAL(break_info.count, 0);
+
+ smbcli_close(cli1->tree, fnum);
+ smbcli_close(cli1->tree, fnum2);
+
+done:
+ smb_raw_exit(cli1->session);
+ smb_raw_exit(cli2->session);
+ smbcli_deltree(cli1->tree, BASEDIR);
+ return ret;
+}
+
/*
basic testing of oplocks
*/
@@ -2237,6 +2423,8 @@ struct torture_suite *torture_raw_oplock(TALLOC_CTX *mem_ctx)
torture_suite_add_2smb_test(suite, "BATCH18", test_raw_oplock_batch18);
torture_suite_add_2smb_test(suite, "BATCH19", test_raw_oplock_batch19);
torture_suite_add_2smb_test(suite, "BATCH20", test_raw_oplock_batch20);
+ torture_suite_add_2smb_test(suite, "BATCH21", test_raw_oplock_batch21);
+ torture_suite_add_2smb_test(suite, "BATCH22", test_raw_oplock_batch22);
return suite;
}