3 * Copyright (C) 2017 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * FILE: sdk/lib/drivers/copysup/copysup.c
23 * PURPOSE: CopySup library
24 * PROGRAMMER: Pierre Schweitzer (pierre@reactos.org)
27 /* INCLUDES *****************************************************************/
30 #include <pseh/pseh2.h>
34 /* FUNCTIONS ****************************************************************/
41 IN PFILE_OBJECT FileObject
,
42 IN PLARGE_INTEGER FileOffset
,
47 OUT PIO_STATUS_BLOCK IoStatus
,
48 IN PDEVICE_OBJECT DeviceObject
,
49 IN PVOID TopLevelContext
)
53 LARGE_INTEGER FinalOffset
;
54 PFSRTL_COMMON_FCB_HEADER Fcb
;
55 PFAST_IO_DISPATCH FastIoDispatch
;
56 PDEVICE_OBJECT RelatedDeviceObject
;
61 PageCount
= ADDRESS_AND_SIZE_TO_SPAN_PAGES(FileOffset
, Length
);
63 /* Null-length read is always OK */
66 IoStatus
->Information
= 0;
67 IoStatus
->Status
= STATUS_SUCCESS
;
72 /* Check we don't overflow */
73 FinalOffset
.QuadPart
= FileOffset
->QuadPart
+ Length
;
74 if (FinalOffset
.QuadPart
<= 0)
79 /* Get the FCB (at least, its header) */
80 Fcb
= FileObject
->FsContext
;
82 FsRtlEnterFileSystem();
84 /* Acquire its resource (shared) */
87 ExAcquireResourceSharedLite(Fcb
->Resource
, TRUE
);
91 if (!ExAcquireResourceSharedLite(Fcb
->Resource
, FALSE
))
98 /* If cache wasn't initialized, or FastIO isn't possible, fail */
99 if (FileObject
->PrivateCacheMap
== NULL
|| Fcb
->IsFastIoPossible
== FastIoIsNotPossible
)
105 /* If FastIO is questionable, then, question! */
106 if (Fcb
->IsFastIoPossible
== FastIoIsQuestionable
)
108 RelatedDeviceObject
= IoGetRelatedDeviceObject(FileObject
);
109 FastIoDispatch
= RelatedDeviceObject
->DriverObject
->FastIoDispatch
;
110 ASSERT(FastIoDispatch
!= NULL
);
111 ASSERT(FastIoDispatch
->FastIoCheckIfPossible
!= NULL
);
113 /* If it's not possible, then fail */
114 if (!FastIoDispatch
->FastIoCheckIfPossible(FileObject
, FileOffset
, Length
,
115 Wait
, LockKey
, TRUE
, IoStatus
, RelatedDeviceObject
))
122 /* If we get beyond file end... */
123 if (FinalOffset
.QuadPart
> Fcb
->FileSize
.QuadPart
)
125 /* Fail if the offset was already beyond file end */
126 if (FileOffset
->QuadPart
>= Fcb
->FileSize
.QuadPart
)
128 IoStatus
->Information
= 0;
129 IoStatus
->Status
= STATUS_END_OF_FILE
;
133 /* Otherwise, just fix read length */
134 Length
= (ULONG
)(Fcb
->FileSize
.QuadPart
- FileOffset
->QuadPart
);
137 /* Set caller provided context as TLI */
138 IoSetTopLevelIrp(TopLevelContext
);
142 /* If we cannot wait, or if file is bigger than 4GB */
143 if (!Wait
|| (FinalOffset
.HighPart
| Fcb
->FileSize
.HighPart
) != 0)
146 Ret
= CcCopyRead(FileObject
, FileOffset
, Length
, Wait
, Buffer
, IoStatus
);
147 SetFlag(FileObject
->Flags
, FO_FILE_FAST_IO_READ
);
149 /* Validate output */
150 ASSERT(!Ret
|| (IoStatus
->Status
== STATUS_END_OF_FILE
) || (((ULONGLONG
)FileOffset
->QuadPart
+ IoStatus
->Information
) <= (ULONGLONG
)Fcb
->FileSize
.QuadPart
));
155 CcFastCopyRead(FileObject
, FileOffset
->LowPart
, Length
, PageCount
, Buffer
, IoStatus
);
156 SetFlag(FileObject
->Flags
, FO_FILE_FAST_IO_READ
);
158 /* Validate output */
159 ASSERT((IoStatus
->Status
== STATUS_END_OF_FILE
) || ((FileOffset
->LowPart
+ IoStatus
->Information
) <= Fcb
->FileSize
.LowPart
));
162 /* If read was successful, update the byte offset in the FO */
165 FileObject
->CurrentByteOffset
.QuadPart
= FileOffset
->QuadPart
+ IoStatus
->Information
;
168 _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
169 EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH
)
176 IoSetTopLevelIrp(NULL
);
179 ExReleaseResourceLite(Fcb
->Resource
);
181 FsRtlExitFileSystem();
191 IN PFILE_OBJECT FileObject
,
192 IN PLARGE_INTEGER FileOffset
,
197 OUT PIO_STATUS_BLOCK IoStatus
,
198 IN PDEVICE_OBJECT DeviceObject
,
199 IN PVOID TopLevelContext
)
201 IO_STATUS_BLOCK LocalIoStatus
;
202 PFSRTL_ADVANCED_FCB_HEADER Fcb
;
203 BOOLEAN WriteToEof
, AcquiredShared
, FileSizeChanged
, Ret
;
204 LARGE_INTEGER WriteOffset
, LastOffset
, InitialFileSize
, InitialValidDataLength
;
208 /* First, check whether we're writing to EOF */
209 WriteToEof
= ((FileOffset
->LowPart
== FILE_WRITE_TO_END_OF_FILE
) &&
210 (FileOffset
->HighPart
== -1));
212 /* If Cc says we cannot write, fail now */
213 if (!CcCanIWrite(FileObject
, Length
, Wait
, FALSE
))
218 /* Write through means no cache */
219 if (BooleanFlagOn(FileObject
->Flags
, FO_WRITE_THROUGH
))
224 /* If write is > 64Kb, don't use FastIO */
225 if (!CcCopyWriteWontFlush(FileObject
, FileOffset
, Length
))
230 /* Initialize the IO_STATUS_BLOCK */
231 IoStatus
->Status
= STATUS_SUCCESS
;
232 IoStatus
->Information
= Length
;
234 /* No length, it's already written! */
240 AcquiredShared
= FALSE
;
241 FileSizeChanged
= FALSE
;
242 Fcb
= FileObject
->FsContext
;
244 FsRtlEnterFileSystem();
246 /* If we cannot wait, or deal with files bigger then 4GB */
247 if (!Wait
|| (Fcb
->AllocationSize
.HighPart
!= 0))
249 /* If we're to extend the file, then, acquire exclusively */
250 if (WriteToEof
|| FileOffset
->QuadPart
+ Length
> Fcb
->ValidDataLength
.QuadPart
)
252 if (!ExAcquireResourceExclusiveLite(Fcb
->Resource
, Wait
))
254 FsRtlExitFileSystem();
258 /* Otherwise, a shared lock is enough */
261 if (!ExAcquireResourceSharedLite(Fcb
->Resource
, Wait
))
263 FsRtlExitFileSystem();
267 AcquiredShared
= TRUE
;
270 /* Get first write offset, and last */
273 WriteOffset
.QuadPart
= Fcb
->FileSize
.QuadPart
;
274 LastOffset
.QuadPart
= WriteOffset
.QuadPart
+ Length
;
278 WriteOffset
.QuadPart
= FileOffset
->QuadPart
;
279 LastOffset
.QuadPart
= WriteOffset
.QuadPart
+ Length
;
282 /* If cache wasn't initialized, fail */
283 if (FileObject
->PrivateCacheMap
== NULL
||
284 Fcb
->IsFastIoPossible
== FastIoIsNotPossible
)
286 ExReleaseResourceLite(Fcb
->Resource
);
287 FsRtlExitFileSystem();
292 /* If we're to write beyond allocation size, it's no go,
293 * same is we create a hole bigger than 8kb
295 if ((Fcb
->ValidDataLength
.QuadPart
+ 0x2000 <= WriteOffset
.QuadPart
) ||
296 (Length
> MAXLONGLONG
- WriteOffset
.QuadPart
) ||
297 (Fcb
->AllocationSize
.QuadPart
< LastOffset
.QuadPart
))
299 ExReleaseResourceLite(Fcb
->Resource
);
300 FsRtlExitFileSystem();
305 /* If we have to extend the VDL, shared lock isn't enough */
306 if (AcquiredShared
&& LastOffset
.QuadPart
> Fcb
->ValidDataLength
.QuadPart
)
308 /* So release, and attempt to acquire exclusively */
309 ExReleaseResourceLite(Fcb
->Resource
);
310 if (!ExAcquireResourceExclusiveLite(Fcb
->Resource
, Wait
))
312 FsRtlExitFileSystem();
316 /* Get again EOF, in case file size changed in between */
319 WriteOffset
.QuadPart
= Fcb
->FileSize
.QuadPart
;
320 LastOffset
.QuadPart
= WriteOffset
.QuadPart
+ Length
;
323 /* Make sure caching is still enabled */
324 if (FileObject
->PrivateCacheMap
== NULL
||
325 Fcb
->IsFastIoPossible
== FastIoIsNotPossible
)
327 ExReleaseResourceLite(Fcb
->Resource
);
328 FsRtlExitFileSystem();
333 /* And that we're not writting beyond allocation size */
334 if (Fcb
->AllocationSize
.QuadPart
< LastOffset
.QuadPart
)
336 ExReleaseResourceLite(Fcb
->Resource
);
337 FsRtlExitFileSystem();
343 /* If FastIO is questionable, then question */
344 if (Fcb
->IsFastIoPossible
== FastIoIsQuestionable
)
346 PFAST_IO_DISPATCH FastIoDispatch
;
347 PDEVICE_OBJECT RelatedDeviceObject
;
349 RelatedDeviceObject
= IoGetRelatedDeviceObject(FileObject
);
350 FastIoDispatch
= RelatedDeviceObject
->DriverObject
->FastIoDispatch
;
351 ASSERT(FastIoDispatch
!= NULL
);
352 ASSERT(FastIoDispatch
->FastIoCheckIfPossible
!= NULL
);
354 if (!FastIoDispatch
->FastIoCheckIfPossible(FileObject
,
356 Length
, Wait
, LockKey
,
357 FALSE
, &LocalIoStatus
,
358 RelatedDeviceObject
))
360 ExReleaseResourceLite(Fcb
->Resource
);
361 FsRtlExitFileSystem();
367 /* If we write beyond EOF, then, save previous sizes (in case of failure)
368 * and update file size, to allow writing
370 if (LastOffset
.QuadPart
> Fcb
->FileSize
.QuadPart
)
372 FileSizeChanged
= TRUE
;
373 InitialFileSize
.QuadPart
= Fcb
->FileSize
.QuadPart
;
374 InitialValidDataLength
.QuadPart
= Fcb
->ValidDataLength
.QuadPart
;
376 if (LastOffset
.HighPart
!= Fcb
->FileSize
.HighPart
&&
377 Fcb
->PagingIoResource
!= NULL
)
379 ExAcquireResourceExclusiveLite(Fcb
->PagingIoResource
, TRUE
);
380 Fcb
->FileSize
.QuadPart
= LastOffset
.QuadPart
;
381 ExReleaseResourceLite(Fcb
->PagingIoResource
);
385 Fcb
->FileSize
.QuadPart
= LastOffset
.QuadPart
;
389 /* Set caller provided context as top level IRP */
390 IoSetTopLevelIrp(TopLevelContext
);
394 /* And perform the writing */
397 /* Check whether we've to create a hole first */
398 if (LastOffset
.QuadPart
> Fcb
->ValidDataLength
.QuadPart
)
400 Ret
= CcZeroData(FileObject
, &Fcb
->ValidDataLength
,
404 /* If not needed, or if it worked, write data */
407 Ret
= CcCopyWrite(FileObject
, &WriteOffset
,
408 Length
, Wait
, Buffer
);
411 _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
412 EXCEPTION_EXECUTE_HANDLER
:
413 EXCEPTION_CONTINUE_SEARCH
)
419 /* Restore top level IRP */
420 IoSetTopLevelIrp(NULL
);
422 /* If writing succeed */
425 /* If we wrote beyond VDL, update it */
426 if (LastOffset
.QuadPart
> Fcb
->ValidDataLength
.QuadPart
)
428 if (LastOffset
.HighPart
!= Fcb
->ValidDataLength
.HighPart
&&
429 Fcb
->PagingIoResource
!= NULL
)
431 ExAcquireResourceExclusiveLite(Fcb
->PagingIoResource
, TRUE
);
432 Fcb
->ValidDataLength
.QuadPart
= LastOffset
.QuadPart
;
433 ExReleaseResourceLite(Fcb
->PagingIoResource
);
437 Fcb
->ValidDataLength
.QuadPart
= LastOffset
.QuadPart
;
441 /* File was obviously modified */
442 SetFlag(FileObject
->Flags
, FO_FILE_MODIFIED
);
444 /* And if we increased it, modify size in Cc and update FO */
447 (*CcGetFileSizePointer(FileObject
)).QuadPart
= LastOffset
.QuadPart
;
448 SetFlag(FileObject
->Flags
, FO_FILE_SIZE_CHANGED
);
452 FileObject
->CurrentByteOffset
.QuadPart
= WriteOffset
.QuadPart
+ Length
;
456 /* We failed, we need to restore previous sizes */
459 if (Fcb
->PagingIoResource
!= NULL
)
461 ExAcquireResourceExclusiveLite(Fcb
->PagingIoResource
, TRUE
);
462 Fcb
->FileSize
.QuadPart
= InitialFileSize
.QuadPart
;
463 Fcb
->ValidDataLength
.QuadPart
= InitialValidDataLength
.QuadPart
;
464 ExReleaseResourceLite(Fcb
->PagingIoResource
);
468 Fcb
->FileSize
.QuadPart
= InitialFileSize
.QuadPart
;
469 Fcb
->ValidDataLength
.QuadPart
= InitialValidDataLength
.QuadPart
;
478 WriteOffset
.HighPart
= 0;
479 LastOffset
.HighPart
= 0;
481 /* If we're to extend the file, then, acquire exclusively
482 * Here, easy stuff, we know we can wait, no return to check!
484 if (WriteToEof
|| FileOffset
->QuadPart
+ Length
> Fcb
->ValidDataLength
.QuadPart
)
486 ExAcquireResourceExclusiveLite(Fcb
->Resource
, TRUE
);
488 /* Otherwise, a shared lock is enough */
491 ExAcquireResourceSharedLite(Fcb
->Resource
, TRUE
);
492 AcquiredShared
= TRUE
;
495 /* Get first write offset, and last
496 * Also check whether our writing will bring us
501 WriteOffset
.LowPart
= Fcb
->FileSize
.LowPart
;
502 LastOffset
.LowPart
= WriteOffset
.LowPart
+ Length
;
503 AboveFour
= (LastOffset
.LowPart
< Fcb
->FileSize
.LowPart
);
507 WriteOffset
.LowPart
= FileOffset
->LowPart
;
508 LastOffset
.LowPart
= WriteOffset
.LowPart
+ Length
;
509 AboveFour
= (LastOffset
.LowPart
< FileOffset
->LowPart
) ||
510 (FileOffset
->HighPart
!= 0);
513 /* If cache wasn't initialized, fail */
514 if (FileObject
->PrivateCacheMap
== NULL
||
515 Fcb
->IsFastIoPossible
== FastIoIsNotPossible
)
517 ExReleaseResourceLite(Fcb
->Resource
);
518 FsRtlExitFileSystem();
523 /* If we're to write beyond allocation size, it's no go,
524 * same is we create a hole bigger than 8kb
525 * same if we end writing beyond 4GB
527 if ((Fcb
->AllocationSize
.LowPart
< LastOffset
.LowPart
) ||
528 (WriteOffset
.LowPart
>= Fcb
->ValidDataLength
.LowPart
+ 0x2000) ||
531 ExReleaseResourceLite(Fcb
->Resource
);
532 FsRtlExitFileSystem();
537 /* If we have to extend the VDL, shared lock isn't enough */
538 if (AcquiredShared
&& LastOffset
.LowPart
> Fcb
->ValidDataLength
.LowPart
)
540 /* So release, and acquire exclusively */
541 ExReleaseResourceLite(Fcb
->Resource
);
542 ExAcquireResourceExclusiveLite(Fcb
->Resource
, TRUE
);
544 /* Get again EOF, in case file size changed in between and
545 * recheck we won't go beyond 4GB
549 WriteOffset
.LowPart
= Fcb
->FileSize
.LowPart
;
550 LastOffset
.LowPart
= WriteOffset
.LowPart
+ Length
;
551 AboveFour
= (LastOffset
.LowPart
< Fcb
->FileSize
.LowPart
);
554 /* Make sure caching is still enabled */
555 if (FileObject
->PrivateCacheMap
== NULL
||
556 Fcb
->IsFastIoPossible
== FastIoIsNotPossible
)
558 ExReleaseResourceLite(Fcb
->Resource
);
559 FsRtlExitFileSystem();
564 /* And that we're not writting beyond allocation size
565 * and that we're not going above 4GB
567 if ((Fcb
->AllocationSize
.LowPart
< LastOffset
.LowPart
) ||
568 (Fcb
->AllocationSize
.HighPart
!= 0) || AboveFour
)
570 ExReleaseResourceLite(Fcb
->Resource
);
571 FsRtlExitFileSystem();
577 /* If FastIO is questionable, then question */
578 if (Fcb
->IsFastIoPossible
== FastIoIsQuestionable
)
580 PFAST_IO_DISPATCH FastIoDispatch
;
581 PDEVICE_OBJECT RelatedDeviceObject
;
583 RelatedDeviceObject
= IoGetRelatedDeviceObject(FileObject
);
584 FastIoDispatch
= RelatedDeviceObject
->DriverObject
->FastIoDispatch
;
585 ASSERT(FastIoDispatch
!= NULL
);
586 ASSERT(FastIoDispatch
->FastIoCheckIfPossible
!= NULL
);
588 if (!FastIoDispatch
->FastIoCheckIfPossible(FileObject
,
590 Length
, Wait
, LockKey
,
591 FALSE
, &LocalIoStatus
,
592 RelatedDeviceObject
))
594 ExReleaseResourceLite(Fcb
->Resource
);
595 FsRtlExitFileSystem();
601 /* If we write beyond EOF, then, save previous sizes (in case of failure)
602 * and update file size, to allow writing
604 if (LastOffset
.LowPart
> Fcb
->FileSize
.LowPart
)
606 FileSizeChanged
= TRUE
;
607 InitialFileSize
.LowPart
= Fcb
->FileSize
.LowPart
;
608 InitialValidDataLength
.LowPart
= Fcb
->ValidDataLength
.LowPart
;
609 Fcb
->FileSize
.LowPart
= LastOffset
.LowPart
;
612 /* Set caller provided context as top level IRP */
613 IoSetTopLevelIrp(TopLevelContext
);
617 /* And perform the writing */
620 /* Check whether we've to create a hole first -
621 * it cannot fail, we can wait
623 if (LastOffset
.LowPart
> Fcb
->ValidDataLength
.LowPart
)
625 CcZeroData(FileObject
, &Fcb
->ValidDataLength
, &WriteOffset
, TRUE
);
629 CcFastCopyWrite(FileObject
, WriteOffset
.LowPart
, Length
, Buffer
);
631 _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
632 EXCEPTION_EXECUTE_HANDLER
:
633 EXCEPTION_CONTINUE_SEARCH
)
639 /* Restore top level IRP */
640 IoSetTopLevelIrp(NULL
);
642 /* If writing succeed */
645 /* If we wrote beyond VDL, update it */
646 if (LastOffset
.LowPart
> Fcb
->ValidDataLength
.LowPart
)
648 Fcb
->ValidDataLength
.LowPart
= LastOffset
.LowPart
;
651 /* File was obviously modified */
652 SetFlag(FileObject
->Flags
, FO_FILE_MODIFIED
);
654 /* And if we increased it, modify size in Cc and update FO */
657 (*CcGetFileSizePointer(FileObject
)).LowPart
= LastOffset
.LowPart
;
658 SetFlag(FileObject
->Flags
, FO_FILE_SIZE_CHANGED
);
661 /* Update offset - we're still below 4GB, so high part must be 0 */
662 FileObject
->CurrentByteOffset
.LowPart
= WriteOffset
.LowPart
+ Length
;
663 FileObject
->CurrentByteOffset
.HighPart
= 0;
667 /* We failed, we need to restore previous sizes */
670 if (Fcb
->PagingIoResource
!= NULL
)
672 ExAcquireResourceExclusiveLite(Fcb
->PagingIoResource
, TRUE
);
673 Fcb
->FileSize
.LowPart
= InitialFileSize
.LowPart
;
674 Fcb
->ValidDataLength
.LowPart
= InitialValidDataLength
.LowPart
;
675 ExReleaseResourceLite(Fcb
->PagingIoResource
);
679 Fcb
->FileSize
.LowPart
= InitialFileSize
.LowPart
;
680 Fcb
->ValidDataLength
.LowPart
= InitialValidDataLength
.LowPart
;
686 /* Release our resource and leave */
687 ExReleaseResourceLite(Fcb
->Resource
);
689 FsRtlExitFileSystem();