1 /* $Id: rw.c,v 1.14 2001/01/08 02:14:06 dwelch Exp $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: services/fs/vfat/rw.c
6 * PURPOSE: VFAT Filesystem
7 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
11 /* INCLUDES *****************************************************************/
13 #include <ddk/ntddk.h>
15 #include <ntos/minmax.h>
22 /* GLOBALS *******************************************************************/
24 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
25 #define ROUND_DOWN(N, S) ((N) - ((N) % (S)))
27 /* FUNCTIONS *****************************************************************/
30 NextCluster(PDEVICE_EXTENSION DeviceExt
,
32 PULONG CurrentCluster
)
34 DPRINT("NextCluster() (*CurrentCluster) 0x%x\n", (*CurrentCluster
));
35 if (FirstCluster
== 1)
37 (*CurrentCluster
) += DeviceExt
->Boot
->SectorsPerCluster
;
41 (*CurrentCluster
) = GetNextCluster(DeviceExt
, (*CurrentCluster
));
43 DPRINT("NextCluster() finished (*CurrentCluster) 0x%x\n",
48 OffsetToCluster(PDEVICE_EXTENSION DeviceExt
,
55 DPRINT("OffsetToCluster(FirstCluster 0x%x)\n", FirstCluster
);
57 CurrentCluster
= FirstCluster
;
58 if (FirstCluster
== 1)
60 /* root of FAT16 or FAT12 */
61 CurrentCluster
= DeviceExt
->rootStart
+ FileOffset
62 / (DeviceExt
->BytesPerCluster
) * DeviceExt
->Boot
->SectorsPerCluster
;
66 for (i
= 0; i
< FileOffset
/ DeviceExt
->BytesPerCluster
; i
++)
68 CurrentCluster
= GetNextCluster (DeviceExt
, CurrentCluster
);
71 DPRINT("OffsetToCluster() = 0x%x\n", CurrentCluster
);
72 return(CurrentCluster
);
76 VfatReadFileNoCache (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
77 PVOID Buffer
, ULONG Length
, ULONG ReadOffset
,
80 * FUNCTION: Reads data from a file
91 assert (DeviceExt
!= NULL
);
92 assert (DeviceExt
->BytesPerCluster
!= 0);
93 assert (FileObject
!= NULL
);
94 assert (FileObject
->FsContext
!= NULL
);
96 DPRINT ("FsdReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
97 "Length %d, ReadOffset %d)\n", DeviceExt
, FileObject
, Buffer
,
100 Fcb
= ((PVFATCCB
)FileObject
->FsContext2
)->pFcb
;
103 * Find the first cluster
105 if (DeviceExt
->FatType
== FAT32
)
106 CurrentCluster
= Fcb
->entry
.FirstCluster
107 + Fcb
->entry
.FirstClusterHigh
* 65536;
109 CurrentCluster
= Fcb
->entry
.FirstCluster
;
110 FirstCluster
= CurrentCluster
;
113 * Truncate the read if necessary
115 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
117 if (ReadOffset
>= Fcb
->entry
.FileSize
)
119 return (STATUS_END_OF_FILE
);
121 if ((ReadOffset
+ Length
) > Fcb
->entry
.FileSize
)
123 Length
= Fcb
->entry
.FileSize
- ReadOffset
;
131 * Allocate a buffer to hold partial clusters
133 Temp
= ExAllocatePool (NonPagedPool
, DeviceExt
->BytesPerCluster
);
135 return STATUS_UNSUCCESSFUL
;
138 * Find the cluster to start the read from
139 * FIXME: Optimize by remembering the last cluster read and using if
142 if (FirstCluster
== 1)
144 /* root of FAT16 or FAT12 */
145 CurrentCluster
= DeviceExt
->rootStart
+ ReadOffset
146 / (DeviceExt
->BytesPerCluster
) * DeviceExt
->Boot
->SectorsPerCluster
;
149 for (FileOffset
= 0; FileOffset
< ReadOffset
/ DeviceExt
->BytesPerCluster
;
152 CurrentCluster
= GetNextCluster (DeviceExt
, CurrentCluster
);
156 * If the read doesn't begin on a cluster boundary then read a full
157 * cluster and copy it.
159 if ((ReadOffset
% DeviceExt
->BytesPerCluster
) != 0)
161 if (FirstCluster
== 1)
163 /* FIXME: Check status */
164 VfatReadSectors (DeviceExt
->StorageDevice
,
166 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
167 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
171 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
172 CurrentCluster
= GetNextCluster (DeviceExt
, CurrentCluster
);
174 TempLength
= min (Length
, DeviceExt
->BytesPerCluster
-
175 (ReadOffset
% DeviceExt
->BytesPerCluster
));
177 memcpy (Buffer
, Temp
+ ReadOffset
% DeviceExt
->BytesPerCluster
,
180 (*LengthRead
) = (*LengthRead
) + TempLength
;
181 Length
= Length
- TempLength
;
182 Buffer
= Buffer
+ TempLength
;
185 while (Length
>= DeviceExt
->BytesPerCluster
)
187 if (FirstCluster
== 1)
189 /* FIXME: Check status */
190 VfatReadSectors (DeviceExt
->StorageDevice
,
192 DeviceExt
->Boot
->SectorsPerCluster
, Buffer
);
193 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
197 VFATLoadCluster (DeviceExt
, Buffer
, CurrentCluster
);
198 CurrentCluster
= GetNextCluster (DeviceExt
, CurrentCluster
);
200 if (CurrentCluster
== 0xffffffff)
203 return (STATUS_SUCCESS
);
206 (*LengthRead
) = (*LengthRead
) + DeviceExt
->BytesPerCluster
;
207 Buffer
= Buffer
+ DeviceExt
->BytesPerCluster
;
208 Length
= Length
- DeviceExt
->BytesPerCluster
;
213 (*LengthRead
) = (*LengthRead
) + Length
;
214 if (FirstCluster
== 1)
216 /* FIXME: Check status */
217 VfatReadSectors (DeviceExt
->StorageDevice
,
219 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
220 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
224 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
225 CurrentCluster
= GetNextCluster (DeviceExt
, CurrentCluster
);
227 memcpy (Buffer
, Temp
, Length
);
230 return (STATUS_SUCCESS
);
234 VfatRawReadCluster (PDEVICE_EXTENSION DeviceExt
,
236 PULONG CurrentCluster
,
239 DPRINT("VfatRawReadCluster() *CurrentCluster 0x%x\n", *CurrentCluster
);
240 if (FirstCluster
== 1)
242 /* FIXME: Check status */
243 VfatReadSectors (DeviceExt
->StorageDevice
,
245 DeviceExt
->Boot
->SectorsPerCluster
,
247 (*CurrentCluster
) += DeviceExt
->Boot
->SectorsPerCluster
;
251 VFATLoadCluster (DeviceExt
, Destination
, (*CurrentCluster
));
252 (*CurrentCluster
) = GetNextCluster (DeviceExt
, (*CurrentCluster
));
254 DPRINT("VfatRawReadCluster() finished *CurrentCluster 0x%x\n",
256 return(STATUS_SUCCESS
);
260 VfatReadCluster(PDEVICE_EXTENSION DeviceExt
,
264 PULONG CurrentCluster
,
268 ULONG BytesPerCluster
;
271 PCACHE_SEGMENT CacheSeg
;
274 BytesPerCluster
= DeviceExt
->Boot
->SectorsPerCluster
* BLOCKSIZE
;
276 if (BytesPerCluster
>= PAGESIZE
)
279 * In this case the size of a cache segment is the same as a cluster
281 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
286 if (!NT_SUCCESS(Status
))
293 * If necessary read the cluster from the disk
295 Status
= VfatRawReadCluster(DeviceExt
, FirstCluster
, CurrentCluster
,
297 if (!NT_SUCCESS(Status
))
299 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
306 * Otherwise go on to the next cluster
308 if (FirstCluster
== 1)
310 (*CurrentCluster
) += DeviceExt
->Boot
->SectorsPerCluster
;
314 (*CurrentCluster
) = GetNextCluster(DeviceExt
, (*CurrentCluster
));
318 * Copy the data from the cache to the caller
320 memcpy(Destination
, BaseAddress
, BytesPerCluster
);
321 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
327 ULONG InternalOffset
;
330 * Otherwise we read a page of clusters together
332 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
337 if (!NT_SUCCESS(Status
))
343 * If necessary read all the data for the page, unfortunately the
344 * file length may not be page aligned in which case the page will
345 * only be partially filled.
346 * FIXME: So zero fill the rest?
350 for (i
= 0; i
< (PAGESIZE
/ BytesPerCluster
); i
++)
352 Status
= VfatRawReadCluster(DeviceExt
,
355 BaseAddress
+ (i
* BytesPerCluster
));
356 if (!NT_SUCCESS(Status
))
358 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
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
);
453 return STATUS_UNSUCCESSFUL
;
456 * Find the cluster to start the read from
457 * FIXME: Optimize by remembering the last cluster read and using if
460 CurrentCluster
= OffsetToCluster(DeviceExt
, FirstCluster
, ReadOffset
);
463 * If the read doesn't begin on a cluster boundary then read a full
464 * cluster and copy it.
466 if ((ReadOffset
% ChunkSize
) != 0)
468 VfatReadCluster(DeviceExt
, Fcb
,
469 ROUND_DOWN(ReadOffset
, ChunkSize
),
470 FirstCluster
, &CurrentCluster
, Temp
, 1);
471 TempLength
= min (Length
, ChunkSize
- (ReadOffset
% ChunkSize
));
473 memcpy (Buffer
, Temp
+ ReadOffset
% ChunkSize
, TempLength
);
475 (*LengthRead
) = (*LengthRead
) + TempLength
;
476 Length
= Length
- TempLength
;
477 Buffer
= Buffer
+ TempLength
;
478 ReadOffset
= ReadOffset
+ TempLength
;
481 while (Length
>= ChunkSize
)
483 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
484 FirstCluster
, &CurrentCluster
, Buffer
, 1);
485 if (CurrentCluster
== 0xffffffff)
488 return (STATUS_SUCCESS
);
491 (*LengthRead
) = (*LengthRead
) + ChunkSize
;
492 Buffer
= Buffer
+ ChunkSize
;
493 Length
= Length
- ChunkSize
;
494 ReadOffset
= ReadOffset
+ ChunkSize
;
499 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
500 FirstCluster
, &CurrentCluster
, Temp
, 1);
501 (*LengthRead
) = (*LengthRead
) + Length
;
502 memcpy (Buffer
, Temp
, Length
);
505 return (STATUS_SUCCESS
);
510 VfatWriteFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
511 PVOID Buffer
, ULONG Length
, ULONG WriteOffset
)
513 * FUNCTION: Writes data to file
516 ULONG CurrentCluster
;
522 ULONG TempLength
, Length2
= Length
;
523 LARGE_INTEGER SystemTime
, LocalTime
;
525 DPRINT1 ("FsdWriteFile(FileObject %x, Buffer %x, Length %x, "
526 "WriteOffset %x\n", FileObject
, Buffer
, Length
, WriteOffset
);
528 /* Locate the first cluster of the file */
530 pCcb
= (PVFATCCB
) (FileObject
->FsContext2
);
534 if (DeviceExt
->FatType
== FAT32
)
537 Fcb
->entry
.FirstCluster
+ Fcb
->entry
.FirstClusterHigh
* 65536;
541 CurrentCluster
= Fcb
->entry
.FirstCluster
;
543 FirstCluster
= CurrentCluster
;
545 /* Allocate a buffer to hold 1 cluster of data */
546 Temp
= ExAllocatePool (NonPagedPool
, DeviceExt
->BytesPerCluster
);
549 /* Find the cluster according to the offset in the file */
550 if (CurrentCluster
== 1)
552 CurrentCluster
= DeviceExt
->rootStart
+ WriteOffset
553 / DeviceExt
->BytesPerCluster
* DeviceExt
->Boot
->SectorsPerCluster
;
557 if (CurrentCluster
== 0)
562 CurrentCluster
= GetNextWriteCluster (DeviceExt
, 0);
563 if (DeviceExt
->FatType
== FAT32
)
565 Fcb
->entry
.FirstClusterHigh
= CurrentCluster
>> 16;
566 Fcb
->entry
.FirstCluster
= CurrentCluster
;
569 Fcb
->entry
.FirstCluster
= CurrentCluster
;
574 FileOffset
< WriteOffset
/ DeviceExt
->BytesPerCluster
;
578 GetNextWriteCluster (DeviceExt
, CurrentCluster
);
585 * If the offset in the cluster doesn't fall on the cluster boundary
586 * then we have to write only from the specified offset
589 if ((WriteOffset
% DeviceExt
->BytesPerCluster
) != 0)
592 TempLength
= min (Length
, DeviceExt
->BytesPerCluster
-
593 (WriteOffset
% DeviceExt
->BytesPerCluster
));
594 /* Read in the existing cluster data */
595 if (FirstCluster
== 1)
597 /* FIXME: Check status */
598 VfatReadSectors (DeviceExt
->StorageDevice
,
600 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
604 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
607 /* Overwrite the last parts of the data as necessary */
608 memcpy (Temp
+ (WriteOffset
% DeviceExt
->BytesPerCluster
),
611 /* Write the cluster back */
612 Length2
-= TempLength
;
613 if (FirstCluster
== 1)
615 VFATWriteSectors (DeviceExt
->StorageDevice
,
617 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
618 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
622 VFATWriteCluster (DeviceExt
, Temp
, CurrentCluster
);
624 CurrentCluster
= GetNextWriteCluster (DeviceExt
, CurrentCluster
);
626 Buffer
= Buffer
+ TempLength
;
630 /* Write the buffer in chunks of 1 cluster */
632 while (Length2
>= DeviceExt
->BytesPerCluster
)
635 if (CurrentCluster
== 0)
638 return (STATUS_UNSUCCESSFUL
);
640 Length2
-= DeviceExt
->BytesPerCluster
;
641 if (FirstCluster
== 1)
643 VFATWriteSectors (DeviceExt
->StorageDevice
,
645 DeviceExt
->Boot
->SectorsPerCluster
, Buffer
);
646 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
650 VFATWriteCluster (DeviceExt
, Buffer
, CurrentCluster
);
652 CurrentCluster
= GetNextWriteCluster (DeviceExt
, CurrentCluster
);
654 Buffer
= Buffer
+ DeviceExt
->BytesPerCluster
;
658 /* Write the remainder */
663 if (CurrentCluster
== 0)
666 return (STATUS_UNSUCCESSFUL
);
669 /* Read in the existing cluster data */
670 if (FirstCluster
== 1)
672 /* FIXME: Check status */
673 VfatReadSectors (DeviceExt
->StorageDevice
,
675 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
679 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
681 memcpy (Temp
, Buffer
, Length2
);
683 if (FirstCluster
== 1)
685 VFATWriteSectors (DeviceExt
->StorageDevice
,
687 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
691 VFATWriteCluster (DeviceExt
, Temp
, CurrentCluster
);
698 /* set dates and times */
699 KeQuerySystemTime (&SystemTime
);
700 ExSystemTimeToLocalTime (&SystemTime
, &LocalTime
);
701 FsdFileTimeToDosDateTime ((TIME
*) & LocalTime
,
702 &Fcb
->entry
.UpdateDate
, &Fcb
->entry
.UpdateTime
);
703 Fcb
->entry
.AccessDate
= Fcb
->entry
.UpdateDate
;
705 if (Fcb
->entry
.FileSize
< WriteOffset
+ Length
706 && !(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
708 Fcb
->entry
.FileSize
= WriteOffset
+ Length
;
710 * update entry in directory
712 updEntry (DeviceExt
, FileObject
);
716 return (STATUS_SUCCESS
);
721 VfatWrite (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
723 * FUNCTION: Write to a file
729 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation (Irp
);
730 PFILE_OBJECT FileObject
= Stack
->FileObject
;
731 PDEVICE_EXTENSION DeviceExt
= DeviceObject
->DeviceExtension
;
734 DPRINT ("VfatWrite(DeviceObject %x Irp %x)\n", DeviceObject
, Irp
);
736 Length
= Stack
->Parameters
.Write
.Length
;
737 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
738 Offset
= Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
;
740 /* Status = VfatWriteFile (DeviceExt, FileObject, Buffer, Length, Offset); */
741 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
743 Irp
->IoStatus
.Status
= Status
;
744 Irp
->IoStatus
.Information
= Length
;
745 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
751 VfatRead (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
753 * FUNCTION: Read from a file
759 PIO_STACK_LOCATION Stack
;
760 PFILE_OBJECT FileObject
;
761 PDEVICE_EXTENSION DeviceExt
;
766 DPRINT ("VfatRead(DeviceObject %x, Irp %x)\n", DeviceObject
, Irp
);
768 /* Precondition / Initialization */
769 assert (Irp
!= NULL
);
770 Stack
= IoGetCurrentIrpStackLocation (Irp
);
771 assert (Stack
!= NULL
);
772 FileObject
= Stack
->FileObject
;
773 assert (FileObject
!= NULL
);
774 DeviceExt
= DeviceObject
->DeviceExtension
;
775 assert (DeviceExt
!= NULL
);
777 Length
= Stack
->Parameters
.Read
.Length
;
778 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
779 Offset
= Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
;
781 /* fail if file is a directory */
782 Fcb
= ((PVFATCCB
) (FileObject
->FsContext2
))->pFcb
;
783 if (Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
785 Status
= STATUS_FILE_IS_A_DIRECTORY
;
789 Status
= VfatReadFile (DeviceExt
,
790 FileObject
, Buffer
, Length
, Offset
, &LengthRead
);
793 Irp
->IoStatus
.Status
= Status
;
794 Irp
->IoStatus
.Information
= LengthRead
;
795 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);