- Disabled write caching for meta data (FAT, directories) in
[reactos.git] / reactos / ntoskrnl / cc / copy.c
1 /* $Id: copy.c,v 1.9 2002/08/17 15:14:26 hbirr Exp $
2 *
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
8 * UPDATE HISTORY:
9 * Created 05.10.2001
10 */
11
12 /* INCLUDES ******************************************************************/
13
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>
21
22 #define NDEBUG
23 #include <internal/debug.h>
24
25 /* GLOBALS *******************************************************************/
26
27 #define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
28
29 static PHYSICAL_ADDRESS CcZeroPage = (PHYSICAL_ADDRESS)0LL;
30
31 /* FUNCTIONS *****************************************************************/
32
33 VOID
34 CcInitCacheZeroPage(VOID)
35 {
36 NTSTATUS Status;
37
38 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &CcZeroPage);
39 if (!NT_SUCCESS(Status))
40 {
41 DbgPrint("Can't allocate CcZeroPage.\n");
42 KeBugCheck(0);
43 }
44 Status = MiZeroPage(CcZeroPage);
45 if (!NT_SUCCESS(Status))
46 {
47 DbgPrint("Can't zero out CcZeroPage.\n");
48 KeBugCheck(0);
49 }
50 }
51
52 NTSTATUS
53 ReadCacheSegmentChain(PBCB Bcb, ULONG ReadOffset, ULONG Length,
54 PVOID Buffer)
55 {
56 PCACHE_SEGMENT head;
57 PCACHE_SEGMENT current;
58 PCACHE_SEGMENT previous;
59 IO_STATUS_BLOCK Iosb;
60 LARGE_INTEGER SegOffset;
61 NTSTATUS Status;
62 ULONG TempLength;
63
64 Status = CcRosGetCacheSegmentChain(Bcb, ReadOffset, Length, &head);
65 if (!NT_SUCCESS(Status))
66 {
67 return(Status);
68 }
69 current = head;
70 while (current != NULL)
71 {
72 /*
73 * If the current segment is valid then copy it into the
74 * user buffer.
75 */
76 if (current->Valid)
77 {
78 TempLength = min(Bcb->CacheSegmentSize, Length);
79 memcpy(Buffer, current->BaseAddress, TempLength);
80 Buffer += TempLength;
81 Length = Length - TempLength;
82 previous = current;
83 current = current->NextInChain;
84 CcRosReleaseCacheSegment(Bcb, previous, TRUE, FALSE, FALSE);
85 }
86 /*
87 * Otherwise read in as much as we can.
88 */
89 else
90 {
91 PCACHE_SEGMENT current2;
92 ULONG current_size;
93 PMDL Mdl;
94 ULONG i;
95 ULONG offset;
96
97 /*
98 * Count the maximum number of bytes we could read starting
99 * from the current segment.
100 */
101 current2 = current;
102 current_size = 0;
103 while (current2 != NULL && !current2->Valid)
104 {
105 current2 = current2->NextInChain;
106 current_size += Bcb->CacheSegmentSize;
107 }
108
109 /*
110 * Create an MDL which contains all their pages.
111 */
112 Mdl = MmCreateMdl(NULL, NULL, current_size);
113 Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
114 current2 = current;
115 offset = 0;
116 while (current2 != NULL && !current2->Valid)
117 {
118 for (i = 0; i < (Bcb->CacheSegmentSize / PAGESIZE); i++)
119 {
120 PVOID address;
121 PHYSICAL_ADDRESS page;
122 address = current2->BaseAddress + (i * PAGESIZE);
123 page = MmGetPhysicalAddressForProcess(NULL, address);
124 ((PULONG)(Mdl + 1))[offset] = page.u.LowPart;
125 offset++;
126 }
127 current2 = current2->NextInChain;
128 }
129
130 /*
131 * Read in the information.
132 */
133 SegOffset.QuadPart = current->FileOffset;
134 Status = IoPageRead(Bcb->FileObject,
135 Mdl,
136 &SegOffset,
137 &Iosb,
138 TRUE);
139 if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
140 {
141 while (current != NULL)
142 {
143 previous = current;
144 current = current->NextInChain;
145 CcRosReleaseCacheSegment(Bcb, previous, FALSE, FALSE, FALSE);
146 }
147 return(Status);
148 }
149 while (current != NULL && !current->Valid)
150 {
151 previous = current;
152 current = current->NextInChain;
153 TempLength = min(Bcb->CacheSegmentSize, Length);
154 memcpy(Buffer, previous->BaseAddress, TempLength);
155 Buffer += TempLength;
156 Length = Length - TempLength;
157 CcRosReleaseCacheSegment(Bcb, previous, TRUE, FALSE, FALSE);
158 }
159 }
160 }
161 return(STATUS_SUCCESS);
162 }
163
164 NTSTATUS
165 ReadCacheSegment(PCACHE_SEGMENT CacheSeg)
166 {
167 ULONG Size;
168 PMDL Mdl;
169 NTSTATUS Status;
170 LARGE_INTEGER SegOffset;
171 IO_STATUS_BLOCK IoStatus;
172
173 SegOffset.QuadPart = CacheSeg->FileOffset;
174 Size = CacheSeg->Bcb->AllocationSize.QuadPart - CacheSeg->FileOffset;
175 if (Size > CacheSeg->Bcb->CacheSegmentSize)
176 {
177 Size = CacheSeg->Bcb->CacheSegmentSize;
178 }
179 Mdl = MmCreateMdl(NULL, CacheSeg->BaseAddress, Size);
180 MmBuildMdlForNonPagedPool(Mdl);
181 Status = IoPageRead(CacheSeg->Bcb->FileObject, Mdl, &SegOffset, &IoStatus,
182 TRUE);
183 if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
184 {
185 CcRosReleaseCacheSegment(CacheSeg->Bcb, CacheSeg, FALSE, FALSE, FALSE);
186 DPRINT1("IoPageRead failed, Status %x\n", Status);
187 return Status;
188 }
189 if (CacheSeg->Bcb->CacheSegmentSize > Size)
190 {
191 memset (CacheSeg->BaseAddress + Size, 0,
192 CacheSeg->Bcb->CacheSegmentSize - Size);
193 }
194 return STATUS_SUCCESS;
195 }
196
197 NTSTATUS
198 WriteCacheSegment(PCACHE_SEGMENT CacheSeg)
199 {
200 ULONG Size;
201 PMDL Mdl;
202 NTSTATUS Status;
203 IO_STATUS_BLOCK IoStatus;
204 LARGE_INTEGER SegOffset;
205
206 CacheSeg->Dirty = FALSE;
207 SegOffset.QuadPart = CacheSeg->FileOffset;
208 Size = CacheSeg->Bcb->AllocationSize.QuadPart - CacheSeg->FileOffset;
209 if (Size > CacheSeg->Bcb->CacheSegmentSize)
210 {
211 Size = CacheSeg->Bcb->CacheSegmentSize;
212 }
213 Mdl = MmCreateMdl(NULL, CacheSeg->BaseAddress, Size);
214 MmBuildMdlForNonPagedPool(Mdl);
215 Status = IoPageWrite(CacheSeg->Bcb->FileObject, Mdl, &SegOffset, &IoStatus,
216 TRUE);
217 if (!NT_SUCCESS(Status))
218 {
219 DPRINT1("IoPageWrite failed, Status %x\n", Status);
220 CacheSeg->Dirty = TRUE;
221 return(Status);
222 }
223 return(STATUS_SUCCESS);
224 }
225
226 BOOLEAN STDCALL
227 CcCopyRead (IN PFILE_OBJECT FileObject,
228 IN PLARGE_INTEGER FileOffset,
229 IN ULONG Length,
230 IN BOOLEAN Wait,
231 OUT PVOID Buffer,
232 OUT PIO_STATUS_BLOCK IoStatus)
233 {
234 ULONG ReadOffset;
235 ULONG TempLength;
236 NTSTATUS Status = STATUS_SUCCESS;
237 PVOID BaseAddress;
238 PCACHE_SEGMENT CacheSeg;
239 BOOLEAN Valid;
240 ULONG ReadLength = 0;
241 PBCB Bcb;
242 KIRQL oldirql;
243 PLIST_ENTRY current_entry;
244 PCACHE_SEGMENT current;
245
246 DPRINT("CcCopyRead(FileObject %x, FileOffset %x, "
247 "Length %d, Wait %d, Buffer %x, IoStatus %x)\n",
248 FileObject, (ULONG)FileOffset->QuadPart, Length, Wait,
249 Buffer, IoStatus);
250
251 Bcb = ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->Bcb;
252 ReadOffset = FileOffset->QuadPart;
253
254 DPRINT("AllocationSize %d, FileSize %d\n",
255 (ULONG)Bcb->AllocationSize.QuadPart,
256 (ULONG)Bcb->FileSize.QuadPart);
257
258 /*
259 * Check for the nowait case that all the cache segments that would
260 * cover this read are in memory.
261 */
262 if (!Wait)
263 {
264 KeAcquireSpinLock(&Bcb->BcbLock, &oldirql);
265 current_entry = Bcb->BcbSegmentListHead.Flink;
266 while (current_entry != &Bcb->BcbSegmentListHead)
267 {
268 current = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
269 BcbSegmentListEntry);
270 if (!current->Valid && current->FileOffset < ReadOffset + Length
271 && current->FileOffset + Bcb->CacheSegmentSize > ReadOffset)
272 {
273 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
274 IoStatus->Status = STATUS_UNSUCCESSFUL;
275 IoStatus->Information = 0;
276 return FALSE;
277 }
278 current_entry = current_entry->Flink;
279 }
280 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
281 }
282
283 TempLength = ReadOffset % Bcb->CacheSegmentSize;
284 if (TempLength != 0)
285 {
286 TempLength = min (Length, Bcb->CacheSegmentSize - TempLength);
287 Status = CcRosRequestCacheSegment(Bcb,
288 ROUND_DOWN(ReadOffset,
289 Bcb->CacheSegmentSize),
290 &BaseAddress, &Valid, &CacheSeg);
291 if (!NT_SUCCESS(Status))
292 {
293 IoStatus->Information = 0;
294 IoStatus->Status = Status;
295 DPRINT("CcRosRequestCacheSegment faild, Status %x\n", Status);
296 return FALSE;
297 }
298 if (!Valid)
299 {
300 Status = ReadCacheSegment(CacheSeg);
301 if (!NT_SUCCESS(Status))
302 {
303 IoStatus->Information = 0;
304 IoStatus->Status = Status;
305 return FALSE;
306 }
307 }
308 memcpy (Buffer, BaseAddress + ReadOffset % Bcb->CacheSegmentSize,
309 TempLength);
310 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
311 ReadLength += TempLength;
312 Length -= TempLength;
313 ReadOffset += TempLength;
314 Buffer += TempLength;
315 }
316 while (Length > 0)
317 {
318 TempLength = min(max(Bcb->CacheSegmentSize, 65536), Length);
319 ReadCacheSegmentChain(Bcb, ReadOffset, TempLength, Buffer);
320 ReadLength += TempLength;
321 Length -= TempLength;
322 ReadOffset += TempLength;
323 Buffer += TempLength;
324 }
325 IoStatus->Status = STATUS_SUCCESS;
326 IoStatus->Information = ReadLength;
327 DPRINT("CcCopyRead O.K.\n");
328 return TRUE;
329 }
330
331 BOOLEAN STDCALL
332 CcCopyWrite (IN PFILE_OBJECT FileObject,
333 IN PLARGE_INTEGER FileOffset,
334 IN ULONG Length,
335 IN BOOLEAN Wait,
336 IN PVOID Buffer)
337 {
338 NTSTATUS Status;
339 ULONG WriteOffset;
340 KIRQL oldirql;
341 PBCB Bcb;
342 PLIST_ENTRY current_entry;
343 PCACHE_SEGMENT CacheSeg;
344 ULONG TempLength;
345 PVOID BaseAddress;
346 BOOLEAN Valid;
347
348 DPRINT("CcCopyWrite(FileObject %x, FileOffset %x, "
349 "Length %d, Wait %d, Buffer %x)\n",
350 FileObject, (ULONG)FileOffset->QuadPart, Length, Wait, Buffer);
351
352 Bcb = ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->Bcb;
353 WriteOffset = (ULONG)FileOffset->QuadPart;
354
355 if (!Wait)
356 {
357 /* testing, if the requested datas are available */
358 KeAcquireSpinLock(&Bcb->BcbLock, &oldirql);
359 current_entry = Bcb->BcbSegmentListHead.Flink;
360 while (current_entry != &Bcb->BcbSegmentListHead)
361 {
362 CacheSeg = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
363 BcbSegmentListEntry);
364 if (!CacheSeg->Valid)
365 {
366 if ((WriteOffset >= CacheSeg->FileOffset &&
367 WriteOffset < CacheSeg->FileOffset + Bcb->CacheSegmentSize)
368 || (WriteOffset + Length > CacheSeg->FileOffset &&
369 WriteOffset + Length <= CacheSeg->FileOffset +
370 Bcb->CacheSegmentSize))
371 {
372 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
373 /* datas not available */
374 return(FALSE);
375 }
376 }
377 current_entry = current_entry->Flink;
378 }
379 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
380 }
381
382 TempLength = WriteOffset % Bcb->CacheSegmentSize;
383 if (TempLength != 0)
384 {
385 ULONG ROffset;
386 ROffset = ROUND_DOWN(WriteOffset, Bcb->CacheSegmentSize);
387 TempLength = min (Length, Bcb->CacheSegmentSize - TempLength);
388 Status = CcRosRequestCacheSegment(Bcb, ROffset,
389 &BaseAddress, &Valid, &CacheSeg);
390 if (!NT_SUCCESS(Status))
391 {
392 return(FALSE);
393 }
394 if (!Valid)
395 {
396 if (!NT_SUCCESS(ReadCacheSegment(CacheSeg)))
397 {
398 return(FALSE);
399 }
400 }
401 memcpy (BaseAddress + WriteOffset % Bcb->CacheSegmentSize,
402 Buffer, TempLength);
403 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, TRUE, FALSE);
404
405 Length -= TempLength;
406 WriteOffset += TempLength;
407 Buffer += TempLength;
408 }
409
410 while (Length > 0)
411 {
412 TempLength = min (Bcb->CacheSegmentSize, Length);
413 Status = CcRosRequestCacheSegment(Bcb, WriteOffset,
414 &BaseAddress, &Valid, &CacheSeg);
415 if (!NT_SUCCESS(Status))
416 {
417 return(FALSE);
418 }
419 if (!Valid && TempLength < Bcb->CacheSegmentSize)
420 {
421 if (!NT_SUCCESS(ReadCacheSegment(CacheSeg)))
422 {
423 return FALSE;
424 }
425 }
426 memcpy (BaseAddress, Buffer, TempLength);
427 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, TRUE, FALSE);
428 Length -= TempLength;
429 WriteOffset += TempLength;
430 Buffer += TempLength;
431 }
432 return(TRUE);
433 }
434
435 BOOLEAN STDCALL
436 CcZeroData (IN PFILE_OBJECT FileObject,
437 IN PLARGE_INTEGER StartOffset,
438 IN PLARGE_INTEGER EndOffset,
439 IN BOOLEAN Wait)
440 {
441 NTSTATUS Status;
442 LARGE_INTEGER WriteOffset;
443 ULONG Length;
444 PMDL Mdl;
445 ULONG i;
446 IO_STATUS_BLOCK Iosb;
447
448 DPRINT("CcZeroData(FileObject %x, StartOffset %I64x, EndOffset %I64x, "
449 "Wait %d\n", FileObject, StartOffset->QuadPart, EndOffset->QuadPart,
450 Wait);
451
452 Length = EndOffset->u.LowPart - StartOffset->u.LowPart;
453
454 /*
455 * FIXME: NT uses the shared cache map field for cached/non cached detection
456 */
457 if (FileObject->SectionObjectPointers->SharedCacheMap == NULL)
458 {
459 /* File is not cached */
460 WriteOffset.QuadPart = StartOffset->QuadPart;
461
462 while (Length > 0)
463 {
464 if (Length + WriteOffset.u.LowPart % PAGESIZE > 262144)
465 {
466 Mdl = MmCreateMdl(NULL, (PVOID)WriteOffset.u.LowPart,
467 262144 - WriteOffset.u.LowPart % PAGESIZE);
468 WriteOffset.QuadPart +=
469 (262144 - WriteOffset.u.LowPart % PAGESIZE);
470 Length -= (262144 - WriteOffset.u.LowPart % PAGESIZE);
471 }
472 else
473 {
474 Mdl =
475 MmCreateMdl(NULL, (PVOID)WriteOffset.u.LowPart,
476 Length - WriteOffset.u.LowPart % PAGESIZE);
477 WriteOffset.QuadPart +=
478 (Length - WriteOffset.u.LowPart % PAGESIZE);
479 Length = 0;
480 }
481 if (Mdl == NULL)
482 {
483 return(FALSE);
484 }
485 Mdl->MdlFlags |= (MDL_PAGES_LOCKED | MDL_IO_PAGE_READ);
486 for (i = 0; i < ((Mdl->Size - sizeof(MDL)) / sizeof(ULONG)); i++)
487 {
488 ((PULONG)(Mdl + 1))[i] = CcZeroPage.u.LowPart;
489 }
490 Status = IoPageWrite(FileObject, Mdl, StartOffset, &Iosb, TRUE);
491 if (!NT_SUCCESS(Status))
492 {
493 return(FALSE);
494 }
495 }
496 }
497 else
498 {
499 /* File is cached */
500 KIRQL oldirql;
501 PBCB Bcb;
502 PLIST_ENTRY current_entry;
503 PCACHE_SEGMENT CacheSeg, current, previous;
504 ULONG TempLength;
505 ULONG Start;
506 ULONG count;
507 ULONG size;
508 PHYSICAL_ADDRESS page;
509
510 Start = StartOffset->u.LowPart;
511 Bcb = ((REACTOS_COMMON_FCB_HEADER*)FileObject->FsContext)->Bcb;
512 if (Wait)
513 {
514 /* testing, if the requested datas are available */
515 KeAcquireSpinLock(&Bcb->BcbLock, &oldirql);
516 current_entry = Bcb->BcbSegmentListHead.Flink;
517 while (current_entry != &Bcb->BcbSegmentListHead)
518 {
519 CacheSeg = CONTAINING_RECORD(current_entry, CACHE_SEGMENT,
520 BcbSegmentListEntry);
521 if (!CacheSeg->Valid)
522 {
523 if ((Start >= CacheSeg->FileOffset &&
524 Start < CacheSeg->FileOffset + Bcb->CacheSegmentSize)
525 || (Start + Length > CacheSeg->FileOffset &&
526 Start + Length <=
527 CacheSeg->FileOffset + Bcb->CacheSegmentSize))
528 {
529 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
530 /* datas not available */
531 return(FALSE);
532 }
533 }
534 current_entry = current_entry->Flink;
535 }
536 KeReleaseSpinLock(&Bcb->BcbLock, oldirql);
537 }
538
539 while (Length > 0)
540 {
541 ULONG RStart = ROUND_DOWN(Start, Bcb->CacheSegmentSize);
542 WriteOffset.QuadPart = ROUND_DOWN(Start, Bcb->CacheSegmentSize);
543 if (Start % Bcb->CacheSegmentSize + Length > 262144)
544 {
545 Mdl = MmCreateMdl(NULL, NULL, 262144);
546 if (Mdl == NULL)
547 {
548 return FALSE;
549 }
550 Status = CcRosGetCacheSegmentChain (Bcb, RStart,
551 262144, &CacheSeg);
552 if (!NT_SUCCESS(Status))
553 {
554 ExFreePool(Mdl);
555 return(FALSE);
556 }
557 }
558 else
559 {
560 ULONG RLength;
561 Mdl = MmCreateMdl(NULL, (PVOID)RStart,
562 ROUND_UP(Start % Bcb->CacheSegmentSize +
563 Length, Bcb->CacheSegmentSize));
564 if (Mdl == NULL)
565 {
566 return(FALSE);
567 }
568 RLength = ROUND_UP(RStart + Length, Bcb->CacheSegmentSize);
569 RLength = min(RLength, Bcb->AllocationSize.u.LowPart);
570 RLength -= RStart;
571 Status = CcRosGetCacheSegmentChain (Bcb, RStart, RLength,
572 &CacheSeg);
573 if (!NT_SUCCESS(Status))
574 {
575 ExFreePool(Mdl);
576 return(FALSE);
577 }
578 }
579 Mdl->MdlFlags |= (MDL_PAGES_LOCKED|MDL_IO_PAGE_READ);
580 current = CacheSeg;
581 count = 0;
582 while (current != NULL)
583 {
584 if ((Start % Bcb->CacheSegmentSize) != 0 ||
585 Start % Bcb->CacheSegmentSize + Length <
586 Bcb->CacheSegmentSize)
587 {
588 if (!current->Valid)
589 {
590 /* Segment lesen */
591 Status = ReadCacheSegment(current);
592 if (!NT_SUCCESS(Status))
593 {
594 DPRINT1("ReadCacheSegment failed, status %x\n",
595 Status);
596 }
597 }
598 TempLength = min (Length, Bcb->CacheSegmentSize -
599 Start % Bcb->CacheSegmentSize);
600 memset (current->BaseAddress + Start % Bcb->CacheSegmentSize,
601 0, TempLength);
602 }
603 else
604 {
605 TempLength = Bcb->CacheSegmentSize;
606 memset (current->BaseAddress, 0, Bcb->CacheSegmentSize);
607 }
608 Start += TempLength;
609 Length -= TempLength;
610
611 size = ((Mdl->Size - sizeof(MDL)) / sizeof(ULONG));
612 for (i = 0; i < (Bcb->CacheSegmentSize / PAGESIZE) &&
613 count < size; i++)
614 {
615 PVOID Address;
616 Address = current->BaseAddress + (i * PAGESIZE);
617 page =
618 MmGetPhysicalAddressForProcess(NULL, Address);
619 ((PULONG)(Mdl + 1))[count++] = page.u.LowPart;
620 }
621 current = current->NextInChain;
622 }
623
624 /* Write the Segment */
625 Status = IoPageWrite(FileObject, Mdl, &WriteOffset, &Iosb, TRUE);
626 if (!NT_SUCCESS(Status))
627 {
628 DPRINT1("IoPageWrite failed, status %x\n", Status);
629 }
630 current = CacheSeg;
631 while (current != NULL)
632 {
633 previous = current;
634 current = current->NextInChain;
635 CcRosReleaseCacheSegment(Bcb, previous, TRUE, FALSE, FALSE);
636 }
637 }
638 }
639 return(TRUE);
640 }
641