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