// FrcOwn.cpp : Implementation of CForceOwnership

#include "pch.cxx"
#pragma hdrstop

#define TRKDATA_ALLOCATE
#include <trklib.hxx>
#include <trksvr.hxx>
#undef TRKDATA_ALLOCATE

#include "stdafx.h"
#include "ITrkAdmn.h"
#include "FrcOwn.h"




STDMETHODIMP
CTrkForceOwnership::Volumes(BSTR bstrUncPath, long scope )
{

    HRESULT hr = S_OK;
    HANDLE hFile = NULL;
    CVolumeId volid;

    CMachineId mcid( (LPWSTR) bstrUncPath );
    CRpcClientBinding rc;

    CPCVolumes cpcVolumes( &mcid, &_voltab, &_refreshSequenceStorage );
    TRpcPipeControl< TRK_VOLUME_TRACKING_INFORMATION_PIPE,
                     TRK_VOLUME_TRACKING_INFORMATION,
                     CPCVolumes
                   > cpipeVolumes( &cpcVolumes );

    rc.Initialize( mcid );

    if( TRKINFOSCOPE_VOLUME == scope )
    {
        NTSTATUS status;
        IO_STATUS_BLOCK Iosb;
        FILE_FS_OBJECTID_INFORMATION fsobOID;

        status = TrkCreateFile( bstrUncPath, SYNCHRONIZE | FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
                                FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
                                FILE_OPEN_NO_RECALL, NULL, &hFile );
        if( !NT_SUCCESS(status) )
        {
            hFile = NULL;
            TrkLog((TRKDBG_ERROR, TEXT("Couldn't open volume %s"), bstrUncPath ));
            TrkRaiseNtStatus( status );
        }

        status = NtQueryVolumeInformationFile( hFile, &Iosb, &fsobOID, sizeof(fsobOID),
                                               FileFsObjectIdInformation );

        if( STATUS_OBJECT_NAME_NOT_FOUND == status )
        {
            TRK_VOLUME_TRACKING_INFORMATION volinfo;

            volinfo.volindex = -1;
            cpcVolumes.Push( &volinfo, 1 );
            hr = S_OK;
            goto Exit;
        }
        else if( !NT_SUCCESS(status) )
        {
            TrkLog((TRKDBG_ERROR, TEXT("Failed NtQueryVolumeInformationFile (%s)"), bstrUncPath ));
            TrkRaiseNtStatus( status );
        }

        volid.Load( &volid, fsobOID );

        NtClose( hFile );
        hFile = NULL;
    }
    else if( TRKINFOSCOPE_MACHINE != scope )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Bad scope to CTrkForceOwnership::VolumeStatus (%l)"), scope ));
        TrkRaiseWin32Error( ERROR_INVALID_PARAMETER );
    }


    RpcTryExcept
    {
        hr = GetVolumeTrackingInformation( rc, volid, static_cast<TrkInfoScope>(scope), cpipeVolumes );
        if( SUCCEEDED(hr) && SUCCEEDED(cpcVolumes.GetHResult()) )
            hr = TriggerVolumeClaims( rc, cpcVolumes.Count(), cpcVolumes.GetVolIds() );
    }
    RpcExcept( BreakOnDebuggableException() )
    {
        hr = RpcExceptionCode();
    }
    RpcEndExcept;

Exit:

    if( NULL != hFile )
        NtClose( hFile );

    return( hr );
}



