2 /* $Id: rw.c,v 1.21 2001/03/02 15:59:16 cnettel 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
= CcRequestCacheSegment(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 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
167 else if (InternalOffset
!= 0)
169 ExFreePool(BaseAddress
);
175 * Copy the data from the cache to the caller
177 memcpy(Destination
, BaseAddress
+ InternalOffset
, InternalLength
);
180 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
182 else if (InternalOffset
!= 0)
184 ExFreePool(BaseAddress
);
187 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, FALSE
);
188 if (!NT_SUCCESS(Status
))
192 return(STATUS_SUCCESS
);
196 VfatReadSmallCluster(PDEVICE_EXTENSION DeviceExt
,
200 PULONG CurrentCluster
,
203 ULONG InternalOffset
,
204 ULONG InternalLength
)
207 PVOID BaseAddress
= NULL
;
208 PCACHE_SEGMENT CacheSeg
= NULL
;
213 * Otherwise we read a page of clusters together
217 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
222 if (!NT_SUCCESS(Status
))
230 if (InternalOffset
== 0)
232 BaseAddress
= Destination
;
236 BaseAddress
= ExAllocatePool(NonPagedPool
, PAGESIZE
);
237 if (BaseAddress
== NULL
)
239 return(STATUS_NO_MEMORY
);
245 * If necessary read all the data for the page, unfortunately the
246 * file length may not be page aligned in which case the page will
247 * only be partially filled.
248 * FIXME: So zero fill the rest?
252 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
254 Status
= VfatRawReadCluster(DeviceExt
,
257 (i
* DeviceExt
->BytesPerCluster
),
259 if (!NT_SUCCESS(Status
))
263 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
265 else if (InternalOffset
!= 0)
267 ExFreePool(BaseAddress
);
271 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, FALSE
);
272 if ((*CurrentCluster
) == 0xFFFFFFFF)
281 * Otherwise just move onto the next cluster
283 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
285 NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, FALSE
);
286 if ((*CurrentCluster
) == 0xFFFFFFFF)
293 * Copy the data from the cache to the caller
295 memcpy(Destination
, BaseAddress
+ InternalOffset
, InternalLength
);
298 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
300 else if (InternalOffset
!= 0)
302 ExFreePool(BaseAddress
);
304 return(STATUS_SUCCESS
);
308 VfatReadCluster(PDEVICE_EXTENSION DeviceExt
,
312 PULONG CurrentCluster
,
315 ULONG InternalOffset
,
316 ULONG InternalLength
)
318 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
320 return(VfatReadBigCluster(DeviceExt
,
332 return(VfatReadSmallCluster(DeviceExt
,
342 return(STATUS_SUCCESS
);
346 VfatReadFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
347 PVOID Buffer
, ULONG Length
, ULONG ReadOffset
,
348 PULONG LengthRead
, ULONG NoCache
)
350 * FUNCTION: Reads data from a file
353 ULONG CurrentCluster
;
361 assert (DeviceExt
!= NULL
);
362 assert (DeviceExt
->BytesPerCluster
!= 0);
363 assert (FileObject
!= NULL
);
364 assert (FileObject
->FsContext
!= NULL
);
366 DPRINT ("VfatReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
367 "Length %d, ReadOffset 0x%x)\n", DeviceExt
, FileObject
, Buffer
,
370 Fcb
= ((PVFATCCB
)FileObject
->FsContext2
)->pFcb
;
373 * Find the first cluster
375 if (DeviceExt
->FatType
== FAT32
)
376 CurrentCluster
= Fcb
->entry
.FirstCluster
377 + Fcb
->entry
.FirstClusterHigh
* 65536;
379 CurrentCluster
= Fcb
->entry
.FirstCluster
;
380 FirstCluster
= CurrentCluster
;
383 * Truncate the read if necessary
385 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
387 if (ReadOffset
>= Fcb
->entry
.FileSize
)
389 return (STATUS_END_OF_FILE
);
391 if ((ReadOffset
+ Length
) > Fcb
->entry
.FileSize
)
393 Length
= Fcb
->entry
.FileSize
- ReadOffset
;
398 * Select an appropiate size for reads
400 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
402 ChunkSize
= DeviceExt
->BytesPerCluster
;
406 ChunkSize
= PAGESIZE
;
412 * Find the cluster to start the read from
413 * FIXME: Optimize by remembering the last cluster read and using if
416 Status
= OffsetToCluster(DeviceExt
,
418 ROUND_DOWN(ReadOffset
, ChunkSize
),
421 if (!NT_SUCCESS(Status
))
427 * If the read doesn't begin on a chunk boundary then we need special
430 if ((ReadOffset
% ChunkSize
) != 0)
432 TempLength
= min (Length
, ChunkSize
- (ReadOffset
% ChunkSize
));
433 VfatReadCluster(DeviceExt
, Fcb
, ROUND_DOWN(ReadOffset
, ChunkSize
),
434 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
,
435 ReadOffset
% ChunkSize
, TempLength
);
437 (*LengthRead
) = (*LengthRead
) + TempLength
;
438 Length
= Length
- TempLength
;
439 Buffer
= Buffer
+ TempLength
;
440 ReadOffset
= ReadOffset
+ TempLength
;
443 while (Length
>= ChunkSize
)
445 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
446 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
, 0,
449 (*LengthRead
) = (*LengthRead
) + ChunkSize
;
450 Buffer
= Buffer
+ ChunkSize
;
451 Length
= Length
- ChunkSize
;
452 ReadOffset
= ReadOffset
+ ChunkSize
;
456 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
457 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
, 0,
459 (*LengthRead
) = (*LengthRead
) + Length
;
461 return (STATUS_SUCCESS
);
465 VfatWriteBigCluster(PDEVICE_EXTENSION DeviceExt
,
469 PULONG CurrentCluster
,
472 ULONG InternalOffset
,
473 ULONG InternalLength
,
478 PCACHE_SEGMENT CacheSeg
;
482 * In this case the size of a cache segment is the same as a cluster
486 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
491 if (!NT_SUCCESS(Status
))
500 * If we are bypassing the cache and not writing starting on
501 * cluster boundary then allocate a temporary buffer
503 if (InternalOffset
!= 0)
505 BaseAddress
= ExAllocatePool(NonPagedPool
,
506 DeviceExt
->BytesPerCluster
);
507 if (BaseAddress
== NULL
)
509 return(STATUS_NO_MEMORY
);
513 if (!Valid
&& InternalLength
!= DeviceExt
->BytesPerCluster
)
516 * If the data in the cache isn't valid or we are bypassing the
517 * cache and not writing a cluster aligned, cluster sized region
518 * then read data in to base address
520 Status
= VfatRawReadCluster(DeviceExt
, FirstCluster
, BaseAddress
,
522 if (!NT_SUCCESS(Status
))
526 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
528 else if (InternalOffset
!= 0)
530 ExFreePool(BaseAddress
);
535 if (!NoCache
|| InternalLength
!= DeviceExt
->BytesPerCluster
)
538 * If we are writing into the cache or we are writing from a
539 * temporary buffer then copy the data over
541 DPRINT("InternalOffset 0x%x InternalLength 0x%x BA %x\n",
542 InternalOffset
, InternalLength
, BaseAddress
);
543 memcpy(BaseAddress
+ InternalOffset
, Source
, InternalLength
);
546 * Write the data back to disk
548 DPRINT("Writing 0x%x\n", *CurrentCluster
);
549 Status
= VfatRawWriteCluster(DeviceExt
, FirstCluster
, BaseAddress
,
553 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
555 else if (InternalOffset
!= 0)
557 ExFreePool(BaseAddress
);
560 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
, Extend
);
561 if (!NT_SUCCESS(Status
))
565 return(STATUS_SUCCESS
);
569 VfatWriteSmallCluster(PDEVICE_EXTENSION DeviceExt
,
573 PULONG CurrentCluster
,
576 ULONG InternalOffset
,
577 ULONG InternalLength
,
582 PCACHE_SEGMENT CacheSeg
;
588 * Otherwise we read a page of clusters together
593 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
598 if (!NT_SUCCESS(Status
))
606 if (InternalOffset
!= 0)
608 BaseAddress
= ExAllocatePool(NonPagedPool
, PAGESIZE
);
609 if (BaseAddress
== NULL
)
611 return(STATUS_NO_MEMORY
);
616 BaseAddress
= Source
;
621 * If necessary read all the data for the page, unfortunately the
622 * file length may not be page aligned in which case the page will
623 * only be partially filled.
624 * FIXME: So zero fill the rest?
626 if (!Valid
|| InternalLength
!= PAGESIZE
)
628 NCluster
= *CurrentCluster
;
630 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
632 Status
= VfatRawReadCluster(DeviceExt
,
635 (i
* DeviceExt
->BytesPerCluster
),
637 if (!NT_SUCCESS(Status
))
641 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
643 else if (InternalOffset
!= 0)
645 ExFreePool(BaseAddress
);
649 Status
= NextCluster(DeviceExt
, FirstCluster
, &NCluster
, Extend
);
650 if (NCluster
== 0xFFFFFFFF)
659 * Otherwise just move onto the next cluster
661 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
663 NextCluster(DeviceExt
, FirstCluster
, &NCluster
, Extend
);
664 if (NCluster
== 0xFFFFFFFF)
671 if (!NoCache
|| InternalLength
!= PAGESIZE
)
674 * Copy the caller's data if we are using the cache or writing
675 * from temporary buffer
677 memcpy(BaseAddress
+ InternalOffset
, Source
, InternalLength
);
681 * Write the data to the disk
683 NCluster
= *CurrentCluster
;
685 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
687 Status
= VfatRawWriteCluster(DeviceExt
,
690 (i
* DeviceExt
->BytesPerCluster
),
692 if (!NT_SUCCESS(Status
))
696 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
698 else if (InternalOffset
!= 0)
700 ExFreePool(BaseAddress
);
704 Status
= NextCluster(DeviceExt
, FirstCluster
, &NCluster
, Extend
);
705 if (NCluster
== 0xFFFFFFFF)
710 *CurrentCluster
= NCluster
;
714 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
716 else if (InternalOffset
!= 0)
718 ExFreePool(BaseAddress
);
720 return(STATUS_SUCCESS
);
724 VfatWriteCluster(PDEVICE_EXTENSION DeviceExt
,
728 PULONG CurrentCluster
,
731 ULONG InternalOffset
,
732 ULONG InternalLength
,
737 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
739 return(VfatWriteBigCluster(DeviceExt
,
752 return(VfatWriteSmallCluster(DeviceExt
,
766 VfatWriteFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
767 PVOID Buffer
, ULONG Length
, ULONG WriteOffset
,
770 * FUNCTION: Writes data to file
773 ULONG CurrentCluster
;
778 LARGE_INTEGER SystemTime
, LocalTime
;
783 DPRINT ("VfatWriteFile(FileObject %x, Buffer %x, Length %x, "
784 "WriteOffset %x\n", FileObject
, Buffer
, Length
, WriteOffset
);
786 /* Locate the first cluster of the file */
788 pCcb
= (PVFATCCB
) (FileObject
->FsContext2
);
793 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
795 ChunkSize
= DeviceExt
->BytesPerCluster
;
799 ChunkSize
= PAGESIZE
;
802 if (DeviceExt
->FatType
== FAT32
)
805 Fcb
->entry
.FirstCluster
+ Fcb
->entry
.FirstClusterHigh
* 65536;
809 CurrentCluster
= Fcb
->entry
.FirstCluster
;
811 FirstCluster
= CurrentCluster
;
813 /* Find the cluster according to the offset in the file */
814 if (CurrentCluster
== 0)
819 Status
= NextCluster (DeviceExt
, FirstCluster
, &CurrentCluster
,
821 if (DeviceExt
->FatType
== FAT32
)
823 Fcb
->entry
.FirstClusterHigh
= CurrentCluster
>> 16;
824 Fcb
->entry
.FirstCluster
= CurrentCluster
;
828 Fcb
->entry
.FirstCluster
= CurrentCluster
;
830 FirstCluster
= CurrentCluster
;
832 Status
= OffsetToCluster(DeviceExt
, FirstCluster
, WriteOffset
,
833 &CurrentCluster
, TRUE
);
835 if (WriteOffset
+ Length
> Fcb
->entry
.FileSize
&&
836 !(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
838 Fcb
->entry
.FileSize
= WriteOffset
+ Length
;
842 * If the offset in the cluster doesn't fall on the cluster boundary
843 * then we have to write only from the specified offset
846 if ((WriteOffset
% ChunkSize
) != 0)
848 TempLength
= min (Length
, ChunkSize
- (WriteOffset
% ChunkSize
));
849 if ((Length
- TempLength
) > 0)
857 Status
= VfatWriteCluster(DeviceExt
,
859 ROUND_DOWN(WriteOffset
, ChunkSize
),
864 WriteOffset
% ChunkSize
,
867 Buffer
= Buffer
+ TempLength
;
868 Length
= Length
- TempLength
;
869 WriteOffset
= WriteOffset
+ TempLength
;
872 while (Length
>= ChunkSize
)
874 if ((Length
- ChunkSize
) > 0)
882 Status
= VfatWriteCluster(DeviceExt
,
884 ROUND_DOWN(WriteOffset
, ChunkSize
),
892 Buffer
= Buffer
+ ChunkSize
;
893 Length
= Length
- ChunkSize
;
894 WriteOffset
= WriteOffset
+ ChunkSize
;
897 /* Write the remainder */
900 Status
= VfatWriteCluster(DeviceExt
,
902 ROUND_DOWN(WriteOffset
, ChunkSize
),
913 /* set dates and times */
914 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
916 KeQuerySystemTime (&SystemTime
);
917 ExSystemTimeToLocalTime (&SystemTime
, &LocalTime
);
918 FsdFileTimeToDosDateTime ((TIME
*)&LocalTime
,
919 &Fcb
->entry
.UpdateDate
,
920 &Fcb
->entry
.UpdateTime
);
921 Fcb
->entry
.AccessDate
= Fcb
->entry
.UpdateDate
;
922 updEntry (DeviceExt
, FileObject
);
925 return (STATUS_SUCCESS
);
929 VfatWrite (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
931 * FUNCTION: Write to a file
937 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation (Irp
);
938 PFILE_OBJECT FileObject
= Stack
->FileObject
;
939 PDEVICE_EXTENSION DeviceExt
= DeviceObject
->DeviceExtension
;
943 DPRINT ("VfatWrite(DeviceObject %x Irp %x)\n", DeviceObject
, Irp
);
945 Length
= Stack
->Parameters
.Write
.Length
;
946 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
947 Offset
= Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
;
949 if (Irp
->Flags
& IRP_PAGING_IO
||
950 FileObject
->Flags
& FO_NO_INTERMEDIATE_BUFFERING
)
959 Status
= VfatWriteFile (DeviceExt
, FileObject
, Buffer
, Length
, Offset
,
962 Irp
->IoStatus
.Status
= Status
;
963 Irp
->IoStatus
.Information
= Length
;
964 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
970 VfatRead (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
972 * FUNCTION: Read from a file
978 PIO_STACK_LOCATION Stack
;
979 PFILE_OBJECT FileObject
;
980 PDEVICE_EXTENSION DeviceExt
;
986 DPRINT ("VfatRead(DeviceObject %x, Irp %x)\n", DeviceObject
, Irp
);
988 /* Precondition / Initialization */
989 assert (Irp
!= NULL
);
990 Stack
= IoGetCurrentIrpStackLocation (Irp
);
991 assert (Stack
!= NULL
);
992 FileObject
= Stack
->FileObject
;
993 assert (FileObject
!= NULL
);
994 DeviceExt
= DeviceObject
->DeviceExtension
;
995 assert (DeviceExt
!= NULL
);
997 Length
= Stack
->Parameters
.Read
.Length
;
998 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
999 Offset
= Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
;
1001 if (Irp
->Flags
& IRP_PAGING_IO
||
1002 FileObject
->Flags
& FO_NO_INTERMEDIATE_BUFFERING
)
1011 /* fail if file is a directory */
1012 Fcb
= ((PVFATCCB
) (FileObject
->FsContext2
))->pFcb
;
1013 if (Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
1015 Status
= STATUS_FILE_IS_A_DIRECTORY
;
1019 Status
= VfatReadFile (DeviceExt
,
1020 FileObject
, Buffer
, Length
, Offset
, &LengthRead
,
1024 Irp
->IoStatus
.Status
= Status
;
1025 Irp
->IoStatus
.Information
= LengthRead
;
1026 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);