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.

744 lines
22 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1995
  5. //
  6. // File: PropRec.cxx
  7. //
  8. // Contents: Record format for persistent property store
  9. //
  10. // Classes: CPropertyRecord
  11. //
  12. // History: 28-Dec-19 KyleP Created
  13. //
  14. //----------------------------------------------------------------------------
  15. #include <pch.cxx>
  16. #pragma hdrstop
  17. #include <stgvar.hxx>
  18. #include <propvar.h>
  19. #include <propstm.hxx>
  20. #include <proprec.hxx>
  21. #include <eventlog.hxx>
  22. class CCTMABufferAllocator : public PMemoryAllocator
  23. {
  24. public:
  25. void *Allocate(ULONG cbSize)
  26. {
  27. return CoTaskMemAlloc( cbSize );
  28. }
  29. void Free(void *pv)
  30. {
  31. CoTaskMemFree( pv );
  32. }
  33. };
  34. class CNonAlignAllocator : public PMemoryAllocator
  35. {
  36. public:
  37. inline CNonAlignAllocator(ULONG cbBuffer, VOID *pvBuffer)
  38. {
  39. _cbFree = cbBuffer;
  40. _pvCur = _pvBuffer = pvBuffer;
  41. }
  42. VOID *Allocate(ULONG cb)
  43. {
  44. VOID *pv;
  45. cb = (cb + sizeof(LONGLONG) - 1) & ~(sizeof(LONGLONG) - 1);
  46. if (cb > _cbFree)
  47. {
  48. return(NULL);
  49. }
  50. pv = _pvCur;
  51. _pvCur = (BYTE *) _pvCur + cb;
  52. _cbFree -= cb;
  53. return(pv);
  54. }
  55. VOID Free(VOID *pv) { }
  56. inline ULONG GetFreeSize(VOID) { return(_cbFree); }
  57. private:
  58. ULONG _cbFree;
  59. VOID *_pvCur;
  60. VOID *_pvBuffer;
  61. };
  62. //+---------------------------------------------------------------------------
  63. //
  64. // Member: COnDiskPropertyRecord::ReadFixed, public
  65. //
  66. // Synopsis: Read fixed-length property
  67. //
  68. // Arguments: [Ordinal] -- Ordinal of property to locate.
  69. // [mask] -- Bitmask of ordinal. Pre-computed for efficiency.
  70. // [oStart] -- Offset to start of specific fixed property.
  71. // [cTotal] -- Total number of properties in record.
  72. // [Type] -- Data type of value
  73. // [var] -- Value returned here.
  74. // [pbExtra] -- Indirect data stored here
  75. // [pcbExtra] -- On input, size of [pbExtra]. On output,
  76. // amount used.
  77. //
  78. // History: 27-Dec-95 KyleP Created.
  79. //
  80. //----------------------------------------------------------------------------
  81. void COnDiskPropertyRecord::ReadFixed( ULONG Ordinal,
  82. ULONG Mask,
  83. ULONG oStart,
  84. ULONG cTotal,
  85. ULONG Type,
  86. PROPVARIANT & var,
  87. BYTE * pbExtra,
  88. unsigned * pcbExtra )
  89. {
  90. if ( !IsStored( Ordinal, Mask ) )
  91. {
  92. var.vt = VT_EMPTY;
  93. *pcbExtra = 0;
  94. }
  95. else
  96. {
  97. var.vt = (USHORT)Type;
  98. //
  99. // Start is after existance bitmap
  100. //
  101. ULONG const * pulStart = &_aul[ (cTotal-1) / 16 + 1 + oStart];
  102. switch ( Type )
  103. {
  104. case VT_I1:
  105. case VT_UI1:
  106. var.bVal = *(BYTE *)pulStart;
  107. *pcbExtra = 0;
  108. break;
  109. case VT_I2:
  110. case VT_UI2:
  111. case VT_BOOL:
  112. var.uiVal = *(USHORT *)pulStart;
  113. *pcbExtra = 0;
  114. break;
  115. case VT_I4:
  116. case VT_UI4:
  117. case VT_R4:
  118. case VT_ERROR:
  119. var.ulVal = *pulStart;
  120. *pcbExtra = 0;
  121. break;
  122. case VT_I8:
  123. case VT_UI8:
  124. case VT_R8:
  125. case VT_CY:
  126. case VT_DATE:
  127. case VT_FILETIME:
  128. RtlCopyMemory( &var.hVal, pulStart, sizeof(var.hVal) );
  129. *pcbExtra = 0;
  130. break;
  131. case VT_CLSID:
  132. if ( *pcbExtra < sizeof(CLSID) )
  133. {
  134. *pcbExtra = sizeof(CLSID);
  135. return;
  136. }
  137. if ( *pcbExtra == 0xFFFFFFFF )
  138. var.puuid = (CLSID *)CoTaskMemAlloc( sizeof(CLSID) );
  139. else
  140. {
  141. *pcbExtra = sizeof(CLSID);
  142. var.puuid = (CLSID *)pbExtra;
  143. }
  144. RtlCopyMemory( var.puuid, pulStart, sizeof(CLSID) );
  145. break;
  146. default:
  147. ciDebugOut(( DEB_ERROR, "PROPSTORE: Reading invalid fixed type %d.\n", Type ));
  148. Win4Assert( !"How did I get here?" );
  149. ReportCorruptComponent( L"PropertyRecord1" );
  150. THROW( CException( CI_CORRUPT_DATABASE ) );
  151. break;
  152. }
  153. }
  154. }
  155. //+---------------------------------------------------------------------------
  156. //
  157. // Member: COnDiskPropertyRecord::ReadVariable, public
  158. //
  159. // Synopsis: Read variable-length property
  160. //
  161. // Arguments: [Ordinal] -- Ordinal of property to locate.
  162. // [mask] -- Bitmask of ordinal. Pre-computed for efficiency.
  163. // [oStart] -- Offset to start of variable storage area.
  164. // [cTotal] -- Total number of properties in record.
  165. // [cFixed] -- Count of fixed length properties
  166. // [var] -- Value returned here.
  167. // [pbExtra] -- Indirect data stored here
  168. // [pcbExtra] -- On input, size of [pbExtra]. On output,
  169. // amount used.
  170. //
  171. // Returns: FALSE if value must be stored in overflow record.
  172. //
  173. // History: 27-Dec-95 KyleP Created.
  174. //
  175. //----------------------------------------------------------------------------
  176. BOOL COnDiskPropertyRecord::ReadVariable( ULONG Ordinal,
  177. ULONG Mask,
  178. ULONG oStart,
  179. ULONG cTotal,
  180. ULONG cFixed,
  181. PROPVARIANT & var,
  182. BYTE * pbExtra,
  183. unsigned * pcbExtra )
  184. {
  185. if ( !IsStored( Ordinal, Mask ) )
  186. {
  187. var.vt = VT_EMPTY;
  188. *pcbExtra = 0;
  189. }
  190. else
  191. {
  192. //
  193. // Check for overflow.
  194. //
  195. if ( IsStoredOnOverflow( Ordinal, Mask ) )
  196. return FALSE;
  197. //
  198. // Start is after existance bitmap and fixed properties.
  199. //
  200. ULONG * pulStart = FindVariableProp( Ordinal, cFixed, cTotal, oStart );
  201. Win4Assert( !IsOverflow( *pulStart ) );
  202. //
  203. // Compute the length of the property.
  204. //
  205. ULONG cbProp = UsedSize( *pulStart ) * 4;
  206. pulStart++; // Skip size field.
  207. ciDebugOut(( DEB_PROPSTORE,
  208. "Reading variable prop, ordinal %d at offset 0x%x (%d) in record. Size = %d bytes.\n",
  209. Ordinal,
  210. (ULONG)pulStart - (ULONG)this,
  211. (ULONG)pulStart - (ULONG)this,
  212. cbProp ));
  213. ULONG cb = PropertyLengthAsVariant( (SERIALIZEDPROPERTYVALUE *)pulStart,
  214. cbProp,
  215. CP_WINUNICODE,
  216. 0 );
  217. if ( cb <= *pcbExtra )
  218. {
  219. if ( *pcbExtra == 0xFFFFFFFF )
  220. {
  221. //
  222. // Unmarshall the property. Extra allocation via CoTaskMemAlloc
  223. //
  224. CCTMABufferAllocator BufferMgr;
  225. RtlConvertPropertyToVariant( (SERIALIZEDPROPERTYVALUE *)pulStart,
  226. CP_WINUNICODE,
  227. &var,
  228. &BufferMgr );
  229. #if CIDBG==1
  230. if ( (var.vt&0x0fff) > VT_CLSID )
  231. {
  232. ciDebugOut(( DEB_ERROR, "Bad Variant Type 0x%X\n", var.vt ));
  233. Win4Assert( !"Call KyleP" );
  234. }
  235. #endif // CIDBG==1
  236. }
  237. else
  238. {
  239. //
  240. // Unmarshall the property.
  241. //
  242. CNonAlignAllocator BufferMgr( *pcbExtra, pbExtra );
  243. RtlConvertPropertyToVariant( (SERIALIZEDPROPERTYVALUE *)pulStart,
  244. CP_WINUNICODE,
  245. &var,
  246. &BufferMgr );
  247. #if CIDBG==1
  248. if ( (var.vt&0x0fff) > VT_CLSID )
  249. {
  250. ciDebugOut(( DEB_ERROR, "Bad Variant Type 0x%X\n", var.vt ));
  251. Win4Assert( !"Call KyleP" );
  252. }
  253. #endif // CIDBG==1
  254. }
  255. }
  256. *pcbExtra = cb;
  257. }
  258. return TRUE;
  259. }
  260. //+---------------------------------------------------------------------------
  261. //
  262. // Member: COnDiskPropertyRecord::WriteFixed, public
  263. //
  264. // Synopsis: Write fixed-length property
  265. //
  266. // Arguments: [Ordinal] -- Ordinal of property to locate.
  267. // [mask] -- Bitmask of ordinal. Pre-computed for efficiency.
  268. // [oStart] -- Offset to start of specific property.
  269. // [Type] -- Expected data type (for type checking)
  270. // [cTotal] -- Total number of properties in record.
  271. // [var] -- Value to store.
  272. //
  273. // History: 27-Dec-95 KyleP Created.
  274. //
  275. //----------------------------------------------------------------------------
  276. void COnDiskPropertyRecord::WriteFixed( ULONG Ordinal,
  277. ULONG Mask,
  278. ULONG oStart,
  279. ULONG Type,
  280. ULONG cTotal,
  281. CStorageVariant const & var )
  282. {
  283. if ( var.Type() != (VARENUM)Type )
  284. {
  285. # if CIDBG == 1
  286. if ( var.Type() != VT_EMPTY )
  287. ciDebugOut(( DEB_WARN, "Type mismatch (%d vs. %d) writing fixed property\n",
  288. var.Type(), Type ));
  289. # endif
  290. ClearStored( Ordinal, Mask );
  291. return;
  292. }
  293. //
  294. // Start is after existance bitmap
  295. //
  296. ULONG * pulStart = &_aul[ (cTotal-1) / 16 + 1 + oStart];
  297. ciDebugOut(( DEB_PROPSTORE,
  298. "Writing fixed prop, ordinal %d (type %d) at offset 0x%x (%d) in record.\n",
  299. Ordinal,
  300. var.Type(),
  301. (ULONG)pulStart - (ULONG)this,
  302. (ULONG)pulStart - (ULONG)this ));
  303. switch ( var.Type() )
  304. {
  305. case VT_I1:
  306. case VT_UI1:
  307. Win4Assert( !"Fix this!" );
  308. //*(BYTE *)pulStart = var.GetUI1();
  309. break;
  310. case VT_I2:
  311. case VT_UI2:
  312. case VT_BOOL:
  313. {
  314. ULONG ul = var.GetUI2();
  315. *pulStart = ul;
  316. }
  317. break;
  318. case VT_I4:
  319. case VT_UI4:
  320. case VT_R4:
  321. case VT_ERROR:
  322. *pulStart = var.GetUI4();
  323. break;
  324. case VT_I8:
  325. case VT_UI8:
  326. case VT_R8:
  327. case VT_CY:
  328. case VT_DATE:
  329. case VT_FILETIME:
  330. {
  331. ULARGE_INTEGER uli = var.GetUI8();
  332. RtlCopyMemory( pulStart, &uli, sizeof(uli) );
  333. }
  334. break;
  335. case VT_CLSID:
  336. RtlCopyMemory( pulStart, var.GetCLSID(), sizeof(CLSID) );
  337. break;
  338. default:
  339. Win4Assert( !"How did I get here?" );
  340. ClearStored( Ordinal, Mask );
  341. break;
  342. }
  343. SetStored( Ordinal, Mask );
  344. }
  345. //+---------------------------------------------------------------------------
  346. //
  347. // Member: COnDiskPropertyRecord::WriteVariable, public
  348. //
  349. // Synopsis: Write variable-length property
  350. //
  351. // Arguments: [Ordinal] -- Ordinal of property to locate.
  352. // [mask] -- Bitmask of ordinal. Pre-computed for efficiency.
  353. // [oStart] -- Offset to start of variable length area.
  354. // [cTotal] -- Total number of properties in record.
  355. // [cFixed] -- Count of fixed length properties
  356. // [culRec] -- Size (in dwords) of record
  357. // [var] -- Value to store.
  358. //
  359. // Returns: FALSE if record must be stored in overflow record.
  360. //
  361. // History: 27-Dec-95 KyleP Created.
  362. //
  363. //----------------------------------------------------------------------------
  364. BOOL COnDiskPropertyRecord::WriteVariable( ULONG Ordinal,
  365. ULONG Mask,
  366. ULONG oStart,
  367. ULONG cTotal,
  368. ULONG cFixed,
  369. ULONG culRec,
  370. CStorageVariant const & var )
  371. {
  372. ULONG * pulStart = FindVariableProp( Ordinal, cFixed, cTotal, oStart );
  373. //
  374. // Are we freeing this property?
  375. //
  376. if ( var.Type() == VT_EMPTY )
  377. {
  378. ClearStored( Ordinal, Mask );
  379. ClearStoredOnOverflow( Ordinal, Mask );
  380. //
  381. // Overflow case: We need to fake the overflow to clean up linked records.
  382. //
  383. if ( IsOverflow( *pulStart ) )
  384. {
  385. SetUsedSize( pulStart, 0 );
  386. return FALSE;
  387. }
  388. else
  389. {
  390. //
  391. // Adjust size field, and total variable space for record.
  392. //
  393. Win4Assert( _culVariableUsed >= UsedSize( *pulStart ) );
  394. _culVariableUsed -= UsedSize( *pulStart );
  395. SetUsedSize( pulStart, 0 );
  396. return TRUE;
  397. }
  398. }
  399. //
  400. // No matter what happens, we want to indicate the property was stored.
  401. //
  402. SetStored( Ordinal, Mask );
  403. ClearStoredOnOverflow( Ordinal, Mask );
  404. //
  405. // Compute the length of the property.
  406. //
  407. ULONG cul = 0;
  408. RtlConvertVariantToProperty( (PROPVARIANT *)(ULONG)&var,
  409. CP_WINUNICODE,
  410. 0,
  411. &cul,
  412. pidInvalid,
  413. FALSE,
  414. 0 );
  415. Win4Assert( cul > 0 );
  416. cul = (cul - 1) / 4 + 1;
  417. ULONG culPrevUsed = UsedSize( *pulStart );
  418. //
  419. // Do we fit?
  420. //
  421. if ( cul > AllocatedSize( *pulStart ) )
  422. {
  423. //
  424. // Can we fit?
  425. //
  426. if ( cul >
  427. UsedSize( *pulStart ) + FreeVariableSpace( cTotal, cFixed, oStart, culRec ) )
  428. {
  429. ciDebugOut(( DEB_PROPSTORE, "Need overflow buffer for ordinal %u\n", Ordinal ));
  430. //
  431. // If we had a previous value, adjust total variable space for record.
  432. //
  433. if ( !IsOverflow( *pulStart ) )
  434. {
  435. Win4Assert( _culVariableUsed >= UsedSize( *pulStart ) );
  436. _culVariableUsed -= UsedSize( *pulStart );
  437. }
  438. MarkOverflow( pulStart );
  439. SetStoredOnOverflow( Ordinal, Mask );
  440. return FALSE;
  441. }
  442. //
  443. // Need to move properties, but there *is* room in record for the shift.
  444. //
  445. //
  446. // First, compress previous properties.
  447. //
  448. #if CIDBG
  449. ULONG * pulOldStart = pulStart;
  450. #endif
  451. pulStart = LeftCompress( FindVariableProp( cFixed, cFixed, cTotal, oStart ),
  452. 0,
  453. pulStart );
  454. ciDebugOut(( DEB_PROPSTORE,
  455. "Freeing up %d bytes for variable prop %d via left compression\n",
  456. (pulOldStart - pulStart) * 4,
  457. Ordinal ));
  458. //
  459. // Then, if needed, push additional properties farther out.
  460. //
  461. if ( cul > AllocatedSize( *pulStart ) )
  462. {
  463. ULONG * pulNext = pulStart + AllocatedSize(*pulStart) + 1;
  464. ULONG culDelta = cul - AllocatedSize(*pulStart);
  465. ciDebugOut(( DEB_PROPSTORE,
  466. "Freeing up %d bytes for variable prop %d, starting at offset 0x%x (%d) via right compression\n",
  467. culDelta * 4,
  468. Ordinal,
  469. (ULONG)pulNext - (ULONG)this,
  470. (ULONG)pulNext - (ULONG)this ));
  471. Win4Assert( Ordinal < cTotal );
  472. RightCompress( pulNext, // Next property
  473. culDelta, // Amount to shift
  474. cTotal - Ordinal - 1 ); // # props left in record
  475. SetAllocatedSize( pulStart, cul );
  476. }
  477. Win4Assert( cul <= AllocatedSize( *pulStart ) );
  478. }
  479. //
  480. // Adjust size field, and total variable space for record.
  481. //
  482. _culVariableUsed += cul - culPrevUsed;
  483. SetUsedSize( pulStart, cul );
  484. pulStart++; // Skip size field
  485. Win4Assert( AllocatedSize( *(pulStart - 1)) >= UsedSize( *(pulStart - 1) ) );
  486. ciDebugOut(( DEB_PROPSTORE,
  487. "Writing variable prop, ordinal %d at offset 0x%x (%d) in record. Size = %d bytes.\n",
  488. Ordinal,
  489. (ULONG)pulStart - (ULONG)this,
  490. (ULONG)pulStart - (ULONG)this,
  491. UsedSize(*(pulStart - 1)) * 4 ));
  492. cul *= 4;
  493. if ( 0 == RtlConvertVariantToProperty( (PROPVARIANT *)(ULONG)&var,
  494. CP_WINUNICODE,
  495. (SERIALIZEDPROPERTYVALUE *)pulStart,
  496. &cul,
  497. pidInvalid,
  498. FALSE,
  499. 0 ) )
  500. {
  501. ciDebugOut(( DEB_ERROR, "Error marshalling property!\n" ));
  502. ReportCorruptComponent( L"PropertyRecord2" );
  503. THROW( CException( CI_CORRUPT_DATABASE ) );
  504. }
  505. return TRUE;
  506. }
  507. //+---------------------------------------------------------------------------
  508. //
  509. // Member: COnDiskPropertyRecord::CountRecordsToStore, public
  510. //
  511. // Synopsis: Compute size of value
  512. //
  513. // Arguments: [cTotal] -- Total number of properties in record.
  514. // [culRec] -- Size (in dwords) of record
  515. // [var] -- Value to store.
  516. //
  517. // Returns: Size in records needed to store property.
  518. //
  519. // History: 27-Dec-95 KyleP Created.
  520. //
  521. //----------------------------------------------------------------------------
  522. ULONG COnDiskPropertyRecord::CountRecordsToStore( ULONG cTotal,
  523. ULONG culRec,
  524. CStorageVariant const & var )
  525. {
  526. //
  527. // Compute the length of the property.
  528. //
  529. ULONG cul = 0;
  530. RtlConvertVariantToProperty( (PROPVARIANT *)(ULONG)&var,
  531. CP_WINUNICODE,
  532. 0,
  533. &cul,
  534. pidInvalid,
  535. FALSE,
  536. 0 );
  537. Win4Assert( cul > 0 );
  538. cul = (cul - 1) / 4 + 1;
  539. //
  540. // Add on the fixed overhead.
  541. //
  542. cul += FixedOverhead() + // Fixed overhead
  543. (cTotal - 1) / 16 + 1 + // Existence bitmap
  544. cTotal; // Used/Alloc sizes
  545. return (cul - 1) / culRec + 1;
  546. }
  547. //+---------------------------------------------------------------------------
  548. //
  549. // Member: COnDiskPropertyRecord::RightCompress, public
  550. //
  551. // Synopsis: Moves data from [pul], [cul] dwords up.
  552. //
  553. // Arguments: [pul] -- Start of area to move.
  554. // [cul] -- Count of dwords to move
  555. // [cRemaining] -- Count of values remaining in buffer. Needed
  556. // because it is impossible to distinguish
  557. // used/alloc blocks from empty space.
  558. //
  559. // History: 27-Dec-95 KyleP Created.
  560. //
  561. //----------------------------------------------------------------------------
  562. void COnDiskPropertyRecord::RightCompress( ULONG * pul, ULONG cul, ULONG cRemaining )
  563. {
  564. //
  565. // Termination condition for recursion: We've scanned all variable
  566. // properties in this record. Any remaining space in record is
  567. // guaranteed to be free.
  568. //
  569. if ( 0 == cRemaining )
  570. return;
  571. ULONG FreeSpace = AllocatedSize(*pul) - UsedSize(*pul);
  572. if ( FreeSpace >= cul )
  573. {
  574. SetAllocatedSize( pul, AllocatedSize(*pul) - cul );
  575. }
  576. else
  577. {
  578. RightCompress( pul + AllocatedSize(*pul) + 1, cul - FreeSpace, cRemaining - 1 );
  579. SetAllocatedSize( pul, UsedSize(*pul) );
  580. }
  581. RtlMoveMemory( pul + cul, pul, (UsedSize(*pul) + 1) * 4 );
  582. }
  583. //+---------------------------------------------------------------------------
  584. //
  585. // Member: COnDiskPropertyRecord::LeftCompress, public
  586. //
  587. // Synopsis: Moves data from [pul], [cul] dwords down.
  588. //
  589. // Arguments: [pul] -- Start of area to move.
  590. // [cul] -- Count of dwords to move
  591. // [pulEnd] -- Terminating property. This property will have
  592. // it's allocated size increased with all the slack
  593. // from previous properties and have its used size
  594. // set to 0.
  595. //
  596. // Returns: New pointer to pulEnd (which moved down by [cul] bytes).
  597. //
  598. // History: 27-Dec-95 KyleP Created.
  599. //
  600. //----------------------------------------------------------------------------
  601. ULONG * COnDiskPropertyRecord::LeftCompress( ULONG * pul, ULONG cul, ULONG * pulEnd )
  602. {
  603. //
  604. // Terminating recursion? Just copy the allocated info. Used size is
  605. // zero, because data isn't copied.
  606. //
  607. if ( pul == pulEnd )
  608. {
  609. pul -= cul;
  610. SetAllocatedSize( pul, AllocatedSize(*pulEnd) + cul );
  611. SetUsedSize( pul, 0 );
  612. return pul;
  613. }
  614. //
  615. // First, move current record.
  616. //
  617. if ( cul > 0 )
  618. {
  619. RtlMoveMemory( pul - cul, pul, (UsedSize(*pul) + 1) * 4 );
  620. pul -= cul;
  621. }
  622. //
  623. // Adjust allocation size.
  624. //
  625. cul += ( AllocatedSize(*pul) - UsedSize(*pul) );
  626. SetAllocatedSize( pul, UsedSize(*pul) );
  627. return LeftCompress( pul + cul + UsedSize(*pul) + 1, cul, pulEnd );
  628. }