|
|
//+---------------------------------------------------------------------------
//
// 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)); }
|