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.

714 lines
20 KiB

  1. // Tranx.cpp: implementation of the transaction support
  2. //
  3. // Copyright (c)1997-2001 Microsoft Corporation
  4. //
  5. //////////////////////////////////////////////////////////////////////
  6. #include "Tranx.h"
  7. #include "persistmgr.h"
  8. #include "requestobject.h"
  9. //
  10. // this is a file scope global constant, representing the string length needed for a guid
  11. // Our string guid is in this form as what StringFromGUID2 returns (includint braces):
  12. // {ab1ff71d-fff7-4162-818f-13d6e30c3110}
  13. const DWORD GUID_STRING_LENGTH = 39;
  14. /*
  15. Routine Description:
  16. Name:
  17. CTranxID::CTranxID
  18. Functionality:
  19. This is the constructor. Pass along the parameters to the base class
  20. Virtual:
  21. No (you know that, constructor won't be virtual!)
  22. Arguments:
  23. pKeyChain - Pointer to the ISceKeyChain COM interface which is prepared
  24. by the caller who constructs this instance.
  25. pNamespace - Pointer to WMI namespace of our provider (COM interface).
  26. Passed along by the caller. Must not be NULL.
  27. pCtx - Pointer to WMI context object (COM interface). Passed along
  28. by the caller. It's up to WMI whether this interface pointer is NULL or not.
  29. Return Value:
  30. None as any constructor
  31. Notes:
  32. if you create any local members, think about initialize them here
  33. */
  34. CTranxID::CTranxID (
  35. IN ISceKeyChain * pKeyChain,
  36. IN IWbemServices * pNamespace,
  37. IN IWbemContext * pCtx
  38. )
  39. :
  40. CGenericClass(pKeyChain, pNamespace, pCtx)
  41. {
  42. }
  43. /*
  44. Routine Description:
  45. Name:
  46. CTranxID::~CTranxID
  47. Functionality:
  48. Destructor. Necessary as good C++ discipline since we have virtual functions.
  49. Virtual:
  50. Yes.
  51. Arguments:
  52. none as any destructor
  53. Return Value:
  54. None as any destructor
  55. Notes:
  56. if you create any local members, think about whether
  57. there is any need for a non-trivial destructor
  58. */
  59. CTranxID::~CTranxID ()
  60. {
  61. }
  62. /*
  63. Routine Description:
  64. Name:
  65. CTranxID::PutInst
  66. Functionality:
  67. Put an instance as instructed by WMI. Since this class implements
  68. Sce_TransactionID, which is persistence oriented, this will cause
  69. the Sce_TransactionID object's property information to be saved
  70. in our store.
  71. Virtual:
  72. Yes.
  73. Arguments:
  74. pInst - COM interface pointer to the WMI class (Sce_TransactionID) object.
  75. pHandler - COM interface pointer for notifying WMI of any events.
  76. pCtx - COM interface pointer. This interface is just something we pass around.
  77. WMI may mandate it (not now) in the future. But we never construct
  78. such an interface and so, we just pass around for various WMI API's
  79. Return Value:
  80. Success: it must return success code (use SUCCEEDED to test). It is
  81. not guaranteed to return WBEM_NO_ERROR.
  82. Failure: Various errors may occurs. Any such error should indicate the failure of persisting
  83. the instance.
  84. Notes:
  85. */
  86. HRESULT
  87. CTranxID::PutInst (
  88. IN IWbemClassObject * pInst,
  89. IN IWbemObjectSink * pHandler,
  90. IN IWbemContext * pCtx
  91. )
  92. {
  93. //
  94. // CScePropertyMgr helps us to access WMI object's properties
  95. // create an instance and attach the WMI object to it.
  96. // This will always succeed.
  97. //
  98. CScePropertyMgr ScePropMgr;
  99. ScePropMgr.Attach(pInst);
  100. //
  101. // need to get the storepath property from the WMI object
  102. //
  103. CComBSTR vbstrStorePath;
  104. HRESULT hr = ScePropMgr.GetProperty(pStorePath, &vbstrStorePath);
  105. //
  106. // if storepath is missing, we can't continue because we don't know where to store the info
  107. //
  108. if (SUCCEEDED(hr))
  109. {
  110. //
  111. // get the transaction guid (we actually use string)
  112. //
  113. CComBSTR bstrToken;
  114. hr = ScePropMgr.GetProperty(pTranxGuid, &bstrToken);
  115. //
  116. // we will allow the object not to have a guid. In that case, we will create one ourselves
  117. //
  118. if (FAILED(hr) || WBEM_S_RESET_TO_DEFAULT == hr)
  119. {
  120. GUID guid = GUID_NULL;
  121. hr = ::CoCreateGuid(&guid);
  122. if (SUCCEEDED(hr))
  123. {
  124. //
  125. // allocate buffer for the guid's string representation
  126. // warning: don't blindly free this memory, it will be done by CComBSTR automatically!
  127. //
  128. bstrToken.m_str = ::SysAllocStringLen(NULL, GUID_STRING_LENGTH);
  129. if (bstrToken.m_str == NULL)
  130. {
  131. hr = WBEM_E_OUT_OF_MEMORY;
  132. }
  133. else
  134. {
  135. //
  136. // the only possibility for failure is that our buffer is too small
  137. // which should not happen!
  138. //
  139. if (::StringFromGUID2(guid, bstrToken.m_str, GUID_STRING_LENGTH) == 0)
  140. {
  141. hr = WBEM_E_BUFFER_TOO_SMALL;
  142. }
  143. }
  144. }
  145. }
  146. //
  147. // ready to save if everything is fine
  148. //
  149. if (SUCCEEDED(hr))
  150. {
  151. //
  152. // this store will be responsible for persisting the instance
  153. //
  154. CSceStore SceStore;
  155. //
  156. // the persistence is for having a storepath and on behalf of the instance
  157. //
  158. SceStore.SetPersistProperties(pInst, pStorePath);
  159. //
  160. // write the template header for inf files. For databases, this is a no-op
  161. //
  162. DWORD dwDump;
  163. hr = SceStore.WriteSecurityProfileInfo(AreaBogus, (PSCE_PROFILE_INFO)&dwDump, NULL, false);
  164. //
  165. // also, we need to write it to attachment section because it's not a native core object
  166. // without an entry in the attachment section, inf file tempalte can't be imported to
  167. // database stores. For database store, this is no-op
  168. //
  169. if (SUCCEEDED(hr))
  170. {
  171. hr = SceStore.WriteAttachmentSection(SCEWMI_TRANSACTION_ID_CLASS, pszAttachSectionValue);
  172. }
  173. //
  174. // Save all the properties
  175. //
  176. if (SUCCEEDED(hr))
  177. {
  178. hr = SceStore.SavePropertyToStore(SCEWMI_TRANSACTION_ID_CLASS, pTranxGuid, (LPCWSTR)bstrToken);
  179. }
  180. }
  181. }
  182. return hr;
  183. }
  184. /*
  185. Routine Description:
  186. Name:
  187. CTranxID::CreateObject
  188. Functionality:
  189. Create WMI objects (Sce_TransactionID). Depending on parameter atAction,
  190. this creation may mean:
  191. (a) Get a single instance (atAction == ACTIONTYPE_GET)
  192. (b) Get several instances satisfying some criteria (atAction == ACTIONTYPE_QUERY)
  193. (c) Delete an instance (atAction == ACTIONTYPE_DELETE)
  194. Virtual:
  195. Yes.
  196. Arguments:
  197. pHandler - COM interface pointer for notifying WMI for creation result.
  198. atAction - Get single instance ACTIONTYPE_GET
  199. Get several instances ACTIONTYPE_QUERY
  200. Delete a single instance ACTIONTYPE_DELETE
  201. Return Value:
  202. Success: it must return success code (use SUCCEEDED to test). It is
  203. not guaranteed to return WBEM_NO_ERROR. The returned objects are indicated to WMI,
  204. not directly passed back via parameters.
  205. Failure: Various errors may occurs. Except WBEM_E_NOT_FOUND, any such error should indicate
  206. the failure of getting the wanted instance. If WBEM_E_NOT_FOUND is returned in querying
  207. situations, this may not be an error depending on caller's intention.
  208. Notes:
  209. */
  210. HRESULT
  211. CTranxID::CreateObject (
  212. IN IWbemObjectSink * pHandler,
  213. IN ACTIONTYPE atAction
  214. )
  215. {
  216. //
  217. // we know how to:
  218. // Get single instance ACTIONTYPE_GET
  219. // Delete a single instance ACTIONTYPE_DELETE
  220. // Get several instances ACTIONTYPE_QUERY
  221. //
  222. if ( ACTIONTYPE_GET != atAction &&
  223. ACTIONTYPE_DELETE != atAction &&
  224. ACTIONTYPE_QUERY != atAction )
  225. {
  226. return WBEM_E_NOT_SUPPORTED;
  227. }
  228. //
  229. // need to know where to look for the instance(s), i.e., the store path!
  230. //
  231. CComVariant varStorePath;
  232. HRESULT hr = m_srpKeyChain->GetKeyPropertyValue(pStorePath, &varStorePath);
  233. //
  234. // GetKeyPropertyValue returns WBEM_S_FALSE if the key is not recognized.
  235. // WMI may return success code with empty variant. So, we won't proceed
  236. // unless we know that we get a bstr
  237. //
  238. if (SUCCEEDED(hr) && hr != WBEM_S_FALSE && varStorePath.vt == VT_BSTR)
  239. {
  240. //
  241. // our store knows needs to know where to read instances.
  242. // If this fails, it doesn't make sense to continue.
  243. //
  244. CSceStore SceStore;
  245. hr = SceStore.SetPersistPath(varStorePath.bstrVal);
  246. if ( SUCCEEDED(hr) )
  247. {
  248. //
  249. // make sure the store (just a file) really exists. The raw path
  250. // may contain env variables, so we need the expanded path
  251. //
  252. DWORD dwAttrib = GetFileAttributes(SceStore.GetExpandedPath());
  253. if ( dwAttrib == -1 )
  254. {
  255. //
  256. // if store is not there, we indicate that the instance can't be found
  257. //
  258. hr = WBEM_E_NOT_FOUND;
  259. }
  260. else
  261. {
  262. if ( ACTIONTYPE_DELETE == atAction )
  263. {
  264. //
  265. // since we will only ever have one instance,
  266. // deleting the one means to remove the entire section
  267. //
  268. hr = SceStore.DeleteSectionFromStore(SCEWMI_TRANSACTION_ID_CLASS);
  269. }
  270. else
  271. {
  272. //
  273. // Will hold the transation ID.
  274. // Warning! Need to free this memory! So, don't blindly return!
  275. //
  276. LPWSTR pszTranxID = NULL;
  277. DWORD dwRead = 0;
  278. hr = SceStore.GetPropertyFromStore(SCEWMI_TRANSACTION_ID_CLASS, pTranxGuid, &pszTranxID, &dwRead);
  279. //
  280. // If successful, then fill in the tranx guid and store path properties.
  281. // If transaction id can't be found, the object will be useless. So, should abort.
  282. //
  283. if (SUCCEEDED(hr))
  284. {
  285. //
  286. // get from WMI a blank instance. See function definition for details.
  287. // Smart pointer srpObj will automatically release interface pointer.
  288. //
  289. CComPtr<IWbemClassObject> srpObj;
  290. hr = SpawnAnInstance(&srpObj);
  291. if (SUCCEEDED(hr))
  292. {
  293. //
  294. // use property manager to put the properties for this instance srpObj,
  295. // Attach will always succeed.
  296. //
  297. CScePropertyMgr ScePropMgr;
  298. ScePropMgr.Attach(srpObj);
  299. //
  300. // we have two properties to put: pStorePath, and pTranxGuid
  301. //
  302. hr = ScePropMgr.PutProperty(pStorePath, SceStore.GetExpandedPath());
  303. if (SUCCEEDED(hr))
  304. {
  305. hr = ScePropMgr.PutProperty(pTranxGuid, pszTranxID);
  306. }
  307. //
  308. // everything alright, pass to WMI the newly created instance!
  309. //
  310. if (SUCCEEDED(hr))
  311. {
  312. hr = pHandler->Indicate(1, &srpObj);
  313. }
  314. }
  315. }
  316. //
  317. // we promise to release this memory
  318. //
  319. delete [] pszTranxID;
  320. }
  321. }
  322. }
  323. }
  324. if (SUCCEEDED(hr))
  325. {
  326. //
  327. // do the necessary gestures to WMI.
  328. // the use of WBEM_STATUS_REQUIREMENTS in SetStatus is not documented by WMI
  329. // at this point. Consult WMI team for detail if you suspect problems with
  330. // the use of WBEM_STATUS_REQUIREMENTS
  331. //
  332. if ( ACTIONTYPE_QUERY == atAction )
  333. {
  334. pHandler->SetStatus(WBEM_STATUS_REQUIREMENTS, S_FALSE, NULL, NULL);
  335. }
  336. else if (ACTIONTYPE_GET == atAction)
  337. {
  338. pHandler->SetStatus(WBEM_STATUS_REQUIREMENTS, S_OK, NULL, NULL);
  339. }
  340. }
  341. return hr;
  342. }
  343. /*
  344. Routine Description:
  345. Name:
  346. static CTranxID::BeginTransaction
  347. Functionality:
  348. Given a store path (file to the template), this function will start a transaction
  349. by creating our transaction token (an ID).
  350. Virtual:
  351. no.
  352. Arguments:
  353. pszStorePath - 0 terminated string as the tempalte file's path
  354. Return Value:
  355. Success: it must return success code (use SUCCEEDED to test). It is
  356. not guaranteed to return WBEM_NO_ERROR.
  357. Failure: Various errors may occurs. Except WBEM_E_NOT_AVAILABLE, any such error should indicate
  358. the serious failure. WBEM_E_NOT_AVAILABLE is used to indicate that this template does
  359. not have any transaction related information, and therefore the template won't be able
  360. to have its own transaction context. This may or may not be an error
  361. Notes:
  362. Our transction token (Sce_TransactionToken) is not persisted. Our provider provides that
  363. instance on bases of the validility of our global BSTR varialbe g_bstrTranxID. Due to
  364. the global nature of g_bstrTranxID, its access should be protected from different threads.
  365. For that purpose, we use a global critical section g_CS, which is an instance of
  366. CCriticalSection. CCriticalSection is a very simple wrapper of CRITICAL_SECTION to simply
  367. its creation/destruction time.
  368. */
  369. HRESULT
  370. CTranxID::BeginTransaction (
  371. IN LPCWSTR pszStorePath
  372. )
  373. {
  374. //
  375. // we don't allow NULL path or zero length path
  376. //
  377. if (pszStorePath == NULL || *pszStorePath == L'\0')
  378. return WBEM_E_INVALID_PARAMETER;
  379. //
  380. // Inform the store that all its reading action will take place inside this template
  381. //
  382. CSceStore SceStore;
  383. HRESULT hr = SceStore.SetPersistPath(pszStorePath);
  384. if ( SUCCEEDED(hr) )
  385. {
  386. //
  387. // make sure that the template really exists!
  388. // Since passed-in parameter of store path may contain env varaibles, we need to use its
  389. // expanded path for file access!
  390. //
  391. DWORD dwAttrib = ::GetFileAttributes(SceStore.GetExpandedPath());
  392. //
  393. // if the file exist
  394. //
  395. if ( dwAttrib != -1 )
  396. {
  397. DWORD dwRead = 0;
  398. //
  399. // GetPropertyFromStore will allocate memory for the property read.
  400. // We are responsible to free it.
  401. //
  402. LPWSTR pszTranxID = NULL;
  403. //
  404. // Read the transaction id property, dwRead will contain the bytes read from the store.
  405. // Since we have no minimum length requirement, dwRead is ignored as long as something is read!
  406. //
  407. hr = SceStore.GetPropertyFromStore(SCEWMI_TRANSACTION_ID_CLASS, pTranxGuid, &pszTranxID, &dwRead);
  408. if (SUCCEEDED(hr))
  409. {
  410. //
  411. // To flag ourselves that a transaction is in place, we set our global
  412. // varialbe b_bstrTranxID to a valid value (non-empty means valid). Since it's
  413. // global, we need thread protection!
  414. //
  415. g_CS.Enter();
  416. g_bstrTranxID.Empty();
  417. g_bstrTranxID = pszTranxID;
  418. g_CS.Leave();
  419. }
  420. delete [] pszTranxID;
  421. }
  422. else
  423. {
  424. // indicate that the store doesn't exists
  425. hr = WBEM_E_NOT_AVAILABLE;
  426. }
  427. }
  428. return hr;
  429. }
  430. /*
  431. Routine Description:
  432. Name:
  433. static CTranxID::EndTransaction
  434. Functionality:
  435. Terminate the transaction. Unlike real transaction management, we don't
  436. have a commit function because without the user initiated the rollback, we
  437. can't do a rollback. Therefore, our "transaction" only has a begin and end
  438. to define its range of action.
  439. Virtual:
  440. no.
  441. Arguments:
  442. none
  443. Return Value:
  444. WBEM_NO_ERROR
  445. Notes:
  446. See CTranxID::BeginTransaction
  447. */
  448. HRESULT
  449. CTranxID::EndTransaction()
  450. {
  451. //
  452. // now the transaction is over, we will remove the Sce_TransactionToken instance.
  453. // We do that by invalidating our global variable g_bstrTranxID. An empty g_bstrTranxID
  454. // means it's invalid.
  455. //
  456. g_CS.Enter();
  457. g_bstrTranxID.Empty();
  458. g_CS.Leave();
  459. return WBEM_NO_ERROR;
  460. }
  461. /*
  462. Routine Description:
  463. Name:
  464. static CTranxID::SpawnTokenInstance
  465. Functionality:
  466. Caller gives us the transaction ID, we will make a WMI object (Sce_TransactionToken)
  467. to the caller.
  468. Virtual:
  469. no.
  470. Arguments:
  471. pNamespace - COM interface pointer to IWbemServices. This is the namespace.
  472. Can't be NULL.
  473. pszTranxID - caller supplied string representing the transactio id.
  474. pCtx - COM interface pointer to IWbemContext. Need to pass this around in
  475. various WMI calls.
  476. pSink - COM interface pointer to IWbemObjectSink. Used to notify WMI for newly
  477. created instance. Must not be NULL.
  478. Return Value:
  479. Success: it must return success code (use SUCCEEDED to test). It is
  480. not guaranteed to return WBEM_NO_ERROR (up to WMI)
  481. Failure: Various errors may occurs. Any such error should indicate failure to create the
  482. Sce_TransactionToken instance for the caller.
  483. Notes:
  484. Call this function with a transaction ID string will cause an instance of
  485. Sce_TransactionToken being created.
  486. */
  487. HRESULT
  488. CTranxID::SpawnTokenInstance (
  489. IN IWbemServices * pNamespace,
  490. IN LPCWSTR pszTranxID,
  491. IN IWbemContext * pCtx,
  492. IN IWbemObjectSink * pSink
  493. )
  494. {
  495. //
  496. // minimu requirement for the parameters:
  497. // (1) non-NULL namepsace
  498. // (2) a valid id string (length greater than 0)
  499. // (3) a valid sink (so that we can notify WMI)
  500. //
  501. if (pNamespace == NULL || pszTranxID == NULL || *pszTranxID == L'\0' || pSink == NULL)
  502. {
  503. return WBEM_E_INVALID_PARAMETER;
  504. }
  505. //
  506. // GetObject (WMI defined) requires a BSTR. We use a CComBSTR for auto releasing of memory
  507. //
  508. CComBSTR bstrClassName(SCEWMI_TRANSACTION_TOKEN_CLASS);
  509. //
  510. // What returned from pNamespace->GetObject can't be used to fill in properties.
  511. // It's only good for spawn a blank instance which can be used to fill in properties!
  512. // Some weird WMI protocol!
  513. //
  514. CComPtr<IWbemClassObject> srpSpawnObj;
  515. HRESULT hr = pNamespace->GetObject(bstrClassName, 0, NULL, &srpSpawnObj, NULL);
  516. //
  517. // This one will be good to fill in properties
  518. //
  519. CComPtr<IWbemClassObject> srpBlankObj;
  520. if (SUCCEEDED(hr))
  521. {
  522. hr = srpSpawnObj->SpawnInstance(0, &srpBlankObj);
  523. }
  524. if (SUCCEEDED(hr))
  525. {
  526. //
  527. // Use our property manager to fill in property for this newly spawned instance
  528. //
  529. CScePropertyMgr ScePropMgr;
  530. ScePropMgr.Attach(srpBlankObj);
  531. hr = ScePropMgr.PutProperty(pTranxGuid, pszTranxID);
  532. //
  533. // we never want to hand out a Sce_TransactionToken object without
  534. // its critical property of transaction id. So, only when we have successfully
  535. // put in that property will we inform WMI for the object.
  536. //
  537. if (SUCCEEDED(hr))
  538. {
  539. hr = pSink->Indicate(1, &srpBlankObj);
  540. }
  541. }
  542. return hr;
  543. }