xref: /reactos/ntoskrnl/cc/fs.c (revision 9393fc32)
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     LONGLONG StartOffset;
165     LONGLONG EndOffset;
166     LIST_ENTRY FreeList;
167     KIRQL OldIrql;
168     PLIST_ENTRY ListEntry;
169     PROS_VACB Vacb;
170     LONGLONG ViewEnd;
171     BOOLEAN Success;
172 
173     CCTRACE(CC_API_DEBUG, "SectionObjectPointer=%p\n FileOffset=%p Length=%lu UninitializeCacheMaps=%d",
174         SectionObjectPointer, FileOffset, Length, UninitializeCacheMaps);
175 
176     if (UninitializeCacheMaps)
177     {
178         DPRINT1("FIXME: CcPurgeCacheSection not uninitializing private cache maps\n");
179     }
180 
181     SharedCacheMap = SectionObjectPointer->SharedCacheMap;
182     if (!SharedCacheMap)
183     {
184         Success = TRUE;
185         goto purgeMm;
186     }
187 
188     StartOffset = FileOffset != NULL ? FileOffset->QuadPart : 0;
189     if (Length == 0 || FileOffset == NULL)
190     {
191         EndOffset = MAXLONGLONG;
192     }
193     else
194     {
195         EndOffset = StartOffset + Length;
196         ASSERT(EndOffset > StartOffset);
197     }
198 
199     InitializeListHead(&FreeList);
200 
201     /* Assume success */
202     Success = TRUE;
203 
204     OldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
205     KeAcquireSpinLockAtDpcLevel(&SharedCacheMap->CacheMapLock);
206     ListEntry = SharedCacheMap->CacheMapVacbListHead.Flink;
207     while (ListEntry != &SharedCacheMap->CacheMapVacbListHead)
208     {
209         ULONG Refs;
210 
211         Vacb = CONTAINING_RECORD(ListEntry, ROS_VACB, CacheMapVacbListEntry);
212         ListEntry = ListEntry->Flink;
213 
214         /* Skip VACBs outside the range, or only partially in range */
215         if (Vacb->FileOffset.QuadPart < StartOffset)
216         {
217             continue;
218         }
219         ViewEnd = min(Vacb->FileOffset.QuadPart + VACB_MAPPING_GRANULARITY,
220                       SharedCacheMap->SectionSize.QuadPart);
221         if (ViewEnd >= EndOffset)
222         {
223             break;
224         }
225 
226         /* Still in use, it cannot be purged, fail
227          * Allow one ref: VACB is supposed to be always 1-referenced
228          */
229         Refs = CcRosVacbGetRefCount(Vacb);
230         if ((Refs > 1 && !Vacb->Dirty) ||
231             (Refs > 2 && Vacb->Dirty))
232         {
233             Success = FALSE;
234             break;
235         }
236 
237         /* This VACB is in range, so unlink it and mark for free */
238         ASSERT(Refs == 1 || Vacb->Dirty);
239         RemoveEntryList(&Vacb->VacbLruListEntry);
240         InitializeListHead(&Vacb->VacbLruListEntry);
241         if (Vacb->Dirty)
242         {
243             CcRosUnmarkDirtyVacb(Vacb, FALSE);
244         }
245         RemoveEntryList(&Vacb->CacheMapVacbListEntry);
246         InsertHeadList(&FreeList, &Vacb->CacheMapVacbListEntry);
247     }
248     KeReleaseSpinLockFromDpcLevel(&SharedCacheMap->CacheMapLock);
249     KeReleaseQueuedSpinLock(LockQueueMasterLock, OldIrql);
250 
251     while (!IsListEmpty(&FreeList))
252     {
253         ULONG Refs;
254 
255         Vacb = CONTAINING_RECORD(RemoveHeadList(&FreeList),
256                                  ROS_VACB,
257                                  CacheMapVacbListEntry);
258         InitializeListHead(&Vacb->CacheMapVacbListEntry);
259         Refs = CcRosVacbDecRefCount(Vacb);
260         ASSERT(Refs == 0);
261     }
262 
263     /* Now make sure that Mm doesn't hold some pages here. */
264 purgeMm:
265     if (Success)
266         Success = MmPurgeSegment(SectionObjectPointer, FileOffset, Length);
267 
268     return Success;
269 }
270 
271 
272 /*
273  * @implemented
274  */
275 VOID NTAPI
276 CcSetFileSizes (
277     IN PFILE_OBJECT FileObject,
278     IN PCC_FILE_SIZES FileSizes)
279 {
280     KIRQL OldIrql;
281     PROS_SHARED_CACHE_MAP SharedCacheMap;
282     LARGE_INTEGER OldSectionSize;
283 
284     CCTRACE(CC_API_DEBUG, "FileObject=%p FileSizes=%p\n",
285         FileObject, FileSizes);
286 
287     DPRINT("CcSetFileSizes(FileObject 0x%p, FileSizes 0x%p)\n",
288            FileObject, FileSizes);
289     DPRINT("AllocationSize %I64d, FileSize %I64d, ValidDataLength %I64d\n",
290            FileSizes->AllocationSize.QuadPart,
291            FileSizes->FileSize.QuadPart,
292            FileSizes->ValidDataLength.QuadPart);
293 
294     SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
295 
296     /*
297      * It is valid to call this function on file objects that weren't
298      * initialized for caching. In this case it's simple no-op.
299      */
300     if (SharedCacheMap == NULL)
301         return;
302 
303     /* Update the relevant fields */
304     KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &OldIrql);
305     OldSectionSize = SharedCacheMap->SectionSize;
306     SharedCacheMap->SectionSize = FileSizes->AllocationSize;
307     SharedCacheMap->FileSize = FileSizes->FileSize;
308     SharedCacheMap->ValidDataLength = FileSizes->ValidDataLength;
309     KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
310 
311     if (FileSizes->AllocationSize.QuadPart < OldSectionSize.QuadPart)
312     {
313         CcPurgeCacheSection(FileObject->SectionObjectPointer,
314                             &FileSizes->AllocationSize,
315                             0,
316                             FALSE);
317     }
318     else
319     {
320         /* Extend our section object */
321         MmExtendSection(SharedCacheMap->Section, &SharedCacheMap->SectionSize);
322     }
323 }
324 
325 /*
326  * @unimplemented
327  */
328 VOID
329 NTAPI
330 CcSetLogHandleForFile (
331     IN PFILE_OBJECT FileObject,
332     IN PVOID LogHandle,
333     IN PFLUSH_TO_LSN FlushToLsnRoutine)
334 {
335     CCTRACE(CC_API_DEBUG, "FileObject=%p LogHandle=%p FlushToLsnRoutine=%p\n",
336         FileObject, LogHandle, FlushToLsnRoutine);
337 
338     UNIMPLEMENTED;
339 }
340 
341 /*
342  * @unimplemented
343  */
344 BOOLEAN
345 NTAPI
346 CcUninitializeCacheMap (
347     IN PFILE_OBJECT FileObject,
348     IN PLARGE_INTEGER TruncateSize OPTIONAL,
349     IN PCACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent OPTIONAL)
350 {
351     NTSTATUS Status;
352     PROS_SHARED_CACHE_MAP SharedCacheMap;
353     KIRQL OldIrql;
354 
355     CCTRACE(CC_API_DEBUG, "FileObject=%p TruncateSize=%p UninitializeCompleteEvent=%p\n",
356         FileObject, TruncateSize, UninitializeCompleteEvent);
357 
358     if (TruncateSize != NULL &&
359         FileObject->SectionObjectPointer->SharedCacheMap != NULL)
360     {
361         SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
362         KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &OldIrql);
363         if (SharedCacheMap->FileSize.QuadPart > TruncateSize->QuadPart)
364         {
365             SharedCacheMap->FileSize = *TruncateSize;
366         }
367         KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
368         CcPurgeCacheSection(FileObject->SectionObjectPointer,
369                             TruncateSize,
370                             0,
371                             FALSE);
372     }
373 
374     Status = CcRosReleaseFileCache(FileObject);
375     if (UninitializeCompleteEvent)
376     {
377         KeSetEvent(&UninitializeCompleteEvent->Event, IO_NO_INCREMENT, FALSE);
378     }
379     return NT_SUCCESS(Status);
380 }
381 
382 BOOLEAN
383 NTAPI
384 CcGetFileSizes (
385     IN PFILE_OBJECT FileObject,
386     IN PCC_FILE_SIZES FileSizes)
387 {
388     PROS_SHARED_CACHE_MAP SharedCacheMap;
389 
390     SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
391 
392     if (!SharedCacheMap)
393         return FALSE;
394 
395     FileSizes->AllocationSize = SharedCacheMap->SectionSize;
396     FileSizes->FileSize = FileSizes->ValidDataLength = SharedCacheMap->FileSize;
397     return TRUE;
398 }
399