Beispiel #1
0
VOID
CdVerifyVcb (
    IN PIRP_CONTEXT IrpContext,
    IN PVCB Vcb
)

/*++

Routine Description:

    This routine checks that the current Vcb is valid and currently mounted
    on the device.  It will raise on an error condition.

    We check whether the volume needs verification and the current state
    of the Vcb.

Arguments:

    Vcb - This is the volume to verify.

Return Value:

    None

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    IO_STATUS_BLOCK Iosb;
    ULONG MediaChangeCount = 0;
    BOOLEAN ForceVerify = FALSE;
    BOOLEAN DevMarkedForVerify;
    //KIRQL SavedIrql; /* ReactOS Change: GCC Unused variable */

    PAGED_CODE();

    //
    //  Fail immediately if the volume is in the progress of being dismounted
    //  or has been marked invalid.
    //

    if ((Vcb->VcbCondition == VcbInvalid) ||
            ((Vcb->VcbCondition == VcbDismountInProgress) &&
             (IrpContext->MajorFunction != IRP_MJ_CREATE))) {

        CdRaiseStatus( IrpContext, STATUS_FILE_INVALID );
    }

    if (FlagOn( Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA ))  {

        //
        //  Capture the real device verify state.
        //

        DevMarkedForVerify = CdRealDevNeedsVerify( Vcb->Vpb->RealDevice);

        //
        //  If the media is removable and the verify volume flag in the
        //  device object is not set then we want to ping the device
        //  to see if it needs to be verified.
        //

        if (Vcb->VcbCondition != VcbMountInProgress) {

            Status = CdPerformDevIoCtrl( IrpContext,
                                         IOCTL_CDROM_CHECK_VERIFY,
                                         Vcb->TargetDeviceObject,
                                         &MediaChangeCount,
                                         sizeof(ULONG),
                                         FALSE,
                                         FALSE,
                                         &Iosb );

            if (Iosb.Information != sizeof(ULONG)) {

                //
                //  Be safe about the count in case the driver didn't fill it in
                //

                MediaChangeCount = 0;
            }

            //
            //  There are four cases when we want to do a verify.  These are the
            //  first three.
            //
            //  1. We are mounted,  and the device has become empty
            //  2. The device has returned verify required (=> DO_VERIFY_VOL flag is
            //     set, but could be due to hardware condition)
            //  3. Media change count doesn't match the one in the Vcb
            //

            if (((Vcb->VcbCondition == VcbMounted) &&
                    CdIsRawDevice( IrpContext, Status ))
                    ||
                    (Status == STATUS_VERIFY_REQUIRED)
                    ||
                    (NT_SUCCESS(Status) &&
                     (Vcb->MediaChangeCount != MediaChangeCount))) {

                //
                //  If we are currently the volume on the device then it is our
                //  responsibility to set the verify flag.  If we're not on the device,
                //  then we shouldn't touch the flag.
                //

                if (!FlagOn( Vcb->VcbState, VCB_STATE_VPB_NOT_ON_DEVICE) &&
                        !DevMarkedForVerify)  {

                    DevMarkedForVerify = CdMarkDevForVerifyIfVcbMounted( Vcb);
                }

                ForceVerify = TRUE;

                //
                //  NOTE that we no longer update the media change count here. We
                //  do so only when we've actually completed a verify at a particular
                //  change count value.
                //
            }
        }

        //
        //  This is the 4th verify case.
        //
        //  We ALWAYS force CREATE requests on unmounted volumes through the
        //  verify path.  These requests could have been in limbo between
        //  IoCheckMountedVpb and us when a verify/mount took place and caused
        //  a completely different fs/volume to be mounted.  In this case the
        //  checks above may not have caught the condition,  since we may already
        //  have verified (wrong volume) and decided that we have nothing to do.
        //  We want the requests to be re routed to the currently mounted volume,
        //  since they were directed at the 'drive',  not our volume.
        //

        if (NT_SUCCESS( Status) && !ForceVerify &&
                (IrpContext->MajorFunction == IRP_MJ_CREATE))  {

            PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( IrpContext->Irp);

            ForceVerify = (IrpSp->FileObject->RelatedFileObject == NULL) &&
                          ((Vcb->VcbCondition == VcbDismountInProgress) ||
                           (Vcb->VcbCondition == VcbNotMounted));

            //
            //  Note that we don't touch the device verify flag here.  It required
            //  it would have been caught and set by the first set of checks.
            //
        }

        //
        //  Raise the verify / error if neccessary.
        //

        if (ForceVerify || !NT_SUCCESS( Status)) {

            IoSetHardErrorOrVerifyDevice( IrpContext->Irp,
                                          Vcb->Vpb->RealDevice );

            CdRaiseStatus( IrpContext, ForceVerify ? STATUS_VERIFY_REQUIRED : Status);
        }
    }

    //
    //  Based on the condition of the Vcb we'll either return to our
    //  caller or raise an error condition
    //

    switch (Vcb->VcbCondition) {

    case VcbNotMounted:

        IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice );

        CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME );
        break;

    case VcbInvalid:
    case VcbDismountInProgress :

        CdRaiseStatus( IrpContext, STATUS_FILE_INVALID );
        break;

    /* ReactOS Change: GCC "enumeration value not handled in switch" */
    default:
        break;
    }
}
Beispiel #2
0
BOOLEAN
CdVerifyFcbOperation (
    IN PIRP_CONTEXT IrpContext OPTIONAL,
    IN PFCB Fcb
)

