1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
6 /*************************************************************************
10 * Module: UDF File System Driver (Kernel mode execution only)
13 * Contains code to handle the "Flush Buffers" dispatch entry point.
15 *************************************************************************/
19 // define the file specific bug-check id
20 #define UDF_BUG_CHECK_ID UDF_FILE_FLUSH
24 /*************************************************************************
26 * Function: UDFFlush()
29 * The I/O Manager will invoke this routine to handle a flush buffers
32 * Expected Interrupt Level (for execution) :
34 * IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
35 * to be deferred to a worker thread context)
37 * Return Value: STATUS_SUCCESS/Error
39 *************************************************************************/
43 PDEVICE_OBJECT DeviceObject
, // the logical volume device object
44 PIRP Irp
) // I/O Request Packet
46 NTSTATUS RC
= STATUS_SUCCESS
;
47 PtrUDFIrpContext PtrIrpContext
= NULL
;
48 BOOLEAN AreWeTopLevel
= FALSE
;
50 UDFPrint(("UDFFlush: \n"));
52 FsRtlEnterFileSystem();
56 // set the top level context
57 AreWeTopLevel
= UDFIsIrpTopLevel(Irp
);
58 ASSERT(!UDFIsFSDevObj(DeviceObject
));
62 // get an IRP context structure and issue the request
63 PtrIrpContext
= UDFAllocateIrpContext(Irp
, DeviceObject
);
65 RC
= UDFCommonFlush(PtrIrpContext
, Irp
);
67 RC
= STATUS_INSUFFICIENT_RESOURCES
;
68 Irp
->IoStatus
.Status
= RC
;
69 Irp
->IoStatus
.Information
= 0;
71 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
74 } _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext
, _SEH2_GetExceptionInformation())) {
76 RC
= UDFExceptionHandler(PtrIrpContext
, Irp
);
78 UDFLogEvent(UDF_ERROR_INTERNAL_ERROR
, RC
);
82 IoSetTopLevelIrp(NULL
);
85 FsRtlExitFileSystem();
92 /*************************************************************************
94 * Function: UDFCommonFlush()
97 * The actual work is performed here. This routine may be invoked in one'
98 * of the two possible contexts:
99 * (a) in the context of a system worker thread
100 * (b) in the context of the original caller
102 * Expected Interrupt Level (for execution) :
106 * Return Value: STATUS_SUCCESS/Error
108 *************************************************************************/
111 PtrUDFIrpContext PtrIrpContext
,
115 NTSTATUS RC
= STATUS_SUCCESS
;
116 PIO_STACK_LOCATION IrpSp
= NULL
;
117 PFILE_OBJECT FileObject
= NULL
;
118 PtrUDFFCB Fcb
= NULL
;
119 PtrUDFCCB Ccb
= NULL
;
121 PtrUDFNTRequiredFCB NtReqFcb
= NULL
;
122 BOOLEAN AcquiredVCB
= FALSE
;
123 BOOLEAN AcquiredFCB
= FALSE
;
124 BOOLEAN PostRequest
= FALSE
;
125 BOOLEAN CanWait
= TRUE
;
127 UDFPrint(("UDFCommonFlush: \n"));
131 // Get some of the parameters supplied to us
132 CanWait
= ((PtrIrpContext
->IrpContextFlags
& UDF_IRP_CONTEXT_CAN_BLOCK
) ? TRUE
: FALSE
);
133 // If we cannot wait, post the request immediately since a flush is inherently blocking/synchronous.
139 // First, get a pointer to the current I/O stack location
140 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
143 FileObject
= IrpSp
->FileObject
;
146 // Get the FCB and CCB pointers
147 Ccb
= (PtrUDFCCB
)(FileObject
->FsContext2
);
151 NtReqFcb
= Fcb
->NTRequiredFCB
;
153 // Check the type of object passed-in. That will determine the course of
155 if ((Fcb
->NodeIdentifier
.NodeType
== UDF_NODE_TYPE_VCB
) || (Fcb
->FCBFlags
& UDF_FCB_ROOT_DIRECTORY
)) {
157 if (Fcb
->NodeIdentifier
.NodeType
== UDF_NODE_TYPE_VCB
) {
162 Vcb
->VCBFlags
|= UDF_VCB_SKIP_EJECT_CHECK
;
164 #ifdef UDF_DELAYED_CLOSE
165 UDFCloseAllDelayed(Vcb
);
166 #endif //UDF_DELAYED_CLOSE
168 UDFAcquireResourceExclusive(&(Vcb
->VCBResource
), TRUE
);
170 // The caller wishes to flush all files for the mounted
171 // logical volume. The flush volume routine below should simply
172 // walk through all of the open file streams, acquire the
173 // VCB resource, and request the flush operation from the Cache
174 // Manager. Basically, the sequence of operations listed below
175 // for a single file should be executed on all open files.
177 UDFFlushLogicalVolume(PtrIrpContext
, Irp
, Vcb
, 0);
179 UDFReleaseResource(&(Vcb
->VCBResource
));
184 if (!(Fcb
->FCBFlags
& UDF_FCB_DIRECTORY
)) {
185 // This is a regular file.
188 if(!ExIsResourceAcquiredExclusiveLite(&(Vcb
->VCBResource
)) &&
189 !ExIsResourceAcquiredSharedLite(&(Vcb
->VCBResource
))) {
190 UDFAcquireResourceShared(&(Vcb
->VCBResource
), TRUE
);
193 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb
);
194 UDFAcquireResourceExclusive(&(NtReqFcb
->MainResource
), TRUE
);
197 // Request the Cache Manager to perform a flush operation.
198 // Further, instruct the Cache Manager that we wish to flush the
199 // entire file stream.
200 UDFFlushAFile(Fcb
, Ccb
, &(Irp
->IoStatus
), 0);
201 RC
= Irp
->IoStatus
.Status
;
203 // Some log-based FSD implementations may wish to flush their
204 // log files at this time. Finally, we should update the time-stamp
205 // values for the file stream appropriately. This would involve
206 // obtaining the current time and modifying the appropriate directory
217 UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb
);
218 UDFReleaseResource(&(NtReqFcb
->MainResource
));
222 UDFReleaseResource(&(Vcb
->VCBResource
));
226 if(!_SEH2_AbnormalTermination()) {
228 // Nothing to lock now.
230 RC
= UDFPostRequest(PtrIrpContext
, Irp
);
232 // Some applications like this request very much
233 // (ex. WinWord). But it's not a good idea for CD-R/RW media
234 if(Vcb
->FlushMedia
) {
235 PIO_STACK_LOCATION PtrNextIoStackLocation
= NULL
;
236 NTSTATUS RC1
= STATUS_SUCCESS
;
238 // Send the request down at this point.
239 // To do this, we must set the next IRP stack location, and
240 // maybe set a completion routine.
241 // Be careful about marking the IRP pending if the lower level
242 // driver returned pending and we do have a completion routine!
243 PtrNextIoStackLocation
= IoGetNextIrpStackLocation(Irp
);
244 *PtrNextIoStackLocation
= *IrpSp
;
246 // Set the completion routine to "eat-up" any
247 // STATUS_INVALID_DEVICE_REQUEST error code returned by the lower
249 IoSetCompletionRoutine(Irp
, UDFFlushCompletion
, NULL
, TRUE
, TRUE
, TRUE
);
251 RC1
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
253 RC
= ((RC1
== STATUS_INVALID_DEVICE_REQUEST
) ? RC
: RC1
);
255 // Release the IRP context at this time.
256 UDFReleaseIrpContext(PtrIrpContext
);
258 Irp
->IoStatus
.Status
= RC
;
259 Irp
->IoStatus
.Information
= 0;
260 // Free up the Irp Context
261 UDFReleaseIrpContext(PtrIrpContext
);
263 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
270 } // end UDFCommonFlush()
273 /*************************************************************************
275 * Function: UDFFlushAFile()
278 * Tell the Cache Manager to perform a flush.
280 * Expected Interrupt Level (for execution) :
286 *************************************************************************/
291 OUT PIO_STATUS_BLOCK PtrIoStatus
,
295 BOOLEAN SetArchive
= FALSE
;
296 // BOOLEAN PurgeCache = FALSE;
299 UDFPrint(("UDFFlushAFile: \n"));
304 if(Fcb
->Vcb
->VCBFlags
& UDF_VCB_FLAGS_RAW_DISK
)
306 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
309 #ifndef UDF_READ_ONLY_BUILD
310 // Flush Security if required
312 UDFWriteSecurity(Fcb
->Vcb
, Fcb
, &(Fcb
->NTRequiredFCB
->SecurityDesc
));
313 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
316 #endif //UDF_READ_ONLY_BUILD
319 if(UDFHasAStreamDir(Fcb
->FileInfo
) &&
320 Fcb
->FileInfo
->Dloc
->SDirInfo
&&
321 !UDFIsSDirDeleted(Fcb
->FileInfo
->Dloc
->SDirInfo
) ) {
323 UDFFlushADirectory(Fcb
->Vcb
, Fcb
->FileInfo
->Dloc
->SDirInfo
, PtrIoStatus
, FlushFlags
);
325 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
330 if((Fcb
->CachedOpenHandleCount
|| !Fcb
->OpenHandleCount
) &&
331 Fcb
->NTRequiredFCB
->SectionObject
.DataSectionObject
) {
332 if(!(Fcb
->NTRequiredFCB
->NtReqFCBFlags
& UDF_NTREQ_FCB_DELETED
)
334 ((Fcb
->NTRequiredFCB
->NtReqFCBFlags
& UDF_NTREQ_FCB_MODIFIED
) ||
335 (Ccb
&& !(Ccb
->CCBFlags
& UDF_CCB_FLUSHED
)) )) {
336 MmPrint((" CcFlushCache()\n"));
337 CcFlushCache(&(Fcb
->NTRequiredFCB
->SectionObject
), NULL
, 0, PtrIoStatus
);
339 // notice, that we should purge cache
340 // we can't do it now, because it may cause last Close
341 // request & thus, structure deallocation
342 // PurgeCache = TRUE;
344 #ifndef UDF_READ_ONLY_BUILD
346 if( (Ccb
->FileObject
->Flags
& FO_FILE_MODIFIED
) &&
347 !(Ccb
->CCBFlags
& UDF_CCB_WRITE_TIME_SET
)) {
348 if(Fcb
->Vcb
->CompatFlags
& UDF_VCB_IC_UPDATE_MODIFY_TIME
) {
350 KeQuerySystemTime((PLARGE_INTEGER
)&NtTime
);
351 UDFSetFileXTime(Fcb
->FileInfo
, NULL
, NULL
, NULL
, &NtTime
);
352 Fcb
->NTRequiredFCB
->LastWriteTime
.QuadPart
= NtTime
;
355 Ccb
->FileObject
->Flags
&= ~FO_FILE_MODIFIED
;
357 if(Ccb
->FileObject
->Flags
& FO_FILE_SIZE_CHANGED
) {
358 LONGLONG ASize
= UDFGetFileAllocationSize(Fcb
->Vcb
, Fcb
->FileInfo
);
359 UDFSetFileSizeInDirNdx(Fcb
->Vcb
, Fcb
->FileInfo
, &ASize
);
360 Ccb
->FileObject
->Flags
&= ~FO_FILE_SIZE_CHANGED
;
363 #endif //UDF_READ_ONLY_BUILD
365 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
370 #ifndef UDF_READ_ONLY_BUILD
372 (Fcb
->Vcb
->CompatFlags
& UDF_VCB_IC_UPDATE_ARCH_BIT
)) {
374 PDIR_INDEX_ITEM DirNdx
;
375 DirNdx
= UDFDirIndex(UDFGetDirIndexByFileInfo(Fcb
->FileInfo
), Fcb
->FileInfo
->Index
);
377 Attr
= UDFAttributesToNT(DirNdx
, Fcb
->FileInfo
->Dloc
->FileEntry
);
378 if(!(Attr
& FILE_ATTRIBUTE_ARCHIVE
))
379 UDFAttributesToUDF(DirNdx
, Fcb
->FileInfo
->Dloc
->FileEntry
, Attr
| FILE_ATTRIBUTE_ARCHIVE
);
381 #endif //UDF_READ_ONLY_BUILD
382 UDFFlushFile__( Fcb
->Vcb
, Fcb
->FileInfo
, FlushFlags
);
383 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
389 MmPrint((" CcPurgeCacheSection()\n"));
390 CcPurgeCacheSection( &(Fcb->NTRequiredFCB->SectionObject), NULL, 0, FALSE );
391 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
397 } // end UDFFlushAFile()
399 /*************************************************************************
401 * Function: UDFFlushADirectory()
404 * Tell the Cache Manager to perform a flush for all files
405 * in current directory & all subdirectories and flush all metadata
407 * Expected Interrupt Level (for execution) :
413 *************************************************************************/
417 IN PUDF_FILE_INFO FI
,
418 OUT PIO_STATUS_BLOCK PtrIoStatus
,
422 UDFPrint(("UDFFlushADirectory: \n"));
423 // PDIR_INDEX_HDR hDI;
425 // BOOLEAN Referenced = FALSE;
428 if(Vcb
->VCBFlags
& UDF_VCB_FLAGS_RAW_DISK
)
431 if(!FI
|| !FI
->Dloc
|| !FI
->Dloc
->DirIndex
) goto SkipFlushDir
;
432 // hDI = FI->Dloc->DirIndex;
434 // Flush Security if required
436 UDFWriteSecurity(Vcb
, FI
->Fcb
, &(FI
->Fcb
->NTRequiredFCB
->SecurityDesc
));
437 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
442 if(UDFHasAStreamDir(FI
) &&
443 FI
->Dloc
->SDirInfo
&&
444 !UDFIsSDirDeleted(FI
->Dloc
->SDirInfo
) ) {
446 UDFFlushADirectory(Vcb
, FI
->Dloc
->SDirInfo
, PtrIoStatus
, FlushFlags
);
448 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
454 UDF_DIR_SCAN_CONTEXT ScanContext
;
455 PUDF_FILE_INFO tempFI
;
457 if(UDFDirIndexInitScan(FI
, &ScanContext
, 2)) {
458 while((DI
= UDFDirIndexScan(&ScanContext
, &tempFI
))) {
461 if(!tempFI
) continue;
462 if(UDFIsADirectory(tempFI
)) {
463 UDFFlushADirectory(Vcb
, tempFI
, PtrIoStatus
, FlushFlags
);
465 UDFFlushAFile(tempFI
->Fcb
, NULL
, PtrIoStatus
, FlushFlags
);
467 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
470 if(UDFFlushIsBreaking(Vcb
, FlushFlags
)) {
471 ret_val
|= UDF_FLUSH_FLAGS_INTERRUPTED
;
476 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
482 UDFFlushFile__( Vcb
, FI
, FlushFlags
);
483 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
488 } // end UDFFlushADirectory()
490 /*************************************************************************
492 * Function: UDFFlushLogicalVolume()
495 * Flush everything beginning from root directory.
496 * Vcb must be previously acquired exclusively.
498 * Expected Interrupt Level (for execution) :
504 *************************************************************************/
506 UDFFlushLogicalVolume(
507 IN PtrUDFIrpContext PtrIrpContext
,
514 #ifndef UDF_READ_ONLY_BUILD
515 IO_STATUS_BLOCK IoStatus
;
517 UDFPrint(("UDFFlushLogicalVolume: \n"));
520 if(Vcb
->VCBFlags
& (UDF_VCB_FLAGS_RAW_DISK
/* |
521 UDF_VCB_FLAGS_MEDIA_READ_ONLY*/))
523 if(Vcb
->VCBFlags
& UDF_VCB_FLAGS_VOLUME_READ_ONLY
)
525 if(!(Vcb
->VCBFlags
& UDF_VCB_FLAGS_VOLUME_MOUNTED
))
528 // NOTE: This function may also be invoked internally as part of
529 // processing a shutdown request.
530 ASSERT(Vcb
->RootDirFCB
);
531 ret_val
|= UDFFlushADirectory(Vcb
, Vcb
->RootDirFCB
->FileInfo
, &IoStatus
, FlushFlags
);
533 // if(UDFFlushIsBreaking(Vcb, FlushFlags))
535 // flush internal cache
536 if(FlushFlags
& UDF_FLUSH_FLAGS_LITE
) {
537 UDFPrint((" Lite flush, keep Modified=%d.\n", Vcb
->Modified
));
539 if(Vcb
->VerifyOnWrite
) {
540 UDFPrint(("UDF: Flushing cache for verify\n"));
541 //WCacheFlushAll__(&(Vcb->FastCache), Vcb);
542 WCacheFlushBlocks__(&(Vcb
->FastCache
), Vcb
, 0, Vcb
->LastLBA
);
545 // umount (this is internal operation, NT will "dismount" volume later)
548 UDFPreClrModified(Vcb
);
549 WCacheFlushAll__(&(Vcb
->FastCache
), Vcb
);
556 #endif //UDF_READ_ONLY_BUILD
559 } // end UDFFlushLogicalVolume()
562 /*************************************************************************
564 * Function: UDFFlushCompletion()
567 * Eat up any bad errors.
569 * Expected Interrupt Level (for execution) :
575 *************************************************************************/
579 PDEVICE_OBJECT PtrDeviceObject
,
584 // NTSTATUS RC = STATUS_SUCCESS;
586 UDFPrint(("UDFFlushCompletion: \n"));
588 if (Irp
->PendingReturned
) {
589 IoMarkIrpPending(Irp
);
592 if (Irp
->IoStatus
.Status
== STATUS_INVALID_DEVICE_REQUEST
) {
593 // cannot do much here, can we?
594 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
597 return(STATUS_SUCCESS
);
598 } // end UDFFlushCompletion()
602 Check if we should break FlushTree process
610 BOOLEAN ret_val
= FALSE
;
611 // if(!(FlushFlags & UDF_FLUSH_FLAGS_BREAKABLE))
613 UDFAcquireResourceExclusive(&(Vcb
->FlushResource
),TRUE
);
614 ret_val
= (Vcb
->VCBFlags
& UDF_VCB_FLAGS_FLUSH_BREAK_REQ
) ? TRUE
: FALSE
;
615 Vcb
->VCBFlags
&= ~UDF_VCB_FLAGS_FLUSH_BREAK_REQ
;
616 UDFReleaseResource(&(Vcb
->FlushResource
));
618 } // end UDFFlushIsBreaking()
621 Signal FlushTree break request. Note, this is
622 treated as recommendation only
629 UDFAcquireResourceExclusive(&(Vcb
->FlushResource
),TRUE
);
630 Vcb
->VCBFlags
|= UDF_VCB_FLAGS_FLUSH_BREAK_REQ
;
631 UDFReleaseResource(&(Vcb
->FlushResource
));
632 } // end UDFFlushTryBreak()