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 );
}
Example #2
0
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;
}
Example #3
0
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;
}