Sample Code

Windows Driver Samples/ NameChanger File System Minifilter Driver/ C++/ ncfileinfo.c/

/*++

Copyright (c) 2008 - 2009  Microsoft Corporation

Module Name:

    ncfileinfo.c

Abstract:

    Contains routines to process user-initiated query file and set file
    information requests.  

Environment:

    Kernel mode

--*/

#include "nc.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NcPreQueryAlternateName)
#pragma alloc_text(PAGE, NcPostQueryHardLinks)
#pragma alloc_text(PAGE, NcPostQueryName)
#pragma alloc_text(PAGE, NcPreSetDisposition)
#pragma alloc_text(PAGE, NcPreSetLinkInformation)
#pragma alloc_text(PAGE, NcPreSetShortName)
#pragma alloc_text(PAGE, NcPreRename)
#endif

FLT_POSTOP_CALLBACK_STATUS
NcPostQueryName (
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_opt_ PVOID CompletionContext,
    _In_ FLT_POST_OPERATION_FLAGS Flags
    )
/*++

Routine Description:

    This routine is called when the user wants to request a name for a
    previously opened handle.  Since we munged the name to be to the real
    mapping in pre-create, we must munge it back to the user visible view
    in response to name requests, even by opened name.

    Note that this function processes three information classes:

    FileNameInformation
    FileNormalizedNameInformation
    FileAllInformation

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.  We set this to the handle context for
        directory change notifications.

Return Value:

    The return value is the Status of the operation.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    NC_PATH_OVERLAP Overlap;
    PNC_INSTANCE_CONTEXT InstanceContext = NULL;
    FILE_INFORMATION_CLASS InfoClass = Data->Iopb->Parameters.QueryFileInformation.FileInformationClass;
    PVOID UserBuffer = Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
    ULONG UserBufferLength = Data->Iopb->Parameters.QueryFileInformation.Length;
    ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information;
    ULONG LengthNeeded = 0;
    ULONG UserStructureSize = 0;
    ULONG RequiredNameSize = 0;
    ULONG NameLengthAvailable = 0;
    PFILE_NAME_INFORMATION NameInfo;
    BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
                                         FO_OPENED_CASE_SENSITIVE );
    UNICODE_STRING Remainder;
    UNICODE_STRING RemainderCopy = EMPTY_UNICODE_STRING;
    UNICODE_STRING ReturnedName;

    UNREFERENCED_PARAMETER( CompletionContext );
    UNREFERENCED_PARAMETER( Flags );

    PAGED_CODE();

    FLT_ASSERT( IoGetTopLevelIrp() == NULL );

    //
    //  If the operation failed already, we have no processing to do unless the
    //  failure is a buffer overflow.
    //

    if (!NT_SUCCESS( Data->IoStatus.Status ) &&
        (Data->IoStatus.Status != STATUS_BUFFER_OVERFLOW)) {

        Status = Data->IoStatus.Status;
        goto NcPostQueryNameInformationCleanup;
    }

    //
    //  Get our instance context.
    //

    Status = FltGetInstanceContext( FltObjects->Instance,
                                    &InstanceContext);

    if (!NT_SUCCESS( Status )) {

        goto NcPostQueryNameInformationCleanup;
    }

    //
    //  Find the name in the user buffer.
    //
    
    if (InfoClass == FileAllInformation) {

        NameInfo = & ((PFILE_ALL_INFORMATION) UserBuffer)->NameInformation;

    } else {

        FLT_ASSERT( InfoClass == FileNameInformation ||
                    InfoClass == FileNormalizedNameInformation );

        NameInfo = UserBuffer;
    }

    //
    //  If the name is too long for a UNICODE_STRING, we can't process it.
    //  This should never really happen, since UNICODE_STRINGs are used
    //  all across the NT IO model.
    //

    if (NameInfo->FileNameLength >= MAXUSHORT) {

        Status = STATUS_OBJECT_PATH_INVALID;
        goto NcPostQueryNameInformationCleanup;
    }

    //
    //  Now that we have an instance context and NameInfo buffer, see if the file
    //  system failed with a buffer overflow.
    //

    if (Data->IoStatus.Status == STATUS_BUFFER_OVERFLOW) {

        //
        //  We need to bias the FileNameLength field by the difference between
        //  the real and user mapping lengths if the user mapping is longer.  This
        //  is so that if the caller re-issues the name query we won't fail with
        //  a buffer overflow in the filter even if the file system succeeded.
        //

        if (InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length > 
            InstanceContext->Mapping.RealMapping.LongNamePath.VolumelessName.Length) {

            NameInfo->FileNameLength += InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length -
                                        InstanceContext->Mapping.RealMapping.LongNamePath.VolumelessName.Length;
        }

        Status = Data->IoStatus.Status;
        LengthNeeded = SizeActuallyReturned;

        goto NcPostQueryNameInformationCleanup;
    }

    ReturnedName.Buffer = NameInfo->FileName;
    ReturnedName.MaximumLength = 
    ReturnedName.Length = (USHORT)NameInfo->FileNameLength;
    
    //
    //  Check if the name being returned is within the real mapping.
    //  If not, we have no translation to perform.
    //

    NcComparePath( &ReturnedName,
                   &InstanceContext->Mapping.RealMapping,
                   &Remainder,
                   IgnoreCase,
                   FALSE,
                   &Overlap );

    if (!Overlap.InMapping && !Overlap.Match) {

        Status = Data->IoStatus.Status;
        LengthNeeded = SizeActuallyReturned;

        goto NcPostQueryNameInformationCleanup;
    }

    //
    //  Make sure that the user buffer is long enough.
    //

    UserStructureSize = FIELD_OFFSET( FILE_NAME_INFORMATION, FileName );

    if (InfoClass == FileAllInformation) {

        UserStructureSize += FIELD_OFFSET( FILE_ALL_INFORMATION, NameInformation );
    }

    RequiredNameSize = InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length;

    //
    //  Add back the trailing portion of the name.  Note that Remainder
    //  is only defined if InMapping is TRUE.
    //

    if (Overlap.InMapping && !Overlap.Match) {

        RequiredNameSize += Remainder.Length + sizeof(WCHAR);
    }

    LengthNeeded = UserStructureSize + RequiredNameSize;

    //
    //  Whether the user has provided enough buffer or not, the
    //  FILE_NAME_INFORMATION.FileNameLength field has to contain the total length
    //  of the name we want to return.
    //

    NameInfo->FileNameLength = RequiredNameSize;

    //
    //  If the user's buffer is not big enough to handle the name we need to return,
    //  we will copy in as much as we can and return STATUS_BUFFER_OVERFLOW.  The
    //  user expects that the needed name length will be reported in the
    //  FILE_NAME_INFORMATION.FileNameLength field.
    //

    if (UserBufferLength < LengthNeeded) {

        NameLengthAvailable = UserBufferLength - UserStructureSize;

        //
        //  Truncate the LengthNeeded value since it will be returned in the
        //  IoStatus block to tell I/O Manager how much to copy back to the
        //  user's buffer.
        //

        LengthNeeded = UserBufferLength;

        Status = STATUS_BUFFER_OVERFLOW;

    //
    //  We have enough space.  Let's assume we'll succeed to copy the name.
    //

    } else {
        
        NameLengthAvailable = NameInfo->FileNameLength;

        Status = STATUS_SUCCESS;
    }

    if (Overlap.InMapping && !Overlap.Match) {

        //
        //  Copy the remainder of the returned name from the user.  This is
        //  done so that we can rewrite the user's buffer.  Note that if we
        //  are not a match, we expect some remainder.
        //

        FLT_ASSERT( Remainder.Length > 0 );

        RemainderCopy.Buffer = ExAllocatePoolWithTag( PagedPool,
                                                      Remainder.Length,
                                                      NC_TAG );

        if (RemainderCopy.Buffer == NULL) {

            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto NcPostQueryNameInformationCleanup;
        }

        RtlCopyMemory( RemainderCopy.Buffer,
                       Remainder.Buffer,
                       Remainder.Length );

        RemainderCopy.MaximumLength =
        RemainderCopy.Length = Remainder.Length;
    }

    //
    //  Firstly, copy back the name to our mapping.
    //

    RtlCopyMemory( &NameInfo->FileName, 
                   InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Buffer, 
                   min(InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length,
                       NameLengthAvailable) );

    if (NameLengthAvailable > InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length) {

        NameLengthAvailable -= InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length;

    } else {
        
        NameLengthAvailable = 0;
    }

    //
    //  If the object being queried is within the mapping, copy back the
    //  remainder of that name.
    //

    if ((NameLengthAvailable > 0) &&
        Overlap.InMapping && !Overlap.Match) {

        NameInfo->FileName[InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length / sizeof(WCHAR)] = '\\';

        RtlCopyMemory( Add2Ptr( &NameInfo->FileName,
                                InstanceContext->Mapping.UserMapping.LongNamePath.VolumelessName.Length + sizeof(WCHAR) ),
                       RemainderCopy.Buffer,
                       min(RemainderCopy.Length, NameLengthAvailable) );
    }

    //
    //  We have finished the query, complete operation.
    //
    
NcPostQueryNameInformationCleanup:

    Data->IoStatus.Status = Status;

    //
    //  Note that STATUS_BUFFER_OVERFLOW is not a success code, but for name queries
    //  it indicates that the caller needs to allocate a bigger buffer.  The needed
    //  size for the name is stored in the FILE_NAME_INFORMATION.FileNameLength field.
    //  Therefore the IoStatus.Information field must not be 0 for a buffer overflow,
    //  it must contain the size of the data that was actually copied.
    //

    if (NT_SUCCESS( Status ) ||
        (Status == STATUS_BUFFER_OVERFLOW)) {

        Data->IoStatus.Information = LengthNeeded;

    } else {

        Data->IoStatus.Information = 0;
    }

    if (RemainderCopy.Buffer != NULL) {

        ExFreePoolWithTag( RemainderCopy.Buffer, NC_TAG );
    }

    if (InstanceContext != NULL) {

        FltReleaseContext( InstanceContext );
    }

    return FLT_POSTOP_FINISHED_PROCESSING;
}

FLT_PREOP_CALLBACK_STATUS
NcPreQueryAlternateName (
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
    )
/*++

Routine Description:

    This routine is called when the user wants to find the alternate name
    for a previously opened handle.  An alternate name means the short
    half of a long/short name pair.

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.  We set this to the handle context for
        directory change notifications.

Return Value:

    The return value is the Status of the operation.

--*/
{
    NTSTATUS Status;
    FLT_PREOP_CALLBACK_STATUS ReturnValue;
    NC_PATH_OVERLAP Overlap;
    PFLT_FILE_NAME_INFORMATION FileInfo = NULL;
    PNC_INSTANCE_CONTEXT InstanceContext = NULL;
    PVOID UserBuffer = Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
    ULONG UserBufferLength = Data->Iopb->Parameters.QueryFileInformation.Length;
    ULONG LengthNeeded = 0;
    PFILE_NAME_INFORMATION NameInfo;
    BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
                                         FO_OPENED_CASE_SENSITIVE );
    PUNICODE_STRING FinalComponentToReturn;

    UNREFERENCED_PARAMETER( CompletionContext );

    PAGED_CODE();

    FLT_ASSERT( IoGetTopLevelIrp() == NULL );

    //
    //  Get our instance context.
    //

    Status = FltGetInstanceContext( FltObjects->Instance,
                                    &InstanceContext);

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreQueryAlternateNameInformationCleanup;
    }

    //
    //  Get the file's name.
    //

    Status = NcGetFileNameInformation( Data, 
                                       NULL,
                                       NULL,
                                       FLT_FILE_NAME_OPENED | 
                                           FLT_FILE_NAME_QUERY_DEFAULT | 
                                           FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, 
                                       &FileInfo );

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreQueryAlternateNameInformationCleanup;
    }

    Status = FltParseFileNameInformation( FileInfo );

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreQueryAlternateNameInformationCleanup;
    }

    //
    //  We only need to handle the case where a shortname is being
    //  generated on the mapping itself.  These names are final
    //  component path only, so any files within the mapping will
    //  still be correct even if we don't munge them.
    //

    NcComparePath( &FileInfo->Name,
                   &InstanceContext->Mapping.UserMapping,
                   NULL,
                   IgnoreCase,
                   TRUE,
                   &Overlap );

    if (!Overlap.Match) {

        NcComparePath( &FileInfo->Name,
                       &InstanceContext->Mapping.RealMapping,
                       NULL,
                       IgnoreCase,
                       TRUE,
                       &Overlap );

        FLT_ASSERT( !Overlap.Match );

        if (!Overlap.Match) {

            //
            //  This file is not the mapping, so we can just let this
            //  request go down normally.
            //

            ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
            goto NcPreQueryAlternateNameInformationCleanup;
        }
    }

    //
    //  Return the short name.
    //
    //  Note that this behavior differs from the filesystem in two respects:
    //
    //  1. We are not attempting to detect (and fail for) an open-by-ID
    //     handle.  Since these are link agnostic, returning data is
    //     meaningless.
    //
    //  2. We may support having multiple alternate names for multiple
    //     links on the file, if the mapping was created as a file then
    //     a hardlink was created with a mapping name.  This file thus
    //     contains two shortnames, which NTFS does not support.  In
    //     theory APIs should be clean to this (and a future filesystem
    //     may wish to support it.)
    //

    FinalComponentToReturn = &InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName;

    //
    //  Make sure that the user buffer is long enough.
    //

    LengthNeeded = FIELD_OFFSET( FILE_NAME_INFORMATION, FileName );

    LengthNeeded += FinalComponentToReturn->Length;

    if (UserBufferLength < LengthNeeded) {

        Status = STATUS_BUFFER_OVERFLOW;
        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreQueryAlternateNameInformationCleanup;
    }

    NameInfo = UserBuffer;

    //
    //  Copy back the name to our mapping.
    //

    RtlCopyMemory( &NameInfo->FileName, 
                   FinalComponentToReturn->Buffer, 
                   FinalComponentToReturn->Length );

    NameInfo->FileNameLength = FinalComponentToReturn->Length;

    //
    //  We have finished the query, complete operation.
    //
    
    Status = STATUS_SUCCESS;
    ReturnValue = FLT_PREOP_COMPLETE;

