1 /* $Id: rw.c,v 1.13 2001/01/01 04:42:12 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 OffsetToCluster(PDEVICE_EXTENSION DeviceExt
,
37 CurrentCluster
= FirstCluster
;
38 if (FirstCluster
== 1)
40 /* root of FAT16 or FAT12 */
41 CurrentCluster
= DeviceExt
->rootStart
+ FileOffset
42 / (DeviceExt
->BytesPerCluster
) * DeviceExt
->Boot
->SectorsPerCluster
;
46 for (i
= 0; i
< FileOffset
/ DeviceExt
->BytesPerCluster
; i
++)
48 CurrentCluster
= GetNextCluster (DeviceExt
, CurrentCluster
);
51 return(CurrentCluster
);
55 VfatReadFileNoCache (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
56 PVOID Buffer
, ULONG Length
, ULONG ReadOffset
,
59 * FUNCTION: Reads data from a file
70 assert (DeviceExt
!= NULL
);
71 assert (DeviceExt
->BytesPerCluster
!= 0);
72 assert (FileObject
!= NULL
);
73 assert (FileObject
->FsContext
!= NULL
);
75 DPRINT ("FsdReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
76 "Length %d, ReadOffset %d)\n", DeviceExt
, FileObject
, Buffer
,
79 Fcb
= ((PVFATCCB
)FileObject
->FsContext2
)->pFcb
;
82 * Find the first cluster
84 if (DeviceExt
->FatType
== FAT32
)
85 CurrentCluster
= Fcb
->entry
.FirstCluster
86 + Fcb
->entry
.FirstClusterHigh
* 65536;
88 CurrentCluster
= Fcb
->entry
.FirstCluster
;
89 FirstCluster
= CurrentCluster
;
92 * Truncate the read if necessary
94 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
96 if (ReadOffset
>= Fcb
->entry
.FileSize
)
98 return (STATUS_END_OF_FILE
);
100 if ((ReadOffset
+ Length
) > Fcb
->entry
.FileSize
)
102 Length
= Fcb
->entry
.FileSize
- ReadOffset
;
110 * Allocate a buffer to hold partial clusters
112 Temp
= ExAllocatePool (NonPagedPool
, DeviceExt
->BytesPerCluster
);
114 return STATUS_UNSUCCESSFUL
;
117 * Find the cluster to start the read from
118 * FIXME: Optimize by remembering the last cluster read and using if
121 if (FirstCluster
== 1)
123 /* root of FAT16 or FAT12 */
124 CurrentCluster
= DeviceExt
->rootStart
+ ReadOffset
125 / (DeviceExt
->BytesPerCluster
) * DeviceExt
->Boot
->SectorsPerCluster
;
128 for (FileOffset
= 0; FileOffset
< ReadOffset
/ DeviceExt
->BytesPerCluster
;
131 CurrentCluster
= GetNextCluster (DeviceExt
, CurrentCluster
);
135 * If the read doesn't begin on a cluster boundary then read a full
136 * cluster and copy it.
138 if ((ReadOffset
% DeviceExt
->BytesPerCluster
) != 0)
140 if (FirstCluster
== 1)
142 VFATReadSectors (DeviceExt
->StorageDevice
,
144 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
145 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
149 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
150 CurrentCluster
= GetNextCluster (DeviceExt
, CurrentCluster
);
152 TempLength
= min (Length
, DeviceExt
->BytesPerCluster
-
153 (ReadOffset
% DeviceExt
->BytesPerCluster
));
155 memcpy (Buffer
, Temp
+ ReadOffset
% DeviceExt
->BytesPerCluster
,
158 (*LengthRead
) = (*LengthRead
) + TempLength
;
159 Length
= Length
- TempLength
;
160 Buffer
= Buffer
+ TempLength
;
163 while (Length
>= DeviceExt
->BytesPerCluster
)
165 if (FirstCluster
== 1)
167 VFATReadSectors (DeviceExt
->StorageDevice
,
169 DeviceExt
->Boot
->SectorsPerCluster
, Buffer
);
170 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
174 VFATLoadCluster (DeviceExt
, Buffer
, CurrentCluster
);
175 CurrentCluster
= GetNextCluster (DeviceExt
, CurrentCluster
);
177 if (CurrentCluster
== 0xffffffff)
180 return (STATUS_SUCCESS
);
183 (*LengthRead
) = (*LengthRead
) + DeviceExt
->BytesPerCluster
;
184 Buffer
= Buffer
+ DeviceExt
->BytesPerCluster
;
185 Length
= Length
- DeviceExt
->BytesPerCluster
;
190 (*LengthRead
) = (*LengthRead
) + Length
;
191 if (FirstCluster
== 1)
193 VFATReadSectors (DeviceExt
->StorageDevice
,
195 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
196 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
200 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
201 CurrentCluster
= GetNextCluster (DeviceExt
, CurrentCluster
);
203 memcpy (Buffer
, Temp
, Length
);
206 return (STATUS_SUCCESS
);
210 VfatRawReadCluster (PDEVICE_EXTENSION DeviceExt
,
212 PULONG CurrentCluster
,
215 if (FirstCluster
== 1)
217 VFATReadSectors (DeviceExt
->StorageDevice
,
219 DeviceExt
->Boot
->SectorsPerCluster
,
221 (*CurrentCluster
) += DeviceExt
->Boot
->SectorsPerCluster
;
225 VFATLoadCluster (DeviceExt
, Destination
, (*CurrentCluster
));
226 (*CurrentCluster
) = GetNextCluster (DeviceExt
, (*CurrentCluster
));
228 return(STATUS_SUCCESS
);
232 VfatReadCluster(PDEVICE_EXTENSION DeviceExt
,
236 PULONG CurrentCluster
,
240 ULONG BytesPerCluster
;
243 PCACHE_SEGMENT CacheSeg
;
246 BytesPerCluster
= DeviceExt
->Boot
->SectorsPerCluster
* BLOCKSIZE
;
248 if (BytesPerCluster
>= PAGESIZE
)
251 * In this case the size of a cache segment is the same as a cluster
253 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
258 if (!NT_SUCCESS(Status
))
265 * If necessary read the cluster from the disk
267 Status
= VfatRawReadCluster(DeviceExt
, FirstCluster
, CurrentCluster
,
269 if (!NT_SUCCESS(Status
))
271 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
278 * Otherwise go on to the next cluster
280 if (FirstCluster
== 1)
282 (*CurrentCluster
) += DeviceExt
->Boot
->SectorsPerCluster
;
286 (*CurrentCluster
) = GetNextCluster(DeviceExt
, (*CurrentCluster
));
290 * Copy the data from the cache to the caller
292 memcpy(Destination
, BaseAddress
, BytesPerCluster
);
293 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
299 ULONG InternalOffset
;
301 ReadOffset
= ROUND_DOWN(StartOffset
, PAGESIZE
);
302 InternalOffset
= StartOffset
% PAGESIZE
;
303 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
308 if (!NT_SUCCESS(Status
))
314 * If necessary read all the data for the page
320 StartCluster
= OffsetToCluster(DeviceExt
, FirstCluster
, ReadOffset
);
322 for (i
= 0; i
< (PAGESIZE
/ BytesPerCluster
); i
++)
324 Status
= VfatRawReadCluster(DeviceExt
,
327 BaseAddress
+ (i
* BytesPerCluster
));
328 if (!NT_SUCCESS(Status
))
330 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
336 * Go on to the next cluster
338 if (FirstCluster
== 1)
340 (*CurrentCluster
) += DeviceExt
->Boot
->SectorsPerCluster
;
344 (*CurrentCluster
) = GetNextCluster(DeviceExt
, (*CurrentCluster
));
347 * Copy the data from the cache to the caller
349 memcpy(Destination
, BaseAddress
+ InternalOffset
, BytesPerCluster
);
350 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
352 return(STATUS_SUCCESS
);
356 VfatReadFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
357 PVOID Buffer
, ULONG Length
, ULONG ReadOffset
,
360 * FUNCTION: Reads data from a file
363 ULONG CurrentCluster
;
371 assert (DeviceExt
!= NULL
);
372 assert (DeviceExt
->BytesPerCluster
!= 0);
373 assert (FileObject
!= NULL
);
374 assert (FileObject
->FsContext
!= NULL
);
376 DPRINT ("FsdReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
377 "Length %d, ReadOffset %d)\n", DeviceExt
, FileObject
, Buffer
,
380 Fcb
= ((PVFATCCB
)FileObject
->FsContext2
)->pFcb
;
383 * Find the first cluster
385 if (DeviceExt
->FatType
== FAT32
)
386 CurrentCluster
= Fcb
->entry
.FirstCluster
387 + Fcb
->entry
.FirstClusterHigh
* 65536;
389 CurrentCluster
= Fcb
->entry
.FirstCluster
;
390 FirstCluster
= CurrentCluster
;
393 * Truncate the read if necessary
395 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
397 if (ReadOffset
>= Fcb
->entry
.FileSize
)
399 return (STATUS_END_OF_FILE
);
401 if ((ReadOffset
+ Length
) > Fcb
->entry
.FileSize
)
403 Length
= Fcb
->entry
.FileSize
- ReadOffset
;
411 * Allocate a buffer to hold partial clusters
413 Temp
= ExAllocatePool (NonPagedPool
, DeviceExt
->BytesPerCluster
);
415 return STATUS_UNSUCCESSFUL
;
418 * Find the cluster to start the read from
419 * FIXME: Optimize by remembering the last cluster read and using if
422 CurrentCluster
= OffsetToCluster(DeviceExt
, FirstCluster
, ReadOffset
);
425 * If the read doesn't begin on a cluster boundary then read a full
426 * cluster and copy it.
428 if ((ReadOffset
% DeviceExt
->BytesPerCluster
) != 0)
430 VfatReadCluster(DeviceExt
, Fcb
,
431 ROUND_DOWN(ReadOffset
, DeviceExt
->BytesPerCluster
),
432 FirstCluster
, &CurrentCluster
, Temp
, 1);
433 TempLength
= min (Length
, DeviceExt
->BytesPerCluster
-
434 (ReadOffset
% DeviceExt
->BytesPerCluster
));
436 memcpy (Buffer
, Temp
+ ReadOffset
% DeviceExt
->BytesPerCluster
,
439 (*LengthRead
) = (*LengthRead
) + TempLength
;
440 Length
= Length
- TempLength
;
441 Buffer
= Buffer
+ TempLength
;
442 ReadOffset
= ReadOffset
+ TempLength
;
445 while (Length
>= DeviceExt
->BytesPerCluster
)
447 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
448 FirstCluster
, &CurrentCluster
, Buffer
, 1);
449 if (CurrentCluster
== 0xffffffff)
452 return (STATUS_SUCCESS
);
455 (*LengthRead
) = (*LengthRead
) + DeviceExt
->BytesPerCluster
;
456 Buffer
= Buffer
+ DeviceExt
->BytesPerCluster
;
457 Length
= Length
- DeviceExt
->BytesPerCluster
;
458 ReadOffset
= ReadOffset
+ DeviceExt
->BytesPerCluster
;
463 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
464 FirstCluster
, &CurrentCluster
, Temp
, 1);
465 (*LengthRead
) = (*LengthRead
) + Length
;
466 memcpy (Buffer
, Temp
, Length
);
469 return (STATUS_SUCCESS
);
474 VfatWriteFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
475 PVOID Buffer
, ULONG Length
, ULONG WriteOffset
)
477 * FUNCTION: Writes data to file
480 ULONG CurrentCluster
;
486 ULONG TempLength
, Length2
= Length
;
487 LARGE_INTEGER SystemTime
, LocalTime
;
489 DPRINT1 ("FsdWriteFile(FileObject %x, Buffer %x, Length %x, "
490 "WriteOffset %x\n", FileObject
, Buffer
, Length
, WriteOffset
);
492 /* Locate the first cluster of the file */
494 pCcb
= (PVFATCCB
) (FileObject
->FsContext2
);
498 if (DeviceExt
->FatType
== FAT32
)
501 Fcb
->entry
.FirstCluster
+ Fcb
->entry
.FirstClusterHigh
* 65536;
505 CurrentCluster
= Fcb
->entry
.FirstCluster
;
507 FirstCluster
= CurrentCluster
;
509 /* Allocate a buffer to hold 1 cluster of data */
510 Temp
= ExAllocatePool (NonPagedPool
, DeviceExt
->BytesPerCluster
);
513 /* Find the cluster according to the offset in the file */
514 if (CurrentCluster
== 1)
516 CurrentCluster
= DeviceExt
->rootStart
+ WriteOffset
517 / DeviceExt
->BytesPerCluster
* DeviceExt
->Boot
->SectorsPerCluster
;
521 if (CurrentCluster
== 0)
526 CurrentCluster
= GetNextWriteCluster (DeviceExt
, 0);
527 if (DeviceExt
->FatType
== FAT32
)
529 Fcb
->entry
.FirstClusterHigh
= CurrentCluster
>> 16;
530 Fcb
->entry
.FirstCluster
= CurrentCluster
;
533 Fcb
->entry
.FirstCluster
= CurrentCluster
;
538 FileOffset
< WriteOffset
/ DeviceExt
->BytesPerCluster
;
542 GetNextWriteCluster (DeviceExt
, CurrentCluster
);
549 * If the offset in the cluster doesn't fall on the cluster boundary
550 * then we have to write only from the specified offset
553 if ((WriteOffset
% DeviceExt
->BytesPerCluster
) != 0)
556 TempLength
= min (Length
, DeviceExt
->BytesPerCluster
-
557 (WriteOffset
% DeviceExt
->BytesPerCluster
));
558 /* Read in the existing cluster data */
559 if (FirstCluster
== 1)
561 VFATReadSectors (DeviceExt
->StorageDevice
,
563 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
567 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
570 /* Overwrite the last parts of the data as necessary */
571 memcpy (Temp
+ (WriteOffset
% DeviceExt
->BytesPerCluster
),
574 /* Write the cluster back */
575 Length2
-= TempLength
;
576 if (FirstCluster
== 1)
578 VFATWriteSectors (DeviceExt
->StorageDevice
,
580 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
581 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
585 VFATWriteCluster (DeviceExt
, Temp
, CurrentCluster
);
587 CurrentCluster
= GetNextWriteCluster (DeviceExt
, CurrentCluster
);
589 Buffer
= Buffer
+ TempLength
;
593 /* Write the buffer in chunks of 1 cluster */
595 while (Length2
>= DeviceExt
->BytesPerCluster
)
598 if (CurrentCluster
== 0)
601 return (STATUS_UNSUCCESSFUL
);
603 Length2
-= DeviceExt
->BytesPerCluster
;
604 if (FirstCluster
== 1)
606 VFATWriteSectors (DeviceExt
->StorageDevice
,
608 DeviceExt
->Boot
->SectorsPerCluster
, Buffer
);
609 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
613 VFATWriteCluster (DeviceExt
, Buffer
, CurrentCluster
);
615 CurrentCluster
= GetNextWriteCluster (DeviceExt
, CurrentCluster
);
617 Buffer
= Buffer
+ DeviceExt
->BytesPerCluster
;
621 /* Write the remainder */
626 if (CurrentCluster
== 0)
629 return (STATUS_UNSUCCESSFUL
);
632 /* Read in the existing cluster data */
633 if (FirstCluster
== 1)
635 VFATReadSectors (DeviceExt
->StorageDevice
,
637 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
641 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
643 memcpy (Temp
, Buffer
, Length2
);
645 if (FirstCluster
== 1)
647 VFATWriteSectors (DeviceExt
->StorageDevice
,
649 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
653 VFATWriteCluster (DeviceExt
, Temp
, CurrentCluster
);
660 /* set dates and times */
661 KeQuerySystemTime (&SystemTime
);
662 ExSystemTimeToLocalTime (&SystemTime
, &LocalTime
);
663 FsdFileTimeToDosDateTime ((TIME
*) & LocalTime
,
664 &Fcb
->entry
.UpdateDate
, &Fcb
->entry
.UpdateTime
);
665 Fcb
->entry
.AccessDate
= Fcb
->entry
.UpdateDate
;
667 if (Fcb
->entry
.FileSize
< WriteOffset
+ Length
668 && !(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
670 Fcb
->entry
.FileSize
= WriteOffset
+ Length
;
672 * update entry in directory
674 updEntry (DeviceExt
, FileObject
);
678 return (STATUS_SUCCESS
);
683 VfatWrite (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
685 * FUNCTION: Write to a file
691 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation (Irp
);
692 PFILE_OBJECT FileObject
= Stack
->FileObject
;
693 PDEVICE_EXTENSION DeviceExt
= DeviceObject
->DeviceExtension
;
696 DPRINT ("VfatWrite(DeviceObject %x Irp %x)\n", DeviceObject
, Irp
);
698 Length
= Stack
->Parameters
.Write
.Length
;
699 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
700 Offset
= Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
;
702 /* Status = VfatWriteFile (DeviceExt, FileObject, Buffer, Length, Offset); */
703 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
705 Irp
->IoStatus
.Status
= Status
;
706 Irp
->IoStatus
.Information
= Length
;
707 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
713 VfatRead (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
715 * FUNCTION: Read from a file
721 PIO_STACK_LOCATION Stack
;
722 PFILE_OBJECT FileObject
;
723 PDEVICE_EXTENSION DeviceExt
;
728 DPRINT ("VfatRead(DeviceObject %x, Irp %x)\n", DeviceObject
, Irp
);
730 /* Precondition / Initialization */
731 assert (Irp
!= NULL
);
732 Stack
= IoGetCurrentIrpStackLocation (Irp
);
733 assert (Stack
!= NULL
);
734 FileObject
= Stack
->FileObject
;
735 assert (FileObject
!= NULL
);
736 DeviceExt
= DeviceObject
->DeviceExtension
;
737 assert (DeviceExt
!= NULL
);
739 Length
= Stack
->Parameters
.Read
.Length
;
740 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
741 Offset
= Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
;
743 /* fail if file is a directory */
744 Fcb
= ((PVFATCCB
) (FileObject
->FsContext2
))->pFcb
;
745 if (Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
747 Status
= STATUS_FILE_IS_A_DIRECTORY
;
751 Status
= VfatReadFile (DeviceExt
,
752 FileObject
, Buffer
, Length
, Offset
, &LengthRead
);
755 Irp
->IoStatus
.Status
= Status
;
756 Irp
->IoStatus
.Information
= LengthRead
;
757 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);