3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/cc/copy.c
6 * PURPOSE: Implements cache managers copy interface
7 * PROGRAMMER: Hartmut Birr
12 /* INCLUDES ******************************************************************/
14 #include <ddk/ntddk.h>
15 #include <ddk/ntifs.h>
16 #include <internal/mm.h>
17 #include <internal/cc.h>
18 #include <internal/pool.h>
19 #include <internal/io.h>
20 #include <ntos/minmax.h>
23 #include <internal/debug.h>
25 /* GLOBALS *******************************************************************/
27 #define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
29 static PHYSICAL_ADDRESS CcZeroPage = (PHYSICAL_ADDRESS)0LL;
31 /* FUNCTIONS *****************************************************************/
34 CcInitCacheZeroPage(VOID)
38 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &CcZeroPage);
39 if (!NT_SUCCESS(Status))
41 DbgPrint("Can't allocate CcZeroPage.\n");
44 Status = MiZeroPage(CcZeroPage);
45 if (!NT_SUCCESS(Status))
47 DbgPrint("Can't zero out CcZeroPage.\n");
53 ReadCacheSegmentChain(PBCB Bcb, ULONG ReadOffset, ULONG Length,
57 PCACHE_SEGMENT current;
58 PCACHE_SEGMENT previous;
60 LARGE_INTEGER SegOffset;
65 Status = CcRosGetCacheSegmentChain(Bcb, ReadOffset, Length, &head);
66 if (!NT_SUCCESS(Status))
71 while (current != NULL)
74 * If the current segment is valid then copy it into the
79 TempLength = min(Bcb->CacheSegmentSize, Length);
80 memcpy(Buffer, current->BaseAddress, TempLength);
82 Length = Length - TempLength;
84 current = current->NextInChain;
85 CcRosReleaseCacheSegment(Bcb, previous, TRUE, FALSE, FALSE);
88 * Otherwise read in as much as we can.
92 PCACHE_SEGMENT current2;
99 * Count the maximum number of bytes we could read starting
100 * from the current segment.
104 while (current2 != NULL && !current2->Valid)
106 current2 = current2->NextInChain;
107 current_size += Bcb->CacheSegmentSize;
111 * Create an MDL which contains all their pages.
113 Mdl = MmCreateMdl(NULL, NULL, current_size);
114 Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
117 while (current2 != NULL && !current2->Valid)
119 for (i = 0; i < (Bcb->CacheSegmentSize / PAGE_SIZE); i++)
122 PHYSICAL_ADDRESS page;
123 address = current2->BaseAddress + (i * PAGE_SIZE);
124 page = MmGetPhysicalAddressForProcess(NULL, address);
125 ((PULONG)(Mdl + 1))[offset] = page.u.LowPart;
128 current2 = current2->NextInChain;
132 * Read in the information.
134 SegOffset.QuadPart = current->FileOffset;
135 KeInitializeEvent(&Event, NotificationEvent, FALSE);
136 Status = IoPageRead(Bcb->FileObject,
141 if (Status == STATUS_PENDING)
143 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
144 Status = Iosb.Status;
146 if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
148 while (current != NULL)
151 current = current->NextInChain;
152 CcRosReleaseCacheSegment(Bcb, previous, FALSE, FALSE, FALSE);
156 while (current != NULL && !current->Valid)
159 current = current->NextInChain;
160 TempLength = min(Bcb->CacheSegmentSize, Length);
161 memcpy(Buffer, previous->BaseAddress, TempLength);
162 Buffer += TempLength;
163 Length = Length - TempLength;
164 CcRosReleaseCacheSegment(Bcb, previous, TRUE, FALSE, FALSE);
168 return(STATUS_SUCCESS);
172 ReadCacheSegment(PCACHE_SEGMENT CacheSeg)
177 LARGE_INTEGER SegOffset;
178 IO_STATUS_BLOCK IoStatus;
181 SegOffset.QuadPart = CacheSeg->FileOffset;
182 Size = CacheSeg->Bcb->AllocationSize.QuadPart - CacheSeg->FileOffset;
183 if (Size > CacheSeg->Bcb->CacheSegmentSize)
185 Size = CacheSeg->Bcb->CacheSegmentSize;
187 Mdl = MmCreateMdl(NULL, CacheSeg->BaseAddress, Size);
188 MmBuildMdlForNonPagedPool(Mdl);
189 KeInitializeEvent(&Event, NotificationEvent, FALSE);
190 Status = IoPageRead(CacheSeg->Bcb->FileObject, Mdl, &SegOffset, & Event, &IoStatus);
191 if (Status == STATUS_PENDING)
193 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
194 Status = IoStatus.Status;
197 if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
199 CcRosReleaseCacheSegment(CacheSeg->Bcb, CacheSeg, FALSE, FALSE, FALSE);
200 DPRINT1("IoPageRead failed, Status %x\n", Status);
203 if (CacheSeg->Bcb->CacheSegmentSize > Size)
205 memset (CacheSeg->BaseAddress + Size, 0,
206 CacheSeg->Bcb->CacheSegmentSize - Size);
208 return STATUS_SUCCESS;
212 WriteCacheSegment(PCACHE_SEGMENT CacheSeg)
217 IO_STATUS_BLOCK IoStatus;
218 LARGE_INTEGER SegOffset;
221 CacheSeg->Dirty = FALSE;
222 SegOffset.QuadPart = CacheSeg->FileOffset;
223 Size = CacheSeg->Bcb->AllocationSize.QuadPart - CacheSeg->FileOffset;
224 if (Size > CacheSeg->Bcb->CacheSegmentSize)
226 Size = CacheSeg->Bcb->CacheSegmentSize;
228 Mdl = MmCreateMdl(NULL, CacheSeg->BaseAddress, Size);
229 MmBuildMdlForNonPagedPool(Mdl);
230 KeInitializeEvent(&Event, NotificationEvent, FALSE);
231 Status = IoPageWrite(CacheSeg->Bcb->FileObject, Mdl, &SegOffset, &Event, &IoStatus);
232 if (Status == STATUS_PENDING)
234 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
235 Status = IoStatus.Status;
237 if (!NT_SUCCESS(Status))
239 DPRINT1("IoPageWrite failed, Status %x\n", Status);
240 CacheSeg->Dirty = TRUE;
243 return(STATUS_SUCCESS);
247 CcCopyRead (IN PFILE_OBJECT FileObject,
248 IN PLARGE_INTEGER FileOffset,
252 OUT PIO_STATUS_BLOCK IoStatus)
256 NTSTATUS Status = STATUS_SUCCESS;
258 PCACHE_SEGMENT CacheSeg;
260 ULONG ReadLength = 0;
263 PLIST_ENTRY current_entry;
264 PCACHE_SEGMENT current;
266 DPRINT("CcCopyRead(FileObject %x, FileOffset %x, "
267 "Length %d, Wait %d, Buffer %x, IoStatus %x)\n",
268 FileObject, (ULONG)FileOffset->QuadPart, Length, Wait,
271 Bcb = ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->Bcb;
272 ReadOffset = FileOffset->QuadPart;
274 DPRINT("AllocationSize %d, FileSize %d\n",
275 (ULONG)Bcb->AllocationSize.QuadPart,
276 (ULONG)Bcb->FileSize.QuadPart);
279 * Check for the nowait case that all the cache segments that would
280 * cover this read are in memory.
284 KeAcquireSpinLock(&Bcb->BcbLock, &oldirql);
285 current_entry = Bcb->BcbSegmentListHead.Flink;
286 while (current_entry != &Bcb->BcbSegmentListHead)
288 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
289 BcbSegmentListEntry);
290 if (!current->Valid && current->FileOffset < ReadOffset + Length
291 && current->FileOffset + Bcb->CacheSegmentSize > ReadOffset)
293 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
294 IoStatus->Status = STATUS_UNSUCCESSFUL;
295 IoStatus->Information = 0;
298 current_entry = current_entry->Flink;
300 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
303 TempLength = ReadOffset % Bcb->CacheSegmentSize;
306 TempLength = min (Length, Bcb->CacheSegmentSize - TempLength);
307 Status = CcRosRequestCacheSegment(Bcb,
308 ROUND_DOWN(ReadOffset,
309 Bcb->CacheSegmentSize),
310 &BaseAddress, &Valid, &CacheSeg);
311 if (!NT_SUCCESS(Status))
313 IoStatus->Information = 0;
314 IoStatus->Status = Status;
315 DPRINT("CcRosRequestCacheSegment faild, Status %x\n", Status);
320 Status = ReadCacheSegment(CacheSeg);
321 if (!NT_SUCCESS(Status))
323 IoStatus->Information = 0;
324 IoStatus->Status = Status;
328 memcpy (Buffer, BaseAddress + ReadOffset % Bcb->CacheSegmentSize,
330 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
331 ReadLength += TempLength;
332 Length -= TempLength;
333 ReadOffset += TempLength;
334 Buffer += TempLength;
338 TempLength = min(max(Bcb->CacheSegmentSize, 65536), Length);
339 ReadCacheSegmentChain(Bcb, ReadOffset, TempLength, Buffer);
340 ReadLength += TempLength;
341 Length -= TempLength;
342 ReadOffset += TempLength;
343 Buffer += TempLength;
345 IoStatus->Status = STATUS_SUCCESS;
346 IoStatus->Information = ReadLength;
347 DPRINT("CcCopyRead O.K.\n");
352 CcCopyWrite (IN PFILE_OBJECT FileObject,
353 IN PLARGE_INTEGER FileOffset,
362 PLIST_ENTRY current_entry;
363 PCACHE_SEGMENT CacheSeg;
368 DPRINT("CcCopyWrite(FileObject %x, FileOffset %x, "
369 "Length %d, Wait %d, Buffer %x)\n",
370 FileObject, (ULONG)FileOffset->QuadPart, Length, Wait, Buffer);
372 Bcb = ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->Bcb;
373 WriteOffset = (ULONG)FileOffset->QuadPart;
377 /* testing, if the requested datas are available */
378 KeAcquireSpinLock(&Bcb->BcbLock, &oldirql);
379 current_entry = Bcb->BcbSegmentListHead.Flink;
380 while (current_entry != &Bcb->BcbSegmentListHead)
382 CacheSeg = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
383 BcbSegmentListEntry);
384 if (!CacheSeg->Valid)
386 if ((WriteOffset >= CacheSeg->FileOffset &&
387 WriteOffset < CacheSeg->FileOffset + Bcb->CacheSegmentSize)
388 || (WriteOffset + Length > CacheSeg->FileOffset &&
389 WriteOffset + Length <= CacheSeg->FileOffset +
390 Bcb->CacheSegmentSize))
392 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
393 /* datas not available */
397 current_entry = current_entry->Flink;
399 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
402 TempLength = WriteOffset % Bcb->CacheSegmentSize;
406 ROffset = ROUND_DOWN(WriteOffset, Bcb->CacheSegmentSize);
407 TempLength = min (Length, Bcb->CacheSegmentSize - TempLength);
408 Status = CcRosRequestCacheSegment(Bcb, ROffset,
409 &BaseAddress, &Valid, &CacheSeg);
410 if (!NT_SUCCESS(Status))
416 if (!NT_SUCCESS(ReadCacheSegment(CacheSeg)))
421 memcpy (BaseAddress + WriteOffset % Bcb->CacheSegmentSize,
423 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, TRUE, FALSE);
425 Length -= TempLength;
426 WriteOffset += TempLength;
427 Buffer += TempLength;
432 TempLength = min (Bcb->CacheSegmentSize, Length);
433 Status = CcRosRequestCacheSegment(Bcb, WriteOffset,
434 &BaseAddress, &Valid, &CacheSeg);
435 if (!NT_SUCCESS(Status))
439 if (!Valid && TempLength < Bcb->CacheSegmentSize)
441 if (!NT_SUCCESS(ReadCacheSegment(CacheSeg)))
446 memcpy (BaseAddress, Buffer, TempLength);
447 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, TRUE, FALSE);
448 Length -= TempLength;
449 WriteOffset += TempLength;
450 Buffer += TempLength;
456 CcZeroData (IN PFILE_OBJECT FileObject,
457 IN PLARGE_INTEGER StartOffset,
458 IN PLARGE_INTEGER EndOffset,
462 LARGE_INTEGER WriteOffset;
466 IO_STATUS_BLOCK Iosb;
469 DPRINT("CcZeroData(FileObject %x, StartOffset %I64x, EndOffset %I64x, "
470 "Wait %d\n", FileObject, StartOffset->QuadPart, EndOffset->QuadPart,
473 Length = EndOffset->u.LowPart - StartOffset->u.LowPart;
476 * FIXME: NT uses the shared cache map field for cached/non cached detection
478 if (FileObject->SectionObjectPointers->SharedCacheMap == NULL)
480 /* File is not cached */
481 WriteOffset.QuadPart = StartOffset->QuadPart;
485 if (Length + WriteOffset.u.LowPart % PAGE_SIZE > 262144)
487 Mdl = MmCreateMdl(NULL, (PVOID)WriteOffset.u.LowPart,
488 262144 - WriteOffset.u.LowPart % PAGE_SIZE);
489 WriteOffset.QuadPart +=
490 (262144 - WriteOffset.u.LowPart % PAGE_SIZE);
491 Length -= (262144 - WriteOffset.u.LowPart % PAGE_SIZE);
496 MmCreateMdl(NULL, (PVOID)WriteOffset.u.LowPart,
497 Length - WriteOffset.u.LowPart % PAGE_SIZE);
498 WriteOffset.QuadPart +=
499 (Length - WriteOffset.u.LowPart % PAGE_SIZE);
506 Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
507 for (i = 0; i < ((Mdl->Size - sizeof(MDL)) / sizeof(ULONG)); i++)
509 ((PULONG)(Mdl + 1))[i] = CcZeroPage.u.LowPart;
511 KeInitializeEvent(&Event, NotificationEvent, FALSE);
512 Status = IoPageWrite(FileObject, Mdl, StartOffset, &Event, &Iosb);
513 if (Status == STATUS_PENDING)
515 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
516 Status = Iosb.Status;
518 if (!NT_SUCCESS(Status))
529 PLIST_ENTRY current_entry;
530 PCACHE_SEGMENT CacheSeg, current, previous;
535 PHYSICAL_ADDRESS page;
537 Start = StartOffset->u.LowPart;
538 Bcb = ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->Bcb;
541 /* testing, if the requested datas are available */
542 KeAcquireSpinLock(&Bcb->BcbLock, &oldirql);
543 current_entry = Bcb->BcbSegmentListHead.Flink;
544 while (current_entry != &Bcb->BcbSegmentListHead)
546 CacheSeg = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
547 BcbSegmentListEntry);
548 if (!CacheSeg->Valid)
550 if ((Start >= CacheSeg->FileOffset &&
551 Start < CacheSeg->FileOffset + Bcb->CacheSegmentSize)
552 || (Start + Length > CacheSeg->FileOffset &&
554 CacheSeg->FileOffset + Bcb->CacheSegmentSize))
556 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
557 /* datas not available */
561 current_entry = current_entry->Flink;
563 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
568 ULONG RStart = ROUND_DOWN(Start, Bcb->CacheSegmentSize);
569 WriteOffset.QuadPart = ROUND_DOWN(Start, Bcb->CacheSegmentSize);
570 if (Start % Bcb->CacheSegmentSize + Length > 262144)
572 Mdl = MmCreateMdl(NULL, NULL, 262144);
577 Status = CcRosGetCacheSegmentChain (Bcb, RStart,
579 if (!NT_SUCCESS(Status))
588 RLength = Start % Bcb->CacheSegmentSize + Length;
589 RLength = ROUND_UP(RLength, Bcb->CacheSegmentSize);
590 Mdl = MmCreateMdl(NULL, (PVOID)RStart, RLength);
595 Status = CcRosGetCacheSegmentChain (Bcb, RStart, RLength,
597 if (!NT_SUCCESS(Status))
603 Mdl->MdlFlags |= (MDL_PAGES_LOCKED|MDL_IO_PAGE_READ);
606 while (current != NULL)
608 if ((Start % Bcb->CacheSegmentSize) != 0 ||
609 Start % Bcb->CacheSegmentSize + Length <
610 Bcb->CacheSegmentSize)
615 Status = ReadCacheSegment(current);
616 if (!NT_SUCCESS(Status))
618 DPRINT1("ReadCacheSegment failed, status %x\n",
622 TempLength = min (Length, Bcb->CacheSegmentSize -
623 Start % Bcb->CacheSegmentSize);
624 memset (current->BaseAddress + Start % Bcb->CacheSegmentSize,
629 TempLength = Bcb->CacheSegmentSize;
630 memset (current->BaseAddress, 0, Bcb->CacheSegmentSize);
633 Length -= TempLength;
635 size = ((Mdl->Size - sizeof(MDL)) / sizeof(ULONG));
636 for (i = 0; i < (Bcb->CacheSegmentSize / PAGE_SIZE) &&
640 Address = current->BaseAddress + (i * PAGE_SIZE);
642 MmGetPhysicalAddressForProcess(NULL, Address);
643 ((PULONG)(Mdl + 1))[count++] = page.u.LowPart;
645 current = current->NextInChain;
648 /* Write the Segment */
649 KeInitializeEvent(&Event, NotificationEvent, FALSE);
650 Status = IoPageWrite(FileObject, Mdl, &WriteOffset, &Event, &Iosb);
651 if (Status == STATUS_PENDING)
653 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
654 Status = Iosb.Status;
656 if (!NT_SUCCESS(Status))
658 DPRINT1("IoPageWrite failed, status %x\n", Status);
661 while (current != NULL)
664 current = current->NextInChain;
665 CcRosReleaseCacheSegment(Bcb, previous, TRUE, FALSE, FALSE);