/*++

Routine Description:

    This routine is called to verify that the state of the Fcb is valid
    to allow the current operation to continue.  We use the state of the
    Vcb, target device and type of operation to determine this.

Arguments:

    IrpContext - IrpContext for the request.  If not present then we
        were called from the fast IO path.

    Fcb - Fcb to perform the request on.

Return Value:

    BOOLEAN - TRUE if the request can continue, FALSE otherwise.

--*/

{
    //NTSTATUS Status = STATUS_SUCCESS; /* ReactOS Change: GCC Unused variable */
    PVCB Vcb = Fcb->Vcb;
    PDEVICE_OBJECT RealDevice = Vcb->Vpb->RealDevice;
    PIRP Irp;

    PAGED_CODE();

    //
    //  Check that the fileobject has not been cleaned up.
    //

    if ( ARGUMENT_PRESENT( IrpContext ))  {

        PFILE_OBJECT FileObject;

        Irp = IrpContext->Irp;
        FileObject = IoGetCurrentIrpStackLocation( Irp)->FileObject;

        if ( FileObject && FlagOn( FileObject->Flags, FO_CLEANUP_COMPLETE))  {

            PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );

            //
            //  Following FAT,  we allow certain operations even on cleaned up
            //  file objects.  Everything else,  we fail.
            //

            if ( (FlagOn(Irp->Flags, IRP_PAGING_IO)) ||
                    (IrpSp->MajorFunction == IRP_MJ_CLOSE ) ||
                    (IrpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION) ||
                    ( (IrpSp->MajorFunction == IRP_MJ_READ) &&
                      FlagOn(IrpSp->MinorFunction, IRP_MN_COMPLETE) ) ) {

                NOTHING;

            } else {

                CdRaiseStatus( IrpContext, STATUS_FILE_CLOSED );
            }
        }
    }

    //
    //  Fail immediately if the volume is in the progress of being dismounted
    //  or has been marked invalid.
    //

    if ((Vcb->VcbCondition == VcbInvalid) ||
            (Vcb->VcbCondition == VcbDismountInProgress)) {

        if (ARGUMENT_PRESENT( IrpContext )) {

            CdRaiseStatus( IrpContext, STATUS_FILE_INVALID );
        }

        return FALSE;
    }

    //
    //  Always fail if the volume needs to be verified.
    //

    if (CdRealDevNeedsVerify( RealDevice)) {

        if (ARGUMENT_PRESENT( IrpContext )) {

            IoSetHardErrorOrVerifyDevice( IrpContext->Irp,
                                          RealDevice );

            CdRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED );
        }

        return FALSE;

        //
        //
        //  All operations are allowed on mounted.
        //

    } else if ((Vcb->VcbCondition == VcbMounted) ||
               (Vcb->VcbCondition == VcbMountInProgress)) {

        return TRUE;

        //
        //  Fail all requests for fast Io on other Vcb conditions.
        //

    } else if (!ARGUMENT_PRESENT( IrpContext )) {

        return FALSE;

        //
        //  The remaining case is VcbNotMounted.
        //  Mark the device to be verified and raise WRONG_VOLUME.
        //

    } else if (Vcb->VcbCondition == VcbNotMounted) {

        if (ARGUMENT_PRESENT( IrpContext )) {

            IoSetHardErrorOrVerifyDevice( IrpContext->Irp, RealDevice );
            CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME );
        }

        return FALSE;
    }

    return TRUE;
}
Beispiel #3
0
BOOLEAN
CdVerifyFcbOperation (
    IN PIRP_CONTEXT IrpContext OPTIONAL,
    IN PFCB Fcb
    )

