Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

3458 lines
100 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)
{
TRY
{
wcsncpy(awcPath, pwcsPath, _MAX_PATH+1);
awcPath[MAX_PATH] = L'\0';
}
CATCH(CException, e)
{
UNREFERENCED_PARM(e);
fsErr(EH_Err, STG_E_INVALIDPOINTER);
}
END_CATCH
}
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;
#ifndef UNICODE
AnsiToUnicodeOem(awcPath, atcPath, _MAX_PATH+1);
#else
wcsncpy(awcPath, atcPath, _MAX_PATH+1);
awcPath[MAX_PATH] = L'\0';
#endif
}
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)
{
BOOL fWinDir = FALSE;
SCODE sc;
LUID luid;
// 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.
//
if(0 == GetTempPath(_MAX_PATH, ptcTmpPath))
{
if(0 == GetWindowsDirectory(ptcTmpPath, _MAX_PATH))
fsErr(EH_Err, LAST_STG_SCODE);
fWinDir = TRUE;
}
if (AllocateLocallyUniqueId (&luid) == FALSE)
fsErr (EH_Err, LAST_STG_SCODE);
if(0 == GetTempFileName(ptcTmpPath, TEMPFILE_PREFIX, luid.LowPart, ptcPath))
{
if (fWinDir)
fsErr(EH_Err, LAST_STG_SCODE);
if (0 == GetWindowsDirectory(ptcTmpPath, _MAX_PATH))
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;
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);
}
#ifndef UNICODE
AnsiToUnicodeOem(pwcPath, atcPath, _MAX_PATH+1);
#else
wcsncpy(pwcPath, atcPath, _MAX_PATH+1);
#endif
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,
NULL,
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;
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);
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;
HANDLE hfile;
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);
wsprintfW(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() ));
hfile = _hFile;
//
// Create the Mapping. This only covers the orignal file size.
// RESERVED uncommitted extensions are done in MapView.
//
// Note: Using a WideWrap routine to handle the !UNICODE case.
//
_hMapObject = CreateFileMapping(hfile,
NULL, // 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);
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;
FILEH hOldFile;
WCHAR awcOldName[_MAX_PATH];
WCHAR wcsFile[_MAX_PATH];
DWORD dwOldStartFlags;
ULARGE_INTEGER ulPos;
ULONG cbBufferSave;
#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
lstrcpyW(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;
}
// Attempt to create new file
TRY
{
lstrcpyW(wcsFile, ptcsFile);
}
CATCH(CException, e)
{
UNREFERENCED_PARM(e);
fsErr(EH_ReplaceOld, STG_E_INVALIDPOINTER);
}
END_CATCH
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 S_OK;
#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);
}