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