Windows: AFSFindObjectInfo update last access time
[openafs.git] / src / WINNT / afsrdr / kernel / lib / AFSWorker.cpp
index 639da47..32b2bd9 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kernel Drivers, LLC.
- * Copyright (c) 2009, 2010, 2011 Your File System, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Your File System, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * - Redistributions of source code must retain the above copyright notice,
  *   this list of conditions and the following disclaimer.
  * - Redistributions in binary form must reproduce the above copyright
- *   notice,
- *   this list of conditions and the following disclaimer in the
- *   documentation
- *   and/or other materials provided with the distribution.
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
  * - Neither the names of Kernel Drivers, LLC and Your File System, Inc.
  *   nor the names of their contributors may be used to endorse or promote
  *   products derived from this software without specific prior written
 
 #include "AFSCommon.h"
 
+static
+VOID
+AFSPostedDeferredWrite( IN PVOID Context1,
+                        IN PVOID Context2);
+
 //
 // Function: AFSInitializeWorkerPool
 //
@@ -67,18 +70,6 @@ AFSInitializeWorkerPool()
         // Initialize the worker threads.
         //
 
-        pDevExt->Specific.Library.WorkerCount = 0;
-
-        KeInitializeEvent( &pDevExt->Specific.Library.WorkerQueueHasItems,
-                           SynchronizationEvent,
-                           FALSE);
-
-        //
-        // Initialize the queue resource
-        //
-
-        ExInitializeResourceLite( &pDevExt->Specific.Library.QueueLock);
-
         while( pDevExt->Specific.Library.WorkerCount < AFS_WORKER_COUNT)
         {
 
@@ -89,9 +80,9 @@ AFSInitializeWorkerPool()
             if( pCurrentWorker == NULL)
             {
 
-                AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+                AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                               AFS_TRACE_LEVEL_ERROR,
-                              "AFSInitializeWorkerPool Failed to allocate worker context\n");
+                              "AFSInitializeWorkerPool Failed to allocate worker context\n"));
 
                 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
 
@@ -107,9 +98,10 @@ AFSInitializeWorkerPool()
             if( !NT_SUCCESS( ntStatus))
             {
 
-                AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+                AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                               AFS_TRACE_LEVEL_ERROR,
-                              "AFSInitializeWorkerPool Failed to initialize worker thread Status %08lX\n", ntStatus);
+                              "AFSInitializeWorkerPool Failed to initialize worker thread Status %08lX\n",
+                              ntStatus));
 
                 ExFreePool( pCurrentWorker);
 
@@ -149,18 +141,6 @@ AFSInitializeWorkerPool()
         // Now our IO Worker queue
         //
 
-        pDevExt->Specific.Library.IOWorkerCount = 0;
-
-        KeInitializeEvent( &pDevExt->Specific.Library.IOWorkerQueueHasItems,
-                           SynchronizationEvent,
-                           FALSE);
-
-        //
-        // Initialize the queue resource
-        //
-
-        ExInitializeResourceLite( &pDevExt->Specific.Library.IOQueueLock);
-
         while( pDevExt->Specific.Library.IOWorkerCount < AFS_IO_WORKER_COUNT)
         {
 
@@ -171,9 +151,9 @@ AFSInitializeWorkerPool()
             if( pCurrentWorker == NULL)
             {
 
-                AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+                AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                               AFS_TRACE_LEVEL_ERROR,
-                              "AFSInitializeWorkerPool Failed to allocate IO worker context\n");
+                              "AFSInitializeWorkerPool Failed to allocate IO worker context\n"));
 
                 ntStatus = STATUS_INSUFFICIENT_RESOURCES;
 
@@ -189,9 +169,10 @@ AFSInitializeWorkerPool()
             if( !NT_SUCCESS( ntStatus))
             {
 
-                AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+                AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                               AFS_TRACE_LEVEL_ERROR,
-                              "AFSInitializeWorkerPool Failed to initialize IO worker thread Status %08lX\n", ntStatus);
+                              "AFSInitializeWorkerPool Failed to initialize IO worker thread Status %08lX\n",
+                              ntStatus));
 
                 ExFreePool( pCurrentWorker);
 
@@ -315,12 +296,10 @@ AFSRemoveWorkerPool()
 
     pDevExt->Specific.Library.PoolHead = NULL;
 
-    ExDeleteResourceLite( &pDevExt->Specific.Library.QueueLock);
-
     //
     // Loop through the IO workers shutting them down in two stages.
     // First, clear AFS_WORKER_PROCESS_REQUESTS so that workers
-    // stop processing requests.  Second, call AFSShutdownWorkerThread()
+    // stop processing requests.  Second, call AFSShutdownIOWorkerThread()
     // to wake the workers and wait for them to exit.
     //
 
@@ -370,8 +349,6 @@ AFSRemoveWorkerPool()
 
     pDevExt->Specific.Library.IOPoolHead = NULL;
 
-    ExDeleteResourceLite( &pDevExt->Specific.Library.IOQueueLock);
-
     return ntStatus;
 }
 