STDMETHODIMP
CTrkForceOwnership::Files(BSTR bstrUncPath, long scope)
{
    HRESULT hr = E_FAIL;
    HANDLE hFile = NULL;

    CPCFiles cpcFiles( &_idt );
    TRpcPipeControl< TRK_FILE_TRACKING_INFORMATION_PIPE,
                     TRK_FILE_TRACKING_INFORMATION,
                     CPCFiles
                   > cpipeFiles( &cpcFiles);

    CMachineId mcid( (LPWSTR) bstrUncPath );
    CRpcClientBinding rc;

    __try
    {
        NTSTATUS status;
        CDomainRelativeObjId droidCurrent, droidBirth;

        if( TRKINFOSCOPE_ONE_FILE == scope )
        {

            // BUGBUG P2:  Optimize this; we don't need droidBirth
            status = GetDroids( bstrUncPath, &droidCurrent, &droidBirth, RGO_READ_OBJECTID );

            if( STATUS_OBJECT_NAME_NOT_FOUND == status )
            {
                TRK_FILE_TRACKING_INFORMATION fileinfo;

                _tcscpy( fileinfo.tszFilePath, TEXT("") );
                fileinfo.hr = HRESULT_FROM_NT( status );

                cpcFiles.Push( &fileinfo, 1 );
                hr = S_OK;
                goto Exit;
            }
            else if( !NT_SUCCESS(status) )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed GetDroids (%s)"), bstrUncPath ));
                TrkRaiseNtStatus( status );
            }
        }
        else if( TRKINFOSCOPE_VOLUME == scope )
        {
            NTSTATUS status;
            IO_STATUS_BLOCK Iosb;
            FILE_FS_OBJECTID_INFORMATION fsobOID;
            CVolumeId volid;

            status = TrkCreateFile( bstrUncPath, SYNCHRONIZE | FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
                                    FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
                                    FILE_OPEN_NO_RECALL, NULL, &hFile );
            if( !NT_SUCCESS(status) )
            {
                hFile = NULL;
                TrkLog((TRKDBG_ERROR, TEXT("Couldn't open volume %s"), bstrUncPath ));
                TrkRaiseNtStatus( status );
            }

            status = NtQueryVolumeInformationFile( hFile, &Iosb, &fsobOID, sizeof(fsobOID),
                                                   FileFsObjectIdInformation );

            if( STATUS_OBJECT_NAME_NOT_FOUND == status )
            {
                TRK_FILE_TRACKING_INFORMATION fileinfo;

                _tcscpy( fileinfo.tszFilePath, TEXT("") );
                fileinfo.hr = HRESULT_FROM_NT( status );

                cpcFiles.Push( &fileinfo, 1 );
                hr = S_OK;
                goto Exit;
            }
            else if( !NT_SUCCESS(status) )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed NtQueryVolumeInformationFile (%s)"), bstrUncPath ));
                TrkRaiseNtStatus( status );
            }
            volid.Load( &volid, fsobOID );
            droidCurrent = CDomainRelativeObjId( volid, CObjId() );

            NtClose( hFile );
            hFile = NULL;
        }
        else if( TRKINFOSCOPE_MACHINE != scope )
        {
            TrkLog((TRKDBG_ERROR, TEXT("Bad scope to CTrkForceOwnership::FileStatus (%d)"), scope ));
            TrkRaiseWin32Error( ERROR_INVALID_PARAMETER );
        }
    
        rc.Initialize( mcid );

        RpcTryExcept
        {
            hr = GetFileTrackingInformation( rc, droidCurrent, static_cast<TrkInfoScope>(scope), cpipeFiles );
        }
        RpcExcept( BreakOnDebuggableException() )
        {
            hr = HRESULT_FROM_WIN32( RpcExceptionCode() );
        }
        RpcEndExcept;
        if( FAILED(hr) ) goto Exit;
    }
    __except( BreakOnDebuggableException() )
    {
        hr = GetExceptionCode();
    }


Exit:

    if( NULL != hFile )
        NtClose( hFile );

    return( hr );
}

