//+--------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1991 - 1992.
//
//  File:       filest.cxx
//
//  Contents:   Generic 16/32 filestream code
//
//  History:    20-Nov-91       DrewB   Created
//
//---------------------------------------------------------------

#include <exphead.cxx>
#pragma hdrstop

#include <marshl.hxx>

//+---------------------------------------------------------------------------
//
//  Member:     CFileStream::CFileStream, public
//
//  Synopsis:   Empty object constructor
//
//  History:    26-Oct-92       DrewB   Created
//
//----------------------------------------------------------------------------


CFileStream::CFileStream(IMalloc * const pMalloc)
        : _pMalloc(pMalloc)
{
    _cReferences = 1;
    _hFile = INVALID_FH;
    _hReserved = INVALID_FH;
    _hPreDuped = INVALID_FH;
    _pgfst = NULL;
    _grfLocal = 0;
    _sig = CFILESTREAM_SIG;

#ifdef USE_FILEMAPPING
    _hMapObject = NULL;
    _pbBaseAddr = NULL;
    _cbViewSize = 0;
#endif

#ifdef ASYNC
    _ppc = NULL;
#endif
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::InitGlobal, public
//
//  Synopsis:   Constructor for flags only
//
//  Arguments:  [dwStartFlags] - Startup flags
//              [df] - Permissions
//
//  History:    08-Apr-92       DrewB   Created
//
//---------------------------------------------------------------


SCODE CFileStream::InitGlobal(DWORD dwStartFlags,
                             DFLAGS df)
{
    SCODE sc = S_OK;
    CGlobalFileStream *pgfstTemp;

    fsAssert(_pgfst == NULL);

    fsMem(pgfstTemp = new (_pMalloc) CGlobalFileStream(_pMalloc,
                                     NULL, df, dwStartFlags));
    _pgfst = P_TO_BP(CBasedGlobalFileStreamPtr, pgfstTemp);
    _pgfst->Add(this);
    // Fall through
 EH_Err:
    return sc;
}

//+---------------------------------------------------------------------------
//
//  Member:     CFileStream::InitFromGlobal, public
//
//  Synopsis:   Initializes a filestream with a global filestream
//
//  Arguments:  [pgfst] - Global object
//
//  History:    26-Oct-92       DrewB   Created
//
//----------------------------------------------------------------------------


void CFileStream::InitFromGlobal(CGlobalFileStream *pgfst)
{
    _pgfst = P_TO_BP(CBasedGlobalFileStreamPtr, pgfst);
    _pgfst->AddRef();
    _pgfst->Add(this);
}

//+---------------------------------------------------------------------------
//
//  Member:     CFileStream::InitFromFileStream public
//
//  Synopsis:   Initializes a filestream with another CFileStream
//
//  Arguments:  [pfst] - Global object
//
//  History:    24-Sep-1998     HenryLee  Created
//
//----------------------------------------------------------------------------

void CFileStream::InitFromFileStream (CFileStream *pfst)
{
    _hFile = pfst->_hFile;
    _hMapObject = pfst->_hMapObject;
    _pbBaseAddr = pfst->_pbBaseAddr;
    _cbViewSize = pfst->_cbViewSize;
}

//+---------------------------------------------------------------------------
//
//  Member:     CGlobalFileStream::InitFromGlobalFileStream public
//
//  Synopsis:   Initializes a global filestream from another global filestream
//
//  Arguments:  [pgfs] - Global object
//
//  History:    24-Sep-1998     HenryLee  Created
//
//----------------------------------------------------------------------------

void CGlobalFileStream::InitFromGlobalFileStream (CGlobalFileStream *pgfs)
{
#ifdef LARGE_DOCFILE
    _ulPos = pgfs->_ulPos;
#else
    _ulLowPos = pgfs->_ulLowPos;
#endif
    _cbMappedFileSize = pgfs->_cbMappedFileSize;
    _cbMappedCommitSize = pgfs->_cbMappedCommitSize;
    _dwMapFlags = pgfs->_dwMapFlags;
    StringCbCopy (_awcMapName, sizeof(_awcMapName), pgfs->_awcMapName);
    StringCbCopy (_awcPath, sizeof(_awcPath), pgfs->_awcPath);

#ifdef ASYNC
    _dwTerminate = pgfs->_dwTerminate;
    _ulHighWater = pgfs->_ulHighWater;
    _ulFailurePoint = pgfs->_ulFailurePoint;
#endif // ASYNC
#if DBG == 1
    _ulLastFilePos = pgfs->_ulLastFilePos;
#endif
    _cbSector = pgfs->_cbSector;
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::vRelease, public
//
//  Synopsis:   PubList support
//
//  History:    19-Aug-92       DrewB   Created
//
//---------------------------------------------------------------


ULONG CFileStream::vRelease(void)
{
    LONG lRet;
    filestDebug((DEB_ITRACE, "In  CFileStream::vRelease:%p()\n", this));
    fsAssert(_cReferences > 0);
    lRet = InterlockedDecrement(&_cReferences);
    if (lRet == 0)
    {
#ifdef ASYNC
        if (_ppc != NULL)
        {
#ifdef MULTIHEAP
            CSafeMultiHeap smh(_ppc);
#endif
            SCODE sc;
            sc = TakeSem();
            fsAssert(SUCCEEDED(sc));
            CPerContext *ppc = _ppc;

            _ppc = NULL;
            delete this;

            if (ppc->ReleaseSharedMem() == 0)
            {
#ifdef MULTIHEAP
                g_smAllocator.Uninit();
#endif
            }
        }
        else
#endif
            delete this;
    }
    return (ULONG)lRet;
    filestDebug((DEB_ITRACE, "Out CFileStream::vRelease => %d\n",lRet));
}


//+--------------------------------------------------------------
//
//  Member:     CFileStream::Release, public
//
//  Synopsis:   Releases resources for an LStream
//
//  Returns:    Appropriate status code
//
//  History:    20-Feb-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP_(ULONG) CFileStream::Release(void)
{
    ULONG ulRet;

    filestDebug((DEB_ITRACE, "In  CFileStream::Release()\n"));

    fsAssert(_cReferences >= 1);

    ulRet = CFileStream::vRelease();

    filestDebug((DEB_ITRACE, "Out CFileStream::Release\n"));
    return ulRet;
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::AddRef, public
//
//  Synopsis:   Increases the ref count
//
//  History:    27-Feb-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP_(ULONG) CFileStream::AddRef(void)
{
    ULONG ulRet;

    filestDebug((DEB_ITRACE, "In  CFileStream::AddRef()\n"));


    CFileStream::vAddRef();
    ulRet = _cReferences;

    filestDebug((DEB_ITRACE, "Out CFileStream::AddRef, %ld\n", _cReferences));
    return ulRet;
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::GetName, public
//
//  Synopsis:   Returns the internal path
//
//  Arguments:  [ppwcsName] - Name pointer return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppwcsName]
//
//  History:    24-Mar-92       DrewB   Created
//
//---------------------------------------------------------------


SCODE CFileStream::GetName(WCHAR **ppwcsName)
{
    SCODE sc;

    filestDebug((DEB_ITRACE, "In  CFileStream::GetName(%p)\n",
                ppwcsName));
    fsAssert(_pgfst->HasName());
    fsChk(DfAllocWC(lstrlenW(_pgfst->GetName())+1, ppwcsName));
    StringCchCopyW(*ppwcsName, lstrlenW(_pgfst->GetName())+1, _pgfst->GetName());

    filestDebug((DEB_ITRACE, "Out CFileStream::GetName => %ws\n",
                *ppwcsName));
EH_Err:
    return sc;
}

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


STDMETHODIMP CFileStream::QueryInterface(REFIID iid, void **ppvObj)
{
    SCODE sc;
#ifdef ASYNC
    BOOL fIsAsync = (_ppc != NULL);
#else
    const BOOL fIsAsync = FALSE;
#endif

    filestDebug((DEB_ITRACE, "In  CFileStream::QueryInterface(?, %p)\n",
                ppvObj));


    sc = S_OK;
    if (IsEqualIID(iid, IID_IFileLockBytes) ||
        IsEqualIID(iid, IID_IUnknown))
    {
        *ppvObj = (IFileLockBytes *)this;
        CFileStream::vAddRef();
    }
    else if ((IsEqualIID(iid, IID_ILockBytes)) && !fIsAsync)
    {
        *ppvObj = (ILockBytes *)this;
        CFileStream::vAddRef();
    }
    else if ((IsEqualIID(iid, IID_IMarshal)) && !fIsAsync)
    {
        *ppvObj = (IMarshal *)this;
        CFileStream::vAddRef();
    }
#ifdef ASYNC
    else if (IsEqualIID(iid, IID_IFillLockBytes))
    {
        *ppvObj = (IFillLockBytes *)this;
        CFileStream::vAddRef();
    }
    else if (IsEqualIID(iid, IID_IFillInfo))
    {
        *ppvObj = (IFillInfo *)this;
        CFileStream::vAddRef();
    }
#endif
#if WIN32 >= 300
    else if (IsEqualIID(iid, IID_IAccessControl))
    {
        DWORD grfMode = 0;
        if (_pgfst->GetDFlags() & DF_TRANSACTED)
           grfMode |= STGM_TRANSACTED;
        if (_pgfst->GetDFlags() & DF_ACCESSCONTROL)
           grfMode |= STGM_EDIT_ACCESS_RIGHTS;

        // check if underlying file system supports security
        if (SUCCEEDED(sc = InitAccessControl(_hFile, grfMode, TRUE, NULL)))
        {
            *ppvObj = (IAccessControl *) this;
            CFileStream::vAddRef();
        }
        else sc = E_NOINTERFACE;
    }
#endif
    else
    {
        sc = E_NOINTERFACE;
    }

    filestDebug((DEB_ITRACE, "Out CFileStream::QueryInterface => %p\n",
                ppvObj));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::Unmarshal, public
//
//  Synopsis:   Creates a duplicate FileStream
//
//  Arguments:  [ptsm] - Marshal stream
//              [ppv] - New filestream return
//              [mshlflags] - Marshal flags
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppv]
//
//  History:    14-Apr-92       DrewB   Created
//
//---------------------------------------------------------------


SCODE CFileStream::Unmarshal(IStream *pstm,
                             void **ppv,
                             DWORD mshlflags)
{
    SCODE sc;
    CFileStream *pfst;
    CGlobalFileStream *pgfst;

    filestDebug((DEB_ITRACE, "In  CFileStream::Unmarshal(%p, %p, %lu)\n",
                 pstm, ppv, mshlflags));

    fsChk(UnmarshalPointer(pstm, (void **)&pgfst));
    pfst = pgfst->Find(GetCurrentContextId());

    if (pfst != NULL && !pfst->IsHandleValid())
        pfst = NULL;

    if (pfst)
    {
            pfst->AddRef();

            //
            // Scratch CFileStreams are always marshaled.  If we marshal a
            // direct-mode Base file, then an unopen uninitialized scratch
            // CFileStream is also marshaled (don't call InitUnmarshal here).
            // If a substorage is later opened in transacted mode then the
            // unopen scratch CFileStream is initialized (given a filename
            // and opened).  If the scratch is then marshaled, the reciever
            // must initialize his unopened scratch CFileStream.
            //
            if (pgfst->HasName())
            {
                fsChkTo(EH_pfst, pfst->InitUnmarshal());
            }
    }
    else
    {
            fsMemTo(EH_pgfst,
                    pfst = new (pgfst->GetMalloc())
                    CFileStream(pgfst->GetMalloc()));
            pfst->InitFromGlobal(pgfst);

            if (pgfst->HasName())
            {
                fsChkTo(EH_pfst, pfst->InitUnmarshal());
            }
    }
    *ppv = (void *)pfst;

    filestDebug((DEB_ITRACE, "Out CFileStream::Unmarshal => %p\n", *ppv));

    return S_OK;

 EH_pfst:
    pfst->Release();
 EH_pgfst:
 EH_Err:
    return sc;
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::GetUnmarshalClass, public
//
//  Synopsis:   Returns the class ID
//
//  Arguments:  [riid] - IID of object
//              [pv] - Unreferenced
//              [dwDestContext] - Unreferenced
//              [pvDestContext] - Unreferenced
//              [mshlflags] - Unreferenced
//              [pcid] - CLSID return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pcid]
//
//  History:    04-May-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CFileStream::GetUnmarshalClass(REFIID riid,
                                            void *pv,
                                            DWORD dwDestContext,
                                            LPVOID pvDestContext,
                                            DWORD mshlflags,
                                            LPCLSID pcid)
{
    SCODE sc;

    filestDebug((DEB_ITRACE, "In  CFileStream::GetUnmarshalClass("
                "riid, %p, %lu, %p, %lu, %p)\n", pv, dwDestContext,
                pvDestContext, mshlflags, pcid));

    UNREFERENCED_PARM(pv);
    UNREFERENCED_PARM(mshlflags);


    *pcid = CLSID_DfMarshal;
    sc = S_OK;

    filestDebug((DEB_ITRACE, "Out CFileStream::GetUnmarshalClass\n"));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::GetMarshalSizeMax, public
//
//  Synopsis:   Returns the size needed for the marshal buffer
//
//  Arguments:  [iid] - IID of object being marshaled
//              [pv] - Unreferenced
//              [dwDestContext] - Unreferenced
//              [pvDestContext] - Unreferenced
//              [mshlflags] - Marshal flags
//              [pcbSize] - Size return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pcbSize]
//
//  History:    04-May-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CFileStream::GetMarshalSizeMax(REFIID riid,
                                            void *pv,
                                            DWORD dwDestContext,
                                            LPVOID pvDestContext,
                                            DWORD mshlflags,
                                            LPDWORD pcbSize)
{
    SCODE sc;

    UNREFERENCED_PARM(pv);
    fsChk(Validate());
    sc = GetStdMarshalSize(riid, IID_ILockBytes, dwDestContext, pvDestContext,
                           mshlflags, pcbSize, sizeof(CFileStream *),
#ifdef ASYNC
                           NULL,
                           FALSE,
                           NULL,
#else
                           NULL,
#endif
                           FALSE);

    fsAssert (_ppc == NULL);   // async lockbytes are standard marshaled
EH_Err:
    return sc;
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::MarshalInterface, public
//
//  Synopsis:   Marshals a given object
//
//  Arguments:  [pstStm] - Stream to write marshal data into
//              [iid] - Interface to marshal
//              [pv] - Unreferenced
//              [dwDestContext] - Unreferenced
//              [pvDestContext] - Unreferenced
//              [mshlflags] - Marshal flags
//
//  Returns:    Appropriate status code
//
//  History:    04-May-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CFileStream::MarshalInterface(IStream *pstStm,
                                           REFIID riid,
                                           void *pv,
                                           DWORD dwDestContext,
                                           LPVOID pvDestContext,
                                           DWORD mshlflags)
{
    SCODE sc;
#ifdef ASYNC
    fsAssert (_ppc == NULL);   // async lockbytes are standard marshaled
#endif

    filestDebug((DEB_ITRACE, "In  CFileStream::MarshalInterface("
                "%p, iid, %p, %lu, %p, %lu)\n", pstStm, pv, dwDestContext,
                pvDestContext, mshlflags));

    UNREFERENCED_PARM(pv);

    fsChk(StartMarshal(pstStm, riid, IID_ILockBytes, mshlflags));
    fsChk(MarshalPointer(pstStm, BP_TO_P(CGlobalFileStream *, _pgfst)));

    filestDebug((DEB_ITRACE, "Out CFileStream::MarshalInterface\n"));
EH_Err:
    return sc;
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::UnmarshalInterface, public
//
//  Synopsis:   Non-functional
//
//  Arguments:  [pstStm] -
//              [iid] -
//              [ppvObj] -
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppvObj]
//
//  History:    04-May-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CFileStream::UnmarshalInterface(IStream *pstStm,
                                             REFIID iid,
                                             void **ppvObj)
{
    return ResultFromScode(STG_E_INVALIDFUNCTION);
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::StaticReleaseMarshalData, public static
//
//  Synopsis:   Releases any references held in marshal data
//
//  Arguments:  [pstStm] - Marshal data stream
//
//  Returns:    Appropriate status code
//
//  History:    02-Feb-94       DrewB   Created
//
//  Notes:      Assumes standard marshal header has already been read
//
//---------------------------------------------------------------


SCODE CFileStream::StaticReleaseMarshalData(IStream *pstStm,
                                            DWORD mshlflags)
{
    SCODE sc;
    CGlobalFileStream *pgfst;

    filestDebug((DEB_ITRACE, "In  CFileStream::StaticReleaseMarshalData:("
                "%p, %lX)\n", pstStm, mshlflags));

    fsChk(UnmarshalPointer(pstStm, (void **)&pgfst));

    filestDebug((DEB_ITRACE, "Out CFileStream::StaticReleaseMarshalData\n"));
EH_Err:
    return sc;
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::ReleaseMarshalData, public
//
//  Synopsis:   Non-functional
//
//  Arguments:  [pstStm] -
//
//  Returns:    Appropriate status code
//
//  History:    18-Sep-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CFileStream::ReleaseMarshalData(IStream *pstStm)
{
    SCODE sc;
    DWORD mshlflags;
    IID iid;

    filestDebug((DEB_ITRACE, "In  CFileStream::ReleaseMarshalData:%p(%p)\n",
                this, pstStm));


    fsChk(SkipStdMarshal(pstStm, &iid, &mshlflags));
    fsAssert(IsEqualIID(iid, IID_ILockBytes));
    sc = StaticReleaseMarshalData(pstStm, mshlflags);

    filestDebug((DEB_ITRACE, "Out CFileStream::ReleaseMarshalData\n"));
EH_Err:
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::DisconnectObject, public
//
//  Synopsis:   Non-functional
//
//  Arguments:  [dwReserved] -
//
//  Returns:    Appropriate status code
//
//  History:    18-Sep-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CFileStream::DisconnectObject(DWORD dwReserved)
{
    return ResultFromScode(STG_E_INVALIDFUNCTION);
}

//+--------------------------------------------------------------
//
//  Member:     CFileStream::GetLocksSupported, public
//
//  Synopsis:   Return lock capabilities
//
//  Arguments:  [pdwLockFlags] -- place holder for lock flags
//
//  Returns:    Appropriate status code
//
//  History:    12-Jul-93       AlexT   Created
//
//---------------------------------------------------------------


STDMETHODIMP CFileStream::GetLocksSupported(DWORD *pdwLockFlags)
{
    if (pdwLockFlags != NULL)
        *pdwLockFlags = LOCK_EXCLUSIVE | LOCK_ONLYONCE;
    else
        return STG_E_INVALIDPOINTER;

    return S_OK ;
}