reactos/drivers/filesystems/udfs/cleanup.cpp
2021-06-11 15:33:08 +03:00

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()