2 /* $Id: rw.c,v 1.28 2001/08/05 16:35:52 hbirr Exp $
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)
12 /* INCLUDES *****************************************************************/
14 #include <ddk/ntddk.h>
16 #include <ntos/minmax.h>
23 /* GLOBALS *******************************************************************/
25 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
26 #define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
28 /* FUNCTIONS *****************************************************************/
31 NextCluster(PDEVICE_EXTENSION DeviceExt
,
33 PULONG CurrentCluster
,
36 * Return the next cluster in a FAT chain, possibly extending the chain if
40 if (FirstCluster
== 1)
42 (*CurrentCluster
) += DeviceExt
->Boot
->SectorsPerCluster
;
43 return(STATUS_SUCCESS
);
46 /* CN: FIXME: Real bug here or in dirwr, where CurrentCluster isn't initialized when 0*/
47 if (FirstCluster
== 0)
51 Status
= GetNextCluster(DeviceExt
, 0, CurrentCluster
,
59 Status
= GetNextCluster(DeviceExt
, (*CurrentCluster
), CurrentCluster
,
66 OffsetToCluster(PDEVICE_EXTENSION DeviceExt
,
72 * Return the cluster corresponding to an offset within a file,
73 * possibly extending the file if necessary
80 if (FirstCluster
== 1)
82 /* root of FAT16 or FAT12 */
83 *Cluster
= DeviceExt
->rootStart
+ FileOffset
84 / (DeviceExt
->BytesPerCluster
) * DeviceExt
->Boot
->SectorsPerCluster
;
85 return(STATUS_SUCCESS
);
89 CurrentCluster
= FirstCluster
;
90 for (i
= 0; i
< FileOffset
/ DeviceExt
->BytesPerCluster
; i
++)
92 Status
= GetNextCluster (DeviceExt
, CurrentCluster
, &CurrentCluster
,
94 if (!NT_SUCCESS(Status
))
99 *Cluster
= CurrentCluster
;
100 return(STATUS_SUCCESS
);
105 VfatReadBigCluster(PDEVICE_EXTENSION DeviceExt
,
109 PULONG CurrentCluster
,
112 ULONG InternalOffset
,
113 ULONG InternalLength
)
116 PVOID BaseAddress
= NULL
;
117 PCACHE_SEGMENT CacheSeg
= NULL
;
121 * In this case the size of a cache segment is the same as a cluster
126 Status
= CcRosRequestCacheSegment(Fcb
->RFCB
.Bcb
,
131 if (!NT_SUCCESS(Status
))
139 if (InternalOffset
== 0)
141 BaseAddress
= Destination
;
145 BaseAddress
= ExAllocatePool(NonPagedPool
,
146 DeviceExt
->BytesPerCluster
);
147 if (BaseAddress
== NULL
)
149 return(STATUS_NO_MEMORY
);
157 * If necessary read the cluster from the disk
159 Status
= VfatRawReadCluster(DeviceExt
, FirstCluster
, BaseAddress
,
161 if (!NT_SUCCESS(Status
))
165 CcRosReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
167 else if (InternalOffset
!= 0)
169 ExFreePool(BaseAddress
);
175 * Copy the data from the cache to the caller
177 if (InternalOffset
!= 0 || !NoCache
)
179 memcpy(Destination
, BaseAddress
+ InternalOffset
, InternalLength
);
183 CcRosReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
185 else if (InternalOffset
!= 0)
187 ExFreePool(BaseAddress
);
190 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, FALSE
);
191 if (!NT_SUCCESS(Status
))
195 return(STATUS_SUCCESS
);
199 VfatReadSmallCluster(PDEVICE_EXTENSION DeviceExt
,
203 PULONG CurrentCluster
,
206 ULONG InternalOffset
,
207 ULONG InternalLength
)
210 PVOID BaseAddress
= NULL
;
211 PCACHE_SEGMENT CacheSeg
= NULL
;
216 * Otherwise we read a page of clusters together
220 Status
= CcRosRequestCacheSegment(Fcb
->RFCB
.Bcb
,
225 if (!NT_SUCCESS(Status
))
233 if (InternalOffset
== 0)
235 BaseAddress
= Destination
;
239 BaseAddress
= ExAllocatePool(NonPagedPool
, PAGESIZE
);
240 if (BaseAddress
== NULL
)
242 return(STATUS_NO_MEMORY
);
248 * If necessary read all the data for the page, unfortunately the
249 * file length may not be page aligned in which case the page will
250 * only be partially filled.
251 * FIXME: So zero fill the rest?
255 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
257 Status
= VfatRawReadCluster(DeviceExt
,
260 (i
* DeviceExt
->BytesPerCluster
),
262 if (!NT_SUCCESS(Status
))
266 CcRosReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
268 else if (InternalOffset
!= 0)
270 ExFreePool(BaseAddress
);
274 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, FALSE
);
275 if ((*CurrentCluster
) == 0xFFFFFFFF)
284 * Otherwise just move onto the next cluster
286 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
288 NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, FALSE
);
289 if ((*CurrentCluster
) == 0xFFFFFFFF)
296 * Copy the data from the cache to the caller
298 if (InternalOffset
!= 0 || !NoCache
)
300 memcpy(Destination
, BaseAddress
+ InternalOffset
, InternalLength
);
304 CcRosReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
306 else if (InternalOffset
!= 0)
308 ExFreePool(BaseAddress
);
310 return(STATUS_SUCCESS
);
314 VfatReadCluster(PDEVICE_EXTENSION DeviceExt
,
318 PULONG CurrentCluster
,
321 ULONG InternalOffset
,
322 ULONG InternalLength
)
324 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
326 return(VfatReadBigCluster(DeviceExt
,
338 return(VfatReadSmallCluster(DeviceExt
,
348 return(STATUS_SUCCESS
);
352 VfatReadFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
353 PVOID Buffer
, ULONG Length
, ULONG ReadOffset
,
354 PULONG LengthRead
, ULONG NoCache
)
356 * FUNCTION: Reads data from a file
359 ULONG CurrentCluster
;
367 assert (DeviceExt
!= NULL
);
368 assert (DeviceExt
->BytesPerCluster
!= 0);
369 assert (FileObject
!= NULL
);
370 assert (FileObject
->FsContext
!= NULL
);
372 DPRINT("VfatReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
373 "Length %d, ReadOffset 0x%x)\n", DeviceExt
, FileObject
, Buffer
,
378 Fcb
= ((PVFATCCB
)FileObject
->FsContext2
)->pFcb
;
381 * Find the first cluster
383 if (DeviceExt
->FatType
== FAT32
)
384 CurrentCluster
= Fcb
->entry
.FirstCluster
385 + Fcb
->entry
.FirstClusterHigh
* 65536;
387 CurrentCluster
= Fcb
->entry
.FirstCluster
;
388 FirstCluster
= CurrentCluster
;
389 DPRINT( "FirstCluster = %x\n", FirstCluster
);
391 * Truncate the read if necessary
393 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
395 if (ReadOffset
>= Fcb
->entry
.FileSize
)
397 return (STATUS_END_OF_FILE
);
399 if ((ReadOffset
+ Length
) > Fcb
->entry
.FileSize
)
401 Length
= Fcb
->entry
.FileSize
- ReadOffset
;
406 * Select an appropiate size for reads
408 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
410 ChunkSize
= DeviceExt
->BytesPerCluster
;
414 ChunkSize
= PAGESIZE
;
418 * Find the cluster to start the read from
419 * FIXME: Optimize by remembering the last cluster read and using if
422 Status
= OffsetToCluster(DeviceExt
,
424 ROUND_DOWN(ReadOffset
, ChunkSize
),
427 if (!NT_SUCCESS(Status
))
433 * If the read doesn't begin on a chunk boundary then we need special
436 if ((ReadOffset
% ChunkSize
) != 0 )
438 TempLength
= min (Length
, ChunkSize
- (ReadOffset
% ChunkSize
));
439 VfatReadCluster(DeviceExt
, Fcb
, ROUND_DOWN(ReadOffset
, ChunkSize
),
440 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
,
441 ReadOffset
% ChunkSize
, TempLength
);
443 (*LengthRead
) = (*LengthRead
) + TempLength
;
444 Length
= Length
- TempLength
;
445 Buffer
= Buffer
+ TempLength
;
446 ReadOffset
= ReadOffset
+ TempLength
;
449 while (Length
>= ChunkSize
&& CurrentCluster
)
451 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
452 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
, 0,
455 (*LengthRead
) = (*LengthRead
) + ChunkSize
;
456 Buffer
= Buffer
+ ChunkSize
;
457 Length
= Length
- ChunkSize
;
458 ReadOffset
= ReadOffset
+ ChunkSize
;
460 if (Length
> 0 && CurrentCluster
)
462 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
463 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
, 0,
465 (*LengthRead
) = (*LengthRead
) + Length
;
467 return (STATUS_SUCCESS
);
471 VfatWriteBigCluster(PDEVICE_EXTENSION DeviceExt
,
475 PULONG CurrentCluster
,
478 ULONG InternalOffset
,
479 ULONG InternalLength
,
484 PCACHE_SEGMENT CacheSeg
;
488 * In this case the size of a cache segment is the same as a cluster
492 Status
= CcRosRequestCacheSegment(Fcb
->RFCB
.Bcb
,
497 if (!NT_SUCCESS(Status
))
506 * If we are bypassing the cache and not writing starting on
507 * cluster boundary then allocate a temporary buffer
509 if (InternalOffset
!= 0)
511 BaseAddress
= ExAllocatePool(NonPagedPool
,
512 DeviceExt
->BytesPerCluster
);
513 if (BaseAddress
== NULL
)
515 return(STATUS_NO_MEMORY
);
519 if (!Valid
&& InternalLength
!= DeviceExt
->BytesPerCluster
)
522 * If the data in the cache isn't valid or we are bypassing the
523 * cache and not writing a cluster aligned, cluster sized region
524 * then read data in to base address
526 Status
= VfatRawReadCluster(DeviceExt
, FirstCluster
, BaseAddress
,
528 if (!NT_SUCCESS(Status
))
532 CcRosReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
534 else if (InternalOffset
!= 0)
536 ExFreePool(BaseAddress
);
541 if (!NoCache
|| InternalLength
!= DeviceExt
->BytesPerCluster
)
544 * If we are writing into the cache or we are writing from a
545 * temporary buffer then copy the data over
547 DPRINT("InternalOffset 0x%x InternalLength 0x%x BA %x\n",
548 InternalOffset
, InternalLength
, BaseAddress
);
549 memcpy(BaseAddress
+ InternalOffset
, Source
, InternalLength
);
552 * Write the data back to disk
554 DPRINT("Writing 0x%x\n", *CurrentCluster
);
555 Status
= VfatRawWriteCluster(DeviceExt
, FirstCluster
, BaseAddress
,
559 CcRosReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
561 else if (InternalOffset
!= 0)
563 ExFreePool(BaseAddress
);
566 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, Extend
);
567 if (!NT_SUCCESS(Status
))
571 return(STATUS_SUCCESS
);
575 VfatWriteSmallCluster(PDEVICE_EXTENSION DeviceExt
,
579 PULONG CurrentCluster
,
582 ULONG InternalOffset
,
583 ULONG InternalLength
,
588 PCACHE_SEGMENT CacheSeg
;
594 * Otherwise we read a page of clusters together
599 Status
= CcRosRequestCacheSegment(Fcb
->RFCB
.Bcb
,
604 if (!NT_SUCCESS(Status
))
612 if (InternalOffset
!= 0)
614 BaseAddress
= ExAllocatePool(NonPagedPool
, PAGESIZE
);
615 if (BaseAddress
== NULL
)
617 return(STATUS_NO_MEMORY
);
622 BaseAddress
= Source
;
627 * If necessary read all the data for the page, unfortunately the
628 * file length may not be page aligned in which case the page will
629 * only be partially filled.
630 * FIXME: So zero fill the rest?
632 if (!Valid
|| InternalLength
!= PAGESIZE
)
634 NCluster
= *CurrentCluster
;
636 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
638 Status
= VfatRawReadCluster(DeviceExt
,
641 (i
* DeviceExt
->BytesPerCluster
),
643 if (!NT_SUCCESS(Status
))
647 CcRosReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
649 else if (InternalOffset
!= 0)
651 ExFreePool(BaseAddress
);
655 Status
= NextCluster(DeviceExt
, FirstCluster
, &NCluster
, Extend
);
656 if (NCluster
== 0xFFFFFFFF)
665 * Otherwise just move onto the next cluster
667 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
669 NextCluster(DeviceExt
, FirstCluster
, &NCluster
, Extend
);
670 if (NCluster
== 0xFFFFFFFF)
677 if (!NoCache
|| InternalLength
!= PAGESIZE
)
680 * Copy the caller's data if we are using the cache or writing
681 * from temporary buffer
683 memcpy(BaseAddress
+ InternalOffset
, Source
, InternalLength
);
687 * Write the data to the disk
689 NCluster
= *CurrentCluster
;
691 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
693 Status
= VfatRawWriteCluster(DeviceExt
,
696 (i
* DeviceExt
->BytesPerCluster
),
698 if (!NT_SUCCESS(Status
))
702 CcRosReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
704 else if (InternalOffset
!= 0)
706 ExFreePool(BaseAddress
);
710 Status
= NextCluster(DeviceExt
, FirstCluster
, &NCluster
, Extend
);
711 if (NCluster
== 0xFFFFFFFF)
716 *CurrentCluster
= NCluster
;
720 CcRosReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
722 else if (InternalOffset
!= 0)
724 ExFreePool(BaseAddress
);
726 return(STATUS_SUCCESS
);
730 VfatWriteCluster(PDEVICE_EXTENSION DeviceExt
,
734 PULONG CurrentCluster
,
737 ULONG InternalOffset
,
738 ULONG InternalLength
,
743 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
745 return(VfatWriteBigCluster(DeviceExt
,
758 return(VfatWriteSmallCluster(DeviceExt
,
772 VfatWriteFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
773 PVOID Buffer
, ULONG Length
, ULONG WriteOffset
,
776 * FUNCTION: Writes data to file
779 ULONG CurrentCluster
;
784 LARGE_INTEGER SystemTime
, LocalTime
;
789 DPRINT ("VfatWriteFile(FileObject %x, Buffer %x, Length %x, "
790 "WriteOffset %x\n", FileObject
, Buffer
, Length
, WriteOffset
);
792 /* Locate the first cluster of the file */
794 pCcb
= (PVFATCCB
) (FileObject
->FsContext2
);
799 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
801 ChunkSize
= DeviceExt
->BytesPerCluster
;
805 ChunkSize
= PAGESIZE
;
808 if (DeviceExt
->FatType
== FAT32
)
811 Fcb
->entry
.FirstCluster
+ Fcb
->entry
.FirstClusterHigh
* 65536;
815 CurrentCluster
= Fcb
->entry
.FirstCluster
;
817 FirstCluster
= CurrentCluster
;
819 /* Find the cluster according to the offset in the file */
820 if (CurrentCluster
== 0)
825 Status
= NextCluster (DeviceExt
, FirstCluster
, &CurrentCluster
,
827 if (DeviceExt
->FatType
== FAT32
)
829 Fcb
->entry
.FirstClusterHigh
= CurrentCluster
>> 16;
830 Fcb
->entry
.FirstCluster
= CurrentCluster
;
834 Fcb
->entry
.FirstCluster
= CurrentCluster
;
836 FirstCluster
= CurrentCluster
;
838 Status
= OffsetToCluster(DeviceExt
, FirstCluster
, WriteOffset
,
839 &CurrentCluster
, TRUE
);
841 if (WriteOffset
+ Length
> Fcb
->entry
.FileSize
&&
842 !(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
844 Fcb
->entry
.FileSize
= WriteOffset
+ Length
;
848 * If the offset in the cluster doesn't fall on the cluster boundary
849 * then we have to write only from the specified offset
852 if ((WriteOffset
% ChunkSize
) != 0)
854 TempLength
= min (Length
, ChunkSize
- (WriteOffset
% ChunkSize
));
855 if ((Length
- TempLength
) > 0)
863 Status
= VfatWriteCluster(DeviceExt
,
865 ROUND_DOWN(WriteOffset
, ChunkSize
),
870 WriteOffset
% ChunkSize
,
873 Buffer
= Buffer
+ TempLength
;
874 Length
= Length
- TempLength
;
875 WriteOffset
= WriteOffset
+ TempLength
;
878 while (Length
>= ChunkSize
)
880 if ((Length
- ChunkSize
) > 0)
888 Status
= VfatWriteCluster(DeviceExt
,
890 ROUND_DOWN(WriteOffset
, ChunkSize
),
898 Buffer
= Buffer
+ ChunkSize
;
899 Length
= Length
- ChunkSize
;
900 WriteOffset
= WriteOffset
+ ChunkSize
;
903 /* Write the remainder */
906 Status
= VfatWriteCluster(DeviceExt
,
908 ROUND_DOWN(WriteOffset
, ChunkSize
),
919 /* set dates and times */
920 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
922 KeQuerySystemTime (&SystemTime
);
923 ExSystemTimeToLocalTime (&SystemTime
, &LocalTime
);
924 FsdFileTimeToDosDateTime ((TIME
*)&LocalTime
,
925 &Fcb
->entry
.UpdateDate
,
926 &Fcb
->entry
.UpdateTime
);
927 Fcb
->entry
.AccessDate
= Fcb
->entry
.UpdateDate
;
928 updEntry (DeviceExt
, FileObject
);
931 return (STATUS_SUCCESS
);
935 VfatWrite (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
937 * FUNCTION: Write to a file
943 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation (Irp
);
944 PFILE_OBJECT FileObject
= Stack
->FileObject
;
945 PDEVICE_EXTENSION DeviceExt
= DeviceObject
->DeviceExtension
;
949 DPRINT ("VfatWrite(DeviceObject %x Irp %x)\n", DeviceObject
, Irp
);
951 Length
= Stack
->Parameters
.Write
.Length
;
952 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
953 Offset
= Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
;
955 if (Irp
->Flags
& IRP_PAGING_IO
||
956 FileObject
->Flags
& FO_NO_INTERMEDIATE_BUFFERING
)
965 Status
= VfatWriteFile (DeviceExt
, FileObject
, Buffer
, Length
, Offset
,
968 Irp
->IoStatus
.Status
= Status
;
969 Irp
->IoStatus
.Information
= Length
;
970 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
976 VfatRead (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
978 * FUNCTION: Read from a file
984 PIO_STACK_LOCATION Stack
;
985 PFILE_OBJECT FileObject
;
986 PDEVICE_EXTENSION DeviceExt
;
992 DPRINT ("VfatRead(DeviceObject %x, Irp %x)\n", DeviceObject
, Irp
);
994 /* Precondition / Initialization */
995 assert (Irp
!= NULL
);
996 Stack
= IoGetCurrentIrpStackLocation (Irp
);
997 assert (Stack
!= NULL
);
998 FileObject
= Stack
->FileObject
;
999 assert (FileObject
!= NULL
);
1000 DeviceExt
= DeviceObject
->DeviceExtension
;
1001 assert (DeviceExt
!= NULL
);
1003 Length
= Stack
->Parameters
.Read
.Length
;
1004 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
1005 Offset
= Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
;
1007 if (Irp
->Flags
& IRP_PAGING_IO
||
1008 FileObject
->Flags
& FO_NO_INTERMEDIATE_BUFFERING
)
1017 /* fail if file is a directory */
1018 Fcb
= ((PVFATCCB
) (FileObject
->FsContext2
))->pFcb
;
1019 if (Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
1021 Status
= STATUS_FILE_IS_A_DIRECTORY
;
1025 Status
= VfatReadFile (DeviceExt
,
1026 FileObject
, Buffer
, Length
, Offset
, &LengthRead
,
1030 Irp
->IoStatus
.Status
= Status
;
1031 Irp
->IoStatus
.Information
= LengthRead
;
1032 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);