Example #1
0
BOOLEAN
CdEnumerateIndex (
    _In_ PIRP_CONTEXT IrpContext,
    _In_ PCCB Ccb,
    _Inout_ PFILE_ENUM_CONTEXT FileContext,
    _In_ BOOLEAN ReturnNextEntry
    )

/*++

Routine Description:

    This routine is the worker routine for index enumeration.  We are positioned
    at some dirent in the directory and will either return the first match
    at that point or look to the next entry.  The Ccb contains details about
    the type of matching to do.  If the user didn't specify a version in
    his search string then we only return the first version of a sequence
    of files with versions.  We also don't return any associated files.

Arguments:

    Ccb - Ccb for this directory handle.

    FileContext - File context already positioned at some entry in the directory.

    ReturnNextEntry - Indicates if we are returning this entry or should start
        with the next entry.

Return Value:

    BOOLEAN - TRUE if next entry is found, FALSE otherwise.

--*/

{
    PDIRENT PreviousDirent = NULL;
    PDIRENT ThisDirent = &FileContext->InitialDirent->Dirent;

    BOOLEAN Found = FALSE;

    PAGED_CODE();

    //
    //  Loop until we find a match or exaust the directory.
    //

    while (TRUE) {

        //
        //  Move to the next entry unless we want to consider the current
        //  entry.
        //

        if (ReturnNextEntry) {

            if (!CdLookupNextInitialFileDirent( IrpContext, Ccb->Fcb, FileContext )) {

                break;
            }

            PreviousDirent = ThisDirent;
            ThisDirent = &FileContext->InitialDirent->Dirent;

            CdUpdateDirentName( IrpContext, ThisDirent, FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
        
        } else {

            ReturnNextEntry = TRUE;
        }

        //
        //  Don't bother if we have a constant entry and are ignoring them.
        //
        
        if (FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ) &&
            FlagOn( Ccb->Flags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY )) {

            continue;
        }

        //
        //  Look at the current entry if it is not an associated file
        //  and the name doesn't match the previous file if the version
        //  name is not part of the search.
        //

        if (!FlagOn( ThisDirent->DirentFlags, CD_ATTRIBUTE_ASSOC )) {

            //
            //  Check if this entry matches the previous entry except
            //  for version number and whether we should return the
            //  entry in that case.  Go directly to the name comparison
            //  if:
            //
            //      There is no previous entry.
            //      The search expression has a version component.
            //      The name length doesn't match the length of the previous entry.
            //      The base name strings don't match.
            //

            if ((PreviousDirent == NULL) ||
                (Ccb->SearchExpression.VersionString.Length != 0) ||
                (PreviousDirent->CdCaseFileName.FileName.Length != ThisDirent->CdCaseFileName.FileName.Length) ||
                FlagOn( PreviousDirent->DirentFlags, CD_ATTRIBUTE_ASSOC ) ||
                !RtlEqualMemory( PreviousDirent->CdCaseFileName.FileName.Buffer,
                                 ThisDirent->CdCaseFileName.FileName.Buffer,
                                 ThisDirent->CdCaseFileName.FileName.Length )) {

                //
                //  If we match all names then return to our caller.
                //

                if (FlagOn( Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL )) {

                    FileContext->ShortName.FileName.Length = 0;
                    Found = TRUE;
                    break;
                }

                //
                //  Check if the long name matches the search expression.
                //

                if (CdIsNameInExpression( IrpContext,
                                          &ThisDirent->CdCaseFileName,
                                          &Ccb->SearchExpression,
                                          Ccb->Flags,
                                          TRUE )) {

                    //
                    //  Let our caller know we found an entry.
                    //

                    Found = TRUE;
                    FileContext->ShortName.FileName.Length = 0;
                    break;
                }

                //
                //  The long name didn't match so we need to check for a
                //  possible short name match.  There is no match if the
                //  long name is 8dot3 or the search expression has a
                //  version component.  Special case the self and parent
                //  entries.
                //

                if ((Ccb->SearchExpression.VersionString.Length == 0) &&
                    !FlagOn( ThisDirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY ) &&
                    !CdIs8dot3Name( IrpContext,
                                    ThisDirent->CdFileName.FileName )) {

                    CdGenerate8dot3Name( IrpContext,
                                         &ThisDirent->CdCaseFileName.FileName,
                                         ThisDirent->DirentOffset,
                                         FileContext->ShortName.FileName.Buffer,
                                         &FileContext->ShortName.FileName.Length );

                    //
                    //  Check if this name matches.
                    //

                    if (CdIsNameInExpression( IrpContext,
                                              &FileContext->ShortName,
                                              &Ccb->SearchExpression,
                                              Ccb->Flags,
                                              FALSE )) {

                        //
                        //  Let our caller know we found an entry.
                        //

                        Found = TRUE;
                        break;
                    }
                }
            }
        }
    }

    //
    //  If we found the entry then make sure we walk through all of the
    //  file dirents.
    //

    if (Found) {

        CdLookupLastFileDirent( IrpContext, Ccb->Fcb, FileContext );
    }

    return Found;
}
Example #2
0
VOID
CdInitializeEnumeration (
    _In_ PIRP_CONTEXT IrpContext,
    _In_ PIO_STACK_LOCATION IrpSp,
    _In_ PFCB Fcb,
    _Inout_ PCCB Ccb,
    _Inout_ PFILE_ENUM_CONTEXT FileContext,
    _Out_ PBOOLEAN ReturnNextEntry,
    _Out_ PBOOLEAN ReturnSingleEntry,
    _Out_ PBOOLEAN InitialQuery
    )

