2 /* $Id: rw.c,v 1.15 2001/01/12 21:00:08 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
)
35 if (FirstCluster
== 1)
37 (*CurrentCluster
) += DeviceExt
->Boot
->SectorsPerCluster
;
38 return(STATUS_SUCCESS
);
44 Status
= GetNextCluster(DeviceExt
, (*CurrentCluster
), CurrentCluster
);
50 OffsetToCluster(PDEVICE_EXTENSION DeviceExt
,
59 if (FirstCluster
== 1)
61 /* root of FAT16 or FAT12 */
62 *Cluster
= DeviceExt
->rootStart
+ FileOffset
63 / (DeviceExt
->BytesPerCluster
) * DeviceExt
->Boot
->SectorsPerCluster
;
64 return(STATUS_SUCCESS
);
68 CurrentCluster
= FirstCluster
;
69 for (i
= 0; i
< FileOffset
/ DeviceExt
->BytesPerCluster
; i
++)
71 Status
= GetNextCluster (DeviceExt
, CurrentCluster
, &CurrentCluster
);
72 if (!NT_SUCCESS(Status
))
77 *Cluster
= CurrentCluster
;
78 return(STATUS_SUCCESS
);
83 VfatReadFileNoCache (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
84 PVOID Buffer
, ULONG Length
, ULONG ReadOffset
,
87 * FUNCTION: Reads data from a file
99 assert (DeviceExt
!= NULL
);
100 assert (DeviceExt
->BytesPerCluster
!= 0);
101 assert (FileObject
!= NULL
);
102 assert (FileObject
->FsContext
!= NULL
);
104 DPRINT ("FsdReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
105 "Length %d, ReadOffset %d)\n", DeviceExt
, FileObject
, Buffer
,
108 Fcb
= ((PVFATCCB
)FileObject
->FsContext2
)->pFcb
;
111 * Find the first cluster
113 if (DeviceExt
->FatType
== FAT32
)
114 CurrentCluster
= Fcb
->entry
.FirstCluster
115 + Fcb
->entry
.FirstClusterHigh
* 65536;
117 CurrentCluster
= Fcb
->entry
.FirstCluster
;
118 FirstCluster
= CurrentCluster
;
121 * Truncate the read if necessary
123 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
125 if (ReadOffset
>= Fcb
->entry
.FileSize
)
127 return (STATUS_END_OF_FILE
);
129 if ((ReadOffset
+ Length
) > Fcb
->entry
.FileSize
)
131 Length
= Fcb
->entry
.FileSize
- ReadOffset
;
139 * Allocate a buffer to hold partial clusters
141 Temp
= ExAllocatePool (NonPagedPool
, DeviceExt
->BytesPerCluster
);
143 return STATUS_UNSUCCESSFUL
;
146 * Find the cluster to start the read from
147 * FIXME: Optimize by remembering the last cluster read and using if
150 if (FirstCluster
== 1)
152 /* root of FAT16 or FAT12 */
153 CurrentCluster
= DeviceExt
->rootStart
+ ReadOffset
154 / (DeviceExt
->BytesPerCluster
) * DeviceExt
->Boot
->SectorsPerCluster
;
157 for (FileOffset
= 0; FileOffset
< ReadOffset
/ DeviceExt
->BytesPerCluster
;
160 Status
= GetNextCluster (DeviceExt
, CurrentCluster
, &CurrentCluster
);
165 * If the read doesn't begin on a cluster boundary then read a full
166 * cluster and copy it.
168 if ((ReadOffset
% DeviceExt
->BytesPerCluster
) != 0)
170 if (FirstCluster
== 1)
172 /* FIXME: Check status */
173 VfatReadSectors (DeviceExt
->StorageDevice
,
175 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
176 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
180 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
181 Status
= GetNextCluster (DeviceExt
, CurrentCluster
, &CurrentCluster
);
183 TempLength
= min (Length
, DeviceExt
->BytesPerCluster
-
184 (ReadOffset
% DeviceExt
->BytesPerCluster
));
186 memcpy (Buffer
, Temp
+ ReadOffset
% DeviceExt
->BytesPerCluster
,
189 (*LengthRead
) = (*LengthRead
) + TempLength
;
190 Length
= Length
- TempLength
;
191 Buffer
= Buffer
+ TempLength
;
194 while (Length
>= DeviceExt
->BytesPerCluster
)
196 if (FirstCluster
== 1)
198 /* FIXME: Check status */
199 VfatReadSectors (DeviceExt
->StorageDevice
,
201 DeviceExt
->Boot
->SectorsPerCluster
, Buffer
);
202 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
206 VFATLoadCluster (DeviceExt
, Buffer
, CurrentCluster
);
207 Status
= GetNextCluster (DeviceExt
, CurrentCluster
, &CurrentCluster
);
209 if (CurrentCluster
== 0xffffffff)
212 return (STATUS_SUCCESS
);
215 (*LengthRead
) = (*LengthRead
) + DeviceExt
->BytesPerCluster
;
216 Buffer
= Buffer
+ DeviceExt
->BytesPerCluster
;
217 Length
= Length
- DeviceExt
->BytesPerCluster
;
222 (*LengthRead
) = (*LengthRead
) + Length
;
223 if (FirstCluster
== 1)
225 /* FIXME: Check status */
226 VfatReadSectors (DeviceExt
->StorageDevice
,
228 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
229 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
233 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
234 Status
= GetNextCluster (DeviceExt
, CurrentCluster
, &CurrentCluster
);
236 memcpy (Buffer
, Temp
, Length
);
239 return (STATUS_SUCCESS
);
243 VfatRawReadCluster (PDEVICE_EXTENSION DeviceExt
,
245 PULONG CurrentCluster
,
250 if (FirstCluster
== 1)
252 Status
= VfatReadSectors (DeviceExt
->StorageDevice
,
254 DeviceExt
->Boot
->SectorsPerCluster
,
260 VFATLoadCluster (DeviceExt
, Destination
, (*CurrentCluster
));
261 Status
= STATUS_SUCCESS
;
267 VfatReadCluster(PDEVICE_EXTENSION DeviceExt
,
271 PULONG CurrentCluster
,
275 ULONG BytesPerCluster
;
278 PCACHE_SEGMENT CacheSeg
;
281 BytesPerCluster
= DeviceExt
->Boot
->SectorsPerCluster
* BLOCKSIZE
;
283 if (BytesPerCluster
>= PAGESIZE
)
286 * In this case the size of a cache segment is the same as a cluster
288 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
293 if (!NT_SUCCESS(Status
))
300 * If necessary read the cluster from the disk
302 Status
= VfatRawReadCluster(DeviceExt
, FirstCluster
, CurrentCluster
,
304 if (!NT_SUCCESS(Status
))
306 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
311 * Copy the data from the cache to the caller
313 memcpy(Destination
, BaseAddress
, BytesPerCluster
);
314 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
316 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
);
317 if (!NT_SUCCESS(Status
))
326 ULONG InternalOffset
;
329 * Otherwise we read a page of clusters together
331 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
336 if (!NT_SUCCESS(Status
))
342 * If necessary read all the data for the page, unfortunately the
343 * file length may not be page aligned in which case the page will
344 * only be partially filled.
345 * FIXME: So zero fill the rest?
349 for (i
= 0; i
< (PAGESIZE
/ BytesPerCluster
); i
++)
351 Status
= VfatRawReadCluster(DeviceExt
,
354 BaseAddress
+ (i
* BytesPerCluster
));
355 if (!NT_SUCCESS(Status
))
357 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
360 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
);
361 if ((*CurrentCluster
) == 0xFFFFFFFF)
370 * Otherwise just move onto the next cluster
372 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
374 NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
);
375 if ((*CurrentCluster
) == 0xFFFFFFFF)
382 * Copy the data from the cache to the caller
384 memcpy(Destination
, BaseAddress
+ InternalOffset
, BytesPerCluster
);
385 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
387 return(STATUS_SUCCESS
);
391 VfatReadFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
392 PVOID Buffer
, ULONG Length
, ULONG ReadOffset
,
395 * FUNCTION: Reads data from a file
398 ULONG CurrentCluster
;
407 assert (DeviceExt
!= NULL
);
408 assert (DeviceExt
->BytesPerCluster
!= 0);
409 assert (FileObject
!= NULL
);
410 assert (FileObject
->FsContext
!= NULL
);
412 DPRINT ("FsdReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
413 "Length %d, ReadOffset 0x%x)\n", DeviceExt
, FileObject
, Buffer
,
416 Fcb
= ((PVFATCCB
)FileObject
->FsContext2
)->pFcb
;
419 * Find the first cluster
421 if (DeviceExt
->FatType
== FAT32
)
422 CurrentCluster
= Fcb
->entry
.FirstCluster
423 + Fcb
->entry
.FirstClusterHigh
* 65536;
425 CurrentCluster
= Fcb
->entry
.FirstCluster
;
426 FirstCluster
= CurrentCluster
;
429 * Truncate the read if necessary
431 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
433 if (ReadOffset
>= Fcb
->entry
.FileSize
)
435 return (STATUS_END_OF_FILE
);
437 if ((ReadOffset
+ Length
) > Fcb
->entry
.FileSize
)
439 Length
= Fcb
->entry
.FileSize
- ReadOffset
;
444 ChunkSize
= max(DeviceExt
->BytesPerCluster
, PAGESIZE
);
449 * Allocate a buffer to hold partial clusters
451 Temp
= ExAllocatePool (NonPagedPool
, ChunkSize
);
454 return(STATUS_NO_MEMORY
);
458 * Find the cluster to start the read from
459 * FIXME: Optimize by remembering the last cluster read and using if
462 Status
= OffsetToCluster(DeviceExt
, FirstCluster
, ReadOffset
,
464 if (!NT_SUCCESS(Status
))
470 * If the read doesn't begin on a cluster boundary then read a full
471 * cluster and copy it.
473 if ((ReadOffset
% ChunkSize
) != 0)
475 VfatReadCluster(DeviceExt
, Fcb
,
476 ROUND_DOWN(ReadOffset
, ChunkSize
),
477 FirstCluster
, &CurrentCluster
, Temp
, 1);
478 TempLength
= min (Length
, ChunkSize
- (ReadOffset
% ChunkSize
));
480 memcpy (Buffer
, Temp
+ ReadOffset
% ChunkSize
, TempLength
);
482 (*LengthRead
) = (*LengthRead
) + TempLength
;
483 Length
= Length
- TempLength
;
484 Buffer
= Buffer
+ TempLength
;
485 ReadOffset
= ReadOffset
+ TempLength
;
488 while (Length
>= ChunkSize
)
490 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
491 FirstCluster
, &CurrentCluster
, Buffer
, 1);
492 if (CurrentCluster
== 0xffffffff)
495 return (STATUS_SUCCESS
);
498 (*LengthRead
) = (*LengthRead
) + ChunkSize
;
499 Buffer
= Buffer
+ ChunkSize
;
500 Length
= Length
- ChunkSize
;
501 ReadOffset
= ReadOffset
+ ChunkSize
;
506 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
507 FirstCluster
, &CurrentCluster
, Temp
, 1);
508 (*LengthRead
) = (*LengthRead
) + Length
;
509 memcpy (Buffer
, Temp
, Length
);
512 return (STATUS_SUCCESS
);
517 VfatWriteFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
518 PVOID Buffer
, ULONG Length
, ULONG WriteOffset
)
520 * FUNCTION: Writes data to file
523 ULONG CurrentCluster
;
529 ULONG TempLength
, Length2
= Length
;
530 LARGE_INTEGER SystemTime
, LocalTime
;
532 DPRINT1 ("FsdWriteFile(FileObject %x, Buffer %x, Length %x, "
533 "WriteOffset %x\n", FileObject
, Buffer
, Length
, WriteOffset
);
535 /* Locate the first cluster of the file */
537 pCcb
= (PVFATCCB
) (FileObject
->FsContext2
);
541 if (DeviceExt
->FatType
== FAT32
)
544 Fcb
->entry
.FirstCluster
+ Fcb
->entry
.FirstClusterHigh
* 65536;
548 CurrentCluster
= Fcb
->entry
.FirstCluster
;
550 FirstCluster
= CurrentCluster
;
552 /* Allocate a buffer to hold 1 cluster of data */
553 Temp
= ExAllocatePool (NonPagedPool
, DeviceExt
->BytesPerCluster
);
556 /* Find the cluster according to the offset in the file */
557 if (CurrentCluster
== 1)
559 CurrentCluster
= DeviceExt
->rootStart
+ WriteOffset
560 / DeviceExt
->BytesPerCluster
* DeviceExt
->Boot
->SectorsPerCluster
;
564 if (CurrentCluster
== 0)
569 CurrentCluster
= GetNextWriteCluster (DeviceExt
, 0);
570 if (DeviceExt
->FatType
== FAT32
)
572 Fcb
->entry
.FirstClusterHigh
= CurrentCluster
>> 16;
573 Fcb
->entry
.FirstCluster
= CurrentCluster
;
576 Fcb
->entry
.FirstCluster
= CurrentCluster
;
581 FileOffset
< WriteOffset
/ DeviceExt
->BytesPerCluster
;
585 GetNextWriteCluster (DeviceExt
, CurrentCluster
);
592 * If the offset in the cluster doesn't fall on the cluster boundary
593 * then we have to write only from the specified offset
596 if ((WriteOffset
% DeviceExt
->BytesPerCluster
) != 0)
599 TempLength
= min (Length
, DeviceExt
->BytesPerCluster
-
600 (WriteOffset
% DeviceExt
->BytesPerCluster
));
601 /* Read in the existing cluster data */
602 if (FirstCluster
== 1)
604 /* FIXME: Check status */
605 VfatReadSectors (DeviceExt
->StorageDevice
,
607 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
611 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
614 /* Overwrite the last parts of the data as necessary */
615 memcpy (Temp
+ (WriteOffset
% DeviceExt
->BytesPerCluster
),
618 /* Write the cluster back */
619 Length2
-= TempLength
;
620 if (FirstCluster
== 1)
622 VFATWriteSectors (DeviceExt
->StorageDevice
,
624 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
625 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
629 VFATWriteCluster (DeviceExt
, Temp
, CurrentCluster
);
631 CurrentCluster
= GetNextWriteCluster (DeviceExt
, CurrentCluster
);
633 Buffer
= Buffer
+ TempLength
;
637 /* Write the buffer in chunks of 1 cluster */
639 while (Length2
>= DeviceExt
->BytesPerCluster
)
642 if (CurrentCluster
== 0)
645 return (STATUS_UNSUCCESSFUL
);
647 Length2
-= DeviceExt
->BytesPerCluster
;
648 if (FirstCluster
== 1)
650 VFATWriteSectors (DeviceExt
->StorageDevice
,
652 DeviceExt
->Boot
->SectorsPerCluster
, Buffer
);
653 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
657 VFATWriteCluster (DeviceExt
, Buffer
, CurrentCluster
);
659 CurrentCluster
= GetNextWriteCluster (DeviceExt
, CurrentCluster
);
661 Buffer
= Buffer
+ DeviceExt
->BytesPerCluster
;
665 /* Write the remainder */
670 if (CurrentCluster
== 0)
673 return (STATUS_UNSUCCESSFUL
);
676 /* Read in the existing cluster data */
677 if (FirstCluster
== 1)
679 /* FIXME: Check status */
680 VfatReadSectors (DeviceExt
->StorageDevice
,
682 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
686 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
688 memcpy (Temp
, Buffer
, Length2
);
690 if (FirstCluster
== 1)
692 VFATWriteSectors (DeviceExt
->StorageDevice
,
694 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
698 VFATWriteCluster (DeviceExt
, Temp
, CurrentCluster
);
705 /* set dates and times */
706 KeQuerySystemTime (&SystemTime
);
707 ExSystemTimeToLocalTime (&SystemTime
, &LocalTime
);
708 FsdFileTimeToDosDateTime ((TIME
*) & LocalTime
,
709 &Fcb
->entry
.UpdateDate
, &Fcb
->entry
.UpdateTime
);
710 Fcb
->entry
.AccessDate
= Fcb
->entry
.UpdateDate
;
712 if (Fcb
->entry
.FileSize
< WriteOffset
+ Length
713 && !(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
715 Fcb
->entry
.FileSize
= WriteOffset
+ Length
;
717 * update entry in directory
719 updEntry (DeviceExt
, FileObject
);
723 return (STATUS_SUCCESS
);
728 VfatWrite (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
730 * FUNCTION: Write to a file
736 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation (Irp
);
737 /* PFILE_OBJECT FileObject = Stack->FileObject; */
738 /* PDEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension; */
741 DPRINT ("VfatWrite(DeviceObject %x Irp %x)\n", DeviceObject
, Irp
);
743 Length
= Stack
->Parameters
.Write
.Length
;
744 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
745 Offset
= Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
;
747 /* Status = VfatWriteFile (DeviceExt, FileObject, Buffer, Length, Offset); */
748 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
750 Irp
->IoStatus
.Status
= Status
;
751 Irp
->IoStatus
.Information
= Length
;
752 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
758 VfatRead (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
760 * FUNCTION: Read from a file
766 PIO_STACK_LOCATION Stack
;
767 PFILE_OBJECT FileObject
;
768 PDEVICE_EXTENSION DeviceExt
;
773 DPRINT ("VfatRead(DeviceObject %x, Irp %x)\n", DeviceObject
, Irp
);
775 /* Precondition / Initialization */
776 assert (Irp
!= NULL
);
777 Stack
= IoGetCurrentIrpStackLocation (Irp
);
778 assert (Stack
!= NULL
);
779 FileObject
= Stack
->FileObject
;
780 assert (FileObject
!= NULL
);
781 DeviceExt
= DeviceObject
->DeviceExtension
;
782 assert (DeviceExt
!= NULL
);
784 Length
= Stack
->Parameters
.Read
.Length
;
785 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
786 Offset
= Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
;
788 /* fail if file is a directory */
789 Fcb
= ((PVFATCCB
) (FileObject
->FsContext2
))->pFcb
;
790 if (Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
792 Status
= STATUS_FILE_IS_A_DIRECTORY
;
796 Status
= VfatReadFile (DeviceExt
,
797 FileObject
, Buffer
, Length
, Offset
, &LengthRead
);
800 Irp
->IoStatus
.Status
= Status
;
801 Irp
->IoStatus
.Information
= LengthRead
;
802 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);