xref: /reactos/ntoskrnl/cc/fs.c (revision 69931a4a)
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