2 /* $Id: rw.c,v 1.16 2001/01/13 12:40:21 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
,
274 ULONG InternalOffset
,
275 ULONG InternalLength
)
277 ULONG BytesPerCluster
;
280 PCACHE_SEGMENT CacheSeg
;
283 BytesPerCluster
= DeviceExt
->Boot
->SectorsPerCluster
* BLOCKSIZE
;
285 if (BytesPerCluster
>= PAGESIZE
)
288 * In this case the size of a cache segment is the same as a cluster
290 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
295 if (!NT_SUCCESS(Status
))
302 * If necessary read the cluster from the disk
304 Status
= VfatRawReadCluster(DeviceExt
, FirstCluster
, CurrentCluster
,
306 if (!NT_SUCCESS(Status
))
308 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
313 * Copy the data from the cache to the caller
315 memcpy(Destination
, BaseAddress
+ InternalOffset
, InternalLength
);
316 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
318 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
);
319 if (!NT_SUCCESS(Status
))
328 ULONG InternalOffset
;
331 * Otherwise we read a page of clusters together
333 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
338 if (!NT_SUCCESS(Status
))
344 * If necessary read all the data for the page, unfortunately the
345 * file length may not be page aligned in which case the page will
346 * only be partially filled.
347 * FIXME: So zero fill the rest?
351 for (i
= 0; i
< (PAGESIZE
/ BytesPerCluster
); i
++)
353 Status
= VfatRawReadCluster(DeviceExt
,
356 BaseAddress
+ (i
* BytesPerCluster
));
357 if (!NT_SUCCESS(Status
))
359 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
362 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
);
363 if ((*CurrentCluster
) == 0xFFFFFFFF)
372 * Otherwise just move onto the next cluster
374 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
376 NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
);
377 if ((*CurrentCluster
) == 0xFFFFFFFF)
384 * Copy the data from the cache to the caller
386 memcpy(Destination
, BaseAddress
+ InternalOffset
, InternalLength
);
387 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
389 return(STATUS_SUCCESS
);
393 VfatReadFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
394 PVOID Buffer
, ULONG Length
, ULONG ReadOffset
,
397 * FUNCTION: Reads data from a file
400 ULONG CurrentCluster
;
408 assert (DeviceExt
!= NULL
);
409 assert (DeviceExt
->BytesPerCluster
!= 0);
410 assert (FileObject
!= NULL
);
411 assert (FileObject
->FsContext
!= NULL
);
413 DPRINT ("FsdReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
414 "Length %d, ReadOffset 0x%x)\n", DeviceExt
, FileObject
, Buffer
,
417 Fcb
= ((PVFATCCB
)FileObject
->FsContext2
)->pFcb
;
420 * Find the first cluster
422 if (DeviceExt
->FatType
== FAT32
)
423 CurrentCluster
= Fcb
->entry
.FirstCluster
424 + Fcb
->entry
.FirstClusterHigh
* 65536;
426 CurrentCluster
= Fcb
->entry
.FirstCluster
;
427 FirstCluster
= CurrentCluster
;
430 * Truncate the read if necessary
432 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
434 if (ReadOffset
>= Fcb
->entry
.FileSize
)
436 return (STATUS_END_OF_FILE
);
438 if ((ReadOffset
+ Length
) > Fcb
->entry
.FileSize
)
440 Length
= Fcb
->entry
.FileSize
- ReadOffset
;
445 ChunkSize
= max(DeviceExt
->BytesPerCluster
, PAGESIZE
);
450 * Find the cluster to start the read from
451 * FIXME: Optimize by remembering the last cluster read and using if
454 Status
= OffsetToCluster(DeviceExt
, FirstCluster
, ReadOffset
,
456 if (!NT_SUCCESS(Status
))
462 * If the read doesn't begin on a cluster boundary then read a full
463 * cluster and copy it.
465 if ((ReadOffset
% ChunkSize
) != 0)
467 TempLength
= min (Length
, ChunkSize
- (ReadOffset
% ChunkSize
));
468 VfatReadCluster(DeviceExt
, Fcb
,
469 ROUND_DOWN(ReadOffset
, ChunkSize
),
470 FirstCluster
, &CurrentCluster
, Buffer
, 1,
471 ReadOffset
% ChunkSize
, TempLength
);
473 (*LengthRead
) = (*LengthRead
) + TempLength
;
474 Length
= Length
- TempLength
;
475 Buffer
= Buffer
+ TempLength
;
476 ReadOffset
= ReadOffset
+ TempLength
;
479 while (Length
>= ChunkSize
)
481 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
482 FirstCluster
, &CurrentCluster
, Buffer
, 1, 0, ChunkSize
);
483 if (CurrentCluster
== 0xffffffff)
485 return (STATUS_SUCCESS
);
488 (*LengthRead
) = (*LengthRead
) + ChunkSize
;
489 Buffer
= Buffer
+ ChunkSize
;
490 Length
= Length
- ChunkSize
;
491 ReadOffset
= ReadOffset
+ ChunkSize
;
495 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
496 FirstCluster
, &CurrentCluster
, Buffer
, 1, 0, Length
);
497 (*LengthRead
) = (*LengthRead
) + Length
;
499 return (STATUS_SUCCESS
);
504 VfatWriteFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
505 PVOID Buffer
, ULONG Length
, ULONG WriteOffset
)
507 * FUNCTION: Writes data to file
510 ULONG CurrentCluster
;
516 ULONG TempLength
, Length2
= Length
;
517 LARGE_INTEGER SystemTime
, LocalTime
;
519 DPRINT1 ("FsdWriteFile(FileObject %x, Buffer %x, Length %x, "
520 "WriteOffset %x\n", FileObject
, Buffer
, Length
, WriteOffset
);
522 /* Locate the first cluster of the file */
524 pCcb
= (PVFATCCB
) (FileObject
->FsContext2
);
528 if (DeviceExt
->FatType
== FAT32
)
531 Fcb
->entry
.FirstCluster
+ Fcb
->entry
.FirstClusterHigh
* 65536;
535 CurrentCluster
= Fcb
->entry
.FirstCluster
;
537 FirstCluster
= CurrentCluster
;
539 /* Allocate a buffer to hold 1 cluster of data */
540 Temp
= ExAllocatePool (NonPagedPool
, DeviceExt
->BytesPerCluster
);
543 /* Find the cluster according to the offset in the file */
544 if (CurrentCluster
== 1)
546 CurrentCluster
= DeviceExt
->rootStart
+ WriteOffset
547 / DeviceExt
->BytesPerCluster
* DeviceExt
->Boot
->SectorsPerCluster
;
551 if (CurrentCluster
== 0)
556 CurrentCluster
= GetNextWriteCluster (DeviceExt
, 0);
557 if (DeviceExt
->FatType
== FAT32
)
559 Fcb
->entry
.FirstClusterHigh
= CurrentCluster
>> 16;
560 Fcb
->entry
.FirstCluster
= CurrentCluster
;
563 Fcb
->entry
.FirstCluster
= CurrentCluster
;
568 FileOffset
< WriteOffset
/ DeviceExt
->BytesPerCluster
;
572 GetNextWriteCluster (DeviceExt
, CurrentCluster
);
579 * If the offset in the cluster doesn't fall on the cluster boundary
580 * then we have to write only from the specified offset
583 if ((WriteOffset
% DeviceExt
->BytesPerCluster
) != 0)
586 TempLength
= min (Length
, DeviceExt
->BytesPerCluster
-
587 (WriteOffset
% DeviceExt
->BytesPerCluster
));
588 /* Read in the existing cluster data */
589 if (FirstCluster
== 1)
591 /* FIXME: Check status */
592 VfatReadSectors (DeviceExt
->StorageDevice
,
594 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
598 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
601 /* Overwrite the last parts of the data as necessary */
602 memcpy (Temp
+ (WriteOffset
% DeviceExt
->BytesPerCluster
),
605 /* Write the cluster back */
606 Length2
-= TempLength
;
607 if (FirstCluster
== 1)
609 VFATWriteSectors (DeviceExt
->StorageDevice
,
611 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
612 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
616 VFATWriteCluster (DeviceExt
, Temp
, CurrentCluster
);
618 CurrentCluster
= GetNextWriteCluster (DeviceExt
, CurrentCluster
);
620 Buffer
= Buffer
+ TempLength
;
624 /* Write the buffer in chunks of 1 cluster */
626 while (Length2
>= DeviceExt
->BytesPerCluster
)
629 if (CurrentCluster
== 0)
632 return (STATUS_UNSUCCESSFUL
);
634 Length2
-= DeviceExt
->BytesPerCluster
;
635 if (FirstCluster
== 1)
637 VFATWriteSectors (DeviceExt
->StorageDevice
,
639 DeviceExt
->Boot
->SectorsPerCluster
, Buffer
);
640 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
644 VFATWriteCluster (DeviceExt
, Buffer
, CurrentCluster
);
646 CurrentCluster
= GetNextWriteCluster (DeviceExt
, CurrentCluster
);
648 Buffer
= Buffer
+ DeviceExt
->BytesPerCluster
;
652 /* Write the remainder */
657 if (CurrentCluster
== 0)
660 return (STATUS_UNSUCCESSFUL
);
663 /* Read in the existing cluster data */
664 if (FirstCluster
== 1)
666 /* FIXME: Check status */
667 VfatReadSectors (DeviceExt
->StorageDevice
,
669 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
673 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
675 memcpy (Temp
, Buffer
, Length2
);
677 if (FirstCluster
== 1)
679 VFATWriteSectors (DeviceExt
->StorageDevice
,
681 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
685 VFATWriteCluster (DeviceExt
, Temp
, CurrentCluster
);
692 /* set dates and times */
693 KeQuerySystemTime (&SystemTime
);
694 ExSystemTimeToLocalTime (&SystemTime
, &LocalTime
);
695 FsdFileTimeToDosDateTime ((TIME
*) & LocalTime
,
696 &Fcb
->entry
.UpdateDate
, &Fcb
->entry
.UpdateTime
);
697 Fcb
->entry
.AccessDate
= Fcb
->entry
.UpdateDate
;
699 if (Fcb
->entry
.FileSize
< WriteOffset
+ Length
700 && !(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
702 Fcb
->entry
.FileSize
= WriteOffset
+ Length
;
704 * update entry in directory
706 updEntry (DeviceExt
, FileObject
);
710 return (STATUS_SUCCESS
);
715 VfatWrite (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
717 * FUNCTION: Write to a file
723 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation (Irp
);
724 /* PFILE_OBJECT FileObject = Stack->FileObject; */
725 /* PDEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension; */
728 DPRINT ("VfatWrite(DeviceObject %x Irp %x)\n", DeviceObject
, Irp
);
730 Length
= Stack
->Parameters
.Write
.Length
;
731 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
732 Offset
= Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
;
734 /* Status = VfatWriteFile (DeviceExt, FileObject, Buffer, Length, Offset); */
735 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
737 Irp
->IoStatus
.Status
= Status
;
738 Irp
->IoStatus
.Information
= Length
;
739 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
745 VfatRead (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
747 * FUNCTION: Read from a file
753 PIO_STACK_LOCATION Stack
;
754 PFILE_OBJECT FileObject
;
755 PDEVICE_EXTENSION DeviceExt
;
760 DPRINT ("VfatRead(DeviceObject %x, Irp %x)\n", DeviceObject
, Irp
);
762 /* Precondition / Initialization */
763 assert (Irp
!= NULL
);
764 Stack
= IoGetCurrentIrpStackLocation (Irp
);
765 assert (Stack
!= NULL
);
766 FileObject
= Stack
->FileObject
;
767 assert (FileObject
!= NULL
);
768 DeviceExt
= DeviceObject
->DeviceExtension
;
769 assert (DeviceExt
!= NULL
);
771 Length
= Stack
->Parameters
.Read
.Length
;
772 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
773 Offset
= Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
;
775 /* fail if file is a directory */
776 Fcb
= ((PVFATCCB
) (FileObject
->FsContext2
))->pFcb
;
777 if (Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
779 Status
= STATUS_FILE_IS_A_DIRECTORY
;
783 Status
= VfatReadFile (DeviceExt
,
784 FileObject
, Buffer
, Length
, Offset
, &LengthRead
);
787 Irp
->IoStatus
.Status
= Status
;
788 Irp
->IoStatus
.Information
= LengthRead
;
789 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);