@@ -380,7 +357,7 @@ AFSInitVolumeWorker( IN AFSVolumeCB *VolumeCB)
 {
 
     NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSWorkQueueContext *pWorker = &VolumeCB->VolumeWorkerContext;
+    AFSWorkQueueContext *pWorker = &VolumeCB->NonPagedVcb->VolumeWorkerContext;
     HANDLE hThread;
     AFSDeviceExt *pControlDeviceExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
     PKSTART_ROUTINE pStartRoutine = NULL;
@@ -389,16 +366,13 @@ AFSInitVolumeWorker( IN AFSVolumeCB *VolumeCB)
     __Enter
     {
 
-        if( VolumeCB == AFSGlobalRoot)
+        if ( VolumeCB != AFSGlobalRoot)
         {
 
-            pStartRoutine = AFSPrimaryVolumeWorkerThread;
+            return STATUS_INVALID_PARAMETER;
         }
-        else
-        {
 
-            pStartRoutine = AFSVolumeWorkerThread;
-        }
+        pStartRoutine = AFSPrimaryVolumeWorkerThread;
 
         //
         // Initialize the worker thread
@@ -530,23 +504,25 @@ AFSShutdownVolumeWorker( IN AFSVolumeCB *VolumeCB)
 {
 
     NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSWorkQueueContext *pWorker = &VolumeCB->VolumeWorkerContext;
+    AFSWorkQueueContext *pWorker = &VolumeCB->NonPagedVcb->VolumeWorkerContext;
 
-    if( pWorker->WorkerThreadObject != NULL &&
-        BooleanFlagOn( pWorker->State, AFS_WORKER_INITIALIZED))
-    {
+    //
+    // Clear the 'keep processing' flag
+    //
 
-        //
-        // Clear the 'keep processing' flag
-        //
+    ClearFlag( pWorker->State, AFS_WORKER_PROCESS_REQUESTS);
 
-        ClearFlag( pWorker->State, AFS_WORKER_PROCESS_REQUESTS);
+    if( pWorker->WorkerThreadObject != NULL)
+    {
+        while ( BooleanFlagOn( pWorker->State, AFS_WORKER_INITIALIZED) )
+        {
 
-        ntStatus = KeWaitForSingleObject( pWorker->WorkerThreadObject,
-                                          Executive,
-                                          KernelMode,
-                                          FALSE,
-                                          NULL);
+            ntStatus = KeWaitForSingleObject( pWorker->WorkerThreadObject,
+                                              Executive,
+                                              KernelMode,
+                                              FALSE,
+                                              NULL);
+        }
 
         ObDereferenceObject( pWorker->WorkerThreadObject);
 
@@ -573,25 +549,28 @@ AFSShutdownWorkerThread( IN AFSWorkQueueContext *PoolContext)
 {
 
     NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSDeviceExt *pDeviceExt = (AFSDeviceExt *)AFSLibraryDeviceObject->DeviceExtension;
+    AFSDeviceExt *pControlDeviceExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
 
-    if( PoolContext->WorkerThreadObject != NULL &&
-        BooleanFlagOn( PoolContext->State, AFS_WORKER_INITIALIZED))
+    if( PoolContext->WorkerThreadObject != NULL)
     {
 
-        //
-        // Wake up the thread if it is a sleep
-        //
+        while ( BooleanFlagOn( PoolContext->State, AFS_WORKER_INITIALIZED) )
+        {
 
-        KeSetEvent( &pDeviceExt->Specific.Library.WorkerQueueHasItems,
-                    0,
-                    FALSE);
+            //
+            // Wake up the thread if it is a sleep
+            //
 
-        ntStatus = KeWaitForSingleObject( PoolContext->WorkerThreadObject,
-                                          Executive,
-                                          KernelMode,
-                                          FALSE,
-                                          NULL);
+            KeSetEvent( &pControlDeviceExt->Specific.Control.WorkerQueueHasItems,
+                        0,
+                        FALSE);
+
+            ntStatus = KeWaitForSingleObject( PoolContext->WorkerThreadObject,
+                                              Executive,
+                                              KernelMode,
+                                              FALSE,
+                                              NULL);
+        }
 
         ObDereferenceObject( PoolContext->WorkerThreadObject);
 
@@ -618,25 +597,28 @@ AFSShutdownIOWorkerThread( IN AFSWorkQueueContext *PoolContext)
 {
 
     NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSDeviceExt *pDeviceExt = (AFSDeviceExt *)AFSLibraryDeviceObject->DeviceExtension;
+    AFSDeviceExt *pControlDeviceExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
 
-    if( PoolContext->WorkerThreadObject != NULL &&
-        BooleanFlagOn( PoolContext->State, AFS_WORKER_INITIALIZED))
+    if( PoolContext->WorkerThreadObject != NULL)
     {
 
-        //
-        // Wake up the thread if it is a sleep
-        //
+        while ( BooleanFlagOn( PoolContext->State, AFS_WORKER_INITIALIZED) )
+        {
 
-        KeSetEvent( &pDeviceExt->Specific.Library.IOWorkerQueueHasItems,
-                    0,
-                    FALSE);
+            //
+            // Wake up the thread if it is a sleep
+            //
 
-        ntStatus = KeWaitForSingleObject( PoolContext->WorkerThreadObject,
-                                          Executive,
-                                          KernelMode,
-                                          FALSE,
-                                          NULL);
+            KeSetEvent( &pControlDeviceExt->Specific.Control.IOWorkerQueueHasItems,
+                        0,
+                        FALSE);
+
+            ntStatus = KeWaitForSingleObject( PoolContext->WorkerThreadObject,
+                                              Executive,
+                                              KernelMode,
+                                              FALSE,
+                                              NULL);
+        }
 
         ObDereferenceObject( PoolContext->WorkerThreadObject);
 
@@ -666,10 +648,10 @@ AFSWorkerThread( IN PVOID Context)
     AFSWorkQueueContext *pPoolContext = (AFSWorkQueueContext *)Context;
     AFSWorkItem *pWorkItem;
     BOOLEAN freeWorkItem = TRUE;
-    AFSDeviceExt *pLibraryDevExt = NULL;
+    AFSDeviceExt *pControlDevExt = NULL;
     LONG lCount;
 
-    pLibraryDevExt = (AFSDeviceExt *)AFSLibraryDeviceObject->DeviceExtension;
+    pControlDevExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
 
     //
     // Indicate that we are initialized and ready
@@ -685,7 +667,7 @@ AFSWorkerThread( IN PVOID Context)
 
     SetFlag( pPoolContext->State, AFS_WORKER_INITIALIZED);
 
-    ntStatus = KeWaitForSingleObject( &pLibraryDevExt->Specific.Library.WorkerQueueHasItems,
+    ntStatus = KeWaitForSingleObject( &pControlDevExt->Specific.Control.WorkerQueueHasItems,
                                       Executive,
                                       KernelMode,
                                       FALSE,
@@ -697,9 +679,10 @@ AFSWorkerThread( IN PVOID Context)
         if( !NT_SUCCESS( ntStatus))
         {
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                           AFS_TRACE_LEVEL_ERROR,
-                          "AFSWorkerThread Wait for queue items failed Status %08lX\n", ntStatus);
+                          "AFSWorkerThread Wait for queue items failed Status %08lX\n",
+                          ntStatus));
 
             ntStatus = STATUS_SUCCESS;
         }
@@ -711,7 +694,7 @@ AFSWorkerThread( IN PVOID Context)
             if( pWorkItem == NULL)
             {
 
-                ntStatus = KeWaitForSingleObject( &pLibraryDevExt->Specific.Library.WorkerQueueHasItems,
+                ntStatus = KeWaitForSingleObject( &pControlDevExt->Specific.Control.WorkerQueueHasItems,
                                                   Executive,
                                                   KernelMode,
                                                   FALSE,
@@ -779,9 +762,10 @@ AFSWorkerThread( IN PVOID Context)
 
                     default:
 
-                        AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+                        AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                                       AFS_TRACE_LEVEL_ERROR,
-                                      "AFSWorkerThread Unknown request type %d\n", pWorkItem->RequestType);
+                                      "AFSWorkerThread Unknown request type %d\n",
+                                      pWorkItem->RequestType));
 
                         break;
                 }
@@ -801,7 +785,7 @@ AFSWorkerThread( IN PVOID Context)
 
     // Wake up another worker so they too can exit
 
-    KeSetEvent( &pLibraryDevExt->Specific.Library.WorkerQueueHasItems,
+    KeSetEvent( &pControlDevExt->Specific.Control.WorkerQueueHasItems,
                 0,
                 FALSE);
 
@@ -818,9 +802,9 @@ AFSIOWorkerThread( IN PVOID Context)
     AFSWorkQueueContext *pPoolContext = (AFSWorkQueueContext *)Context;
     AFSWorkItem *pWorkItem;
     BOOLEAN freeWorkItem = TRUE;
-    AFSDeviceExt *pLibraryDevExt = NULL, *pRdrDevExt = NULL;
+    AFSDeviceExt *pControlDevExt = NULL, *pRdrDevExt = NULL;
 
-    pLibraryDevExt = (AFSDeviceExt *)AFSLibraryDeviceObject->DeviceExtension;
+    pControlDevExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
 
     //
     // Indicate that we are initialized and ready
@@ -837,7 +821,7 @@ AFSIOWorkerThread( IN PVOID Context)
 
     SetFlag( pPoolContext->State, AFS_WORKER_INITIALIZED);
 
-    ntStatus = KeWaitForSingleObject( &pLibraryDevExt->Specific.Library.IOWorkerQueueHasItems,
+    ntStatus = KeWaitForSingleObject( &pControlDevExt->Specific.Control.IOWorkerQueueHasItems,
                                       Executive,
                                       KernelMode,
                                       FALSE,
@@ -849,9 +833,10 @@ AFSIOWorkerThread( IN PVOID Context)
         if( !NT_SUCCESS( ntStatus))
         {
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                           AFS_TRACE_LEVEL_ERROR,
-                          "AFSIOWorkerThread Wait for queue items failed Status %08lX\n", ntStatus);
+                          "AFSIOWorkerThread Wait for queue items failed Status %08lX\n",
+                          ntStatus));
 
             ntStatus = STATUS_SUCCESS;
         }
@@ -863,7 +848,7 @@ AFSIOWorkerThread( IN PVOID Context)
             if( pWorkItem == NULL)
             {
 
-                ntStatus = KeWaitForSingleObject( &pLibraryDevExt->Specific.Library.IOWorkerQueueHasItems,
+                ntStatus = KeWaitForSingleObject( &pControlDevExt->Specific.Control.IOWorkerQueueHasItems,
                                                   Executive,
                                                   KernelMode,
                                                   FALSE,
@@ -911,11 +896,25 @@ AFSIOWorkerThread( IN PVOID Context)
                         break;
                     }
 
+                    case AFS_WORK_DEFERRED_WRITE:
+                    {
+
+                        ntStatus = AFSCommonWrite( pWorkItem->Specific.AsynchIo.Device,
+                                                   pWorkItem->Specific.AsynchIo.Irp,
+                                                   pWorkItem->Specific.AsynchIo.CallingProcess,
+                                                   TRUE);
+
+                        freeWorkItem = TRUE;
+
+                        break;
+                    }
+
                     default:
 
-                        AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+                        AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                                       AFS_TRACE_LEVEL_ERROR,
-                                      "AFSWorkerThread Unknown request type %d\n", pWorkItem->RequestType);
+                                      "AFSIOWorkerThread Unknown request type %d\n",
+                                      pWorkItem->RequestType));
 
                         break;
                 }
@@ -935,7 +934,7 @@ AFSIOWorkerThread( IN PVOID Context)
 
     // Wake up another IOWorker so they too can exit
 
-    KeSetEvent( &pLibraryDevExt->Specific.Library.IOWorkerQueueHasItems,
+    KeSetEvent( &pControlDevExt->Specific.Control.IOWorkerQueueHasItems,
                 0,
                 FALSE);
 
@@ -944,757 +943,759 @@ AFSIOWorkerThread( IN PVOID Context)
     return;
 }
 
-void
-AFSPrimaryVolumeWorkerThread( IN PVOID Context)
-{
+//
+// Called with VolumeCB->ObjectInfoTree.TreeLock held exclusive.
+// pCurrentObject->ObjectReferenceCount is incremented by the caller.
+//
+// The *pbReleaseVolumeLock is set to FALSE if the TreeLock is dropped
+// before returning.
+//
+// pCurrentObject must either be destroyed or the reference count
+// decremented before returning.
+//
 
+static void
+AFSExamineObjectInfo( IN AFSObjectInfoCB * pCurrentObject,
+                      IN BOOLEAN           bVolumeObject,
+                      IN OUT BOOLEAN     * pbReleaseVolumeLock)
+{
     NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSWorkQueueContext *pPoolContext = (AFSWorkQueueContext *)&AFSGlobalRoot->VolumeWorkerContext;
-    AFSDeviceExt *pControlDeviceExt = NULL;
-    AFSDeviceExt *pRDRDeviceExt = NULL;
-    LARGE_INTEGER DueTime;
-    LONG TimeOut;
-    KTIMER Timer;
-    BOOLEAN bFoundOpenEntry = FALSE;
-    AFSObjectInfoCB *pCurrentObject = NULL, *pNextObject = NULL, *pCurrentChildObject = NULL;
+    AFSDeviceExt *pControlDeviceExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
+    AFSDeviceExt *pRDRDeviceExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;
     AFSDirectoryCB *pCurrentDirEntry = NULL, *pNextDirEntry = NULL;
-    BOOLEAN bReleaseVolumeLock = FALSE;
-    AFSVolumeCB *pVolumeCB = NULL, *pNextVolume = NULL;
-    AFSFcb *pFcb = NULL;
-    LONG lFileType;
+    AFSObjectInfoCB *pCurrentChildObject = NULL;
+    AFSVolumeCB * pVolumeCB = pCurrentObject->VolumeCB;
     LARGE_INTEGER liCurrentTime;
-    BOOLEAN bVolumeObject = FALSE;
-    BOOLEAN bFcbBusy = FALSE;
     LONG lCount;
+    BOOLEAN bTemp;
 
-    pControlDeviceExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
-
-    pRDRDeviceExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;
-
-    AFSDbgLogMsg( AFS_SUBSYSTEM_CLEANUP_PROCESSING,
-                  AFS_TRACE_LEVEL_VERBOSE,
-                  "AFSPrimaryVolumeWorkerThread Initialized\n");
-
-    //
-    // Initialize the timer for the worker thread
-    //
-
-    DueTime.QuadPart = -(5000);
-
-    TimeOut = 5000;
-
-    KeInitializeTimerEx( &Timer,
-                         SynchronizationTimer);
-
-    KeSetTimerEx( &Timer,
-                  DueTime,
-                  TimeOut,
-                  NULL);
-
-    //
-    // Indicate that we are initialized and ready
-    //
-
-    KeSetEvent( &pPoolContext->WorkerThreadReady,
-                0,
-                FALSE);
-
-    //
-    // Indicate we are initialized
-    //
-
-    SetFlag( pPoolContext->State, AFS_WORKER_INITIALIZED);
-
-    while( BooleanFlagOn( pPoolContext->State, AFS_WORKER_PROCESS_REQUESTS))
+    __Enter
     {
 
-        if ( bFcbBusy == FALSE)
-        {
+        ASSERT( ExIsResourceAcquiredExclusiveLite( pVolumeCB->ObjectInfoTree.TreeLock));
 
-            KeWaitForSingleObject( &Timer,
-                                   Executive,
-                                   KernelMode,
-                                   FALSE,
-                                   NULL);
-        }
-        else
+        switch ( pCurrentObject->FileType)
         {
 
-            bFcbBusy = FALSE;
-        }
-
-        //
-        // This is the primary volume worker so it will traverse the volume list
-        // looking for cleanup or volumes requiring private workers
-        //
+        case AFS_FILE_TYPE_DIRECTORY:
+        {
 
-        AFSAcquireShared( &pRDRDeviceExt->Specific.RDR.VolumeListLock,
-                          TRUE);
+            if ( BooleanFlagOn( pRDRDeviceExt->DeviceFlags, AFS_DEVICE_FLAG_REDIRECTOR_SHUTDOWN))
+            {
 
-        pVolumeCB = pRDRDeviceExt->Specific.RDR.VolumeListHead;
+                try_return( ntStatus);
+            }
 
-        while( pVolumeCB != NULL)
-        {
+            //
+            // If this object is deleted then remove it from the parent, if we can
+            //
 
-            if( pVolumeCB == AFSGlobalRoot ||
-                !AFSAcquireExcl( pVolumeCB->VolumeLock,
-                                 FALSE))
+            if( BooleanFlagOn( pCurrentObject->Flags, AFS_OBJECT_FLAGS_DELETED))
             {
 
-                pVolumeCB = (AFSVolumeCB *)pVolumeCB->ListEntry.fLink;
+                if ( pCurrentObject->ObjectReferenceCount == 1 &&
+                     ( pCurrentObject->Fcb == NULL ||
+                       pCurrentObject->Fcb->OpenReferenceCount == 0) &&
+                     pCurrentObject->Specific.Directory.DirectoryNodeListHead == NULL &&
+                     pCurrentObject->Specific.Directory.ChildOpenReferenceCount == 0)
+                {
 
-                continue;
-            }
+                    AFSAcquireExcl( &pCurrentObject->NonPagedInfo->ObjectInfoLock,
+                                    TRUE);
 
-            if( pVolumeCB->ObjectInfoListHead == NULL)
-            {
+                    if ( pCurrentObject->Fcb != NULL)
+                    {
 
-                AFSReleaseResource( pVolumeCB->VolumeLock);
+                        AFSRemoveFcb( &pCurrentObject->Fcb);
+                    }
 
-                AFSReleaseResource( &pRDRDeviceExt->Specific.RDR.VolumeListLock);
+                    if( pCurrentObject->Specific.Directory.PIOCtlDirectoryCB != NULL)
+                    {
 
-                AFSAcquireExcl( pRDRDeviceExt->Specific.RDR.VolumeTree.TreeLock,
-                                TRUE);
+                        AFSAcquireExcl( &pCurrentObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation->NonPagedInfo->ObjectInfoLock,
+                                        TRUE);
 
-                AFSAcquireExcl( &pRDRDeviceExt->Specific.RDR.VolumeListLock,
-                                TRUE);
+                        AFSRemoveFcb( &pCurrentObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation->Fcb);
 
-                if( !AFSAcquireExcl( pVolumeCB->VolumeLock,
-                                     FALSE))
-                {
+                        AFSReleaseResource( &pCurrentObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation->NonPagedInfo->ObjectInfoLock);
 
-                    AFSConvertToShared( &pRDRDeviceExt->Specific.RDR.VolumeListLock);
+                        lCount = AFSObjectInfoDecrement( pCurrentObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation,
+                                                         AFS_OBJECT_REFERENCE_PIOCTL);
 
-                    AFSReleaseResource( pRDRDeviceExt->Specific.RDR.VolumeTree.TreeLock);
+                        AFSDbgTrace(( AFS_SUBSYSTEM_OBJECT_REF_COUNTING,
+                                      AFS_TRACE_LEVEL_VERBOSE,
+                                      "AFSExamineObjectInfo Decrement count on object %p Cnt %d\n",
+                                      pCurrentObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation,
+                                      lCount));
 
-                    pVolumeCB = (AFSVolumeCB *)pVolumeCB->ListEntry.fLink;
+                        ASSERT( lCount == 0);
 
-                    continue;
-                }
+                        if ( lCount == 0)
+                        {
 
-                KeQueryTickCount( &liCurrentTime);
+                            AFSDeleteObjectInfo( &pCurrentObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation);
+                        }
 
-                pNextVolume = (AFSVolumeCB *)pVolumeCB->ListEntry.fLink;
+                        ExDeleteResourceLite( &pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB->NonPaged->Lock);
 
-                AFSAcquireShared( &pVolumeCB->ObjectInformation.NonPagedInfo->ObjectInfoLock,
-                                  TRUE);
+                        AFSExFreePoolWithTag( pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB->NonPaged, AFS_DIR_ENTRY_NP_TAG);
 
-                if( pVolumeCB->ObjectInfoListHead == NULL &&
-                    pVolumeCB->DirectoryCB->DirOpenReferenceCount <= 0 &&
-                    pVolumeCB->VolumeReferenceCount == 1 &&
-                    ( pVolumeCB->RootFcb == NULL ||
-                      pVolumeCB->RootFcb->OpenReferenceCount == 0) &&
-                    pVolumeCB->ObjectInformation.ObjectReferenceCount <= 0)
-                {
+                        AFSDbgTrace(( AFS_SUBSYSTEM_DIRENTRY_ALLOCATION,
+                                      AFS_TRACE_LEVEL_VERBOSE,
+                                      "AFSExamineObjectInfo (pioctl) AFS_DIR_ENTRY_TAG deallocating %p\n",
+                                      pCurrentObject->Specific.Directory.PIOCtlDirectoryCB));
 
-                    if( pVolumeCB->RootFcb != NULL)
-                    {
+                        AFSExFreePoolWithTag( pCurrentObject->Specific.Directory.PIOCtlDirectoryCB, AFS_DIR_ENTRY_TAG);
 
-                        AFSRemoveRootFcb( pVolumeCB->RootFcb);
+                        pCurrentObject->Specific.Directory.PIOCtlDirectoryCB = NULL;
                     }
 
-                    AFSReleaseResource( &pVolumeCB->ObjectInformation.NonPagedInfo->ObjectInfoLock);
+                    AFSReleaseResource( &pCurrentObject->NonPagedInfo->ObjectInfoLock);
 
-                    AFSRemoveVolume( pVolumeCB);
-                }
-                else
-                {
+                    lCount = AFSObjectInfoDecrement( pCurrentObject,
+                                                     AFS_OBJECT_REFERENCE_WORKER);
 
-                    AFSReleaseResource( &pVolumeCB->ObjectInformation.NonPagedInfo->ObjectInfoLock);
+                    AFSDbgTrace(( AFS_SUBSYSTEM_OBJECT_REF_COUNTING,
+                                  AFS_TRACE_LEVEL_VERBOSE,
+                                  "AFSExamineObjectInfo Decrement1 count on object %p Cnt %d\n",
+                                  pCurrentObject,
+                                  lCount));
 
-                    AFSReleaseResource( pVolumeCB->VolumeLock);
-                }
+                    AFSDbgTrace(( AFS_SUBSYSTEM_CLEANUP_PROCESSING,
+                                  AFS_TRACE_LEVEL_VERBOSE,
+                                  "AFSExamineObjectInfo Deleting deleted object %p\n",
+                                  pCurrentObject));
 
-                AFSConvertToShared( &pRDRDeviceExt->Specific.RDR.VolumeListLock);
+                    //
+                    // The CurrentReferenceCount must be zero or we would not
+                    // have gotten this far.   It is safe to delete the ObjectInfoCB.
+                    //
 
-                AFSReleaseResource( pRDRDeviceExt->Specific.RDR.VolumeTree.TreeLock);
+                    AFSDeleteObjectInfo( &pCurrentObject);
+                }
 
-                pVolumeCB = pNextVolume;
+                //
+                // Finished processing the AFS_OBJECT_FLAGS_DELETED case.
+                //
 
-                continue;
+                try_return( ntStatus);
             }
 
             //
-            // Don't need this lock anymore now that we have a volume cb to work with
+            // pCurrentObject not marked Deleted.
             //
 
-            AFSReleaseResource( &pRDRDeviceExt->Specific.RDR.VolumeListLock);
-
-            //
-            // For now we only need the volume lock shared
-            //
+            if ( pCurrentObject->Fcb != NULL &&
+                 pCurrentObject->Fcb->CcbListHead != NULL)
+            {
 
-            AFSConvertToShared( pVolumeCB->VolumeLock);
+                try_return( ntStatus);
+            }
 
-            if( AFSAcquireShared( pVolumeCB->ObjectInfoTree.TreeLock,
-                                  FALSE))
+            if( pCurrentObject->Specific.Directory.ChildOpenReferenceCount > 0 ||
+                ( pCurrentObject->Fcb != NULL &&
+                  pCurrentObject->Fcb->OpenReferenceCount > 0))
             {
 
-                pCurrentObject = pVolumeCB->ObjectInfoListHead;
+                try_return( ntStatus);
+            }
 
-                pNextObject = NULL;
+            if ( pCurrentObject->FileType == AFS_FILE_TYPE_DIRECTORY &&
+                 pCurrentObject->Specific.Directory.DirectoryNodeListHead != NULL)
+            {
 
-                bReleaseVolumeLock = TRUE;
+                //
+                // Directory Entry Processing
+                //
+                // First pass is performed with the TreeLock held shared.
+                // If we detect any objects in use, we give up quickly without
+                // making any changes and without blocking other threads.
+                // The second pass is performed with the TreeLock held exclusive
+                // so deletions can take place.
+                //
 
-                while( pCurrentObject != NULL)
+                if( !AFSAcquireShared( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock,
+                                       FALSE))
                 {
 
-                    if( pCurrentObject != &pVolumeCB->ObjectInformation)
-                    {
+                    try_return( ntStatus);
+                }
 
-                        pNextObject = (AFSObjectInfoCB *)pCurrentObject->ListEntry.fLink;
+                KeQueryTickCount( &liCurrentTime);
 
-                        if( pNextObject == NULL &&
-                            pVolumeCB != AFSGlobalRoot)  // Don't free up the root of the global
-                        {
+                pCurrentDirEntry = pCurrentObject->Specific.Directory.DirectoryNodeListHead;
 
-                            pNextObject = &pVolumeCB->ObjectInformation;
-                        }
+                while( pCurrentDirEntry != NULL)
+                {
 
-                        bVolumeObject = FALSE;
-                    }
-                    else
+                    if( pCurrentDirEntry->DirOpenReferenceCount > 0 ||
+                        pCurrentDirEntry->NameArrayReferenceCount > 0)
                     {
 
-                        pNextObject = NULL;
+                        AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
 
-                        bVolumeObject = TRUE;
+                        try_return( ntStatus);
                     }
 
-                    if( pCurrentObject->FileType == AFS_FILE_TYPE_DIRECTORY &&
-                        !BooleanFlagOn( pRDRDeviceExt->DeviceFlags, AFS_DEVICE_FLAG_REDIRECTOR_SHUTDOWN))  // If we are in shutdown mode skip directories
+                    if ( pCurrentDirEntry->ObjectInformation != NULL)
                     {
 
-                        //
-                        // If this object is deleted then remove it from the parent, if we can
-                        //
-
-                        if( BooleanFlagOn( pCurrentObject->Flags, AFS_OBJECT_FLAGS_DELETED) &&
-                            pCurrentObject->ObjectReferenceCount <= 0 &&
-                            ( pCurrentObject->Fcb == NULL ||
-                              pCurrentObject->Fcb->OpenReferenceCount == 0) &&
-                            pCurrentObject->Specific.Directory.DirectoryNodeListHead == NULL &&
-                            pCurrentObject->Specific.Directory.ChildOpenReferenceCount == 0)
+                        if ( pCurrentDirEntry->ObjectInformation->Fcb != NULL &&
+                             pCurrentDirEntry->ObjectInformation->Fcb->OpenReferenceCount > 0)
                         {
 
-                            AFSReleaseResource( pVolumeCB->ObjectInfoTree.TreeLock);
-
-                            //
-                            // Dropping the TreeLock permits the
-                            // pCurrentObject->ObjectReferenceCount to change
-                            //
-
-                            if( AFSAcquireExcl( pVolumeCB->ObjectInfoTree.TreeLock,
-                                                FALSE))
-                            {
-
-                                AFSAcquireExcl( &pCurrentObject->NonPagedInfo->ObjectInfoLock,
-                                                TRUE);
+                            AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
 
-                                if ( pCurrentObject->ObjectReferenceCount <= 0 &&
-                                     ( pCurrentObject->Fcb == NULL ||
-                                       pCurrentObject->Fcb->OpenReferenceCount == 0 &&
-                                       pCurrentObject->Fcb->Specific.File.ExtentCount == 0))
-                                {
+                            try_return( ntStatus);
+                        }
 
-                                    AFSRemoveFcb( &pCurrentObject->Fcb);
+                        if ( liCurrentTime.QuadPart <= pCurrentDirEntry->ObjectInformation->LastAccessCount.QuadPart ||
+                             liCurrentTime.QuadPart - pCurrentDirEntry->ObjectInformation->LastAccessCount.QuadPart <
+                             pControlDeviceExt->Specific.Control.ObjectLifeTimeCount.QuadPart)
+                        {
 
-                                    if( pCurrentObject->Specific.Directory.PIOCtlDirectoryCB != NULL)
-                                    {
+                            AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
 
-                                        AFSAcquireExcl( &pCurrentObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation->NonPagedInfo->ObjectInfoLock,
-                                                        TRUE);
+                            try_return( ntStatus);
+                        }
 
-                                        AFSRemoveFcb( &pCurrentObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation->Fcb);
+                        if ( pCurrentDirEntry->ObjectInformation->FileType == AFS_FILE_TYPE_DIRECTORY)
+                        {
 
-                                        AFSReleaseResource( &pCurrentObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation->NonPagedInfo->ObjectInfoLock);
+                            if ( pCurrentDirEntry->ObjectInformation->Specific.Directory.DirectoryNodeListHead != NULL)
+                            {
 
-                                        AFSDeleteObjectInfo( pCurrentObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation);
+                                break;
+                            }
 
-                                        ExDeleteResourceLite( &pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB->NonPaged->Lock);
+                            if ( pCurrentDirEntry->ObjectInformation->Specific.Directory.ChildOpenReferenceCount > 0)
+                            {
 
-                                        AFSExFreePoolWithTag( pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB->NonPaged, AFS_DIR_ENTRY_NP_TAG);
+                                break;
+                            }
+                        }
 
-                                        AFSExFreePoolWithTag( pCurrentObject->Specific.Directory.PIOCtlDirectoryCB, AFS_DIR_ENTRY_TAG);
-                                    }
+                        if ( pCurrentDirEntry->ObjectInformation->FileType == AFS_FILE_TYPE_FILE)
+                        {
 
-                                    AFSReleaseResource( &pCurrentObject->NonPagedInfo->ObjectInfoLock);
-
-                                    AFSDbgLogMsg( AFS_SUBSYSTEM_CLEANUP_PROCESSING,
-                                                  AFS_TRACE_LEVEL_VERBOSE,
-                                                  "AFSPrimaryVolumeWorkerThread Deleting deleted object %08lX\n",
-                                                  pCurrentObject);
-
-                                    AFSDeleteObjectInfo( pCurrentObject);
-                                }
-                                else
-                                {
-
-                                    AFSReleaseResource( &pCurrentObject->NonPagedInfo->ObjectInfoLock);
-                                }
-
-                                AFSConvertToShared( pVolumeCB->ObjectInfoTree.TreeLock);
-
-                                pCurrentObject = pNextObject;
-
-                                continue;
-                            }
-                            else
-                            {
-
-                                bReleaseVolumeLock = FALSE;
+                            if ( pCurrentDirEntry->ObjectInformation->Fcb != NULL &&
+                                 pCurrentDirEntry->ObjectInformation->Fcb->Specific.File.ExtentsDirtyCount > 0)
+                            {
 
                                 break;
                             }
                         }
+                    }
 
-                        if( pCurrentObject->Specific.Directory.ChildOpenReferenceCount > 0 ||
-                            ( pCurrentObject->Fcb != NULL &&
-                              pCurrentObject->Fcb->OpenReferenceCount > 0) ||
-                            pCurrentObject->Specific.Directory.DirectoryNodeListHead == NULL)
-                        {
+                    pCurrentDirEntry = (AFSDirectoryCB *)pCurrentDirEntry->ListEntry.fLink;
+                }
 
-                            pCurrentObject = pNextObject;
+                AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
 
-                            continue;
-                        }
+                if( pCurrentDirEntry != NULL)
+                {
 
-                        if( !AFSAcquireShared( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock,
-                                               FALSE))
-                        {
+                    try_return( ntStatus);
+                }
 
-                            pCurrentObject = pNextObject;
+                //
+                // Attempt the second pass with the TreeLock held exclusive.
+                // The the TreeLock cannot be obtained without blocking it means that
+                // the directory is in active use, so do nothing.
+                //
 
-                            continue;
-                        }
+                if( AFSAcquireExcl( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock,
+                                    FALSE))
+                {
 
-                        KeQueryTickCount( &liCurrentTime);
+                    if( pCurrentObject->Specific.Directory.ChildOpenReferenceCount > 0)
+                    {
 
-                        pCurrentDirEntry = pCurrentObject->Specific.Directory.DirectoryNodeListHead;
+                        AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
 
-                        while( pCurrentDirEntry != NULL)
-                        {
+                        try_return( ntStatus);
+                    }
 
-                            if( pCurrentDirEntry->DirOpenReferenceCount > 0 ||
-                                ( pCurrentDirEntry->ObjectInformation->Fcb != NULL &&
-                                  pCurrentDirEntry->ObjectInformation->Fcb->OpenReferenceCount > 0) ||
-                                liCurrentTime.QuadPart <= pCurrentDirEntry->ObjectInformation->LastAccessCount.QuadPart ||
-                                liCurrentTime.QuadPart - pCurrentDirEntry->ObjectInformation->LastAccessCount.QuadPart <
-                                                                        pControlDeviceExt->Specific.Control.ObjectLifeTimeCount.QuadPart ||
-                                ( pCurrentDirEntry->ObjectInformation->FileType == AFS_FILE_TYPE_DIRECTORY &&
-                                   ( pCurrentDirEntry->ObjectInformation->Specific.Directory.DirectoryNodeListHead != NULL ||
-                                     pCurrentDirEntry->ObjectInformation->Specific.Directory.ChildOpenReferenceCount > 0)) ||
-                                ( pCurrentDirEntry->ObjectInformation->FileType == AFS_FILE_TYPE_FILE &&
-                                  pCurrentDirEntry->ObjectInformation->Fcb != NULL &&
-                                  pCurrentDirEntry->ObjectInformation->Fcb->Specific.File.ExtentsDirtyCount > 0))
-                            {
+                    KeQueryTickCount( &liCurrentTime);
 
-                                break;
-                            }
+                    pCurrentDirEntry = pCurrentObject->Specific.Directory.DirectoryNodeListHead;
 
-                            pCurrentDirEntry = (AFSDirectoryCB *)pCurrentDirEntry->ListEntry.fLink;
-                        }
+                    while( pCurrentDirEntry != NULL)
+                    {
 
-                        if( pCurrentDirEntry != NULL)
+                        if( pCurrentDirEntry->DirOpenReferenceCount > 0)
                         {
 
-                            AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
+                            break;
+                        }
 
-                            pCurrentObject = pNextObject;
+                        if ( pCurrentDirEntry->NameArrayReferenceCount > 0)
+                        {
 
-                            continue;
+                            break;
                         }
 
-                        AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
+                        if ( pCurrentDirEntry->ObjectInformation != NULL)
+                        {
 
-                        AFSReleaseResource( pVolumeCB->ObjectInfoTree.TreeLock);
+                            if ( pCurrentDirEntry->ObjectInformation->Fcb != NULL &&
+                                 pCurrentDirEntry->ObjectInformation->Fcb->OpenReferenceCount > 0)
+                            {
 
-                        //
-                        // Now acquire the locks excl
-                        //
+                                break;
+                            }
 
-                        if( AFSAcquireExcl( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock,
-                                            FALSE))
-                        {
+                            if ( liCurrentTime.QuadPart <= pCurrentDirEntry->ObjectInformation->LastAccessCount.QuadPart ||
+                                 liCurrentTime.QuadPart - pCurrentDirEntry->ObjectInformation->LastAccessCount.QuadPart <
+                                 pControlDeviceExt->Specific.Control.ObjectLifeTimeCount.QuadPart)
+                            {
 
-                            if( AFSAcquireExcl( pVolumeCB->ObjectInfoTree.TreeLock,
-                                                FALSE))
+                                break;
+                            }
+
+                            if ( pCurrentDirEntry->ObjectInformation->FileType == AFS_FILE_TYPE_DIRECTORY)
                             {
 
-                                if( pCurrentObject->Specific.Directory.ChildOpenReferenceCount > 0)
+                                if ( pCurrentDirEntry->ObjectInformation->Specific.Directory.DirectoryNodeListHead != NULL)
                                 {
 
-                                    AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
-
-                                    AFSConvertToShared( pVolumeCB->ObjectInfoTree.TreeLock);
+                                    break;
+                                }
 
-                                    pCurrentObject = pNextObject;
+                                if ( pCurrentDirEntry->ObjectInformation->Specific.Directory.ChildOpenReferenceCount > 0)
+                                {
 
-                                    continue;
+                                    break;
                                 }
+                            }
 
-                                KeQueryTickCount( &liCurrentTime);
-
-                                pCurrentDirEntry = pCurrentObject->Specific.Directory.DirectoryNodeListHead;
+                            if ( pCurrentDirEntry->ObjectInformation->FileType == AFS_FILE_TYPE_FILE)
+                            {
 
-                                while( pCurrentDirEntry != NULL)
+                                if ( pCurrentDirEntry->ObjectInformation->Fcb != NULL &&
+                                     pCurrentDirEntry->ObjectInformation->Fcb->Specific.File.ExtentsDirtyCount > 0)
                                 {
 
-                                    if( pCurrentDirEntry->DirOpenReferenceCount > 0 ||
-                                        ( pCurrentDirEntry->ObjectInformation->Fcb != NULL &&
-                                          pCurrentDirEntry->ObjectInformation->Fcb->OpenReferenceCount > 0) ||
-                                        liCurrentTime.QuadPart <= pCurrentDirEntry->ObjectInformation->LastAccessCount.QuadPart ||
-                                        liCurrentTime.QuadPart - pCurrentDirEntry->ObjectInformation->LastAccessCount.QuadPart <
-                                                                                pControlDeviceExt->Specific.Control.ObjectLifeTimeCount.QuadPart ||
-                                        ( pCurrentDirEntry->ObjectInformation->FileType == AFS_FILE_TYPE_DIRECTORY &&
-                                          ( pCurrentDirEntry->ObjectInformation->Specific.Directory.DirectoryNodeListHead != NULL ||
-                                            pCurrentDirEntry->ObjectInformation->Specific.Directory.ChildOpenReferenceCount > 0)) ||
-                                        ( pCurrentDirEntry->ObjectInformation->FileType == AFS_FILE_TYPE_FILE &&
-                                          pCurrentDirEntry->ObjectInformation->Fcb != NULL &&
-                                          pCurrentDirEntry->ObjectInformation->Fcb->Specific.File.ExtentsDirtyCount > 0))
-                                    {
-
-                                        break;
-                                    }
-
-                                    pCurrentDirEntry = (AFSDirectoryCB *)pCurrentDirEntry->ListEntry.fLink;
+                                    break;
                                 }
+                            }
+                        }
 
-                                if( pCurrentDirEntry != NULL)
-                                {
+                        pCurrentDirEntry = (AFSDirectoryCB *)pCurrentDirEntry->ListEntry.fLink;
+                    }
 
-                                    AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
+                    if( pCurrentDirEntry != NULL)
+                    {
 
-                                    AFSConvertToShared( pVolumeCB->ObjectInfoTree.TreeLock);
+                        //
+                        // At least one entry in the directory is actively in use.
+                        // Drop the lock and exit without removing anything.
+                        //
 
-                                    pCurrentObject = pNextObject;
+                        AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
 
-                                    continue;
-                                }
+                        try_return( ntStatus);
+                    }
 
-                                pCurrentDirEntry = pCurrentObject->Specific.Directory.DirectoryNodeListHead;
+                    //
+                    // Third pass, process each directory entry and remove what we can.
+                    // The VolumeCB TreeLock and the ObjectInfo TreeLock are still held exclusive.
+                    //
 
-                                while( pCurrentDirEntry != NULL)
-                                {
+                    pCurrentDirEntry = pCurrentObject->Specific.Directory.DirectoryNodeListHead;
 
-                                    pNextDirEntry = (AFSDirectoryCB *)pCurrentDirEntry->ListEntry.fLink;
+                    while( pCurrentDirEntry != NULL)
+                    {
 
-                                    pCurrentChildObject = pCurrentDirEntry->ObjectInformation;
+                        pNextDirEntry = (AFSDirectoryCB *)pCurrentDirEntry->ListEntry.fLink;
 
-                                    pFcb = NULL;
+                        //
+                        // Delete the DirectoryCB which in turn removes the DIRENTRY reference
+                        // count from the associated ObjectInfoCB.  The reference count held above
+                        // may now be the only one left.
+                        //
 
-                                    AFSDbgLogMsg( AFS_SUBSYSTEM_CLEANUP_PROCESSING | AFS_SUBSYSTEM_DIRENTRY_REF_COUNTING,
-                                                  AFS_TRACE_LEVEL_VERBOSE,
-                                                  "AFSPrimaryVolumeWorkerThread Deleting DE %wZ Object %08lX\n",
-                                                  &pCurrentDirEntry->NameInformation.FileName,
-                                                  pCurrentChildObject);
+                        AFSDbgTrace(( AFS_SUBSYSTEM_CLEANUP_PROCESSING | AFS_SUBSYSTEM_DIRENTRY_REF_COUNTING,
+                                      AFS_TRACE_LEVEL_VERBOSE,
+                                      "AFSExamineObjectInfo Deleting DE %wZ Object %p\n",
+                                      &pCurrentDirEntry->NameInformation.FileName,
+                                      pCurrentDirEntry->ObjectInformation));
 
-                                    AFSDeleteDirEntry( pCurrentObject,
-                                                       pCurrentDirEntry);
+                        AFSDeleteDirEntry( pCurrentObject,
+                                           &pCurrentDirEntry);
 
+                        pCurrentDirEntry = pNextDirEntry;
+                    }
 
-                                    //
-                                    // Acquire ObjectInfoLock shared here so as not to deadlock
-                                    // with an invalidation call from the service during AFSCleanupFcb
-                                    //
+                    //
+                    // Clear our enumerated flag on this object so we retrieve info again on next access
+                    //
 
-                                    lCount = AFSObjectInfoIncrement( pCurrentChildObject);
+                    ClearFlag( pCurrentObject->Flags, AFS_OBJECT_FLAGS_DIRECTORY_ENUMERATED);
 
-                                    if( lCount == 1 &&
-                                        pCurrentChildObject->Fcb != NULL &&
-                                        pCurrentChildObject->FileType == AFS_FILE_TYPE_FILE)
-                                    {
+                    AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
+                }
+            }
+            else if ( bVolumeObject == FALSE)
+            {
+                //
+                // No children
+                //
 
-                                        //
-                                        // We must not hold pVolumeCB->ObjectInfoTree.TreeLock exclusive
-                                        // across an AFSCleanupFcb call since it can deadlock with an
-                                        // invalidation call from the service.
-                                        //
+                if( pCurrentObject->ObjectReferenceCount > 1 ||
+                    pCurrentObject->Fcb != NULL &&
+                    pCurrentObject->Fcb->OpenReferenceCount > 0)
+                {
 
-                                        AFSReleaseResource( pVolumeCB->ObjectInfoTree.TreeLock);
+                    try_return( ntStatus);
+                }
 
-                                        //
-                                        // Cannot hold a TreeLock across an AFSCleanupFcb call
-                                        // as it can deadlock with an invalidation ioctl initiated
-                                        // from the service.
-                                        //
-                                        // Dropping the TreeLock permits the
-                                        // pCurrentObject->ObjectReferenceCount to change
-                                        //
+                AFSAcquireExcl( &pCurrentObject->NonPagedInfo->ObjectInfoLock,
+                                TRUE);
 
-                                        ntStatus = AFSCleanupFcb( pCurrentChildObject->Fcb,
-                                                                  TRUE);
+                KeQueryTickCount( &liCurrentTime);
 
-                                        if ( ntStatus == STATUS_RETRY)
-                                        {
+                if( pCurrentObject->ObjectReferenceCount == 1 &&
+                    ( pCurrentObject->Fcb == NULL ||
+                      pCurrentObject->Fcb->OpenReferenceCount == 0) &&
+                    liCurrentTime.QuadPart > pCurrentObject->LastAccessCount.QuadPart &&
+                    liCurrentTime.QuadPart - pCurrentObject->LastAccessCount.QuadPart >
+                    pControlDeviceExt->Specific.Control.ObjectLifeTimeCount.QuadPart)
+                {
 
-                                            bFcbBusy = TRUE;
-                                        }
+                    AFSRemoveFcb( &pCurrentObject->Fcb);
 
-                                        AFSAcquireExcl( pVolumeCB->ObjectInfoTree.TreeLock,
-                                                        TRUE);
-                                    }
+                    AFSReleaseResource( &pCurrentObject->NonPagedInfo->ObjectInfoLock);
 
-                                    lCount = AFSObjectInfoDecrement( pCurrentChildObject);
+                    lCount = AFSObjectInfoDecrement( pCurrentObject,
+                                                     AFS_OBJECT_REFERENCE_WORKER);
 
-                                    AFSAcquireExcl( &pCurrentChildObject->NonPagedInfo->ObjectInfoLock,
-                                                    TRUE);
+                    AFSDbgTrace(( AFS_SUBSYSTEM_OBJECT_REF_COUNTING,
+                                  AFS_TRACE_LEVEL_VERBOSE,
+                                  "AFSExamineObjectInfo Decrement4 count on object %p Cnt %d\n",
+                                  pCurrentObject,
+                                  lCount));
 
-                                    if( pCurrentChildObject->ObjectReferenceCount <= 0 &&
-                                        ( pCurrentChildObject->Fcb == NULL ||
-                                          pCurrentChildObject->Fcb->OpenReferenceCount == 0 &&
-                                          pCurrentChildObject->Fcb->Specific.File.ExtentCount == 0))
-                                    {
+                    //
+                    // The Volume TreeLock is held exclusive.  Therefore, the ObjectReferenceCount
+                    // cannot change.  It is therefore safe to delete the ObjectInfoCB
+                    //
 
-                                        AFSRemoveFcb( &pCurrentChildObject->Fcb);
+                    AFSDeleteObjectInfo( &pCurrentObject);
+                }
+                else
+                {
 
-                                        if( pCurrentChildObject->FileType == AFS_FILE_TYPE_DIRECTORY &&
-                                            pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB != NULL)
-                                        {
+                    AFSReleaseResource( &pCurrentObject->NonPagedInfo->ObjectInfoLock);
+                }
+            }
 
-                                            AFSAcquireExcl( &pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation->NonPagedInfo->ObjectInfoLock,
-                                                            TRUE);
+            break;
+        }
 
-                                            AFSRemoveFcb( &pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation->Fcb);
+        case AFS_FILE_TYPE_FILE:
+        {
 
-                                            AFSReleaseResource( &pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation->NonPagedInfo->ObjectInfoLock);
+            if( pCurrentObject->ObjectReferenceCount > 1 ||
+                pCurrentObject->Fcb != NULL &&
+                pCurrentObject->Fcb->OpenReferenceCount > 0)
+            {
 
-                                            AFSDeleteObjectInfo( pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB->ObjectInformation);
+                try_return( ntStatus);
+            }
 
-                                            ExDeleteResourceLite( &pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB->NonPaged->Lock);
+            if( pCurrentObject->Fcb != NULL)
+            {
 
-                                            AFSExFreePoolWithTag( pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB->NonPaged, AFS_DIR_ENTRY_NP_TAG);
+                AFSReleaseResource( pVolumeCB->ObjectInfoTree.TreeLock);
 
-                                            AFSExFreePoolWithTag( pCurrentChildObject->Specific.Directory.PIOCtlDirectoryCB, AFS_DIR_ENTRY_TAG);
-                                        }
+                //
+                // Dropping the VolumeCB TreeLock permits the
+                // pCurrentObject->ObjectReferenceCount to change.
+                // But it cannot be held across the AFSCleanupFcb
+                // call.
+                //
 
-                                        AFSReleaseResource( &pCurrentChildObject->NonPagedInfo->ObjectInfoLock);
+                ntStatus = AFSCleanupFcb( pCurrentObject->Fcb,
+                                          FALSE);
 
-                                        AFSDbgLogMsg( AFS_SUBSYSTEM_CLEANUP_PROCESSING,
-                                                      AFS_TRACE_LEVEL_VERBOSE,
-                                                      "AFSPrimaryVolumeWorkerThread Deleting object %08lX\n",
-                                                      pCurrentChildObject);
+                if (!AFSAcquireExcl( pVolumeCB->ObjectInfoTree.TreeLock,
+                                     FALSE))
+                {
 
-                                        AFSDeleteObjectInfo( pCurrentChildObject);
-                                    }
-                                    else
-                                    {
+                    *pbReleaseVolumeLock = FALSE;
+                }
 
-                                        AFSReleaseResource( &pCurrentChildObject->NonPagedInfo->ObjectInfoLock);
-                                    }
+                if ( ntStatus == STATUS_RETRY ||
+                     *pbReleaseVolumeLock == FALSE)
+                {
 
-                                    pCurrentDirEntry = pNextDirEntry;
+                    //
+                    // The Fcb is in use.
+                    //
 
-                                }
+                    try_return( ntStatus);
+                }
+            }
 
-                                pCurrentObject->Specific.Directory.DirectoryNodeListHead = NULL;
+            //
+            // VolumeCB is held exclusive
+            //
 
-                                pCurrentObject->Specific.Directory.DirectoryNodeListTail = NULL;
+            AFSAcquireExcl( &pCurrentObject->NonPagedInfo->ObjectInfoLock,
+                            TRUE);
 
-                                pCurrentObject->Specific.Directory.ShortNameTree = NULL;
+            KeQueryTickCount( &liCurrentTime);
 
-                                pCurrentObject->Specific.Directory.DirectoryNodeHdr.CaseSensitiveTreeHead = NULL;
+            if( pCurrentObject->ObjectReferenceCount == 1 &&
+                ( pCurrentObject->Fcb == NULL ||
+                  ( pCurrentObject->Fcb->OpenReferenceCount == 0 &&
+                    pCurrentObject->Fcb->Specific.File.ExtentsDirtyCount == 0)) &&
+                liCurrentTime.QuadPart > pCurrentObject->LastAccessCount.QuadPart &&
+                liCurrentTime.QuadPart - pCurrentObject->LastAccessCount.QuadPart >
+                pControlDeviceExt->Specific.Control.ObjectLifeTimeCount.QuadPart)
+            {
 
-                                pCurrentObject->Specific.Directory.DirectoryNodeHdr.CaseInsensitiveTreeHead = NULL;
+                AFSRemoveFcb( &pCurrentObject->Fcb);
 
-                                pCurrentObject->Specific.Directory.DirectoryNodeCount = 0;
+                AFSReleaseResource( &pCurrentObject->NonPagedInfo->ObjectInfoLock);
 
-                                AFSDbgLogMsg( AFS_SUBSYSTEM_DIR_NODE_COUNT,
-                                              AFS_TRACE_LEVEL_VERBOSE,
-                                              "AFSPrimaryVolumeWorkerThread Reset count to 0 on parent FID %08lX-%08lX-%08lX-%08lX\n",
-                                              pCurrentObject->FileId.Cell,
-                                              pCurrentObject->FileId.Volume,
-                                              pCurrentObject->FileId.Vnode,
-                                              pCurrentObject->FileId.Unique);
+                lCount = AFSObjectInfoDecrement( pCurrentObject,
+                                                 AFS_OBJECT_REFERENCE_WORKER);
 
-                                //
-                                // Clear our enumerated flag on this object so we retrieve info again on next access
-                                //
+                AFSDbgTrace(( AFS_SUBSYSTEM_OBJECT_REF_COUNTING,
+                              AFS_TRACE_LEVEL_VERBOSE,
+                              "AFSExamineObjectInfo Decrement5 count on object %p Cnt %d\n",
+                              pCurrentObject,
+                              lCount));
 
-                                ClearFlag( pCurrentObject->Flags, AFS_OBJECT_FLAGS_DIRECTORY_ENUMERATED);
+                //
+                // The VolumeCB TreeLock is held exclusive so the
+                // ObjectReferenceCount cannot change.
+                //
 
-                                AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
+                AFSDeleteObjectInfo( &pCurrentObject);
+            }
+            else
+            {
 
-                                AFSConvertToShared( pVolumeCB->ObjectInfoTree.TreeLock);
-                            }
-                            else
-                            {
+                AFSReleaseResource( &pCurrentObject->NonPagedInfo->ObjectInfoLock);
+            }
 
-                                AFSReleaseResource( pCurrentObject->Specific.Directory.DirectoryNodeHdr.TreeLock);
+            break;
+        }
 
-                                bReleaseVolumeLock = FALSE;
+        default:
+        {
 
-                                break;
-                            }
-                        }
-                        else
-                        {
+            AFSAcquireExcl( &pCurrentObject->NonPagedInfo->ObjectInfoLock,
+                            TRUE);
 
-                            //
-                            // Try to grab the volume lock again ... no problem if we don't
-                            //
+            KeQueryTickCount( &liCurrentTime);
 
-                            if( !AFSAcquireExcl( pVolumeCB->ObjectInfoTree.TreeLock,
-                                                 FALSE))
-                            {
+            if( pCurrentObject->ObjectReferenceCount == 1 &&
+                ( pCurrentObject->Fcb == NULL ||
+                  pCurrentObject->Fcb->OpenReferenceCount == 0) &&
+                liCurrentTime.QuadPart > pCurrentObject->LastAccessCount.QuadPart &&
+                liCurrentTime.QuadPart - pCurrentObject->LastAccessCount.QuadPart >
+                pControlDeviceExt->Specific.Control.ObjectLifeTimeCount.QuadPart)
+            {
 
-                                bReleaseVolumeLock = FALSE;
+                AFSRemoveFcb( &pCurrentObject->Fcb);
 
-                                break;
-                            }
-                        }
+                AFSReleaseResource( &pCurrentObject->NonPagedInfo->ObjectInfoLock);
 
-                        if( pCurrentObject != &pVolumeCB->ObjectInformation)
-                        {
+                lCount = AFSObjectInfoDecrement( pCurrentObject,
+                                                 AFS_OBJECT_REFERENCE_WORKER);
 
-                            pCurrentObject = (AFSObjectInfoCB *)pCurrentObject->ListEntry.fLink;
+                AFSDbgTrace(( AFS_SUBSYSTEM_OBJECT_REF_COUNTING,
+                              AFS_TRACE_LEVEL_VERBOSE,
+                              "AFSExamineObjectInfo Decrement6 count on object %p Cnt %d\n",
+                              pCurrentObject,
+                              lCount));
 
-                            if( pCurrentObject == NULL &&
-                                pVolumeCB != AFSGlobalRoot)
-                            {
+                //
+                // The VolumeCB TreeLock is held exclusive so the
+                // ObjectReferenceCount cannot change.
+                //
 
-                                pCurrentObject = &pVolumeCB->ObjectInformation;
-                            }
-                        }
-                        else
-                        {
+                AFSDeleteObjectInfo( &pCurrentObject);
+            }
+            else
+            {
 
-                            pCurrentObject = NULL;
-                        }
+                AFSReleaseResource( &pCurrentObject->NonPagedInfo->ObjectInfoLock);
+            }
+        }
+        }
 
-                        continue;
-                    }
-                    else if( pCurrentObject->FileType == AFS_FILE_TYPE_FILE)
-                    {
+      try_exit:
 
-                        AFSReleaseResource( pVolumeCB->ObjectInfoTree.TreeLock);
+        if ( pCurrentObject != NULL)
+        {
 
-                        lCount = AFSObjectInfoIncrement( pCurrentObject);
+            lCount = AFSObjectInfoDecrement( pCurrentObject,
+                                             AFS_OBJECT_REFERENCE_WORKER);
 
-                        if( lCount == 0 &&
-                            pCurrentObject->Fcb != NULL)
-                        {
+            AFSDbgTrace(( AFS_SUBSYSTEM_OBJECT_REF_COUNTING,
+                          AFS_TRACE_LEVEL_VERBOSE,
+                          "AFSExamineObjectInfo Decrement count on object %p Cnt %d\n",
+                          pCurrentObject,
+                          lCount));
+        }
+    }
+}
 
-                            //
-                            // Dropping the TreeLock permits the
-                            // pCurrentObject->ObjectReferenceCount to change
-                            //
+//
+// Called with VolumeCB->VolumeLock held shared.
+//
 
-                            ntStatus = AFSCleanupFcb( pCurrentObject->Fcb,
-                                                      FALSE);
+static void
+AFSExamineVolume( IN AFSVolumeCB *pVolumeCB)
+{
+    NTSTATUS ntStatus = STATUS_SUCCESS;
+    AFSObjectInfoCB *pCurrentObject = NULL, *pNextObject = NULL;
+    BOOLEAN bReleaseVolumeTreeLock = FALSE;
+    BOOLEAN bVolumeObject = FALSE;
+    LONG lCount;
 
-                            if ( ntStatus == STATUS_RETRY)
-                            {
+    //
+    // The Volume ObjectInfoTree TreeLock must be held exclusive to
+    // prevent other threads from obtaining a reference to an ObjectInfoCB
+    // via AFSFindObjectInfo() while it is being deleted.  This is
+    // annoying but the alternative is to hold the TreeLock shared during
+    // garbage collection and exclusive during find operations.
+    //
 
-                                bFcbBusy = TRUE;
-                            }
-                        }
+    if( AFSAcquireExcl( pVolumeCB->ObjectInfoTree.TreeLock,
+                        TRUE))
+    {
 
-                        lCount = AFSObjectInfoDecrement( pCurrentObject);
+        bReleaseVolumeTreeLock = TRUE;
 
-                        if( !AFSAcquireExcl( pVolumeCB->ObjectInfoTree.TreeLock,
-                                             FALSE))
-                        {
+        pCurrentObject = pVolumeCB->ObjectInfoListHead;
 
-                            bReleaseVolumeLock = FALSE;
+        lCount = AFSObjectInfoIncrement( pCurrentObject,
+                                         AFS_OBJECT_REFERENCE_WORKER);
 
-                            break;
-                        }
+        AFSDbgTrace(( AFS_SUBSYSTEM_OBJECT_REF_COUNTING,
+                      AFS_TRACE_LEVEL_VERBOSE,
+                      "AFSExamineVolume Increment count on object %p Cnt %d\n",
+                      pCurrentObject,
+                      lCount));
 
-                        AFSAcquireExcl( &pCurrentObject->NonPagedInfo->ObjectInfoLock,
-                                        TRUE);
+        pNextObject = NULL;
 
-                        if( BooleanFlagOn( pCurrentObject->Flags, AFS_OBJECT_FLAGS_DELETED) &&
-                            pCurrentObject->ObjectReferenceCount <= 0 &&
-                            ( pCurrentObject->Fcb == NULL ||
-                              pCurrentObject->Fcb->OpenReferenceCount == 0 &&
-                              pCurrentObject->Fcb->Specific.File.ExtentCount == 0))
-                        {
+        while( pCurrentObject != NULL &&
+               bReleaseVolumeTreeLock == TRUE)
+        {
 
-                            AFSRemoveFcb( &pCurrentObject->Fcb);
+            if( pCurrentObject != &pVolumeCB->ObjectInformation)
+            {
 
-                            AFSReleaseResource( &pCurrentObject->NonPagedInfo->ObjectInfoLock);
+                pNextObject = (AFSObjectInfoCB *)pCurrentObject->ListEntry.fLink;
 
-                            AFSDeleteObjectInfo( pCurrentObject);
-                        }
-                        else
-                        {
+                //
+                // If the end of the VolumeCB ObjectInfo List is reached, then
+                // the next ObjectInformationCB to examine is the one embedded within
+                // the VolumeCB itself except when the VolumeCB is the AFSGlobalRoot.
+                //
+                // bVolumeObject is used to indicate whether the embedded ObjectInfoCB
+                // is being examined.
+                //
 
-                            AFSReleaseResource( &pCurrentObject->NonPagedInfo->ObjectInfoLock);
-                        }
+                if( pNextObject == NULL &&
+                    pVolumeCB != AFSGlobalRoot)  // Don't free up the root of the global
+                {
 
-                        AFSConvertToShared( pVolumeCB->ObjectInfoTree.TreeLock);
+                    pNextObject = &pVolumeCB->ObjectInformation;
+                }
 
-                        pCurrentObject = pNextObject;
+                bVolumeObject = FALSE;
 
-                        continue;
-                    }
+                if ( pNextObject)
+                {
+
+                    lCount = AFSObjectInfoIncrement( pNextObject,
+                                                     AFS_OBJECT_REFERENCE_WORKER);
 
-                    pCurrentObject = pNextObject;
+                    AFSDbgTrace(( AFS_SUBSYSTEM_OBJECT_REF_COUNTING,
+                                  AFS_TRACE_LEVEL_VERBOSE,
+                                  "AFSExamineVolume Increment count on object %p Cnt %d\n",
+                                  pNextObject,
+                                  lCount));
                 }
+            }
+            else
+            {
 
-                if( bReleaseVolumeLock)
-                {
+                pNextObject = NULL;
 
-                    AFSReleaseResource( pVolumeCB->ObjectInfoTree.TreeLock);
-                }
+                bVolumeObject = TRUE;
             }
 
+            AFSExamineObjectInfo( pCurrentObject, bVolumeObject, &bReleaseVolumeTreeLock);
+
             //
-            // Next volume cb
+            // The CurrentObject is either destroyed or the reference count has been
+            // dropped by AFSExamineObjectInfo().
             //
 
-            AFSReleaseResource( pVolumeCB->VolumeLock);
+            if ( bReleaseVolumeTreeLock == FALSE)
+            {
 
-            AFSAcquireShared( &pRDRDeviceExt->Specific.RDR.VolumeListLock,
-                              TRUE);
+                //
+                // Try to obtain the Volume's ObjectInfoTree.TreeLock after dropping
+                // other locks and continue.
+                //
 
-            pVolumeCB = (AFSVolumeCB *)pVolumeCB->ListEntry.fLink;
-        }
+                bReleaseVolumeTreeLock = AFSAcquireExcl( pVolumeCB->ObjectInfoTree.TreeLock,
+                                                         TRUE);
+            }
 
-        AFSReleaseResource( &pRDRDeviceExt->Specific.RDR.VolumeListLock);
+            pCurrentObject = pNextObject;
 
-    } // worker thread loop
+            pNextObject = NULL;
+        }
 
-    KeCancelTimer( &Timer);
+        if ( pCurrentObject != NULL)
+        {
 
-    AFSDbgLogMsg( AFS_SUBSYSTEM_CLEANUP_PROCESSING,
-                  AFS_TRACE_LEVEL_VERBOSE,
-                  "AFSPrimaryVolumeWorkerThread Exiting\n");
+            lCount = AFSObjectInfoDecrement( pCurrentObject,
+                                             AFS_OBJECT_REFERENCE_WORKER);
 
-    lCount = InterlockedDecrement( &pControlDeviceExt->Specific.Control.VolumeWorkerThreadCount);
+            AFSDbgTrace(( AFS_SUBSYSTEM_OBJECT_REF_COUNTING,
+                          AFS_TRACE_LEVEL_VERBOSE,
+                          "AFSExamineVolume Decrement count on object %p Cnt %d\n",
+                          pCurrentObject,
+                          lCount));
+        }
 
-    if( lCount == 0)
-    {
+        if( bReleaseVolumeTreeLock)
+        {
 
-        KeSetEvent( &pControlDeviceExt->Specific.Control.VolumeWorkerCloseEvent,
-                    0,
-                    FALSE);
+            AFSReleaseResource( pVolumeCB->ObjectInfoTree.TreeLock);
+        }
     }
-
-    PsTerminateSystemThread( 0);
-
-    return;
 }
 
 void
-AFSVolumeWorkerThread( IN PVOID Context)
+AFSPrimaryVolumeWorkerThread( IN PVOID Context)
 {
 
-    NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSVolumeCB *pVolumeCB = (AFSVolumeCB * )Context;
-    AFSWorkQueueContext *pPoolContext = (AFSWorkQueueContext *)&pVolumeCB->VolumeWorkerContext;
-    AFSDeviceExt *pControlDeviceExt = NULL;
-    AFSDeviceExt *pRDRDeviceExt = NULL;
-    BOOLEAN exitThread = FALSE;
+    UNREFERENCED_PARAMETER(Context);
+    AFSWorkQueueContext *pPoolContext = (AFSWorkQueueContext *)&AFSGlobalRoot->NonPagedVcb->VolumeWorkerContext;
+    AFSDeviceExt *pControlDeviceExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
+    AFSDeviceExt *pRDRDeviceExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;
     LARGE_INTEGER DueTime;
     LONG TimeOut;
     KTIMER Timer;
+    AFSVolumeCB *pVolumeCB = NULL, *pNextVolume = NULL;
     LONG lCount;
 
-    pControlDeviceExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
-
-    pRDRDeviceExt = (AFSDeviceExt *)AFSRDRDeviceObject->DeviceExtension;
+    AFSDbgTrace(( AFS_SUBSYSTEM_CLEANUP_PROCESSING,
+                  AFS_TRACE_LEVEL_VERBOSE,
+                  "AFSPrimaryVolumeWorkerThread Initialized\n"));
 
     //
     // Initialize the timer for the worker thread
@@ -1729,36 +1730,140 @@ AFSVolumeWorkerThread( IN PVOID Context)
     while( BooleanFlagOn( pPoolContext->State, AFS_WORKER_PROCESS_REQUESTS))
     {
 
-        ntStatus = KeWaitForSingleObject( &Timer,
-                                          Executive,
-                                          KernelMode,
-                                          FALSE,
-                                          NULL);
+        KeWaitForSingleObject( &Timer,
+                               Executive,
+                               KernelMode,
+                               FALSE,
+                               NULL);
 
-        if( !NT_SUCCESS( ntStatus))
-        {
+        //
+        // This is the primary volume worker so it will traverse the volume list
+        // looking for cleanup or volumes requiring private workers
+        //
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
-                          AFS_TRACE_LEVEL_ERROR,
-                          "AFSVolumeWorkerThread Wait for queue items failed Status %08lX\n", ntStatus);
-        }
-        else
+        AFSAcquireShared( &pRDRDeviceExt->Specific.RDR.VolumeListLock,
+                          TRUE);
+
+        pVolumeCB = pRDRDeviceExt->Specific.RDR.VolumeListHead;
+
+        while( pVolumeCB != NULL)
         {
 
-            //
-            // If we are in shutdown mode and the dirty flag is clear then get out now
-            //
+            if( pVolumeCB == AFSGlobalRoot ||
+                !AFSAcquireExcl( pVolumeCB->VolumeLock,
+                                 FALSE))
+            {
+
+                pVolumeCB = (AFSVolumeCB *)pVolumeCB->ListEntry.fLink;
+
+                continue;
+            }
 
-            if( BooleanFlagOn( pRDRDeviceExt->DeviceFlags, AFS_DEVICE_FLAG_REDIRECTOR_SHUTDOWN))
+            if( pVolumeCB->ObjectInfoListHead == NULL)
             {
 
-                break;
+                AFSReleaseResource( pVolumeCB->VolumeLock);
+
+                AFSReleaseResource( &pRDRDeviceExt->Specific.RDR.VolumeListLock);
+
+                AFSAcquireExcl( pRDRDeviceExt->Specific.RDR.VolumeTree.TreeLock,
+                                TRUE);
+
+                AFSAcquireExcl( &pRDRDeviceExt->Specific.RDR.VolumeListLock,
+                                TRUE);
+
+                if( !AFSAcquireExcl( pVolumeCB->VolumeLock,
+                                     FALSE))
+                {
+
+                    AFSConvertToShared( &pRDRDeviceExt->Specific.RDR.VolumeListLock);
+
+                    AFSReleaseResource( pRDRDeviceExt->Specific.RDR.VolumeTree.TreeLock);
+
+                    pVolumeCB = (AFSVolumeCB *)pVolumeCB->ListEntry.fLink;
+
+                    continue;
+                }
+
+                pNextVolume = (AFSVolumeCB *)pVolumeCB->ListEntry.fLink;
+
+                AFSAcquireExcl( &pVolumeCB->ObjectInformation.NonPagedInfo->ObjectInfoLock,
+                                TRUE);
+
+                //
+                // If VolumeCB is idle, the Volume can be garbage collected
+                //
+
+                if( pVolumeCB->ObjectInfoListHead == NULL &&
+                    pVolumeCB->DirectoryCB->DirOpenReferenceCount <= 0 &&
+                    pVolumeCB->DirectoryCB->NameArrayReferenceCount <= 0 &&
+                    pVolumeCB->VolumeReferenceCount == 0 &&
+                    ( pVolumeCB->RootFcb == NULL ||
+                      pVolumeCB->RootFcb->OpenReferenceCount == 0) &&
+                    pVolumeCB->ObjectInformation.ObjectReferenceCount <= 0)
+                {
+
+                    AFSRemoveRootFcb( pVolumeCB);
+
+                    AFSReleaseResource( &pVolumeCB->ObjectInformation.NonPagedInfo->ObjectInfoLock);
+
+                    AFSRemoveVolume( pVolumeCB);
+                }
+                else
+                {
+
+                    AFSReleaseResource( &pVolumeCB->ObjectInformation.NonPagedInfo->ObjectInfoLock);
+
+                    AFSReleaseResource( pVolumeCB->VolumeLock);
+                }
+
+                AFSConvertToShared( &pRDRDeviceExt->Specific.RDR.VolumeListLock);
+
+                AFSReleaseResource( pRDRDeviceExt->Specific.RDR.VolumeTree.TreeLock);
+
+                pVolumeCB = pNextVolume;
+
+                continue;
             }
+
+            //
+            // Don't need this lock anymore now that we have a volume cb to work with
+            //
+
+            AFSReleaseResource( &pRDRDeviceExt->Specific.RDR.VolumeListLock);
+
+            //
+            // For now we only need the volume lock shared
+            //
+
+            AFSConvertToShared( pVolumeCB->VolumeLock);
+
+            AFSExamineVolume( pVolumeCB);
+
+            //
+            // Next volume cb
+            //
+
+            AFSReleaseResource( pVolumeCB->VolumeLock);
+
+            AFSAcquireShared( &pRDRDeviceExt->Specific.RDR.VolumeListLock,
+                              TRUE);
+
+            pVolumeCB = (AFSVolumeCB *)pVolumeCB->ListEntry.fLink;
         }
+
+        AFSReleaseResource( &pRDRDeviceExt->Specific.RDR.VolumeListLock);
+
     } // worker thread loop
 
     KeCancelTimer( &Timer);
 
+    ClearFlag( pPoolContext->State, AFS_WORKER_INITIALIZED);
+
+    AFSDbgTrace(( AFS_SUBSYSTEM_CLEANUP_PROCESSING,
+                  AFS_TRACE_LEVEL_VERBOSE,
+                  "AFSPrimaryVolumeWorkerThread Exiting\n"));
+
     lCount = InterlockedDecrement( &pControlDeviceExt->Specific.Control.VolumeWorkerThreadCount);
 
     if( lCount == 0)
@@ -1779,48 +1884,48 @@ AFSInsertWorkitem( IN AFSWorkItem *WorkItem)
 {
 
     NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSDeviceExt *pDevExt = NULL;
+    AFSDeviceExt *pControlDevExt = NULL;
     LONG lCount;
 
-    pDevExt = (AFSDeviceExt *)AFSLibraryDeviceObject->DeviceExtension;
+    pControlDevExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
 
-    AFSDbgLogMsg( AFS_SUBSYSTEM_LOCK_PROCESSING,
+    AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
                   AFS_TRACE_LEVEL_VERBOSE,
-                  "AFSInsertWorkitem Acquiring Control QueueLock lock %08lX EXCL %08lX\n",
-                  &pDevExt->Specific.Library.QueueLock,
-                  PsGetCurrentThread());
+                  "AFSInsertWorkitem Acquiring Control QueueLock lock %p EXCL %08lX\n",
+                  &pControlDevExt->Specific.Control.QueueLock,
+                  PsGetCurrentThread()));
 
-    AFSAcquireExcl( &pDevExt->Specific.Library.QueueLock,
+    AFSAcquireExcl( &pControlDevExt->Specific.Control.QueueLock,
                     TRUE);
 
-    lCount = InterlockedIncrement( &pDevExt->Specific.Library.QueueItemCount);
+    lCount = InterlockedIncrement( &pControlDevExt->Specific.Control.QueueItemCount);
 
-    AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+    AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                   AFS_TRACE_LEVEL_VERBOSE,
-                  "AFSInsertWorkitem Inserting work item %08lX Count %08lX\n",
+                  "AFSInsertWorkitem Inserting work item %p Count %d\n",
                   WorkItem,
-                  lCount);
+                  lCount));
 
-    if( pDevExt->Specific.Library.QueueTail != NULL) // queue already has nodes
+    if( pControlDevExt->Specific.Control.QueueTail != NULL) // queue already has nodes
     {
 
-        pDevExt->Specific.Library.QueueTail->next = WorkItem;
+        pControlDevExt->Specific.Control.QueueTail->next = WorkItem;
     }
     else // first node
     {
 
-        pDevExt->Specific.Library.QueueHead = WorkItem;
+        pControlDevExt->Specific.Control.QueueHead = WorkItem;
     }
 
     WorkItem->next = NULL;
-    pDevExt->Specific.Library.QueueTail = WorkItem;
+    pControlDevExt->Specific.Control.QueueTail = WorkItem;
 
     // indicate that the queue has nodes
-    KeSetEvent( &(pDevExt->Specific.Library.WorkerQueueHasItems),
+    KeSetEvent( &(pControlDevExt->Specific.Control.WorkerQueueHasItems),
                 0,
                 FALSE);
 
-    AFSReleaseResource( &pDevExt->Specific.Library.QueueLock);
+    AFSReleaseResource( &pControlDevExt->Specific.Control.QueueLock);
 
     return ntStatus;
 }
@@ -1830,48 +1935,48 @@ AFSInsertIOWorkitem( IN AFSWorkItem *WorkItem)
 {
 
     NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSDeviceExt *pDevExt = NULL;
+    AFSDeviceExt *pControlDevExt = NULL;
     LONG lCount;
 
-    pDevExt = (AFSDeviceExt *)AFSLibraryDeviceObject->DeviceExtension;
+    pControlDevExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
 
-    AFSDbgLogMsg( AFS_SUBSYSTEM_LOCK_PROCESSING,
+    AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
                   AFS_TRACE_LEVEL_VERBOSE,
-                  "AFSInsertIOWorkitem Acquiring Control QueueLock lock %08lX EXCL %08lX\n",
-                  &pDevExt->Specific.Library.IOQueueLock,
-                  PsGetCurrentThread());
+                  "AFSInsertIOWorkitem Acquiring Control QueueLock lock %p EXCL %08lX\n",
+                  &pControlDevExt->Specific.Control.IOQueueLock,
+                  PsGetCurrentThread()));
 
-    AFSAcquireExcl( &pDevExt->Specific.Library.IOQueueLock,
+    AFSAcquireExcl( &pControlDevExt->Specific.Control.IOQueueLock,
                     TRUE);
 
-    lCount = InterlockedIncrement( &pDevExt->Specific.Library.IOQueueItemCount);
+    lCount = InterlockedIncrement( &pControlDevExt->Specific.Control.IOQueueItemCount);
 
-    AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+    AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                   AFS_TRACE_LEVEL_VERBOSE,
-                  "AFSInsertWorkitem Inserting IO work item %08lX Count %08lX\n",
+                  "AFSInsertWorkitem Inserting IO work item %p Count %d\n",
                   WorkItem,
-                  lCount);
+                  lCount));
 
