Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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