STDMETHODIMP
CTrkForceOwnership::VolumeStatus(BSTR bstrUncPath, long scope,
                                 VARIANT *pvarlongVolIndex, VARIANT *pvarbstrVolId, VARIANT *pvarlongStatus)
{
    HRESULT hr = E_FAIL;
    HANDLE hFile = NULL;
    SAFEARRAYBOUND sabound;
    CVolumeId volid;

    // Determine the machine ID
    CMachineId mcid( (LPWSTR) bstrUncPath );
    CRpcClientBinding rc;

    // This is used by the RPC server to pull the bstrUncPath
    CPCPath cpcPath( bstrUncPath );
    TRpcPipeControl< TCHAR_PIPE, TCHAR, CPCPath> cpipePath( &cpcPath );

    // This is used by the RPC server to push the volume information
    CPCVolumeStatus cpcVolumeStatus( &_voltab );
    TRpcPipeControl< TRK_VOLUME_TRACKING_INFORMATION_PIPE, TRK_VOLUME_TRACKING_INFORMATION, CPCVolumeStatus
                   > cpipeVolumeStatus( &cpcVolumeStatus );


    __try
    {
        NTSTATUS status;

        // Initialize the outputs
        VariantInit( pvarlongVolIndex );
        VariantInit( pvarbstrVolId );
        VariantInit( pvarlongStatus );

        // Initialize the pipe callback
        cpcVolumeStatus.Initialize( &mcid, pvarlongVolIndex, pvarbstrVolId, pvarlongStatus );

        // Connect to the workstation in question
        rc.Initialize( mcid );

        if( TRKINFOSCOPE_VOLUME == scope )
        {
            IO_STATUS_BLOCK Iosb;
            FILE_FS_OBJECTID_INFORMATION fsobOID;

            status = TrkCreateFile( bstrUncPath, SYNCHRONIZE | FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
                                    FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
                                    FILE_OPEN_NO_RECALL, NULL, &hFile );
            if( !NT_SUCCESS(status) )
            {
                hFile = NULL;
                TrkLog((TRKDBG_ERROR, TEXT("Couldn't open volume %s"), bstrUncPath ));
                TrkRaiseNtStatus( status );
            }

            status = NtQueryVolumeInformationFile( hFile, &Iosb, &fsobOID, sizeof(fsobOID),
                                                   FileFsObjectIdInformation );

            if( STATUS_OBJECT_NAME_NOT_FOUND == status )
            {
                TRK_VOLUME_TRACKING_INFORMATION volinfo;

                volinfo.volindex = -1;
                cpcVolumeStatus.Push( &volinfo, 1 );
                hr = S_OK;
                goto Exit;
            }
            else if( !NT_SUCCESS(status) )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed NtQueryVolumeInformationFile (%s)"), bstrUncPath ));
                TrkRaiseNtStatus( status );
            }
            volid.Load( &volid, fsobOID );

            NtClose( hFile );
            hFile = NULL;
        }
        else if( TRKINFOSCOPE_MACHINE != scope )
        {
            TrkLog((TRKDBG_ERROR, TEXT("Bad scope to CTrkForceOwnership::VolumeStatus (%l)"), scope ));
            TrkRaiseWin32Error( ERROR_INVALID_PARAMETER );
        }


        RpcTryExcept
        {
            hr = GetVolumeTrackingInformation( rc, volid, static_cast<TrkInfoScope>(scope), cpipeVolumeStatus );
        }
        RpcExcept( BreakOnDebuggableException() )
        {
            hr = HRESULT_FROM_WIN32( RpcExceptionCode() );
        }
        RpcEndExcept;

        if( FAILED(hr) )
        {
            TrkLog((TRKDBG_ERROR, TEXT("Failed call to GetVolumeTrackInformation")));
            TrkRaiseException( hr );
        }

        if( FAILED(cpcVolumeStatus.GetHResult()) )
        {
            TrkLog((TRKDBG_ERROR, TEXT("Failed in VolumeStatus pipe callback")));
            TrkRaiseException( cpcVolumeStatus.GetHResult() );
        }

        // Truncate the safearrays
        cpcVolumeStatus.Compact();

    }
    __except( BreakOnDebuggableException() )
    {
        cpcVolumeStatus.UnInitialize();
        hr = GetExceptionCode();
    }

