1029 lines
34 KiB
C++
1029 lines
34 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1997 - 2000.
|
|
//
|
|
// File: propbkp.cxx
|
|
//
|
|
// Contents: Property store backup
|
|
//
|
|
// Classes: CPropStoreBackupStream
|
|
//
|
|
// History: 31-May-97 KrishnaN Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <cistore.hxx>
|
|
#include <propbkp.hxx>
|
|
|
|
// CPropStoreBackupStream class implementation
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CPropStoreBackupStream, private
|
|
//
|
|
// Synopsis: Constructor.
|
|
//
|
|
// Arguments: [cMegToLeaveOnDisk] -- Number of megabytes not to write to
|
|
//
|
|
// Returns: None
|
|
//
|
|
// History: 30-May-97 KrishnaN Created
|
|
// 20-Nov-98 KLam Added cMegToLeaveOnDisk
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CPropStoreBackupStream::CPropStoreBackupStream( ULONG cMegToLeaveOnDisk ) :
|
|
_hFile( INVALID_HANDLE_VALUE ),
|
|
_cPages( 0 ),
|
|
_pSector( 0 ),
|
|
_ulCurrentSector( invalidSector ),
|
|
_cFileSizeInBytes( 0 ),
|
|
_fOpenForRecovery( FALSE ),
|
|
_pBigBuffer( 0 ),
|
|
_cMegToLeaveOnDisk ( cMegToLeaveOnDisk ),
|
|
#if CIDBG
|
|
_cPagesBackedUp( 0 ),
|
|
_cPagesCommited( 0 ),
|
|
_cFlushes( 0 ),
|
|
_cFieldsCommited( 0 ),
|
|
#endif // CIDBG
|
|
_pageTable( CI_PROPERTY_STORE_BACKUP_SIZE_DEFAULT )
|
|
{
|
|
RtlZeroMemory(&_header, sizeof(SHeader));
|
|
_header.cMaxPages = CI_PROPERTY_STORE_BACKUP_SIZE_DEFAULT;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ~CPropStoreBackupStream, public
|
|
//
|
|
// Synopsis: Desctructor.
|
|
//
|
|
// Arguments: None
|
|
//
|
|
// Returns: None
|
|
//
|
|
// History: 30-May-97 KrishnaN Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CPropStoreBackupStream::~CPropStoreBackupStream()
|
|
{
|
|
Close();
|
|
delete[] _pBigBuffer;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: OpenForBackup, public
|
|
//
|
|
// Synopsis: Opens the backup stream for backup (write).
|
|
//
|
|
// Arguments: [path] - file path
|
|
// [modeShare] -- sharing mode
|
|
// [modeCreate] -- create mode
|
|
// [ulMaxPages] -- Max # of pages to backup
|
|
//
|
|
// Returns: None
|
|
//
|
|
// History: 30-May-97 KrishnaN Created
|
|
// 18-Nov-98 KLam Instantiate volume information
|
|
//
|
|
// Notes: This file should be opened with FILE_FLAG_NO_BUFFERING because
|
|
// everything written to it should be immediately written to disk.
|
|
// We don't need to read this file often, so we don't need any
|
|
// read caching (so we don't use FILE_FLAGWrite_THROUGH).
|
|
//
|
|
// Files opened with FILE_FLAG_NO_BUFFERING should always
|
|
// write in increments of the volume sector size and should always
|
|
// start writing on sector boundaries.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CPropStoreBackupStream::OpenForBackup( const WCHAR* wcsPath,
|
|
ULONG modeShare,
|
|
ULONG modeCreate,
|
|
ULONG ulMaxPages)
|
|
{
|
|
Win4Assert(!IsOpen());
|
|
Win4Assert ( _xDriveInfo.IsNull() );
|
|
|
|
_xDriveInfo.Set ( new CDriveInfo ( wcsPath, _cMegToLeaveOnDisk ));
|
|
|
|
_hFile = CreateFile(wcsPath,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
modeShare,
|
|
NULL,
|
|
modeCreate,
|
|
FILE_FLAG_NO_BUFFERING | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
|
|
NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE == _hFile)
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"CPropStoreBackupStream::OpenForBackup -- CreateFile on %ws returned %d\n",
|
|
wcsPath, GetLastError() ));
|
|
THROW( CException() );
|
|
}
|
|
|
|
_fOpenForRecovery = FALSE;
|
|
|
|
GetSystemParams();
|
|
|
|
Win4Assert(0 == _pSector && invalidSector == _ulCurrentSector);
|
|
_pBigBuffer = new BYTE[2*_header.ulSectorSize];
|
|
// get a buffer that starts at sector size aligned address
|
|
_pSector = (PBYTE)( (((ULONG_PTR)_pBigBuffer + _header.ulSectorSize) / _header.ulSectorSize) * _header.ulSectorSize);
|
|
Win4Assert( ((ULONG_PTR)_pSector % _header.ulSectorSize) == 0);
|
|
|
|
//
|
|
// If this file is being created from scratch, we should claim all
|
|
// the space we need to backup _header.cMaxPages number of pages.
|
|
//
|
|
|
|
if (CREATE_ALWAYS == modeCreate || CREATE_NEW == modeCreate ||
|
|
OPEN_ALWAYS == modeCreate || TRUNCATE_EXISTING == modeCreate)
|
|
Reset(ulMaxPages);
|
|
else
|
|
{
|
|
ReadSector(0, _pSector);
|
|
RtlCopyMemory(&_header, _pSector, sizeof(SHeader));
|
|
}
|
|
|
|
ciDebugOut((DEB_PROPSTORE, "Successfully created/opened backup file.\n"
|
|
"Sector size: %d, Page size: %d, Max pages: %d\n",
|
|
_header.ulSectorSize, _header.ulPageSize, _header.cMaxPages));
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: OpenForRecovery, public
|
|
//
|
|
// Synopsis: Opens the backup stream for recovery (read).
|
|
//
|
|
// Arguments: [path] - file path
|
|
// [modeShare] -- sharing mode
|
|
//
|
|
// Returns: None
|
|
//
|
|
// History: 30-May-97 KrishnaN Created
|
|
// 18-Nov-98 KLam Instantiate volume info
|
|
//
|
|
// Notes: This file is opened for reading and should be opened with page
|
|
// caching enabled (normal behavior) so we can read arbitrary lengths
|
|
// of data starting at arbitrary points in the backup stream.
|
|
//
|
|
// This file could have been copied from a different architecture,
|
|
// so we cannot assume that the page size hard coded in this file
|
|
// will be the same as the page size used by the architecture. Get
|
|
// the page size from the directory section and use that to restore
|
|
// the corresponding sections of the property store.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CPropStoreBackupStream::OpenForRecovery ( const WCHAR* wcsPath,
|
|
ULONG modeShare)
|
|
{
|
|
Win4Assert(!IsOpen());
|
|
Win4Assert ( _xDriveInfo.IsNull() );
|
|
|
|
_xDriveInfo.Set ( new CDriveInfo ( wcsPath, _cMegToLeaveOnDisk ));
|
|
|
|
_hFile = CreateFile(wcsPath,
|
|
GENERIC_READ,
|
|
modeShare,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE == _hFile)
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"CPropStoreBackupStream::OpenForRecovery -- CreateFile on %ws returned %d\n",
|
|
wcsPath, GetLastError() ));
|
|
THROW( CException() );
|
|
}
|
|
_fOpenForRecovery = TRUE;
|
|
|
|
if (IsCorrupt())
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"CPropStoreBackupStream::OpenForRecovery -- Propstore backup file is corrupt\n"));
|
|
THROW( CException(CI_CORRUPT_DATABASE) );
|
|
}
|
|
|
|
ReadFromFile(0, sizeof(SHeader), &_header);
|
|
|
|
Win4Assert(0 == _pSector && invalidSector == _ulCurrentSector);
|
|
_pBigBuffer = new BYTE[2*_header.ulSectorSize];
|
|
// get a buffer that starts at sector size aligned address
|
|
_pSector = (PBYTE)( (((ULONG_PTR)_pBigBuffer + _header.ulSectorSize) / _header.ulSectorSize) * _header.ulSectorSize);
|
|
|
|
Win4Assert( ((ULONG_PTR)_pSector % _header.ulSectorSize) == 0);
|
|
|
|
_cPages = CountPages();
|
|
|
|
ciDebugOut((DEB_PROPSTORE, "Successfully opened backup file for recovery.\n"
|
|
"Sector size: %d, Page size: %d, Max pages: %d, pages: %d\n",
|
|
_header.ulSectorSize, _header.ulPageSize,
|
|
_header.cMaxPages, _cPages));
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Close, public
|
|
//
|
|
// Synopsis: Closes the backup stream.
|
|
//
|
|
// Arguments: None
|
|
//
|
|
// Returns: None
|
|
//
|
|
// History: 30-May-97 KrishnaN Created
|
|
// 18-Nov-98 KLam Freed volume info
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CPropStoreBackupStream::Close()
|
|
{
|
|
//CLock lock(_mtxWrite);
|
|
|
|
if (IsOpen())
|
|
{
|
|
CloseHandle(_hFile);
|
|
_hFile = INVALID_HANDLE_VALUE;
|
|
_xDriveInfo.Free();
|
|
}
|
|
|
|
#if CIDBG
|
|
ciDebugOut((DEB_PSBACKUP, "Close: Closed backup. Pages backed up = %d, committed = %d, "
|
|
"fields commited = %d, and backup flushed %d times.\n"
|
|
"Percentage of times backed up pages were committed = %d.\n",
|
|
_cPagesBackedUp, _cPagesCommited, _cFieldsCommited, _cFlushes,
|
|
_cPagesCommited*100/(_cPagesBackedUp?_cPagesBackedUp:1)));
|
|
_cPagesBackedUp = _cPagesCommited = _cFlushes = 0;
|
|
#endif // CIDBG
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Reset, public
|
|
//
|
|
// Synopsis: Resets the backup stream. Typically called after the property
|
|
// store is flushed.
|
|
//
|
|
// Arguments: None
|
|
//
|
|
// Returns: None
|
|
//
|
|
// History: 30-May-97 KrishnaN Created
|
|
// 29-Oct-98 KLam Check for disk space before extending file
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CPropStoreBackupStream::Reset(ULONG cMaxPages)
|
|
{
|
|
//CLock lock(_mtxWrite);
|
|
|
|
Win4Assert(IsOpen() && !IsOpenForRecovery());
|
|
|
|
ciDebugOut((DEB_PSBACKUP, "Reset: Pages backed up = %d, committed = %d, "
|
|
"fields commited = %d, and backup flushed %d times.\n"
|
|
"Percentage of times backed up pages were committed = %d.\n",
|
|
_cPagesBackedUp, _cPagesCommited, _cFieldsCommited, ++_cFlushes,
|
|
_cPagesCommited*100/(_cPagesBackedUp?_cPagesBackedUp:1)));
|
|
|
|
//
|
|
// Compute the total size of the backup file. It is the sum of
|
|
// space needed for directory section and the data section.
|
|
// Directory section contains the header, SHeader, followed by
|
|
// _header.cMaxPages slots (ULONG). All structures are sector
|
|
// aligned. The header is treated as multiple slots.
|
|
//
|
|
|
|
// Enforce ranges
|
|
if (cMaxPages < CI_PROPERTY_STORE_BACKUP_SIZE_MIN)
|
|
_header.cMaxPages = CI_PROPERTY_STORE_BACKUP_SIZE_MIN;
|
|
else if (cMaxPages > CI_PROPERTY_STORE_BACKUP_SIZE_MAX)
|
|
_header.cMaxPages = CI_PROPERTY_STORE_BACKUP_SIZE_MAX;
|
|
else
|
|
_header.cMaxPages = cMaxPages;
|
|
|
|
_header.ulDataOffset = _header.ulSectorSize *
|
|
roundup(ComputePageDescriptorOffset(_header.ulSectorSize, _header.cMaxPages),
|
|
_header.ulSectorSize);
|
|
ULONG cbNewFileSize = _header.ulDataOffset + _header.cMaxPages*_header.ulPageSize;
|
|
|
|
//
|
|
// If the file is growing, make sure there is enough space on disk
|
|
//
|
|
if ( cbNewFileSize > _cFileSizeInBytes )
|
|
{
|
|
Win4Assert ( !_xDriveInfo.IsNull() );
|
|
__int64 cbTotal, cbRemaining;
|
|
_xDriveInfo->GetDiskSpace( cbTotal, cbRemaining );
|
|
if ( cbRemaining < ( cbNewFileSize - _cFileSizeInBytes ) )
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"CPropStoreBackupStream::Reset -- Not enough disk space, need %d more bytes\n",
|
|
(cbNewFileSize - _cFileSizeInBytes) - cbRemaining ));
|
|
THROW( CException( CI_E_CONFIG_DISK_FULL ) );
|
|
}
|
|
}
|
|
|
|
_cFileSizeInBytes = cbNewFileSize;
|
|
|
|
if ( SetFilePointer ( _hFile,
|
|
_cFileSizeInBytes,
|
|
0,
|
|
FILE_BEGIN ) == 0xFFFFFFFF &&
|
|
GetLastError() != NO_ERROR )
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"CPropStoreBackupStream::Reset -- SetFilePointer returned %d\n",
|
|
GetLastError() ));
|
|
THROW( CException() );
|
|
}
|
|
|
|
if ( !SetEndOfFile( _hFile ) )
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"CPropStoreBackupStream::Reset -- SetEndOfFile returned %d\n",
|
|
GetLastError() ));
|
|
THROW( CException() );
|
|
}
|
|
|
|
ciDebugOut(( DEB_PSBACKUP, "Reset: Backup has %d maxpages, is %d bytes, and data page begins at offset %d (0x%x)\n",
|
|
_header.cMaxPages, _cFileSizeInBytes, _header.ulDataOffset, _header.ulDataOffset));
|
|
|
|
// clear the hash table of all pages and init page count to 0
|
|
_pageTable.DeleteAllEntries();
|
|
_cPages = 0;
|
|
|
|
Init();
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ReadPage, public
|
|
//
|
|
// Synopsis: Read the i-th page. The page buffer is assumed to be the
|
|
// size of the operating system page.
|
|
//
|
|
// Arguments: [ulPage] -- i-th page (0 based index) in backup to be read.
|
|
// [pulLoc] -- Buffer to return the page's loc in prop store.
|
|
// [pbPage] -- buffer to copy the page to. Contents undefined
|
|
// if FALSE is returned.
|
|
//
|
|
// Returns: TRUE if the page was successfully read.
|
|
// FALSE otherwise.
|
|
//
|
|
// History: 30-May-97 KrishnaN Created
|
|
//
|
|
// Notes: This function is not re-entrant. It will only be called for recovery
|
|
// and one page will be read at a time. To make this re-entrant
|
|
// allocate the buffer used for the sector on the stack.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CPropStoreBackupStream::ReadPage(ULONG ulPage, ULONG *pulLoc, void *pbPage)
|
|
{
|
|
Win4Assert(pbPage && pulLoc);
|
|
|
|
// Copy the ulPage of the slot to pulLoc.
|
|
*pulLoc = GetPageLocation(ulPage);
|
|
if (invalidPage == *pulLoc)
|
|
return FALSE;
|
|
|
|
// Go to the data page and read it into pbPage
|
|
ReadFromFile(_header.ulDataOffset + ulPage*_header.ulPageSize,
|
|
_header.ulPageSize,
|
|
pbPage);
|
|
|
|
ciDebugOut(( DEB_PSBACKUP, "ReadPage: Successfully read page %d (page %d in backup) into address 0x%x\n",
|
|
*pulLoc, ulPage, pbPage));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: GetPageLocation, public
|
|
//
|
|
// Synopsis: Get the location of the i-th page.
|
|
//
|
|
// Arguments: [nPage] -- i-th page (0 based index) to be read.
|
|
//
|
|
// Returns: The i-th page's location in property store. invalidPage if there is
|
|
// no i-th page..
|
|
//
|
|
// History: 30-May-97 KrishnaN Created
|
|
//
|
|
// Notes: This function is not re-entrant. It will only be called for recovery
|
|
// and one page will be read at a time. To make this re-entrant
|
|
// allocate the buffer used for the sector into a stack variable.
|
|
//
|
|
//+---------------------------------------------------------------------------
|
|
|
|
ULONG CPropStoreBackupStream::GetPageLocation(ULONG nPage)
|
|
{
|
|
Win4Assert(IsOpen() && IsOpenForRecovery() && _pSector && nPage < _cPages);
|
|
|
|
if (nPage >= _cPages)
|
|
return invalidPage;
|
|
|
|
// Get sector containing the page's descriptor and read it in.
|
|
ULONG ulSlotOffset = ComputePageDescriptorOffset(_header.ulSectorSize, nPage);
|
|
ULONG ulSector = ulSlotOffset/_header.ulSectorSize;
|
|
if (_ulCurrentSector != ulSector)
|
|
{
|
|
ReadSector(ulSector, _pSector);
|
|
_ulCurrentSector = ulSector;
|
|
}
|
|
|
|
// Return the page location
|
|
ulSlotOffset %= _header.ulSectorSize;
|
|
return *(ULONG *)(_pSector+ulSlotOffset);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CommitPages, public
|
|
//
|
|
// Synopsis: Check if there is space to add
|
|
//
|
|
// Arguments: [cPages] -- Number of pages.
|
|
// [pSlots] -- Array of page descriptors.
|
|
// [ppvPages] -- Array of page pointers to backup.
|
|
//
|
|
// Returns: TRUE if all pages could be committed. FALSE otherwise.
|
|
//
|
|
// Notes: For efficiency, call this only to commit multiple pages.
|
|
//
|
|
// History: 09-Jun-97 KrishnaN Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CPropStoreBackupStream::CommitPages(ULONG cPages,
|
|
ULONG const *pSlots,
|
|
void const * const * ppvPages)
|
|
{
|
|
// Count pages not in the page table. Ignore duplicates.
|
|
ULONG cMissingPages = 0;
|
|
THashTable<ULONG> missingPageTable(cPages); // to detect duplicates
|
|
ULONG ulPos;
|
|
|
|
for (ULONG i = 0; i < cPages; i++)
|
|
{
|
|
// if it is not in the page table and if it has not already
|
|
// been counted, count it.
|
|
if (!_pageTable.LookUp(pSlots[i], ulPos) && !missingPageTable.LookUp(pSlots[i]))
|
|
{
|
|
missingPageTable.AddEntry(pSlots[i]);
|
|
cMissingPages++;
|
|
}
|
|
}
|
|
|
|
if (0 == cMissingPages)
|
|
return TRUE; // nothing to do
|
|
|
|
// do we have enough space to accomodate the missing pages?
|
|
if (cMissingPages > (MaxPages() - Pages()))
|
|
return FALSE; // not enough space
|
|
|
|
// Commit only pages not already committed
|
|
BOOL fSuccessful = TRUE;
|
|
for (i = 0; i < cPages && fSuccessful; i++)
|
|
{
|
|
// Attempt to commit only pages known to be missing from the page table
|
|
// This saves us some cycles that would otherwise be spent by CommitPage
|
|
// which would have to lookup in a larger hash table to figure out if
|
|
// this page exists.
|
|
|
|
if (missingPageTable.LookUp(pSlots[i]))
|
|
fSuccessful = fSuccessful && CommitPage(pSlots[i], ppvPages[i]);
|
|
}
|
|
|
|
return fSuccessful;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CommitPage, public
|
|
//
|
|
// Synopsis: Append a page at the end of the stream. The page buffer is
|
|
// assumed to be the size of the operating system page. If the
|
|
// page already exists, overwrite it in place.
|
|
//
|
|
// Arguments: [slot] -- Page descriptor for the page.
|
|
// [pbPage] -- Buffer containing contents of the page.
|
|
//
|
|
// Returns: TRUE if the page could be committed, FALSE if it couldn't be.
|
|
//
|
|
// Notes: Call this for single page commits.
|
|
//
|
|
// History: 30-May-97 KrishnaN Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CPropStoreBackupStream::CommitPage(ULONG slot, void const *pbPage)
|
|
{
|
|
//CLock lock(_mtxWrite);
|
|
|
|
Win4Assert(IsOpen() && !IsOpenForRecovery() && _pSector);
|
|
Win4Assert(++_cPagesBackedUp);
|
|
|
|
ULONG ulPos;
|
|
|
|
// Does it already exist in the backup?
|
|
if (_pageTable.LookUp(slot, ulPos))
|
|
return TRUE; // no need to commit again
|
|
|
|
|
|
ciDebugOut(( DEB_PSBACKUP, "CommitPage: Commiting page %d, address 0x%x\n", slot, pbPage));
|
|
|
|
// First write the page to disk.
|
|
BOOL fSuccessful = WriteToFile(_header.ulDataOffset + _cPages*_header.ulPageSize,
|
|
_header.ulPageSize, pbPage);
|
|
if (fSuccessful)
|
|
{
|
|
// Get sector containing the page's descriptor and read it in.
|
|
ULONG ulSlotOffset = ComputePageDescriptorOffset(_header.ulSectorSize, _cPages);
|
|
ULONG ulSector = ulSlotOffset/_header.ulSectorSize;
|
|
if (_ulCurrentSector != ulSector)
|
|
{
|
|
ReadSector(ulSector, _pSector);
|
|
_ulCurrentSector = ulSector;
|
|
}
|
|
|
|
// Copy the page descriptor into the sector and commit it.
|
|
RtlCopyMemory(_pSector + ulSlotOffset%_header.ulSectorSize, &slot, sizeof(ULONG));
|
|
fSuccessful = CommitSector(_ulCurrentSector, _pSector);
|
|
Win4Assert(fSuccessful);
|
|
_pageTable.AddEntry(slot, _cPages);
|
|
|
|
// Now we are truly done commiting the page
|
|
_cPages++;
|
|
|
|
Win4Assert(++_cPagesCommited);
|
|
}
|
|
|
|
return fSuccessful;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CommitField, public
|
|
//
|
|
// Synopsis: Modify a field in place, sector by sector. This is faster than
|
|
// modifying an entire page.
|
|
//
|
|
// Arguments: [ulPage] -- The page to modify
|
|
// [ulOffset] -- Offset in page where modification should begin.
|
|
// [cSize] -- Size, in bytes, of the field to be modified.
|
|
// [pvBuffer] -- Buffer containing the new data to write.
|
|
//
|
|
// Returns: TRUE if the page could be committed, FALSE if it couldn't be.
|
|
//
|
|
// History: 30-May-97 KrishnaN Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CPropStoreBackupStream::CommitField(ULONG ulPage,
|
|
ULONG ulOffset,
|
|
ULONG cSize,
|
|
void const *pvBuffer)
|
|
{
|
|
ULONG ulPos = 0xFFFFFFFF;
|
|
_pageTable.LookUp(ulPage, ulPos);
|
|
Win4Assert(IsOpen() && !IsOpenForRecovery() && _pSector && _pageTable.LookUp(ulPage, ulPos));
|
|
Win4Assert((ulOffset+cSize) <= _header.ulPageSize && pvBuffer);
|
|
Win4Assert(++_cFieldsCommited);
|
|
|
|
Win4Assert(ulPos < _cPages);
|
|
|
|
|
|
//
|
|
// Figure out which sector(s) the field spans and modify them in place
|
|
//
|
|
|
|
ULONG ulSector = ComputeFirstSectorOfPage(ulPos) + ulOffset/_header.ulSectorSize;
|
|
ULONG ulOffsetInSector = ulOffset%_header.ulSectorSize;
|
|
|
|
//
|
|
// The way we backup right now, we cannot have multiple sector writes, so assert
|
|
// that we are acutally writing only one sector. The code to support multiple
|
|
// sector writes, of course, will always be there and doing its job.
|
|
//
|
|
Win4Assert((ulOffsetInSector + cSize) <= _header.ulSectorSize);
|
|
|
|
for (ULONG cBytesCommited = 0; cBytesCommited < cSize; ulSector++)
|
|
{
|
|
_ulCurrentSector = ulSector;
|
|
ReadSector(_ulCurrentSector, _pSector);
|
|
RtlCopyMemory(_pSector + ulOffsetInSector,
|
|
((PBYTE)pvBuffer + cBytesCommited),
|
|
min(cSize-cBytesCommited, _header.ulSectorSize));
|
|
CommitSector(ulSector, _pSector);
|
|
cBytesCommited += min(cSize-cBytesCommited, _header.ulSectorSize);
|
|
|
|
// After the first time, the offset of field in the sector is always 0
|
|
ulOffsetInSector = 0;
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Init, private
|
|
//
|
|
// Synopsis: Initialize the header and directory section of the file.
|
|
//
|
|
// Arguments: None
|
|
//
|
|
// Returns: None
|
|
//
|
|
// History: 04-Jun-97 KrishnaN Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CPropStoreBackupStream::Init()
|
|
{
|
|
//CLock lock(_mtxWrite);
|
|
|
|
Win4Assert(sizeof(SHeader) <= _header.ulSectorSize && _pSector);
|
|
Win4Assert(_pSector);
|
|
|
|
//
|
|
// prepare the header portion of sector 0
|
|
//
|
|
|
|
_ulCurrentSector = 0;
|
|
RtlCopyMemory(_pSector, &_header, sizeof(SHeader));
|
|
ULONG ulNextSlotPtr = ComputePageDescriptorOffset(_header.ulSectorSize, 0); // write next slot here
|
|
|
|
//
|
|
// Prepare the sectors and write them to disk
|
|
//
|
|
|
|
for (ULONG cSlotsWritten = 0; cSlotsWritten < _header.cMaxPages; )
|
|
{
|
|
// Fill up the current sector
|
|
for (; (ulNextSlotPtr + sizeof(ULONG)) <= _header.ulSectorSize &&
|
|
cSlotsWritten < _header.cMaxPages;
|
|
ulNextSlotPtr += sizeof(ULONG), cSlotsWritten++)
|
|
{
|
|
RtlCopyMemory(_pSector+ulNextSlotPtr, &invalidPage, sizeof(ULONG));
|
|
}
|
|
|
|
// Anything to write in the current sector?
|
|
if (ulNextSlotPtr > 0)
|
|
{
|
|
CommitSector(_ulCurrentSector, _pSector);
|
|
|
|
// move to the next sector
|
|
_ulCurrentSector++;
|
|
ulNextSlotPtr = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CommitSector, private
|
|
//
|
|
// Synopsis: Commits ulSector - th sector of the data section to disk.
|
|
//
|
|
// Arguments: [ulSector] -- Sector to commit.
|
|
// [pbBuffer] -- Buffer with sector's data to commit.
|
|
//
|
|
// Returns: None
|
|
//
|
|
// History: 04-Jun-97 KrishnaN Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CPropStoreBackupStream::CommitSector(ULONG ulSector, void const *pbBuffer)
|
|
{
|
|
Win4Assert(IsOpen() &&
|
|
!IsOpenForRecovery() &&
|
|
pbBuffer);
|
|
ciDebugOut(( DEB_PSBACKUP, "CommitSector: About to commit sector %d\n", ulSector));
|
|
|
|
if ( 0 == pbBuffer )
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"CPropStoreBackupStream::CommitSector attempting to write an invalid sector (sector %d, address: %0x8x)\n",
|
|
ulSector, pbBuffer ));
|
|
return FALSE;
|
|
}
|
|
|
|
return WriteToFile(ulSector*_header.ulSectorSize, _header.ulSectorSize, pbBuffer);
|
|
} //CommitSector
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: WriteToFile, private
|
|
//
|
|
// Synopsis: Commits a buffer to disk.
|
|
//
|
|
// Arguments: [ulStartLoc] -- Starting location, relative to beginning of file.
|
|
// [ulNumBytes] -- Number of bytes to commit.
|
|
// [pbBuffer] -- Buffer containing data to commit.
|
|
//
|
|
// Returns: None
|
|
//
|
|
// History: 04-Jun-97 KrishnaN Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CPropStoreBackupStream::WriteToFile(ULONG ulStartLoc, ULONG ulNumBytes,
|
|
void const *pbBuffer)
|
|
{
|
|
Win4Assert(IsOpen() && !IsOpenForRecovery());
|
|
// The buffer should also begin on a sector boundary
|
|
Win4Assert( ((ULONG_PTR)pbBuffer % _header.ulSectorSize) == 0);
|
|
// All writes to this file should begin and end on sector boundaries
|
|
Win4Assert(ulStartLoc%_header.ulSectorSize == 0 && ulNumBytes%_header.ulSectorSize == 0);
|
|
|
|
DWORD dwNumWritten = 0;
|
|
dwNumWritten = SetFilePointer(_hFile, ulStartLoc, 0, FILE_BEGIN);
|
|
if (0xFFFFFFFF == dwNumWritten)
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"CPropStoreBackupStream::WriteToFile -- SetFilePointer returned %d\n",
|
|
GetLastError() ));
|
|
return FALSE;
|
|
}
|
|
Win4Assert(ulStartLoc == dwNumWritten);
|
|
|
|
if (!WriteFile(_hFile, pbBuffer, ulNumBytes, &dwNumWritten, NULL))
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"CPropStoreBackupStream::WriteToFile -- WriteFile returned %d\n",
|
|
GetLastError() ));
|
|
return FALSE;
|
|
}
|
|
Win4Assert(ulNumBytes == dwNumWritten);
|
|
|
|
return TRUE;
|
|
} //WriteToFile
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ReadSector, private
|
|
//
|
|
// Synopsis: Reads specified sector of the data section from disk.
|
|
//
|
|
// Arguments: [ulSector] -- i-th sector of data section to read.
|
|
// [pbBuffer] -- Buffer to hold the sector
|
|
//
|
|
// Returns: None
|
|
//
|
|
// History: 04-Jun-97 KrishnaN Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
inline void CPropStoreBackupStream::ReadSector(ULONG ulSector, PBYTE pbBuffer)
|
|
{
|
|
Win4Assert(IsOpen() && pbBuffer);
|
|
|
|
ReadFromFile(ulSector*_header.ulSectorSize, _header.ulSectorSize, pbBuffer);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ReadFromFile, private
|
|
//
|
|
// Synopsis: Reads specified data from file.
|
|
//
|
|
// Arguments: [ulStartLoc] -- starting location of data to read.
|
|
// [ulNumBytes] -- number of bytes to read.
|
|
// [pbBuffer] -- Buffer to hold the read data.
|
|
//
|
|
// Returns: None
|
|
//
|
|
// History: 04-Jun-97 KrishnaN Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CPropStoreBackupStream::ReadFromFile(ULONG ulStartLoc, ULONG ulNumBytes,
|
|
void *pbBuffer)
|
|
{
|
|
Win4Assert(IsOpen());
|
|
|
|
DWORD dwNumRead = 0;
|
|
dwNumRead = SetFilePointer(_hFile, ulStartLoc, 0, FILE_BEGIN);
|
|
if (0xFFFFFFFF == dwNumRead)
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"CPropStoreBackupStream::ReadFromFile -- SetFilePointer returned %d\n",
|
|
GetLastError() ));
|
|
THROW( CException() );
|
|
}
|
|
Win4Assert(ulStartLoc == dwNumRead);
|
|
|
|
if (!ReadFile(_hFile, pbBuffer, ulNumBytes, &dwNumRead, NULL))
|
|
{
|
|
ciDebugOut(( DEB_ERROR,
|
|
"CPropStoreBackupStream::ReadFromFile -- ReadFile returned %d\n",
|
|
GetLastError() ));
|
|
THROW( CException() );
|
|
}
|
|
Win4Assert(ulNumBytes == dwNumRead);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CountPages, private
|
|
//
|
|
// Synopsis: Counts the pages backed up.
|
|
//
|
|
// Arguments: None
|
|
//
|
|
// Returns: Number of pages in the backup file.
|
|
//
|
|
// History: 04-Jun-97 KrishnaN Created
|
|
//
|
|
// Notes: Assumes that the file is not corrupt. It is the job of IsCorrupt()
|
|
// to detect corruption.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
ULONG CPropStoreBackupStream::CountPages()
|
|
{
|
|
Win4Assert(IsOpen() && IsOpenForRecovery() && !IsCorrupt());
|
|
|
|
//
|
|
// Read in a page descriptor at a time until the first "free" page descriptor
|
|
// slot is found OR until you reach the end of the directory section.
|
|
//
|
|
|
|
ULONG cPages;
|
|
ULONG ulNextSlotPtr = ComputePageDescriptorOffset(_header.ulSectorSize, 0); // read next slot here
|
|
ULONG slot;
|
|
|
|
for (cPages = 0; cPages < _header.cMaxPages; cPages++)
|
|
{
|
|
ReadFromFile(ulNextSlotPtr, sizeof(ULONG), &slot);
|
|
if (invalidPage == slot)
|
|
break;
|
|
|
|
ulNextSlotPtr += sizeof(ULONG);
|
|
}
|
|
|
|
ciDebugOut((DEB_PSBACKUP, "Found %u data pages in property store backup.\n", cPages));
|
|
|
|
return cPages;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: IsCorrupt, private
|
|
//
|
|
// Synopsis: Checks for corruption.
|
|
//
|
|
// Arguments: None
|
|
//
|
|
// Returns: TRUE or FALSE.
|
|
//
|
|
// History: 04-Jun-97 KrishnaN Created
|
|
//
|
|
// Notes: This can only verify the directory section. It cannot
|
|
// validate the contents of the data section.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
BOOL CPropStoreBackupStream::IsCorrupt()
|
|
{
|
|
Win4Assert(IsOpen() && IsOpenForRecovery());
|
|
|
|
SHeader header;
|
|
ReadFromFile(0, sizeof(SHeader), &header);
|
|
|
|
//
|
|
// Remember that the file may have just been copied from a different architecture,
|
|
// so we can't verify against values provided by system calls. They should be
|
|
// powers of 2, so that would be a good check. The page size should also be an
|
|
// integral of sector size. So that would be another good check.
|
|
//
|
|
|
|
if (header.ulPageSize % header.ulSectorSize != 0)
|
|
{
|
|
ciDebugOut((DEB_PSBACKUP, "Page size (%d) in not an integral multiple of sector size (%d).\n",
|
|
header.ulPageSize, header.ulSectorSize));
|
|
return TRUE;
|
|
}
|
|
|
|
if (!IsPowerOf2(header.ulPageSize) || !IsPowerOf2(header.ulSectorSize))
|
|
{
|
|
ciDebugOut((DEB_PSBACKUP, "Page size (%d) in not an integral multiple of sector size (%d).\n",
|
|
header.ulPageSize, header.ulSectorSize));
|
|
return TRUE;
|
|
}
|
|
|
|
// Verify that cMaxPages looks "reasonable"
|
|
if (header.cMaxPages < CI_PROPERTY_STORE_BACKUP_SIZE_MIN || header.cMaxPages > CI_PROPERTY_STORE_BACKUP_SIZE_MAX)
|
|
{
|
|
ciDebugOut((DEB_PSBACKUP, "Max pages in backup file is %d. Should be between %d and %d\n",
|
|
header.cMaxPages, CI_PROPERTY_STORE_BACKUP_SIZE_MIN, CI_PROPERTY_STORE_BACKUP_SIZE_MAX));
|
|
return TRUE;
|
|
}
|
|
|
|
// Verify that ulDataOffset is valid.
|
|
ULONG ulDataOffset = header.ulSectorSize *
|
|
roundup(ComputePageDescriptorOffset(header.ulSectorSize, header.cMaxPages),
|
|
header.ulSectorSize);
|
|
if (header.ulDataOffset != ulDataOffset)
|
|
{
|
|
ciDebugOut((DEB_ERROR, "Data section of backup file should begin at offset %d."
|
|
" Instead, it is %d \n",
|
|
ulDataOffset, header.ulDataOffset));
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Read in a page descriptor at a time until you reach the end of the
|
|
// directory section. Verify that each slot is free or has "reasonable"
|
|
// values. Once a free slot is found, all subsequent slots should also
|
|
// be free.
|
|
//
|
|
|
|
ULONG cPages;
|
|
ULONG ulNextSlotPtr = ComputePageDescriptorOffset(header.ulSectorSize, 0); // read next slot here
|
|
ULONG slot;
|
|
|
|
for (cPages = 0; cPages < header.cMaxPages; cPages++)
|
|
{
|
|
ReadFromFile(ulNextSlotPtr, sizeof(ULONG), &slot);
|
|
if (invalidPage == slot)
|
|
break;
|
|
|
|
// How to validate ulPage of ULONG?
|
|
|
|
ulNextSlotPtr += sizeof(ULONG);
|
|
}
|
|
|
|
// Verify that the remaining slots are free
|
|
for (ulNextSlotPtr += sizeof(ULONG); cPages < header.cMaxPages; cPages++)
|
|
{
|
|
ReadFromFile(ulNextSlotPtr, sizeof(ULONG), &slot);
|
|
if (invalidPage != slot)
|
|
{
|
|
ciDebugOut((DEB_PSBACKUP, "Found an invalid page descriptor in backup file "
|
|
"where a free slot is expected.\n"));
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: GetSystemParams, private
|
|
//
|
|
// Synopsis: Gets the volume sector size and page size.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
// History: 04-Jun-97 KrishnaN Created
|
|
// 18-Nov-98 KLam Removed path parameter, used volume info
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
void CPropStoreBackupStream::GetSystemParams()
|
|
{
|
|
Win4Assert ( !_xDriveInfo.IsNull() );
|
|
_header.ulSectorSize = _xDriveInfo->GetSectorSize();
|
|
|
|
SYSTEM_INFO systemInfo;
|
|
GetSystemInfo(&systemInfo);
|
|
_header.ulPageSize = systemInfo.dwPageSize;
|
|
|
|
if (_header.ulPageSize % _header.ulSectorSize != 0)
|
|
{
|
|
ciDebugOut((DEB_ERROR, "CPropStoreBackupStream::GetSystemParams: Page size (%d) in not an integral multiple of sector size (%d).\n",
|
|
_header.ulPageSize, _header.ulSectorSize));
|
|
THROW( CException(CI_E_STRANGE_PAGEORSECTOR_SIZE) );
|
|
}
|
|
|
|
ciDebugOut(( DEB_PSBACKUP, "GetSystemParams: Volume sector size is %d bytes and system page size is %d (0x%x)",
|
|
_header.ulSectorSize, _header.ulPageSize, _header.ulPageSize));
|
|
}
|
|
|
|
|