xref: /reactos/drivers/filesystems/vfatfs/misc.c (revision 542e9f2b)
1 /*
2  * PROJECT:     VFAT Filesystem
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Misc routines
5  * COPYRIGHT:   Copyright 1998 Jason Filby <jasonfilby@yahoo.com>
6  *              Copyright 2015-2018 Pierre Schweitzer <pierre@reactos.org>
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include "vfat.h"
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS ******************************************************************/
17 
18 const char* MajorFunctionNames[] =
19 {
20     "IRP_MJ_CREATE",
21     "IRP_MJ_CREATE_NAMED_PIPE",
22     "IRP_MJ_CLOSE",
23     "IRP_MJ_READ",
24     "IRP_MJ_WRITE",
25     "IRP_MJ_QUERY_INFORMATION",
26     "IRP_MJ_SET_INFORMATION",
27     "IRP_MJ_QUERY_EA",
28     "IRP_MJ_SET_EA",
29     "IRP_MJ_FLUSH_BUFFERS",
30     "IRP_MJ_QUERY_VOLUME_INFORMATION",
31     "IRP_MJ_SET_VOLUME_INFORMATION",
32     "IRP_MJ_DIRECTORY_CONTROL",
33     "IRP_MJ_FILE_SYSTEM_CONTROL",
34     "IRP_MJ_DEVICE_CONTROL",
35     "IRP_MJ_INTERNAL_DEVICE_CONTROL",
36     "IRP_MJ_SHUTDOWN",
37     "IRP_MJ_LOCK_CONTROL",
38     "IRP_MJ_CLEANUP",
39     "IRP_MJ_CREATE_MAILSLOT",
40     "IRP_MJ_QUERY_SECURITY",
41     "IRP_MJ_SET_SECURITY",
42     "IRP_MJ_POWER",
43     "IRP_MJ_SYSTEM_CONTROL",
44     "IRP_MJ_DEVICE_CHANGE",
45     "IRP_MJ_QUERY_QUOTA",
46     "IRP_MJ_SET_QUOTA",
47     "IRP_MJ_PNP",
48     "IRP_MJ_MAXIMUM_FUNCTION"
49 };
50 
51 static LONG QueueCount = 0;
52 
53 static VOID VfatFreeIrpContext(PVFAT_IRP_CONTEXT);
54 static PVFAT_IRP_CONTEXT VfatAllocateIrpContext(PDEVICE_OBJECT, PIRP);
55 static NTSTATUS VfatQueueRequest(PVFAT_IRP_CONTEXT);
56 
57 /* FUNCTIONS ****************************************************************/
58 
59 static
60 NTSTATUS
VfatLockControl(IN PVFAT_IRP_CONTEXT IrpContext)61 VfatLockControl(
62     IN PVFAT_IRP_CONTEXT IrpContext)
63 {
64     PVFATFCB Fcb;
65     NTSTATUS Status;
66 
67     DPRINT("VfatLockControl(IrpContext %p)\n", IrpContext);
68 
69     ASSERT(IrpContext);
70 
71     Fcb = (PVFATFCB)IrpContext->FileObject->FsContext;
72 
73     if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
74     {
75         return STATUS_INVALID_DEVICE_REQUEST;
76     }
77 
78     if (vfatFCBIsDirectory(Fcb))
79     {
80         return STATUS_INVALID_PARAMETER;
81     }
82 
83     IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
84     Status = FsRtlProcessFileLock(&Fcb->FileLock,
85                                   IrpContext->Irp,
86                                   NULL);
87     return Status;
88 }
89 
90 static
91 NTSTATUS
VfatDeviceControl(IN PVFAT_IRP_CONTEXT IrpContext)92 VfatDeviceControl(
93     IN PVFAT_IRP_CONTEXT IrpContext)
94 {
95     IoSkipCurrentIrpStackLocation(IrpContext->Irp);
96 
97     IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
98 
99     return IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
100 }
101 
102 static
103 NTSTATUS
VfatDispatchRequest(IN PVFAT_IRP_CONTEXT IrpContext)104 VfatDispatchRequest(
105     IN PVFAT_IRP_CONTEXT IrpContext)
106 {
107     NTSTATUS Status;
108     BOOLEAN QueueIrp, CompleteIrp;
109 
110     DPRINT("VfatDispatchRequest (IrpContext %p), is called for %s\n", IrpContext,
111            IrpContext->MajorFunction >= IRP_MJ_MAXIMUM_FUNCTION ? "????" : MajorFunctionNames[IrpContext->MajorFunction]);
112 
113     ASSERT(IrpContext);
114 
115     FsRtlEnterFileSystem();
116 
117     switch (IrpContext->MajorFunction)
118     {
119         case IRP_MJ_CLOSE:
120             Status = VfatClose(IrpContext);
121             break;
122 
123         case IRP_MJ_CREATE:
124             Status = VfatCreate(IrpContext);
125             break;
126 
127         case IRP_MJ_READ:
128             Status = VfatRead(IrpContext);
129             break;
130 
131         case IRP_MJ_WRITE:
132             Status = VfatWrite(&IrpContext);
133             break;
134 
135         case IRP_MJ_FILE_SYSTEM_CONTROL:
136             Status = VfatFileSystemControl(IrpContext);
137             break;
138 
139         case IRP_MJ_QUERY_INFORMATION:
140             Status = VfatQueryInformation(IrpContext);
141             break;
142 
143         case IRP_MJ_SET_INFORMATION:
144             Status = VfatSetInformation(IrpContext);
145             break;
146 
147         case IRP_MJ_DIRECTORY_CONTROL:
148             Status = VfatDirectoryControl(IrpContext);
149             break;
150 
151         case IRP_MJ_QUERY_VOLUME_INFORMATION:
152             Status = VfatQueryVolumeInformation(IrpContext);
153             break;
154 
155         case IRP_MJ_SET_VOLUME_INFORMATION:
156             Status = VfatSetVolumeInformation(IrpContext);
157             break;
158 
159         case IRP_MJ_LOCK_CONTROL:
160             Status = VfatLockControl(IrpContext);
161             break;
162 
163         case IRP_MJ_DEVICE_CONTROL:
164             Status = VfatDeviceControl(IrpContext);
165             break;
166 
167         case IRP_MJ_CLEANUP:
168             Status = VfatCleanup(IrpContext);
169             break;
170 
171         case IRP_MJ_FLUSH_BUFFERS:
172             Status = VfatFlush(IrpContext);
173             break;
174 
175         case IRP_MJ_PNP:
176             Status = VfatPnp(IrpContext);
177             break;
178 
179         default:
180             DPRINT1("Unexpected major function %x\n", IrpContext->MajorFunction);
181             Status = STATUS_DRIVER_INTERNAL_ERROR;
182     }
183 
184     if (IrpContext != NULL)
185     {
186         QueueIrp = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_QUEUE);
187         CompleteIrp = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_COMPLETE);
188 
189         ASSERT((!CompleteIrp && !QueueIrp) ||
190                (CompleteIrp && !QueueIrp) ||
191                (!CompleteIrp && QueueIrp));
192 
193         if (CompleteIrp)
194         {
195             IrpContext->Irp->IoStatus.Status = Status;
196             IoCompleteRequest(IrpContext->Irp, IrpContext->PriorityBoost);
197         }
198 
199         if (QueueIrp)
200         {
201             /* Reset our status flags before queueing the IRP */
202             IrpContext->Flags |= IRPCONTEXT_COMPLETE;
203             IrpContext->Flags &= ~IRPCONTEXT_QUEUE;
204             Status = VfatQueueRequest(IrpContext);
205         }
206         else
207         {
208             /* Unless the IRP was queued, always free the IRP context */
209             VfatFreeIrpContext(IrpContext);
210         }
211     }
212 
213     FsRtlExitFileSystem();
214 
215     return Status;
216 }
217 
218 VOID
219 NTAPI
VfatHandleDeferredWrite(IN PVOID IrpContext,IN PVOID Unused)220 VfatHandleDeferredWrite(
221     IN PVOID IrpContext,
222     IN PVOID Unused)
223 {
224     VfatDispatchRequest((PVFAT_IRP_CONTEXT)IrpContext);
225 }
226 
227 NTSTATUS
228 NTAPI
VfatBuildRequest(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)229 VfatBuildRequest(
230     IN PDEVICE_OBJECT DeviceObject,
231     IN PIRP Irp)
232 {
233     NTSTATUS Status;
234     PVFAT_IRP_CONTEXT IrpContext;
235 
236     DPRINT("VfatBuildRequest (DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
237 
238     ASSERT(DeviceObject);
239     ASSERT(Irp);
240 
241     IrpContext = VfatAllocateIrpContext(DeviceObject, Irp);
242     if (IrpContext == NULL)
243     {
244         Status = STATUS_INSUFFICIENT_RESOURCES;
245         Irp->IoStatus.Status = Status;
246         IoCompleteRequest(Irp, IO_NO_INCREMENT);
247     }
248     else
249     {
250         Status = VfatDispatchRequest(IrpContext);
251     }
252     return Status;
253 }
254 
255 static
256 VOID
VfatFreeIrpContext(PVFAT_IRP_CONTEXT IrpContext)257 VfatFreeIrpContext(
258     PVFAT_IRP_CONTEXT IrpContext)
259 {
260     ASSERT(IrpContext);
261     ExFreeToNPagedLookasideList(&VfatGlobalData->IrpContextLookasideList, IrpContext);
262 }
263 
264 static
265 PVFAT_IRP_CONTEXT
VfatAllocateIrpContext(PDEVICE_OBJECT DeviceObject,PIRP Irp)266 VfatAllocateIrpContext(
267     PDEVICE_OBJECT DeviceObject,
268     PIRP Irp)
269 {
270     PVFAT_IRP_CONTEXT IrpContext;
271     /*PIO_STACK_LOCATION Stack;*/
272     UCHAR MajorFunction;
273 
274     DPRINT("VfatAllocateIrpContext(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
275 
276     ASSERT(DeviceObject);
277     ASSERT(Irp);
278 
279     IrpContext = ExAllocateFromNPagedLookasideList(&VfatGlobalData->IrpContextLookasideList);
280     if (IrpContext)
281     {
282         RtlZeroMemory(IrpContext, sizeof(VFAT_IRP_CONTEXT));
283         IrpContext->Irp = Irp;
284         IrpContext->DeviceObject = DeviceObject;
285         IrpContext->DeviceExt = DeviceObject->DeviceExtension;
286         IrpContext->Stack = IoGetCurrentIrpStackLocation(Irp);
287         ASSERT(IrpContext->Stack);
288         MajorFunction = IrpContext->MajorFunction = IrpContext->Stack->MajorFunction;
289         IrpContext->MinorFunction = IrpContext->Stack->MinorFunction;
290         IrpContext->FileObject = IrpContext->Stack->FileObject;
291         IrpContext->Flags = IRPCONTEXT_COMPLETE;
292 
293         /* Easy cases that can wait */
294         if (MajorFunction == IRP_MJ_CLEANUP ||
295             MajorFunction == IRP_MJ_CREATE ||
296             MajorFunction == IRP_MJ_SHUTDOWN ||
297             MajorFunction == IRP_MJ_CLOSE /* likely to be fixed */)
298         {
299             SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
300         }
301         /* Cases that can wait if synchronous IRP */
302         else if ((MajorFunction == IRP_MJ_DEVICE_CONTROL ||
303                   MajorFunction == IRP_MJ_QUERY_INFORMATION ||
304                   MajorFunction == IRP_MJ_SET_INFORMATION ||
305                   MajorFunction == IRP_MJ_FLUSH_BUFFERS ||
306                   MajorFunction == IRP_MJ_LOCK_CONTROL ||
307                   MajorFunction == IRP_MJ_QUERY_VOLUME_INFORMATION ||
308                   MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION ||
309                   MajorFunction == IRP_MJ_DIRECTORY_CONTROL ||
310                   MajorFunction == IRP_MJ_WRITE ||
311                   MajorFunction == IRP_MJ_READ) &&
312                  IoIsOperationSynchronous(Irp))
313         {
314             SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
315         }
316         /* Cases that can wait if synchronous or if no FO */
317         else if ((MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL ||
318                   MajorFunction == IRP_MJ_PNP) &&
319                  (IoGetCurrentIrpStackLocation(Irp)->FileObject == NULL ||
320                   IoIsOperationSynchronous(Irp)))
321         {
322             SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
323         }
324 
325         KeInitializeEvent(&IrpContext->Event, NotificationEvent, FALSE);
326         IrpContext->RefCount = 0;
327         IrpContext->PriorityBoost = IO_NO_INCREMENT;
328     }
329     return IrpContext;
330 }
331 
332 static WORKER_THREAD_ROUTINE VfatDoRequest;
333 
334 static
335 VOID
336 NTAPI
VfatDoRequest(PVOID Context)337 VfatDoRequest(
338     PVOID Context)
339 {
340     PVFAT_IRP_CONTEXT IrpContext = Context;
341     PDEVICE_EXTENSION DeviceExt;
342     KIRQL OldIrql;
343 
344     InterlockedDecrement(&QueueCount);
345 
346     if (IrpContext->Stack->FileObject != NULL)
347     {
348         DeviceExt = IrpContext->Stack->DeviceObject->DeviceExtension;
349         ObReferenceObject(DeviceExt->VolumeDevice);
350     }
351 
352     do
353     {
354         DPRINT("VfatDoRequest(IrpContext %p), MajorFunction %x, %d\n",
355                IrpContext, IrpContext->MajorFunction, QueueCount);
356         VfatDispatchRequest(IrpContext);
357         IrpContext = NULL;
358 
359         /* Now process any overflow items */
360         if (DeviceExt != NULL)
361         {
362             KeAcquireSpinLock(&DeviceExt->OverflowQueueSpinLock, &OldIrql);
363             if (DeviceExt->OverflowQueueCount != 0)
364             {
365                 IrpContext = CONTAINING_RECORD(RemoveHeadList(&DeviceExt->OverflowQueue),
366                                                VFAT_IRP_CONTEXT,
367                                                WorkQueueItem.List);
368                 DeviceExt->OverflowQueueCount--;
369                 DPRINT("Processing overflow item for IRP %p context %p (%lu)\n",
370                        IrpContext->Irp, IrpContext, DeviceExt->OverflowQueueCount);
371             }
372             else
373             {
374                 ASSERT(IsListEmpty(&DeviceExt->OverflowQueue));
375                 DeviceExt->PostedRequestCount--;
376             }
377             KeReleaseSpinLock(&DeviceExt->OverflowQueueSpinLock, OldIrql);
378         }
379     } while (IrpContext != NULL);
380 
381     if (DeviceExt != NULL)
382     {
383         ObDereferenceObject(DeviceExt->VolumeDevice);
384     }
385 }
386 
387 static
388 NTSTATUS
VfatQueueRequest(PVFAT_IRP_CONTEXT IrpContext)389 VfatQueueRequest(
390     PVFAT_IRP_CONTEXT IrpContext)
391 {
392     PDEVICE_EXTENSION DeviceExt;
393     KIRQL OldIrql;
394     BOOLEAN Overflow;
395 
396     InterlockedIncrement(&QueueCount);
397     DPRINT("VfatQueueRequest(IrpContext %p), %d\n", IrpContext, QueueCount);
398 
399     ASSERT(IrpContext != NULL);
400     ASSERT(IrpContext->Irp != NULL);
401     ASSERT(!(IrpContext->Flags & IRPCONTEXT_QUEUE) &&
402            (IrpContext->Flags & IRPCONTEXT_COMPLETE));
403 
404     Overflow = FALSE;
405     IrpContext->Flags |= IRPCONTEXT_CANWAIT;
406     IoMarkIrpPending(IrpContext->Irp);
407 
408     /* We should not block more than two worker threads per volume,
409      * or we might stop Cc from doing the work to unblock us.
410      * Add additional requests into the overflow queue instead and process
411      * them all in an existing worker thread (see VfatDoRequest above).
412      */
413     if (IrpContext->Stack->FileObject != NULL)
414     {
415         DeviceExt = IrpContext->Stack->DeviceObject->DeviceExtension;
416         KeAcquireSpinLock(&DeviceExt->OverflowQueueSpinLock, &OldIrql);
417         if (DeviceExt->PostedRequestCount > 2)
418         {
419             DeviceExt->OverflowQueueCount++;
420             DPRINT("Queue overflow. Adding IRP %p context %p to overflow queue (%lu)\n",
421                    IrpContext->Irp, IrpContext, DeviceExt->OverflowQueueCount);
422             InsertTailList(&DeviceExt->OverflowQueue,
423                            &IrpContext->WorkQueueItem.List);
424             Overflow = TRUE;
425         }
426         else
427         {
428             DeviceExt->PostedRequestCount++;
429         }
430         KeReleaseSpinLock(&DeviceExt->OverflowQueueSpinLock, OldIrql);
431     }
432 
433     if (!Overflow)
434     {
435         ExInitializeWorkItem(&IrpContext->WorkQueueItem, VfatDoRequest, IrpContext);
436         ExQueueWorkItem(&IrpContext->WorkQueueItem, CriticalWorkQueue);
437     }
438 
439     return STATUS_PENDING;
440 }
441 
442 PVOID
VfatGetUserBuffer(IN PIRP Irp,IN BOOLEAN Paging)443 VfatGetUserBuffer(
444     IN PIRP Irp,
445     IN BOOLEAN Paging)
446 {
447     ASSERT(Irp);
448 
449     if (Irp->MdlAddress)
450     {
451         return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, (Paging ? HighPagePriority : NormalPagePriority));
452     }
453     else
454     {
455         return Irp->UserBuffer;
456     }
457 }
458 
459 NTSTATUS
VfatLockUserBuffer(IN PIRP Irp,IN ULONG Length,IN LOCK_OPERATION Operation)460 VfatLockUserBuffer(
461     IN PIRP Irp,
462     IN ULONG Length,
463     IN LOCK_OPERATION Operation)
464 {
465     ASSERT(Irp);
466 
467     if (Irp->MdlAddress)
468     {
469         return STATUS_SUCCESS;
470     }
471 
472     IoAllocateMdl(Irp->UserBuffer, Length, FALSE, FALSE, Irp);
473 
474     if (!Irp->MdlAddress)
475     {
476         return STATUS_INSUFFICIENT_RESOURCES;
477     }
478 
479     _SEH2_TRY
480     {
481         MmProbeAndLockPages(Irp->MdlAddress, Irp->RequestorMode, Operation);
482     }
483     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
484     {
485         IoFreeMdl(Irp->MdlAddress);
486         Irp->MdlAddress = NULL;
487         _SEH2_YIELD(return _SEH2_GetExceptionCode());
488     }
489     _SEH2_END;
490 
491     return STATUS_SUCCESS;
492 }
493 
494 BOOLEAN
VfatCheckForDismount(IN PDEVICE_EXTENSION DeviceExt,IN BOOLEAN Force)495 VfatCheckForDismount(
496     IN PDEVICE_EXTENSION DeviceExt,
497     IN BOOLEAN Force)
498 {
499     KIRQL OldIrql;
500     ULONG UnCleanCount;
501     PVPB Vpb;
502     BOOLEAN Delete;
503 
504     DPRINT1("VfatCheckForDismount(%p, %u)\n", DeviceExt, Force);
505 
506     /* If the VCB is OK (not under uninitialization) and we don't force dismount, do nothing */
507     if (BooleanFlagOn(DeviceExt->Flags, VCB_GOOD) && !Force)
508     {
509         return FALSE;
510     }
511 
512     /*
513      * NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
514      * as well as MS' fastfat, perform a comparison check of the current VCB's
515      * VPB ReferenceCount with some sort of "dangling"/"residual" open count,
516      * depending on whether or not we are in IRP_MJ_CREATE.
517      * It seems to be related to the fact that the volume root directory as
518      * well as auxiliary data stream(s) are still opened, and only these are
519      * allowed to be opened at that moment. After analysis it appears that for
520      * the ReactOS' vfatfs, this number is equal to "2".
521      */
522     UnCleanCount = 2;
523 
524     /* Lock VPB */
525     IoAcquireVpbSpinLock(&OldIrql);
526 
527     /* Reference it and check if a create is being done */
528     Vpb = DeviceExt->IoVPB;
529     DPRINT("Vpb->ReferenceCount = %d\n", Vpb->ReferenceCount);
530     if (Vpb->ReferenceCount != UnCleanCount || DeviceExt->OpenHandleCount != 0)
531     {
532         /* If we force-unmount, copy the VPB to our local own to prepare later dismount */
533         if (Force && Vpb->RealDevice->Vpb == Vpb && DeviceExt->SpareVPB != NULL)
534         {
535             RtlZeroMemory(DeviceExt->SpareVPB, sizeof(VPB));
536             DeviceExt->SpareVPB->Type = IO_TYPE_VPB;
537             DeviceExt->SpareVPB->Size = sizeof(VPB);
538             DeviceExt->SpareVPB->RealDevice = DeviceExt->IoVPB->RealDevice;
539             DeviceExt->SpareVPB->DeviceObject = NULL;
540             DeviceExt->SpareVPB->Flags = DeviceExt->IoVPB->Flags & VPB_REMOVE_PENDING;
541             DeviceExt->IoVPB->RealDevice->Vpb = DeviceExt->SpareVPB;
542             DeviceExt->SpareVPB = NULL;
543             DeviceExt->IoVPB->Flags |= VPB_PERSISTENT;
544 
545             /* We are uninitializing, the VCB cannot be used anymore */
546             ClearFlag(DeviceExt->Flags, VCB_GOOD);
547         }
548 
549         /* Don't do anything for now */
550         Delete = FALSE;
551     }
552     else
553     {
554         /* Otherwise, delete the volume */
555         Delete = TRUE;
556 
557         /* Swap the VPB with our local own */
558         if (Vpb->RealDevice->Vpb == Vpb && DeviceExt->SpareVPB != NULL)
559         {
560             RtlZeroMemory(DeviceExt->SpareVPB, sizeof(VPB));
561             DeviceExt->SpareVPB->Type = IO_TYPE_VPB;
562             DeviceExt->SpareVPB->Size = sizeof(VPB);
563             DeviceExt->SpareVPB->RealDevice = DeviceExt->IoVPB->RealDevice;
564             DeviceExt->SpareVPB->DeviceObject = NULL;
565             DeviceExt->SpareVPB->Flags = DeviceExt->IoVPB->Flags & VPB_REMOVE_PENDING;
566             DeviceExt->IoVPB->RealDevice->Vpb = DeviceExt->SpareVPB;
567             DeviceExt->SpareVPB = NULL;
568             DeviceExt->IoVPB->Flags |= VPB_PERSISTENT;
569 
570             /* We are uninitializing, the VCB cannot be used anymore */
571             ClearFlag(DeviceExt->Flags, VCB_GOOD);
572         }
573 
574         /*
575          * We defer setting the VPB's DeviceObject to NULL for later because
576          * we want to handle the closing of the internal opened meta-files.
577          */
578 
579         /* Clear the mounted and locked flags in the VPB */
580         ClearFlag(Vpb->Flags, VPB_MOUNTED | VPB_LOCKED);
581     }
582 
583     /* Release lock and return status */
584     IoReleaseVpbSpinLock(OldIrql);
585 
586     /* If we were to delete, delete volume */
587     if (Delete)
588     {
589         LARGE_INTEGER Zero = {{0,0}};
590         PVFATFCB Fcb;
591 
592         /* We are uninitializing, the VCB cannot be used anymore */
593         ClearFlag(DeviceExt->Flags, VCB_GOOD);
594 
595         /* Invalidate and close the internal opened meta-files */
596         if (DeviceExt->RootFcb)
597         {
598             Fcb = DeviceExt->RootFcb;
599             CcUninitializeCacheMap(Fcb->FileObject,
600                                    &Zero,
601                                    NULL);
602             ObDereferenceObject(Fcb->FileObject);
603             DeviceExt->RootFcb = NULL;
604             vfatDestroyFCB(Fcb);
605         }
606         if (DeviceExt->VolumeFcb)
607         {
608             Fcb = DeviceExt->VolumeFcb;
609 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
610             CcUninitializeCacheMap(Fcb->FileObject,
611                                    &Zero,
612                                    NULL);
613             ObDereferenceObject(Fcb->FileObject);
614 #endif
615             DeviceExt->VolumeFcb = NULL;
616             vfatDestroyFCB(Fcb);
617         }
618         if (DeviceExt->FATFileObject)
619         {
620             Fcb = DeviceExt->FATFileObject->FsContext;
621             CcUninitializeCacheMap(DeviceExt->FATFileObject,
622                                    &Zero,
623                                    NULL);
624             DeviceExt->FATFileObject->FsContext = NULL;
625             ObDereferenceObject(DeviceExt->FATFileObject);
626             DeviceExt->FATFileObject = NULL;
627             vfatDestroyFCB(Fcb);
628         }
629 
630         ASSERT(DeviceExt->OverflowQueueCount == 0);
631         ASSERT(IsListEmpty(&DeviceExt->OverflowQueue));
632         ASSERT(DeviceExt->PostedRequestCount == 0);
633 
634         /*
635          * Now that the closing of the internal opened meta-files has been
636          * handled, we can now set the VPB's DeviceObject to NULL.
637          */
638         Vpb->DeviceObject = NULL;
639 
640         /* If we have a local VPB, we'll have to delete it
641          * but we won't dismount us - something went bad before
642          */
643         if (DeviceExt->SpareVPB)
644         {
645             ExFreePool(DeviceExt->SpareVPB);
646         }
647         /* Otherwise, delete any of the available VPB if its reference count is zero */
648         else if (DeviceExt->IoVPB->ReferenceCount == 0)
649         {
650             ExFreePool(DeviceExt->IoVPB);
651         }
652 
653         /* Remove the volume from the list */
654         ExAcquireResourceExclusiveLite(&VfatGlobalData->VolumeListLock, TRUE);
655         RemoveEntryList(&DeviceExt->VolumeListEntry);
656         ExReleaseResourceLite(&VfatGlobalData->VolumeListLock);
657 
658         /* Uninitialize the notify synchronization object */
659         FsRtlNotifyUninitializeSync(&DeviceExt->NotifySync);
660 
661         /* Release resources */
662         ExFreePoolWithTag(DeviceExt->Statistics, TAG_STATS);
663         ExDeleteResourceLite(&DeviceExt->DirResource);
664         ExDeleteResourceLite(&DeviceExt->FatResource);
665 
666         /* Dismount our device if possible */
667         ObDereferenceObject(DeviceExt->StorageDevice);
668         IoDeleteDevice(DeviceExt->VolumeDevice);
669     }
670 
671     return Delete;
672 }
673