/* Routine Description: This routines performs an IoVerifyVolume operation and takes the appropriate action. If the verify is successful then we send the originating Irp off to an Ex Worker Thread. This routine is called from the exception handler. No file system resources are held when this routine is called. Arguments: Irp - The irp to send off after all is well and done. Device - The real device needing verification. */ NTSTATUS UDFPerformVerify( IN PtrUDFIrpContext IrpContext, IN PIRP Irp, IN PDEVICE_OBJECT DeviceToVerify ) { PVCB Vcb; NTSTATUS RC = STATUS_SUCCESS; PIO_STACK_LOCATION IrpSp; KdPrint(("UDFPerformVerify:\n")); if(!IrpContext) return STATUS_INVALID_PARAMETER; if(!Irp) return STATUS_INVALID_PARAMETER; // Check if this Irp has a status of Verify required and if it does // then call the I/O system to do a verify. // // Skip the IoVerifyVolume if this is a mount or verify request // itself. Trying a recursive mount will cause a deadlock with // the DeviceObject->DeviceLock. if ((IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) && ((IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME) || (IrpContext->MinorFunction == IRP_MN_VERIFY_VOLUME))) { return UDFPostRequest(IrpContext, Irp); } // Extract a pointer to the Vcb from the VolumeDeviceObject. // Note that since we have specifically excluded mount, // requests, we know that IrpSp->DeviceObject is indeed a // volume device object. IrpSp = IoGetCurrentIrpStackLocation(Irp); Vcb = (PVCB)IrpSp->DeviceObject->DeviceExtension; KdPrint(("UDFPerformVerify: check\n")); // Check if the volume still thinks it needs to be verified, // if it doesn't then we can skip doing a verify because someone // else beat us to it. _SEH2_TRY { if (DeviceToVerify->Flags & DO_VERIFY_VOLUME) { // If the IopMount in IoVerifyVolume did something, and // this is an absolute open, force a reparse. RC = IoVerifyVolume( DeviceToVerify, FALSE ); // Bug? /* if (UDFIsRawDevice(RC)) { RC = STATUS_WRONG_VOLUME; }*/ // If the verify operation completed it will return // either STATUS_SUCCESS or STATUS_WRONG_VOLUME, exactly. if (RC == STATUS_SUCCESS) { IrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_EXCEPTION; } // If UDFVerifyVolume encountered an error during // processing, it will return that error. If we got // STATUS_WRONG_VOLUME from the verify, and our volume // is now mounted, commute the status to STATUS_SUCCESS. if ((RC == STATUS_WRONG_VOLUME) && (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) { RC = STATUS_SUCCESS; } // Do a quick unprotected check here. The routine will do // a safe check. After here we can release the resource. // Note that if the volume really went away, we will be taking // the Reparse path. // If the device might need to go away then call our dismount routine. if ( (!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) || (Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED)) && (Vcb->VCBOpenCount <= UDF_RESIDUAL_REFERENCE) ) { KdPrint(("UDFPerformVerify: UDFCheckForDismount\n")); UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE); UDFCheckForDismount( IrpContext, Vcb, FALSE ); UDFReleaseResource(&(UDFGlobalData.GlobalDataResource)); } // If this is a create and the verify succeeded then complete the // request with a REPARSE status. if ((IrpContext->MajorFunction == IRP_MJ_CREATE) && (IrpSp->FileObject->RelatedFileObject == NULL) && ((RC == STATUS_SUCCESS) || (RC == STATUS_WRONG_VOLUME)) ) { KdPrint(("UDFPerformVerify: IO_REMOUNT\n")); Irp->IoStatus.Information = IO_REMOUNT; Irp->IoStatus.Status = STATUS_REPARSE; IoCompleteRequest(Irp,IO_DISK_INCREMENT); UDFReleaseIrpContext(IrpContext); RC = STATUS_REPARSE; Irp = NULL; IrpContext = NULL; // If there is still an error to process then call the Io system // for a popup. } else if ((Irp != NULL) && !NT_SUCCESS( RC )) { KdPrint(("UDFPerformVerify: check IoIsErrorUserInduced\n")); // Fill in the device object if required. if (IoIsErrorUserInduced( RC ) ) { IoSetHardErrorOrVerifyDevice( Irp, DeviceToVerify ); } KdPrint(("UDFPerformVerify: UDFNormalizeAndRaiseStatus\n")); UDFNormalizeAndRaiseStatus( IrpContext, RC ); } } // If there is still an Irp, send it off to an Ex Worker thread. if (IrpContext != NULL) { RC = UDFPostRequest( IrpContext, Irp ); } } _SEH2_EXCEPT(UDFExceptionFilter( IrpContext, _SEH2_GetExceptionInformation())) { // We had some trouble trying to perform the verify or raised // an error ourselves. So we'll abort the I/O request with // the error status that we get back from the execption code. RC = UDFExceptionHandler( IrpContext, Irp); } _SEH2_END; KdPrint(("UDFPerformVerify: RC = %x\n", RC)); return RC; } // end UDFPerformVerify()
/************************************************************************* * * Function: UDFQueryDirectory() * * Description: * Query directory request. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: STATUS_SUCCESS/Error * *************************************************************************/ NTSTATUS NTAPI UDFQueryDirectory( PtrUDFIrpContext PtrIrpContext, PIRP Irp, PIO_STACK_LOCATION IrpSp, PFILE_OBJECT FileObject, PtrUDFFCB Fcb, PtrUDFCCB Ccb ) { NTSTATUS RC = STATUS_SUCCESS; BOOLEAN PostRequest = FALSE; PtrUDFNTRequiredFCB NtReqFcb = NULL; BOOLEAN CanWait = FALSE; PVCB Vcb = NULL; BOOLEAN AcquiredFCB = FALSE; unsigned long BufferLength = 0; UNICODE_STRING SearchPattern; PUNICODE_STRING PtrSearchPattern; FILE_INFORMATION_CLASS FileInformationClass; BOOLEAN ReturnSingleEntry = FALSE; PUCHAR Buffer = NULL; BOOLEAN FirstTimeQuery = FALSE; LONG NextMatch; LONG PrevMatch = -1; ULONG CurrentOffset; ULONG BaseLength; ULONG FileNameBytes; ULONG Information = 0; ULONG LastOffset = 0; BOOLEAN AtLeastOneFound = FALSE; PEXTENDED_IO_STACK_LOCATION pStackLocation = (PEXTENDED_IO_STACK_LOCATION) IrpSp; PUDF_FILE_INFO DirFileInfo = NULL; PDIR_INDEX_HDR hDirIndex = NULL; PFILE_BOTH_DIR_INFORMATION DirInformation = NULL; // Returned from udf_info module PFILE_BOTH_DIR_INFORMATION BothDirInformation = NULL; // Pointer in callers buffer PFILE_NAMES_INFORMATION NamesInfo; ULONG BytesRemainingInBuffer; UCHAR FNM_Flags = 0; PHASH_ENTRY cur_hashes = NULL; PDIR_INDEX_ITEM DirNdx; // do some pre-init... SearchPattern.Buffer = NULL; UDFPrint(("UDFQueryDirectory: @=%#x\n", &PtrIrpContext)); #define CanBe8dot3 (FNM_Flags & UDF_FNM_FLAG_CAN_BE_8D3) #define IgnoreCase (FNM_Flags & UDF_FNM_FLAG_IGNORE_CASE) #define ContainsWC (FNM_Flags & UDF_FNM_FLAG_CONTAINS_WC) _SEH2_TRY { // Validate the sent-in FCB if ((Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) || !(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) { // We will only allow notify requests on directories. try_return(RC = STATUS_INVALID_PARAMETER); } // Obtain the callers parameters NtReqFcb = Fcb->NTRequiredFCB; CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; Vcb = Fcb->Vcb; //Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_CASE_SENSETIVE) ? 0 : UDF_FNM_FLAG_IGNORE_CASE; DirFileInfo = Fcb->FileInfo; BufferLength = pStackLocation->Parameters.QueryDirectory.Length; // If the caller does not want to block, it would be easier to // simply post the request now. if (!CanWait) { PostRequest = TRUE; try_return(RC = STATUS_PENDING); } // Continue obtaining the callers parameters... if(IgnoreCase && pStackLocation->Parameters.QueryDirectory.FileName) { PtrSearchPattern = &SearchPattern; if(!NT_SUCCESS(RC = RtlUpcaseUnicodeString(PtrSearchPattern, (PUNICODE_STRING)(pStackLocation->Parameters.QueryDirectory.FileName), TRUE))) try_return(RC); } else { PtrSearchPattern = (PUNICODE_STRING)(pStackLocation->Parameters.QueryDirectory.FileName); } FileInformationClass = pStackLocation->Parameters.QueryDirectory.FileInformationClass; // Calculate baselength (without name) for each InfoClass switch (FileInformationClass) { case FileDirectoryInformation: BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[0] ); break; case FileFullDirectoryInformation: BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName[0] ); break; case FileNamesInformation: BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName[0] ); break; case FileBothDirectoryInformation: BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] ); break; default: try_return(RC = STATUS_INVALID_INFO_CLASS); } // Some additional arguments that affect the FSD behavior ReturnSingleEntry = (IrpSp->Flags & SL_RETURN_SINGLE_ENTRY) ? TRUE : FALSE; UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFAcquireResourceShared(&(NtReqFcb->MainResource), TRUE); AcquiredFCB = TRUE; // We must determine the buffer pointer to be used. Since this // routine could either be invoked directly in the context of the // calling thread, or in the context of a worker thread, here is // a general way of determining what we should use. if(Irp->MdlAddress) { Buffer = (PUCHAR) MmGetSystemAddressForMdlSafer(Irp->MdlAddress); if(!Buffer) try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } else { Buffer = (PUCHAR) Irp->UserBuffer; if(!Buffer) try_return(RC = STATUS_INVALID_USER_BUFFER); } // The method of determining where to look from and what to look for is // unfortunately extremely confusing. However, here is a methodology // we broadly adopt: // (a) We have to maintain a search buffer per CCB structure. // (b) This search buffer is initialized the very first time // a query directory operation is performed using the file object. // (For the UDF FSD, the search buffer is stored in the // DirectorySearchPattern field) // However, the caller still has the option of "overriding" this stored // search pattern by supplying a new one in a query directory operation. if(PtrSearchPattern && PtrSearchPattern->Buffer && !(PtrSearchPattern->Buffer[PtrSearchPattern->Length/sizeof(WCHAR) - 1])) { PtrSearchPattern->Length -= sizeof(WCHAR); } if(IrpSp->Flags & SL_INDEX_SPECIFIED) { // Good idea from M$: we should continue search from NEXT item // when FileIndex specified... // Strange idea from M$: we should do it with EMPTY pattern... PtrSearchPattern = NULL; Ccb->CCBFlags |= UDF_CCB_MATCH_ALL; } else if(PtrSearchPattern && PtrSearchPattern->Buffer && !UDFIsMatchAllMask(PtrSearchPattern, NULL) ) { Ccb->CCBFlags &= ~(UDF_CCB_MATCH_ALL | UDF_CCB_WILDCARD_PRESENT | UDF_CCB_CAN_BE_8_DOT_3); // Once we have validated the search pattern, we must // check whether we need to store this search pattern in // the CCB. if(Ccb->DirectorySearchPattern) { MyFreePool__(Ccb->DirectorySearchPattern->Buffer); MyFreePool__(Ccb->DirectorySearchPattern); Ccb->DirectorySearchPattern = NULL; } // This must be the very first query request. FirstTimeQuery = TRUE; // Now, allocate enough memory to contain the caller // supplied search pattern and fill in the DirectorySearchPattern // field in the CCB Ccb->DirectorySearchPattern = (PUNICODE_STRING)MyAllocatePool__(NonPagedPool,sizeof(UNICODE_STRING)); if(!(Ccb->DirectorySearchPattern)) { try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } Ccb->DirectorySearchPattern->Length = PtrSearchPattern->Length; Ccb->DirectorySearchPattern->MaximumLength = PtrSearchPattern->MaximumLength; Ccb->DirectorySearchPattern->Buffer = (PWCHAR)MyAllocatePool__(NonPagedPool,PtrSearchPattern->MaximumLength); if(!(Ccb->DirectorySearchPattern->Buffer)) { try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } RtlCopyMemory(Ccb->DirectorySearchPattern->Buffer,PtrSearchPattern->Buffer, PtrSearchPattern->MaximumLength); if(FsRtlDoesNameContainWildCards(PtrSearchPattern)) { Ccb->CCBFlags |= UDF_CCB_WILDCARD_PRESENT; } else { UDFBuildHashEntry(Vcb, PtrSearchPattern, cur_hashes = &(Ccb->hashes), HASH_POSIX | HASH_ULFN); } if(UDFCanNameBeA8dot3(PtrSearchPattern)) Ccb->CCBFlags |= UDF_CCB_CAN_BE_8_DOT_3; } else if(!Ccb->DirectorySearchPattern && !(Ccb->CCBFlags & UDF_CCB_MATCH_ALL) ) { // If the filename is not specified or is a single '*' then we will // match all names. FirstTimeQuery = TRUE; PtrSearchPattern = NULL; Ccb->CCBFlags |= UDF_CCB_MATCH_ALL; } else { // The caller has not supplied any search pattern that we are // forced to use. However, the caller had previously supplied // a pattern (or we must have invented one) and we will use it. // This is definitely not the first query operation on this // directory using this particular file object. if(Ccb->CCBFlags & UDF_CCB_MATCH_ALL) { PtrSearchPattern = NULL; /* if(Ccb->CurrentIndex) Ccb->CurrentIndex++;*/ } else { PtrSearchPattern = Ccb->DirectorySearchPattern; if(!(Ccb->CCBFlags & UDF_CCB_WILDCARD_PRESENT)) { cur_hashes = &(Ccb->hashes); } } } if(IrpSp->Flags & SL_INDEX_SPECIFIED) { // Caller has told us wherefrom to begin. // We may need to round this to an appropriate directory entry // entry alignment value. NextMatch = pStackLocation->Parameters.QueryDirectory.FileIndex + 1; } else if(IrpSp->Flags & SL_RESTART_SCAN) { NextMatch = 0; } else { // Get the starting offset from the CCB. // Remember to update this value on our way out from this function. // But, do not update the CCB CurrentByteOffset field if our reach // the end of the directory (or get an error reading the directory) // while performing the search. NextMatch = Ccb->CurrentIndex + 1; // Last good index } FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_WILDCARD_PRESENT) ? UDF_FNM_FLAG_CONTAINS_WC : 0; // this is used only when mask is supplied FNM_Flags |= (Ccb->CCBFlags & UDF_CCB_CAN_BE_8_DOT_3) ? UDF_FNM_FLAG_CAN_BE_8D3 : 0; // This is an additional verifying if(!UDFIsADirectory(DirFileInfo)) { try_return(RC = STATUS_INVALID_PARAMETER); } hDirIndex = DirFileInfo->Dloc->DirIndex; if(!hDirIndex) { try_return(RC = STATUS_INVALID_PARAMETER); } RC = STATUS_SUCCESS; // Allocate buffer enough to save both DirInformation and FileName DirInformation = (PFILE_BOTH_DIR_INFORMATION)MyAllocatePool__(NonPagedPool, sizeof(FILE_BOTH_DIR_INFORMATION)+((ULONG)UDF_NAME_LEN*sizeof(WCHAR)) ); if(!DirInformation) { try_return(RC = STATUS_INSUFFICIENT_RESOURCES); } CurrentOffset=0; BytesRemainingInBuffer = pStackLocation->Parameters.QueryDirectory.Length; RtlZeroMemory(Buffer,BytesRemainingInBuffer); if((!FirstTimeQuery) && !UDFDirIndex(hDirIndex, (uint_di)NextMatch) ) { try_return( RC = STATUS_NO_MORE_FILES); } // One final note though: // If we do not find a directory entry OR while searching we reach the // end of the directory, then the return code should be set as follows: // (a) If any files have been returned (i.e. ReturnSingleEntry was FALSE // and we did find at least one match), then return STATUS_SUCCESS // (b) If no entry is being returned then: // (i) If this is the first query i.e. FirstTimeQuery is TRUE // then return STATUS_NO_SUCH_FILE // (ii) Otherwise, return STATUS_NO_MORE_FILES while(TRUE) { // If the user had requested only a single match and we have // returned that, then we stop at this point. if(ReturnSingleEntry && AtLeastOneFound) { try_return(RC); } // We call UDFFindNextMatch to look down the next matching dirent. RC = UDFFindNextMatch(Vcb, hDirIndex,&NextMatch,PtrSearchPattern, FNM_Flags, cur_hashes, &DirNdx); // If we didn't receive next match, then we are at the end of the // directory. If we have returned any files, we exit with // success, otherwise we return STATUS_NO_MORE_FILES. if(!NT_SUCCESS(RC)) { RC = AtLeastOneFound ? STATUS_SUCCESS : (FirstTimeQuery ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES); try_return(RC); } // We found at least one matching file entry AtLeastOneFound = TRUE; if(!NT_SUCCESS(RC = UDFFileDirInfoToNT(Vcb, DirNdx, DirInformation))) { // this happends when we can't allocate tmp buffers try_return(RC); } DirInformation->FileIndex = NextMatch; FileNameBytes = DirInformation->FileNameLength; if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) { // We haven't successfully transfered current data & // later NextMatch will be incremented. Thus we should // prevent loosing information in such a way: if(NextMatch) NextMatch --; // If this won't fit and we have returned a previous entry then just // return STATUS_SUCCESS. Otherwise // use a status code of STATUS_BUFFER_OVERFLOW. if(CurrentOffset) { try_return(RC = STATUS_SUCCESS); } // strange policy... ReturnSingleEntry = TRUE; FileNameBytes = BaseLength + FileNameBytes - BytesRemainingInBuffer; RC = STATUS_BUFFER_OVERFLOW; } // Now we have an entry to return to our caller. // We'll case on the type of information requested and fill up // the user buffer if everything fits. switch (FileInformationClass) { case FileBothDirectoryInformation: case FileFullDirectoryInformation: case FileDirectoryInformation: BothDirInformation = (PFILE_BOTH_DIR_INFORMATION)(Buffer + CurrentOffset); RtlCopyMemory(BothDirInformation,DirInformation,BaseLength); BothDirInformation->FileIndex = NextMatch; BothDirInformation->FileNameLength = FileNameBytes; break; case FileNamesInformation: NamesInfo = (PFILE_NAMES_INFORMATION)(Buffer + CurrentOffset); NamesInfo->FileIndex = NextMatch; NamesInfo->FileNameLength = FileNameBytes; break; default: break; } if (FileNameBytes) { // This is a Unicode name, we can copy the bytes directly. RtlCopyMemory( (PVOID)(Buffer + CurrentOffset + BaseLength), DirInformation->FileName, FileNameBytes ); } Information = CurrentOffset + BaseLength + FileNameBytes; // ((..._INFORMATION)(PointerToPreviousEntryInBuffer))->NextEntryOffset = CurrentOffset - LastOffset; *((PULONG)(Buffer+LastOffset)) = CurrentOffset - LastOffset; // Set up our variables for the next dirent. FirstTimeQuery = FALSE; LastOffset = CurrentOffset; PrevMatch = NextMatch; NextMatch++; CurrentOffset = UDFQuadAlign(Information); BytesRemainingInBuffer = BufferLength - CurrentOffset; } try_exit: NOTHING; } _SEH2_FINALLY { if (PostRequest) { if (AcquiredFCB) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); } // Map the users buffer and then post the request. RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, BufferLength); ASSERT(NT_SUCCESS(RC)); RC = UDFPostRequest(PtrIrpContext, Irp); } else { #ifdef UDF_DBG if(!NT_SUCCESS(RC)) { UDFPrint((" Not found\n")); } #endif // UDF_DBG // Remember to update the CurrentByteOffset field in the CCB if required. if(Ccb) Ccb->CurrentIndex = PrevMatch; if (AcquiredFCB) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); } if (!_SEH2_AbnormalTermination()) { // complete the IRP Irp->IoStatus.Status = RC; Irp->IoStatus.Information = Information; IoCompleteRequest(Irp, IO_DISK_INCREMENT); // Free up the Irp Context UDFReleaseIrpContext(PtrIrpContext); } } if(SearchPattern.Buffer) RtlFreeUnicodeString(&SearchPattern); if(DirInformation) MyFreePool__(DirInformation); } _SEH2_END; return(RC); } // end UDFQueryDirectory()
/************************************************************************* * * Function: UDFNotifyChangeDirectory() * * Description: * Handle the notify request. * * Expected Interrupt Level (for execution) : * * IRQL_PASSIVE_LEVEL * * Return Value: STATUS_SUCCESS/Error * *************************************************************************/ NTSTATUS NTAPI UDFNotifyChangeDirectory( PtrUDFIrpContext PtrIrpContext, PIRP Irp, PIO_STACK_LOCATION IrpSp, PFILE_OBJECT FileObject, PtrUDFFCB Fcb, PtrUDFCCB Ccb ) { NTSTATUS RC = STATUS_SUCCESS; BOOLEAN CompleteRequest = FALSE; BOOLEAN PostRequest = FALSE; PtrUDFNTRequiredFCB NtReqFcb = NULL; BOOLEAN CanWait = FALSE; ULONG CompletionFilter = 0; BOOLEAN WatchTree = FALSE; PVCB Vcb = NULL; BOOLEAN AcquiredFCB = FALSE; PEXTENDED_IO_STACK_LOCATION pStackLocation = (PEXTENDED_IO_STACK_LOCATION) IrpSp; UDFPrint(("UDFNotifyChangeDirectory\n")); _SEH2_TRY { // Validate the sent-in FCB if ( (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) || !(Fcb->FCBFlags & UDF_FCB_DIRECTORY)) { CompleteRequest = TRUE; try_return(RC = STATUS_INVALID_PARAMETER); } NtReqFcb = Fcb->NTRequiredFCB; CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE; Vcb = Fcb->Vcb; // Acquire the FCB resource shared UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); if (!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) { PostRequest = TRUE; try_return(RC = STATUS_PENDING); } AcquiredFCB = TRUE; // If the file is marked as DELETE_PENDING then complete this // request immediately. if(Fcb->FCBFlags & UDF_FCB_DELETE_ON_CLOSE) { ASSERT(!(Fcb->FCBFlags & UDF_FCB_ROOT_DIRECTORY)); try_return(RC = STATUS_DELETE_PENDING); } // Obtain some parameters sent by the caller CompletionFilter = pStackLocation ->Parameters.NotifyDirectory.CompletionFilter; WatchTree = (IrpSp->Flags & SL_WATCH_TREE) ? TRUE : FALSE; // If we wish to capture the subject context, we can do so as // follows: // { // PSECURITY_SUBJECT_CONTEXT SubjectContext; // SubjectContext = MyAllocatePool__(PagedPool, // sizeof(SECURITY_SUBJECT_CONTEXT)); // SeCaptureSubjectContext(SubjectContext); // } FsRtlNotifyFullChangeDirectory(Vcb->NotifyIRPMutex, &(Vcb->NextNotifyIRP), (PVOID)Ccb, (Fcb->FileInfo->ParentFile) ? (PSTRING)&(Fcb->FCBName->ObjectName) : (PSTRING)&(UDFGlobalData.UnicodeStrRoot), WatchTree, FALSE, CompletionFilter, Irp, NULL, // UDFTraverseAccessCheck(...) ? NULL); // SubjectContext ? RC = STATUS_PENDING; try_exit: NOTHING; } _SEH2_FINALLY { if (PostRequest) { // Perform appropriate related post processing here if (AcquiredFCB) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); AcquiredFCB = FALSE; } RC = UDFPostRequest(PtrIrpContext, Irp); } else if (CompleteRequest) { if (!_SEH2_AbnormalTermination()) { Irp->IoStatus.Status = RC; Irp->IoStatus.Information = 0; // Free up the Irp Context UDFReleaseIrpContext(PtrIrpContext); // complete the IRP IoCompleteRequest(Irp, IO_DISK_INCREMENT); } } else { // Simply free up the IrpContext since the IRP has been queued if (!_SEH2_AbnormalTermination()) UDFReleaseIrpContext(PtrIrpContext); } // Release the FCB resources if acquired. if (AcquiredFCB) { UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb); UDFReleaseResource(&(NtReqFcb->MainResource)); AcquiredFCB = FALSE; } } _SEH2_END; return(RC); } // end UDFNotifyChangeDirectory()
/************************************************************************* * * Function: UDFCommonClose() * * 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: must be STATUS_SUCCESS * *************************************************************************/ NTSTATUS UDFCommonClose( 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; // PERESOURCE PtrResourceAcquired = NULL; BOOLEAN AcquiredVcb = FALSE; BOOLEAN AcquiredGD = FALSE; PUDF_FILE_INFO fi; ULONG i = 0; // ULONG clean_stat = 0; // BOOLEAN CompleteIrp = TRUE; BOOLEAN PostRequest = FALSE; #ifdef UDF_DBG UNICODE_STRING CurName; PDIR_INDEX_HDR DirNdx; #endif AdPrint(("UDFCommonClose: \n")); _SEH2_TRY { if (Irp) { // If this is the first (IOManager) request // 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->CCBFlags & UDF_CCB_READ_ONLY) { PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_READ_ONLY; } Fcb = Ccb->Fcb; } else { // If this is a queued call (for our dispatch) // Get saved Fcb address Fcb = PtrIrpContext->Fcb; i = PtrIrpContext->TreeLength; } ASSERT(Fcb); Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension); ASSERT(Vcb); ASSERT(Vcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB); // Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK; // Steps we shall take at this point are: // (a) Acquire the VCB shared // (b) Acquire the FCB's CCB list exclusively // (c) Delete the CCB structure (free memory) // (d) If this is the last close, release the FCB structure // (unless we keep these around for "delayed close" functionality. // Note that it is often the case that the close dispatch entry point is invoked // in the most inconvenient of situations (when it is not possible, for example, // to safely acquire certain required resources without deadlocking or waiting). // Therefore, be extremely careful in implementing this close dispatch entry point. // Also note that we do not have the option of returning a failure code from the // close dispatch entry point; the system expects that the close will always succeed. UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE); AcquiredVcb = TRUE; // Is this is the first (IOManager) request ? if (Irp) { PtrIrpContext->TreeLength = i = Ccb->TreeLength; // remember the number of incomplete Close requests InterlockedIncrement((PLONG)&(Fcb->CcbCount)); // we can release CCB in any case UDFCleanUpCCB(Ccb); FileObject->FsContext2 = NULL; #ifdef DBG /* } else { ASSERT(Fcb->NTRequiredFCB); if(Fcb->NTRequiredFCB) { ASSERT(Fcb->NTRequiredFCB->FileObject); if(Fcb->NTRequiredFCB->FileObject) { ASSERT(!Fcb->NTRequiredFCB->FileObject->FsContext2); } }*/ #endif //DBG } #ifdef UDF_DELAYED_CLOSE // check if this is the last Close (no more Handles) // and try to Delay it.... if((Fcb->FCBFlags & UDF_FCB_DELAY_CLOSE) && (Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) && !(Vcb->VCBFlags & UDF_VCB_FLAGS_NO_DELAYED_CLOSE) && !(Fcb->OpenHandleCount)) { UDFReleaseResource(&(Vcb->VCBResource)); AcquiredVcb = FALSE; if((RC = UDFQueueDelayedClose(PtrIrpContext,Fcb)) == STATUS_SUCCESS) try_return(RC = STATUS_SUCCESS); // do standard Close if we can't Delay this opeartion AdPrint((" Cant queue Close Irp, status=%x\n", RC)); } #endif //UDF_DELAYED_CLOSE if(Irp) { // We should post actual procesing if this is a recursive call if((PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_NOT_TOP_LEVEL) || (Fcb->NTRequiredFCB->AcqFlushCount)) { AdPrint((" post NOT_TOP_LEVEL Irp\n")); PostRequest = TRUE; try_return(RC = STATUS_SUCCESS); } } // Close request is near completion, Vcb is acquired. // Now we can safely decrease CcbCount, because no Rename // operation can run until Vcb release. InterlockedDecrement((PLONG)&(Fcb->CcbCount)); UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount)); if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_READ_ONLY) UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCountRO)); if(!i || (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB)) { AdPrint(("UDF: Closing volume\n")); AdPrint(("UDF: ReferenceCount: %x\n",Fcb->ReferenceCount)); if (Vcb->VCBOpenCount > UDF_RESIDUAL_REFERENCE) { ASSERT(Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB); UDFInterlockedDecrement((PLONG)&(Fcb->ReferenceCount)); ASSERT(Fcb->NTRequiredFCB); UDFInterlockedDecrement((PLONG)&(Fcb->NTRequiredFCB->CommonRefCount)); try_return(RC = STATUS_SUCCESS); } UDFInterlockedIncrement((PLONG)&(Vcb->VCBOpenCount)); if(AcquiredVcb) { UDFReleaseResource(&(Vcb->VCBResource)); AcquiredVcb = FALSE; } else { BrutePoint(); } // Acquire GlobalDataResource UDFAcquireResourceExclusive(&(UDFGlobalData.GlobalDataResource), TRUE); AcquiredGD = TRUE; // // Acquire Vcb UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE); AcquiredVcb = TRUE; UDFInterlockedDecrement((PLONG)&(Vcb->VCBOpenCount)); ASSERT(Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB); UDFInterlockedDecrement((PLONG)&(Fcb->ReferenceCount)); ASSERT(Fcb->NTRequiredFCB); UDFInterlockedDecrement((PLONG)&(Fcb->NTRequiredFCB->CommonRefCount)); //AdPrint(("UDF: Closing volume, reset driver (e.g. stop BGF)\n")); //UDFResetDeviceDriver(Vcb, Vcb->TargetDeviceObject, FALSE); AdPrint(("UDF: Closing volume, reset write status\n")); RC = UDFPhSendIOCTL(IOCTL_CDRW_RESET_WRITE_STATUS, Vcb->TargetDeviceObject, NULL, 0, NULL, 0, TRUE, NULL); if((Vcb->VCBFlags & UDF_VCB_FLAGS_BEING_DISMOUNTED) || ((!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED)) && (Vcb->VCBOpenCount <= UDF_RESIDUAL_REFERENCE))) { // Try to KILL dismounted volume.... // w2k requires this, NT4 - recomends AcquiredVcb = UDFCheckForDismount(PtrIrpContext, Vcb, TRUE); } try_return(RC = STATUS_SUCCESS); } fi = Fcb->FileInfo; #ifdef UDF_DBG if(!fi) { BrutePoint(); } DirNdx = UDFGetDirIndexByFileInfo(fi); if(DirNdx) { CurName.Buffer = UDFDirIndex(DirNdx,fi->Index)->FName.Buffer; if(CurName.Buffer) { AdPrint(("Closing file: %ws %8.8x\n", CurName.Buffer, FileObject)); } else { AdPrint(("Closing file: ??? \n")); } } AdPrint(("UDF: ReferenceCount: %x\n",Fcb->ReferenceCount)); #endif // UDF_DBG // try to clean up as long chain as it is possible UDFCleanUpFcbChain(Vcb, fi, i, TRUE); try_exit: NOTHING; } _SEH2_FINALLY { if(AcquiredVcb) { UDFReleaseResource(&(Vcb->VCBResource)); } if(AcquiredGD) { UDFReleaseResource(&(UDFGlobalData.GlobalDataResource)); } // Post IRP if required if (PostRequest) { // Perform the post operation & complete the IRP // if this is first call of UDFCommonClose // and will return STATUS_SUCCESS back to us PtrIrpContext->Irp = NULL; PtrIrpContext->Fcb = Fcb; UDFPostRequest(PtrIrpContext, NULL); } if (!_SEH2_AbnormalTermination()) { // If this is not async close complete the IRP if (Irp) { /* if( FileObject ) { if(clean_stat & UDF_CLOSE_NTREQFCB_DELETED) { // ASSERT(!FileObject->FsContext2); FileObject->FsContext = NULL; #ifdef DBG } else { UDFNTRequiredFCB* NtReqFcb = ((UDFNTRequiredFCB*)(FileObject->FsContext)); if(NtReqFcb->FileObject == FileObject) { NtReqFcb->FileObject = NULL; } #endif //DBG } }*/ Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_DISK_INCREMENT); } // Free up the Irp Context if(!PostRequest) UDFReleaseIrpContext(PtrIrpContext); } } _SEH2_END; // end of "__finally" processing return STATUS_SUCCESS ; } // end UDFCommonClose()