Exit:

    if( FAILED(hr) )
    {
        VariantClear( pvarlongVolIndex );
        VariantClear( pvarbstrVolId );
        VariantClear( pvarlongStatus );
    }

    if( NULL != hFile )
        NtClose( hFile );

    return( hr );
}


void
CPCVolumeStatus::Initialize( CMachineId *pmcid, VARIANT *pvarlongVolIndex, VARIANT *pvarbstrVolId, VARIANT *pvarlongStatus )
{
    _pmcid = pmcid;
    _pvarlongVolIndex = pvarlongVolIndex;
    _pvarbstrVolId = pvarbstrVolId;
    _pvarlongStatus = pvarlongStatus;

    _iArrays = 0;
    _hr = S_OK;
    _sabound.lLbound = 0;
    _sabound.cElements = NUM_VOLUMES;

    _fInitialized = TRUE;

    _pvarlongVolIndex->parray = SafeArrayCreate( VT_I4, 1, &_sabound );
    if( NULL == _pvarlongVolIndex->parray )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayCreate")));
        TrkRaiseWin32Error( E_OUTOFMEMORY );
    }
    _pvarlongVolIndex->vt = VT_I4 | VT_ARRAY;

    _pvarbstrVolId->parray = SafeArrayCreate( VT_BSTR, 1, &_sabound );
    if( NULL == _pvarbstrVolId->parray )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayCreate")));
        TrkRaiseWin32Error( E_OUTOFMEMORY );
    }
    _pvarbstrVolId->vt = VT_BSTR | VT_ARRAY;

    _pvarlongStatus->parray = SafeArrayCreate( VT_I4, 1, &_sabound );
    if( NULL == _pvarlongStatus->parray )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayCreate")));
        TrkRaiseWin32Error( E_OUTOFMEMORY );
    }
    _pvarlongStatus->vt = VT_I4 | VT_ARRAY;

}

void
CPCVolumeStatus::UnInitialize()
{
    // Nothing to do, the Variants are cleaned by the caller
    return;
}


void
CPCVolumeStatus::Push( TRK_VOLUME_TRACKING_INFORMATION *pVolInfo, unsigned long cElems )
{
    TrkLog(( TRKDBG_ADMIN, TEXT("CPCVolumeStatus received %d elements"), cElems ));

    HRESULT hr = S_OK;
    HRESULT hrGet;
    ULONG iElem = 0;
    BSTR bstrVolId = NULL;

    __try
    {
        for( iElem = 0; iElem < cElems; iElem++ )
        {
            CMachineId mcidCheck;
            CVolumeSecret volsecCheck;
            SequenceNumber seqCheck;
            CVolumeId volNULL;

            TCHAR tszVolId[ 40 ];
            TCHAR *ptszVolId = tszVolId;

            long VolOwnership = OBJOWN_UNKNOWN;

            if( pVolInfo[iElem].volume != volNULL )
                hrGet = _pvoltab->GetVolumeInfo( pVolInfo[iElem].volume, &mcidCheck, &volsecCheck, &seqCheck );

            if( _iArrays >= static_cast<LONG>(_sabound.cElements) )
            {
                TrkAssert( !TEXT("Not yet implemented") );
                // BUGBUG: do a SafeArrayReDim
                return;
            }


            TrkAssert( sizeof(long) == sizeof(pVolInfo[iElem].volindex) );
            hr = SafeArrayPutElement( _pvarlongVolIndex->parray, &_iArrays, &pVolInfo[iElem].volindex );
            if( FAILED(hr) )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayPutElement")));
                TrkRaiseException( hr );
            }

            // BUGBUG:  Add a Serialize(BSTR) method to CVolumeId
            pVolInfo[iElem].volume.Stringize( ptszVolId );

            bstrVolId = SysAllocString( tszVolId );
            if( NULL == bstrVolId )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed SysAllocString")));
                TrkRaiseWin32Error( E_OUTOFMEMORY );
            }

            hr = SafeArrayPutElement( _pvarbstrVolId->parray, &_iArrays, bstrVolId );
            if( FAILED(hr) )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayPutElement")));
                TrkRaiseException( hr );
            }

            if( S_OK != hrGet )
            {
                VolOwnership = OBJOWN_DOESNT_EXIST;
            }
            else if( mcidCheck == *_pmcid )
            {
                VolOwnership = OBJOWN_OWNED;
            }
            else if( volNULL == pVolInfo[iElem].volume )
            {
                VolOwnership = OBJOWN_NO_ID;
            }
            else
            {
                VolOwnership = OBJOWN_NOT_OWNED;
            }

            hr = SafeArrayPutElement( _pvarlongStatus->parray, &_iArrays, &VolOwnership );
            if( FAILED(hr) )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayPutElemnt")));
                TrkRaiseException( hr );
            }

            SysFreeString( bstrVolId ); bstrVolId = NULL;
            _iArrays++;
        }

    }
    __except( BreakOnDebuggableException() )
    {
        hr = GetExceptionCode();
    }


