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