-    if( pDevExt->Specific.Library.IOQueueTail != NULL) // queue already has nodes
+    if( pControlDevExt->Specific.Control.IOQueueTail != NULL) // queue already has nodes
     {
 
-        pDevExt->Specific.Library.IOQueueTail->next = WorkItem;
+        pControlDevExt->Specific.Control.IOQueueTail->next = WorkItem;
     }
     else // first node
     {
 
-        pDevExt->Specific.Library.IOQueueHead = WorkItem;
+        pControlDevExt->Specific.Control.IOQueueHead = WorkItem;
     }
 
     WorkItem->next = NULL;
-    pDevExt->Specific.Library.IOQueueTail = WorkItem;
+    pControlDevExt->Specific.Control.IOQueueTail = WorkItem;
 
     // indicate that the queue has nodes
-    KeSetEvent( &(pDevExt->Specific.Library.IOWorkerQueueHasItems),
+    KeSetEvent( &(pControlDevExt->Specific.Control.IOWorkerQueueHasItems),
                 0,
                 FALSE);
 
-    AFSReleaseResource( &pDevExt->Specific.Library.IOQueueLock);
+    AFSReleaseResource( &pControlDevExt->Specific.Control.IOQueueLock);
 
     return ntStatus;
 }
@@ -1881,41 +1986,41 @@ AFSInsertWorkitemAtHead( IN AFSWorkItem *WorkItem)
 {
 
     NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSDeviceExt *pDevExt = NULL;
+    AFSDeviceExt *pControlDevExt = NULL;
     LONG lCount;
 
-    pDevExt = (AFSDeviceExt *)AFSLibraryDeviceObject->DeviceExtension;
+    pControlDevExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
 
-    AFSDbgLogMsg( AFS_SUBSYSTEM_LOCK_PROCESSING,
+    AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
                   AFS_TRACE_LEVEL_VERBOSE,
-                  "AFSInsertWorkitemAtHead Acquiring Control QueueLock lock %08lX EXCL %08lX\n",
-                  &pDevExt->Specific.Library.QueueLock,
-                  PsGetCurrentThread());
+                  "AFSInsertWorkitemAtHead Acquiring Control QueueLock lock %p EXCL %08lX\n",
+                  &pControlDevExt->Specific.Control.QueueLock,
+                  PsGetCurrentThread()));
 