Exit:

    if( FAILED(hr) )
    {
        if( NULL != bstrVolId )
            SysFreeString( bstrVolId );

        if( !FAILED(_hr) )
            _hr = hr;
    }

    return;
}


void
CPCVolumeStatus::Compact()
{
    HRESULT hr = S_OK;

    _sabound.cElements = _iArrays;

    hr = SafeArrayRedim( _pvarlongVolIndex->parray, &_sabound );
    if( FAILED(hr) )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Couldn't redim safearray")));
        TrkRaiseException( hr );
    }

    hr = SafeArrayRedim( _pvarbstrVolId->parray, &_sabound );
    if( FAILED(hr) )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Couldn't redim safearray")));
        TrkRaiseException( hr );
    }

    hr = SafeArrayRedim( _pvarlongStatus->parray, &_sabound );
    if( FAILED(hr) )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Couldn't redim safearray")));
        TrkRaiseException( hr );
    }
}


void
CPCVolumes::Push( TRK_VOLUME_TRACKING_INFORMATION *pVolInfo, unsigned long cElems )
{
    TrkLog(( TRKDBG_ADMIN, TEXT("CPCVolumeStatus received %d elements"), cElems ));

    HRESULT hr = S_OK;
    ULONG iElem = 0;

    __try
    {
        for( iElem = 0; iElem < cElems; iElem++ )
        {

            hr = _pvoltab->SetMachine( pVolInfo[iElem].volume, *_pmcid );
            if( TRK_S_VOLUME_NOT_FOUND == hr )
                _pvoltab->CreateVolume(
                    pVolInfo[iElem].volume,
                    *_pmcid,
                    CVolumeSecret(),
                    _pRefreshSequenceStorage->GetSequenceNumber() );

            // BUGBUG P1:  Handle this error
            TrkAssert( SUCCEEDED(hr) );

            _rgvolid[ _cVolIds++ ] = pVolInfo[iElem].volume;
            
        }
    }
    __except( BreakOnDebuggableException() )
    {
        hr = GetExceptionCode();
    }


    if( FAILED(hr) )
    {
        if( !FAILED(_hr) )
            _hr = hr;
    }

    return;
}

void
CPCFiles::Push( TRK_FILE_TRACKING_INFORMATION *pFileInfo, unsigned long cElems )
{
    ULONG iFile;

    __try
    {
        // BUGBUG P2:  Instead of a loop, batch these up
        for( iFile = 0; iFile < cElems; iFile++ )
        {
            _pidt->Delete( pFileInfo[iFile].droidBirth );

            TrkVerify( _pidt->Add( pFileInfo[iFile].droidBirth, pFileInfo[iFile].droidLast,
                       pFileInfo[iFile].droidBirth ));
        }
    }
    __except( BreakOnDebuggableException() )
    {
        TrkLog(( TRKDBG_ERROR, TEXT("CPCFiles::Push had an exception (%08x)"), GetExceptionCode() ));
    }

    
}


