xref: /reactos/ntoskrnl/cc/fs.c (revision 1734f297)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/cc/fs.c
5  * PURPOSE:         Implements cache managers functions useful for File Systems
6  *
7  * PROGRAMMERS:     Alex Ionescu
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <ntoskrnl.h>
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* FUNCTIONS *****************************************************************/
18 
19 /*
20  * @unimplemented
21  */
22 LARGE_INTEGER
23 NTAPI
24 CcGetDirtyPages (
25     IN PVOID LogHandle,
26     IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,
27     IN PVOID Context1,
28     IN PVOID Context2)
29 {
30     LARGE_INTEGER i;
31 
32     CCTRACE(CC_API_DEBUG, "LogHandle=%p DirtyPageRoutine=%p Context1=%p Context2=%p\n",
33         LogHandle, DirtyPageRoutine, Context1, Context2);
34 
35     UNIMPLEMENTED;
36     i.QuadPart = 0;
37     return i;
38 }
39 
40 /*
41  * @implemented
42  */
43 PFILE_OBJECT
44 NTAPI
45 CcGetFileObjectFromBcb (
46     IN PVOID Bcb)
47 {
48     PINTERNAL_BCB iBcb = CONTAINING_RECORD(Bcb, INTERNAL_BCB, PFCB);
49 
50     CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb);
51 
52     return iBcb->Vacb->SharedCacheMap->FileObject;
53 }
54 
55 /*
56  * @unimplemented
57  */
58 LARGE_INTEGER
59 NTAPI
60 CcGetLsnForFileObject (
61     IN PFILE_OBJECT FileObject,
62     OUT PLARGE_INTEGER OldestLsn OPTIONAL)
63 {
64     LARGE_INTEGER i;
65 
66     CCTRACE(CC_API_DEBUG, "FileObject=%p\n", FileObject);
67 
68     UNIMPLEMENTED;
69     i.QuadPart = 0;
70     return i;
71 }
72 
73 /*
74  * @unimplemented
75  */
76 VOID
77 NTAPI
78 CcInitializeCacheMap (
79     IN PFILE_OBJECT FileObject,
80     IN PCC_FILE_SIZES FileSizes,
81     IN BOOLEAN PinAccess,
82     IN PCACHE_MANAGER_CALLBACKS CallBacks,
83     IN PVOID LazyWriterContext)
84 {
85     NTSTATUS Status;
86 
87     ASSERT(FileObject);
88     ASSERT(FileSizes);
89 
90     CCTRACE(CC_API_DEBUG, "FileObject=%p FileSizes=%p PinAccess=%d CallBacks=%p LazyWriterContext=%p\n",
91         FileObject, FileSizes, PinAccess, CallBacks, LazyWriterContext);
92 
93     /* Call old ROS cache init function */
94     Status = CcRosInitializeFileCache(FileObject,
95                                       FileSizes,
96                                       PinAccess,
97                                       CallBacks,
98                                       LazyWriterContext);
99     if (!NT_SUCCESS(Status))
100         ExRaiseStatus(Status);
101 }
102 
103 /*
104  * @implemented
105  */
106 BOOLEAN
107 NTAPI
108 CcIsThereDirtyData (
109     IN PVPB Vpb)
110 {
111     PROS_VACB Vacb;
112     PLIST_ENTRY Entry;
113     KIRQL oldIrql;
114     /* Assume no dirty data */
115     BOOLEAN Dirty = FALSE;
116 
117     CCTRACE(CC_API_DEBUG, "Vpb=%p\n", Vpb);
118 
119     oldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
120 
121     /* Browse dirty VACBs */
122     for (Entry = DirtyVacbListHead.Flink; Entry != &DirtyVacbListHead; Entry = Entry->Flink)
123     {
124         Vacb = CONTAINING_RECORD(Entry, ROS_VACB, DirtyVacbListEntry);
125         /* Look for these associated with our volume */
126         if (Vacb->SharedCacheMap->FileObject->Vpb != Vpb)
127         {
128             continue;
129         }
130 
131         /* From now on, we are associated with our VPB */
132 
133         /* Temporary files are not counted as dirty */
134         if (BooleanFlagOn(Vacb->SharedCacheMap->FileObject->Flags, FO_TEMPORARY_FILE))
135         {
136             continue;
137         }
138 
139         /* A single dirty VACB is enough to have dirty data */
140         if (Vacb->Dirty)
141         {
142             Dirty = TRUE;
143             break;
144         }
145     }
146 
147     KeReleaseQueuedSpinLock(LockQueueMasterLock, oldIrql);
148 
149     return Dirty;
150 }
151 
152 /*
153  * @unimplemented
154  */
155 BOOLEAN
156 NTAPI
157 CcPurgeCacheSection (
158     IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
159     IN PLARGE_INTEGER FileOffset OPTIONAL,
160     IN ULONG Length,
161     IN BOOLEAN UninitializeCacheMaps)
162 {
163     PROS_SHARED_CACHE_MAP SharedCacheMap;
164     PPRIVATE_CACHE_MAP PrivateCacheMap;
165     LONGLONG StartOffset;
166     LONGLONG EndOffset;
167     LIST_ENTRY FreeList;
168     KIRQL OldIrql;
169     PLIST_ENTRY ListEntry;
170     PROS_VACB Vacb;
171     LONGLONG ViewEnd;
172     BOOLEAN Success;
173 
174     CCTRACE(CC_API_DEBUG, "SectionObjectPointer=%p\n FileOffset=%p Length=%lu UninitializeCacheMaps=%d",
175         SectionObjectPointer, FileOffset, Length, UninitializeCacheMaps);
176 
177     /* Obtain the shared cache from the section */
178     SharedCacheMap = SectionObjectPointer->SharedCacheMap;
179     if (!SharedCacheMap)
180     {
181         Success = TRUE;
182         goto purgeMm;
183     }
184 
185     if (UninitializeCacheMaps)
186     {
187         /*
188          * We have gotten the acknowledgement that
189          * the caller wants to unintialize the private
190          * cache maps so let's do this. Since we already
191          * have the shared cache map from above, iterate
192          * over that cache's private lists.
193          */
194         while (!IsListEmpty(&SharedCacheMap->PrivateList))
195         {
196             /*
197              * This list is not empty, grab the
198              * private cache map.
199              */
200             PrivateCacheMap = CONTAINING_RECORD(SharedCacheMap->PrivateList.Flink, PRIVATE_CACHE_MAP, PrivateLinks);
201 
202             /* Unintialize the private cache now */
203             CcUninitializeCacheMap(PrivateCacheMap->FileObject, NULL, NULL);
204         }
205     }
206 
207     StartOffset = FileOffset != NULL ? FileOffset->QuadPart : 0;
208     if (Length == 0 || FileOffset == NULL)
209     {
210         EndOffset = MAXLONGLONG;
211     }
212     else
213     {
214         EndOffset = StartOffset + Length;
215         ASSERT(EndOffset > StartOffset);
216     }
217 
218     InitializeListHead(&FreeList);
219 
220     /* Assume success */
221     Success = TRUE;
222 
223     OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
224     KeAcquireSpinLockAtDpcLevel(&SharedCacheMap->CacheMapLock);
225     ListEntry = SharedCacheMap->CacheMapVacbListHead.Flink;
226     while (ListEntry != &SharedCacheMap->CacheMapVacbListHead)
227     {
228         ULONG Refs;
229 
230         Vacb = CONTAINING_RECORD(ListEntry, ROS_VACB, CacheMapVacbListEntry);
231         ListEntry = ListEntry->Flink;
232 
233         /* Skip VACBs outside the range, or only partially in range */
234         if (Vacb->FileOffset.QuadPart < StartOffset)
235         {
236             continue;
237         }
238         ViewEnd = min(Vacb->FileOffset.QuadPart + VACB_MAPPING_GRANULARITY,
239                       SharedCacheMap->SectionSize.QuadPart);
240         if (ViewEnd >= EndOffset)
241         {
242             break;
243         }
244 
245         /* Still in use, it cannot be purged, fail
246          * Allow one ref: VACB is supposed to be always 1-referenced
247          */
248         Refs = CcRosVacbGetRefCount(Vacb);
249         if ((Refs > 1 && !Vacb->Dirty) ||
250             (Refs > 2 && Vacb->Dirty))
251         {
252             Success = FALSE;
253             break;
254         }
255 
256         /* This VACB is in range, so unlink it and mark for free */
257         ASSERT(Refs == 1 || Vacb->Dirty);
258         RemoveEntryList(&Vacb->VacbLruListEntry);
259         InitializeListHead(&Vacb->VacbLruListEntry);
260         if (Vacb->Dirty)
261         {
262             CcRosUnmarkDirtyVacb(Vacb, FALSE);
263         }
264         RemoveEntryList(&Vacb->CacheMapVacbListEntry);
265         InsertHeadList(&FreeList, &Vacb->CacheMapVacbListEntry);
266     }
267     KeReleaseSpinLockFromDpcLevel(&SharedCacheMap->CacheMapLock);
268     KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
269 
270     while (!IsListEmpty(&FreeList))
271     {
272         ULONG Refs;
273 
274         Vacb = CONTAINING_RECORD(RemoveHeadList(&FreeList),
275                                  ROS_VACB,
276                                  CacheMapVacbListEntry);
277         InitializeListHead(&Vacb->CacheMapVacbListEntry);
278         Refs = CcRosVacbDecRefCount(Vacb);
279         ASSERT(Refs == 0);
280     }
281 
282     /* Now make sure that Mm doesn't hold some pages here. */
283 purgeMm:
284     if (Success)
285         Success = MmPurgeSegment(SectionObjectPointer, FileOffset, Length);
286 
287     return Success;
288 }
289 
290 
291 /*
292  * @implemented
293  */
294 VOID NTAPI
295 CcSetFileSizes (
296     IN PFILE_OBJECT FileObject,
297     IN PCC_FILE_SIZES FileSizes)
298 {
299     KIRQL OldIrql;
300     PROS_SHARED_CACHE_MAP SharedCacheMap;
301     LARGE_INTEGER OldSectionSize;
302 
303     CCTRACE(CC_API_DEBUG, "FileObject=%p FileSizes=%p\n",
304         FileObject, FileSizes);
305 
306     DPRINT("CcSetFileSizes(FileObject 0x%p, FileSizes 0x%p)\n",
307            FileObject, FileSizes);
308     DPRINT("AllocationSize %I64d, FileSize %I64d, ValidDataLength %I64d\n",
309            FileSizes->AllocationSize.QuadPart,
310            FileSizes->FileSize.QuadPart,
311            FileSizes->ValidDataLength.QuadPart);
312 
313     SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
314 
315     /*
316      * It is valid to call this function on file objects that weren't
317      * initialized for caching. In this case it's simple no-op.
318      */
319     if (SharedCacheMap == NULL)
320         return;
321 
322     /* Update the relevant fields */
323     KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &OldIrql);
324     OldSectionSize = SharedCacheMap->SectionSize;
325     SharedCacheMap->SectionSize = FileSizes->AllocationSize;
326     SharedCacheMap->FileSize = FileSizes->FileSize;
327     SharedCacheMap->ValidDataLength = FileSizes->ValidDataLength;
328     KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
329 
330     if (FileSizes->AllocationSize.QuadPart < OldSectionSize.QuadPart)
331     {
332         CcPurgeCacheSection(FileObject->SectionObjectPointer,
333                             &FileSizes->AllocationSize,
334                             0,
335                             FALSE);
336     }
337     else
338     {
339         /* Extend our section object */
340         MmExtendSection(SharedCacheMap->Section, &SharedCacheMap->SectionSize);
341     }
342 }
343 
344 /*
345  * @unimplemented
346  */
347 VOID
348 NTAPI
349 CcSetLogHandleForFile (
350     IN PFILE_OBJECT FileObject,
351     IN PVOID LogHandle,
352     IN PFLUSH_TO_LSN FlushToLsnRoutine)
353 {
354     CCTRACE(CC_API_DEBUG, "FileObject=%p LogHandle=%p FlushToLsnRoutine=%p\n",
355         FileObject, LogHandle, FlushToLsnRoutine);
356 
357     UNIMPLEMENTED;
358 }
359 
360 /*
361  * @unimplemented
362  */
363 BOOLEAN
364 NTAPI
365 CcUninitializeCacheMap (
366     IN PFILE_OBJECT FileObject,
367     IN PLARGE_INTEGER TruncateSize OPTIONAL,
368     IN PCACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent OPTIONAL)
369 {
370     NTSTATUS Status;
371     PROS_SHARED_CACHE_MAP SharedCacheMap;
372     KIRQL OldIrql;
373 
374     CCTRACE(CC_API_DEBUG, "FileObject=%p TruncateSize=%p UninitializeCompleteEvent=%p\n",
375         FileObject, TruncateSize, UninitializeCompleteEvent);
376 
377     if (TruncateSize != NULL &&
378         FileObject->SectionObjectPointer->SharedCacheMap != NULL)
379     {
380         SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
381         KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &OldIrql);
382         if (SharedCacheMap->FileSize.QuadPart > TruncateSize->QuadPart)
383         {
384             SharedCacheMap->FileSize = *TruncateSize;
385         }
386         KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
387         CcPurgeCacheSection(FileObject->SectionObjectPointer,
388                             TruncateSize,
389                             0,
390                             FALSE);
391     }
392 
393     Status = CcRosReleaseFileCache(FileObject);
394     if (UninitializeCompleteEvent)
395     {
396         KeSetEvent(&UninitializeCompleteEvent->Event, IO_NO_INCREMENT, FALSE);
397     }
398     return NT_SUCCESS(Status);
399 }
400 
401 BOOLEAN
402 NTAPI
403 CcGetFileSizes (
404     IN PFILE_OBJECT FileObject,
405     IN PCC_FILE_SIZES FileSizes)
406 {
407     PROS_SHARED_CACHE_MAP SharedCacheMap;
408 
409     SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
410 
411     if (!SharedCacheMap)
412         return FALSE;
413 
414     FileSizes->AllocationSize = SharedCacheMap->SectionSize;
415     FileSizes->FileSize = FileSizes->ValidDataLength = SharedCacheMap->FileSize;
416     return TRUE;
417 }
418