/*++

Routine Description:

    This routine is called to verify that the state of the Fcb is valid
    to allow the current operation to continue.  We use the state of the
    Vcb, target device and type of operation to determine this.

Arguments:

    IrpContext - IrpContext for the request.  If not present then we
        were called from the fast IO path.

    Fcb - Fcb to perform the request on.

Return Value:

    BOOLEAN - TRUE if the request can continue, FALSE otherwise.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PVCB Vcb = Fcb->Vcb;
    PDEVICE_OBJECT RealDevice = Vcb->Vpb->RealDevice;

    PAGED_CODE();

    //
    //  Fail immediately if the volume is in the progress of being dismounted
    //  or has been marked invalid.
    //

    if ((Vcb->VcbCondition == VcbInvalid) ||
        (Vcb->VcbCondition == VcbDismountInProgress)) {

        if (ARGUMENT_PRESENT( IrpContext )) {

            CdRaiseStatus( IrpContext, STATUS_FILE_INVALID );
        }

        return FALSE;
    }

    //
    //  Always fail if the volume needs to be verified.
    //

    if (FlagOn( RealDevice->Flags, DO_VERIFY_VOLUME )) {

        if (ARGUMENT_PRESENT( IrpContext )) {

            IoSetHardErrorOrVerifyDevice( IrpContext->Irp,
                                          RealDevice );

            CdRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED );
        }

        return FALSE;

    //
    //
    //  All operations are allowed on mounted.
    //

    } else if ((Vcb->VcbCondition == VcbMounted) ||
               (Vcb->VcbCondition == VcbMountInProgress)) {

        return TRUE;

    //
    //  Fail all requests for fast Io on other Vcb conditions.
    //

    } else if (!ARGUMENT_PRESENT( IrpContext )) {

        return FALSE;

    //
    //  The remaining case is VcbNotMounted.
    //  Mark the device to be verified and raise WRONG_VOLUME.
    //

    } else if (Vcb->VcbCondition == VcbNotMounted) {

        if (ARGUMENT_PRESENT( IrpContext )) {

            SetFlag(RealDevice->Flags, DO_VERIFY_VOLUME);

            IoSetHardErrorOrVerifyDevice( IrpContext->Irp, RealDevice );
            CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME );
        }

        return FALSE;
    }

    return TRUE;
}
Beispiel #4
0
BOOLEAN
CdAcquireResource (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PERESOURCE Resource,
    _In_ BOOLEAN IgnoreWait,
    _In_ TYPE_OF_ACQUIRE Type
    )

/*++

Routine Description:

    This is the single routine used to acquire file system resources.  It
    looks at the IgnoreWait flag to determine whether to try to acquire the
    resource without waiting.  Returning TRUE/FALSE to indicate success or
    failure.  Otherwise it is driven by the WAIT flag in the IrpContext and
    will raise CANT_WAIT on a failure.

Arguments:

    Resource - This is the resource to try and acquire.

    IgnoreWait - If TRUE then this routine will not wait to acquire the
        resource and will return a boolean indicating whether the resource was
        acquired.  Otherwise we use the flag in the IrpContext and raise
        if the resource is not acquired.

    Type - Indicates how we should try to get the resource.

Return Value:

    BOOLEAN - TRUE if the resource is acquired.  FALSE if not acquired and
        IgnoreWait is specified.  Otherwise we raise CANT_WAIT.

--*/

{
    BOOLEAN Wait = FALSE;
    BOOLEAN Acquired;
    PAGED_CODE();

    //
    //  We look first at the IgnoreWait flag, next at the flag in the Irp
    //  Context to decide how to acquire this resource.
    //

    if (!IgnoreWait && FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {

        Wait = TRUE;
    }

    //
    //  Attempt to acquire the resource either shared or exclusively.
    //

    switch (Type) {
        case AcquireExclusive:

#pragma prefast( suppress:28137, "prefast believes Wait should be a constant, but this is ok for CDFS" )
            Acquired = ExAcquireResourceExclusiveLite( Resource, Wait );
            break;

        case AcquireShared:

#pragma prefast( suppress:28137, "prefast believes Wait should be a constant, but this is ok for CDFS" )
            Acquired = ExAcquireResourceSharedLite( Resource, Wait );
            break;

        case AcquireSharedStarveExclusive:

#pragma prefast( suppress:28137, "prefast believes Wait should be a constant, but this is ok for CDFS" )
            Acquired = ExAcquireSharedStarveExclusive( Resource, Wait );
            break;

        default:
            Acquired = FALSE;
            NT_ASSERT( FALSE );
    }

    //
    //  If not acquired and the user didn't specifiy IgnoreWait then
    //  raise CANT_WAIT.
    //

    if (!Acquired && !IgnoreWait) {

        CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
    }

    return Acquired;
}
Beispiel #5
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 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 );
                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;
}
Beispiel #6
0
VOID
CdVerifyVcb (
    IN PIRP_CONTEXT IrpContext,
    IN PVCB Vcb
    )

