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.

965 lines
27 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1991 - 1999.
  5. //
  6. // File: CIDIR.CXX
  7. //
  8. // Contents: Persistent directory
  9. //
  10. // Classes: CiDirectory
  11. //
  12. // History: 3-Apr-91 BartoszM Created stub.
  13. // 3-May-96 dlee Don't read it in -- map on-disk data
  14. // 4-Nov-97 dlee Support merge shrink from front
  15. //
  16. // Notes: File format is two initial ULONGs with the count of level 1
  17. // and count of level 2 keys, then the actual level 1 and level
  18. // 2 keys.
  19. //
  20. //----------------------------------------------------------------------------
  21. #include <pch.cxx>
  22. #pragma hdrstop
  23. #include <mmstrm.hxx>
  24. #include <cidir.hxx>
  25. #include <cistore.hxx>
  26. #include <eventlog.hxx>
  27. //+---------------------------------------------------------------------------
  28. //
  29. // Member: CiDirectory::CiDirectory, public
  30. //
  31. // Synopsis: Open existing or create empty directory
  32. //
  33. // Arguments: [storage] -- Through which streams are opened
  34. // [objectId] -- Wid to open
  35. // [mode] -- Mode to open the stream
  36. //
  37. // History: 13-Jun-91 BartoszM Created.
  38. //
  39. //----------------------------------------------------------------------------
  40. CiDirectory::CiDirectory(
  41. CiStorage & storage,
  42. WORKID objectId,
  43. PStorage::EOpenMode mode ) :
  44. _storage( storage ),
  45. _objectId( objectId ),
  46. _cKeys( 0 ),
  47. _cLevel2Keys( 0 ),
  48. _pbCurrent( 0 ),
  49. _pcKeys( 0 ),
  50. _fReadOnly( PStorage::eOpenForRead == mode )
  51. {
  52. // If opening for create, don't try reading in existing data if the file
  53. // exists since we want to blow it all away later anyway.
  54. if ( PStorage::eCreate != mode )
  55. ReadIn( !_fReadOnly );
  56. } //CiDirectory
  57. //+---------------------------------------------------------------------------
  58. //
  59. // Member: CiDirectory::DoSeekForKeyBuf
  60. //
  61. // Synopsis: Find the key in the array of keys.
  62. //
  63. // Arguments: [key] -- search key
  64. // [aKeys] -- array of keys to search
  65. // [low] -- the index of the lower bound of the search
  66. // [cKeys] -- # of keys over which the search happens
  67. //
  68. // Returns: index of the greatest key <= the search key
  69. //
  70. // History: 5-May-96 dlee Created
  71. //
  72. //----------------------------------------------------------------------------
  73. inline ULONG CiDirectory::DoSeekForKeyBuf(
  74. const CKeyBuf & key,
  75. CDirectoryKey ** aKeys,
  76. ULONG low,
  77. ULONG cKeys )
  78. {
  79. #if CIDBG == 1
  80. Win4Assert( 0 != cKeys );
  81. ULONG cArray = cKeys;
  82. #endif // CIDBG == 1
  83. ULONG iHi = low + cKeys - 1;
  84. ULONG iLo = low;
  85. // do a binary search looking for the key
  86. do
  87. {
  88. ULONG cHalf = cKeys / 2;
  89. if ( 0 != cHalf )
  90. {
  91. ULONG cTmp = cHalf - 1 + ( cKeys & 1 );
  92. ULONG iMid = iLo + cTmp;
  93. Win4Assert( iMid < ( low + cArray ) );
  94. if ( aKeys[ iMid ]->IsGreaterThanKeyBuf( key ) )
  95. {
  96. iHi = iMid - 1;
  97. cKeys = cTmp;
  98. }
  99. else if ( ! aKeys[ iMid + 1 ]->IsGreaterThanKeyBuf( key ) )
  100. {
  101. iLo = iMid + 1;
  102. cKeys = cHalf;
  103. }
  104. else
  105. {
  106. return iMid;
  107. }
  108. }
  109. else if ( cKeys > 1 )
  110. {
  111. Win4Assert( ( iLo + 1 ) < ( low + cArray ) );
  112. if ( aKeys[ iLo + 1 ]->IsGreaterThanKeyBuf( key ) )
  113. return iLo;
  114. else
  115. return iLo + 1;
  116. }
  117. else return iLo;
  118. }
  119. while ( TRUE );
  120. Win4Assert(( ! "Invalid doseek function exit point" ));
  121. return 0;
  122. } //DoSeekForKeyBuf
  123. //+---------------------------------------------------------------------------
  124. //
  125. // Member: CiDirectory::Seek
  126. //
  127. // Synopsis: Find bit offset and key of the greatest key less than or
  128. // equal to search key.
  129. // If none is found <= the search key, return the first item.
  130. //
  131. // Arguments: [key] -- search key
  132. // [pKeyInit] -- key found: less or equal to search key.
  133. // only returned if non-zero
  134. // [off] -- bit offset of keyInit
  135. //
  136. // History: 22-Apr-92 BartoszM Created
  137. //
  138. //----------------------------------------------------------------------------
  139. void CiDirectory::Seek(
  140. const CKeyBuf & key,
  141. CKeyBuf * pKeyInit,
  142. BitOffset & off )
  143. {
  144. // If a master merge failed, the stream may not be mapped. Map it.
  145. ReMapIfNeeded();
  146. ULONG iKey;
  147. if ( ! key.IsMinKey() )
  148. {
  149. //
  150. // Check if there is no level 2 (ie the dir is being created)
  151. // In the case where there is no level 2, there is no max key
  152. // at the end of the array. That's ok because queries will go
  153. // to the old index for keys greater than the last key in this
  154. // directory.
  155. //
  156. if ( 0 == _cLevel2Keys )
  157. {
  158. iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), 0, _cKeys );
  159. Win4Assert( iKey < _cKeys );
  160. Win4Assert( ( iKey == ( _cKeys - 1 ) ) ||
  161. ( _aKeys[ iKey + 1 ]->IsGreaterThanKeyBuf( key ) ) );
  162. }
  163. else
  164. {
  165. iKey = DoSeekForKeyBuf( key, _aLevel2Keys.GetPointer(), 0, _cLevel2Keys );
  166. iKey *= eDirectoryFanOut;
  167. Win4Assert( iKey < _cKeys );
  168. unsigned cKeys = __min( eDirectoryFanOut, _cKeys - iKey );
  169. iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), iKey, cKeys );
  170. Win4Assert( ( 1 == _cKeys ) ||
  171. ( iKey < ( _cKeys - 1 ) ) );
  172. Win4Assert( ( 1 == _cKeys) ||
  173. ( _aKeys[ iKey + 1 ]->IsGreaterThanKeyBuf( key ) ) );
  174. }
  175. Win4Assert( 0 == iKey ||
  176. !_aKeys[ iKey ]->IsGreaterThanKeyBuf( key ) );
  177. // If the search key is <= the first key, return the min key
  178. if ( ( 0 == iKey ) &&
  179. ( ( 0 == _cKeys ) ||
  180. ( _aKeys[ 0 ]->IsGreaterThanKeyBuf( key ) ) ) )
  181. {
  182. // the key is less than the first key, return minkey
  183. if ( 0 != pKeyInit )
  184. pKeyInit->FillMin();
  185. off.Init( 0, 0 );
  186. return;
  187. }
  188. }
  189. else
  190. {
  191. iKey = 0;
  192. }
  193. _aKeys[ iKey ]->Offset( off );
  194. if ( 0 != pKeyInit )
  195. _aKeys[ iKey ]->MakeKeyBuf( *pKeyInit );
  196. } //Seek
  197. //+---------------------------------------------------------------------------
  198. //
  199. // Member: CiDirectory::Seek
  200. //
  201. // Synopsis: Find bit offset and key of the greatest key less than or
  202. // equal to search key.
  203. // If none is found <= the search key, return the first item.
  204. //
  205. // Arguments: [key] -- search key
  206. // [pKeyInit] -- key found: less or equal to search key.
  207. // only returned if non-zero
  208. // [off] -- bit offset of keyInit
  209. //
  210. // History: 22-Apr-92 BartoszM Created
  211. //
  212. //----------------------------------------------------------------------------
  213. void CiDirectory::Seek(
  214. const CKey & key,
  215. CKeyBuf * pKeyInit,
  216. BitOffset & off )
  217. {
  218. CKeyBuf keyBuf;
  219. keyBuf = key;
  220. Seek( keyBuf, pKeyInit, off );
  221. } //Seek
  222. //+---------------------------------------------------------------------------
  223. //
  224. // Member: CiDirectory::SeekNext
  225. //
  226. // Synopsis: Find the first key in the next page
  227. //
  228. // Arguments: [key] -- the search key in this page
  229. // [off] -- the offset of the first key
  230. //
  231. // History: 8-Mar-94 t-joshh Created
  232. //
  233. //----------------------------------------------------------------------------
  234. void CiDirectory::SeekNext (
  235. const CKeyBuf & key,
  236. CKeyBuf * pKeyInit,
  237. BitOffset & off )
  238. {
  239. ULONG iKey;
  240. if ( _cKeys > 0 && ! key.IsMinKey() )
  241. {
  242. if ( 0 == _cLevel2Keys )
  243. {
  244. iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), 0, _cKeys );
  245. }
  246. else
  247. {
  248. iKey = DoSeekForKeyBuf( key, _aLevel2Keys.GetPointer(), 0, _cLevel2Keys );
  249. iKey *= eDirectoryFanOut;
  250. Win4Assert( iKey < _cKeys );
  251. unsigned cKeys = __min( eDirectoryFanOut, _cKeys - iKey );
  252. iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), iKey, cKeys );
  253. }
  254. // this IS a seek next
  255. iKey++;
  256. }
  257. else
  258. {
  259. iKey = 0;
  260. }
  261. Win4Assert ( iKey < _cKeys );
  262. _aKeys[ iKey ]->Offset( off );
  263. if ( 0 != pKeyInit )
  264. _aKeys[ iKey ]->MakeKeyBuf( *pKeyInit );
  265. #if CIDBG == 1
  266. _aKeys[ iKey ]->MakeKeyBuf( _keyLast );
  267. #endif // CIDBG == 1
  268. } //SeekNext
  269. //+---------------------------------------------------------------------------
  270. //
  271. // Member: CiDirectory::Add
  272. //
  273. // Synopsis: Adds entry to directory
  274. //
  275. // Arguments: [posKey] -- offset of key in file
  276. // [key] -- key to add
  277. //
  278. // History: 22-Apr-92 BartoszM Created
  279. //
  280. //----------------------------------------------------------------------------
  281. void CiDirectory::Add(
  282. BitOffset & posKey,
  283. const CKeyBuf & key )
  284. {
  285. Win4Assert( !_fReadOnly );
  286. Win4Assert( &key != 0 );
  287. ciDebugOut(( DEB_PDIR,
  288. "PDir::Add %.*ws at %d:%d\n",
  289. key.StrLen(),
  290. key.GetStr(),
  291. posKey.Page(),
  292. posKey.Offset() ));
  293. LokWriteKey( key, posKey );
  294. #if CIDBG==1
  295. _bitOffLastAdded = posKey;
  296. #endif // CIDBG
  297. } //Add
  298. //+---------------------------------------------------------------------------
  299. //
  300. // Member: CiDirectory::ReMapIfNeeded, private
  301. //
  302. // Synopsis: ReMaps the directory stream if it isn't mapped due to a
  303. // failure to extend or map the directory stream during a
  304. // shrink from front master merge.
  305. //
  306. // History: 6-Aug-98 dlee Created
  307. //
  308. //----------------------------------------------------------------------------
  309. void CiDirectory::ReMapIfNeeded()
  310. {
  311. // If it's already mapped, we're set
  312. if ( 0 != _streamBuf.Get() )
  313. return;
  314. Win4Assert( !_fReadOnly );
  315. // Map the file
  316. _stream->MapAll( _streamBuf );
  317. // Rebase pointers if the new stream pointer is different
  318. if ( 0 == _cKeys )
  319. {
  320. _pcKeys = (ULONG *) _streamBuf.Get();
  321. _pbCurrent = (BYTE *) ( _pcKeys + 2 );
  322. }
  323. else if ( _pcKeys != (ULONG *) _streamBuf.Get() )
  324. {
  325. BYTE *pbOldBase = (BYTE *) _pcKeys;
  326. BYTE *pbNewBase = (BYTE *) _streamBuf.Get();
  327. UINT_PTR cb = _pbCurrent - pbOldBase;
  328. for ( unsigned i = 0; i < _cKeys; i++ )
  329. {
  330. BYTE * p = (BYTE *) _aKeys[ i ];
  331. p = p - pbOldBase + pbNewBase;
  332. _aKeys[ i ] = (CDirectoryKey *) p;
  333. }
  334. _pcKeys = (ULONG *) _streamBuf.Get();
  335. _pbCurrent = pbNewBase + cb;
  336. }
  337. } //ReMapIfNeeded
  338. #if CIDBG == 1 // for testing failure path
  339. BOOL g_fFailDirectoryMergeExtend = FALSE;
  340. #endif // CIDBG == 1
  341. //+---------------------------------------------------------------------------
  342. //
  343. // Member: CiDirectory::GrowIfNeeded, private
  344. //
  345. // Synopsis: Grows the file if necessary and maps the new section
  346. //
  347. // Arguments: [cbToGrow] -- # of bytes to append to the file
  348. //
  349. // History: 8-May-96 dlee Created
  350. //
  351. //----------------------------------------------------------------------------
  352. void CiDirectory::GrowIfNeeded(
  353. unsigned cbToGrow )
  354. {
  355. if ( ( _pbCurrent + cbToGrow ) >=
  356. ( (BYTE *) _streamBuf.Get() + _streamBuf.Size() ) )
  357. {
  358. BYTE * pbOldBase = (BYTE *) _streamBuf.Get();
  359. unsigned cbOld = (unsigned)(_pbCurrent - (BYTE *) _streamBuf.Get());
  360. _stream->Unmap( _streamBuf );
  361. ULONG sizeNew = CommonPageRound( cbOld + cbToGrow );
  362. _stream->SetSize( _storage, sizeNew );
  363. #if CIDBG == 1
  364. // For testing failure at this point...
  365. if ( g_fFailDirectoryMergeExtend )
  366. {
  367. g_fFailDirectoryMergeExtend = FALSE;
  368. THROW( CException( E_OUTOFMEMORY ) );
  369. }
  370. #endif // CIDBG == 1
  371. _stream->MapAll( _streamBuf );
  372. _pcKeys = (ULONG *) _streamBuf.Get();
  373. _pbCurrent = (BYTE*) _streamBuf.Get() + cbOld;
  374. //
  375. // Rebase all the pointers in the key array if the new base
  376. // address of the mapping is different than the old one.
  377. //
  378. BYTE * pbNewBase = (BYTE *) _streamBuf.Get();
  379. if ( pbOldBase != pbNewBase )
  380. {
  381. for ( unsigned i = 0; i < _cKeys; i++ )
  382. {
  383. BYTE * p = (BYTE *) _aKeys[ i ];
  384. p = p - pbOldBase + pbNewBase;
  385. _aKeys[ i ] = (CDirectoryKey *) p;
  386. }
  387. }
  388. else
  389. {
  390. ciDebugOut(( DEB_ITRACE, "no cidir rebasing needed\n" ));
  391. }
  392. // Toss the .dir file pages out of the working set
  393. _streamBuf.PurgeFromWorkingSet( -1, -1 );
  394. }
  395. } //GrowIfNeeded
  396. //+---------------------------------------------------------------------------
  397. //
  398. // Member: CiDirectory::LokWriteKey, private
  399. //
  400. // Synopsis: Adds entry to directory
  401. //
  402. // Arguments: [key] -- key to add
  403. // [bitOffset] -- offset of key in file
  404. //
  405. // History: 02-May-94 DwightKr Created
  406. //
  407. // Notes: Writes a single key to the mapped buffer.
  408. //
  409. //----------------------------------------------------------------------------
  410. inline void CiDirectory::LokWriteKey(
  411. const CKeyBuf & key,
  412. BitOffset & bitOffset )
  413. {
  414. // If this is the first key, open the stream for writing
  415. if ( 0 == _cKeys )
  416. {
  417. // Close any existing stream from a failed merge
  418. LokClose();
  419. _stream.Set( _storage.QueryExistingDirStream ( _objectId, TRUE ) );
  420. if ( 0 == _stream.GetPointer() || !_stream->Ok() )
  421. {
  422. _stream.Free();
  423. _stream.Set( _storage.QueryNewDirStream ( _objectId ) );
  424. }
  425. if ( ( 0 == _stream.GetPointer() ) || !_stream->Ok() )
  426. THROW( CException ( STATUS_NO_MEMORY ) );
  427. _stream->SetSize( _storage, COMMON_PAGE_SIZE );
  428. _stream->MapAll( _streamBuf );
  429. _pcKeys = (ULONG *) _streamBuf.Get();
  430. _pbCurrent = (BYTE*) ( _pcKeys + 2 );
  431. // Make sure the stream is in good shape in case we stop and restart
  432. // the merge.
  433. _pcKeys[0] = 0;
  434. _pcKeys[1] = 0;
  435. _stream->Flush( _streamBuf, 2 * sizeof ULONG, TRUE );
  436. }
  437. Win4Assert( key.Count() <= 0xff );
  438. BYTE count = (BYTE)key.Count();
  439. unsigned size = CDirectoryKey::ComputeSize( count, key.Pid() );
  440. GrowIfNeeded( size );
  441. CDirectoryKey *pkey = new( _pbCurrent ) CDirectoryKey;
  442. // write the key
  443. pkey->Write( count, bitOffset, key.Pid(), key.GetBuf() );
  444. if ( _cKeys >= _aKeys.Count() )
  445. _aKeys.ReSize( __max( 32, _cKeys * 2 + 1 ) );
  446. _aKeys[ _cKeys ] = pkey;
  447. _pbCurrent += size;
  448. _cKeys++;
  449. } //LokWriteKey
  450. //+---------------------------------------------------------------------------
  451. //
  452. // Member: CiDirectory::LokBuildDir, public
  453. //
  454. // Synopsis: Builds the directory tree correspdong to the leaf pages
  455. //
  456. // Arguments: [maxKey] -- largest key to add to directory
  457. //
  458. // History: ??-???-?? ???????? Created
  459. // 02-May-94 DwightKr Added maxKey argument
  460. //
  461. // Notes: Writes keys from the beginning of the array up to and including
  462. // maxKey (which is usually the splitKey). This will result in
  463. // a directory that contains entries upto and including maxKey.
  464. // It is certainly possible that the key array has keys past
  465. // maxKey, but we don't want to add them to the directory since
  466. // maxKey represents the largest key that has persistently
  467. // written to disk during a master merge.
  468. //
  469. // The above would matter if downlevel master merges were
  470. // atomic throughout. If they ever become atomic, we
  471. // need to truncate the file after this key.
  472. //
  473. //----------------------------------------------------------------------------
  474. void CiDirectory::LokBuildDir(
  475. const CKeyBuf & maxKey )
  476. {
  477. ciDebugOut(( DEB_PDIR, "PDir::LokBuildDir %d\n", _cKeys ));
  478. CKeyBuf maxKeyValue;
  479. maxKeyValue.FillMax();
  480. //
  481. // Write out a maximum key at the end.
  482. // The offset for the maximum key is not actually used my anyone,
  483. // since the directory is structured to find a key <= the desired
  484. // key. If someone generates a query for maxKey then everything
  485. // will be returned. The offset of maxKey is therefore arbitrary.
  486. //
  487. BitOffset maxBitOffset;
  488. maxBitOffset.Init( 0, 0 );
  489. LokWriteKey( maxKeyValue, maxBitOffset );
  490. //
  491. // Write the keys in the level 2 tree.
  492. // Write every DirectoryFanOut'th, so we can do a search on this Level2
  493. // first, and reduce the working set.
  494. //
  495. _cLevel2Keys = 0;
  496. CDirectoryKey *pkey = _aKeys[ 0 ];
  497. for ( unsigned x = 0; x < _cKeys; x++ )
  498. {
  499. if ( 0 == ( x & ( eDirectoryFanOut - 1 ) ) )
  500. {
  501. unsigned cb = pkey->Size();
  502. unsigned oKey = (unsigned)((BYTE *) pkey - (BYTE *) _streamBuf.Get());
  503. GrowIfNeeded( cb );
  504. pkey = (CDirectoryKey *) ( (BYTE *) _streamBuf.Get() + oKey );
  505. RtlCopyMemory( _pbCurrent, pkey, cb );
  506. _pbCurrent += cb;
  507. _cLevel2Keys++;
  508. }
  509. pkey = pkey->NextKey();
  510. }
  511. //
  512. // Write another max-key at the end. Decrement the # of keys since it
  513. // is incorrectly incremented in LokWriteKey()
  514. //
  515. LokWriteKey( maxKeyValue, maxBitOffset );
  516. _cLevel2Keys++;
  517. _cKeys--;
  518. // write the # of keys in the file and the # of keys in level 2.
  519. _pcKeys[0] = _cKeys;
  520. _pcKeys[1] = _cLevel2Keys;
  521. // remember the size of the file used and release the mapping
  522. unsigned cbFile = (unsigned)(_pbCurrent - (BYTE *) _streamBuf.Get());
  523. _stream->Flush( _streamBuf, cbFile, TRUE );
  524. _stream->Unmap( _streamBuf );
  525. // truncate and close the file
  526. _stream->SetSize( _storage, cbFile );
  527. _stream->Close();
  528. _stream.Free();
  529. _cKeys = 0;
  530. // read the directory back in, in read-only mode
  531. ReadIn( FALSE );
  532. } //LokBuildDir
  533. //+-------------------------------------------------------------------------
  534. //
  535. // Method: CiDirectory::ReadIn
  536. //
  537. // Synopsis: Load directory from storage
  538. //
  539. // Arguments: [fWrite] -- If TRUE, we're recovering from a stopped master
  540. // merge, either clean or dirty. This can be TRUE
  541. // even on read only catalogs if a MM is paused.
  542. //
  543. // History: 17-Feb-1994 KyleP Added header
  544. //
  545. //--------------------------------------------------------------------------
  546. void CiDirectory::ReadIn( BOOL fWrite )
  547. {
  548. _stream.Set( _storage.QueryExistingDirStream( _objectId, fWrite ) );
  549. if ( ( 0 == _stream.GetPointer() ) || !_stream->Ok() )
  550. {
  551. if ( fWrite )
  552. {
  553. // New index; it'll be created later
  554. _stream.Free();
  555. return;
  556. }
  557. else
  558. {
  559. Win4Assert( !"Corrupt directory" );
  560. _storage.ReportCorruptComponent( L"IndexDirectory1" );
  561. THROW( CException( CI_CORRUPT_DATABASE ) );
  562. }
  563. }
  564. _stream->MapAll( _streamBuf );
  565. BYTE * pbBuf = (BYTE *) _streamBuf.Get();
  566. BYTE * pbEnd = pbBuf + _streamBuf.Size();
  567. if ( _streamBuf.Size() <= ( 2 * sizeof ULONG ) )
  568. {
  569. Win4Assert( !"Corrupt directory" );
  570. _storage.ReportCorruptComponent( L"IndexDirectory2" );
  571. THROW( CException( CI_CORRUPT_DATABASE ) );
  572. }
  573. // The count of keys is stored in the first 4 bytes
  574. // The count of level 2 keys is stored in the next 4 bytes
  575. // There can be 0 level 2 keys if we stopped during the middle
  576. // of a master merge.
  577. // An empty index has a directory with 1 level 1 max key and 2 level
  578. // 2 max keys.
  579. _pcKeys = (ULONG *) pbBuf;
  580. _cKeys = _pcKeys[0];
  581. _cLevel2Keys = _pcKeys[1];
  582. if ( ( !fWrite && 0 == _cKeys ) ||
  583. ( _cLevel2Keys > ( _cKeys + 1 ) ) ||
  584. ( !fWrite && ( 0 == _cLevel2Keys ) ) )
  585. {
  586. Win4Assert( !"Corrupt directory" );
  587. _storage.ReportCorruptComponent( L"IndexDirectory3" );
  588. THROW( CException( CI_CORRUPT_DATABASE ) );
  589. }
  590. pbBuf += ( 2 * sizeof ULONG );
  591. // Store pointers to each of the keys
  592. _aKeys.Free();
  593. _aKeys.Init( _cKeys );
  594. CDirectoryKey *pkey = new( pbBuf ) CDirectoryKey;
  595. for ( unsigned x = 0; x < _cKeys; x++ )
  596. {
  597. if ( (BYTE *) pkey >= pbEnd )
  598. {
  599. Win4Assert( !"Corrupt directory" );
  600. _storage.ReportCorruptComponent( L"IndexDirectory5" );
  601. THROW( CException( CI_CORRUPT_DATABASE ) );
  602. }
  603. _aKeys[ x ] = pkey;
  604. pkey = pkey->NextKey();
  605. }
  606. _aLevel2Keys.Free();
  607. //
  608. // Level2 keys can exist when fWrite is TRUE if we failed a master
  609. // merge after building the level2 directory. It's likely the failure
  610. // is in recording the transaction that the merge completed.
  611. //
  612. if ( fWrite )
  613. _cLevel2Keys = 0;
  614. else if ( 0 != _cLevel2Keys )
  615. {
  616. _aLevel2Keys.Init( _cLevel2Keys );
  617. for ( x = 0; x < _cLevel2Keys; x++ )
  618. {
  619. if ( (BYTE *) pkey >= pbEnd )
  620. {
  621. Win4Assert( !"Corrupt directory" );
  622. _storage.ReportCorruptComponent( L"IndexDirectory6" );
  623. THROW( CException( CI_CORRUPT_DATABASE ) );
  624. }
  625. _aLevel2Keys[ x ] = pkey;
  626. pkey = pkey->NextKey();
  627. }
  628. // If we didn't read as many keys as expected, the file is corrupt.
  629. // This is only true if the directory has level 2 keys, since the
  630. // file may have bogus data at the end if we are in the middle of
  631. // a master merge.
  632. if ( (BYTE *) pkey != pbEnd )
  633. {
  634. ciDebugOut(( DEB_WARN, "pkey, pbEnd: 0x%x, 0x%x\n", pkey, pbEnd ));
  635. Win4Assert( !"Corrupt directory" );
  636. _storage.ReportCorruptComponent( L"IndexDirectory4" );
  637. THROW( CException( CI_CORRUPT_DATABASE ) );
  638. }
  639. }
  640. // Initialize _pbCurrent, so we can restart a failed master merge
  641. if ( fWrite )
  642. _pbCurrent = (BYTE *) pkey;
  643. // Toss the .dir file pages out of the working set
  644. _streamBuf.PurgeFromWorkingSet( -1, -1 );
  645. // toss the level1 directory from the working set
  646. VirtualUnlock( _aKeys.GetPointer(), _aKeys.SizeOf() );
  647. _fReadOnly = !fWrite;
  648. } //ReadIn
  649. //+-------------------------------------------------------------------------
  650. //
  651. // Member: CiDirectory::Close, public
  652. //
  653. // Effects: Closes the directory. Usually called when a merge fails.
  654. //
  655. // History: 21-Apr-92 BartoszM Created
  656. //
  657. //--------------------------------------------------------------------------
  658. void CiDirectory::Close()
  659. {
  660. LokClose();
  661. } //Close
  662. //+-------------------------------------------------------------------------
  663. //
  664. // Member: CiDirectory::LokClose, public
  665. //
  666. // Effects: Closes the directory. Usually called when a merge fails.
  667. //
  668. // History: 18-Nov-97 dlee Created
  669. //
  670. //--------------------------------------------------------------------------
  671. void CiDirectory::LokClose()
  672. {
  673. if ( !_stream.IsNull() )
  674. {
  675. // The _stream object can exist but not have a file open at this
  676. // point if a merge failed, so only unmap what is mapped.
  677. if ( ( _stream->Ok() ) && ( 0 != _streamBuf.Get() ) )
  678. _stream->Unmap( _streamBuf );
  679. _stream.Free();
  680. }
  681. } //LokClose
  682. //+---------------------------------------------------------------------------
  683. //
  684. // Function: DeleteKeysAfter
  685. //
  686. // Synopsis: Deletes all keys in the tree after the specified key.
  687. //
  688. // Arguments: [key] - The key after which all keys in the tree must be
  689. // deleted.
  690. //
  691. // History: 13-Nov-97 dlee Created
  692. //
  693. // Notes: Note that "key" is NOT deleted - only keys after "key" are
  694. // deleted.
  695. //
  696. //----------------------------------------------------------------------------
  697. void CiDirectory::DeleteKeysAfter( const CKeyBuf & key )
  698. {
  699. //
  700. // If we're restarting a failed master merge after LokBuildDir
  701. // succeeded, we have to close re-open the stream for write.
  702. //
  703. if ( _fReadOnly )
  704. {
  705. LokClose();
  706. ReadIn( TRUE );
  707. }
  708. else if ( 0 != _cKeys )
  709. ReMapIfNeeded();
  710. // Now, wipe the level 2, since we're still building level 1
  711. _cLevel2Keys = 0;
  712. _aLevel2Keys.Free();
  713. // Truncate level 1 at keys <= key
  714. if ( 0 != _cKeys )
  715. {
  716. // use the tree to find the greatest key <= key
  717. ULONG iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), 0, _cKeys );
  718. //
  719. // Move to the next key which must be > the seek key, but may be
  720. // beyond the # of keys in the directory.
  721. //
  722. iKey++;
  723. if ( iKey < _cKeys )
  724. {
  725. Win4Assert( _aKeys[ iKey ]->IsGreaterThanKeyBuf( key ) );
  726. _cKeys = iKey;
  727. _pbCurrent = (BYTE *) _aKeys[iKey];
  728. }
  729. }
  730. } //DeleteKeysAfter
  731. //+---------------------------------------------------------------------------
  732. //
  733. // Function: LokFlushDir
  734. //
  735. // Synopsis: Flushes the current state of the directory, up to and
  736. // including the key specified. Called at checkpoints during
  737. // a master merge. There may be some keys after the key
  738. // specified. Leave them in the file, but don't include them
  739. // in the count of keys.
  740. //
  741. // Arguments: [key] - The key before which and including is flushed
  742. //
  743. // History: 10-Aug-98 dlee Created
  744. //
  745. //----------------------------------------------------------------------------
  746. void CiDirectory::LokFlushDir( const CKeyBuf & key )
  747. {
  748. Win4Assert( !_fReadOnly );
  749. Win4Assert( 0 == _cLevel2Keys );
  750. // Truncate level 1 at keys <= key
  751. if ( 0 != _cKeys )
  752. {
  753. ReMapIfNeeded();
  754. // use the tree to find the greatest key <= key
  755. ULONG iKey = DoSeekForKeyBuf( key, _aKeys.GetPointer(), 0, _cKeys );
  756. _pcKeys[0] = iKey + 1;
  757. _pcKeys[1] = 0;
  758. _streamBuf.Flush( TRUE );
  759. }
  760. } //LokFlushDir
  761. //+---------------------------------------------------------------------------
  762. //
  763. // Member: CiDirectory::MakeBackupCopy
  764. //
  765. // Synopsis: Creates a backup of the persistent directory using the
  766. // storage provided.
  767. //
  768. // Arguments: [storage] - Destination storage
  769. // [progressTracker] - Track progress and aborts.
  770. //
  771. // History: 3-18-97 srikants Created
  772. //
  773. //----------------------------------------------------------------------------
  774. void CiDirectory::MakeBackupCopy( PStorage & storage,
  775. PSaveProgressTracker & progressTracker )
  776. {
  777. CiStorage & dstStorage = *((CiStorage *)&storage);
  778. XPtr<PMmStream> dstStream;
  779. CMmStreamBuf dstStreamBuf;
  780. dstStream.Set( dstStorage.QueryExistingDirStream( _objectId, TRUE ) );
  781. if ( 0 == dstStream.GetPointer() || !dstStream->Ok() )
  782. {
  783. dstStream.Free();
  784. dstStream.Set( dstStorage.QueryNewDirStream( _objectId ) );
  785. }
  786. Win4Assert( 0 != _stream.GetPointer() );
  787. ULONG cb = _streamBuf.Size();
  788. dstStream->SetSize( dstStorage, cb );
  789. dstStream->MapAll( dstStreamBuf );
  790. RtlCopyMemory( dstStreamBuf.Get(), _streamBuf.Get(), cb );
  791. dstStream->Flush( dstStreamBuf, cb );
  792. dstStream->Unmap( dstStreamBuf );
  793. } //MakeBackupCopy