-    AFSAcquireExcl( &pDevExt->Specific.Library.QueueLock,
+    AFSAcquireExcl( &pControlDevExt->Specific.Control.QueueLock,
                     TRUE);
 
-    WorkItem->next = pDevExt->Specific.Library.QueueHead;
+    WorkItem->next = pControlDevExt->Specific.Control.QueueHead;
 
-    pDevExt->Specific.Library.QueueHead = WorkItem;
+    pControlDevExt->Specific.Control.QueueHead = WorkItem;
 
-    lCount = InterlockedIncrement( &pDevExt->Specific.Library.QueueItemCount);
+    lCount = InterlockedIncrement( &pControlDevExt->Specific.Control.QueueItemCount);
 
-    AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+    AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                   AFS_TRACE_LEVEL_VERBOSE,
-                  "AFSInsertWorkitemAtHead Inserting work item %08lX Count %08lX\n",
+                  "AFSInsertWorkitemAtHead Inserting work item %p Count %d\n",
                   WorkItem,
-                  lCount);
+                  lCount));
 
     //
     // indicate that the queue has nodes
     //
 
-    KeSetEvent( &(pDevExt->Specific.Library.WorkerQueueHasItems),
+    KeSetEvent( &(pControlDevExt->Specific.Control.WorkerQueueHasItems),
                 0,
                 FALSE);
 
-    AFSReleaseResource( &pDevExt->Specific.Library.QueueLock);
+    AFSReleaseResource( &pControlDevExt->Specific.Control.QueueLock);
 
     return ntStatus;
 }
