1263 lines
34 KiB
C
1263 lines
34 KiB
C
/*++
|
||
|
||
Copyright (c) 1996-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
DirSup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the support for walking across on-disk directory
|
||
structures.
|
||
|
||
// @@BEGIN_DDKSPLIT
|
||
|
||
Author:
|
||
|
||
Dan Lovinger [DanLo] 11-Jun-1996
|
||
|
||
Revision History:
|
||
|
||
// @@END_DDKSPLIT
|
||
|
||
--*/
|
||
|
||
#include "UdfProcs.h"
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (UDFS_BUG_CHECK_DIRSUP)
|
||
|
||
//
|
||
// The local debug trace level
|
||
//
|
||
|
||
#define Dbg (UDFS_DEBUG_LEVEL_DIRSUP)
|
||
|
||
//
|
||
// Local support routines.
|
||
//
|
||
|
||
BOOLEAN
|
||
UdfLookupDirEntryPostProcessing (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PDIR_ENUM_CONTEXT DirContext,
|
||
IN BOOLEAN ReturnError
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, UdfCleanupDirContext)
|
||
#pragma alloc_text(PAGE, UdfFindDirEntry)
|
||
#pragma alloc_text(PAGE, UdfInitializeDirContext)
|
||
#pragma alloc_text(PAGE, UdfLookupDirEntryPostProcessing)
|
||
#pragma alloc_text(PAGE, UdfLookupInitialDirEntry)
|
||
#pragma alloc_text(PAGE, UdfLookupNextDirEntry)
|
||
#pragma alloc_text(PAGE, UdfUpdateDirNames)
|
||
#endif
|
||
|
||
|
||
VOID
|
||
UdfInitializeDirContext (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDIR_ENUM_CONTEXT DirContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes a directory enumeartion context.
|
||
|
||
Call this exactly once in the lifetime of a context.
|
||
|
||
Arguments:
|
||
|
||
DirContext - a context to initialize
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
//
|
||
// Check inputs.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
//
|
||
// Provide defaults for fields, nothing too special.
|
||
//
|
||
|
||
RtlZeroMemory( DirContext, sizeof(DIR_ENUM_CONTEXT) );
|
||
}
|
||
|
||
|
||
VOID
|
||
UdfCleanupDirContext (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDIR_ENUM_CONTEXT DirContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine cleans up a directory enumeration context for reuse.
|
||
|
||
Arguments:
|
||
|
||
DirContext - a context to clean.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check input.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
//
|
||
// Dump the allocation we store the triple of names in.
|
||
//
|
||
|
||
UdfFreePool( &DirContext->NameBuffer );
|
||
|
||
//
|
||
// And the short name.
|
||
//
|
||
|
||
UdfFreePool( &DirContext->ShortObjectName.Buffer );
|
||
|
||
//
|
||
// Unpin the view.
|
||
//
|
||
|
||
UdfUnpinData( IrpContext, &DirContext->Bcb );
|
||
|
||
//
|
||
// Free a buffered Fid that may remain.
|
||
//
|
||
|
||
if (FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_FID_BUFFERED )) {
|
||
|
||
UdfFreePool( &DirContext->Fid );
|
||
}
|
||
|
||
//
|
||
// Zero everything else out.
|
||
//
|
||
|
||
RtlZeroMemory( DirContext, sizeof( DIR_ENUM_CONTEXT ) );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
UdfLookupInitialDirEntry (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PDIR_ENUM_CONTEXT DirContext,
|
||
IN PLONGLONG InitialOffset OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine begins the enumeration of a directory by setting the context
|
||
at the first avaliable directory entry.
|
||
|
||
Arguments:
|
||
|
||
Fcb - the directory being enumerated.
|
||
|
||
DirContext - a corresponding context for the enumeration.
|
||
|
||
InitialOffset - an optional starting byte offset to base the enumeration.
|
||
|
||
Return Value:
|
||
|
||
If InitialOffset is unspecified, TRUE will always be returned. Failure will result
|
||
in a raised status indicating corruption.
|
||
|
||
If InitialOffset is specified, TRUE will be returned if a valid entry is found at this
|
||
offset, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN Result;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check inputs.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB_INDEX( Fcb );
|
||
|
||
//
|
||
// Create the internal stream if it isn't already in place.
|
||
//
|
||
|
||
if (Fcb->FileObject == NULL) {
|
||
|
||
UdfCreateInternalStream( IrpContext, Fcb->Vcb, Fcb );
|
||
}
|
||
|
||
//
|
||
// Reset the flags.
|
||
//
|
||
|
||
DirContext->Flags = 0;
|
||
|
||
if (InitialOffset) {
|
||
|
||
//
|
||
// If we are beginning in the middle of the stream, adjust the sanity check flags.
|
||
//
|
||
|
||
if (*InitialOffset != 0) {
|
||
|
||
DirContext->Flags = DIR_CONTEXT_FLAG_SEEN_NONCONSTANT | DIR_CONTEXT_FLAG_SEEN_PARENT;
|
||
}
|
||
|
||
//
|
||
// Now set up the range we will map. This is constrained by the size of a cache view.
|
||
//
|
||
|
||
DirContext->BaseOffset.QuadPart = GenericTruncate( *InitialOffset, VACB_MAPPING_GRANULARITY );
|
||
DirContext->ViewOffset = (ULONG) GenericOffset( *InitialOffset, VACB_MAPPING_GRANULARITY );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Map at the beginning.
|
||
//
|
||
|
||
DirContext->BaseOffset.QuadPart = 0;
|
||
DirContext->ViewOffset = 0;
|
||
}
|
||
|
||
//
|
||
// Contain the view length by the size of the stream and map.
|
||
//
|
||
|
||
DirContext->ViewLength = VACB_MAPPING_GRANULARITY;
|
||
|
||
if (DirContext->BaseOffset.QuadPart + DirContext->ViewLength > Fcb->FileSize.QuadPart) {
|
||
|
||
DirContext->ViewLength = (ULONG) (Fcb->FileSize.QuadPart - DirContext->BaseOffset.QuadPart);
|
||
}
|
||
|
||
UdfUnpinData( IrpContext, &DirContext->Bcb );
|
||
|
||
CcMapData( Fcb->FileObject,
|
||
&DirContext->BaseOffset,
|
||
DirContext->ViewLength,
|
||
TRUE,
|
||
&DirContext->Bcb,
|
||
&DirContext->View );
|
||
|
||
DirContext->Fid = Add2Ptr( DirContext->View, DirContext->ViewOffset, PNSR_FID );
|
||
|
||
//
|
||
// The state of the context is now valid. Tail off into our common post-processor
|
||
// to finish the work.
|
||
//
|
||
|
||
return UdfLookupDirEntryPostProcessing( IrpContext,
|
||
Fcb,
|
||
DirContext,
|
||
(BOOLEAN) (InitialOffset != NULL));
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
UdfLookupNextDirEntry (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PDIR_ENUM_CONTEXT DirContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine advances the enumeration of a directory by one entry.
|
||
|
||
Arguments:
|
||
|
||
Fcb - the directory being enumerated.
|
||
|
||
DirContext - a corresponding context for the enumeration.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN True if another Fid is avaliable, False if we are at the end.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check inputs.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB_INDEX( Fcb );
|
||
|
||
//
|
||
// If we have reached the end, stop.
|
||
//
|
||
|
||
if (DirContext->BaseOffset.QuadPart + DirContext->NextFidOffset == Fcb->FileSize.QuadPart) {
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// If the previous Fid was buffered, dismantle it now.
|
||
//
|
||
|
||
if (FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_FID_BUFFERED )) {
|
||
|
||
ClearFlag( DirContext->Flags, DIR_CONTEXT_FLAG_FID_BUFFERED );
|
||
UdfFreePool( &DirContext->Fid );
|
||
}
|
||
|
||
//
|
||
// Move the pointers based on the knowledge generated in the previous iteration.
|
||
//
|
||
|
||
DirContext->ViewOffset = DirContext->NextFidOffset;
|
||
DirContext->Fid = Add2Ptr( DirContext->View, DirContext->ViewOffset, PNSR_FID );
|
||
|
||
//
|
||
// The state of the context is now valid. Tail off into our common post-processor
|
||
// to finish the work.
|
||
//
|
||
|
||
return UdfLookupDirEntryPostProcessing( IrpContext,
|
||
Fcb,
|
||
DirContext,
|
||
FALSE );
|
||
}
|
||
|
||
|
||
VOID
|
||
UdfUpdateDirNames (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDIR_ENUM_CONTEXT DirContext,
|
||
IN BOOLEAN IgnoreCase
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine fills in the non-short names of a directory enumeration context
|
||
for the Fid currently referenced.
|
||
|
||
Arguments:
|
||
|
||
DirContext - a corresponding context to fill in.
|
||
|
||
IgnoreCase - whether the caller wants to be insensitive to case.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PUCHAR NameDstring;
|
||
BOOLEAN ContainsIllegal;
|
||
|
||
USHORT NameLength;
|
||
USHORT RequiredBufferLength;
|
||
USHORT PresentLength;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check input.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
|
||
DebugTrace(( +1, Dbg, "UdfUpdateDirNames\n" ));
|
||
|
||
//
|
||
// Handle the case of the self directory entry.
|
||
//
|
||
|
||
if (DirContext->Fid == NULL) {
|
||
|
||
//
|
||
// Simply synthesize
|
||
//
|
||
|
||
//
|
||
// It doesn't hurt to be pedantic about initialization, so do it all.
|
||
//
|
||
|
||
DirContext->PureObjectName.Length =
|
||
DirContext->CaseObjectName.Length =
|
||
DirContext->ObjectName.Length = UdfUnicodeDirectoryNames[SELF_ENTRY].Length;
|
||
|
||
DirContext->PureObjectName.MaximumLength =
|
||
DirContext->CaseObjectName.MaximumLength =
|
||
DirContext->ObjectName.MaximumLength = UdfUnicodeDirectoryNames[SELF_ENTRY].MaximumLength;
|
||
|
||
DirContext->PureObjectName.Buffer =
|
||
DirContext->CaseObjectName.Buffer =
|
||
DirContext->ObjectName.Buffer = UdfUnicodeDirectoryNames[SELF_ENTRY].Buffer;
|
||
|
||
//
|
||
// All done.
|
||
//
|
||
|
||
DebugTrace(( 0, Dbg, "Self Entry case\n" ));
|
||
DebugTrace(( -1, Dbg, "UdfUpdateDirNames -> VOID\n" ));
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Handle the case of the parent directory entry.
|
||
//
|
||
|
||
if (FlagOn( DirContext->Fid->Flags, NSR_FID_F_PARENT )) {
|
||
|
||
//
|
||
// Parent entries must occur at the front of the directory and
|
||
// have a fid length of zero (13346 4/14.4.4).
|
||
//
|
||
|
||
if (FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT ) ||
|
||
DirContext->Fid->FileIDLen != 0) {
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
//
|
||
// Note that we have seen the parent entry.
|
||
//
|
||
|
||
SetFlag( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_PARENT );
|
||
|
||
//
|
||
// It doesn't hurt to be pedantic about initialization, so do it all.
|
||
//
|
||
|
||
DirContext->PureObjectName.Length =
|
||
DirContext->CaseObjectName.Length =
|
||
DirContext->ObjectName.Length = UdfUnicodeDirectoryNames[PARENT_ENTRY].Length;
|
||
|
||
DirContext->PureObjectName.MaximumLength =
|
||
DirContext->CaseObjectName.MaximumLength =
|
||
DirContext->ObjectName.MaximumLength = UdfUnicodeDirectoryNames[PARENT_ENTRY].MaximumLength;
|
||
|
||
DirContext->PureObjectName.Buffer =
|
||
DirContext->CaseObjectName.Buffer =
|
||
DirContext->ObjectName.Buffer = UdfUnicodeDirectoryNames[PARENT_ENTRY].Buffer;
|
||
|
||
//
|
||
// All done.
|
||
//
|
||
|
||
DebugTrace(( 0, Dbg, "Parent Entry case\n" ));
|
||
DebugTrace(( -1, Dbg, "UdfUpdateDirNames -> VOID\n" ));
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// We now know that we will need to convert the name in a real FID, so figure out where
|
||
// it sits in the descriptor.
|
||
//
|
||
|
||
NameDstring = Add2Ptr( DirContext->Fid, ISONsrFidConstantSize + DirContext->Fid->ImpUseLen, PUCHAR );
|
||
|
||
//
|
||
// Every directory must record a parent entry.
|
||
//
|
||
|
||
if (!FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_PARENT)) {
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
//
|
||
// Note that we are proceeding into the non-constant portion of a directory.
|
||
//
|
||
|
||
SetFlag( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT );
|
||
|
||
//
|
||
// Make sure the dstring is good CS0
|
||
//
|
||
|
||
UdfCheckLegalCS0Dstring( IrpContext,
|
||
NameDstring,
|
||
DirContext->Fid->FileIDLen,
|
||
0,
|
||
FALSE );
|
||
|
||
//
|
||
// Don't bother allocating tiny buffers - always make sure we get enough for an 8.3 name.
|
||
//
|
||
|
||
RequiredBufferLength =
|
||
NameLength = Max( BYTE_COUNT_8_DOT_3, UdfCS0DstringUnicodeSize( IrpContext,
|
||
NameDstring,
|
||
DirContext->Fid->FileIDLen) );
|
||
|
||
//
|
||
// Illegality is both actual illegal characters and too many characters.
|
||
//
|
||
|
||
ContainsIllegal = (!UdfCS0DstringIsLegalFileName( NameDstring, DirContext->Fid->FileIDLen ) ||
|
||
(NameLength / sizeof( WCHAR )) > MAX_LEN);
|
||
|
||
|
||
//
|
||
// If we're illegal, we will need more characters to hold the uniqifying stamp.
|
||
//
|
||
|
||
if (ContainsIllegal) {
|
||
|
||
RequiredBufferLength = (NameLength += (CRC_LEN * sizeof(WCHAR)));
|
||
}
|
||
|
||
|
||
//
|
||
// If we need to build a case insensitive name, need more space.
|
||
//
|
||
|
||
if (IgnoreCase) {
|
||
|
||
RequiredBufferLength += NameLength;
|
||
}
|
||
|
||
//
|
||
// If we need to render the names due to illegal characters, more space again.
|
||
//
|
||
|
||
if (ContainsIllegal) {
|
||
|
||
RequiredBufferLength += NameLength;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Make sure the names aren't seperated. If more illegal names are found we can
|
||
// resplit the buffer but until then avoid the expense of having to copy bytes
|
||
// ... odds are that illegal characters are going to be a rarish occurance.
|
||
//
|
||
|
||
DirContext->PureObjectName.Buffer = DirContext->ObjectName.Buffer;
|
||
}
|
||
|
||
//
|
||
// We expect the name lengths and hence buffer size to be multiple of WCHAR
|
||
//
|
||
|
||
ASSERT( 0 == (RequiredBufferLength & 1));
|
||
|
||
DebugTrace(( 0, Dbg,
|
||
"Ob %s%sneeds %d bytes (%d byte chunks), have %d\n",
|
||
(IgnoreCase? "Ic " : ""),
|
||
(ContainsIllegal? "Ci " : ""),
|
||
RequiredBufferLength,
|
||
NameLength,
|
||
DirContext->AllocLength ));
|
||
|
||
//
|
||
// Check if we need more space for the names. We will need more if the name size is greater
|
||
// than the maximum we can currently store, or if we have stumbled across illegal characters
|
||
// and the current Pure name is not seperated from the exposed Object name.
|
||
//
|
||
// Note that IgnoreCase remains constant across usage of a context so we don't have to wonder
|
||
// if it has been seperated from the ObjectName - it'll always be correct.
|
||
//
|
||
|
||
if ((NameLength > DirContext->ObjectName.MaximumLength) ||
|
||
(ContainsIllegal && (DirContext->ObjectName.Buffer == DirContext->PureObjectName.Buffer))) {
|
||
|
||
USHORT DividedBufferLength = 0;
|
||
|
||
DebugTrace(( 0, Dbg, "Resizing buffers\n" ));
|
||
|
||
//
|
||
// Figure out if we can break up the current allocation in a different way before falling
|
||
// back to a new allocation. Ensure we use even byte size chunks, or else we can land
|
||
// up with alignment faults on IA64.
|
||
//
|
||
|
||
if (DirContext->AllocLength >= RequiredBufferLength) {
|
||
|
||
DividedBufferLength = (DirContext->AllocLength / (1 +
|
||
(IgnoreCase? 1 : 0) +
|
||
(ContainsIllegal? 1 : 0))) & ~(USHORT)1;
|
||
}
|
||
|
||
if (DividedBufferLength >= NameLength) {
|
||
|
||
//
|
||
// So we can still use the current allocation, re-divided.
|
||
//
|
||
|
||
DirContext->PureObjectName.MaximumLength =
|
||
DirContext->CaseObjectName.MaximumLength =
|
||
DirContext->ObjectName.MaximumLength = DividedBufferLength;
|
||
|
||
DebugTrace(( 0, Dbg,
|
||
"... by resplit into %d byte chunks\n",
|
||
DirContext->ObjectName.MaximumLength ));
|
||
|
||
//
|
||
// Set the buffer pointers up. Required adjustment will occur below.
|
||
//
|
||
|
||
DirContext->PureObjectName.Buffer =
|
||
DirContext->CaseObjectName.Buffer =
|
||
DirContext->ObjectName.Buffer = DirContext->NameBuffer;
|
||
|
||
} else {
|
||
|
||
DebugTrace(( 0, Dbg, "... by allocating new pool\n" ));
|
||
|
||
//
|
||
// Oh well, no choice but to fall back into the pool. Drop our previous hunk.
|
||
//
|
||
|
||
UdfFreePool( &DirContext->NameBuffer );
|
||
DirContext->AllocLength = 0;
|
||
|
||
//
|
||
// The names share an allocation for efficiency.
|
||
//
|
||
|
||
DirContext->PureObjectName.MaximumLength =
|
||
DirContext->CaseObjectName.MaximumLength =
|
||
DirContext->ObjectName.MaximumLength = NameLength;
|
||
|
||
DirContext->NameBuffer =
|
||
DirContext->PureObjectName.Buffer =
|
||
DirContext->CaseObjectName.Buffer =
|
||
DirContext->ObjectName.Buffer = FsRtlAllocatePoolWithTag( UdfPagedPool,
|
||
RequiredBufferLength,
|
||
TAG_FILE_NAME );
|
||
DirContext->AllocLength = RequiredBufferLength;
|
||
}
|
||
|
||
//
|
||
// In the presence of the "as appropriate" names, adjust the buffer locations. Note
|
||
// that ObjectName.Buffer is always the base of the allocated space.
|
||
//
|
||
|
||
if (IgnoreCase) {
|
||
|
||
DirContext->CaseObjectName.Buffer = Add2Ptr( DirContext->ObjectName.Buffer,
|
||
DirContext->ObjectName.MaximumLength,
|
||
PWCHAR );
|
||
}
|
||
|
||
if (ContainsIllegal) {
|
||
|
||
DirContext->PureObjectName.Buffer = Add2Ptr( DirContext->CaseObjectName.Buffer,
|
||
DirContext->CaseObjectName.MaximumLength,
|
||
PWCHAR );
|
||
}
|
||
}
|
||
|
||
ASSERT( RequiredBufferLength <= DirContext->AllocLength );
|
||
|
||
//
|
||
// Convert the dstring.
|
||
//
|
||
|
||
UdfConvertCS0DstringToUnicode( IrpContext,
|
||
NameDstring,
|
||
DirContext->Fid->FileIDLen,
|
||
0,
|
||
&DirContext->PureObjectName );
|
||
|
||
//
|
||
// If illegal characters were present, run the name through the UDF transmogrifier.
|
||
//
|
||
|
||
if (ContainsIllegal) {
|
||
|
||
UdfRenderNameToLegalUnicode( IrpContext,
|
||
&DirContext->PureObjectName,
|
||
&DirContext->ObjectName );
|
||
|
||
//
|
||
// The ObjectName is the same as the PureObjectName.
|
||
//
|
||
|
||
} else {
|
||
|
||
DirContext->ObjectName.Length = DirContext->PureObjectName.Length;
|
||
}
|
||
|
||
//
|
||
// Upcase the result if required.
|
||
//
|
||
|
||
if (IgnoreCase) {
|
||
|
||
UdfUpcaseName( IrpContext,
|
||
&DirContext->ObjectName,
|
||
&DirContext->CaseObjectName );
|
||
}
|
||
|
||
DebugTrace(( -1, Dbg, "UdfUpdateDirNames -> VOID\n" ));
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
UdfFindDirEntry (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PUNICODE_STRING Name,
|
||
IN BOOLEAN IgnoreCase,
|
||
IN BOOLEAN ShortName,
|
||
IN PDIR_ENUM_CONTEXT DirContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine walks the directory specified for an entry which matches the input
|
||
criteria.
|
||
|
||
Arguments:
|
||
|
||
Fcb - the directory to search
|
||
|
||
Name - name to search for
|
||
|
||
IgnoreCase - whether this search should be case-insensitive (Name will already
|
||
be upcased)
|
||
|
||
ShortName - whether the name should be searched for according to short name rules
|
||
|
||
DirContext - context structure to use and return results in
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN True if a matching directory entry is being returned, False otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PUNICODE_STRING MatchName;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check inputs.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB_INDEX( Fcb );
|
||
|
||
DebugTrace(( +1, Dbg,
|
||
"UdfFindDirEntry, Fcb=%08x Name=\"%wZ\" Ignore=%u Short=%u, DC=%08x\n",
|
||
Fcb,
|
||
Name,
|
||
IgnoreCase,
|
||
ShortName,
|
||
DirContext ));
|
||
|
||
//
|
||
// Depending on the kind of search we are performing a different flavor of the found name
|
||
// wil be used in the comparison.
|
||
//
|
||
|
||
if (ShortName) {
|
||
|
||
MatchName = &DirContext->ShortObjectName;
|
||
|
||
} else {
|
||
|
||
MatchName = &DirContext->CaseObjectName;
|
||
}
|
||
|
||
|
||
//
|
||
// Go get the first entry.
|
||
//
|
||
|
||
UdfLookupInitialDirEntry( IrpContext,
|
||
Fcb,
|
||
DirContext,
|
||
NULL );
|
||
|
||
//
|
||
// Now loop looking for a good match.
|
||
//
|
||
|
||
do {
|
||
|
||
//
|
||
// If it is deleted, we obviously aren't interested in it.
|
||
//
|
||
|
||
if (FlagOn( DirContext->Fid->Flags, NSR_FID_F_DELETED )) {
|
||
|
||
continue;
|
||
}
|
||
|
||
UdfUpdateDirNames( IrpContext,
|
||
DirContext,
|
||
IgnoreCase );
|
||
|
||
|
||
//
|
||
// If this is a constant entry, just keep going.
|
||
//
|
||
|
||
if (!FlagOn( DirContext->Flags, DIR_CONTEXT_FLAG_SEEN_NONCONSTANT )) {
|
||
|
||
continue;
|
||
}
|
||
|
||
DebugTrace(( 0, Dbg,
|
||
"\"%wZ\" (pure \"%wZ\") @ +%08x\n",
|
||
&DirContext->ObjectName,
|
||
&DirContext->PureObjectName,
|
||
DirContext->ViewOffset ));
|
||
|
||
//
|
||
// If we are searching for generated shortnames, a small subset of the names
|
||
// in the directory are actually candidates for a match. Go get the name.
|
||
//
|
||
|
||
if (ShortName) {
|
||
|
||
//
|
||
// Now, only if this Fid's name is non 8.3 is it neccesary to work with it.
|
||
//
|
||
|
||
if (!UdfIs8dot3Name( IrpContext, DirContext->ObjectName )) {
|
||
|
||
//
|
||
// Allocate the shortname if it isn't already done.
|
||
//
|
||
|
||
if (DirContext->ShortObjectName.Buffer == NULL) {
|
||
|
||
DirContext->ShortObjectName.Buffer = FsRtlAllocatePoolWithTag( UdfPagedPool,
|
||
BYTE_COUNT_8_DOT_3,
|
||
TAG_SHORT_FILE_NAME );
|
||
DirContext->ShortObjectName.MaximumLength = BYTE_COUNT_8_DOT_3;
|
||
}
|
||
|
||
UdfGenerate8dot3Name( IrpContext,
|
||
&DirContext->PureObjectName,
|
||
&DirContext->ShortObjectName );
|
||
|
||
DebugTrace(( 0, Dbg,
|
||
"built shortname \"%wZ\"\n", &DirContext->ShortObjectName ));
|
||
|
||
} else {
|
||
|
||
//
|
||
// As an 8.3 name already, this name will not have caused us to have to generate
|
||
// a short name, so it can't be the case that the caller is looking for it.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (UdfFullCompareNames( IrpContext,
|
||
MatchName,
|
||
Name ) == EqualTo) {
|
||
|
||
//
|
||
// Got a match, so give it up.
|
||
//
|
||
|
||
DebugTrace(( 0, Dbg, "HIT\n" ));
|
||
DebugTrace(( -1, Dbg, "UdfFindDirEntry -> TRUE\n" ));
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
} while ( UdfLookupNextDirEntry( IrpContext,
|
||
Fcb,
|
||
DirContext ));
|
||
|
||
//
|
||
// No match was found.
|
||
//
|
||
|
||
DebugTrace(( -1, Dbg, "UdfFindDirEntry -> FALSE\n" ));
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
//
|
||
// Local support routine
|
||
//
|
||
|
||
BOOLEAN
|
||
UdfLookupDirEntryPostProcessing (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN PDIR_ENUM_CONTEXT DirContext,
|
||
IN BOOLEAN ReturnError
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the core engine of directory stream enumeration. It receives
|
||
a context which has been advanced and does the integrity checks and final
|
||
extraction of the Fid with respect to file cache granularity restrictions.
|
||
|
||
NOTE: we assume that a Fid cannot span a cache view. The maximum size of a
|
||
Fid is just over 32k, so this is a good and likely permanent assumption.
|
||
|
||
Arguments:
|
||
|
||
Fcb - the directory being enumerated.
|
||
|
||
DirContext - a corresponding context for the enumeration.
|
||
|
||
ReturnError - whether errors should be returned (or raised)
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN according to the successful extraction of the Fid. If ReturnError is
|
||
FALSE, then failure will result in a raised status.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN Result = TRUE;
|
||
|
||
PNSR_FID FidBufferC = NULL;
|
||
PNSR_FID FidBuffer = NULL;
|
||
|
||
PNSR_FID FidC;
|
||
PNSR_FID Fid;
|
||
|
||
ULONG FidSize;
|
||
|
||
ULONG FidBytesInPreviousView = 0;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Check inputs.
|
||
//
|
||
|
||
ASSERT_IRP_CONTEXT( IrpContext );
|
||
ASSERT_FCB_INDEX( Fcb );
|
||
|
||
try {
|
||
|
||
//
|
||
// First check that the stream can contain another FID.
|
||
//
|
||
|
||
if (DirContext->BaseOffset.QuadPart +
|
||
DirContext->ViewOffset +
|
||
ISONsrFidConstantSize > Fcb->FileSize.QuadPart) {
|
||
|
||
DebugTrace(( 0, Dbg,
|
||
"UdfLookupDirEntryPostProcessing: DC %p, constant header overlaps end of dir\n",
|
||
DirContext ));
|
||
|
||
try_leave( Result = FALSE );
|
||
}
|
||
|
||
//
|
||
// We now build up the constant portion of the FID for use. It may be the case that
|
||
// this spans a view boundary and must be buffered, or is entirely in the next view
|
||
// and we simply need to advance.
|
||
//
|
||
|
||
if (GenericTruncatePtr( Add2Ptr( DirContext->Fid, ISONsrFidConstantSize - 1, PUCHAR ), VACB_MAPPING_GRANULARITY ) !=
|
||
DirContext->View) {
|
||
|
||
FidBytesInPreviousView = GenericRemainderPtr( DirContext->Fid, VACB_MAPPING_GRANULARITY );
|
||
|
||
//
|
||
// Only buffer if there are really bytes in the previous view.
|
||
//
|
||
|
||
if (FidBytesInPreviousView) {
|
||
|
||
FidC =
|
||
FidBufferC = FsRtlAllocatePoolWithTag( UdfPagedPool,
|
||
ISONsrFidConstantSize,
|
||
TAG_FID_BUFFER );
|
||
|
||
RtlCopyMemory( FidBufferC,
|
||
DirContext->Fid,
|
||
FidBytesInPreviousView );
|
||
}
|
||
|
||
//
|
||
// Now advance into the next view to pick up the rest.
|
||
//
|
||
|
||
DirContext->BaseOffset.QuadPart += VACB_MAPPING_GRANULARITY;
|
||
DirContext->ViewOffset = 0;
|
||
|
||
//
|
||
// Contain the view length by the size of the stream and map.
|
||
//
|
||
|
||
DirContext->ViewLength = VACB_MAPPING_GRANULARITY;
|
||
|
||
if (DirContext->BaseOffset.QuadPart + DirContext->ViewLength > Fcb->FileSize.QuadPart) {
|
||
|
||
DirContext->ViewLength = (ULONG) (Fcb->FileSize.QuadPart - DirContext->BaseOffset.QuadPart);
|
||
}
|
||
|
||
UdfUnpinData( IrpContext, &DirContext->Bcb );
|
||
|
||
CcMapData( Fcb->FileObject,
|
||
&DirContext->BaseOffset,
|
||
DirContext->ViewLength,
|
||
TRUE,
|
||
&DirContext->Bcb,
|
||
&DirContext->View );
|
||
|
||
//
|
||
// We are guaranteed that this much lies in the stream. Build the rest of the
|
||
// constant header.
|
||
//
|
||
|
||
if (FidBytesInPreviousView) {
|
||
|
||
RtlCopyMemory( Add2Ptr( FidBufferC, FidBytesInPreviousView, PUCHAR ),
|
||
DirContext->View,
|
||
ISONsrFidConstantSize - FidBytesInPreviousView );
|
||
|
||
//
|
||
// In fact, this FID is perfectly aligned to the front of this view. No buffering
|
||
// is required, and we just set the FID pointer.
|
||
//
|
||
|
||
} else {
|
||
|
||
|
||
DirContext->Fid = DirContext->View;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If no buffering was required, we can use the cache directly.
|
||
//
|
||
|
||
if (!FidBytesInPreviousView) {
|
||
|
||
FidC = DirContext->Fid;
|
||
}
|
||
|
||
//
|
||
// Now we can check that the Fid data lies within the stream bounds and is sized
|
||
// within a logical block (per UDF). This will complete the size-wise integrity
|
||
// verification.
|
||
//
|
||
|
||
if (((DirContext->BaseOffset.QuadPart +
|
||
DirContext->ViewOffset -
|
||
FidBytesInPreviousView +
|
||
ISONsrFidSize( FidC ) > Fcb->FileSize.QuadPart) &&
|
||
DebugTrace(( 0, Dbg,
|
||
"UdfLookupDirEntryPostProcessing: DC %p, FID (FidC %p, FBIPV %u) overlaps end of dir\n",
|
||
DirContext,
|
||
FidC,
|
||
FidBytesInPreviousView )))
|
||
||
|
||
|
||
(ISONsrFidSize( FidC ) > BlockSize( Fcb->Vcb ) &&
|
||
DebugTrace(( 0, Dbg,
|
||
"UdfLookupDirEntryPostProcessing: DC %p, FID (FidC %p) larger than a logical block\n",
|
||
DirContext,
|
||
FidC )))) {
|
||
|
||
try_leave( Result = FALSE );
|
||
|
||
}
|
||
|
||
//
|
||
// Final Fid rollup.
|
||
//
|
||
|
||
//
|
||
// The Fid may span a view boundary and should be buffered. If we already buffered, we know
|
||
// we have to do this.
|
||
//
|
||
|
||
if (FidBytesInPreviousView ||
|
||
GenericTruncatePtr( Add2Ptr( DirContext->Fid, ISONsrFidSize( FidC ) - 1, PUCHAR ), VACB_MAPPING_GRANULARITY ) !=
|
||
DirContext->View) {
|
||
|
||
Fid =
|
||
FidBuffer = FsRtlAllocatePoolWithTag( UdfPagedPool,
|
||
ISONsrFidSize( FidC ),
|
||
TAG_FID_BUFFER );
|
||
|
||
//
|
||
// Pull the fidsize out now in case we're still pointing to the cache (ie. no
|
||
// buffering was required for fixed portion) but are about to change the mapping
|
||
// below (need to buffer for variable portion).
|
||
//
|
||
|
||
FidSize = ISONsrFidSize( FidC);
|
||
|
||
//
|
||
// If we already buffered and advanced for the header, just prefill
|
||
// the final Fid buffer with the bytes that are now unavaliable.
|
||
//
|
||
|
||
if (FidBytesInPreviousView) {
|
||
|
||
RtlCopyMemory( FidBuffer,
|
||
FidBufferC,
|
||
FidBytesInPreviousView );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Buffer and advance the view.
|
||
//
|
||
|
||
FidBytesInPreviousView = GenericRemainderPtr( DirContext->Fid, VACB_MAPPING_GRANULARITY );
|
||
|
||
RtlCopyMemory( FidBuffer,
|
||
DirContext->Fid,
|
||
FidBytesInPreviousView );
|
||
|
||
//
|
||
// Now advance into the next view to pick up the rest.
|
||
//
|
||
|
||
DirContext->BaseOffset.QuadPart += VACB_MAPPING_GRANULARITY;
|
||
DirContext->ViewOffset = 0;
|
||
|
||
//
|
||
// Contain the view length by the size of the stream and map.
|
||
//
|
||
|
||
DirContext->ViewLength = VACB_MAPPING_GRANULARITY;
|
||
|
||
if (DirContext->BaseOffset.QuadPart + DirContext->ViewLength > Fcb->FileSize.QuadPart) {
|
||
|
||
DirContext->ViewLength = (ULONG) (Fcb->FileSize.QuadPart - DirContext->BaseOffset.QuadPart);
|
||
}
|
||
|
||
UdfUnpinData( IrpContext, &DirContext->Bcb );
|
||
|
||
CcMapData( Fcb->FileObject,
|
||
&DirContext->BaseOffset,
|
||
DirContext->ViewLength,
|
||
TRUE,
|
||
&DirContext->Bcb,
|
||
&DirContext->View );
|
||
}
|
||
|
||
//
|
||
// We are guaranteed that this much lies in the stream.
|
||
//
|
||
|
||
RtlCopyMemory( Add2Ptr( FidBuffer, FidBytesInPreviousView, PUCHAR ),
|
||
DirContext->View,
|
||
FidSize - FidBytesInPreviousView );
|
||
|
||
} else {
|
||
|
||
Fid = DirContext->Fid;
|
||
}
|
||
|
||
//
|
||
// We finally have the whole Fid safely extracted from the cache, so the
|
||
// integrity check is now the last step before success. For simplicity's
|
||
// sake we trust the Lbn field.
|
||
//
|
||
|
||
Result = UdfVerifyDescriptor( IrpContext,
|
||
&Fid->Destag,
|
||
DESTAG_ID_NSR_FID,
|
||
ISONsrFidSize( Fid ),
|
||
Fid->Destag.Lbn,
|
||
ReturnError );
|
||
|
||
//
|
||
// Prepare to return a buffered Fid.
|
||
//
|
||
|
||
if (FidBuffer && Result) {
|
||
|
||
SetFlag( DirContext->Flags, DIR_CONTEXT_FLAG_FID_BUFFERED );
|
||
DirContext->Fid = FidBuffer;
|
||
FidBuffer = NULL;
|
||
}
|
||
|
||
} finally {
|
||
|
||
UdfFreePool( &FidBuffer );
|
||
UdfFreePool( &FidBufferC );
|
||
}
|
||
|
||
if (!ReturnError && !Result) {
|
||
|
||
UdfRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
//
|
||
// On success update the next Fid information in the context.
|
||
// Note that we must drop in a hint as to where the next Fid
|
||
// will be found so that the next advance will know how much
|
||
// of a buffered Fid isn't in this view.
|
||
//
|
||
|
||
if (Result) {
|
||
|
||
DirContext->NextFidOffset = DirContext->ViewOffset +
|
||
ISONsrFidSize( Fid );
|
||
|
||
if (FidBytesInPreviousView) {
|
||
|
||
DirContext->NextFidOffset -= FidBytesInPreviousView;
|
||
}
|
||
}
|
||
|
||
return Result;
|
||
}
|
||
|
||
|