/*++ 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; }