void
CPCFileStatus::Push( TRK_FILE_TRACKING_INFORMATION *pFileInfo, unsigned long cElems )
{
    TrkLog(( TRKDBG_ADMIN, TEXT("CPCFileStatus received %d elements"), cElems ));

    HRESULT hr = S_OK;
    ULONG iElem = 0;
    BSTR bstr = NULL;

    __try
    {
        for( iElem = 0; iElem < cElems; iElem++ )
        {
            BOOL fExistsInTable = FALSE;
            BOOL fAtBirthplace = FALSE;

            CDomainRelativeObjId droidBirthCheck;
            CDomainRelativeObjId droidNowCheck;
            const CDomainRelativeObjId droidNULL;
            TCHAR tszDroid[ MAX_PATH ];
            long FileOwnership;

            if( droidNULL == pFileInfo[iElem].droidBirth
                ||
                pFileInfo[iElem].droidBirth == pFileInfo[iElem].droidLast )
            {
                fAtBirthplace = TRUE;
            }
            else
            {
                fExistsInTable = _pidt->Query( pFileInfo[iElem].droidBirth, &droidNowCheck, &droidBirthCheck );
            }

            if( _iArrays >= static_cast<LONG>(_sabound.cElements) )
            {
                TrkAssert( !TEXT("Not yet impelemented") );
                // BUGBUG: do a SafeArrayReDim
                return;
            }

            bstr = SysAllocString( pFileInfo[iElem].tszFilePath );
            if( NULL == bstr )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed SysAllocString")));
                TrkRaiseWin32Error( E_OUTOFMEMORY );
            }

            hr = SafeArrayPutElement( _pvarrgbstrFileName->parray, &_iArrays, bstr );
            if( FAILED(hr) )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayPutElement")));
                TrkRaiseException( hr );
            }
            SysFreeString( bstr ); bstr = NULL;

            // BUGBUG:  Add a Serialize(BSTR) method to CDroid
            pFileInfo[iElem].droidBirth.Stringize( tszDroid, sizeof(tszDroid) );
            bstr = SysAllocString( tszDroid );
            if( NULL == bstr )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed SysAllocString")));
                TrkRaiseWin32Error( E_OUTOFMEMORY );
            }

            hr = SafeArrayPutElement( _pvarrgbstrFileId->parray, &_iArrays, bstr );
            if( FAILED(hr) )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayPutElement")));
                TrkRaiseException( hr );
            }

            if( fAtBirthplace )
                FileOwnership = OBJOWN_OWNED;

            else if( !fExistsInTable )
                FileOwnership = OBJOWN_DOESNT_EXIST;

            else if( droidNowCheck == pFileInfo[iElem].droidLast
                     &&
                     droidBirthCheck == pFileInfo[iElem].droidBirth )
                FileOwnership = OBJOWN_OWNED;

            else
                FileOwnership = OBJOWN_NOT_OWNED;


            hr = SafeArrayPutElement( _pvarrglongStatus->parray, &_iArrays, &FileOwnership );
            if( FAILED(hr) )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayPutElemnt")));
                TrkRaiseException( hr );
            }

            _iArrays++;
        }

    }
    __except( BreakOnDebuggableException() )
    {
        hr = GetExceptionCode();
    }


Exit:

    if( FAILED(hr) )
    {
        if( NULL != bstr )
            SysFreeString( bstr );

        if( !FAILED(_hr) )
            _hr = hr;
    }

    return;
}



