69ccf3c8c433593bf3dd5d347a0b4c4e5436bc48
[reactos.git] / drivers / filesystems / fastfat_new / close.c
1 /*
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)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #define NDEBUG
12 #include "fastfat.h"
13
14 VOID NTAPI
15 FatQueueClose(IN PCLOSE_CONTEXT CloseContext,
16 IN BOOLEAN DelayClose);
17
18 PCLOSE_CONTEXT NTAPI
19 FatRemoveClose(PVCB Vcb OPTIONAL,
20 PVCB LastVcbHint OPTIONAL);
21
22 const ULONG FatMaxDelayedCloseCount = 16;
23
24 /* FUNCTIONS ****************************************************************/
25
26 NTSTATUS
27 NTAPI
28 FatiCommonClose(IN PVCB Vcb,
29 IN PFCB Fcb,
30 IN PCCB Ccb,
31 IN TYPE_OF_OPEN TypeOfOpen,
32 IN BOOLEAN Wait,
33 OUT PBOOLEAN VcbDeleted)
34 {
35 NTSTATUS Status;
36 PFCB ParentDcb;
37 BOOLEAN RecursiveClose, VcbDeletedLv = FALSE;
38 FAT_IRP_CONTEXT IrpContext;
39
40 if (VcbDeleted) *VcbDeleted = FALSE;
41
42 if (TypeOfOpen == UnopenedFileObject)
43 {
44 DPRINT1("Closing unopened file object\n");
45 Status = STATUS_SUCCESS;
46 return Status;
47 }
48
49 RtlZeroMemory(&IrpContext, sizeof(FAT_IRP_CONTEXT));
50
51 IrpContext.NodeTypeCode = FAT_NTC_IRP_CONTEXT;
52 IrpContext.NodeByteSize = sizeof(IrpContext);
53 IrpContext.MajorFunction = IRP_MJ_CLOSE;
54
55 if (Wait) SetFlag(IrpContext.Flags, IRPCONTEXT_CANWAIT);
56
57 if (!ExAcquireResourceExclusiveLite(&Vcb->Resource, Wait)) return STATUS_PENDING;
58
59 if (Vcb->State & VCB_STATE_FLAG_CLOSE_IN_PROGRESS)
60 {
61 RecursiveClose = TRUE;
62 }
63 else
64 {
65 SetFlag(Vcb->State, VCB_STATE_FLAG_CLOSE_IN_PROGRESS);
66 RecursiveClose = FALSE;
67
68 Vcb->OpenFileCount++;
69 }
70
71 /* Update on-disk structures */
72 switch (TypeOfOpen)
73 {
74 case VirtualVolumeFile:
75 DPRINT1("Close VirtualVolumeFile\n");
76
77 InterlockedDecrement((PLONG)&(Vcb->InternalOpenCount));
78 InterlockedDecrement((PLONG)&(Vcb->ResidualOpenCount));
79
80 Status = STATUS_SUCCESS;
81 goto close_done;
82 break;
83
84 case UserVolumeOpen:
85 DPRINT1("Close UserVolumeOpen\n");
86
87 Vcb->DirectAccessOpenCount--;
88 Vcb->OpenFileCount--;
89 if (FlagOn(Ccb->Flags, CCB_READ_ONLY)) Vcb->ReadOnlyCount--;
90
91 FatDeleteCcb(&IrpContext, Ccb);
92
93 Status = STATUS_SUCCESS;
94 goto close_done;
95 break;
96
97 case EaFile:
98 UNIMPLEMENTED;
99 break;
100
101 case DirectoryFile:
102 DPRINT1("Close DirectoryFile\n");
103
104 InterlockedDecrement((PLONG)&(Fcb->Dcb.DirectoryFileOpenCount));
105 InterlockedDecrement((PLONG)&(Vcb->InternalOpenCount));
106
107 if (FatNodeType(Fcb) == FAT_NTC_ROOT_DCB)
108 {
109 InterlockedDecrement((PLONG)&(Vcb->ResidualOpenCount));
110 }
111
112 if (RecursiveClose)
113 {
114 Status = STATUS_SUCCESS;
115 goto close_done;
116 }
117 else
118 {
119 break;
120 }
121
122 case UserDirectoryOpen:
123 case UserFileOpen:
124 DPRINT("Close UserFileOpen/UserDirectoryOpen\n");
125
126 if ((FatNodeType(Fcb) == FAT_NTC_DCB) &&
127 IsListEmpty(&Fcb->Dcb.ParentDcbList) &&
128 (Fcb->OpenCount == 1) &&
129 (Fcb->Dcb.DirectoryFile != NULL))
130 {
131 PFILE_OBJECT DirectoryFileObject = Fcb->Dcb.DirectoryFile;
132
133 DPRINT1("Uninitialize the stream file object\n");
134
135 CcUninitializeCacheMap(DirectoryFileObject, NULL, NULL);
136
137 Fcb->Dcb.DirectoryFile = NULL;
138 ObDereferenceObject(DirectoryFileObject);
139 }
140
141 Fcb->OpenCount--;
142 Vcb->OpenFileCount--;
143 if (FlagOn(Ccb->Flags, CCB_READ_ONLY)) Vcb->ReadOnlyCount --;
144
145 FatDeleteCcb(&IrpContext, Ccb);
146 break;
147
148 default:
149 KeBugCheckEx(FAT_FILE_SYSTEM, __LINE__, (ULONG_PTR)TypeOfOpen, 0, 0);
150 }
151
152 /* Update in-memory structures */
153 if (((FatNodeType(Fcb) == FAT_NTC_FCB) &&
154 (Fcb->OpenCount == 0))
155 ||
156 ((FatNodeType(Fcb) == FAT_NTC_DCB) &&
157 (IsListEmpty(&Fcb->Dcb.ParentDcbList)) &&
158 (Fcb->OpenCount == 0) &&
159 (Fcb->Dcb.DirectoryFileOpenCount == 0)))
160 {
161 ParentDcb = Fcb->ParentFcb;
162
163 SetFlag(Vcb->State, VCB_STATE_FLAG_DELETED_FCB);
164
165 FatDeleteFcb(&IrpContext, Fcb);
166
167 while ((FatNodeType(ParentDcb) == FAT_NTC_DCB) &&
168 IsListEmpty(&ParentDcb->Dcb.ParentDcbList) &&
169 (ParentDcb->OpenCount == 0) &&
170 (ParentDcb->Dcb.DirectoryFile != NULL))
171 {
172 PFILE_OBJECT DirectoryFileObject;
173
174 DirectoryFileObject = ParentDcb->Dcb.DirectoryFile;
175
176 DPRINT1("Uninitialize parent Stream Cache Map\n");
177
178 CcUninitializeCacheMap(DirectoryFileObject, NULL, NULL);
179
180 ParentDcb->Dcb.DirectoryFile = NULL;
181
182 ObDereferenceObject(DirectoryFileObject);
183
184 if (ParentDcb->Dcb.DirectoryFileOpenCount == 0)
185 {
186 PFCB CurrentDcb;
187
188 CurrentDcb = ParentDcb;
189 ParentDcb = CurrentDcb->ParentFcb;
190
191 SetFlag(Vcb->State, VCB_STATE_FLAG_DELETED_FCB);
192
193 FatDeleteFcb(&IrpContext, CurrentDcb);
194 }
195 else
196 {
197 break;
198 }
199 }
200 }
201
202 Status = STATUS_SUCCESS;
203
204 close_done:
205 /* Closing is done, check if VCB could be closed too */
206 if (!RecursiveClose)
207 {
208 /* One open left - yes, VCB can go away */
209 if (Vcb->OpenFileCount == 1 &&
210 !FlagOn(Vcb->State, VCB_STATE_FLAG_DISMOUNT_IN_PROGRESS)
211 && VcbDeleted)
212 {
213 FatReleaseVcb(&IrpContext, Vcb );
214
215 SetFlag(IrpContext.Flags, IRPCONTEXT_CANWAIT);
216
217 FatAcquireExclusiveGlobal(&IrpContext);
218
219 FatAcquireExclusiveVcb(&IrpContext, Vcb);
220
221 Vcb->OpenFileCount--;
222
223 VcbDeletedLv = FatCheckForDismount(&IrpContext, Vcb, FALSE);
224
225 FatReleaseGlobal(&IrpContext);
226
227 if (VcbDeleted) *VcbDeleted = VcbDeletedLv;
228 }
229 else
230 {
231 /* Remove extra referenec */
232 Vcb->OpenFileCount --;
233 }
234
235 /* Clear recursion flag if necessary */
236 if (!VcbDeletedLv)
237 {
238 ClearFlag(Vcb->State, VCB_STATE_FLAG_CLOSE_IN_PROGRESS);
239 }
240 }
241
242 /* Release VCB if it wasn't deleted */
243 if (!VcbDeletedLv)
244 FatReleaseVcb(&IrpContext, Vcb);
245
246 return Status;
247 }
248
249 NTSTATUS
250 NTAPI
251 FatiClose(IN PFAT_IRP_CONTEXT IrpContext,
252 IN PIRP Irp)
253 {
254 PIO_STACK_LOCATION IrpSp;
255 TYPE_OF_OPEN TypeOfOpen;
256 PVCB Vcb;
257 PFCB Fcb;
258 PCCB Ccb;
259 BOOLEAN TopLevel, Wait, VcbDeleted = FALSE, DelayedClose = FALSE;
260 NTSTATUS Status = STATUS_SUCCESS;
261 PCLOSE_CONTEXT CloseContext = NULL;
262
263 TopLevel = FatIsTopLevelIrp(Irp);
264
265 /* Get current IRP stack location */
266 IrpSp = IoGetCurrentIrpStackLocation(Irp);
267
268 /* Decode incoming file object */
269 TypeOfOpen = FatDecodeFileObject(IrpSp->FileObject, &Vcb, &Fcb, &Ccb);
270
271 /* Set CCB read only flag */
272 if (Ccb && IsFileObjectReadOnly(IrpSp->FileObject))
273 SetFlag(Ccb->Flags, CCB_READ_ONLY);
274
275 /* It's possible to wait only if we are top level or not a system process */
276 Wait = TopLevel && (PsGetCurrentProcess() != FatGlobalData.SystemProcess);
277
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)
282 {
283 DelayedClose = TRUE;
284 }
285
286 /* If close is not delayed, try to perform the close operation */
287 if (!DelayedClose)
288 Status = FatiCommonClose(Vcb, Fcb, Ccb, TypeOfOpen, Wait, &VcbDeleted);
289
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)
293 {
294 DPRINT1("Queuing a pending close, Vcb %p, Fcb %p, Ccb %p\n", Vcb, Fcb, Ccb);
295
296 /* Check if a close context should be allocated */
297 if (TypeOfOpen == VirtualVolumeFile)
298 {
299 ASSERT(Vcb->CloseContext != NULL);
300 CloseContext = Vcb->CloseContext;
301 Vcb->CloseContext = NULL;
302 CloseContext->Free = TRUE;
303 }
304 else if (TypeOfOpen == DirectoryFile ||
305 TypeOfOpen == EaFile)
306 {
307 UNIMPLEMENTED;
308 //CloseContext = FatAllocateCloseContext(Vcb);
309 //ASSERT(CloseContext != NULL);
310 CloseContext->Free = TRUE;
311 }
312 else
313 {
314 //TODO: FatDeallocateCcbStrings( Ccb );
315
316 /* Set CloseContext to a buffer inside Ccb */
317 CloseContext = &Ccb->CloseContext;
318 CloseContext->Free = FALSE;
319 SetFlag(Ccb->Flags, CCB_CLOSE_CONTEXT);
320 }
321
322 /* Save all info in the close context */
323 CloseContext->Vcb = Vcb;
324 CloseContext->Fcb = Fcb;
325 CloseContext->TypeOfOpen = TypeOfOpen;
326
327 /* Queue the close */
328 FatQueueClose(CloseContext, (BOOLEAN)(Fcb && FlagOn(Fcb->State, FCB_STATE_DELAY_CLOSE)));
329 }
330 else
331 {
332 /* Close finished right away */
333 if (TypeOfOpen == VirtualVolumeFile ||
334 TypeOfOpen == DirectoryFile ||
335 TypeOfOpen == EaFile)
336 {
337 if (TypeOfOpen == VirtualVolumeFile)
338 {
339 /* Free close context for the not deleted VCB */
340 if (!VcbDeleted)
341 {
342 CloseContext = Vcb->CloseContext;
343 Vcb->CloseContext = NULL;
344
345 ASSERT(CloseContext != NULL);
346 }
347 }
348 else
349 {
350 //CloseContext = FatAllocateCloseContext(Vcb);
351 DPRINT1("TODO: Allocate close context!\n");
352 ASSERT(CloseContext != NULL);
353 }
354
355 /* Free close context */
356 if (CloseContext) ExFreePool(CloseContext);
357 }
358 }
359
360 /* Complete the request */
361 FatCompleteRequest(NULL, Irp, Status);
362
363 /* Reset the top level IRP if necessary */
364 if (TopLevel) IoSetTopLevelIrp(NULL);
365
366 return Status;
367 }
368
369 NTSTATUS
370 NTAPI
371 FatClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
372 {
373 PFAT_IRP_CONTEXT IrpContext;
374 NTSTATUS Status;
375
376 DPRINT("FatClose(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
377
378 /* FatClose works only with a volume device object */
379 if (DeviceObject == FatGlobalData.DiskDeviceObject)
380 {
381 /* Complete the request and return success */
382 Irp->IoStatus.Status = STATUS_SUCCESS;
383 Irp->IoStatus.Information = FILE_OPENED;
384
385 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
386
387 return STATUS_SUCCESS;
388 }
389
390 /* Enter FsRtl critical region */
391 FsRtlEnterFileSystem();
392
393 /* Build an irp context */
394 IrpContext = FatBuildIrpContext(Irp, TRUE);
395
396 /* Call internal function */
397 Status = FatiClose(IrpContext, Irp);
398
399 /* Leave FsRtl critical region */
400 FsRtlExitFileSystem();
401
402 return Status;
403 }
404
405 VOID
406 NTAPI
407 FatPendingClose(IN PVCB Vcb OPTIONAL)
408 {
409 PCLOSE_CONTEXT CloseContext;
410 PVCB CurrentVcb = NULL;
411 PVCB LastVcb = NULL;
412 BOOLEAN FreeContext;
413 ULONG Loops = 0;
414
415 /* Do the top-level IRP trick */
416 if (!Vcb) IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
417
418 while ((CloseContext = FatRemoveClose(Vcb, LastVcb)))
419 {
420 if (!Vcb)
421 {
422 if (!FatGlobalData.ShutdownStarted)
423 {
424 if (CloseContext->Vcb != CurrentVcb)
425 {
426 Loops = 0;
427
428 /* Release previous VCB */
429 if (CurrentVcb)
430 ExReleaseResourceLite(&CurrentVcb->Resource);
431
432 /* Lock the new VCB */
433 CurrentVcb = CloseContext->Vcb;
434 (VOID)ExAcquireResourceExclusiveLite(&CurrentVcb->Resource, TRUE);
435 }
436 else
437 {
438 /* Try to lock */
439 if (++Loops >= 20)
440 {
441 if (ExGetSharedWaiterCount(&CurrentVcb->Resource) +
442 ExGetExclusiveWaiterCount(&CurrentVcb->Resource))
443 {
444 ExReleaseResourceLite(&CurrentVcb->Resource);
445 (VOID)ExAcquireResourceExclusiveLite(&CurrentVcb->Resource, TRUE);
446 }
447
448 Loops = 0;
449 }
450 }
451
452 /* Check open count */
453 if (CurrentVcb->OpenFileCount <= 1)
454 {
455 ExReleaseResourceLite(&CurrentVcb->Resource);
456 CurrentVcb = NULL;
457 }
458 }
459 else if (CurrentVcb)
460 {
461 ExReleaseResourceLite(&CurrentVcb->Resource);
462 CurrentVcb = NULL;
463 }
464 }
465
466 LastVcb = CurrentVcb;
467
468 /* Remember if we should free the context */
469 FreeContext = CloseContext->Free;
470
471 FatiCommonClose(CloseContext->Vcb,
472 CloseContext->Fcb,
473 (FreeContext ? NULL : CONTAINING_RECORD(CloseContext, CCB, CloseContext)),
474 CloseContext->TypeOfOpen,
475 TRUE,
476 NULL);
477
478 /* Free context if necessary */
479 if (FreeContext) ExFreePool(CloseContext);
480 }
481
482 /* Release VCB if necessary */
483 if (CurrentVcb) ExReleaseResourceLite(&CurrentVcb->Resource);
484
485 /* Reset top level IRP */
486 if (!Vcb) IoSetTopLevelIrp( NULL );
487 }
488
489 VOID
490 NTAPI
491 FatCloseWorker(IN PDEVICE_OBJECT DeviceObject,
492 IN PVOID Context)
493 {
494 FsRtlEnterFileSystem();
495
496 FatPendingClose((PVCB)Context);
497
498 FsRtlExitFileSystem();
499 }
500
501 VOID
502 NTAPI
503 FatQueueClose(IN PCLOSE_CONTEXT CloseContext,
504 IN BOOLEAN DelayClose)
505 {
506 BOOLEAN RunWorker = FALSE;
507
508 /* Acquire the close lists mutex */
509 ExAcquireFastMutexUnsafe(&FatCloseQueueMutex);
510
511 /* Add it to the desired list */
512 if (DelayClose)
513 {
514 InsertTailList(&FatGlobalData.DelayedCloseList,
515 &CloseContext->GlobalLinks);
516 InsertTailList(&CloseContext->Vcb->DelayedCloseList,
517 &CloseContext->VcbLinks);
518
519 FatGlobalData.DelayedCloseCount++;
520
521 if (FatGlobalData.DelayedCloseCount > FatMaxDelayedCloseCount &&
522 !FatGlobalData.AsyncCloseActive)
523 {
524 FatGlobalData.AsyncCloseActive = TRUE;
525 RunWorker = TRUE;
526 }
527 }
528 else
529 {
530 InsertTailList(&FatGlobalData.AsyncCloseList,
531 &CloseContext->GlobalLinks);
532 InsertTailList(&CloseContext->Vcb->AsyncCloseList,
533 &CloseContext->VcbLinks);
534
535 FatGlobalData.AsyncCloseCount++;
536
537 if (!FatGlobalData.AsyncCloseActive)
538 {
539 FatGlobalData.AsyncCloseActive = TRUE;
540 RunWorker = TRUE;
541 }
542 }
543
544 /* Release the close lists mutex */
545 ExReleaseFastMutexUnsafe(&FatCloseQueueMutex);
546
547 if (RunWorker)
548 IoQueueWorkItem(FatGlobalData.FatCloseItem, FatCloseWorker, CriticalWorkQueue, NULL);
549 }
550
551 PCLOSE_CONTEXT
552 NTAPI
553 FatRemoveClose(PVCB Vcb OPTIONAL,
554 PVCB LastVcbHint OPTIONAL)
555 {
556 PLIST_ENTRY Entry;
557 PCLOSE_CONTEXT CloseContext;
558 BOOLEAN IsWorker = FALSE;
559
560 /* Acquire the close lists mutex */
561 ExAcquireFastMutexUnsafe(&FatCloseQueueMutex);
562
563 if (!Vcb) IsWorker = TRUE;
564
565 if (Vcb == NULL && LastVcbHint != NULL)
566 {
567 // TODO: A very special case of overflowing the queue
568 UNIMPLEMENTED;
569 }
570
571 /* Usual processing from a worker thread */
572 if (!Vcb)
573 {
574 TryToCloseAgain:
575
576 /* Is there anything in the async close list */
577 if (!IsListEmpty(&FatGlobalData.AsyncCloseList))
578 {
579 Entry = RemoveHeadList(&FatGlobalData.AsyncCloseList);
580 FatGlobalData.AsyncCloseCount--;
581
582 CloseContext = CONTAINING_RECORD(Entry,
583 CLOSE_CONTEXT,
584 GlobalLinks);
585
586 RemoveEntryList(&CloseContext->VcbLinks);
587 } else if (!IsListEmpty(&FatGlobalData.DelayedCloseList) &&
588 (FatGlobalData.DelayedCloseCount > FatMaxDelayedCloseCount/2 ||
589 FatGlobalData.ShutdownStarted))
590 {
591 /* In case of a shutdown or when delayed queue is filled at half - perform closing */
592 Entry = RemoveHeadList(&FatGlobalData.DelayedCloseList);
593 FatGlobalData.DelayedCloseCount--;
594
595 CloseContext = CONTAINING_RECORD(Entry,
596 CLOSE_CONTEXT,
597 GlobalLinks);
598 RemoveEntryList(&CloseContext->VcbLinks);
599 }
600 else
601 {
602 /* Nothing to close */
603 CloseContext = NULL;
604 if (IsWorker) FatGlobalData.AsyncCloseActive = FALSE;
605 }
606 }
607 else
608 {
609 if (!IsListEmpty(&Vcb->AsyncCloseList))
610 {
611 /* Is there anything in the async close list */
612 Entry = RemoveHeadList(&Vcb->AsyncCloseList);
613 FatGlobalData.AsyncCloseCount--;
614
615 CloseContext = CONTAINING_RECORD(Entry,
616 CLOSE_CONTEXT,
617 VcbLinks);
618
619 RemoveEntryList(&CloseContext->GlobalLinks);
620 }
621 else if (!IsListEmpty(&Vcb->DelayedCloseList))
622 {
623 /* Process delayed close list */
624 Entry = RemoveHeadList(&Vcb->DelayedCloseList);
625 FatGlobalData.DelayedCloseCount--;
626
627 CloseContext = CONTAINING_RECORD(Entry,
628 CLOSE_CONTEXT,
629 VcbLinks);
630
631 RemoveEntryList(&CloseContext->GlobalLinks);
632 }
633 else if (LastVcbHint)
634 {
635 /* Try again */
636 goto TryToCloseAgain;
637 }
638 else
639 {
640 /* Nothing to close */
641 CloseContext = NULL;
642 }
643 }
644
645 /* Release the close lists mutex */
646 ExReleaseFastMutexUnsafe(&FatCloseQueueMutex);
647
648 return CloseContext;
649 }
650
651 /* EOF */