2015-06-06 12:52:13 +00:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
|
|
|
|
// All rights reserved
|
2015-06-08 14:24:47 +00:00
|
|
|
// This file was released under the GPLv2 on June 2015.
|
2015-06-06 12:52:13 +00:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
/*************************************************************************
|
|
|
|
*
|
|
|
|
* 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;
|
|
|
|
|
2017-06-03 20:09:21 +00:00
|
|
|
UDFPrint(("UDFQueryDirectory: @=%#x\n", &PtrIrpContext));
|
2015-06-06 12:52:13 +00:00
|
|
|
|
|
|
|
#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;
|
2015-06-09 10:40:26 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2015-06-06 12:52:13 +00:00
|
|
|
}
|
|
|
|
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)) {
|
2017-06-03 20:09:21 +00:00
|
|
|
UDFPrint((" Not found\n"));
|
2015-06-06 12:52:13 +00:00
|
|
|
}
|
|
|
|
#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)
|
|
|
|
|
2015-06-09 10:40:26 +00:00
|
|
|
for(;(DirNdx = UDFDirIndex(hDirIndex, EntryNumber));EntryNumber++) {
|
2015-06-06 12:52:13 +00:00
|
|
|
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;
|
|
|
|
|
2017-06-03 20:09:21 +00:00
|
|
|
UDFPrint(("UDFNotifyChangeDirectory\n"));
|
2015-06-06 12:52:13 +00:00
|
|
|
|
|
|
|
_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()
|