xref: /reactos/ntoskrnl/cc/fs.c (revision c3483339)
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 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS   *****************************************************************/
17 
18 NTSTATUS CcRosInternalFreeVacb(PROS_VACB Vacb);
19 
20 /* FUNCTIONS *****************************************************************/
21 
22 /*
23  * @unimplemented
24  */
25 LARGE_INTEGER
26 NTAPI
27 CcGetDirtyPages (
28     IN PVOID LogHandle,
29     IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,
30     IN PVOID Context1,
31     IN PVOID Context2)
32 {
33     LARGE_INTEGER i;
34 
35     CCTRACE(CC_API_DEBUG, "LogHandle=%p DirtyPageRoutine=%p Context1=%p Context2=%p\n",
36         LogHandle, DirtyPageRoutine, Context1, Context2);
37 
38     UNIMPLEMENTED;
39     i.QuadPart = 0;
40     return i;
41 }
42 
43 /*
44  * @implemented
45  */
46 PFILE_OBJECT
47 NTAPI
48 CcGetFileObjectFromBcb (
49     IN PVOID Bcb)
50 {
51     PINTERNAL_BCB iBcb = (PINTERNAL_BCB)Bcb;
52 
53     CCTRACE(CC_API_DEBUG, "Bcb=%p\n", Bcb);
54 
55     return iBcb->Vacb->SharedCacheMap->FileObject;
56 }
57 
58 /*
59  * @unimplemented
60  */
61 LARGE_INTEGER
62 NTAPI
63 CcGetLsnForFileObject (
64     IN PFILE_OBJECT FileObject,
65     OUT PLARGE_INTEGER OldestLsn OPTIONAL)
66 {
67     LARGE_INTEGER i;
68 
69     CCTRACE(CC_API_DEBUG, "FileObject=%p\n", FileObject);
70 
71     UNIMPLEMENTED;
72     i.QuadPart = 0;
73     return i;
74 }
75 
76 /*
77  * @unimplemented
78  */
79 VOID
80 NTAPI
81 CcInitializeCacheMap (
82     IN PFILE_OBJECT FileObject,
83     IN PCC_FILE_SIZES FileSizes,
84     IN BOOLEAN PinAccess,
85     IN PCACHE_MANAGER_CALLBACKS CallBacks,
86     IN PVOID LazyWriterContext)
87 {
88     NTSTATUS Status;
89 
90     ASSERT(FileObject);
91     ASSERT(FileSizes);
92 
93     CCTRACE(CC_API_DEBUG, "FileObject=%p FileSizes=%p PinAccess=%d CallBacks=%p LazyWriterContext=%p\n",
94         FileObject, FileSizes, PinAccess, CallBacks, LazyWriterContext);
95 
96     /* Call old ROS cache init function */
97     Status = CcRosInitializeFileCache(FileObject,
98                                       FileSizes,
99                                       PinAccess,
100                                       CallBacks,
101                                       LazyWriterContext);
102     if (!NT_SUCCESS(Status))
103         ExRaiseStatus(Status);
104 }
105 
106 /*
107  * @implemented
108  */
109 BOOLEAN
110 NTAPI
111 CcIsThereDirtyData (
112     IN PVPB Vpb)
113 {
114     PROS_VACB Vacb;
115     PLIST_ENTRY Entry;
116     KIRQL oldIrql;
117     /* Assume no dirty data */
118     BOOLEAN Dirty = FALSE;
119 
120     CCTRACE(CC_API_DEBUG, "Vpb=%p\n", Vpb);
121 
122     oldIrql = KeAcquireQueuedSpinLock(LockQueueMasterLock);
123 
124     /* Browse dirty VACBs */
125     for (Entry = DirtyVacbListHead.Flink; Entry != &DirtyVacbListHead; Entry = Entry->Flink)
126     {
127         Vacb = CONTAINING_RECORD(Entry, ROS_VACB, DirtyVacbListEntry);
128         /* Look for these associated with our volume */
129         if (Vacb->SharedCacheMap->FileObject->Vpb != Vpb)
130         {
131             continue;
132         }
133 
134         /* From now on, we are associated with our VPB */
135 
136         /* Temporary files are not counted as dirty */
137         if (BooleanFlagOn(Vacb->SharedCacheMap->FileObject->Flags, FO_TEMPORARY_FILE))
138         {
139             continue;
140         }
141 
142         /* A single dirty VACB is enough to have dirty data */
143         if (Vacb->Dirty)
144         {
145             Dirty = TRUE;
146             break;
147         }
148     }
149 
150     KeReleaseQueuedSpinLock(LockQueueMasterLock, oldIrql);
151 
152     return Dirty;
153 }
154 
155 /*
156  * @unimplemented
157  */
158 BOOLEAN
159 NTAPI
160 CcPurgeCacheSection (
161     IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
162     IN PLARGE_INTEGER FileOffset OPTIONAL,
163     IN ULONG Length,
164     IN BOOLEAN UninitializeCacheMaps)
165 {
166     PROS_SHARED_CACHE_MAP SharedCacheMap;
167     LONGLONG StartOffset;
168     LONGLONG EndOffset;
169     LIST_ENTRY FreeList;
170     KIRQL OldIrql;
171     PLIST_ENTRY ListEntry;
172     PROS_VACB Vacb;
173     LONGLONG ViewEnd;
174     BOOLEAN Success;
175 
176     CCTRACE(CC_API_DEBUG, "SectionObjectPointer=%p\n FileOffset=%p Length=%lu UninitializeCacheMaps=%d",
177         SectionObjectPointer, FileOffset, Length, UninitializeCacheMaps);
178 
179     if (UninitializeCacheMaps)
180     {
181         DPRINT1("FIXME: CcPurgeCacheSection not uninitializing private cache maps\n");
182     }
183 
184     SharedCacheMap = SectionObjectPointer->SharedCacheMap;
185     if (!SharedCacheMap)
186         return FALSE;
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     return Success;
264 }
265 
266 
267 /*
268  * @implemented
269  */
270 VOID NTAPI
271 CcSetFileSizes (
272     IN PFILE_OBJECT FileObject,
273     IN PCC_FILE_SIZES FileSizes)
274 {
275     KIRQL oldirql;
276     PROS_SHARED_CACHE_MAP SharedCacheMap;
277 
278     CCTRACE(CC_API_DEBUG, "FileObject=%p FileSizes=%p\n",
279         FileObject, FileSizes);
280 
281     DPRINT("CcSetFileSizes(FileObject 0x%p, FileSizes 0x%p)\n",
282            FileObject, FileSizes);
283     DPRINT("AllocationSize %I64d, FileSize %I64d, ValidDataLength %I64d\n",
284            FileSizes->AllocationSize.QuadPart,
285            FileSizes->FileSize.QuadPart,
286            FileSizes->ValidDataLength.QuadPart);
287 
288     SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
289 
290     /*
291      * It is valid to call this function on file objects that weren't
292      * initialized for caching. In this case it's simple no-op.
293      */
294     if (SharedCacheMap == NULL)
295         return;
296 
297     if (FileSizes->AllocationSize.QuadPart < SharedCacheMap->SectionSize.QuadPart)
298     {
299         CcPurgeCacheSection(FileObject->SectionObjectPointer,
300                             &FileSizes->AllocationSize,
301                             0,
302                             FALSE);
303     }
304     else
305     {
306         PROS_VACB LastVacb;
307 
308         /*
309          * If file (allocation) size has increased, then we need to check whether
310          * it just grows in a single VACB (the last one).
311          * If so, we must mark the VACB as invalid to trigger a read to the
312          * FSD at the next VACB usage, and thus avoid returning garbage
313          */
314 
315         /* Check for allocation size and the last VACB */
316         if (SharedCacheMap->SectionSize.QuadPart < FileSizes->AllocationSize.QuadPart &&
317             SharedCacheMap->SectionSize.QuadPart % VACB_MAPPING_GRANULARITY)
318         {
319             LastVacb = CcRosLookupVacb(SharedCacheMap,
320                                        SharedCacheMap->SectionSize.QuadPart);
321             if (LastVacb != NULL)
322             {
323                 /* Mark it as invalid */
324                 CcRosReleaseVacb(SharedCacheMap, LastVacb, LastVacb->Dirty ? LastVacb->Valid : FALSE, FALSE, FALSE);
325             }
326         }
327 
328         /* Check for file size and the last VACB */
329         if (SharedCacheMap->FileSize.QuadPart < FileSizes->FileSize.QuadPart &&
330             SharedCacheMap->FileSize.QuadPart % VACB_MAPPING_GRANULARITY)
331         {
332             LastVacb = CcRosLookupVacb(SharedCacheMap,
333                                        SharedCacheMap->FileSize.QuadPart);
334             if (LastVacb != NULL)
335             {
336                 /* Mark it as invalid */
337                 CcRosReleaseVacb(SharedCacheMap, LastVacb, LastVacb->Dirty ? LastVacb->Valid : FALSE, FALSE, FALSE);
338             }
339         }
340     }
341 
342     KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &oldirql);
343     SharedCacheMap->SectionSize = FileSizes->AllocationSize;
344     SharedCacheMap->FileSize = FileSizes->FileSize;
345     KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, oldirql);
346 }
347 
348 /*
349  * @unimplemented
350  */
351 VOID
352 NTAPI
353 CcSetLogHandleForFile (
354     IN PFILE_OBJECT FileObject,
355     IN PVOID LogHandle,
356     IN PFLUSH_TO_LSN FlushToLsnRoutine)
357 {
358     CCTRACE(CC_API_DEBUG, "FileObject=%p LogHandle=%p FlushToLsnRoutine=%p\n",
359         FileObject, LogHandle, FlushToLsnRoutine);
360 
361     UNIMPLEMENTED;
362 }
363 
364 /*
365  * @unimplemented
366  */
367 BOOLEAN
368 NTAPI
369 CcUninitializeCacheMap (
370     IN PFILE_OBJECT FileObject,
371     IN PLARGE_INTEGER TruncateSize OPTIONAL,
372     IN PCACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent OPTIONAL)
373 {
374     NTSTATUS Status;
375     PROS_SHARED_CACHE_MAP SharedCacheMap;
376     KIRQL OldIrql;
377 
378     CCTRACE(CC_API_DEBUG, "FileObject=%p TruncateSize=%p UninitializeCompleteEvent=%p\n",
379         FileObject, TruncateSize, UninitializeCompleteEvent);
380 
381     if (TruncateSize != NULL &&
382         FileObject->SectionObjectPointer->SharedCacheMap != NULL)
383     {
384         SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
385         KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &OldIrql);
386         if (SharedCacheMap->FileSize.QuadPart > TruncateSize->QuadPart)
387         {
388             SharedCacheMap->FileSize = *TruncateSize;
389         }
390         KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
391         CcPurgeCacheSection(FileObject->SectionObjectPointer,
392                             TruncateSize,
393                             0,
394                             FALSE);
395     }
396 
397     Status = CcRosReleaseFileCache(FileObject);
398     if (UninitializeCompleteEvent)
399     {
400         KeSetEvent(&UninitializeCompleteEvent->Event, IO_NO_INCREMENT, FALSE);
401     }
402     return NT_SUCCESS(Status);
403 }
404 
405 BOOLEAN
406 NTAPI
407 CcGetFileSizes (
408     IN PFILE_OBJECT FileObject,
409     IN PCC_FILE_SIZES FileSizes)
410 {
411     PROS_SHARED_CACHE_MAP SharedCacheMap;
412 
413     SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
414 
415     if (!SharedCacheMap)
416         return FALSE;
417 
418     FileSizes->AllocationSize = SharedCacheMap->SectionSize;
419     FileSizes->FileSize = FileSizes->ValidDataLength = SharedCacheMap->FileSize;
420     return TRUE;
421 }
422