mirror of
https://github.com/reactos/reactos.git
synced 2024-08-21 10:53:15 +00:00
1550 lines
44 KiB
C
1550 lines
44 KiB
C
/*++
|
|
|
|
Copyright (c) 1989-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
DirCtrl.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the File Directory Control routines for Cdfs called
|
|
by the Fsd/Fsp dispatch drivers.
|
|
|
|
|
|
--*/
|
|
|
|
#include "cdprocs.h"
|
|
|
|
//
|
|
// The Bug check file id for this module
|
|
//
|
|
|
|
#define BugCheckFileId (CDFS_BUG_CHECK_DIRCTRL)
|
|
|
|
//
|
|
// Local support routines
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
NTSTATUS
|
|
CdQueryDirectory (
|
|
_Inout_ PIRP_CONTEXT IrpContext,
|
|
_Inout_ PIRP Irp,
|
|
_In_ PIO_STACK_LOCATION IrpSp,
|
|
_In_ PFCB Fcb,
|
|
_In_ PCCB Ccb
|
|
);
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
NTSTATUS
|
|
CdNotifyChangeDirectory (
|
|
_Inout_ PIRP_CONTEXT IrpContext,
|
|
_Inout_ PIRP Irp,
|
|
_In_ PIO_STACK_LOCATION IrpSp,
|
|
_In_ PCCB Ccb
|
|
);
|
|
|
|
VOID
|
|
CdInitializeEnumeration (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PIO_STACK_LOCATION IrpSp,
|
|
_In_ PFCB Fcb,
|
|
_Inout_ PCCB Ccb,
|
|
_Inout_ PFILE_ENUM_CONTEXT FileContext,
|
|
_Out_ PBOOLEAN ReturnNextEntry,
|
|
_Out_ PBOOLEAN ReturnSingleEntry,
|
|
_Out_ PBOOLEAN InitialQuery
|
|
);
|
|
|
|
BOOLEAN
|
|
CdEnumerateIndex (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PCCB Ccb,
|
|
_Inout_ PFILE_ENUM_CONTEXT FileContext,
|
|
_In_ BOOLEAN ReturnNextEntry
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, CdCommonDirControl)
|
|
#pragma alloc_text(PAGE, CdEnumerateIndex)
|
|
#pragma alloc_text(PAGE, CdInitializeEnumeration)
|
|
#pragma alloc_text(PAGE, CdNotifyChangeDirectory)
|
|
#pragma alloc_text(PAGE, CdQueryDirectory)
|
|
#endif
|
|
|
|
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
NTSTATUS
|
|
CdCommonDirControl (
|
|
_Inout_ PIRP_CONTEXT IrpContext,
|
|
_Inout_ PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the entry point for the directory control operations. These
|
|
are directory enumerations and directory notify calls. We verify the
|
|
user's handle is for a directory and then call the appropriate routine.
|
|
|
|
Arguments:
|
|
|
|
Irp - Irp for this request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status returned from the lower level routines.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
PFCB Fcb;
|
|
PCCB Ccb;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Decode the user file object and fail this request if it is not
|
|
// a user directory.
|
|
//
|
|
|
|
if (CdDecodeFileObject( IrpContext,
|
|
IrpSp->FileObject,
|
|
&Fcb,
|
|
&Ccb ) != UserDirectoryOpen) {
|
|
|
|
CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// We know this is a directory control so we'll case on the
|
|
// minor function, and call a internal worker routine to complete
|
|
// the irp.
|
|
//
|
|
|
|
switch (IrpSp->MinorFunction) {
|
|
|
|
case IRP_MN_QUERY_DIRECTORY:
|
|
|
|
Status = CdQueryDirectory( IrpContext, Irp, IrpSp, Fcb, Ccb );
|
|
break;
|
|
|
|
case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
|
|
|
|
Status = CdNotifyChangeDirectory( IrpContext, Irp, IrpSp, Ccb );
|
|
break;
|
|
|
|
default:
|
|
|
|
CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routines
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
NTSTATUS
|
|
CdQueryDirectory (
|
|
_Inout_ PIRP_CONTEXT IrpContext,
|
|
_Inout_ PIRP Irp,
|
|
_In_ PIO_STACK_LOCATION IrpSp,
|
|
_In_ PFCB Fcb,
|
|
_In_ PCCB Ccb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the query directory operation. It is responsible
|
|
for either completing of enqueuing the input Irp. We store the state of the
|
|
search in the Ccb.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp to process
|
|
|
|
IrpSp - Stack location for this Irp.
|
|
|
|
Fcb - Fcb for this directory.
|
|
|
|
Ccb - Ccb for this directory open.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Information = 0;
|
|
|
|
ULONG LastEntry = 0;
|
|
ULONG NextEntry = 0;
|
|
|
|
ULONG FileNameBytes;
|
|
ULONG SeparatorBytes;
|
|
ULONG VersionStringBytes;
|
|
|
|
FILE_ENUM_CONTEXT FileContext;
|
|
PDIRENT ThisDirent = NULL;
|
|
BOOLEAN InitialQuery;
|
|
BOOLEAN ReturnNextEntry = FALSE;
|
|
BOOLEAN ReturnSingleEntry;
|
|
BOOLEAN Found;
|
|
BOOLEAN DoCcbUpdate = FALSE;
|
|
|
|
PCHAR UserBuffer;
|
|
ULONG BytesRemainingInBuffer;
|
|
|
|
ULONG BaseLength;
|
|
|
|
PFILE_BOTH_DIR_INFORMATION DirInfo = NULL;
|
|
PFILE_NAMES_INFORMATION NamesInfo;
|
|
PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo;
|
|
PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Check if we support this search mode. Also remember the size of the base part of
|
|
// each of these structures.
|
|
//
|
|
|
|
switch (IrpSp->Parameters.QueryDirectory.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 FileIdFullDirectoryInformation:
|
|
|
|
BaseLength = FIELD_OFFSET( FILE_ID_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;
|
|
|
|
case FileIdBothDirectoryInformation:
|
|
|
|
BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION,
|
|
FileName[0] );
|
|
break;
|
|
|
|
default:
|
|
|
|
CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_INFO_CLASS );
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
//
|
|
// Get the user buffer.
|
|
//
|
|
|
|
CdMapUserBuffer( IrpContext, &UserBuffer);
|
|
|
|
//
|
|
// Initialize our search context.
|
|
//
|
|
|
|
CdInitializeFileContext( IrpContext, &FileContext );
|
|
|
|
//
|
|
// Acquire the directory.
|
|
//
|
|
|
|
CdAcquireFileShared( IrpContext, Fcb );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
_SEH2_TRY {
|
|
|
|
//
|
|
// Verify the Fcb is still good.
|
|
//
|
|
|
|
CdVerifyFcbOperation( IrpContext, Fcb );
|
|
|
|
//
|
|
// Start by getting the initial state for the enumeration. This will set up the Ccb with
|
|
// the initial search parameters and let us know the starting offset in the directory
|
|
// to search.
|
|
//
|
|
|
|
CdInitializeEnumeration( IrpContext,
|
|
IrpSp,
|
|
Fcb,
|
|
Ccb,
|
|
&FileContext,
|
|
&ReturnNextEntry,
|
|
&ReturnSingleEntry,
|
|
&InitialQuery );
|
|
|
|
//
|
|
// The current dirent is stored in the InitialDirent field. We capture
|
|
// this here so that we have a valid restart point even if we don't
|
|
// find a single entry.
|
|
//
|
|
|
|
ThisDirent = &FileContext.InitialDirent->Dirent;
|
|
|
|
//
|
|
// At this point we are about to enter our query loop. We have
|
|
// determined the index into the directory file to begin the
|
|
// search. LastEntry and NextEntry are used to index into the user
|
|
// buffer. LastEntry is the last entry we've added, NextEntry is
|
|
// current one we're working on. If NextEntry is non-zero, then
|
|
// at least one entry was added.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// If the user had requested only a single match and we have
|
|
// returned that, then we stop at this point. We update the Ccb with
|
|
// the status based on the last entry returned.
|
|
//
|
|
|
|
if ((NextEntry != 0) && ReturnSingleEntry) {
|
|
|
|
DoCcbUpdate = TRUE;
|
|
try_leave( Status );
|
|
}
|
|
|
|
//
|
|
// We try to locate the next matching dirent. Our search if based on a starting
|
|
// dirent offset, whether we should return the current or next entry, whether
|
|
// we should be doing a short name search and finally whether we should be
|
|
// checking for a version match.
|
|
//
|
|
|
|
Found = CdEnumerateIndex( IrpContext, Ccb, &FileContext, ReturnNextEntry );
|
|
|
|
//
|
|
// Initialize the value for the next search.
|
|
//
|
|
|
|
ReturnNextEntry = TRUE;
|
|
|
|
//
|
|
// If we didn't receive a dirent, 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 (!Found) {
|
|
|
|
if (NextEntry == 0) {
|
|
|
|
Status = STATUS_NO_MORE_FILES;
|
|
|
|
if (InitialQuery) {
|
|
|
|
Status = STATUS_NO_SUCH_FILE;
|
|
}
|
|
}
|
|
|
|
DoCcbUpdate = TRUE;
|
|
try_leave( Status );
|
|
}
|
|
|
|
//
|
|
// Remember the dirent for the file we just found.
|
|
//
|
|
|
|
ThisDirent = &FileContext.InitialDirent->Dirent;
|
|
|
|
//
|
|
// Here are the rules concerning filling up the buffer:
|
|
//
|
|
// 1. The Io system garentees that there will always be
|
|
// enough room for at least one base record.
|
|
//
|
|
// 2. If the full first record (including file name) cannot
|
|
// fit, as much of the name as possible is copied and
|
|
// STATUS_BUFFER_OVERFLOW is returned.
|
|
//
|
|
// 3. If a subsequent record cannot completely fit into the
|
|
// buffer, none of it (as in 0 bytes) is copied, and
|
|
// STATUS_SUCCESS is returned. A subsequent query will
|
|
// pick up with this record.
|
|
//
|
|
|
|
//
|
|
// Let's compute the number of bytes we need to transfer the current entry.
|
|
//
|
|
|
|
SeparatorBytes =
|
|
VersionStringBytes = 0;
|
|
|
|
//
|
|
// We can look directly at the dirent that we found.
|
|
//
|
|
|
|
FileNameBytes = ThisDirent->CdFileName.FileName.Length;
|
|
|
|
//
|
|
// Compute the number of bytes for the version string if
|
|
// we will return this. Allow directories with illegal ";".
|
|
//
|
|
|
|
if (((Ccb->SearchExpression.VersionString.Length != 0) ||
|
|
(FlagOn(ThisDirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY))) &&
|
|
(ThisDirent->CdFileName.VersionString.Length != 0)) {
|
|
|
|
SeparatorBytes = 2;
|
|
|
|
VersionStringBytes = ThisDirent->CdFileName.VersionString.Length;
|
|
}
|
|
|
|
//
|
|
// If the slot for the next entry would be beyond the length of the
|
|
// user's buffer just exit (we know we've returned at least one entry
|
|
// already). This will happen when we align the pointer past the end.
|
|
//
|
|
|
|
if (NextEntry > IrpSp->Parameters.QueryDirectory.Length) {
|
|
|
|
ReturnNextEntry = FALSE;
|
|
DoCcbUpdate = TRUE;
|
|
try_leave( Status = STATUS_SUCCESS );
|
|
}
|
|
|
|
//
|
|
// Compute the number of bytes remaining in the buffer. Round this
|
|
// down to a WCHAR boundary so we can copy full characters.
|
|
//
|
|
|
|
BytesRemainingInBuffer = IrpSp->Parameters.QueryDirectory.Length - NextEntry;
|
|
ClearFlag( BytesRemainingInBuffer, 1 );
|
|
|
|
//
|
|
// If this won't fit and we have returned a previous entry then just
|
|
// return STATUS_SUCCESS.
|
|
//
|
|
|
|
if ((BaseLength + FileNameBytes + SeparatorBytes + VersionStringBytes) > BytesRemainingInBuffer) {
|
|
|
|
//
|
|
// If we already found an entry then just exit.
|
|
//
|
|
|
|
if (NextEntry != 0) {
|
|
|
|
ReturnNextEntry = FALSE;
|
|
DoCcbUpdate = TRUE;
|
|
try_leave( Status = STATUS_SUCCESS );
|
|
}
|
|
|
|
//
|
|
// Don't even try to return the version string if it doesn't all fit.
|
|
// Reduce the FileNameBytes to just fit in the buffer.
|
|
//
|
|
|
|
if ((BaseLength + FileNameBytes) > BytesRemainingInBuffer) {
|
|
|
|
FileNameBytes = BytesRemainingInBuffer - BaseLength;
|
|
}
|
|
|
|
//
|
|
// Don't return any version string bytes.
|
|
//
|
|
|
|
VersionStringBytes =
|
|
SeparatorBytes = 0;
|
|
|
|
//
|
|
// Use a status code of STATUS_BUFFER_OVERFLOW. Also set
|
|
// ReturnSingleEntry so that we will exit the loop at the top.
|
|
//
|
|
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
ReturnSingleEntry = TRUE;
|
|
}
|
|
|
|
//
|
|
// Protect access to the user buffer with an exception handler.
|
|
// Since (at our request) IO doesn't buffer these requests, we have
|
|
// to guard against a user messing with the page protection and other
|
|
// such trickery.
|
|
//
|
|
|
|
_SEH2_TRY {
|
|
|
|
//
|
|
// Zero and initialize the base part of the current entry.
|
|
//
|
|
|
|
RtlZeroMemory( Add2Ptr( UserBuffer, NextEntry, PVOID ),
|
|
BaseLength );
|
|
|
|
//
|
|
// 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 (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
|
|
|
|
case FileBothDirectoryInformation:
|
|
case FileFullDirectoryInformation:
|
|
case FileIdBothDirectoryInformation:
|
|
case FileIdFullDirectoryInformation:
|
|
case FileDirectoryInformation:
|
|
|
|
DirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_BOTH_DIR_INFORMATION );
|
|
|
|
//
|
|
// Use the create time for all the time stamps.
|
|
//
|
|
|
|
CdConvertCdTimeToNtTime( IrpContext,
|
|
FileContext.InitialDirent->Dirent.CdTime,
|
|
&DirInfo->CreationTime );
|
|
|
|
DirInfo->LastWriteTime = DirInfo->ChangeTime = DirInfo->CreationTime;
|
|
|
|
//
|
|
// Set the attributes and sizes separately for directories and
|
|
// files.
|
|
//
|
|
|
|
if (FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) {
|
|
|
|
DirInfo->EndOfFile.QuadPart = DirInfo->AllocationSize.QuadPart = 0;
|
|
|
|
SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
} else {
|
|
|
|
DirInfo->EndOfFile.QuadPart = FileContext.FileSize;
|
|
DirInfo->AllocationSize.QuadPart = LlSectorAlign( FileContext.FileSize );
|
|
|
|
SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_READONLY);
|
|
}
|
|
|
|
if (FlagOn( ThisDirent->DirentFlags,
|
|
CD_ATTRIBUTE_HIDDEN )) {
|
|
|
|
SetFlag( DirInfo->FileAttributes, FILE_ATTRIBUTE_HIDDEN );
|
|
}
|
|
|
|
DirInfo->FileIndex = ThisDirent->DirentOffset;
|
|
|
|
DirInfo->FileNameLength = FileNameBytes + SeparatorBytes + VersionStringBytes;
|
|
|
|
break;
|
|
|
|
case FileNamesInformation:
|
|
|
|
NamesInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_NAMES_INFORMATION );
|
|
|
|
NamesInfo->FileIndex = ThisDirent->DirentOffset;
|
|
|
|
NamesInfo->FileNameLength = FileNameBytes + SeparatorBytes + VersionStringBytes;
|
|
|
|
break;
|
|
|
|
/* ReactOS Change: GCC "enumeration value not handled in switch" */
|
|
default: break;
|
|
}
|
|
|
|
//
|
|
// Fill in the FileId
|
|
//
|
|
|
|
switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) {
|
|
|
|
case FileIdBothDirectoryInformation:
|
|
|
|
IdBothDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_BOTH_DIR_INFORMATION );
|
|
CdSetFidFromParentAndDirent( IdBothDirInfo->FileId, Fcb, ThisDirent );
|
|
break;
|
|
|
|
case FileIdFullDirectoryInformation:
|
|
|
|
IdFullDirInfo = Add2Ptr( UserBuffer, NextEntry, PFILE_ID_FULL_DIR_INFORMATION );
|
|
CdSetFidFromParentAndDirent( IdFullDirInfo->FileId, Fcb, ThisDirent );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now copy as much of the name as possible. We also may have a version
|
|
// string to copy.
|
|
//
|
|
|
|
if (FileNameBytes != 0) {
|
|
|
|
//
|
|
// This is a Unicode name, we can copy the bytes directly.
|
|
//
|
|
|
|
RtlCopyMemory( Add2Ptr( UserBuffer, NextEntry + BaseLength, PVOID ),
|
|
ThisDirent->CdFileName.FileName.Buffer,
|
|
FileNameBytes );
|
|
|
|
if (SeparatorBytes != 0) {
|
|
|
|
*(Add2Ptr( UserBuffer,
|
|
NextEntry + BaseLength + FileNameBytes,
|
|
PWCHAR )) = L';';
|
|
|
|
if (VersionStringBytes != 0) {
|
|
|
|
RtlCopyMemory( Add2Ptr( UserBuffer,
|
|
NextEntry + BaseLength + FileNameBytes + sizeof( WCHAR ),
|
|
PVOID ),
|
|
ThisDirent->CdFileName.VersionString.Buffer,
|
|
VersionStringBytes );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fill in the short name if we got STATUS_SUCCESS. The short name
|
|
// may already be in the file context. Otherwise we will check
|
|
// whether the long name is 8.3. Special case the self and parent
|
|
// directory names.
|
|
//
|
|
|
|
if ((Status == STATUS_SUCCESS) &&
|
|
(IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation ||
|
|
IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation) &&
|
|
(Ccb->SearchExpression.VersionString.Length == 0) &&
|
|
!FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) {
|
|
|
|
//
|
|
// If we already have the short name then copy into the user's buffer.
|
|
//
|
|
|
|
if (FileContext.ShortName.FileName.Length != 0) {
|
|
|
|
RtlCopyMemory( DirInfo->ShortName,
|
|
FileContext.ShortName.FileName.Buffer,
|
|
FileContext.ShortName.FileName.Length );
|
|
|
|
DirInfo->ShortNameLength = (CCHAR) FileContext.ShortName.FileName.Length;
|
|
|
|
//
|
|
// If the short name length is currently zero then check if
|
|
// the long name is not 8.3. We can copy the short name in
|
|
// unicode form directly into the caller's buffer.
|
|
//
|
|
|
|
} else {
|
|
|
|
if (!CdIs8dot3Name( IrpContext,
|
|
ThisDirent->CdFileName.FileName )) {
|
|
|
|
CdGenerate8dot3Name( IrpContext,
|
|
&ThisDirent->CdCaseFileName.FileName,
|
|
ThisDirent->DirentOffset,
|
|
DirInfo->ShortName,
|
|
&FileContext.ShortName.FileName.Length );
|
|
|
|
DirInfo->ShortNameLength = (CCHAR) FileContext.ShortName.FileName.Length;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Sum the total number of bytes for the information field.
|
|
//
|
|
|
|
FileNameBytes += SeparatorBytes + VersionStringBytes;
|
|
|
|
//
|
|
// Update the information with the number of bytes stored in the
|
|
// buffer. We quad-align the existing buffer to add any necessary
|
|
// pad bytes.
|
|
//
|
|
|
|
Information = NextEntry + BaseLength + FileNameBytes;
|
|
|
|
//
|
|
// Go back to the previous entry and fill in the update to this entry.
|
|
//
|
|
|
|
*(Add2Ptr( UserBuffer, LastEntry, PULONG )) = NextEntry - LastEntry;
|
|
|
|
//
|
|
// Set up our variables for the next dirent.
|
|
//
|
|
|
|
InitialQuery = FALSE;
|
|
|
|
LastEntry = NextEntry;
|
|
NextEntry = QuadAlign( Information );
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(suppress: 6320)
|
|
#endif
|
|
} _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
//
|
|
// We had a problem filling in the user's buffer, so stop and
|
|
// fail this request. This is the only reason any exception
|
|
// would have occured at this level.
|
|
//
|
|
|
|
Information = 0;
|
|
try_leave( Status = _SEH2_GetExceptionCode());
|
|
} _SEH2_END;
|
|
}
|
|
|
|
DoCcbUpdate = TRUE;
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
//
|
|
// Cleanup our search context - *before* aquiring the FCB mutex exclusive,
|
|
// else can block on threads in cdcreateinternalstream/purge which
|
|
// hold the FCB but are waiting for all maps in this stream to be released.
|
|
//
|
|
|
|
CdCleanupFileContext( IrpContext, &FileContext );
|
|
|
|
//
|
|
// Now we can safely aqure the FCB mutex if we need to.
|
|
//
|
|
|
|
if (DoCcbUpdate && !NT_ERROR( Status )) {
|
|
|
|
//
|
|
// Update the Ccb to show the current state of the enumeration.
|
|
//
|
|
|
|
CdLockFcb( IrpContext, Fcb );
|
|
|
|
Ccb->CurrentDirentOffset = ThisDirent->DirentOffset;
|
|
|
|
ClearFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
|
|
|
|
if (ReturnNextEntry) {
|
|
|
|
SetFlag( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
|
|
}
|
|
|
|
CdUnlockFcb( IrpContext, Fcb );
|
|
}
|
|
|
|
//
|
|
// Release the Fcb.
|
|
//
|
|
|
|
CdReleaseFile( IrpContext, Fcb );
|
|
} _SEH2_END;
|
|
|
|
//
|
|
// Complete the request here.
|
|
//
|
|
|
|
Irp->IoStatus.Information = Information;
|
|
|
|
CdCompleteRequest( IrpContext, Irp, Status );
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routines
|
|
//
|
|
|
|
_Requires_lock_held_(_Global_critical_region_)
|
|
NTSTATUS
|
|
CdNotifyChangeDirectory (
|
|
_Inout_ PIRP_CONTEXT IrpContext,
|
|
_Inout_ PIRP Irp,
|
|
_In_ PIO_STACK_LOCATION IrpSp,
|
|
_In_ PCCB Ccb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the notify change directory operation. It is
|
|
responsible for either completing of enqueuing the input Irp. Although there
|
|
will never be a notify signalled on a CDROM disk we still support this call.
|
|
|
|
We have already checked that this is not an OpenById handle.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp to process
|
|
|
|
IrpSp - Io stack location for this request.
|
|
|
|
Ccb - Handle to the directory being watched.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - STATUS_PENDING, any other error will raise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Always set the wait bit in the IrpContext so the initial wait can't fail.
|
|
//
|
|
|
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
|
|
|
|
//
|
|
// Acquire the Vcb shared.
|
|
//
|
|
|
|
CdAcquireVcbShared( IrpContext, IrpContext->Vcb, FALSE );
|
|
|
|
//
|
|
// Use a try-finally to facilitate cleanup.
|
|
//
|
|
|
|
_SEH2_TRY {
|
|
|
|
//
|
|
// Verify the Vcb.
|
|
//
|
|
|
|
CdVerifyVcb( IrpContext, IrpContext->Vcb );
|
|
|
|
//
|
|
// Call the Fsrtl package to process the request. We cast the
|
|
// unicode strings to ansi strings as the dir notify package
|
|
// only deals with memory matching.
|
|
//
|
|
|
|
FsRtlNotifyFullChangeDirectory( IrpContext->Vcb->NotifySync,
|
|
&IrpContext->Vcb->DirNotifyList,
|
|
Ccb,
|
|
(PSTRING) &IrpSp->FileObject->FileName,
|
|
BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE ),
|
|
FALSE,
|
|
IrpSp->Parameters.NotifyDirectory.CompletionFilter,
|
|
Irp,
|
|
NULL,
|
|
NULL );
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
//
|
|
// Release the Vcb.
|
|
//
|
|
|
|
CdReleaseVcb( IrpContext, IrpContext->Vcb );
|
|
} _SEH2_END;
|
|
|
|
//
|
|
// Cleanup the IrpContext.
|
|
//
|
|
|
|
CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
VOID
|
|
CdInitializeEnumeration (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PIO_STACK_LOCATION IrpSp,
|
|
_In_ PFCB Fcb,
|
|
_Inout_ PCCB Ccb,
|
|
_Inout_ PFILE_ENUM_CONTEXT FileContext,
|
|
_Out_ PBOOLEAN ReturnNextEntry,
|
|
_Out_ PBOOLEAN ReturnSingleEntry,
|
|
_Out_ PBOOLEAN InitialQuery
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to initialize the enumeration variables and structures.
|
|
We look at the state of a previous enumeration from the Ccb as well as any
|
|
input values from the user. On exit we will position the FileContext at
|
|
a file in the directory and let the caller know whether this entry or the
|
|
next entry should be returned.
|
|
|
|
Arguments:
|
|
|
|
IrpSp - Irp stack location for this request.
|
|
|
|
Fcb - Fcb for this directory.
|
|
|
|
Ccb - Ccb for the directory handle.
|
|
|
|
FileContext - FileContext to use for this enumeration.
|
|
|
|
ReturnNextEntry - Address to store whether we should return the entry at
|
|
the FileContext position or the next entry.
|
|
|
|
ReturnSingleEntry - Address to store whether we should only return
|
|
a single entry.
|
|
|
|
InitialQuery - Address to store whether this is the first enumeration
|
|
query on this handle.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PUNICODE_STRING FileName;
|
|
CD_NAME WildCardName;
|
|
CD_NAME SearchExpression;
|
|
|
|
ULONG CcbFlags;
|
|
|
|
ULONG DirentOffset;
|
|
ULONG LastDirentOffset;
|
|
BOOLEAN KnownOffset;
|
|
|
|
BOOLEAN Found;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If the user has specified that the scan be restarted, and has specicified
|
|
// a new query pattern, reinitialize the CCB.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) {
|
|
|
|
CdLockFcb( IrpContext, Fcb );
|
|
|
|
FileName = (PUNICODE_STRING) IrpSp->Parameters.QueryDirectory.FileName;
|
|
if (FileName && FileName->Length > 0) {
|
|
|
|
if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL )) {
|
|
|
|
CdFreePool( &Ccb->SearchExpression.FileName.Buffer );
|
|
}
|
|
|
|
ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL);
|
|
ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED);
|
|
ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD);
|
|
}
|
|
|
|
CdUnlockFcb( IrpContext, Fcb );
|
|
}
|
|
|
|
//
|
|
// If this is the initial query then build a search expression from the input
|
|
// file name.
|
|
//
|
|
|
|
if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) {
|
|
|
|
FileName = IrpSp->Parameters.QueryDirectory.FileName;
|
|
|
|
CcbFlags = 0;
|
|
|
|
//
|
|
// If the filename is not specified or is a single '*' then we will
|
|
// match all names.
|
|
//
|
|
|
|
if ((FileName == NULL) ||
|
|
(FileName->Buffer == NULL) ||
|
|
(FileName->Length == 0) ||
|
|
((FileName->Length == sizeof( WCHAR )) &&
|
|
(FileName->Buffer[0] == L'*'))) {
|
|
|
|
SetFlag( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL );
|
|
RtlZeroMemory( &SearchExpression, sizeof( SearchExpression ));
|
|
|
|
//
|
|
// Otherwise build the CdName from the name in the stack location.
|
|
// This involves building both the name and version portions and
|
|
// checking for wild card characters. We also upcase the string if
|
|
// this is a case-insensitive search.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Create a CdName to check for wild cards.
|
|
//
|
|
|
|
WildCardName.FileName = *FileName;
|
|
|
|
CdConvertNameToCdName( IrpContext, &WildCardName );
|
|
|
|
//
|
|
// The name better have at least one character.
|
|
//
|
|
|
|
if (WildCardName.FileName.Length == 0) {
|
|
|
|
CdRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER );
|
|
}
|
|
|
|
//
|
|
// Check for wildcards in the separate components.
|
|
//
|
|
|
|
if (FsRtlDoesNameContainWildCards( &WildCardName.FileName)) {
|
|
|
|
SetFlag( CcbFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD );
|
|
}
|
|
|
|
if ((WildCardName.VersionString.Length != 0) &&
|
|
(FsRtlDoesNameContainWildCards( &WildCardName.VersionString ))) {
|
|
|
|
SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD );
|
|
|
|
//
|
|
// Check if this is a wild card only and match all version
|
|
// strings.
|
|
//
|
|
|
|
if ((WildCardName.VersionString.Length == sizeof( WCHAR )) &&
|
|
(WildCardName.VersionString.Buffer[0] == L'*')) {
|
|
|
|
SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now create the search expression to store in the Ccb.
|
|
//
|
|
|
|
SearchExpression.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool,
|
|
FileName->Length,
|
|
TAG_ENUM_EXPRESSION );
|
|
|
|
SearchExpression.FileName.MaximumLength = FileName->Length;
|
|
|
|
//
|
|
// Either copy the name directly or perform the upcase.
|
|
//
|
|
|
|
if (FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )) {
|
|
|
|
Status = RtlUpcaseUnicodeString( (PUNICODE_STRING) &SearchExpression.FileName,
|
|
FileName,
|
|
FALSE );
|
|
|
|
//
|
|
// This should never fail.
|
|
//
|
|
__analysis_assert( Status == STATUS_SUCCESS );
|
|
NT_ASSERT( Status == STATUS_SUCCESS );
|
|
|
|
} else {
|
|
|
|
RtlCopyMemory( SearchExpression.FileName.Buffer,
|
|
FileName->Buffer,
|
|
FileName->Length );
|
|
}
|
|
|
|
//
|
|
// Now split into the separate name and version components.
|
|
//
|
|
|
|
SearchExpression.FileName.Length = WildCardName.FileName.Length;
|
|
SearchExpression.VersionString.Length = WildCardName.VersionString.Length;
|
|
SearchExpression.VersionString.MaximumLength = WildCardName.VersionString.MaximumLength;
|
|
|
|
SearchExpression.VersionString.Buffer = Add2Ptr( SearchExpression.FileName.Buffer,
|
|
SearchExpression.FileName.Length + sizeof( WCHAR ),
|
|
PWCHAR );
|
|
}
|
|
|
|
//
|
|
// But we do not want to return the constant "." and ".." entries for
|
|
// the root directory, for consistency with the rest of Microsoft's
|
|
// filesystems.
|
|
//
|
|
|
|
if (Fcb == Fcb->Vcb->RootIndexFcb) {
|
|
|
|
SetFlag( CcbFlags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY );
|
|
}
|
|
|
|
//
|
|
// Now lock the Fcb in order to update the Ccb with the inital
|
|
// enumeration values.
|
|
//
|
|
|
|
CdLockFcb( IrpContext, Fcb );
|
|
|
|
//
|
|
// Check again that this is the initial search.
|
|
//
|
|
|
|
if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) {
|
|
|
|
//
|
|
// Update the values in the Ccb.
|
|
//
|
|
|
|
Ccb->CurrentDirentOffset = Fcb->StreamOffset;
|
|
Ccb->SearchExpression = SearchExpression;
|
|
|
|
//
|
|
// Set the appropriate flags in the Ccb.
|
|
//
|
|
|
|
SetFlag( Ccb->Flags, CcbFlags | CCB_FLAG_ENUM_INITIALIZED );
|
|
|
|
//
|
|
// Otherwise cleanup any buffer allocated here.
|
|
//
|
|
|
|
} else {
|
|
|
|
if (!FlagOn( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL )) {
|
|
|
|
CdFreePool( &SearchExpression.FileName.Buffer );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Otherwise lock the Fcb so we can read the current enumeration values.
|
|
//
|
|
|
|
} else {
|
|
|
|
CdLockFcb( IrpContext, Fcb );
|
|
}
|
|
|
|
//
|
|
// Capture the current state of the enumeration.
|
|
//
|
|
// If the user specified an index then use his offset. We always
|
|
// return the next entry in this case.
|
|
//
|
|
|
|
if (FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED )) {
|
|
|
|
KnownOffset = FALSE;
|
|
DirentOffset = IrpSp->Parameters.QueryDirectory.FileIndex;
|
|
*ReturnNextEntry = TRUE;
|
|
|
|
//
|
|
// If we are restarting the scan then go from the self entry.
|
|
//
|
|
|
|
} else if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) {
|
|
|
|
KnownOffset = TRUE;
|
|
DirentOffset = Fcb->StreamOffset;
|
|
*ReturnNextEntry = FALSE;
|
|
|
|
//
|
|
// Otherwise use the values from the Ccb.
|
|
//
|
|
|
|
} else {
|
|
|
|
KnownOffset = TRUE;
|
|
DirentOffset = Ccb->CurrentDirentOffset;
|
|
*ReturnNextEntry = BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
|
|
}
|
|
|
|
//
|
|
// Unlock the Fcb.
|
|
//
|
|
|
|
CdUnlockFcb( IrpContext, Fcb );
|
|
|
|
//
|
|
// We have the starting offset in the directory and whether to return
|
|
// that entry or the next. If we are at the beginning of the directory
|
|
// and are returning that entry, then tell our caller this is the
|
|
// initial query.
|
|
//
|
|
|
|
*InitialQuery = FALSE;
|
|
|
|
if ((DirentOffset == Fcb->StreamOffset) &&
|
|
!(*ReturnNextEntry)) {
|
|
|
|
*InitialQuery = TRUE;
|
|
}
|
|
|
|
//
|
|
// If there is no file object then create it now.
|
|
//
|
|
|
|
CdVerifyOrCreateDirStreamFile( IrpContext, Fcb);
|
|
|
|
//
|
|
// Determine the offset in the stream to position the FileContext and
|
|
// whether this offset is known to be a file offset.
|
|
//
|
|
// If this offset is known to be safe then go ahead and position the
|
|
// file context. This handles the cases where the offset is the beginning
|
|
// of the stream, the offset is from a previous search or this is the
|
|
// initial query.
|
|
//
|
|
|
|
if (KnownOffset) {
|
|
|
|
CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, DirentOffset );
|
|
|
|
//
|
|
// Otherwise we walk through the directory from the beginning until
|
|
// we reach the entry which contains this offset.
|
|
//
|
|
|
|
} else {
|
|
|
|
LastDirentOffset = Fcb->StreamOffset;
|
|
Found = TRUE;
|
|
|
|
CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset );
|
|
|
|
//
|
|
// If the requested offset is prior to the beginning offset in the stream
|
|
// then don't return the next entry.
|
|
//
|
|
|
|
if (DirentOffset < LastDirentOffset) {
|
|
|
|
*ReturnNextEntry = FALSE;
|
|
|
|
//
|
|
// Else look for the last entry which ends past the desired index.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Keep walking through the directory until we run out of
|
|
// entries or we find an entry which ends beyond the input
|
|
// index value.
|
|
//
|
|
|
|
do {
|
|
|
|
//
|
|
// If we have passed the index value then exit.
|
|
//
|
|
|
|
if (FileContext->InitialDirent->Dirent.DirentOffset > DirentOffset) {
|
|
|
|
Found = FALSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Remember the current position in case we need to go back.
|
|
//
|
|
|
|
LastDirentOffset = FileContext->InitialDirent->Dirent.DirentOffset;
|
|
|
|
//
|
|
// Exit if the next entry is beyond the desired index value.
|
|
//
|
|
|
|
if (LastDirentOffset + FileContext->InitialDirent->Dirent.DirentLength > DirentOffset) {
|
|
|
|
break;
|
|
}
|
|
|
|
Found = CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext );
|
|
|
|
} while (Found);
|
|
|
|
//
|
|
// If we didn't find the entry then go back to the last known entry.
|
|
// This can happen if the index lies in the unused range at the
|
|
// end of a sector.
|
|
//
|
|
|
|
if (!Found) {
|
|
|
|
CdCleanupFileContext( IrpContext, FileContext );
|
|
CdInitializeFileContext( IrpContext, FileContext );
|
|
|
|
CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Only update the dirent name if we will need it for some reason.
|
|
// Don't update this name if we are returning the next entry and
|
|
// the search string has a version component.
|
|
//
|
|
|
|
FileContext->ShortName.FileName.Length = 0;
|
|
|
|
if (!(*ReturnNextEntry) ||
|
|
(Ccb->SearchExpression.VersionString.Length == 0)) {
|
|
|
|
//
|
|
// Update the name in the dirent into filename and version components.
|
|
//
|
|
|
|
CdUpdateDirentName( IrpContext,
|
|
&FileContext->InitialDirent->Dirent,
|
|
FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
|
|
}
|
|
|
|
//
|
|
// Look at the flag in the IrpSp indicating whether to return just
|
|
// one entry.
|
|
//
|
|
|
|
*ReturnSingleEntry = FALSE;
|
|
|
|
if (FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )) {
|
|
|
|
*ReturnSingleEntry = TRUE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Local support routine
|
|
//
|
|
|
|
BOOLEAN
|
|
CdEnumerateIndex (
|
|
_In_ PIRP_CONTEXT IrpContext,
|
|
_In_ PCCB Ccb,
|
|
_Inout_ PFILE_ENUM_CONTEXT FileContext,
|
|
_In_ BOOLEAN ReturnNextEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the worker routine for index enumeration. We are positioned
|
|
at some dirent in the directory and will either return the first match
|
|
at that point or look to the next entry. The Ccb contains details about
|
|
the type of matching to do. If the user didn't specify a version in
|
|
his search string then we only return the first version of a sequence
|
|
of files with versions. We also don't return any associated files.
|
|
|
|
Arguments:
|
|
|
|
Ccb - Ccb for this directory handle.
|
|
|
|
FileContext - File context already positioned at some entry in the directory.
|
|
|
|
ReturnNextEntry - Indicates if we are returning this entry or should start
|
|
with the next entry.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if next entry is found, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDIRENT PreviousDirent = NULL;
|
|
PDIRENT ThisDirent = &FileContext->InitialDirent->Dirent;
|
|
|
|
BOOLEAN Found = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Loop until we find a match or exaust the directory.
|
|
//
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// Move to the next entry unless we want to consider the current
|
|
// entry.
|
|
//
|
|
|
|
if (ReturnNextEntry) {
|
|
|
|
if (!CdLookupNextInitialFileDirent( IrpContext, Ccb->Fcb, FileContext )) {
|
|
|
|
break;
|
|
}
|
|
|
|
PreviousDirent = ThisDirent;
|
|
ThisDirent = &FileContext->InitialDirent->Dirent;
|
|
|
|
CdUpdateDirentName( IrpContext, ThisDirent, FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
|
|
|
|
} else {
|
|
|
|
ReturnNextEntry = TRUE;
|
|
}
|
|
|
|
//
|
|
// Don't bother if we have a constant entry and are ignoring them.
|
|
//
|
|
|
|
if (FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ) &&
|
|
FlagOn( Ccb->Flags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY )) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Look at the current entry if it is not an associated file
|
|
// and the name doesn't match the previous file if the version
|
|
// name is not part of the search.
|
|
//
|
|
|
|
if (!FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_ASSOC )) {
|
|
|
|
//
|
|
// Check if this entry matches the previous entry except
|
|
// for version number and whether we should return the
|
|
// entry in that case. Go directly to the name comparison
|
|
// if:
|
|
//
|
|
// There is no previous entry.
|
|
// The search expression has a version component.
|
|
// The name length doesn't match the length of the previous entry.
|
|
// The base name strings don't match.
|
|
//
|
|
|
|
if ((PreviousDirent == NULL) ||
|
|
(Ccb->SearchExpression.VersionString.Length != 0) ||
|
|
(PreviousDirent->CdCaseFileName.FileName.Length != ThisDirent->CdCaseFileName.FileName.Length) ||
|
|
FlagOn( PreviousDirent->DirentFlags, CD_ATTRIBUTE_ASSOC ) ||
|
|
!RtlEqualMemory( PreviousDirent->CdCaseFileName.FileName.Buffer,
|
|
ThisDirent->CdCaseFileName.FileName.Buffer,
|
|
ThisDirent->CdCaseFileName.FileName.Length )) {
|
|
|
|
//
|
|
// If we match all names then return to our caller.
|
|
//
|
|
|
|
if (FlagOn( Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL )) {
|
|
|
|
FileContext->ShortName.FileName.Length = 0;
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if the long name matches the search expression.
|
|
//
|
|
|
|
if (CdIsNameInExpression( IrpContext,
|
|
&ThisDirent->CdCaseFileName,
|
|
&Ccb->SearchExpression,
|
|
Ccb->Flags,
|
|
TRUE )) {
|
|
|
|
//
|
|
// Let our caller know we found an entry.
|
|
//
|
|
|
|
Found = TRUE;
|
|
FileContext->ShortName.FileName.Length = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The long name didn't match so we need to check for a
|
|
// possible short name match. There is no match if the
|
|
// long name is 8dot3 or the search expression has a
|
|
// version component. Special case the self and parent
|
|
// entries.
|
|
//
|
|
|
|
if ((Ccb->SearchExpression.VersionString.Length == 0) &&
|
|
!FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ) &&
|
|
!CdIs8dot3Name( IrpContext,
|
|
ThisDirent->CdFileName.FileName )) {
|
|
|
|
CdGenerate8dot3Name( IrpContext,
|
|
&ThisDirent->CdCaseFileName.FileName,
|
|
ThisDirent->DirentOffset,
|
|
FileContext->ShortName.FileName.Buffer,
|
|
&FileContext->ShortName.FileName.Length );
|
|
|
|
//
|
|
// Check if this name matches.
|
|
//
|
|
|
|
if (CdIsNameInExpression( IrpContext,
|
|
&FileContext->ShortName,
|
|
&Ccb->SearchExpression,
|
|
Ccb->Flags,
|
|
FALSE )) {
|
|
|
|
//
|
|
// Let our caller know we found an entry.
|
|
//
|
|
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we found the entry then make sure we walk through all of the
|
|
// file dirents.
|
|
//
|
|
|
|
if (Found) {
|
|
|
|
CdLookupLastFileDirent( IrpContext, Ccb->Fcb, FileContext );
|
|
}
|
|
|
|
return Found;
|
|
}
|
|
|
|
|