2 /* $Id: rw.c,v 1.17 2001/01/13 18:38:09 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 VfatRawReadCluster (PDEVICE_EXTENSION DeviceExt
,
85 PULONG CurrentCluster
,
90 if (FirstCluster
== 1)
92 Status
= VfatReadSectors (DeviceExt
->StorageDevice
,
94 DeviceExt
->Boot
->SectorsPerCluster
,
100 VFATLoadCluster (DeviceExt
, Destination
, (*CurrentCluster
));
101 Status
= STATUS_SUCCESS
;
107 VfatReadCluster(PDEVICE_EXTENSION DeviceExt
,
111 PULONG CurrentCluster
,
114 ULONG InternalOffset
,
115 ULONG InternalLength
)
117 ULONG BytesPerCluster
;
120 PCACHE_SEGMENT CacheSeg
;
123 BytesPerCluster
= DeviceExt
->Boot
->SectorsPerCluster
* BLOCKSIZE
;
125 if (BytesPerCluster
>= PAGESIZE
)
128 * In this case the size of a cache segment is the same as a cluster
133 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
138 if (!NT_SUCCESS(Status
))
150 * If necessary read the cluster from the disk
152 Status
= VfatRawReadCluster(DeviceExt
, FirstCluster
, CurrentCluster
,
154 if (!NT_SUCCESS(Status
))
158 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
164 * Copy the data from the cache to the caller
166 memcpy(Destination
, BaseAddress
+ InternalOffset
, InternalLength
);
169 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
172 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
);
173 if (!NT_SUCCESS(Status
))
183 * Otherwise we read a page of clusters together
188 Status
= CcRequestCacheSegment(Fcb
->RFCB
.Bcb
,
193 if (!NT_SUCCESS(Status
))
204 * If necessary read all the data for the page, unfortunately the
205 * file length may not be page aligned in which case the page will
206 * only be partially filled.
207 * FIXME: So zero fill the rest?
211 for (i
= 0; i
< (PAGESIZE
/ BytesPerCluster
); i
++)
213 Status
= VfatRawReadCluster(DeviceExt
,
216 BaseAddress
+ (i
* BytesPerCluster
));
217 if (!NT_SUCCESS(Status
))
221 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, FALSE
);
225 Status
= NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
);
226 if ((*CurrentCluster
) == 0xFFFFFFFF)
235 * Otherwise just move onto the next cluster
237 for (i
= 0; i
< (PAGESIZE
/ DeviceExt
->BytesPerCluster
); i
++)
239 NextCluster(DeviceExt
, FirstCluster
, CurrentCluster
);
240 if ((*CurrentCluster
) == 0xFFFFFFFF)
247 * Copy the data from the cache to the caller
249 memcpy(Destination
, BaseAddress
+ InternalOffset
, InternalLength
);
252 CcReleaseCacheSegment(Fcb
->RFCB
.Bcb
, CacheSeg
, TRUE
);
255 return(STATUS_SUCCESS
);
259 VfatReadFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
260 PVOID Buffer
, ULONG Length
, ULONG ReadOffset
,
261 PULONG LengthRead
, ULONG NoCache
)
263 * FUNCTION: Reads data from a file
266 ULONG CurrentCluster
;
274 assert (DeviceExt
!= NULL
);
275 assert (DeviceExt
->BytesPerCluster
!= 0);
276 assert (FileObject
!= NULL
);
277 assert (FileObject
->FsContext
!= NULL
);
279 DPRINT ("VfatReadFile(DeviceExt %x, FileObject %x, Buffer %x, "
280 "Length %d, ReadOffset 0x%x)\n", DeviceExt
, FileObject
, Buffer
,
283 Fcb
= ((PVFATCCB
)FileObject
->FsContext2
)->pFcb
;
286 * Find the first cluster
288 if (DeviceExt
->FatType
== FAT32
)
289 CurrentCluster
= Fcb
->entry
.FirstCluster
290 + Fcb
->entry
.FirstClusterHigh
* 65536;
292 CurrentCluster
= Fcb
->entry
.FirstCluster
;
293 FirstCluster
= CurrentCluster
;
296 * Truncate the read if necessary
298 if (!(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
300 if (ReadOffset
>= Fcb
->entry
.FileSize
)
302 return (STATUS_END_OF_FILE
);
304 if ((ReadOffset
+ Length
) > Fcb
->entry
.FileSize
)
306 Length
= Fcb
->entry
.FileSize
- ReadOffset
;
310 if (DeviceExt
->BytesPerCluster
>= PAGESIZE
)
312 ChunkSize
= DeviceExt
->BytesPerCluster
;
316 ChunkSize
= PAGESIZE
;
322 * Find the cluster to start the read from
323 * FIXME: Optimize by remembering the last cluster read and using if
326 Status
= OffsetToCluster(DeviceExt
, FirstCluster
, ReadOffset
,
328 if (!NT_SUCCESS(Status
))
334 * If the read doesn't begin on a cluster boundary then read a full
335 * cluster and copy it.
337 if ((ReadOffset
% ChunkSize
) != 0)
339 TempLength
= min (Length
, ChunkSize
- (ReadOffset
% ChunkSize
));
340 VfatReadCluster(DeviceExt
, Fcb
,
341 ROUND_DOWN(ReadOffset
, ChunkSize
),
342 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
,
343 ReadOffset
% ChunkSize
, TempLength
);
345 (*LengthRead
) = (*LengthRead
) + TempLength
;
346 Length
= Length
- TempLength
;
347 Buffer
= Buffer
+ TempLength
;
348 ReadOffset
= ReadOffset
+ TempLength
;
351 while (Length
>= ChunkSize
)
353 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
354 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
, 0,
356 if (CurrentCluster
== 0xffffffff)
358 return (STATUS_SUCCESS
);
361 (*LengthRead
) = (*LengthRead
) + ChunkSize
;
362 Buffer
= Buffer
+ ChunkSize
;
363 Length
= Length
- ChunkSize
;
364 ReadOffset
= ReadOffset
+ ChunkSize
;
368 VfatReadCluster(DeviceExt
, Fcb
, ReadOffset
,
369 FirstCluster
, &CurrentCluster
, Buffer
, NoCache
, 0,
371 (*LengthRead
) = (*LengthRead
) + Length
;
373 return (STATUS_SUCCESS
);
377 VfatWriteFile (PDEVICE_EXTENSION DeviceExt
, PFILE_OBJECT FileObject
,
378 PVOID Buffer
, ULONG Length
, ULONG WriteOffset
)
380 * FUNCTION: Writes data to file
383 ULONG CurrentCluster
;
389 ULONG TempLength
, Length2
= Length
;
390 LARGE_INTEGER SystemTime
, LocalTime
;
392 DPRINT1 ("VfatWriteFile(FileObject %x, Buffer %x, Length %x, "
393 "WriteOffset %x\n", FileObject
, Buffer
, Length
, WriteOffset
);
395 /* Locate the first cluster of the file */
397 pCcb
= (PVFATCCB
) (FileObject
->FsContext2
);
401 if (DeviceExt
->FatType
== FAT32
)
404 Fcb
->entry
.FirstCluster
+ Fcb
->entry
.FirstClusterHigh
* 65536;
408 CurrentCluster
= Fcb
->entry
.FirstCluster
;
410 FirstCluster
= CurrentCluster
;
412 /* Allocate a buffer to hold 1 cluster of data */
413 Temp
= ExAllocatePool (NonPagedPool
, DeviceExt
->BytesPerCluster
);
416 /* Find the cluster according to the offset in the file */
417 if (CurrentCluster
== 1)
419 CurrentCluster
= DeviceExt
->rootStart
+ WriteOffset
420 / DeviceExt
->BytesPerCluster
* DeviceExt
->Boot
->SectorsPerCluster
;
424 if (CurrentCluster
== 0)
429 // CurrentCluster = GetNextWriteCluster (DeviceExt, 0);
430 if (DeviceExt
->FatType
== FAT32
)
432 Fcb
->entry
.FirstClusterHigh
= CurrentCluster
>> 16;
433 Fcb
->entry
.FirstCluster
= CurrentCluster
;
436 Fcb
->entry
.FirstCluster
= CurrentCluster
;
441 FileOffset
< WriteOffset
/ DeviceExt
->BytesPerCluster
;
446 GetNextWriteCluster (DeviceExt
, CurrentCluster
);
454 * If the offset in the cluster doesn't fall on the cluster boundary
455 * then we have to write only from the specified offset
458 if ((WriteOffset
% DeviceExt
->BytesPerCluster
) != 0)
461 TempLength
= min (Length
, DeviceExt
->BytesPerCluster
-
462 (WriteOffset
% DeviceExt
->BytesPerCluster
));
463 /* Read in the existing cluster data */
464 if (FirstCluster
== 1)
466 /* FIXME: Check status */
467 VfatReadSectors (DeviceExt
->StorageDevice
,
469 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
473 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
476 /* Overwrite the last parts of the data as necessary */
477 memcpy (Temp
+ (WriteOffset
% DeviceExt
->BytesPerCluster
),
480 /* Write the cluster back */
481 Length2
-= TempLength
;
482 if (FirstCluster
== 1)
484 VfatWriteSectors (DeviceExt
->StorageDevice
,
486 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
487 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
491 VfatWriteCluster (DeviceExt
, Temp
, CurrentCluster
);
494 CurrentCluster
= GetNextWriteCluster (DeviceExt
, CurrentCluster
);
497 Buffer
= Buffer
+ TempLength
;
501 /* Write the buffer in chunks of 1 cluster */
503 while (Length2
>= DeviceExt
->BytesPerCluster
)
506 if (CurrentCluster
== 0)
509 return (STATUS_UNSUCCESSFUL
);
511 Length2
-= DeviceExt
->BytesPerCluster
;
512 if (FirstCluster
== 1)
514 VfatWriteSectors (DeviceExt
->StorageDevice
,
516 DeviceExt
->Boot
->SectorsPerCluster
, Buffer
);
517 CurrentCluster
+= DeviceExt
->Boot
->SectorsPerCluster
;
521 VfatWriteCluster (DeviceExt
, Buffer
, CurrentCluster
);
524 CurrentCluster
= GetNextWriteCluster (DeviceExt
, CurrentCluster
);
527 Buffer
= Buffer
+ DeviceExt
->BytesPerCluster
;
531 /* Write the remainder */
536 if (CurrentCluster
== 0)
539 return (STATUS_UNSUCCESSFUL
);
542 /* Read in the existing cluster data */
543 if (FirstCluster
== 1)
545 /* FIXME: Check status */
546 VfatReadSectors (DeviceExt
->StorageDevice
,
548 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
552 VFATLoadCluster (DeviceExt
, Temp
, CurrentCluster
);
554 memcpy (Temp
, Buffer
, Length2
);
556 if (FirstCluster
== 1)
558 VfatWriteSectors (DeviceExt
->StorageDevice
,
560 DeviceExt
->Boot
->SectorsPerCluster
, Temp
);
564 VfatWriteCluster (DeviceExt
, Temp
, CurrentCluster
);
571 /* set dates and times */
572 KeQuerySystemTime (&SystemTime
);
573 ExSystemTimeToLocalTime (&SystemTime
, &LocalTime
);
574 FsdFileTimeToDosDateTime ((TIME
*) & LocalTime
,
575 &Fcb
->entry
.UpdateDate
, &Fcb
->entry
.UpdateTime
);
576 Fcb
->entry
.AccessDate
= Fcb
->entry
.UpdateDate
;
578 if (Fcb
->entry
.FileSize
< WriteOffset
+ Length
579 && !(Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
))
581 Fcb
->entry
.FileSize
= WriteOffset
+ Length
;
583 * update entry in directory
585 // updEntry (DeviceExt, FileObject);
589 return (STATUS_SUCCESS
);
593 VfatWrite (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
595 * FUNCTION: Write to a file
601 PIO_STACK_LOCATION Stack
= IoGetCurrentIrpStackLocation (Irp
);
602 PFILE_OBJECT FileObject
= Stack
->FileObject
;
603 PDEVICE_EXTENSION DeviceExt
= DeviceObject
->DeviceExtension
;
606 DPRINT ("VfatWrite(DeviceObject %x Irp %x)\n", DeviceObject
, Irp
);
608 Length
= Stack
->Parameters
.Write
.Length
;
609 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
610 Offset
= Stack
->Parameters
.Write
.ByteOffset
.u
.LowPart
;
612 Status
= VfatWriteFile (DeviceExt
, FileObject
, Buffer
, Length
, Offset
);
614 Irp
->IoStatus
.Status
= Status
;
615 Irp
->IoStatus
.Information
= Length
;
616 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);
622 VfatRead (PDEVICE_OBJECT DeviceObject
, PIRP Irp
)
624 * FUNCTION: Read from a file
630 PIO_STACK_LOCATION Stack
;
631 PFILE_OBJECT FileObject
;
632 PDEVICE_EXTENSION DeviceExt
;
638 DPRINT ("VfatRead(DeviceObject %x, Irp %x)\n", DeviceObject
, Irp
);
640 /* Precondition / Initialization */
641 assert (Irp
!= NULL
);
642 Stack
= IoGetCurrentIrpStackLocation (Irp
);
643 assert (Stack
!= NULL
);
644 FileObject
= Stack
->FileObject
;
645 assert (FileObject
!= NULL
);
646 DeviceExt
= DeviceObject
->DeviceExtension
;
647 assert (DeviceExt
!= NULL
);
649 Length
= Stack
->Parameters
.Read
.Length
;
650 Buffer
= MmGetSystemAddressForMdl (Irp
->MdlAddress
);
651 Offset
= Stack
->Parameters
.Read
.ByteOffset
.u
.LowPart
;
653 if (Irp
->Flags
& IRP_PAGING_IO
||
654 FileObject
->Flags
& FO_NO_INTERMEDIATE_BUFFERING
)
663 /* fail if file is a directory */
664 Fcb
= ((PVFATCCB
) (FileObject
->FsContext2
))->pFcb
;
665 if (Fcb
->entry
.Attrib
& FILE_ATTRIBUTE_DIRECTORY
)
667 Status
= STATUS_FILE_IS_A_DIRECTORY
;
671 Status
= VfatReadFile (DeviceExt
,
672 FileObject
, Buffer
, Length
, Offset
, &LengthRead
,
676 Irp
->IoStatus
.Status
= Status
;
677 Irp
->IoStatus
.Information
= LengthRead
;
678 IoCompleteRequest (Irp
, IO_NO_INCREMENT
);