diff options
author | Volker Lendecke <vl@samba.org> | 2011-12-14 16:54:07 +0100 |
---|---|---|
committer | Volker Lendecke <vlendec@samba.org> | 2011-12-14 19:08:30 +0100 |
commit | 1bea57a6c35ed92f63a822accf15e4ace79ef60c (patch) | |
tree | 2b49d32579c8410ccd0fcc59f6027c1c79f02105 | |
parent | c889c8d7055102276bc10ae00c60f7ebc5a293c3 (diff) | |
download | samba-1bea57a6c35ed92f63a822accf15e4ace79ef60c.tar.gz samba-1bea57a6c35ed92f63a822accf15e4ace79ef60c.tar.bz2 samba-1bea57a6c35ed92f63a822accf15e4ace79ef60c.zip |
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 <vlendec@samba.org>
Autobuild-Date: Wed Dec 14 19:08:30 CET 2011 on sn-devel-104
-rw-r--r-- | source3/smbd/close.c | 60 |
1 files changed, 55 insertions, 5 deletions
diff --git a/source3/smbd/close.c b/source3/smbd/close.c index 9eedaf9e66..26656f47a2 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; i<lck->num_share_modes; i++) { + num_deferred = 0; + for (i=0; i<lck->num_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; i<lck->num_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; i<num_deferred; i++) { + struct share_mode_entry *e = &deferred[i]; if (procid_is_me(&e->pid)) { /* @@ -195,6 +244,7 @@ static void notify_deferred_opens(struct smbd_server_connection *sconn, MSG_SMB_SHARE_MODE_ENTRY_SIZE); } } + TALLOC_FREE(deferred); } /**************************************************************************** |