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.

2893 lines
73 KiB

  1. /*
  2. * D A V M B . C P P
  3. *
  4. * DAV metabase
  5. *
  6. * Copyright 1986-1998 Microsoft Corporation, All Rights Reserved
  7. */
  8. #include <_davprs.h>
  9. #include <content.h> // IContentTypeMap
  10. #include <custerr.h> // ICustomErrorMap
  11. #include <scrptmps.h> // IScriptMap
  12. // ========================================================================
  13. //
  14. // CLASS CNotifSink
  15. //
  16. // Metabase change notification advise sink. Provides IMSAdminBaseSink
  17. // interface to the real metabase so that we can be informed of
  18. // all changes to it.
  19. //
  20. class CNotifSink : public EXO, public IMSAdminBaseSink
  21. {
  22. //
  23. // Shutdown notification event that we signal when we are
  24. // done -- i.e. when we get destroyed.
  25. //
  26. CEvent& m_evtShutdown;
  27. HRESULT STDMETHODCALLTYPE SinkNotify(
  28. /* [in] */ DWORD dwMDNumElements,
  29. /* [size_is][in] */ MD_CHANGE_OBJECT_W __RPC_FAR pcoChangeList[ ]);
  30. HRESULT STDMETHODCALLTYPE ShutdownNotify()
  31. {
  32. MBTrace ("MB: CNotifSink: shutdown\n");
  33. return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
  34. }
  35. // NOT IMPLEMENTED
  36. //
  37. CNotifSink& operator=(const CNotifSink&);
  38. CNotifSink(const CNotifSink&);
  39. public:
  40. EXO_INCLASS_DECL(CNotifSink);
  41. // CREATORS
  42. //
  43. CNotifSink(CEvent& evtShutdown) :
  44. m_evtShutdown(evtShutdown)
  45. {
  46. }
  47. ~CNotifSink()
  48. {
  49. //
  50. // We cannot process any more notifications at this point.
  51. // Signal our shutdown event.
  52. //
  53. m_evtShutdown.Set();
  54. }
  55. // Wrapper for all the work that needs to be done on the notification
  56. //
  57. static VOID OnNotify( DWORD cCO,
  58. MD_CHANGE_OBJECT_W rgCO[] );
  59. };
  60. BEGIN_INTERFACE_TABLE(CNotifSink)
  61. INTERFACE_MAP(CNotifSink, IMSAdminBaseSink)
  62. END_INTERFACE_TABLE(CNotifSink);
  63. EXO_GLOBAL_DATA_DECL(CNotifSink, EXO);
  64. // ------------------------------------------------------------------------
  65. //
  66. // HrAdviseSink()
  67. //
  68. HRESULT
  69. HrAdviseSink( IMSAdminBase& msAdminBase,
  70. IMSAdminBaseSink * pMSAdminBaseSink,
  71. DWORD * pdwCookie )
  72. {
  73. auto_ref_ptr<IConnectionPoint> pcp;
  74. auto_ref_ptr<IConnectionPointContainer> pcpc;
  75. SCODE sc = S_OK;
  76. Assert( !IsBadReadPtr(&msAdminBase, sizeof(IMSAdminBase)) );
  77. Assert( !IsBadWritePtr(pMSAdminBaseSink, sizeof(IMSAdminBaseSink)) );
  78. // First see if a connection point container is supported
  79. //
  80. sc = msAdminBase.QueryInterface (IID_IConnectionPointContainer,
  81. reinterpret_cast<LPVOID *>(pcpc.load()));
  82. if (FAILED (sc))
  83. {
  84. DebugTrace( "HrAdviseSink() - QI to IConnectionPointContainer() failed 0x%08lX\n", sc );
  85. goto ret;
  86. }
  87. // Find the required connection point
  88. //
  89. sc = pcpc->FindConnectionPoint (IID_IMSAdminBaseSink, pcp.load());
  90. if (FAILED (sc))
  91. {
  92. DebugTrace( "HrAdviseSink() - FindConnectionPoint() failed 0x%08lX\n", sc );
  93. goto ret;
  94. }
  95. // Advise on the sink
  96. //
  97. sc = pcp->Advise (pMSAdminBaseSink, pdwCookie);
  98. if (FAILED (sc))
  99. {
  100. DebugTrace( "HrAdviseSink() - Advise() failed 0x%08lX\n", sc );
  101. goto ret;
  102. }
  103. ret:
  104. return sc;
  105. }
  106. // ------------------------------------------------------------------------
  107. //
  108. // UnadviseSink()
  109. //
  110. VOID
  111. UnadviseSink( IMSAdminBase& msAdminBase,
  112. DWORD dwCookie )
  113. {
  114. auto_ref_ptr<IConnectionPoint> pcp;
  115. auto_ref_ptr<IConnectionPointContainer> pcpc;
  116. SCODE sc = S_OK;
  117. Assert( !IsBadReadPtr(&msAdminBase, sizeof(IMSAdminBase)) );
  118. Assert( dwCookie );
  119. // First see if a connection point container is supported
  120. //
  121. sc = msAdminBase.QueryInterface (IID_IConnectionPointContainer,
  122. reinterpret_cast<LPVOID *>(pcpc.load()));
  123. if (FAILED (sc))
  124. {
  125. DebugTrace( "UnadviseSink() - QI to IConnectionPointContainer() failed 0x%08lX\n", sc );
  126. goto ret;
  127. }
  128. // Find the required connection point
  129. //
  130. sc = pcpc->FindConnectionPoint (IID_IMSAdminBaseSink, pcp.load());
  131. if (FAILED (sc))
  132. {
  133. DebugTrace( "UnadviseSink() - FindConnectionPoint() failed 0x%08lX\n", sc );
  134. goto ret;
  135. }
  136. // Unadvise
  137. //
  138. sc = pcp->Unadvise (dwCookie);
  139. if (FAILED (sc))
  140. {
  141. DebugTrace( "UnadviseSink() - Unadvise() failed 0x%08lX\n", sc );
  142. goto ret;
  143. }
  144. ret:
  145. return;
  146. }
  147. // ========================================================================
  148. //
  149. // CLASS CMDData
  150. //
  151. // Encapsulates access to a resource's metadata.
  152. //
  153. class CMDData :
  154. public IMDData,
  155. public CMTRefCounted
  156. {
  157. //
  158. // Object metadata
  159. //
  160. auto_ref_ptr<IContentTypeMap> m_pContentTypeMap;
  161. auto_ref_ptr<ICustomErrorMap> m_pCustomErrorMap;
  162. auto_ref_ptr<IScriptMap> m_pScriptMap;
  163. //
  164. // Buffer for the raw metadata and count of
  165. // metadata records in that buffer.
  166. //
  167. auto_heap_ptr<BYTE> m_pbData;
  168. DWORD m_dwcMDRecords;
  169. //
  170. // String metadata
  171. //
  172. LPCWSTR m_pwszDefaultDocList;
  173. LPCWSTR m_pwszVRUserName;
  174. LPCWSTR m_pwszVRPassword;
  175. LPCWSTR m_pwszExpires;
  176. LPCWSTR m_pwszBindings;
  177. LPCWSTR m_pwszVRPath;
  178. DWORD m_dwAccessPerms;
  179. DWORD m_dwDirBrowsing;
  180. BOOL m_fFrontPage;
  181. DWORD m_cbIPRestriction;
  182. BYTE* m_pbIPRestriction;
  183. BOOL m_fHasApp;
  184. DWORD m_dwAuthorization;
  185. DWORD m_dwIsIndexed;
  186. //
  187. // Metabase path from which the data for this data set was loaded.
  188. // Used in metabase notifications. See CMetabase::OnNotify() below.
  189. // The string pointed to is at the end of m_pbData.
  190. //
  191. LPCWSTR m_pwszMDPathDataSet;
  192. DWORD m_dwDataSet;
  193. // NOT IMPLEMENTED
  194. //
  195. CMDData& operator=(const CMDData&);
  196. CMDData(const CMDData&);
  197. public:
  198. // CREATORS
  199. //
  200. CMDData(LPCWSTR pwszMDPathDataSet, DWORD dwDataSet);
  201. ~CMDData();
  202. BOOL FInitialize( auto_heap_ptr<BYTE>& pbData, DWORD dwcRecords );
  203. // Implementation of IRefCounted members
  204. // Simply route them to our own CMTRefCounted members.
  205. //
  206. void AddRef()
  207. { CMTRefCounted::AddRef(); }
  208. void Release()
  209. { CMTRefCounted::Release(); }
  210. // ACCESSORS
  211. //
  212. LPCWSTR PwszMDPathDataSet() const { return m_pwszMDPathDataSet; }
  213. DWORD DwDataSet() const { return m_dwDataSet; }
  214. IContentTypeMap * GetContentTypeMap() const { return m_pContentTypeMap.get(); }
  215. const ICustomErrorMap * GetCustomErrorMap() const { return m_pCustomErrorMap.get(); }
  216. const IScriptMap * GetScriptMap() const { return m_pScriptMap.get(); }
  217. LPCWSTR PwszDefaultDocList() const { return m_pwszDefaultDocList; }
  218. LPCWSTR PwszVRUserName() const { return m_pwszVRUserName; }
  219. LPCWSTR PwszVRPassword() const { return m_pwszVRPassword; }
  220. LPCWSTR PwszExpires() const { return m_pwszExpires; }
  221. LPCWSTR PwszBindings() const { return m_pwszBindings; }
  222. LPCWSTR PwszVRPath() const { return m_pwszVRPath; }
  223. DWORD DwDirBrowsing() const { return m_dwDirBrowsing; }
  224. DWORD DwAccessPerms() const { return m_dwAccessPerms; }
  225. BOOL FAuthorViaFrontPage() const { return m_fFrontPage; }
  226. BOOL FHasIPRestriction() const { return !!m_cbIPRestriction; }
  227. BOOL FSameIPRestriction( const IMDData* pIMDD ) const
  228. {
  229. const CMDData* prhs = static_cast<const CMDData*>( pIMDD );
  230. //$ REVIEW: theoretically, there is no need for
  231. // a memcmp. However, in the rare case where
  232. // the sizes are the same and the pointers are
  233. // different, we might still try this.
  234. //
  235. // This way, if/when we go to not using the
  236. // METADATA_REFERNCE flag when getting the
  237. // data, there should be no difference.
  238. //
  239. if ( m_pbIPRestriction == prhs->m_pbIPRestriction )
  240. {
  241. Assert( m_cbIPRestriction == prhs->m_cbIPRestriction );
  242. return TRUE;
  243. }
  244. else if ( m_cbIPRestriction == prhs->m_cbIPRestriction )
  245. {
  246. if ( !memcmp (m_pbIPRestriction,
  247. prhs->m_pbIPRestriction,
  248. prhs->m_cbIPRestriction))
  249. {
  250. return TRUE;
  251. }
  252. }
  253. //
  254. //$ REVIEW: end.
  255. return FALSE;
  256. }
  257. BOOL FHasApp() const { return m_fHasApp; }
  258. DWORD DwAuthorization() const { return m_dwAuthorization; }
  259. BOOL FIsIndexed() const { return (0 != m_dwIsIndexed); }
  260. BOOL FSameStarScriptmapping( const IMDData* pIMDD ) const
  261. {
  262. return m_pScriptMap->FSameStarScriptmapping( pIMDD->GetScriptMap() );
  263. }
  264. };
  265. // ========================================================================
  266. //
  267. // STRUCT SCullInfo
  268. //
  269. // Structure used in culling cached metabase data once the cache reaches
  270. // a certain threshold size.
  271. //
  272. struct SCullInfo
  273. {
  274. //
  275. // Data set number of the entry to be considered for culling.
  276. //
  277. DWORD dwDataSet;
  278. //
  279. // Number of hits to that entry.
  280. //
  281. DWORD dwcHits;
  282. //
  283. // Comparison function used by qsort() to sort the array of
  284. // SCullInfo structures in determining which ones to cull.
  285. //
  286. static int __cdecl Compare( const SCullInfo * pCullInfo1,
  287. const SCullInfo * pCullInfo2 );
  288. };
  289. // ========================================================================
  290. //
  291. // CLASS CMetabase
  292. //
  293. // Encapsulates access to the metabase through a cache. The cache
  294. // provides O(hash) lookup and addition and keeps the cache from
  295. // growing without bound using a LFU (Least Frequently Used) culling
  296. // mechanism whenever the size of the cache exceeds a preset threshold.
  297. //
  298. class CMetabase : public Singleton<CMetabase>
  299. {
  300. //
  301. // Friend declarations required by Singleton template
  302. //
  303. friend class Singleton<CMetabase>;
  304. //
  305. // The IMSAdminBase interface to the real metabase
  306. //
  307. auto_ref_ptr<IMSAdminBase> m_pMSAdminBase;
  308. //
  309. // Cache of metadata objects keyed by data set number
  310. // and a reader/writer lock to protect it.
  311. //
  312. typedef CCache<DwordKey, auto_ref_ptr<CMDData> > CDataSetCache;
  313. CDataSetCache m_cache;
  314. CMRWLock m_mrwCache;
  315. //
  316. // Cookie for metabase advise sink registration and an event that is
  317. // signalled when the sink associated with that registration has been
  318. // shut down and is no longer processing any notifications.
  319. //
  320. DWORD m_dwSinkRegCookie;
  321. CEvent m_evtSinkShutdown;
  322. // ========================================================================
  323. //
  324. // CLASS COpGatherCullInfo
  325. //
  326. // Cache ForEach() operator class for gathering info needed in determining
  327. // which entries to cull from the cache when the cache size reaches the
  328. // culling threshold.
  329. //
  330. class COpGatherCullInfo : public CDataSetCache::IOp
  331. {
  332. //
  333. // Array of cull info
  334. //
  335. SCullInfo * m_rgci;
  336. //
  337. // Current item in the array above
  338. //
  339. int m_ici;
  340. // NOT IMPLEMENTED
  341. //
  342. COpGatherCullInfo( const COpGatherCullInfo& );
  343. COpGatherCullInfo& operator=( const COpGatherCullInfo& );
  344. public:
  345. COpGatherCullInfo( SCullInfo * rgci ) :
  346. m_rgci(rgci),
  347. m_ici(0)
  348. {
  349. Assert( m_rgci );
  350. }
  351. BOOL operator()( const DwordKey& key,
  352. const auto_ref_ptr<CMDData>& pMDData );
  353. };
  354. //
  355. // Interlockable flag to prevent simultaneous culling by multiple threads.
  356. //
  357. LONG m_lfCulling;
  358. // ========================================================================
  359. //
  360. // CLASS COpNotify
  361. //
  362. // Cache ForEach() operator class for gathering info needed in determining
  363. // which entries to blow from the cache when a notification comes in
  364. // from the metabase that the metadata for a path changed.
  365. //
  366. class COpNotify : public CDataSetCache::IOp
  367. {
  368. //
  369. // Array of data set IDs. For entries with a value other than 0,
  370. // the data set with that ID is flagged to be blown from the cache.
  371. // The array is guaranteed to be as big as there are entries in
  372. // the cache.
  373. //
  374. DWORD m_cCacheEntry;
  375. DWORD * m_rgdwDataSets;
  376. //
  377. // Flag set to TRUE if there are any data sets flagged in m_rgdwDataSets.
  378. //
  379. BOOL m_fDataSetsFlagged;
  380. //
  381. // Current item in the array above
  382. //
  383. UINT m_iCacheEntry;
  384. //
  385. // Path being notified and its length in characters
  386. // (set via the Configure() method below).
  387. //
  388. LPCWSTR m_pwszMDPathNotify;
  389. UINT m_cchMDPathNotify;
  390. // NOT IMPLEMENTED
  391. //
  392. COpNotify( const COpNotify& );
  393. COpNotify& operator=( const COpNotify& );
  394. public:
  395. // CREATORS
  396. //
  397. COpNotify( DWORD cce, DWORD * rgdwDataSets ) :
  398. m_rgdwDataSets(rgdwDataSets),
  399. m_cCacheEntry(cce),
  400. m_iCacheEntry(0),
  401. m_fDataSetsFlagged(FALSE)
  402. {
  403. }
  404. // ACCESSORS
  405. //
  406. BOOL FDataSetsFlagged() const { return m_fDataSetsFlagged; }
  407. // MANIPULATORS
  408. //
  409. BOOL operator()( const DwordKey& key,
  410. const auto_ref_ptr<CMDData>& pMDData );
  411. VOID Configure( LPCWSTR pwszMDPathNotify )
  412. {
  413. // Reset our current entry index.
  414. //
  415. m_iCacheEntry = 0;
  416. m_pwszMDPathNotify = pwszMDPathNotify;
  417. m_cchMDPathNotify = static_cast<UINT>(wcslen(pwszMDPathNotify));
  418. }
  419. };
  420. // ========================================================================
  421. //
  422. // CLASS COpMatchExactPath
  423. //
  424. // ForEachMatch() operator that fetches the cache entry whose path matches
  425. // a desired path. Used when inheritance bits are important.
  426. //
  427. class COpMatchExactPath : public CDataSetCache::IOp
  428. {
  429. // The path to match
  430. //
  431. LPCWSTR m_pwszMDPathToMatch;
  432. // The data for the matched path
  433. //
  434. auto_ref_ptr<CMDData> m_pMDDataMatched;
  435. // NOT IMPLEMENTED
  436. //
  437. COpMatchExactPath( const COpMatchExactPath& );
  438. COpMatchExactPath& operator=( const COpMatchExactPath& );
  439. public:
  440. // CREATORS
  441. //
  442. COpMatchExactPath( LPCWSTR pwszMDPath ) :
  443. m_pwszMDPathToMatch(pwszMDPath)
  444. {
  445. }
  446. // MANIPULATORS
  447. //
  448. VOID Invoke( CDataSetCache& cache,
  449. DWORD dwDataSet,
  450. auto_ref_ptr<CMDData> * ppMDData )
  451. {
  452. // Do the ForEachMatch()
  453. //
  454. (VOID) cache.ForEachMatch( dwDataSet, *this );
  455. // Returned the matched data (if any)
  456. //
  457. (*ppMDData).take_ownership(m_pMDDataMatched.relinquish());
  458. }
  459. BOOL operator()( const DwordKey&,
  460. const auto_ref_ptr<CMDData>& pMDData )
  461. {
  462. // If the data's data set number path matches the
  463. // path we are looking for, then return this data set.
  464. // If not then do nothing and keep looking.
  465. //
  466. //$OPT Can we guarantee that all MD paths are one case?
  467. //
  468. if (_wcsicmp(pMDData->PwszMDPathDataSet(), m_pwszMDPathToMatch))
  469. {
  470. return TRUE;
  471. }
  472. else
  473. {
  474. m_pMDDataMatched = pMDData;
  475. return FALSE;
  476. }
  477. }
  478. };
  479. // CREATORS
  480. //
  481. CMetabase() :
  482. m_lfCulling(FALSE),
  483. m_dwSinkRegCookie(0)
  484. {
  485. }
  486. ~CMetabase();
  487. // MANIPULATORS
  488. //
  489. VOID CullCacheEntries();
  490. HRESULT HrCacheData( const IEcb& ecb,
  491. LPCWSTR pwszMDPathAccess,
  492. LPCWSTR pwszMDPathOpen,
  493. CMDData ** ppMDData );
  494. // NOT IMPLEMENTED
  495. //
  496. CMetabase& operator=( const CMetabase& );
  497. CMetabase( const CMetabase& );
  498. public:
  499. enum
  500. {
  501. //
  502. // Number of entries allowed in cache before culling.
  503. //
  504. //$REVIEW Not based on emperical data
  505. //
  506. C_MAX_CACHE_ENTRIES = 100,
  507. //
  508. // Number of entries to cull from cache when culling.
  509. //
  510. //$REVIEW Not based on emperical data
  511. //$REVIEW Consider expressing culling as a factor (percentage)
  512. //$REVIEW rather than an absolute number.
  513. //
  514. C_CULL_CACHE_ENTRIES = 20,
  515. //
  516. // Size of the metadata for the average cache entry.
  517. // This one is based on experential data. 9K is just
  518. // enough to hold all of an object's inherited metadata.
  519. //
  520. CCH_AVG_CACHE_ENTRY = 9 * 1024
  521. };
  522. // CREATORS
  523. //
  524. // Instance creating/destroying routines provided
  525. // by the Singleton template.
  526. //
  527. using Singleton<CMetabase>::CreateInstance;
  528. using Singleton<CMetabase>::DestroyInstance;
  529. using Singleton<CMetabase>::Instance;
  530. BOOL FInitialize();
  531. VOID OnNotify( DWORD dwcChangeObjects,
  532. MD_CHANGE_OBJECT_W rgCO[] );
  533. HRESULT HrGetData( const IEcb& ecb,
  534. LPCWSTR pwszMDPathAccess,
  535. LPCWSTR pwszMDPathOpen,
  536. IMDData ** ppMDData );
  537. DWORD DwChangeNumber( const IEcb * pecb);
  538. HRESULT HrOpenObject( LPCWSTR pwszMDPath,
  539. DWORD dwAccess,
  540. DWORD dwMsecTimeout,
  541. CMDObjectHandle * pmdoh );
  542. HRESULT HrOpenLowestNodeObject( LPWSTR pwszMDPath,
  543. DWORD dwAccess,
  544. LPWSTR * ppwszMDPath,
  545. CMDObjectHandle * pmdoh );
  546. HRESULT HrIsAuthorViaFrontPageNeeded( const IEcb& ecb,
  547. LPCWSTR pwszURI,
  548. BOOL * pfFrontPageWeb );
  549. };
  550. // ========================================================================
  551. //
  552. // CLASS CMDObjectHandle
  553. //
  554. // Encapsulates access to a metabase object through an open handle,
  555. // ensuring that the handle is always propery closed.
  556. //
  557. // ------------------------------------------------------------------------
  558. //
  559. // HrOpen()
  560. //
  561. HRESULT
  562. CMDObjectHandle::HrOpen( IMSAdminBase * pMSAdminBase,
  563. LPCWSTR pwszPath,
  564. DWORD dwAccess,
  565. DWORD dwMsecTimeout )
  566. {
  567. HRESULT hr = S_OK;
  568. safe_revert sr(m_ecb.HitUser());
  569. Assert(pMSAdminBase);
  570. Assert (NULL == m_pMSAdminBase || pMSAdminBase == m_pMSAdminBase);
  571. // METADATA_MASTER_ROOT_HANDLE must be set
  572. //
  573. Assert (METADATA_MASTER_ROOT_HANDLE == m_hMDObject);
  574. // Save the pointer to the interface, so we could use
  575. // it any time later in spite of the fact if opening
  576. // of the key succeeded or not
  577. //
  578. m_pMSAdminBase = pMSAdminBase;
  579. hr = pMSAdminBase->OpenKey(METADATA_MASTER_ROOT_HANDLE,
  580. pwszPath,
  581. dwAccess,
  582. dwMsecTimeout,
  583. &m_hMDObject);
  584. if (ERROR_SUCCESS != hr)
  585. {
  586. if (!FAILED(hr))
  587. {
  588. hr = HRESULT_FROM_WIN32(hr);
  589. }
  590. MBTrace("MB: CMDObjectHandle::HrOpen() - IMSAdminBase::OpenKey() failed 0x%08lX\n", hr );
  591. }
  592. else
  593. {
  594. m_pwszPath = pwszPath;
  595. }
  596. return hr;
  597. }
  598. // ------------------------------------------------------------------------
  599. //
  600. // HrOpenLowestNode()
  601. //
  602. // Purpose:
  603. //
  604. // Opens the LOWEST possible metabase node along the given path.
  605. //
  606. // Parameters:
  607. //
  608. // pMSAdminBase [in] IMSAdminBase interface pointer.
  609. //
  610. // pwszPath [in] A full metabase path. This function opens
  611. // the lowest possible node along this path,
  612. // by working backward from the full path
  613. // up to the root of the metabase until a
  614. // path specifying an existing node is opened.
  615. //
  616. // dwAccess [in] Permissions with which we want to open the
  617. // node.
  618. //
  619. // ppwszPath [out] Points to the remainder of the initial path
  620. // relative to the opened node. This value is
  621. // NULL if the initial path was openable.
  622. //
  623. HRESULT
  624. CMDObjectHandle::HrOpenLowestNode( IMSAdminBase * pMSAdminBase,
  625. LPWSTR pwszPath,
  626. DWORD dwAccess,
  627. LPWSTR * ppwszPath)
  628. {
  629. HRESULT hr = E_FAIL;
  630. WCHAR * pwch;
  631. Assert (pMSAdminBase);
  632. Assert (pwszPath);
  633. Assert (L'/' == pwszPath[0]);
  634. Assert (ppwszPath);
  635. Assert (!IsBadWritePtr(ppwszPath, sizeof(LPWSTR)) );
  636. *ppwszPath = NULL;
  637. pwch = pwszPath + wcslen(pwszPath);
  638. while ( pwch > pwszPath )
  639. {
  640. //
  641. // Split off the path from the root at the current position
  642. //
  643. *pwch = L'\0';
  644. //
  645. // Attempt to open a node at the resulting root
  646. //
  647. hr = HrOpen(pMSAdminBase,
  648. pwszPath,
  649. dwAccess,
  650. METADATA_TIMEOUT);
  651. //
  652. // If we successfully open the node or failed for any reason
  653. // other than that the node wasn't there, we're done.
  654. //
  655. if ( SUCCEEDED(hr) ||
  656. HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) != hr )
  657. {
  658. goto ret;
  659. }
  660. //
  661. // If there was no node, then restore the slash separator
  662. // that we previously nulled out and scan backward to the
  663. // next possible split location.
  664. //
  665. if ( *ppwszPath )
  666. {
  667. *pwch = L'/';
  668. }
  669. pwch--;
  670. while (*pwch != L'/')
  671. {
  672. pwch--;
  673. }
  674. *ppwszPath = pwch + 1;
  675. }
  676. ret:
  677. return hr;
  678. }
  679. // ------------------------------------------------------------------------
  680. //
  681. // HrEnumKeys()
  682. //
  683. HRESULT
  684. CMDObjectHandle::HrEnumKeys( LPCWSTR pwszPath,
  685. LPWSTR pwszChild,
  686. DWORD dwIndex ) const
  687. {
  688. HRESULT hr = S_OK;
  689. safe_revert sr(m_ecb.HitUser());
  690. //
  691. // METADATA_MASTER_ROOT_HANDLE is valid for this operation so no assert
  692. //
  693. Assert (m_pMSAdminBase);
  694. hr = m_pMSAdminBase->EnumKeys(m_hMDObject,
  695. pwszPath,
  696. pwszChild,
  697. dwIndex);
  698. if (ERROR_SUCCESS != hr )
  699. {
  700. if (!FAILED(hr))
  701. {
  702. hr = HRESULT_FROM_WIN32(hr);
  703. }
  704. MBTrace("MB: CMDObjectHandle::HrEnumKeys() - IMSAdminBase::EnumKeys() failed 0x%08lX\n", hr );
  705. }
  706. return hr;
  707. }
  708. // ------------------------------------------------------------------------
  709. //
  710. // HrGetDataPaths()
  711. //
  712. HRESULT
  713. CMDObjectHandle::HrGetDataPaths( LPCWSTR pwszPath,
  714. DWORD dwPropID,
  715. DWORD dwDataType,
  716. LPWSTR pwszDataPaths,
  717. DWORD * pcchDataPaths ) const
  718. {
  719. HRESULT hr = S_OK;
  720. safe_revert sr(m_ecb.HitUser());
  721. //
  722. // METADATA_MASTER_ROOT_HANDLE is valid for this operation so no assert
  723. //
  724. Assert (pwszPath);
  725. Assert (!IsBadReadPtr(pcchDataPaths, sizeof(DWORD)));
  726. Assert (!IsBadWritePtr(pcchDataPaths, sizeof(DWORD)));
  727. Assert (!IsBadWritePtr(pwszDataPaths, *pcchDataPaths * sizeof(WCHAR)));
  728. Assert (m_pMSAdminBase);
  729. hr = m_pMSAdminBase->GetDataPaths(m_hMDObject,
  730. pwszPath,
  731. dwPropID,
  732. dwDataType,
  733. *pcchDataPaths,
  734. pwszDataPaths,
  735. pcchDataPaths);
  736. if (ERROR_SUCCESS != hr )
  737. {
  738. if (!FAILED(hr))
  739. {
  740. hr = HRESULT_FROM_WIN32(hr);
  741. }
  742. MBTrace("MB: CMDObjectHandle::HrGetDataPaths() - IMSAdminBase::GetDataPaths() failed 0x%08lX\n", hr );
  743. }
  744. return hr;
  745. }
  746. // ------------------------------------------------------------------------
  747. //
  748. // HrGetMetaData()
  749. //
  750. HRESULT
  751. CMDObjectHandle::HrGetMetaData( LPCWSTR pwszPath,
  752. METADATA_RECORD * pmdrec,
  753. DWORD * pcbBufRequired ) const
  754. {
  755. HRESULT hr = S_OK;
  756. safe_revert sr(m_ecb.HitUser());
  757. //
  758. // METADATA_MASTER_ROOT_HANDLE is valid for this operation so no assert
  759. //
  760. Assert (m_pMSAdminBase);
  761. hr = m_pMSAdminBase->GetData(m_hMDObject,
  762. const_cast<LPWSTR>(pwszPath),
  763. pmdrec,
  764. pcbBufRequired);
  765. if (ERROR_SUCCESS != hr )
  766. {
  767. if (!FAILED(hr))
  768. {
  769. hr = HRESULT_FROM_WIN32(hr);
  770. }
  771. MBTrace("MB: CMDObjectHandle::HrGetMetaData() - IMSAdminBase::GetData() failed 0x%08lX\n", hr );
  772. }
  773. return hr;
  774. }
  775. // ------------------------------------------------------------------------
  776. //
  777. // HrGetAllMetaData()
  778. //
  779. HRESULT
  780. CMDObjectHandle::HrGetAllMetaData( LPCWSTR pwszPath,
  781. DWORD dwAttributes,
  782. DWORD dwUserType,
  783. DWORD dwDataType,
  784. DWORD * pdwcRecords,
  785. DWORD * pdwDataSet,
  786. DWORD cbBuf,
  787. LPBYTE pbBuf,
  788. DWORD * pcbBufRequired ) const
  789. {
  790. HRESULT hr = S_OK;
  791. safe_revert sr(m_ecb.HitUser());
  792. //
  793. // METADATA_MASTER_ROOT_HANDLE is valid for this operation so no assert
  794. //
  795. Assert (m_pMSAdminBase);
  796. hr = m_pMSAdminBase->GetAllData(m_hMDObject,
  797. pwszPath,
  798. dwAttributes,
  799. dwUserType,
  800. dwDataType,
  801. pdwcRecords,
  802. pdwDataSet,
  803. cbBuf,
  804. pbBuf,
  805. pcbBufRequired);
  806. if (ERROR_SUCCESS != hr )
  807. {
  808. if (!FAILED(hr))
  809. {
  810. hr = HRESULT_FROM_WIN32(hr);
  811. }
  812. MBTrace("MB: CMDObjectHandle::HrGetAllMetaData() - IMSAdminBase::GetAllData() failed 0x%08lX\n", hr );
  813. }
  814. return hr;
  815. }
  816. // ------------------------------------------------------------------------
  817. //
  818. // HrSetMetaData()
  819. //
  820. HRESULT
  821. CMDObjectHandle::HrSetMetaData( LPCWSTR pwszPath,
  822. const METADATA_RECORD * pmdrec ) const
  823. {
  824. HRESULT hr = S_OK;
  825. safe_revert sr(m_ecb.HitUser());
  826. Assert (pmdrec);
  827. // METADATA_MASTER_ROOT_HANDLE not valid for this operation
  828. //
  829. Assert (METADATA_MASTER_ROOT_HANDLE != m_hMDObject);
  830. Assert (m_pMSAdminBase);
  831. hr = m_pMSAdminBase->SetData(m_hMDObject,
  832. pwszPath,
  833. const_cast<METADATA_RECORD *>(pmdrec));
  834. if (ERROR_SUCCESS != hr)
  835. {
  836. if (!FAILED(hr))
  837. {
  838. hr = HRESULT_FROM_WIN32(hr);
  839. }
  840. MBTrace("MB: CMDObjectHandle::HrSetMetaData() - IMSAdminBase::SetData() failed 0x%08lX\n", hr );
  841. }
  842. else
  843. {
  844. // Notify the sinks registered on this IMSAdminBase
  845. // will not get notified, so we need to do all the
  846. // invalidation ourselves. The only sink currently
  847. // being registered is CChildVRCache.
  848. //
  849. SCODE scT;
  850. MD_CHANGE_OBJECT_W mdChObjW;
  851. UINT cchBase = 0;
  852. UINT cchPath = 0;
  853. CStackBuffer<WCHAR,MAX_PATH> pwsz;
  854. UINT cch;
  855. if (m_pwszPath)
  856. {
  857. cchBase = static_cast<UINT>(wcslen(m_pwszPath));
  858. }
  859. if (pwszPath)
  860. {
  861. cchPath = static_cast<UINT>(wcslen(pwszPath));
  862. }
  863. // Allocate enough space for constructed path:
  864. // base, '/' separator,
  865. // path, '/' termination, '\0' termination...
  866. //
  867. cch = cchBase + 1 + cchPath + 2;
  868. if (!pwsz.resize(cch * sizeof(WCHAR)))
  869. return E_OUTOFMEMORY;
  870. scT = ScBuildChangeObject(m_pwszPath,
  871. cchBase,
  872. pwszPath,
  873. cchPath,
  874. MD_CHANGE_TYPE_SET_DATA,
  875. &pmdrec->dwMDIdentifier,
  876. pwsz.get(),
  877. &cch,
  878. &mdChObjW);
  879. // Function above returns S_FALSE when it is short in buffer,
  880. // otherwise it always returns S_OK. The buffer we gave is
  881. // sufficient, so assert that we succeeded
  882. //
  883. Assert( S_OK == scT );
  884. CNotifSink::OnNotify( 1, &mdChObjW );
  885. goto ret;
  886. }
  887. ret:
  888. return hr;
  889. }
  890. // ------------------------------------------------------------------------
  891. //
  892. // HrDeleteMetaData()
  893. //
  894. HRESULT
  895. CMDObjectHandle::HrDeleteMetaData( LPCWSTR pwszPath,
  896. DWORD dwPropID,
  897. DWORD dwDataType ) const
  898. {
  899. HRESULT hr = S_OK;
  900. safe_revert sr(m_ecb.HitUser());
  901. // METADATA_MASTER_ROOT_HANDLE not valid for this operation
  902. //
  903. Assert (METADATA_MASTER_ROOT_HANDLE != m_hMDObject);
  904. Assert (m_pMSAdminBase);
  905. hr = m_pMSAdminBase->DeleteData(m_hMDObject,
  906. pwszPath,
  907. dwPropID,
  908. dwDataType);
  909. if (ERROR_SUCCESS != hr)
  910. {
  911. if (!FAILED(hr))
  912. {
  913. hr = HRESULT_FROM_WIN32(hr);
  914. }
  915. MBTrace("MB: CMDObjectHandle::HrDeleteMetaData() - IMSAdminBase::DeleteData() failed 0x%08lX\n", hr );
  916. }
  917. else
  918. {
  919. // Notify the sinks registered on this IMSAdminBase
  920. // will not get notified, so we need to do all the
  921. // invalidation ourselves. The only sink currently
  922. // being registered is CChildVRCache.
  923. //
  924. SCODE scT;
  925. MD_CHANGE_OBJECT_W mdChObjW;
  926. UINT cchBase = 0;
  927. UINT cchPath = 0;
  928. CStackBuffer<WCHAR,MAX_PATH> pwsz;
  929. UINT cch;
  930. if (m_pwszPath)
  931. {
  932. cchBase = static_cast<UINT>(wcslen(m_pwszPath));
  933. }
  934. if (pwszPath)
  935. {
  936. cchPath = static_cast<UINT>(wcslen(pwszPath));
  937. }
  938. // Allocate enough space for constructed path:
  939. // base, '/' separator,
  940. // path, '/' termination, '\0' termination...
  941. //
  942. cch = cchBase + 1 + cchPath + 2;
  943. if (!pwsz.resize(cch * sizeof(WCHAR)))
  944. return E_OUTOFMEMORY;
  945. scT = ScBuildChangeObject(m_pwszPath,
  946. cchBase,
  947. pwszPath,
  948. cchPath,
  949. MD_CHANGE_TYPE_DELETE_DATA,
  950. &dwPropID,
  951. pwsz.get(),
  952. &cch,
  953. &mdChObjW);
  954. // Function above returns S_FALSE when it is short in buffer,
  955. // otherwise it always returns S_OK. The buffer we gave is
  956. // sufficient, so assert that we succeeded
  957. //
  958. Assert( S_OK == scT );
  959. CNotifSink::OnNotify( 1, &mdChObjW );
  960. goto ret;
  961. }
  962. ret:
  963. return hr;
  964. }
  965. // ------------------------------------------------------------------------
  966. //
  967. // Close()
  968. //
  969. VOID
  970. CMDObjectHandle::Close()
  971. {
  972. if ( METADATA_MASTER_ROOT_HANDLE != m_hMDObject )
  973. {
  974. Assert (m_pMSAdminBase);
  975. m_pMSAdminBase->CloseKey( m_hMDObject );
  976. m_hMDObject = METADATA_MASTER_ROOT_HANDLE;
  977. m_pwszPath = NULL;
  978. }
  979. }
  980. // ------------------------------------------------------------------------
  981. //
  982. // ~CMDObjectHandle()
  983. //
  984. CMDObjectHandle::~CMDObjectHandle()
  985. {
  986. Close();
  987. }
  988. // ------------------------------------------------------------------------
  989. //
  990. // HrReadMetaData()
  991. //
  992. // Reads in the raw metadata from the metabase.
  993. //
  994. HrReadMetaData( const IEcb& ecb,
  995. IMSAdminBase * pMSAdminBase,
  996. LPCWSTR pwszMDPathAccess,
  997. LPCWSTR pwszMDPathOpen,
  998. LPBYTE * ppbData,
  999. DWORD * pdwDataSet,
  1000. DWORD * pdwcRecords,
  1001. LPCWSTR * ppwszMDPathDataSet )
  1002. {
  1003. CMDObjectHandle mdoh(ecb);
  1004. HRESULT hr;
  1005. Assert( ppwszMDPathDataSet );
  1006. //
  1007. // We should never open the root node of the metabase.
  1008. // It's prohibitively expensive.
  1009. //
  1010. Assert( pwszMDPathOpen );
  1011. //
  1012. // If the open path is not the path we are trying to access
  1013. // then the former must be a proper prefix of the latter.
  1014. //
  1015. Assert( pwszMDPathAccess == pwszMDPathOpen ||
  1016. !_wcsnicmp(pwszMDPathOpen, pwszMDPathAccess, wcslen(pwszMDPathOpen)) );
  1017. //
  1018. // Open the specified "open" path. Note that we do not simply open
  1019. // the full path because it may not exist and we don't necessarily
  1020. // want to try opening successive "parent" paths as each attempt
  1021. // costs us a trip through a global critical section in the metabase.
  1022. //
  1023. hr = mdoh.HrOpen( pMSAdminBase,
  1024. pwszMDPathOpen,
  1025. METADATA_PERMISSION_READ,
  1026. 200 ); // timeout in msec (0.2 sec)
  1027. if ( FAILED(hr) )
  1028. {
  1029. DebugTrace( "HrReadMetaData() - Error opening vroot for read 0x%08lX\n", hr );
  1030. return hr;
  1031. }
  1032. //
  1033. // Get all of the metadata. We should go through this loop at most twice --
  1034. // if our inital guess is too small to hold all the data the first time
  1035. // through, we'll go through it again with a buffer of the adequate size.
  1036. //
  1037. // Note that we reserve space at the end of the buffer for a copy of the
  1038. // access path including a slash at the end (to make subpath detection
  1039. // easier).
  1040. //
  1041. DWORD cbBuf = CMetabase::CCH_AVG_CACHE_ENTRY * sizeof(WCHAR);
  1042. DWORD cchMDPathAccess = static_cast<DWORD>(wcslen(pwszMDPathAccess) + 1);
  1043. auto_heap_ptr<BYTE> pbBuf(static_cast<LPBYTE>(ExAlloc(cbBuf + CbSizeWsz(cchMDPathAccess))));
  1044. //
  1045. // Get all the metadata. Include inherited data (METADATA_INHERIT).
  1046. // Return whether the data for a given path was inherited (METADATA_ISINHERITED).
  1047. // If the path does not exist, return the inherited data
  1048. // anyway (METADATA_PARTIAL_PATH).
  1049. //
  1050. hr = mdoh.HrGetAllMetaData( (pwszMDPathOpen == pwszMDPathAccess) ?
  1051. NULL :
  1052. pwszMDPathAccess + wcslen(pwszMDPathOpen),
  1053. METADATA_INHERIT |
  1054. METADATA_ISINHERITED |
  1055. METADATA_PARTIAL_PATH,
  1056. ALL_METADATA,
  1057. ALL_METADATA,
  1058. pdwcRecords,
  1059. pdwDataSet,
  1060. cbBuf,
  1061. pbBuf.get(),
  1062. &cbBuf );
  1063. if ( FAILED(hr) )
  1064. {
  1065. if ( HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr )
  1066. {
  1067. DebugTrace( "HrReadMetaData() - Error getting all metadata 0x%08lX\n", hr );
  1068. return hr;
  1069. }
  1070. //
  1071. // We couldn't read all the metadata because our initial
  1072. // guess was too small so allocate a buffer that is as
  1073. // big as the metabase told us we needed and use that
  1074. // buffer the next time around.
  1075. //
  1076. pbBuf.realloc(cbBuf + CbSizeWsz(cchMDPathAccess));
  1077. hr = mdoh.HrGetAllMetaData( (pwszMDPathOpen == pwszMDPathAccess) ?
  1078. NULL :
  1079. pwszMDPathAccess + wcslen(pwszMDPathOpen),
  1080. METADATA_INHERIT |
  1081. METADATA_PARTIAL_PATH,
  1082. ALL_METADATA,
  1083. ALL_METADATA,
  1084. pdwcRecords,
  1085. pdwDataSet,
  1086. cbBuf,
  1087. pbBuf.get(),
  1088. &cbBuf );
  1089. if ( FAILED(hr) )
  1090. {
  1091. DebugTrace( "HrReadMetaData() - Error getting all metadata 0x%08lX\n", hr );
  1092. return hr;
  1093. }
  1094. }
  1095. //
  1096. // Copy the access path (including the null terminator) to the end
  1097. // of the buffer.
  1098. //
  1099. Assert( L'\0' == pwszMDPathAccess[cchMDPathAccess - 1] );
  1100. memcpy( pbBuf + cbBuf, pwszMDPathAccess, cchMDPathAccess * sizeof(WCHAR) );
  1101. //
  1102. // Tack on a final slash and null terminate.
  1103. // Note: pwszMDPathAccess may or may not already have a terminating slash
  1104. // ** depending on how this function was called **
  1105. // (specifically, deep MOVE/COPY/DELETEs will have slashes on
  1106. // sub-directory URLs)
  1107. //
  1108. LPWSTR pwszT = reinterpret_cast<LPWSTR>(pbBuf + cbBuf + (cchMDPathAccess - 2) * sizeof(WCHAR));
  1109. if ( L'/' != pwszT[0] )
  1110. {
  1111. pwszT[1] = L'/';
  1112. pwszT[2] = L'\0';
  1113. }
  1114. //
  1115. // Return the path
  1116. //
  1117. *ppwszMDPathDataSet = reinterpret_cast<LPWSTR>(pbBuf.get() + cbBuf);
  1118. //
  1119. // And the data
  1120. //
  1121. *ppbData = pbBuf.relinquish();
  1122. return S_OK;
  1123. }
  1124. // ========================================================================
  1125. //
  1126. // CLASS CMDData
  1127. //
  1128. // ------------------------------------------------------------------------
  1129. //
  1130. // CMDData::CMDData()
  1131. //
  1132. CMDData::CMDData( LPCWSTR pwszMDPathDataSet,
  1133. DWORD dwDataSet ) :
  1134. m_pwszMDPathDataSet(pwszMDPathDataSet),
  1135. m_dwDataSet(dwDataSet),
  1136. // Defaults not handled by auto_xxx classes:
  1137. m_pwszDefaultDocList(NULL), // No default doc list
  1138. m_pwszVRUserName(NULL), // No VRoot user name
  1139. m_pwszVRPassword(NULL), // No VRoot password
  1140. m_pwszExpires(NULL), // No expiration
  1141. m_pwszBindings(NULL), // No custom bindings
  1142. m_pwszVRPath(NULL), // No VRoot physical path
  1143. m_dwAccessPerms(0), // Deny all access
  1144. m_dwDirBrowsing(0), // No default dir browsing
  1145. m_fFrontPage(FALSE), // No FrontPage authoring
  1146. m_cbIPRestriction(0), // --
  1147. m_pbIPRestriction(NULL), // --
  1148. m_fHasApp(FALSE), // No registered app
  1149. m_dwAuthorization(0), // No specific authorization method
  1150. m_dwIsIndexed(1) // Indexing is on by default
  1151. {
  1152. Assert(pwszMDPathDataSet);
  1153. Assert(dwDataSet != 0);
  1154. m_cRef = 1;
  1155. }
  1156. // ------------------------------------------------------------------------
  1157. //
  1158. // CMDData::~CMDData()
  1159. //
  1160. CMDData::~CMDData()
  1161. {
  1162. }
  1163. // ------------------------------------------------------------------------
  1164. //
  1165. // CMDData::FInitialize()
  1166. //
  1167. // Populates a metadata object from metadata obtained through an accessor.
  1168. //
  1169. BOOL
  1170. CMDData::FInitialize( auto_heap_ptr<BYTE>& pbData,
  1171. DWORD dwcMDRecords )
  1172. {
  1173. Assert(!IsBadReadPtr(pbData.get(), dwcMDRecords * sizeof(METADATA_RECORD)));
  1174. for ( DWORD iRec = 0; iRec < dwcMDRecords; iRec++ )
  1175. {
  1176. //
  1177. // Locate the metadata record and its data. Note that the
  1178. // pbMDData field of METADATA_RECORD is actually an offset
  1179. // to the data -- not a pointer to it -- from the start of
  1180. // the buffer.
  1181. //
  1182. const METADATA_GETALL_RECORD& mdrec =
  1183. reinterpret_cast<const METADATA_GETALL_RECORD *>(pbData.get())[iRec];
  1184. LPVOID pvRecordData =
  1185. pbData.get() + mdrec.dwMDDataOffset;
  1186. //
  1187. // !!!IMPORTANT!!! The list of identifiers below must be kept up to date
  1188. // with the list in FHasCachedIDs().
  1189. //
  1190. switch ( mdrec.dwMDIdentifier )
  1191. {
  1192. case MD_IP_SEC:
  1193. {
  1194. Assert( mdrec.dwMDDataTag == NULL );
  1195. if ( mdrec.dwMDDataType != BINARY_METADATA )
  1196. return FALSE;
  1197. m_cbIPRestriction = mdrec.dwMDDataLen;
  1198. m_pbIPRestriction = static_cast<LPBYTE>(pvRecordData);
  1199. break;
  1200. }
  1201. case MD_ACCESS_PERM:
  1202. {
  1203. Assert( mdrec.dwMDDataTag == NULL );
  1204. if ( mdrec.dwMDDataType != DWORD_METADATA )
  1205. return FALSE;
  1206. m_dwAccessPerms = *static_cast<LPDWORD>(pvRecordData);
  1207. break;
  1208. }
  1209. case MD_IS_CONTENT_INDEXED:
  1210. {
  1211. Assert( mdrec.dwMDDataTag == NULL );
  1212. if ( mdrec.dwMDDataType != DWORD_METADATA )
  1213. return FALSE;
  1214. m_dwIsIndexed = *static_cast<LPDWORD>(pvRecordData);
  1215. break;
  1216. }
  1217. case MD_FRONTPAGE_WEB:
  1218. {
  1219. Assert( mdrec.dwMDDataTag == NULL );
  1220. if ( mdrec.dwMDDataType != DWORD_METADATA )
  1221. return FALSE;
  1222. //
  1223. // Set the frontpage flag if MD_FRONTPAGE_WEB is
  1224. // explicitly set on this metabase node and not
  1225. // inherited.
  1226. //
  1227. m_fFrontPage = *static_cast<LPDWORD>(pvRecordData) &&
  1228. !(mdrec.dwMDAttributes & METADATA_ISINHERITED);
  1229. break;
  1230. }
  1231. case MD_DIRECTORY_BROWSING:
  1232. {
  1233. Assert( mdrec.dwMDDataTag == NULL );
  1234. if ( mdrec.dwMDDataType != DWORD_METADATA )
  1235. return FALSE;
  1236. m_dwDirBrowsing = *static_cast<LPDWORD>(pvRecordData);
  1237. break;
  1238. }
  1239. case MD_AUTHORIZATION:
  1240. {
  1241. Assert( mdrec.dwMDDataTag == NULL );
  1242. if ( mdrec.dwMDDataType != DWORD_METADATA )
  1243. return FALSE;
  1244. m_dwAuthorization = *static_cast<LPDWORD>(pvRecordData);
  1245. break;
  1246. }
  1247. case MD_DEFAULT_LOAD_FILE:
  1248. {
  1249. Assert( mdrec.dwMDDataTag == NULL );
  1250. if ( mdrec.dwMDDataType != STRING_METADATA )
  1251. return FALSE;
  1252. m_pwszDefaultDocList = static_cast<LPWSTR>(pvRecordData);
  1253. break;
  1254. }
  1255. case MD_CUSTOM_ERROR:
  1256. {
  1257. Assert( mdrec.dwMDDataTag == NULL );
  1258. if ( mdrec.dwMDDataType != MULTISZ_METADATA )
  1259. return FALSE;
  1260. m_pCustomErrorMap.take_ownership(
  1261. NewCustomErrorMap(static_cast<LPWSTR>(pvRecordData)));
  1262. //
  1263. // Bail if we cannot create the map.
  1264. // This means the record data was malformed.
  1265. //
  1266. if ( !m_pCustomErrorMap.get() )
  1267. return FALSE;
  1268. break;
  1269. }
  1270. case MD_MIME_MAP:
  1271. {
  1272. Assert( mdrec.dwMDDataTag == NULL );
  1273. if ( mdrec.dwMDDataType != MULTISZ_METADATA )
  1274. return FALSE;
  1275. m_pContentTypeMap.take_ownership(
  1276. NewContentTypeMap(static_cast<LPWSTR>(pvRecordData),
  1277. !!(mdrec.dwMDAttributes & METADATA_ISINHERITED)));
  1278. //
  1279. // Bail if we cannot create the map.
  1280. // This means the record data was malformed.
  1281. //
  1282. if ( !m_pContentTypeMap.get() )
  1283. return FALSE;
  1284. break;
  1285. }
  1286. case MD_SCRIPT_MAPS:
  1287. {
  1288. Assert( mdrec.dwMDDataTag == NULL );
  1289. if ( mdrec.dwMDDataType != MULTISZ_METADATA )
  1290. return FALSE;
  1291. m_pScriptMap.take_ownership(
  1292. NewScriptMap(static_cast<LPWSTR>(pvRecordData)));
  1293. //
  1294. // Bail if we cannot create the map.
  1295. // This means the record data was malformed.
  1296. //
  1297. if ( !m_pScriptMap.get() )
  1298. return FALSE;
  1299. break;
  1300. }
  1301. case MD_APP_ISOLATED:
  1302. {
  1303. //
  1304. // If this property exists on this node at all
  1305. // (i.e. it is not inherited) then we want to
  1306. // know, regardless of its value.
  1307. //
  1308. if ( mdrec.dwMDAttributes & METADATA_ISINHERITED )
  1309. m_fHasApp = TRUE;
  1310. break;
  1311. }
  1312. case MD_VR_USERNAME:
  1313. {
  1314. if ( mdrec.dwMDDataType != STRING_METADATA )
  1315. return FALSE;
  1316. m_pwszVRUserName = static_cast<LPWSTR>(pvRecordData);
  1317. break;
  1318. }
  1319. case MD_VR_PASSWORD:
  1320. {
  1321. if ( mdrec.dwMDDataType != STRING_METADATA )
  1322. return FALSE;
  1323. m_pwszVRPassword = static_cast<LPWSTR>(pvRecordData);
  1324. break;
  1325. }
  1326. case MD_HTTP_EXPIRES:
  1327. {
  1328. Assert( mdrec.dwMDDataTag == NULL );
  1329. if ( mdrec.dwMDDataType != STRING_METADATA )
  1330. return FALSE;
  1331. m_pwszExpires = static_cast<LPWSTR>(pvRecordData);
  1332. break;
  1333. }
  1334. case MD_SERVER_BINDINGS:
  1335. {
  1336. Assert( mdrec.dwMDDataTag == NULL );
  1337. if ( mdrec.dwMDDataType != MULTISZ_METADATA )
  1338. return FALSE;
  1339. m_pwszBindings = static_cast<LPWSTR>(pvRecordData);
  1340. break;
  1341. }
  1342. case MD_VR_PATH:
  1343. {
  1344. Assert( mdrec.dwMDDataTag == NULL );
  1345. if ( mdrec.dwMDDataType != STRING_METADATA )
  1346. return FALSE;
  1347. m_pwszVRPath = static_cast<LPWSTR>(pvRecordData);
  1348. break;
  1349. }
  1350. //
  1351. //$REVIEW Do we need to worry about any of these?
  1352. //
  1353. case MD_VR_PASSTHROUGH:
  1354. case MD_SSL_ACCESS_PERM:
  1355. default:
  1356. {
  1357. break;
  1358. }
  1359. }
  1360. }
  1361. //
  1362. // If all goes well we take ownership of the data buffer passed in.
  1363. //
  1364. m_pbData = pbData.relinquish();
  1365. m_dwcMDRecords = dwcMDRecords;
  1366. return TRUE;
  1367. }
  1368. // ========================================================================
  1369. //
  1370. // CLASS CMetabase
  1371. //
  1372. // ------------------------------------------------------------------------
  1373. //
  1374. // CMetabase::~CMetabase()
  1375. //
  1376. CMetabase::~CMetabase()
  1377. {
  1378. //
  1379. // If we ever advised a notification sink then unadvise it now.
  1380. //
  1381. if ( m_dwSinkRegCookie )
  1382. {
  1383. //
  1384. // Unadvise the sink
  1385. //
  1386. UnadviseSink(*m_pMSAdminBase.get(), m_dwSinkRegCookie);
  1387. //
  1388. // Wait for the sink to shutdown
  1389. //
  1390. m_evtSinkShutdown.Wait();
  1391. }
  1392. }
  1393. // ------------------------------------------------------------------------
  1394. //
  1395. // CMetabase::FInitialize()
  1396. //
  1397. BOOL
  1398. CMetabase::FInitialize()
  1399. {
  1400. HRESULT hr = S_OK;
  1401. // Init the cache
  1402. //
  1403. if ( !m_cache.FInit() )
  1404. {
  1405. hr = E_OUTOFMEMORY;
  1406. goto ret;
  1407. }
  1408. // Init its reader/writer lock
  1409. //
  1410. if ( !m_mrwCache.FInitialize() )
  1411. {
  1412. hr = E_OUTOFMEMORY;
  1413. goto ret;
  1414. }
  1415. // Create an instance of the com-base metabase interface.
  1416. // Again, we expect that somebody above us has already done
  1417. // this, so it should be fairly cheap as well.
  1418. //
  1419. // Note that we do not init COM at any point. IIS should
  1420. // have already done that for us.
  1421. //
  1422. hr = CoCreateInstance (CLSID_MSAdminBase,
  1423. NULL,
  1424. CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
  1425. IID_IMSAdminBase,
  1426. (LPVOID *)m_pMSAdminBase.load());
  1427. if ( FAILED(hr) )
  1428. {
  1429. DebugTrace( "CMetabase::FInitialize() - CoCreateInstance(CLSID_MDCOM) failed 0x%08lX\n", hr );
  1430. goto ret;
  1431. }
  1432. // Register for metabase change notifications
  1433. //
  1434. {
  1435. auto_ref_ptr<CNotifSink> pSinkNew;
  1436. // First, set up an empty security descriptor and attributes
  1437. // so that the event can be created with no security
  1438. // (i.e. accessible from any security context).
  1439. //
  1440. SECURITY_DESCRIPTOR* psdAllAccess = PsdCreateWorld();
  1441. SECURITY_ATTRIBUTES saAllAccess;
  1442. saAllAccess.nLength = sizeof(saAllAccess);
  1443. saAllAccess.lpSecurityDescriptor = psdAllAccess;
  1444. saAllAccess.bInheritHandle = FALSE;
  1445. //
  1446. // Create the sink shutdown event
  1447. //
  1448. if ( !m_evtSinkShutdown.FCreate( &saAllAccess, // no security
  1449. TRUE, // manual access
  1450. FALSE, // initially non-signalled
  1451. NULL ))
  1452. {
  1453. hr = HRESULT_FROM_WIN32(GetLastError());
  1454. DebugTrace( "CMetabase::FInitialize() - m_evtSinkShutdown.FCreate() failed 0x%08lX\n", hr );
  1455. goto ret;
  1456. }
  1457. //
  1458. // Create the sink
  1459. //
  1460. pSinkNew.take_ownership(new CNotifSink(m_evtSinkShutdown));
  1461. //
  1462. // Advise the sink
  1463. //
  1464. hr = HrAdviseSink(*m_pMSAdminBase.get(),
  1465. pSinkNew.get(),
  1466. &m_dwSinkRegCookie);
  1467. if ( FAILED(hr) )
  1468. {
  1469. DebugTrace( "CMetabase::FInitialize() - HrAdviseSink() failed 0x%08lX\n", hr );
  1470. goto ret;
  1471. }
  1472. LocalFree(psdAllAccess);
  1473. }
  1474. ret:
  1475. return SUCCEEDED(hr);
  1476. }
  1477. // ------------------------------------------------------------------------
  1478. //
  1479. // CMetabase::DwChangeNumber()
  1480. //
  1481. DWORD
  1482. CMetabase::DwChangeNumber(const IEcb * pecb)
  1483. {
  1484. Assert(pecb);
  1485. DWORD dw = 0;
  1486. safe_revert sr(pecb->HitUser());
  1487. Assert(m_pMSAdminBase.get());
  1488. // Note: this function can fail. We are not checking the return value
  1489. // because we need to generate an etag regardless of failure.
  1490. //
  1491. (void) m_pMSAdminBase->GetSystemChangeNumber(&dw);
  1492. return dw;
  1493. }
  1494. // ------------------------------------------------------------------------
  1495. //
  1496. // CMetabase::COpGatherCullInfo::operator()
  1497. //
  1498. BOOL
  1499. CMetabase::COpGatherCullInfo::operator()( const DwordKey& key,
  1500. const auto_ref_ptr<CMDData>& pMDData )
  1501. {
  1502. //
  1503. // Gather and reset the access count of the current metadata object
  1504. //
  1505. m_rgci[m_ici].dwDataSet = key.Dw();
  1506. m_rgci[m_ici].dwcHits = pMDData->LFUData().DwGatherAndResetHitCount();
  1507. ++m_ici;
  1508. //
  1509. // ForEach() operators can cancel the iteration by returning FALSE.
  1510. // We always want to iterate over everything so return TRUE
  1511. //
  1512. return TRUE;
  1513. }
  1514. // ------------------------------------------------------------------------
  1515. //
  1516. // SCullInfo::Compare()
  1517. //
  1518. // Cull info comparison function used by qsort() to sort an array
  1519. // of SCullInfo structures.
  1520. //
  1521. int __cdecl
  1522. SCullInfo::Compare( const SCullInfo * pCullInfo1,
  1523. const SCullInfo * pCullInfo2 )
  1524. {
  1525. return static_cast<int>(pCullInfo1->dwcHits - pCullInfo2->dwcHits);
  1526. }
  1527. // ------------------------------------------------------------------------
  1528. //
  1529. // CMetabase::CullCacheEntries()
  1530. //
  1531. // Called by HrCacheData() when the number of entries in the metabase
  1532. // cache reaches a preset threshold. This function removes those entries
  1533. // that have been used least frequently since the last time the cache
  1534. // was culled.
  1535. //
  1536. VOID
  1537. CMetabase::CullCacheEntries()
  1538. {
  1539. CStackBuffer<SCullInfo,128> rgci;
  1540. int cCacheEntries;
  1541. //
  1542. // Gather cull info for all of the cache entries. We need to do
  1543. // this in a read block so that the cache stays stable (i.e. does
  1544. // not have entries added or removed) while we are in the ForEach()
  1545. // operation.
  1546. //
  1547. {
  1548. //
  1549. // Lock out writers -- anyone trying to add or remove cache entries
  1550. //
  1551. CSynchronizedReadBlock sb(m_mrwCache);
  1552. //
  1553. // Now that the count of cache entries is stable (because
  1554. // we are in the read block) check once again that we
  1555. // are over the culling threshold. If we are not (because
  1556. // enough entries were removed before we got the lock) then
  1557. // don't cull.
  1558. //
  1559. cCacheEntries = m_cache.CItems();
  1560. if ( cCacheEntries < C_CULL_CACHE_ENTRIES )
  1561. return;
  1562. //
  1563. // We need to cull. Run through the cache gathering the access
  1564. // frequency information for each entry.
  1565. //
  1566. if (!rgci.resize(cCacheEntries * sizeof(SCullInfo)))
  1567. return;
  1568. COpGatherCullInfo opGatherCullInfo(rgci.get());
  1569. m_cache.ForEach( opGatherCullInfo );
  1570. }
  1571. //
  1572. // Now that we are out of the reader block, cache entries can be
  1573. // freely added and removed, so there's no guarantee that any of
  1574. // the cull info we just gathered is still accurate at this point.
  1575. // It's important to remember that culling is a hint-driven process.
  1576. // More strict methods would require holding locks longer, increasing
  1577. // the probability of contention.
  1578. //
  1579. //
  1580. // Sort the cull info by increasing number of cache entry hits.
  1581. //
  1582. qsort( rgci.get(),
  1583. cCacheEntries,
  1584. sizeof(SCullInfo),
  1585. reinterpret_cast<int (__cdecl *)(const void *, const void *)>(SCullInfo::Compare) );
  1586. // Run through the sorted cull info and cull entries from the cache
  1587. //
  1588. Assert( cCacheEntries >= C_CULL_CACHE_ENTRIES );
  1589. {
  1590. CSynchronizedWriteBlock sb(m_mrwCache);
  1591. for ( int iCacheEntry = 0;
  1592. iCacheEntry < C_CULL_CACHE_ENTRIES;
  1593. iCacheEntry++ )
  1594. {
  1595. m_cache.Remove( DwordKey(rgci[iCacheEntry].dwDataSet) );
  1596. }
  1597. }
  1598. }
  1599. // ------------------------------------------------------------------------
  1600. //
  1601. // CMetabase::HrCacheData()
  1602. //
  1603. // Add a new cache entry for the metadata for the object at the given
  1604. // access path.
  1605. //
  1606. HRESULT
  1607. CMetabase::HrCacheData( const IEcb& ecb,
  1608. LPCWSTR pwszMDPathAccess,
  1609. LPCWSTR pwszMDPathOpen,
  1610. CMDData ** ppMDData )
  1611. {
  1612. auto_ref_ptr<CMDData> pMDDataRet;
  1613. auto_heap_ptr<BYTE> pbData;
  1614. LPCWSTR pwszMDPathDataSet;
  1615. DWORD dwDataSet;
  1616. DWORD dwcMDRecords;
  1617. HRESULT hr = S_OK;
  1618. //
  1619. // Read in the raw metadata from the metabase
  1620. //
  1621. hr = HrReadMetaData( ecb,
  1622. m_pMSAdminBase.get(),
  1623. pwszMDPathAccess,
  1624. pwszMDPathOpen,
  1625. &pbData,
  1626. &dwDataSet,
  1627. &dwcMDRecords,
  1628. &pwszMDPathDataSet );
  1629. if ( FAILED(hr) )
  1630. {
  1631. DebugTrace( "CMetabase::HrCacheData() - HrReadMetaData() failed 0x%08lX\n", hr );
  1632. goto ret;
  1633. }
  1634. //
  1635. // Digest it into a new metadata object
  1636. //
  1637. pMDDataRet.take_ownership(new CMDData(pwszMDPathDataSet, dwDataSet));
  1638. if ( !pMDDataRet->FInitialize(pbData, dwcMDRecords) )
  1639. {
  1640. //
  1641. //$REVIEW We should probably log this in the event log since
  1642. //$REVIEW there is no other indication to the server admin
  1643. //$REVIEW what is wrong and this is something that an admin
  1644. //$REVIEW could fix.
  1645. //
  1646. hr = E_INVALIDARG;
  1647. DebugTrace( "CMetabase::HrCacheData() - Metadata is malformed\n" );
  1648. goto ret;
  1649. }
  1650. //
  1651. // Add the new data object to the cache. Note: we don't care
  1652. // if we can't add to the cache. We already have a metadata
  1653. // object that we can return to the caller.
  1654. //
  1655. {
  1656. CSynchronizedWriteBlock sb(m_mrwCache);
  1657. if ( !m_cache.Lookup( DwordKey(dwDataSet) ) )
  1658. (void) m_cache.FAdd( DwordKey(dwDataSet), pMDDataRet );
  1659. }
  1660. //
  1661. // If the cache size has exceeded the expiration threshold then
  1662. // start culling entries until it goes below the minimum
  1663. // threshold. The ICE ensures that only the first thread to
  1664. // see the threshold exceeded will do the culling.
  1665. //
  1666. if ( (m_cache.CItems() > C_MAX_CACHE_ENTRIES) &&
  1667. TRUE == InterlockedCompareExchange(&m_lfCulling, TRUE, FALSE) )
  1668. {
  1669. //
  1670. //$REVIEW Consider culling asynchronously. I believe the current
  1671. //$REVIEW mechanism still allows us to hang onto a very large
  1672. //$REVIEW cache which is never reduced if we get hit with a
  1673. //$REVIEW burst of new entries simultaneously.
  1674. //
  1675. CullCacheEntries();
  1676. m_lfCulling = FALSE;
  1677. }
  1678. Assert( pMDDataRet.get() );
  1679. *ppMDData = pMDDataRet.relinquish();
  1680. ret:
  1681. return hr;
  1682. }
  1683. // ------------------------------------------------------------------------
  1684. //
  1685. // CMetabase::HrGetData()
  1686. //
  1687. // Fetch data from the metabase cache. See comment in \cal\src\inc\davmb.h
  1688. // for the distinction between pszMDPathAccess and pszMDPathOpen.
  1689. //
  1690. HRESULT
  1691. CMetabase::HrGetData( const IEcb& ecb,
  1692. LPCWSTR pwszMDPathAccess,
  1693. LPCWSTR pwszMDPathOpen,
  1694. IMDData ** ppMDData )
  1695. {
  1696. auto_ref_ptr<CMDData> pMDDataRet;
  1697. DWORD dwDataSet;
  1698. HRESULT hr;
  1699. // Fetch the data set number for this path directly from the metabase.
  1700. // Items in the metabase with the same data set number have the same data.
  1701. //
  1702. {
  1703. safe_revert sr(ecb.HitUser());
  1704. hr = m_pMSAdminBase->GetDataSetNumber(METADATA_MASTER_ROOT_HANDLE,
  1705. pwszMDPathAccess,
  1706. &dwDataSet);
  1707. if ( FAILED(hr) )
  1708. {
  1709. MBTrace( "CMetabase::HrGetData() - GetDataSetNumber() failed 0x%08lX\n", hr );
  1710. return hr;
  1711. }
  1712. MBTrace("MB: CMetabase::HrGetData() - TID %3d: Retrieved data set number 0x%08lX for path '%S'\n", GetCurrentThreadId(), dwDataSet, pwszMDPathAccess );
  1713. }
  1714. //
  1715. // If we don't care about the exact path then look for any entry
  1716. // in the cache with this data set number. If we do care then
  1717. // look for an entry in the cache with this data set number AND
  1718. // a matching path.
  1719. //
  1720. // Note: a pointer comparison here is sufficient. Callers are expected
  1721. // to use the single path version of HrMDGetData() if they want
  1722. // an metadata for an exact path. That version passes the same
  1723. // string for both pszMDPathAccess and pszMDPathOpen.
  1724. //
  1725. // Why does anyone care about an exact path match? Inheritance.
  1726. //
  1727. {
  1728. CSynchronizedReadBlock sb(m_mrwCache);
  1729. if (pwszMDPathAccess == pwszMDPathOpen)
  1730. {
  1731. MBTrace("MB: CMetabase::HrGetData() - TID %3d: Exact path match! Trying to get CMDData, dataset 0x%08lX\n", GetCurrentThreadId(), dwDataSet);
  1732. COpMatchExactPath(pwszMDPathAccess).Invoke(m_cache, dwDataSet, &pMDDataRet);
  1733. }
  1734. else
  1735. {
  1736. MBTrace("MB: CMetabase::HrGetData() - TID %3d: Not exact path match! Trying to get CMDData, dataset 0x%08lX\n", GetCurrentThreadId(), dwDataSet);
  1737. (void) m_cache.FFetch( dwDataSet, &pMDDataRet );
  1738. }
  1739. }
  1740. if ( pMDDataRet.get() )
  1741. {
  1742. MBTrace("MB: CMetabase::HrGetData() - TID %3d: Retrieved cached CMDData, data set number 0x%08lX, path '%S'\n", GetCurrentThreadId(), dwDataSet, pwszMDPathAccess );
  1743. pMDDataRet->LFUData().Touch();
  1744. }
  1745. else
  1746. {
  1747. MBTrace("MB: CMetabase::HrGetData() - TID %3d: No cached data CMDData, data set number 0x%08lX, path '%S'\n", GetCurrentThreadId(), dwDataSet, pwszMDPathAccess );
  1748. //
  1749. // We didn't find an entry in the cache, so create one.
  1750. //
  1751. // Note: nothing here prevents multiple threads from getting here
  1752. // simultaneously and attempting to cache duplicate entries. That
  1753. // is done within HrCacheData().
  1754. //
  1755. hr = HrCacheData( ecb,
  1756. pwszMDPathAccess,
  1757. pwszMDPathOpen,
  1758. pMDDataRet.load() );
  1759. if ( FAILED(hr) )
  1760. {
  1761. MBTrace( "MB: CMetabase::HrGetData() - HrCacheData() failed 0x%08lX\n", hr );
  1762. return hr;
  1763. }
  1764. }
  1765. //
  1766. // Return the data object
  1767. //
  1768. Assert( pMDDataRet.get() );
  1769. *ppMDData = pMDDataRet.relinquish();
  1770. return S_OK;
  1771. }
  1772. // ------------------------------------------------------------------------
  1773. //
  1774. // CMetabase::HrOpenObject()
  1775. //
  1776. HRESULT
  1777. CMetabase::HrOpenObject( LPCWSTR pwszMDPath,
  1778. DWORD dwAccess,
  1779. DWORD dwMsecTimeout,
  1780. CMDObjectHandle * pmdoh )
  1781. {
  1782. Assert(pwszMDPath);
  1783. Assert(pmdoh);
  1784. return pmdoh->HrOpen( m_pMSAdminBase.get(),
  1785. pwszMDPath,
  1786. dwAccess,
  1787. dwMsecTimeout );
  1788. }
  1789. // ------------------------------------------------------------------------
  1790. //
  1791. // CMetabase::HrOpenLowestNodeObject()
  1792. //
  1793. HRESULT
  1794. CMetabase::HrOpenLowestNodeObject( LPWSTR pwszMDPath,
  1795. DWORD dwAccess,
  1796. LPWSTR * ppwszMDPath,
  1797. CMDObjectHandle * pmdoh )
  1798. {
  1799. Assert(pwszMDPath);
  1800. Assert(ppwszMDPath);
  1801. Assert(pmdoh);
  1802. return pmdoh->HrOpenLowestNode( m_pMSAdminBase.get(),
  1803. pwszMDPath,
  1804. dwAccess,
  1805. ppwszMDPath );
  1806. }
  1807. // ------------------------------------------------------------------------
  1808. //
  1809. // CMetabase::HrIsAuthorViaFrontPageNeeded()
  1810. //
  1811. // Description: Function goes directly to the metabase and checks if
  1812. // the given path is configured as "FrontPageWeb". We need
  1813. // to do that via direct read from the metabase rather than
  1814. // going through dataset cache, as as that does not work very
  1815. // well due to the fact, that we are reading inherited
  1816. // metadata and get stuck with it.
  1817. // Parameters:
  1818. //
  1819. // ecb - interface to ecb object, that will be needed for
  1820. // fetching the impersonation token for that we will need
  1821. // to impersonate as as soon as read from metabase is finished
  1822. // pwszMDPath - metabase path that we want to check out
  1823. // pfFrontPageWeb - pointer to the booleanin which the result of operation is
  1824. // returned
  1825. //
  1826. HRESULT
  1827. CMetabase::HrIsAuthorViaFrontPageNeeded(const IEcb& ecb,
  1828. LPCWSTR pwszMDPath,
  1829. BOOL * pfFrontPageWeb)
  1830. {
  1831. HRESULT hr = S_OK;
  1832. CMDObjectHandle mdoh(ecb, m_pMSAdminBase.get());
  1833. BOOL fFrontPageWeb = FALSE;
  1834. DWORD cbData = sizeof(BOOL);
  1835. METADATA_RECORD mdrec;
  1836. Assert( pwszMDPath );
  1837. Assert( pfFrontPageWeb );
  1838. // Assume that we do not have "FrontPageWeb" set to TRUE
  1839. //
  1840. *pfFrontPageWeb = FALSE;
  1841. // We want just explicitely set data, not inherited one
  1842. //
  1843. mdrec.dwMDIdentifier = MD_FRONTPAGE_WEB;
  1844. mdrec.dwMDAttributes = METADATA_NO_ATTRIBUTES;
  1845. mdrec.dwMDUserType = IIS_MD_UT_SERVER;
  1846. mdrec.dwMDDataType = DWORD_METADATA;
  1847. mdrec.dwMDDataLen = cbData;
  1848. mdrec.pbMDData = reinterpret_cast<PBYTE>(&fFrontPageWeb);
  1849. hr = mdoh.HrGetMetaData(pwszMDPath,
  1850. &mdrec,
  1851. &cbData);
  1852. if (FAILED(hr))
  1853. {
  1854. MBTrace( "MB: CMetabase::HrIsAuthorViaFrontPageNeeded() - CMDObjectHandle::HrGetMetaData() failed 0x%08lX\n", hr );
  1855. goto ret;
  1856. }
  1857. // If we succeeded then we should have the value in our hands
  1858. //
  1859. *pfFrontPageWeb = fFrontPageWeb;
  1860. ret:
  1861. return hr;
  1862. }
  1863. // The way IID_IMSAdminBaseSinkW is defined in IADMW.H does
  1864. // not work well with EXO. So it needs to be redefined
  1865. // here in such a way that it will work.
  1866. //
  1867. const IID IID_IMSAdminBaseSinkW = {
  1868. 0xa9e69612,
  1869. 0xb80d,
  1870. 0x11d0,
  1871. {
  1872. 0xb9, 0xb9, 0x0, 0xa0,
  1873. 0xc9, 0x22, 0xe7, 0x50
  1874. }
  1875. };
  1876. // ------------------------------------------------------------------------
  1877. //
  1878. // FHasCachedIDs()
  1879. //
  1880. // Returns TRUE if any one of the IDs rgdwDataIDs is one of the IDs that
  1881. // we care about in CMDData::FInitialize().
  1882. //
  1883. // !!!IMPORTANT!!! The list of IDs in this function *MUST* be kept up to
  1884. // date with the cases in CMDData::FInitialize().
  1885. //
  1886. __inline BOOL
  1887. FHasCachedIDs( DWORD dwcDataIDs,
  1888. DWORD * rgdwDataIDs )
  1889. {
  1890. for ( DWORD iID = 0; iID < dwcDataIDs; iID++ )
  1891. {
  1892. switch ( rgdwDataIDs[iID] )
  1893. {
  1894. case MD_IP_SEC:
  1895. case MD_ACCESS_PERM:
  1896. case MD_IS_CONTENT_INDEXED:
  1897. case MD_FRONTPAGE_WEB:
  1898. case MD_DIRECTORY_BROWSING:
  1899. case MD_AUTHORIZATION:
  1900. case MD_DEFAULT_LOAD_FILE:
  1901. case MD_CUSTOM_ERROR:
  1902. case MD_MIME_MAP:
  1903. case MD_SCRIPT_MAPS:
  1904. case MD_APP_ISOLATED:
  1905. case MD_VR_USERNAME:
  1906. case MD_VR_PASSWORD:
  1907. case MD_HTTP_EXPIRES:
  1908. case MD_SERVER_BINDINGS:
  1909. return TRUE;
  1910. }
  1911. }
  1912. return FALSE;
  1913. }
  1914. // ------------------------------------------------------------------------
  1915. //
  1916. // CMetabase::COpNotify::operator()
  1917. //
  1918. BOOL
  1919. CMetabase::COpNotify::operator()( const DwordKey& key,
  1920. const auto_ref_ptr<CMDData>& pMDData )
  1921. {
  1922. //
  1923. // If the path for this cache entry is a child of the path
  1924. // being notified, then set this entry's data set ID in the
  1925. // array of IDs to blow from the cache.
  1926. //
  1927. if ( !_wcsnicmp( m_pwszMDPathNotify,
  1928. pMDData->PwszMDPathDataSet(),
  1929. m_cchMDPathNotify ) )
  1930. {
  1931. Assert (m_iCacheEntry < m_cCacheEntry);
  1932. m_rgdwDataSets[m_iCacheEntry] = pMDData->DwDataSet();
  1933. m_fDataSetsFlagged = TRUE;
  1934. }
  1935. ++m_iCacheEntry;
  1936. //
  1937. // ForEach() operators can cancel the iteration by returning FALSE.
  1938. // We always want to iterate over everything so return TRUE
  1939. //
  1940. return TRUE;
  1941. }
  1942. // ------------------------------------------------------------------------
  1943. //
  1944. // CMetabase::OnNotify()
  1945. //
  1946. VOID
  1947. CMetabase::OnNotify( DWORD cCO,
  1948. MD_CHANGE_OBJECT_W rgCO[] )
  1949. {
  1950. INT cCacheEntries;
  1951. CStackBuffer<DWORD> rgdwDataSets;
  1952. BOOL fDataSetsFlagged;
  1953. //
  1954. // Grab a read lock on the cache and go through it
  1955. // figuring out which items we want to blow away.
  1956. //
  1957. {
  1958. CSynchronizedReadBlock sb(m_mrwCache);
  1959. cCacheEntries = m_cache.CItems();
  1960. if (!rgdwDataSets.resize(sizeof(DWORD) * cCacheEntries))
  1961. return;
  1962. memset(rgdwDataSets.get(), 0, sizeof(DWORD) * cCacheEntries);
  1963. COpNotify opNotify(cCacheEntries, rgdwDataSets.get());
  1964. for ( DWORD iCO = 0; iCO < cCO; iCO++ )
  1965. {
  1966. LPWSTR pwszMDPath = reinterpret_cast<LPWSTR>(rgCO[iCO].pszMDPath);
  1967. // Quick litmus test: ignore any change that is not
  1968. // related to anything that we would ever cache -- i.e.
  1969. // anything that is not one of the following:
  1970. //
  1971. // - The global mimemap (LM/MimeMap)
  1972. // - Anything in the W3SVC tree (LM/W3SVC/...)
  1973. //
  1974. // Also ignore MD_CHANGE_TYPE_ADD_OBJECT notifications --
  1975. // even in combination with other notifications. We simply
  1976. // don't care when something is added because we always
  1977. // read from the metabase when we don't find an item in
  1978. // the cache.
  1979. //
  1980. // Finally, ignore changes to any data that isn't interesting
  1981. // to us -- i.e. that we don't cache.
  1982. //
  1983. if ( (!_wcsnicmp(gc_wsz_Lm_MimeMap, pwszMDPath, gc_cch_Lm_MimeMap) ||
  1984. !_wcsnicmp(gc_wsz_Lm_W3Svc, pwszMDPath, gc_cch_Lm_W3Svc - 1)) &&
  1985. !(rgCO[iCO].dwMDChangeType & MD_CHANGE_TYPE_ADD_OBJECT) &&
  1986. FHasCachedIDs( rgCO[iCO].dwMDNumDataIDs,
  1987. rgCO[iCO].pdwMDDataIDs ) )
  1988. {
  1989. //
  1990. // Flag each entry in the cache whose data set corresponds
  1991. // to a path that is a child of the one being notified.
  1992. //
  1993. MBTrace ("MB: cache: flagging '%S' as dirty\n", pwszMDPath);
  1994. opNotify.Configure( pwszMDPath );
  1995. m_cache.ForEach( opNotify );
  1996. }
  1997. }
  1998. fDataSetsFlagged = opNotify.FDataSetsFlagged();
  1999. }
  2000. //
  2001. // If any data sets were flagged in our pass above then
  2002. // grab a write lock now and blow `em away.
  2003. //
  2004. // Note: we don't care about any change to the cache between the
  2005. // time we sweep above and now. If data sets are culled,
  2006. // and even re-added, between then and now, that's fine.
  2007. // The worst thing that this does is cause them to be
  2008. // faulted in again. On the flip side, any new data sets
  2009. // brought into the cache after our pass above by definition
  2010. // has more recent data, so there is no possibility of
  2011. // missing any cached entries and ending up with stale data.
  2012. //
  2013. if ( fDataSetsFlagged )
  2014. {
  2015. CSynchronizedWriteBlock sb(m_mrwCache);
  2016. for ( INT iCacheEntry = 0;
  2017. iCacheEntry < cCacheEntries;
  2018. iCacheEntry++ )
  2019. {
  2020. if ( rgdwDataSets[iCacheEntry] )
  2021. m_cache.Remove( DwordKey(rgdwDataSets[iCacheEntry]) );
  2022. }
  2023. }
  2024. }
  2025. // ========================================================================
  2026. //
  2027. // CLASS CNotifSink
  2028. //
  2029. // ------------------------------------------------------------------------
  2030. //
  2031. // CNotifSink::SinkNotify()
  2032. //
  2033. // Metabase change notification callback
  2034. //
  2035. HRESULT STDMETHODCALLTYPE
  2036. CNotifSink::SinkNotify(/* [in] */ DWORD dwMDNumElements,
  2037. /* [size_is][in] */ MD_CHANGE_OBJECT_W __RPC_FAR pcoChangeList[ ])
  2038. {
  2039. OnNotify( dwMDNumElements,
  2040. pcoChangeList );
  2041. return S_OK;
  2042. }
  2043. VOID
  2044. CNotifSink::OnNotify( DWORD cCO,
  2045. MD_CHANGE_OBJECT_W rgCO[] )
  2046. {
  2047. // Trace out the information with which we have been called
  2048. //
  2049. #ifdef DBG
  2050. MBTrace("MB: CNotifSink::OnNotify() - TID %3d: MD_CHANGE_OBJECT_W array length 0x%08lX\n", GetCurrentThreadId(), cCO );
  2051. for ( DWORD idwElem = 0; idwElem < cCO; idwElem++ )
  2052. {
  2053. MBTrace(" Element %d:\n", idwElem );
  2054. MBTrace(" pszMDPath '%S'\n", rgCO[idwElem].pszMDPath );
  2055. MBTrace(" dwMDChangeType 0x%08lX\n", rgCO[idwElem].dwMDChangeType );
  2056. MBTrace(" dwMDNumDataIDs 0x%08lX\n", rgCO[idwElem].dwMDNumDataIDs );
  2057. for (DWORD idwID = 0; idwID < rgCO[idwElem].dwMDNumDataIDs; idwID++)
  2058. {
  2059. MBTrace(" pdwMDDataIDs[%d] is 0x%08lX\n", idwID, rgCO[idwElem].pdwMDDataIDs[idwID] );
  2060. }
  2061. }
  2062. #endif
  2063. CMetabase::Instance().OnNotify( cCO,
  2064. rgCO );
  2065. CChildVRCache::Instance().OnNotify( cCO,
  2066. rgCO );
  2067. }
  2068. // ========================================================================
  2069. //
  2070. // FREE Functions
  2071. //
  2072. BOOL
  2073. FMDInitialize()
  2074. {
  2075. // Instantiate the CMetabase object and initialize it.
  2076. // Note that if initialization fails, we don't destroy
  2077. // the instance. MDDeinitialize() must still be called.
  2078. //
  2079. return CMetabase::CreateInstance().FInitialize();
  2080. }
  2081. VOID
  2082. MDDeinitialize()
  2083. {
  2084. CMetabase::DestroyInstance();
  2085. }
  2086. // ------------------------------------------------------------------------
  2087. //
  2088. // In the future we might need Copy/Rename/Delete operations
  2089. // on metabase objects.
  2090. // For Copy following steps should apply:
  2091. // a) Lock dst
  2092. // b) Kick dst and children out of cache
  2093. // c) Copy the raw metadata
  2094. // d) Unlock dst
  2095. // e) Send update notifications
  2096. //
  2097. // For Rename:
  2098. // a) Lock common parent of src and dst
  2099. // b) Kick dst and children out of cache
  2100. // c) Rename src to dst
  2101. // d) Kick src and children out of cache
  2102. // e) Unlock common parent of src and dst
  2103. // f) Send update notifications
  2104. // For Delete:
  2105. // a) Lock path
  2106. // b) Kick path and children out of cache
  2107. // c) Unlock path
  2108. // d) Send update notifications
  2109. // ------------------------------------------------------------------------
  2110. //
  2111. // HrMDGetData()
  2112. //
  2113. // Intended primarily for use by impls. This call fetches the metadata
  2114. // for the specified URI. If the URI is the request URI then this
  2115. // function uses the copy of the metadata cached on the ecb. This saves
  2116. // a cache lookup (and read lock) in the majority of cases.
  2117. //
  2118. HRESULT
  2119. HrMDGetData( const IEcb& ecb,
  2120. LPCWSTR pwszURI,
  2121. IMDData ** ppMDData )
  2122. {
  2123. SCODE sc = S_OK;
  2124. auto_heap_ptr<WCHAR> pwszMDPathURI;
  2125. auto_heap_ptr<WCHAR> pwszMDPathOpenOnHeap;
  2126. LPWSTR pwszMDPathOpen;
  2127. //
  2128. // If the URI is the request URI then we already have the data cached.
  2129. //
  2130. // Note that we only test for pointer equality here because
  2131. // typically callers will pass in THE request URI from
  2132. // the ECB rather than a copy of it.
  2133. //
  2134. if ( ecb.LpwszRequestUrl() == pwszURI )
  2135. {
  2136. *ppMDData = &ecb.MetaData();
  2137. Assert (*ppMDData);
  2138. (*ppMDData)->AddRef();
  2139. goto ret;
  2140. }
  2141. //
  2142. // Map the URI to its equivalent metabase path, and make sure
  2143. // the URL is stripped before we call into the MDPath processing
  2144. //
  2145. Assert (pwszURI == PwszUrlStrippedOfPrefix (pwszURI));
  2146. pwszMDPathURI = static_cast<LPWSTR>(ExAlloc(CbMDPathW(ecb, pwszURI)));
  2147. if (NULL == pwszMDPathURI.get())
  2148. {
  2149. sc = E_OUTOFMEMORY;
  2150. goto ret;
  2151. }
  2152. MDPathFromURIW(ecb, pwszURI, pwszMDPathURI);
  2153. pwszMDPathOpen = const_cast<LPWSTR>(ecb.PwszMDPathVroot());
  2154. // If the URI requested is in NOT in the current request's vroot,
  2155. // start the metabase search from the virtual server root.
  2156. //
  2157. if (_wcsnicmp(pwszMDPathURI, pwszMDPathOpen, wcslen(pwszMDPathOpen)))
  2158. {
  2159. pwszMDPathOpenOnHeap = static_cast<LPWSTR>(ExAlloc(CbMDPathW(ecb, L"")));
  2160. if (NULL == pwszMDPathOpenOnHeap.get())
  2161. {
  2162. sc = E_OUTOFMEMORY;
  2163. goto ret;
  2164. }
  2165. pwszMDPathOpen = pwszMDPathOpenOnHeap.get();
  2166. MDPathFromURIW(ecb, L"", pwszMDPathOpen);
  2167. }
  2168. //
  2169. // Fetch and return the metadata
  2170. //
  2171. sc = CMetabase::Instance().HrGetData( ecb,
  2172. pwszMDPathURI,
  2173. pwszMDPathOpen,
  2174. ppMDData );
  2175. ret:
  2176. return sc;
  2177. }
  2178. // ------------------------------------------------------------------------
  2179. //
  2180. // HrMDGetData()
  2181. //
  2182. // Fetch metadata for the specified metabase path.
  2183. //
  2184. HRESULT
  2185. HrMDGetData( const IEcb& ecb,
  2186. LPCWSTR pwszMDPathAccess,
  2187. LPCWSTR pwszMDPathOpen,
  2188. IMDData ** ppMDData )
  2189. {
  2190. return CMetabase::Instance().HrGetData( ecb,
  2191. pwszMDPathAccess,
  2192. pwszMDPathOpen,
  2193. ppMDData );
  2194. }
  2195. // ------------------------------------------------------------------------
  2196. //
  2197. // DwMDChangeNumber()
  2198. //
  2199. // Get the metabase change number .
  2200. //
  2201. DWORD
  2202. DwMDChangeNumber(const IEcb * pecb)
  2203. {
  2204. return CMetabase::Instance().DwChangeNumber(pecb);
  2205. }
  2206. // ------------------------------------------------------------------------
  2207. //
  2208. // HrMDOpenMetaObject()
  2209. //
  2210. // Open a metadata object, given a path
  2211. //
  2212. HRESULT
  2213. HrMDOpenMetaObject( LPCWSTR pwszMDPath,
  2214. DWORD dwAccess,
  2215. DWORD dwMsecTimeout,
  2216. CMDObjectHandle * pmdoh )
  2217. {
  2218. return CMetabase::Instance().HrOpenObject( pwszMDPath,
  2219. dwAccess,
  2220. dwMsecTimeout,
  2221. pmdoh );
  2222. }
  2223. HRESULT
  2224. HrMDOpenLowestNodeMetaObject( LPWSTR pwszMDPath,
  2225. DWORD dwAccess,
  2226. LPWSTR * ppwszMDPath,
  2227. CMDObjectHandle * pmdoh )
  2228. {
  2229. return CMetabase::Instance().HrOpenLowestNodeObject( pwszMDPath,
  2230. dwAccess,
  2231. ppwszMDPath,
  2232. pmdoh );
  2233. }
  2234. HRESULT
  2235. HrMDIsAuthorViaFrontPageNeeded(const IEcb& ecb,
  2236. LPCWSTR pwszURI,
  2237. BOOL * pfFrontPageWeb)
  2238. {
  2239. return CMetabase::Instance().HrIsAuthorViaFrontPageNeeded( ecb,
  2240. pwszURI,
  2241. pfFrontPageWeb );
  2242. }
  2243. // class CMetaOp -------------------------------------------------------------
  2244. //
  2245. SCODE __fastcall
  2246. CMetaOp::ScEnumOp (LPWSTR pwszMetaPath, UINT cch)
  2247. {
  2248. Assert (cch <= METADATA_MAX_NAME_LEN);
  2249. DWORD dwIndex = 0;
  2250. LPWSTR pwszKey;
  2251. SCODE sc = S_OK;
  2252. // First and formost, call out on the key handed in
  2253. //
  2254. MBTrace ("MB: CMetaOp::ScEnumOp(): calling op() on '%S'\n", pwszMetaPath);
  2255. sc = ScOp (pwszMetaPath, cch);
  2256. if (FAILED (sc))
  2257. goto ret;
  2258. // If the Op() returns S_FALSE, that means the operation
  2259. // knows enough that it does not have to be called for any
  2260. // more metabase paths.
  2261. //
  2262. if (S_FALSE == sc)
  2263. goto ret;
  2264. // Then enumerate all the child nodes and recurse. To do
  2265. // this, we are going use the fact that we have been passed
  2266. // a buffer big enough to handle CCH_BUFFER_SIZE chars.
  2267. //
  2268. Assert ((cch + 1 + METADATA_MAX_NAME_LEN) <= CCH_BUFFER_SIZE);
  2269. pwszKey = pwszMetaPath + cch;
  2270. *(pwszKey++) = L'/';
  2271. *pwszKey = L'\0';
  2272. while (TRUE)
  2273. {
  2274. // Enum the next key in the set of child keys, and process it.
  2275. //
  2276. sc = m_mdoh.HrEnumKeys (pwszMetaPath, pwszKey, dwIndex);
  2277. if (FAILED (sc))
  2278. {
  2279. sc = S_OK;
  2280. break;
  2281. }
  2282. // Recurse on the new path.
  2283. //
  2284. Assert (wcslen(pwszKey) <= METADATA_MAX_NAME_LEN);
  2285. sc = ScEnumOp (pwszMetaPath, cch + 1 + static_cast<UINT>(wcslen(pwszKey)));
  2286. if (FAILED (sc))
  2287. goto ret;
  2288. // If the EnumOp() returns S_FALSE, that means the operation
  2289. // knows enough that it does not have to be called for any
  2290. // more metabase paths.
  2291. //
  2292. if (S_FALSE == sc)
  2293. goto ret;
  2294. // Increment the index to make sure the traversing continues
  2295. //
  2296. dwIndex++;
  2297. // Truncate the metapath again
  2298. //
  2299. *pwszKey = 0;
  2300. }
  2301. ret:
  2302. return sc;
  2303. }
  2304. SCODE __fastcall
  2305. CMetaOp::ScMetaOp()
  2306. {
  2307. auto_heap_ptr<WCHAR> prgwchMDPaths;
  2308. SCODE sc = S_OK;
  2309. // Initialize the metabase
  2310. //
  2311. sc = HrMDOpenMetaObject( m_pwszMetaPath,
  2312. m_fWrite ? METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE : METADATA_PERMISSION_READ,
  2313. 5000,
  2314. &m_mdoh );
  2315. if (FAILED (sc))
  2316. {
  2317. // If the path is not found, then it really is
  2318. // not a problem...
  2319. //
  2320. if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == sc)
  2321. {
  2322. MBTrace ("MB: CMetaOp::ScMetaOp(): '%S' does not exist\n", m_pwszMetaPath);
  2323. return S_OK;
  2324. }
  2325. DebugTrace ("Dav: MCD: unable to initialize metabase\n");
  2326. return sc;
  2327. }
  2328. // Get the set of paths for which the metabase property
  2329. // is explicitly specified.
  2330. //
  2331. // Since the size of the buffer needed to hold the paths
  2332. // is initially unknown, guess at a reasonable size. If
  2333. // it's not large enough, then then we will fallback to
  2334. // iterating throught the tree.
  2335. //
  2336. // Either way, for each directory where the value is set
  2337. // explicitly, the call to ScOp() will be called (and in
  2338. // the fallback scenario, sometimes it won't be set).
  2339. //
  2340. prgwchMDPaths = static_cast<LPWSTR>(g_heap.Alloc(CCH_BUFFER_SIZE * sizeof(WCHAR)));
  2341. DWORD cchMDPaths = CCH_BUFFER_SIZE;
  2342. sc = m_mdoh.HrGetDataPaths( L"",
  2343. m_dwId,
  2344. m_dwType,
  2345. prgwchMDPaths,
  2346. &cchMDPaths );
  2347. if (FAILED(sc))
  2348. {
  2349. // Ok, this is the fallback position...
  2350. //
  2351. MBTrace ("MB: CMetaOp::ScMetaOp(): falling back to enumeration for op()\n");
  2352. //
  2353. // We want to enumerate all the possible metabase paths and call
  2354. // the sub-op for each. In this scenario, the sub-op is must be
  2355. // able to handle the case where the value is not explicitly set.
  2356. //
  2357. // We are first going to copy the metapath into our buffer from
  2358. // above and pass it in so that we can use it and not have to
  2359. // do any real allocations.
  2360. //
  2361. *prgwchMDPaths = 0;
  2362. sc = ScEnumOp (prgwchMDPaths, 0);
  2363. // Error or failure - we are done with processing this
  2364. // request.
  2365. //
  2366. goto ret;
  2367. }
  2368. else
  2369. {
  2370. // Woo hoo. The number/size of the paths all fit within
  2371. // the initial buffer!
  2372. //
  2373. // Go ahead and call the sub-op for each of these paths
  2374. //
  2375. LPCWSTR pwsz = prgwchMDPaths;
  2376. while (*pwsz)
  2377. {
  2378. MBTrace ("MB: CMetaOp::ScMetaOp(): calling op() on '%S'\n", pwsz);
  2379. // Call the sub-op. The sub-op is responsible for
  2380. // handling all possible errors, but can pass back
  2381. // any terminating errors.
  2382. //
  2383. UINT cch = static_cast<UINT>(wcslen (pwsz));
  2384. sc = ScOp (pwsz, cch);
  2385. if (FAILED (sc))
  2386. goto ret;
  2387. // If the Op() returns S_FALSE, that means the operation
  2388. // knows enough that it does not have to be called for any
  2389. // more metabase paths.
  2390. //
  2391. if (S_FALSE == sc)
  2392. goto ret;
  2393. // Move to the next metapath
  2394. //
  2395. pwsz += cch + 1;
  2396. }
  2397. // All the explict paths have been processed. We are done
  2398. // with processing this request.
  2399. //
  2400. goto ret;
  2401. }
  2402. ret:
  2403. // Close up the metabase regardless
  2404. //
  2405. m_mdoh.Close();
  2406. return sc;
  2407. }
  2408. // ------------------------------------------------------------------------
  2409. //
  2410. // FParseMDData()
  2411. //
  2412. // Parses a comma-delimited metadata string into fields. Any whitespace
  2413. // around the delimiters is considered insignificant and removed.
  2414. //
  2415. // Returns TRUE if the data parsed into the expected number of fields
  2416. // and FALSE otherwise.
  2417. //
  2418. // Pointers to the parsed are returned in rgpwszFields. If a string
  2419. // parses into fewer than the expected number of fields, NULLs are
  2420. // returned for all of the fields beyond the last one parsed.
  2421. //
  2422. // If a string parses into the expected number of fields then
  2423. // the last field is always just the remainder of the string beyond
  2424. // the second to last field, regardless whether the string could be
  2425. // parsed into additional fields. For example " foo , bar , baz "
  2426. // parses into three fields as "foo", "bar" and "baz", but parses
  2427. // into two fields as "foo" and "bar , baz"
  2428. //
  2429. // The total number of characters in pwszData, including the null
  2430. // terminator, is also returned in *pcchData.
  2431. //
  2432. // Note: this function MODIFIES pwszData.
  2433. //
  2434. BOOL
  2435. FParseMDData( LPWSTR pwszData,
  2436. LPWSTR rgpwszFields[],
  2437. UINT cFields,
  2438. UINT * pcchData )
  2439. {
  2440. Assert( pwszData );
  2441. Assert( pcchData );
  2442. Assert( cFields > 0 );
  2443. Assert( !IsBadWritePtr(rgpwszFields, cFields * sizeof(LPWSTR)) );
  2444. // Clear our "out" parameter
  2445. //
  2446. memset(rgpwszFields, 0, sizeof(LPWSTR) * cFields);
  2447. WCHAR * pwchDataEnd = NULL;
  2448. LPWSTR pwszField = pwszData;
  2449. BOOL fLastField = FALSE;
  2450. UINT iField = 0;
  2451. while (!fLastField)
  2452. {
  2453. WCHAR * pwch;
  2454. //
  2455. // Strip leading WS
  2456. //
  2457. while ( *pwszField && L' ' == *pwszField )
  2458. ++pwszField;
  2459. //
  2460. // Locate the delimiter following the field.
  2461. // For all fields but the last field the delimiter
  2462. // is a ','. For the last field, the "delimiter"
  2463. // is the terminating null.
  2464. //
  2465. if ( cFields - 1 == iField )
  2466. {
  2467. pwch = pwszField + wcslen(pwszField);
  2468. fLastField = TRUE;
  2469. }
  2470. else
  2471. {
  2472. pwch = wcschr(pwszField, L',');
  2473. if ( NULL == pwch )
  2474. {
  2475. //
  2476. // If we don't find a comma after the field
  2477. // then it is the last field.
  2478. //
  2479. pwch = pwszField + wcslen(pwszField);
  2480. fLastField = TRUE;
  2481. }
  2482. }
  2483. // At this point we should have found a comma
  2484. // or the null terimator after the field.
  2485. //
  2486. Assert( pwch );
  2487. pwchDataEnd = pwch;
  2488. //
  2489. // Eat trailing whitespace at the end of the
  2490. // field up to the delimiter we just found
  2491. // by backing up from the delimiter's position
  2492. // and null-terminating the field after the
  2493. // last non-whitespace character.
  2494. //
  2495. while ( pwch-- > pwszField && L' ' == *pwch )
  2496. ;
  2497. *++pwch = '\0';
  2498. //
  2499. // Fill in the pointer to this field
  2500. //
  2501. rgpwszFields[iField] = pwszField;
  2502. //
  2503. // Proceed to the next field
  2504. //
  2505. pwszField = pwchDataEnd + 1;
  2506. ++iField;
  2507. }
  2508. Assert( pwchDataEnd > pwszData );
  2509. *pcchData = static_cast<UINT>(pwchDataEnd - pwszData + 1);
  2510. return iField == cFields;
  2511. }