Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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