NcPreQueryAlternateNameInformationCleanup:

    if (ReturnValue == FLT_PREOP_COMPLETE) {

        Data->IoStatus.Status = Status;

        //
        //  Note that STATUS_BUFFER_OVERFLOW is not a success code, and
        //  will result in zero bytes being copied back to the caller.
        //

        if (NT_SUCCESS( Status )) {

            Data->IoStatus.Information = LengthNeeded;
        } else {

            Data->IoStatus.Information = 0;
        }
    }

    if (FileInfo != NULL) {

        FltReleaseFileNameInformation( FileInfo );
    }

    if (InstanceContext != NULL) {

        FltReleaseContext( InstanceContext );
    }

    return ReturnValue;
}


FLT_POSTOP_CALLBACK_STATUS
NcPostQueryHardLinks (
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _In_opt_ PVOID CompletionContext,
    _In_ FLT_POST_OPERATION_FLAGS Flags
    )
/*++

Routine Description:

    This routine is called when the user wants to enumerate all hard links
    for a previously opened handle.

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.  We set this to the handle context for
        directory change notifications.

Return Value:

    The return value is the Status of the operation.

--*/
{
    NTSTATUS Status;
    PNC_INSTANCE_CONTEXT InstanceContext = NULL;
    BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
                                         FO_OPENED_CASE_SENSITIVE );

    //
    //  Pointer to the buffer returned to us, which we will also return to
    //  our caller; length of the buffer; size of the buffer filled in
    //  by the filesystem; size of the buffer that we filled in
    //

    PFILE_LINKS_INFORMATION UserBuffer = Data->Iopb->Parameters.QueryFileInformation.InfoBuffer;
    ULONG UserBufferLength = Data->Iopb->Parameters.QueryFileInformation.Length;
    ULONG SizeActuallyReturned = (ULONG)Data->IoStatus.Information;
    ULONG BytesWritten = 0;

    //
    //  Length of the current entry that we're processing; a UNICODE_STRING
    //  for the final component of the name that we're processing; and
    //  a flag indicating whether this entry is being modified
    //

    ULONG EntrySize;
    UNICODE_STRING EntryName;
    BOOLEAN MungeEntry;

    //
    //  Copy of the buffer returned from the filesystem, our iterators as
    //  we process this buffer, and a pointer to the previous destination
    //  entry (if one exists) so we can zero the offset to next entry
    //  field on completion
    //

    PFILE_LINKS_INFORMATION OriginalBuffer = NULL;
    PFILE_LINK_ENTRY_INFORMATION SourceEntry;
    PFILE_LINK_ENTRY_INFORMATION DestEntry;
    PFILE_LINK_ENTRY_INFORMATION PrevDestEntry = NULL;


    //
    //  Variables that we use to obtain IDs to the mapping parents.
    //

    OBJECT_ATTRIBUTES MappingParentAttributes;
    HANDLE MappingParentHandle = NULL;
    PFILE_OBJECT MappingParentFileObject = NULL;
    IO_STATUS_BLOCK MappingParentStatusBlock;

    //
    //  File IDs for the mapping parents
    //

    LONGLONG RealMappingParentId;
    LONGLONG UserMappingParentId;

    UNREFERENCED_PARAMETER( CompletionContext );
    UNREFERENCED_PARAMETER( Flags );

    PAGED_CODE();

    FLT_ASSERT( IoGetTopLevelIrp() == NULL );

    //
    //  If the buffer was invalid, or if the call failed, leave now.  Note
    //  that this may be STATUS_BUFFER_OVERFLOW and the number of bytes the
    //  caller needs will be reported inaccurately.  To handle this we'd
    //  really need to issue our own call to get the full buffer, then
    //  transform it to find the "correct" length the user will need.
    //
    //  Rather than do this, we return the caller a value for bytes required
    //  which may not be accurate.  When they call us again, we will
    //  have data to transform, and can then fail the call again specifying
    //  a new value for bytes required that is accurate.
    //

    if (SizeActuallyReturned <= FIELD_OFFSET( FILE_LINKS_INFORMATION, Entry ) ||
        !NT_SUCCESS( Data->IoStatus.Status )) {
            
        BytesWritten = SizeActuallyReturned;

        Status = Data->IoStatus.Status;
        goto NcPostQueryHardLinkInformationCleanup;
    }

    //
    //  Get our instance context.
    //

    Status = FltGetInstanceContext( FltObjects->Instance,
                                    &InstanceContext);

    if (!NT_SUCCESS( Status )) {

        goto NcPostQueryHardLinkInformationCleanup;
    }

    //
    //  Open the mapping parents and query IDs.
    //
        
    InitializeObjectAttributes( &MappingParentAttributes,
                                &InstanceContext->Mapping.RealMapping.LongNamePath.ParentPath,
                                OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0),
                                NULL,
                                NULL);
        
    Status = NcCreateFileHelper( NcGlobalData.FilterHandle,            // Filter
                                 Data->Iopb->TargetInstance,           // Instance
                                 &MappingParentHandle,                 // Returned Handle
                                 &MappingParentFileObject,             // Returned FileObject
                                 FILE_READ_ATTRIBUTES|FILE_TRAVERSE,   // Desired Access
                                 &MappingParentAttributes,             // object attributes
                                 &MappingParentStatusBlock,            // Returned IOStatusBlock
                                 0,                                    // Allocation Size
                                 FILE_ATTRIBUTE_NORMAL,                // File Attributes
                                 0,                                    // Share Access
                                 FILE_OPEN,                            // Create Disposition
                                 FILE_DIRECTORY_FILE,                  // Create Options
                                 NULL,                                 // Ea Buffer
                                 0,                                    // EA Length
                                 IO_IGNORE_SHARE_ACCESS_CHECK,         // Flags
                                 Data->Iopb->TargetFileObject );       // Transaction info.
           
    if (!NT_SUCCESS( Status )) {

        FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND &&
                    Status != STATUS_OBJECT_NAME_NOT_FOUND );

        goto NcPostQueryHardLinkInformationCleanup;
    }

    Status = FltQueryInformationFile( Data->Iopb->TargetInstance,
                                      MappingParentFileObject,
                                      &RealMappingParentId,
                                      sizeof(RealMappingParentId),
                                      FileInternalInformation,
                                      NULL );

    if (!NT_SUCCESS( Status )) {

        goto NcPostQueryHardLinkInformationCleanup;
    }

    FltClose( MappingParentHandle );
    ObDereferenceObject( MappingParentFileObject );

    MappingParentHandle = NULL;
    MappingParentFileObject = NULL;

    InitializeObjectAttributes( &MappingParentAttributes,
                                &InstanceContext->Mapping.UserMapping.LongNamePath.ParentPath,
                                OBJ_KERNEL_HANDLE | (IgnoreCase?OBJ_CASE_INSENSITIVE:0),
                                NULL,
                                NULL);
        
    Status = NcCreateFileHelper( NcGlobalData.FilterHandle,            // Filter
                                 Data->Iopb->TargetInstance,           // Instance
                                 &MappingParentHandle,                 // Returned Handle
                                 &MappingParentFileObject,             // Returned FileObject
                                 FILE_READ_ATTRIBUTES|FILE_TRAVERSE,   // Desired Access
                                 &MappingParentAttributes,             // object attributes
                                 &MappingParentStatusBlock,            // Returned IOStatusBlock
                                 0,                                    // Allocation Size
                                 FILE_ATTRIBUTE_NORMAL,                // File Attributes
                                 0,                                    // Share Access
                                 FILE_OPEN,                            // Create Disposition
                                 FILE_DIRECTORY_FILE,                  // Create Options
                                 NULL,                                 // Ea Buffer
                                 0,                                    // EA Length
                                 IO_IGNORE_SHARE_ACCESS_CHECK,         // Flags
                                 Data->Iopb->TargetFileObject );       // Transaction info.
           
    if (!NT_SUCCESS( Status )) {

        FLT_ASSERT( Status != STATUS_OBJECT_PATH_NOT_FOUND &&
                    Status != STATUS_OBJECT_NAME_NOT_FOUND );

        goto NcPostQueryHardLinkInformationCleanup;
    }

    Status = FltQueryInformationFile( Data->Iopb->TargetInstance,
                                      MappingParentFileObject,
                                      &UserMappingParentId,
                                      sizeof(UserMappingParentId),
                                      FileInternalInformation,
                                      NULL );

    if (!NT_SUCCESS( Status )) {

        goto NcPostQueryHardLinkInformationCleanup;
    }

    FltClose( MappingParentHandle );
    ObDereferenceObject( MappingParentFileObject );

    MappingParentHandle = NULL;
    MappingParentFileObject = NULL;

    //
    //  Take a copy of the results of the call from the filesystem.
    //

    OriginalBuffer = ExAllocatePoolWithTag( PagedPool,
                                            SizeActuallyReturned,
                                            NC_TAG );

    if (OriginalBuffer == NULL) {

        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto NcPostQueryHardLinkInformationCleanup;
    }

    RtlCopyMemory( OriginalBuffer, UserBuffer, SizeActuallyReturned );

    //
    //  Set up our iterators to walk through the returned links.
    //

    DestEntry = &UserBuffer->Entry;

    if (UserBuffer->EntriesReturned >= 1) {
        SourceEntry = &OriginalBuffer->Entry;
    } else {
        SourceEntry = NULL;
    }
    UserBuffer->EntriesReturned = 0;
    BytesWritten = FIELD_OFFSET( FILE_LINKS_INFORMATION, Entry );
    UserBuffer->BytesNeeded = BytesWritten;

    while( SourceEntry ) {

        //
        //  Assume we don't need to munge the link.  If the parent IDs,
        //  final component lengths and final component strings correspond
        //  to the real mapping, we will need to transform it.
        //

        MungeEntry = FALSE;

        if (SourceEntry->ParentFileId == RealMappingParentId) {

            if (SourceEntry->FileNameLength * sizeof(WCHAR) >= MAXUSHORT) {

                Status = STATUS_OBJECT_PATH_INVALID;
                goto NcPostQueryHardLinkInformationCleanup;
            }

            EntryName.Buffer = SourceEntry->FileName;
            EntryName.Length = EntryName.MaximumLength = (USHORT)SourceEntry->FileNameLength * sizeof(WCHAR);

            if (EntryName.Length == InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName.Length &&
                RtlCompareUnicodeString( &EntryName, &InstanceContext->Mapping.RealMapping.LongNamePath.FinalComponentName, IgnoreCase ) == 0) {

                MungeEntry = TRUE;
            }

            //
            //  TODO: Preserve shortness in output
            //

            if (EntryName.Length == InstanceContext->Mapping.RealMapping.ShortNamePath.FinalComponentName.Length &&
                RtlCompareUnicodeString( &EntryName, &InstanceContext->Mapping.RealMapping.ShortNamePath.FinalComponentName, IgnoreCase ) == 0) {

                MungeEntry = TRUE;
            }
        }

        //
        //  Calculate the length of the entry that we want to write.
        //

        if (MungeEntry) {

            EntrySize = FIELD_OFFSET( FILE_LINK_ENTRY_INFORMATION, FileName ) +
                        InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length;

            EntrySize = AlignToSize( EntrySize, 8 );

        } else {

            EntrySize = FIELD_OFFSET( FILE_LINK_ENTRY_INFORMATION, FileName ) +
                        SourceEntry->FileNameLength * sizeof(WCHAR);

            EntrySize = AlignToSize( EntrySize, 8 );
        }

        //
        //  Record how much space the caller would need to return all entries.
        //

        UserBuffer->BytesNeeded += EntrySize;

        //
        //  If we have space, copy this entry into the user's buffer and
        //  advance our destination iterator.
        //

        if (BytesWritten + EntrySize <= UserBufferLength) {

            if (MungeEntry) {

                DestEntry->NextEntryOffset = EntrySize;
                DestEntry->ParentFileId = UserMappingParentId;
                DestEntry->FileNameLength = InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length / sizeof(WCHAR);
                RtlCopyMemory( DestEntry->FileName,
                               InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Buffer,
                               InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName.Length );
            } else {

                RtlCopyMemory( DestEntry, SourceEntry, EntrySize );
            }
            PrevDestEntry = DestEntry;
            DestEntry = Add2Ptr( DestEntry, EntrySize );
            UserBuffer->EntriesReturned++;
            BytesWritten += EntrySize;
        }

        //
        //  If we still have links that we have not yet consumed, move
        //  to those.
        //

        if (SourceEntry->NextEntryOffset != 0) {
            SourceEntry = Add2Ptr( SourceEntry, SourceEntry->NextEntryOffset );
        } else {
            SourceEntry = NULL;
        }
    }

    //
    //  If we already copied one or more links, make sure our list is
    //  correctly terminated.
    //

    if (PrevDestEntry != NULL) {
        PrevDestEntry->NextEntryOffset = 0;
    }

    //
    //  If we copied all results, return STATUS_SUCCESS.  If we saw entries
    //  that we did not copy, return STATUS_BUFFER_OVERFLOW.
    //

    if (BytesWritten == UserBuffer->BytesNeeded) {
        Status = STATUS_SUCCESS;
    } else {
        Status = STATUS_BUFFER_OVERFLOW;
    }