/*++

Routine Description:

    This routine checks that the current Vcb is valid and currently mounted
    on the device.  It will raise on an error condition.

    We check whether the volume needs verification and the current state
    of the Vcb.

Arguments:

    Vcb - This is the volume to verify.

Return Value:

    None

--*/

{
    NTSTATUS Status;
    IO_STATUS_BLOCK Iosb;
    ULONG MediaChangeCount = 0;

    PAGED_CODE();

    //
    //  Fail immediately if the volume is in the progress of being dismounted
    //  or has been marked invalid.
    //

    if ((Vcb->VcbCondition == VcbInvalid) ||
        (Vcb->VcbCondition == VcbDismountInProgress)) {

        CdRaiseStatus( IrpContext, STATUS_FILE_INVALID );
    }

    //
    //  If the media is removable and the verify volume flag in the
    //  device object is not set then we want to ping the device
    //  to see if it needs to be verified
    //

    if ((Vcb->VcbCondition != VcbMountInProgress) &&
        FlagOn( Vcb->VcbState, VCB_STATE_REMOVABLE_MEDIA ) &&
        !FlagOn( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME )) {

        Status = CdPerformDevIoCtrl( IrpContext,
                                     IOCTL_CDROM_CHECK_VERIFY,
                                     Vcb->TargetDeviceObject,
                                     &MediaChangeCount,
                                     sizeof(ULONG),
                                     FALSE,
                                     FALSE,
                                     &Iosb );

        if (Iosb.Information != sizeof(ULONG)) {
    
            //
            //  Be safe about the count in case the driver didn't fill it in
            //
    
            MediaChangeCount = 0;
        }

        //
        //  If the volume is now an empty device, or we have receieved a
        //  bare STATUS_VERIFY_REQUIRED (various hardware conditions such
        //  as bus resets, etc., will trigger this in the drivers), or the
        //  media change count has moved since we last inspected the device,
        //  then mark the volume to be verified.
        //      

        if ((Vcb->VcbCondition == VcbMounted &&
             CdIsRawDevice( IrpContext, Status )) ||
            (Status == STATUS_VERIFY_REQUIRED) ||
            (NT_SUCCESS(Status) &&
             (Vcb->MediaChangeCount != MediaChangeCount))) {

            SetFlag( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME );

            //
            //  If the volume is not mounted and we got a media change count,
            //  update the Vcb so we do not trigger a verify again at this
            //  count value.  If the verify->mount path detects that the media
            //  has actually changed and this Vcb is valid again, this will have
            //  done nothing.  We are already synchronized since the caller has
            //  the Vcb.
            //

            if ((Vcb->VcbCondition == VcbNotMounted) &&
                NT_SUCCESS(Status)) {

                Vcb->MediaChangeCount = MediaChangeCount;
            }

        //
        //  Raise the error condition otherwise.
        //

        } else if (!NT_SUCCESS( Status )) {

            CdNormalizeAndRaiseStatus( IrpContext, Status );
        }

    }

    //
    //  The Vcb may be mounted but the underlying real device may need to be verified.
    //  If it does then we'll set the Iosb in the irp to be our real device
    //  and raise Verify required
    //

    if (FlagOn( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME )) {

        IoSetHardErrorOrVerifyDevice( IrpContext->Irp,
                                      Vcb->Vpb->RealDevice );

        CdRaiseStatus( IrpContext, STATUS_VERIFY_REQUIRED );
    }

    //
    //  Based on the condition of the Vcb we'll either return to our
    //  caller or raise an error condition
    //

    switch (Vcb->VcbCondition) {

    case VcbNotMounted:

        SetFlag( Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME );

        IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice );

        CdRaiseStatus( IrpContext, STATUS_WRONG_VOLUME );
        break;

    case VcbInvalid:
    case VcbDismountInProgress :

        CdRaiseStatus( IrpContext, STATUS_FILE_INVALID );
        break;
    }

    return;
}
Beispiel #7
0
NTSTATUS
CdCommonWrite (
    _Inout_ PIRP_CONTEXT IrpContext,
    _Inout_ PIRP Irp
    )

/*++

Routine Description:

    This is the common entry point for NtWriteFile calls.  For synchronous requests,
    CommonWrite will complete the request in the current thread.  If not
    synchronous the request will be passed to the Fsp if there is a need to
    block.

Arguments:

    Irp - Supplies the Irp to process

Return Value:

    NTSTATUS - The result of this operation.

--*/