void
CPCFileStatus::Initialize( CMachineId *pmcid, VARIANT *pvarrgbstrFileName,
                           VARIANT *pvarrgbstrFileId, VARIANT *pvarrglongStatus )
{
    _pmcid = pmcid;
    _pvarrgbstrFileName = pvarrgbstrFileName;
    _pvarrgbstrFileId = pvarrgbstrFileId;
    _pvarrglongStatus = pvarrglongStatus;

    _iArrays = 0;
    _hr = S_OK;
    _sabound.lLbound = 0;
    _sabound.cElements = 10;

    _fInitialized = TRUE;

    _pvarrgbstrFileName->parray = SafeArrayCreate( VT_BSTR, 1, &_sabound );
    if( NULL == _pvarrgbstrFileName->parray )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayCreate")));
        TrkRaiseWin32Error( E_OUTOFMEMORY );
    }
    _pvarrgbstrFileName->vt = VT_BSTR | VT_ARRAY;

    _pvarrgbstrFileId->parray = SafeArrayCreate( VT_BSTR, 1, &_sabound );
    if( NULL == _pvarrgbstrFileId->parray )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayCreate")));
        TrkRaiseWin32Error( E_OUTOFMEMORY );
    }
    _pvarrgbstrFileId->vt = VT_BSTR | VT_ARRAY;

    _pvarrglongStatus->parray = SafeArrayCreate( VT_I4, 1, &_sabound );
    if( NULL == _pvarrglongStatus->parray )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Failed SafeArrayCreate")));
        TrkRaiseWin32Error( E_OUTOFMEMORY );
    }
    _pvarrglongStatus->vt = VT_I4 | VT_ARRAY;


}   // CPCFileStatus::Initialize



void
CPCFileStatus::Compact()
{
    HRESULT hr = S_OK;

    _sabound.cElements = _iArrays;

    hr = SafeArrayRedim( _pvarrgbstrFileName->parray, &_sabound );
    if( FAILED(hr) )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Couldn't redim safearray")));
        TrkRaiseException( hr );
    }

    hr = SafeArrayRedim( _pvarrgbstrFileId->parray, &_sabound );
    if( FAILED(hr) )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Couldn't redim safearray")));
        TrkRaiseException( hr );
    }

    hr = SafeArrayRedim( _pvarrglongStatus->parray, &_sabound );
    if( FAILED(hr) )
    {
        TrkLog((TRKDBG_ERROR, TEXT("Couldn't redim safearray")));
        TrkRaiseException( hr );
    }
}