/*++

Routine Description:

    This routine is called to initialize the enumeration variables and structures.
    We look at the state of a previous enumeration from the Ccb as well as any
    input values from the user.  On exit we will position the FileContext at
    a file in the directory and let the caller know whether this entry or the
    next entry should be returned.

Arguments:

    IrpSp - Irp stack location for this request.

    Fcb - Fcb for this directory.

    Ccb - Ccb for the directory handle.

    FileContext - FileContext to use for this enumeration.

    ReturnNextEntry - Address to store whether we should return the entry at
        the FileContext position or the next entry.

    ReturnSingleEntry - Address to store whether we should only return
        a single entry.

    InitialQuery - Address to store whether this is the first enumeration
        query on this handle.

Return Value:

    None.

--*/

{
    NTSTATUS Status;

    PUNICODE_STRING FileName;
    CD_NAME WildCardName;
    CD_NAME SearchExpression;

    ULONG CcbFlags;

    ULONG DirentOffset;
    ULONG LastDirentOffset;
    BOOLEAN KnownOffset;

    BOOLEAN Found;

    PAGED_CODE();

    //
    //  If the user has specified that the scan be restarted, and has specicified 
    //  a new query pattern, reinitialize the CCB.
    //

    if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) {

        CdLockFcb( IrpContext, Fcb );

        FileName = (PUNICODE_STRING) IrpSp->Parameters.QueryDirectory.FileName;
        if (FileName && FileName->Length > 0) {

            if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL )) {

                CdFreePool( &Ccb->SearchExpression.FileName.Buffer );
            }

            ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_MATCH_ALL);
            ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED);
            ClearFlag(Ccb->Flags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD);
        }

        CdUnlockFcb( IrpContext, Fcb );
    }

    //
    //  If this is the initial query then build a search expression from the input
    //  file name.
    //

    if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) {

        FileName = IrpSp->Parameters.QueryDirectory.FileName;

        CcbFlags = 0;

        //
        //  If the filename is not specified or is a single '*' then we will
        //  match all names.
        //

        if ((FileName == NULL) ||
            (FileName->Buffer == NULL) ||
            (FileName->Length == 0) ||
            ((FileName->Length == sizeof( WCHAR )) &&
             (FileName->Buffer[0] == L'*'))) {

            SetFlag( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL );
            RtlZeroMemory( &SearchExpression, sizeof( SearchExpression ));

        //
        //  Otherwise build the CdName from the name in the stack location.
        //  This involves building both the name and version portions and
        //  checking for wild card characters.  We also upcase the string if
        //  this is a case-insensitive search.
        //

        } else {

            //
            //  Create a CdName to check for wild cards.
            //

            WildCardName.FileName = *FileName;

            CdConvertNameToCdName( IrpContext, &WildCardName );

            //
            //  The name better have at least one character.
            //

            if (WildCardName.FileName.Length == 0) {

                CdRaiseStatus( IrpContext, STATUS_INVALID_PARAMETER );
            }

            //
            //  Check for wildcards in the separate components.
            //

            if (FsRtlDoesNameContainWildCards( &WildCardName.FileName)) {

                SetFlag( CcbFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD );
            }

            if ((WildCardName.VersionString.Length != 0) &&
                (FsRtlDoesNameContainWildCards( &WildCardName.VersionString ))) {

                SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD );

                //
                //  Check if this is a wild card only and match all version
                //  strings.
                //

                if ((WildCardName.VersionString.Length == sizeof( WCHAR )) &&
                    (WildCardName.VersionString.Buffer[0] == L'*')) {

                    SetFlag( CcbFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL );
                }
            }

            //
            //  Now create the search expression to store in the Ccb.
            //

            SearchExpression.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool,
                                                                         FileName->Length,
                                                                         TAG_ENUM_EXPRESSION );

            SearchExpression.FileName.MaximumLength = FileName->Length;

            //
            //  Either copy the name directly or perform the upcase.
            //

            if (FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE )) {

                Status = RtlUpcaseUnicodeString( (PUNICODE_STRING) &SearchExpression.FileName,
                                                 FileName,
                                                 FALSE );

                //
                //  This should never fail.
                //
                __analysis_assert( Status == STATUS_SUCCESS );
                NT_ASSERT( Status == STATUS_SUCCESS );

            } else {

                RtlCopyMemory( SearchExpression.FileName.Buffer,
                               FileName->Buffer,
                               FileName->Length );
            }

            //
            //  Now split into the separate name and version components.
            //

            SearchExpression.FileName.Length = WildCardName.FileName.Length;
            SearchExpression.VersionString.Length = WildCardName.VersionString.Length;
            SearchExpression.VersionString.MaximumLength = WildCardName.VersionString.MaximumLength;

            SearchExpression.VersionString.Buffer = Add2Ptr( SearchExpression.FileName.Buffer,
                                                             SearchExpression.FileName.Length + sizeof( WCHAR ),
                                                             PWCHAR );
        }

        //
        //  But we do not want to return the constant "." and ".." entries for
        //  the root directory, for consistency with the rest of Microsoft's
        //  filesystems.
        //

        if (Fcb == Fcb->Vcb->RootIndexFcb) {

            SetFlag( CcbFlags, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY );
        }

        //
        //  Now lock the Fcb in order to update the Ccb with the inital
        //  enumeration values.
        //

        CdLockFcb( IrpContext, Fcb );

        //
        //  Check again that this is the initial search.
        //

        if (!FlagOn( Ccb->Flags, CCB_FLAG_ENUM_INITIALIZED )) {

            //
            //  Update the values in the Ccb.
            //

            Ccb->CurrentDirentOffset = Fcb->StreamOffset;
            Ccb->SearchExpression = SearchExpression;

            //
            //  Set the appropriate flags in the Ccb.
            //

            SetFlag( Ccb->Flags, CcbFlags | CCB_FLAG_ENUM_INITIALIZED );

        //
        //  Otherwise cleanup any buffer allocated here.
        //

        } else {

            if (!FlagOn( CcbFlags, CCB_FLAG_ENUM_MATCH_ALL )) {

                CdFreePool( &SearchExpression.FileName.Buffer );
            }
        }

    //
    //  Otherwise lock the Fcb so we can read the current enumeration values.
    //

    } else {

        CdLockFcb( IrpContext, Fcb );
    }

    //
    //  Capture the current state of the enumeration.
    //
    //  If the user specified an index then use his offset.  We always
    //  return the next entry in this case.
    //

    if (FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED )) {

        KnownOffset = FALSE;
        DirentOffset = IrpSp->Parameters.QueryDirectory.FileIndex;
        *ReturnNextEntry = TRUE;

    //
    //  If we are restarting the scan then go from the self entry.
    //

    } else if (FlagOn( IrpSp->Flags, SL_RESTART_SCAN )) {

        KnownOffset = TRUE;
        DirentOffset = Fcb->StreamOffset;
        *ReturnNextEntry = FALSE;

    //
    //  Otherwise use the values from the Ccb.
    //

    } else {

        KnownOffset = TRUE;
        DirentOffset = Ccb->CurrentDirentOffset;
        *ReturnNextEntry = BooleanFlagOn( Ccb->Flags, CCB_FLAG_ENUM_RETURN_NEXT );
    }

    //
    //  Unlock the Fcb.
    //

    CdUnlockFcb( IrpContext, Fcb );

    //
    //  We have the starting offset in the directory and whether to return
    //  that entry or the next.  If we are at the beginning of the directory
    //  and are returning that entry, then tell our caller this is the
    //  initial query.
    //

    *InitialQuery = FALSE;

    if ((DirentOffset == Fcb->StreamOffset) &&
        !(*ReturnNextEntry)) {

        *InitialQuery = TRUE;
    }

    //
    //  If there is no file object then create it now.
    //

    CdVerifyOrCreateDirStreamFile( IrpContext, Fcb);

    //
    //  Determine the offset in the stream to position the FileContext and
    //  whether this offset is known to be a file offset.
    //
    //  If this offset is known to be safe then go ahead and position the
    //  file context.  This handles the cases where the offset is the beginning
    //  of the stream, the offset is from a previous search or this is the
    //  initial query.
    //

    if (KnownOffset) {

        CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, DirentOffset );

    //
    //  Otherwise we walk through the directory from the beginning until
    //  we reach the entry which contains this offset.
    //

    } else {

        LastDirentOffset = Fcb->StreamOffset;
        Found = TRUE;

        CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset );

        //
        //  If the requested offset is prior to the beginning offset in the stream
        //  then don't return the next entry.
        //

        if (DirentOffset < LastDirentOffset) {

            *ReturnNextEntry = FALSE;

        //
        //  Else look for the last entry which ends past the desired index.
        //

        } else {

            //
            //  Keep walking through the directory until we run out of
            //  entries or we find an entry which ends beyond the input
            //  index value.
            //

            do {

                //
                //  If we have passed the index value then exit.
                //

                if (FileContext->InitialDirent->Dirent.DirentOffset > DirentOffset) {

                    Found = FALSE;
                    break;
                }

                //
                //  Remember the current position in case we need to go back.
                //

                LastDirentOffset = FileContext->InitialDirent->Dirent.DirentOffset;

                //
                //  Exit if the next entry is beyond the desired index value.
                //

                if (LastDirentOffset + FileContext->InitialDirent->Dirent.DirentLength > DirentOffset) {

                    break;
                }

                Found = CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext );

            } while (Found);

            //
            //  If we didn't find the entry then go back to the last known entry.
            //  This can happen if the index lies in the unused range at the
            //  end of a sector.
            //

            if (!Found) {

                CdCleanupFileContext( IrpContext, FileContext );
                CdInitializeFileContext( IrpContext, FileContext );

                CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, LastDirentOffset );
            }
        }
    }

    //
    //  Only update the dirent name if we will need it for some reason.
    //  Don't update this name if we are returning the next entry and
    //  the search string has a version component.
    //

    FileContext->ShortName.FileName.Length = 0;

    if (!(*ReturnNextEntry) ||
        (Ccb->SearchExpression.VersionString.Length == 0)) {

        //
        //  Update the name in the dirent into filename and version components.
        //

        CdUpdateDirentName( IrpContext,
                            &FileContext->InitialDirent->Dirent,
                            FlagOn( Ccb->Flags, CCB_FLAG_IGNORE_CASE ));
    }

    //
    //  Look at the flag in the IrpSp indicating whether to return just
    //  one entry.
    //

    *ReturnSingleEntry = FALSE;

    if (FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY )) {

        *ReturnSingleEntry = TRUE;
    }

    return;
}
Example #3
0
__drv_mustHoldCriticalRegion
NTSTATUS
CdQueryAlternateNameInfo (
    __in PIRP_CONTEXT IrpContext,
    __in PFCB Fcb,
    __in PCCB Ccb,
    __out PFILE_NAME_INFORMATION Buffer,
    __inout PULONG Length
    )

