2 /* $Id: rw.c,v 1.19 2001/01/16 09:55:02 dwelch 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
);
49 Status
= GetNextCluster(DeviceExt
, (*CurrentCluster
), CurrentCluster
,
56 OffsetToCluster(PDEVICE_EXTENSION DeviceExt
,
62 * Return the cluster corresponding to an offset within a file,
63 * possibly extending the file if necessary
70 if (FirstCluster
== 1)
72 /* root of FAT16 or FAT12 */
73 *Cluster
= DeviceExt
->rootStart
+ FileOffset
74 / (DeviceExt
->BytesPerCluster
) * DeviceExt
->Boot
->SectorsPerCluster
;
75 return(STATUS_SUCCESS
);
79 CurrentCluster
= FirstCluster
;
80 for (i
= 0; i
< FileOffset
/ DeviceExt
->BytesPerCluster
; i
++)
82 Status
= GetNextCluster (DeviceExt
, CurrentCluster
, &CurrentCluster
,
84 if (!NT_SUCCESS(Status
))
89 *Cluster
= CurrentCluster
;
90 return(STATUS_SUCCESS
);
95 VfatReadBigCluster(PDEVICE_EXTENSION DeviceExt
,
99 PULONG CurrentCluster
,
102 ULONG InternalOffset
,
103 ULONG InternalLength
)
106 PVOID BaseAddress
= NULL
;
107 PCACHE_SEGMENT CacheSeg
= NULL
;
111 * In this case the size of a cache segment is the same as a cluster
116 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
121 if (!NT_SUCCESS(Status
))
129 if (InternalOffset
== 0)
131 BaseAddress
= Destination
;
135 BaseAddress
= ExAllocatePool(NonPagedPool
,
136 DeviceExt
->BytesPerCluster
);
137 if (BaseAddress
== NULL
)
139 return(STATUS_NO_MEMORY
);
147 * If necessary read the cluster from the disk
149 Status
= VfatRawReadCluster(DeviceExt
, FirstCluster
, BaseAddress
,
151 if (!NT_SUCCESS(Status
))
155 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
157 else if (InternalOffset
!= 0)
159 ExFreePool(BaseAddress
);
165 * Copy the data from the cache to the caller
167 memcpy(Destination
, BaseAddress
+ InternalOffset
, InternalLength
);
170 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
172 else if (InternalOffset
!= 0)
174 ExFreePool(BaseAddress
);
177 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, FALSE
);
178 if (!NT_SUCCESS(Status
))
182 return(STATUS_SUCCESS
);
186 VfatReadSmallCluster(PDEVICE_EXTENSION DeviceExt
,
190 PULONG CurrentCluster
,
193 ULONG InternalOffset
,
194 ULONG InternalLength
)
197 PVOID BaseAddress
= NULL
;
198 PCACHE_SEGMENT CacheSeg
= NULL
;
203 * Otherwise we read a page of clusters together
207 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
212 if (!NT_SUCCESS(Status
))
220 if (InternalOffset
== 0)
222 BaseAddress
= Destination
;
226 BaseAddress
= ExAllocatePool(NonPagedPool
, PAGESIZE
);
227 if (BaseAddress
== NULL
)
229 return(STATUS_NO_MEMORY
);
235 * If necessary read all the data for the page, unfortunately the
236 * file length may not be page aligned in which case the page will
237 * only be partially filled.
238 * FIXME: So zero fill the rest?
242 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
244 Status
= VfatRawReadCluster(DeviceExt
,
247 (i
* DeviceExt
->BytesPerCluster
),
249 if (!NT_SUCCESS(Status
))
253 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
255 else if (InternalOffset
!= 0)
257 ExFreePool(BaseAddress
);
261 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, FALSE
);
262 if ((*CurrentCluster
) == 0xFFFFFFFF)
271 * Otherwise just move onto the next cluster
273 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
275 NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, FALSE
);
276 if ((*CurrentCluster
) == 0xFFFFFFFF)
283 * Copy the data from the cache to the caller
285 memcpy(Destination
, BaseAddress
+ InternalOffset
, InternalLength
);
288 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
290 else if (InternalOffset
!= 0)
292 ExFreePool(BaseAddress
);
294 return(STATUS_SUCCESS
);
298 VfatReadCluster(PDEVICE_EXTENSION DeviceExt
,
302 PULONG CurrentCluster
,
305 ULONG InternalOffset
,
306 ULONG InternalLength
)
308 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
310 return(VfatReadBigCluster(DeviceExt
,
322 return(VfatReadSmallCluster(DeviceExt
,
332 return(STATUS_SUCCESS
);
336 VfatReadFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
337 PVOID Buffer
, ULONG Length
, ULONG ReadOffset
,
338 PULONG LengthRead
, ULONG NoCache
)
340 * FUNCTION: Reads data from a file
343 ULONG CurrentCluster
;
351 assert (DeviceExt
!= NULL
);
352 assert (DeviceExt
->BytesPerCluster
!= 0);
353 assert (FileObject
!= NULL
);
354 assert (FileObject
->FsContext
!= NULL
);
356 DPRINT ("VfatReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
357 "Length %d, ReadOffset 0x%x)\n", DeviceExt
, FileObject
, Buffer
,
360 Fcb
= ((PVFATCCB
)FileObject
->FsContext2
)->pFcb
;
363 * Find the first cluster
365 if (DeviceExt
->FatType
== FAT32
)
366 CurrentCluster
= Fcb
->entry
.FirstCluster
367 + Fcb
->entry
.FirstClusterHigh
* 65536;
369 CurrentCluster
= Fcb
->entry
.FirstCluster
;
370 FirstCluster
= CurrentCluster
;
373 * Truncate the read if necessary
375 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
377 if (ReadOffset
>= Fcb
->entry
.FileSize
)
379 return (STATUS_END_OF_FILE
);
381 if ((ReadOffset
+ Length
) > Fcb
->entry
.FileSize
)
383 Length
= Fcb
->entry
.FileSize
- ReadOffset
;
388 * Select an appropiate size for reads
390 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
392 ChunkSize
= DeviceExt
->BytesPerCluster
;
396 ChunkSize
= PAGESIZE
;
402 * Find the cluster to start the read from
403 * FIXME: Optimize by remembering the last cluster read and using if
406 Status
= OffsetToCluster(DeviceExt
,
408 ROUND_DOWN(ReadOffset
, ChunkSize
),
411 if (!NT_SUCCESS(Status
))
417 * If the read doesn't begin on a chunk boundary then we need special
420 if ((ReadOffset
% ChunkSize
) != 0)
422 TempLength
= min (Length
, ChunkSize
- (ReadOffset
% ChunkSize
));
423 VfatReadCluster(DeviceExt
, Fcb
, ROUND_DOWN(ReadOffset
, ChunkSize
),
424 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
,
425 ReadOffset
% ChunkSize
, TempLength
);
427 (*LengthRead
) = (*LengthRead
) + TempLength
;
428 Length
= Length
- TempLength
;
429 Buffer
= Buffer
+ TempLength
;
430 ReadOffset
= ReadOffset
+ TempLength
;
433 while (Length
>= ChunkSize
)
435 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
436 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
, 0,
439 (*LengthRead
) = (*LengthRead
) + ChunkSize
;
440 Buffer
= Buffer
+ ChunkSize
;
441 Length
= Length
- ChunkSize
;
442 ReadOffset
= ReadOffset
+ ChunkSize
;
446 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
447 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
, 0,
449 (*LengthRead
) = (*LengthRead
) + Length
;
451 return (STATUS_SUCCESS
);
455 VfatWriteBigCluster(PDEVICE_EXTENSION DeviceExt
,
459 PULONG CurrentCluster
,
462 ULONG InternalOffset
,
463 ULONG InternalLength
,
468 PCACHE_SEGMENT CacheSeg
;
472 * In this case the size of a cache segment is the same as a cluster
476 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
481 if (!NT_SUCCESS(Status
))
490 * If we are bypassing the cache and not writing starting on
491 * cluster boundary then allocate a temporary buffer
493 if (InternalOffset
!= 0)
495 BaseAddress
= ExAllocatePool(NonPagedPool
,
496 DeviceExt
->BytesPerCluster
);
497 if (BaseAddress
== NULL
)
499 return(STATUS_NO_MEMORY
);
503 if (!Valid
&& InternalLength
!= DeviceExt
->BytesPerCluster
)
506 * If the data in the cache isn't valid or we are bypassing the
507 * cache and not writing a cluster aligned, cluster sized region
508 * then read data in to base address
510 Status
= VfatRawReadCluster(DeviceExt
, FirstCluster
, BaseAddress
,
512 if (!NT_SUCCESS(Status
))
516 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
518 else if (InternalOffset
!= 0)
520 ExFreePool(BaseAddress
);
525 if (!NoCache
|| InternalLength
!= DeviceExt
->BytesPerCluster
)
528 * If we are writing into the cache or we are writing from a
529 * temporary buffer then copy the data over
531 DPRINT1("InternalOffset 0x%x InternalLength 0x%x BA %x Byte1 %c\n",
532 InternalOffset
, InternalLength
, BaseAddress
, *(PUCHAR
)Source
);
533 memcpy(BaseAddress
+ InternalOffset
, Source
, InternalLength
);
536 * Write the data back to disk
538 DPRINT("Writing 0x%x\n", *CurrentCluster
);
539 Status
= VfatRawWriteCluster(DeviceExt
, FirstCluster
, BaseAddress
,
543 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
545 else if (InternalOffset
!= 0)
547 ExFreePool(BaseAddress
);
550 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, Extend
);
551 if (!NT_SUCCESS(Status
))
555 return(STATUS_SUCCESS
);
559 VfatWriteSmallCluster(PDEVICE_EXTENSION DeviceExt
,
563 PULONG CurrentCluster
,
566 ULONG InternalOffset
,
567 ULONG InternalLength
,
572 PCACHE_SEGMENT CacheSeg
;
578 * Otherwise we read a page of clusters together
583 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
588 if (!NT_SUCCESS(Status
))
596 if (InternalOffset
!= 0)
598 BaseAddress
= ExAllocatePool(NonPagedPool
, PAGESIZE
);
599 if (BaseAddress
== NULL
)
601 return(STATUS_NO_MEMORY
);
606 BaseAddress
= Source
;
611 * If necessary read all the data for the page, unfortunately the
612 * file length may not be page aligned in which case the page will
613 * only be partially filled.
614 * FIXME: So zero fill the rest?
616 if (!Valid
|| InternalLength
!= PAGESIZE
)
618 NCluster
= *CurrentCluster
;
620 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
622 Status
= VfatRawReadCluster(DeviceExt
,
625 (i
* DeviceExt
->BytesPerCluster
),
627 if (!NT_SUCCESS(Status
))
631 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
633 else if (InternalOffset
!= 0)
635 ExFreePool(BaseAddress
);
639 Status
= NextCluster(DeviceExt
, FirstCluster
, &NCluster
, Extend
);
640 if (NCluster
== 0xFFFFFFFF)
649 * Otherwise just move onto the next cluster
651 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
653 NextCluster(DeviceExt
, FirstCluster
, &NCluster
, Extend
);
654 if (NCluster
== 0xFFFFFFFF)
661 if (!NoCache
|| InternalLength
!= PAGESIZE
)
664 * Copy the caller's data if we are using the cache or writing
665 * from temporary buffer
667 memcpy(BaseAddress
+ InternalOffset
, Source
, InternalLength
);
671 * Write the data to the disk
673 NCluster
= *CurrentCluster
;
675 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
677 Status
= VfatRawWriteCluster(DeviceExt
,
680 (i
* DeviceExt
->BytesPerCluster
),
682 if (!NT_SUCCESS(Status
))
686 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
688 else if (InternalOffset
!= 0)
690 ExFreePool(BaseAddress
);
694 Status
= NextCluster(DeviceExt
, FirstCluster
, &NCluster
, Extend
);
695 if (NCluster
== 0xFFFFFFFF)
700 *CurrentCluster
= NCluster
;
704 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
706 else if (InternalOffset
!= 0)
708 ExFreePool(BaseAddress
);
710 return(STATUS_SUCCESS
);
714 VfatWriteCluster(PDEVICE_EXTENSION DeviceExt
,
718 PULONG CurrentCluster
,
721 ULONG InternalOffset
,
722 ULONG InternalLength
,
727 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
729 return(VfatWriteBigCluster(DeviceExt
,
742 return(VfatWriteSmallCluster(DeviceExt
,
756 VfatWriteFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
757 PVOID Buffer
, ULONG Length
, ULONG WriteOffset
,
760 * FUNCTION: Writes data to file
763 ULONG CurrentCluster
;
768 LARGE_INTEGER SystemTime
, LocalTime
;
773 DPRINT1 ("VfatWriteFile(FileObject %x, Buffer %x, Length %x, "
774 "WriteOffset %x\n", FileObject
, Buffer
, Length
, WriteOffset
);
776 /* Locate the first cluster of the file */
778 pCcb
= (PVFATCCB
) (FileObject
->FsContext2
);
783 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
785 ChunkSize
= DeviceExt
->BytesPerCluster
;
789 ChunkSize
= PAGESIZE
;
792 if (DeviceExt
->FatType
== FAT32
)
795 Fcb
->entry
.FirstCluster
+ Fcb
->entry
.FirstClusterHigh
* 65536;
799 CurrentCluster
= Fcb
->entry
.FirstCluster
;
801 FirstCluster
= CurrentCluster
;
803 /* Find the cluster according to the offset in the file */
804 if (CurrentCluster
== 0)
809 Status
= NextCluster (DeviceExt
, FirstCluster
, &CurrentCluster
,
811 if (DeviceExt
->FatType
== FAT32
)
813 Fcb
->entry
.FirstClusterHigh
= CurrentCluster
>> 16;
814 Fcb
->entry
.FirstCluster
= CurrentCluster
;
818 Fcb
->entry
.FirstCluster
= CurrentCluster
;
820 FirstCluster
= CurrentCluster
;
822 Status
= OffsetToCluster(DeviceExt
, FirstCluster
, WriteOffset
,
823 &CurrentCluster
, TRUE
);
825 if (WriteOffset
+ Length
> Fcb
->entry
.FileSize
&&
826 !(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
828 Fcb
->entry
.FileSize
= WriteOffset
+ Length
;
832 * If the offset in the cluster doesn't fall on the cluster boundary
833 * then we have to write only from the specified offset
836 if ((WriteOffset
% ChunkSize
) != 0)
838 TempLength
= min (Length
, ChunkSize
- (WriteOffset
% ChunkSize
));
839 if ((Length
- TempLength
) > 0)
847 Status
= VfatWriteCluster(DeviceExt
,
849 ROUND_DOWN(WriteOffset
, ChunkSize
),
854 WriteOffset
% ChunkSize
,
857 Buffer
= Buffer
+ TempLength
;
858 Length
= Length
- TempLength
;
859 WriteOffset
= WriteOffset
+ TempLength
;
862 while (Length
>= ChunkSize
)
864 if ((Length
- ChunkSize
) > 0)
872 Status
= VfatWriteCluster(DeviceExt
,
874 ROUND_DOWN(WriteOffset
, ChunkSize
),
882 Buffer
= Buffer
+ ChunkSize
;
883 Length
= Length
- ChunkSize
;
884 WriteOffset
= WriteOffset
+ ChunkSize
;
887 /* Write the remainder */
890 Status
= VfatWriteCluster(DeviceExt
,
892 ROUND_DOWN(WriteOffset
, ChunkSize
),
903 /* set dates and times */
904 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
906 KeQuerySystemTime (&SystemTime
);
907 ExSystemTimeToLocalTime (&SystemTime
, &LocalTime
);
908 FsdFileTimeToDosDateTime ((TIME
*)&LocalTime
,
909 &Fcb
->entry
.UpdateDate
,
910 &Fcb
->entry
.UpdateTime
);
911 Fcb
->entry
.AccessDate
= Fcb
->entry
.UpdateDate
;
912 updEntry (DeviceExt
, FileObject
);
915 return (STATUS_SUCCESS
);
919 VfatWrite (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
921 * FUNCTION: Write to a file
927 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation (Irp
);
928 PFILE_OBJECT FileObject
= Stack
->FileObject
;
929 PDEVICE_EXTENSION DeviceExt
= DeviceObject
->DeviceExtension
;
933 DPRINT ("VfatWrite(DeviceObject %x Irp %x)\n", DeviceObject
, Irp
);
935 Length
= Stack
->Parameters
.Write
.Length
;
936 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
937 Offset
= Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
;
939 if (Irp
->Flags
& IRP_PAGING_IO
||
940 FileObject
->Flags
& FO_NO_INTERMEDIATE_BUFFERING
)
949 Status
= VfatWriteFile (DeviceExt
, FileObject
, Buffer
, Length
, Offset
,
952 Irp
->IoStatus
.Status
= Status
;
953 Irp
->IoStatus
.Information
= Length
;
954 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
960 VfatRead (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
962 * FUNCTION: Read from a file
968 PIO_STACK_LOCATION Stack
;
969 PFILE_OBJECT FileObject
;
970 PDEVICE_EXTENSION DeviceExt
;
976 DPRINT ("VfatRead(DeviceObject %x, Irp %x)\n", DeviceObject
, Irp
);
978 /* Precondition / Initialization */
979 assert (Irp
!= NULL
);
980 Stack
= IoGetCurrentIrpStackLocation (Irp
);
981 assert (Stack
!= NULL
);
982 FileObject
= Stack
->FileObject
;
983 assert (FileObject
!= NULL
);
984 DeviceExt
= DeviceObject
->DeviceExtension
;
985 assert (DeviceExt
!= NULL
);
987 Length
= Stack
->Parameters
.Read
.Length
;
988 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
989 Offset
= Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
;
991 if (Irp
->Flags
& IRP_PAGING_IO
||
992 FileObject
->Flags
& FO_NO_INTERMEDIATE_BUFFERING
)
1001 /* fail if file is a directory */
1002 Fcb
= ((PVFATCCB
) (FileObject
->FsContext2
))->pFcb
;
1003 if (Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
1005 Status
= STATUS_FILE_IS_A_DIRECTORY
;
1009 Status
= VfatReadFile (DeviceExt
,
1010 FileObject
, Buffer
, Length
, Offset
, &LengthRead
,
1014 Irp
->IoStatus
.Status
= Status
;
1015 Irp
->IoStatus
.Information
= LengthRead
;
1016 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);