NcPostQueryHardLinkInformationCleanup:

    Data->IoStatus.Status = Status;

    if (NT_SUCCESS( Status ) || Status == STATUS_BUFFER_OVERFLOW) {

        Data->IoStatus.Information = BytesWritten;
    } else {

        Data->IoStatus.Information = 0;
    }

    if (MappingParentHandle != NULL) {

        FltClose( MappingParentHandle );
        MappingParentHandle = NULL;
    }

    if (MappingParentFileObject != NULL) {

        ObDereferenceObject( MappingParentFileObject );
        MappingParentFileObject = NULL;
    }

    if (OriginalBuffer != NULL) {

        ExFreePoolWithTag( OriginalBuffer, NC_TAG );
    }

    if (InstanceContext != NULL) {

        FltReleaseContext( InstanceContext );
    }

    return FLT_POSTOP_FINISHED_PROCESSING;
}

FLT_PREOP_CALLBACK_STATUS
NcPreSetShortName (
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
    )
/*++

Routine Description:

    This routine is called when the user wants to change the short name
    for a previously opened handle.

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.  We set this to the handle context for
        directory change notifications.

Return Value:

    The return value is the Status of the operation.

--*/
{
    FLT_PREOP_CALLBACK_STATUS ReturnValue;
    NTSTATUS Status;
    PFILE_NAME_INFORMATION NameInfo = 
        Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
    PFLT_FILE_NAME_INFORMATION FileInfo = NULL;
    PNC_INSTANCE_CONTEXT InstanceContext = NULL;
    NC_PATH_OVERLAP RealOverlap;
    NC_PATH_OVERLAP UserOverlap;
    BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
                                         FO_OPENED_CASE_SENSITIVE );

    PAGED_CODE();

    FLT_ASSERT( IoGetTopLevelIrp() == NULL );

    UNREFERENCED_PARAMETER( CompletionContext );

    Status = FltGetInstanceContext( FltObjects->Instance,
                                    &InstanceContext);

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetShortNameCleanup;
    }

    //
    //  Let's skip doing any of this work for file systems that we know don't
    //  have short names.
    //

    if ((InstanceContext->VolumeFilesystemType == FLT_FSTYPE_EXFAT) ||
        (InstanceContext->VolumeFilesystemType == FLT_FSTYPE_REFS)) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetShortNameCleanup;
    }

    //
    //  Get the file's name.
    //

    Status = NcGetFileNameInformation( Data, 
                                       NULL,
                                       NULL,
                                       FLT_FILE_NAME_OPENED | 
                                           FLT_FILE_NAME_QUERY_DEFAULT | 
                                           FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER, 
                                       &FileInfo );

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetShortNameCleanup;
    }

    Status = FltParseFileNameInformation( FileInfo );

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetShortNameCleanup;
    }

    //
    //  Calculate Overlap and Remainder
    //

    NcComparePath( &FileInfo->Name,
                   &InstanceContext->Mapping.UserMapping,
                   NULL,
                   IgnoreCase,
                   TRUE,
                   &UserOverlap );

    NcComparePath( &FileInfo->Name,
                   &InstanceContext->Mapping.RealMapping,
                   NULL,
                   IgnoreCase,
                   TRUE,
                   &RealOverlap );

    //
    //  Currently the file names that we use are read only, so changing a
    //  shortname on the mapping or any of its ancestors cannot be
    //  supported.
    //
    //  TODO: Should we support this?
    //

    if (RealOverlap.Match ||
        UserOverlap.Match ||
        RealOverlap.Ancestor ||
        UserOverlap.Ancestor ||
        NameInfo->FileNameLength > MAXUSHORT) {

        Status = STATUS_ACCESS_DENIED;
        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetShortNameCleanup;
    }

    //
    //  If the user is attempting to set a name which is used by the real
    //  file, we let the request go to the file system (which will fail
    //  it.)  For names used by the user mapping, we need to detect and
    //  fail those.
    //

    if (UserOverlap.Peer) {

        UNICODE_STRING TargetComponent;

        TargetComponent.Buffer = NameInfo->FileName;
        TargetComponent.Length = TargetComponent.MaximumLength = (USHORT)NameInfo->FileNameLength;

        if( RtlCompareUnicodeString( &TargetComponent,
                                     &InstanceContext->Mapping.UserMapping.LongNamePath.FinalComponentName,
                                     IgnoreCase) == 0 ||
            RtlCompareUnicodeString( &TargetComponent,
                                     &InstanceContext->Mapping.UserMapping.ShortNamePath.FinalComponentName,
                                     IgnoreCase) == 0 ) {

            Status = STATUS_ACCESS_DENIED;
            ReturnValue = FLT_PREOP_COMPLETE;
            goto NcPreSetShortNameCleanup;
        }

    }

    //
    //  If the user is not setting a short name on our mapping, an ancestor
    //  of our mapping or targetting our mapping, let the request go to the
    //  file system.
    //

    Status = STATUS_SUCCESS;
    ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;

