Leaked source code of windows server 2003
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.

7381 lines
227 KiB

  1. //+--------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1993
  5. //
  6. // File: propstm.cxx
  7. //
  8. // Contents: property set value extraction code
  9. //
  10. // The OLE 2.0 Appendix B property set specifies multiple sections in the
  11. // property stream specification. Multiple sections were intended to allow
  12. // the schema associated with the property set to evolve over a period of
  13. // time, but there is no reason that new PROPIDs cannot serve the same
  14. // purpose. The current implementation of the property stream is limited to
  15. // one section, except for the Office DocumentSummaryInformation property
  16. // set's specific use of a second section. Other property sets with multiple
  17. // sections can only be accessed in read-only mode, and then only for the
  18. // first property section. The current implementation of property set stream
  19. // is built around a class called CPropertySetStream. The various details of
  20. // the OLE property spec is confined to this class. This class encapsulates a
  21. // stream implementation (CMappedStream). This is different from other
  22. // stream implementations in that the fundamental mechanism provided
  23. // for acessing the contents is Map/Unmap rather than Read/Write.
  24. //
  25. //---------------------------------------------------------------------------
  26. #include "pch.cxx"
  27. #ifdef _UNIX
  28. #define qsort ref_qsort
  29. #include "qsort.h"
  30. #endif
  31. #define Dbg DEBTRACE_PROPERTY
  32. #define szX "x" // allows radix change for offsets & sizes
  33. //#define szX "d" // allows radix change for offsets & sizes
  34. #ifndef newk
  35. #define newk(Tag, pCounter) new
  36. #endif
  37. #ifndef IsDwordAligned
  38. #define IsDwordAligned(p) (((ULONG) (p) & (sizeof(ULONG) - 1)) == 0)
  39. #endif
  40. #ifndef DwordRemain
  41. #define DwordRemain(cb) \
  42. ((sizeof(ULONG) - ((cb) % sizeof(ULONG))) % sizeof(ULONG))
  43. #endif
  44. // Macro to create the OS Version field of the
  45. // property set header.
  46. #define MAKEPSVER(oskind, major, minor) \
  47. (((oskind) << 16) | ((minor) << 8) | (major))
  48. // we specify a new 'OS' type for the reference implementation
  49. #define CURRENT_OSKIND OSKIND_REF
  50. // reference implementation starts at version 1.00
  51. #define CURRENT_OSVER 0x01
  52. #define PROPSETVER_CURRENT \
  53. MAKEPSVER(CURRENT_OSKIND, CURRENT_OSVER >> 8, CURRENT_OSVER & 0xff)
  54. #define PROPSETVER_WIN310 MAKEPSVER(OSKIND_WINDOWS, 3, 10)
  55. #define PROPSETVER_WIN333 MAKEPSVER(OSKIND_WIN32, 3, 0x33)
  56. extern GUID guidSummary;
  57. extern GUID guidDocumentSummary;
  58. extern GUID guidDocumentSummarySection2;
  59. #define CP_DEFAULT_NONUNICODE 1252 // ANSI Latin1 (US, Western Europe)
  60. extern "C" UNICODECALLOUTS UnicodeCallouts;
  61. #define CP_CREATEDEFAULT(state) (*UnicodeCallouts.pfnGetACP)()
  62. #if DBGPROP
  63. #define StatusCorruption(pstatus, szReason) \
  64. _StatusCorruption(szReason " ", pstatus)
  65. #else
  66. #define StatusCorruption(pstatus, szReason) \
  67. _StatusCorruption(pstatus)
  68. #endif
  69. VOID RtlpConvertToUnicode(
  70. IN CHAR const *pch,
  71. IN ULONG cb,
  72. IN USHORT CodePage,
  73. OUT WCHAR **ppwc,
  74. OUT ULONG *pcb,
  75. OUT NTSTATUS *pstatus);
  76. #if DBGPROP
  77. #define CB_VALUEDISPLAY 8 // Number of bytes to display
  78. #define CB_VALUESTRING (CB_VALUEDISPLAY * 3 + 3) // "xx xx xx xx...\0"
  79. char *
  80. ValueToString(SERIALIZEDPROPERTYVALUE const *pprop, ULONG cbprop, char buf[])
  81. {
  82. char *p = buf;
  83. BYTE const *pb = pprop->rgb;
  84. BOOLEAN fOverflow = FALSE;
  85. static char szDots[] = "...";
  86. if (cbprop >= FIELD_OFFSET(SERIALIZEDPROPERTYVALUE, rgb))
  87. {
  88. cbprop -= FIELD_OFFSET(SERIALIZEDPROPERTYVALUE, rgb);
  89. if (cbprop > CB_VALUEDISPLAY)
  90. {
  91. cbprop = CB_VALUEDISPLAY;
  92. fOverflow = TRUE;
  93. }
  94. while (cbprop-- > 0)
  95. {
  96. if (p != buf)
  97. {
  98. *p++ = ' ';
  99. }
  100. p += PropSprintfA( p, "%02.2x", *pb++ );
  101. }
  102. }
  103. *p = '\0';
  104. PROPASSERT(p - buf + sizeof(szDots) <= CB_VALUESTRING);
  105. if (fOverflow)
  106. {
  107. strcpy(p, szDots);
  108. }
  109. return(buf);
  110. }
  111. #define CB_VARIANT_TO_STRING 35
  112. char *
  113. VariantToString(PROPVARIANT const &var, char buf[], ULONG cbprop)
  114. {
  115. char *p = buf;
  116. PROPASSERT( cbprop >= CB_VARIANT_TO_STRING );
  117. // Add the VT to the output buffer.
  118. p += PropSprintfA( p, "vt=%04.4x", var.vt );
  119. p += PropSprintfA( p, ", val=(%08.8x, %08.8x)", var.uhVal.QuadPart );
  120. *p = '\0';
  121. PROPASSERT( (p - buf) == CB_VARIANT_TO_STRING);
  122. return(buf);
  123. }
  124. #endif
  125. //+--------------------------------------------------------------------------
  126. // Member: CPropertySetStream::_MapOffsetToAddress, private
  127. //
  128. // Synopsis: maps an offset to an address
  129. //
  130. // Arguments: [Offset] -- the offset in the section
  131. //
  132. // Returns: ptr to the offset mapped
  133. //+--------------------------------------------------------------------------
  134. inline VOID *
  135. CPropertySetStream::_MapOffsetToAddress(ULONG Offset) const
  136. {
  137. PROPASSERT(_cSection != 0);
  138. return(Add2Ptr(_GetSectionHeader(), Offset));
  139. }
  140. //+--------------------------------------------------------------------------
  141. // Member: CPropertySetStream::_DictionaryEntryLength
  142. //
  143. // Synopsis: Calculate the length of an entry in the
  144. // dictionary. This is non-trivial because
  145. // it is codepage-dependent.
  146. //
  147. // Arguments: [pent] -- pointer to a dictionary entry.
  148. //
  149. //
  150. // Returns: The entry's length.
  151. //+--------------------------------------------------------------------------
  152. inline ULONG
  153. CPropertySetStream::_DictionaryEntryLength(
  154. IN ENTRY UNALIGNED const * pentArg
  155. ) const
  156. {
  157. #if i386 == 0 // copy into an aligned structure
  158. ENTRY ent; // allocate on stack -> faster
  159. ENTRY *pent=&ent;
  160. memcpy(pent, pentArg, sizeof(ENTRY));
  161. #else
  162. ENTRY UNALIGNED const *pent = pentArg;
  163. #endif
  164. // If this is a Unicode property set, it should be DWORD-aligned.
  165. PROPASSERT( _CodePage != CP_WINUNICODE
  166. ||
  167. IsDwordAligned( (ULONG) pent ));
  168. // The size consists of the length of the
  169. // PROPID and character count ...
  170. ULONG ulSize = CB_ENTRY;
  171. // Plus the length of the string ...
  172. ulSize += PropByteSwap( pent->cch ) *
  173. ( _CodePage == CP_WINUNICODE
  174. ? sizeof( WCHAR )
  175. : sizeof( CHAR )
  176. );
  177. // Plus, possibly, padding to make the entry DWORD-aligned
  178. // (for Unicode property sets).
  179. if( _CodePage == CP_WINUNICODE )
  180. {
  181. ulSize = DwordAlign( ulSize );
  182. }
  183. return( ulSize );
  184. }
  185. //+--------------------------------------------------------------------------
  186. // Member: CPropertySetStream::_NextDictionaryEntry
  187. //
  188. // Synopsis: Given a pointer to an entry in the dictionary,
  189. // create a pointer to the next entry.
  190. //
  191. // Arguments: [pent] -- pointer to a dictionary entry.
  192. //
  193. // Returns: Pointer to the next entry. If the input
  194. // points to the last entry in the dictionary,
  195. // then return a pointer to just beyond the
  196. // end of the dictionary.
  197. //+--------------------------------------------------------------------------
  198. inline ENTRY UNALIGNED *
  199. CPropertySetStream::_NextDictionaryEntry(
  200. IN ENTRY UNALIGNED const * pent
  201. ) const
  202. {
  203. return (ENTRY UNALIGNED *)
  204. Add2Ptr( pent, _DictionaryEntryLength( pent ));
  205. }
  206. //+--------------------------------------------------------------------------
  207. // Member: CPropertySetStream::_SignalCorruption
  208. //
  209. // Synopsis: possibly PROPASSERT and return data corrupt error
  210. //
  211. // Arguments: [szReason] -- string explanation (DBGPROP only)
  212. // [pstatus] -- NTSTATUS code.
  213. //
  214. // Returns: None
  215. //+--------------------------------------------------------------------------
  216. VOID
  217. CPropertySetStream::_StatusCorruption(
  218. #if DBGPROP
  219. char *szReason,
  220. #endif
  221. OUT NTSTATUS *pstatus
  222. ) const
  223. {
  224. #if DBGPROP
  225. DebugTrace(0, DEBTRACE_ERROR, (
  226. "_StatusCorruption(%s, psstm=%lx, mapstm=%lx, %s, flags=%x)\n",
  227. szReason,
  228. this,
  229. KERNELSELECT(&_mstm, _pmstm),
  230. KERNELSELECT("Kernel", _MSTM(IsNtMappedStream)()? "Nt" : "DocFile"),
  231. _Flags));
  232. PROPASSERTMSG(szReason, FALSE);
  233. DebugTrace(0, DEBTRACE_WARN, (
  234. "_StatusCorruption(%s, psstm=%lx, mapstm=%lx, %s, flags=%x)\n",
  235. szReason,
  236. this,
  237. KERNELSELECT(&_mstm, _pmstm),
  238. KERNELSELECT("Kernel", _MSTM(IsNtMappedStream)()? "Nt" : "DocFile"),
  239. _Flags));
  240. if (DebugLevel & DEBTRACE_WARN)
  241. {
  242. PROPASSERTMSG(szReason, FALSE);
  243. }
  244. #endif // DBGPROP
  245. *pstatus = STATUS_INTERNAL_DB_CORRUPTION;
  246. return;
  247. }
  248. //+--------------------------------------------------------------------------
  249. // Function: _PropMoveMemory
  250. //
  251. // Synopsis: call DebugTrace and RtlMoveMemory
  252. //
  253. // Arguments: [pszReason] -- string explanation (Debug only)
  254. // [pvSection] -- base of section (Debug only)
  255. // [pvDst] -- destination
  256. // [pvSrc] -- source
  257. // [cbMove] -- byte count to move
  258. //
  259. // Returns: None
  260. //+--------------------------------------------------------------------------
  261. #if DBGPROP
  262. #define PropMoveMemory(pszReason, pvSection, pvDst, pvSrc, cbMove) \
  263. _PropMoveMemory(pszReason, pvSection, pvDst, pvSrc, cbMove)
  264. #else
  265. #define PropMoveMemory(pszReason, pvSection, pvDst, pvSrc, cbMove) \
  266. _PropMoveMemory(pvDst, pvSrc, cbMove)
  267. #endif
  268. inline VOID
  269. _PropMoveMemory(
  270. #if DBGPROP
  271. char *pszReason,
  272. VOID *pvSection,
  273. #endif
  274. VOID *pvDst,
  275. VOID const *pvSrc,
  276. ULONG cbMove)
  277. {
  278. DebugTrace(0, Dbg, (
  279. "%s: Moving Dst=%lx(%l" szX ") Src=%lx(%l" szX ") Size=%l" szX "\n",
  280. pszReason,
  281. pvDst,
  282. (BYTE *) pvDst - (BYTE *) pvSection,
  283. pvSrc,
  284. (BYTE *) pvSrc - (BYTE *) pvSection,
  285. cbMove));
  286. RtlMoveMemory(pvDst, pvSrc, cbMove);
  287. }
  288. inline BOOLEAN
  289. IsReadOnlyPropertySet(BYTE flags, BYTE state)
  290. {
  291. return(
  292. (flags & CREATEPROP_MODEMASK) == CREATEPROP_READ ||
  293. (state & CPSS_USERDEFINEDDELETED) ||
  294. (state & (CPSS_MULTIPLESECTIONS | CPSS_DOCUMENTSUMMARYINFO)) ==
  295. CPSS_MULTIPLESECTIONS);
  296. }
  297. inline BOOLEAN
  298. IsReadOnlyPropid(PROPID pid)
  299. {
  300. return(
  301. pid == PID_DICTIONARY ||
  302. pid == PID_CODEPAGE ||
  303. pid == PID_LOCALE ||
  304. pid == PID_MODIFY_TIME ||
  305. pid == PID_SECURITY);
  306. }
  307. //+--------------------------------------------------------------------------
  308. // Member: CStreamChunkList::CStreamChunkList
  309. //
  310. // Synopsis: constructor
  311. //
  312. // Arguments: [cChunks] -- count of chunks that will be needed
  313. //
  314. // Returns: None
  315. //+--------------------------------------------------------------------------
  316. CStreamChunkList::CStreamChunkList(
  317. ULONG cChunks,
  318. CStreamChunk *ascnk) :
  319. _cMaxChunks(cChunks),
  320. _cChunks(0),
  321. _ascnk(ascnk),
  322. _fDelete(FALSE)
  323. {
  324. }
  325. //+--------------------------------------------------------------------------
  326. // Member: CStreamChunkList::Delete
  327. //
  328. // Synopsis: destructor
  329. //
  330. // Arguments: None
  331. //
  332. // Returns: None
  333. //+--------------------------------------------------------------------------
  334. inline
  335. VOID
  336. CStreamChunkList::Delete(VOID)
  337. {
  338. if (_fDelete)
  339. {
  340. delete [] _ascnk;
  341. }
  342. #if DBGPROP
  343. _cMaxChunks = _cChunks = 0;
  344. _ascnk = NULL;
  345. _fDelete = FALSE;
  346. #endif
  347. }
  348. //+--------------------------------------------------------------------------
  349. // Member: CStreamChunkList::GetChunk
  350. //
  351. // Synopsis: retrieves a chunk given the index
  352. //
  353. // Arguments: [i] -- index of the chunk to retrieve
  354. //
  355. // Returns: specified chunk pointer
  356. //+--------------------------------------------------------------------------
  357. inline
  358. CStreamChunk const *
  359. CStreamChunkList::GetChunk(ULONG i) const
  360. {
  361. PROPASSERT(i < _cChunks);
  362. PROPASSERT(i < _cMaxChunks);
  363. PROPASSERT(_ascnk != NULL);
  364. return(&_ascnk[i]);
  365. }
  366. //+--------------------------------------------------------------------------
  367. // Member: CStreamChunkList::Count
  368. //
  369. // Synopsis: returns the count of chunks
  370. //
  371. // Arguments: None
  372. //
  373. // Returns: the number of chunks.
  374. //+--------------------------------------------------------------------------
  375. inline ULONG
  376. CStreamChunkList::Count(VOID) const
  377. {
  378. return(_cChunks);
  379. }
  380. //+--------------------------------------------------------------------------
  381. // Member: CStreamChunkList::GetFreeChunk
  382. //
  383. // Synopsis: gets a unused chunk descriptor
  384. //
  385. // Arguments: [pstatus] -- NTSTATUS code
  386. //
  387. // Returns: a ptr to a stream chunk descriptor.
  388. // This will be NULL if there was an
  389. // error.
  390. //+--------------------------------------------------------------------------
  391. CStreamChunk *
  392. CStreamChunkList::GetFreeChunk(OUT NTSTATUS *pstatus)
  393. {
  394. CStreamChunk *pscnk = NULL;
  395. *pstatus = STATUS_SUCCESS;
  396. PROPASSERT(_cChunks < _cMaxChunks);
  397. if (_ascnk == NULL)
  398. {
  399. PROPASSERT(_cChunks == 0);
  400. _ascnk = newk(mtPropSetStream, NULL) CStreamChunk[_cMaxChunks];
  401. if (_ascnk == NULL)
  402. {
  403. StatusNoMemory(pstatus, "GetFreeChunk");
  404. goto Exit;
  405. }
  406. _fDelete = TRUE;
  407. }
  408. pscnk = &_ascnk[_cChunks++];
  409. // ----
  410. // Exit
  411. // ----
  412. Exit:
  413. return( pscnk );
  414. }
  415. //+--------------------------------------------------------------------------
  416. // Member: CStreamChunkList::AssertCbChangeTotal
  417. //
  418. // Synopsis: make sure the computed cbChangeTotal is correct for the chunk
  419. //
  420. // Arguments: None
  421. //
  422. // Returns: Nothing
  423. //+--------------------------------------------------------------------------
  424. #if DBGPROP
  425. VOID
  426. CStreamChunkList::AssertCbChangeTotal(
  427. CStreamChunk const *pscnk,
  428. ULONG cbChangeTotal) const
  429. {
  430. ULONG cb = 0;
  431. ULONG i;
  432. for (i = 0; i < Count(); i++)
  433. {
  434. CStreamChunk const *pscnkT = GetChunk(i);
  435. cb += pscnkT->cbChange;
  436. if (pscnk == pscnkT)
  437. {
  438. PROPASSERT(cb == cbChangeTotal);
  439. return;
  440. }
  441. }
  442. PROPASSERT(i < Count());
  443. }
  444. #endif
  445. //+--------------------------------------------------------------------------
  446. // Member: fnChunkCompare
  447. //
  448. // Synopsis: qsort helper to compare chunks in the chunk list.
  449. //
  450. // Arguments: [pscnk1] -- pointer to chunk1
  451. // [pscnk2] -- pointer to chunk2
  452. //
  453. // Returns: difference
  454. //+--------------------------------------------------------------------------
  455. INT __cdecl
  456. fnChunkCompare(VOID const *pscnk1, VOID const *pscnk2)
  457. {
  458. return(((CStreamChunk const *) pscnk1)->oOld -
  459. ((CStreamChunk const *) pscnk2)->oOld);
  460. }
  461. //+--------------------------------------------------------------------------
  462. // Member: CStreamChunkList::SortByStartAddress
  463. //
  464. // Synopsis: sort all the chunks that are being modified in a stream in the
  465. // ascending order.
  466. //
  467. // Arguments: None
  468. //
  469. // Returns: None
  470. //+--------------------------------------------------------------------------
  471. VOID
  472. CStreamChunkList::SortByStartAddress(VOID)
  473. {
  474. DebugTrace(0, Dbg, ("Sorting %l" szX " Chunks @%lx\n", _cChunks, _ascnk));
  475. qsort(_ascnk, _cChunks, sizeof(_ascnk[0]), fnChunkCompare);
  476. #if DBGPROP
  477. LONG cbChangeTotal;
  478. ULONG i;
  479. cbChangeTotal = 0;
  480. for (i = 0; i < _cChunks; i++)
  481. {
  482. cbChangeTotal += _ascnk[i].cbChange;
  483. DebugTrace(0, Dbg, (
  484. "Chunk[%l" szX "] oOld=%l" szX " cbChange=%s%l" szX
  485. " cbChangeTotal=%s%l" szX "\n",
  486. i,
  487. _ascnk[i].oOld,
  488. _ascnk[i].cbChange < 0? "-" : "",
  489. _ascnk[i].cbChange < 0? -_ascnk[i].cbChange : _ascnk[i].cbChange,
  490. cbChangeTotal < 0? "-" : "",
  491. cbChangeTotal < 0? -cbChangeTotal : cbChangeTotal));
  492. }
  493. #endif
  494. }
  495. //+--------------------------------------------------------------------------
  496. // Member: CPropertySetStream::_GetFormatidOffset
  497. //
  498. // Synopsis: Get a pointer to the (first) section header
  499. //
  500. // Arguments: None
  501. //
  502. // Returns: pointer to section header
  503. //+--------------------------------------------------------------------------
  504. inline FORMATIDOFFSET *
  505. CPropertySetStream::_GetFormatidOffset(ULONG iSection) const
  506. {
  507. return(&((FORMATIDOFFSET *) Add2Ptr(_pph, sizeof(*_pph)))[iSection]);
  508. }
  509. //+--------------------------------------------------------------------------
  510. // Member: CPropertySetStream::_GetSectionHeader
  511. //
  512. // Synopsis: Get a pointer to the (first) section header
  513. //
  514. // Arguments: None
  515. //
  516. // Returns: pointer to section header
  517. //+--------------------------------------------------------------------------
  518. inline PROPERTYSECTIONHEADER *
  519. CPropertySetStream::_GetSectionHeader(VOID) const
  520. {
  521. return((PROPERTYSECTIONHEADER *) Add2Ptr(_pph, _oSection));
  522. }
  523. //+--------------------------------------------------------------------------
  524. // Member: CPropertySetStream::_GetSectionHeader
  525. //
  526. // Synopsis: Get a pointer to the specified section header
  527. //
  528. // Arguments: [iSection] -- section number
  529. // [pstatus] -- Pointer to NTSTATUS code.
  530. //
  531. // Returns: pointer to specified section header
  532. //+--------------------------------------------------------------------------
  533. PROPERTYSECTIONHEADER *
  534. CPropertySetStream::_GetSectionHeader(ULONG iSection, OUT NTSTATUS *pstatus)
  535. {
  536. *pstatus = STATUS_SUCCESS;
  537. PROPERTYSECTIONHEADER *psh = NULL;
  538. ULONG oSection = 0; // Assume no header
  539. ULONG cbstm = _MSTM(GetSize)(pstatus);
  540. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  541. // Don't assume *any* class variables (except _pph) are loaded yet!
  542. PROPASSERT(_HasPropHeader() || iSection == 0);
  543. if (_HasPropHeader())
  544. {
  545. PROPASSERT(iSection < _pph->reserved );
  546. oSection = MAXULONG;
  547. if (cbstm >= CB_PROPERTYSETHEADER + (iSection + 1) * CB_FORMATIDOFFSET)
  548. {
  549. oSection = _GetFormatidOffset(iSection)->dwOffset;
  550. }
  551. }
  552. if (oSection != MAXULONG &&
  553. cbstm >= oSection + CB_PROPERTYSECTIONHEADER)
  554. {
  555. psh = (PROPERTYSECTIONHEADER *) Add2Ptr(_pph, oSection);
  556. if( cbstm >= oSection + psh->cbSection )
  557. {
  558. goto Exit;
  559. }
  560. else
  561. {
  562. psh = NULL;
  563. }
  564. }
  565. StatusCorruption(pstatus, "GetSectionHeader(i): stream size");
  566. // ----
  567. // Exit
  568. // ----
  569. Exit:
  570. return(psh);
  571. }
  572. //+--------------------------------------------------------------------------
  573. // Member: CPropertySetStream::_SearchForCodePage, private
  574. //
  575. // Synopsis: Searches a section of a property set for the code page.
  576. //
  577. // This routine searches for the code page by iterating
  578. // through the PID/Offset array in search of
  579. // PID_CODEPAGE. The difference between calling
  580. // this routine, and calling GetValue(PID_CODEPAGE),
  581. // is that this routine does not assume that the
  582. // property set is formatted correctly; it only assumes
  583. // that the PID/Offset array is correct.
  584. //
  585. // Note that this routine is like a specialized _LoadProperty(),
  586. // the important difference is that this routine must use
  587. // unaligned pointers, since it cannot assume that the
  588. // property set is aligned properly.
  589. //
  590. // Pre-Conditions:
  591. // The PID/Offset array is correct.
  592. // &&
  593. // _oSection & _cSection are set correctly.
  594. //
  595. // Post-Conditions:
  596. // If PID_CODEPAGE exists, it is put into _CodePage.
  597. // If it doesn't exist, _CodePage is left unchanged.
  598. //
  599. // Arguments: [pstatus] -- Pointer to NTSTATUS code.
  600. //
  601. // Returns: None.
  602. //+-------------------------------------------------------------------------
  603. VOID
  604. CPropertySetStream::_SearchForCodePage( OUT NTSTATUS *pstatus )
  605. {
  606. PROPERTYSECTIONHEADER UNALIGNED *psh;
  607. PROPERTYIDOFFSET UNALIGNED *ppo;
  608. PROPERTYIDOFFSET UNALIGNED *ppoMax;
  609. #if i386 == 0
  610. PROPERTYSECTIONHEADER shCopy;
  611. #endif
  612. PROPERTYSECTIONHEADER *pshCopy;
  613. ULONG cbstm;
  614. *pstatus = STATUS_SUCCESS;
  615. // Verify the pre-conditions.
  616. PROPASSERT( _oSection != 0 );
  617. PROPASSERT( _cSection != 0 );
  618. // It's invalid to call any function on a deleted
  619. // DocSumInfo user-defined (section section) section.
  620. if (_State & CPSS_USERDEFINEDDELETED)
  621. {
  622. StatusAccessDenied(pstatus, "GetValue: deleted");
  623. goto Exit;
  624. }
  625. // Get the section's header and first & last PID/Offset pointers.
  626. // We can't use _LoadPropertyOffsetPointers, because it assumes
  627. // alignment.
  628. psh = _GetSectionHeader();
  629. #if i386 == 0
  630. memcpy(&shCopy, psh, sizeof(shCopy));
  631. // note that the copy should not be used to access rgprop, which
  632. // is a variable sized char arr.
  633. pshCopy=&shCopy;
  634. #else
  635. pshCopy = psh; // okay to access unaligned on i386
  636. #endif
  637. cbstm = _MSTM(GetSize)(pstatus);
  638. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  639. if (cbstm < _oSection + CB_PROPERTYSECTIONHEADER
  640. || cbstm < _oSection + CB_PROPERTYSECTIONHEADER
  641. + pshCopy->cProperties * CB_PROPERTYIDOFFSET
  642. || cbstm < _oSection + pshCopy->cbSection)
  643. {
  644. StatusCorruption(pstatus, "_SearchForCodePage: stream size");
  645. goto Exit;
  646. }
  647. ppo = (PROPERTYIDOFFSET UNALIGNED *)
  648. Add2Ptr(psh, CB_PROPERTYSECTIONHEADER);
  649. ppoMax = ppo + pshCopy->cProperties;
  650. // Search the PID/Offset array for PID_CODEPAGE
  651. for ( ; ppo < ppoMax; ppo++)
  652. {
  653. if (PIDOFFSET_GetPropid(ppo) == PID_CODEPAGE)
  654. {
  655. SERIALIZEDPROPERTYVALUE UNALIGNED *pprop;
  656. pprop = (SERIALIZEDPROPERTYVALUE *)
  657. _MapOffsetToAddress( PIDOFFSET_GetOffset(ppo) );
  658. // Get the real address of serialized property.
  659. // Check for corruption.
  660. if ( ( ( PIDOFFSET_GetOffset(ppo) +
  661. CB_SERIALIZEDPROPERTYVALUE + sizeof(DWORD) )
  662. >
  663. pshCopy->cbSection
  664. )
  665. ||
  666. PropByteSwap(SPV_GetType(pprop)) != VT_I2
  667. )
  668. {
  669. StatusCorruption(pstatus, "_SearchForCodePage");
  670. goto Exit;
  671. }
  672. // Set the member code page from the serialized property.
  673. // (The codepage is an I2).
  674. // we do memcpy to avoid alignment problems
  675. memcpy(&_CodePage, SPV_GetRgb(pprop), sizeof(_CodePage));
  676. _CodePage = PropByteSwap(_CodePage);
  677. break;
  678. } // if (PIDOFFSET_GetPropid(ppo) == PID_CODEPAGE)
  679. } // for ( ; ppo < ppoMax; ppo++)
  680. // ----
  681. // Exit
  682. // ----
  683. Exit:
  684. return;
  685. } // CPropertySetStream::_SearchForCodePage()
  686. //+--------------------------------------------------------------------------
  687. // Member: CPropertySetStream::_MapAddressToOffset, private
  688. //
  689. // Synopsis: maps an address to an offset
  690. //
  691. // Arguments: [pvAddr] -- the address in the section
  692. //
  693. // Returns: section-relative offset for passed pointer
  694. //+--------------------------------------------------------------------------
  695. inline ULONG
  696. CPropertySetStream::_MapAddressToOffset(VOID const *pvAddr) const
  697. {
  698. PROPASSERT(_cSection != 0);
  699. // Get a ptr to the section header.
  700. VOID const *pvSectionHeader = _GetSectionHeader();
  701. PROPASSERT((BYTE const *) pvAddr >= (BYTE const *) pvSectionHeader);
  702. return((BYTE const *) pvAddr - (BYTE const *) pvSectionHeader);
  703. }
  704. //+--------------------------------------------------------------------------
  705. // Member: CPropertySetStream::_MapAbsOffsetToAddress, private
  706. //
  707. // Synopsis: maps an address to an offset
  708. //
  709. // Arguments: [oAbsolute] -- the absolute offset
  710. //
  711. // Returns: a ptr to the offset mapped
  712. //+--------------------------------------------------------------------------
  713. inline VOID *
  714. CPropertySetStream::_MapAbsOffsetToAddress(ULONG oAbsolute) const
  715. {
  716. return(Add2Ptr(_pph, oAbsolute));
  717. }
  718. //+--------------------------------------------------------------------------
  719. // Member: CPropertySetStream::_MapAddressToAbsOffset, private
  720. //
  721. // Synopsis: maps an address to an offset
  722. //
  723. // Arguments: [pvAddr] -- the address
  724. //
  725. // Returns: the absolute offset
  726. //+--------------------------------------------------------------------------
  727. inline ULONG
  728. CPropertySetStream::_MapAddressToAbsOffset(VOID const *pvAddr) const
  729. {
  730. PROPASSERT((BYTE const *) pvAddr >= (BYTE *) _pph);
  731. return((BYTE const *) pvAddr - (BYTE *) _pph);
  732. }
  733. //+--------------------------------------------------------------------------
  734. // Member: CPropertySetStream::CPropertySetStream
  735. //
  736. // Synopsis: constructor for property set class
  737. //
  738. // Arguments:UK [Flags] -- NONSIMPLE|*1* of READ/WRITE/CREATE/CREATEIF/DELETE
  739. // K [pscb] -- SCB for property stream
  740. // K [pirpc] -- pointer to Irp Context
  741. // K [State] -- CPSS_PROPHEADER
  742. // U [pmstm] -- mapped stream implementation
  743. // U [pma] -- caller's memory allocator
  744. //
  745. // Returns: None
  746. //---------------------------------------------------------------------------
  747. CPropertySetStream::CPropertySetStream(
  748. IN USHORT Flags, // NONSIMPLE|*1* of READ/WRITE/CREATE/CREATEIF/DELETE
  749. IN CMappedStream *pmstm, // mapped stream impelementation
  750. IN PMemoryAllocator *pma // caller's memory allocator
  751. )
  752. :
  753. _Flags((BYTE) Flags),
  754. _State(0),
  755. _pmstm(pmstm),
  756. _pma(pma),
  757. _pph(NULL)
  758. {
  759. _CodePage = CP_CREATEDEFAULT(_State); // Default if not present
  760. PROPASSERT(_Flags == Flags); // Should fit in a byte
  761. _oSection = 0;
  762. _cSection = 0;
  763. _cbTail = 0;
  764. }
  765. //+--------------------------------------------------------------------------
  766. // Member: CPropertySetStream::Close
  767. //
  768. // Synopsis: shutdown property set prior to calling destructor
  769. //
  770. // Arguments: [pstatus] -- Pointer to NTSTATUS code.
  771. //
  772. // Returns: None
  773. //---------------------------------------------------------------------------
  774. VOID
  775. CPropertySetStream::Close(OUT NTSTATUS *pstatus)
  776. {
  777. *pstatus = STATUS_SUCCESS;
  778. // Validate the byte-order (_pph could be NULL in certain
  779. // close scenarios, e.g. an RtlCreatePropertySet fails).
  780. PROPASSERT(NULL == _pph || PROPSET_BYTEORDER == _pph->wByteOrder);
  781. PROPASSERT(
  782. (_Flags & CREATEPROP_MODEMASK) != CREATEPROP_READ ||
  783. !IsModified());
  784. _MSTM(Unmap)(IsModified(), (VOID **) &_pph);
  785. _MSTM(Close)(pstatus);
  786. // if( !NT_SUCCESS(*pstatus) ) goto Exit;
  787. //Exit:
  788. return;
  789. }
  790. //+--------------------------------------------------------------------------
  791. // Member: CPropertySetStream::Open
  792. //
  793. // Synopsis: Open property set image
  794. //
  795. // Arguments: None
  796. //
  797. // Returns: None
  798. //---------------------------------------------------------------------------
  799. VOID
  800. CPropertySetStream::Open(
  801. IN GUID const *pfmtid, // property set fmtid
  802. OPTIONAL IN GUID const *pclsid, // CLASSID of propset code (create only)
  803. IN ULONG LocaleId, // Locale Id (create only)
  804. OPTIONAL OUT ULONG *pOSVersion, // OS Version from header
  805. IN USHORT CodePage, // CodePage of property set (create only)
  806. OUT NTSTATUS *pstatus
  807. )
  808. {
  809. *pstatus = STATUS_SUCCESS;
  810. LOADSTATE LoadState;
  811. PROPASSERT(!_IsMapped());
  812. if( pOSVersion != NULL )
  813. *pOSVersion = PROPSETHDR_OSVERSION_UNKNOWN;
  814. // Open the underlying stream which holds the property set.
  815. // We give it a callback pointer so that it can call
  816. // RtlOnMappedStreamEvent.
  817. _MSTM(Open)(this, pstatus);
  818. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  819. // Load the header, including fixing the in-memory image of
  820. // poorly-formatted property sets.
  821. LoadState = _LoadHeader(pfmtid, _Flags & CREATEPROP_MODEMASK, pstatus);
  822. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  823. if (LoadState != LOADSTATE_DONE)
  824. {
  825. switch (_Flags & CREATEPROP_MODEMASK)
  826. {
  827. case CREATEPROP_READ:
  828. case CREATEPROP_WRITE:
  829. if (LoadState == LOADSTATE_FAIL)
  830. {
  831. StatusCorruption(pstatus, "Open: _LoadHeader");
  832. goto Exit;
  833. }
  834. PROPASSERT(
  835. LoadState == LOADSTATE_BADFMTID ||
  836. LoadState == LOADSTATE_USERDEFINEDNOTFOUND);
  837. DebugTrace(0, DEBTRACE_ERROR, (
  838. "_LoadHeader: LoadState=%x\n", LoadState));
  839. *pstatus = STATUS_PROPSET_NOT_FOUND;
  840. goto Exit;
  841. }
  842. _Create(
  843. pfmtid,
  844. pclsid,
  845. LocaleId,
  846. CodePage,
  847. LoadState,
  848. pstatus
  849. );
  850. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  851. } // if (LoadState != LOADSTATE_DONE)
  852. PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder);
  853. if (_HasPropHeader() &&
  854. (_pph->dwOSVer == PROPSETVER_WIN310 ||
  855. _pph->dwOSVer == PROPSETVER_WIN333))
  856. {
  857. DebugTrace(0, DEBTRACE_PROPPATCH, (
  858. "Open(%s) downlevel: %x\n",
  859. (_Flags & CREATEPROP_MODEMASK) == CREATEPROP_READ? "Read" : "Write",
  860. _Flags));
  861. _State |= CPSS_DOWNLEVEL;
  862. }
  863. if ((_Flags & CREATEPROP_MODEMASK) != CREATEPROP_READ)
  864. {
  865. if (_State & CPSS_PACKEDPROPERTIES)
  866. {
  867. StatusAccessDenied(pstatus, "Open: writing Unaligned propset");
  868. goto Exit;
  869. }
  870. if ((_State & (CPSS_MULTIPLESECTIONS | CPSS_DOCUMENTSUMMARYINFO)) ==
  871. CPSS_MULTIPLESECTIONS)
  872. {
  873. StatusAccessDenied(pstatus, "Open: writing unknown multiple section propset");
  874. goto Exit;
  875. }
  876. }
  877. // Return the OS Version to the caller.
  878. if( pOSVersion != NULL )
  879. *pOSVersion = _pph->dwOSVer;
  880. // ----
  881. // Exit
  882. // ----
  883. Exit:
  884. return;
  885. }
  886. //+--------------------------------------------------------------------------
  887. // Member: CPropertySetStream::ReOpen
  888. //
  889. // Synopsis: ReOpen property set image
  890. //
  891. // Arguments: [pstatus] -- Pointer to NSTATUS code.
  892. //
  893. // Returns: Number of properties.
  894. //---------------------------------------------------------------------------
  895. ULONG
  896. CPropertySetStream::ReOpen(OUT NTSTATUS *pstatus)
  897. {
  898. LOADSTATE LoadState;
  899. PROPERTYSECTIONHEADER const *psh;
  900. ULONG cProperties = 0;
  901. *pstatus = STATUS_SUCCESS;
  902. PROPASSERT(_IsMapped());
  903. _MSTM(ReOpen)((VOID **) &_pph, pstatus);
  904. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  905. if (_State & CPSS_USERDEFINEDDELETED)
  906. {
  907. goto Exit;
  908. }
  909. LoadState = _LoadHeader(NULL,
  910. CREATEPROP_READ, // all we need is !create
  911. pstatus);
  912. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  913. if (LoadState != LOADSTATE_DONE)
  914. {
  915. DebugTrace(0, DEBTRACE_ERROR, (
  916. "ReOpen: LoadState=%lx\n",
  917. LoadState));
  918. StatusCorruption(pstatus, "ReOpen: _LoadHeader");
  919. goto Exit;
  920. }
  921. PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder);
  922. psh = _GetSectionHeader();
  923. PROPASSERT(psh != NULL);
  924. cProperties = psh->cProperties;
  925. // ----
  926. // Exit
  927. // ----
  928. Exit:
  929. return( cProperties );
  930. }
  931. //+--------------------------------------------------------------------------
  932. // Member: CPropertySetStream::_InitSection
  933. //
  934. // Synopsis: Initialize a section header and the default properties.
  935. //
  936. // Arguments: [pfo] -- pointer to section info
  937. // [LocaleId] -- Locale Id
  938. //
  939. // Returns: None
  940. //---------------------------------------------------------------------------
  941. // Serialized Code-Page size
  942. #define CB_CODEPAGE (sizeof(ULONG) + DwordAlign(sizeof(USHORT)))
  943. // Serialized Locale ID (LCID) size.
  944. #define CB_LOCALE (sizeof(ULONG) + sizeof(ULONG))
  945. // Minimum section size (minimum has Code Page & LCID)
  946. #define CB_MINSECTIONSIZE (CB_PROPERTYSECTIONHEADER \
  947. + 2 * CB_PROPERTYIDOFFSET \
  948. + CB_CODEPAGE \
  949. + CB_LOCALE)
  950. // Minimum serialized dictionary size (a dict with no entries).
  951. #define CB_EMPTYDICTSIZE (sizeof(DWORD)) // Entry count
  952. // Minimum User-Defined section size (in DocumentSummaryInformation propset).
  953. // (Must include an empty dictionary & a PID/Offset for it.)
  954. #define CB_MINUSERDEFSECTIONSIZE \
  955. (CB_MINSECTIONSIZE \
  956. + \
  957. CB_PROPERTYIDOFFSET \
  958. + \
  959. CB_EMPTYDICTSIZE)
  960. VOID
  961. CPropertySetStream::_InitSection(
  962. IN FORMATIDOFFSET *pfo,
  963. IN ULONG LocaleId,
  964. IN BOOL fCreateDictionary // Create an empty dictionary?
  965. )
  966. {
  967. PROPERTYSECTIONHEADER *psh;
  968. ULONG ulPropIndex; // Index into the PID/Offset array.
  969. DWORD dwPropValOffset; // The offset to where the next prop val will be written.
  970. // Pointer to a serialized property value.
  971. SERIALIZEDPROPERTYVALUE *pprop;
  972. psh = (PROPERTYSECTIONHEADER *) _MapAbsOffsetToAddress(pfo->dwOffset);
  973. // Set the property count and section size in the section header.
  974. // This must account for the Code Page and Locale ID properties, and
  975. // might need to account for an empty dictionary property.
  976. // dwPropValOffset identifies the location of the next property value
  977. // to be written.
  978. if( fCreateDictionary )
  979. {
  980. // Three properties: Code Page, LCID, and Dictionary.
  981. psh->cProperties = 3;
  982. dwPropValOffset = CB_PROPERTYSECTIONHEADER + 3 * CB_PROPERTYIDOFFSET;
  983. psh->cbSection = CB_MINUSERDEFSECTIONSIZE;
  984. }
  985. else
  986. {
  987. // Two properties: Code Page and LCID (no dictionary).
  988. psh->cProperties = 2;
  989. dwPropValOffset = CB_PROPERTYSECTIONHEADER + 2 * CB_PROPERTYIDOFFSET;
  990. psh->cbSection = CB_MINSECTIONSIZE;
  991. }
  992. ulPropIndex = 0;
  993. // If requested by the caller, create a dictionary property, but
  994. // leave the dictionary empty. We always create this first. It shouldn't
  995. // matter where it's located, but Office95 requires it
  996. // and it doesn't do any harm to put it there.
  997. if( fCreateDictionary )
  998. {
  999. // Fill in the PID/Offset table.
  1000. psh->rgprop[ ulPropIndex ].propid = PID_DICTIONARY;
  1001. psh->rgprop[ ulPropIndex ].dwOffset = dwPropValOffset;
  1002. // Fill in the property value.
  1003. pprop = (SERIALIZEDPROPERTYVALUE *) Add2Ptr( psh, dwPropValOffset );
  1004. pprop->dwType = 0L; // For the dictonary, this is actually the entry count.
  1005. // Advance the table & value indices.
  1006. ulPropIndex++;
  1007. dwPropValOffset += CB_EMPTYDICTSIZE;
  1008. } // if( fCreateDictionary )
  1009. // Write the code page. We write a zero first to initialize
  1010. // the padding bytes.
  1011. psh->rgprop[ ulPropIndex ].propid = PID_CODEPAGE;
  1012. psh->rgprop[ ulPropIndex ].dwOffset = dwPropValOffset;
  1013. pprop = (SERIALIZEDPROPERTYVALUE *) Add2Ptr( psh, dwPropValOffset );
  1014. pprop->dwType = PropByteSwap((DWORD) VT_I2);
  1015. *(DWORD *) pprop->rgb = 0; // Zero out extra two bytes.
  1016. *(WORD *) pprop->rgb = PropByteSwap( _CodePage );
  1017. ulPropIndex++;
  1018. dwPropValOffset += CB_CODEPAGE;
  1019. // Write the Locale ID.
  1020. psh->rgprop[ ulPropIndex ].propid = PID_LOCALE;
  1021. psh->rgprop[ ulPropIndex ].dwOffset = dwPropValOffset;
  1022. pprop = (SERIALIZEDPROPERTYVALUE *) Add2Ptr(psh, dwPropValOffset );
  1023. pprop->dwType = PropByteSwap( (DWORD) VT_UI4 );
  1024. *(DWORD *) pprop->rgb = PropByteSwap( (DWORD) LocaleId );
  1025. }
  1026. //+---------------------------------------------------------------------------
  1027. // Member: CPropertySetStream:: _MultiByteToWideChar, private
  1028. //
  1029. // Synopsis: Convert a MultiByte string to a Unicode string,
  1030. // using the _pma memory allocator if necessary.
  1031. //
  1032. // Arguments: [pch] -- pointer to MultiByte string
  1033. // [cb] -- byte length of MultiByte string
  1034. // (-1 if null terminated)
  1035. // [CodePage] -- Codepage of input string.
  1036. // [ppwc] -- pointer to pointer to converted string
  1037. // (if *ppwc is NULL, it will be alloced,
  1038. // if non-NULL, *ppwc must be *pcb bytes long).
  1039. // [pcb] -- IN: byte length of *ppwc
  1040. // OUT: byte length of Unicode string.
  1041. // [pstatus] -- pointer to NTSTATUS code
  1042. //
  1043. // Returns: Nothing
  1044. //---------------------------------------------------------------------------
  1045. VOID
  1046. CPropertySetStream::_MultiByteToWideChar(
  1047. IN CHAR const *pch,
  1048. IN ULONG cb,
  1049. IN USHORT CodePage,
  1050. OUT WCHAR **ppwc,
  1051. OUT ULONG *pcb,
  1052. OUT NTSTATUS *pstatus)
  1053. {
  1054. // ------
  1055. // Locals
  1056. // ------
  1057. // Did we allocate *ppwc?
  1058. BOOL fAlloc = FALSE;
  1059. // --------------
  1060. // Initialization
  1061. // --------------
  1062. *pstatus = STATUS_SUCCESS;
  1063. PROPASSERT(pch != NULL);
  1064. PROPASSERT(ppwc != NULL);
  1065. PROPASSERT(pcb != NULL);
  1066. PROPASSERT(IsAnsiString(pch, ((ULONG)-1 == cb ) ? MAXULONG : cb));
  1067. PROPASSERT(NULL != *ppwc || 0 == *pcb);
  1068. PROPASSERT(UnicodeCallouts.pfnMultiByteToWideChar != NULL);
  1069. // ------------------
  1070. // Convert the String
  1071. // ------------------
  1072. // We will pass through this loop once (if the caller provided a buffer
  1073. // or twice (otherwise).
  1074. while (TRUE)
  1075. {
  1076. // Attempt to convert the string.
  1077. *pcb = (*UnicodeCallouts.pfnMultiByteToWideChar)(
  1078. CodePage, // Source codepage
  1079. 0, // Flags
  1080. pch, // Source string
  1081. cb, // Source string length
  1082. *ppwc, // Target string
  1083. *pcb); // Size of target string buffer
  1084. // The converted length should never be zero.
  1085. if (0 == *pcb)
  1086. {
  1087. // If we alloced a buffer, free it now.
  1088. if( fAlloc )
  1089. {
  1090. _pma->Free( *ppwc );
  1091. *ppwc = NULL;
  1092. }
  1093. // If there was an error, assume that it was a code-page
  1094. // incompatibility problem.
  1095. StatusError(pstatus, "_MultiByteToWideChar error",
  1096. STATUS_UNMAPPABLE_CHARACTER);
  1097. goto Exit;
  1098. }
  1099. // There was no error. If we provided a non-NULL buffer,
  1100. // then the conversion was performed and we're done.
  1101. *pcb *= sizeof(WCHAR); // cch => cb
  1102. if (*ppwc != NULL)
  1103. {
  1104. DebugTrace(0, DEBTRACE_PROPERTY, (
  1105. "_MultiByteToWideChar: pch='%s'[%x] pwc='%ws'[%x->%x]\n",
  1106. pch,
  1107. cb,
  1108. *ppwc,
  1109. *pcb,
  1110. *pcb * sizeof(WCHAR)));
  1111. break;
  1112. }
  1113. // We haven't actually the string yet. Now that
  1114. // we know the length, we can allocate a buffer and try the
  1115. // conversion for real.
  1116. *ppwc = (WCHAR *) _pma->Allocate( *pcb );
  1117. if (NULL == *ppwc)
  1118. {
  1119. StatusNoMemory(pstatus, "_MultiByteToWideChar: no memory");
  1120. goto Exit;
  1121. }
  1122. fAlloc = TRUE;
  1123. } // while(TRUE)
  1124. // ----
  1125. // Exit
  1126. // ----
  1127. Exit:
  1128. return;
  1129. } // CPropertySetStream::_MultiByteToWideChar
  1130. //+---------------------------------------------------------------------------
  1131. // Member: CPropertySetStream::_WideCharToMultiByte, private
  1132. //
  1133. // Synopsis: Convert a Unicode string to a MultiByte string,
  1134. // using the _pma memory allocator if necessary.
  1135. //
  1136. // Arguments: [pwc] -- pointer to Unicode string
  1137. // [cch] -- character length of Unicode string
  1138. // (-1 if null terminated)
  1139. // [CodePage] -- codepage of target string
  1140. // [ppch] -- pointer to pointer to converted string
  1141. // (if *ppch is NULL, it will be alloced,
  1142. // if non-NULL, *ppch must be *pcb bytes long).
  1143. // [pcb] -- IN: byte length of *ppch
  1144. // OUT: byte length of MultiByte string
  1145. // [pstatus] -- pointer to NTSTATUS code
  1146. //
  1147. // Returns: Nothing
  1148. //---------------------------------------------------------------------------
  1149. VOID
  1150. CPropertySetStream::_WideCharToMultiByte(
  1151. IN WCHAR const *pwc,
  1152. IN ULONG cch,
  1153. IN USHORT CodePage,
  1154. OUT CHAR **ppch,
  1155. OUT ULONG *pcb,
  1156. OUT NTSTATUS *pstatus)
  1157. {
  1158. // ------
  1159. // Locals
  1160. // ------
  1161. // Did we allocate *ppch?
  1162. BOOL fAlloc = FALSE;
  1163. // --------------
  1164. // Initialization
  1165. // --------------
  1166. *pstatus = STATUS_SUCCESS;
  1167. PROPASSERT(pwc != NULL);
  1168. PROPASSERT(ppch != NULL);
  1169. PROPASSERT(pcb != NULL);
  1170. PROPASSERT(IsUnicodeString(pwc, ((ULONG)-1 == cch ) ? MAXULONG : cch*sizeof(WCHAR)));
  1171. PROPASSERT(NULL != *ppch || 0 == *pcb);
  1172. PROPASSERT(UnicodeCallouts.pfnWideCharToMultiByte != NULL);
  1173. // ------------------
  1174. // Convert the String
  1175. // ------------------
  1176. // We will pass through this loop once (if the caller provided a buffer
  1177. // or twice (otherwise).
  1178. while (TRUE)
  1179. {
  1180. // Attempt the conversion.
  1181. *pcb = (*UnicodeCallouts.pfnWideCharToMultiByte)(
  1182. CodePage, // Codepage to convert to
  1183. 0, // Flags
  1184. pwc, // Source string
  1185. cch, // Size of source string
  1186. *ppch, // Target string
  1187. *pcb, // Size of target string buffer
  1188. NULL, // lpDefaultChar
  1189. NULL); // lpUsedDefaultChar
  1190. // A converted length of zero indicates an error.
  1191. if (0 == *pcb)
  1192. {
  1193. // If we allocated a buffer in this routine, free it.
  1194. if( fAlloc )
  1195. {
  1196. _pma->Free( *ppch );
  1197. *ppch = NULL;
  1198. }
  1199. // If there was an error, assume that it was a code-page
  1200. // incompatibility problem.
  1201. StatusError(pstatus, "_WideCharToMultiByte: WideCharToMultiByte error",
  1202. STATUS_UNMAPPABLE_CHARACTER);
  1203. goto Exit;
  1204. }
  1205. // If we have a non-zero length, and we provided a buffer,
  1206. // then we're done (successfully).
  1207. if (*ppch != NULL)
  1208. {
  1209. DebugTrace(0, DEBTRACE_PROPERTY, (
  1210. "_WideCharToMultiByte: pwc='%ws'[%x] pch='%s'[%x->%x]\n",
  1211. pwc,
  1212. cch,
  1213. *ppch,
  1214. *pcb,
  1215. *pcb));
  1216. break;
  1217. }
  1218. // There were no errors, but we need to allocate a buffer
  1219. // to do the actual conversion.
  1220. *ppch = (CHAR*) _pma->Allocate( *pcb );
  1221. if (*ppch == NULL)
  1222. {
  1223. StatusNoMemory(pstatus, "_WideCharToMultiByte: no memory");
  1224. goto Exit;
  1225. }
  1226. fAlloc = TRUE;
  1227. } // while (TRUE)
  1228. // ----
  1229. // Exit
  1230. // ----
  1231. Exit:
  1232. return;
  1233. } // CPropertySetStream::_WideCharToMultiByte
  1234. //+--------------------------------------------------------------------------
  1235. // Member: CPropertySetStream::ByteSwapHeaders
  1236. //
  1237. // Synopsis: Byte-swap the headers of a property set header
  1238. // (both the propset header and any section headers).
  1239. //
  1240. // Arguments: [PROPERTYSETHEADER*] pph
  1241. // Pointer to the beginning of the property set.
  1242. // [ULONG] cbstm
  1243. // Total size of the property stream.
  1244. // [NTSTATUS*] pstatus
  1245. // Pointer to NTSTATUS code.
  1246. //
  1247. // Pre-Conditions:
  1248. // There are no more than two sections.
  1249. //
  1250. // Note that this routine does not assume anything
  1251. // about the current state of the CPropertySetStream
  1252. // (it accesses no member variables).
  1253. //
  1254. // Post-Conditions:
  1255. // If the byte-order indicator is valid, the
  1256. // propset and section headers are byte-swapped
  1257. //
  1258. // Returns: None. *pstatus will only be non-successful
  1259. // if the Stream was too small for the property set
  1260. // (i.e, the property set is corrupt). If the caller
  1261. // knows this not to be the case, then it can assume
  1262. // that this routine will return STATUS_SUCCESS.
  1263. //
  1264. //---------------------------------------------------------------------------
  1265. VOID
  1266. CPropertySetStream::ByteSwapHeaders( IN PROPERTYSETHEADER *pph,
  1267. IN DWORD cbstm,
  1268. OUT NTSTATUS *pstatus )
  1269. {
  1270. #if LITTLEENDIAN
  1271. *pstatus = STATUS_SUCCESS;
  1272. return;
  1273. #else
  1274. // ------
  1275. // Locals
  1276. // ------
  1277. ULONG cSections;
  1278. ULONG ulIndex, ulSectionIndex;
  1279. // pfoPropSet points into pph, pfoReal is a local copy
  1280. // in the system's endian-ness.
  1281. FORMATIDOFFSET *pfoPropSet, pfoReal[2];
  1282. // Pointers into pph.
  1283. PROPERTYSECTIONHEADER *psh = NULL;
  1284. PROPERTYIDOFFSET *po = NULL;
  1285. // Are we converting *to* the system's endian-ness?
  1286. BOOL fToSystemEndian;
  1287. // ----------
  1288. // Initialize
  1289. // ----------
  1290. *pstatus = STATUS_SUCCESS;
  1291. PROPASSERT( NULL != pph );
  1292. PROPASSERT(PROPSET_BYTEORDER == pph->wByteOrder
  1293. ||
  1294. PROPSET_BYTEORDER == ByteSwap( pph->wByteOrder )
  1295. );
  1296. // ----------------------------
  1297. // Swap the Property Set header
  1298. // ----------------------------
  1299. // Validate the stream length.
  1300. if( sizeof(*pph) > cbstm )
  1301. {
  1302. StatusCorruption(pstatus, "CPropertySetStream::ByteSwapHeaders: PropertySet header size");
  1303. goto Exit;
  1304. }
  1305. // Swap the fields in place.
  1306. PropByteSwap( &pph->wByteOrder );
  1307. PropByteSwap( &pph->wFormat );
  1308. PropByteSwap( &pph->dwOSVer );
  1309. PropByteSwap( &pph->clsid );
  1310. PropByteSwap( &pph->reserved );
  1311. // Are we converting to little-endian?
  1312. if( PROPSET_BYTEORDER == pph->wByteOrder)
  1313. fToSystemEndian = TRUE;
  1314. else
  1315. {
  1316. fToSystemEndian = FALSE;
  1317. PROPASSERT( PROPSET_BYTEORDER == PropByteSwap(pph->wByteOrder) );
  1318. }
  1319. // Get the correctly-endianed section count and validate.
  1320. cSections = fToSystemEndian ? pph->reserved
  1321. : PropByteSwap( pph->reserved );
  1322. if( cSections > 2 )
  1323. {
  1324. StatusCorruption(pstatus, "CPropertySetStream::ByteSwapHeaders: PropertySet header size");
  1325. goto Exit;
  1326. }
  1327. // -------------------------
  1328. // Swap the per-section data
  1329. // -------------------------
  1330. pfoPropSet = (FORMATIDOFFSET*) ((BYTE*) pph + sizeof(*pph));
  1331. for( ulSectionIndex = 0; ulSectionIndex < cSections; ulSectionIndex++ )
  1332. {
  1333. ULONG cbSection, cProperties;
  1334. // ------------------------------
  1335. // Swap the FormatID/Offset entry
  1336. // ------------------------------
  1337. // Is the Stream long enough for the array?
  1338. if( cbstm < (ULONG) &pfoPropSet[ulSectionIndex]
  1339. + sizeof(*pfoPropSet)
  1340. - (ULONG) pph )
  1341. {
  1342. StatusCorruption(pstatus,
  1343. "CPropertySetStream::_ByteSwapHeaders: FormatID/Offset size");
  1344. goto Exit;
  1345. }
  1346. // Swap this FMTID/Offset entry in place.
  1347. PropByteSwap( &pfoPropSet[ulSectionIndex].fmtid );
  1348. PropByteSwap( &pfoPropSet[ulSectionIndex].dwOffset );
  1349. // Get a local copy of this array entry.
  1350. // Since we just swapped these values, we may need to swap the
  1351. // local copies back in order to make them usable.
  1352. pfoReal[ ulSectionIndex ].fmtid = pfoPropSet[ulSectionIndex].fmtid;
  1353. pfoReal[ ulSectionIndex ].dwOffset = pfoPropSet[ulSectionIndex].dwOffset;
  1354. if( !fToSystemEndian )
  1355. {
  1356. PropByteSwap( &pfoReal[ulSectionIndex].fmtid );
  1357. PropByteSwap( &pfoReal[ulSectionIndex].dwOffset );
  1358. }
  1359. // -----------------------
  1360. // Swap the section header
  1361. // -----------------------
  1362. // Locate the section header and the first entry in the
  1363. // PID/Offset table.
  1364. psh = (PROPERTYSECTIONHEADER*)
  1365. ( (BYTE*) pph + pfoReal[ ulSectionIndex ].dwOffset );
  1366. po = (PROPERTYIDOFFSET*)
  1367. ( (BYTE*) psh + sizeof(psh->cbSection) + sizeof(psh->cProperties) );
  1368. // Validate that we can see up to the PID/Offset table.
  1369. if( cbstm < (ULONG) ((BYTE*) po - (BYTE*) pph) )
  1370. {
  1371. StatusCorruption(pstatus,
  1372. "CPropertySetStream::ByteSwapHeaders: Section header size");
  1373. goto Exit;
  1374. }
  1375. // Swap the two fields at the top of the section header,
  1376. // and make local copies. Again, we may need to swap the
  1377. // local copies to make them usable.
  1378. cbSection = psh->cbSection = PropByteSwap(psh->cbSection);
  1379. cProperties = psh->cProperties = PropByteSwap( psh->cProperties );
  1380. if( !fToSystemEndian)
  1381. {
  1382. PropByteSwap( &cbSection );
  1383. PropByteSwap( &cProperties );
  1384. }
  1385. // -------------------------
  1386. // Swap the PID/Offset table
  1387. // -------------------------
  1388. // Validate that we can see the whole table.
  1389. if( cbstm < (BYTE*) po - (BYTE*) pph + cProperties * sizeof(*po) )
  1390. {
  1391. StatusCorruption(pstatus,
  1392. "CPropertySetStream::ByteSwapHeaders: Section header size");
  1393. goto Exit;
  1394. }
  1395. // Swap each of the array entries.
  1396. for( ulIndex = 0; ulIndex < cProperties; ulIndex++ )
  1397. {
  1398. PropByteSwap( &po[ulIndex].propid );
  1399. PropByteSwap( &po[ulIndex].dwOffset );
  1400. }
  1401. } // for( ulSectionIndex = 0; ulSectionIndex < cSections, ulIndex++ )
  1402. // ----
  1403. // Exit
  1404. // ----
  1405. Exit:
  1406. return;
  1407. #endif // #if LITTLEENDIAN ... #else
  1408. } // CPropertySetStream::ByteSwapHeaders
  1409. //+--------------------------------------------------------------------------
  1410. // Member: CPropertySetStream::_CreateUserDefinedSection
  1411. //
  1412. // Synopsis: Create second property section
  1413. //
  1414. // Arguments: [LoadState] -- _LoadHeader returned state
  1415. // [LocaleId] -- Locale Id
  1416. // [pstatus] -- Pointer to NTSTATUS code.
  1417. //
  1418. // Returns: TRUE if LoadState handled successfully. If TRUE,
  1419. // *pstatus will be STATUS_SUCCESS.
  1420. //---------------------------------------------------------------------------
  1421. BOOLEAN
  1422. CPropertySetStream::_CreateUserDefinedSection(
  1423. IN LOADSTATE LoadState,
  1424. IN ULONG LocaleId,
  1425. OUT NTSTATUS *pstatus)
  1426. {
  1427. BOOL fSuccess = FALSE;
  1428. FORMATIDOFFSET *pfo;
  1429. ULONG cbstmNew;
  1430. PROPERTYSECTIONHEADER *psh;
  1431. *pstatus = STATUS_SUCCESS;
  1432. PROPASSERT(_State & CPSS_USERDEFINEDPROPERTIES);
  1433. switch (_Flags & CREATEPROP_MODEMASK)
  1434. {
  1435. case CREATEPROP_CREATEIF:
  1436. case CREATEPROP_CREATE:
  1437. if (LoadState == LOADSTATE_USERDEFINEDNOTFOUND)
  1438. {
  1439. ULONG cbmove;
  1440. PROPASSERT(_cSection == 1);
  1441. pfo = _GetFormatidOffset(0);
  1442. PROPASSERT(pfo->fmtid == guidDocumentSummary);
  1443. PROPASSERT(IsDwordAligned(pfo->dwOffset));
  1444. // Get a pointer to the first section header, using the
  1445. // FmtID/Offset array.
  1446. psh = (PROPERTYSECTIONHEADER *) _MapAbsOffsetToAddress(pfo->dwOffset);
  1447. // Determine if we need to move the first section back in order
  1448. // to make room for this new entry in the FmtID/Offset array.
  1449. cbmove = 0;
  1450. if (pfo->dwOffset < CB_PROPERTYSETHEADER + 2 * CB_FORMATIDOFFSET)
  1451. {
  1452. cbmove = CB_PROPERTYSETHEADER + 2*CB_FORMATIDOFFSET - pfo->dwOffset;
  1453. }
  1454. // How big should the Stream be?
  1455. cbstmNew = pfo->dwOffset // The offset of the first section
  1456. +
  1457. cbmove // Room for new FormatID/Offset array entry
  1458. + // Size of first section
  1459. DwordAlign(psh->cbSection)
  1460. + // Size of User-Defined section.
  1461. CB_MINUSERDEFSECTIONSIZE;
  1462. // Set the stream size.
  1463. _MSTM(SetSize)(cbstmNew, TRUE, (VOID **) &_pph, pstatus);
  1464. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1465. // reload all pointers into mapped image:
  1466. pfo = _GetFormatidOffset(0);
  1467. psh = (PROPERTYSECTIONHEADER *) _MapAbsOffsetToAddress(pfo->dwOffset);
  1468. if (cbmove != 0)
  1469. {
  1470. // Move section back to make room for new FORMATIDOFFSET entry
  1471. PropMoveMemory(
  1472. "_AddSection",
  1473. psh,
  1474. Add2Ptr(psh, cbmove),
  1475. psh,
  1476. psh->cbSection);
  1477. pfo->dwOffset += cbmove;
  1478. PROPASSERT(IsDwordAligned(pfo->dwOffset));
  1479. }
  1480. psh->cbSection = DwordAlign(psh->cbSection);
  1481. PROPASSERT(_oSection == 0);
  1482. PROPASSERT(_cSection == 1);
  1483. PROPASSERT(_pph->reserved == 1);
  1484. _cSection++;
  1485. _pph->reserved++;
  1486. _oSection = pfo->dwOffset + psh->cbSection;
  1487. pfo = _GetFormatidOffset(1);
  1488. pfo->fmtid = guidDocumentSummarySection2;
  1489. pfo->dwOffset = _oSection;
  1490. _InitSection(pfo,
  1491. LocaleId,
  1492. TRUE ); // Create an empty dictionary.
  1493. fSuccess = TRUE;
  1494. }
  1495. break;
  1496. case CREATEPROP_DELETE:
  1497. PROPASSERT(
  1498. LoadState == LOADSTATE_USERDEFINEDDELETE ||
  1499. LoadState == LOADSTATE_USERDEFINEDNOTFOUND);
  1500. if (LoadState == LOADSTATE_USERDEFINEDDELETE)
  1501. {
  1502. PROPASSERT(_cSection == 2);
  1503. PROPASSERT(_pph->reserved == 2);
  1504. pfo = _GetFormatidOffset(1);
  1505. RtlZeroMemory(pfo, sizeof(*pfo));
  1506. _cSection--;
  1507. _pph->reserved--;
  1508. pfo = _GetFormatidOffset(0);
  1509. PROPASSERT(pfo->fmtid == guidDocumentSummary);
  1510. PROPASSERT(IsDwordAligned(pfo->dwOffset));
  1511. psh = (PROPERTYSECTIONHEADER *)
  1512. _MapAbsOffsetToAddress(pfo->dwOffset);
  1513. psh->cbSection = DwordAlign(psh->cbSection);
  1514. cbstmNew = pfo->dwOffset + psh->cbSection;
  1515. _MSTM(SetSize)(cbstmNew, TRUE, (VOID **) &_pph, pstatus);
  1516. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1517. }
  1518. _State |= CPSS_USERDEFINEDDELETED;
  1519. fSuccess = TRUE;
  1520. break;
  1521. default:
  1522. PROPASSERT(!"_Flags: bad open mode");
  1523. }
  1524. // ----
  1525. // Exit
  1526. // ----
  1527. Exit:
  1528. return( fSuccess );
  1529. }
  1530. //+--------------------------------------------------------------------------
  1531. // Member: CPropertySetStream::_Create
  1532. //
  1533. // Synopsis: Create property set image
  1534. //
  1535. // Arguments: [pfmtid] -- format id
  1536. // [pclsid] -- class id
  1537. // [LocaleId] -- Locale Id
  1538. // [CodePage] -- CodePage
  1539. // [LoadState] -- _LoadHeader returned state
  1540. //
  1541. // Returns: None
  1542. //---------------------------------------------------------------------------
  1543. VOID
  1544. CPropertySetStream::_Create(
  1545. IN GUID const *pfmtid,
  1546. OPTIONAL IN GUID const *pclsid,
  1547. IN ULONG LocaleId, // Locale Id (create only)
  1548. IN USHORT CodePage,
  1549. IN LOADSTATE LoadState,
  1550. OUT NTSTATUS *pstatus
  1551. )
  1552. {
  1553. ULONG cb;
  1554. FORMATIDOFFSET *pfo;
  1555. *pstatus = STATUS_SUCCESS;
  1556. _SetModified();
  1557. // Set the size of the stream to correspond to the header for the
  1558. // property set as well as the section.
  1559. _CodePage = CodePage;
  1560. ULONG cSectionT = 1;
  1561. // Are we creating the UserDefined property set
  1562. // (the second section of the DocumentSummaryInformation
  1563. // property set)?
  1564. if (_State & CPSS_USERDEFINEDPROPERTIES)
  1565. {
  1566. // Create the UD propset, and set the cSection.
  1567. // If this routine returns TRUE, it means that
  1568. // the first section already existed, and we're done.
  1569. // Otherwise, we must continue and create the first section.
  1570. if (_CreateUserDefinedSection(LoadState, LocaleId, pstatus))
  1571. {
  1572. // If we get here, we know that *pstatus is Success.
  1573. if (pclsid != NULL)
  1574. {
  1575. _pph->clsid = *pclsid;
  1576. }
  1577. goto Exit;
  1578. }
  1579. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1580. cSectionT = 2;
  1581. }
  1582. // Calculate the exact size of the Stream (we know exactly
  1583. // what it will be because we only initialize the set(s) with
  1584. // fixed size data).
  1585. PROPASSERT( 1 <= cSectionT && cSectionT <= 2 );
  1586. cb = CB_PROPERTYSETHEADER // The size of the propset header.
  1587. + // The size of the FmtID/Offset array
  1588. cSectionT * CB_FORMATIDOFFSET
  1589. +
  1590. CB_MINSECTIONSIZE // The size of the first section
  1591. + // Maybe the size of the User-Defined section
  1592. ( cSectionT <= 1 ? 0 : CB_MINUSERDEFSECTIONSIZE );
  1593. DebugTrace(0, Dbg, ("SetSize(%x) init\n", cb));
  1594. // Set the size of the stream
  1595. _MSTM(SetSize)(cb, TRUE, (VOID **) &_pph, pstatus);
  1596. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1597. // And get a mapping of the Stream.
  1598. _MSTM(Map)(TRUE, (VOID **) &_pph);
  1599. RtlZeroMemory(_pph, cb); // Zeros classid, fmtid(s), etc
  1600. // Initialize the header
  1601. _pph->wByteOrder = 0xfffe;
  1602. //_pph->wFormat = 0; // RtlZeroMemory does this
  1603. PROPASSERT(_pph->wFormat == 0);
  1604. _pph->dwOSVer = PROPSETVER_CURRENT;
  1605. if (pclsid != NULL)
  1606. {
  1607. _pph->clsid = *pclsid;
  1608. }
  1609. _pph->reserved = cSectionT;
  1610. // Initialize the format id offset for the section(s).
  1611. pfo = _GetFormatidOffset(0);
  1612. pfo->dwOffset = CB_PROPERTYSETHEADER + cSectionT * CB_FORMATIDOFFSET;
  1613. // Are we creating the second section of the DocSumInfo property set?
  1614. if (cSectionT == 2)
  1615. {
  1616. // We need to initialize any empty first section.
  1617. pfo->fmtid = guidDocumentSummary;
  1618. _InitSection(pfo,
  1619. LocaleId,
  1620. FALSE); // Don't create an empty dictionary.
  1621. // Advance the FmtID/Offset table pointer to the second entry,
  1622. // and set it's offset to just beyond the first section.
  1623. pfo = _GetFormatidOffset(1);
  1624. pfo->dwOffset = CB_PROPERTYSETHEADER +
  1625. cSectionT * CB_FORMATIDOFFSET +
  1626. CB_MINSECTIONSIZE;
  1627. }
  1628. // Initialize the requested property set.
  1629. PROPASSERT(pfmtid != NULL);
  1630. pfo->fmtid = *pfmtid;
  1631. _InitSection(pfo,
  1632. LocaleId,
  1633. // TRUE => Create an empty dictionary
  1634. pfo->fmtid == guidDocumentSummarySection2 );
  1635. _cSection = cSectionT;
  1636. _oSection = pfo->dwOffset;
  1637. // ----
  1638. // Exit
  1639. // ----
  1640. Exit:
  1641. return;
  1642. } // CPropertySetStream::_Create
  1643. //+--------------------------------------------------------------------------
  1644. // Member: CPropertySetStream::_LoadHeader
  1645. //
  1646. // Synopsis: verify header of a property set and read the code page
  1647. //
  1648. // Arguments: [pfmtid] -- format id
  1649. // [Mode] -- open mode
  1650. // [pstatus] -- Pointer to NTSTATUS code.
  1651. //
  1652. // Returns: LOADSTATE
  1653. //---------------------------------------------------------------------------
  1654. LOADSTATE
  1655. CPropertySetStream::_LoadHeader(
  1656. OPTIONAL IN GUID const *pfmtid,
  1657. IN BYTE Mode,
  1658. OUT NTSTATUS *pstatus)
  1659. {
  1660. LOADSTATE loadstate = LOADSTATE_FAIL;
  1661. ULONG cbstm, cbMin;
  1662. PROPERTYSECTIONHEADER *psh;
  1663. FORMATIDOFFSET const *pfo;
  1664. BOOLEAN fSummaryInformation = FALSE;
  1665. #if DBGPROP
  1666. BOOLEAN fFirst = _pph == NULL;
  1667. #endif
  1668. *pstatus = STATUS_SUCCESS;
  1669. PROPASSERT((_State & CPSS_USERDEFINEDDELETED) == 0);
  1670. // If this is one of the DocSumInfo property sets,
  1671. // we need to set some _State bits. If this is an
  1672. // Open, rather than a Create, pfmtid may be NULL.
  1673. // In that case, we'll set these bits after the open
  1674. // (since we can then get the fmtid from the header).
  1675. if( pfmtid != NULL && *pfmtid == guidDocumentSummary )
  1676. {
  1677. _State |= CPSS_DOCUMENTSUMMARYINFO;
  1678. }
  1679. if (pfmtid != NULL && *pfmtid == guidDocumentSummarySection2)
  1680. {
  1681. _State |= CPSS_USERDEFINEDPROPERTIES;
  1682. }
  1683. else
  1684. {
  1685. // If this isn't the UD property set, the Mode
  1686. // better not be "Delete" (all other property sets
  1687. // are deleted simply be deleting the underlying
  1688. // stream).
  1689. if (Mode == CREATEPROP_DELETE)
  1690. {
  1691. DebugTrace(0, Dbg, ("_LoadHeader: CREATEPROP_DELETE\n"));
  1692. StatusInvalidParameter(pstatus, "_LoadHeader: CREATEPROP_DELETE");
  1693. goto Exit;
  1694. }
  1695. if (Mode == CREATEPROP_CREATE)
  1696. {
  1697. goto Exit; // We're going to overwrite it anyway
  1698. }
  1699. }
  1700. // Get the size of the underlying stream.
  1701. cbstm = _MSTM(GetSize)(pstatus);
  1702. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1703. // Map the serialized property set to a pointer.
  1704. _MSTM(Map)(FALSE, (VOID **) &_pph);
  1705. cbMin = _ComputeMinimumSize(cbstm, pstatus);
  1706. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1707. // The following assert should technically ASSERT equality. However,
  1708. // to avoid unmapping and closing sections for every property operation,
  1709. // we allow shrinks to fail when other instances of the same property
  1710. // set are active. So we on occasion will legitimately see streams larger
  1711. // than necessary. The wasted space will be cleaned up when the property
  1712. // set is next modified.
  1713. //PROPASSERT(cbMin == cbstm);
  1714. PROPASSERT(cbMin <= cbstm);
  1715. DebugTrace(0, KERNELSELECT(Dbg, Dbg | DEBTRACE_CREATESTREAM), (
  1716. "ComputeMinimumSize: cbMin=%l" szX " cbstm=%l" szX " cbUnused=%l" szX "\n",
  1717. cbMin,
  1718. cbstm,
  1719. cbstm - cbMin));
  1720. _oSection = 0;
  1721. _cSection = 1;
  1722. _cbTail = 0;
  1723. if (_HasPropHeader())
  1724. {
  1725. // The first expression must be TRUE before we can dereference _pph
  1726. // for the second expression.
  1727. if (cbstm < CB_PROPERTYSETHEADER + CB_FORMATIDOFFSET ||
  1728. cbstm < CB_PROPERTYSETHEADER + _pph->reserved * CB_FORMATIDOFFSET ||
  1729. _pph->wByteOrder != 0xfffe ||
  1730. _pph->wFormat != 0 ||
  1731. _pph->reserved < 1)
  1732. {
  1733. _cSection = 0; // Mark property set invalid
  1734. DebugTrace(0, cbstm != 0? DEBTRACE_ERROR : Dbg, (
  1735. "_LoadHeader: %s (ver=%lx)\n",
  1736. cbstm == 0? "Empty Stream" :
  1737. cbstm < CB_PROPERTYSETHEADER + CB_FORMATIDOFFSET?
  1738. "Stream too small for header" :
  1739. _pph->wByteOrder != 0xfffe? "Bad wByteOrder field" :
  1740. _pph->wFormat != 0? "Bad wFormat field" :
  1741. _pph->reserved < 1? "Bad reserved field" :
  1742. "Bad dwOSVer field",
  1743. _pph != NULL? _pph->dwOSVer : 0));
  1744. goto Exit;
  1745. }
  1746. // Now that we've loaded the property set, check again
  1747. // to see if this is a SumInfo or DocSumInfo set.
  1748. pfo = _GetFormatidOffset(0);
  1749. if (pfo->fmtid == guidDocumentSummary)
  1750. {
  1751. _State |= CPSS_DOCUMENTSUMMARYINFO;
  1752. }
  1753. else if (pfo->fmtid == guidSummary)
  1754. {
  1755. fSummaryInformation = TRUE;
  1756. }
  1757. // If what we're after is the property set in the
  1758. // second section, verify that it's there.
  1759. if (_State & CPSS_USERDEFINEDPROPERTIES)
  1760. {
  1761. // Ensure that this is the second section of
  1762. // the DocSumInfo property set; that's the only
  1763. // two-section property set we support.
  1764. if ((_State & CPSS_DOCUMENTSUMMARYINFO) == 0)
  1765. {
  1766. DebugTrace(0, DEBTRACE_ERROR, ("Not DocumentSummaryInfo 1st FMTID\n"));
  1767. goto Exit;
  1768. }
  1769. // Verify that this proeprty set has two sections, and that
  1770. // the second section is the UD propset.
  1771. if (_pph->reserved < 2 ||
  1772. (pfo = _GetFormatidOffset(1))->fmtid != guidDocumentSummarySection2)
  1773. {
  1774. DebugTrace(
  1775. 0,
  1776. _pph->reserved < 2? Dbg : DEBTRACE_ERROR,
  1777. ("Bad/missing 2nd section FMTID\n"));
  1778. loadstate = LOADSTATE_USERDEFINEDNOTFOUND;
  1779. goto Exit;
  1780. }
  1781. }
  1782. else if (pfmtid != NULL)
  1783. {
  1784. // This isn't the UserDefined property set, so it
  1785. // should be the first section, so it should match
  1786. // the caller-requested format ID.
  1787. if (*pfmtid != pfo->fmtid)
  1788. {
  1789. // The propset's FmtID doesn't match, but maybe that's
  1790. // because it's a MacWord6 SumInfo property set, in which
  1791. // the FmtID isn't byte-swapped. Otherwise, it's a problem.
  1792. if( OSKIND_MACINTOSH == PROPSETHDR_OSVER_KIND(_pph->dwOSVer)
  1793. &&
  1794. guidSummary == *pfmtid
  1795. &&
  1796. IsEqualFMTIDByteSwap( *pfmtid, pfo->fmtid )
  1797. )
  1798. {
  1799. fSummaryInformation = TRUE;
  1800. }
  1801. else
  1802. {
  1803. _cSection = 0;
  1804. DebugTrace(0, DEBTRACE_ERROR, ("Bad FMTID\n"));
  1805. loadstate = LOADSTATE_BADFMTID;
  1806. goto Exit;
  1807. }
  1808. } // if (*pfmtid != pfo->fmtid)
  1809. } // else if (pfmtid != NULL)
  1810. _oSection = pfo->dwOffset;
  1811. _cSection = _pph->reserved;
  1812. } // if (_HasPropHeader())
  1813. psh = _GetSectionHeader();
  1814. if (cbstm < _oSection + CB_PROPERTYSECTIONHEADER ||
  1815. psh->cbSection < CB_PROPERTYSECTIONHEADER +
  1816. psh->cProperties * CB_PROPERTYIDOFFSET ||
  1817. cbstm < _oSection + CB_PROPERTYSECTIONHEADER +
  1818. psh->cProperties * CB_PROPERTYIDOFFSET ||
  1819. cbstm < _oSection + psh->cbSection)
  1820. {
  1821. _cSection = 0;
  1822. DebugTrace(0, Dbg, ("_LoadHeader: too small for section\n"));
  1823. goto Exit;
  1824. }
  1825. if (_HasPropHeader())
  1826. {
  1827. // Scan the property set for a code page, and set _CodePage.
  1828. _SearchForCodePage( pstatus );
  1829. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1830. // If we have multiple sections, record the tail length
  1831. // (the size of the property set beyond this section).
  1832. if (_cSection > 1)
  1833. {
  1834. _State |= CPSS_MULTIPLESECTIONS;
  1835. _cbTail = cbMin - (_oSection + psh->cbSection);
  1836. DebugTrace(0, Dbg, ("_LoadHeader: cbTail=%x\n", _cbTail));
  1837. }
  1838. // Fix all the problems we know how to fix in the in-memory
  1839. // representation of the property set.
  1840. if (fSummaryInformation || (_State & CPSS_DOCUMENTSUMMARYINFO))
  1841. {
  1842. if (fSummaryInformation)
  1843. {
  1844. _FixSummaryInformation(&cbstm, pstatus);
  1845. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1846. }
  1847. _FixPackedPropertySet( pstatus );
  1848. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1849. }
  1850. if (Mode == CREATEPROP_DELETE)
  1851. {
  1852. loadstate = LOADSTATE_USERDEFINEDDELETE;
  1853. goto Exit;
  1854. }
  1855. }
  1856. // ----
  1857. // Exit
  1858. // ----
  1859. loadstate = LOADSTATE_DONE;
  1860. Exit:
  1861. return( loadstate );
  1862. }
  1863. //+--------------------------------------------------------------------------
  1864. // Member: CPropertySetStream::_FixSummaryInformation
  1865. //
  1866. // Synopsis: Fix up the memory image of a SummaryInformation propset,
  1867. // except for packing or padding problems (which are fixed
  1868. // in _FixPackedPropertySet).
  1869. //
  1870. // Arguments: [pcbstm] - The size of the mapped stream. This may
  1871. // be updated by this routine.
  1872. // [pstatus] - Pointer to NTSTATUS code.
  1873. //
  1874. // Returns: None
  1875. //---------------------------------------------------------------------------
  1876. #define PID_THUMBNAIL 0x00000011 // SummaryInformation thumbnail property
  1877. VOID
  1878. CPropertySetStream::_FixSummaryInformation(IN OUT ULONG *pcbstm,
  1879. OUT NTSTATUS *pstatus)
  1880. {
  1881. PROPERTYSECTIONHEADER *psh;
  1882. PROPERTYIDOFFSET *ppo, *ppoMax;
  1883. *pstatus = STATUS_SUCCESS;
  1884. psh = _LoadPropertyOffsetPointers(&ppo, &ppoMax, pstatus);
  1885. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1886. // Look for the MS Publisher problem. Pub only writes
  1887. // a Thumbnail, but it sets the section size too short (by 4 bytes).
  1888. // Pub95 has the additional problem that it doesn't DWORD-align the
  1889. // section and stream size. We fix both of these problems below.
  1890. // Skip all of this processing unless it looks like the Publisher problem.
  1891. if (psh != NULL && *pcbstm == _oSection + psh->cbSection + sizeof(ULONG))
  1892. {
  1893. // Look for the thumbnail property.
  1894. for ( ; ppo < ppoMax; ppo++)
  1895. {
  1896. if (ppo->propid == PID_THUMBNAIL)
  1897. {
  1898. SERIALIZEDPROPERTYVALUE const *pprop;
  1899. // If this property isn't properly aligned, then ignore it.
  1900. if (ppo->dwOffset & (sizeof(DWORD) - 1))
  1901. {
  1902. break;
  1903. }
  1904. // Get a pointer to the property.
  1905. pprop = (SERIALIZEDPROPERTYVALUE *)
  1906. _MapOffsetToAddress(ppo->dwOffset);
  1907. // Look specifically for the Publisher's Thumbnail property.
  1908. // If this is a Publisher set, the lengths won't add
  1909. // up correctly. For the lengths to add up correctly,
  1910. // the offset of the property, plus
  1911. // the length of the thumbnail, plus the size of the VT
  1912. // DWORD and the size of the length DWORD should be the
  1913. // size of the Section. But In the case of Publisher,
  1914. // the section length is 4 bytes short.
  1915. if (PropByteSwap(pprop->dwType) == VT_CF // It's in a clipboard format
  1916. && // For Windows
  1917. *(ULONG *) &pprop->rgb[sizeof(ULONG)] == PropByteSwap((ULONG)MAXULONG)
  1918. &&
  1919. ppo->dwOffset + // And the lengths don't add up
  1920. PropByteSwap( *(ULONG *) pprop->rgb ) +
  1921. (3 - 2) * sizeof(ULONG) == psh->cbSection)
  1922. {
  1923. // We've found the Publisher problem.
  1924. // For Pub95 files, we must dword-align the section
  1925. // and stream size. We don't change the size of the underlying
  1926. // stream, however, just the mapping. This is because if the caller
  1927. // doesn't make any explicit changes, we don't want the mapped Stream
  1928. // to be modified. We do this step before fixing the section-size
  1929. // problem, so if it should fail we haven't touched anything.
  1930. if( !IsDwordAligned( *pcbstm ))
  1931. {
  1932. // Increase the size of the buffer, and reload the
  1933. // psh pointer.
  1934. *pcbstm += DwordRemain(*pcbstm);
  1935. _MSTM(SetSize)(*pcbstm, // The new size
  1936. FALSE, // Don't update the underlying stream
  1937. (VOID **) &_pph, // The new mapping
  1938. pstatus);
  1939. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1940. psh = _LoadPropertyOffsetPointers(&ppo, &ppoMax, pstatus);
  1941. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  1942. // Align the section size.
  1943. psh->cbSection += DwordRemain(psh->cbSection);
  1944. }
  1945. // Now correct the section size.
  1946. DebugTrace(0, DEBTRACE_PROPPATCH, (
  1947. "_FixSummaryInformation: Patch section size: %x->%x\n",
  1948. psh->cbSection,
  1949. psh->cbSection + sizeof(ULONG)));
  1950. psh->cbSection += sizeof(ULONG);
  1951. } // if (pprop->dwType == VT_CF ...
  1952. break;
  1953. } // if (ppo->propid == PID_THUMBNAIL)
  1954. } // for ( ; ppo < ppoMax; ppo++)
  1955. } // if (psh != NULL && cbstm == _oSection + psh->cbSection + sizeof(ULONG))
  1956. // ----
  1957. // Exit
  1958. // ----
  1959. Exit:
  1960. return;
  1961. }
  1962. //+--------------------------------------------------------------------------
  1963. // Member: CPropertySetStream::_FixPackedPropertySet
  1964. //
  1965. // Synopsis: Align the memory image of a propset.
  1966. //
  1967. // Algorithm: We need to move the properties within the
  1968. // property set so that they are properly aligned,
  1969. // and we need to adjust the PID/Offset array accordingly.
  1970. // This is complicated by the fact that we may have to
  1971. // grow some propertes (which are not properly padded
  1972. // for alignement) and at the same time we may have to
  1973. // shrink some properties (which are over-padded).
  1974. //
  1975. // To handle these two constraints, and to
  1976. // avoid growing the underlying stream any more
  1977. // than necessary, we process the property set in
  1978. // two phases. In the Compaction phase, we shrink
  1979. // properties which are over-padded. In the Expansion
  1980. // phase, we grow properties which are under-padded.
  1981. // For example, say we have a property set with 3
  1982. // properties, all of which should be 4 bytes. But
  1983. // say they are currently 2, 4, and 6 bytes. Thus
  1984. // we must grow the first property, hold the second
  1985. // constant, and shrink the third property. In this
  1986. // example, after the Compaction phase, the 3 properties
  1987. // will be 2, 4, and 4 bytes. After the Expansion phase,
  1988. // the properties will be 4, 4, and 4 bytes.
  1989. //
  1990. // To do all of this, we make a copy of the PID/Offset
  1991. // array (apoT) and sort it. We then proceed to make
  1992. // two arrays of just offsets (no PIDs) - aopropShrink
  1993. // and aopropFinal. aopropShrink holds the offset for
  1994. // each property after the Compaction phase. aopropFinal
  1995. // holds the offset for each property after the
  1996. // Expansion phase. (Note that each of these phases
  1997. // could be skipped if they aren't necessary.)
  1998. //
  1999. // Finally, we perform the Compaction and Expansion,
  2000. // using aopropShrink and aopropFinal, respectively,
  2001. // as our guide.
  2002. //
  2003. // Arguments: [pstatus] -- Pointer to NTSTATUS code.
  2004. //
  2005. // Returns: None
  2006. //---------------------------------------------------------------------------
  2007. INT __cdecl fnOffsetCompare(VOID const *ppo1, VOID const *ppo2);
  2008. // DocumentSummaryInformation special case properties (w/packed vector elements)
  2009. #define PID_HEADINGPAIR 0x0000000c // heading pair (VT_VECTOR | VT_VARIANT):
  2010. // {VT_LPSTR, VT_I4} pairs
  2011. #define PID_DOCPARTS 0x0000000d // docparts (VT_VECTOR | VT_LPSTR)
  2012. //#define PID_HLINKS 0x00000015 // hlinks vector
  2013. VOID
  2014. CPropertySetStream::_FixPackedPropertySet(OUT NTSTATUS *pstatus)
  2015. {
  2016. // ------
  2017. // Locals
  2018. // ------
  2019. BOOLEAN fPacked = FALSE;
  2020. BOOLEAN fDocSummaryInfo = FALSE;
  2021. #if DBGPROP
  2022. BOOLEAN fExpandDocSummaryInfo = FALSE;
  2023. #endif
  2024. PROPERTYSECTIONHEADER *psh = NULL;
  2025. PROPERTYIDOFFSET *ppoT, *ppoTMax;
  2026. PROPERTYIDOFFSET *ppo, *ppoBase, *ppoMax;
  2027. PROPERTYIDOFFSET *apoT = NULL;
  2028. ULONG *aopropShrink = NULL;
  2029. ULONG *aopropFinal = NULL;
  2030. ULONG cbprop;
  2031. ULONG cCompact, cExpand;
  2032. ULONG *poprop = NULL;
  2033. #if i386 == 0
  2034. SERIALIZEDPROPERTYVALUE *ppropbuf = NULL;
  2035. #endif
  2036. ULONG cbtotal = 0;
  2037. ULONG cbpropbuf;
  2038. // -----
  2039. // Begin
  2040. // -----
  2041. *pstatus = STATUS_SUCCESS;
  2042. // Determine if this is the first section of the DocSumInfo
  2043. // property set.
  2044. if ((_State & (CPSS_USERDEFINEDPROPERTIES | CPSS_DOCUMENTSUMMARYINFO)) ==
  2045. CPSS_DOCUMENTSUMMARYINFO)
  2046. {
  2047. fDocSummaryInfo = TRUE;
  2048. }
  2049. // Get pointers into this section's header.
  2050. psh = _LoadPropertyOffsetPointers(&ppoBase, &ppoMax, pstatus);
  2051. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  2052. // We know it's packed if the section-length isn't aligned.
  2053. fPacked = !IsDwordAligned(psh->cbSection);
  2054. // If we don't already know it's packed, check each of the properties in
  2055. // the PID/Offset array to see if one is not properly aligned, if so we'll
  2056. // assume that it's packed. Also, if this is an Ansi DocSumInfo property set,
  2057. // (first section), we'll assume that the HeadingPair and DocParts properties
  2058. // are packed (vectors).
  2059. if (!fPacked && psh != NULL)
  2060. {
  2061. for (ppo = ppoBase; ppo < ppoMax; ppo++)
  2062. {
  2063. if ( !IsDwordAligned(ppo->dwOffset)
  2064. ||
  2065. ( fDocSummaryInfo
  2066. &&
  2067. _CodePage != CP_WINUNICODE
  2068. &&
  2069. ( ppo->propid == PID_HEADINGPAIR
  2070. ||
  2071. ppo->propid == PID_DOCPARTS
  2072. )
  2073. )
  2074. )
  2075. {
  2076. fPacked = TRUE;
  2077. break;
  2078. }
  2079. }
  2080. }
  2081. // ----------------------------------------------------
  2082. // Fix the properties if they are packed or if there is
  2083. // unnecessary padding.
  2084. // ----------------------------------------------------
  2085. // If we know there's a problem, set a _State flag
  2086. // now. If we can fix the problem below, we'll clear it.
  2087. // Otherwise, the rest of the Class will know that there's
  2088. // an unresolved problem.
  2089. if (fPacked)
  2090. {
  2091. DebugTrace(0, DEBTRACE_PROPPATCH, (
  2092. "_FixPackedPropertySet: packed properties\n"));
  2093. _State |= CPSS_PACKEDPROPERTIES;
  2094. }
  2095. // ---------------------------------------------------------
  2096. // Create apoT (a sorted array of PID/Offsets), aopropShrink
  2097. // (the offsets for the Compaction phase) and aopropFinal
  2098. // (the offsets for the Expansion phase).
  2099. // ---------------------------------------------------------
  2100. // Create a buffer for a temporary PID/Offset array.
  2101. apoT = newk(mtPropSetStream, NULL) PROPERTYIDOFFSET[psh->cProperties + 1];
  2102. if (apoT == NULL)
  2103. {
  2104. *pstatus = STATUS_NO_MEMORY;
  2105. goto Exit;
  2106. }
  2107. // Copy the PID/offset pairs from the property set to the
  2108. // temporary PID/Offset array.
  2109. RtlCopyMemory(
  2110. apoT,
  2111. psh->rgprop,
  2112. psh->cProperties * CB_PROPERTYIDOFFSET);
  2113. // Mark the end of the temporary array.
  2114. ppoTMax = apoT + psh->cProperties;
  2115. ppoTMax->propid = PID_ILLEGAL;
  2116. ppoTMax->dwOffset = psh->cbSection;
  2117. // Sort the PID/Offset array by offset and check for overlapping values:
  2118. qsort(apoT, psh->cProperties, sizeof(apoT[0]), fnOffsetCompare);
  2119. // Create two arrays which will hold property offsets.
  2120. // aopropShrink holds the offsets for the Compaction phase where
  2121. // we shrink the property set. aopropFinal holds the offsets
  2122. // of the final property set, which will be achieved in the
  2123. // Expansion phase.
  2124. aopropShrink = newk(mtPropSetStream, NULL) ULONG[psh->cProperties + 1];
  2125. if (aopropShrink == NULL)
  2126. {
  2127. *pstatus = STATUS_NO_MEMORY;
  2128. goto Exit;
  2129. }
  2130. aopropFinal = newk(mtPropSetStream, NULL) ULONG[psh->cProperties + 1];
  2131. if (aopropFinal == NULL)
  2132. {
  2133. *pstatus = STATUS_NO_MEMORY;
  2134. goto Exit;
  2135. }
  2136. #if i386 == 0
  2137. // On non-x86 machines, we can't directly access unaligned
  2138. // properties. So, allocate enough (aligned) memory to hold
  2139. // the largest unaligned property. We'll copy properties here
  2140. // when we need to access them.
  2141. cbpropbuf = 0;
  2142. for (ppoT = apoT; ppoT < ppoTMax; ppoT++)
  2143. {
  2144. if (!IsDwordAligned(ppoT->dwOffset))
  2145. {
  2146. cbprop = DwordAlign(ppoT[1].dwOffset - ppoT->dwOffset);
  2147. if (cbpropbuf < cbprop)
  2148. {
  2149. cbpropbuf = cbprop;
  2150. }
  2151. }
  2152. }
  2153. if (cbpropbuf != 0)
  2154. {
  2155. ppropbuf = (SERIALIZEDPROPERTYVALUE *)
  2156. newk(mtPropSetStream, NULL) BYTE[cbpropbuf];
  2157. if (ppropbuf == NULL)
  2158. {
  2159. *pstatus = STATUS_NO_MEMORY;
  2160. goto Exit;
  2161. }
  2162. }
  2163. #endif // i386==0
  2164. // ----------------------------------------------
  2165. // Iterate through the properties, filling in the
  2166. // entries of aopropShrink and aopropFinal.
  2167. // ----------------------------------------------
  2168. // We'll also count the number of compacts and expands
  2169. // necessary.
  2170. aopropShrink[0] = aopropFinal[0] = apoT[0].dwOffset;
  2171. PROPASSERT(IsDwordAligned(aopropShrink[0]));
  2172. cExpand = 0;
  2173. cCompact = 0;
  2174. for (ppoT = apoT; ppoT < ppoTMax; ppoT++)
  2175. {
  2176. SERIALIZEDPROPERTYVALUE *pprop;
  2177. BOOLEAN fDocSumLengthComputed = FALSE;
  2178. ULONG cbpropOriginal;
  2179. // How much space does the property take up in the current
  2180. // property set?
  2181. cbpropOriginal = cbprop = ppoT[1].dwOffset - ppoT->dwOffset;
  2182. pprop = (SERIALIZEDPROPERTYVALUE *)
  2183. _MapOffsetToAddress(ppoT->dwOffset);
  2184. #if i386 == 0
  2185. // If necessary, put this property into an aligned buffer.
  2186. if (!IsDwordAligned(ppoT->dwOffset))
  2187. {
  2188. DebugTrace(0, Dbg, (
  2189. "_FixPackedPropertySet: unaligned pid=%x off=%x\n",
  2190. ppoT->propid,
  2191. ppoT->dwOffset));
  2192. PROPASSERT(DwordAlign(cbprop) <= cbpropbuf);
  2193. RtlCopyMemory((VOID *) ppropbuf, pprop, cbprop);
  2194. pprop = ppropbuf;
  2195. }
  2196. #endif
  2197. // Calculate the actual length of this property, including
  2198. // the necessary padding. This might be bigger than the
  2199. // property's current length (if the propset wasn't properly
  2200. // padded), and it might be smaller than the property's current
  2201. // length (if the propset was over-padded).
  2202. if (ppoT->propid == PID_DICTIONARY)
  2203. {
  2204. // Get the size of the dictionary.
  2205. cbprop = DwordAlign(_DictionaryLength(
  2206. (DICTIONARY const *) pprop,
  2207. cbprop,
  2208. pstatus));
  2209. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  2210. }
  2211. else
  2212. {
  2213. ULONG cbpropT;
  2214. // Ansi DocSumInfo property sets have two vector properties
  2215. // which are packed. If this is one of those properties,
  2216. // we won't fix it yet, but we'll compute the size required
  2217. // when the elements are un-packed.
  2218. if (fDocSummaryInfo && _CodePage != CP_WINUNICODE)
  2219. {
  2220. if (ppoT->propid == PID_HEADINGPAIR)
  2221. {
  2222. fDocSumLengthComputed = _FixHeadingPairVector(
  2223. PATCHOP_COMPUTESIZE,
  2224. pprop,
  2225. &cbpropT);
  2226. }
  2227. else
  2228. if (ppoT->propid == PID_DOCPARTS)
  2229. {
  2230. fDocSumLengthComputed = _FixDocPartsVector(
  2231. PATCHOP_COMPUTESIZE,
  2232. pprop,
  2233. &cbpropT);
  2234. }
  2235. }
  2236. // If we computed a length above, use it, otherwise calculate
  2237. // the length using the standard rules (we've already checked
  2238. // for the special cases).
  2239. if (fDocSumLengthComputed)
  2240. {
  2241. cbprop = cbpropT;
  2242. #if DBGPROP
  2243. fExpandDocSummaryInfo = TRUE;
  2244. #endif
  2245. }
  2246. else
  2247. {
  2248. cbprop = PropertyLength(pprop, DwordAlign(cbprop), 0, pstatus);
  2249. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  2250. }
  2251. } // if (ppoT->propid == PID_DICTIONARY) ... else
  2252. PROPASSERT(IsDwordAligned(cbprop));
  2253. // Now that we know the actual cbprop, use it to update the
  2254. // *next* entry in the two arrays of correct offsets.
  2255. //
  2256. // We want aopropFinal to hold the final, correct offsets,
  2257. // so we'll use cbprop to calculate this array.
  2258. // But for aopropShrink, we only want it to differ from
  2259. // the original array (apoT) when a property is shrinking,
  2260. // so we'll use min(cbNew,cbOld) for this array.
  2261. poprop = &aopropShrink[ ppoT - apoT ]; // 1st do aopropShrink
  2262. poprop[1] = poprop[0] + min(cbprop, cbpropOriginal);
  2263. poprop = &aopropFinal[ ppoT - apoT ]; // 2nd do aopropFinal
  2264. poprop[1] = poprop[0] + cbprop;
  2265. DebugTrace(0, Dbg, (
  2266. "_FixPackedPropertySet: pid=%x off=%x->%x\n",
  2267. ppoT->propid,
  2268. ppoT->dwOffset,
  2269. poprop[0],
  2270. poprop[0] < ppoT->dwOffset?
  2271. " (compact)" :
  2272. poprop[0] > ppoT->dwOffset? " (expand)" : ""));
  2273. // Is this compaction or an expansion?
  2274. // If we computed the doc-sum length, we count it as
  2275. // an expansion, even if the total property size didn't change,
  2276. // because we need the expand the elements within the vector.
  2277. if (cbprop < cbpropOriginal)
  2278. {
  2279. cCompact++;
  2280. }
  2281. else
  2282. if (cbprop > cbpropOriginal || fDocSumLengthComputed)
  2283. {
  2284. cExpand++;
  2285. }
  2286. } // for (ppoT = apoT; ppoT < ppoTMax; ppoT++)
  2287. // -------------------------------
  2288. // Compact/Expand the Property Set
  2289. // -------------------------------
  2290. // We've now generated the complete aopropShrink and aopropFinal
  2291. // offset arrays. Now, if necessary, let's expand and/or compact
  2292. // the property set to match these offsets.
  2293. if (cExpand || cCompact)
  2294. {
  2295. ULONG cbstm;
  2296. LONG cbdelta;
  2297. cbstm = _oSection + psh->cbSection + _cbTail;
  2298. cbdelta = aopropFinal[psh->cProperties] - psh->cbSection;
  2299. DebugTrace(0, Dbg, (
  2300. "_FixPackedPropertySet: cbstm=%x cbdelta=%x cexpand=%x ccompact=%x\n",
  2301. cbstm,
  2302. cbdelta,
  2303. cExpand,
  2304. cCompact));
  2305. // -----------------------------
  2306. // Grow the Stream if necessary.
  2307. // -----------------------------
  2308. if (cbdelta > 0)
  2309. {
  2310. DebugTrace(0, Dbg, (
  2311. "SetSize(%x) _FixPackedPropertySet grow %x bytes\n",
  2312. cbstm + cbdelta,
  2313. cbdelta));
  2314. // On the set-size, say that this is a non-persistent
  2315. // change, so that the underlying Stream isn't modified.
  2316. // At this point, we don't know if this change should remain
  2317. // permanent (if the caller closes without making any changes
  2318. // the file should remain un-changed).
  2319. _MSTM(SetSize)(
  2320. cbstm + cbdelta,
  2321. FALSE, // Not persistent
  2322. (VOID **) &_pph,
  2323. pstatus);
  2324. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  2325. // reload all pointers into mapped image:
  2326. psh = _LoadPropertyOffsetPointers(&ppoBase, &ppoMax, pstatus);
  2327. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  2328. // If there's another section after this one, move it back
  2329. // to the end of the stream now, which will create room for
  2330. // our expansion.
  2331. if (_cbTail != 0)
  2332. {
  2333. VOID *pvSrc = _MapAbsOffsetToAddress(cbstm - _cbTail);
  2334. PropMoveMemory(
  2335. "_FixPackedPropertySet(_cbTail:grow)",
  2336. psh,
  2337. Add2Ptr(pvSrc, cbdelta),
  2338. pvSrc,
  2339. _cbTail);
  2340. }
  2341. } // if (cbdelta > 0)
  2342. // This previous step (growing the Stream), was the last one which can
  2343. // fail. We're about to modify the actual property set (we've been
  2344. // working only with temporary buffers so far). So we're always guaranteed
  2345. // a good property set, or the original set, we'll never end up with a
  2346. // half-updated set.
  2347. // ----------------
  2348. // Compaction Phase
  2349. // ----------------
  2350. // Compact the property set if necessary. I.e., adjust
  2351. // the property set buffer so that it matches aopropShrink.
  2352. if (cCompact > 0)
  2353. {
  2354. // Start at the beginning and move each property up.
  2355. poprop = aopropShrink;
  2356. for (ppoT = apoT; ppoT < ppoTMax; ppoT++, poprop++)
  2357. {
  2358. if (*poprop != ppoT->dwOffset)
  2359. {
  2360. PROPASSERT(*poprop < ppoT->dwOffset);
  2361. PROPASSERT(poprop[1] > *poprop);
  2362. // We're compacting; the property should not grow!
  2363. PROPASSERT(
  2364. poprop[1] - *poprop <=
  2365. ppoT[1].dwOffset - ppoT->dwOffset);
  2366. PropMoveMemory(
  2367. "_FixPackedPropertySet(compact)",
  2368. psh,
  2369. Add2Ptr(psh, *poprop),
  2370. Add2Ptr(psh, ppoT->dwOffset),
  2371. poprop[1] - *poprop);
  2372. }
  2373. } // for (ppoT = apoT; ppoT < ppoTMax; ppoT++, poprop++)
  2374. } // if (cCompact > 0)
  2375. // ---------------
  2376. // Expansion phase
  2377. // ---------------
  2378. // Recall that, whether or not we just did a compaction, aopropShrink
  2379. // holds the property set offsets as they currently exist in the
  2380. // property set.
  2381. if (cExpand > 0)
  2382. {
  2383. // Start at the end and move each property back.
  2384. // The 'poprop' gives us the final correct offset
  2385. // of the current property.
  2386. LONG lOffsetIndex;
  2387. poprop = &aopropFinal[psh->cProperties - 1];
  2388. // Start at the second-to-last entry in the arrays of offsets
  2389. // (the last entry is an artificially added one to mark the end of the
  2390. // property set).
  2391. for (lOffsetIndex = ppoTMax - apoT - 1, ppoT = ppoTMax - 1;
  2392. lOffsetIndex >=0;
  2393. lOffsetIndex--, poprop--, ppoT--)
  2394. {
  2395. // Get a pointer to the final location of this
  2396. // property.
  2397. SERIALIZEDPROPERTYVALUE *pprop;
  2398. pprop = (SERIALIZEDPROPERTYVALUE *)
  2399. Add2Ptr(psh, *poprop);
  2400. if (*poprop != aopropShrink[ lOffsetIndex ])
  2401. {
  2402. ULONG cbCopy, cbOld;
  2403. PROPASSERT(*poprop > aopropShrink[ lOffsetIndex ]);
  2404. PROPASSERT(poprop[1] > *poprop);
  2405. PROPASSERT(aopropShrink[ lOffsetIndex+1 ] > aopropShrink[ lOffsetIndex ]);
  2406. // How many bytes should we copy? The minimum size of the property
  2407. // calculated using the old and new offsets.
  2408. cbCopy = poprop[1] - poprop[0];
  2409. cbOld = aopropShrink[ lOffsetIndex+1 ]
  2410. - aopropShrink[ lOffsetIndex+0 ];
  2411. if (cbCopy > cbOld)
  2412. {
  2413. cbCopy = cbOld;
  2414. }
  2415. // Copy the property from its old location
  2416. // (psh+aopropShrink[lOffsetIndex]) to its new location
  2417. // (pprop == psh+*poprop).
  2418. DebugTrace(0, DEBTRACE_PROPPATCH, (
  2419. "_FixPackedPropertySet:move pid=%x off=%x->%x "
  2420. "cb=%x->%x cbCopy=%x z=%x @%x\n",
  2421. ppoT->propid,
  2422. ppoT->dwOffset,
  2423. *poprop,
  2424. cbOld,
  2425. poprop[1] - *poprop,
  2426. cbCopy,
  2427. DwordRemain(cbCopy),
  2428. _MapAddressToOffset(Add2Ptr(pprop, cbCopy))));
  2429. PropMoveMemory(
  2430. "_FixPackedPropertySet(expand)",
  2431. psh,
  2432. pprop,
  2433. Add2Ptr(psh, aopropShrink[ lOffsetIndex ]),
  2434. cbCopy);
  2435. RtlZeroMemory(
  2436. Add2Ptr(pprop, cbCopy),
  2437. DwordRemain(cbCopy));
  2438. } // if (*poprop != ppoT->dwOffset)
  2439. // If this is an older DocSumInfo property set,
  2440. // and this property is one of the vector values,
  2441. // we must expand the vector elements now that we've
  2442. // room for it.
  2443. if (fDocSummaryInfo && _CodePage != CP_WINUNICODE)
  2444. {
  2445. ULONG cbpropT;
  2446. if (ppoT->propid == PID_HEADINGPAIR)
  2447. {
  2448. _FixHeadingPairVector(
  2449. PATCHOP_EXPAND,
  2450. pprop,
  2451. &cbpropT);
  2452. }
  2453. else
  2454. if (ppoT->propid == PID_DOCPARTS)
  2455. {
  2456. _FixDocPartsVector(
  2457. PATCHOP_EXPAND,
  2458. pprop,
  2459. &cbpropT);
  2460. }
  2461. } // if (fDocSummaryInfo)
  2462. } // for (ppoT = ppoTMax; --ppoT >= apoT; popropNew--)
  2463. } // if (cExpand != 0)
  2464. // ---------------------------------------------------------
  2465. // Patch the section size and the moved properties' offsets.
  2466. // ---------------------------------------------------------
  2467. DebugTrace(0, DEBTRACE_PROPPATCH, (
  2468. "_FixPackedPropertySet: Patch section size %x->%x\n",
  2469. psh->cbSection,
  2470. psh->cbSection + cbdelta));
  2471. psh->cbSection += cbdelta;
  2472. // Iterate through the original PID/Offset array to update the
  2473. // offsets.
  2474. for (ppo = ppoBase; ppo < ppoMax; ppo++)
  2475. {
  2476. // Search the temporary PID/Offset array (which has the updated
  2477. // offsets) for ppo->propid.
  2478. for (ppoT = apoT; ppoT < ppoTMax; ppoT++)
  2479. {
  2480. if (ppo->propid == ppoT->propid)
  2481. {
  2482. // We've found ppo->propid in the temporary PID/Offset
  2483. // array. Copy the offset value from the temporary array
  2484. // to the actual array in the property set.
  2485. PROPASSERT(ppo->dwOffset == ppoT->dwOffset);
  2486. ppo->dwOffset = aopropFinal[ppoT - apoT];
  2487. #if DBGPROP
  2488. if (ppo->dwOffset != ppoT->dwOffset)
  2489. {
  2490. DebugTrace(0, DEBTRACE_PROPPATCH, (
  2491. "_FixPackedPropertySet: Patch propid %x"
  2492. " offset=%x->%x\n",
  2493. ppo->propid,
  2494. ppoT->dwOffset,
  2495. ppo->dwOffset));
  2496. } // if (ppo->dwOffset != ppoT->dwOffset)
  2497. #endif
  2498. break;
  2499. } // if (ppo->propid == ppoT->propid)
  2500. } // for (ppoT = apoT; ppoT < ppoTMax; ppoT++)
  2501. } // for (ppo = ppoBase; ppo < ppoMax; ppo++)
  2502. // ------------
  2503. // Fix the tail
  2504. // ------------
  2505. // If we have a tail, fix it's offset in the FmtID/Offset
  2506. // array. Also, if we've overall shrunk this section, bring
  2507. // the tail in accordingly.
  2508. if (_cbTail != 0)
  2509. {
  2510. if (cbdelta < 0)
  2511. {
  2512. VOID *pvSrc = _MapAbsOffsetToAddress(cbstm - _cbTail);
  2513. PropMoveMemory(
  2514. "_FixPackedPropertySet(_cbTail:shrink)",
  2515. psh,
  2516. Add2Ptr(pvSrc, cbdelta),
  2517. pvSrc,
  2518. _cbTail);
  2519. }
  2520. _PatchSectionOffsets(cbdelta);
  2521. } // if (_cbTail != 0)
  2522. // If we get to this point we've successfully un-packed (or
  2523. // un-over-padded) the property set, so we can clear the
  2524. // state flag.
  2525. _State &= ~CPSS_PACKEDPROPERTIES;
  2526. } // if (cExpand || cCompact)
  2527. // ----
  2528. // Exit
  2529. // ----
  2530. Exit:
  2531. delete [] apoT;
  2532. delete [] aopropShrink;
  2533. delete [] aopropFinal;
  2534. #if i386 == 0
  2535. delete [] (BYTE *) ppropbuf;
  2536. #endif // i386
  2537. } // CPropertySetStream::_FixPackedPropertySet()
  2538. //+--------------------------------------------------------------------------
  2539. // Member: CPropertySetStream::_FixDocPartsVector
  2540. //
  2541. // Synopsis: Align the memory image of a DocParts vector
  2542. // The DocParts property is part of the DocSumInfo
  2543. // property set (first section). It is a vector
  2544. // of strings, and in Ansi property sets it's packed
  2545. // and must be un-packed.
  2546. //
  2547. // Arguments: [PatchOp] -- patch request
  2548. // [pprop] -- property value to be patched or sized
  2549. // [pcbprop] -- pointer to computed property length
  2550. //
  2551. // Returns: TRUE if property type and all elements meet expectations;
  2552. // FALSE on error
  2553. //
  2554. // Note: Operate on a DocumentSummaryInformation first section property,
  2555. // PID_DOCPARTS. This property is assumed to be an array of
  2556. // VT_LPSTRs.
  2557. //
  2558. // PATCHOP_COMPUTESIZE merely computes the size required to unpack
  2559. // the property, and must assume it is currently unaligned.
  2560. //
  2561. // PATCHOP_ALIGNLENGTHS patches all VT_LPSTR lengths to DWORD
  2562. // multiples, and may rely on the property already being aligned.
  2563. //
  2564. // PATCHOP_EXPAND expands the property from the Src to Dst buffer,
  2565. // moving elements to DWORD boundaries, and patching VT_LPSTR
  2566. // lengths to DWORD multiples. The Src buffer is assumed to be
  2567. // unaligned, and the Dst buffer is assumed to be properly sized.
  2568. //---------------------------------------------------------------------------
  2569. BOOLEAN
  2570. CPropertySetStream::_FixDocPartsVector(
  2571. IN PATCHOP PatchOp,
  2572. IN OUT SERIALIZEDPROPERTYVALUE *pprop,
  2573. OUT ULONG *pcbprop)
  2574. {
  2575. PROPASSERT(
  2576. PatchOp == PATCHOP_COMPUTESIZE ||
  2577. PatchOp == PATCHOP_ALIGNLENGTHS ||
  2578. PatchOp == PATCHOP_EXPAND);
  2579. PROPASSERT(pprop != NULL);
  2580. PROPASSERT(pcbprop != NULL);
  2581. // If the property is a variant vector,
  2582. // it's in an ANSI property set, and
  2583. // there are an even number of elements, ...
  2584. if ( PropByteSwap(pprop->dwType) == (VT_VECTOR | VT_LPSTR)
  2585. &&
  2586. _CodePage != CP_WINUNICODE)
  2587. {
  2588. ULONG cString;
  2589. VOID *pv;
  2590. cString = PropByteSwap( *(DWORD *) pprop->rgb );
  2591. pv = Add2Ptr(pprop->rgb, sizeof(DWORD));
  2592. if (_FixDocPartsElements(PatchOp, cString, pv, pv, pcbprop))
  2593. {
  2594. *pcbprop += CB_SERIALIZEDPROPERTYVALUE + sizeof(ULONG);
  2595. return(TRUE);
  2596. }
  2597. }
  2598. return(FALSE); // Not a recognizable DocParts vector
  2599. }
  2600. //+--------------------------------------------------------------------------
  2601. // Member: CPropertySetStream::_FixDocPartsElements
  2602. //
  2603. // Synopsis: Recursively align the memory image of DocParts elements
  2604. //
  2605. // Arguments: [PatchOp] -- patch request
  2606. // [cString] -- count of strings remaining in the vector
  2607. // [pvDst] -- aligned overlapping destination buffer
  2608. // [pvSrc] -- unaligned overlapping source buffer
  2609. // [pcbprop] -- pointer to computed property length
  2610. //
  2611. // Returns: TRUE if all remaining elements meet expectations;
  2612. // FALSE on error
  2613. //
  2614. // Note: The pvDst & pvSrc buffers must be in property-set
  2615. // byte order (little endian).
  2616. //---------------------------------------------------------------------------
  2617. BOOLEAN
  2618. CPropertySetStream::_FixDocPartsElements(
  2619. IN PATCHOP PatchOp,
  2620. IN ULONG cString,
  2621. OUT VOID *pvDst,
  2622. IN VOID UNALIGNED const *pvSrc,
  2623. OUT ULONG *pcbprop)
  2624. {
  2625. ULONG cb;
  2626. PROPASSERT(
  2627. PatchOp == PATCHOP_COMPUTESIZE ||
  2628. PatchOp == PATCHOP_ALIGNLENGTHS ||
  2629. PatchOp == PATCHOP_EXPAND);
  2630. PROPASSERT(pvDst >= pvSrc);
  2631. PROPASSERT(PatchOp != PATCHOP_ALIGNLENGTHS || pvDst == pvSrc);
  2632. if (cString == 0)
  2633. {
  2634. *pcbprop = 0;
  2635. return(TRUE);
  2636. }
  2637. DWORD dwTemp;
  2638. memcpy(&dwTemp, pvSrc, sizeof(DWORD));
  2639. cb = sizeof(DWORD) + PropByteSwap( dwTemp );
  2640. // If the caller serialized the vector properly, all we need to do is
  2641. // to round up the string lengths to DWORD multiples, so readers that
  2642. // treat these vectors as byte-aligned get faked out. We expect
  2643. // readers will not have problems with a DWORD aligned length, and a
  2644. // '\0' character a few bytes earlier than the length indicates.
  2645. if (PatchOp == PATCHOP_ALIGNLENGTHS)
  2646. {
  2647. cb = DwordAlign(cb); // Caller says it's already aligned
  2648. }
  2649. if (_FixDocPartsElements(
  2650. PatchOp,
  2651. cString - 1,
  2652. Add2Ptr(pvDst, DwordAlign(cb)),
  2653. (VOID UNALIGNED const *) Add2ConstPtr(pvSrc, cb),
  2654. pcbprop))
  2655. {
  2656. *pcbprop += DwordAlign(cb);
  2657. if (PatchOp == PATCHOP_EXPAND)
  2658. {
  2659. PropMoveMemory(
  2660. "_FixDocPartsElements",
  2661. _GetSectionHeader(),
  2662. pvDst,
  2663. pvSrc,
  2664. cb);
  2665. RtlZeroMemory(Add2Ptr(pvDst, cb), DwordRemain(cb));
  2666. DebugTrace(0, DEBTRACE_PROPPATCH, (
  2667. "_FixDocPartsElements: Move(%x:%s) "
  2668. "cb=%x->%x off=%x->%x z=%x @%x\n",
  2669. cString,
  2670. Add2Ptr(pvDst, sizeof(ULONG)),
  2671. cb - sizeof(ULONG),
  2672. DwordAlign(cb) - sizeof(ULONG),
  2673. _MapAddressToOffset(pvSrc),
  2674. _MapAddressToOffset(pvDst),
  2675. DwordRemain(cb),
  2676. _MapAddressToOffset(Add2Ptr(pvDst, cb))));
  2677. }
  2678. if (PatchOp != PATCHOP_COMPUTESIZE)
  2679. {
  2680. PROPASSERT(
  2681. PatchOp == PATCHOP_ALIGNLENGTHS ||
  2682. PatchOp == PATCHOP_EXPAND);
  2683. DebugTrace(0, DEBTRACE_PROPPATCH, (
  2684. "_FixDocPartsElements: Patch(%x:%s) cb=%x->%x\n",
  2685. cString,
  2686. Add2Ptr(pvDst, sizeof(ULONG)),
  2687. *(ULONG *) pvDst,
  2688. DwordAlign(*(ULONG *) pvDst)));
  2689. // put in the length
  2690. *(ULONG *) pvDst =
  2691. PropByteSwap( (DWORD) DwordAlign( PropByteSwap(*(ULONG *) pvDst) ) );
  2692. }
  2693. return(TRUE);
  2694. }
  2695. return(FALSE); // Not a recognizable DocParts vector
  2696. }
  2697. //+--------------------------------------------------------------------------
  2698. // Member: CPropertySetStream::_FixHeadingPairVector
  2699. //
  2700. // Synopsis: Align the memory image of a HeadingPair vector.
  2701. // The HeadingPair property is part of the DocSumInfo
  2702. // property set (first section). It's a vector of
  2703. // Variants, where the elements are alternating
  2704. // strings and I4s (the string is a heading name,
  2705. // and the I4 is the count of DocumentParts in that
  2706. // heading). In Ansi property sets, these elements
  2707. // are packed, and must be un-packed.
  2708. //
  2709. // Arguments: [PatchOp] -- patch request
  2710. // [pprop] -- property value to be patched or sized
  2711. // [pcbprop] -- pointer to computed property length
  2712. //
  2713. // Returns: TRUE if property and all elements meet expectations;
  2714. // FALSE on error
  2715. //
  2716. // Note: Operate on a DocumentSummaryInformation first section property,
  2717. // PID_HEADINGPAIR. This property is assumed to be an array of
  2718. // VT_VARIANTs with an even number of elements. Each pair must
  2719. // consist of a VT_LPSTR followed by a VT_I4.
  2720. //
  2721. // PATCHOP_COMPUTESIZE merely computes the size required to unpack
  2722. // the property, and must assume it is currently unaligned.
  2723. //
  2724. // PATCHOP_ALIGNLENGTHS patches all VT_LPSTR lengths to DWORD
  2725. // multiples, and may rely on the property already being aligned.
  2726. //
  2727. // PATCHOP_EXPAND expands the property from the Src to Dst buffer,
  2728. // moving elements to DWORD boundaries, and patching VT_LPSTR
  2729. // lengths to DWORD multiples. The Src buffer is assumed to be
  2730. // unaligned, and the Dst buffer is assumed to be properly sized.
  2731. //---------------------------------------------------------------------------
  2732. BOOLEAN
  2733. CPropertySetStream::_FixHeadingPairVector(
  2734. IN PATCHOP PatchOp,
  2735. IN OUT SERIALIZEDPROPERTYVALUE *pprop,
  2736. OUT ULONG *pcbprop)
  2737. {
  2738. ULONG celem;
  2739. ULONG cbprop = 0;
  2740. PROPASSERT(
  2741. PatchOp == PATCHOP_COMPUTESIZE ||
  2742. PatchOp == PATCHOP_ALIGNLENGTHS ||
  2743. PatchOp == PATCHOP_EXPAND);
  2744. PROPASSERT(pprop != NULL);
  2745. PROPASSERT(pcbprop != NULL);
  2746. // If the property is a variant vector, and
  2747. // there are an even number of elements, ...
  2748. if( PropByteSwap(pprop->dwType) == (VT_VECTOR | VT_VARIANT)
  2749. &&
  2750. ( (celem = PropByteSwap(*(ULONG *) pprop->rgb) ) & 1) == 0
  2751. &&
  2752. _CodePage != CP_WINUNICODE)
  2753. {
  2754. pprop = (SERIALIZEDPROPERTYVALUE *) Add2Ptr(pprop->rgb, sizeof(ULONG));
  2755. if (_FixHeadingPairElements(PatchOp, celem/2, pprop, pprop, pcbprop))
  2756. {
  2757. *pcbprop += CB_SERIALIZEDPROPERTYVALUE + sizeof(ULONG);
  2758. return(TRUE);
  2759. }
  2760. }
  2761. return(FALSE); // Not a recognizable HeadingPair vector
  2762. }
  2763. //+--------------------------------------------------------------------------
  2764. // Member: CPropertySetStream::_FixHeadingPairElements
  2765. //
  2766. // Synopsis: Recursively align the memory image of HeadingPair elements
  2767. //
  2768. // Arguments: [PatchOp] -- patch request
  2769. // [cPairs] -- count of heading pairs remaining
  2770. // [ppropDst] -- aligned overlapping destination buffer
  2771. // [ppropSrc] -- unaligned overlapping source buffer
  2772. // [pcbprop] -- pointer to computed property length
  2773. //
  2774. // Returns: TRUE if all remaining elements meet expectations;
  2775. // FALSE on error
  2776. //---------------------------------------------------------------------------
  2777. BOOLEAN
  2778. CPropertySetStream::_FixHeadingPairElements(
  2779. IN PATCHOP PatchOp,
  2780. IN ULONG cPairs,
  2781. OUT SERIALIZEDPROPERTYVALUE *ppropDst,
  2782. IN SERIALIZEDPROPERTYVALUE UNALIGNED const *ppropSrc,
  2783. OUT ULONG *pcbprop)
  2784. {
  2785. PROPASSERT(
  2786. PatchOp == PATCHOP_COMPUTESIZE ||
  2787. PatchOp == PATCHOP_ALIGNLENGTHS ||
  2788. PatchOp == PATCHOP_EXPAND);
  2789. PROPASSERT(ppropDst >= ppropSrc);
  2790. PROPASSERT(PatchOp != PATCHOP_ALIGNLENGTHS || ppropDst == ppropSrc);
  2791. if (cPairs == 0)
  2792. {
  2793. *pcbprop = 0;
  2794. return(TRUE);
  2795. }
  2796. // If the first element of the pair is a VT_LPSTR, ...
  2797. if( PropByteSwap(SPV_GetType(ppropSrc)) == VT_LPSTR )
  2798. {
  2799. ULONG cb;
  2800. // Compute size of the string element.
  2801. DWORD dwStrLen;
  2802. memcpy(&dwStrLen, SPV_GetRgb(ppropSrc), sizeof(DWORD));
  2803. dwStrLen = PropByteSwap(dwStrLen);
  2804. cb = CB_SERIALIZEDPROPERTYVALUE
  2805. +
  2806. sizeof(ULONG)
  2807. +
  2808. dwStrLen;
  2809. // If the caller serialized the vector properly, all we need to do is
  2810. // to round up the string lengths to DWORD multiples, so readers that
  2811. // treat these vectors as byte-aligned get faked out. We expect
  2812. // readers will not have problems with a DWORD aligned length, and a
  2813. // '\0' character a few bytes earlier than the length indicates.
  2814. if (PatchOp == PATCHOP_ALIGNLENGTHS)
  2815. {
  2816. cb = DwordAlign(cb); // Caller says it's already aligned
  2817. }
  2818. // and if the second element of the pair is a VT_I4, ...
  2819. if ( PropByteSwap( (DWORD) VT_I4 )
  2820. ==
  2821. SPV_GetType(
  2822. (SERIALIZEDPROPERTYVALUE UNALIGNED const *)
  2823. Add2ConstPtr(ppropSrc, cb)
  2824. ))
  2825. {
  2826. cb += CB_SERIALIZEDPROPERTYVALUE + sizeof(DWORD);
  2827. if (_FixHeadingPairElements(
  2828. PatchOp,
  2829. cPairs - 1,
  2830. (SERIALIZEDPROPERTYVALUE *)
  2831. Add2Ptr(ppropDst, DwordAlign(cb)),
  2832. (SERIALIZEDPROPERTYVALUE UNALIGNED const *)
  2833. Add2ConstPtr(ppropSrc, cb),
  2834. pcbprop))
  2835. {
  2836. *pcbprop += DwordAlign(cb);
  2837. if (PatchOp == PATCHOP_EXPAND)
  2838. {
  2839. // Move the unaligned VT_I4 property back in memory to an
  2840. // aligned boundary, move the string back to a (possibly
  2841. // different) aligned boundary, zero the space in between
  2842. // the two and patch the string length to be a DWORD
  2843. // multiple to fake out code that expects vector elements
  2844. // to be byte aligned.
  2845. // Adjust byte count to include just the string element.
  2846. cb -= CB_SERIALIZEDPROPERTYVALUE + sizeof(ULONG);
  2847. // Move the VT_I4 element.
  2848. PropMoveMemory(
  2849. "_FixHeadingPairElements:I4",
  2850. _GetSectionHeader(),
  2851. Add2Ptr(ppropDst, DwordAlign(cb)),
  2852. Add2ConstPtr(ppropSrc, cb),
  2853. CB_SERIALIZEDPROPERTYVALUE + sizeof(ULONG));
  2854. // Move the VT_LPSTR element.
  2855. PropMoveMemory(
  2856. "_FixHeadingPairElements:LPSTR",
  2857. _GetSectionHeader(),
  2858. ppropDst,
  2859. ppropSrc,
  2860. cb);
  2861. // Zero the space in between.
  2862. RtlZeroMemory(Add2Ptr(ppropDst, cb), DwordRemain(cb));
  2863. DebugTrace(0, DEBTRACE_PROPPATCH, (
  2864. "_FixHeadingPairElements: Move(%x:%s) "
  2865. "cb=%x->%x off=%x->%x z=%x @%x\n",
  2866. cPairs,
  2867. &ppropDst->rgb[sizeof(ULONG)],
  2868. PropByteSwap( *(ULONG *) ppropDst->rgb ),
  2869. DwordAlign(PropByteSwap( *(ULONG *) ppropDst->rgb )),
  2870. _MapAddressToOffset(ppropSrc),
  2871. _MapAddressToOffset(ppropDst),
  2872. DwordRemain(cb),
  2873. _MapAddressToOffset(Add2Ptr(ppropDst, cb))));
  2874. }
  2875. if (PatchOp != PATCHOP_COMPUTESIZE)
  2876. {
  2877. PROPASSERT(
  2878. PatchOp == PATCHOP_ALIGNLENGTHS ||
  2879. PatchOp == PATCHOP_EXPAND);
  2880. #ifdef DBGPROP
  2881. SERIALIZEDPROPERTYVALUE const *ppropT =
  2882. (SERIALIZEDPROPERTYVALUE const *)
  2883. Add2Ptr(ppropDst, DwordAlign(cb));
  2884. #endif
  2885. DebugTrace(0, DEBTRACE_PROPPATCH, (
  2886. "_FixHeadingPairElements: Patch(%x:%s) "
  2887. "cb=%x->%x, vt=%x, %x\n",
  2888. cPairs,
  2889. &ppropDst->rgb[sizeof(ULONG)],
  2890. PropByteSwap( *(ULONG *) ppropDst->rgb ),
  2891. DwordAlign( PropByteSwap( *(ULONG *) ppropDst->rgb )),
  2892. PropByteSwap( ppropT->dwType ),
  2893. PropByteSwap( *(ULONG *) ppropT->rgb )));
  2894. // Patch the string length to be a DWORD multiple.
  2895. // and we have to put back the swapped length
  2896. *(ULONG *) ppropDst->rgb
  2897. = PropByteSwap( (DWORD) DwordAlign( PropByteSwap( *(ULONG *) ppropDst->rgb )));
  2898. }
  2899. return(TRUE);
  2900. }
  2901. }
  2902. }
  2903. return(FALSE); // Not a recognizable HeadingPair vector
  2904. }
  2905. //+--------------------------------------------------------------------------
  2906. // Member: CPropertySetStream::QueryPropertySet
  2907. //
  2908. // Synopsis: Return the classid for the property set code
  2909. //
  2910. // Arguments: [pspss] -- pointer to buffer for output
  2911. // [pstatus] -- pointer to NTSTATUS code
  2912. //
  2913. // Returns: None
  2914. //---------------------------------------------------------------------------
  2915. VOID
  2916. CPropertySetStream::QueryPropertySet(OUT STATPROPSETSTG *pspss,
  2917. OUT NTSTATUS *pstatus) const
  2918. {
  2919. *pstatus = STATUS_SUCCESS;
  2920. PROPASSERT(_IsMapped());
  2921. PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder);
  2922. if ((_State & CPSS_USERDEFINEDDELETED) || _cSection < 1)
  2923. {
  2924. StatusAccessDenied(pstatus, "QueryPropertySet: deleted or no section");
  2925. goto Exit;
  2926. }
  2927. _MSTM(QueryTimeStamps)(
  2928. pspss,
  2929. (BOOLEAN) ((_Flags & CREATEPROP_NONSIMPLE) != 0));
  2930. pspss->clsid = _pph->clsid;
  2931. pspss->fmtid = _GetFormatidOffset(
  2932. (_State & CPSS_USERDEFINEDPROPERTIES)? 1 : 0)->fmtid;
  2933. pspss->grfFlags = _CodePage == CP_WINUNICODE?
  2934. PROPSETFLAG_DEFAULT : PROPSETFLAG_ANSI;
  2935. // ----
  2936. // Exit
  2937. // ----
  2938. Exit:
  2939. return;
  2940. }
  2941. //+--------------------------------------------------------------------------
  2942. // Member: CPropertySetStream::SetClassId
  2943. //
  2944. // Synopsis: Set the classid for the property set code
  2945. //
  2946. // Arguments: [pclsid] -- pointer to new ClassId
  2947. // [pstatus] -- pointer to NTSTATUS code
  2948. //
  2949. // Returns: None
  2950. //---------------------------------------------------------------------------
  2951. VOID
  2952. CPropertySetStream::SetClassId(IN GUID const *pclsid,
  2953. OUT NTSTATUS *pstatus)
  2954. {
  2955. *pstatus = STATUS_SUCCESS;
  2956. PROPASSERT(_IsMapped());
  2957. PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder);
  2958. if (IsReadOnlyPropertySet(_Flags, _State))
  2959. {
  2960. StatusAccessDenied(pstatus, "SetClassId: deleted or read-only");
  2961. goto Exit;
  2962. }
  2963. _SetModified();
  2964. _pph->clsid = *pclsid;
  2965. // ----
  2966. // Exit
  2967. // ----
  2968. Exit:
  2969. return;
  2970. }
  2971. //+--------------------------------------------------------------------------
  2972. // Member: CPropertySetStream::EnumeratePropids
  2973. //
  2974. // Synopsis: enumerates the property ids in a prop set
  2975. //
  2976. // Arguments: [pkey] -- pointer to bookmark (0 implies beginning)
  2977. // [pcprop] -- on input: size; on output: # of props returned.
  2978. // [apropids] -- output buffer
  2979. // [pstatus] -- pointer to NTSTATUS code
  2980. //
  2981. // Returns: TRUE if more properties are available
  2982. //---------------------------------------------------------------------------
  2983. BOOLEAN
  2984. CPropertySetStream::EnumeratePropids(
  2985. IN OUT ULONG *pkey,
  2986. IN OUT ULONG *pcprop,
  2987. OPTIONAL OUT PROPID *apropids,
  2988. OUT NTSTATUS *pstatus)
  2989. {
  2990. PROPERTYIDOFFSET *ppo, *ppoStart, *ppoMax;
  2991. ULONG cprop = 0;
  2992. BOOLEAN fMorePropids = FALSE;
  2993. PROPID propidPrev = *pkey;
  2994. *pstatus = STATUS_SUCCESS;
  2995. PROPASSERT(_IsMapped());
  2996. PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder);
  2997. if (_State & CPSS_USERDEFINEDDELETED)
  2998. {
  2999. StatusAccessDenied(pstatus, "EnumeratePropids: deleted");
  3000. goto Exit;
  3001. }
  3002. if (_LoadPropertyOffsetPointers(&ppoStart, &ppoMax, pstatus) == NULL)
  3003. {
  3004. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3005. }
  3006. else
  3007. {
  3008. if (propidPrev != 0) // if not first call, start w/last propid
  3009. {
  3010. for (ppo = ppoStart; ppo < ppoMax; ppo++)
  3011. {
  3012. if (ppo->propid == propidPrev)
  3013. {
  3014. ppoStart = ++ppo;
  3015. break;
  3016. }
  3017. }
  3018. }
  3019. for (ppo = ppoStart; ppo < ppoMax; ppo++)
  3020. {
  3021. if (ppo->propid != PID_DICTIONARY &&
  3022. ppo->propid != PID_CODEPAGE &&
  3023. ppo->propid != PID_LOCALE)
  3024. {
  3025. if (cprop >= *pcprop)
  3026. {
  3027. fMorePropids = TRUE;
  3028. break;
  3029. }
  3030. if (apropids != NULL)
  3031. {
  3032. apropids[cprop] = ppo->propid;
  3033. }
  3034. cprop++;
  3035. propidPrev = ppo->propid;
  3036. }
  3037. }
  3038. }
  3039. *pkey = propidPrev;
  3040. *pcprop = cprop;
  3041. // ----
  3042. // Exit
  3043. // ----
  3044. Exit:
  3045. return(fMorePropids);
  3046. }
  3047. //+--------------------------------------------------------------------------
  3048. // Member: CPropertySetStream::_LoadPropertyOffsetPointers
  3049. //
  3050. // Synopsis: Load start and (past) end pointers to PROPERTYIDOFFSET array
  3051. //
  3052. // Arguments: [pppo] -- pointer to base of PROPERTYIDOFFSET array
  3053. // [pppoMax] -- pointer past end of PROPERTYIDOFFSET array
  3054. // [pstatus] -- pointer to NTSTATUS code
  3055. //
  3056. // Returns: Pointer to Section Header, NULL if section not present
  3057. // or if there was an error.
  3058. //---------------------------------------------------------------------------
  3059. PROPERTYSECTIONHEADER *
  3060. CPropertySetStream::_LoadPropertyOffsetPointers(
  3061. OUT PROPERTYIDOFFSET **pppo,
  3062. OUT PROPERTYIDOFFSET **pppoMax,
  3063. OUT NTSTATUS *pstatus)
  3064. {
  3065. PROPERTYSECTIONHEADER *psh;
  3066. *pstatus = STATUS_SUCCESS;
  3067. PROPASSERT(_IsMapped());
  3068. if (_cSection != 0)
  3069. {
  3070. psh = _GetSectionHeader();
  3071. ULONG cbstm = _MSTM(GetSize)(pstatus);
  3072. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3073. if (cbstm < _oSection + CB_PROPERTYSECTIONHEADER ||
  3074. cbstm < _oSection + CB_PROPERTYSECTIONHEADER +
  3075. psh->cProperties * CB_PROPERTYIDOFFSET ||
  3076. cbstm < _oSection + psh->cbSection)
  3077. {
  3078. StatusCorruption(pstatus, "LoadPropertyOffsetPointers: stream size");
  3079. goto Exit;
  3080. }
  3081. *pppo = psh->rgprop;
  3082. *pppoMax = psh->rgprop + psh->cProperties;
  3083. }
  3084. // ----
  3085. // Exit
  3086. // ----
  3087. Exit:
  3088. if( !NT_SUCCESS(*pstatus) )
  3089. psh = NULL;
  3090. return(psh);
  3091. }
  3092. //+--------------------------------------------------------------------------
  3093. // Member: CPropertySetStream::_LoadProperty
  3094. //
  3095. // Synopsis: return a pointer to the specified property value
  3096. //
  3097. // Arguments: [propid] -- property id for property
  3098. // [pcbprop] -- pointer to return property size, 0 on error
  3099. // [pstatus] -- pointer to NTSTATUS code
  3100. //
  3101. // Returns: SERIALIZEDPROPERTYVALUE * -- NULL if not present
  3102. //---------------------------------------------------------------------------
  3103. SERIALIZEDPROPERTYVALUE *
  3104. CPropertySetStream::_LoadProperty(
  3105. IN PROPID propid,
  3106. OUT OPTIONAL ULONG *pcbprop,
  3107. OUT NTSTATUS *pstatus )
  3108. {
  3109. PROPERTYSECTIONHEADER const *psh;
  3110. PROPERTYIDOFFSET *ppo, *ppoBase, *ppoMax;
  3111. SERIALIZEDPROPERTYVALUE *pprop = NULL;
  3112. *pstatus = STATUS_SUCCESS;
  3113. psh = _LoadPropertyOffsetPointers(&ppoBase, &ppoMax, pstatus);
  3114. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3115. if (psh != NULL)
  3116. {
  3117. for (ppo = ppoBase; ppo < ppoMax; ppo++)
  3118. {
  3119. if (IsDwordAligned(ppo->dwOffset)
  3120. &&
  3121. ppo->dwOffset >= CB_PROPERTYSECTIONHEADER
  3122. +
  3123. psh->cProperties * CB_PROPERTYIDOFFSET
  3124. &&
  3125. psh->cbSection >= ppo->dwOffset + CB_SERIALIZEDPROPERTYVALUE)
  3126. {
  3127. if (ppo->propid != propid)
  3128. {
  3129. continue;
  3130. }
  3131. pprop = (SERIALIZEDPROPERTYVALUE *)
  3132. _MapOffsetToAddress(ppo->dwOffset);
  3133. if (pcbprop != NULL)
  3134. {
  3135. ULONG cb;
  3136. cb = psh->cbSection - ppo->dwOffset;
  3137. if (propid == PID_DICTIONARY)
  3138. {
  3139. *pcbprop = _DictionaryLength(
  3140. (DICTIONARY const *) pprop,
  3141. cb,
  3142. pstatus);
  3143. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3144. }
  3145. else
  3146. {
  3147. *pcbprop = PropertyLength(pprop, cb, 0, pstatus);
  3148. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3149. }
  3150. }
  3151. if (pcbprop == NULL ||
  3152. psh->cbSection >= ppo->dwOffset + *pcbprop)
  3153. {
  3154. // Success
  3155. goto Exit;
  3156. }
  3157. }
  3158. pprop = NULL;
  3159. StatusCorruption(pstatus, "LoadProperty: property offset");
  3160. goto Exit;
  3161. }
  3162. }
  3163. // ----
  3164. // Exit
  3165. // ----
  3166. Exit:
  3167. return(pprop);
  3168. }
  3169. //+--------------------------------------------------------------------------
  3170. // Member: CPropertySetStream::GetValue
  3171. //
  3172. // Synopsis: return a pointer to the specified property value
  3173. //
  3174. // Arguments: [propid] -- property id of property
  3175. // [pcbprop] -- pointer to returned property length
  3176. // [pstatus] -- pointer to NTSTATUS code
  3177. //
  3178. // Returns: pointer to property value
  3179. //---------------------------------------------------------------------------
  3180. SERIALIZEDPROPERTYVALUE const *
  3181. CPropertySetStream::GetValue(
  3182. IN PROPID propid,
  3183. OUT ULONG *pcbprop,
  3184. OUT NTSTATUS *pstatus)
  3185. {
  3186. SERIALIZEDPROPERTYVALUE *pprop = NULL;
  3187. PROPASSERT(_IsMapped());
  3188. PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder);
  3189. if (_State & CPSS_USERDEFINEDDELETED)
  3190. {
  3191. StatusAccessDenied(pstatus, "GetValue: deleted");
  3192. goto Exit;
  3193. }
  3194. if (propid == PID_DICTIONARY)
  3195. {
  3196. DebugTrace(0, DEBTRACE_ERROR, ("GetValue: PID_DICTIONARY\n"));
  3197. StatusInvalidParameter(pstatus, "GetValue: PID_DICTIONARY");
  3198. goto Exit;
  3199. }
  3200. pprop = NULL;
  3201. if (propid == PID_SECURITY || propid == PID_MODIFY_TIME)
  3202. {
  3203. SERIALIZEDPROPERTYVALUE aprop[2];
  3204. PROPASSERT(sizeof(aprop) >= sizeof(ULONG) + sizeof(LONGLONG));
  3205. aprop[0].dwType = PropByteSwap( (DWORD) VT_EMPTY );
  3206. if (propid == PID_SECURITY)
  3207. {
  3208. if (_MSTM(QuerySecurity)((ULONG *) aprop[0].rgb))
  3209. {
  3210. aprop[0].dwType = PropByteSwap( (DWORD) VT_UI4 );
  3211. *pcbprop = 2 * sizeof(ULONG);
  3212. }
  3213. }
  3214. else // (propid == PID_MODIFY_TIME)
  3215. {
  3216. LONGLONG ll;
  3217. if (_MSTM(QueryModifyTime)(&ll))
  3218. {
  3219. PropByteSwap(&ll);
  3220. memcpy(aprop[0].rgb, &ll, sizeof(LONGLONG));
  3221. aprop[0].dwType = PropByteSwap( (DWORD) VT_FILETIME );
  3222. *pcbprop = sizeof(ULONG) + sizeof(LONGLONG);
  3223. }
  3224. }
  3225. if( VT_EMPTY != PropByteSwap(aprop[0].dwType) )
  3226. {
  3227. pprop = (SERIALIZEDPROPERTYVALUE *)
  3228. newk(mtPropSetStream, NULL) BYTE[*pcbprop];
  3229. if (pprop == NULL)
  3230. {
  3231. StatusNoMemory(pstatus, "GetValue: no memory");
  3232. goto Exit;
  3233. }
  3234. DebugTrace(0, Dbg, (
  3235. "GetValue: pprop=%lx, vt=%lx, cb=%lx\n",
  3236. pprop,
  3237. PropByteSwap( aprop[0].dwType ),
  3238. *pcbprop));
  3239. RtlCopyMemory(pprop, aprop, *pcbprop);
  3240. }
  3241. } // if (propid == PID_SECURITY || propid == PID_MODIFY_TIME)
  3242. else
  3243. {
  3244. pprop = _LoadProperty(propid, pcbprop, pstatus);
  3245. if( !NT_SUCCESS(*pstatus) )
  3246. {
  3247. pprop = NULL;
  3248. goto Exit;
  3249. }
  3250. } // if (propid == PID_SECURITY || propid == PID_MODIFY_TIME) ... else
  3251. #if DBGPROP
  3252. if (pprop == NULL)
  3253. {
  3254. DebugTrace(0, Dbg, ("GetValue: propid=%lx pprop=NULL\n", propid));
  3255. }
  3256. else
  3257. {
  3258. char valbuf[CB_VALUESTRING];
  3259. DebugTrace(0, Dbg, (
  3260. "GetValue: propid=%lx pprop=%l" szX " vt=%hx val=%s cb=%l" szX "\n",
  3261. propid,
  3262. _MapAddressToOffset(pprop),
  3263. PropByteSwap( pprop->dwType ),
  3264. ValueToString(pprop, *pcbprop, valbuf),
  3265. *pcbprop));
  3266. }
  3267. #endif
  3268. // ----
  3269. // Exit
  3270. // ----
  3271. Exit:
  3272. return(pprop);
  3273. }
  3274. //+--------------------------------------------------------------------------
  3275. // Member: CPropertySetStream::SetValue
  3276. //
  3277. // Synopsis: update/add/delete property values
  3278. //
  3279. // Arguments: [cprop] -- count of properties
  3280. // [avar] -- PROPVARIANT array
  3281. // [apinfo] -- PROPERTY_INFORMATION array
  3282. // [pstatus] -- pointer to NTSTATUS code
  3283. //
  3284. // Returns: None
  3285. //
  3286. // Note: All the properties in the apinfo array can be classified into
  3287. // one of the following categories:
  3288. //
  3289. // PROPOP_IGNORE:
  3290. // No change. Deleting a non-existent property or the same
  3291. // propid appears later in the apinfo array.
  3292. //
  3293. // PROPOP_DELETE:
  3294. // Deletion of an existing property. Remove the
  3295. // PROPERTYIDOFFSET structure from the property offset array and
  3296. // and pack the remaining entries. Delete the property value
  3297. // and pack remaining property values
  3298. //
  3299. // PROPOP_INSERT:
  3300. // Addition of a new property. Insert the new PROPERTYIDOFFSET
  3301. // structure at the end of the property offset array. Insert
  3302. // the new property value at the end of the section/stream.
  3303. //
  3304. // PROPOP_MOVE:
  3305. // A property whose value needs to be updated out of place
  3306. // because of a change in the property value's size. A property
  3307. // value is moved to the end of the section if it grows or
  3308. // shrinks across a DWORD boundary. The existing value is
  3309. // removed from the section and the remaining values are packed.
  3310. // Then, the new value is inserted at the end of the section.
  3311. // The idea here is to move variable length properties that are
  3312. // being changed frequently as near as possible to the end of
  3313. // the stream to minimize the cost of maintaining a packed set
  3314. // of property values. Note that the property offset structure
  3315. // is not moved around in the array.
  3316. //
  3317. // PROPOP_UPDATE:
  3318. // A property whose value can be updated in-place. The property
  3319. // value's new size is equal to the old size. There are a
  3320. // number of variant types that take up a fixed amount of space,
  3321. // e.g., VT_I4, VT_R8 etc. This would also apply to any
  3322. // variable length property that is updated without changing
  3323. // the property value's size across a DWORD boundary.
  3324. //
  3325. // Note that while the property offset array is itself packed out
  3326. // of necessity (to conform to the spec), there may be unused
  3327. // entries at the end of the array that are not compressed out of
  3328. // the stream when properties are deleted. The unused space is
  3329. // detected and reused when new properties are added later.
  3330. //---------------------------------------------------------------------------
  3331. #define CCHUNKSTACK (sizeof(ascnkStack)/sizeof(ascnkStack[0]))
  3332. VOID
  3333. CPropertySetStream::SetValue(
  3334. IN ULONG cprop,
  3335. IN PROPVARIANT const avar[],
  3336. IN PROPERTY_INFORMATION *apinfo,
  3337. OUT NTSTATUS *pstatus)
  3338. {
  3339. // ------
  3340. // Locals
  3341. // ------
  3342. CStreamChunk ascnkStack[6];
  3343. ULONG cpoReserve;
  3344. ULONG cDelete, cInsert, cMove, cUpdate;
  3345. #if DBGPROP
  3346. ULONG cIgnore;
  3347. char valbuf[CB_VALUESTRING];
  3348. KERNELSELECT(
  3349. char valbuf2[CB_VALUESTRING],
  3350. char varbuf[CB_VARIANT_TO_STRING]);
  3351. #endif
  3352. ULONG iprop;
  3353. ULONG cbstm;
  3354. LONG cbChange, cbInsertMove;
  3355. PROPERTYSECTIONHEADER *psh;
  3356. CStreamChunk *pscnk0 = NULL;
  3357. ULONG cbNewSize;
  3358. // ----------
  3359. // Initialize
  3360. // ----------
  3361. *pstatus = STATUS_SUCCESS;
  3362. PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder);
  3363. // Worst case, we will need chunks for:
  3364. // - the possible growth of the PROPERTYIDOFFSET array,
  3365. // - one for EACH property that is being modified,
  3366. // - and one chunk to mark the end of the property data.
  3367. CStreamChunkList scl(
  3368. 1 + cprop + 1,
  3369. 1 + cprop + 1 <= CCHUNKSTACK?
  3370. ascnkStack : ((CStreamChunk*)NULL) );
  3371. PROPASSERT(_IsMapped());
  3372. // Validate that this property set can be written to.
  3373. if (IsReadOnlyPropertySet(_Flags, _State))
  3374. {
  3375. StatusAccessDenied(pstatus, "SetValue: deleted or read-only");
  3376. goto Exit;
  3377. }
  3378. // Mark the propset dirty.
  3379. _SetModified();
  3380. psh = _GetSectionHeader();
  3381. cpoReserve = 0;
  3382. cDelete = cInsert = cMove = cUpdate = 0;
  3383. #if DBGPROP
  3384. cIgnore = 0;
  3385. #endif
  3386. cbInsertMove = cbChange = 0;
  3387. pscnk0 = scl.GetFreeChunk(pstatus);
  3388. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3389. pscnk0->oOld = 0;
  3390. pscnk0->cbChange = 0;
  3391. PROPASSERT(pscnk0 == scl.GetChunk(0));
  3392. cbstm = _oSection + psh->cbSection + _cbTail;
  3393. PROPASSERT( cbstm <= _MSTM(GetSize)(pstatus) && NT_SUCCESS(*pstatus) );
  3394. PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail);
  3395. // ------------------------
  3396. // Classify all the updates
  3397. // ------------------------
  3398. // Each update gets classified as ignore, delete, insert, move,
  3399. // or update.
  3400. // Lookup the old value for each of the properties specified and
  3401. // compute the current size.
  3402. for (iprop = 0; iprop < cprop; iprop++)
  3403. {
  3404. ULONG i;
  3405. ULONG cbPropOld;
  3406. SERIALIZEDPROPERTYVALUE const *pprop = NULL;
  3407. PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail);
  3408. if (IsReadOnlyPropid(apinfo[iprop].pid))
  3409. {
  3410. if (cprop != 1 ||
  3411. apinfo[0].pid != PID_DICTIONARY ||
  3412. apinfo[0].cbprop == 0 ||
  3413. ( avar == NULL || avar[0].vt != VT_DICTIONARY )
  3414. )
  3415. {
  3416. DebugTrace(0, DEBTRACE_ERROR, (
  3417. "SetValue: read-only propid=%lx\n",
  3418. apinfo[iprop].pid));
  3419. StatusInvalidParameter(pstatus, "SetValue: read-only PROPID");
  3420. goto Exit;
  3421. }
  3422. }
  3423. if (apinfo[iprop].pid != PID_ILLEGAL)
  3424. {
  3425. pprop = _LoadProperty(apinfo[iprop].pid, &cbPropOld, pstatus);
  3426. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3427. PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail);
  3428. }
  3429. // If this propid appears later in the array, ignore it.
  3430. for (i = iprop + 1; i < cprop; i++)
  3431. {
  3432. if (apinfo[i].pid == apinfo[iprop].pid)
  3433. {
  3434. #if DBGPROP
  3435. cIgnore++;
  3436. #endif
  3437. apinfo[iprop].operation = PROPOP_IGNORE;
  3438. break;
  3439. }
  3440. }
  3441. // If this propid appears only once or if it's the last instance,
  3442. // load the property and compute its size.
  3443. if (i == cprop)
  3444. {
  3445. VOID *pvStart = NULL;
  3446. PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail);
  3447. if (pprop != NULL)
  3448. {
  3449. ULONG cbPropNew;
  3450. PROPASSERT(apinfo[iprop].pid != PID_DICTIONARY);
  3451. if (apinfo[iprop].cbprop == 0)
  3452. {
  3453. DebugTrace(0, Dbg, (
  3454. "SetValue: Deleting propid=%lx oOld=%l" szX
  3455. " vt=%hx val=%s cb=%l" szX "\n",
  3456. apinfo[iprop].pid,
  3457. _MapAddressToOffset(pprop),
  3458. PropByteSwap( pprop->dwType ),
  3459. ValueToString(pprop, cbPropOld, valbuf),
  3460. cbPropOld));
  3461. cbPropNew = 0;
  3462. cDelete++;
  3463. apinfo[iprop].operation = PROPOP_DELETE;
  3464. }
  3465. else
  3466. {
  3467. DebugTrace(0, Dbg, (
  3468. "SetValue: Modifying propid=%lx oOld=%l" szX
  3469. " vt=%hx-->%hx cb=%l" szX "-->%l" szX " val=%s-->%s\n",
  3470. apinfo[iprop].pid,
  3471. _MapAddressToOffset(pprop),
  3472. PropByteSwap( pprop->dwType ),
  3473. KERNELSELECT(
  3474. PropByteSwap( apinfo[iprop].pprop->dwType ),
  3475. avar[iprop].vt),
  3476. cbPropOld,
  3477. apinfo[iprop].cbprop,
  3478. ValueToString(pprop, cbPropOld, valbuf),
  3479. KERNELSELECT(
  3480. ValueToString(
  3481. apinfo[iprop].pprop,
  3482. apinfo[iprop].cbprop,
  3483. valbuf2),
  3484. VariantToString(
  3485. avar[iprop],
  3486. varbuf,
  3487. sizeof( varbuf )))));
  3488. cbPropNew = apinfo[iprop].cbprop;
  3489. if (cbPropOld != cbPropNew)
  3490. {
  3491. cbInsertMove += apinfo[iprop].cbprop;
  3492. cMove++;
  3493. apinfo[iprop].operation = PROPOP_MOVE;
  3494. }
  3495. else
  3496. {
  3497. cUpdate++;
  3498. apinfo[iprop].operation = PROPOP_UPDATE;
  3499. }
  3500. }
  3501. if (apinfo[iprop].operation != PROPOP_UPDATE)
  3502. {
  3503. // Update the list of chunks that need to be adjusted
  3504. CStreamChunk *pscnk = scl.GetFreeChunk(pstatus);
  3505. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3506. pscnk->oOld = _MapAddressToOffset(pprop);
  3507. pscnk->cbChange = - (LONG) cbPropOld;
  3508. }
  3509. // Stream size change
  3510. cbChange += cbPropNew - cbPropOld;
  3511. }
  3512. // Delete non-existent property:
  3513. else if (apinfo[iprop].cbprop == 0)
  3514. {
  3515. #if DBGPROP
  3516. cIgnore++;
  3517. #endif
  3518. PROPASSERT(apinfo[iprop].pid != PID_DICTIONARY);
  3519. apinfo[iprop].operation = PROPOP_IGNORE;
  3520. }
  3521. // Insert new property:
  3522. else
  3523. {
  3524. DebugTrace(0, Dbg, (
  3525. "SetValue: Inserting new propid=%lx vt=%hx "
  3526. "cbNew=%l" szX " val=%s\n",
  3527. apinfo[iprop].pid,
  3528. KERNELSELECT(
  3529. PropByteSwap( apinfo[iprop].pprop->dwType ),
  3530. avar[iprop].vt),
  3531. apinfo[iprop].cbprop,
  3532. KERNELSELECT(
  3533. ValueToString(
  3534. apinfo[iprop].pprop,
  3535. apinfo[iprop].cbprop,
  3536. valbuf),
  3537. VariantToString(
  3538. avar[iprop],
  3539. varbuf,
  3540. sizeof( varbuf )))));
  3541. PROPASSERT(apinfo[iprop].pid != PID_ILLEGAL);
  3542. cbInsertMove += apinfo[iprop].cbprop;
  3543. cbChange += apinfo[iprop].cbprop;
  3544. cInsert++;
  3545. apinfo[iprop].operation = PROPOP_INSERT;
  3546. }
  3547. PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail);
  3548. }
  3549. }
  3550. DebugTrace(0, Dbg, ("SetValue: Total Props %l" szX "\n", cprop));
  3551. DebugTrace(0, Dbg, (
  3552. "SetValue: Delete=%l" szX " Insert=%l" szX " Move=%l" szX
  3553. " Update=%l" szX " Ignore=%l" szX "\n",
  3554. cDelete,
  3555. cInsert,
  3556. cMove,
  3557. cUpdate,
  3558. cIgnore));
  3559. PROPASSERT(cDelete + cInsert + cMove + cUpdate + cIgnore == cprop);
  3560. PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail);
  3561. // If we need to grow the property offset array, detect any unused
  3562. // entries at the end of the array that are available for reuse.
  3563. // and adjust the size difference to reflect the reuse.
  3564. if (cInsert > cDelete)
  3565. {
  3566. ULONG cpoReuse, cpoExpand;
  3567. cpoExpand = cInsert - cDelete;
  3568. cpoReuse = _CountFreePropertyOffsets(pstatus);
  3569. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3570. if (cpoReuse > cpoExpand)
  3571. {
  3572. cpoReuse = cpoExpand;
  3573. }
  3574. cpoExpand -= cpoReuse;
  3575. // If adding a small number of new entries, but not reusing any old
  3576. // ones, add 10% more reserved entries (but only up to 10 more) to
  3577. // avoid having to continually grow the property offset array for
  3578. // clients that insist on adding a few properties at a time.
  3579. // We don't do this for the User-Defined property set, however,
  3580. // because older apps assume that the dictionary immediately follows
  3581. // the last entry in the PID/Offset array.
  3582. if (cpoExpand >= 1 && cpoExpand <= 2 && cpoReuse == 0
  3583. &&
  3584. !(_State & CPSS_USERDEFINEDPROPERTIES)
  3585. )
  3586. {
  3587. cpoReserve = 1 + min(psh->cProperties, 90)/10;
  3588. cpoExpand += cpoReserve;
  3589. }
  3590. DebugTrace(0, Dbg, (
  3591. "SetValue: Reusing %l" szX " offsets, Expanding %l" szX
  3592. " offsets\n",
  3593. cpoReuse,
  3594. cpoExpand));
  3595. pscnk0->oOld = CB_PROPERTYSECTIONHEADER +
  3596. (psh->cProperties + cpoReuse) * CB_PROPERTYIDOFFSET;
  3597. pscnk0->cbChange = cpoExpand * CB_PROPERTYIDOFFSET;
  3598. cbChange += cpoExpand * CB_PROPERTYIDOFFSET;
  3599. PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail);
  3600. } // if (cInsert > cDelete)
  3601. // Do we instead need to *shrink* the PID/Offset array?
  3602. // If so, don't shrink any more than necessary. We'll
  3603. // leave up to min(10%,10) blank entries.
  3604. // Also, if this is the User-Defined property set,
  3605. // there can never be any unused entries (for compatibility
  3606. // with older apps), so we do a complete shrink.
  3607. else if (cInsert < cDelete)
  3608. {
  3609. ULONG cpoRemove = 0;
  3610. ULONG cpoDelta = cDelete - cInsert;
  3611. // How many blank entries do we already have?
  3612. ULONG cpoCurBlankEntries = _CountFreePropertyOffsets( pstatus );
  3613. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3614. if( _State & CPSS_USERDEFINEDPROPERTIES )
  3615. {
  3616. cpoRemove = cpoDelta;
  3617. }
  3618. else
  3619. {
  3620. // How many blank entries can we have?
  3621. ULONG cpoMaxBlankEntries;
  3622. cpoMaxBlankEntries = 1 + min(psh->cProperties - cpoDelta, 90)/10;
  3623. // If, after deleting the properties, we'd have too many,
  3624. // remove only enough to get us down to the max allowable.
  3625. if( cpoCurBlankEntries + cpoDelta
  3626. >
  3627. cpoMaxBlankEntries
  3628. )
  3629. {
  3630. cpoRemove = cpoCurBlankEntries + cpoDelta - cpoMaxBlankEntries;
  3631. }
  3632. } // if( _State & CPSS_USERDEFINEDPROPERTIES )
  3633. // Should we remove any PID/Offset entries?
  3634. if( cpoRemove > 0 )
  3635. {
  3636. // Start removing at cpoRemove entries from the end of the PID/Offset array
  3637. pscnk0->oOld = CB_PROPERTYSECTIONHEADER
  3638. +
  3639. (psh->cProperties + cpoCurBlankEntries - cpoRemove)
  3640. *
  3641. CB_PROPERTYIDOFFSET;
  3642. // Remove the bytes of the cpoRemove entries.
  3643. pscnk0->cbChange = - (LONG) (cpoRemove * CB_PROPERTYIDOFFSET );
  3644. // Adjust the size of the section equivalently.
  3645. cbChange += pscnk0->cbChange;
  3646. }
  3647. } // else if (cInsert < cDelete)
  3648. PROPASSERT(
  3649. cbstm + cbChange >=
  3650. _oSection + CB_PROPERTYSECTIONHEADER +
  3651. (psh->cProperties + cInsert - cDelete) * CB_PROPERTYIDOFFSET +
  3652. _cbTail);
  3653. // If we need to grow the stream, do it now.
  3654. if (cbChange > 0)
  3655. {
  3656. if (cbstm + cbChange > CBMAXPROPSETSTREAM)
  3657. {
  3658. StatusDiskFull(pstatus, "SetValue: 256k limit");
  3659. goto Exit;
  3660. }
  3661. DebugTrace(0, Dbg, (
  3662. "SetSize(%x) SetValue grow\n",
  3663. cbstm + cbChange));
  3664. _MSTM(SetSize)(cbstm + cbChange, TRUE, (VOID **) &_pph, pstatus);
  3665. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3666. // reload all pointers into mapped image:
  3667. psh = _GetSectionHeader();
  3668. PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail);
  3669. // If there's another section after this one, move it back to the
  3670. // end of the stream now.
  3671. if (_cbTail != 0)
  3672. {
  3673. VOID *pvSrc = _MapAbsOffsetToAddress(cbstm - _cbTail);
  3674. PropMoveMemory(
  3675. "SetValue(_cbTail:grow)",
  3676. psh,
  3677. Add2Ptr(pvSrc, cbChange),
  3678. pvSrc,
  3679. _cbTail);
  3680. }
  3681. }
  3682. // From this point on, the operation should succeed.
  3683. // If necessary, the stream has already been grown.
  3684. if (cDelete + cInsert + cMove != 0)
  3685. {
  3686. // Delete and compact property offsets in the section header.
  3687. if (cDelete + cMove != 0)
  3688. {
  3689. _DeleteMovePropertyOffsets(apinfo, cprop, pstatus);
  3690. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3691. psh->cProperties -= cDelete;
  3692. }
  3693. PROPASSERT(cbstm == _oSection + psh->cbSection + _cbTail);
  3694. // Use the last chunk to mark the section end, and sort the chunks
  3695. // in ascending order by start offset.
  3696. CStreamChunk *pscnk = scl.GetFreeChunk(pstatus);
  3697. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3698. pscnk->oOld = psh->cbSection;
  3699. pscnk->cbChange = 0;
  3700. scl.SortByStartAddress();
  3701. // If we're reducing the number of properties, we may be shrinking
  3702. // the PID/Offset array. So, update that array now, since
  3703. // we may remove some bytes at the end of it when we compact
  3704. // the stream.
  3705. if( cDelete > cInsert )
  3706. {
  3707. _UpdatePropertyOffsets( &scl, pstatus );
  3708. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3709. }
  3710. // Compact the Stream following the directions in the
  3711. // chunk list.
  3712. _CompactStream(&scl);
  3713. // If the number of properties is holding constant or increasing,
  3714. // we can update the PID/Offset array now (because _CompactStream
  3715. // allocated any necessary space for us).
  3716. if( cDelete <= cInsert )
  3717. {
  3718. _UpdatePropertyOffsets( &scl, pstatus );
  3719. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3720. }
  3721. // Set the new section size to include the deleted and inserted
  3722. // property offsets, and the deleted property values.
  3723. psh->cbSection += cbChange;
  3724. // Insert new property offsets at the end of the array.
  3725. if (cInsert + cMove != 0)
  3726. {
  3727. _InsertMovePropertyOffsets(
  3728. apinfo,
  3729. cprop,
  3730. psh->cbSection - cbInsertMove,
  3731. cpoReserve,
  3732. pstatus);
  3733. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3734. psh->cProperties += cInsert;
  3735. }
  3736. PROPASSERT(cbstm + cbChange == _oSection + psh->cbSection + _cbTail);
  3737. if (_cbTail != 0)
  3738. {
  3739. // There's another section after this one; if we're shrinking
  3740. // the stream, move it up to the new end of the stream now.
  3741. if (cbChange < 0)
  3742. {
  3743. VOID *pvSrc = _MapAbsOffsetToAddress(cbstm - _cbTail);
  3744. PropMoveMemory(
  3745. "SetValue(_cbTail:shrink)",
  3746. psh,
  3747. Add2Ptr(pvSrc, cbChange),
  3748. pvSrc,
  3749. _cbTail);
  3750. }
  3751. _PatchSectionOffsets(cbChange);
  3752. }
  3753. } // if (cDelete + cInsert + cMove != 0)
  3754. // Copy the new values.
  3755. // NOTE: It might seem unnecessary to delay the in-place updates until
  3756. // this for loop. We do not perform the in-place updates while
  3757. // classifying the changes because unmapping, remapping and changing
  3758. // the size required for handling other updates can fail. In the event
  3759. // of such a failure, the update would not be atomic. By delaying the
  3760. // in-place updates, we provide some degree of atomicity.
  3761. if (cInsert + cUpdate + cMove != 0)
  3762. {
  3763. BOOLEAN fDocSummaryInfo = FALSE;
  3764. if ((_State &
  3765. (CPSS_USERDEFINEDPROPERTIES | CPSS_DOCUMENTSUMMARYINFO)) ==
  3766. CPSS_DOCUMENTSUMMARYINFO)
  3767. {
  3768. fDocSummaryInfo = TRUE;
  3769. }
  3770. for (iprop = 0; iprop < cprop; iprop++)
  3771. {
  3772. // Find property in the offset array and copy in the new value.
  3773. if (apinfo[iprop].operation == PROPOP_INSERT ||
  3774. apinfo[iprop].operation == PROPOP_UPDATE ||
  3775. apinfo[iprop].operation == PROPOP_MOVE)
  3776. {
  3777. SERIALIZEDPROPERTYVALUE *pprop=NULL;
  3778. ULONG cbprop;
  3779. PROPID propid = apinfo[iprop].pid;
  3780. pprop = _LoadProperty(propid, NULL, pstatus);
  3781. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3782. PROPASSERT(pprop != NULL);
  3783. // Special case for SetPropertyNames dictionary creation:
  3784. if (propid == PID_DICTIONARY)
  3785. {
  3786. PROPASSERT(CB_SERIALIZEDPROPERTYVALUE == CB_DICTIONARY);
  3787. PROPASSERT(apinfo[iprop].cbprop == CB_SERIALIZEDPROPERTYVALUE);
  3788. PROPASSERT(avar[iprop].vt == VT_DICTIONARY);
  3789. ((DICTIONARY *) pprop)->cEntries = 0;
  3790. } // if (propid == PID_DICTIONARY)
  3791. else
  3792. {
  3793. // In User, serialize the PROPVARIANT in avar
  3794. // directly into the mapped stream.
  3795. cbprop = apinfo[iprop].cbprop;
  3796. pprop = RtlConvertVariantToProperty(
  3797. &avar[iprop],
  3798. _CodePage,
  3799. pprop,
  3800. &cbprop,
  3801. apinfo[iprop].pid,
  3802. FALSE,
  3803. pstatus
  3804. );
  3805. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3806. PROPASSERT(pprop != NULL);
  3807. PROPASSERT(cbprop == DwordAlign(cbprop));
  3808. PROPASSERT(cbprop == apinfo[iprop].cbprop);
  3809. // If writing a DocumentSummaryInformation property
  3810. // for which an alignment hack is provided, hack it now.
  3811. if (fDocSummaryInfo && _CodePage != CP_WINUNICODE)
  3812. {
  3813. // The two vectors in the DocSumInfo property set
  3814. // (if Ansi) are un-packed, but we'll adjust the lengths
  3815. // so that if a propset reader expects them to be packed,
  3816. // it will still work. E.g., a one character string will
  3817. // have a length of 4, with padding of NULL characters.
  3818. ULONG cbpropT;
  3819. if (propid == PID_HEADINGPAIR)
  3820. {
  3821. _FixHeadingPairVector(
  3822. PATCHOP_ALIGNLENGTHS,
  3823. pprop,
  3824. &cbpropT);
  3825. }
  3826. else
  3827. if (propid == PID_DOCPARTS)
  3828. {
  3829. _FixDocPartsVector(
  3830. PATCHOP_ALIGNLENGTHS,
  3831. pprop,
  3832. &cbpropT);
  3833. }
  3834. }
  3835. DebugTrace(0, Dbg, (
  3836. "SetValue:Insert: pph=%x pprop=%x cb=%3l" szX
  3837. " vt=%4x val=%s o=%x oEnd=%x\n",
  3838. _pph,
  3839. pprop,
  3840. apinfo[iprop].cbprop,
  3841. PropByteSwap(pprop->dwType),
  3842. ValueToString(pprop, apinfo[iprop].cbprop, valbuf),
  3843. _MapAddressToOffset(pprop),
  3844. _MapAddressToOffset(pprop) + apinfo[iprop].cbprop));
  3845. } // if (propid == PID_DICTIONARY) ... else
  3846. } // if (apinfo[iprop].operation == PROPOP_INSERT || ...
  3847. } // for (iprop = 0; iprop < cprop; iprop++)
  3848. } // if (cInsert + cUpdate + cMove != 0)
  3849. // If we need to shrink the stream or if we are cleaning up after a
  3850. // previous shrink that failed, do it last.
  3851. cbNewSize = _MSTM(GetSize)(pstatus);
  3852. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3853. if (cbNewSize != cbstm + cbChange)
  3854. {
  3855. DebugTrace(0, Dbg, (
  3856. "SetSize(%x) SetValue shrink\n",
  3857. cbstm + cbChange));
  3858. _MSTM(SetSize)(cbstm + cbChange, TRUE, (VOID **) &_pph, pstatus);
  3859. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3860. }
  3861. // ----
  3862. // Exit
  3863. // ----
  3864. Exit:
  3865. scl.Delete();
  3866. }
  3867. //+--------------------------------------------------------------------------
  3868. // Member: CPropertySetStream::_CountFreePropertyOffsets, private
  3869. //
  3870. // Synopsis: counts available (free) property offsets at and of array
  3871. //
  3872. // Arguments: [pstatus] -- pointer to NTSTATUS code
  3873. //
  3874. // Returns: count of available property offsets at and of array
  3875. //+--------------------------------------------------------------------------
  3876. ULONG
  3877. CPropertySetStream::_CountFreePropertyOffsets(OUT NTSTATUS *pstatus)
  3878. {
  3879. PROPERTYIDOFFSET *ppo, *ppoMax;
  3880. PROPERTYSECTIONHEADER const *psh;
  3881. ULONG oMin = MAXULONG;
  3882. ULONG oEnd;
  3883. ULONG cFree = 0;
  3884. psh = _LoadPropertyOffsetPointers(&ppo, &ppoMax, pstatus);
  3885. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3886. if (psh != NULL)
  3887. {
  3888. for ( ; ppo < ppoMax; ppo++)
  3889. {
  3890. if (oMin > ppo->dwOffset)
  3891. {
  3892. oMin = ppo->dwOffset;
  3893. }
  3894. }
  3895. }
  3896. if (oMin == MAXULONG)
  3897. {
  3898. goto Exit;
  3899. }
  3900. PROPASSERT(psh != NULL);
  3901. oEnd = CB_PROPERTYSECTIONHEADER + psh->cProperties * CB_PROPERTYIDOFFSET;
  3902. PROPASSERT(oEnd <= oMin);
  3903. cFree = (oMin - oEnd)/CB_PROPERTYIDOFFSET;
  3904. Exit:
  3905. return( cFree );
  3906. }
  3907. //+--------------------------------------------------------------------------
  3908. // Member: CPropertySetStream::_DeleteMovePropertyOffsets, private
  3909. //
  3910. // Synopsis: updates the offsets following the changes to the stream
  3911. //
  3912. // Arguments: [apinfo] -- array of property information
  3913. // [cprop] -- number of properties
  3914. // [pstatus] -- pointer to NTSTATUS code
  3915. //
  3916. // Returns: None
  3917. //+--------------------------------------------------------------------------
  3918. VOID
  3919. CPropertySetStream::_DeleteMovePropertyOffsets(
  3920. IN PROPERTY_INFORMATION const *apinfo,
  3921. IN ULONG cprop,
  3922. OUT NTSTATUS *pstatus)
  3923. {
  3924. ULONG i;
  3925. ULONG cDelete;
  3926. PROPERTYSECTIONHEADER const *psh;
  3927. PROPERTYIDOFFSET *ppo, *ppoBase, *ppoMax;
  3928. psh = _LoadPropertyOffsetPointers(&ppoBase, &ppoMax, pstatus);
  3929. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  3930. PROPASSERT(psh != NULL);
  3931. // Remove the deleted properties
  3932. DebugTrace(0, Dbg, ("Marking deleted/moved property offsets\n"));
  3933. cDelete = 0;
  3934. for (i = 0; i < cprop; i++)
  3935. {
  3936. if (apinfo[i].operation == PROPOP_DELETE ||
  3937. apinfo[i].operation == PROPOP_MOVE)
  3938. {
  3939. for (ppo = ppoBase; ppo < ppoMax; ppo++)
  3940. {
  3941. if (ppo->propid == apinfo[i].pid)
  3942. {
  3943. DebugTrace(0, Dbg, (
  3944. "%sing propid=%lx oOld=%l" szX "\n",
  3945. apinfo[i].operation == PROPOP_DELETE? "Delet" : "Mov",
  3946. ppo->propid,
  3947. ppo->dwOffset));
  3948. if (apinfo[i].operation == PROPOP_DELETE)
  3949. {
  3950. cDelete++;
  3951. ppo->dwOffset = MAXULONG;
  3952. }
  3953. else
  3954. {
  3955. ppo->dwOffset = 0;
  3956. }
  3957. break;
  3958. }
  3959. }
  3960. }
  3961. }
  3962. // scan once and compact the property offset array.
  3963. if (cDelete > 0)
  3964. {
  3965. PROPERTYIDOFFSET *ppoDst = ppoBase;
  3966. DebugTrace(0, Dbg, ("Compacting %l" szX " deleted props\n", cDelete));
  3967. for (ppo = ppoBase; ppo < ppoMax; ppo++)
  3968. {
  3969. if (ppo->dwOffset != MAXULONG)
  3970. {
  3971. if (ppo > ppoDst)
  3972. {
  3973. *ppoDst = *ppo;
  3974. }
  3975. DebugTrace(0, Dbg, (
  3976. "%sing propid=%lx oOld=%l" szX "\n",
  3977. ppo > ppoDst? "Compact" : "Preserv",
  3978. ppo->propid,
  3979. ppo->dwOffset));
  3980. ppoDst++;
  3981. }
  3982. }
  3983. PROPASSERT(cDelete == (ULONG) (ppoMax - ppoDst));
  3984. DebugTrace(0, Dbg, ("Zeroing %l" szX " entries\n", cDelete));
  3985. RtlZeroMemory(ppoDst, (BYTE *) ppoMax - (BYTE *) ppoDst);
  3986. }
  3987. // ----
  3988. // Exit
  3989. // ----
  3990. Exit:
  3991. return;
  3992. }
  3993. //+--------------------------------------------------------------------------
  3994. // Member: CPropertySetStream::_UpdatePropertyOffsets, private
  3995. //
  3996. // Synopsis: update property offsets in section header
  3997. //
  3998. // Arguments: [pscl] -- list of chunks in stream that were changed
  3999. // [pstatus] -- pointer to NTSTATUS code
  4000. //
  4001. // Returns: None
  4002. //+--------------------------------------------------------------------------
  4003. VOID
  4004. CPropertySetStream::_UpdatePropertyOffsets(
  4005. IN CStreamChunkList const *pscl,
  4006. OUT NTSTATUS *pstatus)
  4007. {
  4008. PROPERTYSECTIONHEADER const *psh;
  4009. PROPERTYIDOFFSET *ppo, *ppoMax;
  4010. // Update the offsets for the existing properties.
  4011. DebugTrace(0, Dbg, ("Updating existing property offsets\n"));
  4012. psh = _LoadPropertyOffsetPointers(&ppo, &ppoMax, pstatus);
  4013. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4014. PROPASSERT(psh != NULL);
  4015. for ( ; ppo < ppoMax; ppo++)
  4016. {
  4017. if (ppo->dwOffset != 0)
  4018. {
  4019. #if DBGPROP
  4020. ULONG oOld = ppo->dwOffset;
  4021. #endif
  4022. ppo->dwOffset = _GetNewOffset(pscl, ppo->dwOffset);
  4023. DebugTrace(0, Dbg, (
  4024. "UpdatePropertyOffsets: propid=%lx offset=%l" szX "-->%l" szX"\n",
  4025. ppo->propid,
  4026. oOld,
  4027. ppo->dwOffset));
  4028. }
  4029. }
  4030. Exit:
  4031. return;
  4032. }
  4033. //+--------------------------------------------------------------------------
  4034. // Member: CPropertySetStream::_InsertMovePropertyOffsets, private
  4035. //
  4036. // Synopsis: updates the offsets following the changes to the stream
  4037. //
  4038. // Arguments: [apinfo] -- array of property information
  4039. // [cprop] -- number of properties
  4040. // [oInsert] -- offset in section for new properties
  4041. // [cpoReserve] -- newly reserved property offsets to zero
  4042. // [pstatus] -- pointer to NTSTATUS code
  4043. //
  4044. // Returns: None
  4045. //+--------------------------------------------------------------------------
  4046. VOID
  4047. CPropertySetStream::_InsertMovePropertyOffsets(
  4048. IN PROPERTY_INFORMATION const *apinfo,
  4049. IN ULONG cprop,
  4050. IN ULONG oInsert,
  4051. IN ULONG cpoReserve,
  4052. OUT NTSTATUS *pstatus)
  4053. {
  4054. ULONG i;
  4055. PROPERTYSECTIONHEADER const *psh;
  4056. PROPERTYIDOFFSET *ppo, *ppoBase, *ppoMax;
  4057. *pstatus = STATUS_SUCCESS;
  4058. psh = _LoadPropertyOffsetPointers(&ppoBase, &ppoMax, pstatus);
  4059. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4060. PROPASSERT(psh != NULL);
  4061. // Insert the new property offsets at the end.
  4062. DebugTrace(0, Dbg, ("Inserting/Moving/Zeroing property offsets\n"));
  4063. for (i = 0; i < cprop; i++)
  4064. {
  4065. if (apinfo[i].operation == PROPOP_INSERT)
  4066. {
  4067. ppo = ppoMax++;
  4068. ppo->propid = apinfo[i].pid;
  4069. }
  4070. else if (apinfo[i].operation == PROPOP_MOVE)
  4071. {
  4072. for (ppo = ppoBase; ppo < ppoMax; ppo++)
  4073. {
  4074. if (ppo->propid == apinfo[i].pid)
  4075. {
  4076. PROPASSERT(ppo->dwOffset == 0);
  4077. break;
  4078. }
  4079. }
  4080. }
  4081. else
  4082. {
  4083. continue;
  4084. }
  4085. PROPASSERT(ppo->propid == apinfo[i].pid);
  4086. ppo->dwOffset = oInsert;
  4087. oInsert += apinfo[i].cbprop;
  4088. DebugTrace(0, Dbg, (
  4089. "%sing propid=%lx offset=%l" szX " size=%l" szX "\n",
  4090. apinfo[i].operation == PROPOP_INSERT? "Insert" : "Mov",
  4091. ppo->propid,
  4092. ppo->dwOffset,
  4093. apinfo[i].cbprop));
  4094. }
  4095. DebugTrace(0, Dbg, (
  4096. "Zeroing %x property offsets o=%l" szX " size=%l" szX "\n",
  4097. cpoReserve,
  4098. _MapAddressToOffset(ppoMax),
  4099. cpoReserve * CB_PROPERTYIDOFFSET));
  4100. RtlZeroMemory(ppoMax, cpoReserve * CB_PROPERTYIDOFFSET);
  4101. // ----
  4102. // Exit
  4103. // ----
  4104. Exit:
  4105. return;
  4106. }
  4107. //+--------------------------------------------------------------------------
  4108. // Member: CPropertySetStream::_CompactStream, private
  4109. //
  4110. // Synopsis: compact all of the property stream chunks
  4111. //
  4112. // Arguments: [pscl] -- list of chunks in stream that were changed
  4113. //
  4114. // Returns: None
  4115. //
  4116. // Note:
  4117. // Each chunk structure represents a contiguous range of the stream to be
  4118. // completely removed or added. A terminating chunk is appended to
  4119. // transparently mark the end of the data stream. The unmodified data
  4120. // after each chunk (except the last one) must be preserved and compacted
  4121. // as necessary. Chunk structures contain section-relative offsets.
  4122. //
  4123. // Invariants:
  4124. // - Only the first chunk can represent an insertion; subsequent chunks
  4125. // always represent deletions.
  4126. // - The first chunk can never cause a deletion, but it might not cause
  4127. // any change at all.
  4128. // - The last chunk is a dummy used to mark the end of the stream.
  4129. //
  4130. // Algorithm:
  4131. // In the optimal case without insertions, each chunk's trailing data can
  4132. // be moved ahead (compacted) individually in ascending chunk index order.
  4133. // If the first chunk represents an insertion, then some consecutive
  4134. // number of data blocks must be moved back (in *descending* chunk index
  4135. // order) to make room for the insertion.
  4136. //
  4137. // Walk the chunk array to find the first point where the accumulated size
  4138. // change is less than or equal to zero.
  4139. //
  4140. // After (possibly) compacting a single range in descending chunk index
  4141. // order, compact all remaining chunks in ascending chunk index order.
  4142. //
  4143. // Example: the first chunk inserts 18 bytes for new property offsets
  4144. // (apo'[]), and the second two delete 10 bytes each (chnk1 & chnk2).
  4145. // There are four chunks in the array, and three blocks of data to move.
  4146. //
  4147. // oOld cbChange | AccumulatedChange oNew
  4148. // chunk[0]: 38 +18 | +18 38 (apo'[])
  4149. // chunk[1]: 48 -10 | +8 50 (chnk1)
  4150. // chunk[2]: 6c -10 | -8 74 (chnk2)
  4151. // chunk[3]: 8c 0 | -8 84 (end)
  4152. //
  4153. // Data blocks are moved in the following sequence to avoid overlap:
  4154. // DstOff SrcOff cbMove | Chunk#
  4155. // 60 58 14 | 1 chnk1/data2: descending pass (Dst > Src)
  4156. // 50 38 10 | 0 apo'[]/data1: descending pass (Dst > Src)
  4157. // 74 7c 10 | 2 chnk2/data3: ascending pass (Dst < Src)
  4158. //
  4159. // SrcOff = oOld - min(cbChange, 0)
  4160. // DstOff = SrcOff + AccumulatedChange
  4161. // cbMove = chnk[i+1].oOld - SrcOff
  4162. //
  4163. // Before compacting:
  4164. // 0 38 48 58 6c 7c 8c
  4165. // | | | | | | |
  4166. // V V 10 V -10 V 14 V -10 V 10 V
  4167. // +----+-------+----+-------+-------+-------+----------+-------+-------+
  4168. // | ph | afo[] | sh | apo[] | data1 | chnk1 | data2 | chnk2 | data3 |
  4169. // +----+-------+----+-------+-------+-------+----------+-------+-------+
  4170. //
  4171. // After compacting:
  4172. // 0 38 50 60 74 84
  4173. // | | | | | |
  4174. // V V +18 V 10 V 14 V 10 V
  4175. // +----+-------+----+-------+-----------+-------+----------+-------+
  4176. // | ph | afo[] | sh | apo[] | apo'[] | data1 | data2 | data3 |
  4177. // +----+-------+----+-------+-----------+-------+----------+-------+
  4178. //+--------------------------------------------------------------------------
  4179. VOID
  4180. CPropertySetStream::_CompactStream(
  4181. IN CStreamChunkList const *pscl)
  4182. {
  4183. ULONG i, iMax, iAscend;
  4184. LONG cbChangeTotal, cbChangeTotalAscend;
  4185. CStreamChunk const *pscnk;
  4186. // Subtract one to avoid operating on the terminating chunk directly.
  4187. iMax = pscl->Count() - 1;
  4188. // If the first chunk does not indicate an insertion, the first for loop is
  4189. // exited with i == 0.
  4190. //
  4191. // If the first chunk represents an insertion, either i == iMax or i itself
  4192. // indexes the first chunk that can be compacted normally (in ascending
  4193. // chunk index order). In either case, we compact in descending chunk
  4194. // index order starting just below i.
  4195. DebugTrace(0, Dbg, (
  4196. "CompactStream: %l" szX " chunks @%lx\n",
  4197. pscl->Count(),
  4198. pscl->GetChunk(0)));
  4199. cbChangeTotal = 0;
  4200. for (i = 0; i < iMax; i++)
  4201. {
  4202. pscnk = pscl->GetChunk(i);
  4203. PROPASSERT(i == 0 || pscnk->cbChange < 0);
  4204. if (cbChangeTotal + pscnk->cbChange <= 0)
  4205. {
  4206. break;
  4207. }
  4208. cbChangeTotal += pscnk->cbChange;
  4209. }
  4210. iAscend = i; // save ascending order start
  4211. cbChangeTotalAscend = cbChangeTotal;
  4212. DebugTrace(0, Dbg, ("CompactStream: iAscend=%l" szX "\n", iAscend));
  4213. // First compact range in descending chunk index order if necessary:
  4214. while (i-- > 0)
  4215. {
  4216. pscnk = pscl->GetChunk(i);
  4217. PROPASSERT(i == 0 || pscnk->cbChange < 0);
  4218. DebugTrace(0, Dbg, ("CompactStream: descend: i=%l" szX "\n", i));
  4219. #if DBGPROP
  4220. pscl->AssertCbChangeTotal(pscnk, cbChangeTotal);
  4221. #endif
  4222. _CompactChunk(pscnk, cbChangeTotal, pscl->GetChunk(i + 1)->oOld);
  4223. cbChangeTotal -= pscnk->cbChange;
  4224. }
  4225. // Compact any remaining chunks in ascending chunk index order.
  4226. cbChangeTotal = cbChangeTotalAscend;
  4227. for (i = iAscend; i < iMax; i++)
  4228. {
  4229. pscnk = pscl->GetChunk(i);
  4230. PROPASSERT(i == 0 || pscnk->cbChange < 0);
  4231. DebugTrace(0, Dbg, ("CompactStream: ascend: i=%l" szX "\n", i));
  4232. cbChangeTotal += pscnk->cbChange;
  4233. #if DBGPROP
  4234. pscl->AssertCbChangeTotal(pscnk, cbChangeTotal);
  4235. #endif
  4236. _CompactChunk(pscnk, cbChangeTotal, pscl->GetChunk(i + 1)->oOld);
  4237. }
  4238. }
  4239. //+--------------------------------------------------------------------------
  4240. // Member: CPropertySetStream::_CompactChunk, private
  4241. //
  4242. // Synopsis: Compact the data block following one chunk
  4243. //
  4244. // Arguments: [pscnk] -- pointer to stream chunk
  4245. // [cbChangeTotal] -- Bias for this chunk
  4246. // [oOldNext] -- offset of next chunk
  4247. //
  4248. // Returns: None
  4249. //+--------------------------------------------------------------------------
  4250. VOID
  4251. CPropertySetStream::_CompactChunk(
  4252. IN CStreamChunk const *pscnk,
  4253. IN LONG cbChangeTotal,
  4254. IN ULONG oOldNext)
  4255. {
  4256. LONG cbDelta = cbChangeTotal + min(pscnk->cbChange, 0);
  4257. DebugTrace(0, Dbg, (
  4258. "CompactChunk(pscnk->oOld=%l" szX ", pscnk->cbChange=%s%l" szX "\n"
  4259. " cbChangeTotal=%s%l" szX
  4260. ", cbDelta=%s%l" szX
  4261. ", oOldNext=%l" szX ")\n",
  4262. pscnk->oOld,
  4263. pscnk->cbChange < 0? "-" : "",
  4264. pscnk->cbChange < 0? -pscnk->cbChange : pscnk->cbChange,
  4265. cbChangeTotal < 0? "-" : "",
  4266. cbChangeTotal < 0? -cbChangeTotal : cbChangeTotal,
  4267. cbDelta < 0? "-" : "",
  4268. cbDelta < 0? -cbDelta : cbDelta,
  4269. oOldNext));
  4270. if (cbChangeTotal != 0)
  4271. {
  4272. ULONG oSrc;
  4273. VOID const *pvSrc;
  4274. oSrc = pscnk->oOld - min(pscnk->cbChange, 0);
  4275. pvSrc = _MapOffsetToAddress(oSrc);
  4276. PropMoveMemory(
  4277. "CompactChunk",
  4278. _GetSectionHeader(),
  4279. (VOID *) Add2ConstPtr(pvSrc, cbChangeTotal),
  4280. pvSrc,
  4281. oOldNext - oSrc);
  4282. }
  4283. }
  4284. //+--------------------------------------------------------------------------
  4285. // Member: CPropertySetStream::_PatchSectionOffsets, private
  4286. //
  4287. // Synopsis: patch section offsets after moving data around
  4288. //
  4289. // Arguments: [cbChange] -- size delta
  4290. //
  4291. // Returns: none
  4292. //+--------------------------------------------------------------------------
  4293. VOID
  4294. CPropertySetStream::_PatchSectionOffsets(
  4295. LONG cbChange)
  4296. {
  4297. ULONG i;
  4298. for (i = 0; i < _cSection; i++)
  4299. {
  4300. FORMATIDOFFSET *pfo;
  4301. pfo = _GetFormatidOffset(i);
  4302. if (pfo->dwOffset > _oSection)
  4303. {
  4304. DebugTrace(0, DEBTRACE_PROPPATCH, (
  4305. "PatchSectionOffsets(%x): %l" szX " + %l" szX " --> %l" szX "\n",
  4306. i,
  4307. pfo->dwOffset,
  4308. cbChange,
  4309. pfo->dwOffset + cbChange));
  4310. pfo->dwOffset += cbChange;
  4311. }
  4312. }
  4313. }
  4314. //+--------------------------------------------------------------------------
  4315. // Member: CPropertySetStream::_GetNewOffset, private
  4316. //
  4317. // Synopsis: gets the new address
  4318. //
  4319. // Arguments: [pscl] -- list of stream chunks that were changed
  4320. // [oOld] -- old offset
  4321. //
  4322. // Returns: new offset
  4323. //+--------------------------------------------------------------------------
  4324. ULONG
  4325. CPropertySetStream::_GetNewOffset(
  4326. IN CStreamChunkList const *pscl,
  4327. IN ULONG oOld) const
  4328. {
  4329. // The Chunk list is sorted by start offsets. Locate the chunk to which
  4330. // the old offset belongs, then use the total change undergone by the chunk
  4331. // to compute the new offset.
  4332. ULONG i;
  4333. ULONG iMax = pscl->Count();
  4334. LONG cbChangeTotal = 0;
  4335. for (i = 0; i < iMax; i++)
  4336. {
  4337. CStreamChunk const *pscnk = pscl->GetChunk(i);
  4338. if (pscnk->oOld > oOld)
  4339. {
  4340. break;
  4341. }
  4342. cbChangeTotal += pscnk->cbChange;
  4343. if (pscnk->oOld == oOld)
  4344. {
  4345. PROPASSERT(pscnk->cbChange >= 0);
  4346. break;
  4347. }
  4348. }
  4349. PROPASSERT(i < iMax);
  4350. DebugTrace(0, Dbg, (
  4351. "GetNewOffset: %l" szX " + %l" szX " --> %l" szX "\n",
  4352. oOld,
  4353. cbChangeTotal,
  4354. oOld + cbChangeTotal));
  4355. return(oOld + cbChangeTotal);
  4356. }
  4357. //+--------------------------------------------------------------------------
  4358. // Member: CPropertySetStream::_ComputeMinimumSize, private
  4359. //
  4360. // Synopsis: computes the minimum possible size of a property set stream
  4361. //
  4362. // Arguments: [cbstm] -- actual stream size
  4363. // [pstatus] -- pointer to NTSTATUS code
  4364. //
  4365. // Returns: computed highest offset in use
  4366. //+--------------------------------------------------------------------------
  4367. ULONG
  4368. CPropertySetStream::_ComputeMinimumSize(
  4369. IN ULONG cbstm,
  4370. OUT NTSTATUS *pstatus)
  4371. {
  4372. ULONG oMax = 0;
  4373. *pstatus = STATUS_SUCCESS;
  4374. // Don't assume *any* class variables except _pph are loaded yet!
  4375. if (_pph != NULL && cbstm != 0)
  4376. {
  4377. ULONG cbMin;
  4378. ULONG i;
  4379. ULONG cSection;
  4380. cSection = 1;
  4381. cbMin = 0;
  4382. if (_HasPropHeader())
  4383. {
  4384. cSection = _pph->reserved;
  4385. cbMin = CB_PROPERTYSETHEADER + cSection * CB_FORMATIDOFFSET;
  4386. }
  4387. oMax = cbMin;
  4388. // Add the size of each section
  4389. for (i = 0; i < cSection; i++)
  4390. {
  4391. ULONG oSectionEnd;
  4392. PROPERTYSECTIONHEADER const *psh = _GetSectionHeader(i, pstatus);
  4393. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4394. cbMin += psh->cbSection;
  4395. oSectionEnd = _MapAddressToAbsOffset(psh) + psh->cbSection;
  4396. if (oMax < oSectionEnd)
  4397. {
  4398. oMax = oSectionEnd;
  4399. }
  4400. }
  4401. PROPASSERT(oMax <= cbstm);
  4402. PROPASSERT(cbMin <= oMax);
  4403. }
  4404. // ----
  4405. // Exit
  4406. // ----
  4407. Exit:
  4408. // oMax may have been set before an error occurred.
  4409. // In this case, set it to zero.
  4410. if( !NT_SUCCESS(*pstatus) )
  4411. oMax = 0;
  4412. return(oMax);
  4413. }
  4414. //+--------------------------------------------------------------------------
  4415. // Member: CPropertySetStream::_DictionaryLength
  4416. //
  4417. // Synopsis: compute length of property set dictionary
  4418. //
  4419. // Arguments: [pdy] -- pointer to dictionary
  4420. // [cbbuf] -- maximum length of accessible memory at pdy
  4421. // [pstatus] -- pointer to NTSTATUS code
  4422. //
  4423. // Returns: Byte-granular count of bytes in dictionary
  4424. //+--------------------------------------------------------------------------
  4425. ULONG
  4426. CPropertySetStream::_DictionaryLength(
  4427. IN DICTIONARY const *pdy,
  4428. IN ULONG cbbuf,
  4429. OUT NTSTATUS *pstatus ) const
  4430. {
  4431. ENTRY UNALIGNED const *pent;
  4432. ULONG cbDict = CB_DICTIONARY;
  4433. ULONG i;
  4434. *pstatus = STATUS_SUCCESS;
  4435. for (i = 0, pent = &pdy->rgEntry[0];
  4436. i < PropByteSwap( pdy->cEntries );
  4437. i++, pent = _NextDictionaryEntry( pent ))
  4438. {
  4439. if (cbbuf < cbDict + CB_ENTRY ||
  4440. cbbuf < _DictionaryEntryLength( pent ))
  4441. {
  4442. StatusCorruption(pstatus, "_DictionaryLength: section size");
  4443. goto Exit;
  4444. }
  4445. cbDict += _DictionaryEntryLength( pent );
  4446. }
  4447. // ----
  4448. // Exit
  4449. // ----
  4450. Exit:
  4451. return(cbDict);
  4452. }
  4453. //+--------------------------------------------------------------------------
  4454. // Member: CPropertySetStream::_PropertyNameLength
  4455. //
  4456. // Synopsis: compute length (*byte* count) of a property name
  4457. //
  4458. // Arguments: [pvName] -- property name, in the codepage of
  4459. // the property set
  4460. // [pcbName] -- pointer to returned byte length of name
  4461. //
  4462. // Returns: TRUE if name length is valid; else FALSE
  4463. //
  4464. // Note: The OLE 2.0 format mandates that the null be included as part
  4465. // of the length of the name that is stored in the dictionary.
  4466. // If the propset uses the Unicode code page, names contain
  4467. // WCHARs, otherwise they contain CHARs. In either case, the
  4468. // length is a byte count that includes the L'\0' or '\0'.
  4469. //
  4470. // Also note that this routine does not concern itself with
  4471. // the byte-order of the name: for Ansi names, it's irrelevant;
  4472. // and for Unicode names, L'\0' == PropByteSwap(L'\0').
  4473. //
  4474. //+--------------------------------------------------------------------------
  4475. BOOLEAN
  4476. CPropertySetStream::_PropertyNameLength(
  4477. IN VOID const *pvName,
  4478. OUT ULONG *pcbName) const
  4479. {
  4480. ULONG cch;
  4481. if (_CodePage == CP_WINUNICODE)
  4482. {
  4483. cch = Prop_wcslen((WCHAR const *) pvName) + 1;
  4484. *pcbName = cch * sizeof(WCHAR);
  4485. }
  4486. else
  4487. {
  4488. *pcbName = cch = strlen((char const *) pvName) + 1;
  4489. }
  4490. return(cch > 1 && cch <= CCH_MAXPROPNAMESZ );
  4491. }
  4492. //+--------------------------------------------------------------------------
  4493. // Member: CPropertySetStream::_ComparePropertyNames
  4494. //
  4495. // Synopsis: Compare two property names.
  4496. //
  4497. // Pre-Conditions:
  4498. // The property names are in the codepage of the
  4499. // property set.
  4500. //
  4501. // Arguments: [pvName1] -- property name 1
  4502. // [pvName2] -- property name 2
  4503. // [fSameByteOrder]-- TRUE: names are both big- or little-endian
  4504. // FALSE: 2n\d name is wrong endian
  4505. // [cbName] -- byte count of name length
  4506. // (includes terminator)
  4507. //
  4508. // Returns: TRUE if names are equal
  4509. //+--------------------------------------------------------------------------
  4510. BOOLEAN
  4511. CPropertySetStream::_ComparePropertyNames(
  4512. IN VOID const *pvName1,
  4513. IN VOID const *pvName2,
  4514. IN BOOL fSameByteOrder,
  4515. IN ULONG cbName) const
  4516. {
  4517. if (_CodePage == CP_WINUNICODE)
  4518. {
  4519. // On big-endian systems, when the second name
  4520. // is byte-swapped, we'll byte-swap it into a new
  4521. // buffer to use for the comparisson.
  4522. #ifdef BIGENDIAN
  4523. WCHAR awcByteSwap[ CCH_MAXPROPNAMESZ ];
  4524. if( !fSameByteOrder )
  4525. {
  4526. ULONG ulIndex = 0;
  4527. PROPASSERT( (WCHAR)'\0' == ByteSwap( (WCHAR) '\0'));
  4528. do
  4529. {
  4530. awcByteSwap[ ulIndex ] =
  4531. ByteSwap( ((WCHAR*)pvName2)[ ulIndex ] );
  4532. } while (awcByteSwap[ulIndex++] != (WCHAR)'\0');
  4533. }
  4534. #endif // BIGENDIAN
  4535. return(Prop_wcsnicmp(
  4536. (WCHAR const *) pvName1,
  4537. #ifdef BIGENDIAN
  4538. fSameByteOrder ? (WCHAR const *) pvName2
  4539. : awcByteSwap,
  4540. #else
  4541. (WCHAR const * ) pvName2,
  4542. #endif
  4543. cbName / sizeof(WCHAR) ) == 0);
  4544. } // if (_CodePage == CP_WINUNICODE)
  4545. else
  4546. {
  4547. return(_strnicmp(
  4548. (char const *) pvName1,
  4549. (char const *) pvName2,
  4550. cbName) == 0);
  4551. } // if (_CodePage == CP_WINUNICODE) ... else
  4552. }
  4553. //+---------------------------------------------------------------------------
  4554. // Function: CPropertySetStream::DuplicatePropertyName
  4555. //
  4556. // Synopsis: Duplicate an OLECHAR property name string
  4557. //
  4558. // Arguments: [poszName] -- input string
  4559. // [cbName] -- count of bytes in string (includes null)
  4560. // [pstatus] -- pointer to NTSTATUS code
  4561. //
  4562. // Returns: pointer to new string
  4563. //---------------------------------------------------------------------------
  4564. OLECHAR *
  4565. CPropertySetStream::DuplicatePropertyName(
  4566. IN OLECHAR const *poszName,
  4567. IN ULONG cbName,
  4568. OUT NTSTATUS *pstatus) const
  4569. {
  4570. OLECHAR *poc = NULL;
  4571. *pstatus = STATUS_SUCCESS;
  4572. PROPASSERT(cbName != 0);
  4573. PROPASSERT(IsOLECHARString(poszName, cbName));
  4574. if (cbName != 0)
  4575. {
  4576. PROPASSERT((ocslen(poszName) + 1) * sizeof(OLECHAR) == cbName);
  4577. poc = (OLECHAR *) _pma->Allocate(cbName);
  4578. if (NULL == poc)
  4579. {
  4580. StatusNoMemory(pstatus, "DuplicatePropertyName: no memory");
  4581. goto Exit;
  4582. }
  4583. RtlCopyMemory(poc, poszName, cbName);
  4584. }
  4585. // ----
  4586. // Exit
  4587. // ----
  4588. Exit:
  4589. return(poc);
  4590. }
  4591. //+--------------------------------------------------------------------------
  4592. // Member: CPropertySetStream::QueryPropid
  4593. //
  4594. // Synopsis: translate a property name to a property id using the
  4595. // dictionary on the property stream
  4596. //
  4597. // Arguments: [poszName] -- name of property
  4598. // [pstatus] -- pointer to NTSTATUS code
  4599. //
  4600. // Returns: propid for property if found; PID_ILLEGAL if not found
  4601. //---------------------------------------------------------------------------
  4602. PROPID
  4603. CPropertySetStream::QueryPropid(
  4604. IN OLECHAR const *poszName,
  4605. OUT NTSTATUS *pstatus )
  4606. {
  4607. // ------
  4608. // Locals
  4609. // ------
  4610. ULONG cbname;
  4611. DICTIONARY const *pdy;
  4612. ENTRY UNALIGNED const *pent;
  4613. ULONG cdye;
  4614. ULONG cbDict; // BYTE granular size!
  4615. VOID const *pvName=NULL;
  4616. PROPID propid = PID_ILLEGAL;
  4617. // ----------
  4618. // Initialize
  4619. // ----------
  4620. *pstatus = STATUS_SUCCESS;
  4621. PROPASSERT(_HasPropHeader());
  4622. PROPASSERT(_IsMapped());
  4623. PROPASSERT( IsOLECHARString( poszName, MAXULONG ));
  4624. PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder);
  4625. // Make sure this isn't a UD propset which has been deleted.
  4626. if (_State & CPSS_USERDEFINEDDELETED)
  4627. {
  4628. StatusAccessDenied(pstatus, "QueryPropid: deleted");
  4629. goto Exit;
  4630. }
  4631. // Put the name into pvName, converting it if
  4632. // necessary to the code-page of the property set.
  4633. pvName = poszName;
  4634. if (_CodePage == CP_WINUNICODE // Property set is Unicode
  4635. &&
  4636. !OLECHAR_IS_UNICODE ) // Name is in Ansi
  4637. {
  4638. // Convert the caller-provided name from the system
  4639. // Ansi codepage to Unicode.
  4640. ULONG cb = 0;
  4641. pvName = NULL;
  4642. _OLECHARToWideChar( poszName, (ULONG)-1, CP_ACP,
  4643. (WCHAR**)&pvName, &cb, pstatus );
  4644. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4645. // If necessary, swap the WCHARs of the Unicode string.
  4646. //PropByteSwap( (WCHAR*) pvName, cb, sizeof(WCHAR) );
  4647. }
  4648. else
  4649. if (_CodePage != CP_WINUNICODE // Property set is Ansi
  4650. &&
  4651. OLECHAR_IS_UNICODE ) // Name is in Unicode
  4652. {
  4653. // Convert the caller-provided name from Unicode
  4654. // to the propset's Ansi codepage.
  4655. ULONG cb = 0;
  4656. pvName = NULL;
  4657. _OLECHARToMultiByte( poszName, (ULONG)-1, _CodePage,
  4658. (CHAR**)&pvName, &cb, pstatus );
  4659. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4660. }
  4661. // How long is this property name (in bytes)?
  4662. if (!_PropertyNameLength(pvName, &cbname))
  4663. {
  4664. // The length is invalid.
  4665. StatusInvalidParameter(pstatus, "QueryPropid: name length");
  4666. goto Exit;
  4667. }
  4668. // Get a pointer to the raw dictionary.
  4669. pdy = (DICTIONARY const *) _LoadProperty(PID_DICTIONARY, &cbDict, pstatus);
  4670. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4671. // Is there a dictionary?
  4672. if (pdy != NULL)
  4673. {
  4674. // Yes - there is a dictionary.
  4675. PROPERTYSECTIONHEADER const *psh = _GetSectionHeader();
  4676. // Search the dictionary for an entry name matching
  4677. // pvName.
  4678. for (cdye = PropByteSwap(pdy->cEntries), pent = &pdy->rgEntry[0];
  4679. cdye > 0;
  4680. cdye--, pent = _NextDictionaryEntry( pent ))
  4681. {
  4682. // Is the length of this dictionary entry valid?
  4683. if ( _MapAddressToOffset(pent) + _DictionaryEntryLength( pent )
  4684. > psh->cbSection
  4685. )
  4686. {
  4687. StatusCorruption(pstatus, "QueryPropid: section size");
  4688. goto Exit;
  4689. }
  4690. // If the byte-length matches what we're looking for,
  4691. // and the names compare successfully, then we're done.
  4692. if ( CCh2CB(PropByteSwap( ENTRY_GetCch(pent) )) == cbname
  4693. &&
  4694. _ComparePropertyNames(pvName, ENTRY_GetSz(pent),
  4695. FALSE, // pvName, pent->sz could be dif Endians
  4696. cbname)
  4697. )
  4698. {
  4699. propid = PropByteSwap( ENTRY_GetPropid(pent) );
  4700. break;
  4701. }
  4702. } // for (cdye = PropByteSwap(pdy->cEntries), pent = &pdy->rgEntry[0]; ...
  4703. PROPASSERT(cdye > 0 || pent == Add2ConstPtr(pdy, cbDict));
  4704. } // if (pdy != NULL)
  4705. // ----
  4706. // Exit
  4707. // ----
  4708. Exit:
  4709. // If we did an alloc on the name to munge it,
  4710. // delete that buffer now. We must cast pvName
  4711. // as a non-const in order for the compiler to accept
  4712. // the free call.
  4713. if( pvName != poszName )
  4714. _pma->Free( (VOID*) pvName );
  4715. return(propid);
  4716. }
  4717. //+--------------------------------------------------------------------------
  4718. // Member: CPropertySetStream::QueryPropertyNameBuf
  4719. //
  4720. // Synopsis: convert from a property id to a property name using the
  4721. // dictionary in the property set, and putting the result
  4722. // in a caller-provided buffer.
  4723. //
  4724. // Arguments: [propid] -- property id to look up
  4725. // [aocName] -- output buffer
  4726. // [pcbName] -- IN: length of aocName;
  4727. // OUT: actual length of name
  4728. // [pstatus] -- pointer to NTSTATUS code
  4729. //
  4730. // Returns: TRUE if name is found in dictionary
  4731. //---------------------------------------------------------------------------
  4732. BOOLEAN
  4733. CPropertySetStream::QueryPropertyNameBuf(
  4734. IN PROPID propid,
  4735. OUT OLECHAR *aocName,
  4736. IN OUT ULONG *pcbName,
  4737. OUT NTSTATUS *pstatus)
  4738. {
  4739. BOOL fFound = FALSE;
  4740. DICTIONARY const *pdy;
  4741. ULONG cbDict; // BYTE granular size!
  4742. *pstatus = STATUS_SUCCESS;
  4743. PROPASSERT(_IsMapped());
  4744. PROPASSERT(propid != PID_DICTIONARY);
  4745. PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder);
  4746. PROPASSERT(NULL != aocName);
  4747. // Ensure that this isn't an already-deleted UD propset.
  4748. if (_State & CPSS_USERDEFINEDDELETED)
  4749. {
  4750. StatusAccessDenied(pstatus, "QueryPropertyNameBuf: deleted");
  4751. goto Exit;
  4752. }
  4753. // Get a pointer to the raw dictionary.
  4754. pdy = (DICTIONARY const *) _LoadProperty(PID_DICTIONARY, &cbDict, pstatus);
  4755. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4756. // Is there a dictionary?
  4757. if (pdy != NULL)
  4758. {
  4759. // Yes - the dictionary was found.
  4760. ULONG cdye;
  4761. ENTRY UNALIGNED const *pent;
  4762. VOID const *pvDictEnd;
  4763. // Get pointers to the first and last+1 entries.
  4764. pent = pdy->rgEntry;
  4765. pvDictEnd = Add2ConstPtr(pdy, cbDict);
  4766. // Scan through the dictionary, searching for 'propid'.
  4767. for (cdye = PropByteSwap(pdy->cEntries), pent = &pdy->rgEntry[0];
  4768. cdye > 0;
  4769. cdye--, pent = _NextDictionaryEntry( pent ))
  4770. {
  4771. // Make sure this entry doesn't go off the end of the
  4772. // dictionary.
  4773. if (Add2ConstPtr(pent, _DictionaryEntryLength( pent )) > pvDictEnd)
  4774. {
  4775. StatusCorruption(pstatus, "QueryPropertyNameBuf: dictionary entry size");
  4776. goto Exit;
  4777. }
  4778. // Is this the PID we're looking for?
  4779. if (PropByteSwap(ENTRY_GetPropid(pent)) == propid)
  4780. {
  4781. // Yes. Copy or convert the name into the caller's
  4782. // buffer.
  4783. // Is a Unicode to Ansi conversion required?
  4784. if (_CodePage == CP_WINUNICODE // Property set is Unicode
  4785. &&
  4786. !OLECHAR_IS_UNICODE ) // Caller's buffer is Ansi
  4787. {
  4788. WCHAR *pwszName = (WCHAR*) ENTRY_GetSz(pent);
  4789. // If we're byte-swapping, alloc a new buffer, swap
  4790. // pwszName into it (getting the string into system-endian
  4791. // byte-order), and point pwszName to the result.
  4792. PBSInPlaceAlloc( &pwszName, NULL, pstatus );
  4793. if( !NT_SUCCESS( *pstatus )) goto Exit;
  4794. // Convert the Unicode string in the property set
  4795. // to the system default codepage.
  4796. _WideCharToOLECHAR( pwszName, (ULONG)-1, CP_ACP,
  4797. &aocName, pcbName, pstatus );
  4798. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4799. // If we allocated a buffer for byte-swapping,
  4800. // we don't need it any longer.
  4801. if( pwszName != (WCHAR*) ENTRY_GetSz(pent) )
  4802. delete pwszName;
  4803. }
  4804. // Or is an Ansi to Unicode conversion required?
  4805. else
  4806. if (_CodePage != CP_WINUNICODE // Property set is Ansi
  4807. &&
  4808. OLECHAR_IS_UNICODE ) // Caller's buffer is Unicode
  4809. {
  4810. // Convert the Ansi property set name from the
  4811. // propset's codepage to Unicode.
  4812. _MultiByteToOLECHAR( (CHAR*) ENTRY_GetSz(pent),
  4813. (ULONG)-1, _CodePage,
  4814. &aocName, pcbName, pstatus );
  4815. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4816. }
  4817. // Otherwise, no conversion of the name is required
  4818. else
  4819. {
  4820. ULONG ulEntryLen= CCh2CB(PropByteSwap(ENTRY_GetCch(pent)));
  4821. // Copy the name into the caller's buffer.
  4822. RtlCopyMemory(aocName, ENTRY_GetSz(pent),
  4823. min(ulEntryLen, *pcbName));
  4824. // Swap the name to the correct endian
  4825. // (This will do nothing if OLECHARs are CHARs).
  4826. PBSBuffer( aocName,
  4827. min( CCh2CB(PropByteSwap( ENTRY_GetCch(pent) )),
  4828. *pcbName),
  4829. sizeof(OLECHAR) );
  4830. // Tell the caller the actual size of the name.
  4831. *pcbName = ulEntryLen;
  4832. }
  4833. PROPASSERT( NULL == aocName || IsOLECHARString( aocName, MAXULONG ));
  4834. fFound = TRUE;
  4835. break;
  4836. } // if (ENTRY_GetPropid(pent) == propid)
  4837. } // for (cdye = pdy->cEntries, pent = &pdy->rgEntry[0]; ...
  4838. PROPASSERT(fFound || pent == pvDictEnd);
  4839. } // if (pdy != NULL)
  4840. // ----
  4841. // Exit
  4842. // ----
  4843. Exit:
  4844. return( fFound );
  4845. }
  4846. //+--------------------------------------------------------------------------
  4847. // Member: CPropertySetStream::QueryPropertyNames
  4848. //
  4849. // Synopsis: query dictionary names for the passed property ids.
  4850. //
  4851. // Arguments: [cprop] -- count of name to propid mappings to change
  4852. // [apid] -- array of property ids
  4853. // [aposz] -- array of pointers to the new names
  4854. // [pstatus] -- pointer to NTSTATUS code
  4855. //
  4856. // Returns: TRUE if the property exists.
  4857. //+--------------------------------------------------------------------------
  4858. BOOLEAN
  4859. CPropertySetStream::QueryPropertyNames(
  4860. IN ULONG cprop,
  4861. IN PROPID const *apid,
  4862. OUT OLECHAR *aposz[],
  4863. OUT NTSTATUS *pstatus)
  4864. {
  4865. DICTIONARY const *pdy;
  4866. ULONG cbDict; // BYTE granular size!
  4867. ULONG iprop;
  4868. BOOLEAN fFound = FALSE;
  4869. *pstatus = STATUS_SUCCESS;
  4870. PROPASSERT(_HasPropHeader());
  4871. PROPASSERT(_IsMapped());
  4872. PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder);
  4873. // If this is an attempt to access a deleted UD
  4874. // propset, exit now.
  4875. if (_State & CPSS_USERDEFINEDDELETED)
  4876. {
  4877. StatusAccessDenied(pstatus, "QueryPropertyNames: deleted");
  4878. goto Exit;
  4879. }
  4880. // Validate the input array of strings.
  4881. for (iprop = 0; iprop < cprop; iprop++)
  4882. {
  4883. PROPASSERT(aposz[iprop] == NULL);
  4884. }
  4885. // Get a pointer to the beginning of the dictionary
  4886. pdy = (DICTIONARY const *) _LoadProperty(PID_DICTIONARY, &cbDict, pstatus);
  4887. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4888. // Did we get a dictionary?
  4889. if (pdy != NULL)
  4890. {
  4891. // Yes, the dictionary exists.
  4892. ULONG i;
  4893. ENTRY UNALIGNED const *pent;
  4894. // Iterate through each of the entries in the dictionary.
  4895. for (i = 0, pent = &pdy->rgEntry[0];
  4896. i < PropByteSwap( pdy->cEntries );
  4897. i++, pent = _NextDictionaryEntry( pent ))
  4898. {
  4899. // Scan the input array of PIDs to see if one matches
  4900. // this dictionary entry.
  4901. for (iprop = 0; iprop < cprop; iprop++)
  4902. {
  4903. if( PropByteSwap(ENTRY_GetPropid(pent)) == apid[iprop] )
  4904. {
  4905. // We've found an entry in the dictionary
  4906. // that's in the input PID array. Put the property's
  4907. // name in the caller-provided array (aposz).
  4908. PROPASSERT(aposz[iprop] == NULL);
  4909. // Do we need to convert to Unicode?
  4910. if (_CodePage != CP_WINUNICODE // Ansi property set
  4911. &&
  4912. OLECHAR_IS_UNICODE) // Unicode property names
  4913. {
  4914. ULONG cbName = 0;
  4915. _MultiByteToOLECHAR( ENTRY_GetSz(pent),
  4916. (ULONG)-1, _CodePage,
  4917. &aposz[iprop], &cbName, pstatus );
  4918. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4919. }
  4920. // Or, do we need to convert to Ansi?
  4921. else
  4922. if (_CodePage == CP_WINUNICODE // Unicode property set
  4923. &&
  4924. !OLECHAR_IS_UNICODE) // Ansi property names
  4925. {
  4926. ULONG cbName = 0;
  4927. WCHAR *pwszName = (WCHAR*) ENTRY_GetSz(pent);
  4928. // If necessary, swap the Unicode name in the dictionary,
  4929. // pointing pwszName to the new, byte-swapped, buffer.
  4930. PBSInPlaceAlloc( &pwszName, NULL, pstatus );
  4931. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4932. // And convert to Ansi.
  4933. _WideCharToOLECHAR( pwszName, (ULONG)-1, CP_ACP,
  4934. &aposz[iprop], &cbName, pstatus );
  4935. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4936. // If we alloced a new buffer for byte-swapping,
  4937. // we can free it now.
  4938. if( pwszName != (WCHAR*) ENTRY_GetSz(pent) )
  4939. delete pwszName;
  4940. } // else if (_CodePage == CP_WINUNICODE ...
  4941. // Otherwise, both the propset & in-memory property names
  4942. // are both Unicode or both Ansi, so we can just do
  4943. // an alloc & copy.
  4944. else
  4945. {
  4946. aposz[iprop] =
  4947. DuplicatePropertyName( (OLECHAR *)
  4948. ENTRY_GetSz(pent),
  4949. CCh2CB(PropByteSwap(ENTRY_GetCch(pent))),
  4950. pstatus);
  4951. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  4952. // If necessary, swap the in-memory copy.
  4953. PBSBuffer( (OLECHAR*) aposz[iprop],
  4954. CCh2CB( PropByteSwap( pent->cch )),
  4955. sizeof(OLECHAR) );
  4956. } // if (_CodePage != CP_WINUNICODE ... else if ... else
  4957. PROPASSERT( IsOLECHARString( aposz[iprop], MAXULONG ));
  4958. fFound = TRUE;
  4959. } // if (PropByteSwap(ENTRY_GetPropid(pent) == apid[iprop])
  4960. } // for (iprop = 0; iprop < cprop; iprop++)
  4961. } // for (i = 0, pent = &pdy->rgEntry[0];
  4962. PROPASSERT(pent == Add2ConstPtr(pdy, cbDict));
  4963. } // if (pdy != NULL)
  4964. // ----
  4965. // Exit
  4966. // ----
  4967. Exit:
  4968. // If the property name simply didn't exist, return
  4969. // a special success code.
  4970. if( !fFound && NT_SUCCESS(*pstatus) )
  4971. *pstatus = STATUS_BUFFER_ALL_ZEROS;
  4972. return( fFound );
  4973. } // CPropertySetStream::QueryPropertyNames
  4974. //+--------------------------------------------------------------------------
  4975. // Member: CPropertySetStream::SetPropertyNames
  4976. //
  4977. // Synopsis: changes dictionary entry names associated with property ids.
  4978. //
  4979. // Arguments: [cprop] -- count of name to propid mappings to change
  4980. // [apid] -- array of property ids
  4981. // [aposz] -- array of pointers to the new names
  4982. // [pstatus] -- pointer to NTSTATUS code
  4983. //
  4984. // Returns: None
  4985. //
  4986. // Note: Attempting to set a property name for a property that does not
  4987. // exist in the property set is not an error.
  4988. //
  4989. // Attempting to set a property name or property id that would
  4990. // result in a duplicate name or property id causes the existing
  4991. // entry(ies) to be replaced.
  4992. //+--------------------------------------------------------------------------
  4993. VOID
  4994. CPropertySetStream::SetPropertyNames(
  4995. IN ULONG cprop,
  4996. IN const PROPID *apid,
  4997. IN OPTIONAL OLECHAR const * const aposz[],
  4998. OUT NTSTATUS *pstatus )
  4999. {
  5000. // ------
  5001. // Locals
  5002. // ------
  5003. DICTIONARY *pdy = NULL;
  5004. ULONG cbDictOld = 0; // Byte granular Old dictionary size
  5005. ULONG cbDictOldD = 0; // Dword granular Old dictionary size
  5006. ULONG iprop = 0;
  5007. ULONG i = 0;
  5008. ULONG cDel, cAdd;
  5009. LONG cbDel, cbAdd; // Byte granular sizes
  5010. LONG cbChangeD; // Dword granular size
  5011. ENTRY UNALIGNED *pent;
  5012. BOOLEAN fDupPropid = FALSE;
  5013. BOOLEAN fDupName = FALSE;
  5014. BOOLEAN fDeleteByName = FALSE;
  5015. BOOLEAN fDeleteAll = FALSE;
  5016. VOID **appvNames = NULL;
  5017. ULONG cbstm;
  5018. ULONG oDictionary;
  5019. ULONG cbTail;
  5020. ULONG cbNewSize;
  5021. // ----------
  5022. // Initialize
  5023. // ----------
  5024. *pstatus = STATUS_SUCCESS;
  5025. DebugTrace(0, Dbg, (
  5026. "SetPropertyNames(cprop=%x, apid=%x, apwsz=%x)\n",
  5027. cprop,
  5028. apid,
  5029. aposz));
  5030. PROPASSERT(_HasPropHeader());
  5031. PROPASSERT(_IsMapped());
  5032. PROPASSERT(PROPSET_BYTEORDER == _pph->wByteOrder);
  5033. // --------
  5034. // Validate
  5035. // --------
  5036. // Verify that this propset is modifiable.
  5037. if (IsReadOnlyPropertySet(_Flags, _State))
  5038. {
  5039. StatusAccessDenied(pstatus, "SetPropertyNames: deleted or read-only");
  5040. goto Exit;
  5041. }
  5042. // Verify that none of the names are illegally long.
  5043. if (aposz != NULL)
  5044. {
  5045. for (iprop = 0; iprop < cprop; iprop++)
  5046. {
  5047. PROPASSERT( IsOLECHARString( aposz[iprop], MAXULONG ));
  5048. if (ocslen( aposz[iprop] ) > CCH_MAXPROPNAME)
  5049. {
  5050. StatusInvalidParameter(pstatus, "SetPropertyNames: Name is too long" );
  5051. goto Exit;
  5052. }
  5053. }
  5054. } // if (apwsz != NULL)
  5055. // ----------------------------------------------------------------
  5056. // If necessary, convert each of the caller-provided names:
  5057. // to Unicode (if the property set is Unicode) or Ansi (otherwise).
  5058. // ----------------------------------------------------------------
  5059. // In the end, appvNames will have the names in the same codepage
  5060. // as the property set.
  5061. appvNames = (VOID **) aposz;
  5062. if (appvNames != NULL)
  5063. {
  5064. // Do we need to convert the caller's names to Ansi?
  5065. if( _CodePage != CP_WINUNICODE // Property set is Ansi
  5066. &&
  5067. OLECHAR_IS_UNICODE ) // Caller's names are Unicode
  5068. {
  5069. // Allocate an array of cprop string pointers.
  5070. appvNames = (VOID **) newk(mtPropSetStream, NULL) char *[cprop];
  5071. if (appvNames == NULL)
  5072. {
  5073. StatusNoMemory(pstatus, "SetpropertyNames: Ansi Name Pointers");
  5074. goto Exit;
  5075. }
  5076. RtlZeroMemory(appvNames, cprop * sizeof(appvNames[0]));
  5077. // Convert the caller-provided property names from Unicode to
  5078. // the property set's codepage.
  5079. for (iprop = 0; iprop < cprop; iprop++)
  5080. {
  5081. ULONG cb = 0;
  5082. appvNames[iprop] = NULL;
  5083. _OLECHARToMultiByte( (OLECHAR*) aposz[iprop], (ULONG)-1, _CodePage,
  5084. (CHAR**) &appvNames[iprop], &cb, pstatus );
  5085. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5086. }
  5087. } // if( _CodePage != CP_WINUNICODE ...
  5088. // Or, do we need to convert the caller's names to Unicode?
  5089. if( _CodePage == CP_WINUNICODE // Property set is Unicode
  5090. &&
  5091. !OLECHAR_IS_UNICODE ) // Caller's names are Ansi
  5092. {
  5093. // Allocate an array of cprop string pointers.
  5094. appvNames = (VOID **) newk(mtPropSetStream, NULL) WCHAR *[cprop];
  5095. if (appvNames == NULL)
  5096. {
  5097. StatusNoMemory(pstatus, "SetpropertyNames: Unicode Name Pointers");
  5098. goto Exit;
  5099. }
  5100. RtlZeroMemory(appvNames, cprop * sizeof(appvNames[0]));
  5101. // Convert the caller-provided property names from the system
  5102. // default Ansi codepage to Unicode.
  5103. for (iprop = 0; iprop < cprop; iprop++)
  5104. {
  5105. ULONG cb = 0;
  5106. appvNames[iprop] = NULL;
  5107. _OLECHARToWideChar( (OLECHAR*) aposz[iprop], (ULONG)-1, CP_ACP,
  5108. (WCHAR**) &appvNames[iprop], &cb, pstatus );
  5109. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5110. }
  5111. } // if( _CodePage == CP_WINUNICODE )
  5112. } // if (appvNames != NULL)
  5113. // -----------------------------------------------------
  5114. // Compute total size of entries to be modified or added
  5115. // -----------------------------------------------------
  5116. cbAdd = 0;
  5117. cAdd = 0;
  5118. for (iprop = 0; iprop < cprop; iprop++)
  5119. {
  5120. // Did the caller give us no array of names? If so,
  5121. // it means that the name for this PID is to be deleted.
  5122. if (appvNames == NULL)
  5123. {
  5124. // If the PID is for the dictionary, then it must be the
  5125. // only entry in apid, and it indicates that we're going to
  5126. // delete all the names in the dictionary.
  5127. if (apid[iprop] == PID_DICTIONARY)
  5128. {
  5129. if (cprop != 1)
  5130. {
  5131. StatusInvalidParameter(pstatus, "SetPropertyNames: DeleteAll parms");
  5132. goto Exit;
  5133. }
  5134. fDeleteAll = TRUE;
  5135. }
  5136. }
  5137. // Otherwise, we're setting a new name for this PID.
  5138. else
  5139. {
  5140. ULONG cbname;
  5141. // Validate the caller-provided length.
  5142. if (!_PropertyNameLength(appvNames[iprop], &cbname))
  5143. {
  5144. StatusInvalidParameter(pstatus, "SetPropertyNames: name length");
  5145. goto Exit;
  5146. }
  5147. // See if this propid or name appears later in the array.
  5148. for (i = iprop + 1; i < cprop; i++)
  5149. {
  5150. ULONG cbname2;
  5151. if (apid[i] == apid[iprop])
  5152. {
  5153. fDupPropid = TRUE;
  5154. break;
  5155. }
  5156. _PropertyNameLength(appvNames[i], &cbname2);
  5157. if (cbname == cbname2 &&
  5158. _ComparePropertyNames(
  5159. appvNames[iprop],
  5160. appvNames[i],
  5161. TRUE, // Both names are in the same byte-order
  5162. cbname))
  5163. {
  5164. fDupName = TRUE;
  5165. break;
  5166. }
  5167. }
  5168. // If this propid appears only once or if it's the last instance,
  5169. // count it. If the property set is Unicode, include DWORD padding.
  5170. if (i == cprop)
  5171. {
  5172. DebugTrace(0, Dbg, (
  5173. _CodePage == CP_WINUNICODE?
  5174. "Adding New Entry: propid=%lx L'%ws'\n" :
  5175. "Adding New Entry: propid=%lx '%s'\n",
  5176. apid[iprop],
  5177. appvNames[iprop]));
  5178. cAdd++;
  5179. cbAdd += CB_ENTRY + cbname;
  5180. if( _CodePage == CP_WINUNICODE )
  5181. {
  5182. cbAdd = DwordAlign( cbAdd );
  5183. }
  5184. }
  5185. }
  5186. }
  5187. PROPASSERT( _CodePage == CP_WINUNICODE ? IsDwordAligned( cbAdd ) : TRUE );
  5188. // ---------------------------------------------
  5189. // Get the dictionary, creating it if necessary.
  5190. // ---------------------------------------------
  5191. _SetModified();
  5192. for (i = 0; ; i++)
  5193. {
  5194. PROPERTY_INFORMATION pinfo;
  5195. PROPVARIANT var;
  5196. pdy = (DICTIONARY *) _LoadProperty(PID_DICTIONARY, &cbDictOld, pstatus);
  5197. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5198. if (pdy != NULL)
  5199. {
  5200. break;
  5201. }
  5202. PROPASSERT(i == 0);
  5203. if (cprop == 0 || appvNames == NULL)
  5204. {
  5205. // no dictionary and we are deleting or doing nothing -- return
  5206. goto Exit;
  5207. }
  5208. // create dictionary if it doesn't exist
  5209. DebugTrace(0, Dbg, ("Creating empty dictionary\n"));
  5210. PROPASSERT(CB_SERIALIZEDPROPERTYVALUE == CB_DICTIONARY);
  5211. pinfo.cbprop = CB_SERIALIZEDPROPERTYVALUE;
  5212. pinfo.pid = PID_DICTIONARY;
  5213. var.vt = VT_DICTIONARY;
  5214. SetValue(1, &var, &pinfo, pstatus);
  5215. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5216. Validate(pstatus); // Make sure dictionary was properly created
  5217. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5218. DebugTrace(0, Dbg, ("Created empty dictionary\n"));
  5219. } // for (i = 0; ; i++)
  5220. // ----------------------------------------------------------------
  5221. // Compute total size of existing entries to be modified or deleted
  5222. // ----------------------------------------------------------------
  5223. // Walk the dictionary looking for entries which are referenced
  5224. // in the caller's 'apid' array or 'appvNames' array.
  5225. cbDel = 0;
  5226. cDel = 0;
  5227. for (i = 0, pent = &pdy->rgEntry[0];
  5228. i < PropByteSwap( pdy->cEntries );
  5229. i++, pent = _NextDictionaryEntry( pent ))
  5230. {
  5231. DebugTrace(0, Dbg, (
  5232. _CodePage == CP_WINUNICODE?
  5233. "Dictionary Entry @%lx: propid=%lx L'%ws'\n" :
  5234. "Dictionary Entry @%lx: propid=%lx '%s'\n",
  5235. pent,
  5236. PropByteSwap( ENTRY_GetPropid(pent) ),
  5237. pent->sz ));
  5238. // For this dictionary entry, walk the caller's
  5239. // 'apid' and 'appvNames' arrays, looking for a match.
  5240. for (iprop = 0; iprop < cprop; iprop++)
  5241. {
  5242. // If we get to the bottom of this 'for' loop,
  5243. // then we know that we've found an entry to delete.
  5244. // If fDeleteAll, or the PID in apid matches this
  5245. // dictionary entry, then we can fall to the bottom.
  5246. // Otherwise, the following 'if' block checks the
  5247. // name in 'appvNames' against this dictionary entry.
  5248. if (!fDeleteAll
  5249. &&
  5250. apid[iprop] != PropByteSwap( ENTRY_GetPropid(pent) ))
  5251. {
  5252. // The caller's PID didn't match this dictionary entry,
  5253. // does the name?
  5254. ULONG cbname;
  5255. // If we have no names from the caller, then we obviously
  5256. // don't have a match, and we can continue on to check this
  5257. // dictionary entry against the next of the caller's PIDs.
  5258. if (appvNames == NULL)
  5259. {
  5260. continue;
  5261. }
  5262. // Or, if this name from the caller doesn't match this
  5263. // dictionary entry, we again can continue on to check
  5264. // the next of the caller's properties.
  5265. _PropertyNameLength(appvNames[iprop], &cbname);
  5266. if (cbname != CCh2CB( PropByteSwap( ENTRY_GetCch(pent) ))
  5267. ||
  5268. !_ComparePropertyNames(
  5269. appvNames[iprop],
  5270. ENTRY_GetSz(pent),
  5271. FALSE, // appvNames & pent->sz may be dif endians.
  5272. cbname)
  5273. )
  5274. {
  5275. continue;
  5276. }
  5277. fDeleteByName = TRUE;
  5278. } // if (!fDeleteAll ...
  5279. // If we reach this point, we're going to delete this entry
  5280. // in the dictionary. So update cDel & cbDel.
  5281. DebugTrace(0, Dbg, (
  5282. "Deleting Entry (%s) @%lx: propid=%lx\n",
  5283. fDeleteAll? "DeleteAll" :
  5284. apid[iprop] == PropByteSwap(ENTRY_GetPropid(pent))
  5285. ? "replace by propid"
  5286. : "replace by name",
  5287. pent,
  5288. PropByteSwap( ENTRY_GetPropid(pent) )));
  5289. cDel++;
  5290. cbDel += _DictionaryEntryLength( pent );
  5291. // We don't need to continue through the caller's arrays,
  5292. // we can move on to the next dictionary entry.
  5293. break;
  5294. } // for (iprop = 0; iprop < cprop; iprop++)
  5295. } // for (i = 0, pent = &pdy->rgEntry[0]; ...
  5296. PROPASSERT(pent == Add2Ptr(pdy, cbDictOld));
  5297. PROPASSERT( _CodePage == CP_WINUNICODE ? IsDwordAligned( cbDel ) : TRUE );
  5298. cbDictOldD = DwordAlign(cbDictOld);
  5299. cbChangeD = DwordAlign(cbDictOld + cbAdd - cbDel) - cbDictOldD;
  5300. cbstm = _oSection + _GetSectionHeader()->cbSection + _cbTail;
  5301. oDictionary = _MapAddressToOffset(pdy);
  5302. cbTail;
  5303. cbTail = cbstm - (_oSection + oDictionary + cbDictOldD);
  5304. // --------------------------------------------------------
  5305. // Before we change anything, grow the stream if necessary.
  5306. // --------------------------------------------------------
  5307. if (cbChangeD > 0)
  5308. {
  5309. DebugTrace(0, Dbg, (
  5310. "SetSize(%x) dictionary grow\n", cbstm + cbChangeD));
  5311. if (cbstm + cbChangeD > CBMAXPROPSETSTREAM)
  5312. {
  5313. StatusDiskFull(pstatus, "SetPropertyNames: 256k limit");
  5314. goto Exit;
  5315. }
  5316. _MSTM(SetSize)(cbstm + cbChangeD, TRUE, (VOID **) &_pph, pstatus);
  5317. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5318. // reload all pointers into mapped image:
  5319. pdy = (DICTIONARY *) _MapOffsetToAddress(oDictionary);
  5320. // move everything after the dictionary back by cbChangeD bytes.
  5321. PropMoveMemory(
  5322. "SetPropertyNames:TailBack",
  5323. _GetSectionHeader(),
  5324. Add2Ptr(pdy, cbDictOldD + cbChangeD),
  5325. Add2Ptr(pdy, cbDictOldD),
  5326. cbTail);
  5327. }
  5328. // -------------------------------------------------------------------
  5329. // Walk through the existing dictionary and compact unmodified entries
  5330. // toward the front. New and modified entries will be appended later.
  5331. // -------------------------------------------------------------------
  5332. VOID *pvSrc;
  5333. VOID *pvDst;
  5334. ULONG cbCopy;
  5335. pvDst = pvSrc = pent = &pdy->rgEntry[0];
  5336. cbCopy = 0;
  5337. if (!fDeleteAll)
  5338. {
  5339. ULONG cb;
  5340. for (i = 0; i < PropByteSwap(pdy->cEntries); i++)
  5341. {
  5342. for (iprop = 0; iprop < cprop; iprop++)
  5343. {
  5344. if( apid[iprop] == PropByteSwap(ENTRY_GetPropid(pent)) )
  5345. {
  5346. break;
  5347. }
  5348. if (fDeleteByName) // if deleting any properties by name
  5349. {
  5350. ULONG cbname;
  5351. _PropertyNameLength(appvNames[iprop], &cbname);
  5352. if (cbname == CCh2CB( PropByteSwap( ENTRY_GetCch(pent) ))
  5353. &&
  5354. _ComparePropertyNames(
  5355. appvNames[iprop],
  5356. ENTRY_GetSz(pent),
  5357. FALSE, // appvNames & pent->sz may be dif endians
  5358. cbname)
  5359. )
  5360. {
  5361. break; // found an entry to be removed.
  5362. }
  5363. }
  5364. } // for (iprop = 0; iprop < cprop; iprop++)
  5365. cb = _DictionaryEntryLength( pent );
  5366. pent = _NextDictionaryEntry( pent );
  5367. if (iprop == cprop) // keep the dictionary entry
  5368. {
  5369. cbCopy += cb;
  5370. }
  5371. else // remove the dictionary entry
  5372. {
  5373. if (cbCopy != 0)
  5374. {
  5375. if (pvSrc != pvDst)
  5376. {
  5377. PropMoveMemory(
  5378. "SetPropertyNames:Compact",
  5379. _GetSectionHeader(),
  5380. pvDst,
  5381. pvSrc,
  5382. cbCopy);
  5383. }
  5384. pvDst = Add2Ptr(pvDst, cbCopy);
  5385. cbCopy = 0;
  5386. }
  5387. pvSrc = pent;
  5388. }
  5389. } // for (i = 0; i < PropByteSwap(pdy->cEntries); i++)
  5390. // Compact last chunk and point past compacted entries.
  5391. if (cbCopy != 0 && pvSrc != pvDst)
  5392. {
  5393. PropMoveMemory(
  5394. "SetPropertyNames:CompactLast",
  5395. _GetSectionHeader(),
  5396. pvDst,
  5397. pvSrc,
  5398. cbCopy);
  5399. }
  5400. pent = (ENTRY UNALIGNED *) Add2Ptr(pvDst, cbCopy);
  5401. } // if (!fDeleteAll)
  5402. pdy->cEntries = PropByteSwap( PropByteSwap(pdy->cEntries) - cDel );
  5403. // ------------------------------------
  5404. // Append new and modified entries now.
  5405. // ------------------------------------
  5406. if (appvNames != NULL)
  5407. {
  5408. // Add each name to the property set.
  5409. for (iprop = 0; iprop < cprop; iprop++)
  5410. {
  5411. // See if this propid appears later in the array.
  5412. i = cprop;
  5413. if (fDupPropid)
  5414. {
  5415. for (i = iprop + 1; i < cprop; i++)
  5416. {
  5417. if (apid[i] == apid[iprop])
  5418. {
  5419. break;
  5420. }
  5421. }
  5422. }
  5423. // See if this name appears later in the array.
  5424. if (i == cprop && fDupName)
  5425. {
  5426. ULONG cbname;
  5427. _PropertyNameLength(appvNames[iprop], &cbname);
  5428. for (i = iprop + 1; i < cprop; i++)
  5429. {
  5430. ULONG cbname2;
  5431. _PropertyNameLength(appvNames[i], &cbname2);
  5432. if (cbname == cbname2 &&
  5433. _ComparePropertyNames(
  5434. appvNames[iprop],
  5435. appvNames[i],
  5436. TRUE, // Both names are the same endian
  5437. cbname))
  5438. {
  5439. break;
  5440. }
  5441. }
  5442. }
  5443. // If this propid appears only once or if it's the last instance,
  5444. // append the mapping entry.
  5445. if (i == cprop)
  5446. {
  5447. ULONG cbname;
  5448. // Set the PID & character-count fields for this entry.
  5449. _PropertyNameLength(appvNames[iprop], &cbname);
  5450. ENTRY_SetPropid(pent, PropByteSwap( apid[iprop] ) );
  5451. ENTRY_SetCch(pent, PropByteSwap( CB2CCh( cbname ) ) );
  5452. // Copy the name into the dictionary.
  5453. RtlCopyMemory(ENTRY_GetSz(pent), appvNames[iprop], cbname);
  5454. // If this is a Unicode property set, we need to correct
  5455. // the byte-order.
  5456. if( CP_WINUNICODE == _CodePage )
  5457. {
  5458. PBSBuffer( ENTRY_GetSz(pent), cbname, sizeof(WCHAR) );
  5459. }
  5460. // Zero-out the pad bytes.
  5461. RtlZeroMemory(
  5462. Add2Ptr(pent->sz, cbname),
  5463. DwordRemain((ULONG) pent->sz + cbname));
  5464. pent = _NextDictionaryEntry( pent );
  5465. }
  5466. } // for (iprop = 0; iprop < cprop; iprop++)
  5467. // We've added all the names, now let's update the entry count.
  5468. pdy->cEntries = PropByteSwap( PropByteSwap(pdy->cEntries) + cAdd );
  5469. } // if (appvNames != NULL)
  5470. // Zero the possible partial DWORD at the end of the dictionary.
  5471. {
  5472. ULONG cb = (ULONG) ((BYTE *) pent - (BYTE *) pdy);
  5473. PROPASSERT(DwordAlign(cb) == cbDictOldD + cbChangeD);
  5474. RtlZeroMemory(pent, DwordRemain(cb));
  5475. }
  5476. // -----------------------------------------------------
  5477. // Adjust the remaining property offsets in the section.
  5478. // -----------------------------------------------------
  5479. PROPERTYIDOFFSET *ppo, *ppoMax;
  5480. PROPERTYSECTIONHEADER *psh;
  5481. psh = _LoadPropertyOffsetPointers(&ppo, &ppoMax, pstatus);
  5482. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5483. PROPASSERT(psh != NULL);
  5484. // Don't rely on the dictionary being the first property.
  5485. // Skip PID_DICTIONARY and adjust every other higher entry.
  5486. for ( ; ppo < ppoMax; ppo++)
  5487. {
  5488. if (ppo->dwOffset > oDictionary)
  5489. {
  5490. ppo->dwOffset += cbChangeD;
  5491. PROPASSERT(ppo->propid != PID_DICTIONARY);
  5492. }
  5493. }
  5494. // Update the size of the section
  5495. psh->cbSection += cbChangeD;
  5496. if (cbChangeD < 0)
  5497. {
  5498. // move everything after the dictionary forward by cbChangeD bytes.
  5499. PropMoveMemory(
  5500. "SetPropertyNames:TailUp",
  5501. _GetSectionHeader(),
  5502. Add2Ptr(pdy, cbDictOldD + cbChangeD),
  5503. Add2Ptr(pdy, cbDictOldD),
  5504. cbTail);
  5505. }
  5506. if (_cbTail != 0)
  5507. {
  5508. _PatchSectionOffsets(cbChangeD);
  5509. }
  5510. // If we need to shrink the stream or if we are cleaning up after a
  5511. // previous shrink that failed, do it last.
  5512. cbNewSize = _MSTM(GetSize)(pstatus);
  5513. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5514. if ( cbNewSize != cbstm + cbChangeD)
  5515. {
  5516. DebugTrace(0, Dbg, (
  5517. "SetSize(%x) dictionary shrink\n",
  5518. cbstm + cbChangeD));
  5519. _MSTM(SetSize)(cbstm + cbChangeD, TRUE, (VOID **) &_pph, pstatus);
  5520. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5521. }
  5522. // ----
  5523. // Exit
  5524. // ----
  5525. Exit:
  5526. // If we had to convert the array of names into a different
  5527. // codepage, delete those temporary buffers now.
  5528. if (appvNames != NULL && appvNames != (VOID **) aposz)
  5529. {
  5530. for (iprop = 0; iprop < cprop; iprop++)
  5531. {
  5532. _pma->Free( appvNames[iprop] );
  5533. }
  5534. delete [] (char **) appvNames;
  5535. }
  5536. DebugTrace(0, Dbg, ("SetPropertyNames() ==> s=%x\n", STATUS_SUCCESS));
  5537. return;
  5538. }
  5539. //+--------------------------------------------------------------------------
  5540. // Member: CPropertySetStream::_ValidateStructure
  5541. //
  5542. // Synopsis: validate property set structure
  5543. //
  5544. // Arguments: [pstatus] -- pointer to NTSTATUS code
  5545. //
  5546. // Returns: None
  5547. //+--------------------------------------------------------------------------
  5548. #if DBGPROP
  5549. VOID
  5550. CPropertySetStream::_ValidateStructure(OUT NTSTATUS *pstatus)
  5551. {
  5552. PROPID propid;
  5553. ULONG cb;
  5554. OLECHAR aocName[ CCH_MAXPROPNAMESZ];
  5555. ULONG cbName;
  5556. *pstatus = STATUS_SUCCESS;
  5557. // Walk through properties to make sure all properties are consistent
  5558. // and are contained within the section size. A NULL return value
  5559. // means _LoadProperty walked the entire section, so we can quit then.
  5560. for (propid = PID_CODEPAGE; propid != PID_ILLEGAL; propid++)
  5561. {
  5562. SERIALIZEDPROPERTYVALUE const *pprop;
  5563. pprop = GetValue(propid, &cb, pstatus);
  5564. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5565. if (NULL == pprop)
  5566. {
  5567. break;
  5568. }
  5569. }
  5570. // Walk through dictionary entries to make sure all entries are consistent
  5571. // and are contained within the dictionary size. A FALSE return value
  5572. // means QueryPropertyNameBuf walked the entire dictionary, so quit then.
  5573. for (propid = PID_CODEPAGE + 1; propid != PID_ILLEGAL; propid++)
  5574. {
  5575. BOOL fExists;
  5576. cb = 0;
  5577. cbName = sizeof(aocName);
  5578. fExists = QueryPropertyNameBuf(propid, aocName, &cbName, pstatus);
  5579. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5580. if( !fExists )
  5581. {
  5582. break;
  5583. }
  5584. }
  5585. if (_cSection > 1)
  5586. {
  5587. FORMATIDOFFSET const *pfo;
  5588. if (_cSection != 2)
  5589. {
  5590. DebugTrace(0, DEBTRACE_ERROR, (
  5591. "_ValidateStructure: csection(%x) != 2",
  5592. _cSection));
  5593. StatusCorruption(pstatus, "_ValidateStructure: csection != 2");
  5594. goto Exit;
  5595. }
  5596. pfo = _GetFormatidOffset(0);
  5597. if (pfo->fmtid != guidDocumentSummary)
  5598. {
  5599. DebugTrace(0, DEBTRACE_ERROR, (
  5600. "_ValidateStructure: DocumentSummary[0] fmtid"));
  5601. StatusCorruption(pstatus, "_ValidateStructure: DocumentSummary[0] fmtid");
  5602. goto Exit;
  5603. }
  5604. if (!IsDwordAligned(pfo->dwOffset))
  5605. {
  5606. DebugTrace(0, DEBTRACE_ERROR, (
  5607. "_ValidateStructure: dwOffset[0] = %x",
  5608. pfo->dwOffset));
  5609. StatusCorruption(pstatus, "_ValidateStructure: dwOffset[0]");
  5610. goto Exit;
  5611. }
  5612. pfo = _GetFormatidOffset(1);
  5613. if (pfo->fmtid != guidDocumentSummarySection2)
  5614. {
  5615. DebugTrace(0, DEBTRACE_ERROR, (
  5616. "_ValidateStructure: DocumentSummary[1] fmtid"));
  5617. StatusCorruption(pstatus, "_ValidateStructure: DocumentSummary[1] fmtid");
  5618. goto Exit;
  5619. }
  5620. if (!IsDwordAligned(pfo->dwOffset))
  5621. {
  5622. DebugTrace(0, DEBTRACE_ERROR, (
  5623. "_ValidateStructure: dwOffset[1] = %x",
  5624. pfo->dwOffset));
  5625. StatusCorruption(pstatus, "_ValidateStructure: dwOffset[1]");
  5626. goto Exit;
  5627. }
  5628. } // if (_cSection > 1)
  5629. // ----
  5630. // Exit
  5631. // ----
  5632. Exit:
  5633. return;
  5634. }
  5635. #endif
  5636. //+--------------------------------------------------------------------------
  5637. // Member: fnPropidCompare
  5638. //
  5639. // Synopsis: qsort helper to compare propids in a PROPERTYIDOFFSET array.
  5640. //
  5641. // Arguments: [ppo1] -- pointer to PROPERTYIDOFFSET 1
  5642. // [ppo2] -- pointer to PROPERTYIDOFFSET 2
  5643. //
  5644. // Returns: difference
  5645. //+--------------------------------------------------------------------------
  5646. #if DBGPROP
  5647. INT __cdecl
  5648. fnPropidCompare(VOID const *ppo1, VOID const *ppo2)
  5649. {
  5650. return(((PROPERTYIDOFFSET const *) ppo1)->propid -
  5651. ((PROPERTYIDOFFSET const *) ppo2)->propid);
  5652. }
  5653. #endif
  5654. //+--------------------------------------------------------------------------
  5655. // Member: fnOffsetCompare
  5656. //
  5657. // Synopsis: qsort helper to compare offsets in a PROPERTYIDOFFSET array.
  5658. //
  5659. // Arguments: [ppo1] -- pointer to PROPERTYIDOFFSET 1
  5660. // [ppo2] -- pointer to PROPERTYIDOFFSET 2
  5661. //
  5662. // Returns: difference
  5663. //+--------------------------------------------------------------------------
  5664. INT __cdecl
  5665. fnOffsetCompare(VOID const *ppo1, VOID const *ppo2)
  5666. {
  5667. return(((PROPERTYIDOFFSET const *) ppo1)->dwOffset -
  5668. ((PROPERTYIDOFFSET const *) ppo2)->dwOffset);
  5669. }
  5670. //+--------------------------------------------------------------------------
  5671. // Member: GetStringLength
  5672. //
  5673. // Synopsis: return length of possibly unicode string.
  5674. //
  5675. // Arguments: [CodePage] -- TRUE if string is Unicode
  5676. // [pwsz] -- pointer to string
  5677. // [cb] -- MAXULONG or string length with L'\0' or '\0'
  5678. //
  5679. // Returns: length of string in bytes including trailing L'\0' or '\0'
  5680. //+--------------------------------------------------------------------------
  5681. ULONG
  5682. GetStringLength(
  5683. IN USHORT CodePage,
  5684. IN WCHAR const *pwsz,
  5685. IN ULONG cb)
  5686. {
  5687. ULONG i;
  5688. if (CodePage == CP_WINUNICODE)
  5689. {
  5690. for (i = 0; i < cb/sizeof(WCHAR); i++)
  5691. {
  5692. if (pwsz[i] == L'\0')
  5693. {
  5694. break;
  5695. }
  5696. }
  5697. PROPASSERT(cb == MAXULONG || cb == (i + 1) * sizeof(WCHAR));
  5698. return((i + 1) * sizeof(WCHAR));
  5699. }
  5700. else
  5701. {
  5702. char *psz = (char *) pwsz;
  5703. for (i = 0; i < cb; i++)
  5704. {
  5705. if (psz[i] == '\0')
  5706. {
  5707. break;
  5708. }
  5709. }
  5710. PROPASSERT(cb == MAXULONG || cb == (i + 1) * sizeof(char));
  5711. return((i + 1) * sizeof(char));
  5712. }
  5713. }
  5714. //+--------------------------------------------------------------------------
  5715. // Member: CPropertySetStream::_ValidateProperties
  5716. //
  5717. // Synopsis: validate properties
  5718. //
  5719. // Arguments: [pstatus] -- pointer to NTSTATUS code
  5720. //
  5721. // Returns: None
  5722. //+--------------------------------------------------------------------------
  5723. #if DBGPROP
  5724. VOID
  5725. CPropertySetStream::_ValidateProperties(OUT NTSTATUS *pstatus) const
  5726. {
  5727. PROPERTYIDOFFSET *apo = NULL;
  5728. PROPERTYSECTIONHEADER const *psh = _GetSectionHeader();
  5729. static ULONG cValidate = 0;
  5730. ULONG cbwasted = 0;
  5731. ULONG cbtotal = 0;
  5732. *pstatus = STATUS_SUCCESS;
  5733. cValidate++;
  5734. DebugTrace(0, DEBTRACE_PROPVALIDATE, (
  5735. "_ValidateProperties(%x ppsstm=%x state=%x pph=%x)\n",
  5736. cValidate,
  5737. this,
  5738. _State,
  5739. _pph));
  5740. if (psh->cProperties != 0)
  5741. {
  5742. PROPERTYIDOFFSET *ppo, *ppoMax;
  5743. apo = newk(mtPropSetStream, NULL) PROPERTYIDOFFSET[psh->cProperties + 1];
  5744. if (apo == NULL)
  5745. {
  5746. *pstatus = STATUS_NO_MEMORY;
  5747. goto Exit;
  5748. }
  5749. RtlCopyMemory(
  5750. apo,
  5751. psh->rgprop,
  5752. psh->cProperties * CB_PROPERTYIDOFFSET);
  5753. ppoMax = apo + psh->cProperties;
  5754. ppoMax->propid = PID_ILLEGAL;
  5755. ppoMax->dwOffset = psh->cbSection;
  5756. // Sort by property id and check for duplicate propids:
  5757. qsort(apo, psh->cProperties, sizeof(apo[0]), fnPropidCompare);
  5758. for (ppo = apo; ppo < ppoMax; ppo++)
  5759. {
  5760. if (ppo->propid == PID_ILLEGAL ||
  5761. ppo->propid == ppo[1].propid)
  5762. {
  5763. DebugTrace(0, DEBTRACE_ERROR, (
  5764. "_ValidateProperties(bad propid=%x @%x)\n",
  5765. ppo->propid,
  5766. ppo->dwOffset));
  5767. StatusCorruption(pstatus, "_ValidateProperties: bad or dup propid");
  5768. goto Exit;
  5769. }
  5770. }
  5771. // Sort by offset and check for overlapping values:
  5772. qsort(apo, psh->cProperties, sizeof(apo[0]), fnOffsetCompare);
  5773. cbtotal = _oSection;
  5774. for (ppo = apo; ppo < ppoMax; ppo++)
  5775. {
  5776. ULONG cbdiff, cbprop, cbpropraw;
  5777. SERIALIZEDPROPERTYVALUE const *pprop;
  5778. cbprop = MAXULONG;
  5779. cbpropraw = cbprop;
  5780. cbdiff = ppo[1].dwOffset - ppo->dwOffset;
  5781. if (IsDwordAligned(ppo->dwOffset) &&
  5782. IsDwordAligned(ppo[1].dwOffset))
  5783. {
  5784. pprop = (SERIALIZEDPROPERTYVALUE const *)
  5785. _MapOffsetToAddress(ppo->dwOffset);
  5786. if (ppo->propid == PID_DICTIONARY)
  5787. {
  5788. cbprop = _DictionaryLength(
  5789. (DICTIONARY const *) pprop,
  5790. cbdiff,
  5791. pstatus);
  5792. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5793. cbpropraw = cbprop;
  5794. cbprop = DwordAlign(cbprop);
  5795. }
  5796. else
  5797. {
  5798. cbprop = PropertyLength(pprop, cbdiff, 0, pstatus);
  5799. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5800. cbpropraw = cbprop;
  5801. }
  5802. DebugTrace(0, DEBTRACE_PROPVALIDATE, (
  5803. "_ValidateProperties(%x) i=%x cb=%x/%x/%x @%x/%x pid=%x\n",
  5804. cValidate,
  5805. ppo - apo,
  5806. cbprop,
  5807. cbdiff,
  5808. ppo->dwOffset,
  5809. pprop,
  5810. ppo->propid));
  5811. cbtotal += cbdiff;
  5812. // Technically, the OLE spec allows extra unused space
  5813. // between properties, but this implementation never
  5814. // writes out streams with space between properties.
  5815. if (cbdiff == cbprop)
  5816. {
  5817. continue;
  5818. }
  5819. }
  5820. DebugTrace(0, DEBTRACE_ERROR, (
  5821. "_ValidateProperties(bad value length: propid=%x @%x/%x cb=%x/%x/%x ppsstm=%x)\n",
  5822. ppo->propid,
  5823. ppo->dwOffset,
  5824. pprop,
  5825. cbpropraw,
  5826. cbprop,
  5827. cbdiff,
  5828. this));
  5829. StatusCorruption(pstatus, "_ValidateProperties: bad property length");
  5830. goto Exit;
  5831. } // for (ppo = apo; ppo < ppoMax; ppo++)
  5832. } // if (psh->cProperties != 0)
  5833. // ----
  5834. // Exit
  5835. // ----
  5836. Exit:
  5837. delete [] apo;
  5838. DebugTrace(0, cbwasted != 0? 0 : Dbg, (
  5839. "_ValidateProperties(wasted %x bytes, total=%x)\n",
  5840. cbwasted,
  5841. cbtotal));
  5842. }
  5843. #endif
  5844. #if DBGPROP
  5845. typedef struct tagENTRYVALIDATE // ev
  5846. {
  5847. ENTRY UNALIGNED const *pent;
  5848. CPropertySetStream const *ppsstm;
  5849. } ENTRYVALIDATE;
  5850. #endif
  5851. //+--------------------------------------------------------------------------
  5852. // Member: fnEntryPropidCompare
  5853. //
  5854. // Synopsis: qsort helper to compare propids in a ENTRYVALIDATE array.
  5855. //
  5856. // Arguments: [pev1] -- pointer to ENTRYVALIDATE 1
  5857. // [pev2] -- pointer to ENTRYVALIDATE 2
  5858. //
  5859. // Returns: difference
  5860. //+--------------------------------------------------------------------------
  5861. #if DBGPROP
  5862. INT __cdecl
  5863. fnEntryPropidCompare(VOID const *pev1, VOID const *pev2)
  5864. {
  5865. return( ENTRY_GetPropid( ((ENTRYVALIDATE const *) pev1)->pent) -
  5866. ENTRY_GetPropid( ((ENTRYVALIDATE const *) pev2)->pent) );
  5867. }
  5868. #endif
  5869. //+--------------------------------------------------------------------------
  5870. // Member: fnEntryNameCompare
  5871. //
  5872. // Synopsis: qsort helper to compare names in a ENTRYVALIDATE array.
  5873. //
  5874. // Arguments: [pev1] -- pointer to ENTRYVALIDATE 1
  5875. // [pev2] -- pointer to ENTRYVALIDATE 2
  5876. //
  5877. // Returns: difference
  5878. //+--------------------------------------------------------------------------
  5879. #if DBGPROP
  5880. INT __cdecl
  5881. fnEntryNameCompare(VOID const *pev1, VOID const *pev2)
  5882. {
  5883. ENTRY UNALIGNED const *pent1;
  5884. ENTRY UNALIGNED const *pent2;
  5885. INT rc;
  5886. pent1 = ((ENTRYVALIDATE const *) pev1)->pent;
  5887. pent2 = ((ENTRYVALIDATE const *) pev2)->pent;
  5888. rc = PropByteSwap(ENTRY_GetCch(pent1)) - PropByteSwap(ENTRY_GetCch(pent2));
  5889. if (rc == 0)
  5890. {
  5891. rc = !((ENTRYVALIDATE const *) pev1)->ppsstm->_ComparePropertyNames(
  5892. ENTRY_GetSz(pent1),
  5893. ENTRY_GetSz(pent2),
  5894. TRUE, // Both names have the same byte-order
  5895. ( (ENTRYVALIDATE const *)
  5896. pev1
  5897. )->ppsstm->CCh2CB(PropByteSwap( ENTRY_GetCch(pent1))));
  5898. }
  5899. return(rc);
  5900. }
  5901. #endif
  5902. //+--------------------------------------------------------------------------
  5903. // Member: CPropertySetStream::_ValidateDictionary
  5904. //
  5905. // Synopsis: validate property set dictionary
  5906. //
  5907. // Arguments: [pstatus] -- pointer to NTSTATUS code
  5908. //
  5909. // Returns: None
  5910. //+--------------------------------------------------------------------------
  5911. #if DBGPROP
  5912. VOID
  5913. CPropertySetStream::_ValidateDictionary(OUT NTSTATUS *pstatus)
  5914. {
  5915. DICTIONARY const *pdy;
  5916. ULONG cbDict; // BYTE granular size!
  5917. ENTRYVALIDATE *aev = NULL;
  5918. ENTRYVALIDATE *pev, *pevMax;
  5919. PROPERTYSECTIONHEADER const *psh;
  5920. ENTRY UNALIGNED const *pent;
  5921. ENTRY entMax;
  5922. VOID const *pvDictEnd;
  5923. *pstatus = STATUS_SUCCESS;
  5924. pdy = (DICTIONARY const *) _LoadProperty(PID_DICTIONARY, &cbDict, pstatus);
  5925. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  5926. if (pdy != NULL && PropByteSwap(pdy->cEntries) != 0)
  5927. {
  5928. aev = newk (mtPropSetStream, NULL)
  5929. ENTRYVALIDATE[ PropByteSwap(pdy->cEntries) + 1 ];
  5930. if (aev == NULL)
  5931. {
  5932. *pstatus = STATUS_NO_MEMORY;
  5933. goto Exit;
  5934. }
  5935. psh = _GetSectionHeader();
  5936. pent = pdy->rgEntry;
  5937. pvDictEnd = Add2ConstPtr(pdy, cbDict);
  5938. pevMax = aev + PropByteSwap( pdy->cEntries );
  5939. for (pev = aev; pev < pevMax; pev++)
  5940. {
  5941. ULONG cb = _DictionaryEntryLength( pent );
  5942. if (Add2ConstPtr(pent, cb) > pvDictEnd)
  5943. {
  5944. DebugTrace(0, DEBTRACE_ERROR, (
  5945. "_ValidateDictionary(bad entry size for propid=%x)\n",
  5946. PropByteSwap( ENTRY_GetPropid(pev->pent) )));
  5947. StatusCorruption(pstatus, "ValidateDictionary: entry size");
  5948. goto Exit;
  5949. }
  5950. pev->pent = pent;
  5951. pev->ppsstm = this;
  5952. #if DBGPROP
  5953. #if LITTLEENDIAN
  5954. if (_CodePage == CP_WINUNICODE)
  5955. {
  5956. PROPASSERT(IsUnicodeString((WCHAR const *) ENTRY_GetSz(pent),
  5957. CCh2CB(PropByteSwap(ENTRY_GetCch(pent) ))));
  5958. }
  5959. else
  5960. {
  5961. PROPASSERT(IsAnsiString((char const *) ENTRY_GetSz(pent),
  5962. CCh2CB( PropByteSwap(ENTRY_GetCch(pent) ))));
  5963. }
  5964. #endif // LITTLEENDIAN
  5965. #endif // DBGPROP
  5966. pent = _NextDictionaryEntry( pent );
  5967. }
  5968. if ((VOID const *) pent != pvDictEnd)
  5969. {
  5970. StatusCorruption(pstatus, "ValidateDictionary: end offset");
  5971. goto Exit;
  5972. }
  5973. entMax.cch = 0;
  5974. entMax.propid = PID_ILLEGAL;
  5975. pevMax->pent = &entMax;
  5976. pevMax->ppsstm = this;
  5977. // Sort by property id and check for duplicate propids:
  5978. qsort(aev, PropByteSwap(pdy->cEntries), sizeof(aev[0]), fnEntryPropidCompare);
  5979. for (pev = aev; pev < pevMax; pev++)
  5980. {
  5981. if (PID_ILLEGAL == PropByteSwap( ENTRY_GetPropid(pev->pent))
  5982. ||
  5983. PropByteSwap( ENTRY_GetPropid(pev[1].pent) )
  5984. == PropByteSwap( ENTRY_GetPropid(pev->pent) ))
  5985. {
  5986. DebugTrace(0, DEBTRACE_ERROR, (
  5987. "_ValidateDictionary(bad propid=%x)\n",
  5988. PropByteSwap( ENTRY_GetPropid(pev->pent) )));
  5989. StatusCorruption(pstatus, "_ValidateDictionary: bad or dup propid");
  5990. goto Exit;
  5991. }
  5992. }
  5993. // Sort by property name and check for duplicate names:
  5994. qsort(aev, PropByteSwap(pdy->cEntries), sizeof(aev[0]), fnEntryNameCompare);
  5995. for (pev = aev; pev < pevMax; pev++)
  5996. {
  5997. if ( ENTRY_GetCch(pev->pent) == 0
  5998. ||
  5999. ( ENTRY_GetCch(pev->pent) == ENTRY_GetCch(pev[1].pent)
  6000. &&
  6001. _ComparePropertyNames(
  6002. ENTRY_GetSz(pev->pent),
  6003. ENTRY_GetSz(pev[1].pent),
  6004. TRUE, // Names are the same byte-order
  6005. CCh2CB(PropByteSwap(ENTRY_GetCch(pev->pent))))
  6006. )
  6007. )
  6008. {
  6009. DebugTrace(0, DEBTRACE_ERROR, (
  6010. "_ValidateDictionary(bad name for propid=%x)\n",
  6011. PropByteSwap( ENTRY_GetPropid(pev->pent) )));
  6012. StatusCorruption(pstatus, "_ValidateDictionary: bad or dup name");
  6013. goto Exit;
  6014. }
  6015. } // for (pev = aev; pev < pevMax; pev++)
  6016. } // if (pdy != NULL && pdy->cEntries != 0)
  6017. // ----
  6018. // Exit
  6019. // ----
  6020. Exit:
  6021. delete [] aev;
  6022. }
  6023. #endif // DBGPROP
  6024. //+--------------------------------------------------------------------------
  6025. // Member: CPropertySetStream::Validate
  6026. //
  6027. // Synopsis: validate entire property stream
  6028. //
  6029. // Arguments: [pstatus] -- pointer to NTSTATUS code
  6030. //
  6031. // Returns: None
  6032. //+--------------------------------------------------------------------------
  6033. #if DBGPROP
  6034. BOOLEAN fValidatePropSets = KERNELSELECT(DBG, TRUE);
  6035. VOID
  6036. CPropertySetStream::Validate(OUT NTSTATUS *pstatus)
  6037. {
  6038. if (fValidatePropSets && (_State & CPSS_USERDEFINEDDELETED) == 0)
  6039. {
  6040. ULONG cbstm = _MSTM(GetSize)(pstatus);
  6041. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  6042. // Walk through section headers to make sure all sections are contained
  6043. // within the stream size.
  6044. if (_ComputeMinimumSize(cbstm, pstatus) != 0)
  6045. {
  6046. // If an error had occurred in the above call,
  6047. // it would have returned zero.
  6048. _ValidateStructure( pstatus );
  6049. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  6050. _ValidateProperties( pstatus );
  6051. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  6052. _ValidateDictionary( pstatus );
  6053. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  6054. _ComputeMinimumSize(cbstm, pstatus);
  6055. if( !NT_SUCCESS(*pstatus) ) goto Exit;
  6056. }
  6057. } // if (fValidatePropSets && (_State & CPSS_USERDEFINEDDELETED) == 0)
  6058. // ----
  6059. // Exit
  6060. // ----
  6061. Exit:
  6062. return;
  6063. }
  6064. #endif