2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/fs/vfat/rw.c
5 * PURPOSE: VFAT Filesystem
6 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
10 /* INCLUDES *****************************************************************/
16 * Uncomment to enable strict verification of cluster/offset pair
17 * caching. If this option is enabled you lose all the benefits of
18 * the caching and the read/write operations will actually be
19 * slower. It's meant only for debugging!!!
20 * - Filip Navara, 26/07/2004
22 /* #define DEBUG_VERIFY_OFFSET_CACHING */
24 /* FUNCTIONS *****************************************************************/
27 NextCluster(PDEVICE_EXTENSION DeviceExt
,
29 PULONG CurrentCluster
,
32 * Return the next cluster in a FAT chain, possibly extending the chain if
36 if (FirstCluster
== 1)
38 (*CurrentCluster
) += DeviceExt
->FatInfo
.SectorsPerCluster
;
39 return(STATUS_SUCCESS
);
44 return GetNextClusterExtend(DeviceExt
, (*CurrentCluster
), CurrentCluster
);
46 return GetNextCluster(DeviceExt
, (*CurrentCluster
), CurrentCluster
);
51 OffsetToCluster(PDEVICE_EXTENSION DeviceExt
,
57 * Return the cluster corresponding to an offset within a file,
58 * possibly extending the file if necessary
65 DPRINT("OffsetToCluster(DeviceExt %x, Fcb %x, FirstCluster %x,"
66 " FileOffset %x, Cluster %x, Extend %d)\n", DeviceExt,
67 Fcb, FirstCluster, FileOffset, Cluster, Extend);
69 if (FirstCluster
== 0)
71 DbgPrint("OffsetToCluster is called with FirstCluster = 0!\n");
75 if (FirstCluster
== 1)
77 /* root of FAT16 or FAT12 */
78 *Cluster
= DeviceExt
->FatInfo
.rootStart
+ FileOffset
79 / (DeviceExt
->FatInfo
.BytesPerCluster
) * DeviceExt
->FatInfo
.SectorsPerCluster
;
80 return(STATUS_SUCCESS
);
84 CurrentCluster
= FirstCluster
;
87 for (i
= 0; i
< FileOffset
/ DeviceExt
->FatInfo
.BytesPerCluster
; i
++)
89 Status
= GetNextClusterExtend (DeviceExt
, CurrentCluster
, &CurrentCluster
);
90 if (!NT_SUCCESS(Status
))
93 *Cluster
= CurrentCluster
;
97 for (i
= 0; i
< FileOffset
/ DeviceExt
->FatInfo
.BytesPerCluster
; i
++)
99 Status
= GetNextCluster (DeviceExt
, CurrentCluster
, &CurrentCluster
);
100 if (!NT_SUCCESS(Status
))
103 *Cluster
= CurrentCluster
;
105 return(STATUS_SUCCESS
);
110 VfatReadFileData (PVFAT_IRP_CONTEXT IrpContext
,
112 LARGE_INTEGER ReadOffset
,
115 * FUNCTION: Reads data from a file
118 ULONG CurrentCluster
;
122 LARGE_INTEGER StartOffset
;
123 PDEVICE_EXTENSION DeviceExt
;
124 BOOLEAN First
= TRUE
;
128 ULONG BytesPerSector
;
129 ULONG BytesPerCluster
;
135 DeviceExt
= IrpContext
->DeviceExt
;
137 ASSERT(DeviceExt
->FatInfo
.BytesPerCluster
);
138 ASSERT(IrpContext
->FileObject
);
139 ASSERT(IrpContext
->FileObject
->FsContext2
!= NULL
);
141 DPRINT("VfatReadFileData(DeviceExt %p, FileObject %p, "
142 "Length %d, ReadOffset 0x%I64x)\n", DeviceExt
,
143 IrpContext
->FileObject
, Length
, ReadOffset
.QuadPart
);
147 Fcb
= IrpContext
->FileObject
->FsContext
;
148 BytesPerSector
= DeviceExt
->FatInfo
.BytesPerSector
;
149 BytesPerCluster
= DeviceExt
->FatInfo
.BytesPerCluster
;
151 ASSERT(ReadOffset
.QuadPart
+ Length
<= ROUND_UP(Fcb
->RFCB
.FileSize
.QuadPart
, BytesPerSector
));
152 ASSERT(ReadOffset
.u
.LowPart
% BytesPerSector
== 0);
153 ASSERT(Length
% BytesPerSector
== 0);
155 /* Is this a read of the FAT? */
156 if (Fcb
->Flags
& FCB_IS_FAT
)
158 ReadOffset
.QuadPart
+= DeviceExt
->FatInfo
.FATStart
* BytesPerSector
;
159 Status
= VfatReadDiskPartial(IrpContext
, &ReadOffset
, Length
, 0, TRUE
);
161 if (NT_SUCCESS(Status
))
163 *LengthRead
= Length
;
167 DPRINT1("FAT reading failed, Status %x\n", Status
);
171 /* Is this a read of the Volume ? */
172 if (Fcb
->Flags
& FCB_IS_VOLUME
)
174 Status
= VfatReadDiskPartial(IrpContext
, &ReadOffset
, Length
, 0, TRUE
);
175 if (NT_SUCCESS(Status
))
177 *LengthRead
= Length
;
181 DPRINT1("Volume reading failed, Status %x\n", Status
);
187 * Find the first cluster
189 FirstCluster
= CurrentCluster
=
190 vfatDirEntryGetFirstCluster (DeviceExt
, &Fcb
->entry
);
192 if (FirstCluster
== 1)
194 // Directory of FAT12/16 needs a special handling
195 if (ReadOffset
.u
.LowPart
+ Length
> DeviceExt
->FatInfo
.rootDirectorySectors
* BytesPerSector
)
197 Length
= DeviceExt
->FatInfo
.rootDirectorySectors
* BytesPerSector
- ReadOffset
.u
.LowPart
;
199 ReadOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.rootStart
* BytesPerSector
;
201 // Fire up the read command
203 Status
= VfatReadDiskPartial (IrpContext
, &ReadOffset
, Length
, 0, TRUE
);
204 if (NT_SUCCESS(Status
))
206 *LengthRead
= Length
;
211 ExAcquireFastMutex(&Fcb
->LastMutex
);
212 LastCluster
= Fcb
->LastCluster
;
213 LastOffset
= Fcb
->LastOffset
;
214 ExReleaseFastMutex(&Fcb
->LastMutex
);
217 * Find the cluster to start the read from
219 if (LastCluster
> 0 && ReadOffset
.u
.LowPart
>= LastOffset
)
221 Status
= OffsetToCluster(DeviceExt
, LastCluster
,
222 ROUND_DOWN(ReadOffset
.u
.LowPart
, BytesPerCluster
) -
224 &CurrentCluster
, FALSE
);
225 #ifdef DEBUG_VERIFY_OFFSET_CACHING
226 /* DEBUG VERIFICATION */
228 ULONG CorrectCluster
;
229 OffsetToCluster(DeviceExt
, FirstCluster
,
230 ROUND_DOWN(ReadOffset
.u
.LowPart
, BytesPerCluster
),
231 &CorrectCluster
, FALSE
);
232 if (CorrectCluster
!= CurrentCluster
)
233 KeBugCheck(FAT_FILE_SYSTEM
);
239 Status
= OffsetToCluster(DeviceExt
, FirstCluster
,
240 ROUND_DOWN(ReadOffset
.u
.LowPart
, BytesPerCluster
),
241 &CurrentCluster
, FALSE
);
243 if (!NT_SUCCESS(Status
))
248 ExAcquireFastMutex(&Fcb
->LastMutex
);
249 Fcb
->LastCluster
= CurrentCluster
;
250 Fcb
->LastOffset
= ROUND_DOWN (ReadOffset
.u
.LowPart
, BytesPerCluster
);
251 ExReleaseFastMutex(&Fcb
->LastMutex
);
253 KeInitializeEvent(&IrpContext
->Event
, NotificationEvent
, FALSE
);
254 IrpContext
->RefCount
= 1;
256 while (Length
> 0 && CurrentCluster
!= 0xffffffff)
258 StartCluster
= CurrentCluster
;
259 StartOffset
.QuadPart
= ClusterToSector(DeviceExt
, StartCluster
) * BytesPerSector
;
268 BytesDone
= min (Length
, BytesPerCluster
- (ReadOffset
.u
.LowPart
% BytesPerCluster
));
269 StartOffset
.QuadPart
+= ReadOffset
.u
.LowPart
% BytesPerCluster
;
274 if (Length
- BytesDone
> BytesPerCluster
)
276 BytesDone
+= BytesPerCluster
;
283 Status
= NextCluster(DeviceExt
, FirstCluster
, &CurrentCluster
, FALSE
);
285 while (StartCluster
+ ClusterCount
== CurrentCluster
&& NT_SUCCESS(Status
) && Length
> BytesDone
);
286 DPRINT("start %08x, next %08x, count %d\n",
287 StartCluster
, CurrentCluster
, ClusterCount
);
289 ExAcquireFastMutex(&Fcb
->LastMutex
);
290 Fcb
->LastCluster
= StartCluster
+ (ClusterCount
- 1);
291 Fcb
->LastOffset
= ROUND_DOWN(ReadOffset
.u
.LowPart
, BytesPerCluster
) + (ClusterCount
- 1) * BytesPerCluster
;
292 ExReleaseFastMutex(&Fcb
->LastMutex
);
294 // Fire up the read command
295 Status
= VfatReadDiskPartial (IrpContext
, &StartOffset
, BytesDone
, *LengthRead
, FALSE
);
296 if (!NT_SUCCESS(Status
) && Status
!= STATUS_PENDING
)
300 *LengthRead
+= BytesDone
;
302 ReadOffset
.u
.LowPart
+= BytesDone
;
304 if (0 != InterlockedDecrement((PLONG
)&IrpContext
->RefCount
))
306 KeWaitForSingleObject(&IrpContext
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
308 if (NT_SUCCESS(Status
) || Status
== STATUS_PENDING
)
312 Status
= STATUS_UNSUCCESSFUL
;
316 Status
= IrpContext
->Irp
->IoStatus
.Status
;
323 VfatWriteFileData(PVFAT_IRP_CONTEXT IrpContext
,
325 LARGE_INTEGER WriteOffset
)
327 PDEVICE_EXTENSION DeviceExt
;
331 ULONG CurrentCluster
;
335 NTSTATUS Status
= STATUS_SUCCESS
;
336 BOOLEAN First
= TRUE
;
337 ULONG BytesPerSector
;
338 ULONG BytesPerCluster
;
339 LARGE_INTEGER StartOffset
;
346 DeviceExt
= IrpContext
->DeviceExt
;
348 ASSERT(DeviceExt
->FatInfo
.BytesPerCluster
);
349 ASSERT(IrpContext
->FileObject
);
350 ASSERT(IrpContext
->FileObject
->FsContext2
!= NULL
);
352 Fcb
= IrpContext
->FileObject
->FsContext
;
353 BytesPerCluster
= DeviceExt
->FatInfo
.BytesPerCluster
;
354 BytesPerSector
= DeviceExt
->FatInfo
.BytesPerSector
;
356 DPRINT("VfatWriteFileData(DeviceExt %p, FileObject %p, "
357 "Length %d, WriteOffset 0x%I64x), '%wZ'\n", DeviceExt
,
358 IrpContext
->FileObject
, Length
, WriteOffset
,
361 ASSERT(WriteOffset
.QuadPart
+ Length
<= Fcb
->RFCB
.AllocationSize
.QuadPart
);
362 ASSERT(WriteOffset
.u
.LowPart
% BytesPerSector
== 0);
363 ASSERT(Length
% BytesPerSector
== 0);
365 // Is this a write of the volume ?
366 if (Fcb
->Flags
& FCB_IS_VOLUME
)
368 Status
= VfatWriteDiskPartial(IrpContext
, &WriteOffset
, Length
, 0, TRUE
);
369 if (!NT_SUCCESS(Status
))
371 DPRINT1("Volume writing failed, Status %x\n", Status
);
376 // Is this a write to the FAT ?
377 if (Fcb
->Flags
& FCB_IS_FAT
)
379 WriteOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.FATStart
* BytesPerSector
;
380 IrpContext
->RefCount
= 1;
381 for (Count
= 0; Count
< DeviceExt
->FatInfo
.FATCount
; Count
++)
383 Status
= VfatWriteDiskPartial(IrpContext
, &WriteOffset
, Length
, 0, FALSE
);
384 if (!NT_SUCCESS(Status
) && Status
!= STATUS_PENDING
)
386 DPRINT1("FAT writing failed, Status %x\n", Status
);
389 WriteOffset
.u
.LowPart
+= Fcb
->RFCB
.FileSize
.u
.LowPart
;
391 if (0 != InterlockedDecrement((PLONG
)&IrpContext
->RefCount
))
393 KeWaitForSingleObject(&IrpContext
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
395 if (NT_SUCCESS(Status
) || Status
== STATUS_PENDING
)
397 Status
= IrpContext
->Irp
->IoStatus
.Status
;
403 * Find the first cluster
405 FirstCluster
= CurrentCluster
=
406 vfatDirEntryGetFirstCluster (DeviceExt
, &Fcb
->entry
);
408 if (FirstCluster
== 1)
410 ASSERT(WriteOffset
.u
.LowPart
+ Length
<= DeviceExt
->FatInfo
.rootDirectorySectors
* BytesPerSector
);
411 // Directory of FAT12/16 needs a special handling
412 WriteOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.rootStart
* BytesPerSector
;
413 // Fire up the write command
414 Status
= VfatWriteDiskPartial (IrpContext
, &WriteOffset
, Length
, 0, TRUE
);
418 ExAcquireFastMutex(&Fcb
->LastMutex
);
419 LastCluster
= Fcb
->LastCluster
;
420 LastOffset
= Fcb
->LastOffset
;
421 ExReleaseFastMutex(&Fcb
->LastMutex
);
424 * Find the cluster to start the write from
426 if (LastCluster
> 0 && WriteOffset
.u
.LowPart
>= LastOffset
)
428 Status
= OffsetToCluster(DeviceExt
, LastCluster
,
429 ROUND_DOWN(WriteOffset
.u
.LowPart
, BytesPerCluster
) -
431 &CurrentCluster
, FALSE
);
432 #ifdef DEBUG_VERIFY_OFFSET_CACHING
433 /* DEBUG VERIFICATION */
435 ULONG CorrectCluster
;
436 OffsetToCluster(DeviceExt
, FirstCluster
,
437 ROUND_DOWN(WriteOffset
.u
.LowPart
, BytesPerCluster
),
438 &CorrectCluster
, FALSE
);
439 if (CorrectCluster
!= CurrentCluster
)
440 KeBugCheck(FAT_FILE_SYSTEM
);
446 Status
= OffsetToCluster(DeviceExt
, FirstCluster
,
447 ROUND_DOWN(WriteOffset
.u
.LowPart
, BytesPerCluster
),
448 &CurrentCluster
, FALSE
);
451 if (!NT_SUCCESS(Status
))
456 ExAcquireFastMutex(&Fcb
->LastMutex
);
457 Fcb
->LastCluster
= CurrentCluster
;
458 Fcb
->LastOffset
= ROUND_DOWN (WriteOffset
.u
.LowPart
, BytesPerCluster
);
459 ExReleaseFastMutex(&Fcb
->LastMutex
);
461 IrpContext
->RefCount
= 1;
464 while (Length
> 0 && CurrentCluster
!= 0xffffffff)
466 StartCluster
= CurrentCluster
;
467 StartOffset
.QuadPart
= ClusterToSector(DeviceExt
, StartCluster
) * BytesPerSector
;
476 BytesDone
= min (Length
, BytesPerCluster
- (WriteOffset
.u
.LowPart
% BytesPerCluster
));
477 StartOffset
.QuadPart
+= WriteOffset
.u
.LowPart
% BytesPerCluster
;
482 if (Length
- BytesDone
> BytesPerCluster
)
484 BytesDone
+= BytesPerCluster
;
491 Status
= NextCluster(DeviceExt
, FirstCluster
, &CurrentCluster
, FALSE
);
493 while (StartCluster
+ ClusterCount
== CurrentCluster
&& NT_SUCCESS(Status
) && Length
> BytesDone
);
494 DPRINT("start %08x, next %08x, count %d\n",
495 StartCluster
, CurrentCluster
, ClusterCount
);
497 ExAcquireFastMutex(&Fcb
->LastMutex
);
498 Fcb
->LastCluster
= StartCluster
+ (ClusterCount
- 1);
499 Fcb
->LastOffset
= ROUND_DOWN(WriteOffset
.u
.LowPart
, BytesPerCluster
) + (ClusterCount
- 1) * BytesPerCluster
;
500 ExReleaseFastMutex(&Fcb
->LastMutex
);
502 // Fire up the write command
503 Status
= VfatWriteDiskPartial (IrpContext
, &StartOffset
, BytesDone
, BufferOffset
, FALSE
);
504 if (!NT_SUCCESS(Status
) && Status
!= STATUS_PENDING
)
508 BufferOffset
+= BytesDone
;
510 WriteOffset
.u
.LowPart
+= BytesDone
;
512 if (0 != InterlockedDecrement((PLONG
)&IrpContext
->RefCount
))
514 KeWaitForSingleObject(&IrpContext
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
516 if (NT_SUCCESS(Status
) || Status
== STATUS_PENDING
)
520 Status
= STATUS_UNSUCCESSFUL
;
524 Status
= IrpContext
->Irp
->IoStatus
.Status
;
531 VfatRead(PVFAT_IRP_CONTEXT IrpContext
)
536 ULONG ReturnedLength
= 0;
537 PERESOURCE Resource
= NULL
;
538 LARGE_INTEGER ByteOffset
;
540 PDEVICE_OBJECT DeviceToVerify
;
541 ULONG BytesPerSector
;
545 DPRINT("VfatRead(IrpContext %p)\n", IrpContext
);
547 ASSERT(IrpContext
->DeviceObject
);
549 // This request is not allowed on the main device object
550 if (IrpContext
->DeviceObject
== VfatGlobalData
->DeviceObject
)
552 DPRINT("VfatRead is called with the main device object.\n");
553 Status
= STATUS_INVALID_DEVICE_REQUEST
;
557 ASSERT(IrpContext
->DeviceExt
);
558 ASSERT(IrpContext
->FileObject
);
559 Fcb
= IrpContext
->FileObject
->FsContext
;
562 DPRINT("<%wZ>\n", &Fcb
->PathNameU
);
564 if (Fcb
->Flags
& FCB_IS_PAGE_FILE
)
566 PFATINFO FatInfo
= &IrpContext
->DeviceExt
->FatInfo
;
567 IrpContext
->Stack
->Parameters
.Read
.ByteOffset
.QuadPart
+= FatInfo
->dataStart
* FatInfo
->BytesPerSector
;
568 IoSkipCurrentIrpStackLocation(IrpContext
->Irp
);
569 DPRINT("Read from page file, disk offset %I64x\n", IrpContext
->Stack
->Parameters
.Read
.ByteOffset
.QuadPart
);
570 Status
= IoCallDriver(IrpContext
->DeviceExt
->StorageDevice
, IrpContext
->Irp
);
571 VfatFreeIrpContext(IrpContext
);
575 ByteOffset
= IrpContext
->Stack
->Parameters
.Read
.ByteOffset
;
576 Length
= IrpContext
->Stack
->Parameters
.Read
.Length
;
577 BytesPerSector
= IrpContext
->DeviceExt
->FatInfo
.BytesPerSector
;
579 /* fail if file is a directory and no paged read */
580 if (*Fcb
->Attributes
& FILE_ATTRIBUTE_DIRECTORY
&& !(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
))
582 Status
= STATUS_INVALID_PARAMETER
;
587 DPRINT("'%wZ', Offset: %d, Length %d\n", &Fcb
->PathNameU
, ByteOffset
.u
.LowPart
, Length
);
589 if (ByteOffset
.u
.HighPart
&& !(Fcb
->Flags
& FCB_IS_VOLUME
))
591 Status
= STATUS_INVALID_PARAMETER
;
594 if (ByteOffset
.QuadPart
>= Fcb
->RFCB
.FileSize
.QuadPart
)
596 IrpContext
->Irp
->IoStatus
.Information
= 0;
597 Status
= STATUS_END_OF_FILE
;
600 if (IrpContext
->Irp
->Flags
& (IRP_PAGING_IO
| IRP_NOCACHE
) || (Fcb
->Flags
& FCB_IS_VOLUME
))
602 if (ByteOffset
.u
.LowPart
% BytesPerSector
!= 0 || Length
% BytesPerSector
!= 0)
604 DPRINT("%d %d\n", ByteOffset
.u
.LowPart
, Length
);
605 // non cached read must be sector aligned
606 Status
= STATUS_INVALID_PARAMETER
;
612 IrpContext
->Irp
->IoStatus
.Information
= 0;
613 Status
= STATUS_SUCCESS
;
617 if (Fcb
->Flags
& FCB_IS_VOLUME
)
619 Resource
= &IrpContext
->DeviceExt
->DirResource
;
621 else if (IrpContext
->Irp
->Flags
& IRP_PAGING_IO
)
623 Resource
= &Fcb
->PagingIoResource
;
627 Resource
= &Fcb
->MainResource
;
629 if (!ExAcquireResourceSharedLite(Resource
,
630 IrpContext
->Flags
& IRPCONTEXT_CANWAIT
? TRUE
: FALSE
))
633 Status
= STATUS_PENDING
;
637 if (!(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) &&
638 FsRtlAreThereCurrentFileLocks(&Fcb
->FileLock
))
640 if (!FsRtlCheckLockForReadAccess(&Fcb
->FileLock
, IrpContext
->Irp
))
642 Status
= STATUS_FILE_LOCK_CONFLICT
;
647 Buffer
= VfatGetUserBuffer(IrpContext
->Irp
);
650 Status
= STATUS_INVALID_USER_BUFFER
;
654 if (!(IrpContext
->Irp
->Flags
& (IRP_NOCACHE
|IRP_PAGING_IO
)) &&
655 !(Fcb
->Flags
& (FCB_IS_PAGE_FILE
|FCB_IS_VOLUME
)))
658 Status
= STATUS_SUCCESS
;
659 if (ByteOffset
.u
.LowPart
+ Length
> Fcb
->RFCB
.FileSize
.u
.LowPart
)
661 Length
= Fcb
->RFCB
.FileSize
.u
.LowPart
- ByteOffset
.u
.LowPart
;
662 Status
= /*STATUS_END_OF_FILE*/STATUS_SUCCESS
;
665 if (IrpContext
->FileObject
->PrivateCacheMap
== NULL
)
667 CcInitializeCacheMap(IrpContext
->FileObject
,
668 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
670 &(VfatGlobalData
->CacheMgrCallbacks
),
673 if (!CcCopyRead(IrpContext
->FileObject
, &ByteOffset
, Length
,
674 (BOOLEAN
)(IrpContext
->Flags
& IRPCONTEXT_CANWAIT
), Buffer
,
675 &IrpContext
->Irp
->IoStatus
))
677 Status
= STATUS_PENDING
;
680 if (!NT_SUCCESS(IrpContext
->Irp
->IoStatus
.Status
))
682 Status
= IrpContext
->Irp
->IoStatus
.Status
;
688 if (ByteOffset
.QuadPart
+ Length
> ROUND_UP(Fcb
->RFCB
.FileSize
.QuadPart
, BytesPerSector
))
690 Length
= (ULONG
)(ROUND_UP(Fcb
->RFCB
.FileSize
.QuadPart
, BytesPerSector
) - ByteOffset
.QuadPart
);
693 Status
= VfatLockUserBuffer(IrpContext
->Irp
, Length
, IoWriteAccess
);
694 if (!NT_SUCCESS(Status
))
699 Status
= VfatReadFileData(IrpContext
, Length
, ByteOffset
, &ReturnedLength
);
700 if (Status
== STATUS_VERIFY_REQUIRED
)
702 DPRINT("VfatReadFileData returned STATUS_VERIFY_REQUIRED\n");
703 DeviceToVerify
= IoGetDeviceToVerify(PsGetCurrentThread());
704 IoSetDeviceToVerify(PsGetCurrentThread(), NULL
);
705 Status
= IoVerifyVolume (DeviceToVerify
, FALSE
);
707 if (NT_SUCCESS(Status
))
709 Status
= VfatReadFileData(IrpContext
, Length
,
710 ByteOffset
, &ReturnedLength
);
714 if (NT_SUCCESS(Status
))
716 IrpContext
->Irp
->IoStatus
.Information
= ReturnedLength
;
723 ExReleaseResourceLite(Resource
);
726 if (Status
== STATUS_PENDING
)
728 Status
= VfatLockUserBuffer(IrpContext
->Irp
, Length
, IoWriteAccess
);
729 if (NT_SUCCESS(Status
))
731 Status
= VfatQueueRequest(IrpContext
);
735 IrpContext
->Irp
->IoStatus
.Status
= Status
;
736 IoCompleteRequest(IrpContext
->Irp
, IO_NO_INCREMENT
);
737 VfatFreeIrpContext(IrpContext
);
742 IrpContext
->Irp
->IoStatus
.Status
= Status
;
743 if (IrpContext
->FileObject
->Flags
& FO_SYNCHRONOUS_IO
&&
744 !(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) &&
745 (NT_SUCCESS(Status
) || Status
==STATUS_END_OF_FILE
))
747 IrpContext
->FileObject
->CurrentByteOffset
.QuadPart
=
748 ByteOffset
.QuadPart
+ IrpContext
->Irp
->IoStatus
.Information
;
751 IoCompleteRequest(IrpContext
->Irp
,
752 (CCHAR
)(NT_SUCCESS(Status
) ? IO_DISK_INCREMENT
: IO_NO_INCREMENT
));
753 VfatFreeIrpContext(IrpContext
);
755 DPRINT("%x\n", Status
);
759 NTSTATUS
VfatWrite (PVFAT_IRP_CONTEXT IrpContext
)
762 PERESOURCE Resource
= NULL
;
763 LARGE_INTEGER ByteOffset
;
764 LARGE_INTEGER OldFileSize
;
765 NTSTATUS Status
= STATUS_SUCCESS
;
767 ULONG OldAllocationSize
;
769 ULONG BytesPerSector
;
770 PDEVICE_OBJECT DeviceToVerify
;
774 DPRINT("VfatWrite(IrpContext %p)\n", IrpContext
);
776 ASSERT(IrpContext
->DeviceObject
);
778 // This request is not allowed on the main device object
779 if (IrpContext
->DeviceObject
== VfatGlobalData
->DeviceObject
)
781 DPRINT("VfatWrite is called with the main device object.\n");
782 Status
= STATUS_INVALID_DEVICE_REQUEST
;
786 ASSERT(IrpContext
->DeviceExt
);
787 ASSERT(IrpContext
->FileObject
);
788 Fcb
= IrpContext
->FileObject
->FsContext
;
791 DPRINT("<%wZ>\n", &Fcb
->PathNameU
);
793 if (Fcb
->Flags
& FCB_IS_PAGE_FILE
)
795 PFATINFO FatInfo
= &IrpContext
->DeviceExt
->FatInfo
;
796 IrpContext
->Stack
->Parameters
.Write
.ByteOffset
.QuadPart
+= FatInfo
->dataStart
* FatInfo
->BytesPerSector
;
797 IoSkipCurrentIrpStackLocation(IrpContext
->Irp
);
798 DPRINT("Write to page file, disk offset %I64x\n", IrpContext
->Stack
->Parameters
.Write
.ByteOffset
.QuadPart
);
799 Status
= IoCallDriver(IrpContext
->DeviceExt
->StorageDevice
, IrpContext
->Irp
);
800 VfatFreeIrpContext(IrpContext
);
804 /* fail if file is a directory and no paged read */
805 if (*Fcb
->Attributes
& FILE_ATTRIBUTE_DIRECTORY
&& !(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
))
807 Status
= STATUS_INVALID_PARAMETER
;
811 ByteOffset
= IrpContext
->Stack
->Parameters
.Write
.ByteOffset
;
812 if (ByteOffset
.u
.LowPart
== FILE_WRITE_TO_END_OF_FILE
&&
813 ByteOffset
.u
.HighPart
== -1)
815 ByteOffset
.QuadPart
= Fcb
->RFCB
.FileSize
.QuadPart
;
817 Length
= IrpContext
->Stack
->Parameters
.Write
.Length
;
818 BytesPerSector
= IrpContext
->DeviceExt
->FatInfo
.BytesPerSector
;
820 if (ByteOffset
.u
.HighPart
&& !(Fcb
->Flags
& FCB_IS_VOLUME
))
822 Status
= STATUS_INVALID_PARAMETER
;
826 if (Fcb
->Flags
& (FCB_IS_FAT
| FCB_IS_VOLUME
) ||
827 1 == vfatDirEntryGetFirstCluster (IrpContext
->DeviceExt
, &Fcb
->entry
))
829 if (ByteOffset
.QuadPart
+ Length
> Fcb
->RFCB
.FileSize
.QuadPart
)
831 // we can't extend the FAT, the volume or the root on FAT12/FAT16
832 Status
= STATUS_END_OF_FILE
;
837 if (IrpContext
->Irp
->Flags
& (IRP_PAGING_IO
|IRP_NOCACHE
) || (Fcb
->Flags
& FCB_IS_VOLUME
))
839 if (ByteOffset
.u
.LowPart
% BytesPerSector
!= 0 || Length
% BytesPerSector
!= 0)
841 // non cached write must be sector aligned
842 Status
= STATUS_INVALID_PARAMETER
;
850 * Update last write time
852 IrpContext
->Irp
->IoStatus
.Information
= 0;
853 Status
= STATUS_SUCCESS
;
857 if (IrpContext
->Irp
->Flags
& IRP_PAGING_IO
)
859 if (ByteOffset
.u
.LowPart
+ Length
> Fcb
->RFCB
.AllocationSize
.u
.LowPart
)
861 Status
= STATUS_INVALID_PARAMETER
;
864 if (ByteOffset
.u
.LowPart
+ Length
> ROUND_UP(Fcb
->RFCB
.AllocationSize
.u
.LowPart
, BytesPerSector
))
866 Length
= ROUND_UP(Fcb
->RFCB
.FileSize
.u
.LowPart
, BytesPerSector
) - ByteOffset
.u
.LowPart
;
870 if (Fcb
->Flags
& FCB_IS_VOLUME
)
872 Resource
= &IrpContext
->DeviceExt
->DirResource
;
874 else if (IrpContext
->Irp
->Flags
& IRP_PAGING_IO
)
876 Resource
= &Fcb
->PagingIoResource
;
880 Resource
= &Fcb
->MainResource
;
883 if (Fcb
->Flags
& FCB_IS_PAGE_FILE
)
885 if (!ExAcquireResourceSharedLite(Resource
,
886 (BOOLEAN
)(IrpContext
->Flags
& IRPCONTEXT_CANWAIT
)))
889 Status
= STATUS_PENDING
;
895 if (!ExAcquireResourceExclusiveLite(Resource
,
896 (BOOLEAN
)(IrpContext
->Flags
& IRPCONTEXT_CANWAIT
)))
899 Status
= STATUS_PENDING
;
904 if (!(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) &&
905 FsRtlAreThereCurrentFileLocks(&Fcb
->FileLock
))
907 if (!FsRtlCheckLockForWriteAccess(&Fcb
->FileLock
, IrpContext
->Irp
))
909 Status
= STATUS_FILE_LOCK_CONFLICT
;
914 if (!(IrpContext
->Flags
& IRPCONTEXT_CANWAIT
) && !(Fcb
->Flags
& FCB_IS_VOLUME
))
916 if (ByteOffset
.u
.LowPart
+ Length
> Fcb
->RFCB
.AllocationSize
.u
.LowPart
)
918 Status
= STATUS_PENDING
;
923 OldFileSize
= Fcb
->RFCB
.FileSize
;
924 OldAllocationSize
= Fcb
->RFCB
.AllocationSize
.u
.LowPart
;
926 Buffer
= VfatGetUserBuffer(IrpContext
->Irp
);
929 Status
= STATUS_INVALID_USER_BUFFER
;
934 if (!(Fcb
->Flags
& (FCB_IS_FAT
|FCB_IS_VOLUME
)) &&
935 !(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) &&
936 ByteOffset
.u
.LowPart
+ Length
> Fcb
->RFCB
.FileSize
.u
.LowPart
)
938 LARGE_INTEGER AllocationSize
;
939 AllocationSize
.QuadPart
= ByteOffset
.u
.LowPart
+ Length
;
940 Status
= VfatSetAllocationSizeInformation(IrpContext
->FileObject
, Fcb
,
941 IrpContext
->DeviceExt
, &AllocationSize
);
942 if (!NT_SUCCESS (Status
))
948 if (!(IrpContext
->Irp
->Flags
& (IRP_NOCACHE
|IRP_PAGING_IO
)) &&
949 !(Fcb
->Flags
& (FCB_IS_PAGE_FILE
|FCB_IS_VOLUME
)))
953 if (IrpContext
->FileObject
->PrivateCacheMap
== NULL
)
955 CcInitializeCacheMap(IrpContext
->FileObject
,
956 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
958 &VfatGlobalData
->CacheMgrCallbacks
,
961 if (ByteOffset
.QuadPart
> OldFileSize
.QuadPart
)
963 CcZeroData(IrpContext
->FileObject
, &OldFileSize
, &ByteOffset
, TRUE
);
965 if (CcCopyWrite(IrpContext
->FileObject
, &ByteOffset
, Length
,
966 1 /*IrpContext->Flags & IRPCONTEXT_CANWAIT*/, Buffer
))
968 IrpContext
->Irp
->IoStatus
.Information
= Length
;
969 Status
= STATUS_SUCCESS
;
973 Status
= STATUS_UNSUCCESSFUL
;
980 if (ByteOffset
.QuadPart
> OldFileSize
.QuadPart
)
982 CcZeroData(IrpContext
->FileObject
, &OldFileSize
, &ByteOffset
, TRUE
);
985 Status
= VfatLockUserBuffer(IrpContext
->Irp
, Length
, IoReadAccess
);
986 if (!NT_SUCCESS(Status
))
991 Status
= VfatWriteFileData(IrpContext
, Length
, ByteOffset
);
992 if (Status
== STATUS_VERIFY_REQUIRED
)
994 DPRINT("VfatWriteFileData returned STATUS_VERIFY_REQUIRED\n");
995 DeviceToVerify
= IoGetDeviceToVerify(PsGetCurrentThread());
996 IoSetDeviceToVerify(PsGetCurrentThread(), NULL
);
997 Status
= IoVerifyVolume (DeviceToVerify
, FALSE
);
999 if (NT_SUCCESS(Status
))
1001 Status
= VfatWriteFileData(IrpContext
, Length
, ByteOffset
);
1005 if (NT_SUCCESS(Status
))
1007 IrpContext
->Irp
->IoStatus
.Information
= Length
;
1011 if (!(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) &&
1012 !(Fcb
->Flags
& (FCB_IS_FAT
|FCB_IS_VOLUME
)))
1014 if(!(*Fcb
->Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
1016 LARGE_INTEGER SystemTime
;
1017 // set dates and times
1018 KeQuerySystemTime (&SystemTime
);
1019 if (Fcb
->Flags
& FCB_IS_FATX_ENTRY
)
1021 FsdSystemTimeToDosDateTime (IrpContext
->DeviceExt
,
1022 &SystemTime
, &Fcb
->entry
.FatX
.UpdateDate
,
1023 &Fcb
->entry
.FatX
.UpdateTime
);
1024 Fcb
->entry
.FatX
.AccessDate
= Fcb
->entry
.FatX
.UpdateDate
;
1025 Fcb
->entry
.FatX
.AccessTime
= Fcb
->entry
.FatX
.UpdateTime
;
1029 FsdSystemTimeToDosDateTime (IrpContext
->DeviceExt
,
1030 &SystemTime
, &Fcb
->entry
.Fat
.UpdateDate
,
1031 &Fcb
->entry
.Fat
.UpdateTime
);
1032 Fcb
->entry
.Fat
.AccessDate
= Fcb
->entry
.Fat
.UpdateDate
;
1034 /* set date and times to dirty */
1035 Fcb
->Flags
|= FCB_IS_DIRTY
;
1042 ExReleaseResourceLite(Resource
);
1045 if (Status
== STATUS_PENDING
)
1047 Status
= VfatLockUserBuffer(IrpContext
->Irp
, Length
, IoReadAccess
);
1048 if (NT_SUCCESS(Status
))
1050 Status
= VfatQueueRequest(IrpContext
);
1054 IrpContext
->Irp
->IoStatus
.Status
= Status
;
1055 IoCompleteRequest(IrpContext
->Irp
, IO_NO_INCREMENT
);
1056 VfatFreeIrpContext(IrpContext
);
1061 IrpContext
->Irp
->IoStatus
.Status
= Status
;
1062 if (IrpContext
->FileObject
->Flags
& FO_SYNCHRONOUS_IO
&&
1063 !(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) && NT_SUCCESS(Status
))
1065 IrpContext
->FileObject
->CurrentByteOffset
.QuadPart
=
1066 ByteOffset
.QuadPart
+ IrpContext
->Irp
->IoStatus
.Information
;
1069 IoCompleteRequest(IrpContext
->Irp
,
1070 (CCHAR
)(NT_SUCCESS(Status
) ? IO_DISK_INCREMENT
: IO_NO_INCREMENT
));
1071 VfatFreeIrpContext(IrpContext
);
1073 DPRINT("%x\n", Status
);