mirror of
https://github.com/reactos/reactos.git
synced 2024-08-19 18:02:55 +00:00
772 lines
31 KiB
C++
772 lines
31 KiB
C++
////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
|
|
// All rights reserved
|
|
// This file was released under the GPLv2 on June 2015.
|
|
////////////////////////////////////////////////////////////////////
|
|
/*
|
|
|
|
Module name: Cleanup.cpp
|
|
|
|
Abstract:
|
|
|
|
Contains code to handle the "Cleanup" dispatch entry point.
|
|
|
|
Environment:
|
|
|
|
Kernel mode only
|
|
*/
|
|
|
|
#include "udffs.h"
|
|
|
|
// define the file specific bug-check id
|
|
#define UDF_BUG_CHECK_ID UDF_FILE_CLEANUP
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFCleanup()
|
|
*
|
|
* Description:
|
|
* The I/O Manager will invoke this routine to handle a cleanup
|
|
* request
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
|
|
* to be deferred to a worker thread context)
|
|
*
|
|
* Return Value: STATUS_SUCCESS
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS
|
|
NTAPI
|
|
UDFCleanup(
|
|
PDEVICE_OBJECT DeviceObject, // the logical volume device object
|
|
PIRP Irp // I/O Request Packet
|
|
)
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrUDFIrpContext PtrIrpContext = NULL;
|
|
BOOLEAN AreWeTopLevel = FALSE;
|
|
|
|
TmPrint(("UDFCleanup\n"));
|
|
|
|
FsRtlEnterFileSystem();
|
|
ASSERT(DeviceObject);
|
|
ASSERT(Irp);
|
|
|
|
// If we were called with our file system device object instead of a
|
|
// volume device object, just complete this request with STATUS_SUCCESS
|
|
if (UDFIsFSDevObj(DeviceObject)) {
|
|
// this is a cleanup of the FSD itself
|
|
Irp->IoStatus.Status = RC;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if(UDFGlobalData.AutoFormatCount == IoGetCurrentIrpStackLocation(Irp)->FileObject) {
|
|
UDFPrint(("Deregister Autoformat\n"));
|
|
UDFGlobalData.AutoFormatCount = NULL;
|
|
}
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
FsRtlExitFileSystem();
|
|
return(RC);
|
|
}
|
|
|
|
// set the top level context
|
|
AreWeTopLevel = UDFIsIrpTopLevel(Irp);
|
|
|
|
_SEH2_TRY {
|
|
|
|
// get an IRP context structure and issue the request
|
|
PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
|
|
if(PtrIrpContext) {
|
|
RC = UDFCommonCleanup(PtrIrpContext, Irp);
|
|
} else {
|
|
RC = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Status = RC;
|
|
Irp->IoStatus.Information = 0;
|
|
// complete the IRP
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
}
|
|
|
|
} _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
|
|
|
|
RC = UDFExceptionHandler(PtrIrpContext, Irp);
|
|
|
|
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
|
|
} _SEH2_END;
|
|
|
|
if (AreWeTopLevel) {
|
|
IoSetTopLevelIrp(NULL);
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(RC);
|
|
} // end UDFCleanup()
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFCommonCleanup()
|
|
*
|
|
* Description:
|
|
* The actual work is performed here. This routine may be invoked in one'
|
|
* of the two possible contexts:
|
|
* (a) in the context of a system worker thread
|
|
* (b) in the context of the original caller
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: Does not matter!
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS
|
|
UDFCommonCleanup(
|
|
PtrUDFIrpContext PtrIrpContext,
|
|
PIRP Irp)
|
|
{
|
|
IO_STATUS_BLOCK IoStatus;
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
NTSTATUS RC2;
|
|
PIO_STACK_LOCATION IrpSp = NULL;
|
|
PFILE_OBJECT FileObject = NULL;
|
|
PtrUDFFCB Fcb = NULL;
|
|
PtrUDFCCB Ccb = NULL;
|
|
PVCB Vcb = NULL;
|
|
PtrUDFNTRequiredFCB NtReqFcb = NULL;
|
|
ULONG lc = 0;
|
|
BOOLEAN AcquiredVcb = FALSE;
|
|
BOOLEAN AcquiredFCB = FALSE;
|
|
BOOLEAN AcquiredParentFCB = FALSE;
|
|
|
|
// BOOLEAN CompleteIrp = TRUE;
|
|
// BOOLEAN PostRequest = FALSE;
|
|
BOOLEAN ChangeTime = FALSE;
|
|
#ifdef UDF_DBG
|
|
BOOLEAN CanWait = FALSE;
|
|
#endif // UDF_DBG
|
|
BOOLEAN ForcedCleanUp = FALSE;
|
|
|
|
PUDF_FILE_INFO NextFileInfo = NULL;
|
|
#ifdef UDF_DBG
|
|
UNICODE_STRING CurName;
|
|
PDIR_INDEX_HDR DirNdx;
|
|
#endif // UDF_DBG
|
|
// PUDF_DATALOC_INFO Dloc;
|
|
|
|
TmPrint(("UDFCommonCleanup\n"));
|
|
|
|
// BrutePoint();
|
|
|
|
_SEH2_TRY {
|
|
// First, get a pointer to the current I/O stack location
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
if(!IrpSp) try_return(RC = STATUS_INVALID_PARAMETER);
|
|
|
|
FileObject = IrpSp->FileObject;
|
|
|
|
// Get the FCB and CCB pointers
|
|
Ccb = (PtrUDFCCB)(FileObject->FsContext2);
|
|
ASSERT(Ccb);
|
|
Fcb = Ccb->Fcb;
|
|
ASSERT(Fcb);
|
|
|
|
Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension);
|
|
ASSERT(Vcb);
|
|
ASSERT(Vcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB);
|
|
// Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
|
|
#ifdef UDF_DBG
|
|
CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE;
|
|
AdPrint((" %s\n", CanWait ? "Wt" : "nw"));
|
|
ASSERT(CanWait);
|
|
#endif // UDF_DBG
|
|
UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
|
|
AcquiredVcb = TRUE;
|
|
// Steps we shall take at this point are:
|
|
// (a) Acquire the file (FCB) exclusively
|
|
// (b) Flush file data to disk
|
|
// (c) Talk to the FSRTL package (if we use it) about pending oplocks.
|
|
// (d) Notify the FSRTL package for use with pending notification IRPs
|
|
// (e) Unlock byte-range locks (if any were acquired by process)
|
|
// (f) Update time stamp values (e.g. fast-IO had been performed)
|
|
// (g) Inform the Cache Manager to uninitialize Cache Maps ...
|
|
// and other similar stuff.
|
|
// BrutePoint();
|
|
NtReqFcb = Fcb->NTRequiredFCB;
|
|
|
|
if (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) {
|
|
AdPrint(("Cleaning up Volume\n"));
|
|
AdPrint(("UDF: OpenHandleCount: %x\n",Fcb->OpenHandleCount));
|
|
|
|
UDFInterlockedDecrement((PLONG)&(Fcb->OpenHandleCount));
|
|
UDFInterlockedDecrement((PLONG)&(Vcb->VCBHandleCount));
|
|
if(FileObject->Flags & FO_CACHE_SUPPORTED) {
|
|
// we've cached close
|
|
UDFInterlockedDecrement((PLONG)&(Fcb->CachedOpenHandleCount));
|
|
}
|
|
ASSERT(Fcb->OpenHandleCount <= (Fcb->ReferenceCount-1));
|
|
|
|
// If this handle had write access, and actually wrote something,
|
|
// flush the device buffers, and then set the verify bit now
|
|
// just to be safe (in case there is no dismount).
|
|
if( FileObject->WriteAccess &&
|
|
(FileObject->Flags & FO_FILE_MODIFIED)) {
|
|
|
|
Vcb->Vpb->RealDevice->Flags |= DO_VERIFY_VOLUME;
|
|
}
|
|
// User may decide to close locked volume without call to unlock proc
|
|
// So, handle this situation properly & unlock it now...
|
|
if (FileObject == Vcb->VolumeLockFileObject) {
|
|
Vcb->VolumeLockFileObject = NULL;
|
|
Vcb->VolumeLockPID = -1;
|
|
Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_LOCKED;
|
|
Vcb->Vpb->Flags &= ~VPB_LOCKED;
|
|
UDFNotifyVolumeEvent(FileObject, FSRTL_VOLUME_UNLOCK);
|
|
}
|
|
|
|
MmPrint((" CcUninitializeCacheMap()\n"));
|
|
CcUninitializeCacheMap(FileObject, NULL, NULL);
|
|
// reset device
|
|
if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) &&
|
|
(Vcb->VCBFlags & UDF_VCB_FLAGS_OUR_DEVICE_DRIVER)) {
|
|
// this call doesn't modify data buffer
|
|
// it just requires its presence
|
|
UDFResetDeviceDriver(Vcb, Vcb->TargetDeviceObject, TRUE);
|
|
}
|
|
// We must clean up the share access at this time, since we may not
|
|
// get a Close call for awhile if the file was mapped through this
|
|
// File Object.
|
|
IoRemoveShareAccess( FileObject, &(NtReqFcb->FCBShareAccess) );
|
|
|
|
try_return(RC = STATUS_SUCCESS);
|
|
}
|
|
// BrutePoint();
|
|
#ifdef UDF_DBG
|
|
DirNdx = UDFGetDirIndexByFileInfo(Fcb->FileInfo);
|
|
if(DirNdx) {
|
|
CurName.Buffer = UDFDirIndex(DirNdx, Fcb->FileInfo->Index)->FName.Buffer;
|
|
if(CurName.Buffer) {
|
|
AdPrint(("Cleaning up file: %ws %8.8x\n", CurName.Buffer, FileObject));
|
|
} else {
|
|
AdPrint(("Cleaning up file: ??? \n"));
|
|
}
|
|
}
|
|
#endif //UDF_DBG
|
|
AdPrint(("UDF: OpenHandleCount: %x\n",Fcb->OpenHandleCount));
|
|
// Acquire parent object
|
|
if(Fcb->FileInfo->ParentFile) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB);
|
|
UDFAcquireResourceExclusive(&(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB->MainResource),TRUE);
|
|
} else {
|
|
UDFAcquireResourceShared(&(Vcb->VCBResource),TRUE);
|
|
}
|
|
AcquiredParentFCB = TRUE;
|
|
// Acquire current object
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
UDFAcquireResourceExclusive(&(NtReqFcb->MainResource),TRUE);
|
|
AcquiredFCB = TRUE;
|
|
// dereference object
|
|
UDFInterlockedDecrement((PLONG)&(Fcb->OpenHandleCount));
|
|
UDFInterlockedDecrement((PLONG)&(Vcb->VCBHandleCount));
|
|
if(FileObject->Flags & FO_CACHE_SUPPORTED) {
|
|
// we've cached close
|
|
UDFInterlockedDecrement((PLONG)&(Fcb->CachedOpenHandleCount));
|
|
}
|
|
ASSERT(Fcb->OpenHandleCount <= (Fcb->ReferenceCount-1));
|
|
// check if Ccb being cleaned up has DeleteOnClose flag set
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
if(Ccb->CCBFlags & UDF_CCB_DELETE_ON_CLOSE) {
|
|
AdPrint((" DeleteOnClose\n"));
|
|
// Ok, now we'll become 'delete on close'...
|
|
ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
|
|
Fcb->FCBFlags |= UDF_FCB_DELETE_ON_CLOSE;
|
|
FileObject->DeletePending = TRUE;
|
|
// Report this to the dir notify package for a directory.
|
|
if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
|
|
FsRtlNotifyFullChangeDirectory( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
|
|
(PVOID)Ccb, NULL, FALSE, FALSE,
|
|
0, NULL, NULL, NULL );
|
|
}
|
|
}
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
|
|
if(!(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {
|
|
// Unlock all outstanding file locks.
|
|
FsRtlFastUnlockAll(&(NtReqFcb->FileLock),
|
|
FileObject,
|
|
IoGetRequestorProcess(Irp),
|
|
NULL);
|
|
}
|
|
// get Link count
|
|
lc = UDFGetFileLinkCount(Fcb->FileInfo);
|
|
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
if( (Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) &&
|
|
!(Fcb->OpenHandleCount)) {
|
|
// This can be useful for Streams, those were brutally deleted
|
|
// (together with parent object)
|
|
ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
|
|
FileObject->DeletePending = TRUE;
|
|
|
|
// we should mark all streams of the file being deleted
|
|
// for deletion too, if there are no more Links to
|
|
// main data stream
|
|
if((lc <= 1) &&
|
|
!UDFIsSDirDeleted(Fcb->FileInfo->Dloc->SDirInfo)) {
|
|
RC = UDFMarkStreamsForDeletion(Vcb, Fcb, TRUE); // Delete
|
|
}
|
|
// we can release these resources 'cause UDF_FCB_DELETE_ON_CLOSE
|
|
// flag is already set & the file can't be opened
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
UDFReleaseResource(&(NtReqFcb->MainResource));
|
|
AcquiredFCB = FALSE;
|
|
if(Fcb->FileInfo->ParentFile) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(Fcb->ParentFcb->NTRequiredFCB);
|
|
UDFReleaseResource(&(Fcb->ParentFcb->NTRequiredFCB->MainResource));
|
|
} else {
|
|
UDFReleaseResource(&(Vcb->VCBResource));
|
|
}
|
|
AcquiredParentFCB = FALSE;
|
|
UDFReleaseResource(&(Vcb->VCBResource));
|
|
AcquiredVcb = FALSE;
|
|
|
|
// Make system to issue last Close request
|
|
// for our Target ...
|
|
UDFRemoveFromSystemDelayedQueue(Fcb);
|
|
|
|
#ifdef UDF_DELAYED_CLOSE
|
|
// remove file from our DelayedClose queue
|
|
UDFRemoveFromDelayedQueue(Fcb);
|
|
ASSERT(!Fcb->IrpContextLite);
|
|
#endif //UDF_DELAYED_CLOSE
|
|
|
|
UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
|
|
AcquiredVcb = TRUE;
|
|
if(Fcb->FileInfo->ParentFile) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(Fcb->ParentFcb->NTRequiredFCB);
|
|
UDFAcquireResourceExclusive(&(Fcb->ParentFcb->NTRequiredFCB->MainResource),TRUE);
|
|
} else {
|
|
UDFAcquireResourceShared(&(Vcb->VCBResource),TRUE);
|
|
}
|
|
AcquiredParentFCB = TRUE;
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
UDFAcquireResourceExclusive(&(NtReqFcb->MainResource),TRUE);
|
|
AcquiredFCB = TRUE;
|
|
|
|
// we should set file sizes to zero if there are no more
|
|
// links to this file
|
|
if(lc <= 1) {
|
|
// Synchronize here with paging IO
|
|
UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource),TRUE);
|
|
// set file size to zero (for system cache manager)
|
|
// NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart =
|
|
NtReqFcb->CommonFCBHeader.FileSize.QuadPart =
|
|
NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = 0;
|
|
CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&(NtReqFcb->CommonFCBHeader.AllocationSize));
|
|
|
|
UDFReleaseResource(&(NtReqFcb->PagingIoResource));
|
|
}
|
|
}
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
|
|
#ifdef UDF_DELAYED_CLOSE
|
|
if ((Fcb->ReferenceCount == 1) &&
|
|
/*(Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB) &&*/ // see above
|
|
(!(Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE)) ) {
|
|
Fcb->FCBFlags |= UDF_FCB_DELAY_CLOSE;
|
|
}
|
|
#endif //UDF_DELAYED_CLOSE
|
|
|
|
NextFileInfo = Fcb->FileInfo;
|
|
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
// do we need to delete it now ?
|
|
if( (Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) &&
|
|
!(Fcb->OpenHandleCount)) {
|
|
|
|
// can we do it ?
|
|
if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
|
|
ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
|
|
if(!UDFIsDirEmpty__(NextFileInfo)) {
|
|
// forget about it
|
|
Fcb->FCBFlags &= ~UDF_FCB_DELETE_ON_CLOSE;
|
|
goto DiscardDelete;
|
|
}
|
|
} else
|
|
if (lc <= 1) {
|
|
// Synchronize here with paging IO
|
|
BOOLEAN AcquiredPagingIo;
|
|
AcquiredPagingIo = UDFAcquireResourceExclusiveWithCheck(&(NtReqFcb->PagingIoResource));
|
|
// set file size to zero (for UdfInfo package)
|
|
// we should not do this for directories and linked files
|
|
UDFResizeFile__(Vcb, NextFileInfo, 0);
|
|
if(AcquiredPagingIo) {
|
|
UDFReleaseResource(&(NtReqFcb->PagingIoResource));
|
|
}
|
|
}
|
|
// mark parent object for deletion if requested
|
|
if((Fcb->FCBFlags & UDF_FCB_DELETE_PARENT) &&
|
|
Fcb->ParentFcb) {
|
|
ASSERT(!(Fcb->ParentFcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
|
|
Fcb->ParentFcb->FCBFlags |= UDF_FCB_DELETE_ON_CLOSE;
|
|
}
|
|
// flush file. It is required by UDFUnlinkFile__()
|
|
RC = UDFFlushFile__(Vcb, NextFileInfo);
|
|
if(!NT_SUCCESS(RC)) {
|
|
AdPrint(("Error flushing file !!!\n"));
|
|
}
|
|
// try to unlink
|
|
if((RC = UDFUnlinkFile__(Vcb, NextFileInfo, TRUE)) == STATUS_CANNOT_DELETE) {
|
|
// If we can't delete file with Streams due to references,
|
|
// mark SDir & Streams
|
|
// for Deletion. We shall also set DELETE_PARENT flag to
|
|
// force Deletion of the current file later... when curently
|
|
// opened Streams would be cleaned up.
|
|
|
|
// WARNING! We should keep SDir & Streams if there is a
|
|
// link to this file
|
|
if(NextFileInfo->Dloc &&
|
|
NextFileInfo->Dloc->SDirInfo &&
|
|
NextFileInfo->Dloc->SDirInfo->Fcb) {
|
|
|
|
BrutePoint();
|
|
if(!UDFIsSDirDeleted(NextFileInfo->Dloc->SDirInfo)) {
|
|
// RC = UDFMarkStreamsForDeletion(Vcb, Fcb, TRUE); // Delete
|
|
//#ifdef UDF_ALLOW_PRETEND_DELETED
|
|
UDFPretendFileDeleted__(Vcb, Fcb->FileInfo);
|
|
//#endif //UDF_ALLOW_PRETEND_DELETED
|
|
}
|
|
goto NotifyDelete;
|
|
|
|
} else {
|
|
// Getting here means that we can't delete file because of
|
|
// References/PemissionsDenied/Smth.Else,
|
|
// but not Linked+OpenedStream
|
|
BrutePoint();
|
|
// RC = STATUS_SUCCESS;
|
|
goto DiscardDelete_1;
|
|
}
|
|
} else {
|
|
DiscardDelete_1:
|
|
// We have got an ugly ERROR, or
|
|
// file is deleted, so forget about it
|
|
ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
|
|
ForcedCleanUp = TRUE;
|
|
if(NT_SUCCESS(RC))
|
|
Fcb->FCBFlags &= ~UDF_FCB_DELETE_ON_CLOSE;
|
|
Fcb->FCBFlags |= UDF_FCB_DELETED;
|
|
RC = STATUS_SUCCESS;
|
|
}
|
|
NotifyDelete:
|
|
// We should prevent SetEOF operations on completly
|
|
// deleted data streams
|
|
if(lc < 1) {
|
|
NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_DELETED;
|
|
}
|
|
// Report that we have removed an entry.
|
|
if(UDFIsAStream(NextFileInfo)) {
|
|
UDFNotifyFullReportChange( Vcb, NextFileInfo,
|
|
FILE_NOTIFY_CHANGE_STREAM_NAME,
|
|
FILE_ACTION_REMOVED_STREAM);
|
|
} else {
|
|
UDFNotifyFullReportChange( Vcb, NextFileInfo,
|
|
UDFIsADirectory(NextFileInfo) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
|
|
FILE_ACTION_REMOVED);
|
|
}
|
|
} else
|
|
if(Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) {
|
|
DiscardDelete:
|
|
UDFNotifyFullReportChange( Vcb, NextFileInfo,
|
|
((Ccb->CCBFlags & UDF_CCB_ACCESS_TIME_SET) ? FILE_NOTIFY_CHANGE_LAST_ACCESS : 0) |
|
|
((Ccb->CCBFlags & UDF_CCB_WRITE_TIME_SET) ? (FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_LAST_WRITE) : 0) |
|
|
0,
|
|
UDFIsAStream(NextFileInfo) ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED);
|
|
}
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
|
|
if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
|
|
// Report to the dir notify package for a directory.
|
|
FsRtlNotifyCleanup( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP), (PVOID)Ccb );
|
|
}
|
|
|
|
// we can't purge Cache when more than one link exists
|
|
if(lc > 1) {
|
|
ForcedCleanUp = FALSE;
|
|
}
|
|
|
|
if ( (FileObject->Flags & FO_CACHE_SUPPORTED) &&
|
|
(NtReqFcb->SectionObject.DataSectionObject) ) {
|
|
BOOLEAN LastNonCached = (!Fcb->CachedOpenHandleCount &&
|
|
Fcb->OpenHandleCount);
|
|
// If this was the last cached open, and there are open
|
|
// non-cached handles, attempt a flush and purge operation
|
|
// to avoid cache coherency overhead from these non-cached
|
|
// handles later. We ignore any I/O errors from the flush.
|
|
// We shall not flush deleted files
|
|
RC = STATUS_SUCCESS;
|
|
if( LastNonCached
|
|
||
|
|
(!Fcb->OpenHandleCount &&
|
|
!ForcedCleanUp) ) {
|
|
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
LONGLONG OldFileSize, NewFileSize;
|
|
|
|
if( (OldFileSize = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart) <
|
|
(NewFileSize = NtReqFcb->CommonFCBHeader.FileSize.QuadPart)) {
|
|
/* UDFZeroDataEx(NtReqFcb,
|
|
OldFileSize,
|
|
NewFileSize - OldFileSize,
|
|
TRUE, Vcb, FileObject);*/
|
|
|
|
NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = NewFileSize;
|
|
}
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
MmPrint((" CcFlushCache()\n"));
|
|
CcFlushCache( &(NtReqFcb->SectionObject), NULL, 0, &IoStatus );
|
|
if(!NT_SUCCESS(IoStatus.Status)) {
|
|
MmPrint((" CcFlushCache() error: %x\n", IoStatus.Status));
|
|
RC = IoStatus.Status;
|
|
}
|
|
}
|
|
// If file is deleted or it is last cached open, but there are
|
|
// some non-cached handles we should purge cache section
|
|
if(ForcedCleanUp || LastNonCached) {
|
|
if(NtReqFcb->SectionObject.DataSectionObject) {
|
|
MmPrint((" CcPurgeCacheSection()\n"));
|
|
CcPurgeCacheSection( &(NtReqFcb->SectionObject), NULL, 0, FALSE );
|
|
}
|
|
/* MmPrint((" CcPurgeCacheSection()\n"));
|
|
CcPurgeCacheSection( &(NtReqFcb->SectionObject), NULL, 0, FALSE );*/
|
|
}
|
|
// we needn't Flush here. It will be done in UDFCloseFileInfoChain()
|
|
}
|
|
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
// Update FileTimes & Attrs
|
|
if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) &&
|
|
!(Fcb->FCBFlags & (UDF_FCB_DELETE_ON_CLOSE |
|
|
UDF_FCB_DELETED /*|
|
|
UDF_FCB_DIRECTORY |
|
|
UDF_FCB_READ_ONLY*/)) &&
|
|
!UDFIsAStreamDir(NextFileInfo)) {
|
|
LONGLONG NtTime;
|
|
LONGLONG ASize;
|
|
KeQuerySystemTime((PLARGE_INTEGER)&NtTime);
|
|
// Check if we should set ARCHIVE bit & LastWriteTime
|
|
if(FileObject->Flags & FO_FILE_MODIFIED) {
|
|
ULONG Attr;
|
|
PDIR_INDEX_ITEM DirNdx;
|
|
DirNdx = UDFDirIndex(UDFGetDirIndexByFileInfo(NextFileInfo), NextFileInfo->Index);
|
|
ASSERT(DirNdx);
|
|
// Archive bit
|
|
if(!(Ccb->CCBFlags & UDF_CCB_ATTRIBUTES_SET) &&
|
|
(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ARCH_BIT)) {
|
|
Attr = UDFAttributesToNT(DirNdx, NextFileInfo->Dloc->FileEntry);
|
|
if(!(Attr & FILE_ATTRIBUTE_ARCHIVE))
|
|
UDFAttributesToUDF(DirNdx, NextFileInfo->Dloc->FileEntry, Attr | FILE_ATTRIBUTE_ARCHIVE);
|
|
}
|
|
// WriteTime
|
|
if(!(Ccb->CCBFlags & UDF_CCB_WRITE_TIME_SET) &&
|
|
(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_MODIFY_TIME)) {
|
|
UDFSetFileXTime(NextFileInfo, NULL, &NtTime, NULL, &NtTime);
|
|
NtReqFcb->LastWriteTime.QuadPart =
|
|
NtReqFcb->LastAccessTime.QuadPart = NtTime;
|
|
ChangeTime = TRUE;
|
|
}
|
|
}
|
|
if(!(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) {
|
|
// Update sizes in DirIndex
|
|
if(!Fcb->OpenHandleCount) {
|
|
ASize = UDFGetFileAllocationSize(Vcb, NextFileInfo);
|
|
// NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart;
|
|
UDFSetFileSizeInDirNdx(Vcb, NextFileInfo, &ASize);
|
|
} else
|
|
if(FileObject->Flags & FO_FILE_SIZE_CHANGED) {
|
|
ASize = //UDFGetFileAllocationSize(Vcb, NextFileInfo);
|
|
NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart;
|
|
UDFSetFileSizeInDirNdx(Vcb, NextFileInfo, &ASize);
|
|
}
|
|
}
|
|
// AccessTime
|
|
if((FileObject->Flags & FO_FILE_FAST_IO_READ) &&
|
|
!(Ccb->CCBFlags & UDF_CCB_ACCESS_TIME_SET) &&
|
|
(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ACCESS_TIME)) {
|
|
UDFSetFileXTime(NextFileInfo, NULL, &NtTime, NULL, NULL);
|
|
NtReqFcb->LastAccessTime.QuadPart = NtTime;
|
|
// ChangeTime = TRUE;
|
|
}
|
|
// ChangeTime (AttrTime)
|
|
if(!(Ccb->CCBFlags & UDF_CCB_MODIFY_TIME_SET) &&
|
|
(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ATTR_TIME) &&
|
|
(ChangeTime || (Ccb->CCBFlags & (UDF_CCB_ATTRIBUTES_SET |
|
|
UDF_CCB_CREATE_TIME_SET |
|
|
UDF_CCB_ACCESS_TIME_SET |
|
|
UDF_CCB_WRITE_TIME_SET))) ) {
|
|
UDFSetFileXTime(NextFileInfo, NULL, NULL, &NtTime, NULL);
|
|
NtReqFcb->ChangeTime.QuadPart = NtTime;
|
|
}
|
|
}
|
|
#endif //UDF_READ_ONLY_BUILD
|
|
|
|
if(!(Fcb->FCBFlags & UDF_FCB_DIRECTORY) &&
|
|
ForcedCleanUp) {
|
|
// flush system cache
|
|
MmPrint((" CcUninitializeCacheMap()\n"));
|
|
CcUninitializeCacheMap(FileObject, &(UDFGlobalData.UDFLargeZero), NULL);
|
|
} else {
|
|
MmPrint((" CcUninitializeCacheMap()\n"));
|
|
CcUninitializeCacheMap(FileObject, NULL, NULL);
|
|
}
|
|
|
|
// release resources now.
|
|
// they'll be acquired in UDFCloseFileInfoChain()
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
UDFReleaseResource(&(NtReqFcb->MainResource));
|
|
AcquiredFCB = FALSE;
|
|
|
|
if(Fcb->FileInfo->ParentFile) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB);
|
|
UDFReleaseResource(&(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB->MainResource));
|
|
} else {
|
|
UDFReleaseResource(&(Vcb->VCBResource));
|
|
}
|
|
AcquiredParentFCB = FALSE;
|
|
// close the chain
|
|
ASSERT(AcquiredVcb);
|
|
RC2 = UDFCloseFileInfoChain(Vcb, NextFileInfo, Ccb->TreeLength, TRUE);
|
|
if(NT_SUCCESS(RC))
|
|
RC = RC2;
|
|
|
|
Ccb->CCBFlags |= UDF_CCB_CLEANED;
|
|
|
|
// We must clean up the share access at this time, since we may not
|
|
// get a Close call for awhile if the file was mapped through this
|
|
// File Object.
|
|
IoRemoveShareAccess( FileObject, &(NtReqFcb->FCBShareAccess) );
|
|
|
|
NtReqFcb->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb);
|
|
|
|
FileObject->Flags |= FO_CLEANUP_COMPLETE;
|
|
|
|
try_exit: NOTHING;
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
if(AcquiredFCB) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
UDFReleaseResource(&(NtReqFcb->MainResource));
|
|
}
|
|
|
|
if(AcquiredParentFCB) {
|
|
if(Fcb->FileInfo->ParentFile) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB);
|
|
UDFReleaseResource(&(Fcb->FileInfo->ParentFile->Fcb->NTRequiredFCB->MainResource));
|
|
} else {
|
|
UDFReleaseResource(&(Vcb->VCBResource));
|
|
}
|
|
}
|
|
|
|
if(AcquiredVcb) {
|
|
UDFReleaseResource(&(Vcb->VCBResource));
|
|
AcquiredVcb = FALSE;
|
|
}
|
|
|
|
if (!_SEH2_AbnormalTermination()) {
|
|
// complete the IRP
|
|
Irp->IoStatus.Status = RC;
|
|
Irp->IoStatus.Information = 0;
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
// Free up the Irp Context
|
|
UDFReleaseIrpContext(PtrIrpContext);
|
|
}
|
|
|
|
} _SEH2_END; // end of "__finally" processing
|
|
return(RC);
|
|
} // end UDFCommonCleanup()
|
|
|
|
/*
|
|
This routine walks through the tree to RootDir &
|
|
calls UDFCloseFile__() for each file instance
|
|
imho, Useful feature
|
|
*/
|
|
NTSTATUS
|
|
UDFCloseFileInfoChain(
|
|
IN PVCB Vcb,
|
|
IN PUDF_FILE_INFO fi,
|
|
IN ULONG TreeLength,
|
|
IN BOOLEAN VcbAcquired
|
|
)
|
|
{
|
|
PUDF_FILE_INFO ParentFI;
|
|
PtrUDFFCB Fcb;
|
|
PtrUDFFCB ParentFcb = NULL;
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
NTSTATUS RC2;
|
|
|
|
// we can't process Tree until we can acquire Vcb
|
|
if(!VcbAcquired)
|
|
UDFAcquireResourceShared(&(Vcb->VCBResource),TRUE);
|
|
|
|
AdPrint(("UDFCloseFileInfoChain\n"));
|
|
for(; TreeLength && fi; TreeLength--) {
|
|
|
|
// close parent chain (if any)
|
|
// if we started path parsing not from RootDir on Create,
|
|
// we would never get RootDir here
|
|
ValidateFileInfo(fi);
|
|
|
|
// acquire parent
|
|
if((ParentFI = fi->ParentFile)) {
|
|
ParentFcb = fi->Fcb->ParentFcb;
|
|
ASSERT(ParentFcb);
|
|
ASSERT(ParentFcb->NTRequiredFCB);
|
|
UDF_CHECK_PAGING_IO_RESOURCE(ParentFcb->NTRequiredFCB);
|
|
UDFAcquireResourceExclusive(&(ParentFcb->NTRequiredFCB->MainResource),TRUE);
|
|
ASSERT(ParentFcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_FCB);
|
|
ASSERT(ParentFcb->NTRequiredFCB->CommonFCBHeader.NodeTypeCode == UDF_NODE_TYPE_NT_REQ_FCB);
|
|
} else {
|
|
AdPrint(("Acquiring VCB...\n"));
|
|
UDFAcquireResourceShared(&(Vcb->VCBResource),TRUE);
|
|
AdPrint(("Done\n"));
|
|
}
|
|
// acquire current file/dir
|
|
// we must assure that no more threads try to reuse this object
|
|
if((Fcb = fi->Fcb)) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(Fcb->NTRequiredFCB);
|
|
UDFAcquireResourceExclusive(&(Fcb->NTRequiredFCB->MainResource),TRUE);
|
|
ASSERT_REF(Fcb->ReferenceCount >= fi->RefCount);
|
|
if(!(Fcb->FCBFlags & UDF_FCB_DELETED) &&
|
|
(Fcb->FCBFlags & UDF_FCB_VALID))
|
|
UDFWriteSecurity(Vcb, Fcb, &(Fcb->NTRequiredFCB->SecurityDesc));
|
|
RC2 = UDFCloseFile__(Vcb,fi);
|
|
if(!NT_SUCCESS(RC2))
|
|
RC = RC2;
|
|
ASSERT_REF(Fcb->ReferenceCount > fi->RefCount);
|
|
UDF_CHECK_PAGING_IO_RESOURCE(Fcb->NTRequiredFCB);
|
|
UDFReleaseResource(&(Fcb->NTRequiredFCB->MainResource));
|
|
} else {
|
|
BrutePoint();
|
|
RC2 = UDFCloseFile__(Vcb,fi);
|
|
if(!NT_SUCCESS(RC2))
|
|
RC = RC2;
|
|
}
|
|
|
|
if(ParentFI) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(ParentFcb->NTRequiredFCB);
|
|
UDFReleaseResource(&(ParentFcb->NTRequiredFCB->MainResource));
|
|
} else {
|
|
UDFReleaseResource(&(Vcb->VCBResource));
|
|
}
|
|
fi = ParentFI;
|
|
}
|
|
|
|
if(!VcbAcquired)
|
|
UDFReleaseResource(&(Vcb->VCBResource));
|
|
|
|
return RC;
|
|
|
|
} // end UDFCloseFileInfoChain()
|