From 1bea57a6c35ed92f63a822accf15e4ace79ef60c Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Wed, 14 Dec 2011 16:54:07 +0100 Subject: [PATCH] s3: Fix raw.mux after UNUSED_SHARE_MODE_ENTRY was removed See the large comment in notify_deferred_opens for an explanation Autobuild-User: Volker Lendecke Autobuild-Date: Wed Dec 14 19:08:30 CET 2011 on sn-devel-104 --- source3/smbd/close.c | 60 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/source3/smbd/close.c b/source3/smbd/close.c index 9eedaf9e66b..26656f47a27 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -156,6 +156,13 @@ static NTSTATUS close_filestruct(files_struct *fsp) return status; } +static int compare_share_mode_times(const void *p1, const void *p2) +{ + struct share_mode_entry *s1 = (struct share_mode_entry *)p1; + struct share_mode_entry *s2 = (struct share_mode_entry *)p2; + return timeval_compare(&s1->time, &s2->time); +} + /**************************************************************************** If any deferred opens are waiting on this close, notify them. ****************************************************************************/ @@ -163,18 +170,60 @@ static NTSTATUS close_filestruct(files_struct *fsp) static void notify_deferred_opens(struct smbd_server_connection *sconn, struct share_mode_lock *lck) { - int i; + uint32_t i, num_deferred; + struct share_mode_entry *deferred; if (!should_notify_deferred_opens()) { return; } - for (i=0; inum_share_modes; i++) { + num_deferred = 0; + for (i=0; inum_share_modes; i++) { + if (is_deferred_open_entry(&lck->share_modes[i])) { + num_deferred += 1; + } + } + if (num_deferred == 0) { + return; + } + + deferred = talloc_array(talloc_tos(), struct share_mode_entry, + num_deferred); + if (deferred == NULL) { + return; + } + + num_deferred = 0; + for (i=0; inum_share_modes; i++) { struct share_mode_entry *e = &lck->share_modes[i]; + if (is_deferred_open_entry(e)) { + deferred[num_deferred] = *e; + num_deferred += 1; + } + } - if (!is_deferred_open_entry(e)) { - continue; - } + /* + * We need to sort the notifications by initial request time. Imagine + * two opens come in asyncronously, both conflicting with the open we + * just close here. If we don't sort the notifications, the one that + * came in last might get the response before the one that came in + * first. This is demonstrated with the smbtorture4 raw.mux test. + * + * As long as we had the UNUSED_SHARE_MODE_ENTRY, we happened to + * survive this particular test. Without UNUSED_SHARE_MODE_ENTRY, we + * shuffle the share mode entries around a bit, so that we do not + * survive raw.mux anymore. + * + * We could have kept the ordering in del_share_mode, but as the + * ordering was never formalized I think it is better to do it here + * where it is necessary. + */ + + qsort(deferred, num_deferred, sizeof(struct share_mode_entry), + compare_share_mode_times); + + for (i=0; ipid)) { /* @@ -195,6 +244,7 @@ static void notify_deferred_opens(struct smbd_server_connection *sconn, MSG_SMB_SHARE_MODE_ENTRY_SIZE); } } + TALLOC_FREE(deferred); } /**************************************************************************** -- 2.34.1