s3: Fix raw.mux after UNUSED_SHARE_MODE_ENTRY was removed
authorVolker Lendecke <vl@samba.org>
Wed, 14 Dec 2011 15:54:07 +0000 (16:54 +0100)
committerVolker Lendecke <vlendec@samba.org>
Wed, 14 Dec 2011 18:08:30 +0000 (19:08 +0100)
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

source3/smbd/close.c

index 9eedaf9e66bdd2ad81d0a6f506cef45200a5534b..26656f47a2725447ddda09690303f15228c9f418 100644 (file)
@@ -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);
 }
 
 /****************************************************************************