NcPreSetShortNameCleanup:

    if (ReturnValue == FLT_PREOP_COMPLETE) {

        Data->IoStatus.Status = Status;
    }

    if (FileInfo != NULL) {

        FltReleaseFileNameInformation( FileInfo );
    }

    if (InstanceContext != NULL) {

        FltReleaseContext( InstanceContext );
    }

    return ReturnValue;
}


FLT_PREOP_CALLBACK_STATUS
NcPreSetDisposition (
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
    )
/*++

Routine Description:

    Fltmgr callback which manages setting the delete disposition on a file.
    We must disallow setting the delete disposition on an ancestor of either 
    mapping because otherwise we would have to maintain the mapping's
    short/long name pairings.

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.

--*/
{
    NTSTATUS Status;
    FLT_PREOP_CALLBACK_STATUS ReturnValue;
    PFLT_FILE_NAME_INFORMATION FileInfo = NULL;
    PNC_INSTANCE_CONTEXT InstanceContext = NULL;
    PNC_MAPPING Mapping;
    NC_PATH_OVERLAP RealOverlap;
    NC_PATH_OVERLAP UserOverlap;
    PFILE_DISPOSITION_INFORMATION Disposition;
    BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
                                         FO_OPENED_CASE_SENSITIVE );

    PAGED_CODE();

    UNREFERENCED_PARAMETER( CompletionContext );

    FLT_ASSERT( IoGetTopLevelIrp() == NULL );

    //
    //  See if they are setting the delete disposition to false.
    //  If they are we can passthrough. We don't care if people
    //  want to mark the mapping as "don't delete".
    //

    Disposition = Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
    if (Disposition->DeleteFile == FALSE) {

        Status = STATUS_SUCCESS;
        goto NcPreSetDispositionCleanup;
    }

    //
    //  The user is trying to delete a file.
    //  We have to make sure that the file is not an ancestor of either mapping.
    //

    //
    //  Get the file's name.
    //

    Status = NcGetFileNameInformation( Data,
                                       NULL,
                                       NULL,
                                       FLT_FILE_NAME_OPENED | 
                                           FLT_FILE_NAME_QUERY_DEFAULT | 
                                           FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER,
                                       &FileInfo);

    if (!NT_SUCCESS( Status )) {

        goto NcPreSetDispositionCleanup;
    }

    Status = FltParseFileNameInformation( FileInfo );

    if (!NT_SUCCESS( Status )) {

        goto NcPreSetDispositionCleanup;
    }

    //
    //  Get the mapping
    //

    Status = FltGetInstanceContext( FltObjects->Instance,
                                    &InstanceContext );

    if (!NT_SUCCESS( Status )) {
        
        goto NcPreSetDispositionCleanup;
    }

    Mapping = &InstanceContext->Mapping;

    //
    //  Check to see of this will delete an ancestor of the real mapping.
    //

    NcComparePath( &FileInfo->Name,
                   &Mapping->RealMapping,
                   NULL,
                   IgnoreCase,
                   TRUE,
                   &RealOverlap );

    if (RealOverlap.Ancestor) {

        //
        //  The file is an ancestor of the real mapping, so disallow setting
        //  disposition.
        //

        Status = STATUS_ACCESS_DENIED;
        goto NcPreSetDispositionCleanup;
    }

    //
    //  Check the user mapping.
    //

    NcComparePath( &FileInfo->Name,
                   &Mapping->UserMapping,
                   NULL,
                   IgnoreCase,
                   TRUE,
                   &UserOverlap );

    if (UserOverlap.Ancestor) {

        //
        //  The file is an ancestor of the user mapping, so disallow setting
        //  disposition.
        //

        Status = STATUS_ACCESS_DENIED;
        goto NcPreSetDispositionCleanup;
    }

    //
    //  The file is ok to mark for delete.
    //

    Status = STATUS_SUCCESS;
    goto NcPreSetDispositionCleanup;

