reactos/drivers/filesystems/udfs/dircntrl.cpp
2021-06-11 15:33:08 +03:00

794 lines
30 KiB
C++

////////////////////////////////////////////////////////////////////
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
// All rights reserved
// This file was released under the GPLv2 on June 2015.
////////////////////////////////////////////////////////////////////
/*************************************************************************
*
* File: DirCntrl.cpp
*
* Module: UDF File System Driver (Kernel mode execution only)
*
* Description:
* Contains code to handle the "directory control" dispatch entry point.
*
*************************************************************************/
#include "udffs.h"
// define the file specific bug-check id
#define UDF_BUG_CHECK_ID UDF_FILE_DIR_CONTROL
/*
// Local support routine(s):
*/
#define UDF_FNM_FLAG_CAN_BE_8D3 0x01
#define UDF_FNM_FLAG_IGNORE_CASE 0x02
#define UDF_FNM_FLAG_CONTAINS_WC 0x04
NTSTATUS UDFFindNextMatch(
IN PVCB Vcb,
IN PDIR_INDEX_HDR hDirIndex,
IN PLONG CurrentNumber, // Must be modified
IN PUNICODE_STRING PtrSearchPattern,
IN UCHAR FNM_Flags,
IN PHASH_ENTRY hashes,
OUT PDIR_INDEX_ITEM* _DirNdx);
/*************************************************************************
*
* Function: UDFDirControl()
*
* Description:
* The I/O Manager will invoke this routine to handle a directory control
* 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
UDFDirControl(
PDEVICE_OBJECT DeviceObject, // the logical volume device object
PIRP Irp // I/O Request Packet
)
{
NTSTATUS RC = STATUS_SUCCESS;
PtrUDFIrpContext PtrIrpContext = NULL;
BOOLEAN AreWeTopLevel = FALSE;
TmPrint(("UDFDirControl: \n"));
FsRtlEnterFileSystem();
ASSERT(DeviceObject);
ASSERT(Irp);
// set the top level context
AreWeTopLevel = UDFIsIrpTopLevel(Irp);
ASSERT(!UDFIsFSDevObj(DeviceObject));
_SEH2_TRY {
// get an IRP context structure and issue the request
PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
if(PtrIrpContext) {
RC = UDFCommonDirControl(PtrIrpContext, Irp);
} else {
RC = STATUS_INSUFFICIENT_RESOURCES;
Irp->IoStatus.Status = RC;
Irp->IoStatus.Information = 0;
// complete the IRP
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
}
} _SEH2_EXCEPT(UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
RC = UDFExceptionHandler(PtrIrpContext, Irp);
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
} _SEH2_END;
if (AreWeTopLevel) {
IoSetTopLevelIrp(NULL);
}
FsRtlExitFileSystem();
return(RC);
} // end UDFDirControl()
/*************************************************************************
*
* Function: UDFCommonDirControl()
*
* 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
NTAPI
UDFCommonDirControl(
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;
BOOLEAN AcquiredVcb = FALSE;
TmPrint(("UDFCommonDirControl: \n"));
// BrutePoint();
_SEH2_TRY {
// First, get a pointer to the current I/O stack location
IrpSp = IoGetCurrentIrpStackLocation(Irp);
ASSERT(IrpSp);
FileObject = IrpSp->FileObject;
ASSERT(FileObject);
// Get the FCB and CCB pointers
Ccb = (PtrUDFCCB)(FileObject->FsContext2);
ASSERT(Ccb);
Fcb = Ccb->Fcb;
ASSERT(Fcb);
Vcb = (PVCB)(PtrIrpContext->TargetDeviceObject->DeviceExtension);
ASSERT(Vcb);
ASSERT(Vcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB);
// Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
UDFFlushTryBreak(Vcb);
UDFAcquireResourceShared(&(Vcb->VCBResource), TRUE);
AcquiredVcb = TRUE;
// Get some of the parameters supplied to us
switch (IrpSp->MinorFunction) {
case IRP_MN_QUERY_DIRECTORY:
RC = UDFQueryDirectory(PtrIrpContext, Irp, IrpSp, FileObject, Fcb, Ccb);
break;
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
RC = UDFNotifyChangeDirectory(PtrIrpContext, Irp, IrpSp, FileObject, Fcb, Ccb);
break;
default:
// This should not happen.
RC = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Status = RC;
Irp->IoStatus.Information = 0;
// Free up the Irp Context
UDFReleaseIrpContext(PtrIrpContext);
// complete the IRP
IoCompleteRequest(Irp, IO_NO_INCREMENT);
break;
}
//try_exit: NOTHING;
} _SEH2_FINALLY {
if(AcquiredVcb) {
UDFReleaseResource(&(Vcb->VCBResource));
AcquiredVcb = FALSE;
}
} _SEH2_END;
return(RC);
} // end UDFCommonDirControl()
/*************************************************************************
*
* 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()
/*
Return: STATUS_NO_SUCH_FILE if no more files found
*/
NTSTATUS
UDFFindNextMatch(
IN PVCB Vcb,
IN PDIR_INDEX_HDR hDirIndex,
IN PLONG CurrentNumber, // Must be modified in case, when we found next match
IN PUNICODE_STRING PtrSearchPattern,
IN UCHAR FNM_Flags,
IN PHASH_ENTRY hashes,
OUT PDIR_INDEX_ITEM* _DirNdx
)
{
LONG EntryNumber = (*CurrentNumber);
PDIR_INDEX_ITEM DirNdx;
#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)
for(;(DirNdx = UDFDirIndex(hDirIndex, EntryNumber));EntryNumber++) {
if(!DirNdx->FName.Buffer ||
UDFIsDeleted(DirNdx))
continue;
if(hashes &&
(DirNdx->hashes.hLfn != hashes->hLfn) &&
(DirNdx->hashes.hPosix != hashes->hPosix) &&
(!CanBe8dot3 || ((DirNdx->hashes.hDos != hashes->hLfn) && (DirNdx->hashes.hDos != hashes->hPosix))) )
continue;
if(UDFIsNameInExpression(Vcb, &(DirNdx->FName),PtrSearchPattern, NULL,IgnoreCase,
ContainsWC, CanBe8dot3 && !(DirNdx->FI_Flags & UDF_FI_FLAG_DOS),
EntryNumber < 2) &&
!(DirNdx->FI_Flags & UDF_FI_FLAG_FI_INTERNAL))
break;
}
if(DirNdx) {
// Modify CurrentNumber to appropriate value
*CurrentNumber = EntryNumber;
*_DirNdx = DirNdx;
return STATUS_SUCCESS;
} else {
// Do not modify CurrentNumber because we have not found next match entry
return STATUS_NO_MORE_FILES;
}
} // end UDFFindNextMatch()
/*************************************************************************
*
* 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()