//////////////////////////////////////////////////////////////////// // 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; iFcb) { 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; iFileIdCount; 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; iFileIdCount; 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; iFileIdCount; 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