{
    NTSTATUS Status = STATUS_SUCCESS;
    PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );

    TYPE_OF_OPEN TypeOfOpen;
    PFCB Fcb;
    PCCB Ccb;

    BOOLEAN Wait;
    ULONG SynchronousIo;
    PVOID UserBuffer;

    LONGLONG StartingOffset;
    LONGLONG ByteRange;
    ULONG ByteCount;
    ULONG WriteByteCount;
    ULONG OriginalByteCount;

    BOOLEAN ReleaseFile = TRUE;

    CD_IO_CONTEXT LocalIoContext;

    PAGED_CODE();

    //
    //  If this is a zero length write then return SUCCESS immediately.
    //

    if (IrpSp->Parameters.Write.Length == 0) {

        CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
        return STATUS_SUCCESS;
    }

    //
    //  Decode the file object and verify we support write on this.  It
    //  must be a volume file.
    //

    TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb );

    // Internal lock object is acquired if return status is STATUS_PENDING
    _Analysis_suppress_lock_checking_(Fcb->Resource);

    if (TypeOfOpen != UserVolumeOpen) {

        CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
        return STATUS_INVALID_DEVICE_REQUEST;
    }

    //
    //  Examine our input parameters to determine if this is noncached and/or
    //  a paging io operation.
    //

    Wait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
    SynchronousIo = FlagOn( IrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO );


    //
    //  Extract the range of the Io.
    //

    StartingOffset = IrpSp->Parameters.Write.ByteOffset.QuadPart;
    OriginalByteCount = ByteCount = IrpSp->Parameters.Write.Length;

    ByteRange = StartingOffset + ByteCount;

    //
    //  Acquire the file shared to perform the write.
    //

    CdAcquireFileShared( IrpContext, Fcb );

    //
    //  Use a try-finally to facilitate cleanup.
    //

    try {

        //
        //  Verify the Fcb.  Allow writes if this is a DASD handle that is 
        //  dismounting the volume.
        //

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

            CdVerifyFcbOperation( IrpContext, Fcb );
        }

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

            //
            //  Complete the request if it begins beyond the end of file.
            //

            if (StartingOffset >= Fcb->FileSize.QuadPart) {

                try_return( Status = STATUS_END_OF_FILE );
            }

            //
            //  Truncate the write if it extends beyond the end of the file.
            //

            if (ByteRange > Fcb->FileSize.QuadPart) {

                ByteCount = (ULONG) (Fcb->FileSize.QuadPart - StartingOffset);
                ByteRange = Fcb->FileSize.QuadPart;
            }
        }

        //
        //  If we have an unaligned transfer then post this request if
        //  we can't wait.  Unaligned means that the starting offset
        //  is not on a sector boundary or the write is not integral
        //  sectors.
        //

        WriteByteCount = BlockAlign( Fcb->Vcb, ByteCount );

        if (SectorOffset( StartingOffset ) ||
            SectorOffset( WriteByteCount ) ||
            (WriteByteCount > OriginalByteCount)) {

            if (!Wait) {

                CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
            }

            //
            //  Make sure we don't overwrite the buffer.
            //

            WriteByteCount = ByteCount;
        }

        //
        //  Initialize the IoContext for the write.
        //  If there is a context pointer, we need to make sure it was
        //  allocated and not a stale stack pointer.
        //

        if (IrpContext->IoContext == NULL ||
            !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO )) {

            //
            //  If we can wait, use the context on the stack.  Otherwise
            //  we need to allocate one.
            //

            if (Wait) {

                IrpContext->IoContext = &LocalIoContext;
                ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO );

            } else {

                IrpContext->IoContext = CdAllocateIoContext();
                SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO );
            }
        }

        RtlZeroMemory( IrpContext->IoContext, sizeof( CD_IO_CONTEXT ) );

        //
        //  Store whether we allocated this context structure in the structure
        //  itself.
        //

        IrpContext->IoContext->AllocatedContext =
            BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO );

        if (Wait) {

            KeInitializeEvent( &IrpContext->IoContext->SyncEvent,
                               NotificationEvent,
                               FALSE );

        } else {

            IrpContext->IoContext->ResourceThreadId = ExGetCurrentResourceThread();
            IrpContext->IoContext->Resource = Fcb->Resource;
            IrpContext->IoContext->RequestedByteCount = ByteCount;
        }

        Irp->IoStatus.Information = WriteByteCount;

        //
        //  Set the FO_MODIFIED flag here to trigger a verify when this
        //  handle is closed.  Note that we can err on the conservative
        //  side with no problem, i.e. if we accidently do an extra
        //  verify there is no problem.
        //

        SetFlag( IrpSp->FileObject->Flags, FO_FILE_MODIFIED );

        //
        //  Dasd access is always non-cached. Call the Dasd write routine to
        //  perform the actual write.
        //

        Status = CdVolumeDasdWrite( IrpContext, Fcb, StartingOffset, WriteByteCount );

        //
        //  Don't complete this request now if STATUS_PENDING was returned.
        //

        if (Status == STATUS_PENDING) {

            Irp = NULL;
            ReleaseFile = FALSE;

        //
        //  Test is we should zero part of the buffer or update the
        //  synchronous file position.
        //

        } else {

            //
            //  Convert any unknown error code to IO_ERROR.
            //

            if (!NT_SUCCESS( Status )) {

                //
                //  Set the information field to zero.
                //

                Irp->IoStatus.Information = 0;

                //
                //  Raise if this is a user induced error.
                //

                if (IoIsErrorUserInduced( Status )) {

                    CdRaiseStatus( IrpContext, Status );
                }

                Status = FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR );

            //
            //  Check if there is any portion of the user's buffer to zero.
            //

            } else if (WriteByteCount != ByteCount) {

                CdMapUserBuffer( IrpContext, &UserBuffer );
                
                SafeZeroMemory( IrpContext,
                                Add2Ptr( UserBuffer,
                                         ByteCount,
                                         PVOID ),
                                WriteByteCount - ByteCount );

                Irp->IoStatus.Information = ByteCount;
            }

            //
            //  Update the file position if this is a synchronous request.
            //

            if (SynchronousIo && NT_SUCCESS( Status )) {

                IrpSp->FileObject->CurrentByteOffset.QuadPart = ByteRange;
            }
        }

    try_exit:  NOTHING;
    } finally {

        //
        //  Release the Fcb.
        //

        if (ReleaseFile) {

            CdReleaseFile( IrpContext, Fcb );
        }
    }

    //
    //  Post the request if we got CANT_WAIT.
    //

    if (Status == STATUS_CANT_WAIT) {

        Status = CdFsdPostRequest( IrpContext, Irp );

    //
    //  Otherwise complete the request.
    //

    } else {

        CdCompleteRequest( IrpContext, Irp, Status );
    }

    return Status;
}
VOID
CdFindPrefix (
    _In_ PIRP_CONTEXT IrpContext,
    _Inout_ PFCB *CurrentFcb,
    _Inout_ PUNICODE_STRING RemainingName,
    _In_ BOOLEAN IgnoreCase
    )

