//////////////////////////////////////////////////////////////////// // 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: Create.cpp * * Module: UDF File System Driver (Kernel mode execution only) * * Description: * Contains code to handle the "Create"/"Open" dispatch entry point. * *************************************************************************/ #include "udffs.h" #define IsFileObjectReadOnly(FO) (!((FO)->WriteAccess | (FO)->DeleteAccess)) // define the file specific bug-check id #define UDF_BUG_CHECK_ID UDF_FILE_CREATE #define MEM_USABS_TAG "US_Abs" #define MEM_USLOC_TAG "US_Loc" #define MEM_USOBJ_TAG "US_Obj" #define UDF_LOG_CREATE_DISPOSITION /************************************************************************* * * Function: UDFCreate() * * Description: * The I/O Manager will invoke this routine to handle a create/open * 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 UDFCreate( PDEVICE_OBJECT DeviceObject, // the logical volume device object PIRP Irp) // I/O Request Packet { NTSTATUS RC = STATUS_SUCCESS; PtrUDFIrpContext PtrIrpContext; BOOLEAN AreWeTopLevel = FALSE; TmPrint(("UDFCreate:\n")); FsRtlEnterFileSystem(); ASSERT(DeviceObject); ASSERT(Irp); // sometimes, we may be called here with the device object representing // the file system (instead of the device object created for a logical // volume. In this case, there is not much we wish to do (this create // typically will happen 'cause some process has to open the FSD device // object so as to be able to send an IOCTL to the FSD) // All of the logical volume device objects we create have a device // extension whereas the device object representing the FSD has no // device extension. This seems like a good enough method to identify // between the two device objects ... if (UDFIsFSDevObj(DeviceObject)) { // this is an open of the FSD itself Irp->IoStatus.Status = RC; Irp->IoStatus.Information = FILE_OPENED; IoCompleteRequest(Irp, IO_NO_INCREMENT); FsRtlExitFileSystem(); return(RC); } // set the top level context AreWeTopLevel = UDFIsIrpTopLevel(Irp); _SEH2_TRY { // get an IRP context structure and issue the request PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject); if(PtrIrpContext) { RC = UDFCommonCreate(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); } AdPrint(("UDFCreate: %x\n", RC)); FsRtlExitFileSystem(); return(RC); } // end UDFCreate() /* */ VOID __fastcall UDFReleaseResFromCreate( IN PERESOURCE* PagingIoRes, IN PERESOURCE* Res1, IN PERESOURCE* Res2 ) { if(*PagingIoRes) { UDFReleaseResource(*PagingIoRes); (*PagingIoRes) = NULL; } if(*Res1) { UDFReleaseResource(*Res1); (*Res1) = NULL; } if(*Res2) { UDFReleaseResource(*Res2); (*Res2) = NULL; } } // end UDFReleaseResFromCreate() /* */ VOID __fastcall UDFAcquireParent( IN PUDF_FILE_INFO RelatedFileInfo, IN PERESOURCE* Res1, IN PERESOURCE* Res2 ) { if(RelatedFileInfo->Fcb && RelatedFileInfo->Fcb->ParentFcb) { UDF_CHECK_PAGING_IO_RESOURCE(RelatedFileInfo->Fcb->ParentFcb->NTRequiredFCB); UDFAcquireResourceExclusive((*Res2) = &(RelatedFileInfo->Fcb->ParentFcb->NTRequiredFCB->MainResource),TRUE); } UDF_CHECK_PAGING_IO_RESOURCE(RelatedFileInfo->Fcb->NTRequiredFCB); UDFAcquireResourceExclusive((*Res1) = &(RelatedFileInfo->Fcb->NTRequiredFCB->MainResource),TRUE); UDFInterlockedIncrement((PLONG)&(RelatedFileInfo->Fcb->ReferenceCount)); UDFInterlockedIncrement((PLONG)&(RelatedFileInfo->Dloc->CommonFcb->CommonRefCount)); UDFReferenceFile__(RelatedFileInfo); ASSERT_REF(RelatedFileInfo->Fcb->ReferenceCount >= RelatedFileInfo->RefCount); } // end UDFAcquireParent() /************************************************************************* * * Function: UDFCommonCreate() * * 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 UDFCommonCreate( PtrUDFIrpContext PtrIrpContext, PIRP Irp ) { NTSTATUS RC = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp = NULL; PIO_SECURITY_CONTEXT PtrSecurityContext = NULL; PFILE_OBJECT PtrNewFileObject = NULL; PFILE_OBJECT PtrRelatedFileObject = NULL; LONGLONG AllocationSize; // if we create a new file PFILE_FULL_EA_INFORMATION PtrExtAttrBuffer = NULL; ULONG RequestedOptions; ULONG RequestedDisposition; USHORT FileAttributes; USHORT TmpFileAttributes; USHORT ShareAccess; ULONG ExtAttrLength = 0; ACCESS_MASK DesiredAccess; PACCESS_STATE AccessState; _SEH2_VOLATILE PVCB Vcb = NULL; _SEH2_VOLATILE BOOLEAN AcquiredVcb = FALSE; BOOLEAN OpenExisting = FALSE; PERESOURCE Res1 = NULL; PERESOURCE Res2 = NULL; PERESOURCE PagingIoRes = NULL; // BOOLEAN DirectoryOnlyRequested; // BOOLEAN FileOnlyRequested; // BOOLEAN NoBufferingSpecified; BOOLEAN WriteThroughRequested; BOOLEAN DeleteOnCloseSpecified; // BOOLEAN NoExtAttrKnowledge; // BOOLEAN CreateTreeConnection = FALSE; // BOOLEAN OpenByFileId; // Are we dealing with a page file? BOOLEAN PageFileManipulation; // Is this open for a target directory (used in rename operations)? BOOLEAN OpenTargetDirectory; // Should we ignore case when attempting to locate the object? BOOLEAN IgnoreCase; PtrUDFCCB PtrRelatedCCB = NULL, PtrNewCcb = NULL; PtrUDFFCB PtrRelatedFCB = NULL, PtrNewFcb = NULL; PtrUDFNTRequiredFCB NtReqFcb; ULONG ReturnedInformation = 0; UNICODE_STRING TargetObjectName; UNICODE_STRING RelatedObjectName; UNICODE_STRING AbsolutePathName; // '\aaa\cdf\fff\rrrr.tre:s' UNICODE_STRING LocalPath; // '\aaa\cdf' UNICODE_STRING CurName; // 'cdf' UNICODE_STRING TailName; // 'fff\rrrr.tre:s' UNICODE_STRING LastGoodName; // it depends... UNICODE_STRING LastGoodTail; // it depends... UNICODE_STRING StreamName; // ':s' PUDF_FILE_INFO RelatedFileInfo; PUDF_FILE_INFO OldRelatedFileInfo = NULL; PUDF_FILE_INFO NewFileInfo = NULL; PUDF_FILE_INFO LastGoodFileInfo = NULL; PWCHAR TmpBuffer; ULONG TreeLength = 0; // ULONG i = 0; BOOLEAN StreamOpen = FALSE; BOOLEAN StreamExists = FALSE; BOOLEAN RestoreVCBOpenCounter = FALSE; BOOLEAN RestoreShareAccess = FALSE; PWCHAR TailNameBuffer = NULL; ULONG SNameIndex = 0; TmPrint(("UDFCommonCreate:\n")); ASSERT(PtrIrpContext); ASSERT(Irp); _SEH2_TRY { AbsolutePathName.Buffer = LocalPath.Buffer = NULL; // If we were called with our file system device object instead of a // volume device object, just complete this request with STATUS_SUCCESS. if (!(PtrIrpContext->TargetDeviceObject->DeviceExtension)) { ReturnedInformation = FILE_OPENED; try_return(RC = STATUS_SUCCESS); } AbsolutePathName.Length = AbsolutePathName.MaximumLength = LocalPath.Length = LocalPath.MaximumLength = 0; // First, get a pointer to the current I/O stack location IrpSp = IoGetCurrentIrpStackLocation(Irp); ASSERT(IrpSp); // If the caller cannot block, post the request to be handled // asynchronously if (!(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK)) { // We must defer processing of this request since we could // block anytime while performing the create/open ... ASSERT(FALSE); RC = UDFPostRequest(PtrIrpContext, Irp); try_return(RC); } // Now, we can obtain the parameters specified by the user. // Note that the file object is the new object created by the // I/O Manager in anticipation that this create/open request // will succeed. PtrNewFileObject = IrpSp->FileObject; TargetObjectName = PtrNewFileObject->FileName; PtrRelatedFileObject = PtrNewFileObject->RelatedFileObject; // If a related file object is present, get the pointers // to the CCB and the FCB for the related file object if (PtrRelatedFileObject) { PtrRelatedCCB = (PtrUDFCCB)(PtrRelatedFileObject->FsContext2); ASSERT(PtrRelatedCCB); ASSERT(PtrRelatedCCB->NodeIdentifier.NodeType == UDF_NODE_TYPE_CCB); // each CCB in turn points to a FCB PtrRelatedFCB = PtrRelatedCCB->Fcb; ASSERT(PtrRelatedFCB); ASSERT((PtrRelatedFCB->NodeIdentifier.NodeType == UDF_NODE_TYPE_FCB) ||(PtrRelatedFCB->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB)); RelatedObjectName = PtrRelatedFileObject->FileName; if (!(RelatedObjectName.Length) || (RelatedObjectName.Buffer[0] != L'\\')) { if(PtrRelatedFCB->FCBName) RelatedObjectName = PtrRelatedFCB->FCBName->ObjectName; } } // Allocation size is only used if a new file is created // or a file is superseded. AllocationSize = Irp->Overlay.AllocationSize.QuadPart; // Get a ptr to the supplied security context PtrSecurityContext = IrpSp->Parameters.Create.SecurityContext; AccessState = PtrSecurityContext->AccessState; // The desired access can be obtained from the SecurityContext DesiredAccess = PtrSecurityContext->DesiredAccess; // Two values are supplied in the Create.Options field: // (a) the actual user supplied options // (b) the create disposition RequestedOptions = (IrpSp->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS); // The file disposition is packed with the user options ... // Disposition includes FILE_SUPERSEDE, FILE_OPEN_IF, etc. RequestedDisposition = (IrpSp->Parameters.Create.Options >> 24);// & 0xFF; //#ifdef UDF_LOG_CREATE_DISPOSITION switch(RequestedDisposition) { case FILE_SUPERSEDE: AdPrint((" Dispos: FILE_SUPERSEDE\n")); break; case FILE_OPEN: AdPrint((" Dispos: FILE_OPEN\n")); break; case FILE_CREATE: AdPrint((" Dispos: FILE_CREATE\n")); break; case FILE_OPEN_IF: AdPrint((" Dispos: FILE_OPEN_IF\n")); break; case FILE_OVERWRITE: AdPrint((" Dispos: FILE_OVERWRITE\n")); break; case FILE_OVERWRITE_IF: AdPrint((" Dispos: FILE_OVERWRITE_IF\n")); break; default: AdPrint((" Dispos: *** Unknown ***\n")); break; } //#endif // UDF_LOG_CREATE_DISPOSITION FileAttributes = (USHORT)(IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS); ShareAccess = IrpSp->Parameters.Create.ShareAccess; // If the FSD does not support EA manipulation, we might return // invalid parameter if the following are supplied. // EA arguments are only used if a new file is created or a file is // superseded // But some applications _require_ EA support // (Notepad... rather strange, isn't it ?) // So, for such stupid ones // !!! We shall ignore these parameters !!! // PtrExtAttrBuffer = (struct _FILE_FULL_EA_INFORMATION *) Irp->AssociatedIrp.SystemBuffer; // ExtAttrLength = IrpSp->Parameters.Create.EaLength; // Get the options supplied by the user #define OpenForBackup (RequestedOptions & FILE_OPEN_FOR_BACKUP_INTENT) // User specifies that returned object MUST be a directory. // Lack of presence of this flag does not mean it *cannot* be a // directory *unless* FileOnlyRequested is set (see below) // Presence of the flag however, does require that the returned object be // a directory (container) object. #define DirectoryOnlyRequested (RequestedOptions & FILE_DIRECTORY_FILE) // User specifies that returned object MUST NOT be a directory. // Lack of presence of this flag does not mean it *cannot* be a // file *unless* DirectoryOnlyRequested is set (see above). // Presence of the flag however does require that the returned object be // a simple file (non-container) object. #define FileOnlyRequested (RequestedOptions & FILE_NON_DIRECTORY_FILE) // We cannot cache the file if the following flag is set. // However, things do get a little bit interesting if caching // has been already initiated due to a previous open ... // (maintaining consistency then becomes a little bit more // of a headache - see read/write file descriptions) #define NoBufferingSpecified (RequestedOptions & FILE_NO_INTERMEDIATE_BUFFERING) // Write-through simply means that the FSD must *not* return from // a user write request until the data has been flushed to secondary // storage (either to disks directly connected to the node or across // the network in the case of a redirector) WriteThroughRequested = (RequestedOptions & FILE_WRITE_THROUGH) ? TRUE : FALSE; #define SequentialIoRequested (RequestedOptions & FILE_SEQUENTIAL_ONLY ? TRUE : FALSE) // Not all of the native file system implementations support // the delete-on-close option. All this means is that after the // last close on the FCB has been performed, the FSD should // delete the file. It simply saves the caller from issuing a // separate delete request. Also, some FSD implementations might choose // to implement a Windows NT idiosyncratic behavior wherein we // could create such "delete-on-close" marked files under directories // marked for deletion. Ordinarily, a FSD will not allow us to create // a new file under a directory that has been marked for deletion. DeleteOnCloseSpecified = (IrpSp->Parameters.Create.Options & FILE_DELETE_ON_CLOSE) ? TRUE : FALSE; if(DeleteOnCloseSpecified) { AdPrint((" DeleteOnClose\n")); } #define NoExtAttrKnowledge /*(RequestedOptions & FILE_NO_EA_KNOWLEDGE) ?*/ TRUE /*: FALSE*/ // The following flag is only used by the LAN Manager redirector // to initiate a "new mapping" to a remote share. Typically, // a FSD will not see this flag (especially disk based FSD's) // CreateTreeConnection = (RequestedOptions & FILE_CREATE_TREE_CONNECTION) ? TRUE : FALSE; // The NTFS file system for exmaple supports the OpenByFileId option. // The FSD may also be able to associate a unique numerical ID with // an on-disk object. The caller would get this ID in a "query file // information" call. // Later, the caller might decide to reopen the object, this time // though it may supply the FSD with the file identifier instead of // a file/path name. #define OpenByFileId (RequestedOptions & FILE_OPEN_BY_FILE_ID) // Are we dealing with a page file? PageFileManipulation = (IrpSp->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE; // The open target directory flag is used as part of the sequence of // operations performed by the I/O Manager is response to a file/dir // rename operation. See the explanation in the book for details. OpenTargetDirectory = (IrpSp->Flags & SL_OPEN_TARGET_DIRECTORY) ? TRUE : FALSE; // If the FSD supports case-sensitive file name checks, we may // choose to honor the following flag ... IgnoreCase = (IrpSp->Flags & SL_CASE_SENSITIVE) ? FALSE : TRUE; // Ensure that the operation has been directed to a valid VCB ... Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension); ASSERT(Vcb); ASSERT(Vcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB); // Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; WriteThroughRequested = WriteThroughRequested || (Vcb->CompatFlags & UDF_VCB_IC_FORCE_WRITE_THROUGH); // Do some preliminary checks to make sure the operation is supported. // We fail in the following cases immediately. // - Open a paging file. // - Open a file with Eas. if(PageFileManipulation) { ReturnedInformation = 0; AdPrint(("Can't create a page file\n")); try_return(RC = STATUS_ACCESS_DENIED); } if(ExtAttrLength) { ReturnedInformation = 0; AdPrint(("Can't create file with EAs\n")); try_return(RC = STATUS_EAS_NOT_SUPPORTED); } UDFFlushTryBreak(Vcb); if (Vcb->SoftEjectReq) { AdPrint((" Eject requested\n")); ReturnedInformation = FILE_DOES_NOT_EXIST; try_return(RC = STATUS_FILE_INVALID); } // If the volume has been locked, fail the request if ((Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_LOCKED) && (Vcb->VolumeLockPID != GetCurrentPID())) { AdPrint((" Volume is locked\n")); RC = STATUS_ACCESS_DENIED; try_return(RC); } // We need EXCLUSIVE access to Vcb to avoid parallel calls to UDFVerifyVcb() UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); AcquiredVcb = TRUE; // Disk based file systems might decide to verify the logical volume // (if required and only if removable media are supported) at this time RC = UDFVerifyVcb(PtrIrpContext,Vcb); if(!NT_SUCCESS(RC)) try_return(RC); UDFConvertExclusiveToSharedLite(&(Vcb->VCBResource)); ASSERT(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED); // We fail in the following cases for Read-Only volumes // - Open a target directory. // - Create a file. if( ( ((Vcb->origIntegrityType == INTEGRITY_TYPE_OPEN) && (Vcb->CompatFlags & UDF_VCB_IC_DIRTY_RO)) #ifndef UDF_READ_ONLY_BUILD || (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) #endif //UDF_READ_ONLY_BUILD ) && (DeleteOnCloseSpecified || OpenTargetDirectory || (RequestedDisposition == FILE_CREATE) || (RequestedDisposition == FILE_OVERWRITE) || (RequestedDisposition == FILE_OVERWRITE_IF) || (RequestedDisposition == FILE_SUPERSEDE) || AllocationSize) ) { ReturnedInformation = 0; AdPrint((" Write protected or dirty\n")); try_return(RC = STATUS_MEDIA_WRITE_PROTECTED); } /* if(DesiredAccess & (FILE_READ_EA | FILE_WRITE_EA)) { ReturnedInformation = 0; AdPrint((" EAs not supported\n")); try_return(RC = STATUS_ACCESS_DENIED); }*/ // **************** // If a Volume open is requested, satisfy it now // **************** if (!(PtrNewFileObject->FileName.Length) && (!PtrRelatedFileObject || (PtrRelatedFCB->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB))) { BOOLEAN UndoLock = FALSE; AdPrint((" Opening Volume\n")); // If the supplied file name is NULL *and* either there exists // no related file object *or* if a related file object was supplied // but it too refers to a previously opened instance of a logical // volume, this open must be for a logical volume. // Note: the FSD might decide to do "special" things (whatever they // might be) in response to an open request for the logical volume. // Logical volume open requests are done primarily to get/set volume // information, lock the volume, dismount the volume (using the IOCTL // FSCTL_DISMOUNT_VOLUME) etc. // If a volume open is requested, perform checks to ensure that // invalid options have not also been specified ... if ((OpenTargetDirectory) || (PtrExtAttrBuffer)) { try_return(RC = STATUS_INVALID_PARAMETER); } if (DirectoryOnlyRequested) { // a volume is not a directory try_return(RC = STATUS_NOT_A_DIRECTORY); } #ifndef UDF_READ_ONLY_BUILD if (DeleteOnCloseSpecified) { // delete volume.... hmm try_return(RC = STATUS_CANNOT_DELETE); } if ((RequestedDisposition != FILE_OPEN) && (RequestedDisposition != FILE_OPEN_IF)) { // cannot create a new volume, I'm afraid ... ReturnedInformation = FILE_DOES_NOT_EXIST; try_return(RC = STATUS_ACCESS_DENIED); } #endif //UDF_READ_ONLY_BUILD UDFPrint((" ShareAccess %x, DesiredAccess %x\n", ShareAccess, DesiredAccess)); /* if(!(ShareAccess & (FILE_SHARE_WRITE | FILE_SHARE_DELETE)) && !(DesiredAccess & (FILE_GENERIC_WRITE & ~SYNCHRONIZE)) && (ShareAccess & FILE_SHARE_READ) ) { */ if(!(DesiredAccess & ((GENERIC_WRITE | FILE_GENERIC_WRITE) & ~(SYNCHRONIZE | READ_CONTROL))) && (ShareAccess & FILE_SHARE_READ) ) { UDFPrint((" R/O volume open\n")); } else { UDFPrint((" R/W volume open\n")); if(Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_READ_ONLY) { UDFPrint((" media-ro\n")); try_return(RC = STATUS_MEDIA_WRITE_PROTECTED); } } if(!(ShareAccess & (FILE_SHARE_WRITE | FILE_SHARE_DELETE)) && !(DesiredAccess & ((GENERIC_WRITE | FILE_GENERIC_WRITE) & ~(SYNCHRONIZE | READ_CONTROL))) && (ShareAccess & FILE_SHARE_READ) ) { // do nothing } else { if(!(ShareAccess & FILE_SHARE_READ) || (DesiredAccess & ((GENERIC_WRITE | FILE_GENERIC_WRITE) & ~(SYNCHRONIZE | READ_CONTROL))) ) { // As soon as OpenVolume flushes the volume // we should complete all pending requests (Close) UDFPrint((" set UDF_IRP_CONTEXT_FLUSH2_REQUIRED\n")); PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_FLUSH2_REQUIRED; /* UDFInterlockedIncrement((PLONG)&(Vcb->VCBOpenCount)); UDFReleaseResource(&(Vcb->VCBResource)); AcquiredVcb = FALSE; if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)) { UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo); } #ifdef UDF_DELAYED_CLOSE UDFCloseAllDelayed(Vcb); #endif //UDF_DELAYED_CLOSE UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); AcquiredVcb = TRUE; UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount)); */ } } // If the user does not want to share write or delete then we will try // and take out a lock on the volume. if(!(ShareAccess & (FILE_SHARE_WRITE | FILE_SHARE_DELETE))) { // Do a quick check here for handles on exclusive open. if ((Vcb->VCBHandleCount) && !(ShareAccess & FILE_SHARE_READ)) { // Sharing violation UDFPrint((" !FILE_SHARE_READ + open handles (%d)\n", Vcb->VCBHandleCount)); try_return(RC = STATUS_SHARING_VIOLATION); } if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_FLUSH2_REQUIRED) { UDFPrint((" perform flush\n")); PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_FLUSH2_REQUIRED; UDFInterlockedIncrement((PLONG)&(Vcb->VCBOpenCount)); UDFReleaseResource(&(Vcb->VCBResource)); AcquiredVcb = FALSE; if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)) { UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo); } #ifdef UDF_DELAYED_CLOSE UDFCloseAllDelayed(Vcb); #endif //UDF_DELAYED_CLOSE UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); AcquiredVcb = TRUE; UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount)); UDFFlushLogicalVolume(NULL, NULL, Vcb); if((ShareAccess & FILE_SHARE_READ) && ((Vcb->VCBOpenCount - UDF_RESIDUAL_REFERENCE) != (Vcb->VCBOpenCountRO))) { UDFPrint((" FILE_SHARE_READ + R/W handles: %d(%d) -> STATUS_SHARING_VIOLATION ?\n", Vcb->VCBOpenCount - UDF_RESIDUAL_REFERENCE, Vcb->VCBOpenCountRO)); /* we shall not check it here, let System do it in IoCheckShareAccess() */ //try_return(RC = STATUS_SHARING_VIOLATION); } } // Lock the volume if(!(ShareAccess & FILE_SHARE_READ)) { UDFPrint((" set Lock\n")); Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_LOCKED; Vcb->VolumeLockFileObject = PtrNewFileObject; UndoLock = TRUE; } else if(DesiredAccess & ((GENERIC_WRITE | FILE_GENERIC_WRITE) & ~(SYNCHRONIZE | READ_CONTROL))) { UDFPrint((" set UDF_IRP_CONTEXT_FLUSH_REQUIRED\n")); PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_FLUSH_REQUIRED; } } PtrNewFcb = (PtrUDFFCB)Vcb; ASSERT(!(PtrNewFcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE)); RC = UDFOpenFile(Vcb, PtrNewFileObject, PtrNewFcb); if (!NT_SUCCESS(RC)) goto op_vol_accs_dnd; PtrNewCcb = (PtrUDFCCB)(PtrNewFileObject->FsContext2); if(PtrNewCcb) PtrNewCcb->CCBFlags |= UDF_CCB_VOLUME_OPEN; // Check _Security_ RC = UDFCheckAccessRights(NULL, AccessState, Vcb->RootDirFCB, PtrNewCcb, DesiredAccess, ShareAccess); if (!NT_SUCCESS(RC)) { AdPrint((" Access violation (Volume)\n")); goto op_vol_accs_dnd; } // Check _ShareAccess_ RC = UDFCheckAccessRights(PtrNewFileObject, AccessState, PtrNewFcb, PtrNewCcb, DesiredAccess, ShareAccess); if(!NT_SUCCESS(RC)) { AdPrint((" Sharing violation (Volume)\n")); op_vol_accs_dnd: if(UndoLock) { Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_LOCKED; Vcb->VolumeLockFileObject = NULL; } try_return(RC); } // NoBufferingSpecified = TRUE; See #define above RequestedOptions |= FILE_NO_INTERMEDIATE_BUFFERING; ReturnedInformation = FILE_OPENED; UDFNotifyVolumeEvent(PtrNewFileObject, FSRTL_VOLUME_LOCK); try_return(RC); } if((Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) && (!(Vcb->CompatFlags & UDF_VCB_IC_SHOW_BLANK_CD) || UDFGlobalData.AutoFormatCount)) { ReturnedInformation = 0; AdPrint((" Can't open anything on blank volume ;)\n")); try_return(RC = STATUS_OBJECT_NAME_NOT_FOUND); } if(UdfIllegalFcbAccess(Vcb,DesiredAccess)) { ReturnedInformation = 0; AdPrint((" Illegal share access\n")); try_return(RC = STATUS_ACCESS_DENIED); } // we could mount blank R/RW media in order to allow // user-mode applications to get access with Write privileges ASSERT(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED); // we should check appropriate privilege if OpenForBackup requested if(OpenForBackup) { if (!SeSinglePrivilegeCheck(SeExports->SeBackupPrivilege, UserMode)) { try_return(RC = STATUS_PRIVILEGE_NOT_HELD); } } // The FSD might wish to implement the open-by-id option. The "id" // is some unique numerical representation of the on-disk object. // The caller then therefore give us this file id and the FSD // should be completely capable of "opening" the object (it must // exist since the caller received an id for the object from the // FSD in a "query file" call ... // If the file has been deleted in the meantime, we'll return // "not found" // **************** // Open by FileID // **************** if (OpenByFileId) { // perform the open ... PUNICODE_STRING TmpPath; LONGLONG Id; UDFPrint((" open by File ID\n")); if(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) { ReturnedInformation = 0; AdPrint((" Can't open by FileID on blank volume ;)\n")); try_return(RC = STATUS_OBJECT_NAME_NOT_FOUND); } if (TargetObjectName.Length != sizeof(FILE_ID)) { AdPrint((" Invalid file ID\n")); try_return(RC = STATUS_INVALID_PARAMETER); } Id = *((FILE_ID*)(TargetObjectName.Buffer)); AdPrint((" Opening by ID %8.8x%8.8x\n", (ULONG)(Id>>32), (ULONG)Id)); if ((RequestedDisposition != FILE_OPEN) && (RequestedDisposition != FILE_OPEN_IF)) { AdPrint((" Illegal disposition for ID open\n")); try_return(RC = STATUS_ACCESS_DENIED); } RC = UDFGetOpenParamsByFileId(Vcb, Id, &TmpPath, &IgnoreCase); if(!NT_SUCCESS(RC)) { AdPrint((" ID open failed\n")); try_return(RC); } // simulate absolute path open /* if(!NT_SUCCESS(RC = MyInitUnicodeString(&TargetObjectName, L"")) || !NT_SUCCESS(RC = MyAppendUnicodeStringToStringTag(&TargetObjectName, TmpPath, MEM_USABS_TAG))) {*/ if(!NT_SUCCESS(RC = MyCloneUnicodeString(&TargetObjectName, TmpPath))) { AdPrint((" Init String failed\n")); try_return(RC); } //ASSERT(TargetObjectName.Buffer); AbsolutePathName = TargetObjectName; PtrRelatedFileObject = NULL; } else // **************** // Relative open // **************** // Now determine the starting point from which to begin the parsing if (PtrRelatedFileObject) { // We have a user supplied related file object. // This implies a "relative" open i.e. relative to the directory // represented by the related file object ... UDFPrint((" PtrRelatedFileObject %x, FCB %x\n", PtrRelatedFileObject, PtrRelatedFCB)); // Note: The only purpose FSD implementations ever have for // the related file object is to determine whether this // is a relative open or not. At all other times (including // during I/O operations), this field is meaningless from // the FSD's perspective. if (!(PtrRelatedFCB->FCBFlags & UDF_FCB_DIRECTORY)) { // we must have a directory as the "related" object RC = STATUS_INVALID_PARAMETER; AdPrint((" Related object must be a directory\n")); AdPrint((" Flags %x\n", PtrRelatedFCB->FCBFlags)); _SEH2_TRY { AdPrint((" ObjName %x, ", PtrRelatedFCB->FCBName->ObjectName)); AdPrint((" Name %S\n", PtrRelatedFCB->FCBName->ObjectName.Buffer)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { AdPrint((" exception when printing name\n")); } _SEH2_END; try_return(RC); } // So we have a directory, ensure that the name begins with // a "\" i.e. begins at the root and does *not* begin with a "\\" // NOTE: This is just an example of the kind of path-name string // validation that a FSD must do. Although the remainder of // the code may not include such checks, any commercial // FSD *must* include such checking (no one else, including // the I/O Manager will perform checks on the FSD's behalf) if (!(RelatedObjectName.Length) || (RelatedObjectName.Buffer[0] != L'\\')) { AdPrint((" Wrong pathname (1)\n")); RC = STATUS_INVALID_PARAMETER; try_return(RC); } // similarly, if the target file name starts with a "\", it // is wrong since the target file name can no longer be absolute ASSERT(TargetObjectName.Buffer || !TargetObjectName.Length); if (TargetObjectName.Length && (TargetObjectName.Buffer[0] == L'\\')) { AdPrint((" Wrong pathname (2)\n")); RC = STATUS_INVALID_PARAMETER; try_return(RC); } // Create an absolute path-name. We could potentially use // the absolute path-name if we cache previously opened // file/directory object names. /* if(!NT_SUCCESS(RC = MyInitUnicodeString(&AbsolutePathName, L"")) || !NT_SUCCESS(RC MyAppendUnicodeStringToStringTag(&AbsolutePathName, &RelatedObjectName, MEM_USABS_TAG)))*/ if(!NT_SUCCESS(RC = MyCloneUnicodeString(&AbsolutePathName, &RelatedObjectName))) try_return(RC); if(RelatedObjectName.Length && (RelatedObjectName.Buffer[ (RelatedObjectName.Length/sizeof(WCHAR)) - 1 ] != L'\\')) { RC = MyAppendUnicodeToString(&AbsolutePathName, L"\\"); if(!NT_SUCCESS(RC)) try_return(RC); } if(!AbsolutePathName.Length || (AbsolutePathName.Buffer[ (AbsolutePathName.Length/sizeof(WCHAR)) - 1 ] != L'\\')) { ASSERT(TargetObjectName.Buffer); if(TargetObjectName.Length && TargetObjectName.Buffer[0] != L'\\') { RC = MyAppendUnicodeToString(&AbsolutePathName, L"\\"); if(!NT_SUCCESS(RC)) try_return(RC); } } //ASSERT(TargetObjectName.Buffer); RC = MyAppendUnicodeStringToStringTag(&AbsolutePathName, &TargetObjectName, MEM_USABS_TAG); if(!NT_SUCCESS(RC)) try_return(RC); } else { // **************** // Absolute open // **************** // The suplied path-name must be an absolute path-name i.e. // starting at the root of the file system tree UDFPrint((" Absolute open\n")); ASSERT(TargetObjectName.Buffer); if (!TargetObjectName.Length || TargetObjectName.Buffer[0] != L'\\') { AdPrint((" Wrong target name (1)\n")); try_return(RC = STATUS_INVALID_PARAMETER); } /* if(!NT_SUCCESS(RC = MyInitUnicodeString(&AbsolutePathName, L"")) || !NT_SUCCESS(RC = MyAppendUnicodeStringToStringTag(&AbsolutePathName, &TargetObjectName, MEM_USABS_TAG)))*/ ASSERT(TargetObjectName.Buffer); if(!NT_SUCCESS(RC = MyCloneUnicodeString(&AbsolutePathName, &TargetObjectName))) try_return(RC); } // Win 32 protection :) if ((AbsolutePathName.Length >= sizeof(WCHAR)*2) && (AbsolutePathName.Buffer[1] == L'\\') && (AbsolutePathName.Buffer[0] == L'\\')) { // If there are still two beginning backslashes, the name is bogus. if ((AbsolutePathName.Length > 2*sizeof(WCHAR)) && (AbsolutePathName.Buffer[2] == L'\\')) { AdPrint((" Wrong target name (2)\n")); try_return (RC = STATUS_OBJECT_NAME_INVALID); } // Slide the name down in the buffer. RtlMoveMemory( AbsolutePathName.Buffer, AbsolutePathName.Buffer + 1, AbsolutePathName.Length ); // .Length includes // NULL-terminator AbsolutePathName.Length -= sizeof(WCHAR); } if ( (AbsolutePathName.Length > sizeof(WCHAR) ) && (AbsolutePathName.Buffer[ (AbsolutePathName.Length/sizeof(WCHAR)) - 1 ] == L'\\') ) { AbsolutePathName.Length -= sizeof(WCHAR); } // TERMINATOR (2) ;) AbsolutePathName.Buffer[AbsolutePathName.Length/sizeof(WCHAR)] = 0; // Sometimes W2000 decides to duplicate handle of // already opened File/Dir. In this case it sends us // RelatedFileObject & specifies zero-filled RelativePath if(!TargetObjectName.Length) { TargetObjectName = AbsolutePathName; OpenExisting = TRUE; } //ASSERT(TargetObjectName.Buffer); // **************** // First, check if the caller simply wishes to open the Root // of the file system tree. // **************** if (AbsolutePathName.Length == sizeof(WCHAR)) { AdPrint((" Opening RootDir\n")); // this is an open of the root directory, ensure that the caller // has not requested a file only if (FileOnlyRequested || (RequestedDisposition == FILE_SUPERSEDE) || (RequestedDisposition == FILE_OVERWRITE) || (RequestedDisposition == FILE_OVERWRITE_IF)) { AdPrint((" Can't overwrite RootDir\n")); RC = STATUS_FILE_IS_A_DIRECTORY; try_return(RC); } #if 0 CollectStatistics(Vcb, MetaDataReads); #endif if (DeleteOnCloseSpecified) { // delete RootDir.... rather strange idea... I dislike it AdPrint((" Can't delete RootDir\n")); try_return(RC = STATUS_CANNOT_DELETE); } PtrNewFcb = Vcb->RootDirFCB; RC = UDFOpenFile(Vcb, PtrNewFileObject, PtrNewFcb); if(!NT_SUCCESS(RC)) try_return(RC); // DbgPrint("UDF: Open/Create RootDir : ReferenceCount %x\n",PtrNewFcb->ReferenceCount); UDFReferenceFile__(PtrNewFcb->FileInfo); PtrNewCcb = (PtrUDFCCB)(PtrNewFileObject->FsContext2); TreeLength = 1; RC = UDFCheckAccessRights(PtrNewFileObject, AccessState, PtrNewFcb, PtrNewCcb, DesiredAccess, ShareAccess); if(!NT_SUCCESS(RC)) { AdPrint((" Access/Sharing violation (RootDir)\n")); try_return(RC); } ReturnedInformation = FILE_OPENED; try_return(RC); } // end of OpenRootDir _SEH2_TRY { AdPrint((" Opening file %ws %8.8x\n",AbsolutePathName.Buffer, PtrNewFileObject)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { AdPrint((" Exception when printing FN\n")); } _SEH2_END; // **************** // Check if we have DuplicateHandle (or Reopen) request // **************** if(OpenExisting) { // BrutePoint(); // We don't handle OpenTargetDirectory in this case if(OpenTargetDirectory) try_return(RC = STATUS_INVALID_PARAMETER); // Init environment to simulate normal open procedure behavior /* if(!NT_SUCCESS(RC = MyInitUnicodeString(&LocalPath, L"")) || !NT_SUCCESS(RC = MyAppendUnicodeStringToStringTag(&LocalPath, &TargetObjectName, MEM_USLOC_TAG)))*/ ASSERT(TargetObjectName.Buffer); if(!NT_SUCCESS(RC = MyCloneUnicodeString(&LocalPath, &TargetObjectName))) try_return(RC); ASSERT(PtrRelatedFCB); RelatedFileInfo = PtrRelatedFCB->FileInfo; RC = STATUS_SUCCESS; NewFileInfo = LastGoodFileInfo = RelatedFileInfo; RelatedFileInfo = OldRelatedFileInfo = RelatedFileInfo->ParentFile; PtrRelatedFCB = PtrRelatedFCB->ParentFcb; // prevent releasing parent structures UDFAcquireParent(RelatedFileInfo, &Res1, &Res2); TreeLength++; if(Res1) UDFReleaseResource(Res1); if(Res2) UDFReleaseResource(Res2); UDF_CHECK_PAGING_IO_RESOURCE(RelatedFileInfo->Fcb->NTRequiredFCB); UDFAcquireResourceExclusive(Res2 = &(RelatedFileInfo->Fcb->NTRequiredFCB->MainResource),TRUE); PtrNewFcb = NewFileInfo->Fcb; UDF_CHECK_PAGING_IO_RESOURCE(PtrNewFcb->NTRequiredFCB); UDFAcquireResourceExclusive(Res1 = &(PtrNewFcb->NTRequiredFCB->MainResource),TRUE); UDFReferenceFile__(NewFileInfo); TreeLength++; goto AlreadyOpened; } if(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) { ReturnedInformation = 0; AdPrint((" Can't open File on blank volume ;)\n")); ReturnedInformation = FILE_DOES_NOT_EXIST; try_return(RC = STATUS_OBJECT_NAME_NOT_FOUND); } //AdPrint((" Opening file %ws %8.8x\n",AbsolutePathName.Buffer, PtrNewFileObject)); if(AbsolutePathName.Length > UDF_X_PATH_LEN*sizeof(WCHAR)) { try_return(RC = STATUS_OBJECT_NAME_INVALID); } // validate path specified // (sometimes we can see here very strange characters ;) if(!UDFIsNameValid(&AbsolutePathName, &StreamOpen, &SNameIndex)) { AdPrint((" Absolute path is not valid\n")); try_return(RC = STATUS_OBJECT_NAME_INVALID); } if(StreamOpen && !UDFStreamsSupported(Vcb)) { ReturnedInformation = FILE_DOES_NOT_EXIST; try_return(RC = STATUS_OBJECT_NAME_INVALID); } RC = MyInitUnicodeString(&LocalPath, L""); if(!NT_SUCCESS(RC)) try_return(RC); if (PtrRelatedFileObject) { // Our "start directory" is the one identified // by the related file object RelatedFileInfo = PtrRelatedFCB->FileInfo; if(RelatedFileInfo != Vcb->RootDirFCB->FileInfo) { RC = MyAppendUnicodeStringToStringTag(&LocalPath, &(PtrRelatedFCB->FCBName->ObjectName), MEM_USLOC_TAG); if(!NT_SUCCESS(RC)) try_return(RC); } if(TargetObjectName.Buffer != AbsolutePathName.Buffer) { ASSERT(TargetObjectName.Buffer); if(!NT_SUCCESS(RC = MyCloneUnicodeString(&TailName, &TargetObjectName))) { AdPrint((" Init String 'TargetObjectName' failed\n")); try_return(RC); } TailNameBuffer = TailName.Buffer; } else { TailName = AbsolutePathName; } } else { // Start at the root of the file system RelatedFileInfo = Vcb->RootDirFCB->FileInfo; TailName = AbsolutePathName; } if(StreamOpen) { StreamName = AbsolutePathName; StreamName.Buffer += SNameIndex; StreamName.Length -= (USHORT)SNameIndex*sizeof(WCHAR); // if StreamOpen specified & stream name starts with NULL character // we should create Stream Dir at first TailName.Length -= (AbsolutePathName.Length - (USHORT)SNameIndex*sizeof(WCHAR)); AbsolutePathName.Length = (USHORT)SNameIndex*sizeof(WCHAR); } CurName.MaximumLength = TailName.MaximumLength; RC = STATUS_SUCCESS; LastGoodName.Length = 0; LastGoodFileInfo = RelatedFileInfo; // reference RelatedObject to prevent releasing parent structures UDFAcquireParent(RelatedFileInfo, &Res1, &Res2); TreeLength++; // go into a loop parsing the supplied name // Note that we may have to "open" intermediate directory objects // while traversing the path. We should __try to reuse existing code // whenever possible therefore we should consider using a common // open routine regardless of whether the open is on behalf of the // caller or an intermediate (internal) open performed by the driver. // **************** // now we'll parse path to desired file // **************** while (TRUE) { // remember last 'good' ('good' means NO ERRORS before) path tail if(NT_SUCCESS(RC)) { LastGoodTail = TailName; while(LastGoodTail.Buffer[0] == L'\\') { LastGoodTail.Buffer++; LastGoodTail.Length -= sizeof(WCHAR); } } // get next path part... TmpBuffer = TailName.Buffer; TailName.Buffer = UDFDissectName(TailName.Buffer,&(CurName.Length) ); TailName.Length -= (USHORT)((ULONG_PTR)(TailName.Buffer) - (ULONG_PTR)TmpBuffer); CurName.Buffer = TailName.Buffer - CurName.Length; CurName.Length *= sizeof(WCHAR); CurName.MaximumLength = CurName.Length + sizeof(WCHAR); // check if we have already opened the component before last one // in this case OpenTargetDir request will be served in a special // way... if(OpenTargetDirectory && NT_SUCCESS(RC) && !TailName.Length) { // check if we should open SDir.. if(!StreamOpen || (TailName.Buffer[0]!=L':')) { // no, we should not. Continue with OpenTargetDir break; } } if( CurName.Length && (NT_SUCCESS(RC) || !StreamOpen)) { // ...wow! non-zero! try to open! if(!NT_SUCCESS(RC)) { AdPrint((" Error opening path component\n")); // we haven't reached last name part... hm.. // probably, the path specified is invalid.. // or we had a hard error... What else can we do ? // Only say ..CK OFF !!!! if(RC == STATUS_OBJECT_NAME_NOT_FOUND) RC = STATUS_OBJECT_PATH_NOT_FOUND; ReturnedInformation = FILE_DOES_NOT_EXIST; try_return(RC); } ASSERT_REF(RelatedFileInfo->Fcb->ReferenceCount >= RelatedFileInfo->RefCount); if(RelatedFileInfo && (TreeLength>1)) { // it was an internal Open operation. Thus, assume // RelatedFileInfo's Fcb to be valid RelatedFileInfo->Fcb->NTRequiredFCB->NtReqFCBFlags |= UDF_NTREQ_FCB_VALID; RelatedFileInfo->Fcb->FCBFlags |= UDF_FCB_VALID; } // check path fragment size if(CurName.Length > UDF_X_NAME_LEN * sizeof(WCHAR)) { AdPrint((" Path component is too long\n")); try_return(RC = STATUS_OBJECT_NAME_INVALID); } // ...and now release previously acquired objects, if(Res1) UDFReleaseResource(Res1); if(Res2) { UDFReleaseResource(Res2); Res2 = NULL; } // acquire new _parent_ directory & try to open what // we want. UDF_CHECK_PAGING_IO_RESOURCE(RelatedFileInfo->Fcb->NTRequiredFCB); UDFAcquireResourceExclusive(Res1 = &(RelatedFileInfo->Fcb->NTRequiredFCB->MainResource),TRUE); // check traverse rights RC = UDFCheckAccessRights(NULL, NULL, RelatedFileInfo->Fcb, PtrRelatedCCB, FILE_TRAVERSE, 0); if(!NT_SUCCESS(RC)) { NewFileInfo = NULL; AdPrint((" Traverse check failed\n")); goto Skip_open_attempt; } // check if we should open normal File/Dir or SDir if(CurName.Buffer[0] != ':') { // standard open, nothing interesting.... RC = UDFOpenFile__(Vcb, IgnoreCase,TRUE,&CurName, RelatedFileInfo,&NewFileInfo,NULL); if(RC == STATUS_FILE_DELETED) { // file has gone, but system still remembers it... NewFileInfo = NULL; AdPrint((" File deleted\n")); RC = STATUS_ACCESS_DENIED; #ifdef UDF_DBG } else if(RC == STATUS_NOT_A_DIRECTORY) { AdPrint((" Not a directory\n")); #endif // UDF_DBG } else if(RC == STATUS_SHARING_PAUSED) { AdPrint((" Dloc is being initialized\n")); BrutePoint(); RC = STATUS_SHARING_VIOLATION; } } else { // And here we should open Stream Dir (if any, of cource) RC = UDFOpenStreamDir__(Vcb, RelatedFileInfo, &NewFileInfo); if(NT_SUCCESS(RC)) { SuccessOpen_SDir: // this indicates that we needn't Stream Dir creation StreamExists = TRUE; StreamName.Buffer++; StreamName.Length-=sizeof(WCHAR); // update TailName TailName = StreamName; } else if(RC == STATUS_NOT_FOUND) { #ifndef UDF_READ_ONLY_BUILD // Stream Dir doesn't exist, but caller wants it to be // created. Lets try to help him... if((RequestedDisposition == FILE_CREATE) || (RequestedDisposition == FILE_OPEN_IF) || (RequestedDisposition == FILE_OVERWRITE_IF) || OpenTargetDirectory ) { RC = UDFCreateStreamDir__(Vcb, RelatedFileInfo, &NewFileInfo); if(NT_SUCCESS(RC)) goto SuccessOpen_SDir; } #endif //UDF_READ_ONLY_BUILD } /* } else { AdPrint((" File deleted (2)\n")); RC = STATUS_ACCESS_DENIED;*/ } #if 0 CollectStatistics(Vcb, MetaDataReads); #endif Skip_open_attempt: // check if we have successfully opened path component if(NT_SUCCESS(RC)) { // Yesss !!! if (!(PtrNewFcb = NewFileInfo->Fcb)) { // It is a first open operation // Allocate new FCB // Here we set FileObject pointer to NULL to avoid // new CCB allocation RC = UDFFirstOpenFile(Vcb, NULL, &PtrNewFcb, RelatedFileInfo, NewFileInfo, &LocalPath, &CurName); if(!NT_SUCCESS(RC)) { BrutePoint(); AdPrint((" Can't perform FirstOpen\n")); UDFCloseFile__(Vcb, NewFileInfo); if(PtrNewFcb) UDFCleanUpFCB(PtrNewFcb); PtrNewFcb = NULL; NewFileInfo->Fcb = NULL; if(UDFCleanUpFile__(Vcb, NewFileInfo)) { MyFreePool__(NewFileInfo); NewFileInfo = NULL; } try_return(RC); } } else { // It is not a first open operation // Validate Fcb. It is possible to get // not completly initialized Fcb here. if(!(PtrNewFcb->FCBFlags & UDF_FCB_VALID)) { BrutePoint(); AdPrint((" Fcb not valid\n")); UDFCloseFile__(Vcb, NewFileInfo); PtrNewFcb = NULL; if(UDFCleanUpFile__(Vcb, NewFileInfo)) { MyFreePool__(NewFileInfo); NewFileInfo = NULL; } try_return(RC = STATUS_ACCESS_DENIED); } } // Acquire newly opened File... Res2 = Res1; UDF_CHECK_PAGING_IO_RESOURCE(NewFileInfo->Fcb->NTRequiredFCB); UDFAcquireResourceExclusive(Res1 = &(NewFileInfo->Fcb->NTRequiredFCB->MainResource),TRUE); // ...and reference it UDFInterlockedIncrement((PLONG)&(PtrNewFcb->ReferenceCount)); UDFInterlockedIncrement((PLONG)&(PtrNewFcb->NTRequiredFCB->CommonRefCount)); ASSERT_REF(PtrNewFcb->ReferenceCount >= NewFileInfo->RefCount); // update unwind information LastGoodFileInfo = NewFileInfo; LastGoodName = CurName; TreeLength++; // update current path if(!StreamOpen || ((CurName.Buffer[0] != L':') && (!LocalPath.Length || (LocalPath.Buffer[LocalPath.Length/sizeof(WCHAR)-1] != L':'))) ) { // we should not insert '\' before or after ':' ASSERT(!LocalPath.Length || (LocalPath.Buffer[LocalPath.Length/2-1] != L'\\')); RC = MyAppendUnicodeToString(&LocalPath, L"\\"); if(!NT_SUCCESS(RC)) try_return(RC); } RC = MyAppendUnicodeStringToStringTag(&LocalPath, &CurName, MEM_USLOC_TAG); if(!NT_SUCCESS(RC)) try_return(RC); // DbgPrint("UDF: Open/Create File %ws : ReferenceCount %x\n",CurName.Buffer,PtrNewFcb->ReferenceCount); } else { AdPrint((" Can't open file\n")); // We have failed durring last Open attempt // Roll back to last good state PtrUDFNTRequiredFCB NtReqFcb = NULL; // Cleanup FileInfo if any if(NewFileInfo) { PtrNewFcb = NewFileInfo->Fcb; // acquire appropriate resource if possible if(PtrNewFcb && PtrNewFcb->NTRequiredFCB) { NtReqFcb = PtrNewFcb->NTRequiredFCB; Res2 = Res1; UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFAcquireResourceExclusive(Res1 = &(NtReqFcb->MainResource),TRUE); } // cleanup pointer to Fcb in FileInfo to allow // UDF_INFO package release FileInfo if there are // no more references if(PtrNewFcb && !PtrNewFcb->ReferenceCount && !PtrNewFcb->OpenHandleCount) { NewFileInfo->Fcb = NULL; } // cleanup pointer to CommonFcb in Dloc to allow // UDF_INFO package release Dloc if there are // no more references if(NewFileInfo->Dloc && !NewFileInfo->Dloc->LinkRefCount && (!PtrNewFcb || !PtrNewFcb->ReferenceCount)) { NewFileInfo->Dloc->CommonFcb = NULL; } // try to release FileInfo if(UDFCleanUpFile__(Vcb, NewFileInfo)) { ASSERT(!PtrNewFcb); if(PtrNewFcb) { BrutePoint(); UDFCleanUpFCB(PtrNewFcb); } MyFreePool__(NewFileInfo); } else { // if we can't release FileInfo // restore pointers to Fcb & CommonFcb in // FileInfo & Dloc NewFileInfo->Fcb = PtrNewFcb; if(NtReqFcb) NewFileInfo->Dloc->CommonFcb = NtReqFcb; } // forget about last FileInfo & Fcb, // further unwind staff needs only last good // structures PtrNewFcb = NULL; NewFileInfo = NULL; } } // should return error if 'delete in progress' if(LastGoodFileInfo->Fcb->FCBFlags & (UDF_FCB_DELETE_ON_CLOSE | UDF_FCB_DELETED | UDF_FCB_POSTED_RENAME)) { AdPrint((" Return DeletePending (no err)\n")); try_return(RC = STATUS_DELETE_PENDING); } // update last good state information... OldRelatedFileInfo = RelatedFileInfo; RelatedFileInfo = NewFileInfo; // ...and go to the next open cycle } else { // ************ if(StreamOpen && (RC == STATUS_NOT_FOUND)) // handle SDir return code RC = STATUS_OBJECT_NAME_NOT_FOUND; if(RC == STATUS_OBJECT_NAME_NOT_FOUND) { // good path, but no such file.... Amen // break open loop and continue with Create break; } if (!NT_SUCCESS(RC)) { // Hard error or damaged data structures ... #ifdef UDF_DBG if((RC != STATUS_OBJECT_PATH_NOT_FOUND) && (RC != STATUS_ACCESS_DENIED) && (RC != STATUS_NOT_A_DIRECTORY)) { AdPrint((" Hard error or damaged data structures\n")); } #endif // UDF_DBG // ... and exit with error try_return(RC); } // discard changes for last successfully opened file UDFInterlockedDecrement((PLONG)&(PtrNewFcb->ReferenceCount)); UDFInterlockedDecrement((PLONG)&(PtrNewFcb->NTRequiredFCB->CommonRefCount)); RC = STATUS_SUCCESS; ASSERT(!OpenTargetDirectory); // break open loop and continue with Open // (Create will be skipped) break; } } // end of while(TRUE) // **************** // If "open target directory" was specified // **************** if(OpenTargetDirectory) { if(!UDFIsADirectory(LastGoodFileInfo)) { AdPrint((" Not a directory (2)\n")); RC = STATUS_NOT_A_DIRECTORY; } if(!NT_SUCCESS(RC) || TailName.Length) { AdPrint((" Target name should not contain (back)slashes\n")); NewFileInfo = NULL; try_return(RC = STATUS_OBJECT_NAME_INVALID); } NewFileInfo = LastGoodFileInfo; RtlCopyUnicodeString(&(PtrNewFileObject->FileName), &CurName); // now we have to check if last component exists... if(NT_SUCCESS(RC = UDFFindFile__(Vcb, IgnoreCase, &CurName, RelatedFileInfo))) { // file exists, set this information in the Information field ReturnedInformation = FILE_EXISTS; AdPrint((" Open Target: FILE_EXISTS\n")); } else if(RC == STATUS_OBJECT_NAME_NOT_FOUND) { #ifdef UDF_DBG // check name. If there are '\\'s in TailName, some // directories in path specified do not exist for(TmpBuffer = LastGoodTail.Buffer; *TmpBuffer; TmpBuffer++) { if((*TmpBuffer) == L'\\') { ASSERT(FALSE); AdPrint((" Target name should not contain (back)slashes\n")); try_return(RC = STATUS_OBJECT_NAME_INVALID); } } #endif // UDF_DBG // Tell the I/O Manager that file does not exit ReturnedInformation = FILE_DOES_NOT_EXIST; AdPrint((" Open Target: FILE_DOES_NOT_EXIST\n")); RC = STATUS_SUCCESS; // is already set here } else { AdPrint((" Open Target: unexpected error\n")); NewFileInfo = NULL; ReturnedInformation = FILE_DOES_NOT_EXIST; try_return(RC = STATUS_OBJECT_NAME_INVALID); } // RC = STATUS_SUCCESS; // is already set here // Update the file object FsContext and FsContext2 fields // to reflect the fact that the parent directory of the // target has been opened PtrNewFcb = NewFileInfo->Fcb; UDFInterlockedDecrement((PLONG)&(PtrNewFcb->ReferenceCount)); UDFInterlockedDecrement((PLONG)&(PtrNewFcb->NTRequiredFCB->CommonRefCount)); RC = UDFOpenFile(Vcb, PtrNewFileObject, PtrNewFcb); ASSERT_REF(PtrNewFcb->ReferenceCount >= NewFileInfo->RefCount); if (!NT_SUCCESS(RC)) { AdPrint((" Can't perform OpenFile operation for target\n")); try_return(RC); } PtrNewCcb = (PtrUDFCCB)(PtrNewFileObject->FsContext2); ASSERT(Res1); RC = UDFCheckAccessRights(PtrNewFileObject, AccessState, PtrNewFcb, PtrNewCcb, DesiredAccess, ShareAccess); if(!NT_SUCCESS(RC)) { AdPrint((" Access/Share access check failed (Open Target)\n")); } try_return(RC); } // **************** // should we CREATE a new file ? // **************** if (!NT_SUCCESS(RC)) { if (RC == STATUS_OBJECT_NAME_NOT_FOUND || RC == STATUS_OBJECT_PATH_NOT_FOUND) { if( ((RequestedDisposition == FILE_OPEN) || (RequestedDisposition == FILE_OVERWRITE)) /*&& (!StreamOpen || !StreamExists)*/ ){ ReturnedInformation = FILE_DOES_NOT_EXIST; AdPrint((" File doesn't exist\n")); try_return(RC); } } else { // Any other operation return STATUS_ACCESS_DENIED. AdPrint((" Can't create due to unexpected error\n")); try_return(RC); } // Object was not found, create if requested if ((RequestedDisposition != FILE_CREATE) && (RequestedDisposition != FILE_OPEN_IF) && (RequestedDisposition != FILE_OVERWRITE_IF) && (RequestedDisposition != FILE_SUPERSEDE)) { AdPrint((" File doesn't exist (2)\n")); ReturnedInformation = FILE_DOES_NOT_EXIST; try_return(RC); } // Check Volume ReadOnly attr #ifndef UDF_READ_ONLY_BUILD if((Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY)) { #endif //UDF_READ_ONLY_BUILD ReturnedInformation = 0; AdPrint((" Write protected\n")); try_return(RC = STATUS_MEDIA_WRITE_PROTECTED); #ifndef UDF_READ_ONLY_BUILD } // Check r/o + delete on close if(DeleteOnCloseSpecified && (FileAttributes & FILE_ATTRIBUTE_READONLY)) { AdPrint((" Can't create r/o file marked for deletion\n")); try_return(RC = STATUS_CANNOT_DELETE); } // Create a new file/directory here ... if(StreamOpen) StreamName.Buffer[StreamName.Length/sizeof(WCHAR)] = 0; for(TmpBuffer = LastGoodTail.Buffer; *TmpBuffer; TmpBuffer++) { if((*TmpBuffer) == L'\\') { AdPrint((" Target name should not contain (back)slashes\n")); try_return(RC = STATUS_OBJECT_NAME_INVALID); } } if( DirectoryOnlyRequested && ((IrpSp->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_TEMPORARY) || StreamOpen || FALSE)) { AdPrint((" Creation of _temporary_ directory not permited\n")); try_return(RC = STATUS_INVALID_PARAMETER); } // check access rights ASSERT(Res1); RC = UDFCheckAccessRights(NULL, NULL, OldRelatedFileInfo->Fcb, PtrRelatedCCB, DirectoryOnlyRequested ? FILE_ADD_SUBDIRECTORY : FILE_ADD_FILE, 0); if(!NT_SUCCESS(RC)) { AdPrint((" Creation of File/Dir not permitted\n")); try_return(RC); } // Note that a FCB structure will be allocated at this time // and so will a CCB structure. Assume that these are called // PtrNewFcb and PtrNewCcb respectively. // Further, note that since the file is being created, no other // thread can have the file stream open at this time. RelatedFileInfo = OldRelatedFileInfo; RC = UDFCreateFile__(Vcb, IgnoreCase, &LastGoodTail, 0, 0, Vcb->UseExtendedFE || (StreamOpen && !StreamExists), (RequestedDisposition == FILE_CREATE), RelatedFileInfo, &NewFileInfo); if(!NT_SUCCESS(RC)) { AdPrint((" Creation error\n")); Creation_Err_1: if(NewFileInfo) { PtrNewFcb = NewFileInfo->Fcb; ASSERT(!PtrNewFcb); if(PtrNewFcb && !PtrNewFcb->ReferenceCount && !PtrNewFcb->OpenHandleCount) { NewFileInfo->Fcb = NULL; } if(NewFileInfo->Dloc && !NewFileInfo->Dloc->LinkRefCount) { NewFileInfo->Dloc->CommonFcb = NULL; } if(UDFCleanUpFile__(Vcb, NewFileInfo)) { if(PtrNewFcb) { BrutePoint(); UDFCleanUpFCB(PtrNewFcb); } MyFreePool__(NewFileInfo); PtrNewFcb = PtrNewFcb; } else { NewFileInfo->Fcb = PtrNewFcb; } PtrNewFcb = NULL; } try_return(RC); } // Update parent object if((Vcb->CompatFlags & UDF_VCB_IC_UPDATE_DIR_WRITE) && PtrRelatedFCB && PtrRelatedFileObject && (PtrRelatedFCB->FileInfo == NewFileInfo->ParentFile)) { PtrRelatedFileObject->Flags |= (FO_FILE_MODIFIED | FO_FILE_SIZE_CHANGED); } #if 0 CollectStatistics(Vcb, MetaDataWrites); #endif if(DirectoryOnlyRequested) { // user wants the directory to be created RC = UDFRecordDirectory__(Vcb, NewFileInfo); if(!NT_SUCCESS(RC)) { AdPrint((" Can't transform to directory\n")); Undo_Create_1: if((RC != STATUS_FILE_IS_A_DIRECTORY) && (RC != STATUS_NOT_A_DIRECTORY) && (RC != STATUS_ACCESS_DENIED)) { UDFFlushFile__(Vcb, NewFileInfo); UDFUnlinkFile__(Vcb, NewFileInfo, TRUE); } UDFCloseFile__(Vcb, NewFileInfo); BrutePoint(); goto Creation_Err_1; } #if 0 CollectStatistics(Vcb, MetaDataWrites); #endif } else if(AllocationSize) { // set initial file size /* if(!NT_SUCCESS(RC = UDFResizeFile__(Vcb, NewFileInfo, AllocationSize))) { AdPrint((" Can't set initial file size\n")); goto Undo_Create_1; } CollectStatistics(Vcb, MetaDataWrites);*/ } if(StreamOpen && !StreamExists) { // PHASE 0 // Open the newly created object (file) if (!(PtrNewFcb = NewFileInfo->Fcb)) { // It is a first open operation // Allocate new FCB // Here we set FileObject pointer to NULL to avoid // new CCB allocation RC = UDFFirstOpenFile(Vcb, NULL, &PtrNewFcb, RelatedFileInfo, NewFileInfo, &LocalPath, &LastGoodTail); if(!NT_SUCCESS(RC)) { AdPrint((" Can't perform FirstOpenFile operation for file to contain stream\n")); BrutePoint(); UDFCleanUpFCB(NewFileInfo->Fcb); NewFileInfo->Fcb = NULL; goto Creation_Err_1; } } else { BrutePoint(); } // Update unwind information TreeLength++; LastGoodFileInfo = NewFileInfo; // update FCB tree RC = MyAppendUnicodeToString(&LocalPath, L"\\"); if(!NT_SUCCESS(RC)) try_return(RC); RC = MyAppendUnicodeStringToStringTag(&LocalPath, &LastGoodTail, MEM_USLOC_TAG); if(!NT_SUCCESS(RC)) goto Creation_Err_1; UDFInterlockedIncrement((PLONG)&(PtrNewFcb->ReferenceCount)); UDFInterlockedIncrement((PLONG)&(PtrNewFcb->NTRequiredFCB->CommonRefCount)); ASSERT_REF(PtrNewFcb->ReferenceCount >= NewFileInfo->RefCount); PtrNewFcb->NTRequiredFCB->NtReqFCBFlags |= UDF_NTREQ_FCB_VALID; PtrNewFcb->FCBFlags |= UDF_FCB_VALID; UDFNotifyFullReportChange( Vcb, NewFileInfo, UDFIsADirectory(NewFileInfo) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED); // PHASE 1 // we need to create Stream Dir RelatedFileInfo = NewFileInfo; RC = UDFCreateStreamDir__(Vcb, RelatedFileInfo, &NewFileInfo); if(!NT_SUCCESS(RC)) { AdPrint((" Can't create SDir\n")); BrutePoint(); goto Creation_Err_1; } #if 0 CollectStatistics(Vcb, MetaDataWrites); #endif // normalize stream name StreamName.Buffer++; StreamName.Length-=sizeof(WCHAR); // Open the newly created object if (!(PtrNewFcb = NewFileInfo->Fcb)) { // It is a first open operation // Allocate new FCB // Here we set FileObject pointer to NULL to avoid // new CCB allocation RC = UDFFirstOpenFile(Vcb, NULL, &PtrNewFcb, RelatedFileInfo, NewFileInfo, &LocalPath, &(UDFGlobalData.UnicodeStrSDir)); } else { BrutePoint(); } if(!NT_SUCCESS(RC)) { AdPrint((" Can't perform OpenFile operation for SDir\n")); BrutePoint(); goto Creation_Err_1; } // Update unwind information TreeLength++; LastGoodFileInfo = NewFileInfo; // update FCB tree RC = MyAppendUnicodeStringToStringTag(&LocalPath, &(UDFGlobalData.UnicodeStrSDir), MEM_USLOC_TAG); if(!NT_SUCCESS(RC)) { AdPrint((" Can't append UNC str\n")); BrutePoint(); goto Creation_Err_1; } UDFInterlockedIncrement((PLONG)&(PtrNewFcb->ReferenceCount)); UDFInterlockedIncrement((PLONG)&(PtrNewFcb->NTRequiredFCB->CommonRefCount)); ASSERT_REF(PtrNewFcb->ReferenceCount >= NewFileInfo->RefCount); PtrNewFcb->NTRequiredFCB->NtReqFCBFlags |= UDF_NTREQ_FCB_VALID; PtrNewFcb->FCBFlags |= UDF_FCB_VALID; // PHASE 2 // create stream RelatedFileInfo = NewFileInfo; RC = UDFCreateFile__(Vcb, IgnoreCase, &StreamName, 0, 0, Vcb->UseExtendedFE, (RequestedDisposition == FILE_CREATE), RelatedFileInfo, &NewFileInfo); if(!NT_SUCCESS(RC)) { AdPrint((" Can't create Stream\n")); BrutePoint(); goto Creation_Err_1; } #if 0 CollectStatistics(Vcb, MetaDataWrites); #endif // Update unwind information LastGoodTail = StreamName; } // NT wants ARCHIVE bit to be set on Files if(!DirectoryOnlyRequested) FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; // Open the newly created object if (!(PtrNewFcb = NewFileInfo->Fcb)) { // It is a first open operation #ifndef IFS_40 // Set attributes for the file ... UDFAttributesToUDF(UDFDirIndex(UDFGetDirIndexByFileInfo(NewFileInfo),NewFileInfo->Index), NewFileInfo->Dloc->FileEntry, FileAttributes); #endif //IFS_40 // Allocate new FCB // Here we set FileObject pointer to NULL to avoid // new CCB allocation RC = UDFFirstOpenFile(Vcb, PtrNewFileObject, &PtrNewFcb, RelatedFileInfo, NewFileInfo, &LocalPath, &LastGoodTail); } else { BrutePoint(); } if(!NT_SUCCESS(RC)) { AdPrint((" Can't perform OpenFile operation for file or stream\n")); BrutePoint(); goto Undo_Create_1; } PtrNewFcb->NTRequiredFCB->CommonFCBHeader.FileSize.QuadPart = PtrNewFcb->NTRequiredFCB->CommonFCBHeader.ValidDataLength.QuadPart = 0; if(AllocationSize) { // inform NT about size changes PtrNewFcb->NTRequiredFCB->CommonFCBHeader.AllocationSize.QuadPart = AllocationSize; MmPrint((" CcIsFileCached()\n")); if(CcIsFileCached(PtrNewFileObject)) { MmPrint((" CcSetFileSizes()\n")); BrutePoint(); CcSetFileSizes(PtrNewFileObject, (PCC_FILE_SIZES)&(PtrNewFcb->NTRequiredFCB->CommonFCBHeader.AllocationSize)); PtrNewFcb->NTRequiredFCB->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED; } } // Update unwind information TreeLength++; LastGoodFileInfo = NewFileInfo; // Set the Share Access for the file stream. // The FCBShareAccess field will be set by the I/O Manager. PtrNewCcb = (PtrUDFCCB)(PtrNewFileObject->FsContext2); RC = UDFSetAccessRights(PtrNewFileObject, AccessState, PtrNewFcb, PtrNewCcb, DesiredAccess, ShareAccess); if(!NT_SUCCESS(RC)) { AdPrint((" Can't set Access Rights on Create\n")); BrutePoint(); UDFFlushFile__(Vcb, NewFileInfo); UDFUnlinkFile__(Vcb, NewFileInfo, TRUE); try_return(RC); } #ifdef IFS_40 // Set attributes for the file ... UDFAttributesToUDF(UDFDirIndex(UDFGetDirIndexByFileInfo(NewFileInfo),NewFileInfo->Index), NewFileInfo->Dloc->FileEntry, FileAttributes); // It is rather strange for me, but NT requires us to allow // Create operation for r/o + WriteAccess, but denies all // the rest operations in this case. Thus, we should update // r/o flag in Fcb _after_ Access check :-/ if(FileAttributes & FILE_ATTRIBUTE_READONLY) PtrNewFcb->FCBFlags |= UDF_FCB_READ_ONLY; #endif //IFS_40 // We call the notify package to report that the // we have added a stream. if(UDFIsAStream(NewFileInfo)) { UDFNotifyFullReportChange( Vcb, NewFileInfo, FILE_NOTIFY_CHANGE_STREAM_NAME, FILE_ACTION_ADDED_STREAM ); } else { UDFNotifyFullReportChange( Vcb, NewFileInfo, UDFIsADirectory(NewFileInfo) ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED); } /*#ifdef UDF_DBG { ULONG i; PDIR_INDEX_HDR hDirIndex = NewFileInfo->ParentFile->Dloc->DirIndex; for(i=0;DirIndex[i].FName.Buffer;i++) { AdPrint(("%ws\n", DirIndex[i].FName.Buffer)); } } #endif*/ ReturnedInformation = FILE_CREATED; try_return(RC); #endif //UDF_READ_ONLY_BUILD } AlreadyOpened: // **************** // we have always STATUS_SUCCESS here // **************** ASSERT(NewFileInfo != OldRelatedFileInfo); // A new CCB will be allocated. // Assume that this structure named PtrNewCcb RC = UDFOpenFile(Vcb, PtrNewFileObject, PtrNewFcb); if (!NT_SUCCESS(RC)) try_return(RC); PtrNewCcb = (PtrUDFCCB)(PtrNewFileObject->FsContext2); if(RequestedDisposition == FILE_CREATE) { ReturnedInformation = FILE_EXISTS; AdPrint((" Object name collision\n")); try_return(RC = STATUS_OBJECT_NAME_COLLISION); } NtReqFcb = PtrNewFcb->NTRequiredFCB; NtReqFcb->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(PtrNewFcb); // Check if caller wanted a directory only and target object // is not a directory, or caller wanted a file only and target // object is not a file ... if((PtrNewFcb->FCBFlags & UDF_FCB_DIRECTORY) && ((RequestedDisposition == FILE_SUPERSEDE) || (RequestedDisposition == FILE_OVERWRITE) || (RequestedDisposition == FILE_OVERWRITE_IF) || FileOnlyRequested)) { if(FileOnlyRequested) { AdPrint((" Can't open directory as a plain file\n")); } else { AdPrint((" Can't supersede directory\n")); } RC = STATUS_FILE_IS_A_DIRECTORY; try_return(RC); } if(DirectoryOnlyRequested && !(PtrNewFcb->FCBFlags & UDF_FCB_DIRECTORY)) { AdPrint((" This is not a directory\n")); RC = STATUS_NOT_A_DIRECTORY; try_return(RC); } if(DeleteOnCloseSpecified && (PtrNewFcb->FCBFlags & UDF_FCB_READ_ONLY)) { AdPrint((" Can't delete Read-Only file\n")); RC = STATUS_CANNOT_DELETE; try_return(RC); } // Check share access and fail if the share conflicts with an existing // open. ASSERT(Res1 != NULL); ASSERT(Res2 != NULL); RC = UDFCheckAccessRights(PtrNewFileObject, AccessState, PtrNewFcb, PtrNewCcb, DesiredAccess, ShareAccess); if(!NT_SUCCESS(RC)) { AdPrint((" Access/Share access check failed\n")); try_return(RC); } RestoreShareAccess = TRUE; if(FileOnlyRequested) { // If the user wants 'write access' access to the file make sure there // is not a process mapping this file as an image. Any attempt to // delete the file will be stopped in fileinfo.cpp // // If the user wants to delete on close, we must check at this // point though. if( (DesiredAccess & FILE_WRITE_DATA) || DeleteOnCloseSpecified ) { MmPrint((" MmFlushImageSection();\n")); NtReqFcb->AcqFlushCount++; if(!MmFlushImageSection( &(NtReqFcb->SectionObject), MmFlushForWrite )) { NtReqFcb->AcqFlushCount--; RC = DeleteOnCloseSpecified ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION; AdPrint((" File is mapped or deletion in progress\n")); try_return (RC); } NtReqFcb->AcqFlushCount--; } if( NoBufferingSpecified && /* (PtrNewFileObject->Flags & FO_NO_INTERMEDIATE_BUFFERING) &&*/ !(PtrNewFcb->CachedOpenHandleCount) && (NtReqFcb->SectionObject.DataSectionObject) ) { // If this is a non-cached open, and there are no open cached // handles, but there is still a data section, attempt a flush // and purge operation to avoid cache coherency overhead later. // We ignore any I/O errors from the flush. MmPrint((" CcFlushCache()\n")); CcFlushCache( &(NtReqFcb->SectionObject), NULL, 0, NULL ); MmPrint((" CcPurgeCacheSection()\n")); CcPurgeCacheSection( &(NtReqFcb->SectionObject), NULL, 0, FALSE ); } } if(DeleteOnCloseSpecified && UDFIsADirectory(NewFileInfo) && !UDFIsDirEmpty__(NewFileInfo)) { AdPrint((" Directory in not empry\n")); try_return (RC = STATUS_DIRECTORY_NOT_EMPTY); } // Get attributes for the file ... TmpFileAttributes = (USHORT)UDFAttributesToNT(UDFDirIndex(UDFGetDirIndexByFileInfo(NewFileInfo), NewFileInfo->Index), NewFileInfo->Dloc->FileEntry); if(DeleteOnCloseSpecified && (TmpFileAttributes & FILE_ATTRIBUTE_READONLY)) { ASSERT(Res1 != NULL); ASSERT(Res2 != NULL); RC = UDFCheckAccessRights(NULL, NULL, OldRelatedFileInfo->Fcb, PtrRelatedCCB, FILE_DELETE_CHILD, 0); if(!NT_SUCCESS(RC)) { AdPrint((" Read-only. DeleteOnClose attempt failed\n")); try_return (RC = STATUS_CANNOT_DELETE); } } // If a supersede or overwrite was requested, do so now ... if((RequestedDisposition == FILE_SUPERSEDE) || (RequestedDisposition == FILE_OVERWRITE) || (RequestedDisposition == FILE_OVERWRITE_IF)) { // Attempt the operation here ... #ifndef UDF_READ_ONLY_BUILD ASSERT(!UDFIsADirectory(NewFileInfo)); if(RequestedDisposition == FILE_SUPERSEDE) { BOOLEAN RestoreRO = FALSE; ASSERT(Res1 != NULL); ASSERT(Res2 != NULL); // NT wants us to allow Supersede on RO files if(PtrNewFcb->FCBFlags & UDF_FCB_READ_ONLY) { // Imagine, that file is not RO and check other permissions RestoreRO = TRUE; PtrNewFcb->FCBFlags &= ~UDF_FCB_READ_ONLY; } RC = UDFCheckAccessRights(NULL, NULL, PtrNewFcb, PtrNewCcb, DELETE, 0); if(RestoreRO) { // Restore RO state if changed PtrNewFcb->FCBFlags |= UDF_FCB_READ_ONLY; } if(!NT_SUCCESS(RC)) { AdPrint((" Can't supersede. DELETE permission required\n")); try_return (RC); } } else { ASSERT(Res1 != NULL); ASSERT(Res2 != NULL); RC = UDFCheckAccessRights(NULL, NULL, PtrNewFcb, PtrNewCcb, FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES, 0); if(!NT_SUCCESS(RC)) { AdPrint((" Can't overwrite. Permission denied\n")); try_return (RC); } } // Existing & requested System and Hidden bits must match if( (TmpFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) & (FileAttributes ^ (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) ) { AdPrint((" The Hidden and/or System bits do not match\n")); try_return(RC = STATUS_ACCESS_DENIED); } // Before we actually truncate, check to see if the purge // is going to fail. MmPrint((" MmCanFileBeTruncated()\n")); if (!MmCanFileBeTruncated( &NtReqFcb->SectionObject, &(UDFGlobalData.UDFLargeZero) )) { AdPrint((" Can't truncate. File is mapped\n")); try_return(RC = STATUS_USER_MAPPED_FILE); } ASSERT(Res1 != NULL); ASSERT(Res2 != NULL); #if 0 CollectStatistics(Vcb, MetaDataWrites); #endif // Synchronize with PagingIo UDFAcquireResourceExclusive(PagingIoRes = &(NtReqFcb->PagingIoResource),TRUE); // Set file sizes if(!NT_SUCCESS(RC = UDFResizeFile__(Vcb, NewFileInfo, 0))) { AdPrint((" Error during resize operation\n")); try_return(RC); } /* if(AllocationSize) { if(!NT_SUCCESS(RC = UDFResizeFile__(Vcb, NewFileInfo, AllocationSize))) { AdPrint((" Error during resize operation (2)\n")); try_return(RC); } }*/ NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart = UDFSysGetAllocSize(Vcb, AllocationSize); NtReqFcb->CommonFCBHeader.FileSize.QuadPart = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = 0 /*AllocationSize*/; PtrNewFcb->FCBFlags &= ~UDF_FCB_DELAY_CLOSE; MmPrint((" CcSetFileSizes()\n")); CcSetFileSizes(PtrNewFileObject, (PCC_FILE_SIZES)&(NtReqFcb->CommonFCBHeader.AllocationSize)); NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED; // Release PagingIoResource UDFReleaseResource(PagingIoRes); PagingIoRes = NULL; if(NT_SUCCESS(RC)) { FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; if (RequestedDisposition == FILE_SUPERSEDE) { // Set attributes for the file ... UDFAttributesToUDF(UDFDirIndex(UDFGetDirIndexByFileInfo(NewFileInfo), NewFileInfo->Index), NewFileInfo->Dloc->FileEntry, FileAttributes); ReturnedInformation = FILE_SUPERSEDED; } else { // Get attributes for the file ... FileAttributes |= TmpFileAttributes; // Set attributes for the file ... UDFAttributesToUDF(UDFDirIndex(UDFGetDirIndexByFileInfo(NewFileInfo), NewFileInfo->Index), NewFileInfo->Dloc->FileEntry, FileAttributes); ReturnedInformation = FILE_OVERWRITTEN; } } // notify changes UDFNotifyFullReportChange( Vcb, NewFileInfo, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED); // Update parent object if((Vcb->CompatFlags & UDF_VCB_IC_UPDATE_DIR_WRITE) && PtrRelatedFCB && PtrRelatedFileObject && (PtrRelatedFCB->FileInfo == NewFileInfo->ParentFile)) { PtrRelatedFileObject->Flags |= (FO_FILE_MODIFIED | FO_FILE_SIZE_CHANGED); } #else //UDF_READ_ONLY_BUILD try_return(RC = STATUS_ACCESS_DENIED); #endif //UDF_READ_ONLY_BUILD } else { ReturnedInformation = FILE_OPENED; } // Update parent object if((Vcb->CompatFlags & UDF_VCB_IC_UPDATE_DIR_READ) && PtrRelatedFCB && PtrRelatedFileObject && (PtrRelatedFCB->FileInfo == NewFileInfo->ParentFile)) { PtrRelatedFileObject->Flags |= FO_FILE_FAST_IO_READ; } try_exit: NOTHING; } _SEH2_FINALLY { // Complete the request unless we are here as part of unwinding // when an exception condition was encountered, OR // if the request has been deferred (i.e. posted for later handling) if(RestoreVCBOpenCounter) { UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount)); RestoreVCBOpenCounter = FALSE; } if (RC != STATUS_PENDING) { // If any intermediate (directory) open operations were performed, // implement the corresponding close (do *not* however close // the target we have opened on behalf of the caller ...). #if 0 if(NT_SUCCESS(RC)) { CollectStatistics2(Vcb, SuccessfulCreates); } else { CollectStatistics2(Vcb, FailedCreates); } #endif if (NT_SUCCESS(RC) && PtrNewFcb) { // Update the file object such that: // (a) the FsContext field points to the NTRequiredFCB field // in the FCB // (b) the FsContext2 field points to the CCB created as a // result of the open operation // If write-through was requested, then mark the file object // appropriately // directories are not cached // so we should prevent flush attepmts on cleanup if(!(PtrNewFcb->FCBFlags & UDF_FCB_DIRECTORY)) { #ifndef UDF_READ_ONLY_BUILD if(WriteThroughRequested) { PtrNewFileObject->Flags |= FO_WRITE_THROUGH; PtrNewFcb->FCBFlags |= UDF_FCB_WRITE_THROUGH; MmPrint((" FO_WRITE_THROUGH\n")); } #endif //UDF_READ_ONLY_BUILD if(SequentialIoRequested && !(Vcb->CompatFlags & UDF_VCB_IC_IGNORE_SEQUENTIAL_IO)) { PtrNewFileObject->Flags |= FO_SEQUENTIAL_ONLY; MmPrint((" FO_SEQUENTIAL_ONLY\n")); #ifndef UDF_READ_ONLY_BUILD if(Vcb->TargetDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) { PtrNewFileObject->Flags &= ~FO_WRITE_THROUGH; PtrNewFcb->FCBFlags &= ~UDF_FCB_WRITE_THROUGH; MmPrint((" FILE_REMOVABLE_MEDIA + FO_SEQUENTIAL_ONLY => ~FO_WRITE_THROUGH\n")); } #endif //UDF_READ_ONLY_BUILD if(PtrNewFcb->FileInfo) { UDFSetFileAllocMode__(PtrNewFcb->FileInfo, EXTENT_FLAG_ALLOC_SEQUENTIAL); } } if(NoBufferingSpecified) { PtrNewFileObject->Flags |= FO_NO_INTERMEDIATE_BUFFERING; MmPrint((" FO_NO_INTERMEDIATE_BUFFERING\n")); } else { PtrNewFileObject->Flags |= FO_CACHE_SUPPORTED; MmPrint((" FO_CACHE_SUPPORTED\n")); } } if((DesiredAccess & FILE_EXECUTE) /*&& !(PtrNewFcb->FCBFlags & UDF_FCB_DIRECTORY)*/) { MmPrint((" FO_FILE_FAST_IO_READ\n")); PtrNewFileObject->Flags |= FO_FILE_FAST_IO_READ; } // All right. Now we can safely increment OpenHandleCount UDFInterlockedIncrement((PLONG)&(Vcb->VCBHandleCount)); UDFInterlockedIncrement((PLONG)&(PtrNewFcb->OpenHandleCount)); if(PtrNewFileObject->Flags & FO_CACHE_SUPPORTED) UDFInterlockedIncrement((PLONG)&(PtrNewFcb->CachedOpenHandleCount)); // Store some flags in CCB if(PtrNewCcb) { PtrNewCcb->TreeLength = TreeLength; // delete on close #ifndef UDF_READ_ONLY_BUILD if(DeleteOnCloseSpecified) { ASSERT(!(PtrNewFcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)); PtrNewCcb->CCBFlags |= UDF_CCB_DELETE_ON_CLOSE; } #endif //UDF_READ_ONLY_BUILD // case sensetivity if(!IgnoreCase) { // remember this for possible Rename/Move operation PtrNewCcb->CCBFlags |= UDF_CCB_CASE_SENSETIVE; PtrNewFileObject->Flags |= FO_OPENED_CASE_SENSITIVE; } if(IsFileObjectReadOnly(PtrNewFileObject)) { UDFInterlockedIncrement((PLONG)&(Vcb->VCBOpenCountRO)); PtrNewCcb->CCBFlags |= UDF_CCB_READ_ONLY; } } else { BrutePoint(); } // it was a stream... if(StreamOpen) PtrNewFileObject->Flags |= FO_STREAM_FILE; // PtrNewCcb->CCBFlags |= UDF_CCB_VALID; // increment the number of outstanding open operations on this // logical volume (i.e. volume cannot be dismounted) UDFInterlockedIncrement((PLONG)&(Vcb->VCBOpenCount)); PtrNewFcb->NTRequiredFCB->NtReqFCBFlags |= UDF_NTREQ_FCB_VALID; PtrNewFcb->FCBFlags |= UDF_FCB_VALID; #ifdef UDF_DBG // We have no FileInfo for Volume if(PtrNewFcb->FileInfo) { ASSERT_REF(PtrNewFcb->ReferenceCount >= PtrNewFcb->FileInfo->RefCount); } #endif // UDF_DBG AdPrint((" FCB %x, CCB %x, FO %x, Flags %x\n", PtrNewFcb, PtrNewCcb, PtrNewFileObject, PtrNewFcb->FCBFlags)); UDFReleaseResFromCreate(&PagingIoRes, &Res1, &Res2); } else if(!NT_SUCCESS(RC)) { // Perform failure related post-processing now if(RestoreShareAccess && NtReqFcb && PtrNewFileObject) { IoRemoveShareAccess(PtrNewFileObject, &(NtReqFcb->FCBShareAccess)); } UDFCleanUpCCB(PtrNewCcb); if(PtrNewFileObject) { PtrNewFileObject->FsContext2 = NULL; } // We have successfully opened LastGoodFileInfo, // so mark it as VALID to avoid future troubles... if(LastGoodFileInfo && LastGoodFileInfo->Fcb) { LastGoodFileInfo->Fcb->FCBFlags |= UDF_FCB_VALID; if(LastGoodFileInfo->Fcb->NTRequiredFCB) { LastGoodFileInfo->Fcb->NTRequiredFCB->NtReqFCBFlags |= UDF_NTREQ_FCB_VALID; } } // Release resources... UDFReleaseResFromCreate(&PagingIoRes, &Res1, &Res2); ASSERT(AcquiredVcb); // close the chain UDFCloseFileInfoChain(Vcb, LastGoodFileInfo, TreeLength, TRUE); // cleanup FCBs (if any) if( Vcb && (PtrNewFcb != Vcb->RootDirFCB) && LastGoodFileInfo ) { UDFCleanUpFcbChain(Vcb, LastGoodFileInfo, TreeLength, TRUE); } else { ASSERT(!LastGoodFileInfo); } } else { UDFReleaseResFromCreate(&PagingIoRes, &Res1, &Res2); } // As long as this unwinding is not being performed as a result of // an exception condition, complete the IRP ... if (!_SEH2_AbnormalTermination()) { Irp->IoStatus.Status = RC; Irp->IoStatus.Information = ReturnedInformation; // complete the IRP IoCompleteRequest(Irp, IO_DISK_INCREMENT); // Free up the Irp Context UDFReleaseIrpContext(PtrIrpContext); } } else { UDFReleaseResFromCreate(&PagingIoRes, &Res1, &Res2); } if(AcquiredVcb) { UDFReleaseResource(&(Vcb->VCBResource)); } // free allocated tmp buffers (if any) if(AbsolutePathName.Buffer) MyFreePool__(AbsolutePathName.Buffer); if(LocalPath.Buffer) MyFreePool__(LocalPath.Buffer); if(TailNameBuffer) MyFreePool__(TailNameBuffer); } _SEH2_END; return(RC); } // end UDFCommonCreate() /************************************************************************* * * Function: UDFFirstOpenFile() * * Description: * Perform first Open/Create initialization. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: STATUS_SUCCESS/Error * *************************************************************************/ NTSTATUS UDFFirstOpenFile( IN PVCB Vcb, // volume control block IN PFILE_OBJECT PtrNewFileObject, // I/O Mgr. created file object OUT PtrUDFFCB* PtrNewFcb, IN PUDF_FILE_INFO RelatedFileInfo, IN PUDF_FILE_INFO NewFileInfo, IN PUNICODE_STRING LocalPath, IN PUNICODE_STRING CurName ) { // DIR_INDEX NewFileIndex; PtrUDFObjectName NewFCBName; PtrUDFNTRequiredFCB NtReqFcb; NTSTATUS RC; BOOLEAN Linked = TRUE; PDIR_INDEX_HDR hDirIndex; PDIR_INDEX_ITEM DirIndex; AdPrint(("UDFFirstOpenFile\n")); if(!((*PtrNewFcb) = UDFAllocateFCB())) { AdPrint(("Can't allocate FCB\n")); return STATUS_INSUFFICIENT_RESOURCES; } // Allocate and set new FCB unique name (equal to absolute path name) if(!(NewFCBName = UDFAllocateObjectName())) return STATUS_INSUFFICIENT_RESOURCES; if(RelatedFileInfo && RelatedFileInfo->Fcb && !(RelatedFileInfo->Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)) { RC = MyCloneUnicodeString(&(NewFCBName->ObjectName), &(RelatedFileInfo->Fcb->FCBName->ObjectName)); } else { RC = MyInitUnicodeString(&(NewFCBName->ObjectName), L""); } if(!NT_SUCCESS(RC)) return STATUS_INSUFFICIENT_RESOURCES; if( (CurName->Buffer[0] != L':') && (!LocalPath->Length || ((LocalPath->Buffer[LocalPath->Length/sizeof(WCHAR)-1] != L':') /*&& (LocalPath->Buffer[LocalPath->Length/sizeof(WCHAR)-1] != L'\\')*/) )) { RC = MyAppendUnicodeToString(&(NewFCBName->ObjectName), L"\\"); if(!NT_SUCCESS(RC)) { UDFReleaseObjectName(NewFCBName); return STATUS_INSUFFICIENT_RESOURCES; } } // Make link between Fcb and FileInfo (*PtrNewFcb)->FileInfo = NewFileInfo; NewFileInfo->Fcb = (*PtrNewFcb); (*PtrNewFcb)->ParentFcb = RelatedFileInfo->Fcb; if(!((*PtrNewFcb)->NTRequiredFCB = NewFileInfo->Dloc->CommonFcb)) { (*PtrNewFcb)->NTRequiredFCB = (PtrUDFNTRequiredFCB)MyAllocatePool__(NonPagedPool, UDFQuadAlign(sizeof(UDFNTRequiredFCB))); if(!((*PtrNewFcb)->NTRequiredFCB)) { UDFReleaseObjectName(NewFCBName); return STATUS_INSUFFICIENT_RESOURCES; } UDFPrint(("UDFAllocateNtReqFCB: %x\n", (*PtrNewFcb)->NTRequiredFCB)); RtlZeroMemory((*PtrNewFcb)->NTRequiredFCB, UDFQuadAlign(sizeof(UDFNTRequiredFCB))); (*PtrNewFcb)->FileInfo->Dloc->CommonFcb = (*PtrNewFcb)->NTRequiredFCB; Linked = FALSE; } else { if(!(NewFileInfo->Dloc->CommonFcb->NtReqFCBFlags & UDF_NTREQ_FCB_VALID)) { (*PtrNewFcb)->NTRequiredFCB = NULL; BrutePoint(); UDFReleaseObjectName(NewFCBName); return STATUS_ACCESS_DENIED; } } NtReqFcb = (*PtrNewFcb)->NTRequiredFCB; // Set times if(!Linked) { UDFGetFileXTime((*PtrNewFcb)->FileInfo, &(NtReqFcb->CreationTime.QuadPart), &(NtReqFcb->LastAccessTime.QuadPart), &(NtReqFcb->ChangeTime.QuadPart), &(NtReqFcb->LastWriteTime.QuadPart) ); // Set the allocation size for the object is specified NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart = UDFSysGetAllocSize(Vcb, NewFileInfo->Dloc->DataLoc.Length); // NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart = UDFGetFileAllocationSize(Vcb, NewFileInfo); NtReqFcb->CommonFCBHeader.FileSize.QuadPart = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = NewFileInfo->Dloc->DataLoc.Length; } // begin transaction UDFAcquireResourceExclusive(&(Vcb->FcbListResource), TRUE); RC = UDFInitializeFCB(*PtrNewFcb, Vcb, NewFCBName, UDFIsADirectory(NewFileInfo) ? UDF_FCB_DIRECTORY : 0, PtrNewFileObject); if(!NT_SUCCESS(RC)) { if(!Linked) { MyFreePool__((*PtrNewFcb)->NTRequiredFCB); (*PtrNewFcb)->NTRequiredFCB = NULL; } UDFReleaseResource(&(Vcb->FcbListResource)); return RC; } // set Read-only attribute if(!UDFIsAStreamDir(NewFileInfo)) { hDirIndex = UDFGetDirIndexByFileInfo(NewFileInfo); #ifdef UDF_DBG if(!hDirIndex) { BrutePoint(); } else { #endif // UDF_DBG if(UDFAttributesToNT(DirIndex = UDFDirIndex(hDirIndex, NewFileInfo->Index),NULL) & FILE_ATTRIBUTE_READONLY) { (*PtrNewFcb)->FCBFlags |= UDF_FCB_READ_ONLY; } MyAppendUnicodeStringToStringTag(&(NewFCBName->ObjectName), &(DirIndex->FName), MEM_USOBJ_TAG); #ifdef UDF_DBG } #endif // UDF_DBG } else if (RelatedFileInfo->ParentFile) { hDirIndex = UDFGetDirIndexByFileInfo(RelatedFileInfo); if(UDFAttributesToNT(DirIndex = UDFDirIndex(hDirIndex, RelatedFileInfo->Index),NULL) & FILE_ATTRIBUTE_READONLY) { (*PtrNewFcb)->FCBFlags |= UDF_FCB_READ_ONLY; } RC = MyAppendUnicodeStringToStringTag(&(NewFCBName->ObjectName), CurName, MEM_USOBJ_TAG); // } else { // BrutePoint(); } // do not allocate CCB if it is internal Create/Open if(NT_SUCCESS(RC)) { if(PtrNewFileObject) { RC = UDFOpenFile(Vcb, PtrNewFileObject, *PtrNewFcb); } else { RC = STATUS_SUCCESS; } } UDFReleaseResource(&(Vcb->FcbListResource)); // end transaction // if(!NT_SUCCESS(RC)) return RC; return RC; } // end UDFFirstOpenFile() /************************************************************************* * * Function: UDFOpenFile() * * Description: * Open a file/dir for the caller. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: STATUS_SUCCESS/Error * *************************************************************************/ NTSTATUS UDFOpenFile( PVCB Vcb, // volume control block PFILE_OBJECT PtrNewFileObject, // I/O Mgr. created file object PtrUDFFCB PtrNewFcb ) { NTSTATUS RC = STATUS_SUCCESS; PtrUDFCCB Ccb = NULL; PtrUDFNTRequiredFCB NtReqFcb; AdPrint(("UDFOpenFile\n")); ASSERT((PtrNewFcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_FCB) ||(PtrNewFcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB)); _SEH2_TRY { #if 0 CollectStatistics2(Vcb, CreateHits); #endif // create a new CCB structure if (!(Ccb = UDFAllocateCCB())) { AdPrint(("Can't allocate CCB\n")); PtrNewFileObject->FsContext2 = NULL; // UDFInterlockedIncrement((PLONG)&(PtrNewFcb->ReferenceCount)); UDFInterlockedIncrement((PLONG)&(PtrNewFcb->NTRequiredFCB->CommonRefCount)); RC = STATUS_INSUFFICIENT_RESOURCES; try_return(RC); } // initialize the CCB Ccb->Fcb = PtrNewFcb; // initialize the CCB to point to the file object Ccb->FileObject = PtrNewFileObject; // initialize the file object appropriately PtrNewFileObject->FsContext2 = (PVOID)(Ccb); PtrNewFileObject->Vpb = Vcb->Vpb; PtrNewFileObject->FsContext = (PVOID)(NtReqFcb = PtrNewFcb->NTRequiredFCB); PtrNewFileObject->SectionObjectPointer = &(NtReqFcb->SectionObject); #ifdef DBG // NtReqFcb ->FileObject = PtrNewFileObject; #endif //DBG #ifdef UDF_DELAYED_CLOSE PtrNewFcb->FCBFlags &= ~UDF_FCB_DELAY_CLOSE; #endif //UDF_DELAYED_CLOSE UDFAcquireResourceExclusive(&(PtrNewFcb->CcbListResource),TRUE); // insert CCB into linked list of open file object to Fcb or // to Vcb and do other intialization InsertTailList(&(PtrNewFcb->NextCCB), &(Ccb->NextCCB)); UDFInterlockedIncrement((PLONG)&(PtrNewFcb->ReferenceCount)); UDFInterlockedIncrement((PLONG)&(PtrNewFcb->NTRequiredFCB->CommonRefCount)); UDFReleaseResource(&(PtrNewFcb->CcbListResource)); try_exit: NOTHING; } _SEH2_FINALLY { NOTHING; } _SEH2_END; return(RC); } // end UDFOpenFile() /************************************************************************* * * Function: UDFInitializeFCB() * * Description: * Initialize a new FCB structure and also the sent-in file object * (if supplied) * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: None * *************************************************************************/ NTSTATUS UDFInitializeFCB( IN PtrUDFFCB PtrNewFcb, // FCB structure to be initialized IN PVCB Vcb, // logical volume (VCB) pointer IN PtrUDFObjectName PtrObjectName, // name of the object IN ULONG Flags, // is this a file/directory, etc. IN PFILE_OBJECT FileObject) // optional file object to be initialized { AdPrint(("UDFInitializeFCB\n")); NTSTATUS status; BOOLEAN Linked = TRUE; if(!PtrNewFcb->NTRequiredFCB->CommonFCBHeader.Resource) { // record signature PtrNewFcb->NTRequiredFCB->CommonFCBHeader.NodeTypeCode = UDF_NODE_TYPE_NT_REQ_FCB; PtrNewFcb->NTRequiredFCB->CommonFCBHeader.NodeByteSize = sizeof(UDFNTRequiredFCB); // Initialize the ERESOURCE objects if(!NT_SUCCESS(status = UDFInitializeResourceLite(&(PtrNewFcb->NTRequiredFCB->MainResource)))) { AdPrint((" Can't init resource\n")); return status; } if(!NT_SUCCESS(status = UDFInitializeResourceLite(&(PtrNewFcb->NTRequiredFCB->PagingIoResource)))) { AdPrint((" Can't init resource (2)\n")); UDFDeleteResource(&(PtrNewFcb->NTRequiredFCB->MainResource)); return status; } // Fill NT required Fcb part PtrNewFcb->NTRequiredFCB->CommonFCBHeader.Resource = &(PtrNewFcb->NTRequiredFCB->MainResource); PtrNewFcb->NTRequiredFCB->CommonFCBHeader.PagingIoResource = &(PtrNewFcb->NTRequiredFCB->PagingIoResource); // Itialize byte-range locks support structure FsRtlInitializeFileLock(&(PtrNewFcb->NTRequiredFCB->FileLock),NULL,NULL); // Init reference counter PtrNewFcb->NTRequiredFCB->CommonRefCount = 0; Linked = FALSE; } else { ASSERT(PtrNewFcb->NTRequiredFCB->CommonFCBHeader.NodeTypeCode == UDF_NODE_TYPE_NT_REQ_FCB); } if(!NT_SUCCESS(status = UDFInitializeResourceLite(&(PtrNewFcb->CcbListResource)))) { AdPrint((" Can't init resource (3)\n")); BrutePoint(); if(!Linked) { UDFDeleteResource(&(PtrNewFcb->NTRequiredFCB->PagingIoResource)); UDFDeleteResource(&(PtrNewFcb->NTRequiredFCB->MainResource)); PtrNewFcb->NTRequiredFCB->CommonFCBHeader.Resource = PtrNewFcb->NTRequiredFCB->CommonFCBHeader.PagingIoResource = NULL; FsRtlUninitializeFileLock(&(PtrNewFcb->NTRequiredFCB->FileLock)); } return status; } // caller MUST ensure that VCB has been acquired exclusively InsertTailList(&(Vcb->NextFCB), &(PtrNewFcb->NextFCB)); // initialize the various list heads InitializeListHead(&(PtrNewFcb->NextCCB)); PtrNewFcb->ReferenceCount = 0; PtrNewFcb->OpenHandleCount = 0; PtrNewFcb->FCBFlags = Flags | UDF_FCB_INITIALIZED_CCB_LIST_RESOURCE; PtrNewFcb->FCBName = PtrObjectName; PtrNewFcb->Vcb = Vcb; return STATUS_SUCCESS; } // end UDFInitializeFCB()