* Sync up to trunk head (r65394).
[reactos.git] / ntoskrnl / cc / copy.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/cc/copy.c
5 * PURPOSE: Implements cache managers copy interface
6 *
7 * PROGRAMMERS:
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 static PFN_NUMBER CcZeroPage = 0;
19
20 #define MAX_ZERO_LENGTH (256 * 1024)
21
22 typedef enum _CC_COPY_OPERATION
23 {
24 CcOperationRead,
25 CcOperationWrite,
26 CcOperationZero
27 } CC_COPY_OPERATION;
28
29 ULONG CcFastMdlReadWait;
30 ULONG CcFastMdlReadNotPossible;
31 ULONG CcFastReadNotPossible;
32 ULONG CcFastReadWait;
33 ULONG CcFastReadNoWait;
34 ULONG CcFastReadResourceMiss;
35
36 /* FUNCTIONS *****************************************************************/
37
38 VOID
39 NTAPI
40 MiZeroPhysicalPage (
41 IN PFN_NUMBER PageFrameIndex
42 );
43
44 VOID
45 NTAPI
46 CcInitCacheZeroPage (
47 VOID)
48 {
49 NTSTATUS Status;
50
51 MI_SET_USAGE(MI_USAGE_CACHE);
52 //MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
53 Status = MmRequestPageMemoryConsumer(MC_SYSTEM, TRUE, &CcZeroPage);
54 if (!NT_SUCCESS(Status))
55 {
56 DbgPrint("Can't allocate CcZeroPage.\n");
57 KeBugCheck(CACHE_MANAGER);
58 }
59 MiZeroPhysicalPage(CcZeroPage);
60 }
61
62 NTSTATUS
63 NTAPI
64 CcReadVirtualAddress (
65 PROS_VACB Vacb)
66 {
67 ULONG Size;
68 PMDL Mdl;
69 NTSTATUS Status;
70 IO_STATUS_BLOCK IoStatus;
71 KEVENT Event;
72
73 Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart);
74 if (Size > VACB_MAPPING_GRANULARITY)
75 {
76 Size = VACB_MAPPING_GRANULARITY;
77 }
78
79 Mdl = IoAllocateMdl(Vacb->BaseAddress, Size, FALSE, FALSE, NULL);
80 if (!Mdl)
81 {
82 return STATUS_INSUFFICIENT_RESOURCES;
83 }
84
85 MmBuildMdlForNonPagedPool(Mdl);
86 Mdl->MdlFlags |= MDL_IO_PAGE_READ;
87 KeInitializeEvent(&Event, NotificationEvent, FALSE);
88 Status = IoPageRead(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
89 if (Status == STATUS_PENDING)
90 {
91 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
92 Status = IoStatus.Status;
93 }
94
95 IoFreeMdl(Mdl);
96
97 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
98 {
99 DPRINT1("IoPageRead failed, Status %x\n", Status);
100 return Status;
101 }
102
103 if (Size < VACB_MAPPING_GRANULARITY)
104 {
105 RtlZeroMemory((char*)Vacb->BaseAddress + Size,
106 VACB_MAPPING_GRANULARITY - Size);
107 }
108
109 return STATUS_SUCCESS;
110 }
111
112 NTSTATUS
113 NTAPI
114 CcWriteVirtualAddress (
115 PROS_VACB Vacb)
116 {
117 ULONG Size;
118 PMDL Mdl;
119 NTSTATUS Status;
120 IO_STATUS_BLOCK IoStatus;
121 KEVENT Event;
122
123 Vacb->Dirty = FALSE;
124 Size = (ULONG)(Vacb->SharedCacheMap->SectionSize.QuadPart - Vacb->FileOffset.QuadPart);
125 if (Size > VACB_MAPPING_GRANULARITY)
126 {
127 Size = VACB_MAPPING_GRANULARITY;
128 }
129 //
130 // Nonpaged pool PDEs in ReactOS must actually be synchronized between the
131 // MmGlobalPageDirectory and the real system PDE directory. What a mess...
132 //
133 {
134 ULONG i = 0;
135 do
136 {
137 MmGetPfnForProcess(NULL, (PVOID)((ULONG_PTR)Vacb->BaseAddress + (i << PAGE_SHIFT)));
138 } while (++i < (Size >> PAGE_SHIFT));
139 }
140
141 Mdl = IoAllocateMdl(Vacb->BaseAddress, Size, FALSE, FALSE, NULL);
142 if (!Mdl)
143 {
144 return STATUS_INSUFFICIENT_RESOURCES;
145 }
146 MmBuildMdlForNonPagedPool(Mdl);
147 Mdl->MdlFlags |= MDL_IO_PAGE_READ;
148 KeInitializeEvent(&Event, NotificationEvent, FALSE);
149 Status = IoSynchronousPageWrite(Vacb->SharedCacheMap->FileObject, Mdl, &Vacb->FileOffset, &Event, &IoStatus);
150 if (Status == STATUS_PENDING)
151 {
152 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
153 Status = IoStatus.Status;
154 }
155 IoFreeMdl(Mdl);
156 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
157 {
158 DPRINT1("IoPageWrite failed, Status %x\n", Status);
159 Vacb->Dirty = TRUE;
160 return Status;
161 }
162
163 return STATUS_SUCCESS;
164 }
165
166 NTSTATUS
167 ReadWriteOrZero(
168 _Inout_ PVOID BaseAddress,
169 _Inout_opt_ PVOID Buffer,
170 _In_ ULONG Length,
171 _In_ CC_COPY_OPERATION Operation)
172 {
173 NTSTATUS Status = STATUS_SUCCESS;
174
175 if (Operation == CcOperationZero)
176 {
177 /* Zero */
178 RtlZeroMemory(BaseAddress, Length);
179 }
180 else
181 {
182 _SEH2_TRY
183 {
184 if (Operation == CcOperationWrite)
185 RtlCopyMemory(BaseAddress, Buffer, Length);
186 else
187 RtlCopyMemory(Buffer, BaseAddress, Length);
188 }
189 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
190 {
191 Status = _SEH2_GetExceptionCode();
192 }
193 _SEH2_END;
194 }
195 return Status;
196 }
197
198 BOOLEAN
199 CcCopyData (
200 _In_ PFILE_OBJECT FileObject,
201 _In_ LONGLONG FileOffset,
202 _Inout_ PVOID Buffer,
203 _In_ LONGLONG Length,
204 _In_ CC_COPY_OPERATION Operation,
205 _In_ BOOLEAN Wait,
206 _Out_ PIO_STATUS_BLOCK IoStatus)
207 {
208 NTSTATUS Status;
209 LONGLONG CurrentOffset;
210 ULONG BytesCopied;
211 KIRQL OldIrql;
212 PROS_SHARED_CACHE_MAP SharedCacheMap;
213 PLIST_ENTRY ListEntry;
214 PROS_VACB Vacb;
215 ULONG PartialLength;
216 PVOID BaseAddress;
217 BOOLEAN Valid;
218
219 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
220 CurrentOffset = FileOffset;
221 BytesCopied = 0;
222
223 if (!Wait)
224 {
225 /* test if the requested data is available */
226 KeAcquireSpinLock(&SharedCacheMap->CacheMapLock, &OldIrql);
227 /* FIXME: this loop doesn't take into account areas that don't have
228 * a VACB in the list yet */
229 ListEntry = SharedCacheMap->CacheMapVacbListHead.Flink;
230 while (ListEntry != &SharedCacheMap->CacheMapVacbListHead)
231 {
232 Vacb = CONTAINING_RECORD(ListEntry,
233 ROS_VACB,
234 CacheMapVacbListEntry);
235 ListEntry = ListEntry->Flink;
236 if (!Vacb->Valid &&
237 DoRangesIntersect(Vacb->FileOffset.QuadPart,
238 VACB_MAPPING_GRANULARITY,
239 CurrentOffset, Length))
240 {
241 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
242 /* data not available */
243 return FALSE;
244 }
245 if (Vacb->FileOffset.QuadPart >= CurrentOffset + Length)
246 break;
247 }
248 KeReleaseSpinLock(&SharedCacheMap->CacheMapLock, OldIrql);
249 }
250
251 PartialLength = CurrentOffset % VACB_MAPPING_GRANULARITY;
252 if (PartialLength != 0)
253 {
254 PartialLength = min(Length, VACB_MAPPING_GRANULARITY - PartialLength);
255 Status = CcRosRequestVacb(SharedCacheMap,
256 ROUND_DOWN(CurrentOffset,
257 VACB_MAPPING_GRANULARITY),
258 &BaseAddress,
259 &Valid,
260 &Vacb);
261 if (!NT_SUCCESS(Status))
262 ExRaiseStatus(Status);
263 if (!Valid)
264 {
265 Status = CcReadVirtualAddress(Vacb);
266 if (!NT_SUCCESS(Status))
267 {
268 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
269 ExRaiseStatus(Status);
270 }
271 }
272 Status = ReadWriteOrZero((PUCHAR)BaseAddress + CurrentOffset % VACB_MAPPING_GRANULARITY,
273 Buffer,
274 PartialLength,
275 Operation);
276
277 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, Operation != CcOperationRead, FALSE);
278
279 if (!NT_SUCCESS(Status))
280 ExRaiseStatus(STATUS_INVALID_USER_BUFFER);
281
282 Length -= PartialLength;
283 CurrentOffset += PartialLength;
284 BytesCopied += PartialLength;
285
286 if (Buffer)
287 Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength);
288 }
289
290 while (Length > 0)
291 {
292 ASSERT(CurrentOffset % VACB_MAPPING_GRANULARITY == 0);
293 PartialLength = min(VACB_MAPPING_GRANULARITY, Length);
294 Status = CcRosRequestVacb(SharedCacheMap,
295 CurrentOffset,
296 &BaseAddress,
297 &Valid,
298 &Vacb);
299 if (!NT_SUCCESS(Status))
300 ExRaiseStatus(Status);
301 if (!Valid &&
302 (Operation == CcOperationRead ||
303 PartialLength < VACB_MAPPING_GRANULARITY))
304 {
305 Status = CcReadVirtualAddress(Vacb);
306 if (!NT_SUCCESS(Status))
307 {
308 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
309 ExRaiseStatus(Status);
310 }
311 }
312 Status = ReadWriteOrZero(BaseAddress, Buffer, PartialLength, Operation);
313
314 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, Operation != CcOperationRead, FALSE);
315
316 if (!NT_SUCCESS(Status))
317 ExRaiseStatus(STATUS_INVALID_USER_BUFFER);
318
319 Length -= PartialLength;
320 CurrentOffset += PartialLength;
321 BytesCopied += PartialLength;
322
323 if (Buffer)
324 Buffer = (PVOID)((ULONG_PTR)Buffer + PartialLength);
325 }
326 IoStatus->Status = STATUS_SUCCESS;
327 IoStatus->Information = BytesCopied;
328 return TRUE;
329 }
330
331 /*
332 * @unimplemented
333 */
334 BOOLEAN
335 NTAPI
336 CcCanIWrite (
337 IN PFILE_OBJECT FileObject,
338 IN ULONG BytesToWrite,
339 IN BOOLEAN Wait,
340 IN BOOLEAN Retrying)
341 {
342 UNIMPLEMENTED;
343 return FALSE;
344 }
345
346 /*
347 * @implemented
348 */
349 BOOLEAN
350 NTAPI
351 CcCopyRead (
352 IN PFILE_OBJECT FileObject,
353 IN PLARGE_INTEGER FileOffset,
354 IN ULONG Length,
355 IN BOOLEAN Wait,
356 OUT PVOID Buffer,
357 OUT PIO_STATUS_BLOCK IoStatus)
358 {
359 DPRINT("CcCopyRead(FileObject 0x%p, FileOffset %I64x, "
360 "Length %lu, Wait %u, Buffer 0x%p, IoStatus 0x%p)\n",
361 FileObject, FileOffset->QuadPart, Length, Wait,
362 Buffer, IoStatus);
363
364 return CcCopyData(FileObject,
365 FileOffset->QuadPart,
366 Buffer,
367 Length,
368 CcOperationRead,
369 Wait,
370 IoStatus);
371 }
372
373 /*
374 * @implemented
375 */
376 BOOLEAN
377 NTAPI
378 CcCopyWrite (
379 IN PFILE_OBJECT FileObject,
380 IN PLARGE_INTEGER FileOffset,
381 IN ULONG Length,
382 IN BOOLEAN Wait,
383 IN PVOID Buffer)
384 {
385 IO_STATUS_BLOCK IoStatus;
386
387 DPRINT("CcCopyWrite(FileObject 0x%p, FileOffset %I64x, "
388 "Length %lu, Wait %u, Buffer 0x%p)\n",
389 FileObject, FileOffset->QuadPart, Length, Wait, Buffer);
390
391 return CcCopyData(FileObject,
392 FileOffset->QuadPart,
393 Buffer,
394 Length,
395 CcOperationWrite,
396 Wait,
397 &IoStatus);
398 }
399
400 /*
401 * @unimplemented
402 */
403 VOID
404 NTAPI
405 CcDeferWrite (
406 IN PFILE_OBJECT FileObject,
407 IN PCC_POST_DEFERRED_WRITE PostRoutine,
408 IN PVOID Context1,
409 IN PVOID Context2,
410 IN ULONG BytesToWrite,
411 IN BOOLEAN Retrying)
412 {
413 UNIMPLEMENTED;
414 }
415
416 /*
417 * @unimplemented
418 */
419 VOID
420 NTAPI
421 CcFastCopyRead (
422 IN PFILE_OBJECT FileObject,
423 IN ULONG FileOffset,
424 IN ULONG Length,
425 IN ULONG PageCount,
426 OUT PVOID Buffer,
427 OUT PIO_STATUS_BLOCK IoStatus)
428 {
429 UNIMPLEMENTED;
430 }
431 /*
432 * @unimplemented
433 */
434 VOID
435 NTAPI
436 CcFastCopyWrite (
437 IN PFILE_OBJECT FileObject,
438 IN ULONG FileOffset,
439 IN ULONG Length,
440 IN PVOID Buffer)
441 {
442 UNIMPLEMENTED;
443 }
444
445 /*
446 * @unimplemented
447 */
448 NTSTATUS
449 NTAPI
450 CcWaitForCurrentLazyWriterActivity (
451 VOID)
452 {
453 UNIMPLEMENTED;
454 return STATUS_NOT_IMPLEMENTED;
455 }
456
457 /*
458 * @implemented
459 */
460 BOOLEAN
461 NTAPI
462 CcZeroData (
463 IN PFILE_OBJECT FileObject,
464 IN PLARGE_INTEGER StartOffset,
465 IN PLARGE_INTEGER EndOffset,
466 IN BOOLEAN Wait)
467 {
468 NTSTATUS Status;
469 LARGE_INTEGER WriteOffset;
470 LONGLONG Length;
471 ULONG CurrentLength;
472 PMDL Mdl;
473 ULONG i;
474 IO_STATUS_BLOCK Iosb;
475 KEVENT Event;
476
477 DPRINT("CcZeroData(FileObject 0x%p, StartOffset %I64x, EndOffset %I64x, "
478 "Wait %u)\n", FileObject, StartOffset->QuadPart, EndOffset->QuadPart,
479 Wait);
480
481 Length = EndOffset->QuadPart - StartOffset->QuadPart;
482 WriteOffset.QuadPart = StartOffset->QuadPart;
483
484 if (FileObject->SectionObjectPointer->SharedCacheMap == NULL)
485 {
486 /* File is not cached */
487
488 Mdl = _alloca(MmSizeOfMdl(NULL, MAX_ZERO_LENGTH));
489
490 while (Length > 0)
491 {
492 if (Length + WriteOffset.QuadPart % PAGE_SIZE > MAX_ZERO_LENGTH)
493 {
494 CurrentLength = MAX_ZERO_LENGTH - WriteOffset.QuadPart % PAGE_SIZE;
495 }
496 else
497 {
498 CurrentLength = Length;
499 }
500 MmInitializeMdl(Mdl, (PVOID)(ULONG_PTR)WriteOffset.QuadPart, CurrentLength);
501 Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
502 for (i = 0; i < ((Mdl->Size - sizeof(MDL)) / sizeof(ULONG)); i++)
503 {
504 ((PPFN_NUMBER)(Mdl + 1))[i] = CcZeroPage;
505 }
506 KeInitializeEvent(&Event, NotificationEvent, FALSE);
507 Status = IoSynchronousPageWrite(FileObject, Mdl, &WriteOffset, &Event, &Iosb);
508 if (Status == STATUS_PENDING)
509 {
510 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
511 Status = Iosb.Status;
512 }
513 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
514 {
515 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
516 }
517 if (!NT_SUCCESS(Status))
518 {
519 return FALSE;
520 }
521 WriteOffset.QuadPart += CurrentLength;
522 Length -= CurrentLength;
523 }
524 }
525 else
526 {
527 IO_STATUS_BLOCK IoStatus;
528
529 return CcCopyData(FileObject,
530 WriteOffset.QuadPart,
531 NULL,
532 Length,
533 CcOperationZero,
534 Wait,
535 &IoStatus);
536 }
537
538 return TRUE;
539 }