2025-04-27 07:49:33 -04:00

1204 lines
36 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 2000.
//
// File: mmstrm.cxx
//
// Contents: Memory Mapped Stream using win32 API
//
// Classes: CMmStream, CMmStreamBuf
//
// History: 10-Mar-93 BartoszM Created
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <mmstrm.hxx>
//+-------------------------------------------------------------------------
//
// Member: CMmStream::CMmStream
//
// Synopsis: constructor
//
// History: 18-Mar-98 kitmanh Passed fIsReadOnly into CMmStream
// constructor and init _fIsReadOnly
// with its value
// 18-Nov-98 KLam Added cMegToLeaveOnDisk
//
//--------------------------------------------------------------------------
CMmStream::CMmStream( ULONG cMegToLeaveOnDisk, BOOL fIsReadOnly )
: _hFile(INVALID_HANDLE_VALUE),
_hMap(0),
_fSparse(FALSE),
_fWrite(FALSE),
_cMap(0),
_fIsReadOnly( fIsReadOnly ),
_cMegToLeaveOnDisk( cMegToLeaveOnDisk )
{
#if CIDBG == 1
_xwcPath[0] = 0;
#endif
}
//+-------------------------------------------------------------------------
//
// Member: CMmStream::~CMmStream
//
// Synopsis: destructor
//
// History: Jun-23-1995 SrikantS Added a wait for the lock event
// to complete before closing it.
//
//--------------------------------------------------------------------------
CMmStream::~CMmStream()
{
if (Ok())
{
if ( 0 != _hMap )
{
CloseHandle( _hMap );
_hMap = 0;
}
CloseHandle ( _hFile );
_hFile = INVALID_HANDLE_VALUE;
}
if ( 0 != _cMap )
{
ciDebugOut(( DEB_WARN, "closing a stream with %d open views\n", _cMap ));
}
}
//+---------------------------------------------------------------------------
//
// Member: CMmStream::OpenExlusive
//
// Synopsis: Opens the stream for exclusive read/write access.
// Throws if it fails.
//
// Arguments: [wcsPath] - Path of the memory mapped file
//
// History: 5-14-96 srikants Created
// 2-13-98 kitmanh Added code for dealing with read-only
// read-only catalogs
// 3-13-98 kitmanh Always SHARE_READ if catalog is read-only
// 3-18-98 kitmanh fReadOnly is not passed by ref anymore
// 26-Oct-98 KLam Make sure there is enough disk space
//
// Notes:
//
//----------------------------------------------------------------------------
void CMmStream::OpenExclusive( WCHAR * wcsPath, BOOL fReadOnly )
{
Win4Assert( _fIsReadOnly == fReadOnly );
_fWrite = FALSE;
DWORD accessMode = FILE_GENERIC_READ;
if (!_fIsReadOnly)
accessMode |= FILE_GENERIC_WRITE;
#if CIDBG == 1
_xwcPath.SetSize( wcslen( wcsPath + 1 ));
wcscpy( _xwcPath.Get(), wcsPath );
#endif
if ( _xDriveInfo.IsNull() || !_xDriveInfo->IsSameDrive ( wcsPath ) )
{
_xDriveInfo.Free();
if ( CDriveInfo::ContainsDrive ( wcsPath ) )
_xDriveInfo.Set( new CDriveInfo ( wcsPath, _cMegToLeaveOnDisk ) );
}
_status = STATUS_SUCCESS;
DWORD sharing = fReadOnly ? FILE_SHARE_READ : 0;
_hFile = CreateFile( wcsPath,
accessMode,
sharing, // sharing
0, // security
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0 ); // template
if (Ok())
{
if (!fReadOnly)
_fWrite = TRUE;
}
else
THROW( CException() );
_sizeLow = GetFileSize( _hFile, &_sizeHigh );
if (_sizeLow == 0xffffffff && GetLastError() != NO_ERROR)
Close();
DWORD protection = fReadOnly ? PAGE_READONLY : PAGE_READWRITE;
if ( _sizeLow != 0 || _sizeHigh != 0 )
{
_hMap = CreateFileMapping( _hFile,
0, // security
protection,
_sizeHigh,
_sizeLow,
0 ); // name
if ( 0 == _hMap )
{
Close();
THROW( CException() );
}
}
} //OpenExclusive
//+---------------------------------------------------------------------------
//
// Function: IsCreateExisting
//
// Synopsis: Tests if the error was a result of trying to create a
// file that already exists.
//
// Arguments: [Error] -- Error returned by the system.
// [modeAccess] -- access mode specified to CreateFile.
// [modeCreate] -- create mode specified to CreateFile.
//
// Returns: TRUE if the failure is because of trying to create a "New"
// file with the same name as the one already existing.
// FALSE otherwise.
//
// History: 3-22-94 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL IsCreateExisting( ULONG Error, ULONG modeAccess, ULONG modeCreate )
{
return ( (Error == ERROR_ALREADY_EXISTS || Error == ERROR_FILE_EXISTS) &&
(modeAccess & GENERIC_WRITE) &&
(modeCreate & CREATE_NEW) );
}
//+-------------------------------------------------------------------------
//
// Member: CMmStream::Open, public
//
// Synopsis: Open stream
//
// Arguments: [wcsPath] -- file path
// [modeAccess] -- access mode
// [modeShare] -- sharing mode
// [modeCreate] -- create mode
// [fSparse] -- if TRUE, make the stream sparse
//
// History: 13-Apr-93 BartoszM Created
//
//--------------------------------------------------------------------------
void CMmStream::Open(
const WCHAR* wcsPath,
ULONG modeAccess,
ULONG modeShare,
ULONG modeCreate,
ULONG modeAttribute,
BOOL fSparse )
{
#if CIDBG == 1
_xwcPath.SetSize( wcslen( wcsPath + 1 ));
wcscpy( _xwcPath.Get(), wcsPath );
#endif
if ( _xDriveInfo.IsNull() || !_xDriveInfo->IsSameDrive ( wcsPath ) )
{
_xDriveInfo.Free();
if ( CDriveInfo::ContainsDrive ( wcsPath ) )
_xDriveInfo.Set( new CDriveInfo ( wcsPath, _cMegToLeaveOnDisk ) );
}
_fSparse = fSparse;
_status = STATUS_SUCCESS;
_fWrite = FALSE;
//
// Open files
//
UNICODE_STRING uScope;
if ( !RtlDosPathNameToNtPathName_U( wcsPath,
&uScope,
0,
0 ) )
{
ciDebugOut(( DEB_ERROR, "Error converting %ws to Nt path\n", wcsPath ));
THROW( CException(STATUS_INSUFFICIENT_RESOURCES) );
}
IO_STATUS_BLOCK IoStatus;
OBJECT_ATTRIBUTES ObjectAttr;
InitializeObjectAttributes( &ObjectAttr, // Structure
&uScope, // Name
OBJ_CASE_INSENSITIVE, // Attributes
0, // Root
0 ); // Security
switch ( modeCreate )
{
case CREATE_NEW :
case CREATE_ALWAYS :
modeCreate = FILE_OVERWRITE_IF;
break;
case OPEN_EXISTING :
case TRUNCATE_EXISTING :
modeCreate = FILE_OPEN;
break;
case OPEN_ALWAYS :
modeCreate = FILE_OPEN_IF;
break;
}
ULONG modeTranslatedAccess = 0;
if ( modeAccess & GENERIC_READ )
modeTranslatedAccess |= ( FILE_READ_DATA | FILE_READ_ATTRIBUTES );
if ( modeAccess & GENERIC_WRITE )
{
Win4Assert( !_fIsReadOnly );
modeTranslatedAccess |= ( FILE_READ_DATA | FILE_READ_ATTRIBUTES |
FILE_WRITE_DATA | FILE_WRITE_ATTRIBUTES );
}
NTSTATUS Status = NtCreateFile( &_hFile, // Handle
modeTranslatedAccess, // Access
&ObjectAttr, // Object Attributes
&IoStatus, // I/O Status block
0, // Allocation Size
modeAttribute, // File Attributes
modeShare, // File Sharing
modeCreate, // Create Disposition
FILE_SEQUENTIAL_ONLY, // Create Options
0, // EA Buffer
0 ); // EA Buffer Length
RtlFreeHeap( RtlProcessHeap(), 0, uScope.Buffer );
if ( FAILED(Status) )
{
ciDebugOut(( DEB_IERROR, "NtCreateFile(%ws) returned 0x%x\n", wcsPath, Status ));
_hFile = INVALID_HANDLE_VALUE;
_status = Status;
SetLastError ( Status );
}
if ( Ok() )
{
//
// Mark the stream as sparse if it was opened for write and it
// should be marked as sparse. Don't just make the stream sparse
// on file create, since we want to be able to copy catalogs from
// non-ntfs5 volumes to ntfs5 volumes and have things work.
//
if ( ( GENERIC_WRITE & modeAccess ) && fSparse )
{
CEventSem evt;
evt.Reset();
Status = NtFsControlFile( _hFile,
evt.GetHandle(),
0, 0,
&IoStatus,
FSCTL_SET_SPARSE,
0, 0, 0, 0 );
if ( NT_ERROR( Status ) )
{
ciDebugOut(( DEB_WARN,
"ntstatus 0x%x, can't make '%ws' sparse\n",
Status, wcsPath ));
Close();
THROW( CException( Status ) );
}
if ( STATUS_PENDING == Status )
{
// wait for the io to complete
ciDebugOut(( DEB_ITRACE, "make file sparse pending\n" ));
evt.Wait();
Status = STATUS_SUCCESS;
}
}
_sizeLow = GetFileSize ( _hFile, &_sizeHigh );
LARGE_INTEGER sizeOriginal = { _sizeLow, _sizeHigh };
if (_sizeLow == 0xffffffff && GetLastError() != NO_ERROR)
{
Close();
ciDebugOut (( DEB_ERROR, "Open stream %ws failed\n", wcsPath ));
THROW( CException() );
}
if ( modeAccess & GENERIC_WRITE )
{
_fWrite = TRUE;
CommonPageRound(_sizeLow, _sizeHigh);
if (_sizeLow == 0 && _sizeHigh == 0)
{
if ( SetFilePointer ( _hFile,
COMMON_PAGE_SIZE,
0,
FILE_BEGIN ) == 0xFFFFFFFF &&
GetLastError() != NO_ERROR )
{
Close();
ciDebugOut(( DEB_ERROR,
"CMmStream::Open -- SetFilePointer returned %d\n",
GetLastError() ));
THROW( CException() );
}
if ( !SetEndOfFile( _hFile ) )
{
Close();
ciDebugOut(( DEB_ERROR,
"CMmStream::Open -- SetEndOfFile returned %d\n",
GetLastError() ));
THROW( CException() );
}
_sizeLow = COMMON_PAGE_SIZE;
}
}
if ( _sizeLow != 0 || _sizeHigh != 0 )
{
//
// Make sure there is enough disk space if growing the file
//
LARGE_INTEGER sizeNew = { _sizeLow, _sizeHigh };
if ( sizeNew.QuadPart > sizeOriginal.QuadPart )
{
Win4Assert ( !_xDriveInfo.IsNull() );
__int64 sizeRemaining, sizeTotal;
_xDriveInfo->GetDiskSpace ( sizeTotal, sizeRemaining );
if ( sizeRemaining < ( sizeNew.QuadPart - sizeOriginal.QuadPart ))
{
Close();
ciDebugOut(( DEB_ERROR,
"CMmStream::Open -- Not enogh disk space, need %I64d have %I64d\n",
sizeNew.QuadPart - sizeOriginal.QuadPart, sizeRemaining ));
THROW( CException( CI_E_CONFIG_DISK_FULL ) );
}
}
_hMap = CreateFileMapping ( _hFile,
0, // security
_fWrite?PAGE_READWRITE: PAGE_READONLY,
_sizeHigh,
_sizeLow,
0 ); // name
if (_hMap == NULL)
{
Close();
ciDebugOut (( DEB_ERROR, "File mapping failed\n" ));
THROW( CException() );
}
#if CIDBG == 1
if ( FILE_OVERWRITE_IF == modeCreate )
{
// Zero out the entire file upon creation. We need to map/write/flush/unmap to
// accomplish this. Currently all allocations are < _sizeLow, so _sizeHigh should
// be zero.
Win4Assert(0 == _sizeHigh);
CMmStreamBuf sbuf;
Map(sbuf, min(COMMON_PAGE_SIZE, _sizeLow), 0, 0, _fWrite);
WCHAR *wszGarbage = L"Intentionally written garbage. No part of Indexing Service should choke on this! Contact NTQUERY.";
Win4Assert(wcslen(wszGarbage) <= min(COMMON_PAGE_SIZE, _sizeLow));
wcscpy((WCHAR *)sbuf.Get(), wszGarbage);
Flush(sbuf, min(COMMON_PAGE_SIZE, _sizeLow) );
Unmap(sbuf);
}
#endif
}
}
else
{
ciDebugOut (( DEB_ITRACE, "Open failed on MM Stream; GetLastError()=0x%x\n",
GetLastError() ));
}
} //Open
//+-------------------------------------------------------------------------
//
// Member: CMmStream::Close, public
//
// Synopsis: Create all handles
//
// History: 10-Mar-93 BartoszM Created
//
//--------------------------------------------------------------------------
void CMmStream::Close()
{
#if CIDBG == 1
if ( _cMap > 0 )
{
ciDebugOut(( DEB_ERROR, "Closing stream with %u open maps\n", _cMap ));
}
Win4Assert( _cMap == 0 );
#endif // CIDBG == 1
if (Ok())
{
if(_hMap )
{
if ( !CloseHandle(_hMap))
{
ciDebugOut (( DEB_ERROR, "Closing file mapping failed\n" ));
THROW( CException() );
}
_hMap = 0;
}
if ( !CloseHandle ( _hFile ))
{
ciDebugOut (( DEB_ERROR, "Closing file handle failed\n" ));
THROW( CException() );
}
_hFile = INVALID_HANDLE_VALUE;
}
} //Close
//+-------------------------------------------------------------------------
//
// Member: CMmStream::SetSize, public
//
// Synopsis: Increase the size of the (writable) file
//
// Arguments: [storage] -- storage (not used)
// [newSizeLow] -- Low 32 bits of filesize
// [newSizeHigh] -- High 32 bits of filesize
//
// History: 10-Mar-93 BartoszM Created
// 26-Oct-98 KLam Make sure there is enough disk space
// 03-Dec-98 KLam Don't close the stream if there isn't
// enough disk space
//
//--------------------------------------------------------------------------
#if CIDBG == 1
BOOL g_fFailMMFExtend = FALSE;
#endif // CIDBG == 1
void CMmStream::SetSize(
PStorage & storage,
ULONG newSizeLow,
ULONG newSizeHigh )
{
Win4Assert( !_fIsReadOnly );
Win4Assert( TRUE == _fWrite );
LARGE_INTEGER sizeOld = { _sizeLow, _sizeHigh };
LARGE_INTEGER sizeNew = { newSizeLow, newSizeHigh };
if ( sizeNew.QuadPart < sizeOld.QuadPart && 0 != _cMap )
{
//
// Fail silently to truncate the file if there are open views. This
// will happen at the end of a master merge on the new index if the
// index is being used by a query cursor. So we waste a little
// disk space. Leave the map open.
//
return;
}
//
// Make sure there is enough space on the disk if growing the file
//
if ( sizeNew.QuadPart > sizeOld.QuadPart )
{
Win4Assert ( !_xDriveInfo.IsNull() );
__int64 sizeRemaining, sizeTotal;
_xDriveInfo->GetDiskSpace ( sizeTotal, sizeRemaining );
if ( sizeRemaining < ( sizeNew.QuadPart - sizeOld.QuadPart ))
{
ciDebugOut(( DEB_ERROR,
"CMmStream::SetSize -- Not enogh disk space, need %I64d have %I64d\n",
sizeNew.QuadPart - sizeOld.QuadPart, sizeRemaining ));
THROW( CException( CI_E_CONFIG_DISK_FULL ) );
}
}
//
// Free the mapping before trying to shrink the file.
//
if ( 0 != _hMap )
{
HANDLE hMap = _hMap;
_hMap = 0;
CloseHandle( hMap );
}
DWORD dwErr = NO_ERROR;
if (sizeNew.QuadPart < sizeOld.QuadPart)
{
if ( SetFilePointer ( _hFile,
newSizeLow,
(long *)&newSizeHigh,
FILE_BEGIN ) == 0xFFFFFFFF &&
GetLastError() != NO_ERROR )
{
dwErr = GetLastError();
ciDebugOut(( DEB_ERROR,
"CMmStream::SetSize -- SetFilePointer returned %d\n",
dwErr ));
}
if ( NO_ERROR == dwErr && !SetEndOfFile( _hFile ) )
{
dwErr = GetLastError();
ciDebugOut(( DEB_ERROR,
"CMmStream::SetSize -- SetEndOfFile returned %d\n",
dwErr ));
}
}
//
// Restore the mapping or grow the file
//
_hMap = CreateFileMapping( _hFile,
0, // security
PAGE_READWRITE,
newSizeHigh,
newSizeLow,
0 ); // name
#if CIDBG == 1
if ( g_fFailMMFExtend && 0 != _hMap )
{
CloseHandle( _hMap );
_hMap = 0;
SetLastError( ERROR_HANDLE_DISK_FULL );
g_fFailMMFExtend = FALSE;
}
#endif // CIDBG == 1
if ( 0 == _hMap )
{
dwErr = GetLastError();
//
// ok, so we can't grow the file and we're going to throw. At
// least try to restore the mapping. Ignore if this fails.
//
_hMap = CreateFileMapping( _hFile,
0, // security
PAGE_READWRITE,
_sizeHigh,
_sizeLow,
0 ); // name
}
if ( NO_ERROR != dwErr )
THROW( CException( HRESULT_FROM_WIN32( dwErr ) ) );
if ( sizeOld.QuadPart != sizeNew.QuadPart )
FlushMetaData( TRUE );
_sizeLow = newSizeLow;
_sizeHigh = newSizeHigh;
} //SetSize
//+-------------------------------------------------------------------------
//
// Member: CMmStream::MapAll, public
//
// Synopsis: Create file mapping
//
// Arguments:
//
// History:
//
//--------------------------------------------------------------------------
void CMmStream::MapAll ( CMmStreamBuf& sbuf )
{
Win4Assert ( SizeHigh() == 0 );
Map( sbuf, SizeLow(), 0, 0 );
}
//+-------------------------------------------------------------------------
//
// Member: CMmStream::Map, private
//
// Synopsis: Create file mapping
//
// Arguments: [sbuf] -- the stream buffer used to record the view
// [cb] -- size of the mapped area
// [offLow] -- low part of file offset
// [offHigh] -- high part of file offset
// [fMapForWrite] --
//
// History: 10-Mar-93 BartoszM Created
//
//--------------------------------------------------------------------------
void CMmStream::Map( CMmStreamBuf & sbuf,
ULONG cb,
ULONG offLow,
ULONG offHigh,
BOOL fMapForWrite )
{
//
// In success paths, the file would be mapped right now. But if there
// was a failure earlier, attempt to restore the file mapping now.
//
if ( 0 == _hMap )
{
Win4Assert( INVALID_HANDLE_VALUE != _hFile );
// Extend the file if necessary and remap it
if ( fMapForWrite )
{
LARGE_INTEGER li;
li.LowPart = offLow;
li.HighPart = offHigh;
li.QuadPart += cb;
SetSize( *(PStorage *) 0, li.LowPart, li.HighPart );
}
else
{
// Try to re-create the mapping
_hMap = CreateFileMapping( _hFile,
0, // security
_fWrite ? PAGE_READWRITE : PAGE_READONLY,
_sizeHigh,
_sizeLow,
0 ); // name
if ( 0 == _hMap )
THROW( CException() );
}
}
Win4Assert( 0 != _hMap );
//
// The file can be writable, but if the map is for READ, don't try
// mapping beyond the end of the file.
//
if ( !fMapForWrite )
{
//
// Adjust size to be min( cb, sizeoffile - off )
//
LARGE_INTEGER size = { _sizeLow, _sizeHigh };
LARGE_INTEGER off = { offLow, offHigh };
LARGE_INTEGER licb = { cb, 0 };
LARGE_INTEGER diff;
diff.QuadPart = size.QuadPart - off.QuadPart;
if ( diff.QuadPart < licb.QuadPart )
{
cb = diff.LowPart;
ciDebugOut(( DEB_ITRACE,
"CMmStream::Map -- reducing map to 0x%x bytes\n",
cb ));
}
}
Win4Assert( 0 != _hMap );
if ( 0 == offHigh && 0 == _sizeHigh )
{
Win4Assert( offLow < _sizeLow );
Win4Assert( offLow + cb <= _sizeLow );
}
void* buf = MapViewOfFile( _hMap,
_fWrite ? FILE_MAP_WRITE : FILE_MAP_READ,
offHigh,
offLow,
cb );
if ( 0 == buf )
{
ciDebugOut(( DEB_ERROR,
"CMmStream::Map -- MapViewOfFile returned %d\n",
GetLastError() ));
THROW( CException() );
}
sbuf.SetBuf( buf );
sbuf.SetSize ( cb );
sbuf.SetStream ( this );
_cMap++;
} //Map
//+-------------------------------------------------------------------------
//
// Member: CMmStream::Unmap, public
//
// Synopsis: Unmap the view of file
//
// History: 10-Mar-93 BartoszM Created
//
//--------------------------------------------------------------------------
void CMmStream::Unmap( CMmStreamBuf& sbuf )
{
//
// Note that UnmapViewOfFile doesn't take a _hMap, and the _hMap used
// to map the view may already have been closed (but is kept open by
// the refcount due to the view). The current _hMap may be different
// than the one used to create the view.
//
if ( _cMap > 0 )
{
if ( !UnmapViewOfFile( sbuf.Get() ) )
{
ciDebugOut(( DEB_ERROR, "UnmapViewOfFile returned %d\n",
GetLastError() ));
//
// don't throw! -- unmap is called from destructors and can fail
// if the system is really busy
//
}
_cMap--;
}
sbuf.SetBuf( 0 );
} //Unmap
//+-------------------------------------------------------------------------
//
// Member: CMmStream::Flush, public
//
// Synopsis: Flush the view back to disk
//
// History: 10-Mar-93 BartoszM Created
// 04-Mar-98 KitmanH Only flush if CMmStream is writable
//
//--------------------------------------------------------------------------
void CMmStream::Flush( CMmStreamBuf& sbuf, ULONG cb, BOOL fThrowOnFailure )
{
if ( _fWrite )
{
BOOL fOk = FlushViewOfFile( sbuf.Get(), cb );
if ( !fOk )
{
ciDebugOut(( DEB_WARN,
"FlushViewOfFile failed, error %d, throwing: %d\n",
GetLastError(), fThrowOnFailure ));
if ( fThrowOnFailure )
{
//Win4Assert( !"FlushViewOfFile failed!" );
THROW( CException() );
}
}
Win4Assert( INVALID_HANDLE_VALUE != _hFile );
}
} //Flush
//+-------------------------------------------------------------------------
//
// Member: CMmStream::FlushMetaData, public
//
// Synopsis: Flush any and all metadata for a file to disk
//
// History: 5-Mar-01 dlee Created
//
//--------------------------------------------------------------------------
void CMmStream::FlushMetaData( BOOL fThrowOnFailure )
{
if ( _fWrite )
{
//
// FlushViewOfFile writes the contents of the file to disk.
// FlushFileBuffers writes the metadata to disk.
//
if ( INVALID_HANDLE_VALUE != _hFile )
{
BOOL fOk = FlushFileBuffers( _hFile );
if ( !fOk && fThrowOnFailure )
{
THROW( CException() );
}
}
}
} //FlushMetaData
//+-------------------------------------------------------------------------
//
// Member: CMmStream::ShrinkFromFront, public
//
// Synopsis: Decommits the front part of a file
//
// Arguments: [iFirstPage] -- the first 4k page to decommit
// [cPages] -- # of 4k pages to decommit
//
// Returns: # of 4k pages actually shrunk
//
// History: 4-Nov-97 dlee Created
//
//--------------------------------------------------------------------------
ULONG CMmStream::ShrinkFromFront( ULONG iFirstPage, ULONG cPages )
{
ciDebugOut(( DEB_ITRACE, "SFF attempt start 4k page 0x%x, 0x%x pages\n",
iFirstPage, cPages ));
Win4Assert( INVALID_HANDLE_VALUE != _hFile );
Win4Assert( !_fIsReadOnly );
Win4Assert( _fWrite );
if ( !_fSparse )
return 0;
if ( 0 == cPages )
return 0;
//
// When NTFS/MM check in support for zeroing part of a file
// while another part of the file has a mapped view open, change this
// code to not check _cMap and to not close/repoen _hMap.
//
// If there are open views on the mapping, we can't shrink since NTFS
// doesn't support this. Views will be open if there are active queries
// using the current master index.
//
if ( 0 != _cMap )
{
ciDebugOut(( DEB_ITRACE, "can't SFF, %d mappings are open\n", _cMap ));
return 0;
}
//
// Close the mapping; NTFS doesn't allow shrinking with an open map
//
if ( 0 != _hMap )
{
CloseHandle( _hMap );
_hMap = 0;
}
//
// Send the fsctl to do the truncation on 64k boundaries.
//
cPages = ( cPages * 16 ) / 16;
if ( 0 == cPages )
return 0;
FILE_ZERO_DATA_INFORMATION zeroInfo;
zeroInfo.FileOffset.QuadPart = ( (LONGLONG) iFirstPage ) * 4096;
zeroInfo.BeyondFinalZero.QuadPart = ( (LONGLONG) iFirstPage + cPages ) * 4096;
IO_STATUS_BLOCK ioStatusBlock;
CEventSem evt;
evt.Reset();
NTSTATUS s = NtFsControlFile( _hFile,
evt.GetHandle(),
0, 0,
&ioStatusBlock,
FSCTL_SET_ZERO_DATA,
&zeroInfo,
sizeof zeroInfo,
0, 0 );
//
// NOTE: If this fails, do we really care? Sure, we'll be wasting disk
// space, but why abort the master merge because of this? Let's wait
// and see when/if it can fail.
//
if ( NT_ERROR( s ) )
{
ciDebugOut(( DEB_WARN, "set zero data failed 0x%x\n", s ));
THROW( CException( s ) );
}
if ( STATUS_PENDING == s )
{
// wait for the io to complete
ciDebugOut(( DEB_ITRACE, "SFF pending\n" ));
evt.Wait();
s = STATUS_SUCCESS;
}
ciDebugOut(( DEB_ITRACE, "SFF succeeded\n" ));
//
// Re-open the mapping
//
_hMap = CreateFileMapping( _hFile,
0, // security
PAGE_READWRITE,
_sizeHigh,
_sizeLow,
0 );
if ( 0 == _hMap )
{
DWORD dw= GetLastError();
ciDebugOut(( DEB_WARN, "can't re-establish the map in SFF %d\n", dw ));
Close();
THROW( CException( HRESULT_FROM_WIN32( dw ) ) );
}
return cPages;
} //ShrinkFromFront
//+-------------------------------------------------------------------------
//
// Member: CMmStream::Read, public
//
// Synopsis: Reads from the stream
//
// Arguments: [pvBuffer] -- Where the read data will go. This buffer
// must be 0-filled for the full cbToRead.
// [oStart] -- Offset in the file where the read starts
// [cbToRead] -- # of bytes to read
// [cbRead] -- Returns the # of bytes actually read
//
// History: 30-Oct-98 dlee Created
//
// Notes: Win32 does not guarantee coherence if you mix read/write and
// mapped IO in the same part of the file.
//
//--------------------------------------------------------------------------
void CMmStream::Read(
void * pvBuffer,
ULONGLONG oStart,
DWORD cbToRead,
DWORD & cbRead )
{
ciDebugOut(( DEB_ITRACE,
"ReadFile %#x, into %#p, offset %#I64x cb, %d\n",
_hFile, pvBuffer, oStart, cbToRead ));
//
// This function takes a zero-filled buffer. If the buffer isn't zero-
// filled, the caller has a bug. Assume 8-byte alignment.
//
#if CIDBG == 1
Win4Assert( 0 == ( ( (ULONG_PTR) pvBuffer ) & 7 ) );
Win4Assert( 0 == ( cbToRead & 7 ) );
LONGLONG *pll = (LONGLONG *) pvBuffer;
unsigned cll = cbToRead / sizeof LONGLONG;
for ( unsigned i = 0; i < cll; i++, pll++ )
Win4Assert( 0 == *pll );
#endif // CIDBG == 1
_sizeLow = GetFileSize( _hFile, &_sizeHigh );
if ( 0xffffffff == _sizeLow &&
NO_ERROR != GetLastError() )
THROW( CException() );
//
// Always read the amount requested, even if it doesn't exist on disk
//
cbRead = cbToRead;
LARGE_INTEGER liSize;
liSize.LowPart = _sizeLow;
liSize.HighPart = _sizeHigh;
//
// Master merge asks for buffers after the end of the file. The file
// will be extended at when the buffer is written.
//
if ( liSize.QuadPart <= (LONGLONG) oStart )
return;
//
// Truncate the read if the file isn't as large as requested
//
if ( liSize.QuadPart < (LONGLONG) ( oStart + cbToRead ) )
cbToRead = (DWORD) ( liSize.QuadPart - oStart );
//
// The file is opened async, so the IO can be pending
//
LARGE_INTEGER li;
li.QuadPart = oStart;
CEventSem evt;
OVERLAPPED o;
o.Offset = li.LowPart;
o.OffsetHigh = li.HighPart;
o.hEvent = evt.GetHandle();
DWORD cbFromFile;
if ( !ReadFile( _hFile,
pvBuffer,
cbToRead,
&cbFromFile,
&o ) )
{
if ( ERROR_IO_PENDING == GetLastError() )
{
if ( ! GetOverlappedResult( _hFile, &o, &cbFromFile, TRUE ) )
THROW( CException() );
}
else
THROW( CException() );
}
Win4Assert( cbFromFile == cbToRead );
} //Read
//+-------------------------------------------------------------------------
//
// Member: CMmStream::Write, public
//
// Synopsis: Writes to the stream
//
// Arguments: [pvBuffer] -- The data to write
// [oStart] -- Offset in the file where the write starts
// [cbToWrite] -- # of bytes to write
//
// History: 30-Oct-98 dlee Created
// 23-Nov-98 KLam Check for enough disk space
// 03-Dec-98 KLam Don't close the stream if there isn't
// enough disk space
//
// Notes: Win32 does not guarantee coherence if you mix read/write and
// mapped IO in the same part of the file.
//
//--------------------------------------------------------------------------
void CMmStream::Write(
void * pvBuffer,
ULONGLONG oStart,
DWORD cbToWrite )
{
ciDebugOut(( DEB_ITRACE,
"WriteFile %#x, from %#p, offset %#I64x cb, %d\n",
_hFile, pvBuffer, oStart, cbToWrite ));
//
// Make sure there is enough disk space
//
__int64 cbNewSize = oStart + (__int64) cbToWrite;
LARGE_INTEGER sizeCurrent = { _sizeLow, _sizeHigh };
if ( cbNewSize > sizeCurrent.QuadPart )
{
__int64 cbTotal, cbRemaining;
Win4Assert ( !_xDriveInfo.IsNull() );
_xDriveInfo->GetDiskSpace ( cbTotal, cbRemaining );
if ( (cbNewSize - sizeCurrent.QuadPart) > cbRemaining )
{
ciDebugOut(( DEB_ERROR,
"CMmStream::Write -- Not enogh disk space, need %I64d have %I64d\n",
cbNewSize - sizeCurrent.QuadPart, cbRemaining ));
THROW( CException( CI_E_CONFIG_DISK_FULL ) );
}
}
//
// The file is opened async, so the IO can be pending
//
LARGE_INTEGER li;
li.QuadPart = oStart;
CEventSem evt;
OVERLAPPED o;
o.Offset = li.LowPart;
o.OffsetHigh = li.HighPart;
o.hEvent = evt.GetHandle();
DWORD cbWritten;
if ( !WriteFile( _hFile,
pvBuffer,
cbToWrite,
&cbWritten,
&o ) )
{
if ( ERROR_IO_PENDING == GetLastError() )
{
if ( ! GetOverlappedResult( _hFile, &o, &cbWritten, TRUE ) )
THROW( CException() );
}
else
THROW( CException() );
}
_sizeLow = GetFileSize( _hFile, &_sizeHigh );
if ( 0xffffffff == _sizeLow &&
NO_ERROR != GetLastError() )
THROW( CException() );
} //Write