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