mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
2703 lines
106 KiB
C++
2703 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_PTR)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
|