update for HEAD-2003050101
[reactos.git] / drivers / fs / vfat / rw.c
1
2 /* $Id$
3  *
4  * COPYRIGHT:        See COPYING in the top level directory
5  * PROJECT:          ReactOS kernel
6  * FILE:             services/fs/vfat/rw.c
7  * PURPOSE:          VFAT Filesystem
8  * PROGRAMMER:       Jason Filby (jasonfilby@yahoo.com)
9  *
10  */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <wchar.h>
16 #include <ntos/minmax.h>
17
18 #define NDEBUG
19 #include <debug.h>
20
21 #include "vfat.h"
22
23 /* FUNCTIONS *****************************************************************/
24
25 NTSTATUS
26 NextCluster(PDEVICE_EXTENSION DeviceExt,
27             PVFATFCB Fcb,
28             ULONG FirstCluster,
29             PULONG CurrentCluster,
30             BOOLEAN Extend)
31      /*
32       * Return the next cluster in a FAT chain, possibly extending the chain if
33       * necessary
34       */
35 {
36   if (Fcb != NULL && Fcb->Flags & FCB_IS_PAGE_FILE)
37     {
38       ULONG i;
39       PULONG FatChain;
40       NTSTATUS Status;
41       DPRINT("NextCluster(Fcb %x, FirstCluster %x, Extend %d)\n", Fcb, 
42              FirstCluster, Extend);
43       if (Fcb->FatChainSize == 0)
44         {
45           /* Paging file with zero length. */
46           *CurrentCluster = 0xffffffff;
47           if (Extend)
48             {
49               Fcb->FatChain = ExAllocatePool(NonPagedPool, sizeof(ULONG));
50               if (Fcb->FatChain == NULL)
51                 {
52                   return(STATUS_NO_MEMORY);
53                 }
54               Status = GetNextCluster(DeviceExt, 0, CurrentCluster, TRUE);
55               if (!NT_SUCCESS(Status))
56                 {
57                   ExFreePool(Fcb->FatChain);
58                   return(Status);
59                 }
60               Fcb->FatChain[0] = *CurrentCluster;
61               Fcb->FatChainSize = 1;
62               return Status;
63             }
64           else
65             {
66               return STATUS_UNSUCCESSFUL;
67             }
68         }
69       else
70         {
71           for (i = 0; i < Fcb->FatChainSize; i++)
72             {
73               if (Fcb->FatChain[i] == *CurrentCluster)
74                 break;
75             }
76           if (i >= Fcb->FatChainSize)
77             {
78               return STATUS_UNSUCCESSFUL;
79             }
80           if (i == Fcb->FatChainSize - 1)
81             {
82               if (Extend)
83                 {
84                   FatChain = ExAllocatePool(NonPagedPool, 
85                                             (i + 2) * sizeof(ULONG));
86                   if (!FatChain)
87                     {
88                       *CurrentCluster = 0xffffffff;
89                       return STATUS_NO_MEMORY;
90                     }
91                   Status = GetNextCluster(DeviceExt, *CurrentCluster, 
92                                           CurrentCluster, TRUE);
93                   if (NT_SUCCESS(Status) && *CurrentCluster != 0xffffffff)
94                     {
95                       memcpy(FatChain, Fcb->FatChain, (i + 1) * sizeof(ULONG));
96                       FatChain[i + 1] = *CurrentCluster;
97                       ExFreePool(Fcb->FatChain);
98                       Fcb->FatChain = FatChain;
99                       Fcb->FatChainSize = i + 2;
100                     }
101                   else
102                     {
103                       ExFreePool(FatChain);
104                     }
105                   return Status;
106                 }
107               else
108                 {
109                   *CurrentCluster = 0xffffffff;
110                   return STATUS_SUCCESS;
111                 }
112             }
113           *CurrentCluster = Fcb->FatChain[i + 1];
114           return STATUS_SUCCESS;
115         }
116     }
117   if (FirstCluster == 1)
118     {
119       (*CurrentCluster) += DeviceExt->FatInfo.SectorsPerCluster;
120       return(STATUS_SUCCESS);
121     }
122   else
123     {
124       /* 
125        * CN: FIXME: Real bug here or in dirwr, where CurrentCluster isn't 
126        * initialized when 0
127        */
128       if (FirstCluster == 0)
129         {
130           NTSTATUS Status;
131           
132           Status = GetNextCluster(DeviceExt, 0, CurrentCluster,
133                                   Extend);
134           return(Status);
135         }
136       else
137         {
138           NTSTATUS Status;
139           
140           Status = GetNextCluster(DeviceExt, (*CurrentCluster), CurrentCluster,
141                                   Extend);
142           return(Status);
143         }
144     }
145 }
146
147 NTSTATUS
148 OffsetToCluster(PDEVICE_EXTENSION DeviceExt,
149                 PVFATFCB Fcb,
150                 ULONG FirstCluster,
151                 ULONG FileOffset,
152                 PULONG Cluster,
153                 BOOLEAN Extend)
154      /*
155       * Return the cluster corresponding to an offset within a file,
156       * possibly extending the file if necessary
157       */
158 {
159   ULONG CurrentCluster;
160   ULONG i;
161   NTSTATUS Status;
162   DPRINT("OffsetToCluster(DeviceExt %x, Fcb %x, FirstCluster %x,"
163          " FileOffset %x, Cluster %x, Extend %d)\n", DeviceExt, 
164          Fcb, FirstCluster, FileOffset, Cluster, Extend);
165   if (FirstCluster == 0)
166   {
167     DbgPrint("OffsetToCluster is called with FirstCluster = 0!\n");
168     KeBugCheck(0);
169   }
170
171   if (Fcb != NULL && Fcb->Flags & FCB_IS_PAGE_FILE)
172     {
173       ULONG NCluster;
174       ULONG Offset = FileOffset / DeviceExt->FatInfo.BytesPerCluster;
175       PULONG FatChain;
176       int i;
177       if (Fcb->FatChainSize == 0)
178       {
179         DbgPrint("OffsetToCluster is called with FirstCluster = %x"
180                  " and Fcb->FatChainSize = 0!\n", FirstCluster);
181         KeBugCheck(0);
182       }
183       if (Offset < Fcb->FatChainSize)
184       {
185         *Cluster = Fcb->FatChain[Offset];
186         return STATUS_SUCCESS;
187       }
188       else
189       {
190         if (!Extend)
191         {
192           *Cluster = 0xffffffff;
193           return STATUS_UNSUCCESSFUL;
194         }
195         else
196         {
197           FatChain = ExAllocatePool(NonPagedPool, (Offset + 1) * sizeof(ULONG));
198           if (!FatChain)
199           {
200             *Cluster = 0xffffffff;
201             return STATUS_UNSUCCESSFUL;
202           }
203                   
204           CurrentCluster = Fcb->FatChain[Fcb->FatChainSize - 1];
205           FatChain[Fcb->FatChainSize - 1] = CurrentCluster;
206           for (i = Fcb->FatChainSize; i < Offset + 1; i++)
207           {
208             Status = GetNextCluster(DeviceExt, CurrentCluster, &CurrentCluster, TRUE);
209             if (!NT_SUCCESS(Status) || CurrentCluster == 0xFFFFFFFF)
210             {
211               while (i >= Fcb->FatChainSize)
212               {
213                 WriteCluster(DeviceExt, FatChain[i - 1], 0xFFFFFFFF);
214                 i--;
215               }
216               *Cluster = 0xffffffff;
217               ExFreePool(FatChain);
218               if (!NT_SUCCESS(Status))
219                  return Status;
220               return STATUS_UNSUCCESSFUL;
221             }
222             FatChain[i] = CurrentCluster;
223           }
224           memcpy (FatChain, Fcb->FatChain, Fcb->FatChainSize * sizeof(ULONG));
225           ExFreePool(Fcb->FatChain);
226           Fcb->FatChain = FatChain;
227           Fcb->FatChainSize = Offset + 1;
228         }
229       }
230       *Cluster = CurrentCluster;
231       return(STATUS_SUCCESS);
232     }
233   if (FirstCluster == 1)
234     {
235       /* root of FAT16 or FAT12 */
236       *Cluster = DeviceExt->FatInfo.rootStart + FileOffset
237         / (DeviceExt->FatInfo.BytesPerCluster) * DeviceExt->FatInfo.SectorsPerCluster;
238       return(STATUS_SUCCESS);
239     }
240   else
241     {
242       CurrentCluster = FirstCluster;
243       for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
244         {
245           Status = GetNextCluster (DeviceExt, CurrentCluster, &CurrentCluster,
246                                    Extend);
247           if (!NT_SUCCESS(Status))
248             {
249               return(Status);
250             }
251         }
252       *Cluster = CurrentCluster;
253       return(STATUS_SUCCESS);
254     }
255 }
256
257 NTSTATUS
258 VfatReadFileData (PVFAT_IRP_CONTEXT IrpContext, PVOID Buffer,
259                   ULONG Length, LARGE_INTEGER ReadOffset, PULONG LengthRead)
260 /*
261  * FUNCTION: Reads data from a file
262  */
263 {
264   ULONG CurrentCluster;
265   ULONG FirstCluster;
266   ULONG StartCluster;
267   ULONG ClusterCount;
268   LARGE_INTEGER StartOffset;
269   PDEVICE_EXTENSION DeviceExt;
270   BOOLEAN First = TRUE;
271   PVFATFCB Fcb;
272   PVFATCCB Ccb;
273   NTSTATUS Status;
274   ULONG BytesDone;
275   ULONG BytesPerSector;
276   ULONG BytesPerCluster;
277
278   /* PRECONDITION */
279   assert (IrpContext);
280   DeviceExt = IrpContext->DeviceExt;
281   assert (DeviceExt);
282   assert (DeviceExt->FatInfo.BytesPerCluster);
283   assert (IrpContext->FileObject);
284   assert (IrpContext->FileObject->FsContext2 != NULL);
285
286   DPRINT("VfatReadFileData(DeviceExt %x, FileObject %x, Buffer %x, "
287          "Length %d, ReadOffset 0x%I64x)\n", DeviceExt,
288          IrpContext->FileObject, Buffer, Length, ReadOffset.QuadPart);
289
290   *LengthRead = 0;
291
292   Ccb = (PVFATCCB)IrpContext->FileObject->FsContext2;
293   Fcb = IrpContext->FileObject->FsContext;
294   BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
295   BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
296
297   assert(ReadOffset.QuadPart + Length <= ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector));
298   assert(ReadOffset.u.LowPart % BytesPerSector == 0);
299   assert(Length % BytesPerSector == 0);
300
301   /* Is this a read of the FAT? */
302   if (Fcb->Flags & FCB_IS_FAT)
303   {
304     ReadOffset.QuadPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
305     Status = VfatReadDisk(DeviceExt->StorageDevice, &ReadOffset, Length, Buffer);
306
307     if (NT_SUCCESS(Status))
308     {
309       *LengthRead = Length;
310     }
311     else
312     {
313       DPRINT1("FAT reading failed, Status %x\n", Status);
314     }
315     return Status;
316   }
317   /* Is this a read of the Volume ? */
318   if (Fcb->Flags & FCB_IS_VOLUME)
319   {
320     Status = VfatReadDisk(DeviceExt->StorageDevice, &ReadOffset, Length, Buffer);
321     if (NT_SUCCESS(Status))
322     {
323       *LengthRead = Length;
324     }
325     else
326     {
327       DPRINT1("Volume reading failed, Status %x\n", Status);
328     }
329     return Status;
330   }
331
332   /*
333    * Find the first cluster
334    */
335   FirstCluster = CurrentCluster =
336     vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
337
338   if (FirstCluster == 1)
339   {
340     // Directory of FAT12/16 needs a special handling
341     CHECKPOINT;
342     if (ReadOffset.u.LowPart + Length > DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector)
343     {
344       Length = DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector - ReadOffset.u.LowPart;
345     }
346     ReadOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
347
348     // Fire up the read command
349     
350     Status = VfatReadDisk (DeviceExt->StorageDevice, &ReadOffset, Length, Buffer);
351     if (NT_SUCCESS(Status))
352     {
353       *LengthRead += Length;
354     }
355     return Status;
356   }
357   /*
358    * Find the cluster to start the read from
359    */
360   if (Ccb->LastCluster > 0 && ReadOffset.u.LowPart > Ccb->LastOffset)
361   {
362     CurrentCluster = Ccb->LastCluster;
363   }
364   Status = OffsetToCluster(DeviceExt, Fcb, FirstCluster,
365                            ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
366                            &CurrentCluster, FALSE);
367   if (!NT_SUCCESS(Status))
368   {
369     return(Status);
370   }
371
372   Ccb->LastCluster = CurrentCluster;
373   Ccb->LastOffset = ROUND_DOWN (ReadOffset.u.LowPart, BytesPerCluster);
374
375   while (Length > 0 && CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
376   {
377     StartCluster = CurrentCluster;
378     StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
379     BytesDone = 0;
380     ClusterCount = 0;
381
382     do
383     {
384       ClusterCount++;
385       if (First)
386       {
387         BytesDone =  min (Length, BytesPerCluster - (ReadOffset.u.LowPart % BytesPerCluster));
388         StartOffset.QuadPart += ReadOffset.u.LowPart % BytesPerCluster;
389         First = FALSE;
390       }
391       else
392       {
393         if (Length - BytesDone > BytesPerCluster)
394         {
395           BytesDone += BytesPerCluster;
396         }
397         else
398         {
399           BytesDone = Length;
400         }
401       }
402       Status = NextCluster(DeviceExt, Fcb, FirstCluster, &CurrentCluster, FALSE);
403     }
404     while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
405     DPRINT("start %08x, next %08x, count %d\n",
406            StartCluster, CurrentCluster, ClusterCount);
407
408     Ccb->LastCluster = StartCluster + (ClusterCount - 1);
409     Ccb->LastOffset = ReadOffset.u.LowPart + (ClusterCount - 1) * BytesPerCluster;
410
411     // Fire up the read command
412     Status = VfatReadDisk (DeviceExt->StorageDevice, &StartOffset, BytesDone, Buffer);
413
414     if (NT_SUCCESS(Status))
415     {
416       *LengthRead += BytesDone;
417       Buffer += BytesDone;
418       Length -= BytesDone;
419       ReadOffset.u.LowPart += BytesDone;
420     }
421   }
422   return Status;
423 }
424
425 NTSTATUS VfatWriteFileData(PVFAT_IRP_CONTEXT IrpContext,
426                            PVOID Buffer,
427                            ULONG Length,
428                            LARGE_INTEGER WriteOffset)
429 {
430    PDEVICE_EXTENSION DeviceExt;
431    PVFATFCB Fcb;
432    PVFATCCB Ccb;
433    ULONG Count;
434    ULONG FirstCluster;
435    ULONG CurrentCluster;
436    ULONG BytesDone;
437    ULONG StartCluster;
438    ULONG ClusterCount;
439    NTSTATUS Status;
440    BOOLEAN First = TRUE;
441    ULONG BytesPerSector;
442    ULONG BytesPerCluster;
443    LARGE_INTEGER StartOffset;
444
445    /* PRECONDITION */
446    assert (IrpContext);
447    DeviceExt = IrpContext->DeviceExt;
448    assert (DeviceExt);
449    assert (DeviceExt->FatInfo.BytesPerCluster);
450    assert (IrpContext->FileObject);
451    assert (IrpContext->FileObject->FsContext2 != NULL);
452
453    Ccb = (PVFATCCB)IrpContext->FileObject->FsContext2;
454    Fcb = IrpContext->FileObject->FsContext;
455    BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
456    BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
457
458    DPRINT("VfatWriteFileData(DeviceExt %x, FileObject %x, Buffer %x, "
459           "Length %d, WriteOffset 0x%I64x), '%S'\n", DeviceExt,
460           IrpContext->FileObject, Buffer, Length, WriteOffset,
461           Fcb->PathName);
462
463    assert(WriteOffset.QuadPart + Length <= Fcb->RFCB.AllocationSize.QuadPart);
464    assert(WriteOffset.u.LowPart % BytesPerSector == 0);
465    assert(Length % BytesPerSector == 0)
466
467    // Is this a write of the volume ?
468    if (Fcb->Flags & FCB_IS_VOLUME)
469    {
470       Status = VfatWriteDisk(DeviceExt->StorageDevice, &WriteOffset, Length, Buffer);
471       if (!NT_SUCCESS(Status))
472       {
473          DPRINT1("Volume writing failed, Status %x\n", Status);
474       }
475       return Status;
476    }
477
478    // Is this a write to the FAT ?
479    if (Fcb->Flags & FCB_IS_FAT)
480    {
481       WriteOffset.u.LowPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
482       for (Count = 0; Count < DeviceExt->FatInfo.FATCount; Count++)
483       {
484          Status = VfatWriteDisk(DeviceExt->StorageDevice, &WriteOffset, Length, Buffer);
485          if (!NT_SUCCESS(Status))
486          {
487             DPRINT1("FAT writing failed, Status %x\n", Status);
488          }
489          WriteOffset.u.LowPart += Fcb->RFCB.FileSize.u.LowPart;
490       }
491       return Status;
492    }
493
494    /*
495     * Find the first cluster
496     */
497    FirstCluster = CurrentCluster =
498       vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
499
500    if (FirstCluster == 1)
501    {
502       assert(WriteOffset.u.LowPart + Length <= DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector);
503       // Directory of FAT12/16 needs a special handling
504       WriteOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
505       // Fire up the write command
506       Status = VfatWriteDisk (DeviceExt->StorageDevice, &WriteOffset, Length, Buffer);
507       return Status;
508    }
509
510    /*
511     * Find the cluster to start the write from
512     */
513    if (Ccb->LastCluster > 0 && WriteOffset.u.LowPart > Ccb->LastOffset)
514    {
515       CurrentCluster = Ccb->LastCluster;
516    }
517
518    Status = OffsetToCluster(DeviceExt, Fcb, FirstCluster,
519                             ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
520                             &CurrentCluster, FALSE);
521
522    if (!NT_SUCCESS(Status))
523    {
524       return(Status);
525    }
526
527    Ccb->LastCluster = CurrentCluster;
528    Ccb->LastOffset = ROUND_DOWN (WriteOffset.u.LowPart, BytesPerCluster);
529
530    while (Length > 0 && CurrentCluster != 0xffffffff && NT_SUCCESS(Status))
531    {
532       StartCluster = CurrentCluster;
533       StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
534       BytesDone = 0;
535       ClusterCount = 0;
536
537       do
538       {
539          ClusterCount++;
540          if (First)
541          {
542             BytesDone =  min (Length, BytesPerCluster - (WriteOffset.u.LowPart % BytesPerCluster));
543             StartOffset.QuadPart += WriteOffset.u.LowPart % BytesPerCluster;
544             First = FALSE;
545          }
546          else
547          {
548             if (Length - BytesDone > BytesPerCluster)
549             {
550                BytesDone += BytesPerCluster;
551             }
552             else
553             {
554                BytesDone = Length;
555             }
556          }
557          Status = NextCluster(DeviceExt, Fcb, FirstCluster, &CurrentCluster, FALSE);
558       }
559       while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
560       DPRINT("start %08x, next %08x, count %d\n",
561              StartCluster, CurrentCluster, ClusterCount);
562
563       Ccb->LastCluster = StartCluster + (ClusterCount - 1);
564       Ccb->LastOffset = WriteOffset.u.LowPart + (ClusterCount - 1) * BytesPerCluster;
565
566       // Fire up the write command
567       Status = VfatWriteDisk (DeviceExt->StorageDevice, &StartOffset, BytesDone, Buffer);
568       if (NT_SUCCESS(Status))
569       {
570          Buffer += BytesDone;
571          Length -= BytesDone;
572          WriteOffset.u.LowPart += BytesDone;
573       }
574    }
575    return Status;
576 }
577
578 NTSTATUS
579 VfatRead(PVFAT_IRP_CONTEXT IrpContext)
580 {
581    NTSTATUS Status;
582    PVFATFCB Fcb;
583    ULONG Length;
584    ULONG ReturnedLength = 0;
585    PERESOURCE Resource = NULL;
586    LARGE_INTEGER ByteOffset;
587    PVOID Buffer;
588    PDEVICE_OBJECT DeviceToVerify;
589    ULONG BytesPerSector;
590
591    assert(IrpContext);
592
593    DPRINT("VfatRead(IrpContext %x)\n", IrpContext);
594
595    assert(IrpContext->DeviceObject);
596
597    // This request is not allowed on the main device object
598    if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
599    {
600       DPRINT("VfatRead is called with the main device object.\n");
601       Status = STATUS_INVALID_DEVICE_REQUEST;
602       goto ByeBye;
603    }
604
605    assert(IrpContext->DeviceExt);
606    assert(IrpContext->FileObject);
607    Fcb = IrpContext->FileObject->FsContext;
608    assert(Fcb);
609
610    DPRINT("<%S>\n", Fcb->PathName);
611
612    ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
613    Length = IrpContext->Stack->Parameters.Read.Length;
614    BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
615
616    /* fail if file is a directory and no paged read */
617    if (Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
618    {
619       Status = STATUS_INVALID_PARAMETER;
620       goto ByeBye;
621    }
622
623
624    DPRINT("'%S', Offset: %d, Length %d\n", Fcb->PathName, ByteOffset.u.LowPart, Length);
625
626    if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
627    {
628       Status = STATUS_INVALID_PARAMETER;
629       goto ByeBye;
630    }
631    if (ByteOffset.QuadPart >= Fcb->RFCB.FileSize.QuadPart)
632    {
633       IrpContext->Irp->IoStatus.Information = 0;
634       Status = STATUS_END_OF_FILE;
635       goto ByeBye;
636    }
637    if (IrpContext->Irp->Flags & (IRP_PAGING_IO | IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME))
638    {
639       if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
640       {
641          DPRINT("%d %d\n", ByteOffset.u.LowPart, Length);
642          // non chached read must be sector aligned
643          Status = STATUS_INVALID_PARAMETER;
644          goto ByeBye;
645       }
646    }
647    if (Length == 0)
648    {
649       IrpContext->Irp->IoStatus.Information = 0;
650       Status = STATUS_SUCCESS;
651       goto ByeBye;
652    }
653    
654    if (Fcb->Flags & FCB_IS_VOLUME)
655    {
656       Resource = &IrpContext->DeviceExt->DirResource;
657    }
658    else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
659    {
660       Resource = &Fcb->PagingIoResource;
661    }
662    else
663    {
664       Resource = &Fcb->MainResource;
665    }
666    if (!ExAcquireResourceSharedLite(Resource, IrpContext->Flags & IRPCONTEXT_CANWAIT))
667    {
668       Resource = NULL;
669       Status = STATUS_PENDING;
670       goto ByeBye;
671    }
672
673    if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
674       FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
675    {
676       if (!FsRtlCheckLockForReadAccess(&Fcb->FileLock, IrpContext->Irp)) 
677       {
678          Status = STATUS_FILE_LOCK_CONFLICT;
679          goto ByeBye;
680       }
681    }
682
683    if (!(IrpContext->Irp->Flags & (IRP_NOCACHE|IRP_PAGING_IO)) &&
684      !(Fcb->Flags & (FCB_IS_PAGE_FILE|FCB_IS_VOLUME)))
685    {
686       // cached read
687       CHECKPOINT;
688       Status = STATUS_SUCCESS;
689       if (ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
690       {
691          Length = Fcb->RFCB.FileSize.u.LowPart - ByteOffset.u.LowPart;
692          Status = /*STATUS_END_OF_FILE*/STATUS_SUCCESS;
693       }
694
695       Buffer = VfatGetUserBuffer(IrpContext->Irp);
696       if (!Buffer)
697       {
698          Status = STATUS_INVALID_USER_BUFFER;
699          goto ByeBye;
700       }
701
702       CHECKPOINT;
703       if (IrpContext->FileObject->PrivateCacheMap == NULL)
704       {
705           ULONG CacheSize;
706           CacheSize = IrpContext->DeviceExt->FatInfo.BytesPerCluster;
707           if (CacheSize < PAGE_SIZE)
708           {
709              CacheSize = PAGE_SIZE;
710           }
711           CcRosInitializeFileCache(IrpContext->FileObject, CacheSize);
712       }
713       if (!CcCopyRead(IrpContext->FileObject, &ByteOffset, Length,
714                       IrpContext->Flags & IRPCONTEXT_CANWAIT, Buffer,
715                       &IrpContext->Irp->IoStatus))
716       {
717          Status = STATUS_PENDING;
718          goto ByeBye;
719       }
720       CHECKPOINT;
721       if (!NT_SUCCESS(IrpContext->Irp->IoStatus.Status))
722       {
723         Status = IrpContext->Irp->IoStatus.Status;
724       }
725    }
726    else
727    {
728       // non cached read
729       CHECKPOINT;
730       if (ByteOffset.QuadPart + Length > ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector))
731       {
732          Length = ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector) - ByteOffset.QuadPart;
733       }
734
735       Buffer = VfatGetUserBuffer(IrpContext->Irp);
736       if (!Buffer)
737       {
738          Status = STATUS_INVALID_USER_BUFFER;
739          goto ByeBye;
740       }
741
742       Status = VfatReadFileData(IrpContext, Buffer, Length, ByteOffset, &ReturnedLength);
743 /*
744       if (Status == STATUS_VERIFY_REQUIRED)
745       {
746          DPRINT("VfatReadFile returned STATUS_VERIFY_REQUIRED\n");
747          DeviceToVerify = IoGetDeviceToVerify(KeGetCurrentThread());
748          IoSetDeviceToVerify(KeGetCurrentThread(), NULL);
749          Status = IoVerifyVolume (DeviceToVerify, FALSE);
750
751          if (NT_SUCCESS(Status))
752          {
753             Status = VfatReadFileData(IrpContext, Buffer, Length,
754                                       ByteOffset.u.LowPart, &ReturnedLength);
755          }
756       }
757 */
758       if (NT_SUCCESS(Status))
759       {
760          IrpContext->Irp->IoStatus.Information = ReturnedLength;
761       }
762    }
763
764 ByeBye:
765    if (Resource)
766    {
767       ExReleaseResourceLite(Resource);
768    }
769
770    if (Status == STATUS_PENDING)
771    {
772       Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
773       if (NT_SUCCESS(Status))
774       {
775          Status = VfatQueueRequest(IrpContext);
776       }
777       else
778       {
779          IrpContext->Irp->IoStatus.Status = Status;
780          IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
781          VfatFreeIrpContext(IrpContext);
782       }
783    }
784    else
785    {
786       IrpContext->Irp->IoStatus.Status = Status;
787       if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO &&
788           !(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
789           (NT_SUCCESS(Status) || Status==STATUS_END_OF_FILE))
790       {
791          IrpContext->FileObject->CurrentByteOffset.QuadPart =
792            ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
793       }
794
795       IoCompleteRequest(IrpContext->Irp,
796                         NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
797       VfatFreeIrpContext(IrpContext);
798    }
799    DPRINT("%x\n", Status);
800    return Status;
801 }
802
803 NTSTATUS VfatWrite (PVFAT_IRP_CONTEXT IrpContext)
804 {
805    PVFATFCB Fcb;
806    PERESOURCE Resource = NULL;
807    LARGE_INTEGER ByteOffset;
808    LARGE_INTEGER OldFileSize;
809    NTSTATUS Status = STATUS_SUCCESS;
810    ULONG Length;
811    ULONG OldAllocationSize;
812    PVOID Buffer;
813    ULONG BytesPerSector;
814
815    assert (IrpContext);
816
817    DPRINT("VfatWrite(IrpContext %x)\n", IrpContext);
818
819    assert(IrpContext->DeviceObject);
820
821    // This request is not allowed on the main device object
822    if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
823    {
824       DPRINT("VfatWrite is called with the main device object.\n");
825       Status = STATUS_INVALID_DEVICE_REQUEST;
826       goto ByeBye;
827    }
828
829    assert(IrpContext->DeviceExt);
830    assert(IrpContext->FileObject);
831    Fcb = IrpContext->FileObject->FsContext;
832    assert(Fcb);
833
834    DPRINT("<%S>\n", Fcb->PathName);
835
836    /* fail if file is a directory and no paged read */
837    if (Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
838    {
839       Status = STATUS_INVALID_PARAMETER;
840       goto ByeBye;
841    }
842
843    ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
844    Length = IrpContext->Stack->Parameters.Write.Length;
845    BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
846
847    if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
848    {
849       Status = STATUS_INVALID_PARAMETER;
850       goto ByeBye;
851    }
852
853    if (Fcb->Flags & (FCB_IS_FAT | FCB_IS_VOLUME) ||
854        1 == vfatDirEntryGetFirstCluster (IrpContext->DeviceExt, &Fcb->entry))
855    {
856       if (ByteOffset.QuadPart + Length > Fcb->RFCB.FileSize.QuadPart)
857       {
858          // we can't extend the FAT, the volume or the root on FAT12/FAT16
859          Status = STATUS_END_OF_FILE;
860          goto ByeBye;
861       }
862    }
863
864    if (IrpContext->Irp->Flags & (IRP_PAGING_IO|IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME))
865    {
866       if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
867       {
868          // non chached write must be sector aligned
869          Status = STATUS_INVALID_PARAMETER;
870          goto ByeBye;
871       }
872    }
873
874    if (Length == 0)
875    {
876       IrpContext->Irp->IoStatus.Information = 0;
877       Status = STATUS_SUCCESS;
878       goto ByeBye;
879    }
880
881    if (IrpContext->Irp->Flags & IRP_PAGING_IO)
882    {
883       if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
884       {
885          Status = STATUS_INVALID_PARAMETER;
886          goto ByeBye;
887       }
888       if (ByteOffset.u.LowPart + Length > ROUND_UP(Fcb->RFCB.AllocationSize.u.LowPart, BytesPerSector))
889       {
890          Length =  ROUND_UP(Fcb->RFCB.FileSize.u.LowPart, BytesPerSector) - ByteOffset.u.LowPart;
891       }
892    }
893
894    if (Fcb->Flags & FCB_IS_VOLUME)
895    {
896       Resource = &IrpContext->DeviceExt->DirResource;
897    }
898    else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
899    {
900       Resource = &Fcb->PagingIoResource;
901    }
902    else
903    {
904       Resource = &Fcb->MainResource;
905    }
906
907    if (Fcb->Flags & FCB_IS_PAGE_FILE)
908    {
909       if (!ExAcquireResourceSharedLite(Resource, IrpContext->Flags & IRPCONTEXT_CANWAIT))
910       {
911          Resource = NULL;
912          Status = STATUS_PENDING;
913          goto ByeBye;
914       }
915    }
916    else
917    {
918       if (!ExAcquireResourceExclusiveLite(Resource, IrpContext->Flags & IRPCONTEXT_CANWAIT))
919       {
920          Resource = NULL;
921          Status = STATUS_PENDING;
922          goto ByeBye;
923       }
924    }
925
926    if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
927       FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
928    {
929       if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp)) 
930       {
931          Status = STATUS_FILE_LOCK_CONFLICT;
932          goto ByeBye;
933        }
934     }
935
936    if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT) && !(Fcb->Flags & FCB_IS_VOLUME))
937    {
938       if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
939       {
940         Status = STATUS_PENDING;
941         goto ByeBye;
942       }
943    }
944
945    OldFileSize = Fcb->RFCB.FileSize;
946    OldAllocationSize = Fcb->RFCB.AllocationSize.u.LowPart;
947
948    if (!(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)) && 
949        !(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
950        ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
951    {
952       LARGE_INTEGER AllocationSize;
953       AllocationSize.QuadPart = ByteOffset.u.LowPart + Length;
954       Status = VfatSetAllocationSizeInformation(IrpContext->FileObject, Fcb,
955                                                 IrpContext->DeviceExt, &AllocationSize);
956       if (!NT_SUCCESS (Status))
957       {
958          CHECKPOINT;
959          goto ByeBye;
960       }
961    }
962
963
964    if (!(IrpContext->Irp->Flags & (IRP_NOCACHE|IRP_PAGING_IO)) &&
965       !(Fcb->Flags & (FCB_IS_PAGE_FILE|FCB_IS_VOLUME)))
966    {
967       // cached write
968       CHECKPOINT;
969
970       Buffer = VfatGetUserBuffer(IrpContext->Irp);
971       if (!Buffer)
972       {
973          Status = STATUS_INVALID_USER_BUFFER;
974          goto ByeBye;
975       }
976       CHECKPOINT;
977       if (IrpContext->FileObject->PrivateCacheMap == NULL)
978       {
979           ULONG CacheSize;
980           CacheSize = IrpContext->DeviceExt->FatInfo.BytesPerCluster;
981           if (CacheSize < PAGE_SIZE)
982           {
983              CacheSize = PAGE_SIZE;
984           }
985           CcRosInitializeFileCache(IrpContext->FileObject, CacheSize);
986       }
987       if (ByteOffset.QuadPart > OldFileSize.QuadPart)
988       {
989           CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
990       }
991       if (CcCopyWrite(IrpContext->FileObject, &ByteOffset, Length,
992                       1 /*IrpContext->Flags & IRPCONTEXT_CANWAIT*/, Buffer))
993       {
994          IrpContext->Irp->IoStatus.Information = Length;
995          Status = STATUS_SUCCESS;
996       }
997       else
998       {
999          Status = STATUS_UNSUCCESSFUL;
1000       }
1001       CHECKPOINT;
1002    }
1003    else
1004    {
1005       // non cached write
1006       CHECKPOINT;
1007
1008       if (ByteOffset.QuadPart > OldFileSize.QuadPart)
1009       {
1010          CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
1011       }
1012       Buffer = VfatGetUserBuffer(IrpContext->Irp);
1013       if (!Buffer)
1014       {
1015          Status = STATUS_INVALID_USER_BUFFER;
1016          goto ByeBye;
1017       }
1018
1019       Status = VfatWriteFileData(IrpContext, Buffer, Length, ByteOffset);
1020       if (NT_SUCCESS(Status))
1021       {
1022          IrpContext->Irp->IoStatus.Information = Length;
1023       }
1024    }
1025
1026    if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
1027       !(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)))
1028    {
1029       if(!(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
1030       {
1031          LARGE_INTEGER SystemTime, LocalTime;
1032          // set dates and times
1033          KeQuerySystemTime (&SystemTime);
1034          ExSystemTimeToLocalTime (&SystemTime, &LocalTime);
1035          FsdFileTimeToDosDateTime ((TIME*)&LocalTime, &Fcb->entry.UpdateDate,
1036                                    &Fcb->entry.UpdateTime);
1037          Fcb->entry.AccessDate = Fcb->entry.UpdateDate;
1038          // update dates/times and length
1039          VfatUpdateEntry (IrpContext->DeviceExt, IrpContext->FileObject);
1040       }
1041    }
1042
1043 ByeBye:
1044    if (Resource)
1045    {
1046       ExReleaseResourceLite(Resource);
1047    }
1048
1049    if (Status == STATUS_PENDING)
1050    {
1051       Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
1052       if (NT_SUCCESS(Status))
1053       {
1054          Status = VfatQueueRequest(IrpContext);
1055       }
1056       else
1057       {
1058          IrpContext->Irp->IoStatus.Status = Status;
1059          IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
1060          VfatFreeIrpContext(IrpContext);
1061       }
1062    }
1063    else
1064    {
1065       IrpContext->Irp->IoStatus.Status = Status;
1066       if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO &&
1067           !(IrpContext->Irp->Flags & IRP_PAGING_IO) && NT_SUCCESS(Status))
1068       {
1069          IrpContext->FileObject->CurrentByteOffset.QuadPart =
1070            ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
1071       }
1072
1073       IoCompleteRequest(IrpContext->Irp,
1074                         NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
1075       VfatFreeIrpContext(IrpContext);
1076    }
1077    DPRINT("%x\n", Status);
1078    return Status;
1079 }
1080
1081