/*++

Routine Description:

    This routine begins from the given CurrentFcb and walks through all of
    components of the name looking for the longest match in the prefix
    splay trees.  The search is relative to the starting Fcb so the
    full name may not begin with a '\'.  On return this routine will
    update Current Fcb with the lowest point it has travelled in the
    tree.  It will also hold only that resource on return and it must
    hold that resource.

Arguments:

    CurrentFcb - Address to store the lowest Fcb we find on this search.
        On return we will have acquired this Fcb.  On entry this is the
        Fcb to examine.

    RemainingName - Supplies a buffer to store the exact case of the name being
        searched for.  Initially will contain the upcase name based on the
        IgnoreCase flag.

    IgnoreCase - Indicates if we are doing a case-insensitive compare.

Return Value:

    None

--*/

{
    UNICODE_STRING LocalRemainingName;

    UNICODE_STRING FinalName;

    PNAME_LINK NameLink;
    PPREFIX_ENTRY PrefixEntry;

    PAGED_CODE();

    //
    //  Make a local copy of the input strings.
    //

    LocalRemainingName = *RemainingName;

    //
    //  Loop until we find the longest matching prefix.
    //

    while (TRUE) {

        //
        //  If there are no characters left or we are not at an IndexFcb then
        //  return immediately.
        //

        if ((LocalRemainingName.Length == 0) ||
            (SafeNodeType( *CurrentFcb ) != CDFS_NTC_FCB_INDEX)) {

            return;
        }

        //
        //  Split off the next component from the name.
        //

        CdDissectName( IrpContext,
                       &LocalRemainingName,
                       &FinalName );

        //
        //  Check if this name is in the splay tree for this Scb.
        //

        if (IgnoreCase) {

            NameLink = CdFindNameLink( IrpContext,
                                       &(*CurrentFcb)->IgnoreCaseRoot,
                                       &FinalName );

            //
            //  Get the prefix entry from this NameLink.  Don't access any
            //  fields within it until we verify we have a name link.
            //

            PrefixEntry = (PPREFIX_ENTRY) CONTAINING_RECORD( NameLink,
                                                             PREFIX_ENTRY,
                                                             IgnoreCaseName );

        } else {

            NameLink = CdFindNameLink( IrpContext,
                                       &(*CurrentFcb)->ExactCaseRoot,
                                       &FinalName );

            PrefixEntry = (PPREFIX_ENTRY) CONTAINING_RECORD( NameLink,
                                                             PREFIX_ENTRY,
                                                             ExactCaseName );
        }

        //
        //  If we didn't find a match then exit.
        //

        if (NameLink == NULL) { return; }

        //
        //  If this is a case-insensitive match then copy the exact case of the name into
        //  the input buffer.
        //

        if (IgnoreCase) {

            RtlCopyMemory( FinalName.Buffer,
                           PrefixEntry->ExactCaseName.FileName.Buffer,
                           PrefixEntry->ExactCaseName.FileName.Length );
        }

        //
        //  Update the caller's remaining name string to reflect the fact that we found
        //  a match.
        //

        *RemainingName = LocalRemainingName;

        //
        //  Move down to the next component in the tree.  Acquire without waiting.
        //  If this fails then lock the Fcb to reference this Fcb and then drop
        //  the parent and acquire the child.
        //

        if (!CdAcquireFcbExclusive( IrpContext, PrefixEntry->Fcb, TRUE )) {

            //
            //  If we can't wait then raise CANT_WAIT.
            //

            if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT )) {

                CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
            }

            CdLockVcb( IrpContext, IrpContext->Vcb );
            PrefixEntry->Fcb->FcbReference += 1;
            CdUnlockVcb( IrpContext, IrpContext->Vcb );

            CdReleaseFcb( IrpContext, *CurrentFcb );
            CdAcquireFcbExclusive( IrpContext, PrefixEntry->Fcb, FALSE );

            CdLockVcb( IrpContext, IrpContext->Vcb );
            PrefixEntry->Fcb->FcbReference -= 1;
            CdUnlockVcb( IrpContext, IrpContext->Vcb );

        } else {

            CdReleaseFcb( IrpContext, *CurrentFcb );
        }

        *CurrentFcb = PrefixEntry->Fcb;
    }
}
Beispiel #9
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;
}
Beispiel #10
0
__drv_mustHoldCriticalRegion
VOID
CdLookupAllocation (
    __in PIRP_CONTEXT IrpContext,
    __in PFCB Fcb,
    __in LONGLONG FileOffset,
    __out PLONGLONG DiskOffset,
    __out PULONG ByteCount
    )

