1 /*
2  * PROJECT:     VFAT Filesystem
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Cleanup 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 /*
19  * FUNCTION: Cleans up after a file has been closed.
20  * Returns whether the device was deleted
21  */
22 static
23 BOOLEAN
24 VfatCleanupFile(
25     PVFAT_IRP_CONTEXT IrpContext)
26 {
27     PVFATFCB pFcb;
28     PVFATCCB pCcb;
29     BOOLEAN IsVolume;
30     PDEVICE_EXTENSION DeviceExt = IrpContext->DeviceExt;
31     PFILE_OBJECT FileObject = IrpContext->FileObject;
32     BOOLEAN Deleted = FALSE;
33 
34     DPRINT("VfatCleanupFile(DeviceExt %p, FileObject %p)\n",
35            IrpContext->DeviceExt, FileObject);
36 
37     /* FIXME: handle file/directory deletion here */
38     pFcb = (PVFATFCB)FileObject->FsContext;
39     if (!pFcb)
40         return FALSE;
41 
42     IsVolume = BooleanFlagOn(pFcb->Flags, FCB_IS_VOLUME);
43     if (IsVolume)
44     {
45         pFcb->OpenHandleCount--;
46         DeviceExt->OpenHandleCount--;
47 
48         if (pFcb->OpenHandleCount != 0)
49         {
50             IoRemoveShareAccess(FileObject, &pFcb->FCBShareAccess);
51         }
52     }
53     else
54     {
55         ExAcquireResourceExclusiveLite(&pFcb->MainResource, TRUE);
56         ExAcquireResourceExclusiveLite(&pFcb->PagingIoResource, TRUE);
57 
58         pCcb = FileObject->FsContext2;
59         if (BooleanFlagOn(pCcb->Flags, CCB_DELETE_ON_CLOSE))
60         {
61             pFcb->Flags |= FCB_DELETE_PENDING;
62         }
63 
64         /* Notify about the cleanup */
65         FsRtlNotifyCleanup(IrpContext->DeviceExt->NotifySync,
66                            &(IrpContext->DeviceExt->NotifyList),
67                            FileObject->FsContext2);
68 
69         pFcb->OpenHandleCount--;
70         DeviceExt->OpenHandleCount--;
71 
72         if (!vfatFCBIsDirectory(pFcb) &&
73             FsRtlAreThereCurrentFileLocks(&pFcb->FileLock))
74         {
75             /* remove all locks this process have on this file */
76             FsRtlFastUnlockAll(&pFcb->FileLock,
77                                FileObject,
78                                IoGetRequestorProcess(IrpContext->Irp),
79                                NULL);
80         }
81 
82         if (BooleanFlagOn(pFcb->Flags, FCB_IS_DIRTY))
83         {
84             VfatUpdateEntry (DeviceExt, pFcb);
85         }
86 
87         if (BooleanFlagOn(pFcb->Flags, FCB_DELETE_PENDING) &&
88             pFcb->OpenHandleCount == 0)
89         {
90             if (vfatFCBIsDirectory(pFcb) &&
91                 !VfatIsDirectoryEmpty(DeviceExt, pFcb))
92             {
93                 pFcb->Flags &= ~FCB_DELETE_PENDING;
94             }
95             else
96             {
97                 PFILE_OBJECT tmpFileObject;
98                 tmpFileObject = pFcb->FileObject;
99                 if (tmpFileObject != NULL)
100                 {
101                     pFcb->FileObject = NULL;
102                     CcUninitializeCacheMap(tmpFileObject, NULL, NULL);
103                     ClearFlag(pFcb->Flags, FCB_CACHE_INITIALIZED);
104                     ObDereferenceObject(tmpFileObject);
105                 }
106 
107                 pFcb->RFCB.ValidDataLength.QuadPart = 0;
108                 pFcb->RFCB.FileSize.QuadPart = 0;
109                 pFcb->RFCB.AllocationSize.QuadPart = 0;
110             }
111         }
112 
113         /* Uninitialize the cache (should be done even if caching was never initialized) */
114         CcUninitializeCacheMap(FileObject, &pFcb->RFCB.FileSize, NULL);
115 
116         if (BooleanFlagOn(pFcb->Flags, FCB_DELETE_PENDING) &&
117             pFcb->OpenHandleCount == 0)
118         {
119             VfatDelEntry(DeviceExt, pFcb, NULL);
120 
121             vfatReportChange(DeviceExt,
122                              pFcb,
123                              (vfatFCBIsDirectory(pFcb) ?
124                               FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
125                              FILE_ACTION_REMOVED);
126         }
127 
128         if (pFcb->OpenHandleCount != 0)
129         {
130             IoRemoveShareAccess(FileObject, &pFcb->FCBShareAccess);
131         }
132 /* FIXME: causes FS corruption and breaks selfhosting/testbots and so on */
133 #if 0
134         /* If that's the last open handle we just closed, try to see whether
135          * we can delay close operation
136          */
137         else if (!BooleanFlagOn(pFcb->Flags, FCB_DELETE_PENDING) && !BooleanFlagOn(pFcb->Flags, FCB_IS_PAGE_FILE) &&
138                  !BooleanFlagOn(pFcb->Flags, FCB_IS_FAT) && !BooleanFlagOn(pFcb->Flags, FCB_IS_VOLUME))
139         {
140             /* This is only allowed if that's a directory with no open files
141              * OR if it's a file with no section opened
142              */
143             if ((vfatFCBIsDirectory(pFcb) && IsListEmpty(&pFcb->ParentListHead)) ||
144                 (!vfatFCBIsDirectory(pFcb) && FileObject->SectionObjectPointer->DataSectionObject == NULL &&
145                  FileObject->SectionObjectPointer->ImageSectionObject == NULL))
146             {
147                 DPRINT("Delaying close of: %wZ\n", &pFcb->PathNameU);
148                 SetFlag(pFcb->Flags, FCB_DELAYED_CLOSE);
149             }
150         }
151 #endif
152 
153         FileObject->Flags |= FO_CLEANUP_COMPLETE;
154 #ifdef KDBG
155         pFcb->Flags |= FCB_CLEANED_UP;
156 #endif
157 
158         ExReleaseResourceLite(&pFcb->PagingIoResource);
159         ExReleaseResourceLite(&pFcb->MainResource);
160     }
161 
162 #ifdef ENABLE_SWAPOUT
163     if (IsVolume && BooleanFlagOn(DeviceExt->Flags, VCB_DISMOUNT_PENDING))
164     {
165         Deleted = VfatCheckForDismount(DeviceExt, TRUE);
166     }
167 #endif
168 
169     return Deleted;
170 }
171 
172 /*
173  * FUNCTION: Cleans up after a file has been closed.
174  */
175 NTSTATUS
176 VfatCleanup(
177     PVFAT_IRP_CONTEXT IrpContext)
178 {
179     BOOLEAN Deleted;
180 
181     DPRINT("VfatCleanup(DeviceObject %p, Irp %p)\n", IrpContext->DeviceObject, IrpContext->Irp);
182 
183     if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
184     {
185         IrpContext->Irp->IoStatus.Information = 0;
186         return STATUS_SUCCESS;
187     }
188 
189     ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource, TRUE);
190     Deleted = VfatCleanupFile(IrpContext);
191     if (!Deleted) ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
192 
193     IrpContext->Irp->IoStatus.Information = 0;
194     return STATUS_SUCCESS;
195 }
196 
197 /* EOF */
198