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 *****************************************************************/
18 * Uncomment to enable strict verification of cluster/offset pair
19 * caching. If this option is enabled you lose all the benefits of
20 * the caching and the read/write operations will actually be
21 * slower. It's meant only for debugging!!!
22 * - Filip Navara, 26/07/2004
24 /* #define DEBUG_VERIFY_OFFSET_CACHING */
26 /* FUNCTIONS *****************************************************************/
29 * Return the next cluster in a FAT chain, possibly extending the chain if
34 PDEVICE_EXTENSION DeviceExt
,
36 PULONG CurrentCluster
,
39 if (FirstCluster
== 1)
41 (*CurrentCluster
) += DeviceExt
->FatInfo
.SectorsPerCluster
;
42 return STATUS_SUCCESS
;
47 return GetNextClusterExtend(DeviceExt
, (*CurrentCluster
), CurrentCluster
);
49 return GetNextCluster(DeviceExt
, (*CurrentCluster
), CurrentCluster
);
55 PDEVICE_EXTENSION DeviceExt
,
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 * FUNCTION: Reads data from a file
115 PVFAT_IRP_CONTEXT IrpContext
,
117 LARGE_INTEGER ReadOffset
,
120 ULONG CurrentCluster
;
124 LARGE_INTEGER StartOffset
;
125 PDEVICE_EXTENSION DeviceExt
;
126 BOOLEAN First
= TRUE
;
130 ULONG BytesPerSector
;
131 ULONG BytesPerCluster
;
137 DeviceExt
= IrpContext
->DeviceExt
;
139 ASSERT(DeviceExt
->FatInfo
.BytesPerCluster
);
140 ASSERT(IrpContext
->FileObject
);
141 ASSERT(IrpContext
->FileObject
->FsContext2
!= NULL
);
143 DPRINT("VfatReadFileData(DeviceExt %p, FileObject %p, "
144 "Length %u, ReadOffset 0x%I64x)\n", DeviceExt
,
145 IrpContext
->FileObject
, Length
, ReadOffset
.QuadPart
);
149 Fcb
= IrpContext
->FileObject
->FsContext
;
150 BytesPerSector
= DeviceExt
->FatInfo
.BytesPerSector
;
151 BytesPerCluster
= DeviceExt
->FatInfo
.BytesPerCluster
;
153 ASSERT(ReadOffset
.QuadPart
+ Length
<= ROUND_UP(Fcb
->RFCB
.FileSize
.QuadPart
, BytesPerSector
));
154 ASSERT(ReadOffset
.u
.LowPart
% BytesPerSector
== 0);
155 ASSERT(Length
% BytesPerSector
== 0);
157 /* Is this a read of the FAT? */
158 if (Fcb
->Flags
& FCB_IS_FAT
)
160 ReadOffset
.QuadPart
+= DeviceExt
->FatInfo
.FATStart
* BytesPerSector
;
161 Status
= VfatReadDiskPartial(IrpContext
, &ReadOffset
, Length
, 0, TRUE
);
163 if (NT_SUCCESS(Status
))
165 *LengthRead
= Length
;
169 DPRINT1("FAT reading failed, Status %x\n", Status
);
174 /* Is this a read of the Volume ? */
175 if (Fcb
->Flags
& FCB_IS_VOLUME
)
177 Status
= VfatReadDiskPartial(IrpContext
, &ReadOffset
, Length
, 0, TRUE
);
178 if (NT_SUCCESS(Status
))
180 *LengthRead
= Length
;
184 DPRINT1("Volume reading failed, Status %x\n", Status
);
189 /* Find the first cluster */
191 CurrentCluster
= vfatDirEntryGetFirstCluster (DeviceExt
, &Fcb
->entry
);
193 if (FirstCluster
== 1)
195 /* Directory of FAT12/16 needs a special handling */
196 if (ReadOffset
.u
.LowPart
+ Length
> DeviceExt
->FatInfo
.rootDirectorySectors
* BytesPerSector
)
198 Length
= DeviceExt
->FatInfo
.rootDirectorySectors
* BytesPerSector
- ReadOffset
.u
.LowPart
;
200 ReadOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.rootStart
* BytesPerSector
;
202 /* 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
);
216 /* Find the cluster to start the read from */
217 if (LastCluster
> 0 && ReadOffset
.u
.LowPart
>= LastOffset
)
219 Status
= OffsetToCluster(DeviceExt
, LastCluster
,
220 ROUND_DOWN(ReadOffset
.u
.LowPart
, BytesPerCluster
) -
222 &CurrentCluster
, FALSE
);
223 #ifdef DEBUG_VERIFY_OFFSET_CACHING
224 /* DEBUG VERIFICATION */
226 ULONG CorrectCluster
;
227 OffsetToCluster(DeviceExt
, FirstCluster
,
228 ROUND_DOWN(ReadOffset
.u
.LowPart
, BytesPerCluster
),
229 &CorrectCluster
, FALSE
);
230 if (CorrectCluster
!= CurrentCluster
)
231 KeBugCheck(FAT_FILE_SYSTEM
);
237 Status
= OffsetToCluster(DeviceExt
, FirstCluster
,
238 ROUND_DOWN(ReadOffset
.u
.LowPart
, BytesPerCluster
),
239 &CurrentCluster
, FALSE
);
242 if (!NT_SUCCESS(Status
))
247 ExAcquireFastMutex(&Fcb
->LastMutex
);
248 Fcb
->LastCluster
= CurrentCluster
;
249 Fcb
->LastOffset
= ROUND_DOWN (ReadOffset
.u
.LowPart
, BytesPerCluster
);
250 ExReleaseFastMutex(&Fcb
->LastMutex
);
252 KeInitializeEvent(&IrpContext
->Event
, NotificationEvent
, FALSE
);
253 IrpContext
->RefCount
= 1;
255 while (Length
> 0 && CurrentCluster
!= 0xffffffff)
257 StartCluster
= CurrentCluster
;
258 StartOffset
.QuadPart
= ClusterToSector(DeviceExt
, StartCluster
) * BytesPerSector
;
267 BytesDone
= min (Length
, BytesPerCluster
- (ReadOffset
.u
.LowPart
% BytesPerCluster
));
268 StartOffset
.QuadPart
+= ReadOffset
.u
.LowPart
% BytesPerCluster
;
273 if (Length
- BytesDone
> BytesPerCluster
)
275 BytesDone
+= BytesPerCluster
;
282 Status
= NextCluster(DeviceExt
, FirstCluster
, &CurrentCluster
, FALSE
);
284 while (StartCluster
+ ClusterCount
== CurrentCluster
&& NT_SUCCESS(Status
) && Length
> BytesDone
);
285 DPRINT("start %08x, next %08x, count %u\n",
286 StartCluster
, CurrentCluster
, ClusterCount
);
288 ExAcquireFastMutex(&Fcb
->LastMutex
);
289 Fcb
->LastCluster
= StartCluster
+ (ClusterCount
- 1);
290 Fcb
->LastOffset
= ROUND_DOWN(ReadOffset
.u
.LowPart
, BytesPerCluster
) + (ClusterCount
- 1) * BytesPerCluster
;
291 ExReleaseFastMutex(&Fcb
->LastMutex
);
293 /* Fire up the read command */
294 Status
= VfatReadDiskPartial (IrpContext
, &StartOffset
, BytesDone
, *LengthRead
, FALSE
);
295 if (!NT_SUCCESS(Status
) && Status
!= STATUS_PENDING
)
299 *LengthRead
+= BytesDone
;
301 ReadOffset
.u
.LowPart
+= BytesDone
;
304 if (InterlockedDecrement((PLONG
)&IrpContext
->RefCount
) != 0)
306 KeWaitForSingleObject(&IrpContext
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
309 if (NT_SUCCESS(Status
) || Status
== STATUS_PENDING
)
313 Status
= STATUS_UNSUCCESSFUL
;
317 Status
= IrpContext
->Irp
->IoStatus
.Status
;
327 PVFAT_IRP_CONTEXT IrpContext
,
329 LARGE_INTEGER WriteOffset
)
331 PDEVICE_EXTENSION DeviceExt
;
335 ULONG CurrentCluster
;
339 NTSTATUS Status
= STATUS_SUCCESS
;
340 BOOLEAN First
= TRUE
;
341 ULONG BytesPerSector
;
342 ULONG BytesPerCluster
;
343 LARGE_INTEGER StartOffset
;
350 DeviceExt
= IrpContext
->DeviceExt
;
352 ASSERT(DeviceExt
->FatInfo
.BytesPerCluster
);
353 ASSERT(IrpContext
->FileObject
);
354 ASSERT(IrpContext
->FileObject
->FsContext2
!= NULL
);
356 Fcb
= IrpContext
->FileObject
->FsContext
;
357 BytesPerCluster
= DeviceExt
->FatInfo
.BytesPerCluster
;
358 BytesPerSector
= DeviceExt
->FatInfo
.BytesPerSector
;
360 DPRINT("VfatWriteFileData(DeviceExt %p, FileObject %p, "
361 "Length %u, WriteOffset 0x%I64x), '%wZ'\n", DeviceExt
,
362 IrpContext
->FileObject
, Length
, WriteOffset
.QuadPart
,
365 ASSERT(WriteOffset
.QuadPart
+ Length
<= Fcb
->RFCB
.AllocationSize
.QuadPart
);
366 ASSERT(WriteOffset
.u
.LowPart
% BytesPerSector
== 0);
367 ASSERT(Length
% BytesPerSector
== 0);
369 /* Is this a write of the volume? */
370 if (Fcb
->Flags
& FCB_IS_VOLUME
)
372 Status
= VfatWriteDiskPartial(IrpContext
, &WriteOffset
, Length
, 0, TRUE
);
373 if (!NT_SUCCESS(Status
))
375 DPRINT1("Volume writing failed, Status %x\n", Status
);
380 /* Is this a write to the FAT? */
381 if (Fcb
->Flags
& FCB_IS_FAT
)
383 WriteOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.FATStart
* BytesPerSector
;
384 IrpContext
->RefCount
= 1;
385 for (Count
= 0; Count
< DeviceExt
->FatInfo
.FATCount
; Count
++)
387 Status
= VfatWriteDiskPartial(IrpContext
, &WriteOffset
, Length
, 0, FALSE
);
388 if (!NT_SUCCESS(Status
) && Status
!= STATUS_PENDING
)
390 DPRINT1("FAT writing failed, Status %x\n", Status
);
393 WriteOffset
.u
.LowPart
+= Fcb
->RFCB
.FileSize
.u
.LowPart
;
396 if (InterlockedDecrement((PLONG
)&IrpContext
->RefCount
) != 0)
398 KeWaitForSingleObject(&IrpContext
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
401 if (NT_SUCCESS(Status
) || Status
== STATUS_PENDING
)
403 Status
= IrpContext
->Irp
->IoStatus
.Status
;
409 * Find the first cluster
412 CurrentCluster
= vfatDirEntryGetFirstCluster (DeviceExt
, &Fcb
->entry
);
414 if (FirstCluster
== 1)
416 ASSERT(WriteOffset
.u
.LowPart
+ Length
<= DeviceExt
->FatInfo
.rootDirectorySectors
* BytesPerSector
);
417 // Directory of FAT12/16 needs a special handling
418 WriteOffset
.u
.LowPart
+= DeviceExt
->FatInfo
.rootStart
* BytesPerSector
;
419 // Fire up the write command
420 Status
= VfatWriteDiskPartial (IrpContext
, &WriteOffset
, Length
, 0, TRUE
);
424 ExAcquireFastMutex(&Fcb
->LastMutex
);
425 LastCluster
= Fcb
->LastCluster
;
426 LastOffset
= Fcb
->LastOffset
;
427 ExReleaseFastMutex(&Fcb
->LastMutex
);
430 * Find the cluster to start the write from
432 if (LastCluster
> 0 && WriteOffset
.u
.LowPart
>= LastOffset
)
434 Status
= OffsetToCluster(DeviceExt
, LastCluster
,
435 ROUND_DOWN(WriteOffset
.u
.LowPart
, BytesPerCluster
) -
437 &CurrentCluster
, FALSE
);
438 #ifdef DEBUG_VERIFY_OFFSET_CACHING
439 /* DEBUG VERIFICATION */
441 ULONG CorrectCluster
;
442 OffsetToCluster(DeviceExt
, FirstCluster
,
443 ROUND_DOWN(WriteOffset
.u
.LowPart
, BytesPerCluster
),
444 &CorrectCluster
, FALSE
);
445 if (CorrectCluster
!= CurrentCluster
)
446 KeBugCheck(FAT_FILE_SYSTEM
);
452 Status
= OffsetToCluster(DeviceExt
, FirstCluster
,
453 ROUND_DOWN(WriteOffset
.u
.LowPart
, BytesPerCluster
),
454 &CurrentCluster
, FALSE
);
457 if (!NT_SUCCESS(Status
))
462 ExAcquireFastMutex(&Fcb
->LastMutex
);
463 Fcb
->LastCluster
= CurrentCluster
;
464 Fcb
->LastOffset
= ROUND_DOWN (WriteOffset
.u
.LowPart
, BytesPerCluster
);
465 ExReleaseFastMutex(&Fcb
->LastMutex
);
467 IrpContext
->RefCount
= 1;
470 while (Length
> 0 && CurrentCluster
!= 0xffffffff)
472 StartCluster
= CurrentCluster
;
473 StartOffset
.QuadPart
= ClusterToSector(DeviceExt
, StartCluster
) * BytesPerSector
;
482 BytesDone
= min (Length
, BytesPerCluster
- (WriteOffset
.u
.LowPart
% BytesPerCluster
));
483 StartOffset
.QuadPart
+= WriteOffset
.u
.LowPart
% BytesPerCluster
;
488 if (Length
- BytesDone
> BytesPerCluster
)
490 BytesDone
+= BytesPerCluster
;
497 Status
= NextCluster(DeviceExt
, FirstCluster
, &CurrentCluster
, FALSE
);
499 while (StartCluster
+ ClusterCount
== CurrentCluster
&& NT_SUCCESS(Status
) && Length
> BytesDone
);
500 DPRINT("start %08x, next %08x, count %u\n",
501 StartCluster
, CurrentCluster
, ClusterCount
);
503 ExAcquireFastMutex(&Fcb
->LastMutex
);
504 Fcb
->LastCluster
= StartCluster
+ (ClusterCount
- 1);
505 Fcb
->LastOffset
= ROUND_DOWN(WriteOffset
.u
.LowPart
, BytesPerCluster
) + (ClusterCount
- 1) * BytesPerCluster
;
506 ExReleaseFastMutex(&Fcb
->LastMutex
);
508 // Fire up the write command
509 Status
= VfatWriteDiskPartial (IrpContext
, &StartOffset
, BytesDone
, BufferOffset
, FALSE
);
510 if (!NT_SUCCESS(Status
) && Status
!= STATUS_PENDING
)
514 BufferOffset
+= BytesDone
;
516 WriteOffset
.u
.LowPart
+= BytesDone
;
519 if (InterlockedDecrement((PLONG
)&IrpContext
->RefCount
) != 0)
521 KeWaitForSingleObject(&IrpContext
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
524 if (NT_SUCCESS(Status
) || Status
== STATUS_PENDING
)
528 Status
= STATUS_UNSUCCESSFUL
;
532 Status
= IrpContext
->Irp
->IoStatus
.Status
;
540 PVFAT_IRP_CONTEXT IrpContext
)
545 ULONG ReturnedLength
= 0;
546 PERESOURCE Resource
= NULL
;
547 LARGE_INTEGER ByteOffset
;
549 ULONG BytesPerSector
;
553 DPRINT("VfatRead(IrpContext %p)\n", IrpContext
);
555 ASSERT(IrpContext
->DeviceObject
);
557 // This request is not allowed on the main device object
558 if (IrpContext
->DeviceObject
== VfatGlobalData
->DeviceObject
)
560 DPRINT("VfatRead is called with the main device object.\n");
561 Status
= STATUS_INVALID_DEVICE_REQUEST
;
565 ASSERT(IrpContext
->DeviceExt
);
566 ASSERT(IrpContext
->FileObject
);
567 Fcb
= IrpContext
->FileObject
->FsContext
;
570 if (Fcb
->Flags
& FCB_IS_PAGE_FILE
)
572 PFATINFO FatInfo
= &IrpContext
->DeviceExt
->FatInfo
;
573 IrpContext
->Stack
->Parameters
.Read
.ByteOffset
.QuadPart
+= FatInfo
->dataStart
* FatInfo
->BytesPerSector
;
574 IoSkipCurrentIrpStackLocation(IrpContext
->Irp
);
575 DPRINT("Read from page file, disk offset %I64x\n", IrpContext
->Stack
->Parameters
.Read
.ByteOffset
.QuadPart
);
576 Status
= IoCallDriver(IrpContext
->DeviceExt
->StorageDevice
, IrpContext
->Irp
);
577 VfatFreeIrpContext(IrpContext
);
581 DPRINT("<%wZ>\n", &Fcb
->PathNameU
);
583 ByteOffset
= IrpContext
->Stack
->Parameters
.Read
.ByteOffset
;
584 Length
= IrpContext
->Stack
->Parameters
.Read
.Length
;
585 BytesPerSector
= IrpContext
->DeviceExt
->FatInfo
.BytesPerSector
;
587 /* fail if file is a directory and no paged read */
588 if (*Fcb
->Attributes
& FILE_ATTRIBUTE_DIRECTORY
&& !(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
))
590 Status
= STATUS_INVALID_PARAMETER
;
594 DPRINT("'%wZ', Offset: %u, Length %u\n", &Fcb
->PathNameU
, ByteOffset
.u
.LowPart
, Length
);
596 if (ByteOffset
.u
.HighPart
&& !(Fcb
->Flags
& FCB_IS_VOLUME
))
598 Status
= STATUS_INVALID_PARAMETER
;
602 if (ByteOffset
.QuadPart
>= Fcb
->RFCB
.FileSize
.QuadPart
)
604 IrpContext
->Irp
->IoStatus
.Information
= 0;
605 Status
= STATUS_END_OF_FILE
;
609 if (IrpContext
->Irp
->Flags
& (IRP_PAGING_IO
| IRP_NOCACHE
) || (Fcb
->Flags
& FCB_IS_VOLUME
))
611 if (ByteOffset
.u
.LowPart
% BytesPerSector
!= 0 || Length
% BytesPerSector
!= 0)
613 DPRINT("%u %u\n", ByteOffset
.u
.LowPart
, Length
);
614 // non cached read must be sector aligned
615 Status
= STATUS_INVALID_PARAMETER
;
622 IrpContext
->Irp
->IoStatus
.Information
= 0;
623 Status
= STATUS_SUCCESS
;
627 if (Fcb
->Flags
& FCB_IS_VOLUME
)
629 Resource
= &IrpContext
->DeviceExt
->DirResource
;
631 else if (IrpContext
->Irp
->Flags
& IRP_PAGING_IO
)
633 Resource
= &Fcb
->PagingIoResource
;
637 Resource
= &Fcb
->MainResource
;
640 if (!ExAcquireResourceSharedLite(Resource
,
641 IrpContext
->Flags
& IRPCONTEXT_CANWAIT
? TRUE
: FALSE
))
644 Status
= STATUS_PENDING
;
648 if (!(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) &&
649 FsRtlAreThereCurrentFileLocks(&Fcb
->FileLock
))
651 if (!FsRtlCheckLockForReadAccess(&Fcb
->FileLock
, IrpContext
->Irp
))
653 Status
= STATUS_FILE_LOCK_CONFLICT
;
658 Buffer
= VfatGetUserBuffer(IrpContext
->Irp
);
661 Status
= STATUS_INVALID_USER_BUFFER
;
665 if (!(IrpContext
->Irp
->Flags
& (IRP_NOCACHE
|IRP_PAGING_IO
)) &&
666 !(Fcb
->Flags
& (FCB_IS_PAGE_FILE
|FCB_IS_VOLUME
)))
669 Status
= STATUS_SUCCESS
;
670 if (ByteOffset
.u
.LowPart
+ Length
> Fcb
->RFCB
.FileSize
.u
.LowPart
)
672 Length
= Fcb
->RFCB
.FileSize
.u
.LowPart
- ByteOffset
.u
.LowPart
;
673 Status
= /*STATUS_END_OF_FILE*/STATUS_SUCCESS
;
678 if (IrpContext
->FileObject
->PrivateCacheMap
== NULL
)
680 CcInitializeCacheMap(IrpContext
->FileObject
,
681 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
683 &(VfatGlobalData
->CacheMgrCallbacks
),
687 if (!CcCopyRead(IrpContext
->FileObject
,
690 (IrpContext
->Flags
& IRPCONTEXT_CANWAIT
) != 0,
692 &IrpContext
->Irp
->IoStatus
))
694 ASSERT((IrpContext
->Flags
& IRPCONTEXT_CANWAIT
) == 0);
695 Status
= STATUS_PENDING
;
699 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
701 Status
= _SEH2_GetExceptionCode();
706 if (!NT_SUCCESS(IrpContext
->Irp
->IoStatus
.Status
))
708 Status
= IrpContext
->Irp
->IoStatus
.Status
;
714 if (ByteOffset
.QuadPart
+ Length
> ROUND_UP(Fcb
->RFCB
.FileSize
.QuadPart
, BytesPerSector
))
716 Length
= (ULONG
)(ROUND_UP(Fcb
->RFCB
.FileSize
.QuadPart
, BytesPerSector
) - ByteOffset
.QuadPart
);
719 Status
= VfatLockUserBuffer(IrpContext
->Irp
, Length
, IoWriteAccess
);
720 if (!NT_SUCCESS(Status
))
725 Status
= VfatReadFileData(IrpContext
, Length
, ByteOffset
, &ReturnedLength
);
726 if (NT_SUCCESS(Status
))
728 IrpContext
->Irp
->IoStatus
.Information
= ReturnedLength
;
735 ExReleaseResourceLite(Resource
);
738 if (Status
== STATUS_PENDING
)
740 Status
= VfatLockUserBuffer(IrpContext
->Irp
, Length
, IoWriteAccess
);
741 if (NT_SUCCESS(Status
))
743 Status
= VfatQueueRequest(IrpContext
);
747 IrpContext
->Irp
->IoStatus
.Status
= Status
;
748 IoCompleteRequest(IrpContext
->Irp
, IO_NO_INCREMENT
);
749 VfatFreeIrpContext(IrpContext
);
754 IrpContext
->Irp
->IoStatus
.Status
= Status
;
755 if (IrpContext
->FileObject
->Flags
& FO_SYNCHRONOUS_IO
&&
756 !(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) &&
757 (NT_SUCCESS(Status
) || Status
== STATUS_END_OF_FILE
))
759 IrpContext
->FileObject
->CurrentByteOffset
.QuadPart
=
760 ByteOffset
.QuadPart
+ IrpContext
->Irp
->IoStatus
.Information
;
763 IoCompleteRequest(IrpContext
->Irp
,
764 (CCHAR
)(NT_SUCCESS(Status
) ? IO_DISK_INCREMENT
: IO_NO_INCREMENT
));
765 VfatFreeIrpContext(IrpContext
);
767 DPRINT("%x\n", Status
);
773 PVFAT_IRP_CONTEXT IrpContext
)
776 PERESOURCE Resource
= NULL
;
777 LARGE_INTEGER ByteOffset
;
778 LARGE_INTEGER OldFileSize
;
779 NTSTATUS Status
= STATUS_SUCCESS
;
782 ULONG BytesPerSector
;
786 DPRINT("VfatWrite(IrpContext %p)\n", IrpContext
);
788 ASSERT(IrpContext
->DeviceObject
);
790 // This request is not allowed on the main device object
791 if (IrpContext
->DeviceObject
== VfatGlobalData
->DeviceObject
)
793 DPRINT("VfatWrite is called with the main device object.\n");
794 Status
= STATUS_INVALID_DEVICE_REQUEST
;
798 ASSERT(IrpContext
->DeviceExt
);
799 ASSERT(IrpContext
->FileObject
);
800 Fcb
= IrpContext
->FileObject
->FsContext
;
803 if (Fcb
->Flags
& FCB_IS_PAGE_FILE
)
805 PFATINFO FatInfo
= &IrpContext
->DeviceExt
->FatInfo
;
806 IrpContext
->Stack
->Parameters
.Write
.ByteOffset
.QuadPart
+= FatInfo
->dataStart
* FatInfo
->BytesPerSector
;
807 IoSkipCurrentIrpStackLocation(IrpContext
->Irp
);
808 DPRINT("Write to page file, disk offset %I64x\n", IrpContext
->Stack
->Parameters
.Write
.ByteOffset
.QuadPart
);
809 Status
= IoCallDriver(IrpContext
->DeviceExt
->StorageDevice
, IrpContext
->Irp
);
810 VfatFreeIrpContext(IrpContext
);
814 DPRINT("<%wZ>\n", &Fcb
->PathNameU
);
816 /* fail if file is a directory and no paged read */
817 if (*Fcb
->Attributes
& FILE_ATTRIBUTE_DIRECTORY
&& !(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
))
819 Status
= STATUS_INVALID_PARAMETER
;
823 ByteOffset
= IrpContext
->Stack
->Parameters
.Write
.ByteOffset
;
824 if (ByteOffset
.u
.LowPart
== FILE_WRITE_TO_END_OF_FILE
&&
825 ByteOffset
.u
.HighPart
== -1)
827 ByteOffset
.QuadPart
= Fcb
->RFCB
.FileSize
.QuadPart
;
829 Length
= IrpContext
->Stack
->Parameters
.Write
.Length
;
830 BytesPerSector
= IrpContext
->DeviceExt
->FatInfo
.BytesPerSector
;
832 if (ByteOffset
.u
.HighPart
&& !(Fcb
->Flags
& FCB_IS_VOLUME
))
834 Status
= STATUS_INVALID_PARAMETER
;
838 if (Fcb
->Flags
& (FCB_IS_FAT
| FCB_IS_VOLUME
) ||
839 vfatDirEntryGetFirstCluster(IrpContext
->DeviceExt
, &Fcb
->entry
) == 1)
841 if (ByteOffset
.QuadPart
+ Length
> Fcb
->RFCB
.FileSize
.QuadPart
)
843 // we can't extend the FAT, the volume or the root on FAT12/FAT16
844 Status
= STATUS_END_OF_FILE
;
849 if (IrpContext
->Irp
->Flags
& (IRP_PAGING_IO
|IRP_NOCACHE
) || (Fcb
->Flags
& FCB_IS_VOLUME
))
851 if (ByteOffset
.u
.LowPart
% BytesPerSector
!= 0 || Length
% BytesPerSector
!= 0)
853 // non cached write must be sector aligned
854 Status
= STATUS_INVALID_PARAMETER
;
861 /* FIXME: Update last write time */
862 IrpContext
->Irp
->IoStatus
.Information
= 0;
863 Status
= STATUS_SUCCESS
;
867 if (IrpContext
->Irp
->Flags
& IRP_PAGING_IO
)
869 if (ByteOffset
.u
.LowPart
+ Length
> Fcb
->RFCB
.AllocationSize
.u
.LowPart
)
871 Status
= STATUS_INVALID_PARAMETER
;
875 if (ByteOffset
.u
.LowPart
+ Length
> ROUND_UP(Fcb
->RFCB
.AllocationSize
.u
.LowPart
, BytesPerSector
))
877 Length
= ROUND_UP(Fcb
->RFCB
.FileSize
.u
.LowPart
, BytesPerSector
) - ByteOffset
.u
.LowPart
;
881 if (Fcb
->Flags
& FCB_IS_VOLUME
)
883 Resource
= &IrpContext
->DeviceExt
->DirResource
;
885 else if (IrpContext
->Irp
->Flags
& IRP_PAGING_IO
)
887 Resource
= &Fcb
->PagingIoResource
;
891 Resource
= &Fcb
->MainResource
;
894 if (Fcb
->Flags
& FCB_IS_PAGE_FILE
)
896 if (!ExAcquireResourceSharedLite(Resource
,
897 (BOOLEAN
)(IrpContext
->Flags
& IRPCONTEXT_CANWAIT
)))
900 Status
= STATUS_PENDING
;
906 if (!ExAcquireResourceExclusiveLite(Resource
,
907 (BOOLEAN
)(IrpContext
->Flags
& IRPCONTEXT_CANWAIT
)))
910 Status
= STATUS_PENDING
;
915 if (!(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) &&
916 FsRtlAreThereCurrentFileLocks(&Fcb
->FileLock
))
918 if (!FsRtlCheckLockForWriteAccess(&Fcb
->FileLock
, IrpContext
->Irp
))
920 Status
= STATUS_FILE_LOCK_CONFLICT
;
925 if (!(IrpContext
->Flags
& IRPCONTEXT_CANWAIT
) && !(Fcb
->Flags
& FCB_IS_VOLUME
))
927 if (ByteOffset
.u
.LowPart
+ Length
> Fcb
->RFCB
.AllocationSize
.u
.LowPart
)
929 Status
= STATUS_PENDING
;
934 OldFileSize
= Fcb
->RFCB
.FileSize
;
936 Buffer
= VfatGetUserBuffer(IrpContext
->Irp
);
939 Status
= STATUS_INVALID_USER_BUFFER
;
944 if (!(Fcb
->Flags
& (FCB_IS_FAT
|FCB_IS_VOLUME
)) &&
945 !(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) &&
946 ByteOffset
.u
.LowPart
+ Length
> Fcb
->RFCB
.FileSize
.u
.LowPart
)
948 LARGE_INTEGER AllocationSize
;
949 AllocationSize
.QuadPart
= ByteOffset
.u
.LowPart
+ Length
;
950 Status
= VfatSetAllocationSizeInformation(IrpContext
->FileObject
, Fcb
,
951 IrpContext
->DeviceExt
, &AllocationSize
);
952 if (!NT_SUCCESS (Status
))
958 if (!(IrpContext
->Irp
->Flags
& (IRP_NOCACHE
|IRP_PAGING_IO
)) &&
959 !(Fcb
->Flags
& (FCB_IS_PAGE_FILE
|FCB_IS_VOLUME
)))
965 if (IrpContext
->FileObject
->PrivateCacheMap
== NULL
)
967 CcInitializeCacheMap(IrpContext
->FileObject
,
968 (PCC_FILE_SIZES
)(&Fcb
->RFCB
.AllocationSize
),
970 &VfatGlobalData
->CacheMgrCallbacks
,
974 if (ByteOffset
.QuadPart
> OldFileSize
.QuadPart
)
976 CcZeroData(IrpContext
->FileObject
, &OldFileSize
, &ByteOffset
, TRUE
);
979 if (CcCopyWrite(IrpContext
->FileObject
,
982 TRUE
/*(IrpContext->Flags & IRPCONTEXT_CANWAIT) != 0*/,
985 IrpContext
->Irp
->IoStatus
.Information
= Length
;
986 Status
= STATUS_SUCCESS
;
990 ASSERT(FALSE
/*(IrpContext->Flags & IRPCONTEXT_CANWAIT) == 0*/);
991 Status
= STATUS_UNSUCCESSFUL
;
994 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
996 Status
= _SEH2_GetExceptionCode();
1004 if (ByteOffset
.QuadPart
> OldFileSize
.QuadPart
)
1006 CcZeroData(IrpContext
->FileObject
, &OldFileSize
, &ByteOffset
, TRUE
);
1009 Status
= VfatLockUserBuffer(IrpContext
->Irp
, Length
, IoReadAccess
);
1010 if (!NT_SUCCESS(Status
))
1015 Status
= VfatWriteFileData(IrpContext
, Length
, ByteOffset
);
1016 if (NT_SUCCESS(Status
))
1018 IrpContext
->Irp
->IoStatus
.Information
= Length
;
1022 if (!(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) &&
1023 !(Fcb
->Flags
& (FCB_IS_FAT
|FCB_IS_VOLUME
)))
1025 if(!(*Fcb
->Attributes
& FILE_ATTRIBUTE_DIRECTORY
))
1027 LARGE_INTEGER SystemTime
;
1030 // set dates and times
1031 KeQuerySystemTime (&SystemTime
);
1032 if (Fcb
->Flags
& FCB_IS_FATX_ENTRY
)
1034 FsdSystemTimeToDosDateTime(IrpContext
->DeviceExt
,
1035 &SystemTime
, &Fcb
->entry
.FatX
.UpdateDate
,
1036 &Fcb
->entry
.FatX
.UpdateTime
);
1037 Fcb
->entry
.FatX
.AccessDate
= Fcb
->entry
.FatX
.UpdateDate
;
1038 Fcb
->entry
.FatX
.AccessTime
= Fcb
->entry
.FatX
.UpdateTime
;
1042 FsdSystemTimeToDosDateTime(IrpContext
->DeviceExt
,
1043 &SystemTime
, &Fcb
->entry
.Fat
.UpdateDate
,
1044 &Fcb
->entry
.Fat
.UpdateTime
);
1045 Fcb
->entry
.Fat
.AccessDate
= Fcb
->entry
.Fat
.UpdateDate
;
1047 /* set date and times to dirty */
1048 Fcb
->Flags
|= FCB_IS_DIRTY
;
1050 /* Time to notify the OS */
1051 Filter
= FILE_NOTIFY_CHANGE_LAST_WRITE
| FILE_NOTIFY_CHANGE_ATTRIBUTES
;
1052 if (ByteOffset
.QuadPart
!= OldFileSize
.QuadPart
) Filter
|= FILE_NOTIFY_CHANGE_SIZE
;
1054 FsRtlNotifyFullReportChange(IrpContext
->DeviceExt
->NotifySync
,
1055 &(IrpContext
->DeviceExt
->NotifyList
),
1056 (PSTRING
)&Fcb
->PathNameU
,
1057 Fcb
->PathNameU
.Length
- Fcb
->LongNameU
.Length
,
1061 FILE_ACTION_MODIFIED
,
1069 ExReleaseResourceLite(Resource
);
1072 if (Status
== STATUS_PENDING
)
1074 Status
= VfatLockUserBuffer(IrpContext
->Irp
, Length
, IoReadAccess
);
1075 if (NT_SUCCESS(Status
))
1077 Status
= VfatQueueRequest(IrpContext
);
1081 IrpContext
->Irp
->IoStatus
.Status
= Status
;
1082 IoCompleteRequest(IrpContext
->Irp
, IO_NO_INCREMENT
);
1083 VfatFreeIrpContext(IrpContext
);
1088 IrpContext
->Irp
->IoStatus
.Status
= Status
;
1089 if (IrpContext
->FileObject
->Flags
& FO_SYNCHRONOUS_IO
&&
1090 !(IrpContext
->Irp
->Flags
& IRP_PAGING_IO
) && NT_SUCCESS(Status
))
1092 IrpContext
->FileObject
->CurrentByteOffset
.QuadPart
=
1093 ByteOffset
.QuadPart
+ IrpContext
->Irp
->IoStatus
.Information
;
1096 IoCompleteRequest(IrpContext
->Irp
,
1097 (CCHAR
)(NT_SUCCESS(Status
) ? IO_DISK_INCREMENT
: IO_NO_INCREMENT
));
1098 VfatFreeIrpContext(IrpContext
);
1100 DPRINT("%x\n", Status
);