Fix oplock break test 2
authorSachin Prabhu <sprabhu@redhat.com>
Thu, 14 Jun 2018 22:45:35 +0000 (23:45 +0100)
committerMichael Adam <obnox@samba.org>
Tue, 18 Sep 2018 10:34:37 +0000 (12:34 +0200)
The current expected behaviour matches Windows server behaviour which
appears to be incorrect. The behaviour now matches the behaviour of
the latest samba server.

The test has been modified to test both windows and samba servers
which use different channels when sending oplock breaks.

We also add new helper CHECK_VAL_GREATER_THAN()

Signed-off-by: Sachin Prabhu <sprabhu@redhat.com>
source4/torture/smb2/multichannel.c

index 59e35c11b22b5213f6057206a6834e57a8921d60..c9fbe84c9b4d7851c4fe5e0846eae190142c0f97 100644 (file)
@@ -57,6 +57,15 @@ extern struct lease_break_info lease_break_info;
                goto done; \
        }} while (0)
 
+#define CHECK_VAL_GREATER_THAN(v, gt_val) do { \
+       if ((v) <= (gt_val)) { \
+               torture_result(tctx, TORTURE_FAIL, "(%s): wrong value for %s got 0x%x - should be greater than 0x%x\n", \
+                               __location__, #v, (int)v, (int)gt_val); \
+               ret = false; \
+               goto done; \
+       }} while (0)
+
+
 #define CHECK_CREATED(__io, __created, __attribute)                    \
        do {                                                            \
                CHECK_VAL((__io)->out.create_action, NTCREATEX_ACTION_ ## __created); \
@@ -624,21 +633,22 @@ done:
  * open file1 in 2A
  * open file2 in 2B
  * open file1 in session 1
- *     oplock break received on 2C(last opened channel)
- * block 2C
+ *     oplock break received
+ * block channel on which oplock break received
  * open file2 in session 1
- *     oplock break never received
- *     file allowed to be open after ~ 30 secs
+ *     oplock break not received. Retry received.
+ *     file opened
  * write to file2 on 2B
  *     Break sent to session 1(which has file2 open)
+ *     Break sent to session 2A(which has read oplock)
  * close file1 in session 1
  * open file1 with session 1
- * unblock 2C
- * disconnect 2C
- * reconnect 2C
- * open file3 in 2C
+ * unblock blocked channel
+ * disconnect blocked channel
+ * connect channel 2D
+ * open file3 in 2D
  * open file3 in session 1
- *     receive break on 2C
+ *     receive break
  */
 static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
                                           struct smb2_tree *tree1)
@@ -664,8 +674,9 @@ static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
        struct smb2_tree *tree2A = NULL;
        struct smb2_tree *tree2B = NULL;
        struct smb2_tree *tree2C = NULL;
+       struct smb2_tree *tree2D = NULL;
        struct smb2_transport *transport1 = tree1->session->transport;
-       struct smb2_transport *transport2C = NULL;
+       struct smb2_transport *transport2 = NULL;
        struct smbcli_options transport2_options;
        struct smb2_session *session1 = tree1->session;
        uint16_t local_port = 0;
@@ -715,7 +726,6 @@ static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
                                                  &transport2_options,
                                                  &tree2A, &tree2B, &tree2C);
        CHECK_VAL(rval, 0);
-       transport2C = tree2C->session->transport;
 
        torture_comment(tctx, "client2 opens fname1 via session 2A\n");
        io1.in.oplock_level = smb2_util_oplock_level("b");
@@ -748,11 +758,13 @@ static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
        CHECK_VAL(io1.out.oplock_level, smb2_util_oplock_level("s"));
        torture_wait_for_oplock_break(tctx);
        CHECK_VAL(break_info.count, 1);
-       //CHECK_PTR(break_info.received_transport, transport2C);
 
+       /* We use the transport over which this oplock break was received */
+       transport2 = break_info.received_transport;
        torture_reset_break_info(tctx, &break_info);
 
-       block_ok = torture_block_tcp_transport(tctx, transport2C);
+       /* block transport which is used by the server for oplock breaks */
+       block_ok = torture_block_tcp_transport(tctx, transport2);
        torture_assert(tctx, block_ok, "we could not block tcp transport");
 
        torture_comment(tctx, "client1 opens fname2 via session 1\n");
@@ -764,20 +776,16 @@ static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
        CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
 
        /*
-        * Important: when filtered, Windows will really let the open succeed
-        * and *NOT* send a new oplock break over the remaining channels, thus
-        * the break info count stays at zero.
+        * Samba downgrades oplock to a level 2 oplock.
+        * Windows 2016 revokes oplock
         */
-
        torture_wait_for_oplock_break(tctx);
-       CHECK_VAL(break_info.count, 0);
-
+       CHECK_VAL(break_info.count, 1);
        torture_reset_break_info(tctx, &break_info);
 
-       blob = data_blob_string_const("Here I am");
-
        torture_comment(tctx, "Trying write to file2 on tree2B\n");
 
+       blob = data_blob_string_const("Here I am");
        status = smb2_util_write(tree2B,
                                 h_client2_file2,
                                 blob.data,
@@ -786,10 +794,14 @@ static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
        torture_assert_ntstatus_ok(tctx, status,
                "failed to write file2 via channel 2B");
 
-       /* Write above triggers oplock break for session 1 which has file2 open */
+       /*
+        * Samba: Write triggers 2 oplock breaks
+        *              for session 1 which has file2 open
+        *              for session 2 which has type 2 oplock
+        * Windows 2016: Only one oplock break for session 1
+        */
        torture_wait_for_oplock_break(tctx);
-       CHECK_VAL(break_info.count, 1);
-       //CHECK_PTR(break_info.received_transport, transport1);
+       CHECK_VAL_GREATER_THAN(break_info.count, 0);
        torture_reset_break_info(tctx, &break_info);
 
        torture_comment(tctx, "client1 closes fname2 via session 1\n");
@@ -805,27 +817,26 @@ static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
        CHECK_CREATED(&io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE);
        CHECK_VAL(io2.out.oplock_level, smb2_util_oplock_level("s"));
 
-       unblock_ok = torture_unblock_tcp_transport(tctx, transport2C);
+       unblock_ok = torture_unblock_tcp_transport(tctx, transport2);
        torture_assert(tctx, unblock_ok, "we could not unblock tcp transport");
 
-       /* disconnect 2C */
-       torture_comment(tctx, "explicit disconnect of transport2C\n");
-       smbXcli_conn_disconnect(transport2C->conn, NT_STATUS_FOOBAR);
+       /* disconnect transport2 */
+       torture_comment(tctx, "explicit disconnect of transport2\n");
+       smbXcli_conn_disconnect(transport2->conn, NT_STATUS_FOOBAR);
 
        /*
-        * now add a third channel and repeat the test, we need to reestablish
-        * transport2C because the remote end has invalidated our connection
+        * now add a fourth channel and repeat the test, we need to reestablish
+        * transport2 because the remote end has invalidated our connection
         */
-       torture_comment(tctx, "Reconnecting session 2C\n");
-       talloc_free(tree2C);
-       tree2C = test_multichannel_create_channel(tctx, host, share,
-                               credentials, &transport2_options, tree2A);
-       if (!tree2C)
+       torture_comment(tctx, "Connecting session 2D\n");
+       tree2D = test_multichannel_create_channel(tctx, host, share,
+                                       credentials, &transport2_options, tree2B);
+       if (!tree2D)
                goto done;
-       transport2C = tree2C->session->transport;
+       transport2 = tree2D->session->transport;
 
-       torture_comment(tctx, "client 2 opening fname3 over newly established transport2C\n");
-       status = smb2_create(tree2C, mem_ctx, &io3);
+       torture_comment(tctx, "client 2 opening fname3 over transport2D\n");
+       status = smb2_create(tree2D, mem_ctx, &io3);
        CHECK_STATUS(status, NT_STATUS_OK);
        h_client2_file3 = io3.out.file.handle;
        CHECK_CREATED(&io3, CREATED, FILE_ATTRIBUTE_ARCHIVE);
@@ -841,12 +852,11 @@ static bool test_multichannel_oplock_break_test2(struct torture_context *tctx,
        CHECK_VAL(io3.out.oplock_level, smb2_util_oplock_level("s"));
        torture_wait_for_oplock_break(tctx);
        CHECK_VAL(break_info.count, 1);
-       //CHECK_PTR(break_info.received_transport, transport2C);
 
 done:
        if (block_ok && !unblock_ok) {
-               /* unblock tcp connection of transport2C */
-               unblock_ok = torture_unblock_tcp_transport(tctx, transport2C);
+               /* unblock tcp connection of transport2 */
+               unblock_ok = torture_unblock_tcp_transport(tctx, transport2);
        }
 
        tree1->session = session1;
@@ -854,10 +864,10 @@ done:
        smb2_util_close(tree1, h_client1_file1);
        smb2_util_close(tree1, h_client1_file2);
        smb2_util_close(tree1, h_client1_file3);
-       if (tree2A != NULL) {
-               smb2_util_close(tree2A, h_client2_file1);
-               smb2_util_close(tree2A, h_client2_file2);
-               smb2_util_close(tree2A, h_client2_file3);
+       if (tree2B != NULL) {
+               smb2_util_close(tree2B, h_client2_file1);
+               smb2_util_close(tree2B, h_client2_file2);
+               smb2_util_close(tree2B, h_client2_file3);
        }
 
        smb2_util_unlink(tree1, fname1);
@@ -866,6 +876,8 @@ done:
        smb2_deltree(tree1, BASEDIR);
 
        test_multichannel_free_channels2(tree2A, tree2B, tree2C);
+       if (tree2D != NULL)
+               TALLOC_FREE(tree2D);
        talloc_free(tree1);
        talloc_free(mem_ctx);