1 /*
2 * PROJECT: VFAT Filesystem
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: File close routines
5 * COPYRIGHT: Copyright 1998 Jason Filby <jasonfilby@yahoo.com>
6 * Copyright 2014-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 /* FUNCTIONS ****************************************************************/
17
18 VOID
VfatCommonCloseFile(PDEVICE_EXTENSION DeviceExt,PVFATFCB pFcb)19 VfatCommonCloseFile(
20 PDEVICE_EXTENSION DeviceExt,
21 PVFATFCB pFcb)
22 {
23 /* Nothing to do for volumes or for the FAT file object */
24 if (BooleanFlagOn(pFcb->Flags, FCB_IS_FAT | FCB_IS_VOLUME))
25 {
26 return;
27 }
28
29 /* If cache is still initialized, release it
30 * This only affects directories
31 */
32 if (pFcb->OpenHandleCount == 0 && BooleanFlagOn(pFcb->Flags, FCB_CACHE_INITIALIZED))
33 {
34 PFILE_OBJECT tmpFileObject;
35 tmpFileObject = pFcb->FileObject;
36 if (tmpFileObject != NULL)
37 {
38 pFcb->FileObject = NULL;
39 CcUninitializeCacheMap(tmpFileObject, NULL, NULL);
40 ClearFlag(pFcb->Flags, FCB_CACHE_INITIALIZED);
41 ObDereferenceObject(tmpFileObject);
42 }
43 }
44
45 #ifdef KDBG
46 pFcb->Flags |= FCB_CLOSED;
47 #endif
48
49 /* Release the FCB, we likely cause its deletion */
50 vfatReleaseFCB(DeviceExt, pFcb);
51 }
52
53 VOID
54 NTAPI
VfatCloseWorker(IN PDEVICE_OBJECT DeviceObject,IN PVOID Context)55 VfatCloseWorker(
56 IN PDEVICE_OBJECT DeviceObject,
57 IN PVOID Context)
58 {
59 PLIST_ENTRY Entry;
60 PVFATFCB pFcb;
61 PDEVICE_EXTENSION Vcb;
62 PVFAT_CLOSE_CONTEXT CloseContext;
63 BOOLEAN ConcurrentDeletion;
64
65 /* Start removing work items */
66 ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
67 while (!IsListEmpty(&VfatGlobalData->CloseListHead))
68 {
69 Entry = RemoveHeadList(&VfatGlobalData->CloseListHead);
70 CloseContext = CONTAINING_RECORD(Entry, VFAT_CLOSE_CONTEXT, CloseListEntry);
71
72 /* One less */
73 --VfatGlobalData->CloseCount;
74 /* Reset its entry to detect concurrent deletions */
75 InitializeListHead(&CloseContext->CloseListEntry);
76 ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
77
78 /* Get the elements */
79 Vcb = CloseContext->Vcb;
80 pFcb = CloseContext->Fcb;
81 ExAcquireResourceExclusiveLite(&Vcb->DirResource, TRUE);
82 /* If it didn't got deleted in between */
83 if (BooleanFlagOn(pFcb->Flags, FCB_DELAYED_CLOSE))
84 {
85 /* Close it! */
86 DPRINT("Late closing: %wZ\n", &pFcb->PathNameU);
87 ClearFlag(pFcb->Flags, FCB_DELAYED_CLOSE);
88 pFcb->CloseContext = NULL;
89 VfatCommonCloseFile(Vcb, pFcb);
90 ConcurrentDeletion = FALSE;
91 }
92 else
93 {
94 /* Otherwise, mark not to delete it */
95 ConcurrentDeletion = TRUE;
96 }
97 ExReleaseResourceLite(&Vcb->DirResource);
98
99 /* If we were the fastest, delete the context */
100 if (!ConcurrentDeletion)
101 {
102 ExFreeToPagedLookasideList(&VfatGlobalData->CloseContextLookasideList, CloseContext);
103 }
104
105 /* Lock again the list */
106 ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
107 }
108
109 /* We're done, bye! */
110 VfatGlobalData->CloseWorkerRunning = FALSE;
111 ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
112 }
113
114 NTSTATUS
VfatPostCloseFile(PDEVICE_EXTENSION DeviceExt,PFILE_OBJECT FileObject)115 VfatPostCloseFile(
116 PDEVICE_EXTENSION DeviceExt,
117 PFILE_OBJECT FileObject)
118 {
119 PVFAT_CLOSE_CONTEXT CloseContext;
120
121 /* Allocate a work item */
122 CloseContext = ExAllocateFromPagedLookasideList(&VfatGlobalData->CloseContextLookasideList);
123 if (CloseContext == NULL)
124 {
125 return STATUS_INSUFFICIENT_RESOURCES;
126 }
127
128 /* Set relevant fields */
129 CloseContext->Vcb = DeviceExt;
130 CloseContext->Fcb = FileObject->FsContext;
131 CloseContext->Fcb->CloseContext = CloseContext;
132
133 /* Acquire the lock to insert in list */
134 ExAcquireFastMutex(&VfatGlobalData->CloseMutex);
135
136 /* One more element */
137 InsertTailList(&VfatGlobalData->CloseListHead, &CloseContext->CloseListEntry);
138 ++VfatGlobalData->CloseCount;
139
140 /* If we have more than 16 items in list, and no worker thread
141 * start a new one
142 */
143 if (VfatGlobalData->CloseCount > 16 && !VfatGlobalData->CloseWorkerRunning)
144 {
145 VfatGlobalData->CloseWorkerRunning = TRUE;
146 IoQueueWorkItem(VfatGlobalData->CloseWorkItem, VfatCloseWorker, CriticalWorkQueue, NULL);
147 }
148
149 /* We're done */
150 ExReleaseFastMutex(&VfatGlobalData->CloseMutex);
151
152 return STATUS_SUCCESS;
153 }
154
155 /*
156 * FUNCTION: Closes a file
157 */
158 NTSTATUS
VfatCloseFile(PDEVICE_EXTENSION DeviceExt,PFILE_OBJECT FileObject)159 VfatCloseFile(
160 PDEVICE_EXTENSION DeviceExt,
161 PFILE_OBJECT FileObject)
162 {
163 PVFATFCB pFcb;
164 PVFATCCB pCcb;
165 BOOLEAN IsVolume;
166 NTSTATUS Status = STATUS_SUCCESS;
167
168 DPRINT("VfatCloseFile(DeviceExt %p, FileObject %p)\n",
169 DeviceExt, FileObject);
170
171 /* FIXME : update entry in directory? */
172 pCcb = (PVFATCCB) (FileObject->FsContext2);
173 pFcb = (PVFATFCB) (FileObject->FsContext);
174
175 if (pFcb == NULL)
176 {
177 return STATUS_SUCCESS;
178 }
179
180 IsVolume = BooleanFlagOn(pFcb->Flags, FCB_IS_VOLUME);
181
182 if (pCcb)
183 {
184 vfatDestroyCCB(pCcb);
185 }
186
187 /* If we have to close immediately, or if delaying failed, close */
188 if (VfatGlobalData->ShutdownStarted || !BooleanFlagOn(pFcb->Flags, FCB_DELAYED_CLOSE) ||
189 !NT_SUCCESS(VfatPostCloseFile(DeviceExt, FileObject)))
190 {
191 VfatCommonCloseFile(DeviceExt, pFcb);
192 }
193
194 FileObject->FsContext2 = NULL;
195 FileObject->FsContext = NULL;
196 FileObject->SectionObjectPointer = NULL;
197
198 #ifdef ENABLE_SWAPOUT
199 if (IsVolume && DeviceExt->OpenHandleCount == 0)
200 {
201 VfatCheckForDismount(DeviceExt, FALSE);
202 }
203 #endif
204
205 return Status;
206 }
207
208 /*
209 * FUNCTION: Closes a file
210 */
211 NTSTATUS
VfatClose(PVFAT_IRP_CONTEXT IrpContext)212 VfatClose(
213 PVFAT_IRP_CONTEXT IrpContext)
214 {
215 NTSTATUS Status;
216
217 DPRINT("VfatClose(DeviceObject %p, Irp %p)\n", IrpContext->DeviceObject, IrpContext->Irp);
218
219 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
220 {
221 DPRINT("Closing file system\n");
222 IrpContext->Irp->IoStatus.Information = 0;
223 return STATUS_SUCCESS;
224 }
225 if (!ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
226 {
227 return VfatMarkIrpContextForQueue(IrpContext);
228 }
229
230 Status = VfatCloseFile(IrpContext->DeviceExt, IrpContext->FileObject);
231 ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
232
233 IrpContext->Irp->IoStatus.Information = 0;
234
235 return Status;
236 }
237
238 /* EOF */
239