//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//

#include "Udf.h"

extern "C"
{

HANDLE UDF_CreateFile( CVolume* pVolume,
                       HANDLE hProc,
                       LPCWSTR FileName,
                       DWORD Access,
                       DWORD ShareMode,
                       LPSECURITY_ATTRIBUTES pSecurityAttributes,
                       DWORD Create,
                       DWORD FlagsAndAttributes,
                       HANDLE hTemplateFile )
{
    HANDLE Handle = INVALID_HANDLE_VALUE;
    LRESULT Result = ERROR_SUCCESS;
    DWORD dwSize = 0;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_CreateFileW(%s)\r\n"), FileName));
    
    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit_createfile;
    }

    Result = pVolume->CreateFile( hProc, 
                                  FileName, 
                                  Access,
                                  ShareMode,
                                  pSecurityAttributes,
                                  Create,
                                  FlagsAndAttributes,
                                  &Handle );


exit_createfile:
    if (Result != ERROR_SUCCESS)
    {
        SetLastError (Result);
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_CreateFileW returned Handle: 0x%x, LRESULT 0x%x\r\n"), Handle, Result));
    return Handle;
}


BOOL UDF_CloseFile( PFILE_HANDLE pHandle )
{
    LRESULT Result = ERROR_SUCCESS;
    CVolume* pVolume = NULL;
    
    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_CloseFile(0x%x)\r\n"), pHandle));
    
    if (!pHandle)
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    pVolume = pHandle->pStream->GetVolume();
    if( pVolume )
    {
        //
        // TODO::When do we want to close the flush the file handle?
        //
        Result = pVolume->CloseFile( pHandle, FALSE );
    }

exit:
    if (Result != ERROR_SUCCESS)
    {
        SetLastError (Result);
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_CloseFile returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_ReadFile( PFILE_HANDLE pHandle,
                   LPVOID Buffer,
                   DWORD BytesToRead,
                   LPDWORD pNumBytesRead,
                   LPOVERLAPPED pOverlapped )
{
    DWORD NumBytesRead = 0;
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_ReadFile(Handle: 0x%x, 0x%x bytes, Position 0x%x)\r\n"), pHandle, BytesToRead, pHandle->Position));
    
    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }
    if( !(pHandle->dwAccess & GENERIC_READ) )
    {
        Result = ERROR_ACCESS_DENIED;
        goto exit;
    }

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    //
    // This lock shouldn't be needed but...
    // This isn't too big a deal as we will need to lock
    // internally once this is a writeable file system anyways.
    //
    pHandle->pStream->Lock();

    if( pHandle->pStream )
    {
        Result = pHandle->pStream->ReadFile( pHandle->Position,
                                             Buffer,
                                             BytesToRead,
                                             &NumBytesRead );
    }

    pHandle->Position += NumBytesRead;
    pHandle->pStream->Unlock();

    if( pNumBytesRead )
    {
        *pNumBytesRead = NumBytesRead;
    }

exit:
    if (Result != ERROR_SUCCESS)
    {
        SetLastError (Result);
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_ReadFile returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_ReadFileWithSeek( PFILE_HANDLE pHandle,
                           LPVOID Buffer,
                           DWORD BytesToRead,
                           LPDWORD pNumBytesRead,
                           LPOVERLAPPED pOverlapped,
                           DWORD LowOffset,
                           DWORD HighOffset )
{
    DWORD NumBytesRead = 0;
    LRESULT Result = ERROR_SUCCESS;
    
    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_ReadFileWithSeek (Handle: 0x%x, 0x%x bytes, Position 0x%x,0x%x)\r\n"), pHandle, BytesToRead, HighOffset, LowOffset));
    
    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }
    if( !(pHandle->dwAccess & GENERIC_READ) )
    {
        Result = ERROR_ACCESS_DENIED;
        goto exit;
    }

    if( Buffer == NULL && BytesToRead == 0 ) 
    {
        // The caller is probing to see if the file supports RFWS
        Result = ERROR_SUCCESS;
        goto exit;
    }
    
    ULARGE_INTEGER Offset;
    Offset.LowPart = LowOffset;
    Offset.HighPart = HighOffset;

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( pHandle->pStream )
    {
        Result = pHandle->pStream->ReadFile( Offset.QuadPart,
                                             Buffer,
                                             BytesToRead,
                                             &NumBytesRead );
    }
    
    if( pNumBytesRead )
    {
        *pNumBytesRead = NumBytesRead;
    }

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_ReadFileWithSeek returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}

BOOL UDF_ReadFileScatter( PFILE_HANDLE pHandle,
                          FILE_SEGMENT_ELEMENT SegmentArray[],
                          DWORD NumberOfBytesToRead,
                          LPDWORD pReserved,
                          LPOVERLAPPED pOverlapped )
{
    LRESULT Result = ERROR_SUCCESS;

    // Interpret lpReserved as an array of 64-bit offsets if provided.
    FILE_SEGMENT_ELEMENT* OffsetArray = (FILE_SEGMENT_ELEMENT*)pReserved;
    
    DEBUGMSG(ZONE_APIS, (TEXT("UDFS!UDF_ReadFileScatter(Handle: 0x%x, 0x%x bytes)\r\n"), pHandle, NumberOfBytesToRead));

    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }
    if( !(pHandle->dwAccess & GENERIC_READ) )
    {
        Result = ERROR_ACCESS_DENIED;
        goto exit;
    }
    
    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( pHandle->pStream )
    {
        Result = pHandle->pStream->ReadFileScatter( SegmentArray,
                                                    NumberOfBytesToRead,
                                                    OffsetArray,
                                                    pHandle->Position );
    }
    
    if( (Result == ERROR_SUCCESS) && !OffsetArray )
    {
        // Update file handle position
        //
        pHandle->Position += NumberOfBytesToRead;
    }

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_ReadFileScatter returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);

}

BOOL UDF_WriteFile( PFILE_HANDLE pHandle,
                    LPVOID Buffer,
                    DWORD BytesToWrite,
                    LPDWORD pNumBytesWritten,
                    LPOVERLAPPED pOverlapped )
{
    DWORD NumBytesWritten = 0;
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_WriteFile(Handle: 0x%x, 0x%x bytes, Position 0x%x)\r\n"), pHandle, BytesToWrite, pHandle->Position));
    
    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }
    if( !(pHandle->dwAccess & GENERIC_WRITE) )
    {
        Result = ERROR_ACCESS_DENIED;
        goto exit;
    }

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( pHandle->pStream )
    {
        Result = pHandle->pStream->WriteFile( pHandle->Position,
                                              Buffer,
                                              BytesToWrite,
                                              &NumBytesWritten );
    }
    
    pHandle->Position += NumBytesWritten;

    if( pNumBytesWritten )
    {
        *pNumBytesWritten = NumBytesWritten;
    }

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_WriteFile returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_WriteFileWithSeek( PFILE_HANDLE pHandle,
                            LPVOID Buffer,
                            DWORD BytesToWrite,
                            LPDWORD pNumBytesWritten,
                            LPOVERLAPPED pOverlapped,
                            DWORD LowOffset,
                            DWORD HighOffset )
{
    DWORD NumBytesWritten = 0;
    LRESULT Result = ERROR_SUCCESS;
    
    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_WriteFileWithSeek (Handle: 0x%x, 0x%x bytes, Position 0x%x,0x%x)\r\n"), pHandle, BytesToWrite, HighOffset, LowOffset));
    
    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }
    if( !(pHandle->dwAccess & GENERIC_WRITE) )
    {
        Result = ERROR_ACCESS_DENIED;
        goto exit;
    }
    
    ULARGE_INTEGER Offset;
    Offset.LowPart = LowOffset;
    Offset.HighPart = HighOffset;

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( pHandle->pStream )
    {
        Result = pHandle->pStream->WriteFile( Offset.QuadPart,
                                              Buffer,
                                              BytesToWrite,
                                              &NumBytesWritten );
    }
    
    if( pNumBytesWritten )
    {
        *pNumBytesWritten = NumBytesWritten;
    }

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_WriteFileWithSeek returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_WriteFileGather( PFILE_HANDLE pHandle,
                          FILE_SEGMENT_ELEMENT SegmentArray[],
                          DWORD NumberOfBytesToWrite,
                          LPDWORD pReserved,
                          LPOVERLAPPED pOverlapped )
{
    LRESULT Result = ERROR_SUCCESS;

    // Interpret lpReserved as an array of 64-bit offsets if provided.
    FILE_SEGMENT_ELEMENT* OffsetArray = (FILE_SEGMENT_ELEMENT*)pReserved;
    
    DEBUGMSG(ZONE_APIS, (TEXT("UDFS!UDF_WriteFileGather(Handle: 0x%x, 0x%x bytes)\r\n"), pHandle, NumberOfBytesToWrite));

    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }
    if( !(pHandle->dwAccess & GENERIC_WRITE) )
    {
        Result = ERROR_ACCESS_DENIED;
        goto exit;
    }
    
    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( pHandle->pStream )
    {
        Result = pHandle->pStream->WriteFileGather( SegmentArray,
                                                    NumberOfBytesToWrite,
                                                    OffsetArray,
                                                    pHandle->Position );
    }
    
    if( (Result == ERROR_SUCCESS) && !OffsetArray )
    {
        // Update file handle position
        //
        pHandle->Position += NumberOfBytesToWrite;
    }

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_WriteFileGather returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


DWORD UDF_SetFilePointer( PFILE_HANDLE pHandle,
                          LONG DistanceToMove,
                          PLONG pDistanceToMoveHigh,
                          DWORD MoveMethod )
{
    LARGE_INTEGER NewPosition;
    LRESULT Result = ERROR_SUCCESS;
    DWORD Return = (DWORD)-1;
    
    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_SetFilePointer(Handle: 0x%x, Offset 0x%x, Method %d)\r\n"), pHandle, DistanceToMove, MoveMethod));

    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    CStream* pStream = pHandle->pStream;
    if( !pStream )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( pDistanceToMoveHigh )
    {
        NewPosition.LowPart = DistanceToMove;
        NewPosition.HighPart = *pDistanceToMoveHigh;        
    }
    else
    {
        NewPosition.QuadPart = DistanceToMove;
    }

    switch( MoveMethod ) 
    {
        case FILE_BEGIN:
        break;

        case FILE_CURRENT:
        {
            NewPosition.QuadPart += pHandle->Position;
        }
        break;

        case FILE_END:
        {
            NewPosition.QuadPart += pStream->GetFileSize();
        }
        break;

        default:
        {
            Result = ERROR_INVALID_PARAMETER;
            goto exit;
        }
    }

    pHandle->Position = NewPosition.QuadPart;
    
    Return = NewPosition.LowPart;
    if (pDistanceToMoveHigh)
    {
        *pDistanceToMoveHigh = NewPosition.HighPart;
    }

  exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_SetFilePointer returned 0x%x (LRESULT: %d)\r\n"), Return, Result));

    return Return;
}


DWORD UDF_GetFileSize( PFILE_HANDLE pHandle, LPDWORD pFileSizeHigh )
{
    LRESULT Result = ERROR_SUCCESS;
    ULARGE_INTEGER Return;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_GetFileSize(Handle: 0x%x)\r\n"), pHandle));

    if( !pHandle || !pHandle->pStream )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Return.QuadPart = pHandle->pStream->GetFileSize();
    
    if( pFileSizeHigh ) 
    {
        *pFileSizeHigh = Return.HighPart;
    }


  exit:
    if( Result != ERROR_SUCCESS )
    {
        Return.LowPart = 0;
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_GetFileSize returned 0x%x (%d)\r\n"), Return, Result));

    return Return.LowPart;
}


BOOL UDF_GetFileInformationByHandle( PFILE_HANDLE pHandle,
                                     LPBY_HANDLE_FILE_INFORMATION pFileInfo )
{
    LRESULT Result = ERROR_SUCCESS;
    BY_HANDLE_FILE_INFORMATION FileInfo;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_GetFileInformationByHandle(Handle: 0x%x)\r\n"), pHandle));

    if( !pHandle || !pFileInfo )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    CStream* pStream = pHandle->pStream;
    if( !pStream )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    ZeroMemory( &FileInfo, sizeof(FileInfo) );

    Result = pStream->GetFileInformation( pHandle->pLink, &FileInfo );

    if( Result == ERROR_SUCCESS )
    {
        __try
        {
            CopyMemory( pFileInfo, &FileInfo, sizeof(FileInfo) );
        }
        __except( EXCEPTION_EXECUTE_HANDLER )
        {
            Result = ERROR_INVALID_PARAMETER;
        }
    }
    
exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_GetFileInformationByHandle returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_FlushFileBuffers( PFILE_HANDLE pHandle )
{
    DWORD NumBytesWritten = 0;
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_FlushFileBuffers(Handle: 0x%x)\r\n"), pHandle));
    
    if( !pHandle || !pHandle->pStream )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( !(pHandle->dwAccess & GENERIC_WRITE) )
    {
        Result = ERROR_ACCESS_DENIED;
        goto exit;
    }

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pHandle->pStream->FlushFileBuffers();
       
exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_FlushFileBuffers returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_GetFileTime( PFILE_HANDLE pHandle,
                      LPFILETIME pCreation,
                      LPFILETIME pLastAccess,
                      LPFILETIME pLastWrite )
{
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_GetFileTime(Handle: 0x%x)\r\n"), pHandle));

    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    CStream* pStream = pHandle->pStream;
    if( !pStream )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }
    
    Result = pStream->GetFileTime( pCreation,
                                 pLastAccess,
                                 pLastWrite );
    
exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_GetFileTime returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_SetFileTime( PFILE_HANDLE pHandle,
                      const FILETIME *pCreation,
                      const FILETIME *pLastAccess,
                      const FILETIME *pLastWrite )
{
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_SetFileTime(Handle: 0x%x)\r\n"), pHandle));

    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( !(pHandle->dwAccess & GENERIC_WRITE) )
    {
        Result = ERROR_ACCESS_DENIED;
        goto exit;
    }

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    CStream* pStream = pHandle->pStream;
    if (!pStream)
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pStream->SetFileTime( pCreation,
                                   pLastAccess,
                                   pLastWrite );
    
exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_SetFileTime returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_SetEndOfFile( PFILE_HANDLE pHandle )
{
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_SetEndOfFile(Handle: 0x%x)\r\n"), pHandle));

    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( !(pHandle->dwAccess & GENERIC_WRITE) )
    {
        Result = ERROR_ACCESS_DENIED;
        goto exit;
    }

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    CStream* pStream = pHandle->pStream;
    if (!pStream)
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pStream->SetEndOfFile( pHandle->Position );
    
exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_SetEndOfFile returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}

// /////////////////////////////////////////////////////////////////////////////
// UDF_DeviceIoControl
//
BOOL UDF_DeviceIoControl( PFILE_HANDLE pHandle,
                          DWORD IoControlCode,
                          LPVOID pInBuf, 
                          DWORD InBufSize,
                          LPVOID pOutBuf, 
                          DWORD OutBufSize,
                          LPDWORD pBytesReturned,
                          LPOVERLAPPED pOverlapped )
{
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_DeviceIoControl(0x%x,0x%x)\r\n"), pHandle, IoControlCode));

    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( !pHandle->pStream )
    {
        //
        // TODO::How do we get a handle without a stream attached to it?  If
        // this is the case, we can always use the link for something else
        // like a volume pointer.  However, this is never done right now and
        // we will need to add it.
        //
        
        if( !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
        {
            Result = ERROR_INVALID_PARAMETER;
            goto exit;
        }

        //
        // This is a volume handle
        //
        ASSERT(pHandle->pVolume);
        Result = pHandle->pVolume->DeviceIoControl( IoControlCode, 
                                                    pInBuf, 
                                                    InBufSize,
                                                    pOutBuf, 
                                                    OutBufSize,
                                                    pBytesReturned,
                                                    pOverlapped );
                   
    }
    else
    {
        if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
            !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
        {
            ASSERT(FALSE);
            Result = ERROR_INVALID_PARAMETER;
            goto exit;
        }

        //
        // We need to pass in the file handle for any IOCTLs that might
        // require a file offset.  Specifically, I'm referring to the DVD
        // IOCTLs.
        //
        Result = pHandle->pStream->DeviceIoControl( pHandle,
                                                    IoControlCode, 
                                                    pInBuf, 
                                                    InBufSize,
                                                    pOutBuf, 
                                                    OutBufSize,
                                                    pBytesReturned,
                                                    pOverlapped );
        if ( Result == ERROR_CALL_NOT_IMPLEMENTED )
        {
            //
            // Failed file IOCTL, try volume IOCTL instead.
            //
            Result = pHandle->pStream->GetVolume()->DeviceIoControl( IoControlCode,
                                                                     pInBuf,
                                                                     InBufSize,
                                                                     pOutBuf,
                                                                     OutBufSize,
                                                                     pBytesReturned,
                                                                     pOverlapped );
        }
    }

  exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }
    
    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_DeviceIoControl returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}

// /////////////////////////////////////////////////////////////////////////////
// UDF_FsIoControl
//
BOOL UDF_FsIoControl( CVolume* pVolume,
                      DWORD FsIoControlCode,
                      LPVOID pInBuf, 
                      DWORD InBufSize,
                      LPVOID pOutBuf, 
                      DWORD OutBufSize,
                      LPDWORD pBytesReturned,
                      LPOVERLAPPED pOverlapped )
{
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_FsIoControl(0x%p,0x%x)\r\n"), pVolume, FsIoControlCode));

    if( !pVolume )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pVolume->FsIoControl( FsIoControlCode, 
                                   pInBuf, 
                                   InBufSize,
                                   pOutBuf, 
                                   OutBufSize,
                                   pBytesReturned,
                                   pOverlapped );

  exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }
    
    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_FsIoControl returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}

BOOL UDF_LockFileEx( PFILE_HANDLE pHandle, 
                     DWORD Flags, 
                     DWORD Reserved, 
                     DWORD NumberOfBytesToLockLow, 
                     DWORD NumberOfBytesToLockHigh, 
                     LPOVERLAPPED pOverlapped )
{
    // TODO: Implement FAT_LockFileEx
    PREFAST_DEBUGCHK(pHandle);
    return FALSE;
    
#if 0
    return FSDMGR_InstallFileLock(AcquireFileLockState, 
                                  ReleaseFileLockState, 
                                  (DWORD)pHandle, 
                                  Flags, 
                                  Reserved, 
                                  NumberOfBytesToLockLow, 
                                  NumberOfBytesToLockHigh, 
                                  pOverlapped);
#endif
}

BOOL UDF_UnlockFileEx( PFILE_HANDLE pHandle, 
                       DWORD Reserved, 
                       DWORD NumberOfBytesToLockLow, 
                       DWORD NumberOfBytesToLockHigh, 
                       LPOVERLAPPED pOverlapped )
{
    // TODO: Implement FAT_UnlockFileEx
    PREFAST_DEBUGCHK(pHandle);
    return FALSE;

#if 0
    return FSDMGR_RemoveFileLock(AcquireFileLockState, 
                                 ReleaseFileLockState, 
                                 (DWORD)pHandle, 
                                 Reserved, 
                                 NumberOfBytesToLockLow, 
                                 NumberOfBytesToLockHigh, 
                                 pOverlapped);
#endif
}


BOOL UDF_GetVolumeInfo( CVolume* pVolume, FSD_VOLUME_INFO *pInfo )
{
    LRESULT Result = ERROR_SUCCESS;
    FSD_VOLUME_INFO TempInfo = { 0 };

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_GetVolumeInfo(Handle: 0x%x)\r\n"), pVolume));
    
    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pVolume->GetVolumeInfo( &TempInfo );

    if( Result == ERROR_SUCCESS )
    {
        if( !CeSafeCopyMemory( pInfo, &TempInfo, sizeof(TempInfo) ) )
        {
            Result = ERROR_INVALID_PARAMETER;
        }
    }

exit:  
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_GetVolumeInfo returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}

BOOL UDF_FindClose( PSEARCH_HANDLE pHandle )
{
    LRESULT Result = ERROR_SUCCESS;
    CVolume* pVolume = NULL;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_FindClose(Handle: 0x%08x)\r\n"), pHandle));

    if( !pHandle )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }   

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    pVolume = pHandle->pStream->GetVolume();
    if( pVolume )
    {
        pVolume->CloseFile( pHandle, FALSE );
    }

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_FindClose returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}

HANDLE UDF_FindFirstFile( CVolume* pVolume, 
                          HANDLE hProc, 
                          PCWSTR FileSpec, 
                          PWIN32_FIND_DATAW pFindData )
{
    LRESULT Result = ERROR_SUCCESS;
    HANDLE Handle = INVALID_HANDLE_VALUE;
    WIN32_FIND_DATAW FindData;
    WCHAR* strFileName = NULL;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_FindFirstFileW(Pattern: %s)\r\n"), FileSpec));

    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    if( !FileSpec || !pFindData )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    ZeroMemory( &FindData, sizeof(FindData) );

    __try
    {
        DWORD dwLength = wcslen( FileSpec ) + 1;
        
        strFileName = new (UDF_TAG_STRING) WCHAR[ dwLength ];
        if( !strFileName )
        {
            Result = ERROR_NOT_ENOUGH_MEMORY;
            goto exit;
        }

        ZeroMemory( strFileName, dwLength * sizeof(WCHAR) );

        StringCchPrintfW( strFileName, dwLength, L"%s", FileSpec );
    }
    __except( EXCEPTION_EXECUTE_HANDLER )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pVolume->FindFirstFile( hProc, 
                                     strFileName, 
                                     &FindData,
                                     &Handle );

    if( Result == ERROR_SUCCESS )
    {
        __try
        {
            CopyMemory( pFindData, &FindData, sizeof(FindData) );
        }
        __except( EXCEPTION_EXECUTE_HANDLER )
        {
            UDF_FindClose( (PSEARCH_HANDLE)&Handle );
            Result = ERROR_INVALID_PARAMETER;
        }
    }

exit:
    if( strFileName )
    {
        delete[] strFileName;
    }
    
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,
              (TEXT("UDFS!UDF_FindFirstFileW returned (Handle: 0x%x, File: %s, LRESULT: 0x%x)\r\n"),
              Handle,
              Result == ERROR_SUCCESS ? pFindData->cFileName : TEXT(""),
              Result));
    
    return Handle;
}


BOOL UDF_FindNextFile( PSEARCH_HANDLE pHandle, PWIN32_FIND_DATAW pFindData )
{
    LRESULT Result = ERROR_SUCCESS;
    CFileInfo FileInfo;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_FindNextFile(Handle: 0x%08x, Pattern: %s)\r\n"), pHandle, pHandle->pFileName->GetCurrentToken()));

    if( !pHandle || !pFindData )
    {
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }   

    if( !::IsKModeAddr( (DWORD)pHandle->pStream ) || 
        !::IsKModeAddr( (DWORD)pHandle->pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    __try
    {
        CDirectory* pDirectory = (CDirectory*)pHandle->pStream->GetFile();
        ASSERT (pDirectory);

        Result = pDirectory->FindNextFile( pHandle, &FileInfo );

        if( Result == ERROR_SUCCESS )
        {
            //
            // Update last position and search pattern in search handle
            //
            FileInfo.GetFindData( pFindData );
        }
    }
    __except( EXCEPTION_EXECUTE_HANDLER ) 
    {      
        Result = ERROR_INVALID_PARAMETER;
    }
    

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,
              (TEXT("UDFS!UDF_FindNextFile (Handle: 0x%x) returned (File: %s, LRESULT: 0x%x)\r\n"),
              pHandle,
              Result == ERROR_SUCCESS ? pFindData->cFileName : TEXT(""),
              Result));
    
    return (Result == ERROR_SUCCESS);
}



BOOL UDF_CreateDirectory( CVolume* pVolume, 
                          PCWSTR PathName, 
                          LPSECURITY_ATTRIBUTES pSecurityAttributes )
{
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_CreateDirectoryW(%s)\r\n"), PathName));
    
    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pVolume->CreateDirectory( PathName, pSecurityAttributes );

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_CreateDirectoryW returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_RemoveDirectory( CVolume* pVolume, PCWSTR PathName )
{
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_RemoveDirectoryW(%s)\r\n"), PathName));
    
    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pVolume->RemoveDirectory( PathName );

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_RemoveDirectoryW returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


DWORD UDF_GetFileAttributes( CVolume* pVolume, PCWSTR FileName )
{
    LRESULT Result = ERROR_SUCCESS;
    DWORD Attributes = INVALID_FILE_ATTRIBUTES;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_GetFileAttributesW(%s)\r\n"), FileName));
    
    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pVolume->GetFileAttributes( FileName, &Attributes );

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_GetFileAttributesW returned 0x%x (LRESULT 0x%x)\r\n"), Attributes, Result));
    return Attributes;
}


BOOL UDF_SetFileAttributes( CVolume* pVolume, 
                            PCWSTR FileName, 
                            DWORD Attributes )
{
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_SetFileAttributesW(File: %s, Attributes: %d)\r\n"), FileName, Attributes));
    
    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pVolume->SetFileAttributes( FileName, Attributes );

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_SetFileAttributesW returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_DeleteFile( CVolume* pVolume, PCWSTR FileName )
{
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_DeleteFileW(File: %s)\r\n"), FileName));
    
    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pVolume->DeleteFile( FileName );

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_DeleteFileW returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_MoveFile( CVolume* pVolume, 
                   PCWSTR OldFileName, 
                   PCWSTR NewFileName )
{
    LRESULT Result = ERROR_SUCCESS;

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_MoveFileW(OldFile: %s, NewFile: %s)\r\n"), OldFileName, NewFileName));
    
    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pVolume->MoveFile( OldFileName, NewFileName );

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_MoveFileW returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_RegisterFileSystemNotification( CVolume* pVolume, HWND hwnd )
{
    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_RegisterFileSystemNotification(0x%x)\r\n"), hwnd));

    return FALSE;
}


BOOL UDF_RegisterFileSystemFunction( CVolume* pVolume, SHELLFILECHANGEFUNC_t pfn )
{
    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_RegisterFileSystemFunction(0x%x)\r\n"), pfn));

#ifdef SHELL_CALLBACK_NOTIFICATION
    // pfnShell = pfn;
    return TRUE;
#else
    return FALSE;
#endif
}


BOOL UDF_DeleteAndRenameFile( CVolume* pVolume, 
                              PCWSTR OldFileName, 
                              PCWSTR NewFileName )
{
    LRESULT Result = ERROR_SUCCESS;
    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_PrestoChangoFileName(OldFile: %s, NewFile: %s)\r\n"), OldFileName, NewFileName));

    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pVolume->DeleteAndRenameFile( OldFileName, NewFileName );

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_PrestoChangoFileName returned LRESULT 0x%x\r\n"), Result));
    return (Result == ERROR_SUCCESS);
}


BOOL UDF_GetDiskFreeSpace( CVolume* pVolume,
                           PCWSTR PathName,
                           PDWORD pSectorsPerCluster,
                           PDWORD pBytesPerSector,
                           PDWORD pFreeClusters,
                           PDWORD pClusters )
{
    LRESULT Result = ERROR_SUCCESS;
    DEBUGMSG(ZONE_APIS,(TEXT("UDFS!UDF_GetDiskFreeSpace(%s)\r\n"), PathName ? PathName : TEXT("")));

    if( !::IsKModeAddr( (DWORD)pVolume ) )
    {
        ASSERT(FALSE);
        Result = ERROR_INVALID_PARAMETER;
        goto exit;
    }

    Result = pVolume->GetDiskFreeSpace( pSectorsPerCluster,
                                        pBytesPerSector,
                                        pFreeClusters,
                                        pClusters );

exit:
    if( Result != ERROR_SUCCESS )
    {
        SetLastError( Result );
    }

    DEBUGMSG(ZONE_APIS || ZONE_ERROR && Result,(TEXT("UDFS!UDF_GetDiskFreeSpace returned LRESULT 0x%x, 0x%x clusters free\r\n"), Result, pFreeClusters ? *pFreeClusters : 0));
    return (Result == ERROR_SUCCESS);
}

LRESULT UDF_FormatVolume( HDSK hDsk )
{
    LRESULT Result = ERROR_SUCCESS;

    // TODO::Format the volume.

    return ERROR_CALL_NOT_IMPLEMENTED;
//    return FSDMGR_FormatVolume( hDsk, (LPVOID)&FormatParams );   
}

void UDF_Notify( CVolume* pVolume, DWORD Flags )
{
    // TODO::What needs done here?
}

// /////////////////////////////////////////////////////////////////////////////
// UDF_RecognizeVolume
//
// This is exported as the detector function for UDF to identify this media as
// being UDF mountable or not.
//
BOOL UDF_RecognizeVolume( HANDLE hDisk, 
                          const GUID* pGuid, 
                          const BYTE* pBootSector, 
                          DWORD dwBootSectorSize )
{
    BOOL fResult = FALSE;
    DWORD dwSectorSize = CD_SECTOR_SIZE;
    DWORD dwBytesReturned = 0;
    DWORD dwBufferSize = 0;
    USHORT TrackIndx = 0;
    CD_PARTITION_INFO* pPartInfo = NULL;
    TRACK_INFORMATION2* pTrackInfo = NULL;

    //
    // These are used when actually reading the VRS from the disc.
    //
    const DWORD dwBuffSize = MAX_READ_SIZE;
    CDROM_READ CdRead = { 0 };
    BOOL fFoundBEA = FALSE;
    BOOL fFoundTEA = FALSE;
    BOOL fFoundNSR = FALSE;
    BYTE* pVRSBuffer = NULL;
    DWORD dwRead = 0;
    LRESULT lResult = ERROR_SUCCESS;

    //
    // We need to get the sector size, read in the vrs, and check for the 
    // appropriate descriptors here.
    //

    //
    // TODO::Do we need to check for other sector sizes here? I know we do if we
    // want to support hard drives.  DVDs have to be 2k sectors.
    //

    //
    // TODO::Add support for hard drives.
    //

    dwBufferSize = sizeof(CD_PARTITION_INFO) +
                   MAXIMUM_NUMBER_TRACKS_LARGE * sizeof(TRACK_INFORMATION2);
    
    pPartInfo = (PCD_PARTITION_INFO)new(UDF_TAG_BYTE_BUFFER) BYTE[ dwBufferSize ];
    if( !pPartInfo )
    {
        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
        return FALSE;
    }

    //
    // Find out where our session begins, track information, etc.
    //
    if( !FSDMGR_DiskIoControl( (DWORD)hDisk,
                               IOCTL_CDROM_GET_PARTITION_INFO,
                               NULL,
                               0,
                               pPartInfo,
                               dwBufferSize,
                               &dwBytesReturned,
                               NULL ) )
    {
        fResult = FALSE;
        goto exit_udf_recognizevolume;
    }

    //
    // CD Audio will be handled by the CDFS file system.
    //
    if( pPartInfo->fAudio )
    {
        fResult = FALSE;
        goto exit_udf_recognizevolume;
    }

    if( pPartInfo->NumberOfTracks == 0 )
    {
        //
        // Is this necessary here or is there already a check for it?
        //
        fResult = FALSE;
        goto exit_udf_recognizevolume;
    }
    
    //
    // If we are trying to mount this "partition" and it's not an audio 
    // partition then the last track better not be zero or we have issues.
    //
    ASSERT( pPartInfo->LastValidTrackInPartition != 0 );

    //
    // Now check for a valid UDF VRS starting at "the first sector starting 
    // after byte 32767" in the session.  We're reading in max of 16 sectors
    // here.
    //
    pVRSBuffer = new (UDF_TAG_BYTE_BUFFER) BYTE[dwBuffSize];
    if( !pVRSBuffer )
    {
        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
        fResult = FALSE;
        goto exit_udf_recognizevolume;
    }

    for( USHORT Track = pPartInfo->NumberOfTracks; !fResult && Track; Track-- )
    {
        DWORD dwFirstSector = 0;
        
        pTrackInfo = &pPartInfo->TrackInformation[Track - 1];
        
        dwFirstSector = (pTrackInfo->TrackStartAddress[0] << 24) +
                        (pTrackInfo->TrackStartAddress[1] << 16) +
                        (pTrackInfo->TrackStartAddress[2] << 8) +
                        (pTrackInfo->TrackStartAddress[3]);

        CdRead.StartAddr.Mode = CDROM_ADDR_LBA;

        CdRead.StartAddr.Address.lba = 32 * 1024 / dwSectorSize;
        CdRead.StartAddr.Address.lba += dwFirstSector;

        CdRead.sgcount = 1;
        CdRead.TransferLength = MAX_READ_SIZE / dwSectorSize;
        CdRead.sglist[0].sb_buf = pVRSBuffer;
        CdRead.sglist[0].sb_len = dwBuffSize;

        if( !FSDMGR_DiskIoControl( (DWORD)hDisk,
                                   IOCTL_CDROM_READ_SG,
                                   &CdRead, 
                                   sizeof(CdRead),
                                   NULL,
                                   0,
                                   &dwBytesReturned, 
                                   NULL ) )
        {
            goto exit_udf_recognizevolume;
        }

        //
        // Note that we're re-using "dwRead" here to tell us how many
        // sectors we've read through the buffer.  We'll go through the
        // buffer as long as we haven't found the required descriptors
        // for UDF, or until we've hit the end of the buffer.
        //
        // Note that 16K should be enough for any VRS, but there's not really
        // a requirement saying it will be less than 16K.  Or, if a device
        // comes out with large sector sizes, this might not be enough.
        //
        dwRead = 0;
        while( (!fFoundBEA || !fFoundTEA || !fFoundNSR) && 
               (dwRead < dwBuffSize / dwSectorSize) )
        {
            PVSD_GENERIC pVRSSector = (PVSD_GENERIC)(&pVRSBuffer[dwRead * dwSectorSize]);

            dwRead += 1;

            if( pVRSSector->Type == 0 )
            {
                if( strncmp( (char*)pVRSSector->Ident, "TEA01", 5 ) == 0 )
                {
                    fFoundTEA = TRUE;
                    continue;
                }

                if( strncmp( (char*)pVRSSector->Ident, "BEA01", 5 ) == 0 )
                {
                    fFoundBEA = TRUE;
                    continue;
                }

                if( strncmp( (char*)pVRSSector->Ident, "NSR02", 5 ) == 0 )
                {
                    fFoundNSR = TRUE;
                    continue;
                }

                if( strncmp( (char*)pVRSSector->Ident, "NSR03", 5 ) == 0 )
                {
                    fFoundNSR = TRUE;
                    continue;
                }
            }
        }

        fResult = fFoundBEA && fFoundTEA && fFoundNSR;
        if (fResult)
        {
            CVolume* pVolume;
    
            pVolume = new (UDF_TAG_VOLUME ) CVolume();
            if(pVolume == NULL) 
            {
                SetLastError( ERROR_NOT_ENOUGH_MEMORY );
                fResult = FALSE;
            }
            else
            {
                fResult = pVolume->ValidateUDFVolume((HDSK)hDisk);
                delete pVolume;
                pVolume = NULL;
            }
		}
        
    }

exit_udf_recognizevolume:
    if( pVRSBuffer )
    {
        delete [] pVRSBuffer;
    }

    if( pPartInfo )
    {
        delete [] pPartInfo;
    }

    return fResult;
}

}