@@ -1924,42 +2029,41 @@ AFSWorkItem *
 AFSRemoveWorkItem()
 {
 
-    NTSTATUS ntStatus = STATUS_SUCCESS;
     AFSWorkItem        *pWorkItem = NULL;
-    AFSDeviceExt *pDevExt = NULL;
+    AFSDeviceExt *pControlDevExt = NULL;
     LONG lCount;
 
-    pDevExt = (AFSDeviceExt *)AFSLibraryDeviceObject->DeviceExtension;
+    pControlDevExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
 
-    AFSDbgLogMsg( AFS_SUBSYSTEM_LOCK_PROCESSING,
+    AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
                   AFS_TRACE_LEVEL_VERBOSE,
-                  "AFSRemoveWorkItem Acquiring Control QueueLock lock %08lX EXCL %08lX\n",
-                  &pDevExt->Specific.Library.QueueLock,
-                  PsGetCurrentThread());
+                  "AFSRemoveWorkItem Acquiring Control QueueLock lock %p EXCL %08lX\n",
+                  &pControlDevExt->Specific.Control.QueueLock,
+                  PsGetCurrentThread()));
 
-    AFSAcquireExcl( &pDevExt->Specific.Library.QueueLock,
+    AFSAcquireExcl( &pControlDevExt->Specific.Control.QueueLock,
                     TRUE);
 
-    if( pDevExt->Specific.Library.QueueHead != NULL) // queue has nodes
+    if( pControlDevExt->Specific.Control.QueueHead != NULL) // queue has nodes
     {
 
-        pWorkItem = pDevExt->Specific.Library.QueueHead;
+        pWorkItem = pControlDevExt->Specific.Control.QueueHead;
 
-        lCount = InterlockedDecrement( &pDevExt->Specific.Library.QueueItemCount);
+        lCount = InterlockedDecrement( &pControlDevExt->Specific.Control.QueueItemCount);
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                       AFS_TRACE_LEVEL_VERBOSE,
-                      "AFSRemoveWorkItem Removing work item %08lX Count %08lX Thread %08lX\n",
+                      "AFSRemoveWorkItem Removing work item %p Count %d Thread %08lX\n",
                       pWorkItem,
                       lCount,
-                      PsGetCurrentThreadId());
+                      PsGetCurrentThreadId()));
 
