:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[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 = Ccb->pFcb;
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 = Ccb->pFcb;
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    PVFATCCB Ccb;
584    ULONG Length;
585    ULONG ReturnedLength = 0;
586    PERESOURCE Resource = NULL;
587    LARGE_INTEGER ByteOffset;
588    PVOID Buffer;
589    PDEVICE_OBJECT DeviceToVerify;
590    ULONG BytesPerSector;
591
592    assert(IrpContext);
593
594    DPRINT("VfatRead(IrpContext %x)\n", IrpContext);
595
596    assert(IrpContext->DeviceObject);
597
598    // This request is not allowed on the main device object
599    if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
600    {
601       DPRINT("VfatRead is called with the main device object.\n");
602       Status = STATUS_INVALID_DEVICE_REQUEST;
603       goto ByeBye;
604    }
605
606    assert(IrpContext->DeviceExt);
607    assert(IrpContext->FileObject);
608    Ccb = (PVFATCCB) IrpContext->FileObject->FsContext2;
609    assert(Ccb);
610    Fcb =  Ccb->pFcb;
611    assert(Fcb);
612
613    DPRINT("<%S>\n", Fcb->PathName);
614
615    ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
616    Length = IrpContext->Stack->Parameters.Read.Length;
617    BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
618
619    /* fail if file is a directory and no paged read */
620    if (Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
621    {
622       Status = STATUS_INVALID_PARAMETER;
623       goto ByeBye;
624    }
625
626
627    DPRINT("'%S', Offset: %d, Length %d\n", Fcb->PathName, ByteOffset.u.LowPart, Length);
628
629    if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
630    {
631       Status = STATUS_INVALID_PARAMETER;
632       goto ByeBye;
633    }
634    if (ByteOffset.QuadPart >= Fcb->RFCB.FileSize.QuadPart)
635    {
636       IrpContext->Irp->IoStatus.Information = 0;
637       Status = STATUS_END_OF_FILE;
638       goto ByeBye;
639    }
640    if (IrpContext->Irp->Flags & (IRP_PAGING_IO | IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME))
641    {
642       if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
643       {
644          DPRINT("%d %d\n", ByteOffset.u.LowPart, Length);
645          // non chached read must be sector aligned
646          Status = STATUS_INVALID_PARAMETER;
647          goto ByeBye;
648       }
649    }
650    if (Length == 0)
651    {
652       IrpContext->Irp->IoStatus.Information = 0;
653       Status = STATUS_SUCCESS;
654       goto ByeBye;
655    }
656    
657    if (Fcb->Flags & FCB_IS_VOLUME)
658    {
659       Resource = &IrpContext->DeviceExt->DirResource;
660    }
661    else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
662    {
663       Resource = &Fcb->PagingIoResource;
664    }
665    else
666    {
667       Resource = &Fcb->MainResource;
668    }
669    if (!ExAcquireResourceSharedLite(Resource, IrpContext->Flags & IRPCONTEXT_CANWAIT))
670    {
671       Resource = NULL;
672       Status = STATUS_PENDING;
673       goto ByeBye;
674    }
675    if (!(IrpContext->Irp->Flags & (IRP_NOCACHE|IRP_PAGING_IO)) &&
676      !(Fcb->Flags & (FCB_IS_PAGE_FILE|FCB_IS_VOLUME)))
677    {
678       // cached read
679       CHECKPOINT;
680       Status = STATUS_SUCCESS;
681       if (ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
682       {
683          Length = Fcb->RFCB.FileSize.u.LowPart - ByteOffset.u.LowPart;
684          Status = /*STATUS_END_OF_FILE*/STATUS_SUCCESS;
685       }
686
687       Buffer = VfatGetUserBuffer(IrpContext->Irp);
688       if (!Buffer)
689       {
690          Status = STATUS_INVALID_USER_BUFFER;
691          goto ByeBye;
692       }
693
694       CHECKPOINT;
695       if (IrpContext->FileObject->PrivateCacheMap == NULL)
696       {
697           ULONG CacheSize;
698           CacheSize = IrpContext->DeviceExt->FatInfo.BytesPerCluster;
699           if (CacheSize < PAGE_SIZE)
700           {
701              CacheSize = PAGE_SIZE;
702           }
703           CcRosInitializeFileCache(IrpContext->FileObject, &Fcb->RFCB.Bcb, CacheSize);
704       }
705       if (!CcCopyRead(IrpContext->FileObject, &ByteOffset, Length,
706                       IrpContext->Flags & IRPCONTEXT_CANWAIT, Buffer,
707                       &IrpContext->Irp->IoStatus))
708       {
709          Status = STATUS_PENDING;
710          goto ByeBye;
711       }
712       CHECKPOINT;
713       if (!NT_SUCCESS(IrpContext->Irp->IoStatus.Status))
714       {
715         Status = IrpContext->Irp->IoStatus.Status;
716       }
717    }
718    else
719    {
720       // non cached read
721       CHECKPOINT;
722       if (ByteOffset.QuadPart + Length > ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector))
723       {
724          Length = ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector) - ByteOffset.QuadPart;
725       }
726
727       Buffer = VfatGetUserBuffer(IrpContext->Irp);
728       if (!Buffer)
729       {
730          Status = STATUS_INVALID_USER_BUFFER;
731          goto ByeBye;
732       }
733
734       Status = VfatReadFileData(IrpContext, Buffer, Length, ByteOffset, &ReturnedLength);
735 /*
736       if (Status == STATUS_VERIFY_REQUIRED)
737       {
738          DPRINT("VfatReadFile returned STATUS_VERIFY_REQUIRED\n");
739          DeviceToVerify = IoGetDeviceToVerify(KeGetCurrentThread());
740          IoSetDeviceToVerify(KeGetCurrentThread(), NULL);
741          Status = IoVerifyVolume (DeviceToVerify, FALSE);
742
743          if (NT_SUCCESS(Status))
744          {
745             Status = VfatReadFileData(IrpContext, Buffer, Length,
746                                       ByteOffset.u.LowPart, &ReturnedLength);
747          }
748       }
749 */
750       if (NT_SUCCESS(Status))
751       {
752          IrpContext->Irp->IoStatus.Information = ReturnedLength;
753       }
754    }
755
756 ByeBye:
757    if (Resource)
758    {
759       ExReleaseResourceLite(Resource);
760    }
761
762    if (Status == STATUS_PENDING)
763    {
764       Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
765       if (NT_SUCCESS(Status))
766       {
767          Status = VfatQueueRequest(IrpContext);
768       }
769       else
770       {
771          IrpContext->Irp->IoStatus.Status = Status;
772          IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
773          VfatFreeIrpContext(IrpContext);
774       }
775    }
776    else
777    {
778       IrpContext->Irp->IoStatus.Status = Status;
779       if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO &&
780           !(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
781           (NT_SUCCESS(Status) || Status==STATUS_END_OF_FILE))
782       {
783          IrpContext->FileObject->CurrentByteOffset.QuadPart =
784            ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
785       }
786
787       IoCompleteRequest(IrpContext->Irp,
788                         NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
789       VfatFreeIrpContext(IrpContext);
790    }
791    DPRINT("%x\n", Status);
792    return Status;
793 }
794
795 NTSTATUS VfatWrite (PVFAT_IRP_CONTEXT IrpContext)
796 {
797    PVFATCCB Ccb;
798    PVFATFCB Fcb;
799    PERESOURCE Resource = NULL;
800    LARGE_INTEGER ByteOffset;
801    LARGE_INTEGER OldFileSize;
802    NTSTATUS Status = STATUS_SUCCESS;
803    ULONG Length;
804    ULONG OldAllocationSize;
805    PVOID Buffer;
806    ULONG BytesPerSector;
807
808    assert (IrpContext);
809
810    DPRINT("VfatWrite(IrpContext %x)\n", IrpContext);
811
812    assert(IrpContext->DeviceObject);
813
814    // This request is not allowed on the main device object
815    if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
816    {
817       DPRINT("VfatWrite is called with the main device object.\n");
818       Status = STATUS_INVALID_DEVICE_REQUEST;
819       goto ByeBye;
820    }
821
822    assert(IrpContext->DeviceExt);
823    assert(IrpContext->FileObject);
824    Ccb = (PVFATCCB) IrpContext->FileObject->FsContext2;
825    assert(Ccb);
826    Fcb =  Ccb->pFcb;
827    assert(Fcb);
828
829    DPRINT("<%S>\n", Fcb->PathName);
830
831    /* fail if file is a directory and no paged read */
832    if (Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
833    {
834       Status = STATUS_INVALID_PARAMETER;
835       goto ByeBye;
836    }
837
838    ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
839    Length = IrpContext->Stack->Parameters.Write.Length;
840    BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
841
842    if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
843    {
844       Status = STATUS_INVALID_PARAMETER;
845       goto ByeBye;
846    }
847
848    if (Fcb->Flags & (FCB_IS_FAT | FCB_IS_VOLUME) ||
849        1 == vfatDirEntryGetFirstCluster (IrpContext->DeviceExt, &Fcb->entry))
850    {
851       if (ByteOffset.QuadPart + Length > Fcb->RFCB.FileSize.QuadPart)
852       {
853          // we can't extend the FAT, the volume or the root on FAT12/FAT16
854          Status = STATUS_END_OF_FILE;
855          goto ByeBye;
856       }
857    }
858
859    if (IrpContext->Irp->Flags & (IRP_PAGING_IO|IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME))
860    {
861       if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
862       {
863          // non chached write must be sector aligned
864          Status = STATUS_INVALID_PARAMETER;
865          goto ByeBye;
866       }
867    }
868
869    if (Length == 0)
870    {
871       IrpContext->Irp->IoStatus.Information = 0;
872       Status = STATUS_SUCCESS;
873       goto ByeBye;
874    }
875
876    if (IrpContext->Irp->Flags & IRP_PAGING_IO)
877    {
878       if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
879       {
880          Status = STATUS_INVALID_PARAMETER;
881          goto ByeBye;
882       }
883       if (ByteOffset.u.LowPart + Length > ROUND_UP(Fcb->RFCB.AllocationSize.u.LowPart, BytesPerSector))
884       {
885          Length =  ROUND_UP(Fcb->RFCB.FileSize.u.LowPart, BytesPerSector) - ByteOffset.u.LowPart;
886       }
887    }
888
889    if (Fcb->Flags & FCB_IS_VOLUME)
890    {
891       Resource = &IrpContext->DeviceExt->DirResource;
892    }
893    else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
894    {
895       Resource = &Fcb->PagingIoResource;
896    }
897    else
898    {
899       Resource = &Fcb->MainResource;
900    }
901
902    if (Fcb->Flags & FCB_IS_PAGE_FILE)
903    {
904       if (!ExAcquireResourceSharedLite(Resource, IrpContext->Flags & IRPCONTEXT_CANWAIT))
905       {
906          Resource = NULL;
907          Status = STATUS_PENDING;
908          goto ByeBye;
909       }
910    }
911    else
912    {
913       if (!ExAcquireResourceExclusiveLite(Resource, IrpContext->Flags & IRPCONTEXT_CANWAIT))
914       {
915          Resource = NULL;
916          Status = STATUS_PENDING;
917          goto ByeBye;
918       }
919    }
920
921    if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT) && !(Fcb->Flags & FCB_IS_VOLUME))
922    {
923       if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
924       {
925         Status = STATUS_PENDING;
926         goto ByeBye;
927       }
928    }
929
930    OldFileSize = Fcb->RFCB.FileSize;
931    OldAllocationSize = Fcb->RFCB.AllocationSize.u.LowPart;
932
933    if (!(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)) && 
934        !(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
935        ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
936    {
937       LARGE_INTEGER AllocationSize;
938       AllocationSize.QuadPart = ByteOffset.u.LowPart + Length;
939       Status = VfatSetAllocationSizeInformation(IrpContext->FileObject, Fcb,
940                                                 IrpContext->DeviceExt, &AllocationSize);
941       if (!NT_SUCCESS (Status))
942       {
943          CHECKPOINT;
944          goto ByeBye;
945       }
946    }
947
948    if (ByteOffset.QuadPart > OldFileSize.QuadPart)
949    {
950       CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
951    }
952
953    if (!(IrpContext->Irp->Flags & (IRP_NOCACHE|IRP_PAGING_IO)) &&
954       !(Fcb->Flags & (FCB_IS_PAGE_FILE|FCB_IS_VOLUME)))
955    {
956       // cached write
957       CHECKPOINT;
958
959       Buffer = VfatGetUserBuffer(IrpContext->Irp);
960       if (!Buffer)
961       {
962          Status = STATUS_INVALID_USER_BUFFER;
963          goto ByeBye;
964       }
965       CHECKPOINT;
966       if (IrpContext->FileObject->PrivateCacheMap == NULL)
967       {
968           ULONG CacheSize;
969           CacheSize = IrpContext->DeviceExt->FatInfo.BytesPerCluster;
970           if (CacheSize < PAGE_SIZE)
971           {
972              CacheSize = PAGE_SIZE;
973           }
974           CcRosInitializeFileCache(IrpContext->FileObject, &Fcb->RFCB.Bcb, CacheSize);
975       }
976       if (CcCopyWrite(IrpContext->FileObject, &ByteOffset, Length,
977                       1 /*IrpContext->Flags & IRPCONTEXT_CANWAIT*/, Buffer))
978       {
979          IrpContext->Irp->IoStatus.Information = Length;
980          Status = STATUS_SUCCESS;
981       }
982       else
983       {
984          Status = STATUS_UNSUCCESSFUL;
985       }
986       CHECKPOINT;
987    }
988    else
989    {
990       // non cached write
991       CHECKPOINT;
992
993       Buffer = VfatGetUserBuffer(IrpContext->Irp);
994       if (!Buffer)
995       {
996          Status = STATUS_INVALID_USER_BUFFER;
997          goto ByeBye;
998       }
999
1000       Status = VfatWriteFileData(IrpContext, Buffer, Length, ByteOffset);
1001       if (NT_SUCCESS(Status))
1002       {
1003          IrpContext->Irp->IoStatus.Information = Length;
1004       }
1005    }
1006
1007    if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
1008       !(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)))
1009    {
1010       if(!(Fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
1011       {
1012          LARGE_INTEGER SystemTime, LocalTime;
1013          // set dates and times
1014          KeQuerySystemTime (&SystemTime);
1015          ExSystemTimeToLocalTime (&SystemTime, &LocalTime);
1016          FsdFileTimeToDosDateTime ((TIME*)&LocalTime, &Fcb->entry.UpdateDate,
1017                                    &Fcb->entry.UpdateTime);
1018          Fcb->entry.AccessDate = Fcb->entry.UpdateDate;
1019          // update dates/times and length
1020          if (OldAllocationSize != Fcb->RFCB.AllocationSize.u.LowPart)
1021          {
1022             VfatUpdateEntry (IrpContext->DeviceExt, IrpContext->FileObject);
1023             Fcb->Flags &= ~FCB_UPDATE_DIRENTRY;
1024          }
1025          else
1026             Fcb->Flags |= FCB_UPDATE_DIRENTRY;
1027       }
1028    }
1029
1030 ByeBye:
1031    if (Resource)
1032    {
1033       ExReleaseResourceLite(Resource);
1034    }
1035
1036    if (Status == STATUS_PENDING)
1037    {
1038       Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
1039       if (NT_SUCCESS(Status))
1040       {
1041          Status = VfatQueueRequest(IrpContext);
1042       }
1043       else
1044       {
1045          IrpContext->Irp->IoStatus.Status = Status;
1046          IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
1047          VfatFreeIrpContext(IrpContext);
1048       }
1049    }
1050    else
1051    {
1052       IrpContext->Irp->IoStatus.Status = Status;
1053       if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO &&
1054           !(IrpContext->Irp->Flags & IRP_PAGING_IO) && NT_SUCCESS(Status))
1055       {
1056          IrpContext->FileObject->CurrentByteOffset.QuadPart =
1057            ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
1058       }
1059
1060       IoCompleteRequest(IrpContext->Irp,
1061                         NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
1062       VfatFreeIrpContext(IrpContext);
1063    }
1064    DPRINT("%x\n", Status);
1065    return Status;
1066 }
1067
1068