You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3433 lines
102 KiB
3433 lines
102 KiB
//+--------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1991 - 1992.
|
|
//
|
|
// File: filest32.cxx
|
|
//
|
|
// Contents: Win32 LStream implementation
|
|
//
|
|
// History: 12-May-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#include <exphead.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <time.h>
|
|
#include <marshl.hxx>
|
|
#include <df32.hxx>
|
|
#include <logfile.hxx>
|
|
#include <dfdeb.hxx>
|
|
#include <lock.hxx>
|
|
|
|
#if WIN32 != 200
|
|
#define USE_OVERLAPPED
|
|
#endif
|
|
|
|
DECLARE_INFOLEVEL(filest);
|
|
|
|
HRESULT GetNtHandleSectorSize (HANDLE Handle, ULONG * pulSectorSize);
|
|
HRESULT NtStatusToScode(NTSTATUS nts);
|
|
|
|
#if DBG == 1
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::PossibleDiskFull, private
|
|
//
|
|
// Synopsis: In debug builds it can simulate a disk full error
|
|
//
|
|
// Returns: STG_E_MEDIUMFULL when it wants to simulate an error.
|
|
//
|
|
// History: 25-Nov-96 BChapman Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
SCODE CFileStream::PossibleDiskFull(ULONGLONG ulOffset)
|
|
#else
|
|
SCODE CFileStream::PossibleDiskFull(ULONG iOffset)
|
|
#endif
|
|
{
|
|
#ifdef LARGE_DOCFILE
|
|
ULARGE_INTEGER ulCurrentSize;
|
|
|
|
ulCurrentSize.LowPart = GetFileSize(_hFile, &ulCurrentSize.HighPart);
|
|
if (ulOffset > ulCurrentSize.QuadPart)
|
|
#else
|
|
ULONG ulCurrentSize;
|
|
|
|
ulCurrentSize = GetFileSize(_hFile, NULL);
|
|
if ((iOffset) > ulCurrentSize)
|
|
#endif
|
|
{
|
|
if (SimulateFailure(DBF_DISKFULL))
|
|
{
|
|
return STG_E_MEDIUMFULL;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
void CFileStream::CheckSeekPointer(void)
|
|
{
|
|
#ifdef LARGE_DOCFILE
|
|
LARGE_INTEGER ulChk;
|
|
ulChk.QuadPart = 0;
|
|
#else
|
|
LONG lHighChk;
|
|
ULONG ulLowChk;
|
|
lHighChk = 0;
|
|
#endif
|
|
|
|
if (_hFile != INVALID_FH)
|
|
{
|
|
#ifdef LARGE_DOCFILE
|
|
ulChk.LowPart = SetFilePointer(_hFile, 0, &ulChk.HighPart,FILE_CURRENT);
|
|
if (ulChk.LowPart == MAX_ULONG && GetLastError() != NOERROR)
|
|
#else
|
|
ulLowChk = SetFilePointer(_hFile, 0, &lHighChk, FILE_CURRENT);
|
|
|
|
if (ulLowChk == 0xFFFFFFFF)
|
|
#endif
|
|
{
|
|
//An error of some sort occurred.
|
|
filestDebug((DEB_ERROR, "SetFilePointer call failed with %x\n",
|
|
GetLastError()));
|
|
return;
|
|
}
|
|
if (_pgfst != NULL)
|
|
{
|
|
#ifdef LARGE_DOCFILE
|
|
_pgfst->CheckSeekPointer(ulChk.QuadPart);
|
|
#else
|
|
_pgfst->CheckSeekPointer(ulLowChk);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif // DBG == 1
|
|
|
|
#if DBG == 1
|
|
#ifdef LARGE_DOCFILE
|
|
inline void CGlobalFileStream::CheckSeekPointer(ULONGLONG ulChk)
|
|
{
|
|
if(ulChk != _ulPos && MAX_ULONGLONG != _ulPos)
|
|
{
|
|
filestDebug((DEB_ERROR,"Seek pointer mismatch."
|
|
" Cached = 0x%Lx, Real = 0x%Lx, Last Checked = 0x%Lx"
|
|
" %ws\n",
|
|
_ulPos,
|
|
ulChk,
|
|
_ulLastFilePos,
|
|
GetName()));
|
|
fsAssert(aMsg("Cached FilePointer incorrect!!\n"));
|
|
}
|
|
_ulLastFilePos = ulChk;
|
|
}
|
|
#else
|
|
inline void CGlobalFileStream::CheckSeekPointer(DWORD ulLowChk)
|
|
{
|
|
if(ulLowChk != _ulLowPos && 0xFFFFFFFF != _ulLowPos)
|
|
{
|
|
filestDebug((DEB_ERROR,"Seek pointer mismatch."
|
|
" Cached = 0x%06x, Real = 0x%06x, Last Checked = 0x%06x"
|
|
" %ws\n",
|
|
_ulLowPos,
|
|
ulLowChk,
|
|
_ulLastFilePos,
|
|
GetName()));
|
|
fsAssert(aMsg("Cached FilePointer incorrect!!\n"));
|
|
}
|
|
_ulLastFilePos = ulLowChk;
|
|
}
|
|
#endif // LARGE_DOCFILE
|
|
#endif
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::InitWorker, private
|
|
//
|
|
// Synopsis: Constructor
|
|
//
|
|
// Arguments: [pwcsPath] -- Path
|
|
// [dwFSInit] -- Reason why we are initing.
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
// 20-Feb-97 BChapman Rewrote.
|
|
//
|
|
// Notes: [pwcsPath] may be unsafe memory so we must be careful
|
|
// not to propagate faults
|
|
// The PathName will always be OLECHAR (WIDECHAR) so most of the
|
|
// code is Unicode using WideWrap to talk to Win95. Except
|
|
// when creating a TempFile name. To avoid lots of unnecessary
|
|
// converting we work in TCHAR, and convert only the result.
|
|
//---------------------------------------------------------------
|
|
|
|
SCODE CFileStream::InitWorker(WCHAR const *pwcsPath, DWORD dwFSInit)
|
|
{
|
|
WCHAR awcPath[_MAX_PATH+1];
|
|
TCHAR atcPath[_MAX_PATH+1];
|
|
TCHAR atcTempDirPath[_MAX_PATH+1];
|
|
SCODE sc;
|
|
DWORD DFlags = _pgfst->GetDFlags();
|
|
|
|
filestDebug((DEB_ITRACE, "IN CFileStream::InitWorker(%ws, 0x%x)\n",
|
|
pwcsPath, dwFSInit));
|
|
|
|
// If we've already been constructed, leave
|
|
|
|
if (INVALID_FH != _hFile)
|
|
{
|
|
filestDebug((DEB_ITRACE, "OUT CFileStream::Init returning %x\n", S_OK));
|
|
return S_OK;
|
|
}
|
|
|
|
// Get file name from:
|
|
// 1) the global object on Unmarshals (stored in Unicode).
|
|
// 2) the passed parameter on Opens (OLE standard Unicode).
|
|
// 3) MakeTmpName() when making Scratch or SnapShot files.
|
|
//
|
|
if (!_pgfst->HasName())
|
|
{
|
|
//
|
|
// The Global object does not have a name so this CAN'T be an
|
|
// unmarshal. It is possible for scratch files to be marshaled
|
|
// with no name because they have not yet been "demanded" but
|
|
// Unmarshal() doesn't calls us if there is no name.
|
|
//
|
|
// fsAssert(!(dwFSInit & FSINIT_UNMARSHAL));
|
|
|
|
// If we were passed a name, copy it.
|
|
// Watch out for a possible bad pointer from the user.
|
|
if (pwcsPath != NULL)
|
|
{
|
|
if (FAILED(StringCbCopyW (awcPath, sizeof(awcPath), pwcsPath)))
|
|
fsErr(EH_Err, STG_E_INVALIDPOINTER);
|
|
}
|
|
else // If we weren't given a name then make one up.
|
|
{
|
|
// We use native "TCHAR" when finding a temp name.
|
|
//
|
|
fsChk(Init_GetTempName(atcPath, atcTempDirPath));
|
|
dwFSInit |= FSINIT_MADEUPNAME;
|
|
|
|
if (FAILED(StringCbCopyW (awcPath, sizeof(awcPath), atcPath)))
|
|
fsErr(EH_Err, STG_E_INVALIDPOINTER);
|
|
}
|
|
|
|
fsChk(Init_OpenOrCreate(awcPath, // filename: given or created
|
|
atcTempDirPath, // path to temp directory
|
|
dwFSInit)); // various state information
|
|
}
|
|
else
|
|
{ // Name is in the global file object. This is unmarshaling.
|
|
// Or we could be in the 2nd init of a scratch file that was
|
|
// "demanded" after it was marshaled.
|
|
fsAssert( (dwFSInit & FSINIT_UNMARSHAL)
|
|
|| (GetStartFlags() & RSF_SCRATCH) );
|
|
|
|
fsChk(Init_DupFileHandle(dwFSInit));
|
|
}
|
|
|
|
//
|
|
// If this is the first open of the file then we need to store
|
|
// the name into the global object.
|
|
// Set name to fully qualified path to avoid current-directory
|
|
// dependencies.
|
|
// Always store the path as Unicode. We are using "WideWrap" here.
|
|
//
|
|
if (!_pgfst->HasName())
|
|
{
|
|
WCHAR awcFullPath[_MAX_PATH+1];
|
|
WCHAR *pwcBaseName;
|
|
|
|
if(0 == GetFullPathName(awcPath, _MAX_PATH, awcFullPath, &pwcBaseName))
|
|
{
|
|
fsErr(EH_File, LAST_STG_SCODE);
|
|
}
|
|
_pgfst->SetName(awcFullPath);
|
|
|
|
//
|
|
// If this is the first open of a SCRATCH then dup the file handle
|
|
// to any other marshaled instances. This covers the case of a
|
|
// late "demand" scratch.
|
|
//
|
|
if(GetStartFlags() & RSF_SCRATCH)
|
|
DupFileHandleToOthers();
|
|
}
|
|
|
|
CheckSeekPointer();
|
|
filestDebug((DEB_INFO,
|
|
"File=%2x Initialize this=%p thread %lX path %ws\n",
|
|
_hFile, this, GetCurrentThreadId(), _pgfst->GetName()));
|
|
|
|
//
|
|
// When a file is mapped we can't shorten the real filesize except by
|
|
// truncating when we close.
|
|
//
|
|
// If More than one context has the file mapped we can't truncate the
|
|
// file when we close.
|
|
//
|
|
// If multiple writers having a file mapped data can be destroyed when
|
|
// they close and truncate a file that the other guy may have written.
|
|
// There is also a problem with readers holding the file mapped that
|
|
// prevents the writer from truncating the file when he closes.
|
|
//
|
|
// So... the cases Where we want to allow mapping:
|
|
// - Direct, Read
|
|
// - Transacted, Read w/ Deny-Write
|
|
// - AnyMode, Write w/ Deny-Write
|
|
// - Tempfiles (scratch and snapshot)
|
|
//
|
|
if( (P_DIRECT(DFlags) && P_READ(DFlags))
|
|
|| (P_TRANSACTED(DFlags) && P_READ(DFlags) && P_DENYWRITE(DFlags))
|
|
|| (P_WRITE(DFlags) && P_DENYWRITE(DFlags))
|
|
|| (RSF_TEMPFILE & GetStartFlags()) )
|
|
{
|
|
Init_MemoryMap(dwFSInit);
|
|
}
|
|
else
|
|
{
|
|
filestDebug((DEB_MAP, "File=%2x Not creating a memory map for %ws.\n",
|
|
_hFile, _pgfst->GetName()));
|
|
}
|
|
|
|
filestDebug((DEB_ITRACE, "OUT CFileStream::Init returning %x\n", S_OK));
|
|
|
|
return S_OK;
|
|
|
|
EH_File:
|
|
fsVerify(CloseHandle(_hFile));
|
|
_hFile = INVALID_FH;
|
|
if(RSF_CREATE & _pgfst->GetStartFlags())
|
|
DeleteTheFile(awcPath);
|
|
|
|
EH_Err:
|
|
filestDebug((DEB_ITRACE, "OUT CFileStream::Init returning %x\n", sc));
|
|
return sc;
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Init_GetTempName, private
|
|
//
|
|
// Synopsis: Make up a temp filename.
|
|
//
|
|
// Arguments: [ptcPath] -- [out] Tmp filename path
|
|
// [ptcTmpPath] -- [out] Tmp directory path
|
|
//
|
|
// Returns: S_OK
|
|
//
|
|
// History: 13-Jan-97 BChapman Clean up InitWorker.
|
|
//
|
|
// Note: TCHAR issue. We do as much as we can "native" and the
|
|
// caller converts the result. (If necessary)
|
|
//
|
|
//---------------------------------------------------------------
|
|
SCODE CFileStream::Init_GetTempName(TCHAR *ptcPath, TCHAR *ptcTmpPath)
|
|
{
|
|
SCODE sc;
|
|
LUID luid;
|
|
DWORD cch;
|
|
|
|
// Can't truncate since for temporary files we will
|
|
// always be creating
|
|
fsAssert(0 == (_pgfst->GetStartFlags() & RSF_TRUNCATE));
|
|
|
|
// Create the Temp Name with native TCHAR.
|
|
//
|
|
cch = GetTempPath(_MAX_PATH, ptcTmpPath);
|
|
if (0 == cch)
|
|
{
|
|
fsErr(EH_Err, LAST_STG_SCODE);
|
|
}
|
|
else if (cch > _MAX_PATH)
|
|
{
|
|
fsErr(EH_Err, STG_E_PATHNOTFOUND);
|
|
}
|
|
|
|
if (AllocateLocallyUniqueId (&luid) == FALSE)
|
|
fsErr (EH_Err, LAST_STG_SCODE);
|
|
|
|
if(0 == GetTempFileName(ptcTmpPath, TEMPFILE_PREFIX, luid.LowPart, ptcPath))
|
|
{
|
|
fsErr(EH_Err, LAST_STG_SCODE);
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
EH_Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Init_GetNtOpenFlags, private
|
|
//
|
|
// Synopsis: Convert Docfile flags into NT Open flags.
|
|
//
|
|
// Arguments: [pdwAccess] -- [out] NT flags
|
|
// [pdwShare] -- [out] NT flags
|
|
// [pdwCreation] -- [out] NT Flags
|
|
// [pdwFlagAttr] -- [out] NT Flags
|
|
//
|
|
// Returns: S_OK
|
|
//
|
|
// Notes: Uses _pgfst->GetDFlags() and _pgfst->GetStartFlags().
|
|
//
|
|
// History: 6-Nov-96 BChapman Clean up InitWorker.
|
|
//
|
|
//---------------------------------------------------------------
|
|
SCODE CFileStream::Init_GetNtOpenFlags(
|
|
LPDWORD pdwAccess,
|
|
LPDWORD pdwShare,
|
|
LPDWORD pdwCreation,
|
|
LPDWORD pdwFlagAttr)
|
|
{
|
|
DWORD dwStartFlags = _pgfst->GetStartFlags();
|
|
|
|
*pdwAccess = 0;
|
|
*pdwShare = 0;
|
|
*pdwCreation = 0;
|
|
*pdwFlagAttr = 0;
|
|
|
|
if (_pgfst->HasName())
|
|
{
|
|
dwStartFlags &= ~RSF_CREATEFLAGS;
|
|
dwStartFlags |= RSF_OPEN;
|
|
}
|
|
|
|
if (dwStartFlags & RSF_OPENCREATE)
|
|
{
|
|
// This is used for our internal logging
|
|
*pdwCreation = OPEN_ALWAYS;
|
|
}
|
|
else if (dwStartFlags & RSF_CREATE)
|
|
{
|
|
if (dwStartFlags & RSF_TRUNCATE)
|
|
*pdwCreation = CREATE_ALWAYS;
|
|
else
|
|
*pdwCreation = CREATE_NEW;
|
|
}
|
|
else
|
|
{
|
|
if (dwStartFlags & RSF_TRUNCATE)
|
|
*pdwCreation = TRUNCATE_EXISTING;
|
|
else
|
|
*pdwCreation = OPEN_EXISTING;
|
|
}
|
|
|
|
DWORD dwDFlags = _pgfst->GetDFlags();
|
|
if (!P_WRITE(dwDFlags))
|
|
*pdwAccess = GENERIC_READ;
|
|
else
|
|
*pdwAccess = GENERIC_READ | GENERIC_WRITE;
|
|
if (P_DENYWRITE(dwDFlags) && !P_WRITE(dwDFlags))
|
|
*pdwShare = FILE_SHARE_READ;
|
|
else
|
|
*pdwShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
#if WIN32 == 300
|
|
if (dwDFlags & DF_ACCESSCONTROL)
|
|
*pdwAccess |= WRITE_DAC | READ_CONTROL;
|
|
#endif
|
|
|
|
// Make sure we're not attempting to create/truncate a read-only thing
|
|
fsAssert(*pdwAccess != GENERIC_READ ||
|
|
!(dwStartFlags & (RSF_CREATE | RSF_TRUNCATE)));
|
|
|
|
switch(RSF_TEMPFILE & dwStartFlags)
|
|
{
|
|
case RSF_SCRATCH:
|
|
*pdwFlagAttr = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS;
|
|
break;
|
|
|
|
case RSF_SNAPSHOT:
|
|
*pdwFlagAttr = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
|
|
break;
|
|
|
|
case 0: // Normal "original" file
|
|
*pdwFlagAttr = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
|
|
break;
|
|
|
|
default:
|
|
// if we get here something is rotten in the header files
|
|
fsAssert(RSF_TEMPFILE == (RSF_SCRATCH | RSF_SNAPSHOT));
|
|
break;
|
|
}
|
|
|
|
if(RSF_DELETEONRELEASE & dwStartFlags && RSF_TEMPFILE & dwStartFlags)
|
|
{
|
|
*pdwFlagAttr |= FILE_FLAG_DELETE_ON_CLOSE;
|
|
*pdwShare |= FILE_SHARE_DELETE;
|
|
}
|
|
|
|
if (RSF_NO_BUFFERING & dwStartFlags)
|
|
{
|
|
*pdwFlagAttr |= FILE_FLAG_NO_BUFFERING;
|
|
}
|
|
|
|
if (RSF_ENCRYPTED & dwStartFlags)
|
|
{
|
|
*pdwFlagAttr |= FILE_ATTRIBUTE_ENCRYPTED;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Init_OpenOrCreate, private
|
|
//
|
|
// Synopsis: Actually Open the NT file.
|
|
//
|
|
// Arguments: [pwcPath] -- Name of File to open. OLECHAR
|
|
// [ptcTmpPath] -- Path to the Temp Directory. TCHAR
|
|
// [dwFSInit] -- Flags.
|
|
//
|
|
// Returns: S_OK
|
|
//
|
|
// History: 6-Nov-96 BChapman InitWorker was too big.
|
|
//
|
|
// Notes: We pass the Temp directory path as a TCHAR because if we
|
|
// need to construct a new temp file name we want to work
|
|
// "native" as much as possible.
|
|
//
|
|
//---------------------------------------------------------------
|
|
SCODE CFileStream::Init_OpenOrCreate(
|
|
WCHAR *pwcPath,
|
|
TCHAR *ptcTmpPath,
|
|
DWORD dwFSInit)
|
|
{
|
|
SCODE sc;
|
|
TCHAR atcPath[_MAX_PATH+1];
|
|
DWORD dwAccess, dwShare, dwCreation, dwFlagAttr;
|
|
DWORD dwDFlags = _pgfst->GetDFlags();
|
|
|
|
Init_GetNtOpenFlags(&dwAccess, &dwShare, &dwCreation, &dwFlagAttr);
|
|
|
|
//If we're opening with deny-read, we need to let the
|
|
// file system tell us if there are any other readers, to
|
|
// avoid our no-lock trick for the read-only deny-write case.
|
|
//Yes, this is ugly.
|
|
//Yes, it also has a race condition in which two people can
|
|
// get read access while specifying SHARE_DENY_READ.
|
|
//
|
|
if (!(dwFSInit & FSINIT_UNMARSHAL) &&
|
|
!P_WRITE(dwDFlags) && P_DENYREAD(dwDFlags))
|
|
{
|
|
// We open read-only, share exclusive(ie == 0). If this
|
|
// fails, there is already another accessor, so we bail.
|
|
//
|
|
// If we are unmarshalling, we don't do this check because we
|
|
// know there is already another reader, i.e. the original open.
|
|
//
|
|
// Using WideWrap to handle the !UNICODE case.
|
|
//
|
|
_hFile = CreateFile(pwcPath, GENERIC_READ, 0, NULL,
|
|
dwCreation, dwFlagAttr, NULL);
|
|
|
|
if (INVALID_FH == _hFile)
|
|
{
|
|
filestDebug((DEB_INFO, "Can open file %ws in the test open "
|
|
"read-only deny read case\n", pwcPath));
|
|
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
|
fsErr(EH_Err, STG_E_FILEALREADYEXISTS)
|
|
else
|
|
fsErr(EH_Err, LAST_STG_SCODE);
|
|
}
|
|
|
|
CloseHandle(_hFile);
|
|
}
|
|
|
|
//
|
|
// Open the File. We use OLE WideWrap to handle !UNICODE
|
|
//
|
|
_hFile = CreateFile(pwcPath, dwAccess, dwShare, NULL,
|
|
dwCreation, dwFlagAttr, NULL);
|
|
|
|
//
|
|
// If the Open failed w/ error = FILE_EXISTS and this is a temp file
|
|
// then make up a new temp file name and keep tring until we succeed
|
|
// or fail with a different error.
|
|
//
|
|
if (INVALID_FH == _hFile)
|
|
{
|
|
DWORD dwLastError = GetLastError();
|
|
|
|
if (dwLastError != ERROR_ALREADY_EXISTS &&
|
|
dwLastError != ERROR_FILE_EXISTS)
|
|
{
|
|
filestDebug((DEB_INFO, "File %ws Open Failed. err=%x\n",
|
|
pwcPath, dwLastError));
|
|
fsErr(EH_Err, STG_SCODE(dwLastError));
|
|
}
|
|
|
|
//
|
|
// If we didn't make this name (ie. tempfile) then it is
|
|
// time to error out. Otherwise if we did make this name then
|
|
// we can continue to try other names.
|
|
//
|
|
if(!(FSINIT_MADEUPNAME & dwFSInit))
|
|
{
|
|
filestDebug((DEB_INFO, "File Open Failed. File %ws Exists\n",
|
|
pwcPath));
|
|
fsErr(EH_Err, STG_E_FILEALREADYEXISTS);
|
|
}
|
|
|
|
LUID luid;
|
|
CGlobalSecurity gs; // put an secure ACL on the temp files
|
|
fsChk (gs.Init(TRUE));
|
|
|
|
while (1)
|
|
{
|
|
if (AllocateLocallyUniqueId (&luid) == FALSE)
|
|
fsErr (EH_Err, LAST_STG_SCODE);
|
|
|
|
filestDebug((DEB_INFO,
|
|
"CreateFile failed %x tring a new tempfile name.\n",
|
|
dwLastError));
|
|
|
|
if (GetTempFileName(ptcTmpPath, TEMPFILE_PREFIX,
|
|
luid.LowPart, atcPath) == 0)
|
|
{
|
|
fsErr(EH_Err, LAST_STG_SCODE);
|
|
}
|
|
|
|
StringCchCopyW (pwcPath, MAX_PATH+1, atcPath);
|
|
|
|
filestDebug((DEB_INFO,
|
|
"Tempfile CreateFile(%ws, %x, %x, NULL, %x, NORMAL|RANDOM, NULL)\n",
|
|
pwcPath, dwAccess, dwShare, dwCreation));
|
|
|
|
//
|
|
// Using WideWrap to handle the !UNICODE case
|
|
//
|
|
_hFile = CreateFile(
|
|
pwcPath,
|
|
dwAccess,
|
|
dwShare,
|
|
(SECURITY_ATTRIBUTES *) gs,
|
|
dwCreation,
|
|
dwFlagAttr,
|
|
NULL);
|
|
if (INVALID_FH != _hFile)
|
|
break;
|
|
|
|
dwLastError = GetLastError();
|
|
if (dwLastError != ERROR_ALREADY_EXISTS &&
|
|
dwLastError != ERROR_FILE_EXISTS)
|
|
{
|
|
fsErr(EH_Err, STG_SCODE(dwLastError));
|
|
}
|
|
}
|
|
}
|
|
|
|
#if WIN32 == 100
|
|
if (_pgfst->GetStartFlags() & RSF_NO_BUFFERING)
|
|
{
|
|
ULONG cbSector;
|
|
fsChk (GetNtHandleSectorSize (_hFile, &cbSector));
|
|
if ((cbSector % HEADERSIZE) == 0) // only support sector sizes n*512
|
|
_pgfst->SetSectorSize(cbSector);
|
|
}
|
|
#endif
|
|
|
|
filestDebug((DEB_INFO,
|
|
"File=%2x CreateFile(%ws, %x, %x, NULL, %x, %x, NULL)\n",
|
|
_hFile, pwcPath, dwAccess, dwShare,
|
|
dwCreation, dwFlagAttr));
|
|
|
|
//At this point the file handle is valid, so let's look at the
|
|
//seek pointer and see what it is.
|
|
CheckSeekPointer();
|
|
|
|
return S_OK;
|
|
|
|
EH_Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Init_DupFileHandle, private
|
|
//
|
|
// Synopsis: Dup any existing File Handle into _hFile.
|
|
//
|
|
// Returns: S_OK, System Error
|
|
// or STG_E_INVALIDHANDLE if it can't file a file to Dup.
|
|
//
|
|
// History: 15-Apr-96 BChapman Created
|
|
//
|
|
// Notes: This is called from unmarshal. So we can assert that there
|
|
// MUST be another instance of an open handle in the list.
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
SCODE CFileStream::Init_DupFileHandle(DWORD dwFSINIT)
|
|
{
|
|
CFileStream *pfst;
|
|
SCODE sc=E_FAIL;
|
|
HANDLE hprocSrc = NULL;
|
|
HANDLE hFileSrc;
|
|
|
|
fsAssert(INVALID_FH == _hFile);
|
|
|
|
//
|
|
// Another context may have already Dup'ed the File Handle
|
|
// to us. (This is only in the marshaled-undemanded-scratch case)
|
|
//
|
|
if(INVALID_FH != _hPreDuped)
|
|
{
|
|
_hFile = _hPreDuped;
|
|
_hPreDuped = INVALID_FH;
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Search the list of contexts for someone we can Dup from.
|
|
//
|
|
pfst = _pgfst->GetFirstContext();
|
|
fsAssert(NULL != pfst);
|
|
|
|
while (pfst != NULL)
|
|
{
|
|
if (INVALID_FH == pfst->_hFile && INVALID_FH == pfst->_hPreDuped)
|
|
{
|
|
pfst = pfst->GetNext();
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Found someone with the file open. Now Dup it.
|
|
//
|
|
hFileSrc = (INVALID_FH == pfst->_hFile) ?
|
|
pfst->_hPreDuped :
|
|
pfst->_hFile;
|
|
|
|
hprocSrc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, pfst->GetContext());
|
|
if(NULL == hprocSrc)
|
|
{
|
|
sc = LAST_STG_SCODE;
|
|
pfst = pfst->GetNext();
|
|
continue;
|
|
}
|
|
|
|
if(DuplicateHandle(hprocSrc, // Src Process
|
|
hFileSrc, // Src Handle
|
|
GetCurrentProcess(), // Dest Process
|
|
&_hFile, // Dest Handle
|
|
0, FALSE,
|
|
DUPLICATE_SAME_ACCESS))
|
|
{
|
|
sc = S_OK;
|
|
break;
|
|
}
|
|
|
|
sc = LAST_STG_SCODE;
|
|
CloseHandle(hprocSrc);
|
|
pfst = pfst->GetNext();
|
|
}
|
|
if (NULL == pfst)
|
|
fsErr(EH_Err, STG_E_INVALIDHANDLE);
|
|
|
|
|
|
if (hprocSrc)
|
|
CloseHandle(hprocSrc);
|
|
return S_OK;
|
|
|
|
EH_Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::DupFileHandleToOthers, private
|
|
//
|
|
// Synopsis: Dup your file handle into everyone else.
|
|
//
|
|
// Returns: S_OK, or system faliure code.
|
|
//
|
|
// History: 17-Apr-96 BChapman Created
|
|
//
|
|
// Notes: This is used when demanding a marshaled undemanded scratch.
|
|
// We put the Dup'ed handle in "_hPreDuped". The "Init_DupHandle()"
|
|
// routine will find it an move it into _hFile.
|
|
//---------------------------------------------------------------
|
|
|
|
SCODE CFileStream::DupFileHandleToOthers()
|
|
{
|
|
SCODE scAny = S_OK;
|
|
CFileStream *pfst;
|
|
HANDLE hprocDest;
|
|
|
|
pfst = _pgfst->GetFirstContext();
|
|
fsAssert(NULL != pfst);
|
|
|
|
//
|
|
// Walk the list of contexts and Dup the file handle into all
|
|
// contexts that don't already have it open. If any of them fail,
|
|
// keep going. Hold onto the last error code and return it.
|
|
//
|
|
while(NULL != pfst)
|
|
{
|
|
if(INVALID_FH == pfst->_hFile && INVALID_FH == pfst->_hPreDuped)
|
|
{
|
|
//
|
|
// Found someone that needs the file handle.
|
|
//
|
|
hprocDest = OpenProcess(PROCESS_DUP_HANDLE,
|
|
FALSE,
|
|
pfst->GetContext());
|
|
if(NULL == hprocDest)
|
|
{
|
|
scAny = LAST_STG_SCODE;
|
|
}
|
|
else
|
|
{
|
|
if(!DuplicateHandle(GetCurrentProcess(), // Src Process
|
|
_hFile, // Src Handle
|
|
hprocDest, // Dest Process
|
|
&pfst->_hPreDuped, // Dest Handle
|
|
0, FALSE,
|
|
DUPLICATE_SAME_ACCESS))
|
|
{
|
|
scAny = LAST_STG_SCODE;
|
|
}
|
|
CloseHandle(hprocDest);
|
|
}
|
|
}
|
|
pfst = pfst->GetNext();
|
|
}
|
|
|
|
return scAny;
|
|
}
|
|
|
|
|
|
#ifdef USE_FILEMAPPING
|
|
|
|
//+--------------------------------------------------------
|
|
// The following are Memory mapped file support for NT
|
|
// Init_MemoryMap
|
|
// TurnOffMapping
|
|
// MakeFileMapAddressValid
|
|
//+--------------------------------------------------------
|
|
|
|
//
|
|
// This size will need to be revisited in the future. Right now this is
|
|
// a balance between not consuming too much of a 32 address space, and big
|
|
// enough for most files without need to grow.
|
|
// In Nov 1996 (at the time of this writing) most documents are between
|
|
// 80-800K. Some huge spreadsheets like the perf group reportare 18-20Mb.
|
|
//
|
|
#define MINIMUM_MAPPING_SIZE (512*1024)
|
|
|
|
BOOL DfIsRemoteFile (HANDLE hFile)
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
FILE_FS_DEVICE_INFORMATION DeviceInformation;
|
|
|
|
DeviceInformation.Characteristics = 0;
|
|
Status = NtQueryVolumeInformationFile(
|
|
hFile,
|
|
&IoStatus,
|
|
&DeviceInformation,
|
|
sizeof(DeviceInformation),
|
|
FileFsDeviceInformation
|
|
);
|
|
|
|
if ( NT_SUCCESS(Status) &&
|
|
(DeviceInformation.Characteristics & FILE_REMOTE_DEVICE) ) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::IsEncryptedFile, private
|
|
//
|
|
// Synopsis: queries the file handle to see if it's encrypted
|
|
//
|
|
// Arguments: [dwFSInit] -- Flags. Possible values:
|
|
// FSINIT_UNMARSHAL - This is an unmarshal.
|
|
//
|
|
// Returns: TRUE - if the file is encrypted on NTFS
|
|
// FALSE - if we can't tell
|
|
//
|
|
// History: 05-Apr-2000 HenryLee Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CFileStream::IsEncryptedFile ()
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
FILE_BASIC_INFORMATION BasicInformation;
|
|
|
|
Status = NtQueryInformationFile(
|
|
_hFile,
|
|
&IoStatus,
|
|
&BasicInformation,
|
|
sizeof(BasicInformation),
|
|
FileBasicInformation
|
|
);
|
|
|
|
if ( NT_SUCCESS(Status) &&
|
|
(BasicInformation.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) )
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Init_MemoryMap, private
|
|
//
|
|
// Synopsis: Creates and Views a memory map for the current file.
|
|
// Used by First open and Unmarshal.
|
|
//
|
|
// Arguments: [dwFSInit] -- Flags. Possible values:
|
|
// FSINIT_UNMARSHAL - This is an unmarshal.
|
|
//
|
|
// Returns: S_OK - When the mapping is added to the address space.
|
|
// E_FAIL - When the global object says not to map the file.
|
|
// E_OUTOFMEMORY - When the attempt to map the file failed.
|
|
//
|
|
// Private Effect: Sets _hMapObject
|
|
// Sets _pbBaseAddr
|
|
//
|
|
// Note: The field _pbBaseAddr is check before each use of the memory map.
|
|
//
|
|
// History: 19-Oct-96 BChapman Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CFileStream::Init_MemoryMap(DWORD dwFSInit)
|
|
{
|
|
LUID luid;
|
|
WCHAR pwcMapName[MAPNAME_MAXLEN];
|
|
DWORD dwPageFlags, dwMapFlags;
|
|
ULONG cbFileSize;
|
|
ULONG cbFileSizeHigh;
|
|
ULONG cbViewSize;
|
|
ULONG cbCommitedSize;
|
|
SCODE sc;
|
|
BOOL fMakeStub = FALSE;
|
|
|
|
filestDebug((DEB_ITRACE, "In Init_MemoryMap(0x%x)\n", dwFSInit));
|
|
|
|
if (_pgfst->GetStartFlags() & RSF_NO_BUFFERING)
|
|
fsErr (EH_Err, STG_E_INVALIDFUNCTION);
|
|
|
|
cbFileSize = GetFileSize(_hFile, &cbFileSizeHigh);
|
|
if (cbFileSize == MAX_ULONG && NO_ERROR != GetLastError())
|
|
fsErr(EH_Err, LAST_STG_SCODE)
|
|
|
|
if (cbFileSizeHigh > 0)
|
|
fsErr (EH_Err, STG_E_DOCFILETOOLARGE);
|
|
|
|
// disallow remote opens except for read, deny-write for NT4 compatiblity
|
|
if ((P_WRITE(_pgfst->GetDFlags()) || !P_DENYWRITE(_pgfst->GetDFlags())) &&
|
|
DfIsRemoteFile (_hFile))
|
|
fsErr (EH_Err, STG_E_INVALIDFUNCTION);
|
|
|
|
filestDebug((DEB_MAP, "File=%2x Init_MemoryMap filesize = %x\n",
|
|
_hFile, cbFileSize));
|
|
|
|
#if DBG==1
|
|
//
|
|
// DEBUG ASSERT
|
|
// If the file is a temp file it must be delete on release.
|
|
// The performance of mapped temp files that are not DELETE_ON_CLOSE
|
|
// is very slow on FAT.
|
|
//
|
|
if(RSF_TEMPFILE & GetStartFlags())
|
|
fsAssert(RSF_DELETEONRELEASE & GetStartFlags());
|
|
#endif
|
|
|
|
//
|
|
// This routine makes two kinds of mapping.
|
|
// 1) Writeable mappings over filesystem files that grow.
|
|
// 2) Readonly mappings over filesystem files that are fixed size.
|
|
//
|
|
//
|
|
// If we are opening for writing then grow the mapping upto
|
|
// some limit so there is room for writes to extend the file.
|
|
// If we are opening for reading then the mapping is the same
|
|
// size as the original file.
|
|
//
|
|
if(_pgfst->GetDFlags() & DF_WRITE)
|
|
{
|
|
dwPageFlags = PAGE_READWRITE;
|
|
dwMapFlags = FILE_MAP_WRITE;
|
|
//
|
|
// Nt can't Memory map a zero length file, so if the file is
|
|
// zero length and we are open for writing then grow the file
|
|
// to one sector before we create the memory map. But don't set
|
|
// the MappedFileSize or MappedCommitSize because the docfile
|
|
// code uses filesize==0 to determine if this is a create or an
|
|
// open. So it is important to not expose the fact that we grew
|
|
// the file.
|
|
//
|
|
if (0 == cbFileSize)
|
|
{
|
|
//Grow the file to 512 bytes in the create path - in the
|
|
//open path we don't want to change the file size.
|
|
//For the open path, don't map it - we'll fail later with
|
|
//STG_E_INVALIDHEADER.
|
|
if ((GetStartFlags() & RSF_CREATE) ||
|
|
(GetStartFlags() & RSF_TEMPFILE))
|
|
{
|
|
if (SUCCEEDED(MakeFileStub()))
|
|
fMakeStub = TRUE;
|
|
}
|
|
else
|
|
fsErr(EH_Err, E_OUTOFMEMORY);
|
|
}
|
|
|
|
if(cbFileSize < MINIMUM_MAPPING_SIZE/2)
|
|
cbViewSize = MINIMUM_MAPPING_SIZE;
|
|
else
|
|
cbViewSize = cbFileSize * 2;
|
|
}
|
|
else
|
|
{
|
|
dwPageFlags = PAGE_READONLY;
|
|
dwMapFlags = FILE_MAP_READ;
|
|
cbViewSize = cbFileSize;
|
|
}
|
|
|
|
//
|
|
// Get the mapping object. Either open the existing one for this
|
|
// file. Or if this is the first open for this "DF context" then
|
|
// create a new one.
|
|
//
|
|
if(NULL == _pgfst->GetMappingName())
|
|
{
|
|
//
|
|
// If this is a first open for this "context" then we won't have
|
|
// a name and the Unmarshaling flag should be FALSE.
|
|
//
|
|
//fsAssert(!(dwFSInit & FSINIT_UNMARSHAL));
|
|
|
|
//
|
|
// Create a new unique name for the Mapping.
|
|
//
|
|
AllocateLocallyUniqueId(&luid);
|
|
StringCbPrintfW (pwcMapName, sizeof(pwcMapName),
|
|
MAPNAME_FORMAT, luid.HighPart, luid.LowPart);
|
|
_pgfst->SetMappingName(pwcMapName);
|
|
|
|
//
|
|
// Do not map very large files since they can consume
|
|
// too much virtual memory
|
|
//
|
|
MEMORYSTATUS memstatus;
|
|
GlobalMemoryStatus (&memstatus);
|
|
if (cbFileSize > memstatus.dwTotalPhys / 2 ||
|
|
memstatus.dwAvailVirtual < DOCFILE_SM_LIMIT / 2)
|
|
fsErr(EH_Err, E_OUTOFMEMORY);
|
|
|
|
filestDebug((DEB_MAP,
|
|
"File=%2x New MappingName='%ws' FileName='%ws'\n",
|
|
_hFile, pwcMapName, _pgfst->GetName() ));
|
|
|
|
|
|
//
|
|
// Create the Mapping. This only covers the orignal file size.
|
|
// RESERVED uncommitted extensions are done in MapView.
|
|
//
|
|
_hMapObject = CreateFileMappingW(_hFile,
|
|
NULL, // default Security Descriptor.
|
|
dwPageFlags,
|
|
0, 0, // Creation size of 0 means The Entire File.
|
|
_pgfst->GetMappingName()
|
|
);
|
|
|
|
//
|
|
// This mapping is new and did not exist previously.
|
|
//
|
|
fsAssert(ERROR_ALREADY_EXISTS != GetLastError());
|
|
|
|
//
|
|
// Record the size of the file (also the commited region of the map)
|
|
// We waited to record the size until after the mapping is created.
|
|
// A seperate open could have shortened the file in the time between
|
|
// the top of this routine and here. But SetEndOfFile is disallowed
|
|
// once a file mapping is active.
|
|
// Note: Watch out for the case where the file is logically zero size
|
|
// but we grew it a little so we could memory map it.
|
|
//
|
|
if(0 != cbFileSize)
|
|
{
|
|
cbFileSize = GetFileSize(_hFile, &cbFileSizeHigh);
|
|
if (cbFileSize == MAX_ULONG && NO_ERROR != GetLastError())
|
|
{
|
|
sc = LAST_STG_SCODE;
|
|
if (_hMapObject != NULL)
|
|
{
|
|
CloseHandle (_hMapObject);
|
|
_hMapObject = NULL;
|
|
}
|
|
fsErr(EH_Err, sc)
|
|
}
|
|
fsAssert (cbFileSizeHigh == 0);
|
|
_pgfst->SetMappedFileSize(cbFileSize);
|
|
_pgfst->SetMappedCommitSize(cbFileSize);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If the global object already has a mapping name then this must
|
|
// be an unmarshal. Or we could be in the 2nd init of a scratch
|
|
// file that is "demanded" after it was first marshaled.
|
|
//
|
|
fsAssert( (dwFSInit & FSINIT_UNMARSHAL)
|
|
|| (GetStartFlags() & RSF_SCRATCH) );
|
|
|
|
filestDebug((DEB_MAP,
|
|
"File=%2x UnMarshal MappingName='%ws' FileName='%ws'\n",
|
|
_hFile, _pgfst->GetMappingName(),
|
|
_pgfst->GetName() ));
|
|
|
|
// If the global object says the mapping is off,
|
|
// then some other instance of this FileStream has declared
|
|
// the mapping unuseable and no other instance should map it either.
|
|
//
|
|
if( ! _pgfst->TestMapState(FSTSTATE_MAPPED))
|
|
{
|
|
filestDebug((DEB_MAP, "Global Flag says Don't Map.\n"));
|
|
filestDebug((DEB_ITRACE, "Out Init_MemoryMap() => %lx\n", E_FAIL));
|
|
return E_FAIL;
|
|
}
|
|
|
|
_hMapObject = OpenFileMapping(dwMapFlags,
|
|
FALSE, // Don't Inherit
|
|
_pgfst->GetMappingName()
|
|
);
|
|
}
|
|
|
|
if (NULL == _hMapObject)
|
|
{
|
|
filestDebug((DEB_IWARN|DEB_MAP,
|
|
"File=%2x Create FileMapping '%ws' for "
|
|
"filename '%ws' failed, error=0x%x\n",
|
|
_hFile,
|
|
_pgfst->GetMappingName(),
|
|
_pgfst->GetName(),
|
|
GetLastError()));
|
|
fsErr(EH_Err, E_OUTOFMEMORY);
|
|
}
|
|
|
|
filestDebug((DEB_MAP,
|
|
"File=%2x Map=%2x filesize=0x%06x, commitsize=0x%06x\n",
|
|
_hFile, _hMapObject,
|
|
_pgfst->GetMappedFileSize(),
|
|
_pgfst->GetMappedCommitSize()));
|
|
|
|
//
|
|
// Add the file map to the process' address space.
|
|
//
|
|
if(FAILED(MapView(cbViewSize, dwPageFlags, dwFSInit)))
|
|
{
|
|
TurnOffAllMappings();
|
|
fsErr(EH_Err, E_OUTOFMEMORY);
|
|
}
|
|
return S_OK;
|
|
|
|
EH_Err:
|
|
//
|
|
// If we can't get a mapping then set a flag so no other marshal's
|
|
// of this context should try either.
|
|
//
|
|
_pgfst->ResetMapState(FSTSTATE_MAPPED);
|
|
|
|
if (fMakeStub) // try to reset the filesize back to 0
|
|
{
|
|
SetFilePointer(_hFile, 0, NULL, FILE_BEGIN);
|
|
SetEndOfFile(_hFile);
|
|
}
|
|
|
|
filestDebug((DEB_ITRACE, "Out Init_MemoryMap() => %lx\n", sc));
|
|
return sc;
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::MakeFileStub, private
|
|
//
|
|
// Synopsis: Takes a zero length file and makes it 512 bytes. This is
|
|
// needed to support NT file mapping. The important effects
|
|
// are that it does NOT set the saved file size.
|
|
//
|
|
// History: 09-Mar-96 Bchapman Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
HRESULT CFileStream::MakeFileStub()
|
|
{
|
|
SCODE sc=S_OK;
|
|
#if DBG == 1
|
|
ULONG cbFileSizeHigh;
|
|
#endif
|
|
|
|
|
|
fsAssert(0 == GetFilePointer());
|
|
#if DBG == 1
|
|
fsAssert(0 == GetFileSize(_hFile, &cbFileSizeHigh));
|
|
fsAssert(0 == cbFileSizeHigh);
|
|
#endif
|
|
|
|
SetFilePointer(_hFile, 512, NULL, FILE_BEGIN);
|
|
|
|
if(FALSE == SetEndOfFile(_hFile))
|
|
sc = LAST_STG_SCODE;
|
|
|
|
SetFilePointer(_hFile, 0, NULL, FILE_BEGIN);
|
|
|
|
return sc;
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::MapView, private
|
|
//
|
|
// Synopsis: Maps a view of an existing File Mapping.
|
|
//
|
|
// Arguments: Size of the mapping, including extra space for RESERVED
|
|
// pages that can be added with VIrtualAlloc.
|
|
//
|
|
// Returns: S_OK - When the mapping is added to the address space.
|
|
// E_OUTOFMEMORY - When the attempt to map the file failed.
|
|
//
|
|
// Private Effect: Sets _pbBaseAddr
|
|
// Sets _cbViewSize
|
|
//
|
|
// Note:
|
|
//
|
|
// History: 25-Nov-96 Bchapman Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
HRESULT CFileStream::MapView(
|
|
SIZE_T cbViewSize,
|
|
DWORD dwPageFlags,
|
|
DWORD dwFSInit)
|
|
{
|
|
filestDebug((DEB_ITRACE, "In MapView(0x%06x, 0x%x)\n",
|
|
cbViewSize, dwPageFlags));
|
|
|
|
PVOID pvBase=NULL;
|
|
LARGE_INTEGER SectionOffset;
|
|
NTSTATUS Status;
|
|
DWORD dwAllocationType = 0;
|
|
|
|
LISet32(SectionOffset, 0);
|
|
|
|
// confirm that we are mapping the whole file.
|
|
fsAssert(cbViewSize >= _pgfst->GetMappedFileSize());
|
|
|
|
if((PAGE_READWRITE & dwPageFlags))
|
|
dwAllocationType = MEM_RESERVE; // RESERVE uncommited pages.
|
|
|
|
Status = NtMapViewOfSection(
|
|
_hMapObject,
|
|
GetCurrentProcess(),
|
|
&pvBase, // returned pointer to base of map. If the
|
|
// initial value is non-Zero it is a "Hint".
|
|
0L, // ZeroBits: see ntos\mm\mapview.c
|
|
0L, // Commit: amount to initially Commit.
|
|
&SectionOffset, // Offset in file for base of Map.
|
|
&cbViewSize, // Size of VirtAddr chunk to reserve.
|
|
ViewShare,
|
|
dwAllocationType,
|
|
dwPageFlags);
|
|
if(NT_ERROR(Status))
|
|
{
|
|
filestDebug((DEB_WARN|DEB_MAP,
|
|
"File=%2x NtMapViewOfSection Failed, viewsize=0x%06x, "
|
|
"dwPageFlags=%x, status=0x%x\n",
|
|
_hFile, _cbViewSize, dwPageFlags, Status));
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
_pgfst->SetMapState(FSTSTATE_MAPPED);
|
|
_pbBaseAddr = (LPBYTE)pvBase;
|
|
_cbViewSize = (ULONG)cbViewSize;
|
|
|
|
filestDebug((DEB_MAP,
|
|
"File=%2x Attaching Map: address=0x%07x, viewsz=0x%06x, "
|
|
"commitsz=0x%06x\n",
|
|
_hFile, _pbBaseAddr, _cbViewSize,
|
|
_pgfst->GetMappedCommitSize()));
|
|
|
|
filestDebug((DEB_ITRACE, "Out MapView => %lx\n", S_OK));
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::TurnOffMapping, private
|
|
//
|
|
// Synopsis: Turns off the use of file mapping
|
|
//
|
|
// Private Effect: Clears _pbBaseAddr
|
|
// Clears _hMapObject
|
|
//
|
|
// History: 31-Aug-96 DrewB Created
|
|
// 22-Oct-96 BChapman Trim the End of File.
|
|
// Nov-96 BChapman Rewrote
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
SCODE CFileStream::TurnOffMapping(BOOL fFlush)
|
|
{
|
|
filestDebug((DEB_ITRACE, "In TurnOffMapping(%d)\n", fFlush));
|
|
|
|
//
|
|
// We want to be make sure the file mapping was in use. Otherwise
|
|
// the "MappedFileSize" may not be valid when we truncate the file
|
|
// below. Which destroys data.
|
|
//
|
|
// Checking for the map object is better than checking the base pointer
|
|
// because this routine can be called when remapping the view.
|
|
// Then the base pointer will be NULL but we still have a map object.
|
|
//
|
|
if(NULL == _hMapObject)
|
|
return S_OK;
|
|
|
|
//
|
|
// Release the view of the map. And release the Map Object.
|
|
// Don't exit on errors. Do as much as we can.
|
|
//
|
|
if(NULL != _pbBaseAddr)
|
|
{
|
|
filestDebug((DEB_MAP, "File=%2x Detaching Map 0x%07x\n",
|
|
_hFile, _pbBaseAddr));
|
|
// the redirector cannot handle a dirty file mapping followed by
|
|
// ReadFile calls; local filesystems have a single coherent cache
|
|
// fortunately, we no longer map docfiles over the redirector
|
|
if (fFlush)
|
|
fsVerify(FlushViewOfFile(_pbBaseAddr, 0));
|
|
fsVerify(UnmapViewOfFile(_pbBaseAddr));
|
|
_pbBaseAddr = NULL;
|
|
}
|
|
|
|
if(NULL != _hMapObject)
|
|
{
|
|
fsVerify(CloseHandle(_hMapObject));
|
|
_hMapObject = NULL;
|
|
}
|
|
|
|
filestDebug((DEB_MAP, "File=%2x TurnOffMapping RefCount=%x\n",
|
|
_hFile, _pgfst->CountContexts()));
|
|
|
|
//
|
|
// If file was open for writing, and this is the last/only instance
|
|
// of this "DF context" then truncate the file to the proper size.
|
|
// We do this when the file may have grown by commiting pages on
|
|
// the end of the memory map, the system will grow the file in
|
|
// whole MMU page units, so we may need to trim the extra off.
|
|
//
|
|
// Don't do this in the case of multiple 'seperate' writers. Last
|
|
// close wins on the file size and that is not good.
|
|
//
|
|
// Don't fail on errors. We are only trimming the EOF.
|
|
//
|
|
// Bugfix Feb '98 BChapman
|
|
// Don't Set EOF unless the map was written to or SetSize was called.
|
|
// Many people were opening READ/WRITE, not writing, and expecting the
|
|
// Mod. time to remain unchanged.
|
|
//
|
|
if( (_pgfst->CountContexts() == 1)
|
|
&& (_pgfst->GetDFlags() & DF_WRITE)
|
|
&& (_pgfst->TestMapState(FSTSTATE_DIRTY) ) )
|
|
{
|
|
#ifdef LARGE_DOCFILE
|
|
ULONGLONG ret;
|
|
#else
|
|
ULONG ret;
|
|
#endif
|
|
ULONG cbFileSize = _pgfst->GetMappedFileSize();
|
|
|
|
ret = SeekTo( cbFileSize );
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
if (ret == MAX_ULONGLONG) // 0xFFFFFFFFFFFFFFFF
|
|
#else
|
|
if (ret == 0xFFFFFFFF)
|
|
#endif
|
|
{
|
|
filestDebug((DEB_ERROR,
|
|
"File Seek in TurnOffMapping failed err=0x%x\n",
|
|
GetLastError()));
|
|
return S_OK;
|
|
}
|
|
|
|
filestDebug((DEB_MAP,
|
|
"File=%2x TurnOffMapping->SetEndOfFile 0x%06x\n",
|
|
_hFile, cbFileSize));
|
|
|
|
if(FALSE == SetEndOfFile(_hFile))
|
|
{
|
|
filestDebug((DEB_WARN,
|
|
"File=%2x SetEndOfFile in TurnOffMapping Failed. "
|
|
"file may still be open 'seperately'\n",
|
|
_hFile));
|
|
}
|
|
}
|
|
|
|
filestDebug((DEB_ITRACE, "Out TurnOffMapping => S_OK\n"));
|
|
return S_OK;
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::ExtendMapView, private
|
|
//
|
|
// Synopsis: Unmaps the Map object and creates a bigger View based
|
|
// on the new request size. The cbRequest is the requested
|
|
// real size. So we make a view larger than that to leave room.
|
|
//
|
|
// Returns: S_OK - When the mapping is added to the address space.
|
|
// E_OUTOFMEMORY - When the attempt to map the file failed.
|
|
//
|
|
// History: 20-Feb-97 BChapman Created.
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
#define MAX_MAPVIEW_GROWTH (5*1024*1024)
|
|
|
|
SCODE CFileStream::ExtendMapView(ULONG cbRequest)
|
|
{
|
|
ULONG dwPageFlag;
|
|
ULONG cbNewView;
|
|
|
|
//
|
|
// Confirm that we were not needlessly called.
|
|
//
|
|
fsAssert(cbRequest > _cbViewSize);
|
|
|
|
//
|
|
// If the mapping is small, then grow it by doubling.
|
|
// If the mapping is big add a fixed amount.
|
|
//
|
|
if(cbRequest < MAX_MAPVIEW_GROWTH)
|
|
cbNewView = cbRequest * 2;
|
|
else
|
|
cbNewView = cbRequest + MAX_MAPVIEW_GROWTH;
|
|
|
|
//
|
|
// Someone else may have grown the mapping a huge amount.
|
|
// If that is the case then increase the New View to include the
|
|
// entire existing file.
|
|
//
|
|
if(cbNewView < _pgfst->GetMappedFileSize())
|
|
cbNewView = _pgfst->GetMappedFileSize();
|
|
|
|
filestDebug((DEB_MAP,
|
|
"File=%2x Mapping view is being grown from 0x%06x to 0x%06x\n",
|
|
_hFile, _cbViewSize, cbNewView));
|
|
|
|
//
|
|
// Unmap the View and re-map it at more than the currenly needed size.
|
|
//
|
|
filestDebug((DEB_MAP, "File=%2x Detaching Map 0x%07x to grow it.\n",
|
|
_hFile, _pbBaseAddr));
|
|
fsVerify(UnmapViewOfFile(_pbBaseAddr));
|
|
_pbBaseAddr = NULL;
|
|
|
|
//
|
|
// We know the mode must be read/write because we are growing.
|
|
//
|
|
if(FAILED(MapView(cbNewView, PAGE_READWRITE, 0)))
|
|
{
|
|
TurnOffAllMappings();
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
// Helper routine to round up memory request in a consistant manner.
|
|
//+--------------------------------------------------------------
|
|
|
|
ULONG BlockUpCommit(ULONG x) {
|
|
return((x+COMMIT_BLOCK-1) & (~(COMMIT_BLOCK-1)));
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::MakeFileMapAddressValidWorker, private
|
|
//
|
|
// Synopsis: Commits reserved pages that extend a writable
|
|
// Memory Mapped file. Used by WriteAt and SetSize.
|
|
//
|
|
// Returns: S_OK - When the mapping is added to the address space.
|
|
// E_OUTOFMEMORY - When the attempt to map the file failed.
|
|
//
|
|
// History: 31-Oct-96 BChapman Created.
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
SCODE CFileStream::MakeFileMapAddressValidWorker(
|
|
ULONG cbRequested,
|
|
ULONG cbCommitedSize)
|
|
{
|
|
ULONG cbNeeded;
|
|
ULONG iStart, cbGrown;
|
|
SCODE sc;
|
|
|
|
filestDebug((DEB_ITRACE|DEB_MAP,
|
|
"File=%2x MakeFileMappingAddressValidWorker(0x%06x)\n",
|
|
_hFile, cbRequested));
|
|
|
|
// Assert we are not completely confused.
|
|
fsAssert(IsFileMapped());
|
|
fsAssert(cbCommitedSize >= _pgfst->GetMappedFileSize());
|
|
|
|
// Assert we were called correctly.
|
|
fsAssert(cbCommitedSize == _pgfst->GetMappedCommitSize());
|
|
fsAssert(cbRequested > cbCommitedSize);
|
|
|
|
//
|
|
// We allocate pages in clumps to cut down on little VirtualAlloc calls.
|
|
//
|
|
cbNeeded = BlockUpCommit(cbRequested);
|
|
|
|
//
|
|
// If the needed commit size is bigger than the view window, then
|
|
// make the view size bigger first.
|
|
//
|
|
if(FAILED(CheckMapView(cbNeeded)))
|
|
return E_OUTOFMEMORY;
|
|
|
|
//
|
|
// Now commit the new pages.
|
|
//
|
|
iStart = cbCommitedSize;
|
|
cbGrown = cbNeeded - cbCommitedSize;
|
|
|
|
filestDebug((DEB_MAP,
|
|
"File=%2x VirtualAlloc map=0x%07x[0x%06x] + 0x%06x(0x%06x)\n",
|
|
_hFile, _pbBaseAddr,
|
|
iStart, cbGrown, cbNeeded));
|
|
|
|
if(NULL==VirtualAlloc(
|
|
(void*)&_pbBaseAddr[iStart],
|
|
cbGrown,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE))
|
|
{
|
|
// Ran out of Virtual Memory or Filesystem disk space.
|
|
filestDebug((DEB_ERROR|DEB_MAP,
|
|
"File=%2x VirtualAlloc(%x + %x) 1st Failure=%x.\n",
|
|
_hFile,
|
|
&_pbBaseAddr[iStart],
|
|
cbGrown,
|
|
GetLastError()));
|
|
|
|
//
|
|
// If the VirutalAlloc failed we try again. The original
|
|
// request was rounded up to some large block size so there
|
|
// is a slim hope that we might get just what we need.
|
|
//
|
|
cbNeeded = cbRequested;
|
|
cbGrown = cbNeeded - cbCommitedSize;
|
|
|
|
filestDebug((DEB_MAP,
|
|
"File=%2x Retry VirtualAlloc map=0x%07x[0x%06x] + 0x%06x(0x%06x)\n",
|
|
_hFile, _pbBaseAddr,
|
|
iStart, cbGrown, cbNeeded));
|
|
|
|
if(NULL==VirtualAlloc(
|
|
(void*)&_pbBaseAddr[iStart],
|
|
cbGrown,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE))
|
|
{
|
|
// Ran out of Virtual Memory or Filesystem disk space.
|
|
filestDebug((DEB_ERROR|DEB_MAP,
|
|
"File=%2x VirtualAlloc(%x + %x) 2nd Failure=%x.\n",
|
|
_hFile,
|
|
&_pbBaseAddr[iStart],
|
|
cbGrown,
|
|
GetLastError()));
|
|
|
|
TurnOffMapping(TRUE);
|
|
if (_pgfst != NULL)
|
|
_pgfst->ResetMapState(FSTSTATE_MAPPED);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
_pgfst->SetMappedCommitSize(cbNeeded);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#endif // Memory Mapped File Support
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::InitFromHandle, public
|
|
//
|
|
// Synopsis: Creates a filestream by duping an existing handle
|
|
//
|
|
// Arguments: [h] - Handle
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 09-Feb-94 DrewB Created
|
|
//
|
|
// Notes: Intended only for creating a temporary ILockBytes on a file;
|
|
// does not create a true CFileStream; there is no
|
|
// global filestream, no access flags, etc.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CFileStream::InitFromHandle(HANDLE h)
|
|
{
|
|
SCODE sc;
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::InitFromHandle:%p(%p)\n",
|
|
this, h));
|
|
|
|
if (!DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(), &_hFile,
|
|
0, FALSE, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
sc = LAST_STG_SCODE;
|
|
}
|
|
else
|
|
{
|
|
sc = S_OK;
|
|
}
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::InitFromHandle\n"));
|
|
return sc;
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::~CFileStream, public
|
|
//
|
|
// Synopsis: Destructor
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
CFileStream::~CFileStream(void)
|
|
{
|
|
filestDebug((DEB_ITRACE, "In CFileStream::~CFileStream()\n"));
|
|
fsAssert(_cReferences == 0);
|
|
_sig = CFILESTREAM_SIGDEL;
|
|
|
|
CheckSeekPointer();
|
|
|
|
if (INVALID_FH != _hPreDuped)
|
|
fsVerify(CloseHandle(_hPreDuped));
|
|
|
|
if (INVALID_FH != _hFile)
|
|
{
|
|
filestDebug((DEB_INFO, "~CFileStream %p handle %p thread %lX\n",
|
|
this, _hFile, GetCurrentThreadId()));
|
|
|
|
//
|
|
// A CFileStream normally _always_ had a global object connected
|
|
// to it. But due to the abuses of the Debug Logger we need to
|
|
// check this here. Also see "CFileStream::InitFromHandle"
|
|
//
|
|
if(_pgfst)
|
|
TurnOffMapping(FALSE);
|
|
|
|
fsVerify(CloseHandle(_hFile));
|
|
#ifdef ASYNC
|
|
if ((_pgfst) &&
|
|
(_pgfst->GetTerminationStatus() == TERMINATED_ABNORMAL))
|
|
{
|
|
WCHAR *pwcsName;
|
|
SCODE sc = GetName(&pwcsName);
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
DeleteTheFile(pwcsName);
|
|
TaskMemFree(pwcsName);
|
|
}
|
|
}
|
|
#endif //ASYNC
|
|
}
|
|
if (_hReserved != INVALID_FH)
|
|
{
|
|
filestDebug((DEB_INFO, "~CFileStream reserved %p "
|
|
"handle %p thread %lX\n",
|
|
this, _hReserved, GetCurrentThreadId()));
|
|
fsVerify(CloseHandle(_hReserved));
|
|
_hReserved = INVALID_FH;
|
|
}
|
|
|
|
if (_pgfst)
|
|
{
|
|
_pgfst->Remove(this);
|
|
if (_pgfst->HasName())
|
|
{
|
|
if (0 == _pgfst->CountContexts())
|
|
{
|
|
// Delete zero length files also. A zero length file
|
|
// is not a valid docfile so don't leave them around
|
|
if (_pgfst->GetStartFlags() & RSF_DELETEONRELEASE)
|
|
{
|
|
// This is allowed to fail if somebody
|
|
// else has the file open
|
|
DeleteTheFile(_pgfst->GetName());
|
|
}
|
|
}
|
|
}
|
|
_pgfst->Release();
|
|
}
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::~CFileStream\n"));
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::ReadAt, public
|
|
//
|
|
// Synopsis: Reads bytes at a specific point in a stream
|
|
//
|
|
// Arguments: [ulPosition] - Offset in file to read at
|
|
// [pb] - Buffer
|
|
// [cb] - Count of bytes to read
|
|
// [pcbRead] - Return of bytes read
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies: [pcbRead]
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::ReadAt(ULARGE_INTEGER ulPosition,
|
|
VOID *pb,
|
|
ULONG cb,
|
|
ULONG *pcbRead)
|
|
{
|
|
SCODE sc;
|
|
LONG lHigh = ULIGetHigh(ulPosition);
|
|
ULONG uLow = ULIGetLow(ulPosition);
|
|
|
|
#ifdef ASYNC
|
|
fsAssert((_ppc == NULL) || (_ppc->HaveMutex()));
|
|
#endif
|
|
fsAssert((!IsFileMapped() || lHigh == 0) &&
|
|
aMsg("High dword other than zero passed to filestream."));
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::ReadAt("
|
|
"%x:%x, %p, %x, %p)\n", ULIGetHigh(ulPosition),
|
|
ULIGetLow(ulPosition), pb, cb, pcbRead));
|
|
|
|
CheckSeekPointer();
|
|
fsAssert(_hFile != INVALID_FH);
|
|
*pcbRead = 0;
|
|
|
|
#ifdef ASYNC
|
|
DWORD dwTerminate;
|
|
dwTerminate = _pgfst->GetTerminationStatus();
|
|
if (dwTerminate == TERMINATED_ABNORMAL)
|
|
{
|
|
sc = STG_E_INCOMPLETE;
|
|
}
|
|
else if ((dwTerminate != TERMINATED_NORMAL) &&
|
|
#ifdef LARGE_DOCFILE
|
|
(ulPosition.QuadPart + cb > _pgfst->GetHighWaterMark()))
|
|
#else
|
|
(uLow + cb > _pgfst->GetHighWaterMark()))
|
|
#endif
|
|
{
|
|
*pcbRead = 0;
|
|
#ifdef LARGE_DOCFILE
|
|
_pgfst->SetFailurePoint(cb + ulPosition.QuadPart);
|
|
#else
|
|
_pgfst->SetFailurePoint(cb + uLow);
|
|
#endif
|
|
sc = E_PENDING;
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
if(!IsFileMapped())
|
|
{ // Read w/ ReadFile()
|
|
#ifdef LARGE_DOCFILE
|
|
sc = ReadAt_FromFile(ulPosition.QuadPart, pb, cb, pcbRead);
|
|
#else
|
|
sc = ReadAt_FromFile(uLow, pb, cb, pcbRead);
|
|
#endif
|
|
}
|
|
else
|
|
{ // Read from Map
|
|
sc = ReadAt_FromMap(uLow, pb, cb, pcbRead);
|
|
if (!SUCCEEDED(sc))
|
|
#ifdef LARGE_DOCFILE
|
|
sc = ReadAt_FromFile(ulPosition.QuadPart, pb, cb, pcbRead);
|
|
#else
|
|
sc = ReadAt_FromFile(uLow, pb, cb, pcbRead);
|
|
#endif
|
|
}
|
|
|
|
#ifdef ASYNC
|
|
}
|
|
#endif
|
|
|
|
olLowLog(("STGIO - Read : %8x at %8x, %8d, %8d <--\n", _hFile, uLow, cb, *pcbRead));
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::ReadAt => %x\n", sc));
|
|
|
|
CheckSeekPointer();
|
|
return sc;
|
|
}
|
|
|
|
|
|
#ifdef USE_FILEMAPPING
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::ReadAt_FromMap, private
|
|
//
|
|
// Synopsis: Reads bytes at a specific point from the file mapping
|
|
//
|
|
// Arguments: [iPosition] - Offset in file
|
|
// [pb] - Buffer
|
|
// [cb] - Count of bytes to read
|
|
// [pcbRead] - Return of bytes read
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies: [pcbRead]
|
|
//
|
|
// History: 16-Jan-97 BChapman Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
SCODE CFileStream::ReadAt_FromMap(
|
|
ULONG iPosition,
|
|
VOID *pb,
|
|
ULONG cb,
|
|
ULONG *pcbRead)
|
|
{
|
|
SCODE sc = S_OK;
|
|
ULONG cbRead=0;
|
|
ULONG cbFileSize = _pgfst->GetMappedFileSize();
|
|
//
|
|
// If any of the read is before the End of File then
|
|
// we can read something. Reads at EOF
|
|
//
|
|
*pcbRead = 0;
|
|
if (iPosition < cbFileSize)
|
|
{
|
|
ULONG cbTail;
|
|
|
|
filestDebug((DEB_MAPIO,
|
|
"File=%2x Read MapFile @ 0x%06x, 0x%04x, bytes\n",
|
|
_hFile, iPosition, cb));
|
|
|
|
//
|
|
// Possibly shorted the read request to fit inside
|
|
// the logical End of File.
|
|
//
|
|
cbTail = cbFileSize - iPosition;
|
|
|
|
if (cb < cbTail)
|
|
cbRead = cb;
|
|
else
|
|
cbRead = cbTail;
|
|
|
|
if(cb != cbRead)
|
|
{
|
|
filestDebug((DEB_MAPIO,
|
|
"File=%2x\t\t(Short Read 0x%04x bytes)\n",
|
|
_hFile, cbRead));
|
|
}
|
|
|
|
sc = CheckMapView(iPosition+cbRead);
|
|
if (SUCCEEDED(sc))
|
|
{
|
|
__try
|
|
{
|
|
memcpy (pb, _pbBaseAddr+iPosition, cbRead);
|
|
*pcbRead = cbRead;
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
filestDebug((DEB_WARN|DEB_MAP,
|
|
"File=%2x Mapfile READFAULT Xfer 0x%x bytes @ 0x%x %x\n",
|
|
_hFile, cbRead, iPosition, GetExceptionCode()));
|
|
sc = STG_E_READFAULT;
|
|
}
|
|
}
|
|
else // lost the file mapping
|
|
sc = ReadAt_FromFile(iPosition, pb, cb, pcbRead);
|
|
}
|
|
else
|
|
{
|
|
if(cbFileSize < iPosition)
|
|
{
|
|
filestDebug((DEB_WARN|DEB_MAP,
|
|
"File=%2x Read MapFile @ 0x%x, 0x%x bytes is entirely "
|
|
"off the end @ 0x%x\n",
|
|
_hFile, iPosition, cb, cbFileSize));
|
|
|
|
fsAssert(cbFileSize > iPosition);
|
|
}
|
|
}
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::ReadAt => %x\n", sc));
|
|
return sc;
|
|
}
|
|
|
|
#endif // USE_FILEMAPPING
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::ReadAt_FromFile, private
|
|
//
|
|
// Synopsis: Reads bytes at a specific point from the file mapping
|
|
//
|
|
// Arguments: [iPosition] - Offset in file
|
|
// [pb] - Buffer
|
|
// [cb] - Count of bytes to read
|
|
// [pcbRead] - Return of bytes read
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies: [pcbRead]
|
|
//
|
|
// History: 16-Jan-97 BChapman Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
SCODE CFileStream::ReadAt_FromFile(
|
|
#ifdef LARGE_DOCFILE
|
|
ULONGLONG iPosition,
|
|
#else
|
|
ULONG iPosition,
|
|
#endif
|
|
VOID *pb,
|
|
ULONG cb,
|
|
ULONG *pcbRead)
|
|
{
|
|
SCODE sc = S_OK;
|
|
|
|
if(0 == cb)
|
|
{
|
|
*pcbRead = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
#ifndef USE_OVERLAPPED
|
|
negChk(SeekTo(iPosition));
|
|
|
|
filestDebug((DEB_FILEIO,
|
|
"File=%2x ReadFile (old code) @ 0x%06x, 0x%04x bytes\n",
|
|
_hFile, iPosition, cb));
|
|
boolChk(ReadFile(_hFile, pb, cb, pcbRead, NULL));
|
|
|
|
if(cb != *pcbRead)
|
|
{
|
|
filestDebug((DEB_FILEIO,
|
|
"File=%2x\t\t(Short read 0x%x bytes)\n",
|
|
_hFile, *pcbRead));
|
|
}
|
|
|
|
#else // ifndef USE_OVERLAPPED
|
|
if (!FilePointerEqual(iPosition))
|
|
{
|
|
OVERLAPPED Overlapped;
|
|
#ifdef LARGE_DOCFILE
|
|
LARGE_INTEGER ulPosition;
|
|
ulPosition.QuadPart = iPosition;
|
|
|
|
Overlapped.Offset = ulPosition.LowPart;
|
|
Overlapped.OffsetHigh = ulPosition.HighPart;
|
|
#else
|
|
Overlapped.Offset = iPosition;
|
|
Overlapped.OffsetHigh = 0;
|
|
#endif
|
|
Overlapped.hEvent = NULL;
|
|
|
|
filestDebug((DEB_FILEIO,
|
|
"File=%2x ReadFile (w/seek) @ 0x%06x, 0x%04x bytes\n",
|
|
_hFile, (ULONG)iPosition, cb));
|
|
if (!ReadFile(_hFile, pb, cb, pcbRead, &Overlapped))
|
|
{
|
|
if (GetLastError() != ERROR_HANDLE_EOF)
|
|
fsErr(EH_Err, LAST_STG_SCODE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
filestDebug((DEB_FILEIO,
|
|
"File=%2x ReadFile @ 0x%Lx, 0x%04x bytes\n",
|
|
_hFile, iPosition, cb));
|
|
boolChk(ReadFile(_hFile, pb, cb, pcbRead, NULL));
|
|
}
|
|
|
|
if(cb != *pcbRead)
|
|
{
|
|
filestDebug((DEB_FILEIO,
|
|
"File=%2x\t\t(Short read 0x%04x bytes)\n",
|
|
_hFile, *pcbRead));
|
|
}
|
|
#endif // USE_OVERLAPPED
|
|
|
|
// if 0 bytes were read, the seek pointer has not changed
|
|
if (*pcbRead > 0)
|
|
SetCachedFilePointer(iPosition + *pcbRead);
|
|
|
|
return S_OK;
|
|
|
|
EH_Err:
|
|
filestDebug((DEB_ERROR|DEB_FILEIO, "ReadAt_FromFile Error = %x\n", sc));
|
|
return sc;
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::WriteAt, public
|
|
//
|
|
// Synopsis: Writes bytes at a specific point in a stream
|
|
//
|
|
// Arguments: [ulPosition] - Offset in file
|
|
// [pb] - Buffer
|
|
// [cb] - Count of bytes to write
|
|
// [pcbWritten] - Return of bytes written
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies: [pcbWritten]
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
STDMETHODIMP CFileStream::WriteAt(ULARGE_INTEGER ulPosition,
|
|
VOID const *pb,
|
|
ULONG cb,
|
|
ULONG *pcbWritten)
|
|
{
|
|
SCODE sc;
|
|
LONG lHigh = ULIGetHigh(ulPosition);
|
|
ULONG uLow = ULIGetLow(ulPosition);
|
|
|
|
#ifdef ASYNC
|
|
fsAssert((_ppc == NULL) || (_ppc->HaveMutex()));
|
|
#endif
|
|
#ifndef LARGE_DOCFILE
|
|
fsAssert(lHigh == 0 &&
|
|
aMsg("High dword other than zero passed to filestream."));
|
|
#endif
|
|
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::WriteAt:%p("
|
|
"%x:%x, %p, %x, %p)\n", this, ULIGetHigh(ulPosition),
|
|
ULIGetLow(ulPosition), pb, cb, pcbWritten));
|
|
|
|
#ifdef ASYNC
|
|
DWORD dwTerminate;
|
|
dwTerminate = _pgfst->GetTerminationStatus();
|
|
if (dwTerminate == TERMINATED_ABNORMAL)
|
|
{
|
|
sc = STG_E_INCOMPLETE;
|
|
}
|
|
else if ((dwTerminate == TERMINATED_NORMAL) ||
|
|
(uLow + cb <= _pgfst->GetHighWaterMark()))
|
|
{
|
|
#endif
|
|
#ifdef LARGE_DOCFILE
|
|
sc = WriteAtWorker(ulPosition, pb, cb, pcbWritten);
|
|
#else
|
|
sc = WriteAtWorker(uLow, pb, cb, pcbWritten);
|
|
#endif
|
|
#ifdef ASYNC
|
|
}
|
|
else
|
|
{
|
|
*pcbWritten = 0;
|
|
#ifdef LARGE_DOCFILE
|
|
_pgfst->SetFailurePoint(cb + ulPosition.QuadPart);
|
|
#else
|
|
_pgfst->SetFailurePoint(cb + uLow);
|
|
#endif
|
|
sc = E_PENDING;
|
|
}
|
|
|
|
#endif
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::WriteAt => %x\n", sc));
|
|
return sc;
|
|
}
|
|
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
SCODE CFileStream::WriteAtWorker(ULARGE_INTEGER ulPosition,
|
|
#else
|
|
SCODE CFileStream::WriteAtWorker(ULONG uLow,
|
|
#endif
|
|
VOID const *pb,
|
|
ULONG cb,
|
|
ULONG *pcbWritten)
|
|
{
|
|
SCODE sc;
|
|
#ifdef LARGE_DOCFILE
|
|
ULONG uLow = ulPosition.LowPart;
|
|
#endif
|
|
|
|
CheckSeekPointer();
|
|
fsAssert(_hFile != INVALID_FH);
|
|
*pcbWritten = 0;
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
fsChk(PossibleDiskFull(ulPosition.QuadPart + cb));
|
|
#else
|
|
fsChk(PossibleDiskFull(uLow + cb));
|
|
#endif
|
|
|
|
#ifdef USE_FILEMAPPING
|
|
#ifdef LARGE_DOCFILE
|
|
if (ulPosition.QuadPart + cb < MAX_ULONG/2)
|
|
MakeFileMapAddressValid(uLow + cb);
|
|
else
|
|
TurnOffMapping(TRUE);
|
|
#else
|
|
MakeFileMapAddressValid(uLow + cb);
|
|
#endif
|
|
|
|
if (IsFileMapped())
|
|
{
|
|
filestDebug((DEB_MAPIO,
|
|
"File=%2x Write Mapfile @ 0x%06x, 0x%04x bytes\n",
|
|
_hFile, uLow, cb));
|
|
|
|
__try
|
|
{
|
|
memcpy (_pbBaseAddr+uLow, pb, cb);
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
filestDebug((DEB_WARN|DEB_MAP,
|
|
"File=%2x Mapfile WRITEFAULT Xfer 0x%x bytes @ 0x%x %x\n",
|
|
_hFile, cb, uLow, GetExceptionCode()));
|
|
return STG_E_WRITEFAULT;
|
|
}
|
|
*pcbWritten = cb;
|
|
|
|
_pgfst->SetMapState(FSTSTATE_DIRTY);
|
|
|
|
if(_pgfst->GetMappedFileSize() < uLow + cb)
|
|
_pgfst->SetMappedFileSize(uLow + cb);
|
|
return S_OK;
|
|
}
|
|
#endif
|
|
|
|
#ifndef USE_OVERLAPPED
|
|
#ifdef LARGE_DOCFILE
|
|
negChk(SeekTo(ulPosition.QuadPart));
|
|
#else
|
|
negChk(SeekTo(uLow));
|
|
#endif
|
|
|
|
filestDebug((DEB_FILEIO,
|
|
"File=%2x WriteFile (old code) @ 0x%06x, 0x%04x bytes\n",
|
|
_hFile, uLow, cb));
|
|
boolChk(WriteFile(_hFile, pb, cb, pcbWritten, NULL));
|
|
#else // ifndef USE_OVERLAPPED
|
|
#ifdef LARGE_DOCFILE
|
|
if (!FilePointerEqual(ulPosition.QuadPart))
|
|
#else
|
|
if (!FilePointerEqual(uLow))
|
|
#endif
|
|
{
|
|
OVERLAPPED Overlapped;
|
|
#ifdef LARGE_DOCFILE
|
|
Overlapped.Offset = ulPosition.LowPart;
|
|
Overlapped.OffsetHigh = ulPosition.HighPart;
|
|
#else
|
|
Overlapped.Offset = uLow;
|
|
Overlapped.OffsetHigh = 0;
|
|
#endif
|
|
Overlapped.hEvent = NULL;
|
|
filestDebug((DEB_FILEIO,
|
|
"File=%2x WriteFile (w/seek) @ 0x%06x, 0x%04x bytes\n",
|
|
_hFile, uLow, cb));
|
|
boolChk(WriteFile(_hFile, pb, cb, pcbWritten,&Overlapped));
|
|
}
|
|
else
|
|
{
|
|
filestDebug((DEB_FILEIO,
|
|
"File=%2x WriteFile @ 0x%06x, 0x%04x bytes\n",
|
|
_hFile, uLow, cb));
|
|
boolChk(WriteFile(_hFile, pb, cb, pcbWritten, NULL));
|
|
}
|
|
#endif
|
|
#ifdef LARGE_DOCFILE
|
|
SetCachedFilePointer(ulPosition.QuadPart + *pcbWritten);
|
|
#else
|
|
SetCachedFilePointer(uLow + *pcbWritten);
|
|
#endif
|
|
if(_pgfst->GetMappedFileSize() < uLow + cb)
|
|
_pgfst->SetMappedFileSize(uLow + cb);
|
|
|
|
olLowLog(("STGIO - Write: %8x at %8x, %8d, %8d -->\n", _hFile, uLow, cb, *pcbWritten));
|
|
|
|
sc = S_OK;
|
|
|
|
EH_Err:
|
|
CheckSeekPointer();
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Flush, public
|
|
//
|
|
// Synopsis: Flushes buffers
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 24-Mar-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::Flush(void)
|
|
{
|
|
CheckSeekPointer();
|
|
|
|
#if WIN32 == 200
|
|
SCODE sc = S_OK;
|
|
|
|
if (_hReserved == INVALID_FH)
|
|
{
|
|
if (!DuplicateHandle(GetCurrentProcess(), _hFile, GetCurrentProcess(),
|
|
&_hReserved, 0, FALSE, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
//We couldn't get a handle, so flush everything just to be
|
|
//safe.
|
|
sc = FlushCache();
|
|
}
|
|
else
|
|
{
|
|
fsAssert(_hReserved != INVALID_FH);
|
|
fsVerify(CloseHandle(_hReserved));
|
|
_hReserved = INVALID_FH;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//In this case, we already have a duplicate of the file handle
|
|
// reserved, so close it, then reopen it again.
|
|
fsVerify(CloseHandle(_hReserved));
|
|
_hReserved = INVALID_FH;
|
|
}
|
|
|
|
if ((_hReserved == INVALID_FH) && (_grfLocal & LFF_RESERVE_HANDLE))
|
|
{
|
|
//Reacquire reserved handle.
|
|
//If this fails there isn't anything we can do about it. We'll
|
|
// try to reacquire the handle later when we really need it.
|
|
DuplicateHandle(GetCurrentProcess(), _hFile, GetCurrentProcess(),
|
|
&_hReserved, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
|
}
|
|
|
|
return ResultFromScode(sc);
|
|
#else
|
|
|
|
// Otherwise on NT, the file system does the right thing, we think.
|
|
return S_OK;
|
|
#endif
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::FlushCache, public
|
|
//
|
|
// Synopsis: Flushes buffers
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 12-Feb-93 AlexT Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::FlushCache(void)
|
|
{
|
|
SCODE sc;
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::Flush()\n"));
|
|
CheckSeekPointer();
|
|
fsAssert(_hFile != INVALID_FH);
|
|
|
|
boolChk(FlushFileBuffers(_hFile));
|
|
if (IsFileMapped())
|
|
boolChk(FlushViewOfFile(_pbBaseAddr, 0));
|
|
sc = S_OK;
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::Flush\n"));
|
|
EH_Err:
|
|
CheckSeekPointer();
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::SetSize, public
|
|
//
|
|
// Synopsis: Sets the size of the LStream
|
|
//
|
|
// Arguments: [ulSize] - New size
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::SetSize(ULARGE_INTEGER ulSize)
|
|
{
|
|
SCODE sc;
|
|
#ifdef ASYNC
|
|
fsAssert((_ppc == NULL) || (_ppc->HaveMutex()));
|
|
#endif
|
|
|
|
#ifndef LARGE_DOCFILE
|
|
ULONG uLow = ULIGetLow(ulSize);
|
|
LONG lHigh = ULIGetHigh(ulSize);
|
|
fsAssert(lHigh == 0 &&
|
|
aMsg("High dword other than zero passed to filestream."));
|
|
#endif
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::SetSize:%p(%Lx)\n",
|
|
this, ulSize.QuadPart));
|
|
|
|
#ifdef ASYNC
|
|
DWORD dwTerminate;
|
|
dwTerminate = _pgfst->GetTerminationStatus();
|
|
if (dwTerminate == TERMINATED_ABNORMAL)
|
|
{
|
|
sc = STG_E_INCOMPLETE;
|
|
}
|
|
else if ((dwTerminate == TERMINATED_NORMAL) ||
|
|
#ifdef LARGE_DOCFILE
|
|
(ulSize.QuadPart <= _pgfst->GetHighWaterMark()))
|
|
#else
|
|
(uLow <= _pgfst->GetHighWaterMark()))
|
|
#endif
|
|
{
|
|
#endif
|
|
#ifdef LARGE_DOCFILE
|
|
sc = SetSizeWorker(ulSize.QuadPart);
|
|
#else
|
|
sc = SetSizeWorker(uLow);
|
|
#endif
|
|
#ifdef ASYNC
|
|
}
|
|
else
|
|
{
|
|
#ifdef LARGE_DOCFILE
|
|
_pgfst->SetFailurePoint(ulSize.QuadPart);
|
|
#else
|
|
_pgfst->SetFailurePoint(uLow);
|
|
#endif
|
|
sc = E_PENDING;
|
|
}
|
|
#endif
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::SetSizeWorker, Private
|
|
//
|
|
// Synopsis: Sets the size of the File
|
|
//
|
|
// Arguments: [ulSize] - New size
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
// 16-Jan-97 BChapman Added Support for File Mapping
|
|
// 08-Mar-97 BChapman Fixes for File Mapping
|
|
//
|
|
// Note: If the file is mapped then we grow the file with VirtualAlloc
|
|
// and shrink it at unmap time with SetEndOfFile() and the intended
|
|
// size recorded in the _pgfst.
|
|
// If we are NOT mapped then we use normal File I/O. But things can
|
|
// get tricky if we are switching from mapped to unmapped and all the
|
|
// other marshalled opens have not yet unmapped. If anyone has the
|
|
// file mapped then SetEndOfFile doesn't work.
|
|
// To avoid this we grow with WriteFile() and we try to shrink with
|
|
// SetEndOfFile(). But we also always put the intended size in _pgfst,
|
|
// so if the other guy has the file mapped he will shrink the file
|
|
// when he unmaps.
|
|
//---------------------------------------------------------------
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
SCODE CFileStream::SetSizeWorker(ULONGLONG ulSize)
|
|
#else
|
|
SCODE CFileStream::SetSizeWorker(ULONG uLow)
|
|
#endif
|
|
{
|
|
SCODE sc;
|
|
|
|
CheckSeekPointer();
|
|
fsAssert(_hFile != INVALID_FH);
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
fsChk(PossibleDiskFull(ulSize));
|
|
|
|
if (ulSize < MAX_ULONG/2)
|
|
MakeFileMapAddressValid((ULONG)ulSize);
|
|
else
|
|
TurnOffMapping(TRUE);
|
|
#else
|
|
fsChk(PossibleDiskFull(uLow));
|
|
|
|
MakeFileMapAddressValid(uLow);
|
|
#endif
|
|
|
|
|
|
// Always record the intended size, because the mapped size is stored
|
|
// globaly. You might not be mapped but other marshalled opens could
|
|
// be (they will fall back as soon as they run).
|
|
#ifdef USE_FILEMAPPING
|
|
#ifdef LARGE_DOCFILE
|
|
// A memory mapped file can never be greater than 2G, even for 64-bit
|
|
// platforms, because of the range locks at offset 2G in the file.
|
|
_pgfst->SetMappedFileSize(ulSize > MAX_ULONG ? MAX_ULONG : (ULONG) ulSize);
|
|
#else
|
|
_pgfst->SetMappedFileSize(uLow);
|
|
#endif
|
|
_pgfst->SetMapState(FSTSTATE_DIRTY);
|
|
#endif // USE_FILEMAPPING
|
|
|
|
if(!IsFileMapped())
|
|
{
|
|
ULARGE_INTEGER uliFileSize;
|
|
ULONG ulZero=0;
|
|
DWORD cbWritten;
|
|
|
|
fsChk(GetSize(&uliFileSize));
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
if(uliFileSize.QuadPart == ulSize)
|
|
#else
|
|
Assert (0 == uliFileSize.HighPart);
|
|
|
|
if(uliFileSize.LowPart == uLow)
|
|
#endif
|
|
{
|
|
#ifdef LARGE_DOCFILE
|
|
filestDebug((DEB_FILEIO, "File=%2x SetSizeWorker(0x%Lx)"
|
|
" size didn't change\n",
|
|
_hFile, ulSize));
|
|
#else
|
|
filestDebug((DEB_FILEIO, "File=%2x SetSizeWorker(0x%06x)"
|
|
" size didn't change\n",
|
|
_hFile, uLow));
|
|
#endif
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Grow the file.
|
|
//
|
|
#ifdef LARGE_DOCFILE
|
|
if(uliFileSize.QuadPart < ulSize &&
|
|
!(_pgfst->GetDFlags() & DF_LARGE))
|
|
{
|
|
ULARGE_INTEGER uli;
|
|
filestDebug((DEB_FILEIO, "File=%2x SetSizeWorker(0x%Lx)"
|
|
" increasing from 0x%Lx\n",
|
|
_hFile, ulSize, uliFileSize));
|
|
uli.QuadPart = ulSize - 1;
|
|
fsChk(WriteAtWorker(uli, (LPCVOID)&ulZero, 1, &cbWritten));
|
|
}
|
|
#else
|
|
if(uliFileSize.LowPart < uLow)
|
|
{
|
|
filestDebug((DEB_FILEIO, "File=%2x SetSizeWorker(0x%06x)"
|
|
" increasing from 0x%06x\n",
|
|
_hFile, uLow, uliFileSize.LowPart));
|
|
fsChk(WriteAtWorker(uLow-1, (LPCVOID)&ulZero, 1, &cbWritten));
|
|
}
|
|
#endif
|
|
// Shrink the file or large sector docfile
|
|
//
|
|
else
|
|
{
|
|
#ifdef LARGE_DOCFILE
|
|
filestDebug((DEB_FILEIO, "File=%2x SetSizeWorker(0x%Lx)"
|
|
" reducing from 0x%Lx\n",
|
|
_hFile, ulSize, uliFileSize));
|
|
negChk(SeekTo(ulSize));
|
|
#else
|
|
filestDebug((DEB_FILEIO, "File=%2x SetSizeWorker(0x%06x)"
|
|
" reducing from 0x%06x\n",
|
|
_hFile, uLow, uliFileSize.LowPart));
|
|
negChk(SeekTo(uLow));
|
|
#endif
|
|
if(FALSE == SetEndOfFile(_hFile))
|
|
{
|
|
sc = LAST_STG_SCODE;
|
|
// If a seperate marshaling still has the file mapped then
|
|
// this will fail. But this particular error is OK.
|
|
//
|
|
if(WIN32_SCODE(ERROR_USER_MAPPED_FILE) == sc)
|
|
{
|
|
sc = S_OK;
|
|
}
|
|
fsChk (sc);
|
|
}
|
|
}
|
|
}
|
|
|
|
sc = S_OK;
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::SetSize\n"));
|
|
|
|
EH_Err:
|
|
CheckSeekPointer();
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::SeekTo, private
|
|
// CFileStream::GetFilePointer, private
|
|
//
|
|
// Synopsis: Wrap calls to SetFilePointer to make the usage more clear.
|
|
// Esp. in the GetFilePointer case.
|
|
//
|
|
// History: 07-Nov-96 BChapman Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
ULONGLONG CFileStream::SeekTo(ULONGLONG ulPos)
|
|
{
|
|
LARGE_INTEGER uliPos;
|
|
|
|
if(FilePointerEqual(ulPos))
|
|
{
|
|
filestDebug((DEB_SEEK, "File=%2x SeekTo(0x%Lx) (cache hit)\n",
|
|
_hFile, ulPos));
|
|
return ulPos;
|
|
}
|
|
|
|
uliPos.QuadPart = ulPos;
|
|
uliPos.LowPart =
|
|
SetFilePointer(_hFile, uliPos.LowPart, &uliPos.HighPart, FILE_BEGIN);
|
|
|
|
if (uliPos.LowPart == MAX_ULONG && GetLastError() != NO_ERROR)
|
|
return MAX_ULONGLONG; // 0xFFFFFFFFFFFFFFFF
|
|
|
|
SetCachedFilePointer(uliPos.QuadPart);
|
|
|
|
filestDebug((DEB_SEEK, "File=%2x SeekTo(0x%Lx)\n", _hFile, ulPos));
|
|
olLowLog(("STGIO - Seek : %8x at %Lx\n", _hFile, ulPos));
|
|
|
|
return uliPos.QuadPart;
|
|
}
|
|
|
|
ULONGLONG CFileStream::GetFilePointer()
|
|
{
|
|
LARGE_INTEGER ulPos = {0,0};
|
|
|
|
ulPos.LowPart = SetFilePointer(_hFile, 0, &ulPos.HighPart, FILE_CURRENT);
|
|
filestDebug((DEB_SEEK, "File=%2x GetFilePointer() => 0x%Lx\n",
|
|
_hFile, ulPos));
|
|
return ulPos.QuadPart;
|
|
}
|
|
#else
|
|
DWORD CFileStream::SeekTo(ULONG Low)
|
|
{
|
|
DWORD dwPos;
|
|
|
|
if(FilePointerEqual(Low))
|
|
{
|
|
filestDebug((DEB_SEEK, "File=%2x SeekTo(0x%06x) (cache hit)\n",
|
|
_hFile, Low));
|
|
return Low;
|
|
}
|
|
|
|
if(0xFFFFFFFF == (dwPos = SetFilePointer(_hFile, Low, NULL, FILE_BEGIN)))
|
|
return dwPos;
|
|
|
|
SetCachedFilePointer(dwPos);
|
|
|
|
filestDebug((DEB_SEEK, "File=%2x SeekTo(0x%06x)\n", _hFile, Low));
|
|
olLowLog(("STGIO - Seek : %8x at %8x\n", _hFile, Low));
|
|
|
|
return dwPos;
|
|
}
|
|
|
|
DWORD CFileStream::GetFilePointer()
|
|
{
|
|
DWORD dwPos = SetFilePointer(_hFile, 0, NULL, FILE_CURRENT);
|
|
filestDebug((DEB_SEEK, "File=%2x GetFilePointer() => 0x%06x\n",
|
|
_hFile, dwPos));
|
|
return dwPos;
|
|
}
|
|
#endif
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::LockRegion, public
|
|
//
|
|
// Synopsis: Gets a lock on a portion of the LStream
|
|
//
|
|
// Arguments: [ulStartOffset] - Lock start
|
|
// [cbLockLength] - Length
|
|
// [dwLockType] - Exclusive/Read only
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::LockRegion(ULARGE_INTEGER ulStartOffset,
|
|
ULARGE_INTEGER cbLockLength,
|
|
DWORD dwLockType)
|
|
{
|
|
SCODE sc;
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::LockRegion("
|
|
"%x:%x, %x:%x, %x)\n", ULIGetHigh(ulStartOffset),
|
|
ULIGetLow(ulStartOffset), ULIGetHigh(cbLockLength),
|
|
ULIGetLow(cbLockLength), dwLockType));
|
|
CheckSeekPointer();
|
|
fsAssert(_hFile != INVALID_FH);
|
|
|
|
filestDebug((DEB_LOCK, "File=%2x LockRegion %1x:%x bytes @ %1x:%x\n",
|
|
ULIGetLow(cbLockLength), ULIGetHigh(cbLockLength),
|
|
ULIGetLow(ulStartOffset), ULIGetHigh(ulStartOffset)));
|
|
|
|
boolChk(LockFile(_hFile, ULIGetLow(ulStartOffset),
|
|
ULIGetHigh(ulStartOffset), ULIGetLow(cbLockLength),
|
|
ULIGetHigh(cbLockLength)));
|
|
|
|
sc = S_OK;
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::LockRegion\n"));
|
|
EH_Err:
|
|
CheckSeekPointer();
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::UnlockRegion, public
|
|
//
|
|
// Synopsis: Releases an existing lock
|
|
//
|
|
// Arguments: [ulStartOffset] - Lock start
|
|
// [cbLockLength] - Length
|
|
// [dwLockType] - Lock type
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 20-Feb-92 DrewB Created
|
|
//
|
|
// Notes: Must match an existing lock exactly
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::UnlockRegion(ULARGE_INTEGER ulStartOffset,
|
|
ULARGE_INTEGER cbLockLength,
|
|
DWORD dwLockType)
|
|
{
|
|
SCODE sc;
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::UnlockRegion("
|
|
"%x:%x, %x:%x, %x)\n", ULIGetHigh(ulStartOffset),
|
|
ULIGetLow(ulStartOffset), ULIGetHigh(cbLockLength),
|
|
ULIGetLow(cbLockLength), dwLockType));
|
|
|
|
CheckSeekPointer();
|
|
fsAssert(_hFile != INVALID_FH);
|
|
|
|
filestDebug((DEB_LOCK, "File=%2x UnlockRegion %1x:%x bytes @ %1x:%x\n",
|
|
ULIGetLow(cbLockLength), ULIGetHigh(cbLockLength),
|
|
ULIGetLow(ulStartOffset), ULIGetHigh(ulStartOffset)));
|
|
|
|
boolChk(UnlockFile(_hFile, ULIGetLow(ulStartOffset),
|
|
ULIGetHigh(ulStartOffset),
|
|
ULIGetLow(cbLockLength),
|
|
ULIGetHigh(cbLockLength)));
|
|
|
|
sc = S_OK;
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::UnlockRegion\n"));
|
|
EH_Err:
|
|
CheckSeekPointer();
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Function: FileTimeToTimeT, private
|
|
//
|
|
// Synopsis: Converts a FILETIME to a TIME_T
|
|
//
|
|
// Arguments: [pft] - FILETIME
|
|
//
|
|
// Returns: TIME_T
|
|
//
|
|
// History: 12-May-92 DrewB Created
|
|
//
|
|
//+--------------------------------------------------------------
|
|
|
|
#ifdef NOFILETIME
|
|
TIME_T FileTimeToTimeT(LPFILETIME pft)
|
|
{
|
|
WORD dt, tm;
|
|
struct tm tmFile;
|
|
|
|
fsVerify(FileTimeToDosDateTime(pft, &dt, &tm));
|
|
tmFile.tm_sec = (tm&31)*2;
|
|
tmFile.tm_min = (tm>>5)&63;
|
|
tmFile.tm_hour = (tm>>11)&31;
|
|
tmFile.tm_mday = dt&31;
|
|
tmFile.tm_mon = ((dt>>5)&15)-1;
|
|
tmFile.tm_year = (dt>>9)+80;
|
|
return (TIME_T)mktime(&tmFile);
|
|
}
|
|
#endif
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Stat, public
|
|
//
|
|
// Synopsis: Fills in a stat buffer for this object
|
|
//
|
|
// Arguments: [pstatstg] - Buffer
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies: [pstatstg]
|
|
//
|
|
// History: 25-Mar-92 DrewB Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
_OLESTDMETHODIMP CFileStream::Stat(STATSTGW *pstatstg, DWORD grfStatFlag)
|
|
{
|
|
SCODE sc;
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::Stat(%p)\n", pstatstg));
|
|
|
|
CheckSeekPointer();
|
|
fsAssert(_hFile != INVALID_FH);
|
|
|
|
fsChk(GetSize(&pstatstg->cbSize));
|
|
#ifdef NOFILETIME
|
|
FILETIME ftCreation, ftAccess, ftWrite;
|
|
boolChk(GetFileTime(_hFile, &ftCreation, &ftAccess, &ftWrite));
|
|
if (ftCreation.dwLowDateTime == 0 && ftCreation.dwHighDateTime == 0)
|
|
ftCreation = ftWrite;
|
|
if (ftAccess.dwLowDateTime == 0 && ftAccess.dwHighDateTime == 0)
|
|
ftAccess = ftWrite;
|
|
pstatstg->ctime = FileTimeToTimeT(&ftCreation);
|
|
pstatstg->atime = FileTimeToTimeT(&ftAccess);
|
|
pstatstg->mtime = FileTimeToTimeT(&ftWrite);
|
|
#else
|
|
boolChk(GetFileTime(_hFile, &pstatstg->ctime, &pstatstg->atime,
|
|
&pstatstg->mtime));
|
|
#endif
|
|
fsHVerSucc(GetLocksSupported(&pstatstg->grfLocksSupported));
|
|
pstatstg->type = STGTY_LOCKBYTES;
|
|
pstatstg->grfMode = DFlagsToMode(_pgfst->GetDFlags());
|
|
pstatstg->pwcsName = NULL;
|
|
if ((grfStatFlag & STATFLAG_NONAME) == 0)
|
|
{
|
|
fsChk(GetName(&pstatstg->pwcsName));
|
|
}
|
|
sc = S_OK;
|
|
CheckSeekPointer();
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::Stat\n"));
|
|
return NOERROR;
|
|
|
|
EH_Err:
|
|
CheckSeekPointer();
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::SwitchToFile, public
|
|
//
|
|
// Synopsis: Changes the file this filestream uses
|
|
//
|
|
// Arguments: [ptcsFile] - File name
|
|
// [ulCommitSize] -- Size needed to do overwrite commit
|
|
// [cbBuffer] - Buffer size
|
|
// [pvBuffer] - Buffer for file copying
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 08-Jan-93 DrewB Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::SwitchToFile(OLECHAR const *ptcsFile,
|
|
#ifdef LARGE_DOCFILE
|
|
ULONGLONG ulCommitSize,
|
|
#else
|
|
ULONG ulCommitSize,
|
|
#endif
|
|
ULONG cbBuffer,
|
|
void *pvBuffer)
|
|
{
|
|
SCODE sc;
|
|
DWORD cbRead, cbWritten = 0;
|
|
FILEH hOldFile;
|
|
WCHAR awcOldName[_MAX_PATH+1];
|
|
WCHAR wcsFile[_MAX_PATH+1];
|
|
DWORD dwOldStartFlags;
|
|
ULARGE_INTEGER ulPos;
|
|
ULONG cbBufferSave = 0;
|
|
|
|
#ifdef ASYNC
|
|
fsAssert((_ppc == NULL) || (_ppc->HaveMutex()));
|
|
#endif
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::SwitchToFile:%p(%s, %x, %p)\n",
|
|
this, ptcsFile, cbBuffer, pvBuffer));
|
|
|
|
// Check for marshals
|
|
// This must be the only instance of this "context". Other
|
|
// seperate opens are possible though.
|
|
//
|
|
if (_pgfst->CountContexts() != 1)
|
|
fsErr(EH_Err, STG_E_EXTANTMARSHALLINGS);
|
|
|
|
CheckSeekPointer();
|
|
|
|
//
|
|
// We are about to switch physical files for this CFileStream. So
|
|
// turn off the mapping so we can switch to the new file.
|
|
//
|
|
fsChk(TurnOffMapping(TRUE));
|
|
|
|
// Seek to beginning
|
|
negChk(SeekTo(0));
|
|
|
|
// Preserve old file information
|
|
StringCbCopyW (awcOldName, sizeof(awcOldName), _pgfst->GetName());
|
|
hOldFile = _hFile;
|
|
dwOldStartFlags = _pgfst->GetStartFlags();
|
|
|
|
// Set file information to prepare for new Init
|
|
_pgfst->SetName(NULL);
|
|
_pgfst->SetMappingName(NULL);
|
|
_pgfst->SetMappedFileSize(0);
|
|
_pgfst->SetMappedCommitSize(0);
|
|
_pgfst->ResetMapState(~0UL); // Clear All State Flags.
|
|
_hFile = INVALID_FH;
|
|
_pgfst->SetStartFlags((dwOldStartFlags & ~(RSF_CREATEFLAGS |
|
|
RSF_CONVERT |
|
|
RSF_DELETEONRELEASE |
|
|
RSF_OPEN)) |
|
|
RSF_CREATE);
|
|
|
|
// Release reserved file handle so it can be consumed
|
|
if (_hReserved != INVALID_FH)
|
|
{
|
|
fsVerify(CloseHandle(_hReserved));
|
|
_hReserved = INVALID_FH;
|
|
}
|
|
|
|
if (FAILED(StringCbCopy(wcsFile, sizeof(wcsFile), ptcsFile)))
|
|
fsErr(EH_ReplaceOld, STG_E_INVALIDPOINTER);
|
|
|
|
// Attempt to create new file
|
|
fsChkTo(EH_ReplaceOld, InitFile(wcsFile));
|
|
|
|
ULARGE_INTEGER ulNewSize;
|
|
ulNewSize.QuadPart = ulCommitSize;
|
|
|
|
// SetSize to minimum commit size
|
|
fsHChkTo(EH_NewFile, SetSize(ulNewSize));
|
|
|
|
// SetSize changes the file pointer, so move it back to the beginning
|
|
negChkTo(EH_NewFile, SeekTo(0));
|
|
|
|
// Copy file contents
|
|
ulPos.QuadPart = 0;
|
|
for (;;)
|
|
{
|
|
BOOL fRangeLocks = IsInRangeLocks (ulPos.QuadPart, cbBuffer);
|
|
if (fRangeLocks)
|
|
{
|
|
ULONG ulRangeLocksBegin = OLOCKREGIONEND_SECTORALIGNED;
|
|
// The end of the range locks is within this cbBuffer block
|
|
// For unbuffered I/O, make sure we skip a whole sector
|
|
cbBufferSave = cbBuffer;
|
|
ulRangeLocksBegin -= (_pgfst->GetDFlags() & DF_LARGE) ? 4096 : 512;
|
|
cbBuffer = ulRangeLocksBegin - ulPos.LowPart;
|
|
}
|
|
|
|
if (cbBuffer > 0)
|
|
{
|
|
boolChkTo(EH_NewFile,
|
|
ReadFile(hOldFile, pvBuffer, (UINT)cbBuffer, &cbRead, NULL));
|
|
if (cbRead == 0)
|
|
break; // EOF
|
|
|
|
fsChkTo(EH_NewFile, WriteAt(ulPos, pvBuffer, cbRead, &cbWritten));
|
|
if (cbWritten != cbRead)
|
|
fsErr(EH_NewFile, STG_E_WRITEFAULT);
|
|
}
|
|
|
|
if (fRangeLocks)
|
|
{
|
|
cbBuffer = cbBufferSave;
|
|
cbWritten = cbBuffer;
|
|
}
|
|
ulPos.QuadPart += cbWritten;
|
|
if (fRangeLocks)
|
|
{
|
|
// If we've skipped the range locks, move past them
|
|
if (SetFilePointer(hOldFile, ulPos.LowPart, (LONG*)&ulPos.HighPart,
|
|
FILE_BEGIN) == MAX_ULONG)
|
|
fsChkTo(EH_NewFile, STG_SCODE(GetLastError()));
|
|
}
|
|
}
|
|
|
|
fsVerify(CloseHandle(hOldFile));
|
|
if (dwOldStartFlags & RSF_DELETEONRELEASE)
|
|
{
|
|
// This is allowed to fail if somebody else has
|
|
// the file open
|
|
DeleteTheFile(awcOldName);
|
|
}
|
|
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::SwitchToFile\n"));
|
|
SetCachedFilePointer(GetFilePointer());
|
|
return S_OK;
|
|
|
|
EH_NewFile:
|
|
TurnOffMapping(FALSE);
|
|
fsVerify(CloseHandle(_hFile));
|
|
fsVerify(DeleteTheFile(_pgfst->GetName()));
|
|
EH_ReplaceOld:
|
|
_hFile = hOldFile;
|
|
if (_pgfst != NULL)
|
|
{
|
|
_pgfst->SetName(awcOldName);
|
|
_pgfst->SetStartFlags(dwOldStartFlags);
|
|
}
|
|
|
|
EH_Err:
|
|
SetCachedFilePointer(GetFilePointer());
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Delete, public
|
|
//
|
|
// Synopsis: Closes and deletes the file, errors ignored
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 09-Feb-93 DrewB Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CFileStream::Delete(void)
|
|
{
|
|
filestDebug((DEB_ITRACE, "In CFileStream::Delete:%p()\n", this));
|
|
|
|
TurnOffAllMappings();
|
|
|
|
if (_hFile != INVALID_FH)
|
|
CloseHandle(_hFile);
|
|
_hFile = INVALID_FH;
|
|
|
|
if (_hReserved != INVALID_FH)
|
|
CloseHandle(_hReserved);
|
|
_hReserved = INVALID_FH;
|
|
|
|
DeleteTheFile(_pgfst->GetName());
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::Delete\n"));
|
|
}
|
|
|
|
//+--------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::DeleteTheFile, public
|
|
//
|
|
// Synopsis: Delete the file (using WideWrap DeleteFile), but don't
|
|
// if we don't think the system as already done so.
|
|
//
|
|
// History: 21-Jan-97 BChapman Created
|
|
//
|
|
//---------------------------------------------------------------
|
|
|
|
BOOL CFileStream::DeleteTheFile(const WCHAR *pwcName)
|
|
{
|
|
#ifndef MAC
|
|
//
|
|
// If the file is a "TEMPFILE" then it is DELETE_ON_CLOSE
|
|
// and the system already deleted it.
|
|
//
|
|
if(RSF_TEMPFILE & GetStartFlags())
|
|
return TRUE;
|
|
#endif
|
|
return DeleteFile(_pgfst->GetName());
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::ReserveHandle, public
|
|
//
|
|
// Synopsis: Reserves a backup file handle for handle-required operations
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 01-Jul-93 DrewB Created
|
|
//
|
|
// Notes: May be called with a handle already reserved
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::ReserveHandle(void)
|
|
{
|
|
SCODE sc;
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::ReserveHandle:%p()\n", this));
|
|
if (_hReserved == INVALID_FH &&
|
|
!DuplicateHandle(GetCurrentProcess(), _hFile, GetCurrentProcess(),
|
|
&_hReserved, 0, FALSE, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
sc = LAST_STG_SCODE;
|
|
}
|
|
else
|
|
{
|
|
filestDebug((DEB_INFO, "CFileStream reserved %p "
|
|
"handle %p thread %lX\n",
|
|
this, _hReserved, GetCurrentThreadId()));
|
|
sc = S_OK;
|
|
_grfLocal |= LFF_RESERVE_HANDLE;
|
|
}
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::ReserveHandle => %lX\n", sc));
|
|
return ResultFromScode(sc);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::GetSize, public
|
|
//
|
|
// Synopsis: Return the size of the stream
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 12-Jul-93 AlexT Created
|
|
//
|
|
// Notes: This is a separate method from Stat as an optimization
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::GetSize(ULARGE_INTEGER *puliSize)
|
|
{
|
|
SCODE sc = S_OK;
|
|
CheckSeekPointer();
|
|
|
|
if(IsFileMapped())
|
|
{
|
|
puliSize->LowPart = _pgfst->GetMappedFileSize();
|
|
puliSize->HighPart = 0;
|
|
}
|
|
else
|
|
#ifdef LARGE_DOCFILE
|
|
{
|
|
puliSize->LowPart = GetFileSize(_hFile, &puliSize->HighPart);
|
|
if (puliSize->LowPart == MAX_ULONG && NO_ERROR != GetLastError())
|
|
fsErr(EH_Err, LAST_STG_SCODE)
|
|
}
|
|
#else
|
|
negChk(puliSize->LowPart = GetFileSize(_hFile, &puliSize->HighPart));
|
|
#endif
|
|
|
|
EH_Err:
|
|
CheckSeekPointer();
|
|
return(ResultFromScode(sc));
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::SetTime, public
|
|
//
|
|
// Synopsis: Set the times on the ILockbytes
|
|
//
|
|
// Arguments: [tt] -- Which time to set
|
|
// [nt] -- New time stamp
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 24-Mar-95 PhilipLa Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CFileStream::SetTime(WHICHTIME tt,
|
|
TIME_T nt)
|
|
{
|
|
filestDebug((DEB_ITRACE, "In CFileStream::SetTime()\n"));
|
|
|
|
SCODE sc = S_OK;
|
|
|
|
FILETIME *pctime = NULL, *patime = NULL, *pmtime = NULL;
|
|
CheckSeekPointer();
|
|
|
|
if (tt == WT_CREATION)
|
|
{
|
|
pctime = &nt;
|
|
}
|
|
else if (tt == WT_MODIFICATION)
|
|
{
|
|
pmtime = &nt;
|
|
}
|
|
else
|
|
{
|
|
patime = &nt;
|
|
}
|
|
|
|
boolChk(SetFileTime(_hFile,
|
|
pctime,
|
|
patime,
|
|
pmtime));
|
|
|
|
EH_Err:
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::SetTime() => %lx\n", sc));
|
|
CheckSeekPointer();
|
|
return sc;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::SetAllTimes, public
|
|
//
|
|
// Synopsis: Set the times on the ILockbytes
|
|
//
|
|
// Arguments: [atm] Access time
|
|
// [mtm] Modification time
|
|
// [ctm] Creation time
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// History: 24-Nov-95 SusiA Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
SCODE CFileStream::SetAllTimes( TIME_T atm,
|
|
TIME_T mtm,
|
|
TIME_T ctm)
|
|
{
|
|
filestDebug((DEB_ITRACE, "In CFileStream::SetAllTimes()\n"));
|
|
|
|
SCODE sc = S_OK;
|
|
|
|
CheckSeekPointer();
|
|
|
|
boolChk(SetFileTime(_hFile, &ctm, &atm, &mtm));
|
|
|
|
EH_Err:
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::SetAllTimes() => %lx\n", sc));
|
|
CheckSeekPointer();
|
|
return sc;
|
|
}
|
|
|
|
|
|
#ifdef ASYNC
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::FillAppend, public
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 28-Dec-95 PhilipLa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::FillAppend(void const *pv,
|
|
ULONG cb,
|
|
ULONG *pcbWritten)
|
|
{
|
|
SCODE sc;
|
|
SAFE_SEM;
|
|
HANDLE hEvent;
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::FillAppend:%p()\n", this));
|
|
fsChk(TakeSafeSem());
|
|
if (_pgfst->GetTerminationStatus() != UNTERMINATED)
|
|
{
|
|
sc = STG_E_TERMINATED;
|
|
}
|
|
else
|
|
{
|
|
ULONG cbWritten;
|
|
#ifdef LARGE_DOCFILE
|
|
ULARGE_INTEGER ulHighWater;
|
|
ulHighWater.QuadPart = _pgfst->GetHighWaterMark();
|
|
#else
|
|
ULONG ulHighWater = _pgfst->GetHighWaterMark();
|
|
#endif
|
|
|
|
sc = CFileStream::WriteAtWorker(ulHighWater, pv, cb, &cbWritten);
|
|
#ifdef LARGE_DOCFILE
|
|
_pgfst->SetHighWaterMark(ulHighWater.QuadPart + cbWritten);
|
|
#else
|
|
_pgfst->SetHighWaterMark(ulHighWater + cbWritten);
|
|
#endif
|
|
if (pcbWritten != NULL)
|
|
{
|
|
*pcbWritten = cbWritten;
|
|
}
|
|
|
|
hEvent = _ppc->GetNotificationEvent();
|
|
if (!PulseEvent(hEvent))
|
|
{
|
|
sc = Win32ErrorToScode(GetLastError());
|
|
}
|
|
}
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::FillAppend\n"));
|
|
EH_Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::FillAt, public
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 28-Dec-95 PhilipLa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::FillAt(ULARGE_INTEGER ulOffset,
|
|
void const *pv,
|
|
ULONG cb,
|
|
ULONG *pcbWritten)
|
|
{
|
|
filestDebug((DEB_ITRACE, "In CFileStream::FillAt:%p()\n", this));
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::FillAt\n"));
|
|
return STG_E_UNIMPLEMENTEDFUNCTION;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::SetFillSize, public
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 28-Dec-95 PhilipLa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::SetFillSize(ULARGE_INTEGER ulSize)
|
|
{
|
|
SCODE sc;
|
|
SAFE_SEM;
|
|
|
|
filestDebug((DEB_ITRACE,
|
|
"In CFileStream::SetFillSize:%p()\n", this));
|
|
|
|
fsChk(TakeSafeSem());
|
|
if (_pgfst->GetTerminationStatus() == TERMINATED_ABNORMAL)
|
|
{
|
|
sc = STG_E_INCOMPLETE;
|
|
}
|
|
else
|
|
{
|
|
#ifndef LARGE_DOCFILE
|
|
ULONG uLow = ULIGetLow(ulSize);
|
|
LONG lHigh = ULIGetHigh(ulSize);
|
|
fsAssert(lHigh == 0 &&
|
|
aMsg("High dword other than zero passed to filestream."));
|
|
#endif
|
|
|
|
#ifdef LARGE_DOCFILE
|
|
sc = SetSizeWorker(ulSize.QuadPart);
|
|
#else
|
|
sc = SetSizeWorker(uLow);
|
|
#endif
|
|
}
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::SetFillSize\n"));
|
|
EH_Err:
|
|
return sc;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CFileStream::Terminate, public
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 28-Dec-95 PhilipLa Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CFileStream::Terminate(BOOL bCanceled)
|
|
{
|
|
SCODE sc;
|
|
SAFE_SEM;
|
|
HANDLE hEvent;
|
|
|
|
filestDebug((DEB_ITRACE, "In CFileStream::Terminate:%p()\n", this));
|
|
|
|
fsChk(TakeSafeSem());
|
|
_pgfst->SetTerminationStatus((bCanceled) ?
|
|
TERMINATED_ABNORMAL :
|
|
TERMINATED_NORMAL);
|
|
|
|
hEvent = _ppc->GetNotificationEvent();
|
|
|
|
if ((hEvent != INVALID_HANDLE_VALUE) && (!SetEvent(hEvent)))
|
|
{
|
|
return Win32ErrorToScode(GetLastError());
|
|
}
|
|
|
|
filestDebug((DEB_ITRACE, "Out CFileStream::Terminate\n"));
|
|
EH_Err:
|
|
return sc;
|
|
}
|
|
|
|
void CFileStream::StartAsyncMode(void)
|
|
{
|
|
//Note: No semaphore here - this must be called before the ILockBytes
|
|
// is returned to an app.
|
|
_pgfst->SetTerminationStatus(UNTERMINATED);
|
|
}
|
|
|
|
STDMETHODIMP CFileStream::GetFailureInfo(ULONG *pulWaterMark,
|
|
ULONG *pulFailurePoint)
|
|
{
|
|
//We don't take a semaphore here because we don't need it. This call
|
|
// is either made when we're already holding the semaphore, or is made
|
|
// when we don't care about the absolute accuracy of the results.
|
|
//However, we do need a shared heap to access _pgfst
|
|
CSafeMultiHeap smh(_ppc);
|
|
#ifdef LARGE_DOCFILE
|
|
// This method, used by IProgressNotify, cannot handle large integers
|
|
// If the high water mark is too big to fit, return an error
|
|
ULONGLONG ulWaterMark = _pgfst->GetHighWaterMark();
|
|
if (ulWaterMark < MAX_ULONG)
|
|
{
|
|
*pulWaterMark = (ULONG) ulWaterMark;
|
|
*pulFailurePoint = (ULONG) _pgfst->GetFailurePoint();
|
|
return S_OK;
|
|
}
|
|
else return STG_E_INVALIDFUNCTION; // file is too big
|
|
#else
|
|
*pulWaterMark = _pgfst->GetHighWaterMark();
|
|
*pulFailurePoint = _pgfst->GetFailurePoint();
|
|
return S_OK;
|
|
#endif
|
|
}
|
|
|
|
STDMETHODIMP CFileStream::GetTerminationStatus(DWORD *pdwFlags)
|
|
{
|
|
SAFE_SEM;
|
|
TakeSafeSem();
|
|
*pdwFlags = _pgfst->GetTerminationStatus();
|
|
return S_OK;
|
|
}
|
|
|
|
#endif //ASYNC
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: GetNtHandleSectorSize
|
|
//
|
|
// Synopsis: Find a volume's physical sector size
|
|
//
|
|
// Arguments: [Handle] -- file handle
|
|
// [pulSectorSize] -- number of bytes per physical sector
|
|
//
|
|
// Returns: Appropriate status code
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT GetNtHandleSectorSize (HANDLE Handle, ULONG * pulSectorSize)
|
|
{
|
|
FILE_FS_SIZE_INFORMATION SizeInfo;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
NTSTATUS nts = NtQueryVolumeInformationFile( Handle, &iosb,
|
|
&SizeInfo, sizeof(SizeInfo), FileFsSizeInformation );
|
|
|
|
if (NT_SUCCESS(nts))
|
|
{
|
|
*pulSectorSize = SizeInfo.BytesPerSector;
|
|
return S_OK;
|
|
}
|
|
|
|
return NtStatusToScode(nts);
|
|
}
|