/*++

Routine Description:

    This routine looks through the mapping information for the file
    to find the logical diskoffset and number of bytes at that offset.
    We only deal with logical 2048 byte sectors here.

    If the mapping isn't present we will look it up on disk now.
    This routine assumes we are looking up a valid range in the file.  This
    routine raises if it can't find mapping for the file offset.

    The Fcb may not be locked prior to calling this routine.  We will always
    acquire it here.

Arguments:

    Fcb - Fcb representing this stream.

    FileOffset - Lookup the allocation beginning at this point.

    DiskOffset - Address to store the logical disk offset.

    ByteCount - Address to store the number of contiguous bytes beginning
        at DiskOffset above.

Return Value:

    None.

--*/

{
    BOOLEAN FirstPass = TRUE;
    ULONG McbEntryOffset;
    PFCB ParentFcb = NULL;
    BOOLEAN CleanupParent = FALSE;

    BOOLEAN UnlockFcb = FALSE;

    LONGLONG CurrentFileOffset;
    ULONG CurrentMcbOffset;
    PCD_MCB_ENTRY CurrentMcbEntry;

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

    PAGED_CODE();

    ASSERT_IRP_CONTEXT( IrpContext );
    ASSERT_FCB( Fcb );

    //
    //  For DASD IO we already have clamped the read to the volume limits.
    //  We'll allow reading beyond those limits for extended DASD IO, so
    //  no MCB lookup here.
    //

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

        *DiskOffset = FileOffset;
        return;
    }

    //
    //  Use a try finally to facilitate cleanup.
    //

    try {

        //
        //  We use a loop to perform the lookup.  If we don't find the mapping in the
        //  first pass then we look up all of the allocation and then look again.

        while (TRUE) {

            //
            //
            //  Lookup the entry containing this file offset.
            //

            CdLockFcb( IrpContext, Fcb );
            UnlockFcb = TRUE;

            McbEntryOffset = CdFindMcbEntry( IrpContext, Fcb, FileOffset );

            //
            //  If within the Mcb then we use the data out of this entry and are
            //  done.
            //

            if (McbEntryOffset < Fcb->Mcb.CurrentEntryCount) {

                CdDiskOffsetFromMcbEntry( IrpContext,
                                          Fcb->Mcb.McbArray + McbEntryOffset,
                                          FileOffset,
                                          DiskOffset,
                                          ByteCount );

                break;

            //
            //  If this is not the first pass then the disk is corrupt.
            //

            } else if (!FirstPass) {

                CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
            }

            CdUnlockFcb( IrpContext, Fcb );
            UnlockFcb = FALSE;

            //
            //  Initialize the search dirent structures.
            //

            CdInitializeDirContext( IrpContext, &DirContext );
            CdInitializeDirent( IrpContext, &Dirent );

            //
            //  Otherwise we need to walk the dirents for this file until we find
            //  the one containing this entry.  The parent Fcb should always be
            //  present.
            //

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

            //
            //  Do an unsafe test to see if we need to create a file object.
            //

            CdVerifyOrCreateDirStreamFile( IrpContext, ParentFcb);

            //
            //  Initialize the local variables to indicate the first dirent
            //  and lookup the first dirent.
            //

            CurrentFileOffset = 0;
            CurrentMcbOffset = 0;

            CdLookupDirent( IrpContext,
                            ParentFcb,
                            CdQueryFidDirentOffset( Fcb->FileId ),
                            &DirContext );

            //
            //  If we are adding allocation to the Mcb then add all of it.
            //

            while (TRUE ) {

                //
                //  Update the dirent from the on-disk dirent.
                //

                CdUpdateDirentFromRawDirent( IrpContext, ParentFcb, &DirContext, &Dirent );

                //
                //  Add this dirent to the Mcb if not already present.
                //

                CdLockFcb( IrpContext, Fcb );
                UnlockFcb = TRUE;

                if (CurrentMcbOffset >= Fcb->Mcb.CurrentEntryCount) {

                    CdAddAllocationFromDirent( IrpContext, Fcb, CurrentMcbOffset, CurrentFileOffset, &Dirent );
                }

                CdUnlockFcb( IrpContext, Fcb );
                UnlockFcb = FALSE;

                //
                //  If this is the last dirent for the file then exit.
                //

                if (!FlagOn( Dirent.DirentFlags, CD_ATTRIBUTE_MULTI )) {

                    break;
                }

                //
                //  If we couldn't find another entry then the directory is corrupt because
                //  the last dirent for a file doesn't exist.
                //

                if (!CdLookupNextDirent( IrpContext, ParentFcb, &DirContext, &DirContext )) {

                    CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
                }

                //
                //  Update our loop variables.
                //

                CurrentMcbEntry = Fcb->Mcb.McbArray + CurrentMcbOffset;
                CurrentFileOffset += CurrentMcbEntry->ByteCount;
                CurrentMcbOffset += 1;
            }

            //
            //  All of the allocation is loaded.  Go back and look up the mapping again.
            //  It better be there this time.
            //

            FirstPass = FALSE;
        }

    } finally {

        if (CleanupParent) {

            //
            //  Release the parent and cleanup the dirent structures.
            //

            CdReleaseFile( IrpContext, ParentFcb );

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

        if (UnlockFcb) { CdUnlockFcb( IrpContext, Fcb ); }
    }

    return;
}
Beispiel #11
0
VOID
CdConvertBigToLittleEndian (
    IN PIRP_CONTEXT IrpContext,
    IN PCHAR BigEndian,
    IN ULONG ByteCount,
    OUT PCHAR LittleEndian
    )

/*++

Routine Description:

    This routine is called to convert a unicode string in big endian to
    little endian.  We start by copying all of the source bytes except
    the first.  This will put the low order bytes in the correct position.
    We then copy each high order byte in its correct position.

Arguments:

    BigEndian - Pointer to the string of big endian characters.

    ByteCount - Number of unicode characters in this string.

    LittleEndian - Pointer to array to store the little endian characters.

Return Value:

    None.

--*/

{
    ULONG RemainingByteCount = ByteCount;

    PCHAR Source = BigEndian;
    PCHAR Destination = LittleEndian;

    PAGED_CODE();

    //
    //  If the byte count isn't an even number then the disk is corrupt.
    //

    if (FlagOn( ByteCount, 1 )) {

        CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
    }

    //
    //  Start by copy the low-order bytes into the correct position.  Do
    //  this by skipping the first byte in the BigEndian string.
    //

    RtlCopyMemory( Destination,
                   Source + 1,
                   RemainingByteCount - 1 );

    //
    //  Now move the high-order bytes into position.
    //

    Destination += 1;

    while (RemainingByteCount != 0) {

        *Destination = *Source;

        Source += 2;
        Destination += 2;

        RemainingByteCount -= 2;
    }

    return;
}