STDMETHODIMP CTrkForceOwnership::FileStatus(BSTR bstrUncPath, long scope,
                                            VARIANT * pvarrgbstrFileName, VARIANT *pvarrgbstrFileId, VARIANT * pvarrglongStatus)
{
    HRESULT hr = E_FAIL;
    SAFEARRAYBOUND sabound;
    HANDLE hFile = NULL;

    // Determine the machine ID
    CMachineId mcid( (LPWSTR) bstrUncPath );
    CRpcClientBinding rc;

    // This is used by the RPC server to push the volume information
    CPCFileStatus cpcFileStatus( &_idt );
    TRpcPipeControl< TRK_FILE_TRACKING_INFORMATION_PIPE, TRK_FILE_TRACKING_INFORMATION, CPCFileStatus
                   > cpipeFileStatus( &cpcFileStatus );


    __try
    {
        NTSTATUS status;

        // Initialize the outputs
        VariantInit( pvarrgbstrFileName );
        VariantInit( pvarrgbstrFileId );
        VariantInit( pvarrglongStatus );

        CDomainRelativeObjId droidCurrent, droidBirth;

        // Initialize the pipe callback
        cpcFileStatus.Initialize( &mcid, pvarrgbstrFileName, pvarrgbstrFileId, pvarrglongStatus );

        if( TRKINFOSCOPE_ONE_FILE == scope )
        {

            // BUGBUG P2:  Optimize this; we don't need droidBirth
            status = GetDroids( bstrUncPath, &droidCurrent, &droidBirth, RGO_READ_OBJECTID );

            if( STATUS_OBJECT_NAME_NOT_FOUND == status )
            {
                TRK_FILE_TRACKING_INFORMATION fileinfo;

                _tcscpy( fileinfo.tszFilePath, TEXT("") );
                fileinfo.hr = HRESULT_FROM_NT( status );

                cpcFileStatus.Push( &fileinfo, 1 );
                hr = S_OK;
                goto Exit;
            }
            else if( !NT_SUCCESS(status) )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed GetDroids (%s)"), bstrUncPath ));
                TrkRaiseNtStatus( status );
            }
        }
        else if( TRKINFOSCOPE_VOLUME == scope )
        {
            NTSTATUS status;
            IO_STATUS_BLOCK Iosb;
            FILE_FS_OBJECTID_INFORMATION fsobOID;
            CVolumeId volid;

            status = TrkCreateFile( bstrUncPath, SYNCHRONIZE | FILE_READ_ATTRIBUTES, FILE_ATTRIBUTE_NORMAL,
                                    FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN,
                                    FILE_OPEN_NO_RECALL, NULL, &hFile );
            if( !NT_SUCCESS(status) )
            {
                hFile = NULL;
                TrkLog((TRKDBG_ERROR, TEXT("Couldn't open volume %s"), bstrUncPath ));
                TrkRaiseNtStatus( status );
            }

            status = NtQueryVolumeInformationFile( hFile, &Iosb, &fsobOID, sizeof(fsobOID),
                                                   FileFsObjectIdInformation );

            if( STATUS_OBJECT_NAME_NOT_FOUND == status )
            {
                TRK_FILE_TRACKING_INFORMATION fileinfo;

                _tcscpy( fileinfo.tszFilePath, TEXT("") );
                fileinfo.hr = HRESULT_FROM_NT( status );

                cpcFileStatus.Push( &fileinfo, 1 );
                hr = S_OK;
                goto Exit;
            }
            else if( !NT_SUCCESS(status) )
            {
                TrkLog((TRKDBG_ERROR, TEXT("Failed NtQueryVolumeInformationFile (%s)"), bstrUncPath));
                TrkRaiseNtStatus( status );
            }
            volid.Load( &volid, fsobOID );
            droidCurrent = CDomainRelativeObjId( volid, CObjId() );

            NtClose( hFile );
            hFile = NULL;
        }
        else if( TRKINFOSCOPE_MACHINE != scope )
        {
            TrkLog((TRKDBG_ERROR, TEXT("Bad scope to CTrkForceOwnership::FileStatus (%d)"), scope ));
            TrkRaiseWin32Error( ERROR_INVALID_PARAMETER );
        }

        // Connect to the workstation in question
        rc.Initialize( mcid );

        // Get the volume status info
        RpcTryExcept
        {
            hr = GetFileTrackingInformation( rc, droidCurrent, static_cast<TrkInfoScope>(scope), cpipeFileStatus );
        }
        RpcExcept( BreakOnDebuggableException() )
        {
            hr = HRESULT_FROM_WIN32( RpcExceptionCode() );
        }
        RpcEndExcept;

        if( FAILED(hr) )
        {
            TrkLog((TRKDBG_ERROR, TEXT("Failed call to GetFileTrackInformation")));
            TrkRaiseException( hr );
        }

        if( FAILED(cpcFileStatus.GetHResult()) )
        {
            TrkLog((TRKDBG_ERROR, TEXT("Failed in FileStatus pipe callback")));
            TrkRaiseException( cpcFileStatus.GetHResult() );
        }
        // Truncate the safearrays
        cpcFileStatus.Compact();

    }
    __except( BreakOnDebuggableException() )
    {
        cpcFileStatus.UnInitialize();
        hr = GetExceptionCode();
    }

Exit:

    if( NULL != hFile )
        NtClose( hFile );

    if( FAILED(hr) )
    {
        VariantClear( pvarrgbstrFileName );
        VariantClear( pvarrgbstrFileId );
        VariantClear( pvarrglongStatus );
    }

    return( hr );
}