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.

4934 lines
158 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1991 - 2000.
  5. //
  6. // File: PropStor.cxx
  7. //
  8. // Contents: Persistent property store (external to docfile)
  9. //
  10. // Classes: CPropertyStore
  11. //
  12. // History: 27-Dec-1995 KyleP Created
  13. //
  14. //----------------------------------------------------------------------------
  15. #include <pch.cxx>
  16. #pragma hdrstop
  17. #include <cistore.hxx>
  18. #include <rcstrmit.hxx>
  19. #include <proprec.hxx>
  20. #include <propstor.hxx>
  21. #include <propiter.hxx>
  22. #include <borrow.hxx>
  23. #include <propobj.hxx>
  24. #include <eventlog.hxx>
  25. #include <psavtrak.hxx>
  26. #include <catalog.hxx>
  27. #include <imprsnat.hxx>
  28. #include <mmstrm.hxx>
  29. #include <cievtmsg.h>
  30. unsigned const MAX_DIRECT = 10;
  31. const ULONG lowDiskWaterMark = 3 * 512 * 1024; // 1.5 MB
  32. //+---------------------------------------------------------------------------
  33. //
  34. // Member: CPropStoreInfo::CPropStoreInfo, public
  35. //
  36. // Synopsis: Required for C++ EH.
  37. //
  38. // History: 27-Dec-95 KyleP Created.
  39. //
  40. //----------------------------------------------------------------------------
  41. CPropStoreInfo::CPropStoreInfo(DWORD dwStoreLevel)
  42. : _fOwned(FALSE)
  43. {
  44. _info.dwStoreLevel = dwStoreLevel;
  45. _info.fDirty = 0;
  46. //
  47. // We intend to have a lean primary property store and a normal secondary
  48. // property store. Whatever is set here can be changed later as new info
  49. // becomes available.
  50. //
  51. _info.eRecordFormat = (PRIMARY_STORE == dwStoreLevel) ? eLean : eNormal;
  52. SYSTEM_INFO si;
  53. GetSystemInfo(&si);
  54. _ulOSPageSize = si.dwPageSize;
  55. Win4Assert(0 == COMMON_PAGE_SIZE%_ulOSPageSize);
  56. _cOSPagesPerLargePage = COMMON_PAGE_SIZE/_ulOSPageSize;
  57. }
  58. //+---------------------------------------------------------------------------
  59. //
  60. // Member: CPropStoreInfo::CPropStoreInfo, public
  61. //
  62. // Synopsis: Copy constructor
  63. //
  64. // Arguments: [psi] -- Source
  65. //
  66. // History: 16-Jan-96 KyleP Created.
  67. //
  68. //----------------------------------------------------------------------------
  69. CPropStoreInfo::CPropStoreInfo( CPropStoreInfo const & psi )
  70. {
  71. _cRecPerPage = psi._cRecPerPage;
  72. _info = psi._info;
  73. _info.fDirty = fDirtyPropStore;
  74. _info.widMax = 0;
  75. _info.widFreeHead = 0;
  76. _info.widFreeTail = 0;
  77. _info.widStream = widInvalid;
  78. _info.cTopLevel = 0;
  79. _info.dwStoreLevel = psi._info.dwStoreLevel;
  80. _info.eRecordFormat = psi._info.eRecordFormat;
  81. _fOwned = FALSE;
  82. _aProp.Init( psi._aProp );
  83. _cOSPagesPerLargePage = psi._cOSPagesPerLargePage;
  84. _ulOSPageSize = psi._ulOSPageSize;
  85. }
  86. //+---------------------------------------------------------------------------
  87. //
  88. // Member: CPropStoreInfo::Empty
  89. //
  90. // Synopsis: Empties the contents. This method is called due to a failed
  91. // initialization of CI. After cleanup, another call to Init
  92. // may be made.
  93. //
  94. // History: 3-18-96 srikants Created
  95. //
  96. //----------------------------------------------------------------------------
  97. void CPropStoreInfo::Empty()
  98. {
  99. delete [] _aProp.Acquire();
  100. if ( _fOwned )
  101. _xrsoPropStore.Free();
  102. else
  103. _xrsoPropStore.Acquire();
  104. }
  105. //+---------------------------------------------------------------------------
  106. //
  107. // Member: CPropStoreInfo::FastTransfer
  108. //
  109. // Synopsis: Companion call to CPropertyStore::FastTransfer. Adjusts
  110. // metadata assuming CPropertyStore::FastTransfer has just
  111. // been called.
  112. //
  113. // History: 10-Oct-1997 KyleP Created
  114. //
  115. //----------------------------------------------------------------------------
  116. void CPropStoreInfo::FastTransfer( CPropStoreInfo const & psi )
  117. {
  118. _info.widMax = psi._info.widMax;
  119. _info.widFreeHead = psi._info.widFreeHead;
  120. _info.widFreeTail = psi._info.widFreeTail;
  121. _info.cTopLevel = psi._info.cTopLevel;
  122. }
  123. //+---------------------------------------------------------------------------
  124. //
  125. // Member: CPropStoreInfo::Init, public
  126. //
  127. // Synopsis: Loads metadata from persistent location into memory.
  128. //
  129. // Arguments: [pobj] -- Stream(s) in which metadata is stored.
  130. //
  131. // History: 27-Dec-95 KyleP Created.
  132. //
  133. //----------------------------------------------------------------------------
  134. void CPropStoreInfo::Init( XPtr<PRcovStorageObj> & xobj,
  135. DWORD dwStoreLevel )
  136. {
  137. _xrsoPropStore.Set( xobj.Acquire() );
  138. _fOwned = TRUE;
  139. //
  140. // Load header
  141. //
  142. CRcovStorageHdr & hdr = _xrsoPropStore->GetHeader();
  143. struct CRcovUserHdr data;
  144. hdr.GetUserHdr( hdr.GetPrimary(), data );
  145. RtlCopyMemory( &_info, &data._abHdr, sizeof(_info) );
  146. _info.dwStoreLevel = dwStoreLevel;
  147. //
  148. // If we only have no properties, set the record format based on
  149. // storage type. Else, assert that we have the expected format type.
  150. //
  151. if ( 0 == CountProps() )
  152. {
  153. _info.eRecordFormat = (PRIMARY_STORE == dwStoreLevel) ? eLean : eNormal;
  154. }
  155. #if CIDBG == 1
  156. else
  157. {
  158. if ( CountFixedProps() == CountProps() )
  159. Win4Assert( eLean == GetRecordFormat() );
  160. else
  161. Win4Assert( eNormal == GetRecordFormat() );
  162. }
  163. #endif // CIDBG
  164. //
  165. // For consistency...
  166. //
  167. if ( 0 == _info.widStream )
  168. _info.widStream = widInvalid;
  169. if ( 0 == _info.culRecord )
  170. _info.culRecord = (eLean == GetRecordFormat()) ?
  171. COnDiskPropertyRecord::FixedOverheadLean() :
  172. COnDiskPropertyRecord::FixedOverheadNormal();
  173. _cRecPerPage = COMMON_PAGE_SIZE / (_info.culRecord * sizeof(ULONG));
  174. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: %s Store.\n",
  175. (dwStoreLevel == PRIMARY_STORE) ? "Primary" : "Secondary"));
  176. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: Version = 0x%x\n", _info.Version ));
  177. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: Record size = %d bytes\n", _info.culRecord * sizeof(ULONG) ));
  178. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: Fixed record size = %d bytes\n", _info.culFixed * sizeof(ULONG) ));
  179. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: Store is %s\n", (_info.fDirty & fDirtyPropStore) ? "dirty" : "clean" ));
  180. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: Is NOT bacup mode: %d\n", 0 != (_info.fDirty & fNotBackedUp) ));
  181. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: %d properties stored\n", _info.cTotal ));
  182. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: %d fixed properties stored\n", _info.cFixed ));
  183. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: %s record format.\n",
  184. (eLean == GetRecordFormat()) ? "Lean" : "Normal (not-lean)"));
  185. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: Hash size = %u\n", _info.cHash ));
  186. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: Max workid = %u\n", _info.widMax ));
  187. ciDebugOut(( DEB_PROPSTORE, "PROPSTORE: %d records per %dK page\n", _cRecPerPage, COMMON_PAGE_SIZE / 1024 ));
  188. //
  189. // Load properties
  190. //
  191. _aProp.Init( _info.cHash );
  192. CRcovStrmReadTrans xact( _xrsoPropStore.GetReference() );
  193. CRcovStrmReadIter iter( xact, sizeof( CPropDesc ) );
  194. CPropDesc temp;
  195. while ( !iter.AtEnd() )
  196. {
  197. iter.GetRec( &temp );
  198. if ( temp.IsFixedSize() )
  199. ciDebugOut(( DEB_PROPSTORE,
  200. "PROPSTORE: pid = 0x%x, ordinal = %u, size = %u, offset = %u, fixed\n",
  201. temp.Pid(),
  202. temp.Ordinal(),
  203. temp.Size(),
  204. temp.Offset() ));
  205. else
  206. ciDebugOut(( DEB_PROPSTORE,
  207. "PROPSTORE: pid = 0x%x, ordinal = %u, cbMax = %d, vt = 0x%x variable\n",
  208. temp.Pid(),
  209. temp.Ordinal(),
  210. temp.Size(),
  211. temp.Type() ));
  212. _aProp[Lookup(temp.Pid())] = temp;
  213. }
  214. }
  215. //+---------------------------------------------------------------------------
  216. //
  217. // Member: CPropStoreInfo::Commit
  218. //
  219. // Synopsis: Commits the transaction on disk and then in-memory.
  220. //
  221. // Arguments: [psi] - The new property store information.
  222. // [xact] - The persistent transaction to be committed.
  223. //
  224. // History: 3-26-96 srikants Created
  225. //
  226. // Notes:
  227. //
  228. //----------------------------------------------------------------------------
  229. void CPropStoreInfo::Commit( CPropStoreInfo & psi, CRcovStrmWriteTrans & xact )
  230. {
  231. //
  232. // Copy the in-memory structure to disk.
  233. //
  234. CRcovStorageHdr & hdr = _xrsoPropStore->GetHeader();
  235. CRcovStrmWriteIter iter( xact, sizeof(CPropDesc) );
  236. hdr.SetUserDataSize( hdr.GetBackup(), iter.UserDataSize( psi._info.cTotal ) );
  237. unsigned iRec = 0;
  238. for ( unsigned i = 0; i < psi._aProp.Count(); i++ )
  239. {
  240. ciDebugOut(( DEB_ITRACE, "_aProp[%d], Pid 0x%x Size %d Type 0x%x\n",
  241. i,
  242. psi._aProp[i].Pid(),
  243. psi._aProp[i].Size(),
  244. psi._aProp[i].Type() ));
  245. if ( !psi._aProp[i].IsFree() )
  246. {
  247. psi._aProp[i].SetRecord( iRec );
  248. iter.SetRec( &psi._aProp[i], iRec );
  249. ciDebugOut(( DEB_ITRACE, "Pid 0x%x --> Slot %d\n", psi._aProp[i].Pid(), iRec ));
  250. iRec++;
  251. }
  252. }
  253. Win4Assert( iRec == psi._info.cTotal );
  254. struct CRcovUserHdr data;
  255. RtlCopyMemory( &data._abHdr, &psi._info, sizeof(_info) );
  256. hdr.SetCount(hdr.GetBackup(), psi._info.cTotal );
  257. hdr.SetUserHdr( hdr.GetBackup(), data );
  258. //
  259. // First commit the on-disk transaction.
  260. //
  261. xact.Commit();
  262. //
  263. // NO FAILURES AFTER THIS
  264. //
  265. //
  266. // Copy the data from the new property store info.
  267. //
  268. _cRecPerPage = psi._cRecPerPage;
  269. _info = psi._info;
  270. Win4Assert( psi._xrsoPropStore.IsNull() );
  271. Win4Assert( _fOwned && !psi._fOwned );
  272. //
  273. // Update the property array.
  274. //
  275. delete [] _aProp.Acquire();
  276. unsigned count = psi._aProp.Count();
  277. _aProp.Set( count, psi._aProp.Acquire() );
  278. }
  279. //+---------------------------------------------------------------------------
  280. //
  281. // Member: CPropStoreInfo::NextWorkId
  282. //
  283. // Synopsis: Changes the workid for the stream to be the next logical
  284. // one.
  285. //
  286. // Returns: The new work id.
  287. //
  288. // History: 3-26-96 srikants Created
  289. //
  290. //----------------------------------------------------------------------------
  291. WORKID CPropStoreInfo::NextWorkId(CiStorage & storage)
  292. {
  293. _info.Version++;
  294. _info.widStream = storage.CreateObjectId( CIndexId( _info.Version % (1 << 16), 0 ),
  295. PStorage::eNonSparseIndex );
  296. return _info.widStream;
  297. }
  298. //+---------------------------------------------------------------------------
  299. //
  300. // Member: CPropStoreInfo::InitWorkId
  301. //
  302. // Synopsis: Initializes the workid from the version number.
  303. //
  304. // Arguments: [storage] - Destination storage
  305. //
  306. // Returns: The WorkId generated for the property store stream
  307. //
  308. // History: 3-28-97 srikants Created
  309. //
  310. // Notes: When we add a property, we have to bump up the version # but
  311. // in case we are creating a backup, we should use the same
  312. // version #.
  313. //
  314. //----------------------------------------------------------------------------
  315. WORKID CPropStoreInfo::InitWorkId(CiStorage & storage)
  316. {
  317. _info.widStream = storage.CreateObjectId( CIndexId( _info.Version % (1 << 16), 0 ),
  318. PStorage::eNonSparseIndex );
  319. return _info.widStream;
  320. }
  321. //+---------------------------------------------------------------------------
  322. //
  323. // Member: CPropStoreInfo::Add, public
  324. //
  325. // Synopsis: Add a new property to set of cached properties.
  326. //
  327. // Arguments: [pid] -- Propid
  328. // [vt] -- Datatype
  329. // [cbMaxLen] -- Soft-maximum length. Used to compute default
  330. // size of record.
  331. // [fCanBeModified]
  332. // -- Indicates if the property can be modified once set.
  333. // [storage] -- Storage object (for object creation).
  334. //
  335. // Returns: TRUE if a change was made to metadata
  336. //
  337. // History: 27-Dec-95 KyleP Created.
  338. // 05-Jun-96 KyleP Moved on-disk transaction to higher level
  339. // 13-Nov-97 KrishnaN Modifiable?
  340. // 19-Dec-97 KrishnaN Lean record support.
  341. //
  342. //----------------------------------------------------------------------------
  343. BOOL CPropStoreInfo::Add( PROPID pid,
  344. ULONG vt,
  345. unsigned cbMaxLen,
  346. BOOL fCanBeModified,
  347. CiStorage & storage )
  348. {
  349. //
  350. // Check for exact duplicate.
  351. //
  352. CPropDesc const * pdesc = GetDescription( pid );
  353. if ( 0 != pdesc )
  354. {
  355. if ( !pdesc->Modifiable() )
  356. {
  357. ciDebugOut(( DEB_ITRACE, "Pid 0x%x: Exists in cache and marked UNModifiable!\n",
  358. pid ));
  359. return FALSE;
  360. }
  361. if ( vt == pdesc->Type() && cbMaxLen == pdesc->Size() )
  362. {
  363. ciDebugOut(( DEB_ITRACE, "Pid 0x%x: Type %d, Size %d already in cache.\n",
  364. pid, vt, cbMaxLen ));
  365. return FALSE;
  366. }
  367. ciDebugOut(( DEB_ITRACE, "Pid 0x%x: Type (%d, %d), Size (%d, %d)\n",
  368. pid, pdesc->Type(), vt, pdesc->Size(), cbMaxLen ));
  369. }
  370. else
  371. ciDebugOut(( DEB_ITRACE, "Can't find pid 0x%x in cache.\n", pid ));
  372. //
  373. // Compute size and position (ordinal) of property.
  374. //
  375. BOOL fFixed;
  376. switch ( vt )
  377. {
  378. case VT_I1:
  379. case VT_UI1:
  380. fFixed = TRUE;
  381. cbMaxLen = 1;
  382. break;
  383. case VT_I2:
  384. case VT_UI2:
  385. case VT_BOOL:
  386. fFixed = TRUE;
  387. cbMaxLen = 2;
  388. break;
  389. case VT_I4:
  390. case VT_UI4:
  391. case VT_R4:
  392. case VT_ERROR:
  393. fFixed = TRUE;
  394. cbMaxLen = 4;
  395. break;
  396. case VT_I8:
  397. case VT_UI8:
  398. case VT_R8:
  399. case VT_CY:
  400. case VT_DATE:
  401. case VT_FILETIME:
  402. fFixed = TRUE;
  403. cbMaxLen = 8;
  404. break;
  405. case VT_CLSID:
  406. fFixed = TRUE;
  407. cbMaxLen = sizeof(GUID);
  408. break;
  409. default:
  410. fFixed = FALSE;
  411. break;
  412. }
  413. // Ensure we don't exceed the max possible size of a single record.
  414. // The trick to the check is the assumption that the maximum impact
  415. // on the size of the record is the size of the largest fixed prop.
  416. // Currently it is sizeof(GUID), so we just check that there is
  417. // enough space for it. And if there is, we are fine!
  418. // Fix for bug 119508.
  419. // If this assert doesn't hold, then change it to whatever size is the
  420. // maximum fixed size and change the (_info.culFixed*4 + sizeof(GUID))
  421. // portion of the following if statement to reflect that!
  422. Win4Assert(!fFixed || (fFixed && cbMaxLen <= sizeof(GUID)));
  423. if ((_info.culFixed*4 + sizeof(GUID)) >= COMMON_PAGE_SIZE)
  424. {
  425. ciDebugOut(( DEB_ITRACE, "Total fixed size (in bytes) %d exceeds"
  426. " max record size of %d\n",
  427. _info.culFixed*4 + cbMaxLen, COMMON_PAGE_SIZE ));
  428. return FALSE;
  429. }
  430. BOOL fDidDeletion = Delete( pid, storage );
  431. //
  432. // Hash table size will have to change if:
  433. // a) pid > MAX_DIRECT and we're in direct hash mode, or
  434. // b) hash table is over 3/4 full, or
  435. //
  436. if ( (pid > MAX_DIRECT && _info.cHash == MAX_DIRECT) ||
  437. // (hdr.GetCount(hdr.GetPrimary()) * 3 + 1 > _info.cHash * sizeof(ULONG)) )
  438. (_info.cTotal * sizeof(ULONG) + 1 > _info.cHash * 3) )
  439. {
  440. XArray<CPropDesc> xold( _aProp );
  441. _info.cHash *= 2;
  442. if ( 0 == _info.cHash )
  443. _info.cHash = MAX_DIRECT;
  444. ciDebugOut(( DEB_PROPSTORE, "growing _aProp size from %d to %d\n",
  445. xold.Count(), _info.cHash ));
  446. _aProp.Init( _info.cHash );
  447. for ( unsigned i = 0; i < xold.Count(); i++ )
  448. {
  449. if ( !xold[i].IsFree() )
  450. {
  451. ciDebugOut(( DEB_PROPSTORE,
  452. "re-adding pid %d from [%d] to [%d]\n",
  453. i,
  454. LookupNew(xold[i].Pid()) ));
  455. _aProp[LookupNew(xold[i].Pid())] = xold[i];
  456. }
  457. }
  458. }
  459. //
  460. // Ordinal and starting offset are computed differently for fixed and
  461. // variable properties. A new fixed property goes at the end of the
  462. // fixed section, and a new variable property goes at the end.
  463. //
  464. DWORD oStart;
  465. DWORD ordinal;
  466. if ( fFixed )
  467. {
  468. ordinal = _info.cFixed;
  469. oStart = _info.culFixed;
  470. _info.cFixed++;
  471. _info.culFixed += (cbMaxLen - 1) / sizeof(ULONG) + 1;
  472. //
  473. // Since we just added an ordinal in the middle, we now need to go through and
  474. // find the first variable length property and move it to the end.
  475. //
  476. for ( unsigned i = 0; i < _aProp.Count(); i++ )
  477. {
  478. if ( !_aProp[i].IsFree() && _aProp[i].Ordinal() == ordinal )
  479. {
  480. #if DBG == 1 || CIDBG == 1
  481. ULONG ordinal2 = _aProp[i].Ordinal();
  482. #endif
  483. _aProp[i].SetOrdinal( _info.cTotal );
  484. ciDebugOut(( DEB_PROPSTORE,
  485. "PROPSTORE: pid = 0x%x moved from ordinal = %u to ordinal %u\n",
  486. _aProp[i].Pid(),
  487. ordinal2,
  488. _aProp[i].Ordinal() ));
  489. break;
  490. }
  491. }
  492. }
  493. else
  494. {
  495. ordinal = _info.cTotal;
  496. oStart = 0xFFFFFFFF;
  497. }
  498. //
  499. // Add new record to in-memory and on-disk structures.
  500. //
  501. ciDebugOut(( DEB_PROPSTORE,
  502. "PROPSTORE: pid = 0x%x added at _aProp[%d], vt: 0x%x, old pid: 0x%x\n",
  503. pid,
  504. LookupNew(pid),
  505. vt,
  506. _aProp[LookupNew(pid)].Pid() ));
  507. _aProp[LookupNew(pid)].Init( pid, // Propid
  508. vt, // Data type
  509. oStart, // Starting offset
  510. cbMaxLen, // Length estimate
  511. ordinal, // Ordinal (position) of property
  512. 0, // Record number (filled in later)
  513. fCanBeModified); // Can this metadata be modified later?
  514. _info.cTotal++;
  515. //
  516. // Adjust size. We preallocated _aul[PREV], _aul[NEXT] and _aul[FREEBLOCKSIZE] to serve as
  517. // free list ptrs. So we should avoid allocating these ULONGs again. Watch the
  518. // allocation of dword for existence bits and space for first property. Since
  519. // all properties are allocated on sizeof(ULONG)-byte boundaries, we can monitor the allocation
  520. // of space for the first property and be sure that aul[1]'s preallocation gets
  521. // accounted for.
  522. //
  523. // The first time a property is added, it will cause a _aul[PREV] to be allocated
  524. // for existence bits. Since we preallocated that, we won't increase the
  525. // record length for the first set of 16 existence bits.
  526. if ( _info.cTotal > 1 && (_info.cTotal % 16) == 1 )
  527. _info.culRecord += 1; // New dword of existence bits
  528. _info.culRecord += (cbMaxLen-1) / sizeof(ULONG) + 1; // The property itself
  529. // account for the preallocation of _aul[NEXT] (when space for first prop is allocated)
  530. // and _aul[FREEBLOCKSIZE] (when space for second prop is allocated). If we only have one prop allocated,
  531. // we will run with an overhead of 1 DWORD, but we know that we have many more than 2 properties!
  532. if ( _info.cTotal <= 2 )
  533. _info.culRecord -= 1;
  534. if ( !fFixed )
  535. _info.culRecord += 1; // Variable size dword
  536. _cRecPerPage = COMMON_PAGE_SIZE / (_info.culRecord * sizeof(ULONG));
  537. ciDebugOut(( DEB_PROPSTORE, "New record size: %d bytes. %d records per %dK page\n",
  538. _info.culRecord * sizeof(ULONG),
  539. _cRecPerPage,
  540. COMMON_PAGE_SIZE / 1024 ));
  541. Win4Assert( _cRecPerPage > 0 );
  542. if ( _cRecPerPage == 0 )
  543. {
  544. ciDebugOut(( DEB_ERROR, "Record size > %u bytes!\n", COMMON_PAGE_SIZE ));
  545. THROW( CException( STATUS_INVALID_PARAMETER ) );
  546. }
  547. #if CIDBG == 1
  548. for ( unsigned i = 0; i < _aProp.Count(); i++ )
  549. {
  550. ciDebugOut(( DEB_PROPSTORE, "_aProp[%d].pid: 0x%x\n",
  551. i, _aProp[i].Pid() ));
  552. }
  553. #endif // CIDBG == 1
  554. return TRUE;
  555. }
  556. //+---------------------------------------------------------------------------
  557. //
  558. // Member: CPropStoreInfo::DetectFormat, public
  559. //
  560. // Synopsis: Detect the type of records to be used and change according to that.
  561. //
  562. // History: 31-Dec-97 KrishnaN Created.
  563. //
  564. //----------------------------------------------------------------------------
  565. void CPropStoreInfo::DetectFormat()
  566. {
  567. // In CPropStoreInfo::Init() we initialized record length based on the
  568. // assumption that primary is lean and secondary is normal. That could have
  569. // changed, so account for that.
  570. LONG lOldOverhead = (eLean == GetRecordFormat()) ?
  571. COnDiskPropertyRecord::FixedOverheadLean() :
  572. COnDiskPropertyRecord::FixedOverheadNormal();
  573. // Determine the format of the records in the target property store.
  574. SetRecordFormat( CountFixedProps() == CountProps() ? eLean : eNormal);
  575. LONG lNewOverhead = (eLean == GetRecordFormat()) ?
  576. COnDiskPropertyRecord::FixedOverheadLean() :
  577. COnDiskPropertyRecord::FixedOverheadNormal();
  578. // Adjust the overhead
  579. _info.culRecord += (lNewOverhead - lOldOverhead);
  580. _cRecPerPage = COMMON_PAGE_SIZE / (_info.culRecord * sizeof(ULONG));
  581. ciDebugOut(( DEB_PROPSTORE, "Incorporated props from registry. "
  582. "New record size: %d bytes. %d records per %dK page\n",
  583. _info.culRecord * sizeof(ULONG),
  584. _cRecPerPage,
  585. COMMON_PAGE_SIZE / 1024 ));
  586. }
  587. //+---------------------------------------------------------------------------
  588. //
  589. // Member: CPropStoreInfo::Delete, public
  590. //
  591. // Synopsis: Deletes a property from set of cached properties.
  592. //
  593. // Arguments: [pid] -- Propid
  594. // [storage] -- Storage object (for object creation).
  595. //
  596. // Returns: TRUE if a change was made to metadata
  597. //
  598. // History: 27-Dec-95 KyleP Created.
  599. // 05-Jun-96 KyleP Moved on-disk transaction to higher level
  600. //
  601. //----------------------------------------------------------------------------
  602. BOOL CPropStoreInfo::Delete( PROPID pid, CiStorage & storage )
  603. {
  604. //
  605. // Is there anything to get rid of?
  606. //
  607. CPropDesc const * pdesc = GetDescription( pid );
  608. if ( 0 == pdesc )
  609. return FALSE;
  610. if (!pdesc->Modifiable())
  611. {
  612. ciDebugOut(( DEB_ITRACE, "Pid 0x%x: Cannot be deleted. Marked UNModifiable!\n",
  613. pid ));
  614. return FALSE;
  615. }
  616. if ( pdesc->IsFixedSize() )
  617. {
  618. _info.cFixed--;
  619. _info.culFixed -= (pdesc->Size() - 1) / sizeof(ULONG) + 1;
  620. }
  621. for ( unsigned i = 0; i < _aProp.Count(); i++ )
  622. {
  623. if ( _aProp[i].Pid() != pidInvalid && _aProp[i].Ordinal() > pdesc->Ordinal() )
  624. {
  625. if ( pdesc->IsFixedSize() && _aProp[i].IsFixedSize() )
  626. _aProp[i].SetOffset( _aProp[i].Offset() - ((pdesc->Size() - 1) / sizeof(ULONG) + 1) );
  627. _aProp[i].SetOrdinal( _aProp[i].Ordinal() - 1 );
  628. }
  629. }
  630. //
  631. // Global bookeeping
  632. //
  633. _info.cTotal--;
  634. //
  635. // Adjust size
  636. //
  637. _info.culRecord -= (pdesc->Size()-1) / sizeof(ULONG) + 1; // The property itself
  638. if ( !pdesc->IsFixedSize() )
  639. _info.culRecord -= 1; // Variable size dword
  640. if ( ((_info.cTotal) % 16) == 0 )
  641. _info.culRecord -= 1; // New dword of existence bits
  642. _cRecPerPage = COMMON_PAGE_SIZE / (_info.culRecord * sizeof(ULONG));
  643. ciDebugOut(( DEB_PROPSTORE, "New record size: %d bytes. %d records per %dK page\n",
  644. _info.culRecord * sizeof(ULONG),
  645. _cRecPerPage,
  646. COMMON_PAGE_SIZE / 1024 ));
  647. Win4Assert( _cRecPerPage > 0 );
  648. if ( _cRecPerPage == 0 )
  649. {
  650. ciDebugOut(( DEB_ERROR, "Record size > %u bytes!\n", COMMON_PAGE_SIZE ));
  651. THROW( CException( STATUS_INVALID_PARAMETER ) );
  652. }
  653. // Ensure that we have space for _aul[PREV], _aul[NEXT], and _aul[FREEBLOCKSIZE] We will have that as
  654. // long as we have at least two properties.
  655. ULONG ulOverhead = (eLean == GetRecordFormat()) ?
  656. COnDiskPropertyRecord::FixedOverheadLean() :
  657. COnDiskPropertyRecord::FixedOverheadNormal();
  658. if ( _info.culRecord < ulOverhead )
  659. {
  660. // As long as we have 2 or more properties, we wouldn't go under the
  661. // fixed overhead. Assert that!
  662. Win4Assert(_info.cTotal <= 1);
  663. _info.culRecord = ulOverhead;
  664. }
  665. //
  666. // Free record.
  667. //
  668. _aProp[Lookup(pid)].Free();
  669. return TRUE;
  670. }
  671. //+---------------------------------------------------------------------------
  672. //
  673. // Member: CPropStoreInfo::ChangeDirty, public
  674. //
  675. // Synopsis: Persistently change state of dirty bitfield
  676. //
  677. // Arguments: [fDirty] -- New state for dirty bitfield.
  678. //
  679. // History: 16-Jan-96 KyleP Created.
  680. //
  681. //----------------------------------------------------------------------------
  682. void CPropStoreInfo::ChangeDirty( int fDirty )
  683. {
  684. // In some error cases this can be null.
  685. if ( _xrsoPropStore.IsNull() )
  686. return;
  687. _info.fDirty = fDirty;
  688. //
  689. // Atomically write dirty bit.
  690. //
  691. CRcovStorageHdr & hdr = _xrsoPropStore->GetHeader();
  692. CRcovStrmWriteTrans xact( _xrsoPropStore.GetReference() );
  693. struct CRcovUserHdr data;
  694. RtlCopyMemory( &data._abHdr, &_info, sizeof(_info) );
  695. hdr.SetCount(hdr.GetBackup(), hdr.GetCount(hdr.GetPrimary()) );
  696. hdr.SetUserHdr( hdr.GetBackup(), data );
  697. xact.Commit();
  698. }
  699. //+---------------------------------------------------------------------------
  700. //
  701. // Member: CPropStoreInfo::Lookup, public
  702. //
  703. // Synopsis: Looks up pid in hash table.
  704. //
  705. // Arguments: [pid] -- Propid
  706. //
  707. // Returns: Index into hash table (_aProp) of pid, or first unused
  708. // entry on chain if pid doesn't exist.
  709. //
  710. // History: 27-Dec-95 KyleP Created.
  711. //
  712. //----------------------------------------------------------------------------
  713. unsigned CPropStoreInfo::Lookup( PROPID pid )
  714. {
  715. unsigned hash = pid % _info.cHash;
  716. // short-path for common case
  717. if ( pid == _aProp[hash].Pid() && _aProp[hash].IsInUse() )
  718. return hash;
  719. unsigned start = hash;
  720. unsigned probe = hash;
  721. while ( pid != _aProp[hash].Pid() && _aProp[hash].IsInUse() )
  722. {
  723. //ciDebugOut(( DEB_ERROR, "Hash: %u, Probe: %u, pid 0x%x != table 0x%x\n",
  724. // hash, probe, pid, _aProp[hash].Pid() ));
  725. hash = (hash + probe) % _info.cHash;
  726. if ( start == hash )
  727. {
  728. Win4Assert( probe != 1 );
  729. probe = 1;
  730. hash = (hash + probe) % _info.cHash;
  731. }
  732. }
  733. return hash;
  734. }
  735. //+---------------------------------------------------------------------------
  736. //
  737. // Member: CPropStoreInfo::LookupNew, public
  738. //
  739. // Synopsis: Looks up pid in hash table, treats nulled entries as empty.
  740. //
  741. // Arguments: [pid] -- Propid
  742. //
  743. // Returns: Index into hash table (_aProp) of pid, or first unused
  744. // entry on chain if pid doesn't exist.
  745. //
  746. // History: 27-Dec-95 KyleP Created.
  747. //
  748. //----------------------------------------------------------------------------
  749. unsigned CPropStoreInfo::LookupNew( PROPID pid )
  750. {
  751. unsigned hash = pid % _info.cHash;
  752. unsigned start = hash;
  753. unsigned probe = hash;
  754. while ( pid != _aProp[hash].Pid() && !_aProp[hash].IsFree() )
  755. {
  756. hash = (hash + probe) % _info.cHash;
  757. if ( start == hash )
  758. {
  759. Win4Assert( probe != 1 );
  760. probe = 1;
  761. hash = (hash + probe) % _info.cHash;
  762. }
  763. }
  764. return hash;
  765. }
  766. void CPhysPropertyStore::ReOpenStream()
  767. {
  768. Win4Assert( _stream.IsNull() );
  769. Win4Assert( !"Don't call CPhysPropertyStore::ReOpenStream" );
  770. //_stream = _storage.QueryExistingPropStream ( _obj, PStorage::eOpenForWrite );
  771. }
  772. //+---------------------------------------------------------------------------
  773. //
  774. // Member: CPropertyStore::CPropertyStore, public
  775. //
  776. // Synopsis: Required for C++ EH.
  777. //
  778. // History: 27-Dec-95 KyleP Created.
  779. //
  780. //----------------------------------------------------------------------------
  781. CPropertyStore::CPropertyStore(CPropStoreManager& propStoreMgr, DWORD dwStoreLevel)
  782. : _pStorage( 0 ),
  783. _aFreeBlocks( 0 ),
  784. _fAbort(FALSE),
  785. _fIsConsistent(TRUE),
  786. _ppsNew( 0 ),
  787. _fNew( FALSE ),
  788. _ulBackupSizeInPages( CI_PROPERTY_STORE_BACKUP_SIZE_DEFAULT ),
  789. _ulPSMappedCache( CI_PROPERTY_STORE_MAPPED_CACHE_DEFAULT ),
  790. _PropStoreInfo( dwStoreLevel ),
  791. _propStoreMgr( propStoreMgr ),
  792. _dwStoreLevel( dwStoreLevel )
  793. {
  794. #if CIDBG == 1
  795. _sigPSDebug = 0x2047554245445350i64; // PSDEBUG
  796. _tidReadSet = _tidReadReset = _tidWriteSet = _tidWriteReset = 0xFFFFFFFF;
  797. _xPerThreadReadCounts.Init( cTrackThreads );
  798. _xPerThreadWriteCounts.Init( cTrackThreads );
  799. RtlZeroMemory( _xPerThreadReadCounts.GetPointer(),
  800. cTrackThreads * sizeof(_xPerThreadReadCounts[0]) );
  801. RtlZeroMemory( _xPerThreadWriteCounts.GetPointer(),
  802. cTrackThreads * sizeof(_xPerThreadWriteCounts[0]) );
  803. #endif
  804. Win4Assert(PRIMARY_STORE == dwStoreLevel || SECONDARY_STORE == dwStoreLevel);
  805. #if CIDBG == 1
  806. // Allocate an array to track what records are currently locked.
  807. // To be used to acquire all write locks.
  808. _pbRecordLockTracker = new BYTE[LockMgr().UniqueRecordCount()];
  809. RtlZeroMemory(_pbRecordLockTracker, sizeof(BYTE)*LockMgr().UniqueRecordCount());
  810. #endif // CIDBG
  811. }
  812. //+---------------------------------------------------------------------------
  813. //
  814. // Member: CPropertyStore::~CPropertyStore, public
  815. //
  816. // Synopsis: Closes/flushes property cache.
  817. //
  818. // History: 27-Dec-95 KyleP Created.
  819. //
  820. //----------------------------------------------------------------------------
  821. CPropertyStore::~CPropertyStore()
  822. {
  823. delete _ppsNew;
  824. delete _aFreeBlocks;
  825. #if CIDBG == 1
  826. delete [] _pbRecordLockTracker;
  827. #endif
  828. }
  829. //+---------------------------------------------------------------------------
  830. //
  831. // Member: CPropertyStore::Empty
  832. //
  833. // Synopsis: Empties out the intitialized members and prepares for a
  834. // re-init.
  835. //
  836. // History: 3-18-96 srikants Created
  837. //
  838. //----------------------------------------------------------------------------
  839. void CPropertyStore::Empty()
  840. {
  841. _PropStoreInfo.Empty();
  842. delete _xPhysStore.Acquire();
  843. _pStorage = 0;
  844. }
  845. //+---------------------------------------------------------------------------
  846. //
  847. // Member: CPropertyStore::FastInit, public
  848. //
  849. // Synopsis: Initialize property store (two-phase construction)
  850. //
  851. // Arguments: [pStorage] -- Storage object.
  852. //
  853. // History: 27-Dec-95 KyleP Created.
  854. // 06-Mar-96 SrikantS Split into FastInit and LongInit
  855. // 23-Feb-98 KitmanH Code added to deal with read-only
  856. // catalogs
  857. //
  858. //----------------------------------------------------------------------------
  859. void CPropertyStore::FastInit( CiStorage * pStorage)
  860. {
  861. Win4Assert( 0 != _ulPSMappedCache );
  862. _pStorage = pStorage;
  863. XPtr<PRcovStorageObj> xObj( _pStorage->QueryPropStore( 0, _dwStoreLevel ) );
  864. _PropStoreInfo.Init( xObj, _dwStoreLevel );
  865. Win4Assert(GetStoreLevel() == _dwStoreLevel);
  866. WORKID wid = _PropStoreInfo.WorkId();
  867. if ( widInvalid != wid )
  868. {
  869. SStorageObject xobj( _pStorage->QueryObject( wid ) );
  870. PStorage::EOpenMode mode = _pStorage->IsReadOnly() ? PStorage::eOpenForRead : PStorage::eOpenForWrite;
  871. XPtr<PMmStream> xmmstrm ( _pStorage->QueryExistingPropStream( xobj.GetObj(),
  872. mode,
  873. GetStoreLevel() ));
  874. Win4Assert( !xmmstrm.IsNull() );
  875. if ( !xmmstrm->Ok() )
  876. {
  877. ciDebugOut(( DEB_ERROR, "Open of index %08x failed\n", wid ));
  878. NTSTATUS status = xmmstrm->GetStatus();
  879. if ( STATUS_DISK_FULL == status ||
  880. HRESULT_FROM_WIN32(ERROR_DISK_FULL) == status ||
  881. STATUS_INSUFFICIENT_RESOURCES == status ||
  882. CI_E_CONFIG_DISK_FULL == status )
  883. {
  884. CEventLog eventLog( NULL, wcsCiEventSource );
  885. CEventItem item( EVENTLOG_WARNING_TYPE,
  886. CI_SERVICE_CATEGORY,
  887. MSG_CI_LOW_DISK_SPACE,
  888. 2 );
  889. item.AddArg( _pStorage->GetVolumeName() );
  890. item.AddArg( lowDiskWaterMark );
  891. eventLog.ReportEvent( item );
  892. THROW( CException( CI_E_CONFIG_DISK_FULL ) );
  893. }
  894. else if ( xmmstrm->FStatusFileNotFound() )
  895. {
  896. //
  897. // We don't have code to handle such failures, hence mark
  898. // catalog as corrupt; otherwise throw e_fail
  899. //
  900. ciDebugOut(( DEB_ERROR, "Stream %08x not found\n", wid ));
  901. Win4Assert( !"Stream not found\n" );
  902. _pStorage->ReportCorruptComponent( L"PhysStorage2" );
  903. THROW( CException( CI_CORRUPT_DATABASE ));
  904. }
  905. __int64 sizeRemaining, sizeTotal;
  906. _pStorage->GetDiskSpace ( sizeTotal, sizeRemaining );
  907. if ( sizeRemaining < lowDiskWaterMark )
  908. {
  909. CEventLog eventLog( NULL, wcsCiEventSource );
  910. CEventItem item( EVENTLOG_WARNING_TYPE,
  911. CI_SERVICE_CATEGORY,
  912. MSG_CI_LOW_DISK_SPACE,
  913. 2 );
  914. item.AddArg( _pStorage->GetVolumeName() );
  915. item.AddArg( lowDiskWaterMark );
  916. eventLog.ReportEvent( item );
  917. THROW( CException( CI_E_CONFIG_DISK_FULL ) );
  918. }
  919. else
  920. THROW( CException( status ));
  921. }
  922. //
  923. // mmstrm ownership is transferred regardless of whether the
  924. // constructor succeeds.
  925. //
  926. _xPhysStore.Set( new CPhysPropertyStore( *_pStorage,
  927. xobj.GetObj(),
  928. wid,
  929. xmmstrm.Acquire(),
  930. mode,
  931. _ulPSMappedCache ) );
  932. // grow the file 2 meg at a time
  933. _xPhysStore->SetPageGrowth( 32 * COMMON_PAGE_SIZE / CI_PAGE_SIZE );
  934. }
  935. }
  936. //+---------------------------------------------------------------------------
  937. //
  938. // Member: CPropertyStore::LongInit
  939. //
  940. // Synopsis: If the propstore was dirty when shut down, run the recovery
  941. // operation.
  942. //
  943. // Arguments: [fWasDirty] -- dirty flag is returned here
  944. // [cInconsistencies] -- returns number of inconsistencies found
  945. // [pfnUpdateCallback]-- Callback to be called to update docs during
  946. // recovery. the prop store has no knowledge of
  947. // doc store, so this callback is needed.
  948. // [pUserData] -- will be echoed back through callback.
  949. //
  950. // Returns:
  951. //
  952. // History: 3-06-96 srikants Created
  953. //
  954. // Notes: The propstore is locked for write during recovery, but
  955. // reads are still permitted.
  956. //
  957. //----------------------------------------------------------------------------
  958. void CPropertyStore::LongInit( BOOL & fWasDirty, ULONG & cInconsistencies,
  959. T_UpdateDoc pfnUpdateCallback, void const *pUserData )
  960. {
  961. //
  962. // Close the existing prop store backup stream
  963. //
  964. _xPSBkpStrm.Free();
  965. //
  966. // Recover from dirty shutdown.
  967. //
  968. if ( _PropStoreInfo.IsDirty() )
  969. {
  970. ciDebugOut(( DEB_WARN, "Property store shut down dirty. Restoring...\n" ));
  971. fWasDirty = TRUE;
  972. CPropertyStoreRecovery recover(*this, pfnUpdateCallback, pUserData);
  973. recover.DoRecovery();
  974. cInconsistencies = recover.GetInconsistencyCount();
  975. }
  976. else
  977. {
  978. fWasDirty = FALSE;
  979. cInconsistencies = 0;
  980. InitFreeList();
  981. }
  982. WORKID wid = _PropStoreInfo.WorkId();
  983. Win4Assert( widInvalid != wid );
  984. SStorageObject xobj( _pStorage->QueryObject( wid ) );
  985. //
  986. // At this point we have no use for any existing property store backup file.
  987. // Create a new backup file so it will be initialized correctly based on the
  988. // volume's sector size and architecture's page size and user specified number
  989. // of pages to be backed up.
  990. //
  991. _xPSBkpStrm.Set(_pStorage->QueryNewPSBkpStream( xobj.GetObj(),
  992. _ulBackupSizeInPages, GetStoreLevel() ));
  993. }
  994. //+---------------------------------------------------------------------------
  995. //
  996. // Member: CPropertyStore::BeginTransaction, public
  997. //
  998. // Synopsis: Begins a schema transaction. Any existing transaction will be
  999. // aborted.
  1000. //
  1001. // Returns: Token representing transaction.
  1002. //
  1003. // History: 27-Dec-95 KyleP Created.
  1004. //
  1005. //----------------------------------------------------------------------------
  1006. ULONG_PTR CPropertyStore::BeginTransaction()
  1007. {
  1008. //
  1009. // Do we already have pending changes?
  1010. //
  1011. if ( 0 != _ppsNew )
  1012. EndTransaction( (ULONG_PTR)_ppsNew, FALSE, pidSecurity );
  1013. _fNew = FALSE;
  1014. _ppsNew = new CPropertyStore( *this, _pStorage );
  1015. return (ULONG_PTR)_ppsNew;
  1016. }
  1017. //+---------------------------------------------------------------------------
  1018. //
  1019. // Member: CPropertyStore::Setup, public
  1020. //
  1021. // Synopsis: Setup a property description. Property may already exist
  1022. // in the cache.
  1023. //
  1024. // Arguments: [pid] -- Propid
  1025. // [vt] -- Datatype of property. VT_VARIANT if unknown.
  1026. // [cbMaxLen] -- Soft-maximum length for variable length
  1027. // properties. This much space is pre-allocated
  1028. // in original record.
  1029. // [ulToken] -- Token of transaction
  1030. // [fCanBeModified] - Can the prop meta info be modified once set?
  1031. //
  1032. // Returns: TRUE if meta info has changed. FALSE otherwise.
  1033. //
  1034. // History: 27-Dec-95 KyleP Created.
  1035. //
  1036. //----------------------------------------------------------------------------
  1037. void CPropertyStore::Setup( PROPID pid,
  1038. ULONG vt,
  1039. DWORD cbMaxLen,
  1040. ULONG_PTR ulToken,
  1041. BOOL fCanBeModified )
  1042. {
  1043. if ( ulToken != (ULONG_PTR)_ppsNew )
  1044. {
  1045. ciDebugOut(( DEB_ERROR, "Transaction mismatch: 0x%x vs. 0x%x\n", ulToken, _ppsNew ));
  1046. THROW( CException( STATUS_TRANSACTION_NO_MATCH ) );
  1047. }
  1048. TRY
  1049. {
  1050. //
  1051. // Make the change. NOTE: "|| _fNew" must be after call, or Add/Delete may not be called.
  1052. //
  1053. if ( 0 == cbMaxLen )
  1054. _fNew = _ppsNew->_PropStoreInfo.Delete( pid, *_pStorage ) || _fNew;
  1055. else
  1056. _fNew = _ppsNew->_PropStoreInfo.Add( pid, vt, cbMaxLen, fCanBeModified, *_pStorage ) || _fNew;
  1057. }
  1058. CATCH( CException, e )
  1059. {
  1060. ciDebugOut(( DEB_ERROR, "Error 0x%X while setting up property 0x%X\n",
  1061. e.GetErrorCode(), pid ));
  1062. delete _ppsNew;
  1063. _ppsNew = 0;
  1064. _fNew = FALSE;
  1065. RETHROW();
  1066. }
  1067. END_CATCH
  1068. }
  1069. //+---------------------------------------------------------------------------
  1070. //
  1071. // Member: CPropertyStore::EndTransaction, public
  1072. //
  1073. // Synopsis: End property transaction, and maybe commit changes.
  1074. //
  1075. // Arguments: [ulToken] -- Token of transaction
  1076. // [fCommit] -- TRUE --> Commit transaction
  1077. // [pidFixed] -- Every workid with this pid will move to the
  1078. // same workid in the new property cache.
  1079. // Usually pidPath.
  1080. //
  1081. // History: 27-Dec-95 KyleP Created.
  1082. //
  1083. //----------------------------------------------------------------------------
  1084. void CPropertyStore::EndTransaction( ULONG_PTR ulToken, BOOL fCommit, PROPID pidFixed )
  1085. {
  1086. if ( ulToken != (ULONG_PTR)_ppsNew )
  1087. {
  1088. ciDebugOut(( DEB_ERROR,
  1089. "PropertyStore: Transaction mismatch: 0x%x vs. 0x%x\n",
  1090. ulToken, _ppsNew ));
  1091. THROW( CException( STATUS_TRANSACTION_NO_MATCH ) );
  1092. }
  1093. //
  1094. // Squirrel away previous store.
  1095. //
  1096. WORKID widOld = _PropStoreInfo.WorkId();
  1097. WORKID widNew = widInvalid;
  1098. TRY
  1099. {
  1100. if ( fCommit && _fNew )
  1101. {
  1102. ciDebugOut(( DEB_ITRACE, "Committing changes to property metadata.\n" ));
  1103. CRcovStrmWriteTrans xact( *_PropStoreInfo.GetRcovObj() );
  1104. _ppsNew->_PropStoreInfo.DetectFormat();
  1105. _ppsNew->CreateStorage( widInvalid ); // use next workid
  1106. _ppsNew->InitFreeList();
  1107. widNew = _ppsNew->_PropStoreInfo.WorkId();
  1108. // Transfer existing data to new.
  1109. BOOL fAbort = FALSE;
  1110. if ( widOld != widInvalid )
  1111. Transfer( *_ppsNew, pidFixed, fAbort );
  1112. //
  1113. // Prevent any readers from coming in until we commit the transaction.
  1114. // Lock down the source so no readers will be able to read and no writers
  1115. // will be able to write.
  1116. //
  1117. CWriteAccess writeLock( _rwAccess );
  1118. CLockAllRecordsForWrite lockAll(*this);
  1119. _PropStoreInfo.Commit( _ppsNew->_PropStoreInfo, xact );
  1120. //
  1121. // Transfer the property store information from the new to current
  1122. //
  1123. delete _xPhysStore.Acquire();
  1124. _xPhysStore.Set( _ppsNew->_xPhysStore.Acquire() );
  1125. delete _aFreeBlocks;
  1126. _aFreeBlocks = _ppsNew->_aFreeBlocks;
  1127. _ppsNew->_aFreeBlocks = 0;
  1128. _PropStoreInfo.MarkClean();
  1129. }
  1130. else
  1131. ciDebugOut(( DEB_ITRACE, "No changes to property metadata. Rolling back transaction.\n" ));
  1132. delete _ppsNew;
  1133. _ppsNew = 0;
  1134. _fNew = FALSE;
  1135. }
  1136. CATCH( CException, e )
  1137. {
  1138. ciDebugOut(( DEB_ERROR, "Error 0x%X while commiting transaction.\n", e.GetErrorCode() ));
  1139. delete _ppsNew;
  1140. _ppsNew = 0;
  1141. _fNew = FALSE;
  1142. //
  1143. // Delete the newly created property store from disk
  1144. //
  1145. if ( widInvalid != widNew )
  1146. _pStorage->DeleteObject( widNew );
  1147. RETHROW();
  1148. }
  1149. END_CATCH
  1150. if ( widOld != widInvalid )
  1151. _pStorage->DeleteObject( widOld );
  1152. }
  1153. //+---------------------------------------------------------------------------
  1154. //
  1155. // Member: CPropertyStore::MakeBackupCopy
  1156. //
  1157. // Synopsis: Makes a backup copy of the property storage. It makes a
  1158. // full copy if the pIEnumWorkids is NULL. Otherwise, it makes
  1159. // a copy of only the changed workids.
  1160. //
  1161. // Arguments: [pIProgressEnum] - Progress indication
  1162. // [pfAbort] - Caller initiated abort flag
  1163. // [dstStorage] - Destination storage to use
  1164. // [pIEnumWorkids] - List of workids to copy. If null, all the
  1165. // workids are copied.
  1166. // [pidFixed] - Which is the fixed pid ?
  1167. // [ppFileList] - List of propstore files copied.
  1168. //
  1169. // History: 3-26-97 srikants Created
  1170. //
  1171. // Notes: Incremental not implemented yet
  1172. //
  1173. //----------------------------------------------------------------------------
  1174. void CPropertyStore::MakeBackupCopy( IProgressNotify * pIProgressEnum,
  1175. BOOL & fAbort,
  1176. CiStorage & dstStorage,
  1177. ICiEnumWorkids * pIEnumWorkids,
  1178. IEnumString **ppFileList )
  1179. {
  1180. #if CIDBG == 1
  1181. if (pIEnumWorkids)
  1182. {
  1183. Win4Assert(!"For secondary level store, are you translating wids? Look in CPropStoreManager::MakeBackupCopy.");
  1184. }
  1185. #endif // CIDBG
  1186. //
  1187. // Create a backup copy of the property store.
  1188. //
  1189. //
  1190. // For a FULL backup, it is possible to just make a copy of the streams
  1191. // but if there are any problems with the data in this property store, they
  1192. // will get carried over. Also, doing a "Transfer" may defrag the target
  1193. // property store.
  1194. //
  1195. //
  1196. // Delete any existing PropertyStore meta data info.
  1197. //
  1198. dstStorage.RemovePropStore(0, GetStoreLevel());
  1199. TRY
  1200. {
  1201. //
  1202. // Make a backup copy of the PropStoreInfo.
  1203. //
  1204. XPtr<PRcovStorageObj> xObj( dstStorage.QueryPropStore( 0, GetStoreLevel() ) );
  1205. CCopyRcovObject copyRcov( xObj.GetReference(), *_PropStoreInfo.GetRcovObj() );
  1206. copyRcov.DoIt();
  1207. XPtr<CPropertyStore> xPropStore( new CPropertyStore( *this, &dstStorage ) );
  1208. xPropStore->_PropStoreInfo.InitWorkId( dstStorage );
  1209. xPropStore->CreateStorage( _PropStoreInfo.WorkId() ); // use same workid
  1210. xPropStore->InitFreeList();
  1211. xPropStore->_PropStoreInfo.Accept( xObj );
  1212. FastTransfer( xPropStore.GetReference(), fAbort, pIProgressEnum );
  1213. xPropStore->_PropStoreInfo.FastTransfer( _PropStoreInfo );
  1214. //
  1215. // return a list of file names only on demand
  1216. //
  1217. _propStoreMgr.Flush();
  1218. if (0 != ppFileList)
  1219. {
  1220. Win4Assert( 0 != _pStorage );
  1221. CEnumString * pEnumString = new CEnumString();
  1222. XInterface<IEnumString> xEnumStr(pEnumString);
  1223. dstStorage.ListPropStoreFileNames( *pEnumString,
  1224. _PropStoreInfo.WorkId(),
  1225. GetStoreLevel() );
  1226. *ppFileList = xEnumStr.Acquire();
  1227. }
  1228. }
  1229. CATCH( CException, e )
  1230. {
  1231. dstStorage.RemovePropStore(0, GetStoreLevel());
  1232. dstStorage.DeleteObject( _PropStoreInfo.WorkId() );
  1233. RETHROW();
  1234. }
  1235. END_CATCH
  1236. }
  1237. //+---------------------------------------------------------------------------
  1238. //
  1239. // Member: CPropertyStore::WriteProperty, public
  1240. //
  1241. // Synopsis: Write a property to the cache.
  1242. //
  1243. // Arguments: [PropRecord] -- Previously opened property record.
  1244. // [pid] -- Propid
  1245. // [var] -- Value
  1246. // [fBackup] -- Backup?
  1247. //
  1248. // Returns: S_OK if everything went well.
  1249. // S_FALSE if specified pid is not in store.
  1250. // Error code if an error occurred.
  1251. //
  1252. // History: 27-Dec-95 KyleP Created.
  1253. // 30-Dec-97 KrishnaN Improved error handling/reporting.
  1254. // 27-Jan-2000 KLam Extended assert to handle out of
  1255. // memory condition.
  1256. //
  1257. //----------------------------------------------------------------------------
  1258. SCODE CPropertyStore::WriteProperty( CPropRecordForWrites &PropRecord,
  1259. PROPID pid,
  1260. CStorageVariant const & var,
  1261. BOOL fBackup)
  1262. {
  1263. Win4Assert( sizeof(CPropRecordForWrites) <= sizeof_CPropRecord );
  1264. WORKID wid = PropRecord._wid;
  1265. ciDebugOut(( DEB_PROPSTORE, "WRITE: wid = 0x%x, pid = 0x%x, type = %d\n", wid, pid, var.Type() ));
  1266. CPropDesc const * pdesc = _PropStoreInfo.GetDescription( pid );
  1267. if ( 0 != pdesc )
  1268. {
  1269. COnDiskPropertyRecord * prec = PropRecord._prec;
  1270. // If CPropRecord was passed in widInvalid, prec would be 0. So Write should fail!
  1271. if (0 == prec)
  1272. return E_INVALIDARG;
  1273. if ( !prec->IsTopLevel() )
  1274. {
  1275. ciDebugOut(( DEB_IWARN, "Trying to write to non-toplevel wid 0x%X\n",
  1276. wid ));
  1277. return E_INVALIDARG;
  1278. }
  1279. SCODE sc = S_OK;
  1280. TRY
  1281. {
  1282. if (fBackup)
  1283. {
  1284. CBackupWid backupTopLevel(this, wid, prec->CountRecords());
  1285. _PropStoreInfo.MarkDirty();
  1286. }
  1287. _PropStoreInfo.MarkDirty();
  1288. if ( pdesc->IsFixedSize() )
  1289. {
  1290. #if CIDBG == 1
  1291. if ( ( pidSize == pid ) &&
  1292. ( VT_I8 == var.vt ) )
  1293. Win4Assert( 0xdddddddddddddddd != var.hVal.QuadPart );
  1294. if ( ( pidAttrib == pid ) &&
  1295. ( VT_UI4 == var.vt ) )
  1296. Win4Assert( 0xdddddddd != var.ulVal );
  1297. #endif // CIDBG == 1
  1298. prec->WriteFixed( pdesc->Ordinal(),
  1299. pdesc->Mask(),
  1300. pdesc->Offset(),
  1301. pdesc->Type(),
  1302. _PropStoreInfo.CountProps(),
  1303. var );
  1304. }
  1305. else
  1306. {
  1307. Win4Assert(!prec->IsLeanRecord());
  1308. Win4Assert( 0 != _pStorage );
  1309. BOOL fOk = prec->WriteVariable( pdesc->Ordinal(),
  1310. pdesc->Mask(),
  1311. _PropStoreInfo.FixedRecordSize(),
  1312. _PropStoreInfo.CountProps(),
  1313. _PropStoreInfo.CountFixedProps(),
  1314. _PropStoreInfo.RecordSize(),
  1315. var,
  1316. *_pStorage );
  1317. //
  1318. // Did we fit?
  1319. //
  1320. CBorrowed BorrowedOverflow( _xPhysStore.GetReference(),
  1321. _PropStoreInfo.RecordsPerPage(),
  1322. _PropStoreInfo.RecordSize() );
  1323. while ( !fOk )
  1324. {
  1325. //
  1326. // Check for existing overflow block.
  1327. //
  1328. WORKID widOverflow = prec->OverflowBlock();
  1329. BOOL fNewBlock = FALSE;
  1330. ULONG cWid = 1;
  1331. //
  1332. // Need new overflow block.
  1333. //
  1334. if ( 0 == widOverflow )
  1335. {
  1336. Win4Assert(!prec->IsLeanRecord());
  1337. fNewBlock = TRUE;
  1338. cWid = COnDiskPropertyRecord::CountNormalRecordsToStore(
  1339. _PropStoreInfo.CountProps() - _PropStoreInfo.CountFixedProps(),
  1340. _PropStoreInfo.RecordSize(),
  1341. var );
  1342. // We cannot have a single record greater than COMMON_PAGE_SIZE.
  1343. // Throw if we get into that situation. Fix for bug 119508.
  1344. if (cWid > RecordsPerPage())
  1345. THROW(CException(CI_E_PROPERTY_TOOLARGE));
  1346. widOverflow = LokNewWorkId( cWid, FALSE, fBackup );
  1347. prec->SetOverflowBlock( widOverflow );
  1348. PropRecord._prec->IncrementOverflowChainLength();
  1349. }
  1350. BorrowedOverflow.Release();
  1351. BorrowedOverflow.Set( widOverflow );
  1352. prec = BorrowedOverflow.Get();
  1353. if ( fNewBlock )
  1354. {
  1355. # if CIDBG == 1
  1356. if ( prec->HasProperties( _PropStoreInfo.CountProps() ) )
  1357. {
  1358. ciDebugOut(( DEB_ERROR, "New long record at %d, size = %d, p = 0x%x has stored properties!\n",
  1359. widOverflow, cWid, prec ));
  1360. }
  1361. if ( !prec->IsNormalEmpty( _PropStoreInfo.RecordSize() ) )
  1362. {
  1363. ciDebugOut(( DEB_ERROR, "New long record at %d, size = %d, p = 0x%x is not empty!\n",
  1364. widOverflow, cWid, prec ));
  1365. }
  1366. // Win4Assert( !prec->HasProperties(_PropStoreInfo.CountProps()) &&
  1367. // prec->IsEmpty( _PropStoreInfo.RecordSize() ) );
  1368. # endif // CIDBG == 1
  1369. ciDebugOut(( DEB_PROPSTORE, "New long record at %d, size = %d\n", widOverflow, cWid ));
  1370. prec->MakeLongRecord( cWid );
  1371. prec->SetOverflowBlock( 0 );
  1372. prec->SetToplevelBlock( wid );
  1373. }
  1374. else
  1375. {
  1376. //
  1377. // Every record in the chain gets backed up because of
  1378. // this. That is the way it should be because we are
  1379. // writing to every record in the chain as part of
  1380. // overflow handling. If there is not sufficient space
  1381. // in the overflow records, we will be creating a new
  1382. // record and chaining it to the last one in the
  1383. // existing chain. The backed up records will have no
  1384. // evidence of the link made to the new record and that
  1385. // is the way it should be.
  1386. //
  1387. if (fBackup)
  1388. {
  1389. CBackupWid backupOverflow(this, widOverflow, prec->CountRecords());
  1390. _PropStoreInfo.MarkDirty();
  1391. }
  1392. //
  1393. // NTRAID#DB-NTBUG9-84451-2000/07/31-dlee Indexing Service Property Store doesn't handle values in records that grow out of the record
  1394. // Consider the case where a property was *in* a long record.
  1395. // and then no longer fit in that long record...
  1396. //
  1397. Win4Assert( prec->ToplevelBlock() == wid );
  1398. }
  1399. ULONG Ordinal = pdesc->Ordinal() - _PropStoreInfo.CountFixedProps();
  1400. DWORD Mask = (1 << ((Ordinal % 16) * 2));
  1401. Win4Assert( 0 != _pStorage );
  1402. fOk = prec->WriteVariable( Ordinal, // Ordinal (assuming 0 fixed)
  1403. Mask, // Mask (assuming 0 fixed)
  1404. 0, // Fixed properties
  1405. _PropStoreInfo.CountProps() - _PropStoreInfo.CountFixedProps(),
  1406. 0, // Count of fixed properties
  1407. _PropStoreInfo.RecordSize(),
  1408. var,
  1409. *_pStorage );
  1410. Win4Assert( fOk || !fNewBlock ); // Property *must* fit in a fresh block!
  1411. }
  1412. }
  1413. #if CIDBG == 1
  1414. // Assert that we have a dirty property store and that something
  1415. // was written to the backup file (if fBackup is enabled)
  1416. Win4Assert(_PropStoreInfo.IsDirty());
  1417. if (fBackup)
  1418. {
  1419. Win4Assert(BackupStream()->Pages() > 0);
  1420. }
  1421. #endif // CIDBG
  1422. }
  1423. CATCH( CException, e )
  1424. {
  1425. sc = e.GetErrorCode();
  1426. ciDebugOut(( DEB_ERROR, "Exception 0x%x caught writing pid %d in wid %d. prec = 0x%x\n",
  1427. sc, pid, wid, prec ));
  1428. }
  1429. END_CATCH
  1430. return sc;
  1431. }
  1432. return S_FALSE;
  1433. } //WriteProperty
  1434. //+---------------------------------------------------------------------------
  1435. //
  1436. // Member: CPropertyStore::WriteProperty, public
  1437. //
  1438. // Synopsis: Write a property to the cache.
  1439. //
  1440. // Arguments: [wid] -- Workid
  1441. // [pid] -- Propid
  1442. // [var] -- Value
  1443. // [fBackup] -- Backup?
  1444. //
  1445. // Returns: S_OK if everything went well.
  1446. // S_FALSE if specified pid is not in store.
  1447. // Error code if an error occurred.
  1448. //
  1449. // History: 27-Dec-95 KyleP Created.
  1450. // 30-Dec-97 KrishnaN Improved error handling/reporting.
  1451. //
  1452. //----------------------------------------------------------------------------
  1453. SCODE CPropertyStore::WriteProperty( WORKID wid,
  1454. PROPID pid,
  1455. CStorageVariant const & var,
  1456. BOOL fBackup )
  1457. {
  1458. CPropRecordForWrites PropRecord( wid, *this );
  1459. return WriteProperty( PropRecord, pid, var, fBackup );
  1460. }
  1461. //+---------------------------------------------------------------------------
  1462. //
  1463. // Member: CPropertyStore::ReadProperty, public
  1464. //
  1465. // Synopsis: Read a property from the cache. Version which uses property
  1466. // record.
  1467. //
  1468. // Arguments: [PropRec] -- Pre-opened property record
  1469. // [pid] -- Propid
  1470. // [pbData] -- Place to return the value
  1471. // [pcb] -- On input, the maximum number of bytes to
  1472. // write at pbData. On output, the number of
  1473. // bytes written if the call was successful,
  1474. // else the number of bytes required.
  1475. //
  1476. // History: 03-Apr-96 KyleP Created.
  1477. //
  1478. //----------------------------------------------------------------------------
  1479. BOOL CPropertyStore::ReadProperty( CPropRecordNoLock & PropRec, PROPID pid, PROPVARIANT * pbData, unsigned * pcb )
  1480. {
  1481. *pcb -= sizeof(PROPVARIANT);
  1482. BOOL fOk = ReadProperty( PropRec, pid, *pbData, (BYTE *)(pbData + 1), pcb );
  1483. *pcb += sizeof(PROPVARIANT);
  1484. return fOk;
  1485. }
  1486. //+---------------------------------------------------------------------------
  1487. //
  1488. // Member: CPropertyStore::ReadProperty, public
  1489. //
  1490. // Synopsis: Read a property from the cache. Triggers CoTaskMemAlloc
  1491. //
  1492. // Arguments: [wid] -- Workid
  1493. // [pid] -- Propid
  1494. // [var] -- Place to return the value
  1495. //
  1496. // History: 27-Dec-95 KyleP Created.
  1497. //
  1498. //----------------------------------------------------------------------------
  1499. BOOL CPropertyStore::ReadProperty( WORKID wid, PROPID pid, PROPVARIANT & var )
  1500. {
  1501. unsigned cb = 0xFFFFFFFF;
  1502. return ReadProperty( wid, pid, var, 0, &cb );
  1503. }
  1504. //+---------------------------------------------------------------------------
  1505. //
  1506. // Member: CPropertyStore::ReadProperty, public
  1507. //
  1508. // Synopsis: Read a property from the cache. Separate variable buffer.
  1509. //
  1510. // Arguments: [wid] -- Workid
  1511. // [pid] -- Propid
  1512. // [var] -- Variant written here
  1513. // [pbExtra] -- Place to store additional pointer(s).
  1514. // [pcbExtra] -- On input, the maximum number of bytes to
  1515. // write at pbExtra. On output, the number of
  1516. // bytes written if the call was successful,
  1517. // else the number of bytes required.
  1518. //
  1519. // History: 27-Dec-95 KyleP Created.
  1520. //
  1521. //----------------------------------------------------------------------------
  1522. BOOL CPropertyStore::ReadProperty( WORKID wid,
  1523. PROPID pid,
  1524. PROPVARIANT & var,
  1525. BYTE * pbExtra,
  1526. unsigned * pcbExtra )
  1527. {
  1528. CPropRecord PropRecord( wid, *this );
  1529. return ReadProperty( PropRecord, pid, var, pbExtra, pcbExtra );
  1530. }
  1531. //+---------------------------------------------------------------------------
  1532. //
  1533. // Member: CPropertyStore::ReadProperty, public
  1534. //
  1535. // Synopsis: Read a property from the cache. Separate variable buffer.
  1536. // Uses pre-opened property record.
  1537. //
  1538. // Arguments: [PropRec] -- Pre-opened property record.
  1539. // [pid] -- Propid
  1540. // [var] -- Variant written here
  1541. // [pbExtra] -- Place to store additional pointer(s).
  1542. // [pcbExtra] -- On input, the maximum number of bytes to
  1543. // write at pbExtra. On output, the number of
  1544. // bytes written if the call was successful,
  1545. // else the number of bytes required.
  1546. //
  1547. // History: 03-Apr-96 KyleP Created.
  1548. //
  1549. //----------------------------------------------------------------------------
  1550. BOOL CPropertyStore::ReadProperty( CPropRecordNoLock & PropRecord,
  1551. PROPID pid,
  1552. PROPVARIANT & var,
  1553. BYTE * pbExtra,
  1554. unsigned * pcbExtra )
  1555. {
  1556. ciDebugOut(( DEB_PROPSTORE, "READ: PropRec = 0x%x, pid = 0x%x\n", &PropRecord, pid ));
  1557. if (!PropRecord.IsValid())
  1558. return FALSE;
  1559. COnDiskPropertyRecord * prec = PropRecord._prec;
  1560. return ReadProperty(prec, pid, var, pbExtra, pcbExtra);
  1561. }
  1562. //+---------------------------------------------------------------------------
  1563. //
  1564. // Member: CPropertyStore::ReadProperty, public
  1565. //
  1566. // Synopsis: Read a property from the cache. Separate variable buffer.
  1567. // Uses pre-opened property record.
  1568. //
  1569. // Arguments: [prec] -- Ptr to preopened property record.
  1570. // [pid] -- Propid
  1571. // [var] -- Variant written here
  1572. // [pbExtra] -- Place to store additional pointer(s).
  1573. // [pcbExtra] -- On input, the maximum number of bytes to
  1574. // write at pbExtra. On output, the number of
  1575. // bytes written if the call was successful,
  1576. // else the number of bytes required.
  1577. //
  1578. // History: 17-Mar-1998 KrishnaN Created.
  1579. // 15-Mar-2000 KLam Add STATUS_INSUFFICIENT_RESOURCES to assert
  1580. //
  1581. //----------------------------------------------------------------------------
  1582. BOOL CPropertyStore::ReadProperty( COnDiskPropertyRecord *prec,
  1583. PROPID pid,
  1584. PROPVARIANT & var,
  1585. BYTE * pbExtra,
  1586. unsigned * pcbExtra )
  1587. {
  1588. CPropDesc const * pdesc = _PropStoreInfo.GetDescription( pid );
  1589. //
  1590. // Is the property cached?
  1591. //
  1592. if ( 0 == pdesc )
  1593. return FALSE;
  1594. // If CPropRecord was passed in widInvalid, prec would be 0. So Read should fail!
  1595. if (0 == prec)
  1596. return FALSE;
  1597. if ( !prec->IsInUse() )
  1598. {
  1599. ciDebugOut(( DEB_IWARN,
  1600. "Trying to read from a deleted wid in prec = 0x%X\n",
  1601. prec ));
  1602. return FALSE;
  1603. }
  1604. if ( !prec->IsTopLevel() )
  1605. {
  1606. ciDebugOut(( DEB_IWARN,
  1607. "Trying to start read from a non-toplevel prec = 0x%X\n",
  1608. prec ));
  1609. return FALSE;
  1610. }
  1611. TRY
  1612. {
  1613. if ( pdesc->IsFixedSize() )
  1614. {
  1615. Win4Assert( 0 != _pStorage );
  1616. prec->ReadFixed( pdesc->Ordinal(),
  1617. pdesc->Mask(),
  1618. pdesc->Offset(),
  1619. _PropStoreInfo.CountProps(),
  1620. pdesc->Type(),
  1621. var,
  1622. pbExtra,
  1623. pcbExtra,
  1624. *_pStorage );
  1625. }
  1626. else
  1627. {
  1628. BOOL fOk = prec->ReadVariable( pdesc->Ordinal(),
  1629. pdesc->Mask(),
  1630. _PropStoreInfo.FixedRecordSize(),
  1631. _PropStoreInfo.CountProps(),
  1632. _PropStoreInfo.CountFixedProps(),
  1633. var,
  1634. pbExtra,
  1635. pcbExtra );
  1636. if (! fOk )
  1637. {
  1638. CBorrowed BorrowedOverflow( _xPhysStore.GetReference(),
  1639. _PropStoreInfo.RecordsPerPage(),
  1640. _PropStoreInfo.RecordSize() );
  1641. do
  1642. {
  1643. //
  1644. // Check for existing overflow block.
  1645. //
  1646. WORKID widOverflow = prec->OverflowBlock();
  1647. //
  1648. // Need new overflow block.
  1649. //
  1650. if ( 0 == widOverflow )
  1651. return FALSE;
  1652. Win4Assert( _xPhysStore->PageSize() * CI_PAGE_SIZE >=
  1653. COnDiskPropertyRecord::MinStreamSize( widOverflow, _PropStoreInfo.RecordSize() ) );
  1654. BorrowedOverflow.Release();
  1655. BorrowedOverflow.Set( widOverflow, FALSE );
  1656. prec = BorrowedOverflow.Get();
  1657. ULONG Ordinal = pdesc->Ordinal() - _PropStoreInfo.CountFixedProps();
  1658. DWORD Mask = (1 << ((Ordinal % 16) * 2) );
  1659. fOk = prec->ReadVariable( Ordinal, // Ordinal (assuming 0 fixed)
  1660. Mask, // Mask (assuming 0 fixed)
  1661. 0, // Fixed properties
  1662. _PropStoreInfo.CountProps() - _PropStoreInfo.CountFixedProps(),
  1663. 0, // Count of fixed properties
  1664. var,
  1665. pbExtra,
  1666. pcbExtra );
  1667. } while ( !fOk );
  1668. }
  1669. }
  1670. }
  1671. CATCH( CException, e )
  1672. {
  1673. ciDebugOut(( DEB_ERROR, "Exception 0x%x caught reading pid %d in prec = 0x%x\n",
  1674. e.GetErrorCode(), pid, prec ));
  1675. // assert if the error is other than out of memory
  1676. Win4Assert( ( e.GetErrorCode() == E_OUTOFMEMORY ||
  1677. e.GetErrorCode() == HRESULT_FROM_WIN32(STATUS_SHARING_VIOLATION) ||
  1678. e.GetErrorCode() == HRESULT_FROM_WIN32(STATUS_INSUFFICIENT_RESOURCES) )
  1679. && "Exception reading property" );
  1680. RETHROW();
  1681. }
  1682. END_CATCH
  1683. return TRUE;
  1684. }
  1685. //+---------------------------------------------------------------------------
  1686. //
  1687. // Member: CPropertyStore::DeleteRecord, public
  1688. //
  1689. // Synopsis: Free a record and any records chained off it.
  1690. //
  1691. // Arguments: [wid] -- Workid
  1692. //
  1693. // History: 27-Dec-95 KyleP Created.
  1694. //
  1695. //----------------------------------------------------------------------------
  1696. void CPropertyStore::DeleteRecord( WORKID wid, BOOL fBackup )
  1697. {
  1698. Win4Assert(wid != widInvalid && wid != 0);
  1699. ciDebugOut(( DEB_PROPSTORE, "DELETE: wid = 0x%x\n", wid ));
  1700. ULONG cbStream = COnDiskPropertyRecord::MinStreamSize( wid, _PropStoreInfo.RecordSize() );
  1701. if ( _xPhysStore->PageSize() * CI_PAGE_SIZE < cbStream )
  1702. return;
  1703. CBorrowed BorrowedTopLevel( _xPhysStore.GetReference(),
  1704. wid,
  1705. _PropStoreInfo.RecordsPerPage(),
  1706. _PropStoreInfo.RecordSize() );
  1707. CLockRecordForWrite wlock( *this, wid );
  1708. WORKID widStart = wid;
  1709. COnDiskPropertyRecord * pRecTopLevel = BorrowedTopLevel.Get();
  1710. // Return if we have nothing to delete.
  1711. if (0 == pRecTopLevel)
  1712. return;
  1713. BOOL fIsConsistent = TRUE;
  1714. if ( !pRecTopLevel->IsTopLevel() )
  1715. {
  1716. ciDebugOut(( DEB_ERROR, "Delete wid (0x%X) prec (0x%X) is not top level\n",
  1717. wid, pRecTopLevel ));
  1718. Win4Assert( !"Corruption detected in PropertyStore" );
  1719. fIsConsistent = FALSE;
  1720. }
  1721. ULONG cRemainingBlocks = 1; // won't change for a lean record
  1722. if (eNormal == GetRecordFormat())
  1723. cRemainingBlocks = pRecTopLevel->GetOverflowChainLength()+1;
  1724. while ( wid != 0 && wid <= _PropStoreInfo.MaxWorkId() )
  1725. {
  1726. if ( 0 == cRemainingBlocks )
  1727. {
  1728. //
  1729. // We are either in some kind of corruption or loop. In either,
  1730. // case, we have freed up as many as are probably safe. Just
  1731. // get out of here.
  1732. //
  1733. ciDebugOut(( DEB_ERROR,
  1734. "Delete wid (0x%X) overflow chain is corrupt\n",
  1735. wid ));
  1736. Win4Assert( !"Corruption detected in PropertyStore" );
  1737. fIsConsistent = FALSE;
  1738. break;
  1739. }
  1740. CBorrowed Borrowed( _xPhysStore.GetReference(),
  1741. wid,
  1742. _PropStoreInfo.RecordsPerPage(),
  1743. _PropStoreInfo.RecordSize() );
  1744. COnDiskPropertyRecord * prec = Borrowed.Get();
  1745. WORKID widNext = 0; // causes loop termination for lean records
  1746. if (eNormal == GetRecordFormat())
  1747. {
  1748. widNext = prec->OverflowBlock();
  1749. if ( (wid != widStart) && !prec->IsOverflow() )
  1750. {
  1751. ciDebugOut(( DEB_ERROR,
  1752. "Wid (0x%x) - prec (0x%x). Not in use record to be deleted\n",
  1753. wid, prec ));
  1754. Win4Assert( !"Corruption detected in PropertyStore" );
  1755. fIsConsistent = FALSE;
  1756. break;
  1757. }
  1758. }
  1759. LokFreeRecord( wid, prec->CountRecords(), prec, fBackup );
  1760. wid = widNext;
  1761. cRemainingBlocks--;
  1762. }
  1763. _PropStoreInfo.DecRecordsInUse();
  1764. if ( !fIsConsistent )
  1765. {
  1766. _fIsConsistent = FALSE;
  1767. THROW( CException( CI_PROPSTORE_INCONSISTENCY ) );
  1768. }
  1769. #if CIDBG == 1
  1770. // Assert that we have a dirty property store and that something was written to
  1771. // the backup file (if fBackup is enabled)
  1772. Win4Assert(_PropStoreInfo.IsDirty());
  1773. if (fBackup)
  1774. {
  1775. Win4Assert(BackupStream()->Pages() > 0);
  1776. }
  1777. #endif // CIDBG
  1778. }
  1779. //+---------------------------------------------------------------------------
  1780. //
  1781. // Member: CPropertyStore::InitFreeList
  1782. //
  1783. // Synopsis: Initialize the free list block pointer array.
  1784. //
  1785. // Returns: Nothing
  1786. //
  1787. // History: 07 May 96 AlanW Created
  1788. //
  1789. // Notes: The free list block pointer array speeds allocation and
  1790. // deallocation of records by storing the starting record number
  1791. // of free records of a particular size. The free list is
  1792. // sorted by the size of the record.
  1793. //
  1794. //----------------------------------------------------------------------------
  1795. void CPropertyStore::InitFreeList( )
  1796. {
  1797. if ( 0 != _aFreeBlocks )
  1798. {
  1799. delete _aFreeBlocks;
  1800. _aFreeBlocks = 0;
  1801. }
  1802. _aFreeBlocks = new WORKID[ _PropStoreInfo.RecordsPerPage() + 1 ];
  1803. RtlZeroMemory( _aFreeBlocks,
  1804. (_PropStoreInfo.RecordsPerPage()+1) * sizeof (WORKID) );
  1805. WORKID wid = _PropStoreInfo.FreeListHead();
  1806. ciDebugOut(( DEB_PROPSTORE, "Scanning free list starting with wid %u\n", wid ));
  1807. if (0 != wid)
  1808. {
  1809. CBorrowed Borrowed( _xPhysStore.GetReference(),
  1810. wid,
  1811. _PropStoreInfo.RecordsPerPage(),
  1812. _PropStoreInfo.RecordSize() );
  1813. COnDiskPropertyRecord * prec = Borrowed.Get();
  1814. if ( prec->GetNextFreeRecord() != 0)
  1815. {
  1816. CBorrowed BorrowedNext( _xPhysStore.GetReference(),
  1817. prec->GetNextFreeRecord(),
  1818. _PropStoreInfo.RecordsPerPage(),
  1819. _PropStoreInfo.RecordSize() );
  1820. COnDiskPropertyRecord * precNext = BorrowedNext.Get();
  1821. if ( precNext->GetPreviousFreeRecord() != wid ||
  1822. precNext->CountRecords() != prec->GetNextFreeSize() )
  1823. {
  1824. // Old-style free list
  1825. Win4Assert( FALSE );
  1826. return;
  1827. }
  1828. }
  1829. if ( prec->GetPreviousFreeRecord() != 0 )
  1830. {
  1831. //
  1832. // Minor inconsistency in the first free record. Fix it.
  1833. //
  1834. ciDebugOut(( DEB_WARN, "PROPSTORE: Repair free list previous pointer(0x%X)\n", wid ));
  1835. prec->SetPreviousFreeRecord( 0 );
  1836. }
  1837. if ( prec->GetNextFreeRecord() == 0 &&
  1838. _PropStoreInfo.FreeListTail( ) != wid )
  1839. {
  1840. //
  1841. // Minor inconsistency in a single free record. Fix it.
  1842. //
  1843. ciDebugOut(( DEB_WARN, "PROPSTORE: Repair free list tail pointer(0x%X)\n", wid ));
  1844. prec->SetNextFree( 0, 0 );
  1845. _PropStoreInfo.SetFreeListTail( wid );
  1846. }
  1847. }
  1848. else if ( _PropStoreInfo.FreeListTail() != 0 )
  1849. {
  1850. //
  1851. // Free list tail not set correctly for an empty list. Fix it.
  1852. //
  1853. _PropStoreInfo.SetFreeListTail( 0 );
  1854. }
  1855. CBorrowed Borrowed( _xPhysStore.GetReference(),
  1856. _PropStoreInfo.RecordsPerPage(),
  1857. _PropStoreInfo.RecordSize() );
  1858. #if CIDBG == 1
  1859. WORKID widPrev = 0;
  1860. ULONG cRecPrev = 0;
  1861. #endif // CIDBG == 1
  1862. while ( 0 != wid )
  1863. {
  1864. Borrowed.Set(wid);
  1865. COnDiskPropertyRecord * prec = Borrowed.Get();
  1866. Win4Assert( prec->IsFreeRecord() &&
  1867. prec->GetPreviousFreeRecord() == widPrev );
  1868. Win4Assert( cRecPrev == 0 ||
  1869. prec->CountRecords() == cRecPrev );
  1870. if (_aFreeBlocks[prec->CountRecords()] == 0)
  1871. {
  1872. _aFreeBlocks[prec->CountRecords()] = wid;
  1873. ciDebugOut(( DEB_PROPSTORE,
  1874. " _aFreeBlocks[%03d] = 0x%X\n",
  1875. prec->CountRecords(), wid ));
  1876. }
  1877. if (prec->CountRecords() == 1)
  1878. break;
  1879. #if CIDBG == 1
  1880. widPrev = wid;
  1881. cRecPrev = prec->GetNextFreeSize();
  1882. #endif // CIDBG == 1
  1883. wid = prec->GetNextFreeRecord();
  1884. Borrowed.Release();
  1885. }
  1886. }
  1887. //+---------------------------------------------------------------------------
  1888. //
  1889. // Member: CPropertyStore::LokFreeRecord, private
  1890. //
  1891. // Synopsis: Add a free record to the free list.
  1892. //
  1893. // Arguments: [widFree] -- Workid of record to free
  1894. // [cFree] -- Number of records in freed chunk
  1895. // [precFree] -- On-disk Property record
  1896. // [fBackup] -- Backup record?
  1897. //
  1898. // Notes: The free list is maintained in decreasing order of free
  1899. // block size. The _aFreeBlocks array is updated.
  1900. //
  1901. // History: 01 May 96 AlanW Created.
  1902. //
  1903. //----------------------------------------------------------------------------
  1904. void CPropertyStore::LokFreeRecord( WORKID widFree,
  1905. ULONG cFree,
  1906. COnDiskPropertyRecord * precFree,
  1907. BOOL fBackup )
  1908. {
  1909. CImpersonateSystem impersonate;
  1910. if (fBackup)
  1911. {
  1912. CBackupWid backup(this, widFree, cFree);
  1913. _PropStoreInfo.MarkDirty();
  1914. }
  1915. WORKID widListHead = _PropStoreInfo.FreeListHead();
  1916. ciDebugOut(( DEB_PROPSTORE,
  1917. " free wid 0x%X, size = %d\n", widFree, cFree ));
  1918. _PropStoreInfo.MarkDirty();
  1919. for (unsigned i = cFree; i <= _PropStoreInfo.RecordsPerPage(); i++)
  1920. if (_aFreeBlocks[i] != 0)
  1921. break;
  1922. if ( 0 == widListHead ||
  1923. i > _PropStoreInfo.RecordsPerPage() ||
  1924. (i == cFree && widListHead == _aFreeBlocks[i]) )
  1925. {
  1926. //
  1927. // The block will go at the head of the list
  1928. //
  1929. WORKID widNext = widListHead;
  1930. ULONG cFreeNext = 0;
  1931. if ( 0 != widNext )
  1932. {
  1933. CBorrowed BorrowedNext( _xPhysStore.GetReference(),
  1934. widNext,
  1935. _PropStoreInfo.RecordsPerPage(),
  1936. _PropStoreInfo.RecordSize() );
  1937. COnDiskPropertyRecord * precNext = BorrowedNext.Get();
  1938. if (fBackup)
  1939. {
  1940. CBackupWid backup(this, widNext, 1);
  1941. _PropStoreInfo.MarkDirty();
  1942. }
  1943. precNext->SetPreviousFreeRecord( widFree );
  1944. cFreeNext = precNext->CountRecords();
  1945. }
  1946. else
  1947. {
  1948. _PropStoreInfo.SetFreeListTail( widFree );
  1949. }
  1950. if (precFree->IsLeanRecord())
  1951. precFree->MakeLeanFreeRecord( cFree,
  1952. widNext,
  1953. cFreeNext,
  1954. _PropStoreInfo.RecordSize() );
  1955. else
  1956. precFree->MakeNormalFreeRecord( cFree,
  1957. widNext,
  1958. cFreeNext,
  1959. _PropStoreInfo.RecordSize() );
  1960. precFree->SetPreviousFreeRecord( 0 );
  1961. Win4Assert( _aFreeBlocks[ cFree ] == 0 || i == cFree );
  1962. _aFreeBlocks[cFree] = widFree;
  1963. _PropStoreInfo.SetFreeListHead( widFree );
  1964. return;
  1965. }
  1966. if ( i != cFree )
  1967. {
  1968. //
  1969. // A block of this size doesn't exist; find the next smaller
  1970. // size and insert before it.
  1971. //
  1972. for ( i = cFree; i > 0; i-- )
  1973. if (_aFreeBlocks[i] != 0)
  1974. break;
  1975. }
  1976. if ( i > 0 )
  1977. {
  1978. //
  1979. // Insert the block into the list
  1980. //
  1981. WORKID widNext = _aFreeBlocks[i];
  1982. ULONG cFreeNext = i;
  1983. CBorrowed BorrowedNext( _xPhysStore.GetReference(),
  1984. widNext,
  1985. _PropStoreInfo.RecordsPerPage(),
  1986. _PropStoreInfo.RecordSize() );
  1987. COnDiskPropertyRecord * precNext = BorrowedNext.Get();
  1988. WORKID widPrev = precNext->GetPreviousFreeRecord();
  1989. precNext->SetPreviousFreeRecord( widFree );
  1990. Win4Assert( cFreeNext == precNext->CountRecords() );
  1991. if (precFree->IsLeanRecord())
  1992. precFree->MakeLeanFreeRecord( cFree,
  1993. widNext,
  1994. cFreeNext,
  1995. _PropStoreInfo.RecordSize() );
  1996. else
  1997. precFree->MakeNormalFreeRecord( cFree,
  1998. widNext,
  1999. cFreeNext,
  2000. _PropStoreInfo.RecordSize() );
  2001. precFree->SetPreviousFreeRecord( widPrev );
  2002. // Insertion at list head is handled above...
  2003. Win4Assert( widPrev != 0 );
  2004. if (widPrev != 0)
  2005. {
  2006. CBorrowed BorrowedPrev( _xPhysStore.GetReference(),
  2007. widPrev,
  2008. _PropStoreInfo.RecordsPerPage(),
  2009. _PropStoreInfo.RecordSize() );
  2010. COnDiskPropertyRecord * precPrev = BorrowedPrev.Get();
  2011. precPrev->SetNextFree( widFree, cFree );
  2012. }
  2013. _aFreeBlocks[cFree] = widFree;
  2014. return;
  2015. }
  2016. //
  2017. // No blocks of this size or smaller found. Append to list
  2018. //
  2019. WORKID widPrev = _PropStoreInfo.FreeListTail();
  2020. Win4Assert( widPrev != 0 );
  2021. CBorrowed BorrowedPrev( _xPhysStore.GetReference(),
  2022. widPrev,
  2023. _PropStoreInfo.RecordsPerPage(),
  2024. _PropStoreInfo.RecordSize() );
  2025. COnDiskPropertyRecord * precPrev = BorrowedPrev.Get();
  2026. precPrev->SetNextFree( widFree, cFree );
  2027. if (precFree->IsLeanRecord())
  2028. precFree->MakeLeanFreeRecord( cFree,
  2029. 0,
  2030. 0,
  2031. _PropStoreInfo.RecordSize() );
  2032. else
  2033. precFree->MakeNormalFreeRecord( cFree,
  2034. 0,
  2035. 0,
  2036. _PropStoreInfo.RecordSize() );
  2037. precFree->SetPreviousFreeRecord( widPrev );
  2038. _PropStoreInfo.SetFreeListTail( widFree );
  2039. Win4Assert( _aFreeBlocks[cFree] == 0 );
  2040. _aFreeBlocks[cFree] = widFree;
  2041. return;
  2042. }
  2043. //+---------------------------------------------------------------------------
  2044. //
  2045. // Member: CPropertyStore::LokAllocRecord, private
  2046. //
  2047. // Synopsis: Allocate a record from the free list
  2048. //
  2049. // Arguments: [cFree] -- Number of contiguous records required
  2050. //
  2051. // Returns: WORKID - the work ID of the record allocated. 0 if none of
  2052. // sufficient size could be found.
  2053. //
  2054. // History: 09 May 96 AlanW Created.
  2055. //
  2056. //----------------------------------------------------------------------------
  2057. WORKID CPropertyStore::LokAllocRecord( ULONG cFree )
  2058. {
  2059. CImpersonateSystem impersonate;
  2060. _PropStoreInfo.MarkDirty();
  2061. for (unsigned i = cFree; i <= _PropStoreInfo.RecordsPerPage(); i++)
  2062. if (_aFreeBlocks[i] != 0)
  2063. break;
  2064. if ( i > _PropStoreInfo.RecordsPerPage() )
  2065. return 0;
  2066. WORKID widFree = _aFreeBlocks[i];
  2067. CBorrowed Borrowed( _xPhysStore.GetReference(),
  2068. widFree,
  2069. _PropStoreInfo.RecordsPerPage(),
  2070. _PropStoreInfo.RecordSize() );
  2071. COnDiskPropertyRecord * prec = Borrowed.Get();
  2072. WORKID widNext = prec->GetNextFreeRecord();
  2073. WORKID widPrev = prec->GetPreviousFreeRecord();
  2074. if ( prec->CountRecords() != prec->GetNextFreeSize() )
  2075. _aFreeBlocks[i] = 0;
  2076. else
  2077. _aFreeBlocks[i] = prec->GetNextFreeRecord();
  2078. if ( widNext != 0 )
  2079. {
  2080. CBorrowed BorrowedNext( _xPhysStore.GetReference(),
  2081. widNext,
  2082. _PropStoreInfo.RecordsPerPage(),
  2083. _PropStoreInfo.RecordSize() );
  2084. COnDiskPropertyRecord * precNext = BorrowedNext.Get();
  2085. Win4Assert( precNext->IsFreeRecord() );
  2086. Win4Assert( prec->GetNextFreeSize() == precNext->CountRecords() );
  2087. precNext->SetPreviousFreeRecord( widPrev );
  2088. }
  2089. else
  2090. {
  2091. Win4Assert( _PropStoreInfo.FreeListTail() == widFree );
  2092. _PropStoreInfo.SetFreeListTail( widPrev );
  2093. }
  2094. if ( widPrev != 0 )
  2095. {
  2096. CBorrowed BorrowedPrev( _xPhysStore.GetReference(),
  2097. widPrev,
  2098. _PropStoreInfo.RecordsPerPage(),
  2099. _PropStoreInfo.RecordSize() );
  2100. COnDiskPropertyRecord * precPrev = BorrowedPrev.Get();
  2101. Win4Assert( precPrev->IsFreeRecord() );
  2102. precPrev->SetNextFree( widNext, prec->GetNextFreeSize() );
  2103. }
  2104. else
  2105. {
  2106. Win4Assert( _PropStoreInfo.FreeListHead() == widFree );
  2107. _PropStoreInfo.SetFreeListHead( widNext );
  2108. }
  2109. ciDebugOut(( DEB_PROPSTORE,
  2110. " alloc wid 0x%X, size = %d\n", widFree, cFree ));
  2111. return widFree;
  2112. }
  2113. //+---------------------------------------------------------------------------
  2114. //
  2115. // Member: CPropertyStore::CPropertyStore, private
  2116. //
  2117. // Synopsis: Copy constructor
  2118. //
  2119. // Arguments: [rhs] -- Source metadata
  2120. //
  2121. // History: 03-Jan-96 KyleP Created.
  2122. // 26-Mar-96 SrikantS Modifed for better recovery.
  2123. //
  2124. //----------------------------------------------------------------------------
  2125. CPropertyStore::CPropertyStore( CPropertyStore & rhs, CiStorage * pStorage )
  2126. : _pStorage(0),
  2127. _PropStoreInfo( rhs._PropStoreInfo ),
  2128. _aFreeBlocks(0),
  2129. _fAbort(FALSE),
  2130. _ppsNew( 0 ),
  2131. _fNew( FALSE ),
  2132. _ulBackupSizeInPages( rhs._ulBackupSizeInPages ),
  2133. _ulPSMappedCache( rhs._ulPSMappedCache ),
  2134. _propStoreMgr( rhs._propStoreMgr )
  2135. {
  2136. #if CIDBG == 1
  2137. _sigPSDebug = 0x2047554245445350i64; // PSDEBUG
  2138. _tidReadSet = _tidReadReset = _tidWriteSet = _tidWriteReset = 0xFFFFFFFF;
  2139. _xPerThreadReadCounts.Init( cTrackThreads );
  2140. _xPerThreadWriteCounts.Init( cTrackThreads );
  2141. RtlZeroMemory( _xPerThreadReadCounts.GetPointer(),
  2142. cTrackThreads * sizeof(_xPerThreadReadCounts[0]) );
  2143. RtlZeroMemory( _xPerThreadWriteCounts.GetPointer(),
  2144. cTrackThreads * sizeof(_xPerThreadWriteCounts[0]) );
  2145. #endif
  2146. _pStorage = pStorage;
  2147. #if CIDBG == 1
  2148. // Allocate an array to track what records are currently locked.
  2149. // To be used to acquire all write locks.
  2150. _pbRecordLockTracker = new BYTE[LockMgr().UniqueRecordCount()];
  2151. RtlZeroMemory(_pbRecordLockTracker, sizeof(BYTE)*LockMgr().UniqueRecordCount());
  2152. #endif // CIDBG
  2153. }
  2154. //+---------------------------------------------------------------------------
  2155. //
  2156. // Member: CPropertyStore::CreateStorage, private
  2157. //
  2158. // Synopsis: Creates property store storage.
  2159. //
  2160. // Arguments: [rhs] -- Source metadata
  2161. //
  2162. // Returns: WorkId of new storage
  2163. //
  2164. // History: 03-Jun-96 KyleP Created.
  2165. //
  2166. //----------------------------------------------------------------------------
  2167. WORKID CPropertyStore::CreateStorage( WORKID widGiven )
  2168. {
  2169. WORKID wid = widInvalid == widGiven ?
  2170. _PropStoreInfo.NextWorkId(*_pStorage) : widGiven;
  2171. SStorageObject xobj( _pStorage->QueryObject( wid ) );
  2172. XPtr<PMmStream> xmmstrm ( _pStorage->QueryNewPropStream( xobj.GetObj(),
  2173. _PropStoreInfo.GetStoreLevel() ));
  2174. _xPhysStore.Set(
  2175. new CPhysPropertyStore( *_pStorage,
  2176. xobj.GetObj(),
  2177. _PropStoreInfo.WorkId(),
  2178. xmmstrm.Acquire(),
  2179. PStorage::eOpenForWrite,
  2180. _ulPSMappedCache ) );
  2181. // grow the file 2 meg at a time
  2182. _xPhysStore->SetPageGrowth( 32 * COMMON_PAGE_SIZE / CI_PAGE_SIZE );
  2183. // create a prop store backup stream
  2184. _xPSBkpStrm.Set(_pStorage->QueryNewPSBkpStream( xobj.GetObj(),
  2185. _ulBackupSizeInPages,
  2186. GetStoreLevel() ));
  2187. return wid;
  2188. }
  2189. //+---------------------------------------------------------------------------
  2190. //
  2191. // Member: CPropertyStore::WritePropertyInSpecificNewRecord, private
  2192. //
  2193. // Synopsis: Write a property to the cache. Allocate specified wid
  2194. // for property.
  2195. //
  2196. // Arguments: [wid] -- Workid. Must be > MaxWorkId.
  2197. // [pid] -- Propid
  2198. // [var] -- Value
  2199. // [fBackup] -- Backup?
  2200. //
  2201. // History: 27-Dec-95 KyleP Created.
  2202. //
  2203. //----------------------------------------------------------------------------
  2204. void CPropertyStore::WritePropertyInSpecificNewRecord( WORKID wid,
  2205. PROPID pid,
  2206. CStorageVariant const & var,
  2207. BOOL fBackup)
  2208. {
  2209. //
  2210. // Note: We don't need to lock here, because this method is used before
  2211. // a property store comes on-line.
  2212. //
  2213. //
  2214. // Since wid must be larger than current max, then we need to adjust the
  2215. // maximum and store the records in-between on the free list.
  2216. //
  2217. Win4Assert( wid > _PropStoreInfo.MaxWorkId() );
  2218. WORKID widFree = _PropStoreInfo.MaxWorkId() + 1;
  2219. while ( widFree < wid )
  2220. {
  2221. WORKID widInRec = widFree % _PropStoreInfo.RecordsPerPage();
  2222. //
  2223. // Build as long a record as possible for the in-between free records.
  2224. // Don't span large page boundaries.
  2225. //
  2226. ULONG cWid = wid - widFree;
  2227. if ( widInRec + cWid - 1 >= _PropStoreInfo.RecordsPerPage() )
  2228. cWid = _PropStoreInfo.RecordsPerPage() - widInRec;
  2229. CBorrowed Borrowed( _xPhysStore.GetReference(),
  2230. _PropStoreInfo.RecordsPerPage(),
  2231. _PropStoreInfo.RecordSize() );
  2232. Borrowed.SetMaybeNew( widFree );
  2233. COnDiskPropertyRecord * prec = Borrowed.Get();
  2234. ciDebugOut(( DEB_PROPSTORE, "Putting records 0x%x - 0x%x on free list.\n", widFree, widFree + cWid - 1 ));
  2235. // Backup the records before freeing them. Back them up as one long
  2236. // record because that is how they are being added to the free list.
  2237. // For recovery purposes it doesn't matter if they are backed up
  2238. // one by one or all together, but we prefer the latter for efficiency.
  2239. // The free block is at most one large page. Assert that the backup is large
  2240. // enough to hold the max size of the free block.
  2241. Win4Assert(COMMON_PAGE_SIZE <= _PropStoreInfo.OSPageSize()*_xPSBkpStrm->MaxPages());
  2242. LokFreeRecord( widFree, cWid, prec, fBackup );
  2243. widFree += cWid;
  2244. }
  2245. //
  2246. // Are we on a fresh large page?
  2247. //
  2248. CBorrowed Borrowed( _xPhysStore.GetReference(),
  2249. _PropStoreInfo.RecordsPerPage(),
  2250. _PropStoreInfo.RecordSize() );
  2251. Borrowed.SetMaybeNew( wid );
  2252. COnDiskPropertyRecord * prec = Borrowed.Get();
  2253. // Tracking assert for bug 125604. Ensure that what we are overwriting
  2254. // is indeed a free or a virgin record.
  2255. Win4Assert(prec->IsFreeOrVirginRecord());
  2256. // IMPORTANT: This is a new block, so it is going to overwrite an
  2257. // existing free record. Write the toplevel wid in the TopLevel field
  2258. // of the record to be written over when the page is written to backup.
  2259. // Note that if we are backing up a "lean record", we don't need to remember
  2260. // the top-level of the displacing wid in the displaced wid. Because all
  2261. // "in use" lean records will always be only "one-record" long, we know that
  2262. // the displaced record was displaced by the same wid.
  2263. if (eLean == GetRecordFormat())
  2264. {
  2265. Win4Assert(1 == prec->CountRecords());
  2266. // Save the "to be displaced" record before actually displacing it.
  2267. if (fBackup)
  2268. {
  2269. CBackupWid backupNewWid(this, wid, prec->CountRecords());
  2270. _PropStoreInfo.MarkDirty();
  2271. }
  2272. prec->MakeNewLeanTopLevel();
  2273. }
  2274. else
  2275. {
  2276. Win4Assert(eNormal == GetRecordFormat());
  2277. // Save the "to be displaced" record before actually displacing it.
  2278. if (fBackup)
  2279. {
  2280. CBackupWid backupNewWid(this, wid, prec->CountRecords(), eTopLevelField,
  2281. (ULONG)wid, prec);
  2282. _PropStoreInfo.MarkDirty();
  2283. }
  2284. prec->MakeNewNormalTopLevel();
  2285. }
  2286. _PropStoreInfo.SetMaxWorkId( wid );
  2287. SCODE scWrite = WriteProperty( wid, pid, var, fBackup );
  2288. if (FAILED(scWrite))
  2289. THROW(CException(scWrite));
  2290. _PropStoreInfo.IncRecordsInUse();
  2291. #if CIDBG == 1
  2292. // Assert that we have a dirty property store and that something was written to
  2293. // the backup file (if fBackup is enabled)
  2294. Win4Assert(_PropStoreInfo.IsDirty());
  2295. if (fBackup)
  2296. {
  2297. Win4Assert(BackupStream()->Pages() > 0);
  2298. }
  2299. #endif // CIDBG
  2300. }
  2301. //+---------------------------------------------------------------------------
  2302. //
  2303. // Member: CPropertyStore::InitNewRecord, private inline
  2304. //
  2305. // Synopsis: Initializes a new property record
  2306. //
  2307. // Arguments: [wid] -- WORKID of the new wid
  2308. // [cWid] -- Count of contiguous workids (records) needed.
  2309. // [prec] -- pointer to the on-disk record
  2310. // [fTopLevel] -- true if the new wid is a top-level wid
  2311. //
  2312. // History: 27-Feb-96 dlee Created from code in LokNewWorkId
  2313. // 13-Jun-97 KrishnaN Backup support.
  2314. //
  2315. //----------------------------------------------------------------------------
  2316. inline void CPropertyStore::InitNewRecord(
  2317. WORKID wid,
  2318. ULONG cWid,
  2319. COnDiskPropertyRecord * prec,
  2320. BOOL fTopLevel,
  2321. BOOL fBackup)
  2322. {
  2323. // Tracking assert for bug 125604. Ensure that what we are overwriting
  2324. // is indeed a free or a virgin record.
  2325. Win4Assert(prec->IsFreeOrVirginRecord());
  2326. // This is the only exception to the rule "backup before touching".
  2327. // As a result of this exception, the backup file contains a free or virgin
  2328. // record that is cWid long. During restore from backup, this length field
  2329. // helps to identify the entire block as a free block, so processing can be
  2330. // a little more efficient. Otherwise, we will have to work with cWid individual
  2331. // free or virgin records.
  2332. prec->MakeLongRecord(cWid);
  2333. // Backup the record before touching it.
  2334. if ( fTopLevel )
  2335. {
  2336. // Record the top-level wid of the occupying record
  2337. // in the backup as part of the occupied free record.
  2338. // Note that if we are backing up a "lean record", we don't need to remember
  2339. // the top-level of the displacing wid in the displaced wid. Because all
  2340. // "in use" lean records will always be only "one-record" long, we know that
  2341. // the displaced record was displaced by the same wid.
  2342. if (eLean == GetRecordFormat())
  2343. {
  2344. Win4Assert(1 == prec->CountRecords());
  2345. // Save the "to be displaced" record before actually displacing it.
  2346. if (fBackup)
  2347. {
  2348. CBackupWid backupNewWid(this, wid, prec->CountRecords());
  2349. _PropStoreInfo.MarkDirty();
  2350. }
  2351. prec->MakeNewLeanTopLevel();
  2352. }
  2353. else
  2354. {
  2355. Win4Assert(eNormal == GetRecordFormat());
  2356. // Save the "to be displaced" record before actually displacing it.
  2357. if (fBackup)
  2358. {
  2359. CBackupWid backupNewWid(this, wid, prec->CountRecords(), eTopLevelField,
  2360. (ULONG)wid, prec);
  2361. _PropStoreInfo.MarkDirty();
  2362. }
  2363. prec->MakeNewNormalTopLevel();
  2364. }
  2365. }
  2366. else
  2367. {
  2368. Win4Assert(!prec->IsLeanRecord());
  2369. Win4Assert(eNormal == GetRecordFormat());
  2370. if (fBackup)
  2371. {
  2372. CBackupWid backupWid(this, wid, prec->CountRecords());
  2373. _PropStoreInfo.MarkDirty();
  2374. }
  2375. prec->MakeNewOverflow();
  2376. }
  2377. }
  2378. //+---------------------------------------------------------------------------
  2379. //
  2380. // Member: CPropertyStore::LokNewWorkId, private
  2381. //
  2382. // Synopsis: Find next available workid
  2383. //
  2384. // Arguments: [cWid] -- Count of contiguous workids (records) needed.
  2385. // [fTopLevel] -- true if the new wid is a top-level wid
  2386. //
  2387. // Returns: Next available workid.
  2388. //
  2389. // History: 03-Jan-96 KyleP Created.
  2390. //
  2391. //----------------------------------------------------------------------------
  2392. WORKID CPropertyStore::LokNewWorkId( ULONG cWid, BOOL fTopLevel, BOOL fBackup )
  2393. {
  2394. //
  2395. // First, try to find a free block of the appropriate size.
  2396. // Search for the best-fit, scanning the list until an exact
  2397. // match or the first smaller block is found.
  2398. //
  2399. COnDiskPropertyRecord * prec = 0;
  2400. COnDiskPropertyRecord * precPrev = 0;
  2401. CBorrowed Borrowed( _xPhysStore.GetReference(),
  2402. _PropStoreInfo.RecordsPerPage(),
  2403. _PropStoreInfo.RecordSize() );
  2404. ciDebugOut(( DEB_PROPSTORE, "Looking for free record of size %u\n", cWid ));
  2405. WORKID wid = LokAllocRecord( cWid );
  2406. if (wid != 0)
  2407. {
  2408. Borrowed.Set( wid );
  2409. prec = Borrowed.Get();
  2410. ciDebugOut(( DEB_PROPSTORE,
  2411. " Free wid 0x%x, size = %u\n",
  2412. wid, prec->CountRecords() ));
  2413. //
  2414. // Is it big enough?
  2415. //
  2416. Win4Assert ( prec->CountRecords() >= cWid );
  2417. //
  2418. // Adjust size, and put extra back on free list.
  2419. //
  2420. if ( prec->CountRecords() > cWid )
  2421. {
  2422. CBorrowed BorrowedRemainder( _xPhysStore.GetReference(),
  2423. wid + cWid,
  2424. _PropStoreInfo.RecordsPerPage(),
  2425. _PropStoreInfo.RecordSize() );
  2426. COnDiskPropertyRecord * precRemainder = BorrowedRemainder.Get();
  2427. LokFreeRecord( wid+cWid,
  2428. prec->CountRecords() - cWid,
  2429. precRemainder,
  2430. fBackup );
  2431. }
  2432. InitNewRecord( wid, cWid, prec, fTopLevel, fBackup );
  2433. }
  2434. else
  2435. {
  2436. Win4Assert( cWid <= _PropStoreInfo.RecordsPerPage() );
  2437. wid = _PropStoreInfo.MaxWorkId() + 1;
  2438. //
  2439. // Do we need a fresh page?
  2440. //
  2441. WORKID widInRec = wid % _PropStoreInfo.RecordsPerPage();
  2442. if ( widInRec + cWid - 1 >= _PropStoreInfo.RecordsPerPage() )
  2443. {
  2444. ciDebugOut(( DEB_PROPSTORE, "Aligning for Multi-workid request...\n" ));
  2445. CBorrowed Borrowed( _xPhysStore.GetReference(),
  2446. wid,
  2447. _PropStoreInfo.RecordsPerPage(),
  2448. _PropStoreInfo.RecordSize() );
  2449. COnDiskPropertyRecord * prec = Borrowed.Get();
  2450. #if CIDBG == 1
  2451. if (eLean == GetRecordFormat())
  2452. Win4Assert( prec->IsLeanEmpty(_PropStoreInfo.RecordSize()) );
  2453. else
  2454. Win4Assert( prec->IsNormalEmpty( _PropStoreInfo.RecordSize() ) );
  2455. #endif // CIDBG
  2456. ULONG cFree = _PropStoreInfo.RecordsPerPage() - widInRec;
  2457. LokFreeRecord( wid, cFree, prec, fBackup );
  2458. wid += cFree;
  2459. Win4Assert( (wid % _PropStoreInfo.RecordsPerPage() ) == 0 );
  2460. }
  2461. if ( cWid > 1 )
  2462. {
  2463. ciDebugOut(( DEB_PROPSTORE, "New max workid = %d\n", _PropStoreInfo.MaxWorkId() ));
  2464. }
  2465. CBorrowed Borrowed( _xPhysStore.GetReference(),
  2466. _PropStoreInfo.RecordsPerPage(),
  2467. _PropStoreInfo.RecordSize() );
  2468. Borrowed.SetMaybeNew( wid );
  2469. COnDiskPropertyRecord * prec = Borrowed.Get();
  2470. _PropStoreInfo.SetMaxWorkId( wid + cWid - 1 );
  2471. #if CIDBG==1
  2472. if ( prec->IsInUse() )
  2473. {
  2474. ciDebugOut(( DEB_ERROR, "Wid (0x%X) pRec (0x%X) is in use.\n",
  2475. wid, prec ));
  2476. // Win4Assert( !"InUse bit must not be set here" );
  2477. }
  2478. if ( prec->IsTopLevel() )
  2479. {
  2480. ciDebugOut(( DEB_ERROR, "Wid (0x%X) pRec (0x%X) is top level record.\n",
  2481. wid, prec ));
  2482. // Win4Assert( !prec->IsTopLevel() );
  2483. }
  2484. #endif // CIDBG==1
  2485. InitNewRecord( wid, cWid, prec, fTopLevel, fBackup );
  2486. }
  2487. return wid;
  2488. }
  2489. //+---------------------------------------------------------------------------
  2490. //
  2491. // Member: CPropertyStore::Transfer, private
  2492. //
  2493. // Synopsis: Transfer complete contents to new store.
  2494. //
  2495. // Arguments: [Target] -- Target property store.
  2496. // [pid] -- Property to transfer first. Must be on every
  2497. // record.
  2498. // [fAbort] -- Set this variable to TRUE to abort transfer.
  2499. // [pProgress] -- Progress reported here.
  2500. //
  2501. // History: 16-Jan-96 KyleP Created.
  2502. // 19-Sep-97 KrishnaN Disabled record transfer during backup.
  2503. //
  2504. // Notes: For progress notification, we are assuming that copying the
  2505. // top level takes about 50% of the time and the remaining 50%
  2506. // is for the properties other than the first property.
  2507. //
  2508. //----------------------------------------------------------------------------
  2509. void CPropertyStore::Transfer( CPropertyStore & Target, PROPID pid,
  2510. BOOL & fAbort,
  2511. IProgressNotify * pProgress )
  2512. {
  2513. //
  2514. // Make Sure that the pid specified is a valid pid and is of fixed
  2515. // length. If it is of variable length, that property may overflow
  2516. // the top level records. If that happens, we cannot guarantee retaining
  2517. // the same top level wid number in the new propstore.
  2518. //
  2519. CPropDesc const * pdesc = _PropStoreInfo.GetDescription( pid );
  2520. if ( !pdesc || !pdesc->IsFixedSize() )
  2521. {
  2522. ciDebugOut(( DEB_ERROR, "PropId 0x%X is not of fixed size.\n", pid ));
  2523. THROW( CException( E_INVALIDARG ) );
  2524. }
  2525. //
  2526. // Copy data from old to new.
  2527. //
  2528. CPropertyStoreWids iter( *this );
  2529. PROPVARIANT var;
  2530. XArray<BYTE> abExtra( COMMON_PAGE_SIZE );
  2531. //
  2532. // Transfer special property first. This gives same top-level workids as
  2533. // previous situation. Assumption: Property will *not* overflow record.
  2534. //
  2535. ULONG iRec = 0;
  2536. const ULONG cTotal = _PropStoreInfo.CountRecordsInUse();
  2537. const cUpdInterval = 500; // every 500 records
  2538. for ( WORKID wid = iter.WorkId(); wid != widInvalid; wid = iter.LokNextWorkId() )
  2539. {
  2540. if ( _fAbort || fAbort )
  2541. {
  2542. ciDebugOut(( DEB_WARN,"Stopping Transfer because of abort\n" ));
  2543. THROW( CException(STATUS_TOO_LATE) );
  2544. }
  2545. unsigned cb = abExtra.Count();
  2546. BOOL fOk = ReadProperty( wid, pid, var, abExtra.GetPointer(), &cb );
  2547. Win4Assert( fOk );
  2548. // Win4Assert( var.vt != VT_EMPTY );
  2549. if ( fOk )
  2550. {
  2551. Target.WritePropertyInSpecificNewRecord( wid, pid,
  2552. *(CStorageVariant *)(ULONG_PTR)&var,
  2553. FALSE);
  2554. }
  2555. iRec++;
  2556. if ( pProgress )
  2557. {
  2558. if ( (iRec % cUpdInterval) == 0 )
  2559. {
  2560. pProgress->OnProgress(
  2561. (DWORD) iRec,
  2562. (DWORD) 2*cTotal, // We are copying only the top level now
  2563. FALSE, // Not accurate
  2564. FALSE // Ownership of Blocking Behavior
  2565. );
  2566. }
  2567. }
  2568. }
  2569. Win4Assert( iRec == cTotal );
  2570. //
  2571. // Transfer remaining properties.
  2572. //
  2573. CPropertyStoreWids iter2( *this );
  2574. iRec = 0;
  2575. for ( wid = iter2.WorkId(); wid != widInvalid; wid = iter2.LokNextWorkId() )
  2576. {
  2577. if ( _fAbort || fAbort )
  2578. {
  2579. ciDebugOut(( DEB_WARN,"Stopping Transfer2 because of abort\n" ));
  2580. THROW( CException(STATUS_TOO_LATE) );
  2581. }
  2582. for ( unsigned i = 0; i < _PropStoreInfo.CountProps(); i++ )
  2583. {
  2584. CPropDesc const * pdesc = _PropStoreInfo.GetDescriptionByOrdinal( i );
  2585. if ( 0 != pdesc && pdesc->Pid() != pid )
  2586. {
  2587. unsigned cb = abExtra.Count();
  2588. if ( ReadProperty( wid, pdesc->Pid(), var, abExtra.GetPointer(), &cb ) &&
  2589. var.vt != VT_EMPTY )
  2590. {
  2591. Target.WriteProperty( wid, pdesc->Pid(),
  2592. *(CStorageVariant *)(ULONG_PTR)&var,
  2593. FALSE);
  2594. }
  2595. }
  2596. }
  2597. iRec++;
  2598. if ( pProgress )
  2599. {
  2600. if ( (iRec++ % cUpdInterval) == 0 )
  2601. {
  2602. pProgress->OnProgress(
  2603. (DWORD) (iRec+cTotal),
  2604. (DWORD) 2*cTotal, // We are copying only the top level now
  2605. FALSE, // Not accurate
  2606. FALSE // Ownership of Blocking Behavior
  2607. );
  2608. }
  2609. }
  2610. }
  2611. }
  2612. //+---------------------------------------------------------------------------
  2613. //
  2614. // Member: CPropertyStore::FastTransfer, private
  2615. //
  2616. // Synopsis: Transfer complete contents to new store, without changes.
  2617. //
  2618. // Arguments: [Target] -- Target property store.
  2619. // [fAbort] -- Set this variable to TRUE to abort transfer.
  2620. // [pProgress] -- Progress reported here.
  2621. //
  2622. // History: 16-Oct-97 KyleP Created (based on ::Transfer)
  2623. //
  2624. //----------------------------------------------------------------------------
  2625. void CPropertyStore::FastTransfer( CPropertyStore & Target,
  2626. BOOL & fAbort,
  2627. IProgressNotify * pProgress )
  2628. {
  2629. //
  2630. // Just transfer the storage in bulk. There's no modification to the
  2631. // property store here, thus no reason to iterate by record.
  2632. //
  2633. CProgressTracker Tracker;
  2634. Tracker.LokStartTracking( pProgress, &fAbort );
  2635. _xPhysStore->MakeBackupCopy( Target._xPhysStore.GetReference(),
  2636. Tracker );
  2637. Tracker.LokStopTracking();
  2638. }
  2639. //+---------------------------------------------------------------------------
  2640. //
  2641. // Member: CPropertyStore::Flush
  2642. //
  2643. // Synopsis: Flushes the data in the property store and marks it clean.
  2644. //
  2645. // Returns: TRUE if the store is clean. FALSE otherwise.
  2646. //
  2647. // History: 3-20-96 srikants Created
  2648. //
  2649. //----------------------------------------------------------------------------
  2650. BOOL CPropertyStore::Flush()
  2651. {
  2652. CImpersonateSystem impersonate;
  2653. //
  2654. // Don't reset the backup stream here.
  2655. // That will happen in the manager.
  2656. //
  2657. if ( _xPhysStore.GetPointer() )
  2658. {
  2659. if ( !_pStorage->IsReadOnly() )
  2660. _xPhysStore->Flush();
  2661. if ( _fIsConsistent )
  2662. _PropStoreInfo.MarkClean();
  2663. else
  2664. _PropStoreInfo.MarkDirty();
  2665. }
  2666. return !IsDirty();
  2667. }
  2668. //+---------------------------------------------------------------------------
  2669. //
  2670. // Member: CPropertyStore::AcquireRead, private
  2671. //
  2672. // Synopsis: Acquires read lock for a record
  2673. //
  2674. // Arguments: [record] -- Record
  2675. //
  2676. // History: 19-Jan-96 KyleP Created.
  2677. //
  2678. //----------------------------------------------------------------------------
  2679. void CPropertyStore::AcquireRead( CReadWriteLockRecord & record )
  2680. {
  2681. #if CIDBG == 1
  2682. DWORD tid = GetCurrentThreadId();
  2683. if ( tid < cTrackThreads )
  2684. {
  2685. Win4Assert( _xPerThreadReadCounts[tid] == 0 ); // Double-read
  2686. Win4Assert( _xPerThreadWriteCounts[tid] == 0 ); // Write before Read
  2687. }
  2688. #endif
  2689. do
  2690. {
  2691. if ( record.isBeingWritten() )
  2692. SyncRead( record );
  2693. record.AddReader();
  2694. if ( !record.isBeingWritten() )
  2695. break;
  2696. else
  2697. SyncReadDecrement( record );
  2698. } while ( TRUE );
  2699. #if CIDBG == 1
  2700. if ( tid < cTrackThreads )
  2701. _xPerThreadReadCounts[tid]++;
  2702. #endif
  2703. } //AcquireRead
  2704. //+---------------------------------------------------------------------------
  2705. //
  2706. // Member: CPropertyStore::SyncRead, private
  2707. //
  2708. // Synopsis: Helper for AcquireRead
  2709. //
  2710. // Arguments: [record] -- Record
  2711. //
  2712. // History: 19-Jan-96 KyleP Created.
  2713. //
  2714. //----------------------------------------------------------------------------
  2715. void CPropertyStore::SyncRead(
  2716. CReadWriteLockRecord & record )
  2717. {
  2718. do
  2719. {
  2720. BOOL fNeedToWait = FALSE;
  2721. {
  2722. CLock lock( _mtxRW );
  2723. if ( record.isBeingWritten() )
  2724. {
  2725. ciDebugOut(( DEB_PROPSTORE, "READ.RESET\n" ));
  2726. _ReSetReadTid();
  2727. _evtRead.Reset();
  2728. fNeedToWait = TRUE;
  2729. }
  2730. }
  2731. if ( fNeedToWait )
  2732. {
  2733. ciDebugOut(( DEB_PROPSTORE, "READ.WAIT\n" ));
  2734. _evtRead.Wait();
  2735. }
  2736. } while ( record.isBeingWritten() );
  2737. } //SyncRead
  2738. //+---------------------------------------------------------------------------
  2739. //
  2740. // Member: CPropertyStore::SyncReadDecrement, private
  2741. //
  2742. // Synopsis: Helper for AcquireRead
  2743. //
  2744. // Arguments: [record] -- Record
  2745. //
  2746. // History: 19-Jan-96 KyleP Created.
  2747. // 21-Feb-97 dlee Rearranged to be more like
  2748. // readwrit.hxx, for consistency
  2749. //
  2750. // Notes: The *very* important invariant of this method is that the
  2751. // read count will be decremented once and only once.
  2752. // The lack of this invarariant may very
  2753. // well be the last Tripoli 1.0 bug.
  2754. //
  2755. //----------------------------------------------------------------------------
  2756. void CPropertyStore::SyncReadDecrement(
  2757. CReadWriteLockRecord & record )
  2758. {
  2759. BOOL fDecrementRead = TRUE;
  2760. do
  2761. {
  2762. BOOL fNeedToWait = FALSE;
  2763. {
  2764. CLock lock( _mtxRW );
  2765. if ( record.isBeingWritten() )
  2766. {
  2767. if ( fDecrementRead )
  2768. {
  2769. record.RemoveReader();
  2770. if ( !record.isBeingRead() )
  2771. {
  2772. ciDebugOut(( DEB_PROPSTORE, "WRITE.SET\n" ));
  2773. _SetWriteTid();
  2774. _evtWrite.Set();
  2775. }
  2776. }
  2777. ciDebugOut(( DEB_PROPSTORE, "READ.RESET\n" ));
  2778. _ReSetReadTid();
  2779. _evtRead.Reset();
  2780. fNeedToWait = TRUE;
  2781. }
  2782. else
  2783. {
  2784. if ( fDecrementRead )
  2785. record.RemoveReader();
  2786. }
  2787. fDecrementRead = FALSE;
  2788. }
  2789. if ( fNeedToWait )
  2790. {
  2791. ciDebugOut(( DEB_PROPSTORE, "READ.WAIT\n" ));
  2792. _evtRead.Wait();
  2793. }
  2794. } while ( record.isBeingWritten() );
  2795. Win4Assert( !fDecrementRead );
  2796. } //SyncReadDecrement
  2797. //+---------------------------------------------------------------------------
  2798. //
  2799. // Member: CPropertyStore::ReleaseRead, private
  2800. //
  2801. // Synopsis: Releases read lock for a record
  2802. //
  2803. // Arguments: [record] -- Record
  2804. //
  2805. // History: 19-Jan-96 KyleP Created.
  2806. //
  2807. //----------------------------------------------------------------------------
  2808. void CPropertyStore::ReleaseRead( CReadWriteLockRecord & record )
  2809. {
  2810. if ( record.isBeingWritten() )
  2811. {
  2812. CLock lock( _mtxRW );
  2813. record.RemoveReader();
  2814. if ( !record.isBeingRead() )
  2815. {
  2816. ciDebugOut(( DEB_PROPSTORE, "WRITE.SET\n" ));
  2817. _SetWriteTid();
  2818. _evtWrite.Set();
  2819. }
  2820. }
  2821. else
  2822. {
  2823. record.RemoveReader();
  2824. if ( record.isBeingWritten() )
  2825. {
  2826. CLock lock( _mtxRW );
  2827. if ( !record.isBeingRead() )
  2828. {
  2829. ciDebugOut(( DEB_PROPSTORE, "WRITE.SET\n" ));
  2830. _SetWriteTid();
  2831. _evtWrite.Set();
  2832. }
  2833. }
  2834. }
  2835. #if CIDBG == 1
  2836. DWORD tid = GetCurrentThreadId();
  2837. if ( tid < cTrackThreads )
  2838. _xPerThreadReadCounts[tid]--;
  2839. #endif
  2840. } //ReleaseRead
  2841. //+---------------------------------------------------------------------------
  2842. //
  2843. // Member: CPropertyStore::AcquireWrite, private
  2844. //
  2845. // Synopsis: Acquires write lock for a record
  2846. //
  2847. // Arguments: [record] -- Record
  2848. //
  2849. // History: 19-Jan-96 KyleP Created.
  2850. // 20-Nov-97 KrishnaN Code reorg, but no functionality change.
  2851. //
  2852. //----------------------------------------------------------------------------
  2853. void CPropertyStore::AcquireWrite( CReadWriteLockRecord & record )
  2854. {
  2855. #if CIDBG == 1
  2856. DWORD tid = GetCurrentThreadId();
  2857. if ( tid < cTrackThreads )
  2858. {
  2859. Win4Assert( _xPerThreadReadCounts[tid] == 0 ); // Read before Write
  2860. Win4Assert( _xPerThreadWriteCounts[tid] == 0 ); // Double-write
  2861. }
  2862. #endif
  2863. AcquireWrite2(record);
  2864. #if CIDBG == 1
  2865. if ( tid < cTrackThreads )
  2866. _xPerThreadWriteCounts[tid]++;
  2867. #endif
  2868. } //AcquireWrite
  2869. //+---------------------------------------------------------------------------
  2870. //
  2871. // Member: CPropertyStore::AcquireWrite2, private
  2872. //
  2873. // Synopsis: Acquires write lock for a record. There are no per thread
  2874. // checks to ensure that a thread is only writing once. The
  2875. // caller will check that.
  2876. //
  2877. // Arguments: [record] -- Record
  2878. //
  2879. // History: 20-Nov-97 KrishnaN Created.
  2880. //
  2881. //----------------------------------------------------------------------------
  2882. void CPropertyStore::AcquireWrite2( CReadWriteLockRecord & record )
  2883. {
  2884. record.AddWriter();
  2885. BOOL fNeedToWait = FALSE;
  2886. {
  2887. CLock lock( _mtxRW );
  2888. Win4Assert( !record.LokIsBeingWrittenTwice() );
  2889. if ( record.isBeingRead() )
  2890. {
  2891. ciDebugOut(( DEB_PROPSTORE, "WRITE.RESET\n" ));
  2892. _ReSetWriteTid();
  2893. _evtWrite.Reset();
  2894. fNeedToWait = TRUE;
  2895. }
  2896. }
  2897. if ( fNeedToWait )
  2898. {
  2899. ciDebugOut(( DEB_PROPSTORE, "WRITE.WAIT\n" ));
  2900. _evtWrite.Wait();
  2901. }
  2902. } //AcquireWrite2
  2903. //+---------------------------------------------------------------------------
  2904. //
  2905. // Member: CPropertyStore::ReleaseWrite, private
  2906. //
  2907. // Synopsis: Release write lock for a record
  2908. //
  2909. // Arguments: [record] -- Record
  2910. //
  2911. // History: 19-Jan-96 KyleP Created.
  2912. //
  2913. //----------------------------------------------------------------------------
  2914. void CPropertyStore::ReleaseWrite( CReadWriteLockRecord & record )
  2915. {
  2916. ReleaseWrite2(record);
  2917. #if CIDBG == 1
  2918. DWORD tid = GetCurrentThreadId();
  2919. if ( tid < cTrackThreads )
  2920. _xPerThreadWriteCounts[tid]--;
  2921. #endif
  2922. } //ReleaseWrite
  2923. //+---------------------------------------------------------------------------
  2924. //
  2925. // Member: CPropertyStore::ReleaseWrite2, private
  2926. //
  2927. // Synopsis: Release write lock for a record. No per thread tracking. The
  2928. // caller will do that.
  2929. //
  2930. // Arguments: [record] -- Record
  2931. //
  2932. // History: 20-Nov-97 KrishnaN Created.
  2933. //
  2934. //----------------------------------------------------------------------------
  2935. void CPropertyStore::ReleaseWrite2( CReadWriteLockRecord & record )
  2936. {
  2937. CLock lock( _mtxRW );
  2938. record.RemoveWriter();
  2939. ciDebugOut(( DEB_PROPSTORE, "READ.SET\n" ));
  2940. _SetReadTid();
  2941. _evtRead.Set();
  2942. } //ReleaseWrite2
  2943. //+---------------------------------------------------------------------------
  2944. //
  2945. // Member: CPropertyStore::AcquireWriteOnAllRecords, private
  2946. //
  2947. // Synopsis: Lock out all readers.
  2948. //
  2949. // History: 18-Nov-97 KrishnaN Created.
  2950. //
  2951. //----------------------------------------------------------------------------
  2952. void CPropertyStore::AcquireWriteOnAllRecords()
  2953. {
  2954. ULONG cRecs = LockMgr().UniqueRecordCount();
  2955. // lock down each record, one by one
  2956. for (ULONG i = 0; i < cRecs; i++)
  2957. {
  2958. Win4Assert(0 == _pbRecordLockTracker[i]);
  2959. AcquireWrite2( LockMgr().GetRecord( (WORKID) i) );
  2960. #if CIDBG == 1
  2961. _pbRecordLockTracker[i]++;
  2962. #endif // CIDBG
  2963. }
  2964. }
  2965. //+---------------------------------------------------------------------------
  2966. //
  2967. // Member: CPropertyStore::ReleaseWriteOnAllRecords, private
  2968. //
  2969. // Synopsis: Release all write locked records.
  2970. //
  2971. // History: 18-Nov-97 KrishnaN Created.
  2972. //
  2973. //----------------------------------------------------------------------------
  2974. void CPropertyStore::ReleaseWriteOnAllRecords()
  2975. {
  2976. ULONG cRecs = LockMgr().UniqueRecordCount();
  2977. for (ULONG i = 0; i < cRecs; i++)
  2978. {
  2979. Win4Assert( 1 == _pbRecordLockTracker[i] );
  2980. ReleaseWrite2(LockMgr().GetRecord((WORKID)i));
  2981. }
  2982. #if CIDBG == 1
  2983. RtlZeroMemory(_pbRecordLockTracker, sizeof(BYTE)*LockMgr().UniqueRecordCount());
  2984. #endif // CIDBG
  2985. }
  2986. //+---------------------------------------------------------------------------
  2987. //
  2988. // Member: CPropertyStore::GetTotalSizeInKB, public
  2989. //
  2990. // Synopsis: Compute the size of the property store, including the backup file.
  2991. //
  2992. // Return: The size in KiloBytes.
  2993. //
  2994. // History: 19-Jan-96 KyleP Created.
  2995. //
  2996. //----------------------------------------------------------------------------
  2997. ULONG CPropertyStore::GetTotalSizeInKB()
  2998. {
  2999. // primary store size
  3000. ULONG cSizeInKB = 0;
  3001. if ( !_xPhysStore.IsNull() )
  3002. cSizeInKB += CI_PAGE_SIZE*_xPhysStore->PageSize()/1024;
  3003. if (!_xPSBkpStrm.IsNull())
  3004. {
  3005. // backup store size
  3006. cSizeInKB += roundup(_xPSBkpStrm->GetSizeInBytes(), 1024);
  3007. }
  3008. return cSizeInKB;
  3009. } //GetTotalSizeInKB
  3010. //+-------------------------------------------------------------------------
  3011. //
  3012. // Member: CiCat::ClearNonStorageProperties, public
  3013. //
  3014. // Synopsis: write VT_EMPTY into the properties in the PropertyStore (
  3015. // except the non-modifiable ones)
  3016. //
  3017. // Arguments: [rec] -- Property record for writes
  3018. //
  3019. // History: 06-Oct-2000 KitmanH Created
  3020. //
  3021. //--------------------------------------------------------------------------
  3022. void CPropertyStore::ClearNonStorageProperties( CCompositePropRecordForWrites & rec )
  3023. {
  3024. CPropDesc const * pDesc = 0;
  3025. CStorageVariant var;
  3026. var.SetEMPTY();
  3027. for ( unsigned i = 0; i < _PropStoreInfo.CountDescription(); i++ )
  3028. {
  3029. pDesc = _PropStoreInfo.GetDescription(i);
  3030. if ( 0 != pDesc && pDesc->Modifiable() )
  3031. {
  3032. ciDebugOut(( DEB_ITRACE, "ClearNonStorageProperties: CPropertyStore %d: about to overwrite pid %d\n",
  3033. GetStoreLevel(),
  3034. pDesc->Pid() ));
  3035. _propStoreMgr.WriteProperty( GetStoreLevel(),
  3036. rec,
  3037. pDesc->Pid(),
  3038. var );
  3039. }
  3040. #if CIDBG
  3041. else
  3042. {
  3043. if ( 0 != pDesc )
  3044. ciDebugOut(( DEB_ITRACE, "ClearNonStorageProperties: CPropertyStore %d: pid %d is !MODIFIABLE\n",
  3045. GetStoreLevel(),
  3046. pDesc->Pid() ));
  3047. else
  3048. ciDebugOut(( DEB_ITRACE, "CPropertyStore::ClearNonStorageProperties: pDesc is 0\n" ));
  3049. }
  3050. #endif
  3051. }
  3052. }
  3053. // CBackupWid methods
  3054. //+---------------------------------------------------------------------------
  3055. //
  3056. // Function: CBackupWid::BackupWid method
  3057. //
  3058. // Synopsis: Backup the page(s) containing the wid. If necessary, write
  3059. // the top-level wid in the free record.
  3060. //
  3061. // Arguments: [wid] -- Wid to backup.
  3062. // [cRecsInWid] -- Length of the wid in physical records.
  3063. // [FieldToCommit] -- Field to commit to disk.
  3064. // [ulValue] -- When valid, this is the field value to
  3065. // be written as part of the wid in the backup's
  3066. // copy.
  3067. // [pRec] -- Address of the record being modified.
  3068. //
  3069. // Returns: Nothing.
  3070. //
  3071. // History: 09-June-97 KrishnaN Created
  3072. // 27-Jan-2000 KLam Removed bogus fBackedUp asserts
  3073. //
  3074. //----------------------------------------------------------------------------
  3075. void CBackupWid::BackupWid(WORKID wid,
  3076. ULONG cRecsInWid,
  3077. EField FieldToCommit,
  3078. ULONG ulValue,
  3079. COnDiskPropertyRecord const *pRec)
  3080. {
  3081. Win4Assert( _pPropStor );
  3082. Win4Assert( _pPropStor->BackupStream() );
  3083. ULONG cPages = TryDescribeWidInAPage(wid, cRecsInWid);
  3084. cPages++; // actual number of pages
  3085. ULONG dSlot = _ulFirstPage;
  3086. ciDebugOut(( DEB_PSBACKUP, "BackupWid: wid = %d (0x%x), %d recs long, FieldToCommit = %d, "
  3087. "Value to commit = 0x%x, pRec = 0x%x, pages needed = %d\n",
  3088. wid, wid, cRecsInWid, FieldToCommit, ulValue, pRec, cPages));
  3089. //
  3090. // If we need more than one page to describe this wid, allocate
  3091. // space for the data structures and describe them.
  3092. //
  3093. BOOL fBackedUp = FALSE;
  3094. ULONG ulOffset = 0xFFFFFFFF;
  3095. if (1 == cPages)
  3096. {
  3097. fBackedUp = BackupPages(1, &_ulFirstPage, (void **)&_pbFirstPage);
  3098. if (eFieldNone != FieldToCommit)
  3099. DescribeField(FieldToCommit, pRec, 1, (void **)&_pbFirstPage, ulOffset);
  3100. }
  3101. else
  3102. {
  3103. CDynArrayInPlace<ULONG> adSlots(cPages);
  3104. CDynArrayInPlace<void *> aPagePtrs(cPages);
  3105. void const * const* paPagePtrs = aPagePtrs.GetPointer();
  3106. DescribeWid(wid, cRecsInWid, adSlots, aPagePtrs);
  3107. fBackedUp = BackupPages(cPages, adSlots.GetPointer(), paPagePtrs);
  3108. // reuse dSlot
  3109. if (eFieldNone != FieldToCommit)
  3110. dSlot = adSlots[DescribeField(FieldToCommit, pRec, cPages, paPagePtrs, ulOffset)];
  3111. }
  3112. //
  3113. // commit the widTopLevel to top-level field of free record to disk
  3114. // At this point the page has already been commited to backup, so it should be there.
  3115. //
  3116. if (fBackedUp && eFieldNone != FieldToCommit)
  3117. fBackedUp = _pPropStor->BackupStream()->CommitField(dSlot, ulOffset, sizeof(WORKID), &ulValue);
  3118. // Throw the error that caused file i/o to fail!
  3119. if (!fBackedUp)
  3120. {
  3121. ciDebugOut(( DEB_ERROR, "Unexpected error writing a page to the backup file!"));
  3122. THROW( CException() );
  3123. }
  3124. }
  3125. //+---------------------------------------------------------------------------
  3126. //
  3127. // Member: CBackupWid::TryDescribeWidInAPage, public
  3128. //
  3129. // Synopsis: Determine the OS pages containing the wids and the location of
  3130. // the first wid in each OS page.
  3131. //
  3132. // Arguments: [wid] -- Wid to describe.
  3133. // [cRecsInWid] -- Number of records in wid.
  3134. //
  3135. // Returns: The number of additional OS pages needed for the wid. The caller
  3136. // should use DescribeWids if the return value is > 0.
  3137. //
  3138. // History: 09-Jun-97 KrishnaN Created.
  3139. //
  3140. //----------------------------------------------------------------------------
  3141. inline ULONG CBackupWid::TryDescribeWidInAPage(WORKID wid,
  3142. ULONG cRecsInWid)
  3143. {
  3144. Win4Assert(widInvalid != wid );
  3145. // Determine the first and last OS pages, relative to start of property store file, which contain wid
  3146. ComputeOSPageLocations(wid, cRecsInWid, &_ulFirstPage, &_ulLastPage);
  3147. _nLargePage = _ulFirstPage/_pPropStor->OSPagesPerPage();
  3148. // this wid should be within a large page. Assert that.
  3149. Win4Assert(_nLargePage == _ulLastPage/_pPropStor->OSPagesPerPage());
  3150. _pbFirstPage = GetOSPagePointer(_ulFirstPage);
  3151. return (_ulLastPage - _ulFirstPage);
  3152. }
  3153. //+---------------------------------------------------------------------------
  3154. //
  3155. // Member: CBackupWid::DescribeWids, public
  3156. //
  3157. // Synopsis: Determine the OS pages containing the wids.
  3158. //
  3159. // Arguments: [Wid] -- Wid to describe.
  3160. // [cRecsInWid] -- Count of records in wid.
  3161. // [pcPages] -- Number of pages.
  3162. // [pulPages] -- Dynamic array of page descriptors.
  3163. // [ppvPages] -- Dynamic array of page pointers to be backedup.
  3164. //
  3165. // Returns: Index into hash table (_aProp) of pid, or first unused
  3166. // entry on chain if pid doesn't exist.
  3167. //
  3168. // Notes: Using this requires at least two dynamic allocations on the heap.
  3169. // If that is deemed expensive, we can attempt to describe only one
  3170. // wid at a time using TryDescribeWibInAPage.
  3171. //
  3172. // History: 09-Jun-97 KrishnaN Created.
  3173. //
  3174. //----------------------------------------------------------------------------
  3175. inline void CBackupWid::DescribeWid( WORKID wid,
  3176. ULONG cRecsInWid,
  3177. CDynArrayInPlace<ULONG> &pulPages,
  3178. CDynArrayInPlace<void *> &ppvPages)
  3179. {
  3180. Win4Assert(widInvalid != wid );
  3181. // First and last page and the first page ptr should have already
  3182. // been computed by calling TryDescribeWidInAPage
  3183. Win4Assert(_ulFirstPage != 0xFFFFFFFF && _ulLastPage != 0xFFFFFFFF);
  3184. Win4Assert(_pbFirstPage);
  3185. Win4Assert(_ulLastPage > _ulFirstPage);
  3186. ULONG cPages, page;
  3187. for (page = _ulFirstPage, cPages = 0;
  3188. page <= _ulLastPage;
  3189. page++, cPages++)
  3190. {
  3191. pulPages[cPages] = page;
  3192. ppvPages[cPages] = _pbFirstPage + cPages*_pPropStor->OSPageSize();
  3193. }
  3194. }
  3195. //+---------------------------------------------------------------------------
  3196. //
  3197. // Function: CBackupWid::BackupPages, public
  3198. //
  3199. // Synopsis: Backup pages. If space is not available for backup,
  3200. //
  3201. // Arguments: [cPages] -- Number of pages to backup.
  3202. // [pSlots] -- Array of page descriptors.
  3203. // [ppbPages] -- Array of page pointers to backup.
  3204. //
  3205. // Returns: TRUE if the pages were successfully backed up.
  3206. // FALSE otherwise.
  3207. //
  3208. // History: 09-June-97 KrishnaN Created
  3209. //
  3210. //----------------------------------------------------------------------------
  3211. BOOL CBackupWid::BackupPages(ULONG cPages,
  3212. ULONG const *pulPages,
  3213. void const * const * ppvPages)
  3214. {
  3215. Win4Assert(cPages && ppvPages && pulPages);
  3216. Win4Assert( _pPropStor->BackupStream() );
  3217. ciDebugOut(( DEB_PSBACKUP, "BackupPages: About to backup %2d pages: ", cPages));
  3218. #if CIDBG
  3219. // We should have no more than 16 pages to backup because that is the max
  3220. // a property can straddle (a prop is limited to a 64K common page size). On
  3221. // alpha the max is only 8.
  3222. Win4Assert(cPages <= 16);
  3223. WCHAR szBuff[2048];
  3224. szBuff[0] = 0;
  3225. for (ULONG j = 0; j < cPages; j++)
  3226. {
  3227. WCHAR szPage[100];
  3228. swprintf(szPage, L"%10d, 0x%x; ", pulPages[j], ppvPages[j]);
  3229. wcscat(szBuff, szPage);
  3230. }
  3231. ciDebugOut((DEB_PSBACKUP, "%ws\n", szBuff));
  3232. #endif // CIDBG
  3233. // Commit pages.
  3234. BOOL fOK;
  3235. if (1 == cPages)
  3236. fOK = _pPropStor->BackupStream()->CommitPage(pulPages[0], ppvPages[0]);
  3237. else
  3238. fOK = _pPropStor->BackupStream()->CommitPages(cPages, pulPages, ppvPages);
  3239. if (fOK)
  3240. return TRUE;
  3241. // Not enough space to commit pages. Flush the property store
  3242. // and try again.
  3243. ciDebugOut((DEB_PSBACKUP, "Flushing the property store to make space for pages.\n"));
  3244. _pPropStor->_propStoreMgr.Flush();
  3245. if (1 == cPages)
  3246. return _pPropStor->BackupStream()->CommitPage(pulPages[0], ppvPages[0]);
  3247. else
  3248. return _pPropStor->BackupStream()->CommitPages(cPages, pulPages, ppvPages);
  3249. }
  3250. //+---------------------------------------------------------------------------
  3251. //
  3252. // Function: CBackupWid::GetOSPagePointer, private
  3253. //
  3254. // Synopsis: Get the OS page pointer for the given page.
  3255. //
  3256. // Arguments: [ulPage] -- i-th OS page (0-based index) of prop store.
  3257. //
  3258. // Returns: A memory pointer to the specified page.
  3259. //
  3260. // History: 09-June-97 KrishnaN Created
  3261. //
  3262. // Notes: This will be returning read-only pages.
  3263. //
  3264. //----------------------------------------------------------------------------
  3265. inline BYTE * CBackupWid::GetOSPagePointer(ULONG ulPage)
  3266. {
  3267. PBYTE pLargePage = (PBYTE)_pPropStor->PhysStore()->BorrowLargeBuffer(
  3268. ulPage/_pPropStor->OSPagesPerPage(),
  3269. FALSE,
  3270. FALSE );
  3271. return (pLargePage + _pPropStor->OSPageSize()*(ulPage%_pPropStor->OSPagesPerPage()));
  3272. }
  3273. //+---------------------------------------------------------------------------
  3274. //
  3275. // Function: CBackupWid::ComputeOSPageLocations, private
  3276. //
  3277. // Synopsis: Compute the os pages containing the given wid..
  3278. //
  3279. // Arguments: [wid] -- wid in question
  3280. // [cRecsInWid] -- # of records in wid
  3281. // [pFirstPage] -- Ptr where first page's loc should be stored
  3282. // [pLastLage] -- Ptr where last page's loc should be stored
  3283. //
  3284. // Returns: A memory pointer to the specified page.
  3285. //
  3286. // History: 09-June-97 KrishnaN Created
  3287. //
  3288. // Notes: This will be returning read-only pages.
  3289. //
  3290. //----------------------------------------------------------------------------
  3291. inline void CBackupWid::ComputeOSPageLocations(WORKID wid, ULONG cRecsInWid,
  3292. ULONG *pFirstPage, ULONG *pLastPage)
  3293. {
  3294. // Determine the i-th large page and the position of wid in the large page
  3295. ULONG nLargePage = wid / _pPropStor->RecordsPerPage();
  3296. ULONG widInLargePage = wid % _pPropStor->RecordsPerPage();
  3297. *pFirstPage = nLargePage*_pPropStor->OSPagesPerPage() +
  3298. (widInLargePage * _pPropStor->RecordSize() * sizeof(ULONG))/_pPropStor->OSPageSize();
  3299. *pLastPage = nLargePage*_pPropStor->OSPagesPerPage() +
  3300. ((widInLargePage+cRecsInWid)*_pPropStor->RecordSize()*4 - 1)/_pPropStor->OSPageSize();
  3301. }
  3302. //+---------------------------------------------------------------------------
  3303. //
  3304. // Function: CBackupWid::DescribeField, private
  3305. //
  3306. // Synopsis: Obtain the location of the top-level field in the list of pages.
  3307. //
  3308. // Arguments: [FieldToCommit] -- Field to commit. Should not be eFieldNone.
  3309. // [pRec] -- Address of the record being modified.
  3310. // [cPages] -- Number of pages in the list of pages.
  3311. // [ppvPages] -- List of pages.
  3312. // [pulOffset] -- Ptr to location containing offset of field.
  3313. //
  3314. // Returns: i-th page in the ppvPages array that has the field.
  3315. //
  3316. // History: 09-June-97 KrishnaN Created
  3317. //
  3318. //----------------------------------------------------------------------------
  3319. ULONG CBackupWid::DescribeField( EField FieldToCommit,
  3320. COnDiskPropertyRecord const *pRec,
  3321. ULONG cPages,
  3322. void const * const* ppvPages,
  3323. ULONG &ulOffset)
  3324. {
  3325. Win4Assert(eFieldNone != FieldToCommit);
  3326. Win4Assert(!pRec->IsLeanRecord()); // no need to do this for lean records!
  3327. ulOffset = 0xFFFFFFFF;
  3328. void const *pvFieldLoc = 0;
  3329. switch (FieldToCommit)
  3330. {
  3331. case eTopLevelField:
  3332. pvFieldLoc = pRec->GetTopLevelFieldAddress();
  3333. break;
  3334. case eFieldNone:
  3335. default:
  3336. Win4Assert(!"Invalid field to describe!");
  3337. return 0xFFFFFFFF;
  3338. }
  3339. // We have the pointer to the wid field. Now identify the page it belongs to,
  3340. for (ULONG i = 0; i < cPages; i++)
  3341. if (pvFieldLoc >= ppvPages[i] &&
  3342. pvFieldLoc < (PVOID)(PBYTE(ppvPages[i]) + _pPropStor->OSPageSize()))
  3343. break;
  3344. Win4Assert( i < cPages );
  3345. ulOffset = (ULONG) ((PBYTE)pvFieldLoc - (PBYTE)ppvPages[i]);
  3346. return i;
  3347. }
  3348. //+---------------------------------------------------------------------------
  3349. //
  3350. // Member: Constructor for the CPropertyStoreRecovery method
  3351. //
  3352. // Arguments: [propStore] -
  3353. //
  3354. // History: 4-10-96 srikants Created
  3355. //
  3356. //----------------------------------------------------------------------------
  3357. CPropertyStoreRecovery::CPropertyStoreRecovery( CPropertyStore & propStore,
  3358. T_UpdateDoc pfnUpdateCallback,
  3359. void const *pUserData)
  3360. : _propStore(propStore),
  3361. _PropStoreInfo(propStore._PropStoreInfo),
  3362. _wid(1),
  3363. _pRec(0),
  3364. _cRec(0),
  3365. _widMax(0),
  3366. _cTopLevel(0),
  3367. _cInconsistencies(0),
  3368. _cForceFreed(0),
  3369. _pageTable( 10 ),
  3370. _fnUpdateCallback( pfnUpdateCallback ),
  3371. _pUserData( pUserData )
  3372. {
  3373. _pPhysStore = propStore._xPhysStore.GetPointer();
  3374. _cRecPerPage = _PropStoreInfo.RecordsPerPage();
  3375. _aFreeBlocks = new WORKID[ _PropStoreInfo.RecordsPerPage() + 1 ];
  3376. RtlZeroMemory( _aFreeBlocks,
  3377. (_PropStoreInfo.RecordsPerPage()+1) * sizeof (WORKID) );
  3378. // Open the backup stream in read mode for recovery
  3379. WORKID wid = _PropStoreInfo.WorkId();
  3380. if (widInvalid != wid)
  3381. {
  3382. SStorageObject xobj( _propStore.GetStorage().QueryObject( wid ) );
  3383. _xPSBkpStrm.Set(_propStore.GetCiStorage().OpenExistingPSBkpStreamForRecovery(
  3384. xobj.GetObj(), propStore.GetStoreLevel() ));
  3385. }
  3386. }
  3387. //+---------------------------------------------------------------------------
  3388. //
  3389. // Member: CPropertyStoreRecovery::~CPropertyStoreRecovery, public
  3390. //
  3391. // Synopsis: Destructor of the CPropertyStoreRecovery.
  3392. //
  3393. // Notes: In a successful recovery, the _aFreeBlocks will be transferred
  3394. // to the CPropertyStore class.
  3395. //
  3396. // History: 10 May 96 AlanW Created.
  3397. //
  3398. //----------------------------------------------------------------------------
  3399. CPropertyStoreRecovery::~CPropertyStoreRecovery()
  3400. {
  3401. delete _aFreeBlocks;
  3402. }
  3403. //+---------------------------------------------------------------------------
  3404. //
  3405. // Member: CPropertyStoreRecovery::AddToFreeList, private
  3406. //
  3407. // Synopsis: Add a free record to a free list.
  3408. //
  3409. // Arguments: [widFree] -- Workid of record to free
  3410. // [cFree] -- Number of records in freed chunk
  3411. // [precFree] -- On-disk Property record
  3412. // [widListHead] -- Start of free list
  3413. //
  3414. // Returns: WORKID - new start of list
  3415. //
  3416. // Notes: All blocks in the free list passed are assumed to be of
  3417. // the same length.
  3418. //
  3419. // History: 01 May 96 AlanW Created.
  3420. //
  3421. //----------------------------------------------------------------------------
  3422. WORKID CPropertyStoreRecovery::AddToFreeList( WORKID widFree,
  3423. ULONG cFree,
  3424. COnDiskPropertyRecord * precFree,
  3425. WORKID widListHead )
  3426. {
  3427. if ( widListHead != 0 )
  3428. {
  3429. CBorrowed Borrowed( *_pPhysStore,
  3430. widListHead,
  3431. _PropStoreInfo.RecordsPerPage(),
  3432. _PropStoreInfo.RecordSize() );
  3433. COnDiskPropertyRecord * prec = Borrowed.Get();
  3434. Win4Assert( prec->CountRecords() == cFree );
  3435. Win4Assert( prec->GetNextFreeRecord() == 0 ||
  3436. prec->GetNextFreeSize() == cFree );
  3437. prec->SetPreviousFreeRecord( widFree );
  3438. }
  3439. //
  3440. // Insert new record at beginning of list
  3441. //
  3442. if (eLean == _PropStoreInfo.GetRecordFormat())
  3443. precFree->MakeLeanFreeRecord( cFree,
  3444. widListHead,
  3445. widListHead==0? 0 : cFree,
  3446. _PropStoreInfo.RecordSize() );
  3447. else
  3448. precFree->MakeNormalFreeRecord( cFree,
  3449. widListHead,
  3450. widListHead==0? 0 : cFree,
  3451. _PropStoreInfo.RecordSize() );
  3452. return widFree;
  3453. }
  3454. //+---------------------------------------------------------------------------
  3455. //
  3456. // Member: CPropertyStoreRecovery::SetFree
  3457. //
  3458. // Synopsis: Marks the current record as free for _cRec length of
  3459. // records.
  3460. //
  3461. // History: 4-10-96 srikants Created
  3462. //
  3463. //----------------------------------------------------------------------------
  3464. void CPropertyStoreRecovery::SetFree()
  3465. {
  3466. Win4Assert( _pRec );
  3467. Win4Assert( _cRec <= _PropStoreInfo.RecordsPerPage() );
  3468. _aFreeBlocks[_cRec] = AddToFreeList( _wid,
  3469. _cRec,
  3470. _pRec,
  3471. _aFreeBlocks[_cRec] );
  3472. }
  3473. //+---------------------------------------------------------------------------
  3474. //
  3475. // Member: CPropertyStoreRecovery::CheckOverflowChain
  3476. //
  3477. // Synopsis: Verify and fix the forward length links.
  3478. //
  3479. // History: 4-10-96 srikants Created
  3480. //
  3481. //----------------------------------------------------------------------------
  3482. BOOL CPropertyStoreRecovery::CheckOverflowChain()
  3483. {
  3484. Win4Assert( _pRec->IsNormalTopLevel() );
  3485. CBorrowed Borrowed( *_pPhysStore,
  3486. _wid,
  3487. _PropStoreInfo.RecordsPerPage(),
  3488. _PropStoreInfo.RecordSize() );
  3489. COnDiskPropertyRecord * prec = Borrowed.Get();
  3490. ULONG cOverflowBlocks = 0;
  3491. BOOL fIsConsistent = TRUE;
  3492. for ( WORKID widOvfl = prec->OverflowBlock();
  3493. 0 != widOvfl;
  3494. widOvfl = prec->OverflowBlock() )
  3495. {
  3496. if ( widOvfl > _widMax )
  3497. {
  3498. ciDebugOut(( DEB_ERROR,
  3499. "widOvfl (0x%X) > widMax (0x%X)\n",
  3500. widOvfl, _widMax ));
  3501. fIsConsistent = FALSE;
  3502. break;
  3503. }
  3504. Borrowed.Release();
  3505. Borrowed.Set( widOvfl );
  3506. prec = Borrowed.Get();
  3507. //
  3508. // If this is not marked as an overflow record, what should we do?
  3509. //
  3510. if ( !prec->IsOverflow() )
  3511. {
  3512. ciDebugOut(( DEB_WARN, "Wid (0x%X) should be an overflow record, but not!\n",
  3513. widOvfl ));
  3514. fIsConsistent = FALSE;
  3515. break;
  3516. }
  3517. //
  3518. // Check the validity of the toplevel pointers.
  3519. //
  3520. if ( prec->ToplevelBlock() != _wid )
  3521. {
  3522. ciDebugOut(( DEB_WARN, "Changing the toplevel wid of (0x%X) from (0x%X) to (0x%X)\n",
  3523. widOvfl, prec->ToplevelBlock(), _wid ));
  3524. fIsConsistent = FALSE;
  3525. break;
  3526. }
  3527. cOverflowBlocks++;
  3528. }
  3529. if ( fIsConsistent )
  3530. {
  3531. fIsConsistent = cOverflowBlocks == _pRec->GetOverflowChainLength();
  3532. if (!fIsConsistent)
  3533. {
  3534. ciDebugOut(( DEB_WARN, "Overflow length for toplevel wid %d(0x%x) is %d. Expected %d\n",
  3535. _wid, _wid, cOverflowBlocks, _pRec->GetOverflowChainLength() ));
  3536. }
  3537. }
  3538. return fIsConsistent;
  3539. }
  3540. //+---------------------------------------------------------------------------
  3541. //
  3542. // Member: CPropertyStoreRecovery::FreeChain, private
  3543. //
  3544. // Synopsis: Free an overflow record chain to the free list
  3545. //
  3546. // History: 02 May 96 AlanW Added header
  3547. //
  3548. //----------------------------------------------------------------------------
  3549. void CPropertyStoreRecovery::FreeChain( )
  3550. {
  3551. Win4Assert(!_pRec->IsLeanRecord());
  3552. if ( !_pRec->IsTopLevel() )
  3553. {
  3554. ciDebugOut(( DEB_WARN, "Ignore chain with non-top-level wid (0x%X)\n", _wid ));
  3555. return;
  3556. }
  3557. ciDebugOut(( DEB_WARN, "Freeing chain with wid (0x%X)\n", _wid ));
  3558. BOOL fOverflow = FALSE;
  3559. WORKID wid = _wid;
  3560. CBorrowed Borrowed( *_pPhysStore,
  3561. _PropStoreInfo.RecordsPerPage(),
  3562. _PropStoreInfo.RecordSize() );
  3563. while ( 0 != wid && wid <= _widMax )
  3564. {
  3565. Borrowed.Release();
  3566. Borrowed.Set(wid);
  3567. COnDiskPropertyRecord * prec = Borrowed.Get();
  3568. if ( fOverflow )
  3569. {
  3570. if ( !prec->IsOverflow() )
  3571. {
  3572. ciDebugOut(( DEB_WARN,
  3573. "Ignore chain with non-overflow wid (0x%X)\n",
  3574. _wid ));
  3575. break;
  3576. }
  3577. }
  3578. else
  3579. fOverflow = TRUE;
  3580. ciDebugOut(( DEB_PROPSTORE,
  3581. "Force freeing up wid (0x%X) prec (0x%X)\n",
  3582. wid, prec ));
  3583. WORKID widNext = prec->OverflowBlock();
  3584. ULONG cRec = prec->CountRecords();
  3585. if ( ! prec->IsValidLength( wid, _cRecPerPage ) )
  3586. {
  3587. ciDebugOut(( DEB_WARN,
  3588. "Ignore chain with invalid block size (0x%X)\n",
  3589. _wid ));
  3590. cRec = 1;
  3591. widNext = 0;
  3592. }
  3593. _aFreeBlocks[cRec] = AddToFreeList( wid, cRec, prec, _aFreeBlocks[cRec] );
  3594. _cForceFreed++;
  3595. wid = widNext;
  3596. }
  3597. _cTopLevel--;
  3598. }
  3599. //+---------------------------------------------------------------------------
  3600. //
  3601. // Member: CPropertyStoreRecovery::Pass0
  3602. //
  3603. // Synopsis: Restore sections of the property store from the backup file.
  3604. //
  3605. // History: 12-Jun-97 KrishnaN Created
  3606. //
  3607. //----------------------------------------------------------------------------
  3608. void CPropertyStoreRecovery::Pass0()
  3609. {
  3610. if (!_xPSBkpStrm->IsOpenForRecovery())
  3611. {
  3612. ciDebugOut((DEB_WARN, "PROPSTORE: Backup file is not available for recovery.\n"));
  3613. return;
  3614. }
  3615. ULONG cPages = _xPSBkpStrm->Pages();
  3616. _pageTable.Reset(cPages);
  3617. // Remember that the property store could have been moved from a different architecture.
  3618. ULONG cPageSize = _xPSBkpStrm->PageSize();
  3619. if (0 != COMMON_PAGE_SIZE%cPageSize)
  3620. {
  3621. ciDebugOut((DEB_WARN, "PROPSTORE: Backup file's page size (%d) is not compatible with "
  3622. "the large page size (%d) used by prop store.\n",
  3623. cPageSize, COMMON_PAGE_SIZE));
  3624. return;
  3625. }
  3626. ULONG cCustomPagesPerLargePage = COMMON_PAGE_SIZE/cPageSize;
  3627. // Read each page from the backup and graft those pages at the appropriate location
  3628. // in the property store.
  3629. for (ULONG i = 0; i < cPages; i++)
  3630. {
  3631. ULONG ulLoc = _xPSBkpStrm->GetPageLocation(i);
  3632. if (invalidPage == ulLoc)
  3633. {
  3634. Win4Assert(!"How did we get an invalid page in backup!");
  3635. ciDebugOut((DEB_PSBACKUP, "Page %d in backup is invalid.\n", i));
  3636. continue;
  3637. }
  3638. _pageTable.AddEntry(ulLoc);
  3639. // takes care of borrowing/returning large page and actual copy of page
  3640. // from backup to primary
  3641. CGraftPage graftPage(i, ulLoc, cPageSize, cCustomPagesPerLargePage,
  3642. _pPhysStore, _xPSBkpStrm.GetPointer());
  3643. }
  3644. // Close it now. We will open it again later, most likely for backup.
  3645. _xPSBkpStrm->Close();
  3646. }
  3647. //+---------------------------------------------------------------------------
  3648. //
  3649. // Member: CPropertyStoreRecovery::Pass1
  3650. //
  3651. // Synopsis: Perform the pass1 recovery operation.
  3652. //
  3653. // History: 4-10-96 srikants Created
  3654. // 6-16-97 KrishnaN Modified to enumerate wids in backed
  3655. // up pages as part of the pass.
  3656. //
  3657. //----------------------------------------------------------------------------
  3658. void CPropertyStoreRecovery::Pass1()
  3659. {
  3660. Win4Assert(_fnUpdateCallback);
  3661. CPageHashTable widsRefiltered(_xPSBkpStrm->Pages()+1);
  3662. //
  3663. // Extract the pidLastSeenTime info.
  3664. //
  3665. CPropDesc const * pFTDesc =
  3666. _PropStoreInfo.GetDescription( pidLastSeenTime );
  3667. FILETIME ftLastSeen;
  3668. RtlZeroMemory( &ftLastSeen, sizeof(ftLastSeen) );
  3669. CStorageVariant varFtLast( ftLastSeen );
  3670. if ( _pPhysStore->PageSize() > 0 )
  3671. {
  3672. ULONG cCustomPagesPerLargePage = COMMON_PAGE_SIZE/_xPSBkpStrm->PageSize();
  3673. ULONG cLargePages = (_PropStoreInfo.MaxWorkId() / _PropStoreInfo.RecordsPerPage()) + 1;
  3674. Win4Assert( cLargePages <= _pPhysStore->PageSize() / (COMMON_PAGE_SIZE / CI_PAGE_SIZE) );
  3675. //
  3676. // Loop through and rebuild free list and max workid.
  3677. //
  3678. CBorrowed Borrowed( *_pPhysStore, _PropStoreInfo.RecordsPerPage(),
  3679. _PropStoreInfo.RecordSize() );
  3680. while ( TRUE )
  3681. {
  3682. if ( _propStore._fAbort )
  3683. {
  3684. ciDebugOut(( DEB_WARN,
  3685. "Stopping Restore because of abort\n" ));
  3686. THROW( CException(STATUS_TOO_LATE) );
  3687. }
  3688. //
  3689. // End of file?
  3690. //
  3691. ULONG nLargePage = _wid / _PropStoreInfo.RecordsPerPage();
  3692. if ( nLargePage >= cLargePages )
  3693. break;
  3694. //
  3695. // Valid record.
  3696. //
  3697. Borrowed.Release();
  3698. Borrowed.Set( _wid );
  3699. CLockRecordForWrite recLock( _propStore, _wid );
  3700. _pRec = Borrowed.Get();
  3701. //
  3702. // If this wid falls in one of the backed up pages we should
  3703. // enumerate it. Wids in backup should be scheduled for
  3704. // re-filtering and if necessary, deleted.
  3705. //
  3706. // compute i-th custom page based on other params
  3707. ULONG nCustomPage = nLargePage*cCustomPagesPerLargePage +
  3708. (_PropStoreInfo.RecordSize()*sizeof(ULONG)*(_wid%_PropStoreInfo.RecordsPerPage())) /
  3709. _xPSBkpStrm->PageSize();
  3710. if ( _pRec->IsValidInUseRecord(_wid, _cRecPerPage) )
  3711. {
  3712. WORKID widToplevel;
  3713. _cRec = _pRec->CountRecords();
  3714. //
  3715. // If this is a topLevel record, set the pidLastSeenTime to 0.
  3716. //
  3717. if ( _pRec->IsTopLevel() )
  3718. {
  3719. widToplevel = _wid;
  3720. //
  3721. // Set the pidLastSeenTime to 0
  3722. //
  3723. if ( pFTDesc )
  3724. {
  3725. // We should be doing this only in the store containing pidLastSeenTime.
  3726. Win4Assert(PRIMARY_STORE == _PropStoreInfo.GetStoreLevel());
  3727. _pRec->WriteFixed( pFTDesc->Ordinal(),
  3728. pFTDesc->Mask(),
  3729. pFTDesc->Offset(),
  3730. pFTDesc->Type(),
  3731. _PropStoreInfo.CountProps(),
  3732. varFtLast );
  3733. }
  3734. _cTopLevel++;
  3735. }
  3736. else
  3737. {
  3738. Win4Assert( eNormal == _PropStoreInfo.GetRecordFormat() );
  3739. widToplevel = _pRec->ToplevelBlock();
  3740. Win4Assert( _pRec->IsOverflow() );
  3741. ciDebugOut(( DEB_PROPSTORE,
  3742. "PROPSTORE: Overflow Record 0x%X\n",
  3743. _wid ));
  3744. }
  3745. _widMax = _wid+_cRec-1;
  3746. // If this wid is in a backed up page we should
  3747. // schedule the top-level wid for re-filtering.
  3748. if (_pageTable.LookUp(nCustomPage))
  3749. {
  3750. ULONG ul;
  3751. // Refilter it if it has not already been
  3752. if (!widsRefiltered.LookUp((ULONG)widToplevel, ul))
  3753. {
  3754. _fnUpdateCallback(widToplevel, FALSE, _pUserData);
  3755. ciDebugOut((DEB_PSBACKUP, "Wid %d (0x%x) in custom page %d scheduled for re-filtering.\n",
  3756. widToplevel, widToplevel, nCustomPage));
  3757. widsRefiltered.AddEntry((ULONG)widToplevel, 0);
  3758. }
  3759. }
  3760. }
  3761. else
  3762. {
  3763. // This assert could go off in case of file corruption.
  3764. Win4Assert(!_pRec->IsInUse());
  3765. // For the Normal Format records:
  3766. // If this wid is in a backed up page and points to the wid
  3767. // occupying its place in the primary, we should delete the
  3768. // occupying wid.
  3769. //
  3770. // For the Lean Format records:
  3771. // We do not store the top-level wid of the occupying wid for lean records.
  3772. // Each lean record is only "one record" long. So every displaced
  3773. // free record was displaced by a newly indexed document. Since we
  3774. // delete newly indexed docs as part of restore, we need to delete
  3775. // _wid if it is found in the backup.
  3776. //
  3777. if (_pageTable.LookUp(nCustomPage))
  3778. {
  3779. if (eNormal == _PropStoreInfo.GetRecordFormat() && _pRec->ToplevelBlock() == _wid)
  3780. {
  3781. _pRec->ClearToplevelField();
  3782. ciDebugOut((DEB_PSBACKUP, "Wid %d (0x%x) in page %d scheduled for deletion\n",
  3783. _wid, _wid, nCustomPage));
  3784. _fnUpdateCallback(_wid, TRUE, _pUserData);
  3785. }
  3786. else if (eLean == _PropStoreInfo.GetRecordFormat())
  3787. {
  3788. ciDebugOut((DEB_PSBACKUP, "Wid %d (0x%x) in page %d scheduled for deletion\n",
  3789. _wid, _wid, nCustomPage));
  3790. _fnUpdateCallback(_wid, TRUE, _pUserData);
  3791. }
  3792. }
  3793. if ( _pRec->IsFreeRecord() &&
  3794. _pRec->IsValidLength(_wid, _cRecPerPage) )
  3795. {
  3796. _cRec = _pRec->CountRecords();
  3797. }
  3798. else
  3799. {
  3800. _cRec = 1;
  3801. }
  3802. //
  3803. // coalesce any adjacent free blocks.
  3804. //
  3805. CBorrowed BorrowedNext( *_pPhysStore,
  3806. _PropStoreInfo.RecordsPerPage(),
  3807. _PropStoreInfo.RecordSize() );
  3808. WORKID widNext = _wid + _cRec;
  3809. BOOL fCoalesced = FALSE;
  3810. #if CIDBG
  3811. WCHAR wszDeletedWids[4096];
  3812. wszDeletedWids[0] = 0;
  3813. short cWritten = 0;
  3814. #endif // CIDBG
  3815. while ( widNext % _cRecPerPage != 0 )
  3816. {
  3817. BorrowedNext.Set( widNext );
  3818. COnDiskPropertyRecord * precNext = BorrowedNext.Get();
  3819. // Schedule the wid for deletion or re-filtering as appropriate
  3820. if (precNext->IsInUse())
  3821. break;
  3822. // compute i-th custom page based on other params
  3823. nCustomPage = nLargePage*cCustomPagesPerLargePage +
  3824. (_PropStoreInfo.RecordSize()*sizeof(DWORD)*(widNext%_PropStoreInfo.RecordsPerPage())) /
  3825. _xPSBkpStrm->PageSize();
  3826. if (_pageTable.LookUp(nCustomPage))
  3827. {
  3828. #if CIDBG == 1
  3829. BOOL fDump = TRUE;
  3830. #endif // CIDBG
  3831. if (eNormal == _PropStoreInfo.GetRecordFormat() && precNext->ToplevelBlock() == widNext)
  3832. {
  3833. precNext->ClearToplevelField();
  3834. _fnUpdateCallback(widNext, TRUE, _pUserData);
  3835. }
  3836. else if (eLean == _PropStoreInfo.GetRecordFormat())
  3837. {
  3838. _fnUpdateCallback(widNext, TRUE, _pUserData);
  3839. }
  3840. #if CIDBG == 1
  3841. else
  3842. fDump = FALSE;
  3843. if (fDump)
  3844. {
  3845. // Writing individual wids to debugout is
  3846. // overwhelming it. So batch up a few at a time.
  3847. WCHAR szbuff[12];
  3848. swprintf(szbuff, L"%d,", widNext);
  3849. wcscat(wszDeletedWids, szbuff);
  3850. cWritten++;
  3851. if (cWritten % 32 == 0)
  3852. {
  3853. ciDebugOut((DEB_PSBACKUP, "Scheduled the following %d wids for deletion\n", cWritten));
  3854. ciDebugOut((DEB_PSBACKUP, "%ws\n", wszDeletedWids));
  3855. cWritten = 0;
  3856. wszDeletedWids[0] = 0;
  3857. }
  3858. }
  3859. #endif // CIDBG
  3860. }
  3861. if ( precNext->IsFreeRecord() &&
  3862. precNext->IsValidLength(_wid, _cRecPerPage) )
  3863. _cRec += precNext->CountRecords();
  3864. else if ( !precNext->IsInUse() )
  3865. _cRec += 1;
  3866. else
  3867. break;
  3868. fCoalesced = TRUE;
  3869. widNext = _wid + _cRec;
  3870. BorrowedNext.Release();
  3871. }
  3872. #if CIDBG
  3873. if (cWritten > 0 && cWritten < 32)
  3874. {
  3875. ciDebugOut((DEB_PSBACKUP, "Scheduled the following %d wids for deletion\n", cWritten));
  3876. ciDebugOut((DEB_PSBACKUP, "%ws\n", wszDeletedWids));
  3877. cWritten = 0;
  3878. wszDeletedWids[0] = 0;
  3879. }
  3880. #endif // CIDBG
  3881. if (fCoalesced)
  3882. {
  3883. ciDebugOut(( DEB_PROPSTORE,
  3884. "PROPSTORE: Coalesced free records 0x%X(%d)\n",
  3885. _wid, _cRec ));
  3886. }
  3887. SetFree();
  3888. }
  3889. _wid += _cRec;
  3890. }
  3891. }
  3892. }
  3893. //+---------------------------------------------------------------------------
  3894. //
  3895. // Member: CPropertyStoreRecovery::Pass2
  3896. //
  3897. // Synopsis: Pass2 of the recovery phase.
  3898. //
  3899. // History: 4-10-96 srikants Created
  3900. //
  3901. // NTRAID#DB-NTBUG9-84467-2000/07/31-dlee Indexing Service property store doesn't checked for orphaned overflow chains
  3902. //
  3903. //----------------------------------------------------------------------------
  3904. void CPropertyStoreRecovery::Pass2()
  3905. {
  3906. ciDebugOut(( DEB_WARN, "PROPSTORE: Recovery Pass2\n" ));
  3907. if ( _pPhysStore->PageSize() > 0 )
  3908. {
  3909. ULONG cLargePages = _pPhysStore->PageSize() / (COMMON_PAGE_SIZE / CI_PAGE_SIZE);
  3910. //
  3911. // Check each top-level record for consistency
  3912. //
  3913. CBorrowed Borrowed( *_pPhysStore, _PropStoreInfo.RecordsPerPage(),
  3914. _PropStoreInfo.RecordSize() );
  3915. _wid = 1;
  3916. while ( TRUE )
  3917. {
  3918. if ( _propStore._fAbort )
  3919. {
  3920. ciDebugOut(( DEB_WARN,
  3921. "Stopping Restore because of abort\n" ));
  3922. THROW( CException(STATUS_TOO_LATE) );
  3923. }
  3924. //
  3925. // End of file?
  3926. //
  3927. ULONG nLargePage = _wid / _PropStoreInfo.RecordsPerPage();
  3928. if ( nLargePage >= cLargePages )
  3929. break;
  3930. //
  3931. // Valid record.
  3932. //
  3933. Borrowed.Release();
  3934. Borrowed.Set( _wid );
  3935. CLockRecordForWrite recLock( _propStore, _wid );
  3936. _pRec = Borrowed.Get();
  3937. _cRec = _pRec->CountRecords();
  3938. // There is no overflow chaining involved in a property store
  3939. // made up of lean records, so check that only for normal records.
  3940. if ( _pRec->IsNormalTopLevel() )
  3941. {
  3942. if ( !CheckOverflowChain() )
  3943. {
  3944. //
  3945. // Free up this workid.
  3946. //
  3947. _cInconsistencies++;
  3948. FreeChain();
  3949. }
  3950. }
  3951. _wid += _cRec;
  3952. }
  3953. }
  3954. }
  3955. //+---------------------------------------------------------------------------
  3956. //
  3957. // Member: CPropertyStoreRecovery::Complete
  3958. //
  3959. // Synopsis: Complete the recovery operation.
  3960. //
  3961. // History: 4-10-96 srikants Created
  3962. //
  3963. //----------------------------------------------------------------------------
  3964. void CPropertyStoreRecovery::Complete()
  3965. {
  3966. //
  3967. // Now chain all the free lists together and remove entries that
  3968. // are beyond _widMax.
  3969. //
  3970. WORKID widFreeListHead = 0;
  3971. WORKID widFreeListTail = 0;
  3972. COnDiskPropertyRecord * prec = 0;
  3973. COnDiskPropertyRecord * precPrev = 0;
  3974. CBorrowed Borrowed( *_pPhysStore,
  3975. _PropStoreInfo.RecordsPerPage(),
  3976. _PropStoreInfo.RecordSize() );
  3977. CBorrowed BorrowedPrev( *_pPhysStore,
  3978. _PropStoreInfo.RecordsPerPage(),
  3979. _PropStoreInfo.RecordSize() );
  3980. for (unsigned cSize = _PropStoreInfo.RecordsPerPage(); cSize >= 1; cSize-- )
  3981. {
  3982. WORKID wid = _aFreeBlocks[ cSize ];
  3983. // Skip to the first valid block in the free list
  3984. while ( wid != 0 && wid > _widMax )
  3985. {
  3986. Borrowed.Set( wid );
  3987. prec = Borrowed.Get();
  3988. ciDebugOut(( DEB_PROPSTORE,
  3989. " Tossing free entry %x(%d)\n",
  3990. wid, prec->CountRecords() ));
  3991. wid = prec->GetNextFreeRecord();
  3992. prec->ClearAll( _PropStoreInfo.RecordSize() );
  3993. Borrowed.Release();
  3994. }
  3995. _aFreeBlocks[ cSize ] = wid;
  3996. if ( wid == 0 )
  3997. continue;
  3998. Borrowed.Set( wid );
  3999. prec = Borrowed.Get();
  4000. //
  4001. // Found a valid wid. Splice this list to the previous list
  4002. //
  4003. if (widFreeListHead == 0)
  4004. widFreeListHead = wid;
  4005. else
  4006. {
  4007. Win4Assert( widFreeListTail != 0 &&
  4008. precPrev != 0 );
  4009. prec->SetPreviousFreeRecord( widFreeListTail );
  4010. precPrev->SetNextFree( wid, cSize );
  4011. }
  4012. WORKID widPrev = widFreeListTail;
  4013. widFreeListTail = wid;
  4014. while (wid != 0)
  4015. {
  4016. BorrowedPrev.Release();
  4017. BorrowedPrev = Borrowed;
  4018. precPrev = prec;
  4019. Borrowed.Set( wid );
  4020. prec = Borrowed.Get();
  4021. while ( wid != 0 && wid > _widMax )
  4022. {
  4023. //
  4024. // Remove an entry > widMax from the list
  4025. //
  4026. ciDebugOut(( DEB_PROPSTORE,
  4027. " Tossing free entry %x(%d)\n",
  4028. wid, prec->CountRecords() ));
  4029. wid = prec->GetNextFreeRecord();
  4030. precPrev->SetNextFree( wid, prec->GetNextFreeSize() );
  4031. prec->ClearAll( _PropStoreInfo.RecordSize() );
  4032. Borrowed.Release();
  4033. if (wid != 0)
  4034. {
  4035. Borrowed.Set( wid );
  4036. prec = Borrowed.Get();
  4037. }
  4038. }
  4039. if (wid == 0)
  4040. continue;
  4041. prec->SetPreviousFreeRecord( widPrev );
  4042. widFreeListTail = wid;
  4043. widPrev = wid;
  4044. wid = prec->GetNextFreeRecord();
  4045. }
  4046. BorrowedPrev.Release();
  4047. BorrowedPrev = Borrowed;
  4048. precPrev = prec;
  4049. }
  4050. ciDebugOut(( DEB_WARN,
  4051. "PROPSTORE: 0x%x top level, widFreeList = 0x%x : 0x%x, widMax = 0x%x\n",
  4052. _cTopLevel, widFreeListHead, widFreeListTail, _widMax ));
  4053. if ( _cInconsistencies )
  4054. {
  4055. ciDebugOut(( DEB_WARN,
  4056. "PROPSTORE: Inconsistencies= 0x%X; Records forcefully freed= 0x%X\n",
  4057. _cInconsistencies, _cForceFreed ));
  4058. }
  4059. _PropStoreInfo.SetRecordsInUse( _cTopLevel );
  4060. _PropStoreInfo.SetMaxWorkId( _widMax );
  4061. _PropStoreInfo.SetFreeListHead( widFreeListHead );
  4062. _PropStoreInfo.SetFreeListTail( widFreeListTail );
  4063. _propStore._aFreeBlocks = _aFreeBlocks;
  4064. _aFreeBlocks = 0;
  4065. // Don't flush here. We will flush both the prop stores from the manager
  4066. // after LongInit has been performed on both.
  4067. }
  4068. //+---------------------------------------------------------------------------
  4069. //
  4070. // Member: CPropertyStoreRecovery::DoRecovery
  4071. //
  4072. // Synopsis: Do the recovery operation
  4073. //
  4074. // History: 4-10-96 srikants Created
  4075. // 6-13-97 KrishnaN Added pass 0 to repair propstore from bkp.
  4076. //
  4077. //----------------------------------------------------------------------------
  4078. void CPropertyStoreRecovery::DoRecovery()
  4079. {
  4080. NTSTATUS status = STATUS_SUCCESS;
  4081. TRY
  4082. {
  4083. CLock mtxLock( _propStore._mtxWrite );
  4084. Pass0();
  4085. Pass1();
  4086. Pass2();
  4087. Complete();
  4088. }
  4089. CATCH( CException, e )
  4090. {
  4091. ciDebugOut(( DEB_ERROR, "Error 0x%X doing PropertyStore restore\n",
  4092. e.GetErrorCode() ));
  4093. status = e.GetErrorCode();
  4094. }
  4095. END_CATCH
  4096. if ( STATUS_SUCCESS != status )
  4097. {
  4098. if ( STATUS_ACCESS_VIOLATION == status )
  4099. {
  4100. Win4Assert ( 0 != _propStore._pStorage );
  4101. _propStore._pStorage->ReportCorruptComponent( L"PropertyCache1" );
  4102. status = CI_CORRUPT_CATALOG;
  4103. }
  4104. THROW( CException( status ) );
  4105. }
  4106. }