-        pDevExt->Specific.Library.QueueHead = pDevExt->Specific.Library.QueueHead->next;
+        pControlDevExt->Specific.Control.QueueHead = pControlDevExt->Specific.Control.QueueHead->next;
 
-        if( pDevExt->Specific.Library.QueueHead == NULL) // if queue just became empty
+        if( pControlDevExt->Specific.Control.QueueHead == NULL) // if queue just became empty
         {
 
-            pDevExt->Specific.Library.QueueTail = NULL;
+            pControlDevExt->Specific.Control.QueueTail = NULL;
         }
         else
         {
@@ -1968,13 +2072,13 @@ AFSRemoveWorkItem()
             // Wake up another worker
             //
 
-            KeSetEvent( &(pDevExt->Specific.Library.WorkerQueueHasItems),
+            KeSetEvent( &(pControlDevExt->Specific.Control.WorkerQueueHasItems),
                         0,
                         FALSE);
         }
     }
 
-    AFSReleaseResource( &pDevExt->Specific.Library.QueueLock);
+    AFSReleaseResource( &pControlDevExt->Specific.Control.QueueLock);
 
     return pWorkItem;
 }
@@ -1983,42 +2087,41 @@ AFSWorkItem *
 AFSRemoveIOWorkItem()
 {
 
-    NTSTATUS ntStatus = STATUS_SUCCESS;
     AFSWorkItem        *pWorkItem = NULL;
-    AFSDeviceExt *pDevExt = NULL;
+    AFSDeviceExt *pControlDevExt = NULL;
     LONG lCount;
 
-    pDevExt = (AFSDeviceExt *)AFSLibraryDeviceObject->DeviceExtension;
+    pControlDevExt = (AFSDeviceExt *)AFSControlDeviceObject->DeviceExtension;
 
-    AFSDbgLogMsg( AFS_SUBSYSTEM_LOCK_PROCESSING,
+    AFSDbgTrace(( AFS_SUBSYSTEM_LOCK_PROCESSING,
                   AFS_TRACE_LEVEL_VERBOSE,
-                  "AFSRemoveIOWorkItem Acquiring Control QueueLock lock %08lX EXCL %08lX\n",
-                  &pDevExt->Specific.Library.IOQueueLock,
-                  PsGetCurrentThread());
+                  "AFSRemoveIOWorkItem Acquiring Control QueueLock lock %p EXCL %08lX\n",
+                  &pControlDevExt->Specific.Control.IOQueueLock,
+                  PsGetCurrentThread()));
 
-    AFSAcquireExcl( &pDevExt->Specific.Library.IOQueueLock,
+    AFSAcquireExcl( &pControlDevExt->Specific.Control.IOQueueLock,
                     TRUE);
 
-    if( pDevExt->Specific.Library.IOQueueHead != NULL) // queue has nodes
+    if( pControlDevExt->Specific.Control.IOQueueHead != NULL) // queue has nodes
     {
 
-        pWorkItem = pDevExt->Specific.Library.IOQueueHead;
+        pWorkItem = pControlDevExt->Specific.Control.IOQueueHead;
 
-        lCount = InterlockedDecrement( &pDevExt->Specific.Library.IOQueueItemCount);
+        lCount = InterlockedDecrement( &pControlDevExt->Specific.Control.IOQueueItemCount);
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                       AFS_TRACE_LEVEL_VERBOSE,
-                      "AFSRemoveWorkItem Removing work item %08lX Count %08lX Thread %08lX\n",
+                      "AFSRemoveWorkItem Removing work item %p Count %d Thread %08lX\n",
                       pWorkItem,
                       lCount,
-                      PsGetCurrentThreadId());
+                      PsGetCurrentThreadId()));
 
