2 /* $Id: rw.c,v 1.29 2001/08/07 15:38:04 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
,
840 ROUND_DOWN(WriteOffset
, ChunkSize
),
844 if (WriteOffset
+ Length
> Fcb
->entry
.FileSize
&&
845 !(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
847 Fcb
->entry
.FileSize
= WriteOffset
+ Length
;
851 * If the offset in the cluster doesn't fall on the cluster boundary
852 * then we have to write only from the specified offset
855 if ((WriteOffset
% ChunkSize
) != 0)
857 TempLength
= min (Length
, ChunkSize
- (WriteOffset
% ChunkSize
));
858 if ((Length
- TempLength
) > 0)
866 Status
= VfatWriteCluster(DeviceExt
,
868 ROUND_DOWN(WriteOffset
, ChunkSize
),
873 WriteOffset
% ChunkSize
,
876 Buffer
= Buffer
+ TempLength
;
877 Length
= Length
- TempLength
;
878 WriteOffset
= WriteOffset
+ TempLength
;
881 while (Length
>= ChunkSize
)
883 if ((Length
- ChunkSize
) > 0)
891 Status
= VfatWriteCluster(DeviceExt
,
893 ROUND_DOWN(WriteOffset
, ChunkSize
),
901 Buffer
= Buffer
+ ChunkSize
;
902 Length
= Length
- ChunkSize
;
903 WriteOffset
= WriteOffset
+ ChunkSize
;
906 /* Write the remainder */
909 Status
= VfatWriteCluster(DeviceExt
,
911 ROUND_DOWN(WriteOffset
, ChunkSize
),
922 /* set dates and times */
923 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
925 KeQuerySystemTime (&SystemTime
);
926 ExSystemTimeToLocalTime (&SystemTime
, &LocalTime
);
927 FsdFileTimeToDosDateTime ((TIME
*)&LocalTime
,
928 &Fcb
->entry
.UpdateDate
,
929 &Fcb
->entry
.UpdateTime
);
930 Fcb
->entry
.AccessDate
= Fcb
->entry
.UpdateDate
;
931 updEntry (DeviceExt
, FileObject
);
934 return (STATUS_SUCCESS
);
938 VfatWrite (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
940 * FUNCTION: Write to a file
946 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation (Irp
);
947 PFILE_OBJECT FileObject
= Stack
->FileObject
;
948 PDEVICE_EXTENSION DeviceExt
= DeviceObject
->DeviceExtension
;
952 DPRINT ("VfatWrite(DeviceObject %x Irp %x)\n", DeviceObject
, Irp
);
954 Length
= Stack
->Parameters
.Write
.Length
;
955 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
956 Offset
= Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
;
958 if (Irp
->Flags
& IRP_PAGING_IO
||
959 FileObject
->Flags
& FO_NO_INTERMEDIATE_BUFFERING
)
968 Status
= VfatWriteFile (DeviceExt
, FileObject
, Buffer
, Length
, Offset
,
971 Irp
->IoStatus
.Status
= Status
;
972 Irp
->IoStatus
.Information
= Length
;
973 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
979 VfatRead (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
981 * FUNCTION: Read from a file
987 PIO_STACK_LOCATION Stack
;
988 PFILE_OBJECT FileObject
;
989 PDEVICE_EXTENSION DeviceExt
;
995 DPRINT ("VfatRead(DeviceObject %x, Irp %x)\n", DeviceObject
, Irp
);
997 /* Precondition / Initialization */
998 assert (Irp
!= NULL
);
999 Stack
= IoGetCurrentIrpStackLocation (Irp
);
1000 assert (Stack
!= NULL
);
1001 FileObject
= Stack
->FileObject
;
1002 assert (FileObject
!= NULL
);
1003 DeviceExt
= DeviceObject
->DeviceExtension
;
1004 assert (DeviceExt
!= NULL
);
1006 Length
= Stack
->Parameters
.Read
.Length
;
1007 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
1008 Offset
= Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
;
1010 if (Irp
->Flags
& IRP_PAGING_IO
||
1011 FileObject
->Flags
& FO_NO_INTERMEDIATE_BUFFERING
)
1020 /* fail if file is a directory */
1021 Fcb
= ((PVFATCCB
) (FileObject
->FsContext2
))->pFcb
;
1022 if (Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
1024 Status
= STATUS_FILE_IS_A_DIRECTORY
;
1028 Status
= VfatReadFile (DeviceExt
,
1029 FileObject
, Buffer
, Length
, Offset
, &LengthRead
,
1033 Irp
->IoStatus
.Status
= Status
;
1034 Irp
->IoStatus
.Information
= LengthRead
;
1035 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);