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