-        pDevExt->Specific.Library.IOQueueHead = pDevExt->Specific.Library.IOQueueHead->next;
+        pControlDevExt->Specific.Control.IOQueueHead = pControlDevExt->Specific.Control.IOQueueHead->next;
 
-        if( pDevExt->Specific.Library.IOQueueHead == NULL) // if queue just became empty
+        if( pControlDevExt->Specific.Control.IOQueueHead == NULL) // if queue just became empty
         {
 
-            pDevExt->Specific.Library.IOQueueTail = NULL;
+            pControlDevExt->Specific.Control.IOQueueTail = NULL;
         }
         else
         {
@@ -2027,13 +2130,13 @@ AFSRemoveIOWorkItem()
             // Wake up another worker
             //
 
-            KeSetEvent( &(pDevExt->Specific.Library.IOWorkerQueueHasItems),
+            KeSetEvent( &(pControlDevExt->Specific.Control.IOWorkerQueueHasItems),
                         0,
                         FALSE);
         }
     }
 
-    AFSReleaseResource( &pDevExt->Specific.Library.IOQueueLock);
+    AFSReleaseResource( &pControlDevExt->Specific.Control.IOQueueLock);
 
     return pWorkItem;
 }
@@ -2043,7 +2146,6 @@ AFSQueueWorkerRequest( IN AFSWorkItem *WorkItem)
 {
 
     NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSDeviceExt *pDevExt = NULL;
     BOOLEAN bWait = BooleanFlagOn( WorkItem->RequestFlags, AFS_SYNCHRONOUS_REQUEST);
 
     //
@@ -2074,7 +2176,6 @@ AFSQueueIOWorkerRequest( IN AFSWorkItem *WorkItem)
 {
 
     NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSDeviceExt *pDevExt = NULL;
     BOOLEAN bWait = BooleanFlagOn( WorkItem->RequestFlags, AFS_SYNCHRONOUS_REQUEST);
 
     //
@@ -2105,7 +2206,6 @@ AFSQueueWorkerRequestAtHead( IN AFSWorkItem *WorkItem)
 {
 
     NTSTATUS ntStatus = STATUS_SUCCESS;
-    AFSDeviceExt *pDevExt = NULL;
     BOOLEAN bWait = BooleanFlagOn( WorkItem->RequestFlags, AFS_SYNCHRONOUS_REQUEST);
 
     //
@@ -2144,13 +2244,13 @@ AFSQueueFlushExtents( IN AFSFcb *Fcb,
     __try
     {
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                       AFS_TRACE_LEVEL_VERBOSE,
                       "AFSQueueFlushExtents Queuing request for FID %08lX-%08lX-%08lX-%08lX\n",
                       Fcb->ObjectInformation->FileId.Cell,
                       Fcb->ObjectInformation->FileId.Volume,
                       Fcb->ObjectInformation->FileId.Vnode,
-                      Fcb->ObjectInformation->FileId.Unique);
+                      Fcb->ObjectInformation->FileId.Unique));
 
         //
         // Increment our flush count here just to keep the number of items in the
@@ -2162,13 +2262,13 @@ AFSQueueFlushExtents( IN AFSFcb *Fcb,
         if( lCount > 3)
         {
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                           AFS_TRACE_LEVEL_VERBOSE,
                           "AFSQueueFlushExtents Max queued items for FID %08lX-%08lX-%08lX-%08lX\n",
                           Fcb->ObjectInformation->FileId.Cell,
                           Fcb->ObjectInformation->FileId.Volume,
                           Fcb->ObjectInformation->FileId.Vnode,
-                          Fcb->ObjectInformation->FileId.Unique);
+                          Fcb->ObjectInformation->FileId.Unique));
 
             try_return( ntStatus);
         }
@@ -2176,9 +2276,9 @@ AFSQueueFlushExtents( IN AFSFcb *Fcb,
         if( BooleanFlagOn( pRDRDeviceExt->DeviceFlags, AFS_DEVICE_FLAG_REDIRECTOR_SHUTDOWN))
         {
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                           AFS_TRACE_LEVEL_ERROR,
-                          "AFSQueueFlushExtents Failing request, in shutdown\n");
+                          "AFSQueueFlushExtents Failing request, in shutdown\n"));
 
             try_return( ntStatus = STATUS_TOO_LATE);
         }
@@ -2187,16 +2287,16 @@ AFSQueueFlushExtents( IN AFSFcb *Fcb,
         // Allocate our request structure and send it to the worker
         //
 
-        pWorkItem = (AFSWorkItem *)AFSLibExAllocatePoolWithTag( NonPagedPool,
-                                                                sizeof( AFSWorkItem),
-                                                                AFS_WORK_ITEM_TAG);
+        pWorkItem = (AFSWorkItem *)AFSExAllocatePoolWithTag( NonPagedPool,
+                                                             sizeof( AFSWorkItem),
+                                                             AFS_WORK_ITEM_TAG);
 
         if( pWorkItem == NULL)
         {
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                           AFS_TRACE_LEVEL_ERROR,
-                          "AFSQueueFlushExtents Failed to allocate work item\n");
+                          "AFSQueueFlushExtents Failed to allocate work item\n"));
 
             try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
         }
@@ -2232,33 +2332,33 @@ AFSQueueFlushExtents( IN AFSFcb *Fcb,
 
         lCount = InterlockedIncrement( &Fcb->OpenReferenceCount);
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_FCB_REF_COUNTING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_FCB_REF_COUNTING,
                       AFS_TRACE_LEVEL_VERBOSE,
-                      "AFSQueueFlushExtents Increment count on Fcb %08lX Cnt %d\n",
+                      "AFSQueueFlushExtents Increment count on Fcb %p Cnt %d\n",
                       Fcb,
-                      lCount);
+                      lCount));
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                       AFS_TRACE_LEVEL_VERBOSE,
-                      "AFSQueueFlushExtents Workitem %08lX for FID %08lX-%08lX-%08lX-%08lX\n",
+                      "AFSQueueFlushExtents Workitem %p for FID %08lX-%08lX-%08lX-%08lX\n",
                       pWorkItem,
                       Fcb->ObjectInformation->FileId.Cell,
                       Fcb->ObjectInformation->FileId.Volume,
                       Fcb->ObjectInformation->FileId.Vnode,
-                      Fcb->ObjectInformation->FileId.Unique);
+                      Fcb->ObjectInformation->FileId.Unique));
 
         ntStatus = AFSQueueWorkerRequest( pWorkItem);
 
 try_exit:
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                       AFS_TRACE_LEVEL_VERBOSE,
                       "AFSQueueFlushExtents Request complete Status %08lX FID %08lX-%08lX-%08lX-%08lX\n",
                       Fcb->ObjectInformation->FileId.Cell,
                       Fcb->ObjectInformation->FileId.Volume,
                       Fcb->ObjectInformation->FileId.Vnode,
                       Fcb->ObjectInformation->FileId.Unique,
-                      ntStatus);
+                      ntStatus));
 
         //
         // Remove the count we added above
@@ -2266,7 +2366,7 @@ try_exit:
 
         lCount = InterlockedDecrement( &Fcb->Specific.File.QueuedFlushCount);
 
