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.

1028 lines
34 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997 - 2000.
  5. //
  6. // File: propbkp.cxx
  7. //
  8. // Contents: Property store backup
  9. //
  10. // Classes: CPropStoreBackupStream
  11. //
  12. // History: 31-May-97 KrishnaN Created
  13. //
  14. //----------------------------------------------------------------------------
  15. #include <pch.cxx>
  16. #pragma hdrstop
  17. #include <cistore.hxx>
  18. #include <propbkp.hxx>
  19. // CPropStoreBackupStream class implementation
  20. //+---------------------------------------------------------------------------
  21. //
  22. // Function: CPropStoreBackupStream, private
  23. //
  24. // Synopsis: Constructor.
  25. //
  26. // Arguments: [cMegToLeaveOnDisk] -- Number of megabytes not to write to
  27. //
  28. // Returns: None
  29. //
  30. // History: 30-May-97 KrishnaN Created
  31. // 20-Nov-98 KLam Added cMegToLeaveOnDisk
  32. //
  33. //----------------------------------------------------------------------------
  34. CPropStoreBackupStream::CPropStoreBackupStream( ULONG cMegToLeaveOnDisk ) :
  35. _hFile( INVALID_HANDLE_VALUE ),
  36. _cPages( 0 ),
  37. _pSector( 0 ),
  38. _ulCurrentSector( invalidSector ),
  39. _cFileSizeInBytes( 0 ),
  40. _fOpenForRecovery( FALSE ),
  41. _pBigBuffer( 0 ),
  42. _cMegToLeaveOnDisk ( cMegToLeaveOnDisk ),
  43. #if CIDBG
  44. _cPagesBackedUp( 0 ),
  45. _cPagesCommited( 0 ),
  46. _cFlushes( 0 ),
  47. _cFieldsCommited( 0 ),
  48. #endif // CIDBG
  49. _pageTable( CI_PROPERTY_STORE_BACKUP_SIZE_DEFAULT )
  50. {
  51. RtlZeroMemory(&_header, sizeof(SHeader));
  52. _header.cMaxPages = CI_PROPERTY_STORE_BACKUP_SIZE_DEFAULT;
  53. }
  54. //+---------------------------------------------------------------------------
  55. //
  56. // Function: ~CPropStoreBackupStream, public
  57. //
  58. // Synopsis: Desctructor.
  59. //
  60. // Arguments: None
  61. //
  62. // Returns: None
  63. //
  64. // History: 30-May-97 KrishnaN Created
  65. //
  66. //----------------------------------------------------------------------------
  67. CPropStoreBackupStream::~CPropStoreBackupStream()
  68. {
  69. Close();
  70. delete[] _pBigBuffer;
  71. }
  72. //+---------------------------------------------------------------------------
  73. //
  74. // Function: OpenForBackup, public
  75. //
  76. // Synopsis: Opens the backup stream for backup (write).
  77. //
  78. // Arguments: [path] - file path
  79. // [modeShare] -- sharing mode
  80. // [modeCreate] -- create mode
  81. // [ulMaxPages] -- Max # of pages to backup
  82. //
  83. // Returns: None
  84. //
  85. // History: 30-May-97 KrishnaN Created
  86. // 18-Nov-98 KLam Instantiate volume information
  87. //
  88. // Notes: This file should be opened with FILE_FLAG_NO_BUFFERING because
  89. // everything written to it should be immediately written to disk.
  90. // We don't need to read this file often, so we don't need any
  91. // read caching (so we don't use FILE_FLAGWrite_THROUGH).
  92. //
  93. // Files opened with FILE_FLAG_NO_BUFFERING should always
  94. // write in increments of the volume sector size and should always
  95. // start writing on sector boundaries.
  96. //
  97. //----------------------------------------------------------------------------
  98. void CPropStoreBackupStream::OpenForBackup( const WCHAR* wcsPath,
  99. ULONG modeShare,
  100. ULONG modeCreate,
  101. ULONG ulMaxPages)
  102. {
  103. Win4Assert(!IsOpen());
  104. Win4Assert ( _xDriveInfo.IsNull() );
  105. _xDriveInfo.Set ( new CDriveInfo ( wcsPath, _cMegToLeaveOnDisk ));
  106. _hFile = CreateFile(wcsPath,
  107. GENERIC_READ | GENERIC_WRITE,
  108. modeShare,
  109. NULL,
  110. modeCreate,
  111. FILE_FLAG_NO_BUFFERING | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
  112. NULL);
  113. if (INVALID_HANDLE_VALUE == _hFile)
  114. {
  115. ciDebugOut(( DEB_ERROR,
  116. "CPropStoreBackupStream::OpenForBackup -- CreateFile on %ws returned %d\n",
  117. wcsPath, GetLastError() ));
  118. THROW( CException() );
  119. }
  120. _fOpenForRecovery = FALSE;
  121. GetSystemParams();
  122. Win4Assert(0 == _pSector && invalidSector == _ulCurrentSector);
  123. _pBigBuffer = new BYTE[2*_header.ulSectorSize];
  124. // get a buffer that starts at sector size aligned address
  125. _pSector = (PBYTE)( (((ULONG_PTR)_pBigBuffer + _header.ulSectorSize) / _header.ulSectorSize) * _header.ulSectorSize);
  126. Win4Assert( ((ULONG_PTR)_pSector % _header.ulSectorSize) == 0);
  127. //
  128. // If this file is being created from scratch, we should claim all
  129. // the space we need to backup _header.cMaxPages number of pages.
  130. //
  131. if (CREATE_ALWAYS == modeCreate || CREATE_NEW == modeCreate ||
  132. OPEN_ALWAYS == modeCreate || TRUNCATE_EXISTING == modeCreate)
  133. Reset(ulMaxPages);
  134. else
  135. {
  136. ReadSector(0, _pSector);
  137. RtlCopyMemory(&_header, _pSector, sizeof(SHeader));
  138. }
  139. ciDebugOut((DEB_PROPSTORE, "Successfully created/opened backup file.\n"
  140. "Sector size: %d, Page size: %d, Max pages: %d\n",
  141. _header.ulSectorSize, _header.ulPageSize, _header.cMaxPages));
  142. }
  143. //+---------------------------------------------------------------------------
  144. //
  145. // Function: OpenForRecovery, public
  146. //
  147. // Synopsis: Opens the backup stream for recovery (read).
  148. //
  149. // Arguments: [path] - file path
  150. // [modeShare] -- sharing mode
  151. //
  152. // Returns: None
  153. //
  154. // History: 30-May-97 KrishnaN Created
  155. // 18-Nov-98 KLam Instantiate volume info
  156. //
  157. // Notes: This file is opened for reading and should be opened with page
  158. // caching enabled (normal behavior) so we can read arbitrary lengths
  159. // of data starting at arbitrary points in the backup stream.
  160. //
  161. // This file could have been copied from a different architecture,
  162. // so we cannot assume that the page size hard coded in this file
  163. // will be the same as the page size used by the architecture. Get
  164. // the page size from the directory section and use that to restore
  165. // the corresponding sections of the property store.
  166. //
  167. //----------------------------------------------------------------------------
  168. void CPropStoreBackupStream::OpenForRecovery ( const WCHAR* wcsPath,
  169. ULONG modeShare)
  170. {
  171. Win4Assert(!IsOpen());
  172. Win4Assert ( _xDriveInfo.IsNull() );
  173. _xDriveInfo.Set ( new CDriveInfo ( wcsPath, _cMegToLeaveOnDisk ));
  174. _hFile = CreateFile(wcsPath,
  175. GENERIC_READ,
  176. modeShare,
  177. NULL,
  178. OPEN_EXISTING,
  179. FILE_ATTRIBUTE_NORMAL,
  180. NULL);
  181. if (INVALID_HANDLE_VALUE == _hFile)
  182. {
  183. ciDebugOut(( DEB_ERROR,
  184. "CPropStoreBackupStream::OpenForRecovery -- CreateFile on %ws returned %d\n",
  185. wcsPath, GetLastError() ));
  186. THROW( CException() );
  187. }
  188. _fOpenForRecovery = TRUE;
  189. if (IsCorrupt())
  190. {
  191. ciDebugOut(( DEB_ERROR,
  192. "CPropStoreBackupStream::OpenForRecovery -- Propstore backup file is corrupt\n"));
  193. THROW( CException(CI_CORRUPT_DATABASE) );
  194. }
  195. ReadFromFile(0, sizeof(SHeader), &_header);
  196. Win4Assert(0 == _pSector && invalidSector == _ulCurrentSector);
  197. _pBigBuffer = new BYTE[2*_header.ulSectorSize];
  198. // get a buffer that starts at sector size aligned address
  199. _pSector = (PBYTE)( (((ULONG_PTR)_pBigBuffer + _header.ulSectorSize) / _header.ulSectorSize) * _header.ulSectorSize);
  200. Win4Assert( ((ULONG_PTR)_pSector % _header.ulSectorSize) == 0);
  201. _cPages = CountPages();
  202. ciDebugOut((DEB_PROPSTORE, "Successfully opened backup file for recovery.\n"
  203. "Sector size: %d, Page size: %d, Max pages: %d, pages: %d\n",
  204. _header.ulSectorSize, _header.ulPageSize,
  205. _header.cMaxPages, _cPages));
  206. }
  207. //+---------------------------------------------------------------------------
  208. //
  209. // Function: Close, public
  210. //
  211. // Synopsis: Closes the backup stream.
  212. //
  213. // Arguments: None
  214. //
  215. // Returns: None
  216. //
  217. // History: 30-May-97 KrishnaN Created
  218. // 18-Nov-98 KLam Freed volume info
  219. //
  220. //----------------------------------------------------------------------------
  221. void CPropStoreBackupStream::Close()
  222. {
  223. //CLock lock(_mtxWrite);
  224. if (IsOpen())
  225. {
  226. CloseHandle(_hFile);
  227. _hFile = INVALID_HANDLE_VALUE;
  228. _xDriveInfo.Free();
  229. }
  230. #if CIDBG
  231. ciDebugOut((DEB_PSBACKUP, "Close: Closed backup. Pages backed up = %d, committed = %d, "
  232. "fields commited = %d, and backup flushed %d times.\n"
  233. "Percentage of times backed up pages were committed = %d.\n",
  234. _cPagesBackedUp, _cPagesCommited, _cFieldsCommited, _cFlushes,
  235. _cPagesCommited*100/(_cPagesBackedUp?_cPagesBackedUp:1)));
  236. _cPagesBackedUp = _cPagesCommited = _cFlushes = 0;
  237. #endif // CIDBG
  238. }
  239. //+---------------------------------------------------------------------------
  240. //
  241. // Function: Reset, public
  242. //
  243. // Synopsis: Resets the backup stream. Typically called after the property
  244. // store is flushed.
  245. //
  246. // Arguments: None
  247. //
  248. // Returns: None
  249. //
  250. // History: 30-May-97 KrishnaN Created
  251. // 29-Oct-98 KLam Check for disk space before extending file
  252. //
  253. //----------------------------------------------------------------------------
  254. void CPropStoreBackupStream::Reset(ULONG cMaxPages)
  255. {
  256. //CLock lock(_mtxWrite);
  257. Win4Assert(IsOpen() && !IsOpenForRecovery());
  258. ciDebugOut((DEB_PSBACKUP, "Reset: Pages backed up = %d, committed = %d, "
  259. "fields commited = %d, and backup flushed %d times.\n"
  260. "Percentage of times backed up pages were committed = %d.\n",
  261. _cPagesBackedUp, _cPagesCommited, _cFieldsCommited, ++_cFlushes,
  262. _cPagesCommited*100/(_cPagesBackedUp?_cPagesBackedUp:1)));
  263. //
  264. // Compute the total size of the backup file. It is the sum of
  265. // space needed for directory section and the data section.
  266. // Directory section contains the header, SHeader, followed by
  267. // _header.cMaxPages slots (ULONG). All structures are sector
  268. // aligned. The header is treated as multiple slots.
  269. //
  270. // Enforce ranges
  271. if (cMaxPages < CI_PROPERTY_STORE_BACKUP_SIZE_MIN)
  272. _header.cMaxPages = CI_PROPERTY_STORE_BACKUP_SIZE_MIN;
  273. else if (cMaxPages > CI_PROPERTY_STORE_BACKUP_SIZE_MAX)
  274. _header.cMaxPages = CI_PROPERTY_STORE_BACKUP_SIZE_MAX;
  275. else
  276. _header.cMaxPages = cMaxPages;
  277. _header.ulDataOffset = _header.ulSectorSize *
  278. roundup(ComputePageDescriptorOffset(_header.ulSectorSize, _header.cMaxPages),
  279. _header.ulSectorSize);
  280. ULONG cbNewFileSize = _header.ulDataOffset + _header.cMaxPages*_header.ulPageSize;
  281. //
  282. // If the file is growing, make sure there is enough space on disk
  283. //
  284. if ( cbNewFileSize > _cFileSizeInBytes )
  285. {
  286. Win4Assert ( !_xDriveInfo.IsNull() );
  287. __int64 cbTotal, cbRemaining;
  288. _xDriveInfo->GetDiskSpace( cbTotal, cbRemaining );
  289. if ( cbRemaining < ( cbNewFileSize - _cFileSizeInBytes ) )
  290. {
  291. ciDebugOut(( DEB_ERROR,
  292. "CPropStoreBackupStream::Reset -- Not enough disk space, need %d more bytes\n",
  293. (cbNewFileSize - _cFileSizeInBytes) - cbRemaining ));
  294. THROW( CException( CI_E_CONFIG_DISK_FULL ) );
  295. }
  296. }
  297. _cFileSizeInBytes = cbNewFileSize;
  298. if ( SetFilePointer ( _hFile,
  299. _cFileSizeInBytes,
  300. 0,
  301. FILE_BEGIN ) == 0xFFFFFFFF &&
  302. GetLastError() != NO_ERROR )
  303. {
  304. ciDebugOut(( DEB_ERROR,
  305. "CPropStoreBackupStream::Reset -- SetFilePointer returned %d\n",
  306. GetLastError() ));
  307. THROW( CException() );
  308. }
  309. if ( !SetEndOfFile( _hFile ) )
  310. {
  311. ciDebugOut(( DEB_ERROR,
  312. "CPropStoreBackupStream::Reset -- SetEndOfFile returned %d\n",
  313. GetLastError() ));
  314. THROW( CException() );
  315. }
  316. ciDebugOut(( DEB_PSBACKUP, "Reset: Backup has %d maxpages, is %d bytes, and data page begins at offset %d (0x%x)\n",
  317. _header.cMaxPages, _cFileSizeInBytes, _header.ulDataOffset, _header.ulDataOffset));
  318. // clear the hash table of all pages and init page count to 0
  319. _pageTable.DeleteAllEntries();
  320. _cPages = 0;
  321. Init();
  322. }
  323. //+---------------------------------------------------------------------------
  324. //
  325. // Function: ReadPage, public
  326. //
  327. // Synopsis: Read the i-th page. The page buffer is assumed to be the
  328. // size of the operating system page.
  329. //
  330. // Arguments: [ulPage] -- i-th page (0 based index) in backup to be read.
  331. // [pulLoc] -- Buffer to return the page's loc in prop store.
  332. // [pbPage] -- buffer to copy the page to. Contents undefined
  333. // if FALSE is returned.
  334. //
  335. // Returns: TRUE if the page was successfully read.
  336. // FALSE otherwise.
  337. //
  338. // History: 30-May-97 KrishnaN Created
  339. //
  340. // Notes: This function is not re-entrant. It will only be called for recovery
  341. // and one page will be read at a time. To make this re-entrant
  342. // allocate the buffer used for the sector on the stack.
  343. //
  344. //----------------------------------------------------------------------------
  345. BOOL CPropStoreBackupStream::ReadPage(ULONG ulPage, ULONG *pulLoc, void *pbPage)
  346. {
  347. Win4Assert(pbPage && pulLoc);
  348. // Copy the ulPage of the slot to pulLoc.
  349. *pulLoc = GetPageLocation(ulPage);
  350. if (invalidPage == *pulLoc)
  351. return FALSE;
  352. // Go to the data page and read it into pbPage
  353. ReadFromFile(_header.ulDataOffset + ulPage*_header.ulPageSize,
  354. _header.ulPageSize,
  355. pbPage);
  356. ciDebugOut(( DEB_PSBACKUP, "ReadPage: Successfully read page %d (page %d in backup) into address 0x%x\n",
  357. *pulLoc, ulPage, pbPage));
  358. return TRUE;
  359. }
  360. //+---------------------------------------------------------------------------
  361. //
  362. // Function: GetPageLocation, public
  363. //
  364. // Synopsis: Get the location of the i-th page.
  365. //
  366. // Arguments: [nPage] -- i-th page (0 based index) to be read.
  367. //
  368. // Returns: The i-th page's location in property store. invalidPage if there is
  369. // no i-th page..
  370. //
  371. // History: 30-May-97 KrishnaN Created
  372. //
  373. // Notes: This function is not re-entrant. It will only be called for recovery
  374. // and one page will be read at a time. To make this re-entrant
  375. // allocate the buffer used for the sector into a stack variable.
  376. //
  377. //+---------------------------------------------------------------------------
  378. ULONG CPropStoreBackupStream::GetPageLocation(ULONG nPage)
  379. {
  380. Win4Assert(IsOpen() && IsOpenForRecovery() && _pSector && nPage < _cPages);
  381. if (nPage >= _cPages)
  382. return invalidPage;
  383. // Get sector containing the page's descriptor and read it in.
  384. ULONG ulSlotOffset = ComputePageDescriptorOffset(_header.ulSectorSize, nPage);
  385. ULONG ulSector = ulSlotOffset/_header.ulSectorSize;
  386. if (_ulCurrentSector != ulSector)
  387. {
  388. ReadSector(ulSector, _pSector);
  389. _ulCurrentSector = ulSector;
  390. }
  391. // Return the page location
  392. ulSlotOffset %= _header.ulSectorSize;
  393. return *(ULONG *)(_pSector+ulSlotOffset);
  394. }
  395. //+---------------------------------------------------------------------------
  396. //
  397. // Function: CommitPages, public
  398. //
  399. // Synopsis: Check if there is space to add
  400. //
  401. // Arguments: [cPages] -- Number of pages.
  402. // [pSlots] -- Array of page descriptors.
  403. // [ppvPages] -- Array of page pointers to backup.
  404. //
  405. // Returns: TRUE if all pages could be committed. FALSE otherwise.
  406. //
  407. // Notes: For efficiency, call this only to commit multiple pages.
  408. //
  409. // History: 09-Jun-97 KrishnaN Created
  410. //
  411. //----------------------------------------------------------------------------
  412. BOOL CPropStoreBackupStream::CommitPages(ULONG cPages,
  413. ULONG const *pSlots,
  414. void const * const * ppvPages)
  415. {
  416. // Count pages not in the page table. Ignore duplicates.
  417. ULONG cMissingPages = 0;
  418. THashTable<ULONG> missingPageTable(cPages); // to detect duplicates
  419. ULONG ulPos;
  420. for (ULONG i = 0; i < cPages; i++)
  421. {
  422. // if it is not in the page table and if it has not already
  423. // been counted, count it.
  424. if (!_pageTable.LookUp(pSlots[i], ulPos) && !missingPageTable.LookUp(pSlots[i]))
  425. {
  426. missingPageTable.AddEntry(pSlots[i]);
  427. cMissingPages++;
  428. }
  429. }
  430. if (0 == cMissingPages)
  431. return TRUE; // nothing to do
  432. // do we have enough space to accomodate the missing pages?
  433. if (cMissingPages > (MaxPages() - Pages()))
  434. return FALSE; // not enough space
  435. // Commit only pages not already committed
  436. BOOL fSuccessful = TRUE;
  437. for (i = 0; i < cPages && fSuccessful; i++)
  438. {
  439. // Attempt to commit only pages known to be missing from the page table
  440. // This saves us some cycles that would otherwise be spent by CommitPage
  441. // which would have to lookup in a larger hash table to figure out if
  442. // this page exists.
  443. if (missingPageTable.LookUp(pSlots[i]))
  444. fSuccessful = fSuccessful && CommitPage(pSlots[i], ppvPages[i]);
  445. }
  446. return fSuccessful;
  447. }
  448. //+---------------------------------------------------------------------------
  449. //
  450. // Function: CommitPage, public
  451. //
  452. // Synopsis: Append a page at the end of the stream. The page buffer is
  453. // assumed to be the size of the operating system page. If the
  454. // page already exists, overwrite it in place.
  455. //
  456. // Arguments: [slot] -- Page descriptor for the page.
  457. // [pbPage] -- Buffer containing contents of the page.
  458. //
  459. // Returns: TRUE if the page could be committed, FALSE if it couldn't be.
  460. //
  461. // Notes: Call this for single page commits.
  462. //
  463. // History: 30-May-97 KrishnaN Created
  464. //
  465. //----------------------------------------------------------------------------
  466. BOOL CPropStoreBackupStream::CommitPage(ULONG slot, void const *pbPage)
  467. {
  468. //CLock lock(_mtxWrite);
  469. Win4Assert(IsOpen() && !IsOpenForRecovery() && _pSector);
  470. Win4Assert(++_cPagesBackedUp);
  471. ULONG ulPos;
  472. // Does it already exist in the backup?
  473. if (_pageTable.LookUp(slot, ulPos))
  474. return TRUE; // no need to commit again
  475. ciDebugOut(( DEB_PSBACKUP, "CommitPage: Commiting page %d, address 0x%x\n", slot, pbPage));
  476. // First write the page to disk.
  477. BOOL fSuccessful = WriteToFile(_header.ulDataOffset + _cPages*_header.ulPageSize,
  478. _header.ulPageSize, pbPage);
  479. if (fSuccessful)
  480. {
  481. // Get sector containing the page's descriptor and read it in.
  482. ULONG ulSlotOffset = ComputePageDescriptorOffset(_header.ulSectorSize, _cPages);
  483. ULONG ulSector = ulSlotOffset/_header.ulSectorSize;
  484. if (_ulCurrentSector != ulSector)
  485. {
  486. ReadSector(ulSector, _pSector);
  487. _ulCurrentSector = ulSector;
  488. }
  489. // Copy the page descriptor into the sector and commit it.
  490. RtlCopyMemory(_pSector + ulSlotOffset%_header.ulSectorSize, &slot, sizeof(ULONG));
  491. fSuccessful = CommitSector(_ulCurrentSector, _pSector);
  492. Win4Assert(fSuccessful);
  493. _pageTable.AddEntry(slot, _cPages);
  494. // Now we are truly done commiting the page
  495. _cPages++;
  496. Win4Assert(++_cPagesCommited);
  497. }
  498. return fSuccessful;
  499. }
  500. //+---------------------------------------------------------------------------
  501. //
  502. // Function: CommitField, public
  503. //
  504. // Synopsis: Modify a field in place, sector by sector. This is faster than
  505. // modifying an entire page.
  506. //
  507. // Arguments: [ulPage] -- The page to modify
  508. // [ulOffset] -- Offset in page where modification should begin.
  509. // [cSize] -- Size, in bytes, of the field to be modified.
  510. // [pvBuffer] -- Buffer containing the new data to write.
  511. //
  512. // Returns: TRUE if the page could be committed, FALSE if it couldn't be.
  513. //
  514. // History: 30-May-97 KrishnaN Created
  515. //
  516. //----------------------------------------------------------------------------
  517. BOOL CPropStoreBackupStream::CommitField(ULONG ulPage,
  518. ULONG ulOffset,
  519. ULONG cSize,
  520. void const *pvBuffer)
  521. {
  522. ULONG ulPos = 0xFFFFFFFF;
  523. _pageTable.LookUp(ulPage, ulPos);
  524. Win4Assert(IsOpen() && !IsOpenForRecovery() && _pSector && _pageTable.LookUp(ulPage, ulPos));
  525. Win4Assert((ulOffset+cSize) <= _header.ulPageSize && pvBuffer);
  526. Win4Assert(++_cFieldsCommited);
  527. Win4Assert(ulPos < _cPages);
  528. //
  529. // Figure out which sector(s) the field spans and modify them in place
  530. //
  531. ULONG ulSector = ComputeFirstSectorOfPage(ulPos) + ulOffset/_header.ulSectorSize;
  532. ULONG ulOffsetInSector = ulOffset%_header.ulSectorSize;
  533. //
  534. // The way we backup right now, we cannot have multiple sector writes, so assert
  535. // that we are acutally writing only one sector. The code to support multiple
  536. // sector writes, of course, will always be there and doing its job.
  537. //
  538. Win4Assert((ulOffsetInSector + cSize) <= _header.ulSectorSize);
  539. for (ULONG cBytesCommited = 0; cBytesCommited < cSize; ulSector++)
  540. {
  541. _ulCurrentSector = ulSector;
  542. ReadSector(_ulCurrentSector, _pSector);
  543. RtlCopyMemory(_pSector + ulOffsetInSector,
  544. ((PBYTE)pvBuffer + cBytesCommited),
  545. min(cSize-cBytesCommited, _header.ulSectorSize));
  546. CommitSector(ulSector, _pSector);
  547. cBytesCommited += min(cSize-cBytesCommited, _header.ulSectorSize);
  548. // After the first time, the offset of field in the sector is always 0
  549. ulOffsetInSector = 0;
  550. }
  551. return TRUE;
  552. }
  553. //+---------------------------------------------------------------------------
  554. //
  555. // Function: Init, private
  556. //
  557. // Synopsis: Initialize the header and directory section of the file.
  558. //
  559. // Arguments: None
  560. //
  561. // Returns: None
  562. //
  563. // History: 04-Jun-97 KrishnaN Created
  564. //
  565. //----------------------------------------------------------------------------
  566. void CPropStoreBackupStream::Init()
  567. {
  568. //CLock lock(_mtxWrite);
  569. Win4Assert(sizeof(SHeader) <= _header.ulSectorSize && _pSector);
  570. Win4Assert(_pSector);
  571. //
  572. // prepare the header portion of sector 0
  573. //
  574. _ulCurrentSector = 0;
  575. RtlCopyMemory(_pSector, &_header, sizeof(SHeader));
  576. ULONG ulNextSlotPtr = ComputePageDescriptorOffset(_header.ulSectorSize, 0); // write next slot here
  577. //
  578. // Prepare the sectors and write them to disk
  579. //
  580. for (ULONG cSlotsWritten = 0; cSlotsWritten < _header.cMaxPages; )
  581. {
  582. // Fill up the current sector
  583. for (; (ulNextSlotPtr + sizeof(ULONG)) <= _header.ulSectorSize &&
  584. cSlotsWritten < _header.cMaxPages;
  585. ulNextSlotPtr += sizeof(ULONG), cSlotsWritten++)
  586. {
  587. RtlCopyMemory(_pSector+ulNextSlotPtr, &invalidPage, sizeof(ULONG));
  588. }
  589. // Anything to write in the current sector?
  590. if (ulNextSlotPtr > 0)
  591. {
  592. CommitSector(_ulCurrentSector, _pSector);
  593. // move to the next sector
  594. _ulCurrentSector++;
  595. ulNextSlotPtr = 0;
  596. }
  597. }
  598. }
  599. //+---------------------------------------------------------------------------
  600. //
  601. // Function: CommitSector, private
  602. //
  603. // Synopsis: Commits ulSector - th sector of the data section to disk.
  604. //
  605. // Arguments: [ulSector] -- Sector to commit.
  606. // [pbBuffer] -- Buffer with sector's data to commit.
  607. //
  608. // Returns: None
  609. //
  610. // History: 04-Jun-97 KrishnaN Created
  611. //
  612. //----------------------------------------------------------------------------
  613. BOOL CPropStoreBackupStream::CommitSector(ULONG ulSector, void const *pbBuffer)
  614. {
  615. Win4Assert(IsOpen() &&
  616. !IsOpenForRecovery() &&
  617. pbBuffer);
  618. ciDebugOut(( DEB_PSBACKUP, "CommitSector: About to commit sector %d\n", ulSector));
  619. if ( 0 == pbBuffer )
  620. {
  621. ciDebugOut(( DEB_ERROR,
  622. "CPropStoreBackupStream::CommitSector attempting to write an invalid sector (sector %d, address: %0x8x)\n",
  623. ulSector, pbBuffer ));
  624. return FALSE;
  625. }
  626. return WriteToFile(ulSector*_header.ulSectorSize, _header.ulSectorSize, pbBuffer);
  627. } //CommitSector
  628. //+---------------------------------------------------------------------------
  629. //
  630. // Function: WriteToFile, private
  631. //
  632. // Synopsis: Commits a buffer to disk.
  633. //
  634. // Arguments: [ulStartLoc] -- Starting location, relative to beginning of file.
  635. // [ulNumBytes] -- Number of bytes to commit.
  636. // [pbBuffer] -- Buffer containing data to commit.
  637. //
  638. // Returns: None
  639. //
  640. // History: 04-Jun-97 KrishnaN Created
  641. //
  642. //----------------------------------------------------------------------------
  643. BOOL CPropStoreBackupStream::WriteToFile(ULONG ulStartLoc, ULONG ulNumBytes,
  644. void const *pbBuffer)
  645. {
  646. Win4Assert(IsOpen() && !IsOpenForRecovery());
  647. // The buffer should also begin on a sector boundary
  648. Win4Assert( ((ULONG_PTR)pbBuffer % _header.ulSectorSize) == 0);
  649. // All writes to this file should begin and end on sector boundaries
  650. Win4Assert(ulStartLoc%_header.ulSectorSize == 0 && ulNumBytes%_header.ulSectorSize == 0);
  651. DWORD dwNumWritten = 0;
  652. dwNumWritten = SetFilePointer(_hFile, ulStartLoc, 0, FILE_BEGIN);
  653. if (0xFFFFFFFF == dwNumWritten)
  654. {
  655. ciDebugOut(( DEB_ERROR,
  656. "CPropStoreBackupStream::WriteToFile -- SetFilePointer returned %d\n",
  657. GetLastError() ));
  658. return FALSE;
  659. }
  660. Win4Assert(ulStartLoc == dwNumWritten);
  661. if (!WriteFile(_hFile, pbBuffer, ulNumBytes, &dwNumWritten, NULL))
  662. {
  663. ciDebugOut(( DEB_ERROR,
  664. "CPropStoreBackupStream::WriteToFile -- WriteFile returned %d\n",
  665. GetLastError() ));
  666. return FALSE;
  667. }
  668. Win4Assert(ulNumBytes == dwNumWritten);
  669. return TRUE;
  670. } //WriteToFile
  671. //+---------------------------------------------------------------------------
  672. //
  673. // Function: ReadSector, private
  674. //
  675. // Synopsis: Reads specified sector of the data section from disk.
  676. //
  677. // Arguments: [ulSector] -- i-th sector of data section to read.
  678. // [pbBuffer] -- Buffer to hold the sector
  679. //
  680. // Returns: None
  681. //
  682. // History: 04-Jun-97 KrishnaN Created
  683. //
  684. //----------------------------------------------------------------------------
  685. inline void CPropStoreBackupStream::ReadSector(ULONG ulSector, PBYTE pbBuffer)
  686. {
  687. Win4Assert(IsOpen() && pbBuffer);
  688. ReadFromFile(ulSector*_header.ulSectorSize, _header.ulSectorSize, pbBuffer);
  689. }
  690. //+---------------------------------------------------------------------------
  691. //
  692. // Function: ReadFromFile, private
  693. //
  694. // Synopsis: Reads specified data from file.
  695. //
  696. // Arguments: [ulStartLoc] -- starting location of data to read.
  697. // [ulNumBytes] -- number of bytes to read.
  698. // [pbBuffer] -- Buffer to hold the read data.
  699. //
  700. // Returns: None
  701. //
  702. // History: 04-Jun-97 KrishnaN Created
  703. //
  704. //----------------------------------------------------------------------------
  705. void CPropStoreBackupStream::ReadFromFile(ULONG ulStartLoc, ULONG ulNumBytes,
  706. void *pbBuffer)
  707. {
  708. Win4Assert(IsOpen());
  709. DWORD dwNumRead = 0;
  710. dwNumRead = SetFilePointer(_hFile, ulStartLoc, 0, FILE_BEGIN);
  711. if (0xFFFFFFFF == dwNumRead)
  712. {
  713. ciDebugOut(( DEB_ERROR,
  714. "CPropStoreBackupStream::ReadFromFile -- SetFilePointer returned %d\n",
  715. GetLastError() ));
  716. THROW( CException() );
  717. }
  718. Win4Assert(ulStartLoc == dwNumRead);
  719. if (!ReadFile(_hFile, pbBuffer, ulNumBytes, &dwNumRead, NULL))
  720. {
  721. ciDebugOut(( DEB_ERROR,
  722. "CPropStoreBackupStream::ReadFromFile -- ReadFile returned %d\n",
  723. GetLastError() ));
  724. THROW( CException() );
  725. }
  726. Win4Assert(ulNumBytes == dwNumRead);
  727. }
  728. //+---------------------------------------------------------------------------
  729. //
  730. // Function: CountPages, private
  731. //
  732. // Synopsis: Counts the pages backed up.
  733. //
  734. // Arguments: None
  735. //
  736. // Returns: Number of pages in the backup file.
  737. //
  738. // History: 04-Jun-97 KrishnaN Created
  739. //
  740. // Notes: Assumes that the file is not corrupt. It is the job of IsCorrupt()
  741. // to detect corruption.
  742. //
  743. //----------------------------------------------------------------------------
  744. ULONG CPropStoreBackupStream::CountPages()
  745. {
  746. Win4Assert(IsOpen() && IsOpenForRecovery() && !IsCorrupt());
  747. //
  748. // Read in a page descriptor at a time until the first "free" page descriptor
  749. // slot is found OR until you reach the end of the directory section.
  750. //
  751. ULONG cPages;
  752. ULONG ulNextSlotPtr = ComputePageDescriptorOffset(_header.ulSectorSize, 0); // read next slot here
  753. ULONG slot;
  754. for (cPages = 0; cPages < _header.cMaxPages; cPages++)
  755. {
  756. ReadFromFile(ulNextSlotPtr, sizeof(ULONG), &slot);
  757. if (invalidPage == slot)
  758. break;
  759. ulNextSlotPtr += sizeof(ULONG);
  760. }
  761. ciDebugOut((DEB_PSBACKUP, "Found %u data pages in property store backup.\n", cPages));
  762. return cPages;
  763. }
  764. //+---------------------------------------------------------------------------
  765. //
  766. // Function: IsCorrupt, private
  767. //
  768. // Synopsis: Checks for corruption.
  769. //
  770. // Arguments: None
  771. //
  772. // Returns: TRUE or FALSE.
  773. //
  774. // History: 04-Jun-97 KrishnaN Created
  775. //
  776. // Notes: This can only verify the directory section. It cannot
  777. // validate the contents of the data section.
  778. //
  779. //----------------------------------------------------------------------------
  780. BOOL CPropStoreBackupStream::IsCorrupt()
  781. {
  782. Win4Assert(IsOpen() && IsOpenForRecovery());
  783. SHeader header;
  784. ReadFromFile(0, sizeof(SHeader), &header);
  785. //
  786. // Remember that the file may have just been copied from a different architecture,
  787. // so we can't verify against values provided by system calls. They should be
  788. // powers of 2, so that would be a good check. The page size should also be an
  789. // integral of sector size. So that would be another good check.
  790. //
  791. if (header.ulPageSize % header.ulSectorSize != 0)
  792. {
  793. ciDebugOut((DEB_PSBACKUP, "Page size (%d) in not an integral multiple of sector size (%d).\n",
  794. header.ulPageSize, header.ulSectorSize));
  795. return TRUE;
  796. }
  797. if (!IsPowerOf2(header.ulPageSize) || !IsPowerOf2(header.ulSectorSize))
  798. {
  799. ciDebugOut((DEB_PSBACKUP, "Page size (%d) in not an integral multiple of sector size (%d).\n",
  800. header.ulPageSize, header.ulSectorSize));
  801. return TRUE;
  802. }
  803. // Verify that cMaxPages looks "reasonable"
  804. if (header.cMaxPages < CI_PROPERTY_STORE_BACKUP_SIZE_MIN || header.cMaxPages > CI_PROPERTY_STORE_BACKUP_SIZE_MAX)
  805. {
  806. ciDebugOut((DEB_PSBACKUP, "Max pages in backup file is %d. Should be between %d and %d\n",
  807. header.cMaxPages, CI_PROPERTY_STORE_BACKUP_SIZE_MIN, CI_PROPERTY_STORE_BACKUP_SIZE_MAX));
  808. return TRUE;
  809. }
  810. // Verify that ulDataOffset is valid.
  811. ULONG ulDataOffset = header.ulSectorSize *
  812. roundup(ComputePageDescriptorOffset(header.ulSectorSize, header.cMaxPages),
  813. header.ulSectorSize);
  814. if (header.ulDataOffset != ulDataOffset)
  815. {
  816. ciDebugOut((DEB_ERROR, "Data section of backup file should begin at offset %d."
  817. " Instead, it is %d \n",
  818. ulDataOffset, header.ulDataOffset));
  819. return TRUE;
  820. }
  821. //
  822. // Read in a page descriptor at a time until you reach the end of the
  823. // directory section. Verify that each slot is free or has "reasonable"
  824. // values. Once a free slot is found, all subsequent slots should also
  825. // be free.
  826. //
  827. ULONG cPages;
  828. ULONG ulNextSlotPtr = ComputePageDescriptorOffset(header.ulSectorSize, 0); // read next slot here
  829. ULONG slot;
  830. for (cPages = 0; cPages < header.cMaxPages; cPages++)
  831. {
  832. ReadFromFile(ulNextSlotPtr, sizeof(ULONG), &slot);
  833. if (invalidPage == slot)
  834. break;
  835. // How to validate ulPage of ULONG?
  836. ulNextSlotPtr += sizeof(ULONG);
  837. }
  838. // Verify that the remaining slots are free
  839. for (ulNextSlotPtr += sizeof(ULONG); cPages < header.cMaxPages; cPages++)
  840. {
  841. ReadFromFile(ulNextSlotPtr, sizeof(ULONG), &slot);
  842. if (invalidPage != slot)
  843. {
  844. ciDebugOut((DEB_PSBACKUP, "Found an invalid page descriptor in backup file "
  845. "where a free slot is expected.\n"));
  846. return TRUE;
  847. }
  848. }
  849. return FALSE;
  850. }
  851. //+---------------------------------------------------------------------------
  852. //
  853. // Function: GetSystemParams, private
  854. //
  855. // Synopsis: Gets the volume sector size and page size.
  856. //
  857. // Returns: None.
  858. //
  859. // History: 04-Jun-97 KrishnaN Created
  860. // 18-Nov-98 KLam Removed path parameter, used volume info
  861. //
  862. //----------------------------------------------------------------------------
  863. void CPropStoreBackupStream::GetSystemParams()
  864. {
  865. Win4Assert ( !_xDriveInfo.IsNull() );
  866. _header.ulSectorSize = _xDriveInfo->GetSectorSize();
  867. SYSTEM_INFO systemInfo;
  868. GetSystemInfo(&systemInfo);
  869. _header.ulPageSize = systemInfo.dwPageSize;
  870. if (_header.ulPageSize % _header.ulSectorSize != 0)
  871. {
  872. ciDebugOut((DEB_ERROR, "CPropStoreBackupStream::GetSystemParams: Page size (%d) in not an integral multiple of sector size (%d).\n",
  873. _header.ulPageSize, _header.ulSectorSize));
  874. THROW( CException(CI_E_STRANGE_PAGEORSECTOR_SIZE) );
  875. }
  876. ciDebugOut(( DEB_PSBACKUP, "GetSystemParams: Volume sector size is %d bytes and system page size is %d (0x%x)",
  877. _header.ulSectorSize, _header.ulPageSize, _header.ulPageSize));
  878. }