reactos/drivers/filesystems/udfs/fileinfo.cpp

2704 lines
106 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.
////////////////////////////////////////////////////////////////////
/*************************************************************************
*
* File: Fileinfo.cpp
*
* Module: UDF File System Driver (Kernel mode execution only)
*
* Description:
* Contains code to handle the "set/query file information" dispatch
* entry points.
*
*************************************************************************/
#include "udffs.h"
// define the file specific bug-check id
#define UDF_BUG_CHECK_ID UDF_FILE_INFORMATION
#define MEM_USREN_TAG "US_Ren"
#define MEM_USREN2_TAG "US_Ren2"
#define MEM_USFIDC_TAG "US_FIDC"
#define MEM_USHL_TAG "US_HL"
/*************************************************************************
*
* Function: UDFFileInfo()
*
* Description:
* The I/O Manager will invoke this routine to handle a set/query file
* information 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/Error
*
*************************************************************************/
NTSTATUS
NTAPI
UDFFileInfo(
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(("UDFFileInfo: \n"));
FsRtlEnterFileSystem();
ASSERT(DeviceObject);
ASSERT(Irp);
// set the top level context
AreWeTopLevel = UDFIsIrpTopLevel(Irp);
ASSERT(!UDFIsFSDevObj(DeviceObject));
_SEH2_TRY {
// get an IRP context structure and issue the request
PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
if(PtrIrpContext) {
RC = UDFCommonFileInfo(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 UDFFileInfo()
/*************************************************************************
*
* Function: UDFCommonFileInfo()
*
* 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: STATUS_SUCCESS/Error
*
*************************************************************************/
NTSTATUS
UDFCommonFileInfo(
PtrUDFIrpContext PtrIrpContext,
PIRP Irp
)
{
NTSTATUS RC = STATUS_SUCCESS;
PIO_STACK_LOCATION IrpSp = NULL;
PFILE_OBJECT FileObject = NULL;
PtrUDFFCB Fcb = NULL;
PtrUDFCCB Ccb = NULL;
PVCB Vcb = NULL;
PtrUDFNTRequiredFCB NtReqFcb = NULL;
BOOLEAN MainResourceAcquired = FALSE;
BOOLEAN ParentResourceAcquired = FALSE;
BOOLEAN PagingIoResourceAcquired = FALSE;
PVOID PtrSystemBuffer = NULL;
LONG BufferLength = 0;
FILE_INFORMATION_CLASS FunctionalityRequested;
BOOLEAN CanWait = FALSE;
BOOLEAN PostRequest = FALSE;
BOOLEAN AcquiredVcb = FALSE;
PIRP TopIrp;
TmPrint(("UDFCommonFileInfo: irp %x\n", Irp));
TopIrp = IoGetTopLevelIrp();
switch((ULONG)TopIrp) {
case FSRTL_FSP_TOP_LEVEL_IRP:
UDFPrint((" FSRTL_FSP_TOP_LEVEL_IRP\n"));
break;
case FSRTL_CACHE_TOP_LEVEL_IRP:
UDFPrint((" FSRTL_CACHE_TOP_LEVEL_IRP\n"));
break;
case FSRTL_MOD_WRITE_TOP_LEVEL_IRP:
UDFPrint((" FSRTL_MOD_WRITE_TOP_LEVEL_IRP\n"));
break;
case FSRTL_FAST_IO_TOP_LEVEL_IRP:
UDFPrint((" FSRTL_FAST_IO_TOP_LEVEL_IRP\n"));
BrutePoint()
break;
case NULL:
UDFPrint((" NULL TOP_LEVEL_IRP\n"));
break;
default:
if(TopIrp == Irp) {
UDFPrint((" TOP_LEVEL_IRP\n"));
} else {
UDFPrint((" RECURSIVE_IRP, TOP = %x\n", TopIrp));
}
}
_SEH2_TRY {
// First, get a pointer to the current I/O stack location.
IrpSp = IoGetCurrentIrpStackLocation(Irp);
ASSERT(IrpSp);
FileObject = IrpSp->FileObject;
ASSERT(FileObject);
// Get the FCB and CCB pointers.
Ccb = (PtrUDFCCB)(FileObject->FsContext2);
ASSERT(Ccb);
if(!Ccb) {
// some applications sends us FO without Ccb
// This is not allowed...
RC = STATUS_INVALID_PARAMETER;
try_return(RC);
}
Fcb = Ccb->Fcb;
ASSERT(Fcb);
NtReqFcb = Fcb->NTRequiredFCB;
CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE;
// If the caller has opened a logical volume and is attempting to
// query information for it as a file stream, return an error.
if(Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) {
// This is not allowed. Caller must use get/set volume information instead.
RC = STATUS_INVALID_PARAMETER;
try_return(RC);
}
Vcb = (PVCB)(IrpSp->DeviceObject->DeviceExtension);
ASSERT(Vcb);
ASSERT(Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_FCB);
//Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
// The NT I/O Manager always allocates and supplies a system
// buffer for query and set file information calls.
// Copying information to/from the user buffer and the system
// buffer is performed by the I/O Manager and the FSD need not worry about it.
PtrSystemBuffer = Irp->AssociatedIrp.SystemBuffer;
UDFFlushTryBreak(Vcb);
if(!UDFAcquireResourceShared(&(Vcb->VCBResource), CanWait)) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
AcquiredVcb = TRUE;
if(IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) {
// Now, obtain some parameters.
BufferLength = IrpSp->Parameters.QueryFile.Length;
FunctionalityRequested = IrpSp->Parameters.QueryFile.FileInformationClass;
#ifdef UDF_ENABLE_SECURITY
RC = IoCheckFunctionAccess(
Ccb->PreviouslyGrantedAccess,
PtrIrpContext->MajorFunction,
PtrIrpContext->MinorFunction,
0,
&FunctionalityRequested,
NULL);
if(!NT_SUCCESS(RC)) {
try_return(RC);
}
#endif //UDF_ENABLE_SECURITY
// Acquire the MainResource shared (NOTE: for paging-IO on a
// page file, we should avoid acquiring any resources and simply
// trust the VMM to do the right thing, else we could possibly
// run into deadlocks).
if(!(Fcb->FCBFlags & UDF_FCB_PAGE_FILE)) {
// Acquire the MainResource shared.
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
UDFAcquireResourceShared(&(NtReqFcb->MainResource), TRUE);
MainResourceAcquired = TRUE;
}
// Do whatever the caller asked us to do
switch (FunctionalityRequested) {
case FileBasicInformation:
RC = UDFGetBasicInformation(FileObject, Fcb, (PFILE_BASIC_INFORMATION)PtrSystemBuffer, &BufferLength);
break;
case FileStandardInformation:
RC = UDFGetStandardInformation(Fcb, (PFILE_STANDARD_INFORMATION) PtrSystemBuffer, &BufferLength);
break;
#if(_WIN32_WINNT >= 0x0400)
case FileNetworkOpenInformation:
RC = UDFGetNetworkInformation(Fcb, (PFILE_NETWORK_OPEN_INFORMATION)PtrSystemBuffer, &BufferLength);
break;
#endif // _WIN32_WINNT >= 0x0400
case FileInternalInformation:
RC = UDFGetInternalInformation(PtrIrpContext, Fcb, Ccb, (PFILE_INTERNAL_INFORMATION) PtrSystemBuffer, &BufferLength);
break;
case FileEaInformation:
RC = UDFGetEaInformation(PtrIrpContext, Fcb, (PFILE_EA_INFORMATION) PtrSystemBuffer, &BufferLength);
break;
case FileNameInformation:
RC = UDFGetFullNameInformation(FileObject, (PFILE_NAME_INFORMATION) PtrSystemBuffer, &BufferLength);
break;
case FileAlternateNameInformation:
RC = UDFGetAltNameInformation(Fcb, (PFILE_NAME_INFORMATION) PtrSystemBuffer, &BufferLength);
break;
// case FileCompressionInformation:
// // RC = UDFGetCompressionInformation(...);
// break;
case FilePositionInformation:
RC = UDFGetPositionInformation(FileObject, (PFILE_POSITION_INFORMATION)PtrSystemBuffer, &BufferLength);
break;
case FileStreamInformation:
RC = UDFGetFileStreamInformation(Fcb, (PFILE_STREAM_INFORMATION) PtrSystemBuffer, &BufferLength);
break;
case FileAllInformation:
// The I/O Manager supplies the Mode, Access, and Alignment
// information. The rest is up to us to provide.
// Therefore, decrement the BufferLength appropriately (assuming
// that the above 3 types on information are already in the
// buffer)
{
PFILE_ALL_INFORMATION PtrAllInfo = (PFILE_ALL_INFORMATION)PtrSystemBuffer;
BufferLength -= (sizeof(FILE_MODE_INFORMATION) +
sizeof(FILE_ACCESS_INFORMATION) +
sizeof(FILE_ALIGNMENT_INFORMATION));
// Get the remaining stuff.
if(!NT_SUCCESS(RC = UDFGetBasicInformation(FileObject, Fcb, &(PtrAllInfo->BasicInformation), &BufferLength)) ||
!NT_SUCCESS(RC = UDFGetStandardInformation(Fcb, &(PtrAllInfo->StandardInformation), &BufferLength)) ||
!NT_SUCCESS(RC = UDFGetInternalInformation(PtrIrpContext, Fcb, Ccb, &(PtrAllInfo->InternalInformation), &BufferLength)) ||
!NT_SUCCESS(RC = UDFGetEaInformation(PtrIrpContext, Fcb, &(PtrAllInfo->EaInformation), &BufferLength)) ||
!NT_SUCCESS(RC = UDFGetPositionInformation(FileObject, &(PtrAllInfo->PositionInformation), &BufferLength)) ||
!NT_SUCCESS(RC = UDFGetFullNameInformation(FileObject, &(PtrAllInfo->NameInformation), &BufferLength))
)
try_return(RC);
}
break;
default:
RC = STATUS_INVALID_PARAMETER;
try_return(RC);
}
#ifndef UDF_READ_ONLY_BUILD
} else {
// if(IrpSp->MajorFunction == IRP_MJ_SET_INFORMATION) {
Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
ASSERT(IrpSp->MajorFunction == IRP_MJ_SET_INFORMATION);
// Now, obtain some parameters.
FunctionalityRequested = IrpSp->Parameters.SetFile.FileInformationClass;
if((Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) &&
(FunctionalityRequested != FilePositionInformation)) {
try_return(RC = STATUS_ACCESS_DENIED);
}
#ifdef UDF_ENABLE_SECURITY
RC = IoCheckFunctionAccess(
Ccb->PreviouslyGrantedAccess,
PtrIrpContext->MajorFunction,
PtrIrpContext->MinorFunction,
0,
&FunctionalityRequested,
NULL);
if(!NT_SUCCESS(RC)) {
try_return(RC);
}
#endif //UDF_ENABLE_SECURITY
// If the FSD supports opportunistic locking,
// then we should check whether the oplock state
// allows the caller to proceed.
// Rename, and link operations require creation of a directory
// entry and possibly deletion of another directory entry.
// Unless this is an operation on a page file, we should go ahead and
// acquire the FCB exclusively at this time. Note that we will pretty
// much block out anything being done to the FCB from this point on.
if(!(Fcb->FCBFlags & UDF_FCB_PAGE_FILE) &&
(FunctionalityRequested != FilePositionInformation) &&
(FunctionalityRequested != FileRenameInformation) &&
(FunctionalityRequested != FileLinkInformation)) {
// Acquire the Parent & Main Resources exclusive.
if(Fcb->FileInfo->ParentFile) {
UDF_CHECK_PAGING_IO_RESOURCE(Fcb->ParentFcb->NTRequiredFCB);
if(!UDFAcquireResourceExclusive(&(Fcb->ParentFcb->NTRequiredFCB->MainResource), CanWait)) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
ParentResourceAcquired = TRUE;
}
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
MainResourceAcquired = TRUE;
} else
// The only operations that could conceivably proceed from this point
// on are paging-IO read/write operations. For delete, link (rename),
// set allocation size, and set EOF, should also acquire the paging-IO
// resource, thereby synchronizing with paging-IO requests.
if((Fcb->FCBFlags & UDF_FCB_PAGE_FILE) &&
((FunctionalityRequested == FileDispositionInformation) ||
(FunctionalityRequested == FileAllocationInformation) ||
(FunctionalityRequested == FileEndOfFileInformation)) ) {
// Acquire the MainResource shared.
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
if(!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
MainResourceAcquired = TRUE;
// Acquire the PagingResource exclusive.
if(!UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
PagingIoResourceAcquired = TRUE;
} else if((FunctionalityRequested != FileRenameInformation) &&
(FunctionalityRequested != FileLinkInformation)) {
// Acquire the MainResource shared.
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
if(!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
MainResourceAcquired = TRUE;
}
if((Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) &&
(FunctionalityRequested != FilePositionInformation)) {
AdPrint((" Can't change File Information on blank volume ;)\n"));
try_return(RC = STATUS_ACCESS_DENIED);
}
// Do whatever the caller asked us to do
switch (FunctionalityRequested) {
case FileBasicInformation:
RC = UDFSetBasicInformation(Fcb, Ccb, FileObject, (PFILE_BASIC_INFORMATION)PtrSystemBuffer);
break;
case FilePositionInformation: {
// Check if no intermediate buffering has been specified.
// If it was specified, do not allow non-aligned set file
// position requests to succeed.
PFILE_POSITION_INFORMATION PtrFileInfoBuffer;
PtrFileInfoBuffer = (PFILE_POSITION_INFORMATION)PtrSystemBuffer;
if(FileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) {
if(PtrFileInfoBuffer->CurrentByteOffset.LowPart & IrpSp->DeviceObject->AlignmentRequirement) {
// Invalid alignment.
try_return(RC = STATUS_INVALID_PARAMETER);
}
}
FileObject->CurrentByteOffset = PtrFileInfoBuffer->CurrentByteOffset;
break;
}
case FileDispositionInformation:
RC = UDFSetDispositionInformation(Fcb, Ccb, Vcb, FileObject,
((PFILE_DISPOSITION_INFORMATION)PtrSystemBuffer)->DeleteFile ? TRUE : FALSE);
break;
case FileRenameInformation:
if(!CanWait) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
RC = UDFRename(IrpSp, Fcb, Ccb, FileObject, (PFILE_RENAME_INFORMATION)PtrSystemBuffer);
if(RC == STATUS_PENDING) {
PostRequest = TRUE;
try_return(RC);
}
break;
#ifdef UDF_ALLOW_HARD_LINKS
case FileLinkInformation:
if(!CanWait) {
PostRequest = TRUE;
try_return(RC = STATUS_PENDING);
}
RC = UDFHardLink(IrpSp, Fcb, Ccb, FileObject, (PFILE_LINK_INFORMATION)PtrSystemBuffer);
break;
#endif //UDF_ALLOW_HARD_LINKS
case FileAllocationInformation:
RC = UDFSetAllocationInformation(Fcb, Ccb, Vcb, FileObject,
PtrIrpContext, Irp,
(PFILE_ALLOCATION_INFORMATION)PtrSystemBuffer);
break;
case FileEndOfFileInformation:
RC = UDFSetEOF(IrpSp, Fcb, Ccb, Vcb, FileObject, Irp, (PFILE_END_OF_FILE_INFORMATION)PtrSystemBuffer);
break;
default:
RC = STATUS_INVALID_PARAMETER;
try_return(RC);
}
#endif //UDF_READ_ONLY_BUILD
}
try_exit: NOTHING;
} _SEH2_FINALLY {
if(PagingIoResourceAcquired) {
UDFReleaseResource(&(NtReqFcb->PagingIoResource));
PagingIoResourceAcquired = FALSE;
}
if(MainResourceAcquired) {
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
UDFReleaseResource(&(NtReqFcb->MainResource));
MainResourceAcquired = FALSE;
}
if(ParentResourceAcquired) {
UDF_CHECK_PAGING_IO_RESOURCE(Fcb->ParentFcb->NTRequiredFCB);
UDFReleaseResource(&(Fcb->ParentFcb->NTRequiredFCB->MainResource));
ParentResourceAcquired = FALSE;
}
// Post IRP if required
if(PostRequest) {
// Since, the I/O Manager gave us a system buffer, we do not
// need to "lock" anything.
// Perform the post operation which will mark the IRP pending
// and will return STATUS_PENDING back to us
RC = UDFPostRequest(PtrIrpContext, Irp);
} else {
if (!_SEH2_AbnormalTermination()) {
Irp->IoStatus.Status = RC;
// Set status for "query" requests
if(IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) {
// Return the amount of information transferred.
Irp->IoStatus.Information = IrpSp->Parameters.QueryFile.Length - BufferLength;
#ifndef UDF_READ_ONLY_BUILD
#ifdef UDF_DELAYED_CLOSE
} else
if(NT_SUCCESS(RC)) {
if(FunctionalityRequested == FileDispositionInformation) {
if(AcquiredVcb) {
AcquiredVcb = FALSE;
UDFReleaseResource(&(Vcb->VCBResource));
}
UDFRemoveFromDelayedQueue(Fcb);
}
#endif //UDF_DELAYED_CLOSE
#endif //UDF_READ_ONLY_BUILD
}
// complete the IRP
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
// Free up the Irp Context
UDFReleaseIrpContext(PtrIrpContext);
} // can we complete the IRP ?
}
if(AcquiredVcb) {
UDFReleaseResource(&(Vcb->VCBResource));
}
} _SEH2_END;// end of "__finally" processing
return(RC);
} // end UDFCommonFileInfo()
/*
Return some time-stamps and file attributes to the caller.
*/
NTSTATUS
UDFGetBasicInformation(
IN PFILE_OBJECT FileObject,
IN PtrUDFFCB Fcb,
IN PFILE_BASIC_INFORMATION PtrBuffer,
IN OUT LONG* PtrReturnedLength
)
{
NTSTATUS RC = STATUS_SUCCESS;
PUDF_FILE_INFO FileInfo;
PDIR_INDEX_ITEM DirNdx;
AdPrint(("UDFGetBasicInformation: \n"));
_SEH2_TRY {
if(*PtrReturnedLength < (LONG)sizeof(FILE_BASIC_INFORMATION)) {
try_return(RC = STATUS_BUFFER_OVERFLOW);
}
// Zero out the supplied buffer.
RtlZeroMemory(PtrBuffer, sizeof(FILE_BASIC_INFORMATION));
// Get information from the FCB and update TimesCache in DirIndex
FileInfo = Fcb->FileInfo;
if(!FileInfo) {
AdPrint(("!!!!!!!! Bu-u-u-u-u-g !!!!!!!!!!!\n"));
AdPrint(("!!!! GetBasicInfo to unopened file !!!!\n"));
try_return(RC = STATUS_INVALID_PARAMETER);
}
DirNdx = UDFDirIndex(UDFGetDirIndexByFileInfo(FileInfo), FileInfo->Index);
PtrBuffer->CreationTime = Fcb->NTRequiredFCB->CreationTime;
DirNdx->CreationTime = PtrBuffer->CreationTime.QuadPart;
PtrBuffer->LastAccessTime = Fcb->NTRequiredFCB->LastAccessTime;
DirNdx->LastAccessTime = PtrBuffer->LastAccessTime.QuadPart;
PtrBuffer->LastWriteTime = Fcb->NTRequiredFCB->LastWriteTime;
DirNdx->LastWriteTime = PtrBuffer->LastWriteTime.QuadPart;
PtrBuffer->ChangeTime = Fcb->NTRequiredFCB->ChangeTime;
DirNdx->ChangeTime = PtrBuffer->ChangeTime.QuadPart;
// Now fill in the attributes.
if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
PtrBuffer->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
#ifdef UDF_DBG
if(!FileInfo->Dloc->DirIndex) AdPrint(("*****!!!!! Directory has no DirIndex !!!!!*****\n"));
#endif
}
// Similarly, fill in attributes indicating a hidden file, system
// file, compressed file, temporary file, etc. if the FSD supports
// such file attribute values.
PtrBuffer->FileAttributes |= UDFAttributesToNT(DirNdx,NULL);
if(FileObject->Flags & FO_TEMPORARY_FILE) {
PtrBuffer->FileAttributes |= FILE_ATTRIBUTE_TEMPORARY;
} else {
PtrBuffer->FileAttributes &= ~FILE_ATTRIBUTE_TEMPORARY;
}
if(!PtrBuffer->FileAttributes) {
PtrBuffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
}
try_exit: NOTHING;
} _SEH2_FINALLY {
if(NT_SUCCESS(RC)) {
// Return the amount of information filled in.
(*PtrReturnedLength) -= sizeof(FILE_BASIC_INFORMATION);
}
} _SEH2_END;
return(RC);
} // end UDFGetBasicInformation()
/*
Return file sizes to the caller.
*/
NTSTATUS
UDFGetStandardInformation(
IN PtrUDFFCB Fcb,
IN PFILE_STANDARD_INFORMATION PtrBuffer,
IN OUT LONG* PtrReturnedLength
)
{
NTSTATUS RC = STATUS_SUCCESS;
PUDF_FILE_INFO FileInfo;
// PVCB Vcb;
AdPrint(("UDFGetStandardInformation: \n"));
_SEH2_TRY {
if(*PtrReturnedLength < (LONG)sizeof(FILE_STANDARD_INFORMATION)) {
try_return(RC = STATUS_BUFFER_OVERFLOW);
}
// Zero out the supplied buffer.
RtlZeroMemory(PtrBuffer, sizeof(FILE_STANDARD_INFORMATION));
FileInfo = Fcb->FileInfo;
if(!FileInfo) {
AdPrint(("!!!!!!!! Bu-u-u-u-u-g !!!!!!!!!!!\n"));
AdPrint(("!!!! GetStandardInfo to unopened file !!!!\n"));
try_return(RC = STATUS_INVALID_PARAMETER);
}
// Vcb = Fcb->Vcb;
PtrBuffer->NumberOfLinks = UDFGetFileLinkCount(FileInfo);
PtrBuffer->DeletePending = (Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) ? TRUE : FALSE;
// Case on whether this is a file or a directory, and extract
// the information and fill in the fcb/dcb specific parts
// of the output buffer
if(UDFIsADirectory(Fcb->FileInfo)) {
PtrBuffer->Directory = TRUE;
} else {
if(Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize.LowPart == 0xffffffff) {
Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize.QuadPart =
UDFSysGetAllocSize(Fcb->Vcb, UDFGetFileSize(FileInfo));
}
PtrBuffer->AllocationSize = Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize;
PtrBuffer->EndOfFile = Fcb->NTRequiredFCB->CommonFCBHeader.FileSize;
PtrBuffer->Directory = FALSE;
}
try_exit: NOTHING;
} _SEH2_FINALLY {
if(NT_SUCCESS(RC)) {
// Return the amount of information filled in.
*PtrReturnedLength -= sizeof(FILE_STANDARD_INFORMATION);
}
} _SEH2_END;
return(RC);
} // end UDFGetStandardInformation()
/*
Return some time-stamps and file attributes to the caller.
*/
NTSTATUS
UDFGetNetworkInformation(
IN PtrUDFFCB Fcb,
IN PFILE_NETWORK_OPEN_INFORMATION PtrBuffer,
IN OUT PLONG PtrReturnedLength
)
{
NTSTATUS RC = STATUS_SUCCESS;
PUDF_FILE_INFO FileInfo;
AdPrint(("UDFGetNetworkInformation: \n"));
_SEH2_TRY {
if(*PtrReturnedLength < (LONG)sizeof(FILE_NETWORK_OPEN_INFORMATION)) {
try_return(RC = STATUS_BUFFER_OVERFLOW);
}
// Zero out the supplied buffer.
RtlZeroMemory(PtrBuffer, sizeof(FILE_NETWORK_OPEN_INFORMATION));
// Get information from the FCB.
PtrBuffer->CreationTime = Fcb->NTRequiredFCB->CreationTime;
PtrBuffer->LastAccessTime = Fcb->NTRequiredFCB->LastAccessTime;
PtrBuffer->LastWriteTime = Fcb->NTRequiredFCB->LastWriteTime;
PtrBuffer->ChangeTime = Fcb->NTRequiredFCB->ChangeTime;
FileInfo = Fcb->FileInfo;
if(!FileInfo) {
AdPrint(("!!!!!!!! Bu-u-u-u-u-g !!!!!!!!!!!\n"));
AdPrint(("!!!! UDFGetNetworkInformation to unopened file !!!!\n"));
try_return(RC = STATUS_INVALID_PARAMETER);
}
// Now fill in the attributes.
if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
PtrBuffer->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
#ifdef UDF_DBG
if(!FileInfo->Dloc->DirIndex) AdPrint(("*****!!!!! Directory has no DirIndex !!!!!*****\n"));
#endif
} else {
if(Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize.LowPart == 0xffffffff) {
Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize.QuadPart =
UDFSysGetAllocSize(Fcb->Vcb, UDFGetFileSize(FileInfo));
}
PtrBuffer->AllocationSize = Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize;
PtrBuffer->EndOfFile = Fcb->NTRequiredFCB->CommonFCBHeader.FileSize;
}
// Similarly, fill in attributes indicating a hidden file, system
// file, compressed file, temporary file, etc. if the FSD supports
// such file attribute values.
PtrBuffer->FileAttributes |= UDFAttributesToNT(UDFDirIndex(UDFGetDirIndexByFileInfo(FileInfo), FileInfo->Index),NULL);
if(!PtrBuffer->FileAttributes) {
PtrBuffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
}
try_exit: NOTHING;
} _SEH2_FINALLY {
if(NT_SUCCESS(RC)) {
// Return the amount of information filled in.
(*PtrReturnedLength) -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
}
} _SEH2_END;
return(RC);
} // end UDFGetNetworkInformation()
/*
Return some time-stamps and file attributes to the caller.
*/
NTSTATUS
UDFGetInternalInformation(
PtrUDFIrpContext PtrIrpContext,
IN PtrUDFFCB Fcb,
IN PtrUDFCCB Ccb,
IN PFILE_INTERNAL_INFORMATION PtrBuffer,
IN OUT PLONG PtrReturnedLength
)
{
NTSTATUS RC = STATUS_SUCCESS;
PUDF_FILE_INFO FileInfo;
PVCB Vcb;
AdPrint(("UDFGetInternalInformation\n"));
_SEH2_TRY {
if(*PtrReturnedLength < (LONG)sizeof(FILE_INTERNAL_INFORMATION)) {
try_return(RC = STATUS_BUFFER_OVERFLOW);
}
// Zero out the supplied buffer.
RtlZeroMemory(PtrBuffer, sizeof(FILE_INTERNAL_INFORMATION));
FileInfo = Fcb->FileInfo;
if(!FileInfo) {
AdPrint(("!!!!!!!! Bu-u-u-u-u-g !!!!!!!!!!!\n"));
AdPrint(("!!!! UDFGetInternalInformation to unopened file !!!!\n"));
try_return(RC = STATUS_INVALID_PARAMETER);
}
Vcb = Fcb->Vcb;
PtrBuffer->IndexNumber.QuadPart = UDFGetNTFileId(Vcb, FileInfo, &(Fcb->FCBName->ObjectName));
UDFAcquireResourceExclusive(&(Fcb->Vcb->FileIdResource), TRUE);
// remember File Id & full path
UDFStoreFileId(Fcb->Vcb, Ccb, FileInfo, PtrBuffer->IndexNumber.QuadPart);
UDFReleaseResource(&(Fcb->Vcb->FileIdResource));
try_exit: NOTHING;
} _SEH2_FINALLY {
if(NT_SUCCESS(RC)) {
// Return the amount of information filled in.
*PtrReturnedLength -= sizeof(FILE_INTERNAL_INFORMATION);
}
} _SEH2_END;
return(RC);
} // end UDFGetInternalInformation()
/*
Return zero-filled EAs to the caller.
*/
NTSTATUS
UDFGetEaInformation(
PtrUDFIrpContext PtrIrpContext,
IN PtrUDFFCB Fcb,
IN PFILE_EA_INFORMATION PtrBuffer,
IN OUT PLONG PtrReturnedLength
)
{
NTSTATUS RC = STATUS_SUCCESS;
AdPrint(("UDFGetEaInformation\n"));
_SEH2_TRY {
if(*PtrReturnedLength < (LONG)sizeof(FILE_EA_INFORMATION)) {
try_return(RC = STATUS_BUFFER_OVERFLOW);
}
// Zero out the supplied buffer.
PtrBuffer->EaSize = 0;
try_exit: NOTHING;
} _SEH2_FINALLY {
if(NT_SUCCESS(RC)) {
// Return the amount of information filled in.
*PtrReturnedLength -= sizeof(FILE_EA_INFORMATION);
}
} _SEH2_END;
return(RC);
} // end UDFGetEaInformation()
/*
Return file's long name to the caller.
*/
NTSTATUS
UDFGetFullNameInformation(
IN PFILE_OBJECT FileObject,
IN PFILE_NAME_INFORMATION PtrBuffer,
IN OUT PLONG PtrReturnedLength
)
{
ULONG BytesToCopy;
NTSTATUS RC = STATUS_SUCCESS;
AdPrint(("UDFGetFullNameInformation\n"));
PtrBuffer->FileNameLength = FileObject->FileName.Length;
BytesToCopy = FileObject->FileName.Length;
if (PtrBuffer->FileNameLength + sizeof( ULONG ) > (ULONG)(*PtrReturnedLength)) {
BytesToCopy = *PtrReturnedLength - sizeof( ULONG );
RC = STATUS_BUFFER_OVERFLOW;
}
RtlCopyMemory( PtrBuffer->FileName, FileObject->FileName.Buffer, BytesToCopy );
// Reduce the available bytes by the amount stored into this buffer.
*PtrReturnedLength -= sizeof( ULONG ) + PtrBuffer->FileNameLength;
return RC;
} // end UDFGetFullNameInformation()
/*
Return file short(8.3) name to the caller.
*/
NTSTATUS
UDFGetAltNameInformation(
IN PtrUDFFCB Fcb,
IN PFILE_NAME_INFORMATION PtrBuffer,
IN OUT PLONG PtrReturnedLength
)
{
PDIR_INDEX_ITEM DirNdx;
ULONG BytesToCopy;
UNICODE_STRING ShortName;
WCHAR ShortNameBuffer[13];
AdPrint(("UDFGetAltNameInformation: \n"));
*PtrReturnedLength -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]);
DirNdx = UDFDirIndex(UDFGetDirIndexByFileInfo(Fcb->FileInfo), Fcb->FileInfo->Index);
ShortName.MaximumLength = 13 * sizeof(WCHAR);
ShortName.Buffer = (PWCHAR)&ShortNameBuffer;
UDFDOSName__(Fcb->Vcb, &ShortName, &(DirNdx->FName), Fcb->FileInfo);
if(*PtrReturnedLength < ShortName.Length) {
return(STATUS_BUFFER_OVERFLOW);
} else {
BytesToCopy = ShortName.Length;
*PtrReturnedLength -= ShortName.Length;
}
RtlCopyMemory( &(PtrBuffer->FileName),
ShortName.Buffer,
BytesToCopy );
PtrBuffer->FileNameLength = ShortName.Length;
return(STATUS_SUCCESS);
} // end UDFGetAltNameInformation()
/*
Get file position information
*/
NTSTATUS
UDFGetPositionInformation(
IN PFILE_OBJECT FileObject,
IN PFILE_POSITION_INFORMATION PtrBuffer,
IN OUT PLONG PtrReturnedLength
)
{
if(*PtrReturnedLength < (LONG)sizeof(FILE_POSITION_INFORMATION)) {
return(STATUS_BUFFER_OVERFLOW);
}
PtrBuffer->CurrentByteOffset = FileObject->CurrentByteOffset;
// Modify the local variable for BufferLength appropriately.
*PtrReturnedLength -= sizeof(FILE_POSITION_INFORMATION);
return(STATUS_SUCCESS);
} // end UDFGetAltNameInformation()
/*
Get file file stream(s) information
*/
NTSTATUS
UDFGetFileStreamInformation(
IN PtrUDFFCB Fcb,
IN PFILE_STREAM_INFORMATION PtrBuffer,
IN OUT PLONG PtrReturnedLength
)
{
NTSTATUS RC = STATUS_SUCCESS;
PUDF_FILE_INFO FileInfo;
PUDF_FILE_INFO SDirInfo;
PVCB Vcb;
BOOLEAN FcbAcquired = FALSE;
uint_di i;
LONG l;
PDIR_INDEX_HDR hSDirIndex;
PDIR_INDEX_ITEM SDirIndex;
PFILE_BOTH_DIR_INFORMATION NTFileInfo = NULL;
AdPrint(("UDFGetFileStreamInformation\n"));
_SEH2_TRY {
UDFAcquireResourceExclusive(&(Fcb->Vcb->FileIdResource), TRUE);
FcbAcquired = TRUE;
FileInfo = Fcb->FileInfo;
if(!FileInfo) {
AdPrint(("!!!!!!!! Bu-u-u-u-u-g !!!!!!!!!!!\n"));
AdPrint(("!!!! UDFGetFileStreamInformation to unopened file !!!!\n"));
try_return(RC = STATUS_INVALID_PARAMETER);
}
Vcb = Fcb->Vcb;
// Zero out the supplied buffer.
RtlZeroMemory(PtrBuffer, *PtrReturnedLength);
if(!(SDirInfo = FileInfo->Dloc->SDirInfo) ||
UDFIsSDirDeleted(SDirInfo) ) {
(*PtrReturnedLength) -= (sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR));
try_return(RC = STATUS_SUCCESS);
}
hSDirIndex = SDirInfo->Dloc->DirIndex;
NTFileInfo = (PFILE_BOTH_DIR_INFORMATION)MyAllocatePool__(NonPagedPool, sizeof(FILE_BOTH_DIR_INFORMATION)+UDF_NAME_LEN*sizeof(WCHAR));
if(!NTFileInfo) try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
for(i=2; (SDirIndex = UDFDirIndex(hSDirIndex,i)); i++) {
if((SDirIndex->FI_Flags & UDF_FI_FLAG_FI_INTERNAL) ||
UDFIsDeleted(SDirIndex) ||
!SDirIndex->FName.Buffer )
continue;
// copy data to buffer
if(*PtrReturnedLength < (l = ((sizeof(FILE_STREAM_INFORMATION) - sizeof(WCHAR)) +
SDirIndex->FName.Length + 3) & (~3)) ) {
try_return(RC = STATUS_BUFFER_OVERFLOW);
}
RC = UDFFileDirInfoToNT(Vcb, SDirIndex, NTFileInfo);
PtrBuffer->NextEntryOffset = l;
PtrBuffer->StreamNameLength = SDirIndex->FName.Length;
PtrBuffer->StreamSize = NTFileInfo->EndOfFile;
PtrBuffer->StreamAllocationSize = NTFileInfo->AllocationSize;
RtlCopyMemory(&(PtrBuffer->StreamName), SDirIndex->FName.Buffer, SDirIndex->FName.Length);
*PtrReturnedLength -= l;
*((PCHAR*)(&PtrBuffer)) += l;
}
try_exit: NOTHING;
} _SEH2_FINALLY {
if(FcbAcquired)
UDFReleaseResource(&(Fcb->Vcb->FileIdResource));
if(NTFileInfo)
MyFreePool__(NTFileInfo);
} _SEH2_END;
return(RC);
} // end UDFGetFileStreamInformation()
//*******************************************************************
#ifndef UDF_READ_ONLY_BUILD
/*
Set some time-stamps and file attributes supplied by the caller.
*/
NTSTATUS
UDFSetBasicInformation(
IN PtrUDFFCB Fcb,
IN PtrUDFCCB Ccb,
IN PFILE_OBJECT FileObject,
IN PFILE_BASIC_INFORMATION PtrBuffer)
{
NTSTATUS RC = STATUS_SUCCESS;
ULONG NotifyFilter = 0;
AdPrint(("UDFSetBasicInformation\n"));
_SEH2_TRY {
// Obtain a pointer to the directory entry associated with
// the FCB being modifed. The directory entry is obviously
// part of the data associated with the parent directory that
// contains this particular file stream.
if(PtrBuffer->FileAttributes) {
UDFUpdateAttrTime(Fcb->Vcb, Fcb->FileInfo);
} else
if( UDFIsADirectory(Fcb->FileInfo) &&
!(Fcb->Vcb->CompatFlags & UDF_VCB_IC_UPDATE_UCHG_DIR_ACCESS_TIME) &&
((Fcb->FileInfo->Dloc->DataLoc.Modified ||
Fcb->FileInfo->Dloc->AllocLoc.Modified ||
(Fcb->FileInfo->Dloc->FE_Flags & UDF_FE_FLAG_FE_MODIFIED) ||
Fcb->FileInfo->Dloc->FELoc.Modified))
) {
// ignore Access Time Modification for unchanged Dir
if(!PtrBuffer->CreationTime.QuadPart &&
PtrBuffer->LastAccessTime.QuadPart &&
!PtrBuffer->ChangeTime.QuadPart &&
!PtrBuffer->LastWriteTime.QuadPart)
try_return(RC);
}
UDFSetFileXTime(Fcb->FileInfo,
&(PtrBuffer->CreationTime.QuadPart),
&(PtrBuffer->LastAccessTime.QuadPart),
&(PtrBuffer->ChangeTime.QuadPart),
&(PtrBuffer->LastWriteTime.QuadPart) );
if(PtrBuffer->CreationTime.QuadPart) {
// The interesting thing here is that the user has set certain time
// fields. However, before doing this, the user may have performed
// I/O which in turn would have caused FSD to mark the fact that
// write/access time should be modifed at cleanup.
// We'll mark the fact that such updates are no longer
// required since the user has explicitly specified the values he
// wishes to see associated with the file stream.
Fcb->NTRequiredFCB->CreationTime = PtrBuffer->CreationTime;
Ccb->CCBFlags |= UDF_CCB_CREATE_TIME_SET;
NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
}
if(PtrBuffer->LastAccessTime.QuadPart) {
Fcb->NTRequiredFCB->LastAccessTime = PtrBuffer->LastAccessTime;
Ccb->CCBFlags |= UDF_CCB_ACCESS_TIME_SET;
NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
}
if(PtrBuffer->ChangeTime.QuadPart) {
Fcb->NTRequiredFCB->ChangeTime = PtrBuffer->ChangeTime;
Ccb->CCBFlags |= UDF_CCB_MODIFY_TIME_SET;
}
if(PtrBuffer->LastWriteTime.QuadPart) {
Fcb->NTRequiredFCB->LastWriteTime = PtrBuffer->LastWriteTime;
Ccb->CCBFlags |= UDF_CCB_WRITE_TIME_SET;
NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
}
// Now come the attributes.
if(PtrBuffer->FileAttributes) {
// We have a non-zero attribute value.
// The presence of a particular attribute indicates that the
// user wishes to set the attribute value. The absence indicates
// the user wishes to clear the particular attribute.
// Our routine ignores unsupported flags
PtrBuffer->FileAttributes &= ~(FILE_ATTRIBUTE_NORMAL);
// Similarly, we should pick out other invalid flag values.
if( (PtrBuffer->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
!(Fcb->FCBFlags & UDF_FCB_DIRECTORY))
try_return(RC = STATUS_INVALID_PARAMETER);
if(PtrBuffer->FileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
if(Fcb->FCBFlags & UDF_FCB_DIRECTORY)
try_return(RC = STATUS_INVALID_PARAMETER);
FileObject->Flags |= FO_TEMPORARY_FILE;
} else {
FileObject->Flags &= ~FO_TEMPORARY_FILE;
}
if(PtrBuffer->FileAttributes & FILE_ATTRIBUTE_READONLY) {
Fcb->FCBFlags |= UDF_FCB_READ_ONLY;
} else {
Fcb->FCBFlags &= ~UDF_FCB_READ_ONLY;
}
UDFAttributesToUDF(UDFDirIndex(UDFGetDirIndexByFileInfo(Fcb->FileInfo), Fcb->FileInfo->Index),
NULL, PtrBuffer->FileAttributes);
(UDFDirIndex(UDFGetDirIndexByFileInfo(Fcb->FileInfo), Fcb->FileInfo->Index))
->FI_Flags |= UDF_FI_FLAG_SYS_ATTR;
// If the FSD supports file compression, we may wish to
// note the user's preferences for compressing/not compressing
// the file at this time.
Ccb->CCBFlags |= UDF_CCB_ATTRIBUTES_SET;
NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
}
if(NotifyFilter) {
UDFNotifyFullReportChange( Fcb->Vcb, Fcb->FileInfo,
NotifyFilter, FILE_ACTION_MODIFIED);
UDFSetFileSizeInDirNdx(Fcb->Vcb, Fcb->FileInfo, NULL);
Fcb->FileInfo->Dloc->FE_Flags |= UDF_FE_FLAG_FE_MODIFIED;
}
try_exit: NOTHING;
} _SEH2_FINALLY {
;
} _SEH2_END;
return(RC);
} // end UDFSetBasicInformation()
NTSTATUS
UDFMarkStreamsForDeletion(
IN PVCB Vcb,
IN PtrUDFFCB Fcb,
IN BOOLEAN ForDel
)
{
NTSTATUS RC = STATUS_SUCCESS;
PUDF_FILE_INFO SDirInfo = NULL;
PUDF_FILE_INFO FileInfo = NULL;
ULONG lc;
BOOLEAN SDirAcq = FALSE;
BOOLEAN StrAcq = FALSE;
uint_di d,i;
_SEH2_TRY {
// In some cases we needn't marking Streams for deleteion
// (Not opened or Don't exist)
if(UDFIsAStream(Fcb->FileInfo) ||
UDFIsAStreamDir(Fcb->FileInfo) ||
!UDFHasAStreamDir(Fcb->FileInfo) ||
!Fcb->FileInfo->Dloc->SDirInfo ||
UDFIsSDirDeleted(Fcb->FileInfo->Dloc->SDirInfo) ||
(UDFGetFileLinkCount(Fcb->FileInfo) > 1) )
try_return (RC /*=STATUS_SUCCESS*/);
// We shall mark Streams for deletion if there is no
// Links to the file. Otherwise we'll delete only the file.
// If we are asked to unmark Streams, we'll precess the whole Tree
RC = UDFOpenStreamDir__(Vcb, Fcb->FileInfo, &SDirInfo);
if(!NT_SUCCESS(RC))
try_return(RC);
if(SDirInfo->Fcb &&
SDirInfo->Fcb->NTRequiredFCB) {
UDF_CHECK_PAGING_IO_RESOURCE(SDirInfo->Fcb->NTRequiredFCB);
UDFAcquireResourceExclusive(&(SDirInfo->Fcb->NTRequiredFCB->MainResource),TRUE);
SDirAcq = TRUE;
}
if(!ForDel || ((lc = UDFGetFileLinkCount(Fcb->FileInfo)) < 2)) {
UDF_DIR_SCAN_CONTEXT ScanContext;
PDIR_INDEX_ITEM DirNdx;
// It is not worth checking whether the Stream can be deleted if
// Undelete requested
if(ForDel &&
// scan DirIndex
UDFDirIndexInitScan(SDirInfo, &ScanContext, 2)) {
// Check if we can delete Streams
while((DirNdx = UDFDirIndexScan(&ScanContext, &FileInfo))) {
if(!FileInfo)
continue;
if(FileInfo->Fcb) {
FileInfo->Fcb->NTRequiredFCB->AcqFlushCount++;
MmPrint((" MmFlushImageSection() for Stream\n"));
if(!MmFlushImageSection(&(FileInfo->Fcb->NTRequiredFCB->SectionObject), MmFlushForDelete)) {
FileInfo->Fcb->NTRequiredFCB->AcqFlushCount--;
try_return(RC = STATUS_CANNOT_DELETE);
}
FileInfo->Fcb->NTRequiredFCB->AcqFlushCount--;
}
}
}
// (Un)Mark Streams for deletion
// Perform sequencial Open for Streams & mark 'em
// for deletion. We should not get FileInfo pointers directly
// from DirNdx[i] to prevent great troubles with linked
// files. We should mark for deletion FI with proper ParentFile
// pointer.
d = UDFDirIndexGetLastIndex(SDirInfo->Dloc->DirIndex);
for(i=2; i<d; i++) {
RC = UDFOpenFile__(Vcb,
FALSE,TRUE,NULL,
SDirInfo,&FileInfo,&i);
ASSERT(NT_SUCCESS(RC) || (RC == STATUS_FILE_DELETED));
if(NT_SUCCESS(RC)) {
if(FileInfo->Fcb) {
if(FileInfo->Fcb->NTRequiredFCB) {
UDF_CHECK_PAGING_IO_RESOURCE(FileInfo->Fcb->NTRequiredFCB);
UDFAcquireResourceExclusive(&(FileInfo->Fcb->NTRequiredFCB->MainResource),TRUE);
StrAcq = TRUE;
}
#ifndef UDF_ALLOW_LINKS_TO_STREAMS
if(UDFGetFileLinkCount(FileInfo) >= 2) {
// Currently, UDF_INFO package doesn't
// support this case, so we'll inform developer
// about this to prevent on-disk space leaks...
BrutePoint();
try_return(RC = STATUS_CANNOT_DELETE);
}
#endif //UDF_ALLOW_LINKS_TO_STREAMS
if(ForDel) {
AdPrint((" SET stream DeleteOnClose\n"));
#ifdef UDF_DBG
ASSERT(!(FileInfo->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
if(FileInfo->ParentFile &&
FileInfo->ParentFile->Fcb) {
ASSERT(!(FileInfo->ParentFile->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
}
#endif // UDF_DBG
FileInfo->Fcb->FCBFlags |= (UDF_FCB_DELETE_ON_CLOSE |
UDF_FCB_DELETE_PARENT);
} else {
AdPrint((" CLEAR stream DeleteOnClose\n"));
FileInfo->Fcb->FCBFlags &= ~(UDF_FCB_DELETE_ON_CLOSE |
UDF_FCB_DELETE_PARENT);
}
}
UDFCloseFile__(Vcb, FileInfo);
} else
if(RC == STATUS_FILE_DELETED) {
// That's OK if STATUS_FILE_DELETED returned...
RC = STATUS_SUCCESS;
}
if(FileInfo) {
if(UDFCleanUpFile__(Vcb, FileInfo)) {
ASSERT(!StrAcq && !(FileInfo->Fcb));
MyFreePool__(FileInfo);
}
if(StrAcq) {
UDF_CHECK_PAGING_IO_RESOURCE(FileInfo->Fcb->NTRequiredFCB);
UDFReleaseResource(&(FileInfo->Fcb->NTRequiredFCB->MainResource));
StrAcq = FALSE;
}
}
FileInfo = NULL;
}
// Mark SDir for deletion
if(SDirInfo->Fcb) {
if(ForDel) {
#ifdef UDF_DBG
ASSERT(!(SDirInfo->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
if(SDirInfo->ParentFile &&
SDirInfo->ParentFile->Fcb) {
ASSERT(!(SDirInfo->ParentFile->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
}
#endif // UDF_DBG
AdPrint((" SET stream dir DeleteOnClose\n"));
SDirInfo->Fcb->FCBFlags |= (UDF_FCB_DELETE_ON_CLOSE |
UDF_FCB_DELETE_PARENT);
} else {
AdPrint((" CLEAR stream dir DeleteOnClose\n"));
SDirInfo->Fcb->FCBFlags &= ~(UDF_FCB_DELETE_ON_CLOSE |
UDF_FCB_DELETE_PARENT);
}
}
} else
if(lc >= 2) {
// if caller wants us to perform DelTree for Streams, but
// someone keeps Stream opened and there is a Link to this
// file, we can't delete it immediately (on Cleanup) & should
// not delete the whole Tree. Instead, we'll set DELETE_PARENT
// flag in SDir to kill this file later, when all the Handles
// to Streams, opened via this file, would be closed
#ifdef UDF_DBG
ASSERT(!(SDirInfo->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
if(SDirInfo->ParentFile &&
SDirInfo->ParentFile->Fcb) {
ASSERT(!(SDirInfo->ParentFile->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY));
}
#endif // UDF_DBG
if(SDirInfo->Fcb)
SDirInfo->Fcb->FCBFlags |= UDF_FCB_DELETE_PARENT;
}
try_exit: NOTHING;
} _SEH2_FINALLY {
if(FileInfo) {
UDFCloseFile__(Vcb, FileInfo);
if(UDFCleanUpFile__(Vcb, FileInfo)) {
ASSERT(!StrAcq && !(FileInfo->Fcb));
MyFreePool__(FileInfo);
}
if(StrAcq) {
UDF_CHECK_PAGING_IO_RESOURCE(FileInfo->Fcb->NTRequiredFCB);
UDFReleaseResource(&(FileInfo->Fcb->NTRequiredFCB->MainResource));
}
SDirInfo = NULL;
}
if(SDirInfo) {
UDFCloseFile__(Vcb, SDirInfo);
if(SDirAcq) {
UDF_CHECK_PAGING_IO_RESOURCE(SDirInfo->Fcb->NTRequiredFCB);
UDFReleaseResource(&(SDirInfo->Fcb->NTRequiredFCB->MainResource));
}
if(UDFCleanUpFile__(Vcb, SDirInfo)) {
MyFreePool__(SDirInfo);
}
SDirInfo = NULL;
}
} _SEH2_END;
return RC;
} // end UDFMarkStreamsForDeletion()
/*
(Un)Mark file for deletion.
*/
NTSTATUS
UDFSetDispositionInformation(
IN PtrUDFFCB Fcb,
IN PtrUDFCCB Ccb,
IN PVCB Vcb,
IN PFILE_OBJECT FileObject,
IN BOOLEAN Delete
)
{
NTSTATUS RC = STATUS_SUCCESS;
// PUDF_FILE_INFO SDirInfo = NULL;
// PUDF_FILE_INFO FileInfo = NULL;
ULONG lc;
AdPrint(("UDFSetDispositionInformation\n"));
_SEH2_TRY {
if(!Delete) {
AdPrint((" CLEAR DeleteOnClose\n"));
// "un-delete" the file.
Fcb->FCBFlags &= ~UDF_FCB_DELETE_ON_CLOSE;
if(FileObject)
FileObject->DeletePending = FALSE;
RC = UDFMarkStreamsForDeletion(Vcb, Fcb, FALSE); // Undelete
try_return(RC);
}
AdPrint((" SET DeleteOnClose\n"));
// The easy part is over. Now, we know that the user wishes to
// delete the corresponding directory entry (of course, if this
// is the only link to the file stream, any on-disk storage space
// associated with the file stream will also be released when the
// (only) link is deleted!)
// Do some checking to see if the file can even be deleted.
if(Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) {
// All done!
try_return(RC);
}
if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) {
try_return(RC = STATUS_CANNOT_DELETE);
}
if(Fcb->FCBFlags & UDF_FCB_READ_ONLY) {
RC = UDFCheckAccessRights(NULL, NULL, Fcb->ParentFcb, NULL, FILE_DELETE_CHILD, 0);
if(!NT_SUCCESS(RC)) {
try_return (RC = STATUS_CANNOT_DELETE);
}
}
// It would not be prudent to allow deletion of either a root
// directory or a directory that is not empty.
if(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)
try_return(RC = STATUS_CANNOT_DELETE);
lc = UDFGetFileLinkCount(Fcb->FileInfo);
if(Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
// Perform check to determine whether the directory
// is empty or not.
if(!UDFIsDirEmpty__(Fcb->FileInfo)) {
try_return(RC = STATUS_DIRECTORY_NOT_EMPTY);
}
} else {
// An important step is to check if the file stream has been
// mapped by any process. The delete cannot be allowed to proceed
// in this case.
MmPrint((" MmFlushImageSection()\n"));
Fcb->NTRequiredFCB->AcqFlushCount++;
if(!MmFlushImageSection(&(Fcb->NTRequiredFCB->SectionObject),
(lc > 1) ? MmFlushForWrite : MmFlushForDelete)) {
Fcb->NTRequiredFCB->AcqFlushCount--;
try_return(RC = STATUS_CANNOT_DELETE);
}
Fcb->NTRequiredFCB->AcqFlushCount--;
}
// We should also mark Streams for deletion if there are no
// Links to the file. Otherwise we'll delete only the file
if(lc > 1) {
RC = STATUS_SUCCESS;
} else {
RC = UDFMarkStreamsForDeletion(Vcb, Fcb, TRUE); // Delete
if(!NT_SUCCESS(RC))
try_return(RC);
}
// Set a flag to indicate that this directory entry will become history
// at cleanup.
Fcb->FCBFlags |= UDF_FCB_DELETE_ON_CLOSE;
if(FileObject)
FileObject->DeletePending = TRUE;
if((Fcb->FCBFlags & UDF_FCB_DIRECTORY) && Ccb) {
FsRtlNotifyFullChangeDirectory( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
(PVOID)Ccb, NULL, FALSE, FALSE,
0, NULL, NULL, NULL );
}
try_exit: NOTHING;
} _SEH2_FINALLY {
;
} _SEH2_END;
return(RC);
} // end UDFSetDispositionInformation()
/*
Change file allocation length.
*/
NTSTATUS
UDFSetAllocationInformation(
IN PtrUDFFCB Fcb,
IN PtrUDFCCB Ccb,
IN PVCB Vcb,
IN PFILE_OBJECT FileObject,
IN PtrUDFIrpContext PtrIrpContext,
IN PIRP Irp,
IN PFILE_ALLOCATION_INFORMATION PtrBuffer
)
{
NTSTATUS RC = STATUS_SUCCESS;
BOOLEAN TruncatedFile = FALSE;
BOOLEAN ModifiedAllocSize = FALSE;
BOOLEAN CacheMapInitialized = FALSE;
BOOLEAN AcquiredPagingIo = FALSE;
AdPrint(("UDFSetAllocationInformation\n"));
_SEH2_TRY {
// Increasing the allocation size associated with a file stream
// is relatively easy. All we have to do is execute some FSD
// specific code to check whether we have enough space available
// (and if the FSD supports user/volume quotas, whether the user
// is not exceeding quota), and then increase the file size in the
// corresponding on-disk and in-memory structures.
// Then, all we should do is inform the Cache Manager about the
// increased allocation size.
// First, do whatever error checking is appropriate here (e.g. whether
// the caller is trying the change size for a directory, etc.).
if(Fcb->FCBFlags & UDF_FCB_DIRECTORY)
try_return(RC = STATUS_INVALID_PARAMETER);
Fcb->NTRequiredFCB->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb);
if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
(FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
!FlagOn(Irp->Flags, IRP_PAGING_IO)) {
ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) );
// Now initialize the cache map.
MmPrint((" CcInitializeCacheMap()\n"));
CcInitializeCacheMap( FileObject,
(PCC_FILE_SIZES)&Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize,
FALSE,
&(UDFGlobalData.CacheMgrCallBacks),
Fcb->NTRequiredFCB );
CacheMapInitialized = TRUE;
}
// Are we increasing the allocation size?
if(Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize.QuadPart <
PtrBuffer->AllocationSize.QuadPart) {
// Yes. Do the FSD specific stuff i.e. increase reserved
// space on disk.
if(((LONGLONG)UDFGetFreeSpace(Vcb) << Vcb->LBlockSizeBits) < PtrBuffer->AllocationSize.QuadPart) {
try_return(RC = STATUS_DISK_FULL);
}
// RC = STATUS_SUCCESS;
ModifiedAllocSize = TRUE;
} else if(Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize.QuadPart >
PtrBuffer->AllocationSize.QuadPart) {
// This is the painful part. See if the VMM will allow us to proceed.
// The VMM will deny the request if:
// (a) any image section exists OR
// (b) a data section exists and the size of the user mapped view
// is greater than the new size
// Otherwise, the VMM should allow the request to proceed.
MmPrint((" MmCanFileBeTruncated()\n"));
if(!MmCanFileBeTruncated(&(Fcb->NTRequiredFCB->SectionObject), &(PtrBuffer->AllocationSize))) {
// VMM said no way!
try_return(RC = STATUS_USER_MAPPED_FILE);
}
// Perform our directory entry modifications. Release any on-disk
// space we may need to in the process.
ModifiedAllocSize = TRUE;
TruncatedFile = TRUE;
}
ASSERT(NT_SUCCESS(RC));
// This is a good place to check if we have performed a truncate
// operation. If we have perform a truncate (whether we extended
// or reduced file size or even leave it intact), we should update
// file time stamps.
FileObject->Flags |= FO_FILE_MODIFIED;
// Last, but not the lease, we must inform the Cache Manager of file size changes.
if(ModifiedAllocSize) {
// If we decreased the allocation size to less than the
// current file size, modify the file size value.
// Similarly, if we decreased the value to less than the
// current valid data length, modify that value as well.
AcquiredPagingIo = UDFAcquireResourceExclusiveWithCheck(&(Fcb->NTRequiredFCB->PagingIoResource));
// Update the FCB Header with the new allocation size.
if(TruncatedFile) {
if(Fcb->NTRequiredFCB->CommonFCBHeader.ValidDataLength.QuadPart >
PtrBuffer->AllocationSize.QuadPart) {
// Decrease the valid data length value.
Fcb->NTRequiredFCB->CommonFCBHeader.ValidDataLength =
PtrBuffer->AllocationSize;
}
if(Fcb->NTRequiredFCB->CommonFCBHeader.FileSize.QuadPart >
PtrBuffer->AllocationSize.QuadPart) {
// Decrease the file size value.
Fcb->NTRequiredFCB->CommonFCBHeader.FileSize =
PtrBuffer->AllocationSize;
RC = UDFResizeFile__(Vcb, Fcb->FileInfo, PtrBuffer->AllocationSize.QuadPart);
// UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, NULL);
}
} else {
Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize = PtrBuffer->AllocationSize;
// UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo,
// &(PtrBuffer->AllocationSize.QuadPart));
}
if(AcquiredPagingIo) {
UDFReleaseResource(&(Fcb->NTRequiredFCB->PagingIoResource));
AcquiredPagingIo = FALSE;
}
// If the FCB has not had caching initiated, it is still valid
// for us to invoke the NT Cache Manager. It is possible in such
// situations for the call to be no'oped (unless some user has
// mapped in the file)
// NOTE: The invocation to CcSetFileSizes() will quite possibly
// result in a recursive call back into the file system.
// This is because the NT Cache Manager will typically
// perform a flush before telling the VMM to purge pages
// especially when caching has not been initiated on the
// file stream, but the user has mapped the file into
// the process' virtual address space.
MmPrint((" CcSetFileSizes()\n"));
Fcb->NTRequiredFCB->AcqFlushCount++;
CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&(Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize));
Fcb->NTRequiredFCB->AcqFlushCount--;
Fcb->NTRequiredFCB->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
// Inform any pending IRPs (notify change directory).
if(UDFIsAStream(Fcb->FileInfo)) {
UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
FILE_NOTIFY_CHANGE_STREAM_SIZE,
FILE_ACTION_MODIFIED_STREAM);
} else {
UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
FILE_NOTIFY_CHANGE_SIZE,
FILE_ACTION_MODIFIED);
}
}
try_exit: NOTHING;
} _SEH2_FINALLY {
if(AcquiredPagingIo) {
UDFReleaseResource(&(Fcb->NTRequiredFCB->PagingIoResource));
AcquiredPagingIo = FALSE;
}
if (CacheMapInitialized) {
MmPrint((" CcUninitializeCacheMap()\n"));
CcUninitializeCacheMap( FileObject, NULL, NULL );
}
} _SEH2_END;
return(RC);
} // end UDFSetAllocationInformation()
/*
Set end of file (resize).
*/
NTSTATUS
UDFSetEOF(
IN PIO_STACK_LOCATION PtrSp,
IN PtrUDFFCB Fcb,
IN PtrUDFCCB Ccb,
IN PVCB Vcb,
IN PFILE_OBJECT FileObject,
IN PIRP Irp,
IN PFILE_END_OF_FILE_INFORMATION PtrBuffer
)
{
NTSTATUS RC = STATUS_SUCCESS;
BOOLEAN TruncatedFile = FALSE;
BOOLEAN ModifiedAllocSize = FALSE;
ULONG Attr;
PDIR_INDEX_ITEM DirNdx;
PtrUDFNTRequiredFCB NtReqFcb = NULL;
LONGLONG OldFileSize;
// BOOLEAN ZeroBlock;
BOOLEAN CacheMapInitialized = FALSE;
BOOLEAN AcquiredPagingIo = FALSE;
AdPrint(("UDFSetEOF\n"));
_SEH2_TRY {
// Increasing the allocation size associated with a file stream
// is relatively easy. All we have to do is execute some FSD
// specific code to check whether we have enough space available
// (and if the FSD supports user/volume quotas, whether the user
// is not exceeding quota), and then increase the file size in the
// corresponding on-disk and in-memory structures.
// Then, all we should do is inform the Cache Manager about the
// increased allocation size.
// First, do whatever error checking is appropriate here (e.g. whether
// the caller is trying the change size for a directory, etc.).
if(Fcb->FCBFlags & UDF_FCB_DIRECTORY)
try_return(RC = STATUS_INVALID_PARAMETER);
NtReqFcb = Fcb->NTRequiredFCB;
if((Fcb->FCBFlags & UDF_FCB_DELETED) ||
(NtReqFcb->NtReqFCBFlags & UDF_NTREQ_FCB_DELETED)) {
#ifdef UDF_DBG
if(UDFGetFileLinkCount(Fcb->FileInfo) < 1) {
BrutePoint();
try_return(RC = STATUS_SUCCESS);
} else
#endif // UDF_DBG
try_return(RC = STATUS_SUCCESS);
}
NtReqFcb->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb);
if ((FileObject->SectionObjectPointer->DataSectionObject != NULL) &&
(FileObject->SectionObjectPointer->SharedCacheMap == NULL) &&
!(Irp->Flags & IRP_PAGING_IO)) {
ASSERT( !FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE ) );
// Now initialize the cache map.
MmPrint((" CcInitializeCacheMap()\n"));
CcInitializeCacheMap( FileObject,
(PCC_FILE_SIZES)&Fcb->NTRequiredFCB->CommonFCBHeader.AllocationSize,
FALSE,
&(UDFGlobalData.CacheMgrCallBacks),
Fcb->NTRequiredFCB );
CacheMapInitialized = TRUE;
}
AcquiredPagingIo = UDFAcquireResourceExclusiveWithCheck(&(Fcb->NTRequiredFCB->PagingIoResource));
// Do a special case here for the lazy write of file sizes.
if(PtrSp->Parameters.SetFile.AdvanceOnly) {
// Never have the dirent filesize larger than the fcb filesize
PtrBuffer->EndOfFile.QuadPart =
min(PtrBuffer->EndOfFile.QuadPart,
NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
// Only advance the file size, never reduce it with this call
RC = STATUS_SUCCESS;
if(UDFGetFileSizeFromDirNdx(Vcb, Fcb->FileInfo) >=
PtrBuffer->EndOfFile.QuadPart)
try_return(RC);
UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, &(PtrBuffer->EndOfFile.QuadPart));
goto notify_size_changes;
}
// !!! IMPORTANT !!!
// We can get here after all Handles to the file are closed
// To prevent allocation size incoherency we should
// reference FileInfo _before_ call to UDFResizeFile__()
// and use UDFCloseFile__() _after_ that
// Are we increasing the allocation size?
OldFileSize = NtReqFcb->CommonFCBHeader.FileSize.QuadPart;
if(OldFileSize < PtrBuffer->EndOfFile.QuadPart) {
// Yes. Do the FSD specific stuff i.e. increase reserved
// space on disk.
/*
if (FileObject->PrivateCacheMap)
ZeroBlock = TRUE;
*/
// reference file to pretend that it is opened
UDFReferenceFile__(Fcb->FileInfo);
UDFInterlockedIncrement((PLONG)&(Fcb->ReferenceCount));
UDFInterlockedIncrement((PLONG)&(NtReqFcb->CommonRefCount));
// perform resize operation
RC = UDFResizeFile__(Vcb, Fcb->FileInfo, PtrBuffer->EndOfFile.QuadPart);
// dereference file
UDFCloseFile__(Vcb, Fcb->FileInfo);
UDFInterlockedDecrement((PLONG)&(Fcb->ReferenceCount));
UDFInterlockedDecrement((PLONG)&(NtReqFcb->CommonRefCount));
// update values in NtReqFcb
NtReqFcb->CommonFCBHeader.FileSize.QuadPart =
// NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart =
PtrBuffer->EndOfFile.QuadPart;
ModifiedAllocSize = TRUE;
} else if(NtReqFcb->CommonFCBHeader.FileSize.QuadPart >
PtrBuffer->EndOfFile.QuadPart) {
// This is the painful part. See if the VMM will allow us to proceed.
// The VMM will deny the request if:
// (a) any image section exists OR
// (b) a data section exists and the size of the user mapped view
// is greater than the new size
// Otherwise, the VMM should allow the request to proceed.
MmPrint((" MmCanFileBeTruncated()\n"));
if(!MmCanFileBeTruncated(&(NtReqFcb->SectionObject), &(PtrBuffer->EndOfFile))) {
// VMM said no way!
try_return(RC = STATUS_USER_MAPPED_FILE);
}
// Perform directory entry modifications. Release any on-disk
// space we may need to in the process.
UDFReferenceFile__(Fcb->FileInfo);
UDFInterlockedIncrement((PLONG)&(Fcb->ReferenceCount));
UDFInterlockedIncrement((PLONG)&(NtReqFcb->CommonRefCount));
// perform resize operation
RC = UDFResizeFile__(Vcb, Fcb->FileInfo, PtrBuffer->EndOfFile.QuadPart);
// dereference file
UDFCloseFile__(Vcb, Fcb->FileInfo);
UDFInterlockedDecrement((PLONG)&(Fcb->ReferenceCount));
UDFInterlockedDecrement((PLONG)&(NtReqFcb->CommonRefCount));
ModifiedAllocSize = TRUE;
TruncatedFile = TRUE;
}
// This is a good place to check if we have performed a truncate
// operation. If we have perform a truncate (whether we extended
// or reduced file size), we should update file time stamps.
// Last, but not the least, we must inform the Cache Manager of file size changes.
if(ModifiedAllocSize && NT_SUCCESS(RC)) {
// If we decreased the allocation size to less than the
// current file size, modify the file size value.
// Similarly, if we decreased the value to less than the
// current valid data length, modify that value as well.
if(TruncatedFile) {
if(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart >
PtrBuffer->EndOfFile.QuadPart) {
// Decrease the valid data length value.
NtReqFcb->CommonFCBHeader.ValidDataLength =
PtrBuffer->EndOfFile;
}
if(NtReqFcb->CommonFCBHeader.FileSize.QuadPart >
PtrBuffer->EndOfFile.QuadPart) {
// Decrease the file size value.
NtReqFcb->CommonFCBHeader.FileSize =
PtrBuffer->EndOfFile;
}
UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, NULL);
} else {
// Update the FCB Header with the new allocation size.
// NT expects AllocationSize to be decreased on Close only
NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart =
PtrBuffer->EndOfFile.QuadPart;
// UDFSysGetAllocSize(Vcb, UDFGetFileSize(Fcb->FileInfo));
UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, &(PtrBuffer->EndOfFile.QuadPart));
}
FileObject->Flags |= FO_FILE_MODIFIED;
// UDFGetFileAllocationSize(Vcb, Fcb->FileInfo);
// If the FCB has not had caching initiated, it is still valid
// for us to invoke the NT Cache Manager. It is possible in such
// situations for the call to be no'oped (unless some user has
// mapped in the file)
// Archive bit
if(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ARCH_BIT) {
DirNdx = UDFDirIndex(UDFGetDirIndexByFileInfo(Fcb->FileInfo), Fcb->FileInfo->Index);
Ccb->CCBFlags &= ~UDF_CCB_ATTRIBUTES_SET;
Attr = UDFAttributesToNT(DirNdx, Fcb->FileInfo->Dloc->FileEntry);
if(!(Attr & FILE_ATTRIBUTE_ARCHIVE))
UDFAttributesToUDF(DirNdx, Fcb->FileInfo->Dloc->FileEntry, Attr | FILE_ATTRIBUTE_ARCHIVE);
}
// NOTE: The invocation to CcSetFileSizes() will quite possibly
// result in a recursive call back into the file system.
// This is because the NT Cache Manager will typically
// perform a flush before telling the VMM to purge pages
// especially when caching has not been initiated on the
// file stream, but the user has mapped the file into
// the process' virtual address space.
MmPrint((" CcSetFileSizes(), thrd:%8.8x\n",PsGetCurrentThread()));
Fcb->NTRequiredFCB->AcqFlushCount++;
CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&(NtReqFcb->CommonFCBHeader.AllocationSize));
Fcb->NTRequiredFCB->AcqFlushCount--;
/* if(ZeroBlock) {
UDFZeroDataEx(NtReqFcb,
OldFileSize,
PtrBuffer->EndOfFile.QuadPart - OldFileSize,
TRUE // CanWait, Vcb, FileObject);
}*/
Fcb->NTRequiredFCB->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
notify_size_changes:
if(AcquiredPagingIo) {
UDFReleaseResource(&(Fcb->NTRequiredFCB->PagingIoResource));
AcquiredPagingIo = FALSE;
}
// Inform any pending IRPs (notify change directory).
if(UDFIsAStream(Fcb->FileInfo)) {
UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
FILE_NOTIFY_CHANGE_STREAM_SIZE,
FILE_ACTION_MODIFIED_STREAM);
} else {
UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
FILE_NOTIFY_CHANGE_SIZE,
FILE_ACTION_MODIFIED);
}
}
try_exit: NOTHING;
} _SEH2_FINALLY {
if(AcquiredPagingIo) {
UDFReleaseResource(&(Fcb->NTRequiredFCB->PagingIoResource));
AcquiredPagingIo = FALSE;
}
if (CacheMapInitialized) {
MmPrint((" CcUninitializeCacheMap()\n"));
CcUninitializeCacheMap( FileObject, NULL, NULL );
}
} _SEH2_END;
return(RC);
} // end UDFSetEOF()
NTSTATUS
UDFPrepareForRenameMoveLink(
PVCB Vcb,
PBOOLEAN AcquiredVcb,
PBOOLEAN AcquiredVcbEx,
PBOOLEAN SingleDir,
PBOOLEAN AcquiredDir1,
PBOOLEAN AcquiredFcb1,
IN PtrUDFCCB Ccb1,
PUDF_FILE_INFO File1,
PUDF_FILE_INFO Dir1,
PUDF_FILE_INFO Dir2,
BOOLEAN HardLink
)
{
// convert acquisition to Exclusive
// this will prevent us from the following situation:
// There is a pair of objects among input dirs &
// one of them is a parent of another. Sequential resource
// acquisition may lead to deadlock due to concurrent
// CleanUpFcbChain() or UDFCloseFileInfoChain()
UDFInterlockedIncrement((PLONG)&(Vcb->VCBOpenCount));
UDFReleaseResource(&(Vcb->VCBResource));
(*AcquiredVcb) = FALSE;
// At first, make system to issue last Close request
// for our Source & Target ...
// we needn't flush/purge for Source on HLink
UDFRemoveFromSystemDelayedQueue(Dir2->Fcb);
if(!HardLink && (Dir2 != Dir1))
UDFRemoveFromSystemDelayedQueue(File1->Fcb);
#ifdef UDF_DELAYED_CLOSE
_SEH2_TRY {
// Do actual close for all "delayed close" calls
// ... and now remove the rest from our queue
if(!HardLink) {
UDFCloseAllDelayedInDir(Vcb, Dir1);
if(Dir2 != Dir1)
UDFCloseAllDelayedInDir(Vcb, Dir2);
} else {
UDFCloseAllDelayedInDir(Vcb, Dir2);
}
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
BrutePoint();
UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount));
return (STATUS_DRIVER_INTERNAL_ERROR);
} _SEH2_END;
#endif //UDF_DELAYED_CLOSE
(*SingleDir) = ((Dir1 == Dir2) && (Dir1->Fcb));
if(!(*SingleDir) ||
(UDFGetFileLinkCount(File1) != 1)) {
UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
(*AcquiredVcb) = TRUE;
(*AcquiredVcbEx) = TRUE;
UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount));
} else {
UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
(*AcquiredVcb) = TRUE;
UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount));
UDF_CHECK_PAGING_IO_RESOURCE(Dir1->Fcb->NTRequiredFCB);
UDFAcquireResourceExclusive(&(Dir1->Fcb->NTRequiredFCB->MainResource),TRUE);
(*AcquiredDir1) = TRUE;
UDF_CHECK_PAGING_IO_RESOURCE(File1->Fcb->NTRequiredFCB);
UDFAcquireResourceExclusive(&(File1->Fcb->NTRequiredFCB->MainResource),TRUE);
(*AcquiredFcb1) = TRUE;
}
return STATUS_SUCCESS;
} // end UDFPrepareForRenameMoveLink()
/*
Rename or move file
*/
NTSTATUS
UDFRename(
IN PIO_STACK_LOCATION PtrSp,
IN PtrUDFFCB Fcb1,
IN PtrUDFCCB Ccb1,
IN PFILE_OBJECT FileObject1, // Source File
IN PFILE_RENAME_INFORMATION PtrBuffer
)
{
// Source Directory
PFILE_OBJECT DirObject1 = FileObject1->RelatedFileObject;
// Target Directory
PFILE_OBJECT DirObject2 = PtrSp->Parameters.SetFile.FileObject;
// Overwite Flag
BOOLEAN Replace = PtrSp->Parameters.SetFile.ReplaceIfExists &&
PtrBuffer->ReplaceIfExists;
NTSTATUS RC;
PVCB Vcb = Fcb1->Vcb;
PtrUDFFCB Fcb2;
BOOLEAN ic;
BOOLEAN AcquiredVcb = TRUE;
BOOLEAN AcquiredVcbEx = FALSE;
BOOLEAN AcquiredDir1 = FALSE;
BOOLEAN AcquiredFcb1 = FALSE;
BOOLEAN SingleDir = TRUE;
BOOLEAN UseClose;
PUDF_FILE_INFO File1;
PUDF_FILE_INFO Dir1;
PUDF_FILE_INFO Dir2;
PUDF_FILE_INFO NextFileInfo, fi;
UNICODE_STRING NewName;
UNICODE_STRING LocalPath;
PtrUDFCCB CurCcb = NULL;
PLIST_ENTRY Link;
ULONG i;
ULONG DirRefCount;
ULONG FileInfoRefCount;
ULONG Attr;
PDIR_INDEX_ITEM DirNdx;
AdPrint(("UDFRename %8.8x\n", DirObject2));
LocalPath.Buffer = NULL;
_SEH2_TRY {
// do we try to rename Volume ?
#ifdef UDF_ALLOW_RENAME_MOVE
if(!(File1 = Fcb1->FileInfo))
#endif //UDF_ALLOW_RENAME_MOVE
try_return (RC = STATUS_ACCESS_DENIED);
// do we try to rename RootDir ?
if(!(Dir1 = File1->ParentFile))
try_return (RC = STATUS_ACCESS_DENIED);
// do we try to rename to RootDir or Volume ?
if(!DirObject2) {
Dir2 = File1->ParentFile;
DirObject2 = DirObject1;
} else
if(DirObject2->FsContext2 &&
(Fcb2 = ((PtrUDFCCB)(DirObject2->FsContext2))->Fcb)) {
Dir2 = ((PtrUDFCCB)(DirObject2->FsContext2))->Fcb->FileInfo;
} else {
try_return (RC = STATUS_INVALID_PARAMETER);
}
// invalid destination ?
if(!Dir2) try_return (RC = STATUS_ACCESS_DENIED);
// Stream can't be a Dir or have StreamDir
if(UDFIsAStreamDir(Dir2)) {
#ifdef UDF_ENABLE_SECURITY
if(UDFIsADirectory(File1)) {
try_return (RC = STATUS_ACCESS_DENIED);
}
// We should check whether File1 has only Internal
// (or Deleted) streams. In this case SDir should be
// removed (in UDFRenameMoveFile__()). Otherwise
// return STATUS_ACCESS_DENIED
if(UDFHasAStreamDir(File1)) {
UDFPrint(("TODO: We should remove Streams from source file\n"));
try_return (RC = STATUS_ACCESS_DENIED);
}
#else //UDF_ENABLE_SECURITY
if(UDFIsADirectory(File1) ||
UDFHasAStreamDir(File1)) {
try_return (RC = STATUS_ACCESS_DENIED);
}
#endif //UDF_ENABLE_SECURITY
}
RC = UDFPrepareForRenameMoveLink(Vcb, &AcquiredVcb, &AcquiredVcbEx,
&SingleDir,
&AcquiredDir1, &AcquiredFcb1,
Ccb1, File1,
Dir1, Dir2,
FALSE); // it is Rename operation
if(!NT_SUCCESS(RC))
try_return(RC);
// check if the source file is in use
if(Fcb1->OpenHandleCount > 1)
try_return (RC = STATUS_ACCESS_DENIED);
ASSERT(Fcb1->OpenHandleCount);
ASSERT(!Fcb1->IrpContextLite);
if(Fcb1->IrpContextLite) {
try_return (RC = STATUS_ACCESS_DENIED);
}
// Check if we have parallel/pending Close threads
if(Fcb1->CcbCount && !SingleDir) {
// if this is the 1st attempt, we'll try to
// synchronize with Close requests
// otherwise fail request
RC = STATUS_ACCESS_DENIED;
post_rename:
if(Fcb1->FCBFlags & UDF_FCB_POSTED_RENAME) {
Fcb1->FCBFlags &= ~UDF_FCB_POSTED_RENAME;
try_return (RC);
}
Fcb1->FCBFlags |= UDF_FCB_POSTED_RENAME;
try_return (RC = STATUS_PENDING);
}
if(!DirObject2) {
// Make sure the name is of legal length.
if(PtrBuffer->FileNameLength > UDF_NAME_LEN*sizeof(WCHAR)) {
try_return(RC = STATUS_OBJECT_NAME_INVALID);
}
NewName.Length = NewName.MaximumLength = (USHORT)(PtrBuffer->FileNameLength);
NewName.Buffer = (PWCHAR)&(PtrBuffer->FileName);
} else {
// This name is by definition legal.
NewName = *((PUNICODE_STRING)&DirObject2->FileName);
}
ic = (Ccb1->CCBFlags & UDF_CCB_CASE_SENSETIVE) ? FALSE : TRUE;
AdPrint((" %ws ->\n %ws\n",
Fcb1->FCBName->ObjectName.Buffer,
NewName.Buffer));
if(UDFIsDirOpened__(File1)) {
// We can't rename file because of unclean references.
// UDF_INFO package can safely do it, but NT side cannot.
// In this case NT requires STATUS_OBJECT_NAME_COLLISION
// rather than STATUS_ACCESS_DENIED
if(NT_SUCCESS(UDFFindFile__(Vcb, ic, &NewName, Dir2)))
try_return(RC = STATUS_OBJECT_NAME_COLLISION);
try_return (RC = STATUS_ACCESS_DENIED);
} else {
// Last check before Moving.
// We can't move across Dir referenced (even internally) file
if(!SingleDir) {
RC = UDFDoesOSAllowFileToBeMoved__(File1);
if(!NT_SUCCESS(RC)) {
// try_return(RC);
goto post_rename;
}
}
ASSERT_REF(Fcb1->ReferenceCount >= File1->RefCount);
ASSERT_REF(Dir1->Fcb->ReferenceCount >= Dir1->RefCount);
ASSERT_REF(Dir2->Fcb->ReferenceCount >= Dir2->RefCount);
RC = UDFRenameMoveFile__(Vcb, ic, &Replace, &NewName, Dir1, Dir2, File1);
}
if(!NT_SUCCESS(RC))
try_return (RC);
ASSERT(UDFDirIndex(File1->ParentFile->Dloc->DirIndex, File1->Index)->FileInfo == File1);
RC = MyCloneUnicodeString(&LocalPath, (Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ?
&UDFGlobalData.UnicodeStrRoot :
&(Dir2->Fcb->FCBName->ObjectName) );
if(!NT_SUCCESS(RC)) try_return (RC);
// RC = MyAppendUnicodeStringToString(&LocalPath, (Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ? &(UDFGlobalData.UnicodeStrRoot) : &(Dir2->Fcb->FCBName->ObjectName));
// if(!NT_SUCCESS(RC)) try_return (RC);
if(Dir2->ParentFile) {
RC = MyAppendUnicodeToString(&LocalPath, L"\\");
if(!NT_SUCCESS(RC)) try_return (RC);
}
RC = MyAppendUnicodeStringToStringTag(&LocalPath, &NewName, MEM_USREN_TAG);
if(!NT_SUCCESS(RC)) try_return (RC);
// Set Archive bit
DirNdx = UDFDirIndex(File1->ParentFile->Dloc->DirIndex, File1->Index);
if(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_ARCH_BIT) {
Attr = UDFAttributesToNT(DirNdx, File1->Dloc->FileEntry);
if(!(Attr & FILE_ATTRIBUTE_ARCHIVE))
UDFAttributesToUDF(DirNdx, File1->Dloc->FileEntry, Attr | FILE_ATTRIBUTE_ARCHIVE);
}
// Update Parent Objects (mark 'em as modified)
if(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_DIR_WRITE) {
if(DirObject1)
DirObject1->Flags |= FO_FILE_MODIFIED;
if(DirObject2) {
DirObject2->Flags |= FO_FILE_MODIFIED;
if(!Replace)
DirObject2->Flags |= FO_FILE_SIZE_CHANGED;
}
}
// report changes
if(SingleDir && !Replace) {
UDFNotifyFullReportChange( Vcb, File1,
UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_RENAMED_OLD_NAME);
/* UDFNotifyFullReportChange( Vcb, File2,
UDFIsADirectory(File2) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_RENAMED_NEW_NAME );*/
FsRtlNotifyFullReportChange( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
(PSTRING)&LocalPath,
((Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ? 0 : Dir2->Fcb->FCBName->ObjectName.Length) + sizeof(WCHAR),
NULL,NULL,
UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_RENAMED_NEW_NAME,
NULL);
} else {
UDFNotifyFullReportChange( Vcb, File1,
UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_REMOVED);
if(Replace) {
/* UDFNotifyFullReportChange( Vcb, File2,
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_EA,
FILE_ACTION_MODIFIED );*/
FsRtlNotifyFullReportChange( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
(PSTRING)&LocalPath,
((Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ?
0 : Dir2->Fcb->FCBName->ObjectName.Length) + sizeof(WCHAR),
NULL,NULL,
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_EA,
FILE_ACTION_MODIFIED,
NULL);
} else {
/* UDFNotifyFullReportChange( Vcb, File2,
UDFIsADirectory(File2) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_ADDED );*/
FsRtlNotifyFullReportChange( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
(PSTRING)&LocalPath,
((Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ?
0 : Dir2->Fcb->FCBName->ObjectName.Length) + sizeof(WCHAR),
NULL,NULL,
UDFIsADirectory(File1) ?
FILE_NOTIFY_CHANGE_DIR_NAME :
FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_ADDED,
NULL);
}
}
// this will prevent structutre release before call to
// UDFCleanUpFcbChain()
UDFInterlockedIncrement((PLONG)&(Dir1->Fcb->ReferenceCount));
UDFInterlockedIncrement((PLONG)&(Dir1->Fcb->NTRequiredFCB->CommonRefCount));
ASSERT_REF(Dir1->Fcb->ReferenceCount >= Dir1->RefCount);
// Look through Ccb list & decrement OpenHandleCounter(s)
// acquire CcbList
if(!SingleDir) {
UDFAcquireResourceExclusive(&(Fcb1->CcbListResource),TRUE);
Link = Fcb1->NextCCB.Flink;
DirRefCount = 0;
FileInfoRefCount = 0;
ASSERT(Link != &(Fcb1->NextCCB));
while (Link != &(Fcb1->NextCCB)) {
NextFileInfo = Dir1;
CurCcb = CONTAINING_RECORD(Link, UDFCCB, NextCCB);
ASSERT(CurCcb->TreeLength);
i = (CurCcb->TreeLength) ? (CurCcb->TreeLength - 1) : 0;
Link = Link->Flink;
UseClose = (CurCcb->CCBFlags & UDF_CCB_CLEANED) ? FALSE : TRUE;
AdPrint((" Ccb:%x:%s:i:%x\n", CurCcb, UseClose ? "Close" : "",i));
// cleanup old parent chain
for(; i && NextFileInfo; i--) {
// remember parent file now
// it will prevent us from data losses
// due to eventual structure release
fi = NextFileInfo->ParentFile;
if(UseClose) {
ASSERT_REF(NextFileInfo->Fcb->ReferenceCount >= NextFileInfo->RefCount);
UDFCloseFile__(Vcb, NextFileInfo);
}
ASSERT_REF(NextFileInfo->Fcb->ReferenceCount > NextFileInfo->RefCount);
ASSERT_REF(NextFileInfo->Fcb->ReferenceCount);
ASSERT_REF(NextFileInfo->Fcb->NTRequiredFCB->CommonRefCount);
UDFInterlockedDecrement((PLONG)&(NextFileInfo->Fcb->ReferenceCount));
UDFInterlockedDecrement((PLONG)&(NextFileInfo->Fcb->NTRequiredFCB->CommonRefCount));
ASSERT_REF(NextFileInfo->Fcb->ReferenceCount >= NextFileInfo->RefCount);
NextFileInfo = fi;
}
if(CurCcb->TreeLength > 1) {
DirRefCount++;
if(UseClose)
FileInfoRefCount++;
CurCcb->TreeLength = 2;
#ifdef UDF_DBG
} else {
BrutePoint();
#endif // UDF_DBG
}
}
UDFReleaseResource(&(Fcb1->CcbListResource));
ASSERT_REF(DirRefCount >= FileInfoRefCount);
// update counters & pointers
Fcb1->ParentFcb = Dir2->Fcb;
// move references to Dir2
UDFInterlockedExchangeAdd((PLONG)&(Dir2->Fcb->ReferenceCount), DirRefCount);
UDFInterlockedExchangeAdd((PLONG)&(Dir2->Fcb->NTRequiredFCB->CommonRefCount), DirRefCount);
ASSERT_REF(Dir2->Fcb->ReferenceCount > Dir2->RefCount);
UDFReferenceFileEx__(Dir2,FileInfoRefCount);
ASSERT_REF(Dir2->Fcb->ReferenceCount >= Dir2->RefCount);
}
ASSERT_REF(Dir2->Fcb->ReferenceCount >= Dir2->RefCount);
ASSERT_REF(Dir2->RefCount);
ASSERT_REF(Dir1->Fcb->ReferenceCount >= Dir1->RefCount);
// Modify name in Fcb1
if(Fcb1->FCBName) {
if(Fcb1->FCBName->ObjectName.Buffer) {
MyFreePool__(Fcb1->FCBName->ObjectName.Buffer);
}
UDFReleaseObjectName(Fcb1->FCBName);
}
Fcb1->FCBName = UDFAllocateObjectName();
if(!(Fcb1->FCBName)) {
insuf_res:
BrutePoint();
// UDFCleanUpFcbChain()...
if(AcquiredFcb1) {
UDF_CHECK_PAGING_IO_RESOURCE(Fcb1->NTRequiredFCB);
UDFReleaseResource(&(Fcb1->NTRequiredFCB->MainResource));
AcquiredDir1 = FALSE;
}
if(AcquiredDir1) {
UDF_CHECK_PAGING_IO_RESOURCE(Dir1->Fcb->NTRequiredFCB);
UDFReleaseResource(&(Dir1->Fcb->NTRequiredFCB->MainResource));
AcquiredDir1 = FALSE;
}
UDFCleanUpFcbChain(Vcb, Dir1, 1, TRUE);
try_return(RC = STATUS_INSUFFICIENT_RESOURCES);
}
RC = MyCloneUnicodeString(&(Fcb1->FCBName->ObjectName), &(Fcb2->FCBName->ObjectName));
if(!NT_SUCCESS(RC))
goto insuf_res;
/* RC = MyAppendUnicodeStringToString(&(Fcb1->FCBName->ObjectName), &(Fcb2->FCBName->ObjectName));
if(!NT_SUCCESS(RC))
goto insuf_res;*/
// if Dir2 is a RootDir, we shoud not append '\\' because
// uit will be the 2nd '\\' character (RootDir's name is also '\\')
if(Dir2->ParentFile) {
RC = MyAppendUnicodeToString(&(Fcb1->FCBName->ObjectName), L"\\");
if(!NT_SUCCESS(RC))
goto insuf_res;
}
RC = MyAppendUnicodeStringToStringTag(&(Fcb1->FCBName->ObjectName), &NewName, MEM_USREN2_TAG);
if(!NT_SUCCESS(RC))
goto insuf_res;
ASSERT_REF(Fcb1->ReferenceCount >= File1->RefCount);
ASSERT_REF(Dir1->Fcb->ReferenceCount >= Dir1->RefCount);
ASSERT_REF(Dir2->Fcb->ReferenceCount >= Dir2->RefCount);
RC = STATUS_SUCCESS;
try_exit: NOTHING;
} _SEH2_FINALLY {
if(AcquiredFcb1) {
UDF_CHECK_PAGING_IO_RESOURCE(Fcb1->NTRequiredFCB);
UDFReleaseResource(&(Fcb1->NTRequiredFCB->MainResource));
}
if(AcquiredDir1) {
UDF_CHECK_PAGING_IO_RESOURCE(Dir1->Fcb->NTRequiredFCB);
UDFReleaseResource(&(Dir1->Fcb->NTRequiredFCB->MainResource));
}
// perform protected structure release
if(NT_SUCCESS(RC) &&
(RC != STATUS_PENDING)) {
ASSERT(AcquiredVcb);
UDFCleanUpFcbChain(Vcb, Dir1, 1, TRUE);
ASSERT_REF(Fcb1->ReferenceCount >= File1->RefCount);
ASSERT_REF(Dir2->Fcb->ReferenceCount >= Dir2->RefCount);
}
if(AcquiredVcb) {
if(AcquiredVcbEx)
UDFConvertExclusiveToSharedLite(&(Vcb->VCBResource));
} else {
// caller assumes Vcb to be acquired shared
BrutePoint();
UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
}
if(LocalPath.Buffer) {
MyFreePool__(LocalPath.Buffer);
}
} _SEH2_END;
return RC;
} // end UDFRename()
#endif //UDF_READ_ONLY_BUILD
LONG
UDFFindFileId(
IN PVCB Vcb,
IN LONGLONG Id
)
{
if(!Vcb->FileIdCache) return (-1);
for(ULONG i=0; i<Vcb->FileIdCount; i++) {
if(Vcb->FileIdCache[i].Id == Id) return i;
}
return (-1);
} // end UDFFindFileId()
LONG
UDFFindFreeFileId(
IN PVCB Vcb,
IN LONGLONG Id
)
{
if(!Vcb->FileIdCache) {
if(!(Vcb->FileIdCache = (PUDFFileIDCacheItem)MyAllocatePool__(NonPagedPool, sizeof(UDFFileIDCacheItem)*FILE_ID_CACHE_GRANULARITY)))
return (-1);
RtlZeroMemory(Vcb->FileIdCache, FILE_ID_CACHE_GRANULARITY*sizeof(UDFFileIDCacheItem));
Vcb->FileIdCount = FILE_ID_CACHE_GRANULARITY;
}
for(ULONG i=0; i<Vcb->FileIdCount; i++) {
if(!Vcb->FileIdCache[i].FullName.Buffer) return i;
}
if(!MyReallocPool__((PCHAR)(Vcb->FileIdCache), Vcb->FileIdCount*sizeof(UDFFileIDCacheItem),
(PCHAR*)&(Vcb->FileIdCache), (Vcb->FileIdCount+FILE_ID_CACHE_GRANULARITY)*sizeof(UDFFileIDCacheItem))) {
return (-1);
}
RtlZeroMemory(&(Vcb->FileIdCache[Vcb->FileIdCount]), FILE_ID_CACHE_GRANULARITY*sizeof(UDFFileIDCacheItem));
Vcb->FileIdCount += FILE_ID_CACHE_GRANULARITY;
return (Vcb->FileIdCount - FILE_ID_CACHE_GRANULARITY);
} // end UDFFindFreeFileId()
NTSTATUS
UDFStoreFileId(
IN PVCB Vcb,
IN PtrUDFCCB Ccb,
IN PUDF_FILE_INFO fi,
IN LONGLONG Id
)
{
LONG i;
NTSTATUS RC = STATUS_SUCCESS;
if((i = UDFFindFileId(Vcb, Id)) == (-1)) {
if((i = UDFFindFreeFileId(Vcb, Id)) == (-1)) return STATUS_INSUFFICIENT_RESOURCES;
} else {
return STATUS_SUCCESS;
}
Vcb->FileIdCache[i].Id = Id;
Vcb->FileIdCache[i].CaseSens = (Ccb->CCBFlags & UDF_CCB_CASE_SENSETIVE) ? TRUE : FALSE;
RC = MyCloneUnicodeString(&(Vcb->FileIdCache[i].FullName), &(Ccb->Fcb->FCBName->ObjectName));
/* if(NT_SUCCESS(RC)) {
RC = MyAppendUnicodeStringToStringTag(&(Vcb->FileIdCache[i].FullName), &(Ccb->Fcb->FCBName->ObjectName), MEM_USFIDC_TAG);
}*/
return RC;
} // end UDFStoreFileId()
NTSTATUS
UDFRemoveFileId(
IN PVCB Vcb,
IN LONGLONG Id
)
{
LONG i;
if((i = UDFFindFileId(Vcb, Id)) == (-1)) return STATUS_INVALID_PARAMETER;
MyFreePool__(Vcb->FileIdCache[i].FullName.Buffer);
RtlZeroMemory(&(Vcb->FileIdCache[i]), sizeof(UDFFileIDCacheItem));
return STATUS_SUCCESS;
} // end UDFRemoveFileId()
VOID
UDFReleaseFileIdCache(
IN PVCB Vcb
)
{
if(!Vcb->FileIdCache) return;
for(ULONG i=0; i<Vcb->FileIdCount; i++) {
if(Vcb->FileIdCache[i].FullName.Buffer) {
MyFreePool__(Vcb->FileIdCache[i].FullName.Buffer);
}
}
MyFreePool__(Vcb->FileIdCache);
Vcb->FileIdCache = NULL;
Vcb->FileIdCount = 0;
} // end UDFReleaseFileIdCache()
NTSTATUS
UDFGetOpenParamsByFileId(
IN PVCB Vcb,
IN LONGLONG Id,
OUT PUNICODE_STRING* FName,
OUT BOOLEAN* CaseSens
)
{
LONG i;
if((i = UDFFindFileId(Vcb, Id)) == (-1)) return STATUS_NOT_FOUND;
(*FName) = &(Vcb->FileIdCache[i].FullName);
(*CaseSens) = !(Vcb->FileIdCache[i].CaseSens);
return STATUS_SUCCESS;
} // end UDFGetOpenParamsByFileId()
#ifndef UDF_READ_ONLY_BUILD
#ifdef UDF_ALLOW_HARD_LINKS
/*
create hard link for the file
*/
NTSTATUS
UDFHardLink(
IN PIO_STACK_LOCATION PtrSp,
IN PtrUDFFCB Fcb1,
IN PtrUDFCCB Ccb1,
IN PFILE_OBJECT FileObject1, // Source File
IN PFILE_LINK_INFORMATION PtrBuffer
)
{
// Target Directory
PFILE_OBJECT DirObject2 = PtrSp->Parameters.SetFile.FileObject;
// Overwite Flag
BOOLEAN Replace = PtrSp->Parameters.SetFile.ReplaceIfExists &&
PtrBuffer->ReplaceIfExists;
NTSTATUS RC;
PVCB Vcb = Fcb1->Vcb;
PtrUDFFCB Fcb2;
BOOLEAN ic;
BOOLEAN AcquiredVcb = TRUE;
BOOLEAN AcquiredVcbEx = FALSE;
BOOLEAN AcquiredDir1 = FALSE;
BOOLEAN AcquiredFcb1 = FALSE;
BOOLEAN SingleDir = TRUE;
PUDF_FILE_INFO File1;
PUDF_FILE_INFO Dir1 = NULL;
PUDF_FILE_INFO Dir2;
UNICODE_STRING NewName;
UNICODE_STRING LocalPath;
// PtrUDFCCB CurCcb = NULL;
AdPrint(("UDFHardLink\n"));
LocalPath.Buffer = NULL;
_SEH2_TRY {
// do we try to link Volume ?
if(!(File1 = Fcb1->FileInfo))
try_return (RC = STATUS_ACCESS_DENIED);
// do we try to link RootDir ?
if(!(Dir1 = File1->ParentFile))
try_return (RC = STATUS_ACCESS_DENIED);
// do we try to link Stream / Stream Dir ?
#ifdef UDF_ALLOW_LINKS_TO_STREAMS
if(UDFIsAStreamDir(File1))
try_return (RC = STATUS_ACCESS_DENIED);
#else //UDF_ALLOW_LINKS_TO_STREAMS
if(UDFIsAStream(File1) || UDFIsAStreamDir(File1) /*||
UDFIsADirectory(File1) || UDFHasAStreamDir(File1)*/)
try_return (RC = STATUS_ACCESS_DENIED);
#endif // UDF_ALLOW_LINKS_TO_STREAMS
// do we try to link to RootDir or Volume ?
if(!DirObject2) {
Dir2 = File1->ParentFile;
DirObject2 = FileObject1->RelatedFileObject;
} else
if(DirObject2->FsContext2 &&
(Fcb2 = ((PtrUDFCCB)(DirObject2->FsContext2))->Fcb)) {
Dir2 = ((PtrUDFCCB)(DirObject2->FsContext2))->Fcb->FileInfo;
} else {
try_return (RC = STATUS_INVALID_PARAMETER);
}
// check target dir
if(!Dir2) try_return (RC = STATUS_ACCESS_DENIED);
// Stream can't be a Dir or have Streams
if(UDFIsAStreamDir(Dir2)) {
try_return (RC = STATUS_ACCESS_DENIED);
/* if(UDFIsADirectory(File1) ||
UDFHasAStreamDir(File1)) {
BrutePoint();
try_return (RC = STATUS_ACCESS_DENIED);
}*/
}
/* if(UDFIsAStreamDir(Dir2))
try_return (RC = STATUS_ACCESS_DENIED);*/
RC = UDFPrepareForRenameMoveLink(Vcb, &AcquiredVcb, &AcquiredVcbEx,
&SingleDir,
&AcquiredDir1, &AcquiredFcb1,
Ccb1, File1,
Dir1, Dir2,
TRUE); // it is HLink operation
if(!NT_SUCCESS(RC))
try_return(RC);
// check if the source file is used
if(!DirObject2) {
// Make sure the name is of legal length.
if(PtrBuffer->FileNameLength > UDF_NAME_LEN*sizeof(WCHAR)) {
try_return(RC = STATUS_OBJECT_NAME_INVALID);
}
NewName.Length = NewName.MaximumLength = (USHORT)(PtrBuffer->FileNameLength);
NewName.Buffer = (PWCHAR)&(PtrBuffer->FileName);
} else {
// This name is by definition legal.
NewName = *((PUNICODE_STRING)&DirObject2->FileName);
}
ic = (Ccb1->CCBFlags & UDF_CCB_CASE_SENSETIVE) ? FALSE : TRUE;
AdPrint((" %ws ->\n %ws\n",
Fcb1->FCBName->ObjectName.Buffer,
NewName.Buffer));
RC = UDFHardLinkFile__(Vcb, ic, &Replace, &NewName, Dir1, Dir2, File1);
if(!NT_SUCCESS(RC)) try_return (RC);
// Update Parent Objects (mark 'em as modified)
if(Vcb->CompatFlags & UDF_VCB_IC_UPDATE_DIR_WRITE) {
if(DirObject2) {
DirObject2->Flags |= FO_FILE_MODIFIED;
if(!Replace)
DirObject2->Flags |= FO_FILE_SIZE_CHANGED;
}
}
// report changes
UDFNotifyFullReportChange( Vcb, File1,
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_LAST_ACCESS,
FILE_ACTION_MODIFIED );
RC = MyCloneUnicodeString(&LocalPath, (Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ?
&UDFGlobalData.UnicodeStrRoot :
&(Dir2->Fcb->FCBName->ObjectName));
if(!NT_SUCCESS(RC)) try_return (RC);
/* RC = MyAppendUnicodeStringToString(&LocalPath, (Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ? &(UDFGlobalData.UnicodeStrRoot) : &(Dir2->Fcb->FCBName->ObjectName));
if(!NT_SUCCESS(RC)) try_return (RC);*/
// if Dir2 is a RootDir, we shoud not append '\\' because
// it will be the 2nd '\\' character (RootDir's name is also '\\')
if(Dir2->ParentFile) {
RC = MyAppendUnicodeToString(&LocalPath, L"\\");
if(!NT_SUCCESS(RC)) try_return (RC);
}
RC = MyAppendUnicodeStringToStringTag(&LocalPath, &NewName, MEM_USHL_TAG);
if(!NT_SUCCESS(RC)) try_return (RC);
if(!Replace) {
/* UDFNotifyFullReportChange( Vcb, File2,
UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_ADDED );*/
FsRtlNotifyFullReportChange( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
(PSTRING)&LocalPath,
((Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ? 0 : Dir2->Fcb->FCBName->ObjectName.Length) + sizeof(WCHAR),
NULL,NULL,
UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_ACTION_ADDED,
NULL);
} else {
/* UDFNotifyFullReportChange( Vcb, File2,
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_EA,
FILE_ACTION_MODIFIED );*/
FsRtlNotifyFullReportChange( Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP),
(PSTRING)&LocalPath,
((Dir2->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY) ? 0 : Dir2->Fcb->FCBName->ObjectName.Length) + sizeof(WCHAR),
NULL,NULL,
UDFIsADirectory(File1) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME,
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
FILE_NOTIFY_CHANGE_CREATION |
FILE_NOTIFY_CHANGE_EA,
NULL);
}
RC = STATUS_SUCCESS;
try_exit: NOTHING;
} _SEH2_FINALLY {
if(AcquiredFcb1) {
UDF_CHECK_PAGING_IO_RESOURCE(Fcb1->NTRequiredFCB);
UDFReleaseResource(&(Fcb1->NTRequiredFCB->MainResource));
}
if(AcquiredDir1) {
UDF_CHECK_PAGING_IO_RESOURCE(Dir1->Fcb->NTRequiredFCB);
UDFReleaseResource(&(Dir1->Fcb->NTRequiredFCB->MainResource));
}
if(AcquiredVcb) {
if(AcquiredVcbEx)
UDFConvertExclusiveToSharedLite(&(Vcb->VCBResource));
} else {
// caller assumes Vcb to be acquired shared
BrutePoint();
UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
}
if(LocalPath.Buffer) {
MyFreePool__(LocalPath.Buffer);
}
} _SEH2_END;
return RC;
} // end UDFHardLink()
#endif //UDF_ALLOW_HARD_LINKS
#endif //UDF_READ_ONLY_BUILD