1 /* $Id: copy.c,v 1.20 2003/12/30 18:52:03 fireball Exp $
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)))
30 static PHYSICAL_ADDRESS CcZeroPage
= (PHYSICAL_ADDRESS
)0LL;
32 static PHYSICAL_ADDRESS CcZeroPage
= { 0 };
35 /* FUNCTIONS *****************************************************************/
38 CcInitCacheZeroPage(VOID
)
42 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, FALSE
, &CcZeroPage
);
43 if (!NT_SUCCESS(Status
))
45 DbgPrint("Can't allocate CcZeroPage.\n");
48 Status
= MiZeroPage(CcZeroPage
);
49 if (!NT_SUCCESS(Status
))
51 DbgPrint("Can't zero out CcZeroPage.\n");
57 ReadCacheSegmentChain(PBCB Bcb
, ULONG ReadOffset
, ULONG Length
,
61 PCACHE_SEGMENT current
;
62 PCACHE_SEGMENT previous
;
64 LARGE_INTEGER SegOffset
;
69 Status
= CcRosGetCacheSegmentChain(Bcb
, ReadOffset
, Length
, &head
);
70 if (!NT_SUCCESS(Status
))
75 while (current
!= NULL
)
78 * If the current segment is valid then copy it into the
83 TempLength
= min(Bcb
->CacheSegmentSize
, Length
);
84 memcpy(Buffer
, current
->BaseAddress
, TempLength
);
94 Length
= Length
- TempLength
;
96 current
= current
->NextInChain
;
97 CcRosReleaseCacheSegment(Bcb
, previous
, TRUE
, FALSE
, FALSE
);
100 * Otherwise read in as much as we can.
104 PCACHE_SEGMENT current2
;
111 * Count the maximum number of bytes we could read starting
112 * from the current segment.
116 while (current2
!= NULL
&& !current2
->Valid
)
118 current2
= current2
->NextInChain
;
119 current_size
+= Bcb
->CacheSegmentSize
;
123 * Create an MDL which contains all their pages.
125 Mdl
= MmCreateMdl(NULL
, NULL
, current_size
);
126 Mdl
->MdlFlags
|= (MDL_PAGES_LOCKED
| MDL_IO_PAGE_READ
);
129 while (current2
!= NULL
&& !current2
->Valid
)
131 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGE_SIZE
); i
++)
134 PHYSICAL_ADDRESS page
;
135 address
= (char*)current2
->BaseAddress
+ (i
* PAGE_SIZE
);
136 page
= MmGetPhysicalAddressForProcess(NULL
, address
);
137 ((PULONG
)(Mdl
+ 1))[offset
] = page
.u
.LowPart
;
140 current2
= current2
->NextInChain
;
144 * Read in the information.
146 SegOffset
.QuadPart
= current
->FileOffset
;
147 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
148 Status
= IoPageRead(Bcb
->FileObject
,
153 if (Status
== STATUS_PENDING
)
155 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
156 Status
= Iosb
.Status
;
158 if (!NT_SUCCESS(Status
) && Status
!= STATUS_END_OF_FILE
)
160 while (current
!= NULL
)
163 current
= current
->NextInChain
;
164 CcRosReleaseCacheSegment(Bcb
, previous
, FALSE
, FALSE
, FALSE
);
168 while (current
!= NULL
&& !current
->Valid
)
171 current
= current
->NextInChain
;
172 TempLength
= min(Bcb
->CacheSegmentSize
, Length
);
173 memcpy(Buffer
, previous
->BaseAddress
, TempLength
);
174 #if defined(__GNUC__)
175 Buffer
+= TempLength
;
178 char* pTemp
= Buffer
;
183 Length
= Length
- TempLength
;
184 CcRosReleaseCacheSegment(Bcb
, previous
, TRUE
, FALSE
, FALSE
);
188 return(STATUS_SUCCESS
);
192 ReadCacheSegment(PCACHE_SEGMENT CacheSeg
)
197 LARGE_INTEGER SegOffset
;
198 IO_STATUS_BLOCK IoStatus
;
201 SegOffset
.QuadPart
= CacheSeg
->FileOffset
;
202 Size
= (ULONG
)(CacheSeg
->Bcb
->AllocationSize
.QuadPart
- CacheSeg
->FileOffset
);
203 if (Size
> CacheSeg
->Bcb
->CacheSegmentSize
)
205 Size
= CacheSeg
->Bcb
->CacheSegmentSize
;
207 Mdl
= MmCreateMdl(NULL
, CacheSeg
->BaseAddress
, Size
);
208 MmBuildMdlForNonPagedPool(Mdl
);
209 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
210 Status
= IoPageRead(CacheSeg
->Bcb
->FileObject
, Mdl
, &SegOffset
, & Event
, &IoStatus
);
211 if (Status
== STATUS_PENDING
)
213 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
214 Status
= IoStatus
.Status
;
217 if (!NT_SUCCESS(Status
) && Status
!= STATUS_END_OF_FILE
)
219 DPRINT1("IoPageRead failed, Status %x\n", Status
);
222 if (CacheSeg
->Bcb
->CacheSegmentSize
> Size
)
224 memset ((char*)CacheSeg
->BaseAddress
+ Size
, 0,
225 CacheSeg
->Bcb
->CacheSegmentSize
- Size
);
227 return STATUS_SUCCESS
;
231 WriteCacheSegment(PCACHE_SEGMENT CacheSeg
)
236 IO_STATUS_BLOCK IoStatus
;
237 LARGE_INTEGER SegOffset
;
240 CacheSeg
->Dirty
= FALSE
;
241 SegOffset
.QuadPart
= CacheSeg
->FileOffset
;
242 Size
= (ULONG
)(CacheSeg
->Bcb
->AllocationSize
.QuadPart
- CacheSeg
->FileOffset
);
243 if (Size
> CacheSeg
->Bcb
->CacheSegmentSize
)
245 Size
= CacheSeg
->Bcb
->CacheSegmentSize
;
247 Mdl
= MmCreateMdl(NULL
, CacheSeg
->BaseAddress
, Size
);
248 MmBuildMdlForNonPagedPool(Mdl
);
249 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
250 Status
= IoPageWrite(CacheSeg
->Bcb
->FileObject
, Mdl
, &SegOffset
, &Event
, &IoStatus
);
251 if (Status
== STATUS_PENDING
)
253 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
254 Status
= IoStatus
.Status
;
256 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_END_OF_FILE
))
258 DPRINT1("IoPageWrite failed, Status %x\n", Status
);
259 CacheSeg
->Dirty
= TRUE
;
262 return(STATUS_SUCCESS
);
269 CcCopyRead (IN PFILE_OBJECT FileObject
,
270 IN PLARGE_INTEGER FileOffset
,
274 OUT PIO_STATUS_BLOCK IoStatus
)
278 NTSTATUS Status
= STATUS_SUCCESS
;
280 PCACHE_SEGMENT CacheSeg
;
282 ULONG ReadLength
= 0;
285 PLIST_ENTRY current_entry
;
286 PCACHE_SEGMENT current
;
288 DPRINT("CcCopyRead(FileObject %x, FileOffset %x, "
289 "Length %d, Wait %d, Buffer %x, IoStatus %x)\n",
290 FileObject
, (ULONG
)FileOffset
->QuadPart
, Length
, Wait
,
293 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
294 ReadOffset
= (ULONG
)FileOffset
->QuadPart
;
296 DPRINT("AllocationSize %d, FileSize %d\n",
297 (ULONG
)Bcb
->AllocationSize
.QuadPart
,
298 (ULONG
)Bcb
->FileSize
.QuadPart
);
301 * Check for the nowait case that all the cache segments that would
302 * cover this read are in memory.
306 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldirql
);
307 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
308 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
310 current
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
311 BcbSegmentListEntry
);
312 if (!current
->Valid
&& current
->FileOffset
< ReadOffset
+ Length
313 && current
->FileOffset
+ Bcb
->CacheSegmentSize
> ReadOffset
)
315 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
316 IoStatus
->Status
= STATUS_UNSUCCESSFUL
;
317 IoStatus
->Information
= 0;
320 current_entry
= current_entry
->Flink
;
322 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
325 TempLength
= ReadOffset
% Bcb
->CacheSegmentSize
;
328 TempLength
= min (Length
, Bcb
->CacheSegmentSize
- TempLength
);
329 Status
= CcRosRequestCacheSegment(Bcb
,
330 ROUND_DOWN(ReadOffset
,
331 Bcb
->CacheSegmentSize
),
332 &BaseAddress
, &Valid
, &CacheSeg
);
333 if (!NT_SUCCESS(Status
))
335 IoStatus
->Information
= 0;
336 IoStatus
->Status
= Status
;
337 DPRINT("CcRosRequestCacheSegment faild, Status %x\n", Status
);
342 Status
= ReadCacheSegment(CacheSeg
);
343 if (!NT_SUCCESS(Status
))
345 IoStatus
->Information
= 0;
346 IoStatus
->Status
= Status
;
347 CcRosReleaseCacheSegment(Bcb
, CacheSeg
, FALSE
, FALSE
, FALSE
);
351 memcpy (Buffer
, (char*)BaseAddress
+ ReadOffset
% Bcb
->CacheSegmentSize
,
353 CcRosReleaseCacheSegment(Bcb
, CacheSeg
, TRUE
, FALSE
, FALSE
);
354 ReadLength
+= TempLength
;
355 Length
-= TempLength
;
356 ReadOffset
+= TempLength
;
357 #if defined(__GNUC__)
358 Buffer
+= TempLength
;
361 char* pTemp
= Buffer
;
369 TempLength
= min(max(Bcb
->CacheSegmentSize
, 65536), Length
);
370 ReadCacheSegmentChain(Bcb
, ReadOffset
, TempLength
, Buffer
);
371 ReadLength
+= TempLength
;
372 Length
-= TempLength
;
373 ReadOffset
+= TempLength
;
374 #if defined(__GNUC__)
375 Buffer
+= TempLength
;
378 char* pTemp
= Buffer
;
384 IoStatus
->Status
= STATUS_SUCCESS
;
385 IoStatus
->Information
= ReadLength
;
386 DPRINT("CcCopyRead O.K.\n");
394 CcCopyWrite (IN PFILE_OBJECT FileObject
,
395 IN PLARGE_INTEGER FileOffset
,
404 PLIST_ENTRY current_entry
;
405 PCACHE_SEGMENT CacheSeg
;
410 DPRINT("CcCopyWrite(FileObject %x, FileOffset %x, "
411 "Length %d, Wait %d, Buffer %x)\n",
412 FileObject
, (ULONG
)FileOffset
->QuadPart
, Length
, Wait
, Buffer
);
414 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
415 WriteOffset
= (ULONG
)FileOffset
->QuadPart
;
419 /* testing, if the requested datas are available */
420 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldirql
);
421 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
422 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
424 CacheSeg
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
425 BcbSegmentListEntry
);
426 if (!CacheSeg
->Valid
)
428 if ((WriteOffset
>= CacheSeg
->FileOffset
&&
429 WriteOffset
< CacheSeg
->FileOffset
+ Bcb
->CacheSegmentSize
)
430 || (WriteOffset
+ Length
> CacheSeg
->FileOffset
&&
431 WriteOffset
+ Length
<= CacheSeg
->FileOffset
+
432 Bcb
->CacheSegmentSize
))
434 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
435 /* datas not available */
439 current_entry
= current_entry
->Flink
;
441 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
444 TempLength
= WriteOffset
% Bcb
->CacheSegmentSize
;
448 ROffset
= ROUND_DOWN(WriteOffset
, Bcb
->CacheSegmentSize
);
449 TempLength
= min (Length
, Bcb
->CacheSegmentSize
- TempLength
);
450 Status
= CcRosRequestCacheSegment(Bcb
, ROffset
,
451 &BaseAddress
, &Valid
, &CacheSeg
);
452 if (!NT_SUCCESS(Status
))
458 if (!NT_SUCCESS(ReadCacheSegment(CacheSeg
)))
463 memcpy ((char*)BaseAddress
+ WriteOffset
% Bcb
->CacheSegmentSize
,
465 CcRosReleaseCacheSegment(Bcb
, CacheSeg
, TRUE
, TRUE
, FALSE
);
467 Length
-= TempLength
;
468 WriteOffset
+= TempLength
;
469 #if defined(__GNUC__)
470 Buffer
+= TempLength
;
473 char* pTemp
= Buffer
;
482 TempLength
= min (Bcb
->CacheSegmentSize
, Length
);
483 Status
= CcRosRequestCacheSegment(Bcb
, WriteOffset
,
484 &BaseAddress
, &Valid
, &CacheSeg
);
485 if (!NT_SUCCESS(Status
))
489 if (!Valid
&& TempLength
< Bcb
->CacheSegmentSize
)
491 if (!NT_SUCCESS(ReadCacheSegment(CacheSeg
)))
493 CcRosReleaseCacheSegment(Bcb
, CacheSeg
, FALSE
, FALSE
, FALSE
);
497 memcpy (BaseAddress
, Buffer
, TempLength
);
498 CcRosReleaseCacheSegment(Bcb
, CacheSeg
, TRUE
, TRUE
, FALSE
);
499 Length
-= TempLength
;
500 WriteOffset
+= TempLength
;
501 #if defined(__GNUC__)
502 Buffer
+= TempLength
;
505 char* pTemp
= Buffer
;
518 CcZeroData (IN PFILE_OBJECT FileObject
,
519 IN PLARGE_INTEGER StartOffset
,
520 IN PLARGE_INTEGER EndOffset
,
524 LARGE_INTEGER WriteOffset
;
528 IO_STATUS_BLOCK Iosb
;
531 DPRINT("CcZeroData(FileObject %x, StartOffset %I64x, EndOffset %I64x, "
532 "Wait %d\n", FileObject
, StartOffset
->QuadPart
, EndOffset
->QuadPart
,
535 Length
= EndOffset
->u
.LowPart
- StartOffset
->u
.LowPart
;
537 if (FileObject
->SectionObjectPointer
->SharedCacheMap
== NULL
)
539 /* File is not cached */
540 WriteOffset
.QuadPart
= StartOffset
->QuadPart
;
544 if (Length
+ WriteOffset
.u
.LowPart
% PAGE_SIZE
> 262144)
546 Mdl
= MmCreateMdl(NULL
, (PVOID
)WriteOffset
.u
.LowPart
,
547 262144 - WriteOffset
.u
.LowPart
% PAGE_SIZE
);
548 WriteOffset
.QuadPart
+=
549 (262144 - WriteOffset
.u
.LowPart
% PAGE_SIZE
);
550 Length
-= (262144 - WriteOffset
.u
.LowPart
% PAGE_SIZE
);
555 MmCreateMdl(NULL
, (PVOID
)WriteOffset
.u
.LowPart
,
556 Length
- WriteOffset
.u
.LowPart
% PAGE_SIZE
);
557 WriteOffset
.QuadPart
+=
558 (Length
- WriteOffset
.u
.LowPart
% PAGE_SIZE
);
565 Mdl
->MdlFlags
|= (MDL_PAGES_LOCKED
| MDL_IO_PAGE_READ
);
566 for (i
= 0; i
< ((Mdl
->Size
- sizeof(MDL
)) / sizeof(ULONG
)); i
++)
568 ((PULONG
)(Mdl
+ 1))[i
] = CcZeroPage
.u
.LowPart
;
570 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
571 Status
= IoPageWrite(FileObject
, Mdl
, StartOffset
, &Event
, &Iosb
);
572 if (Status
== STATUS_PENDING
)
574 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
575 Status
= Iosb
.Status
;
577 if (!NT_SUCCESS(Status
))
588 PLIST_ENTRY current_entry
;
589 PCACHE_SEGMENT CacheSeg
, current
, previous
;
594 PHYSICAL_ADDRESS page
;
596 Start
= StartOffset
->u
.LowPart
;
597 Bcb
= FileObject
->SectionObjectPointer
->SharedCacheMap
;
600 /* testing, if the requested datas are available */
601 KeAcquireSpinLock(&Bcb
->BcbLock
, &oldirql
);
602 current_entry
= Bcb
->BcbSegmentListHead
.Flink
;
603 while (current_entry
!= &Bcb
->BcbSegmentListHead
)
605 CacheSeg
= CONTAINING_RECORD(current_entry
, CACHE_SEGMENT
,
606 BcbSegmentListEntry
);
607 if (!CacheSeg
->Valid
)
609 if ((Start
>= CacheSeg
->FileOffset
&&
610 Start
< CacheSeg
->FileOffset
+ Bcb
->CacheSegmentSize
)
611 || (Start
+ Length
> CacheSeg
->FileOffset
&&
613 CacheSeg
->FileOffset
+ Bcb
->CacheSegmentSize
))
615 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
616 /* datas not available */
620 current_entry
= current_entry
->Flink
;
622 KeReleaseSpinLock(&Bcb
->BcbLock
, oldirql
);
627 ULONG RStart
= ROUND_DOWN(Start
, Bcb
->CacheSegmentSize
);
628 WriteOffset
.QuadPart
= ROUND_DOWN(Start
, Bcb
->CacheSegmentSize
);
629 if (Start
% Bcb
->CacheSegmentSize
+ Length
> 262144)
631 Mdl
= MmCreateMdl(NULL
, NULL
, 262144);
636 Status
= CcRosGetCacheSegmentChain (Bcb
, RStart
,
638 if (!NT_SUCCESS(Status
))
647 RLength
= Start
% Bcb
->CacheSegmentSize
+ Length
;
648 RLength
= ROUND_UP(RLength
, Bcb
->CacheSegmentSize
);
649 Mdl
= MmCreateMdl(NULL
, (PVOID
)RStart
, RLength
);
654 Status
= CcRosGetCacheSegmentChain (Bcb
, RStart
, RLength
,
656 if (!NT_SUCCESS(Status
))
662 Mdl
->MdlFlags
|= (MDL_PAGES_LOCKED
|MDL_IO_PAGE_READ
);
665 while (current
!= NULL
)
667 if ((Start
% Bcb
->CacheSegmentSize
) != 0 ||
668 Start
% Bcb
->CacheSegmentSize
+ Length
<
669 Bcb
->CacheSegmentSize
)
674 Status
= ReadCacheSegment(current
);
675 if (!NT_SUCCESS(Status
))
677 DPRINT1("ReadCacheSegment failed, status %x\n",
681 TempLength
= min (Length
, Bcb
->CacheSegmentSize
-
682 Start
% Bcb
->CacheSegmentSize
);
683 memset ((char*)current
->BaseAddress
+ Start
% Bcb
->CacheSegmentSize
,
688 TempLength
= Bcb
->CacheSegmentSize
;
689 memset (current
->BaseAddress
, 0, Bcb
->CacheSegmentSize
);
692 Length
-= TempLength
;
694 size
= ((Mdl
->Size
- sizeof(MDL
)) / sizeof(ULONG
));
695 for (i
= 0; i
< (Bcb
->CacheSegmentSize
/ PAGE_SIZE
) &&
699 Address
= (char*)current
->BaseAddress
+ (i
* PAGE_SIZE
);
701 MmGetPhysicalAddressForProcess(NULL
, Address
);
702 ((PULONG
)(Mdl
+ 1))[count
++] = page
.u
.LowPart
;
704 current
= current
->NextInChain
;
707 /* Write the Segment */
708 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
709 Status
= IoPageWrite(FileObject
, Mdl
, &WriteOffset
, &Event
, &Iosb
);
710 if (Status
== STATUS_PENDING
)
712 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
713 Status
= Iosb
.Status
;
715 if (!NT_SUCCESS(Status
))
717 DPRINT1("IoPageWrite failed, status %x\n", Status
);
720 while (current
!= NULL
)
723 current
= current
->NextInChain
;
724 CcRosReleaseCacheSegment(Bcb
, previous
, TRUE
, FALSE
, FALSE
);