The branch, v4-0-test has been updated
       via  18e27aef7be9b21f65f72ab4c656778ce0c23953 (commit)
       via  c459885898c9912df1ae5afff4fef2ff809dc15e (commit)
       via  a5476ee41c140123db160b2e36c8c7084619a738 (commit)
       via  bfb0888578677856b2b6b72471f542d0d5d7b838 (commit)
       via  21772fa33d772a9df6ff04a0ed1b0d8f4f533295 (commit)
       via  2b8934e4ab2dd9673928a6c9a291aedac1ebaa95 (commit)
      from  b0ecd8f8d2c1ebf23957921f4852e90b556812fc (commit)

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=v4-0-test


- Log -----------------------------------------------------------------
commit 18e27aef7be9b21f65f72ab4c656778ce0c23953
Author: Stefan Metzmacher <[EMAIL PROTECTED]>
Date:   Tue Mar 4 14:24:27 2008 +0100

    selftest: use the same oplocktimeout for smbtorture as for smbd
    
    metze

commit c459885898c9912df1ae5afff4fef2ff809dc15e
Author: Stefan Metzmacher <[EMAIL PROTECTED]>
Date:   Tue Mar 4 14:08:32 2008 +0100

    RAW-OPLOCK: add BATCH22 and test the behavior of oplock break timeouts
    
    metze

commit a5476ee41c140123db160b2e36c8c7084619a738
Author: Stefan Metzmacher <[EMAIL PROTECTED]>
Date:   Tue Mar 4 12:15:32 2008 +0100

    RAW-OPLOCK: add BATCH21: a self write with an oplock doesn't break it
    
    metze

commit bfb0888578677856b2b6b72471f542d0d5d7b838
Author: Stefan Metzmacher <[EMAIL PROTECTED]>
Date:   Tue Mar 4 14:16:17 2008 +0100

    pvfs_oplock: auto release oplocks after a timeout
    
    Remember that we sent an oplock break to
    a client and don't resend. If the filesystem
    layer tries to send a new break and the client
    has not released after a former oplock break
    after the timeout interval, we need to auto release
    the oplock.
    
    metze

commit 21772fa33d772a9df6ff04a0ed1b0d8f4f533295
Author: Stefan Metzmacher <[EMAIL PROTECTED]>
Date:   Tue Mar 4 14:11:53 2008 +0100

    pvfs_oplock: only a break level2 oplocks...
    
    It seems that I've tested this in the wrong way before.
    
    metze

commit 2b8934e4ab2dd9673928a6c9a291aedac1ebaa95
Author: Stefan Metzmacher <[EMAIL PROTECTED]>
Date:   Tue Mar 4 14:10:13 2008 +0100

    pvfs_oplock: move pvfs_oplock_release() parts into a helper function
    
    metze

-----------------------------------------------------------------------

Summary of changes:
 source/ntvfs/posix/pvfs_oplock.c |  178 ++++++++++++++++++++++++------------
 source/selftest/samba4_tests.sh  |    2 +-
 source/torture/raw/oplock.c      |  188 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 307 insertions(+), 61 deletions(-)


Changeset truncated at 500 lines:

diff --git a/source/ntvfs/posix/pvfs_oplock.c b/source/ntvfs/posix/pvfs_oplock.c
index cf30ddb..dfa3697 100644
--- a/source/ntvfs/posix/pvfs_oplock.c
+++ b/source/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/source/selftest/samba4_tests.sh b/source/selftest/samba4_tests.sh
index bea4173..8102095 100755
--- a/source/selftest/samba4_tests.sh
+++ b/source/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/source/torture/raw/oplock.c b/source/torture/raw/oplock.c
index 7ac88c0..8a7489c 100644
--- a/source/torture/raw/oplock.c
+++ b/source/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;
 }


-- 
Samba Shared Repository

Reply via email to