NcPreSetDispositionCleanup:

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        Data->IoStatus.Status = Status;

    } else {

        ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
    }

    if (FileInfo) {

        FltReleaseFileNameInformation( FileInfo );
    }

    if (InstanceContext) {

        FltReleaseContext( InstanceContext );
    }

    return ReturnValue;
}


FLT_PREOP_CALLBACK_STATUS
NcPreSetLinkInformation (
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
    )
/*++

Routine Description:

    Fltmgr callback which manages link creation on a file.
    We need to make sure that new links down the user mapping
    are redirected to the real mapping.

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.

--*/
{
    FLT_PREOP_CALLBACK_STATUS ReturnValue;
    NTSTATUS Status;
    PFILE_LINK_INFORMATION LinkInfo = 
        Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
    PFILE_LINK_INFORMATION MungedLinkInfo = NULL;
    ULONG MungedLinkInfoSize;
    PFLT_FILE_NAME_INFORMATION FileInfo = NULL;
    PNC_INSTANCE_CONTEXT InstanceContext = NULL;
    NC_PATH_OVERLAP RealOverlap;
    NC_PATH_OVERLAP UserOverlap;
    UNICODE_STRING UserRemainder;
    UNICODE_STRING MungedName = EMPTY_UNICODE_STRING;
    BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
                                         FO_OPENED_CASE_SENSITIVE );

    PAGED_CODE();

    FLT_ASSERT( IoGetTopLevelIrp() == NULL );

    UNREFERENCED_PARAMETER( CompletionContext );

    Status = FltGetInstanceContext( FltObjects->Instance,
                                    &InstanceContext);

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetLinkInformationCleanup;
    }

    Status = FltGetDestinationFileNameInformation( FltObjects->Instance,
                                                   FltObjects->FileObject,
                                                   LinkInfo->RootDirectory,
                                                   LinkInfo->FileName,
                                                   LinkInfo->FileNameLength,
                                                   FLT_FILE_NAME_OPENED |
                                                       FLT_FILE_NAME_QUERY_DEFAULT |
                                                       FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER,
                                                   &FileInfo);

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetLinkInformationCleanup;
    }

    Status = FltParseFileNameInformation( FileInfo );

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetLinkInformationCleanup;
    }

    //
    //  Calculate Overlap and Remainder
    //

    NcComparePath( &FileInfo->Name,
                   &InstanceContext->Mapping.UserMapping,
                   &UserRemainder,
                   IgnoreCase,
                   TRUE,
                   &UserOverlap );

    NcComparePath( &FileInfo->Name,
                   &InstanceContext->Mapping.RealMapping,
                   NULL,
                   IgnoreCase,
                   TRUE,
                   &RealOverlap );

    //
    //  We cannot allow the user to link inside the real mapping, since it
    //  is hidden.
    //

    if (RealOverlap.Match) {

        Status = STATUS_ACCESS_DENIED;
        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetLinkInformationCleanup;

    } else if (RealOverlap.InMapping) {

        //
        //  We should never get here.  Getting here requires an
        //  OPEN_TARGET_DIRECTORY open which should already have failed.
        //

        FLT_ASSERT( !RealOverlap.InMapping );

        Status = STATUS_OBJECT_PATH_NOT_FOUND;
        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetLinkInformationCleanup;

    } else if ((RealOverlap.Ancestor || UserOverlap.Ancestor) &&
               LinkInfo->ReplaceIfExists) {

        //
        //  The user is attempting to overwrite a parent of the mapping.
        //  Fail this operation.
        //

        Status = STATUS_ACCESS_DENIED;
        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetLinkInformationCleanup;
    }

    //
    //  If the destination path is outside the mapping then we can pass it
    //  through without a problem.
    //

    if (!UserOverlap.InMapping) {

        //
        //  The destination outside the mapping.  We can ignore this IO.
        //

        Status = STATUS_SUCCESS;
        ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
        goto NcPreSetLinkInformationCleanup;
    }

    //
    //  The destination is inside the mapping.  This means we have to issue
    //  our own request and forward the results to the user.
    //

    //
    //  We need to build a new path to link on.
    //
    
    Status = NcConstructPath( &InstanceContext->Mapping.RealMapping,
                              &UserRemainder,
                              TRUE,
                              &MungedName );

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetLinkInformationCleanup;
    }

    //
    //  Create our own link structure.
    //  
    
    MungedLinkInfoSize = sizeof(FILE_LINK_INFORMATION) + MungedName.Length - sizeof(WCHAR);
    MungedLinkInfo = ExAllocatePoolWithTag( PagedPool,
                                            MungedLinkInfoSize,
                                            NC_SET_LINK_BUFFER_TAG );

    if (MungedLinkInfo == NULL) {

        Status = STATUS_INSUFFICIENT_RESOURCES;
        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreSetLinkInformationCleanup;
    }

    MungedLinkInfo->ReplaceIfExists = LinkInfo->ReplaceIfExists;
    MungedLinkInfo->RootDirectory = NULL;
    MungedLinkInfo->FileNameLength = MungedName.Length;

    RtlCopyMemory( &MungedLinkInfo->FileName, MungedName.Buffer, MungedName.Length );

    //
    //  Issue our own request.
    //
    
    Status = FltSetInformationFile( FltObjects->Instance,
                                    FltObjects->FileObject,
                                    MungedLinkInfo,
                                    MungedLinkInfoSize,
                                    FileLinkInformation );

    //
    //  Because we issued the IO, we will pass complete this ourselves.
    //
    
    ReturnValue = FLT_PREOP_COMPLETE;