/*++

Routine Description:

    This routine performs the query alternate name information function.
    We lookup the dirent for this file and then check if there is a
    short name.

Arguments:

    Fcb - Supplies the Fcb being queried, it has been verified.

    Ccb - Ccb for this open handle.

    Buffer - Supplies a pointer to the buffer where the information is to
        be returned.

    Length - Supplies the length of the buffer in bytes, and receives the
        remaining bytes free in the buffer upon return.

Return Value:

    NTSTATUS - STATUS_SUCCESS if the whole name would fit into the user buffer,
               STATUS_OBJECT_NAME_NOT_FOUND if we can't return the name,
               STATUS_BUFFER_OVERFLOW otherwise.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;

    DIRENT_ENUM_CONTEXT DirContext = {0};
    DIRENT Dirent = {0};

    PUNICODE_STRING NameToUse;
    ULONG DirentOffset;

    COMPOUND_PATH_ENTRY CompoundPathEntry = {0};
    FILE_ENUM_CONTEXT FileContext;

    PFCB ParentFcb = NULL;
    BOOLEAN ReleaseParentFcb = FALSE;

    BOOLEAN CleanupFileLookup = FALSE;
    BOOLEAN CleanupDirectoryLookup = FALSE;

    WCHAR ShortNameBuffer[ BYTE_COUNT_8_DOT_3 / 2 ];
    USHORT ShortNameLength;

    PAGED_CODE();

    //
    //  Initialize the buffer length to zero.
    //

    Buffer->FileNameLength = 0;

    //
    //  If this is the root or this file was opened using a version number then
    //  there is no short name.
    //

    if ((Fcb == Fcb->Vcb->RootIndexFcb) ||
        FlagOn( Ccb->Flags, CCB_FLAG_OPEN_WITH_VERSION)) {

        return STATUS_OBJECT_NAME_NOT_FOUND;
    }

    //
    //  Use a try-finally to cleanup the structures.
    //

    try {

        ParentFcb = Fcb->ParentFcb;
        CdAcquireFileShared( IrpContext, ParentFcb );
        ReleaseParentFcb = TRUE;
    
        CdVerifyOrCreateDirStreamFile( IrpContext, ParentFcb);

        if (CdFidIsDirectory( Fcb->FileId)) {

            //
            //  Fcb is for a directory, so we need to dig the dirent from the parent.  In
            //  order to do this we need to get the name of the directory from its pathtable
            //  entry and then search in the parent for a matching dirent.
            //
            //  This could be optimized somewhat.
            //

            CdInitializeCompoundPathEntry( IrpContext, &CompoundPathEntry );
            CdInitializeFileContext( IrpContext, &FileContext );

            CleanupDirectoryLookup = TRUE;

            CdLookupPathEntry( IrpContext,
                               CdQueryFidPathTableOffset( Fcb->FileId ),
                               Fcb->Ordinal,
                               FALSE,
                               &CompoundPathEntry );

            CdUpdatePathEntryName( IrpContext, &CompoundPathEntry.PathEntry, TRUE );

            if (!CdFindDirectory( IrpContext,
                                  ParentFcb,
                                  &CompoundPathEntry.PathEntry.CdCaseDirName,
                                  TRUE,
                                  &FileContext )) {

                //
                //  If we failed to find the child directory by name in the parent
                //  something is quite wrong with this disc.
                //

                CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
            }

            NameToUse = &FileContext.InitialDirent->Dirent.CdCaseFileName.FileName;
            DirentOffset = FileContext.InitialDirent->Dirent.DirentOffset;
        
        } else {

            //
            //  Initialize the search dirent structures.
            //
        
            CdInitializeDirContext( IrpContext, &DirContext );
            CdInitializeDirent( IrpContext, &Dirent );
    
            CleanupFileLookup = TRUE;
        
            CdLookupDirent( IrpContext,
                            ParentFcb,
                            CdQueryFidDirentOffset( Fcb->FileId ),
                            &DirContext );
    
            CdUpdateDirentFromRawDirent( IrpContext,
                                         ParentFcb,
                                         &DirContext,
                                         &Dirent );

            //
            //  Now update the dirent name.
            //
    
            CdUpdateDirentName( IrpContext, &Dirent, TRUE );
    
            NameToUse = &Dirent.CdCaseFileName.FileName;
            DirentOffset = Dirent.DirentOffset;
        }

        //
        //  If the name is 8.3 then fail this request.
        //

        if (CdIs8dot3Name( IrpContext,
                           *NameToUse )) {


            try_return( Status = STATUS_OBJECT_NAME_NOT_FOUND );
        }

        CdGenerate8dot3Name( IrpContext,
                             NameToUse,
                             DirentOffset,
                             ShortNameBuffer,
                             &ShortNameLength );

        //
        //  We now have the short name.  We have left it in Unicode form so copy it directly.
        //

        Buffer->FileNameLength = ShortNameLength;

        if (Buffer->FileNameLength + sizeof( ULONG ) > *Length) {

            Buffer->FileNameLength = *Length - sizeof( ULONG );
            Status = STATUS_BUFFER_OVERFLOW;
        }

        RtlCopyMemory( Buffer->FileName, ShortNameBuffer, Buffer->FileNameLength );

    try_exit:  NOTHING;
    } finally {

        if (CleanupFileLookup) {

            CdCleanupDirContext( IrpContext, &DirContext );
            CdCleanupDirent( IrpContext, &Dirent );

        } else if (CleanupDirectoryLookup) {

            CdCleanupCompoundPathEntry( IrpContext, &CompoundPathEntry );
            CdCleanupFileContext( IrpContext, &FileContext );
        }

        if (ReleaseParentFcb) {

            CdReleaseFile( IrpContext, ParentFcb );
        }
    }

    //
    //  Reduce the available bytes by the amount stored into this buffer.
    //

    if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {

        *Length -= sizeof( ULONG ) + Buffer->FileNameLength;
    }

    return Status;
}