1 /* $Id: copy.c,v 1.13 2002/10/01 19:27:20 chorns 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)))
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
);