2 * PROJECT: ReactOS FAT file system driver
3 * LICENSE: GNU GPLv3 as published by the Free Software Foundation
4 * FILE: drivers/filesystems/fastfat/close.c
5 * PURPOSE: Closing routines
6 * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)
9 /* INCLUDES *****************************************************************/
15 FatQueueClose(IN PCLOSE_CONTEXT CloseContext
,
16 IN BOOLEAN DelayClose
);
19 FatRemoveClose(PVCB Vcb OPTIONAL
,
20 PVCB LastVcbHint OPTIONAL
);
22 const ULONG FatMaxDelayedCloseCount
= 16;
24 /* FUNCTIONS ****************************************************************/
28 FatiCommonClose(IN PVCB Vcb
,
31 IN TYPE_OF_OPEN TypeOfOpen
,
33 OUT PBOOLEAN VcbDeleted
)
37 BOOLEAN RecursiveClose
, VcbDeletedLv
= FALSE
;
38 FAT_IRP_CONTEXT IrpContext
;
40 if (VcbDeleted
) *VcbDeleted
= FALSE
;
42 if (TypeOfOpen
== UnopenedFileObject
)
44 DPRINT1("Closing unopened file object\n");
45 Status
= STATUS_SUCCESS
;
49 RtlZeroMemory(&IrpContext
, sizeof(FAT_IRP_CONTEXT
));
51 IrpContext
.NodeTypeCode
= FAT_NTC_IRP_CONTEXT
;
52 IrpContext
.NodeByteSize
= sizeof(IrpContext
);
53 IrpContext
.MajorFunction
= IRP_MJ_CLOSE
;
55 if (Wait
) SetFlag(IrpContext
.Flags
, IRPCONTEXT_CANWAIT
);
57 if (!ExAcquireResourceExclusiveLite(&Vcb
->Resource
, Wait
)) return STATUS_PENDING
;
59 if (Vcb
->State
& VCB_STATE_FLAG_CLOSE_IN_PROGRESS
)
61 RecursiveClose
= TRUE
;
65 SetFlag(Vcb
->State
, VCB_STATE_FLAG_CLOSE_IN_PROGRESS
);
66 RecursiveClose
= FALSE
;
71 /* Update on-disk structures */
74 case VirtualVolumeFile
:
75 DPRINT1("Close VirtualVolumeFile\n");
77 InterlockedDecrement((PLONG
)&(Vcb
->InternalOpenCount
));
78 InterlockedDecrement((PLONG
)&(Vcb
->ResidualOpenCount
));
80 Status
= STATUS_SUCCESS
;
85 DPRINT1("Close UserVolumeOpen\n");
87 Vcb
->DirectAccessOpenCount
--;
89 if (FlagOn(Ccb
->Flags
, CCB_READ_ONLY
)) Vcb
->ReadOnlyCount
--;
91 FatDeleteCcb(&IrpContext
, Ccb
);
93 Status
= STATUS_SUCCESS
;
102 DPRINT1("Close DirectoryFile\n");
104 InterlockedDecrement((PLONG
)&(Fcb
->Dcb
.DirectoryFileOpenCount
));
105 InterlockedDecrement((PLONG
)&(Vcb
->InternalOpenCount
));
107 if (FatNodeType(Fcb
) == FAT_NTC_ROOT_DCB
)
109 InterlockedDecrement((PLONG
)&(Vcb
->ResidualOpenCount
));
114 Status
= STATUS_SUCCESS
;
122 case UserDirectoryOpen
:
124 DPRINT("Close UserFileOpen/UserDirectoryOpen\n");
126 if ((FatNodeType(Fcb
) == FAT_NTC_DCB
) &&
127 IsListEmpty(&Fcb
->Dcb
.ParentDcbList
) &&
128 (Fcb
->OpenCount
== 1) &&
129 (Fcb
->Dcb
.DirectoryFile
!= NULL
))
131 PFILE_OBJECT DirectoryFileObject
= Fcb
->Dcb
.DirectoryFile
;
133 DPRINT1("Uninitialize the stream file object\n");
135 CcUninitializeCacheMap(DirectoryFileObject
, NULL
, NULL
);
137 Fcb
->Dcb
.DirectoryFile
= NULL
;
138 ObDereferenceObject(DirectoryFileObject
);
142 Vcb
->OpenFileCount
--;
143 if (FlagOn(Ccb
->Flags
, CCB_READ_ONLY
)) Vcb
->ReadOnlyCount
--;
145 FatDeleteCcb(&IrpContext
, Ccb
);
149 KeBugCheckEx(FAT_FILE_SYSTEM
, __LINE__
, (ULONG_PTR
)TypeOfOpen
, 0, 0);
152 /* Update in-memory structures */
153 if (((FatNodeType(Fcb
) == FAT_NTC_FCB
) &&
154 (Fcb
->OpenCount
== 0))
156 ((FatNodeType(Fcb
) == FAT_NTC_DCB
) &&
157 (IsListEmpty(&Fcb
->Dcb
.ParentDcbList
)) &&
158 (Fcb
->OpenCount
== 0) &&
159 (Fcb
->Dcb
.DirectoryFileOpenCount
== 0)))
161 ParentDcb
= Fcb
->ParentFcb
;
163 SetFlag(Vcb
->State
, VCB_STATE_FLAG_DELETED_FCB
);
165 FatDeleteFcb(&IrpContext
, Fcb
);
167 while ((FatNodeType(ParentDcb
) == FAT_NTC_DCB
) &&
168 IsListEmpty(&ParentDcb
->Dcb
.ParentDcbList
) &&
169 (ParentDcb
->OpenCount
== 0) &&
170 (ParentDcb
->Dcb
.DirectoryFile
!= NULL
))
172 PFILE_OBJECT DirectoryFileObject
;
174 DirectoryFileObject
= ParentDcb
->Dcb
.DirectoryFile
;
176 DPRINT1("Uninitialize parent Stream Cache Map\n");
178 CcUninitializeCacheMap(DirectoryFileObject
, NULL
, NULL
);
180 ParentDcb
->Dcb
.DirectoryFile
= NULL
;
182 ObDereferenceObject(DirectoryFileObject
);
184 if (ParentDcb
->Dcb
.DirectoryFileOpenCount
== 0)
188 CurrentDcb
= ParentDcb
;
189 ParentDcb
= CurrentDcb
->ParentFcb
;
191 SetFlag(Vcb
->State
, VCB_STATE_FLAG_DELETED_FCB
);
193 FatDeleteFcb(&IrpContext
, CurrentDcb
);
202 Status
= STATUS_SUCCESS
;
205 /* Closing is done, check if VCB could be closed too */
208 /* One open left - yes, VCB can go away */
209 if (Vcb
->OpenFileCount
== 1 &&
210 !FlagOn(Vcb
->State
, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS
)
213 FatReleaseVcb(&IrpContext
, Vcb
);
215 SetFlag(IrpContext
.Flags
, IRPCONTEXT_CANWAIT
);
217 FatAcquireExclusiveGlobal(&IrpContext
);
219 FatAcquireExclusiveVcb(&IrpContext
, Vcb
);
221 Vcb
->OpenFileCount
--;
223 VcbDeletedLv
= FatCheckForDismount(&IrpContext
, Vcb
, FALSE
);
225 FatReleaseGlobal(&IrpContext
);
227 if (VcbDeleted
) *VcbDeleted
= VcbDeletedLv
;
231 /* Remove extra referenec */
232 Vcb
->OpenFileCount
--;
235 /* Clear recursion flag if necessary */
238 ClearFlag(Vcb
->State
, VCB_STATE_FLAG_CLOSE_IN_PROGRESS
);
242 /* Release VCB if it wasn't deleted */
244 FatReleaseVcb(&IrpContext
, Vcb
);
251 FatiClose(IN PFAT_IRP_CONTEXT IrpContext
,
254 PIO_STACK_LOCATION IrpSp
;
255 TYPE_OF_OPEN TypeOfOpen
;
259 BOOLEAN TopLevel
, Wait
, VcbDeleted
= FALSE
, DelayedClose
= FALSE
;
260 NTSTATUS Status
= STATUS_SUCCESS
;
261 PCLOSE_CONTEXT CloseContext
= NULL
;
263 TopLevel
= FatIsTopLevelIrp(Irp
);
265 /* Get current IRP stack location */
266 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
268 /* Decode incoming file object */
269 TypeOfOpen
= FatDecodeFileObject(IrpSp
->FileObject
, &Vcb
, &Fcb
, &Ccb
);
271 /* Set CCB read only flag */
272 if (Ccb
&& IsFileObjectReadOnly(IrpSp
->FileObject
))
273 SetFlag(Ccb
->Flags
, CCB_READ_ONLY
);
275 /* It's possible to wait only if we are top level or not a system process */
276 Wait
= TopLevel
&& (PsGetCurrentProcess() != FatGlobalData
.SystemProcess
);
278 /* Determine if it's a delayed close, by flags first */
279 if ((TypeOfOpen
== UserFileOpen
|| TypeOfOpen
== UserDirectoryOpen
) &&
280 (Fcb
->State
& FCB_STATE_DELAY_CLOSE
) &&
281 !FatGlobalData
.ShutdownStarted
)
286 /* If close is not delayed, try to perform the close operation */
288 Status
= FatiCommonClose(Vcb
, Fcb
, Ccb
, TypeOfOpen
, Wait
, &VcbDeleted
);
290 /* We have to delay close if either it's defined by a flag or it was not possible
291 to perform it synchronously */
292 if (DelayedClose
|| Status
== STATUS_PENDING
)
294 DPRINT1("Queuing a pending close, Vcb %p, Fcb %p, Ccb %p\n", Vcb
, Fcb
, Ccb
);
296 /* Check if a close context should be allocated */
297 if (TypeOfOpen
== VirtualVolumeFile
)
299 ASSERT(Vcb
->CloseContext
!= NULL
);
300 CloseContext
= Vcb
->CloseContext
;
301 Vcb
->CloseContext
= NULL
;
302 CloseContext
->Free
= TRUE
;
304 else if (TypeOfOpen
== DirectoryFile
||
305 TypeOfOpen
== EaFile
)
308 //CloseContext = FatAllocateCloseContext(Vcb);
309 //ASSERT(CloseContext != NULL);
310 CloseContext
->Free
= TRUE
;
314 //TODO: FatDeallocateCcbStrings( Ccb );
316 /* Set CloseContext to a buffer inside Ccb */
317 CloseContext
= &Ccb
->CloseContext
;
318 CloseContext
->Free
= FALSE
;
319 SetFlag(Ccb
->Flags
, CCB_CLOSE_CONTEXT
);
322 /* Save all info in the close context */
323 CloseContext
->Vcb
= Vcb
;
324 CloseContext
->Fcb
= Fcb
;
325 CloseContext
->TypeOfOpen
= TypeOfOpen
;
327 /* Queue the close */
328 FatQueueClose(CloseContext
, (BOOLEAN
)(Fcb
&& FlagOn(Fcb
->State
, FCB_STATE_DELAY_CLOSE
)));
332 /* Close finished right away */
333 if (TypeOfOpen
== VirtualVolumeFile
||
334 TypeOfOpen
== DirectoryFile
||
335 TypeOfOpen
== EaFile
)
337 if (TypeOfOpen
== VirtualVolumeFile
)
339 /* Free close context for the not deleted VCB */
342 CloseContext
= Vcb
->CloseContext
;
343 Vcb
->CloseContext
= NULL
;
345 ASSERT(CloseContext
!= NULL
);
350 //CloseContext = FatAllocateCloseContext(Vcb);
351 DPRINT1("TODO: Allocate close context!\n");
352 ASSERT(CloseContext
!= NULL
);
355 /* Free close context */
356 if (CloseContext
) ExFreePool(CloseContext
);
360 /* Complete the request */
361 FatCompleteRequest(NULL
, Irp
, Status
);
363 /* Reset the top level IRP if necessary */
364 if (TopLevel
) IoSetTopLevelIrp(NULL
);
371 FatClose(PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
373 PFAT_IRP_CONTEXT IrpContext
;
376 DPRINT("FatClose(DeviceObject %p, Irp %p)\n", DeviceObject
, Irp
);
378 /* FatClose works only with a volume device object */
379 if (DeviceObject
== FatGlobalData
.DiskDeviceObject
)
381 /* Complete the request and return success */
382 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
383 Irp
->IoStatus
.Information
= FILE_OPENED
;
385 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
387 return STATUS_SUCCESS
;
390 /* Enter FsRtl critical region */
391 FsRtlEnterFileSystem();
393 /* Build an irp context */
394 IrpContext
= FatBuildIrpContext(Irp
, TRUE
);
396 /* Call internal function */
397 Status
= FatiClose(IrpContext
, Irp
);
399 /* Leave FsRtl critical region */
400 FsRtlExitFileSystem();
407 FatPendingClose(IN PVCB Vcb OPTIONAL
)
409 PCLOSE_CONTEXT CloseContext
;
410 PVCB CurrentVcb
= NULL
;
415 /* Do the top-level IRP trick */
416 if (!Vcb
) IoSetTopLevelIrp((PIRP
)FSRTL_FSP_TOP_LEVEL_IRP
);
418 while ((CloseContext
= FatRemoveClose(Vcb
, LastVcb
)))
422 if (!FatGlobalData
.ShutdownStarted
)
424 if (CloseContext
->Vcb
!= CurrentVcb
)
428 /* Release previous VCB */
430 ExReleaseResourceLite(&CurrentVcb
->Resource
);
432 /* Lock the new VCB */
433 CurrentVcb
= CloseContext
->Vcb
;
434 (VOID
)ExAcquireResourceExclusiveLite(&CurrentVcb
->Resource
, TRUE
);
441 if (ExGetSharedWaiterCount(&CurrentVcb
->Resource
) +
442 ExGetExclusiveWaiterCount(&CurrentVcb
->Resource
))
444 ExReleaseResourceLite(&CurrentVcb
->Resource
);
445 (VOID
)ExAcquireResourceExclusiveLite(&CurrentVcb
->Resource
, TRUE
);
452 /* Check open count */
453 if (CurrentVcb
->OpenFileCount
<= 1)
455 ExReleaseResourceLite(&CurrentVcb
->Resource
);
461 ExReleaseResourceLite(&CurrentVcb
->Resource
);
466 LastVcb
= CurrentVcb
;
468 /* Remember if we should free the context */
469 FreeContext
= CloseContext
->Free
;
471 FatiCommonClose(CloseContext
->Vcb
,
473 (FreeContext
? NULL
: CONTAINING_RECORD(CloseContext
, CCB
, CloseContext
)),
474 CloseContext
->TypeOfOpen
,
478 /* Free context if necessary */
479 if (FreeContext
) ExFreePool(CloseContext
);
482 /* Release VCB if necessary */
483 if (CurrentVcb
) ExReleaseResourceLite(&CurrentVcb
->Resource
);
485 /* Reset top level IRP */
486 if (!Vcb
) IoSetTopLevelIrp( NULL
);
491 FatCloseWorker(IN PDEVICE_OBJECT DeviceObject
,
494 FsRtlEnterFileSystem();
496 FatPendingClose((PVCB
)Context
);
498 FsRtlExitFileSystem();
503 FatQueueClose(IN PCLOSE_CONTEXT CloseContext
,
504 IN BOOLEAN DelayClose
)
506 BOOLEAN RunWorker
= FALSE
;
508 /* Acquire the close lists mutex */
509 ExAcquireFastMutexUnsafe(&FatCloseQueueMutex
);
511 /* Add it to the desired list */
514 InsertTailList(&FatGlobalData
.DelayedCloseList
,
515 &CloseContext
->GlobalLinks
);
516 InsertTailList(&CloseContext
->Vcb
->DelayedCloseList
,
517 &CloseContext
->VcbLinks
);
519 FatGlobalData
.DelayedCloseCount
++;
521 if (FatGlobalData
.DelayedCloseCount
> FatMaxDelayedCloseCount
&&
522 !FatGlobalData
.AsyncCloseActive
)
524 FatGlobalData
.AsyncCloseActive
= TRUE
;
530 InsertTailList(&FatGlobalData
.AsyncCloseList
,
531 &CloseContext
->GlobalLinks
);
532 InsertTailList(&CloseContext
->Vcb
->AsyncCloseList
,
533 &CloseContext
->VcbLinks
);
535 FatGlobalData
.AsyncCloseCount
++;
537 if (!FatGlobalData
.AsyncCloseActive
)
539 FatGlobalData
.AsyncCloseActive
= TRUE
;
544 /* Release the close lists mutex */
545 ExReleaseFastMutexUnsafe(&FatCloseQueueMutex
);
548 IoQueueWorkItem(FatGlobalData
.FatCloseItem
, FatCloseWorker
, CriticalWorkQueue
, NULL
);
553 FatRemoveClose(PVCB Vcb OPTIONAL
,
554 PVCB LastVcbHint OPTIONAL
)
557 PCLOSE_CONTEXT CloseContext
;
558 BOOLEAN IsWorker
= FALSE
;
560 /* Acquire the close lists mutex */
561 ExAcquireFastMutexUnsafe(&FatCloseQueueMutex
);
563 if (!Vcb
) IsWorker
= TRUE
;
565 if (Vcb
== NULL
&& LastVcbHint
!= NULL
)
567 // TODO: A very special case of overflowing the queue
571 /* Usual processing from a worker thread */
576 /* Is there anything in the async close list */
577 if (!IsListEmpty(&FatGlobalData
.AsyncCloseList
))
579 Entry
= RemoveHeadList(&FatGlobalData
.AsyncCloseList
);
580 FatGlobalData
.AsyncCloseCount
--;
582 CloseContext
= CONTAINING_RECORD(Entry
,
586 RemoveEntryList(&CloseContext
->VcbLinks
);
587 } else if (!IsListEmpty(&FatGlobalData
.DelayedCloseList
) &&
588 (FatGlobalData
.DelayedCloseCount
> FatMaxDelayedCloseCount
/2 ||
589 FatGlobalData
.ShutdownStarted
))
591 /* In case of a shutdown or when delayed queue is filled at half - perform closing */
592 Entry
= RemoveHeadList(&FatGlobalData
.DelayedCloseList
);
593 FatGlobalData
.DelayedCloseCount
--;
595 CloseContext
= CONTAINING_RECORD(Entry
,
598 RemoveEntryList(&CloseContext
->VcbLinks
);
602 /* Nothing to close */
604 if (IsWorker
) FatGlobalData
.AsyncCloseActive
= FALSE
;
609 if (!IsListEmpty(&Vcb
->AsyncCloseList
))
611 /* Is there anything in the async close list */
612 Entry
= RemoveHeadList(&Vcb
->AsyncCloseList
);
613 FatGlobalData
.AsyncCloseCount
--;
615 CloseContext
= CONTAINING_RECORD(Entry
,
619 RemoveEntryList(&CloseContext
->GlobalLinks
);
621 else if (!IsListEmpty(&Vcb
->DelayedCloseList
))
623 /* Process delayed close list */
624 Entry
= RemoveHeadList(&Vcb
->DelayedCloseList
);
625 FatGlobalData
.DelayedCloseCount
--;
627 CloseContext
= CONTAINING_RECORD(Entry
,
631 RemoveEntryList(&CloseContext
->GlobalLinks
);
633 else if (LastVcbHint
)
636 goto TryToCloseAgain
;
640 /* Nothing to close */
645 /* Release the close lists mutex */
646 ExReleaseFastMutexUnsafe(&FatCloseQueueMutex
);