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
CcGetDirtyPages(IN PVOID LogHandle,IN PDIRTY_PAGE_ROUTINE DirtyPageRoutine,IN PVOID Context1,IN PVOID Context2)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
CcGetFileObjectFromBcb(IN PVOID Bcb)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
CcGetLsnForFileObject(IN PFILE_OBJECT FileObject,OUT PLARGE_INTEGER OldestLsn OPTIONAL)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
CcInitializeCacheMap(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes,IN BOOLEAN PinAccess,IN PCACHE_MANAGER_CALLBACKS CallBacks,IN PVOID LazyWriterContext)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
CcIsThereDirtyData(IN PVPB Vpb)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
CcPurgeCacheSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,IN PLARGE_INTEGER FileOffset OPTIONAL,IN ULONG Length,IN BOOLEAN UninitializeCacheMaps)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
CcSetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)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
CcSetLogHandleForFile(IN PFILE_OBJECT FileObject,IN PVOID LogHandle,IN PFLUSH_TO_LSN FlushToLsnRoutine)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
CcUninitializeCacheMap(IN PFILE_OBJECT FileObject,IN PLARGE_INTEGER TruncateSize OPTIONAL,IN PCACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent OPTIONAL)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
CcGetFileSizes(IN PFILE_OBJECT FileObject,IN PCC_FILE_SIZES FileSizes)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