-       ASSERT( lCount >= 0);
+        ASSERT( lCount >= 0);
 
         if( lCount == 0)
         {
@@ -2287,17 +2387,18 @@ try_exit:
                 ExFreePoolWithTag( pWorkItem, AFS_WORK_ITEM_TAG);
             }
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                           AFS_TRACE_LEVEL_ERROR,
-                          "AFSQueueFlushExtents Failed to queue request Status %08lX\n", ntStatus);
+                          "AFSQueueFlushExtents Failed to queue request Status %08lX\n",
+                          ntStatus));
         }
     }
     __except( AFSExceptionFilter( __FUNCTION__, GetExceptionCode(), GetExceptionInformation()) )
     {
 
-        AFSDbgLogMsg( 0,
+        AFSDbgTrace(( 0,
                       0,
-                      "EXCEPTION - AFSQueueFlushExtents\n");
+                      "EXCEPTION - AFSQueueFlushExtents\n"));
 
         AFSDumpTraceFilesFnc();
     }
@@ -2315,15 +2416,15 @@ AFSQueueGlobalRootEnumeration()
     __try
     {
 
-        pWorkItem = (AFSWorkItem *) AFSLibExAllocatePoolWithTag( NonPagedPool,
-                                                                 sizeof(AFSWorkItem),
-                                                                 AFS_WORK_ITEM_TAG);
+        pWorkItem = (AFSWorkItem *) AFSExAllocatePoolWithTag( NonPagedPool,
+                                                              sizeof(AFSWorkItem),
+                                                              AFS_WORK_ITEM_TAG);
         if (NULL == pWorkItem)
         {
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_IO_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING,
                           AFS_TRACE_LEVEL_ERROR,
-                          "AFSQueueGlobalRootEnumeration Failed to allocate work item\n");
+                          "AFSQueueGlobalRootEnumeration Failed to allocate work item\n"));
 
             try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES );
         }
@@ -2335,19 +2436,19 @@ AFSQueueGlobalRootEnumeration()
 
         pWorkItem->RequestType = AFS_WORK_ENUMERATE_GLOBAL_ROOT;
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                       AFS_TRACE_LEVEL_VERBOSE,
-                      "AFSQueueGlobalRootEnumeration Workitem %08lX\n",
-                      pWorkItem);
+                      "AFSQueueGlobalRootEnumeration Workitem %p\n",
+                      pWorkItem));
 
         ntStatus = AFSQueueWorkerRequest( pWorkItem);
 
 try_exit:
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                       AFS_TRACE_LEVEL_VERBOSE,
                       "AFSQueueGlobalRootEnumeration Request complete Status %08lX\n",
-                      ntStatus);
+                      ntStatus));
 
         if( !NT_SUCCESS( ntStatus))
         {
@@ -2358,18 +2459,18 @@ try_exit:
                 ExFreePoolWithTag( pWorkItem, AFS_WORK_ITEM_TAG);
             }
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                           AFS_TRACE_LEVEL_ERROR,
                           "AFSQueueGlobalRootEnumeration Failed to queue request Status %08lX\n",
-                          ntStatus);
+                          ntStatus));
         }
     }
     __except( AFSExceptionFilter( __FUNCTION__, GetExceptionCode(), GetExceptionInformation()) )
     {
 
-        AFSDbgLogMsg( 0,
+        AFSDbgTrace(( 0,
                       0,
-                      "EXCEPTION - AFSQueueGlobalRootEnumeration\n");
+                      "EXCEPTION - AFSQueueGlobalRootEnumeration\n"));
 
         AFSDumpTraceFilesFnc();
     }
@@ -2396,9 +2497,9 @@ AFSQueueStartIos( IN PFILE_OBJECT CacheFileObject,
         if( BooleanFlagOn( pRDRDeviceExt->DeviceFlags, AFS_DEVICE_FLAG_REDIRECTOR_SHUTDOWN))
         {
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                           AFS_TRACE_LEVEL_ERROR,
-                          "AFSQueueStartIos Failing request, in shutdown\n");
+                          "AFSQueueStartIos Failing request, in shutdown\n"));
 
             try_return( ntStatus = STATUS_TOO_LATE);
         }
@@ -2407,16 +2508,16 @@ AFSQueueStartIos( IN PFILE_OBJECT CacheFileObject,
         // Allocate our request structure and send it to the worker
         //
 
-        pWorkItem = (AFSWorkItem *)AFSLibExAllocatePoolWithTag( NonPagedPool,
-                                                                sizeof( AFSWorkItem),
-                                                                AFS_WORK_ITEM_TAG);
+        pWorkItem = (AFSWorkItem *)AFSExAllocatePoolWithTag( NonPagedPool,
+                                                             sizeof( AFSWorkItem),
+                                                             AFS_WORK_ITEM_TAG);
 
         if( pWorkItem == NULL)
         {
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                           AFS_TRACE_LEVEL_ERROR,
-                          "AFSQueueStartIos Failed to allocate work item\n");
+                          "AFSQueueStartIos Failed to allocate work item\n"));
 
             try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES);
         }
@@ -2446,19 +2547,19 @@ AFSQueueStartIos( IN PFILE_OBJECT CacheFileObject,
 
         pWorkItem->Specific.CacheAccess.GatherIo = GatherIo;
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                       AFS_TRACE_LEVEL_VERBOSE,
-                      "AFSQueueStartIos Queuing IO Workitem %08lX\n",
-                      pWorkItem);
+                      "AFSQueueStartIos Queuing IO Workitem %p\n",
+                      pWorkItem));
 
         ntStatus = AFSQueueIOWorkerRequest( pWorkItem);
 
 try_exit:
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                       AFS_TRACE_LEVEL_VERBOSE,
                       "AFSQueueStartIos Request complete Status %08lX\n",
-                      ntStatus);
+                      ntStatus));
 
         if( !NT_SUCCESS( ntStatus))
         {
@@ -2473,9 +2574,9 @@ try_exit:
     __except( AFSExceptionFilter( __FUNCTION__, GetExceptionCode(), GetExceptionInformation()) )
     {
 
-        AFSDbgLogMsg( 0,
+        AFSDbgTrace(( 0,
                       0,
-                      "EXCEPTION - AFSQueueStartIos\n");
+                      "EXCEPTION - AFSQueueStartIos\n"));
 
         AFSDumpTraceFilesFnc();
     }
@@ -2494,15 +2595,15 @@ AFSQueueInvalidateObject( IN AFSObjectInfoCB *ObjectInfo,
     __try
     {
 
-        pWorkItem = (AFSWorkItem *) AFSLibExAllocatePoolWithTag( NonPagedPool,
-                                                                 sizeof(AFSWorkItem),
-                                                                 AFS_WORK_ITEM_TAG);
+        pWorkItem = (AFSWorkItem *) AFSExAllocatePoolWithTag( NonPagedPool,
+                                                              sizeof(AFSWorkItem),
+                                                              AFS_WORK_ITEM_TAG);
         if (NULL == pWorkItem)
         {
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_IO_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING,
                           AFS_TRACE_LEVEL_ERROR,
-                          "AFSQueueInvalidateObject Failed to allocate work item\n");
+                          "AFSQueueInvalidateObject Failed to allocate work item\n"));
 
             try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES );
         }
@@ -2518,19 +2619,19 @@ AFSQueueInvalidateObject( IN AFSObjectInfoCB *ObjectInfo,
 
         pWorkItem->Specific.Invalidate.InvalidateReason = InvalidateReason;
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                       AFS_TRACE_LEVEL_VERBOSE,
-                      "AFSQueueInvalidateObject Workitem %08lX\n",
-                      pWorkItem);
+                      "AFSQueueInvalidateObject Workitem %p\n",
+                      pWorkItem));
 
         ntStatus = AFSQueueWorkerRequest( pWorkItem);
 
 try_exit:
 
-        AFSDbgLogMsg( AFS_SUBSYSTEM_WORKER_PROCESSING,
+        AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
                       AFS_TRACE_LEVEL_VERBOSE,
                       "AFSQueueInvalidateObject Request complete Status %08lX\n",
-                      ntStatus);
+                      ntStatus));
 
         if( !NT_SUCCESS( ntStatus))
         {
@@ -2540,21 +2641,163 @@ try_exit:
                 ExFreePoolWithTag( pWorkItem, AFS_WORK_ITEM_TAG);
             }
 
-            AFSDbgLogMsg( AFS_SUBSYSTEM_FILE_PROCESSING,
+            AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
                           AFS_TRACE_LEVEL_ERROR,
                           "AFSQueueInvalidateObject Failed to queue request Status %08lX\n",
-                          ntStatus);
+                          ntStatus));
         }
     }
     __except( AFSExceptionFilter( __FUNCTION__, GetExceptionCode(), GetExceptionInformation()) )
     {
 
-        AFSDbgLogMsg( 0,
+        AFSDbgTrace(( 0,
                       0,
-                      "EXCEPTION - AFSQueueInvalidateObject\n");
+                      "EXCEPTION - AFSQueueInvalidateObject\n"));
 
         AFSDumpTraceFilesFnc();
     }
 
     return ntStatus;
 }
+
+NTSTATUS
+AFSDeferWrite( IN PDEVICE_OBJECT DeviceObject,
+               IN PFILE_OBJECT FileObject,
+               IN HANDLE CallingUser,
+               IN PIRP Irp,
+               IN ULONG BytesToWrite,
+               IN BOOLEAN bRetrying)
+{
+    NTSTATUS ntStatus = STATUS_SUCCESS;
+    AFSWorkItem *pWorkItem = NULL;
+
+    __try
+    {
+
+        //
+        // Pin the user buffer (first time round only - AFSLockSystemBuffer is
+        // idempotent)
+        //
+
+        if ( NULL == AFSLockSystemBuffer( Irp, BytesToWrite ))
+        {
+
+            AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING,
+                          AFS_TRACE_LEVEL_ERROR,
+                          "%s Could not pin user memory item\n",
+                          __FUNCTION__));
+
+            try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES );
+        }
+
+        pWorkItem = (AFSWorkItem *) AFSExAllocatePoolWithTag( NonPagedPool,
+                                                              sizeof(AFSWorkItem),
+                                                              AFS_WORK_ITEM_TAG);
+
+        if (NULL == pWorkItem)
+        {
+
+            AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING,
+                          AFS_TRACE_LEVEL_ERROR,
+                          "%s Failed to allocate work item\n",
+                          __FUNCTION__));
+
+            try_return( ntStatus = STATUS_INSUFFICIENT_RESOURCES );
+        }
+
+        RtlZeroMemory( pWorkItem,
+                       sizeof(AFSWorkItem));
+
+        pWorkItem->Size = sizeof( AFSWorkItem);
+
+        pWorkItem->RequestType = AFS_WORK_DEFERRED_WRITE;
+
+        pWorkItem->Specific.AsynchIo.CallingProcess = CallingUser;
+
+        pWorkItem->Specific.AsynchIo.Device = DeviceObject;
+
+        pWorkItem->Specific.AsynchIo.Irp = Irp;
+
+        AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING | AFS_SUBSYSTEM_WORKER_PROCESSING,
+                      AFS_TRACE_LEVEL_VERBOSE,
+                      "%s Workitem %p\n",
+                      __FUNCTION__,
+                      pWorkItem));
+
+        CcDeferWrite( FileObject, AFSPostedDeferredWrite, pWorkItem, NULL, BytesToWrite, bRetrying);
+
+        IoMarkIrpPending(Irp);
+
+        ntStatus = STATUS_PENDING;
+    }
+    __except( AFSExceptionFilter( __FUNCTION__, GetExceptionCode(), GetExceptionInformation()) )
+    {
+
+        AFSDbgTrace(( 0,
+                      0,
+                      "EXCEPTION - %s \n",
+                      __FUNCTION__));
+
+        ntStatus = GetExceptionCode();
+    }
+
+try_exit:
+
+    AFSDbgTrace(( AFS_SUBSYSTEM_WORKER_PROCESSING,
+                  AFS_TRACE_LEVEL_VERBOSE,
+                  "%s complete Status %08lX\n",
+                  __FUNCTION__,
+                  ntStatus));
+
+    if( !NT_SUCCESS( ntStatus))
+    {
+
+        if( pWorkItem != NULL)
+        {
+
+            ExFreePoolWithTag( pWorkItem, AFS_WORK_ITEM_TAG);
+        }
+
+        AFSDbgTrace(( AFS_SUBSYSTEM_FILE_PROCESSING,
+                      AFS_TRACE_LEVEL_ERROR,
+                      "%s Failed to queue request Status %08lX\n",
+                      __FUNCTION__,
+                      ntStatus));
+    }
+
+    return ntStatus;
+}
+
+static
+VOID
+AFSPostedDeferredWrite( IN PVOID Context1,
+                        IN PVOID Context2)
+{
+    UNREFERENCED_PARAMETER( Context2);
+    NTSTATUS ntStatus;
+
+    AFSWorkItem *pWorkItem = (AFSWorkItem *) Context1;
+
+    AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING | AFS_SUBSYSTEM_WORKER_PROCESSING,
+                  AFS_TRACE_LEVEL_ERROR,
+                  "%s Workitem %p\n",
+                  __FUNCTION__,
+                  pWorkItem));
+
+    ntStatus = AFSQueueIOWorkerRequest( pWorkItem);
+
+    if (!NT_SUCCESS( ntStatus))
+    {
+
+        AFSCompleteRequest( pWorkItem->Specific.AsynchIo.Irp, ntStatus);
+
+        ExFreePoolWithTag( pWorkItem, AFS_WORK_ITEM_TAG);
+
+        AFSDbgTrace(( AFS_SUBSYSTEM_IO_PROCESSING | AFS_SUBSYSTEM_WORKER_PROCESSING,
+                      AFS_TRACE_LEVEL_ERROR,
+                      "%s (%p) Failed to queue request Status %08lX\n",
+                      __FUNCTION__,
+                      pWorkItem->Specific.AsynchIo.Irp,
+                      ntStatus));
+    }
+}