VOID NcSetFileName ( _In_ PVOID Entry, _In_ PWSTR NewName, _In_ ULONG Length, _In_ CONST PDIRECTORY_CONTROL_OFFSETS Offsets, _In_ BOOLEAN ForceLast ) /*++ Routine Description: Sets a new file name into the entry. Arguments: Entry - A pointer to the start of the entry. NewName - A pointer to the new file name. Length - The length of the new name (in bytes). Offsets - Offsets structure for the information class. ForceLast - If true, the entry's size will be set to zero so that it looks like a valid last entry. Return Value None. --*/ { PWSTR NamePtr; PULONG NameLength; PAGED_CODE(); // // Get a pointer to the name in the buffer. // NamePtr = NcGetFileName( Entry, Offsets); NameLength = Add2Ptr( Entry, Offsets->FileNameLengthDist ); // // Copy the new name into buffer. // RtlCopyMemory( NamePtr, NewName, Length ); *NameLength = Length; // // Now we have to update the size of this entry. // NcSetNextEntryOffset( Entry, Offsets, ForceLast ); }
FLT_PREOP_CALLBACK_STATUS NcEnumerateDirectory ( _Inout_ PFLT_CALLBACK_DATA Data, _In_ PCFLT_RELATED_OBJECTS FltObjects, _Flt_CompletionContext_Outptr_ PVOID *CompletionContext ) /*++ Routine Description: Routine is invoked when a directory enumeration is issued by the user. Arguments: Data - Pointer to the filter CallbackData that is passed to us. FltObjects - Pointer to the FLT_RELATED_OBJECTS data structure containing opaque handles to this filter, instance, its associated volume and file object. CompletionContext - The context for the completion routine for this operation. Return Value: The return value is the Status of the operation. --*/ { //TODO WE SHOULD CONSIDER MOVING THIS TO POST BECAUSE NTFS WILL TAKE CARE // OF SYNC. FLT_PREOP_CALLBACK_STATUS ReturnValue; NTSTATUS Status; PNC_INSTANCE_CONTEXT InstanceContext = NULL; PNC_STREAM_HANDLE_CONTEXT HandleContext = NULL; PNC_DIR_QRY_CONTEXT DirCtx = NULL; PFLT_FILE_NAME_INFORMATION FileNameInformation = NULL; NC_PATH_OVERLAP RealOverlap; NC_PATH_OVERLAP UserOverlap; BOOLEAN Reset = BooleanFlagOn( Data->Iopb->OperationFlags, SL_RESTART_SCAN ); BOOLEAN FirstQuery; BOOLEAN Single = BooleanFlagOn( Data->Iopb->OperationFlags, SL_RETURN_SINGLE_ENTRY ); BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags, FO_OPENED_CASE_SENSITIVE ); FILE_INFORMATION_CLASS InformationClass = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass; PVOID UserBuffer; ULONG BufferSize; //size for user and system buffers. BOOLEAN Unlock = FALSE; //Vars for moving data into user buffer. ULONG NumEntriesCopied; ULONG UserBufferOffset; ULONG LastEntryStart; BOOLEAN MoreRoom; PNC_CACHE_ENTRY NextEntry; DIRECTORY_CONTROL_OFFSETS Offsets; BOOLEAN FoundStructureOffsets; UNREFERENCED_PARAMETER( CompletionContext ); PAGED_CODE(); FLT_ASSERT( IoGetTopLevelIrp() == NULL ); FoundStructureOffsets = NcDetermineStructureOffsets( &Offsets, InformationClass ); if (!FoundStructureOffsets) { Status = STATUS_INVALID_PARAMETER; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // Get our instance context. // Status = FltGetInstanceContext( FltObjects->Instance, &InstanceContext ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // Get the directory's name. // Status = NcGetFileNameInformation( Data, NULL, NULL, FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, &FileNameInformation ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } Status = FltParseFileNameInformation( FileNameInformation ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // See if the directory is parent of either mapping. // NcComparePath( &FileNameInformation->Name, &InstanceContext->Mapping.UserMapping, NULL, IgnoreCase, TRUE, &UserOverlap ); NcComparePath( &FileNameInformation->Name, &InstanceContext->Mapping.RealMapping, NULL, IgnoreCase, TRUE, &RealOverlap ); if (!(UserOverlap.Parent || RealOverlap.Parent )) { // // We are not interested in this directory // because it is not the parent of either // mapping. This means we can just passthrough. // Status = STATUS_SUCCESS; ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK; goto NcEnumerateDirectoryCleanup; } Status = NcStreamHandleContextAllocAndAttach( FltObjects->Filter, FltObjects->Instance, FltObjects->FileObject, &HandleContext ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } FLT_ASSERT( HandleContext != NULL ); DirCtx = &HandleContext->DirectoryQueryContext; _Analysis_assume_( DirCtx != NULL ); // // Before looking at the context, we have to acquire the lock. // NcLockStreamHandleContext( HandleContext ); Unlock = TRUE; // // We don't allow multiple outstanding enumeration requests on // a single handle. // // TODO: This needs to change. // if (DirCtx->EnumerationOutstanding) { Status = STATUS_UNSUCCESSFUL; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } DirCtx->EnumerationOutstanding = TRUE; // // Now drop the lock. We're protected by the EnumerationOutstanding // flag; nobody else can muck with the enumeration context structure. // NcUnlockStreamHandleContext( HandleContext ); Unlock = FALSE; // // Now we need to initialize or clear the cache and query options. // Status = NcStreamHandleContextEnumSetup( DirCtx, InstanceContext, &Offsets, Data, FltObjects, UserOverlap, &FirstQuery ); if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } // // Prepare to populate the user buffer. // UserBuffer = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.DirectoryBuffer; BufferSize = Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length; // // Lets copy data into the user buffer. // NumEntriesCopied = 0; UserBufferOffset = 0; do { // // If there is no cache entry, populate it. // if (DirCtx->Cache.Buffer == NULL) { Status = NcPopulateCacheEntry( FltObjects->Instance, FltObjects->FileObject, Data->Iopb->Parameters.DirectoryControl.QueryDirectory.Length, Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileInformationClass, Data->Iopb->Parameters.DirectoryControl.QueryDirectory.FileName, Reset, &DirCtx->Cache); // // We only want to reset the cache once. // Reset = FALSE; // // There was a problem populating cache, pass up to user. // if (!NT_SUCCESS( Status )) { ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } } NextEntry = NcDirEnumSelectNextEntry( DirCtx, &Offsets, IgnoreCase ); if (NextEntry == NULL) { // // There are no more entries. // break; } if (NcSkipName( &Offsets, DirCtx, RealOverlap, &InstanceContext->Mapping, IgnoreCase )) { // // This entry is the real mapping path. That means we have to mask it... // We will say there is more room and continue. // MoreRoom = TRUE; } else { // // We are keeping this entry! // try { LastEntryStart = UserBufferOffset; UserBufferOffset = NcCopyDirEnumEntry( UserBuffer, UserBufferOffset, BufferSize, NextEntry, &Offsets, &MoreRoom ); } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { Status = STATUS_INVALID_USER_BUFFER; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } if (MoreRoom) { NumEntriesCopied++; } }// end of "we are copying entry" } while (MoreRoom && (Single ? (NumEntriesCopied < 1) : TRUE)); if (NumEntriesCopied > 0) { // // Now we know what the last entry in the user buffer is going to be. // Set its NextEntryOffset to 0, so that the user knows its the last element. // try { NcSetNextEntryOffset( Add2Ptr(UserBuffer, LastEntryStart), &Offsets, TRUE ); } except (NcExceptionFilter( GetExceptionInformation(), TRUE )) { Status = STATUS_INVALID_USER_BUFFER; ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; } } // // We finished copying data. // ReturnValue = FLT_PREOP_COMPLETE; if (NumEntriesCopied == 0) { if (FirstQuery) { Status = STATUS_NO_SUCH_FILE; } else { Status = STATUS_NO_MORE_FILES; } } else { Status = STATUS_SUCCESS; } ReturnValue = FLT_PREOP_COMPLETE; goto NcEnumerateDirectoryCleanup; NcEnumerateDirectoryCleanup: if (ReturnValue == FLT_PREOP_COMPLETE) { // // We need to write back results of query. // Data->IoStatus.Status = Status; if (NT_SUCCESS( Status )) { //success Data->IoStatus.Information = UserBufferOffset; } else { //failure Data->IoStatus.Information = 0; } } if (InstanceContext != NULL) { FltReleaseContext( InstanceContext ); } if (DirCtx != NULL) { if (!Unlock) { NcLockStreamHandleContext( HandleContext ); Unlock = TRUE; } FLT_ASSERT( DirCtx->EnumerationOutstanding ); DirCtx->EnumerationOutstanding = FALSE; NcUnlockStreamHandleContext( HandleContext ); Unlock = FALSE; FltReleaseContext( HandleContext ); } FLT_ASSERT( !Unlock ); if (FileNameInformation != NULL) { FltReleaseFileNameInformation( FileNameInformation ); } return ReturnValue; }
ULONG NcCopyDirEnumEntry ( _Out_ PVOID UserBuffer, _In_ ULONG UserOffset, _In_ ULONG UserSize, _Inout_ PNC_CACHE_ENTRY Entry, _In_ PDIRECTORY_CONTROL_OFFSETS Offsets, _Out_ PBOOLEAN Copied ) /*++ Routine Description: This routine copies a single enumeration result into the caller's buffer. Arguments: UserBuffer - Pointer to the caller's buffer. UserOffset - Offset within the caller's buffer that we intend to write new results. UserSize - Size of the caller's buffer, in bytes. Entry - Pointer to the directory entry that we intend to return. Offsets - Information describing the offsets for this enumeration class. Copied - Pointer to a boolean value indicating whether this routine copied a new entry or not. Return Value: The new offset in the user buffer that any future copies should use. --*/ { PVOID Element = Add2Ptr( Entry->Buffer, Entry->CurrentOffset ); PVOID Dest = Add2Ptr( UserBuffer, UserOffset ); ULONG ElementSize = NcGetEntrySize( Element, Offsets ); BOOLEAN LastElement = (BOOLEAN)(NcGetNextEntryOffset( Element, Offsets ) == 0); PAGED_CODE(); if (UserSize - UserOffset >= ElementSize) { // // There is enough room for this element, so copy it. // RtlCopyMemory( Dest, Element, ElementSize ); UserOffset += ElementSize; *Copied = TRUE; // // Update Entry's Offset // if (LastElement) { // // This was the last element in the entry, so we should clean the // entry. // ExFreePoolWithTag( Entry->Buffer, NC_TAG ); Entry->Buffer = NULL; Entry->CurrentOffset = 0; // // The last element in cached entries have a NextEntryOffset of 0, // make sure that we report the actual next entry offset. // NcSetNextEntryOffset( Dest, Offsets, FALSE ); } else { // // Entry has more elements, update offset counter. // Entry->CurrentOffset += ElementSize; } } else { // // User buffer does not have enough space. // *Copied = FALSE; } return UserOffset; }