NcPreSetLinkInformationCleanup:

    if (ReturnValue == FLT_PREOP_COMPLETE) {

        Data->IoStatus.Status = Status;
    }

    if (MungedLinkInfo != NULL) {

        ExFreePoolWithTag( MungedLinkInfo, NC_SET_LINK_BUFFER_TAG );
    }

    if (FileInfo != NULL) {

        FltReleaseFileNameInformation( FileInfo );
    }

    if (InstanceContext != NULL) {

        FltReleaseContext( InstanceContext );
    }

    if (MungedName.Buffer != NULL) {

        ExFreePoolWithTag( MungedName.Buffer, NC_TAG );
    }

    return ReturnValue;
}

FLT_PREOP_CALLBACK_STATUS
NcPreRename(
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext
    )
/*++

Routine Description:

    Fltmgr callback which manages renaming files.  We must disallow renaming
    on an ancestor of either mapping because otherwise we would have to
    maintain the mapping's short/long name pairings.

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.

--*/
{
    //
    //  Return Values
    //
    
    NTSTATUS Status;
    FLT_PREOP_CALLBACK_STATUS ReturnValue;

    //
    //  Contexts
    //
    
    PNC_INSTANCE_CONTEXT InstanceContext = NULL;
   
    //
    //  Data
    //
    
    PFILE_RENAME_INFORMATION RenameInfo = 
        Data->Iopb->Parameters.SetFileInformation.InfoBuffer;
    
    //
    //  FileInformation
    //
    
    PFLT_FILE_NAME_INFORMATION TargetInfo = NULL;
    PFLT_FILE_NAME_INFORMATION SrcInfo = NULL;
    
    //
    //  Target Real Overlap
    //
    
    NC_PATH_OVERLAP TargetRealOverlap;
    UNICODE_STRING TargetRealRemainder;
    
    //
    //  Target User Overlap
    //
    
    NC_PATH_OVERLAP TargetUserOverlap;
    UNICODE_STRING TargetUserRemainder;

    //
    //  Src Real Overlap
    //
    
    NC_PATH_OVERLAP SrcRealOverlap;
    NC_PATH_OVERLAP SrcUserOverlap;
    
    //
    //  Munge Data
    //
    
    UNICODE_STRING MungedTargetName = EMPTY_UNICODE_STRING;
    PFILE_RENAME_INFORMATION MungedRenameInfo = NULL;
    ULONG MungedRenameLength;
    BOOLEAN IgnoreCase = !BooleanFlagOn( FltObjects->FileObject->Flags,
                                         FO_OPENED_CASE_SENSITIVE );

    PAGED_CODE();

    UNREFERENCED_PARAMETER( CompletionContext );

    FLT_ASSERT( IoGetTopLevelIrp() == NULL );

    
    //
    //  Get Instance Context 
    //

    Status = FltGetInstanceContext( FltObjects->Instance,
                                    &InstanceContext );

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreRenameCleanup;
    }

    //
    //  Find out the src file's name.
    //
    
    Status = NcGetFileNameInformation( Data,
                                       NULL,
                                       NULL,
                                       FLT_FILE_NAME_OPENED | 
                                           FLT_FILE_NAME_QUERY_DEFAULT | 
                                           FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER,
                                       &SrcInfo);

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreRenameCleanup;
    }

    Status = FltParseFileNameInformation( SrcInfo );

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreRenameCleanup;
    }

    //
    //  Find the src's overlap with the real and user mappings.
    //
    
    NcComparePath( &SrcInfo->Name,
                   &InstanceContext->Mapping.RealMapping,
                   NULL,
                   IgnoreCase,
                   TRUE,
                   &SrcRealOverlap );

    NcComparePath( &SrcInfo->Name,
                   &InstanceContext->Mapping.UserMapping,
                   NULL,
                   IgnoreCase,
                   TRUE,
                   &SrcUserOverlap );

    //
    //  If the src is an ancestor of either the user or real mappings we can
    //  fail the request.
    //
    
    if (SrcUserOverlap.Ancestor || SrcRealOverlap.Ancestor) {

        ReturnValue = FLT_PREOP_COMPLETE;
        Status = STATUS_ACCESS_DENIED;
        goto NcPreRenameCleanup;
    }

    //
    //  Find out the target file's name.
    //
    
    Status = FltGetDestinationFileNameInformation( FltObjects->Instance,
                                                   FltObjects->FileObject,
                                                   RenameInfo->RootDirectory,
                                                   RenameInfo->FileName,
                                                   RenameInfo->FileNameLength,
                                                   FLT_FILE_NAME_OPENED |
                                                       FLT_FILE_NAME_QUERY_DEFAULT |
                                                       FLT_FILE_NAME_REQUEST_FROM_CURRENT_PROVIDER,
                                                   &TargetInfo);

    if (!NT_SUCCESS( Status )) {

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreRenameCleanup;
    }

    Status = FltParseFileNameInformation( TargetInfo );

    if( !NT_SUCCESS( Status ) ) {
        
        FLT_ASSERT( NT_SUCCESS( Status ) );

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreRenameCleanup;
    }

    //
    //  Find the target's overlap with the real and user mappings.
    //
    
    NcComparePath( &TargetInfo->Name,
                   &InstanceContext->Mapping.RealMapping,
                   &TargetRealRemainder,
                   IgnoreCase,
                   TRUE,
                   &TargetRealOverlap );

    NcComparePath( &TargetInfo->Name,
                   &InstanceContext->Mapping.UserMapping,
                   &TargetUserRemainder,
                   IgnoreCase,
                   TRUE,
                   &TargetUserOverlap );

    //
    //  If the target is in the real mapping, then disallow the rename.  If
    //  the target is to an ancestor of the mappings, this could change IDs
    //  and is therefore also disallowed.
    //

    if (TargetRealOverlap.InMapping) {

        Status = STATUS_ACCESS_DENIED;
        ReturnValue = FLT_PREOP_COMPLETE;    
        goto NcPreRenameCleanup;

    } else if ((TargetRealOverlap.Ancestor || TargetUserOverlap.Ancestor) &&
                RenameInfo->ReplaceIfExists) {

        Status = STATUS_ACCESS_DENIED;
        ReturnValue = FLT_PREOP_COMPLETE;    
        goto NcPreRenameCleanup;
    }


    //
    //  If the target is in the user mapping, then we need to munge the
    //  name and send the request down.  If this is a stream rename we
    //  do not perform the mapping since only the stream name is changing.
    //

    if (TargetUserOverlap.InMapping &&
        (RenameInfo->FileName[0] != ':')) {

        Status = NcConstructPath( &InstanceContext->Mapping.RealMapping,
                                  &TargetUserRemainder,
                                  TRUE,
                                  &MungedTargetName );

        if (!NT_SUCCESS( Status )) {

            ReturnValue = FLT_PREOP_COMPLETE;
            goto NcPreRenameCleanup;
        }


        //
        //  Because the target is in the user mapping, we have to issue
        //  our own rename down the real mapping.
        //

        //
        //  Allocate rename information structure.
        //

        MungedRenameLength = sizeof(FILE_RENAME_INFORMATION) -
                             sizeof(WCHAR) +
                             MungedTargetName.Length;

        MungedRenameInfo = ExAllocatePoolWithTag( PagedPool,
                                                  MungedRenameLength,
                                                  NC_RENAME_BUFFER_TAG );

        if (MungedRenameInfo == NULL) {

            Status = STATUS_INSUFFICIENT_RESOURCES;
            ReturnValue = FLT_PREOP_COMPLETE;
            goto NcPreRenameCleanup;
        }

        //
        //  Copy user rename parameters.
        //

        MungedRenameInfo->ReplaceIfExists = RenameInfo->ReplaceIfExists;
        MungedRenameInfo->RootDirectory = NULL;
        MungedRenameInfo->FileNameLength = MungedTargetName.Length;
        RtlCopyMemory( &MungedRenameInfo->FileName, 
                       MungedTargetName.Buffer, 
                       MungedTargetName.Length );

        //
        //  Send the request. Note that we cannot just place the new buffer
        //  in the CallbackData; filesystems use the name from a previous
        //  OPEN_TARGET_DIRECTORY open, so changing the buffer here would
        //  result in unexpected (and undefined!) behavior.
        //

        Status = FltSetInformationFile( FltObjects->Instance,
                                        FltObjects->FileObject,
                                        MungedRenameInfo,
                                        MungedRenameLength,
                                        FileRenameInformation );

        //
        //  Complete the IO.
        //

        ReturnValue = FLT_PREOP_COMPLETE;
        goto NcPreRenameCleanup;

    } else {

        //
        //  The target was outside the mapping. The rename does not have 
        //  to be munged. Pass through.
        //

        ReturnValue = FLT_PREOP_SUCCESS_NO_CALLBACK;
        goto NcPreRenameCleanup;

    }

NcPreRenameCleanup:

    if (ReturnValue == FLT_PREOP_COMPLETE) {

        Data->IoStatus.Status = Status;
    }

    if (InstanceContext != NULL) {

        FltReleaseContext( InstanceContext );
    }

    if (TargetInfo != NULL) {

        FltReleaseFileNameInformation( TargetInfo );
    }

    if (SrcInfo != NULL) {

        FltReleaseFileNameInformation( SrcInfo );
    }

    if (MungedTargetName.Buffer != NULL) {

        ExFreePoolWithTag( MungedTargetName.Buffer, NC_GENERATE_NAME_TAG );
    }

    if (MungedRenameInfo != NULL) {

        ExFreePoolWithTag( MungedRenameInfo, NC_RENAME_BUFFER_TAG );
    }

    return ReturnValue;
}


Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS