The branch, master has been updated
       via  b448eae5e98 smb2_tcon: also try to cancel pending compound requests 
on tdis
       via  d4010b9abc4 smbXsrv_session: always cancel pending requests in 
smb2srv_session_shutdown_send() in the same way
       via  f0e55378343 smb2_server: don't cancel pending request if at least 
one channel is still alive
       via  01b675ab323 smb2_server: let smbd_smb2_flush_send_queue() destroy 
pending elements on dead connection
       via  997e9023c0c smbXsrv_open: intruduce smbXsrv_open_replay_cache to 
support FILE_NOT_AVAILABLE
       via  a19180904ea smbXsrv_session: smbXsrv_session_remove_channel() 
should also remove the last channel
       via  87b8049320c s4:torture/smb2: add smb2.session.bind2
       via  f5168a21abd s4:torture/smb2: add smb2.replay.dhv2-pending* tests
       via  ae1c3a0d9ae s4:torture/smb2: provide verbose output when we're 
waiting for potential lease/oplock breaks
       via  aa5f93eb65d s4:torture/smb2: add smb2_util_lease_state_string()
       via  1714a05b992 s4:torture/smb2: make use of torture_reset_break_info() 
in replay.c
       via  e63651cfd6d s4:torture/smb2: make use of 
torture_reset_lease_break_info() in lease.c
       via  2c194c0bc61 vfs_aio_pthread: don't allow async opens when multi 
channel is enabled.
      from  359c6bd2108 Rename mdfind to mdsearch

https://git.samba.org/?p=samba.git;a=shortlog;h=master


- Log -----------------------------------------------------------------
commit b448eae5e983dcf3b7a222c5fc9a73eba88d1b06
Author: Stefan Metzmacher <me...@samba.org>
Date:   Tue Mar 23 11:10:22 2021 +0100

    smb2_tcon: also try to cancel pending compound requests on tdis
    
    There's no reason to do something special here.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>
    
    Autobuild-User(master): Jeremy Allison <j...@samba.org>
    Autobuild-Date(master): Mon Mar 29 20:43:28 UTC 2021 on sn-devel-184

commit d4010b9abc4a303f478420de4295c3c00fbdbbf2
Author: Stefan Metzmacher <me...@samba.org>
Date:   Thu Mar 11 17:04:37 2021 +0100

    smbXsrv_session: always cancel pending requests in 
smb2srv_session_shutdown_send() in the same way
    
    The session is valid for the lifetime of the requests anyway
    and there's no point in having special handling for compound requests.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit f0e553783434dccf0637e6e9e3a87890ae56286c
Author: Stefan Metzmacher <me...@samba.org>
Date:   Wed Feb 24 17:44:12 2021 +0100

    smb2_server: don't cancel pending request if at least one channel is still 
alive
    
    In order to allow replays of requests on a channel failure, we should
    not cancel pending requests, the strategie that seems to make windows
    clients happy is to let the requests running and return
    NT_STATUS_FILE_NOT_AVAILABLE as long as the original request is still
    pending.
    
    Here we introduce xconn->transport.shutdown_wait_queue, this is used
    to keep the xconn alive for the lifetime of pending requests.
    
    Now we only cancel pending requests if the disconnected connection
    is the last channel for a session.
    
    In that case smbXsrv_session_remove_channel() and
    smb2srv_session_shutdown_send() will take care of it.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 01b675ab323a73fc0cf25cd0bf706dbc1dde514b
Author: Stefan Metzmacher <me...@samba.org>
Date:   Fri Mar 12 18:34:06 2021 +0100

    smb2_server: let smbd_smb2_flush_send_queue() destroy pending elements on 
dead connection
    
    Otherwise we'll keep the state of already finished requests arround.
    
    This becomes critical as the next commit will cause us to
    let pending requests running and keep the xconn alive for
    the lifetime of pending requests, so we would not ever
    make progress and deadlock.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 997e9023c0ca94d57d75cd0069f5c6ab1f81be85
Author: Stefan Metzmacher <me...@samba.org>
Date:   Fri Mar 12 15:10:46 2021 +0100

    smbXsrv_open: intruduce smbXsrv_open_replay_cache to support 
FILE_NOT_AVAILABLE
    
    Before processing an open we need to reserve the replay cache entry
    in order to signal that we're still in progress.
    If a reserved record is already present we need to return
    FILE_NOT_AVAILABLE in order to let the client retry again.
    
    [MS-SMB2] contains this:
    
      <152> Section 3.2.5.1: For the following error codes, Windows-based 
clients
      will retry the operation up to three times and then retry the operation 
every 5
      seconds until the count of milliseconds specified by 
Open.ResilientTimeout is
      exceeded:
      - STATUS_SERVER_UNAVAILABLE
      - STATUS_FILE_NOT_AVAILABLE
      - STATUS_SHARE_UNAVAILABLE
    
    This works fine for windows clients, but current windows servers seems to
    return ACCESS_DENIED instead of FILE_NOT_AVAILABLE.
    
    A Windows server doesn't do any replay detection on pending opens,
    which wait for a HANDLE lease to be broken (because of a
    SHARING_VIOLATION), at all.
    
    As this is not really documented for the server part of the current 
[MS-SMB2],
    I found the key hint in "SMB 2.2: Bigger. Faster. Scalier - (Parts 1 and 2)"
    on page 24. There's a picture showing that a replay gets FILE_NOT_AVAILABLE
    as long as the original request is still in progress. See:
    
https://www.snia.org/educational-library/smb-22-bigger-faster-scalier-parts-1-and-2-2011
    
    A Windows client is unhappy with the current windows server behavior if it
    such a situation happens. There's also a very strange interaction with 
oplock
    where the replay gets SHARING_VIOLATION after 35 seconds because it 
conflicts with
    the original open.
    
    I think it's good to follow the intial design from the 2011 presentation and
    make the clients happy by using FILE_NOT_AVAILABLE (and differ from 
Windows).
    I'll report that to doch...@microsoft.com in order to get this hopefully 
fixed in
    their server too).
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit a19180904ea73815e994f3c720613ae0b06099c3
Author: Stefan Metzmacher <me...@samba.org>
Date:   Wed Feb 24 17:44:12 2021 +0100

    smbXsrv_session: smbXsrv_session_remove_channel() should also remove the 
last channel
    
    There's nothing special regarding the last channel,
    as the smb2.session.bind2 test demonstrates.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 87b8049320c6314fab12ff14ac825101876e87d9
Author: Stefan Metzmacher <me...@samba.org>
Date:   Thu Mar 18 14:38:16 2021 +0100

    s4:torture/smb2: add smb2.session.bind2
    
    This demonstrates that a session and it's open handles is destroyed
    when the last explicitly bound channel gets disconnected.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit f5168a21abd029fd57edfd270b86512312c801b1
Author: Stefan Metzmacher <me...@samba.org>
Date:   Fri Mar 12 17:21:47 2021 +0100

    s4:torture/smb2: add smb2.replay.dhv2-pending* tests
    
    These demonstrate that the replay detection for pending opens
    either doesn't exist (for the share_access=NONE => SHARING_VIOLATION
    case) or return the wrong status code => ACCESS_DENIED instead of
    FILE_NOT_AVAILABLE.
    
    Windows clients transparently retry after FILE_NOT_AVAILABLE,
    while they pass ACCESS_DENIED directly to the application.
    
    I'll report that to doch...@microsoft.com in order to
    clarify the situation.
    
    In the meantime I added tests with a '-windows' suffix,
    which demostrate the current windows server behavior,
    while the tests with a '-sane' suffix expect the behavior
    that whould make windows clients happy.
    
    For Samba I'll implement the '-sane' behavior that
    detects all replays and returns FILE_NOT_AVAILABLE
    if the original request is still pending.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit ae1c3a0d9ae00471cbbc8a7787f026b87e76aa45
Author: Stefan Metzmacher <me...@samba.org>
Date:   Wed Mar 17 13:49:28 2021 +0100

    s4:torture/smb2: provide verbose output when we're waiting for potential 
lease/oplock breaks
    
    It makes it easier to follow manual tests.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit aa5f93eb65d2b729770a23624acfb48a688e917a
Author: Stefan Metzmacher <me...@samba.org>
Date:   Mon Mar 22 16:34:36 2021 +0100

    s4:torture/smb2: add smb2_util_lease_state_string()
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 1714a05b992311647a51a6dda007958a7af0f043
Author: Stefan Metzmacher <me...@samba.org>
Date:   Tue Mar 23 00:58:38 2021 +0100

    s4:torture/smb2: make use of torture_reset_break_info() in replay.c
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit e63651cfd6d92805bd44c1245f4534bdcfdf3a7e
Author: Stefan Metzmacher <me...@samba.org>
Date:   Tue Mar 23 00:53:08 2021 +0100

    s4:torture/smb2: make use of torture_reset_lease_break_info() in lease.c
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

commit 2c194c0bc61958b9f569b3808b45035c2de6caef
Author: Stefan Metzmacher <me...@samba.org>
Date:   Fri Feb 26 12:31:29 2021 +0100

    vfs_aio_pthread: don't allow async opens when multi channel is enabled.
    
    We will get this supported later, but for now just disable async
    opens as fsp->mid may not belong the first xconn of client->connections.
    
    BUG: https://bugzilla.samba.org/show_bug.cgi?id=14449
    
    Signed-off-by: Stefan Metzmacher <me...@samba.org>
    Reviewed-by: Jeremy Allison <j...@samba.org>

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

Summary of changes:
 selftest/knownfail.d/smb2.replay            |   29 +
 source3/librpc/idl/smbXsrv.idl              |    7 +
 source3/modules/vfs_aio_pthread.c           |    7 +
 source3/smbd/globals.h                      |    7 +-
 source3/smbd/smb2_create.c                  |   78 +-
 source3/smbd/smb2_server.c                  |   65 +-
 source3/smbd/smb2_tcon.c                    |    7 +-
 source3/smbd/smbXsrv_open.c                 |  233 +-
 source3/smbd/smbXsrv_session.c              |  107 +-
 source4/torture/smb2/lease.c                |  128 +-
 source4/torture/smb2/lease_break_handler.c  |   31 +-
 source4/torture/smb2/oplock_break_handler.c |   21 +-
 source4/torture/smb2/replay.c               | 3510 +++++++++++++++++++++++++--
 source4/torture/smb2/session.c              |  307 +++
 source4/torture/smb2/util.c                 |    8 +
 15 files changed, 4177 insertions(+), 368 deletions(-)
 create mode 100644 selftest/knownfail.d/smb2.replay


Changeset truncated at 500 lines:

diff --git a/selftest/knownfail.d/smb2.replay b/selftest/knownfail.d/smb2.replay
new file mode 100644
index 00000000000..4cac8071ed8
--- /dev/null
+++ b/selftest/knownfail.d/smb2.replay
@@ -0,0 +1,29 @@
+# These tests demonstrate the broken Windows behavior
+# and check for ACCESS_DENIED instead of FILE_NOT_AVAILABLE
+# See https://bugzilla.samba.org/show_bug.cgi?id=14449
+^samba3.smb2.replay.dhv2-pending1n-vs-violation-lease-close-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending1n-vs-violation-lease-ack-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending1n-vs-oplock-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending1n-vs-lease-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending1l-vs-oplock-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending1l-vs-lease-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending1o-vs-oplock-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending1o-vs-lease-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending2n-vs-oplock-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending2n-vs-lease-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending2l-vs-oplock-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending2l-vs-lease-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending2o-vs-oplock-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending2o-vs-lease-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending3n-vs-oplock-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending3n-vs-lease-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending3l-vs-oplock-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending3l-vs-lease-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending3o-vs-oplock-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending3o-vs-lease-windows.nt4_dc
+^samba3.smb2.replay.dhv2-pending1n-vs-oplock-windows.ad_dc
+^samba3.smb2.replay.dhv2-pending1o-vs-oplock-windows.ad_dc
+^samba3.smb2.replay.dhv2-pending2n-vs-oplock-windows.ad_dc
+^samba3.smb2.replay.dhv2-pending2o-vs-oplock-windows.ad_dc
+^samba3.smb2.replay.dhv2-pending3n-vs-oplock-windows.ad_dc
+^samba3.smb2.replay.dhv2-pending3o-vs-oplock-windows.ad_dc
diff --git a/source3/librpc/idl/smbXsrv.idl b/source3/librpc/idl/smbXsrv.idl
index 89df7935c35..fc502009b3b 100644
--- a/source3/librpc/idl/smbXsrv.idl
+++ b/source3/librpc/idl/smbXsrv.idl
@@ -512,4 +512,11 @@ interface smbXsrv
        void smbXsrv_open_decode(
                [in] smbXsrv_openB blob
                );
+
+       const uint32 SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE = 28;
+       typedef [public] struct {
+               GUID                                    holder_req_guid;
+               NTTIME                                  idle_time;
+               uint32                                  local_id;
+       } smbXsrv_open_replay_cache;
 }
diff --git a/source3/modules/vfs_aio_pthread.c 
b/source3/modules/vfs_aio_pthread.c
index 6724e73984c..685f91d9f2b 100644
--- a/source3/modules/vfs_aio_pthread.c
+++ b/source3/modules/vfs_aio_pthread.c
@@ -464,6 +464,13 @@ static int aio_pthread_openat_fn(vfs_handle_struct *handle,
                return -1;
        }
 
+       if (fsp->conn->sconn->client->server_multi_channel_enabled) {
+               /*
+                * This module is not compatible with multi channel yet.
+                */
+               aio_allow_open = false;
+       }
+
        if (!aio_allow_open) {
                /* aio opens turned off. */
                return openat(fsp_get_io_fd(dirfsp),
diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h
index 0cab0d7396c..f49096cba8a 100644
--- a/source3/smbd/globals.h
+++ b/source3/smbd/globals.h
@@ -368,6 +368,7 @@ struct smbXsrv_connection {
        struct {
                NTSTATUS status;
                bool terminating;
+               struct tevent_queue *shutdown_wait_queue;
                int sock;
                struct tevent_fd *fde;
 
@@ -672,8 +673,12 @@ NTSTATUS smb2srv_open_lookup(struct smbXsrv_connection 
*conn,
                             uint64_t volatile_id,
                             NTTIME now,
                             struct smbXsrv_open **_open);
+NTSTATUS smbXsrv_open_purge_replay_cache(struct smbXsrv_client *client,
+                                        const struct GUID *create_guid);
 NTSTATUS smb2srv_open_lookup_replay_cache(struct smbXsrv_connection *conn,
-                                         const struct GUID *create_guid,
+                                         struct GUID caller_req_guid,
+                                         struct GUID create_guid,
+                                         const char *name,
                                          NTTIME now,
                                          struct smbXsrv_open **_open);
 NTSTATUS smb2srv_open_recreate(struct smbXsrv_connection *conn,
diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c
index 8ff57c94aa0..dc6bd72591e 100644
--- a/source3/smbd/smb2_create.c
+++ b/source3/smbd/smb2_create.c
@@ -485,6 +485,7 @@ static NTSTATUS smbd_smb2_create_durable_lease_check(struct 
smb_request *smb1req
 struct smbd_smb2_create_state {
        struct tevent_context *ev;
        struct smbd_smb2_request *smb2req;
+       struct GUID req_guid;
        struct smb_request *smb1req;
        bool open_was_deferred;
        struct tevent_immediate *im;
@@ -504,6 +505,8 @@ struct smbd_smb2_create_state {
        uint64_t allocation_size;
        struct GUID _create_guid;
        struct GUID *create_guid;
+       struct GUID _purge_create_guid;
+       struct GUID *purge_create_guid;
        bool update_open;
        bool durable_requested;
        uint32_t durable_timeout_msec;
@@ -543,6 +546,15 @@ struct smbd_smb2_create_state {
        struct smb2_create_blobs *out_context_blobs;
 };
 
+static void smbd_smb2_create_purge_replay_cache(struct tevent_req *req,
+                                               const char *caller_func);
+
+static void smbd_smb2_create_cleanup(struct tevent_req *req,
+                                    enum tevent_req_state req_state)
+{
+       smbd_smb2_create_purge_replay_cache(req, __func__);
+}
+
 static NTSTATUS smbd_smb2_create_fetch_create_ctx(
        struct tevent_req *req,
        struct smb2_create_blobs *in_context_blobs)
@@ -709,6 +721,10 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX 
*mem_ctx,
        }
        state->smb1req = smb1req;
 
+       state->req_guid = smbd_request_guid(smb1req, 0);
+
+       tevent_req_set_cleanup_fn(req, smbd_smb2_create_cleanup);
+
        if (smb2req->subreq == NULL) {
                DBG_DEBUG("name [%s]\n", in_name);
        } else {
@@ -721,6 +737,9 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX 
*mem_ctx,
                state->request_time = old_state->request_time;
                state->open_rec = talloc_move(state, &old_state->open_rec);
                state->open_was_deferred = old_state->open_was_deferred;
+               state->_purge_create_guid = old_state->_purge_create_guid;
+               state->purge_create_guid = old_state->purge_create_guid;
+               old_state->purge_create_guid = NULL;
        }
 
        TALLOC_FREE(smb2req->subreq);
@@ -1026,6 +1045,31 @@ static struct tevent_req 
*smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
        return req;
 }
 
+static void smbd_smb2_create_purge_replay_cache(struct tevent_req *req,
+                                               const char *caller_func)
+{
+       struct smbd_smb2_create_state *state = tevent_req_data(
+               req, struct smbd_smb2_create_state);
+       NTSTATUS status;
+
+       if (state->purge_create_guid == NULL) {
+               return;
+       }
+
+       status = smbXsrv_open_purge_replay_cache(state->smb2req->xconn->client,
+                                                state->purge_create_guid);
+       if (!NT_STATUS_IS_OK(status)) {
+               struct GUID_txt_buf buf;
+
+               D_ERR("%s: smbXsrv_open_purge_replay_cache(%s) %s\n",
+                     caller_func,
+                     GUID_buf_string(state->purge_create_guid, &buf),
+                     nt_errstr(status));
+       }
+
+       state->purge_create_guid = NULL;
+}
+
 static void smbd_smb2_create_before_exec(struct tevent_req *req)
 {
        struct smbd_smb2_create_state *state = tevent_req_data(
@@ -1117,6 +1161,7 @@ static void smbd_smb2_create_before_exec(struct 
tevent_req *req)
 
        if (state->dh2q != NULL) {
                const uint8_t *p = state->dh2q->data.data;
+               NTTIME now = timeval_to_nttime(&smb2req->request_time);
                uint32_t durable_v2_timeout = 0;
                DATA_BLOB create_guid_blob;
                const uint8_t *hdr;
@@ -1179,14 +1224,33 @@ static void smbd_smb2_create_before_exec(struct 
tevent_req *req)
                        flags & SMB2_HDR_FLAG_REPLAY_OPERATION;
 
                status = smb2srv_open_lookup_replay_cache(smb2req->xconn,
-                                                         state->create_guid,
-                                                         0 /* now */,
+                                                         state->req_guid,
+                                                         *state->create_guid,
+                                                         state->fname,
+                                                         now,
                                                          &state->op);
-               if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               if (NT_STATUS_EQUAL(status, NT_STATUS_FWP_RESERVED)) {
+                       /*
+                        * We've reserved the replay_cache record
+                        * for ourself, indicating we're still
+                        * in progress.
+                        *
+                        * It means the smbd_smb2_create_cleanup()
+                        * may need to call smbXsrv_open_purge_replay_cache()
+                        * in order to cleanup.
+                        */
+                       SMB_ASSERT(state->op == NULL);
+                       state->_purge_create_guid = state->_create_guid;
+                       state->purge_create_guid = &state->_purge_create_guid;
+                       status = NT_STATUS_OK;
                        state->replay_operation = false;
-               } else if (tevent_req_nterror(req, status)) {
+               } else if (NT_STATUS_EQUAL(status, 
NT_STATUS_FILE_NOT_AVAILABLE)) {
+                       tevent_req_nterror(req, status);
+                       return;
+               } else if (!NT_STATUS_IS_OK(status)) {
                        DBG_WARNING("smb2srv_open_lookup_replay_cache "
                                    "failed: %s\n", nt_errstr(status));
+                       tevent_req_nterror(req, status);
                        return;
                } else if (!state->replay_operation) {
                        /*
@@ -1388,6 +1452,12 @@ static void smbd_smb2_create_after_exec(struct 
tevent_req *req)
                        tevent_req_post(req, state->ev);
                        return;
                }
+
+               /*
+                * We should not purge the replay cache anymore
+                * as it's attached to the smbXsrv_open record now.
+                */
+               state->purge_create_guid = NULL;
        }
 
        if (state->dhnq != NULL && state->op->global->durable) {
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index d9de7f4ab06..d5b0df33dde 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -1498,7 +1498,6 @@ size_t smbXsrv_client_valid_connections(struct 
smbXsrv_client *client)
 }
 
 struct smbXsrv_connection_shutdown_state {
-       struct tevent_queue *wait_queue;
        struct smbXsrv_connection *xconn;
 };
 
@@ -1521,6 +1520,7 @@ static struct tevent_req 
*smbXsrv_connection_shutdown_send(TALLOC_CTX *mem_ctx,
         */
        SMB_ASSERT(!NT_STATUS_IS_OK(xconn->transport.status));
        SMB_ASSERT(xconn->transport.terminating);
+       SMB_ASSERT(xconn->transport.shutdown_wait_queue == NULL);
 
        req = tevent_req_create(mem_ctx, &state,
                                struct smbXsrv_connection_shutdown_state);
@@ -1531,45 +1531,48 @@ static struct tevent_req 
*smbXsrv_connection_shutdown_send(TALLOC_CTX *mem_ctx,
        state->xconn = xconn;
        tevent_req_defer_callback(req, ev);
 
-       status = smbXsrv_session_disconnect_xconn(xconn);
-       if (tevent_req_nterror(req, status)) {
-               return tevent_req_post(req, ev);
-       }
-
-       state->wait_queue = tevent_queue_create(state, 
"smbXsrv_connection_shutdown_queue");
-       if (tevent_req_nomem(state->wait_queue, req)) {
+       xconn->transport.shutdown_wait_queue =
+               tevent_queue_create(state, "smbXsrv_connection_shutdown_queue");
+       if (tevent_req_nomem(xconn->transport.shutdown_wait_queue, req)) {
                return tevent_req_post(req, ev);
        }
 
        for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) {
-               /*
-                * The connection is gone so we
-                * don't need to take care of
-                * any crypto
-                */
-               preq->session = NULL;
-               preq->do_signing = false;
-               preq->do_encryption = false;
-               preq->preauth = NULL;
-
-               if (preq->subreq != NULL) {
-                       tevent_req_cancel(preq->subreq);
-               }
-
                /*
                 * Now wait until the request is finished.
                 *
                 * We don't set a callback, as we just want to block the
                 * wait queue and the talloc_free() of the request will
                 * remove the item from the wait queue.
+                *
+                * Note that we don't cancel the requests here
+                * in order to keep the replay detection logic correct.
+                *
+                * However if we teardown the last channel of
+                * a connection, we'll call some logic via
+                * smbXsrv_session_disconnect_xconn()
+                * -> smbXsrv_session_disconnect_xconn_callback()
+                *   -> smbXsrv_session_remove_channel()
+                *     -> smb2srv_session_shutdown_send()
+                * will indeed cancel the request.
                 */
-               subreq = tevent_queue_wait_send(preq, ev, state->wait_queue);
+               subreq = tevent_queue_wait_send(preq, ev,
+                                       xconn->transport.shutdown_wait_queue);
                if (tevent_req_nomem(subreq, req)) {
                        return tevent_req_post(req, ev);
                }
        }
 
-       len = tevent_queue_length(state->wait_queue);
+       /*
+        * This may attach sessions with num_channels == 0
+        * to xconn->transport.shutdown_wait_queue.
+        */
+       status = smbXsrv_session_disconnect_xconn(xconn);
+       if (tevent_req_nterror(req, status)) {
+               return tevent_req_post(req, ev);
+       }
+
+       len = tevent_queue_length(xconn->transport.shutdown_wait_queue);
        if (len == 0) {
                tevent_req_done(req);
                return tevent_req_post(req, ev);
@@ -1580,7 +1583,7 @@ static struct tevent_req 
*smbXsrv_connection_shutdown_send(TALLOC_CTX *mem_ctx,
         * this way we get notified when all pending requests are finished
         * and send to the socket.
         */
-       subreq = tevent_queue_wait_send(state, ev, state->wait_queue);
+       subreq = tevent_queue_wait_send(state, ev, 
xconn->transport.shutdown_wait_queue);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -4604,6 +4607,18 @@ static NTSTATUS smbd_smb2_flush_send_queue(struct 
smbXsrv_connection *xconn)
                bool ok;
                struct msghdr msg;
 
+               if (!NT_STATUS_IS_OK(xconn->transport.status)) {
+                       /*
+                        * we're not supposed to do any io
+                        * just flush all pending stuff.
+                        */
+                       xconn->smb2.send_queue_len--;
+                       DLIST_REMOVE(xconn->smb2.send_queue, e);
+
+                       talloc_free(e->mem_ctx);
+                       continue;
+               }
+
                if (e->sendfile_header != NULL) {
                        size_t size = 0;
                        size_t i = 0;
diff --git a/source3/smbd/smb2_tcon.c b/source3/smbd/smb2_tcon.c
index d7e0cf90f47..14229366efa 100644
--- a/source3/smbd/smb2_tcon.c
+++ b/source3/smbd/smb2_tcon.c
@@ -612,12 +612,7 @@ static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX 
*mem_ctx,
                                continue;
                        }
 
-                       /*
-                        * Never cancel anything in a compound
-                        * request. Way too hard to deal with
-                        * the result.
-                        */
-                       if (!preq->compound_related && preq->subreq != NULL) {
+                       if (preq->subreq != NULL) {
                                tevent_req_cancel(preq->subreq);
                        }
 
diff --git a/source3/smbd/smbXsrv_open.c b/source3/smbd/smbXsrv_open.c
index 5180315449d..b007d61410e 100644
--- a/source3/smbd/smbXsrv_open.c
+++ b/source3/smbd/smbXsrv_open.c
@@ -928,7 +928,16 @@ static NTSTATUS smbXsrv_open_set_replay_cache(struct 
smbXsrv_open *op)
        struct GUID_txt_buf buf;
        char *guid_string;
        struct db_context *db = op->table->local.replay_cache_db_ctx;
+       struct smbXsrv_open_replay_cache rc = {
+               .idle_time = op->idle_time,
+               .local_id = op->local_id,
+       };
+       uint8_t data[SMBXSRV_OPEN_REPLAY_CACHE_FIXED_SIZE];
+       DATA_BLOB blob = data_blob_const(data, ARRAY_SIZE(data));
+       enum ndr_err_code ndr_err;
        NTSTATUS status;
+       TDB_DATA key;
+       TDB_DATA val;
 
        if (!(op->flags & SMBXSRV_OPEN_NEED_REPLAY_CACHE)) {
                return NT_STATUS_OK;
@@ -939,16 +948,18 @@ static NTSTATUS smbXsrv_open_set_replay_cache(struct 
smbXsrv_open *op)
        }
 
        create_guid = &op->global->create_guid;
-       if (GUID_all_zero(create_guid)) {
-               return NT_STATUS_OK;
-       }
-
        guid_string = GUID_buf_string(create_guid, &buf);
-       if (guid_string == NULL) {
-               return NT_STATUS_INVALID_PARAMETER;
+       key = string_term_tdb_data(guid_string);
+
+       ndr_err = ndr_push_struct_into_fixed_blob(&blob, &rc,
+               (ndr_push_flags_fn_t)ndr_push_smbXsrv_open_replay_cache);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               status = ndr_map_error2ntstatus(ndr_err);
+               return status;
        }
+       val = make_tdb_data(blob.data, blob.length);
 
-       status = dbwrap_store_uint32_bystring(db, guid_string, op->local_id);
+       status = dbwrap_store(db, key, val, TDB_REPLACE);
 
        if (NT_STATUS_IS_OK(status)) {
                op->flags |= SMBXSRV_OPEN_HAVE_REPLAY_CACHE;
@@ -958,6 +969,27 @@ static NTSTATUS smbXsrv_open_set_replay_cache(struct 
smbXsrv_open *op)
        return status;
 }
 
+NTSTATUS smbXsrv_open_purge_replay_cache(struct smbXsrv_client *client,
+                                        const struct GUID *create_guid)
+{
+       struct GUID_txt_buf buf;
+       char *guid_string;
+       struct db_context *db;
+
+       if (client->open_table == NULL) {
+               return NT_STATUS_OK;
+       }
+
+       db = client->open_table->local.replay_cache_db_ctx;
+
+       guid_string = GUID_buf_string(create_guid, &buf);
+       if (guid_string == NULL) {
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       return dbwrap_purge_bystring(db, guid_string);
+}
+
 static NTSTATUS smbXsrv_open_clear_replay_cache(struct smbXsrv_open *op)
 {
        struct GUID *create_guid;
@@ -1269,44 +1301,189 @@ NTSTATUS smb2srv_open_lookup(struct smbXsrv_connection 
*conn,
        return status;
 }
 
+/*
+ * This checks or marks the replay cache, we have the following
+ * cases:
+ *
+ * 1. There is no record in the cache
+ *    => we add the passes caller_req_guid as holder_req_guid
+ *       together with local_id as 0.
+ *    => We return STATUS_FWP_RESERVED in order to indicate
+ *       that the caller holds the current reservation
+ *
+ * 2. There is a record in the cache and holder_req_guid
+ *    is already the same as caller_req_guid and local_id is 0
+ *    => We return STATUS_FWP_RESERVED in order to indicate
+ *       that the caller holds the current reservation
+ *
+ * 3. There is a record in the cache with a holder_req_guid
+ *    other than caller_req_guid (and local_id is 0):
+ *    => We return NT_STATUS_FILE_NOT_AVAILABLE to indicate
+ *       the original request is still pending
+ *
+ * 4. There is a record in the cache with a zero holder_req_guid
+ *    and a valid local_id:
+ *    => We lookup the existing open by local_id
+ *    => We return NT_STATUS_OK together with the smbXsrv_open
+ *
+ *
+ * With NT_STATUS_OK the caller can continue the replay processing.
+ *
+ * With STATUS_FWP_RESERVED the caller should continue the normal
+ * open processing:
+ * - On success:
+ *   - smbXsrv_open_update()/smbXsrv_open_set_replay_cache()
+ *     will convert the record to a zero holder_req_guid
+ *     with a valid local_id.
+ * - On failure:
+ *   - smbXsrv_open_purge_replay_cache() should cleanup
+ *     the reservation.
+ *
+ * All other values should be returned to the client,


-- 
Samba Shared Repository

Reply via email to