//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:	filstm.cxx
//
//  Contents:	CNtFileStream implementation
//
//  History:	28-Jun-93	DrewB	Created
//
//----------------------------------------------------------------------------

#include "headers.cxx"
#pragma hdrstop

#include "filstm.hxx"

#define ValidateLockType(lt) \
    ((lt) == LOCK_EXCLUSIVE || (lt) == LOCK_ONLYONCE)

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::QueryInterface, public
//
//  Synopsis:   Returns an object for the requested interface
//
//  Arguments:  [iid] - Interface ID
//              [ppvObj] - Object return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppvObj]
//
//  History:    28-Jun-93       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CNtFileStream::QueryInterface(REFIID iid, void **ppvObj)
{
    SCODE sc;

    ssDebugOut((DEB_TRACE, "In  CNtFileStream::QueryInterface:%p(riid, %p)\n",
                this, ppvObj));
    if (!IsValidIid(iid))
        ssErr(EH_Err, STG_E_INVALIDPARAMETER);
    ssChk(Validate());
    if (IsEqualIID(iid, IID_IStream) || IsEqualIID(iid, IID_IUnknown))
    {
        *ppvObj = (IStream *)this;
        CNtFileStream::AddRef();
    }
    else if(IsEqualIID(iid, IID_IOverlappedStream)) 
    {
        *ppvObj = (IOverlappedStream *) this;
        CNtFileStream::AddRef();
    }
    else
    {
        sc = E_NOINTERFACE;
        *ppvObj = NULL;
    }
    ssDebugOut((DEB_TRACE, "Out CNtFileStream::QueryInterface => %p\n",
                *ppvObj));
 EH_Err:
    return ssResult(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:	CNtFileStream::CNtFileStream, public
//
//  Synopsis:	Empty object constructor
//
//  History:	30-Jun-93	DrewB	Created
//
//----------------------------------------------------------------------------

CNtFileStream::CNtFileStream(void)
{
    ssDebugOut((DEB_ITRACE, "In  CNtFileStream::CNtFileStream:%p()\n", this));
    _sig = 0;
    ssDebugOut((DEB_ITRACE, "Out CNtFileStream::CNtFileStream\n"));
    ENLIST_TRACKING(CNtFileStream);
}

//+---------------------------------------------------------------------------
//
//  Member:	CNtFileStream::InitCommon, private
//
//  Synopsis:	Common initialization code
//
//  Arguments:	[co] - For detecting creation flags
//
//  Returns:	Appropriate status code
//
//  History:	28-Jul-93	DrewB	Created
//
//----------------------------------------------------------------------------

SCODE CNtFileStream::InitCommon(CREATEOPEN co)
{
    NTSTATUS nts;
    SCODE sc;
    IO_STATUS_BLOCK iosb;
    FILE_POSITION_INFORMATION fpi;
    FILE_END_OF_FILE_INFORMATION feofi;
    
    ssDebugOut((DEB_ITRACE, "In  CNtFileStream::InitCommon:%p(%d)\n",
                this, co));

    // If we're supposed to be creating, truncate
    // STGM_CREATE must have been passed since FAILIFTHERE always will fail
    if (co == CO_CREATE)
    {
        feofi.EndOfFile.LowPart = 0;
        feofi.EndOfFile.HighPart = 0;
        nts = NtSetInformationFile(_h, &iosb, &feofi,
                                   sizeof(FILE_END_OF_FILE_INFORMATION),
                                   FileEndOfFileInformation);
        if (!NT_SUCCESS(nts))
            ssErr(EH_Err, NtStatusToScode(nts));
    }

    // Make sure the file pointer is at zero
    fpi.CurrentByteOffset.LowPart = 0;
    fpi.CurrentByteOffset.HighPart = 0;
    nts = NtSetInformationFile(_h, &iosb, &fpi,
                               sizeof(FILE_POSITION_INFORMATION),
                               FilePositionInformation);
    if (!NT_SUCCESS(nts))
        sc = NtStatusToScode(nts);
    else
        sc = S_OK;
    
    ssDebugOut((DEB_ITRACE, "Out CNtFileStream::InitCommon\n"));
 EH_Err:
    return sc;
}

//+---------------------------------------------------------------------------
//
//  Member:	CNtFileStream::InitFromHandle, public
//
//  Synopsis:	Constructor
//
//  Arguments:	[h] - Handle to duplicate
//              [grfMode] - Mode of handle
//
//  Returns:	Appropriate status code
//
//  History:	08-Jul-93	DrewB	Created
//
//  Notes:      Takes a new reference on the given handle
//
//----------------------------------------------------------------------------

SCODE CNtFileStream::InitFromHandle(HANDLE h,
                                    DWORD grfMode,
                                    CREATEOPEN co,
                                    LPSTGSECURITY pssSecurity)
{
    SCODE sc;
    
    ssDebugOut((DEB_ITRACE, "In  CNtFileStream::InitFromHandle:%p("
                "%p, %lX, %d, %p)\n", this, h, grfMode, co, pssSecurity));
    
    ssChk(ValidateMode(grfMode));
    if (pssSecurity != NULL)
        ssErr(EH_Err, STG_E_INVALIDPARAMETER);
    ssChk(ReopenNtHandle(h, grfMode, FD_FILE, &_h));

    ssAssert(co != CO_CREATE || (grfMode & STGM_CREATE));
    ssChk(InitCommon(co));

    _grfMode = grfMode;
    _sig = CNTFILESTREAM_SIG;
    
    ssDebugOut((DEB_ITRACE, "Out CNtFileStream::InitFromHandle\n"));
 EH_Err:
    return sc;
}

//+---------------------------------------------------------------------------
//
//  Member: CNtFileStream::InitFromPath, public
//
//  Synopsis:   Constructor
//
//  Arguments:  [h] - Handle to duplicate
//              [grfMode] - Mode of handle
//
//  Returns:    Appropriate status code
//
//  History:    08-Jul-95   HenryLee Created
//
//  Notes:      Takes a new reference on the given handle
//
//----------------------------------------------------------------------------

SCODE CNtFileStream::InitFromPath(HANDLE hParent,
                                  const WCHAR *pwcsName,
                                  DWORD grfMode,
                                  DWORD grfAttr,
                                  CREATEOPEN co,
                                  LPSTGSECURITY pssSecurity)
{
    SCODE sc;

    ssDebugOut((DEB_ITRACE, "In  CNtFileStream::InitFromPath:%p("
                "%p, %lX, %ws, %d, %p)\n", this, hParent, pwcsName, 
                grfMode, co, pssSecurity));

    ssChk(ValidateMode(grfMode));
    if (pssSecurity != NULL)
        ssErr(EH_Err, STG_E_INVALIDPARAMETER);
    ssChk(GetNtHandle(hParent, pwcsName, grfMode, grfAttr, co, FD_FILE,
          (LPSECURITY_ATTRIBUTES) pssSecurity,&_h));

    //ssAssert(co != CO_CREATE || (grfMode & STGM_CREATE));
    ssChk(InitCommon(co));

    _grfMode = grfMode;
    _sig = CNTFILESTREAM_SIG;

    ssDebugOut((DEB_ITRACE, "Out CNtFileStream::InitFromPath\n"));
 EH_Err:
    return sc;
}

//+---------------------------------------------------------------------------
//
//  Member:	CNtFileStream::InitClone, public
//
//  Synopsis:	Constructor
//
//  Arguments:	[h] - Handle to duplicate
//              [grfMode] - Mode of handle
//
//  Returns:	Appropriate status code
//
//  History:	08-Jul-93	DrewB	Created
//
//  Notes:      Takes a new reference on the given handle
//
//----------------------------------------------------------------------------

SCODE CNtFileStream::InitClone(HANDLE h, DWORD grfMode)
{
    SCODE sc;
    
    ssDebugOut((DEB_ITRACE, "In  CNtFileStream::InitClone:%p(%p, %lX)\n",
                this, h, grfMode));
    
    ssChk(ValidateMode(grfMode));
    ssChk(DupNtHandle(h, &_h));

    _grfMode = grfMode;
    _sig = CNTFILESTREAM_SIG;
    
    ssDebugOut((DEB_ITRACE, "Out CNtFileStream::InitClone\n"));
 EH_Err:
    return sc;
}

//+---------------------------------------------------------------------------
//
//  Member:	CNtFileStream::~CNtFileStream, public
//
//  Synopsis:	Destructor
//
//  History:	28-Jun-93	DrewB	Created
//
//----------------------------------------------------------------------------

CNtFileStream::~CNtFileStream(void)
{
    ssDebugOut((DEB_ITRACE, "In  CNtFileStream::~CNtFileStream:%p()\n", this));
    _sig = CNTFILESTREAM_SIGDEL;
    ssDebugOut((DEB_ITRACE, "Out CNtFileStream::~CNtFileStream\n"));
}

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::Read, public
//
//  Synopsis:   Read from a stream
//
//  Arguments:  [pb] - Buffer
//              [cb] - Count of bytes to read
//              [pcbRead] - Return number of bytes read
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pcbRead]
//
//  History:    28-Jun-93       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CNtFileStream::Read(VOID *pb, ULONG cb, ULONG *pcbRead)
{
    SCODE sc;
    NTSTATUS nts;
    IO_STATUS_BLOCK iosb;

    ssDebugOut((DEB_TRACE, "In  CNtFileStream::Read:%p(%p, %lu, %p)\n",
                this, pb, cb, pcbRead));

    sc = S_OK;
    ssChk(Validate());

    ssAssert(_h != NULL);
    nts = NtReadFile(_h, NULL, NULL, NULL, &iosb, pb, cb, NULL, NULL);
    if (!NT_SUCCESS(nts))
    {
        if (nts != STATUS_END_OF_FILE)
            sc = NtStatusToScode(nts);
        else
            iosb.Information = 0;
    }
    if (pcbRead)
    {
        if (SUCCEEDED(sc))
            *pcbRead = iosb.Information;
        else
            *pcbRead = 0;
    }

    ssDebugOut((DEB_TRACE, "Out CNtFileStream::Read => %lu\n",
                iosb.Information));
EH_Err:
    return ssResult(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::Write, public
//
//  Synopsis:   Write to a stream
//
//  Arguments:  [pb] - Buffer
//              [cb] - Count of bytes to write
//              [pcbWritten] - Return of bytes written
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pcbWritten]
//
//  History:    28-Jun-93       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CNtFileStream::Write(VOID const *pb,
                                ULONG cb,
                                ULONG *pcbWritten)
{
    SCODE sc;
    NTSTATUS nts;
    IO_STATUS_BLOCK iosb;

    ssDebugOut((DEB_TRACE, "In  CNtFileStream::Write:%p(%p, %lu, %p)\n",
                this, pb, cb, pcbWritten));

    sc = S_OK;
    ssChk(Validate());
        
    ssAssert(_h != NULL);
    nts = NtWriteFile(_h, NULL, NULL, NULL, &iosb, (void *)pb, cb,
                      NULL, NULL);
    if (!NT_SUCCESS(nts))
        sc = NtStatusToScode(nts);
    if (pcbWritten)
    {
        if (SUCCEEDED(sc))
            *pcbWritten = iosb.Information;
        else
            *pcbWritten = 0;
    }

    ssDebugOut((DEB_TRACE, "Out CNtFileStream::Write => %lu\n",
                iosb.Information));
EH_Err:
    return ssResult(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::Seek, public
//
//  Synopsis:   Seek to a point in a stream
//
//  Arguments:  [dlibMove] - Offset to move by
//              [dwOrigin] - SEEK_SET, SEEK_CUR, SEEK_END
//              [plibNewPosition] - Return of new offset
//
//  Returns:    Appropriate status code
//
//  Modifies:   [plibNewPosition]
//
//  History:    28-Jun-93       DrewB   Created
//
//
//----------------------------------------------------------------------------

STDMETHODIMP CNtFileStream::Seek(LARGE_INTEGER dlibMove,
                                 DWORD dwOrigin,
                                 ULARGE_INTEGER *plibNewPosition)
{
    SCODE sc;
    NTSTATUS nts;
    IO_STATUS_BLOCK iosb;
    FILE_POSITION_INFORMATION fpi;
    FILE_STANDARD_INFORMATION fsi;

    ssDebugOut((DEB_TRACE, "In  CNtFileStream::Seek:%p(%ld:%ld, %lu, %p)\n",
                this, dlibMove.HighPart, dlibMove.LowPart, dwOrigin,
                plibNewPosition));

    if (dwOrigin != STREAM_SEEK_SET && dwOrigin != STREAM_SEEK_CUR &&
        dwOrigin != STREAM_SEEK_END)
        ssErr(EH_Err, STG_E_INVALIDFUNCTION);

    ssAssert(_h != NULL);
    switch(dwOrigin)
    {
    case STREAM_SEEK_SET:
        fpi.CurrentByteOffset.LowPart = dlibMove.LowPart;
        fpi.CurrentByteOffset.HighPart = dlibMove.HighPart;
        break;
            
    case STREAM_SEEK_CUR:
        nts = NtQueryInformationFile(_h, &iosb, &fpi,
                                     sizeof(FILE_POSITION_INFORMATION),
                                     FilePositionInformation);
        if (!NT_SUCCESS(nts))
            ssErr(EH_Err, NtStatusToScode(nts));

        // Check for seek before beginning and overflow
        if (dlibMove.HighPart < 0)
        {
            if ((-dlibMove.QuadPart) > fpi.CurrentByteOffset.QuadPart)
                ssErr(EH_Err, STG_E_INVALIDFUNCTION);
        }
        else if (!((fpi.CurrentByteOffset.QuadPart + dlibMove.QuadPart) >=
                        fpi.CurrentByteOffset.QuadPart))
        {
            ssErr(EH_Err, STG_E_INVALIDFUNCTION);
        }
        
        fpi.CurrentByteOffset.QuadPart = 
            fpi.CurrentByteOffset.QuadPart + dlibMove.QuadPart;
        break;

    case STREAM_SEEK_END:
        nts = NtQueryInformationFile(_h, &iosb, &fsi,
                                     sizeof(FILE_STANDARD_INFORMATION),
                                     FileStandardInformation);
        if (!NT_SUCCESS(nts))
            ssErr(EH_Err, NtStatusToScode(nts));

        // Check for seek before beginning and overflow
        if (dlibMove.HighPart < 0)
        {
            if ((-dlibMove.QuadPart) > fsi.EndOfFile.QuadPart)
                ssErr(EH_Err, STG_E_INVALIDFUNCTION);
        }
        else if (!((fsi.EndOfFile.QuadPart + dlibMove.QuadPart) >=
                         fsi.EndOfFile.QuadPart))
        {
            ssErr(EH_Err, STG_E_INVALIDFUNCTION);
        }

        fpi.CurrentByteOffset.QuadPart = 
            fsi.EndOfFile.QuadPart + dlibMove.QuadPart;
        break;
    }

    sc = S_OK;
    nts = NtSetInformationFile(_h, &iosb, &fpi,
                               sizeof(FILE_POSITION_INFORMATION),
                               FilePositionInformation);
    if (!NT_SUCCESS(nts))
        sc = NtStatusToScode(nts);
    else if (plibNewPosition)
        *plibNewPosition = *(ULARGE_INTEGER *)&fpi.CurrentByteOffset;

    ssDebugOut((DEB_TRACE, "Out CNtFileStream::Seek => %lu:%lu\n",
                fpi.CurrentByteOffset.HighPart,
                fpi.CurrentByteOffset.LowPart));
EH_Err:
    return ssResult(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::SetSize, public
//
//  Synopsis:   Sets the size of a stream
//
//  Arguments:  [ulNewSize] - New size
//
//  Returns:    Appropriate status code
//
//  History:    28-Jun-93       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CNtFileStream::SetSize(ULARGE_INTEGER ulNewSize)
{
    SCODE sc;
    IO_STATUS_BLOCK iosb;
    FILE_END_OF_FILE_INFORMATION feofi;
    NTSTATUS nts;

    ssDebugOut((DEB_TRACE, "In  CNtFileStream::SetSize:%p(%lu:%lu)\n",
                this, ulNewSize.HighPart, ulNewSize.LowPart));
    
    sc = S_OK;
    ssChk(Validate());

    feofi.EndOfFile.LowPart = ulNewSize.LowPart;
    feofi.EndOfFile.HighPart = (ULONG)ulNewSize.HighPart;
    ssAssert(_h != NULL);
    nts = NtSetInformationFile(_h, &iosb, &feofi,
                               sizeof(FILE_END_OF_FILE_INFORMATION),
                               FileEndOfFileInformation);
    if (!NT_SUCCESS(nts))
        ssErr(EH_Err, NtStatusToScode(nts));

    ssDebugOut((DEB_TRACE, "Out CNtFileStream::SetSize\n"));
    
EH_Err:
    return ssResult(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::CopyTo, public
//
//  Synopsis:   Copies information from one stream to another
//
//  Arguments:  [pstm] - Destination
//              [cb] - Number of bytes to copy
//              [pcbRead] - Return number of bytes read
//              [pcbWritten] - Return number of bytes written
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pcbRead]
//              [pcbWritten]
//
//  History:    28-Jun-93       DrewB   Created
//
//  Notes:      We do our best to handle overlap correctly.  This allows
//              CopyTo to be used to insert and remove space within a
//              stream.
//
//              In the error case, we make no gurantees as to the
//              validity of pcbRead, pcbWritten, or either stream's
//              seek position.
//
//----------------------------------------------------------------------------

#define CBBUFFER 8192

STDMETHODIMP CNtFileStream::CopyTo(IStream *pstm,
                                 ULARGE_INTEGER cb,
                                 ULARGE_INTEGER *pcbRead,
                                 ULARGE_INTEGER *pcbWritten)
{
    SCODE sc;
    LARGE_INTEGER cbRead = {0, 0}, cbWritten = {0, 0}, cbDone;
    LARGE_INTEGER liPosFrom, liPosTo, liTmp;
    LARGE_INTEGER liZero = {0, 0};
    LARGE_INTEGER liStep;
    LARGE_INTEGER licb;
    BOOL fForward;
    ULONG ulcbDone, cbDo;
    SafeBytePtr pbBuffer;

    ssDebugOut((DEB_TRACE, "In  CNtFileStream::CopyTo:%p("
                "%p, %lu:%lu, %p, %p)\n",
                this, pstm, cb.HighPart, cb.LowPart, pcbRead, pcbWritten));

    // BUGBUG - Possible 63-bit overflow problems?
    
    if (!IsValidInterface(pstm))
        ssErr(EH_Err, STG_E_INVALIDPARAMETER);
    sc = S_OK;
    ssChk(Validate());

    if (cb.HighPart >= 0x80000000)
        cb.HighPart = 0x7fffffff;
    licb = *(LARGE_INTEGER *)&cb;
        
    pbBuffer.Attach(new BYTE[CBBUFFER]);
    ssMem((BYTE *)pbBuffer);

    ssHChk(Seek(liZero, STREAM_SEEK_CUR, (ULARGE_INTEGER *)&liPosFrom));
    ssHChk(pstm->Seek(liZero, STREAM_SEEK_CUR,
                      (ULARGE_INTEGER *)&liPosTo));

    // Determine which direction to copy to avoid overlap problems
    if (!(liPosFrom.QuadPart >= liPosTo.QuadPart) &&
        (licb.QuadPart > liStep.QuadPart))
    {
        liStep.HighPart = -1;
        liStep.LowPart = (ULONG)(-CBBUFFER);
        fForward = FALSE;

        // Seek to the end of the copy area plus one step
        // so that we're in place for the loop seek backwards
        liTmp.QuadPart = liPosFrom.QuadPart + licb.QuadPart;
        liTmp.QuadPart = liTmp.QuadPart - liStep.QuadPart;
        ssHChk(Seek(liTmp, STREAM_SEEK_SET, NULL));
        liTmp.QuadPart = liPosTo.QuadPart + licb.QuadPart;
        liTmp.QuadPart = liTmp.QuadPart - liStep.QuadPart;
        ssHChk(pstm->Seek(liTmp, STREAM_SEEK_SET, NULL));

        // We seek backwards by twice the step size, once
        // to back up beyond the step we just read and once
        // more to back up to a fresh step worth of data
        ssAssert(CBBUFFER <= 0x3fffffff);
        liStep.LowPart *= 2;
    }
    else
    {
        liStep.HighPart = 0;
        liStep.LowPart = CBBUFFER;
        fForward = TRUE;
    }
        
    cbDone.HighPart = 0;
    for (;;)
    {
        if (licb.HighPart == 0 && licb.LowPart < CBBUFFER)
            cbDo = licb.LowPart;
        else
            cbDo = CBBUFFER;

        if (!fForward)
        {
            // If we're doing the last fragment make sure we don't overshoot
            // when backing up
            if (cbDo < CBBUFFER)
            {
                liStep.LowPart /= 2;
                liStep.LowPart -= CBBUFFER-(LONG)cbDo;
            }
                
            // Seek backwards to fresh data
            ssHChkTo(EH_End, Seek(liStep, STREAM_SEEK_CUR, NULL));
            ssHChkTo(EH_End, pstm->Seek(liStep, STREAM_SEEK_CUR, NULL));
        }

        ssHChkTo(EH_End, Read(pbBuffer, cbDo, &cbDone.LowPart));
        if (cbDone.LowPart == 0)
            break;
        cbRead.QuadPart = cbRead.QuadPart + cbDone.QuadPart;
        ssHChkTo(EH_End, pstm->Write(pbBuffer, cbDone.LowPart,
                                     &ulcbDone));
        if (ulcbDone != cbDone.LowPart)
            ssErr(EH_End, STG_E_WRITEFAULT);
        cbWritten.QuadPart = cbWritten.QuadPart + cbDone.QuadPart;

        licb.QuadPart = licb.QuadPart - cbDone.QuadPart;
        if (licb.HighPart < 0 ||
            (licb.HighPart == 0 && licb.LowPart == 0))
            break;
    }

 EH_End:
    if (pcbRead)
        *(LARGE_INTEGER *)pcbRead = cbRead;
    if (pcbWritten)
        *(LARGE_INTEGER *)pcbWritten = cbWritten;

    ssDebugOut((DEB_TRACE, "Out CNtFileStream::CopyTo => %lu:%lu, %lu:%lu\n",
                cbRead.HighPart, cbRead.LowPart, cbWritten.HighPart,
                cbWritten.LowPart));
    // Fall through
EH_Err:
    return ssResult(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::Commit, public
//
//  Synopsis:   Commits transacted changes
//
//  Arguments:  [grfCommitFlags] - Flags
//
//  Returns:    Appropriate status code
//
//  History:    28-Jun-93       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CNtFileStream::Commit(DWORD grfCommitFlags)
{
    NTSTATUS nts;
    SCODE sc;
    IO_STATUS_BLOCK iosb;
    
    ssDebugOut((DEB_TRACE, "In  CNtFileStream::Commit:%p(%lX)\n",
                this, grfCommitFlags));

    ssChk(VerifyCommitFlags(grfCommitFlags));
    ssChk(Validate());
    
    ssAssert(_h != NULL);
    nts = NtFlushBuffersFile(_h, &iosb);
    if (!NT_SUCCESS(nts))
        sc = NtStatusToScode(nts);
    else
        sc = S_OK;
    
    ssDebugOut((DEB_TRACE, "Out CNtFileStream::Commit => %lX\n", sc));
 EH_Err:
    return ssResult(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::Revert, public
//
//  Synopsis:   Reverts transacted changes
//
//  Returns:    Appropriate status code
//
//  History:    28-Jun-93       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CNtFileStream::Revert(void)
{
    ssDebugOut((DEB_TRACE, "Stb CNtFileStream::Revert()\n"));
    return NOERROR;
}

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::LockRegion, public
//
//  Synopsis:   Locks a portion of the stream
//
//  Arguments:  [libOffset] - Offset to lock at
//              [cb] - Length of lock
//              [dwLockType] - LockType
//
//  Returns:    Appropriate status code
//
//  History:    28-Jun-93       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CNtFileStream::LockRegion(ULARGE_INTEGER libOffset,
                                     ULARGE_INTEGER cb,
                                     DWORD dwLockType)
{
    SCODE sc;
    NTSTATUS nts;
    IO_STATUS_BLOCK iosb;
    
    ssDebugOut((DEB_TRACE, "In  CNtFileStream::LockRegion:%p("
                "%lu:%lu, %lu:%lu, %lu)\n", this, libOffset.HighPart,
                libOffset.LowPart, cb.HighPart, cb.LowPart, dwLockType));

    sc = S_OK;
    ssChk(Validate());
    ssChk(ValidateLockType(dwLockType));

    ssAssert(_h != NULL);
    nts = NtLockFile(_h, NULL, NULL, NULL, &iosb, (PLARGE_INTEGER)&libOffset,
                     (PLARGE_INTEGER)&cb, 0, TRUE, TRUE);
    if (!NT_SUCCESS(nts))
        sc = NtStatusToScode(nts);
    else
        sc = S_OK;
    
    ssDebugOut((DEB_TRACE, "Out CNtFileStream::LockRegion\n"));

 EH_Err:
    return ssResult(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::UnlockRegion, public
//
//  Synopsis:   Unlocks a locked region
//
//  Arguments:  [libOffset] - Offset to lock at
//              [cb] - Length of lock
//              [dwLockType] - LockType
//
//  Returns:    Appropriate status code
//
//  History:    28-Jun-93       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CNtFileStream::UnlockRegion(ULARGE_INTEGER libOffset,
                                       ULARGE_INTEGER cb,
                                       DWORD dwLockType)
{
    SCODE sc;
    NTSTATUS nts;
    IO_STATUS_BLOCK iosb;
    
    ssDebugOut((DEB_TRACE, "In  CNtFileStream::UnlockRegion:%p("
                "%lu:%lu, %lu:%lu, %lu)\n", this, libOffset.HighPart,
                libOffset.LowPart, cb.HighPart, cb.LowPart, dwLockType));

    sc = S_OK;
    ssChk(Validate());
    ssChk(ValidateLockType(dwLockType));

    ssAssert(_h != NULL);
    nts = NtUnlockFile(_h, &iosb, (PLARGE_INTEGER)&libOffset,
                       (PLARGE_INTEGER)&cb, 0);
    if (!NT_SUCCESS(nts))
        sc = NtStatusToScode(nts);
    else
        sc = S_OK;
    
    ssDebugOut((DEB_TRACE, "Out CNtFileStream::UnlockRegion\n"));

 EH_Err:
    return ssResult(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::Stat, public
//
//  Synopsis:   Fills in a buffer of information about this object
//
//  Arguments:  [pstatstg] - Buffer
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pstatstg]
//
//  History:    28-Jun-93       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CNtFileStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
{
    SCODE sc;
    STATSTG stat;
    FILEDIR fd;
    
    ssDebugOut((DEB_TRACE, "In  CNtFileStream::Stat:%p(%p, %lX)\n",
                this, pstatstg, grfStatFlag));

    ssChk(VerifyStatFlag(grfStatFlag));
    ssChk(Validate());

    __try
    {
        // \CONTENTS must be appended to name
        ssAssert(_h != NULL);
        sc = StatNtHandle(_h, grfStatFlag,
                          sizeof(CONTENTS_STREAM)+sizeof(WCHAR), &stat,
                          NULL, NULL, &fd);
        if (SUCCEEDED(sc))
        {
            if (stat.pwcsName)
            {
                WCHAR *pwcs;

                pwcs = stat.pwcsName+lstrlenW(stat.pwcsName);
                *pwcs++ = L'\\';
                lstrcpyW(pwcs, CONTENTS_STREAM);
            }
            stat.grfMode = _grfMode;
            stat.type = STGTY_STREAM;
            stat.mtime.dwLowDateTime = stat.mtime.dwHighDateTime = 0;
            stat.atime.dwLowDateTime = stat.atime.dwHighDateTime = 0;
            stat.ctime.dwLowDateTime = stat.ctime.dwHighDateTime = 0;
            stat.grfLocksSupported = LOCK_EXCLUSIVE | LOCK_ONLYONCE;
            *pstatstg = stat;
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        if (stat.pwcsName)
            ssVerSucc(CoMemFree(stat.pwcsName));
        sc = HRESULT_FROM_NT(GetExceptionCode());
    }
        
    ssDebugOut((DEB_TRACE, "Out CNtFileStream::Stat\n"));
EH_Err:
    return ssResult(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:     CNtFileStream::Clone, public
//
//  Synopsis:   Clones a stream
//
//  Returns:    Appropriate status code
//
//  History:    28-Jun-93       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CNtFileStream::Clone(IStream **ppstm)
{
    SCODE sc;
    SafeCNtFileStream pfs;

    ssDebugOut((DEB_TRACE, "In  CNtFileStream::Clone:%p(%p)\n",
                this, ppstm));

    sc = S_OK;
    ssChk(Validate());

    pfs.Attach(new CNtFileStream());
    ssMem((CNtFileStream *)pfs);
    ssAssert(_h != NULL);
    ssChk(pfs->InitClone(_h, _grfMode));
    TRANSFER_INTERFACE(pfs, IStream, ppstm);

    ssDebugOut((DEB_TRACE, "Out CNtFileStream::Clone => %p\n", *ppstm));

 EH_Err:
    return ssResult(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:	CNtFileStream::ValidateMode, private
//
//  Synopsis:	Validates a mode
//
//  Arguments:	[grfMode] - Mode
//
//  Returns:	Appropriate status code
//
//  History:	09-Jul-93	DrewB	Created
//
//  Notes:      Streams allow any sharing permissions but ignore all of them
//
//----------------------------------------------------------------------------

SCODE CNtFileStream::ValidateMode(DWORD grfMode)
{
    SCODE sc;
    
    ssDebugOut((DEB_ITRACE, "In  CNtFileStream::ValidateMode:%p(0x%lX)\n",
                this, grfMode));
    if ((grfMode & (STGM_TRANSACTED | STGM_PRIORITY | STGM_DELETEONRELEASE |
                    STGM_CONVERT)))
        sc = STG_E_INVALIDFLAG;
    else
        sc = S_OK;
    ssDebugOut((DEB_ITRACE, "Out CNtFileStream::ValidateMode => 0x%lX\n"));
    return sc;
}