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.

1735 lines
43 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. resource.c
  5. Abstract:
  6. Server side support for Cluster APIs dealing with resources
  7. Author:
  8. John Vert (jvert) 7-Mar-1996
  9. Revision History:
  10. --*/
  11. #include "apip.h"
  12. HRES_RPC
  13. s_ApiOpenResource(
  14. IN handle_t IDL_handle,
  15. IN LPCWSTR lpszResourceName,
  16. OUT error_status_t *Status
  17. )
  18. /*++
  19. Routine Description:
  20. Opens a handle to an existing resource object.
  21. Arguments:
  22. IDL_handle - RPC binding handle, not used.
  23. lpszResourceName - Supplies the name of the resource to open.
  24. Status - Returns any error that may occur.
  25. Return Value:
  26. A context handle to a resource object if successful
  27. NULL otherwise.
  28. --*/
  29. {
  30. HRES_RPC Resource;
  31. PAPI_HANDLE Handle;
  32. Handle = LocalAlloc(LMEM_FIXED, sizeof(API_HANDLE));
  33. if (Handle == NULL) {
  34. *Status = ERROR_NOT_ENOUGH_MEMORY;
  35. return(NULL);
  36. }
  37. Resource = OmReferenceObjectByName(ObjectTypeResource, lpszResourceName);
  38. if (Resource == NULL) {
  39. LocalFree(Handle);
  40. *Status = ERROR_RESOURCE_NOT_FOUND;
  41. ClRtlLogPrint(LOG_NOISE,
  42. "[API] s_ApiOpenResource: Resource %1!ws! not found, status = %2!u!...\n",
  43. lpszResourceName,
  44. *Status);
  45. return(NULL);
  46. }
  47. *Status = ERROR_SUCCESS;
  48. Handle->Type = API_RESOURCE_HANDLE;
  49. Handle->Resource = Resource;
  50. Handle->Flags = 0;
  51. InitializeListHead(&Handle->NotifyList);
  52. return(Handle);
  53. }
  54. HRES_RPC
  55. s_ApiCreateResource(
  56. IN HGROUP_RPC hGroup,
  57. IN LPCWSTR lpszResourceName,
  58. IN LPCWSTR lpszResourceType,
  59. IN DWORD dwFlags,
  60. OUT error_status_t *pStatus
  61. )
  62. /*++
  63. Routine Description:
  64. Creates a new resource object.
  65. Arguments:
  66. hGroup - Supplies the group the resource is to be created in.
  67. lpszResourceName - Supplies the name of the resource to create.
  68. lpszResourceType - Supplies the type of the resource.
  69. dwFlags - Supplies any optional flags.
  70. Status - Returns any error that may occur.
  71. Return Value:
  72. A context handle to a resource object if successful
  73. NULL otherwise.
  74. --*/
  75. {
  76. HRES_RPC Resource=NULL;
  77. PFM_GROUP Group;
  78. UUID Guid;
  79. DWORD Status = ERROR_SUCCESS;
  80. WCHAR *KeyName=NULL;
  81. HDMKEY Key=NULL;
  82. HDMKEY GroupKey=NULL;
  83. HDMKEY TypeKey = NULL;
  84. HDMKEY ParamKey;
  85. DWORD Disposition;
  86. DWORD pollIntervals = CLUSTER_RESOURCE_USE_DEFAULT_POLL_INTERVAL;
  87. PAPI_HANDLE Handle;
  88. PFM_RESTYPE ResType;
  89. DWORD dwPersistentState = 0;
  90. DWORD dwClusterHighestVersion;
  91. if (ApiState != ApiStateOnline)
  92. {
  93. *pStatus = ERROR_SHARING_PAUSED;
  94. return(NULL);
  95. }
  96. if ((hGroup == NULL) || (((PAPI_HANDLE)hGroup)->Type != API_GROUP_HANDLE))
  97. {
  98. *pStatus = ERROR_INVALID_HANDLE;
  99. return(NULL);
  100. }
  101. Group = ((PAPI_HANDLE)hGroup)->Group;
  102. //
  103. // Check for bogus flags.
  104. //
  105. if (dwFlags & ~CLUSTER_RESOURCE_VALID_FLAGS)
  106. {
  107. *pStatus = ERROR_INVALID_PARAMETER;
  108. return(NULL);
  109. }
  110. Handle = LocalAlloc(LMEM_FIXED, sizeof(API_HANDLE));
  111. if (Handle == NULL)
  112. {
  113. *pStatus = ERROR_NOT_ENOUGH_MEMORY;
  114. return(NULL);
  115. }
  116. //
  117. // Chittur Subbaraman (chitturs) - 1/30/2000
  118. //
  119. // If we are dealing with the mixed mode cluster, do the
  120. // registry updates right here since the GUM handler won't do it.
  121. //
  122. NmGetClusterOperationalVersion( &dwClusterHighestVersion,
  123. NULL,
  124. NULL );
  125. //
  126. // Open the resource type key. This validates that the specified type exists.
  127. //
  128. TypeKey = DmOpenKey(DmResourceTypesKey,
  129. lpszResourceType,
  130. KEY_READ);
  131. if (TypeKey == NULL)
  132. {
  133. Status = GetLastError();
  134. goto error_exit;
  135. }
  136. retry:
  137. //
  138. // Create a GUID for this resource.
  139. //
  140. Status = UuidCreate(&Guid);
  141. if (Status != RPC_S_OK)
  142. {
  143. goto error_exit;
  144. }
  145. Status = UuidToString(&Guid, &KeyName);
  146. if (Status != RPC_S_OK)
  147. {
  148. goto error_exit;
  149. }
  150. ClRtlLogPrint(LOG_NOISE,
  151. "[API] Creating resource %1!ws! <%2!ws!> (%3!ws!)\n",
  152. lpszResourceType,
  153. lpszResourceName,
  154. KeyName);
  155. if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
  156. NT51_MAJOR_VERSION )
  157. {
  158. //
  159. // Create the new resource key.
  160. //
  161. Key = DmCreateKey(DmResourcesKey,
  162. KeyName,
  163. 0,
  164. KEY_READ | KEY_WRITE,
  165. NULL,
  166. &Disposition);
  167. if (Key == NULL)
  168. {
  169. Status = GetLastError();
  170. goto error_exit;
  171. }
  172. if (Disposition != REG_CREATED_NEW_KEY)
  173. {
  174. ClRtlLogPrint(LOG_CRITICAL,
  175. "[API] ApiCreateResource generated GUID %1!ws! that already existed! This is impossible.\n",
  176. KeyName);
  177. DmCloseKey(Key);
  178. RpcStringFree(&KeyName);
  179. goto retry;
  180. }
  181. CL_ASSERT(Disposition == REG_CREATED_NEW_KEY);
  182. //
  183. // Set the resource's name in the registry
  184. //
  185. Status = DmSetValue(Key,
  186. CLUSREG_NAME_RES_NAME,
  187. REG_SZ,
  188. (CONST BYTE *)lpszResourceName,
  189. (lstrlenW(lpszResourceName)+1)*sizeof(WCHAR));
  190. if (Status != ERROR_SUCCESS)
  191. {
  192. goto error_exit;
  193. }
  194. //
  195. // Set the resource's type in the registry
  196. // Note we reference the resource type and use its ID
  197. // so that the case is correct.
  198. //
  199. ResType = OmReferenceObjectById(ObjectTypeResType, lpszResourceType);
  200. if ( ResType == NULL )
  201. {
  202. //
  203. // Should not happen normally since we checked if the type existed in
  204. // the registry.
  205. //
  206. Status = ERROR_RESOURCE_TYPE_NOT_FOUND;
  207. goto error_exit;
  208. }
  209. lpszResourceType = OmObjectId(ResType);
  210. OmDereferenceObject(ResType);
  211. Status = DmSetValue(Key,
  212. CLUSREG_NAME_RES_TYPE,
  213. REG_SZ,
  214. (CONST BYTE *)lpszResourceType,
  215. (lstrlenW(lpszResourceType)+1)*sizeof(WCHAR));
  216. if (Status != ERROR_SUCCESS)
  217. {
  218. goto error_exit;
  219. }
  220. //
  221. // Set the resource's poll intervals in the registry.
  222. //
  223. Status = DmSetValue(Key,
  224. CLUSREG_NAME_RES_LOOKS_ALIVE,
  225. REG_DWORD,
  226. (CONST BYTE *)&pollIntervals,
  227. 4);
  228. if (Status != ERROR_SUCCESS)
  229. {
  230. goto error_exit;
  231. }
  232. Status = DmSetValue(Key,
  233. CLUSREG_NAME_RES_IS_ALIVE,
  234. REG_DWORD,
  235. (CONST BYTE *)&pollIntervals,
  236. 4);
  237. if (Status != ERROR_SUCCESS)
  238. {
  239. goto error_exit;
  240. }
  241. //
  242. // If this resource should be started in a separate monitor, set that
  243. // parameter now.
  244. //
  245. if (dwFlags & CLUSTER_RESOURCE_SEPARATE_MONITOR)
  246. {
  247. DWORD SeparateMonitor = 1;
  248. Status = DmSetValue(Key,
  249. CLUSREG_NAME_RES_SEPARATE_MONITOR,
  250. REG_DWORD,
  251. (CONST BYTE *)&SeparateMonitor,
  252. sizeof(SeparateMonitor));
  253. if (Status != ERROR_SUCCESS)
  254. {
  255. goto error_exit;
  256. }
  257. }
  258. //
  259. // Create a Parameters key for the resource.
  260. //
  261. ParamKey = DmCreateKey(Key,
  262. CLUSREG_KEYNAME_PARAMETERS,
  263. 0,
  264. KEY_READ,
  265. NULL,
  266. &Disposition);
  267. if (ParamKey == NULL)
  268. {
  269. CL_LOGFAILURE(GetLastError());
  270. } else
  271. {
  272. DmCloseKey(ParamKey);
  273. }
  274. GroupKey = DmOpenKey(DmGroupsKey, OmObjectId(Group), KEY_READ | KEY_WRITE);
  275. if (GroupKey == NULL)
  276. {
  277. Status = GetLastError();
  278. goto error_exit;
  279. }
  280. //
  281. // Chittur Subbaraman (chitturs) - 5/25/99
  282. //
  283. // Make sure you set the persistent state of the resource to
  284. // ClusterResourceOffline before you create the resource. If
  285. // this is not done, if you create a resource in a group which
  286. // is online, the group's persistent state value (i.e., 1 in
  287. // this case) is inherited by the resource in FmpQueryResourceInfo
  288. // (only the memory state is set and not the registry state and
  289. // this was a problem as well) and if you move such a group to
  290. // another node, it will bring the newly created resource online.
  291. //
  292. Status = DmSetValue( Key,
  293. CLUSREG_NAME_RES_PERSISTENT_STATE,
  294. REG_DWORD,
  295. ( CONST BYTE * )&dwPersistentState,
  296. sizeof( DWORD ) );
  297. if ( Status != ERROR_SUCCESS )
  298. {
  299. goto error_exit;
  300. }
  301. }
  302. Resource = FmCreateResource(Group, KeyName, lpszResourceName, lpszResourceType, dwFlags);
  303. if (Resource == NULL)
  304. {
  305. Status = GetLastError();
  306. //
  307. // HACKHACK: Looks like this retry loop was coded up to retry in case a new resource got the
  308. // GUID of an existing resource. FmpUpdateCreateResource returns this error in case of a conflict.
  309. // It is best to get rid of it since we should assume UUidCreate generates a unique ID that
  310. // doesn't conflict with anything else. Else, it is a bug in that API. We should not be
  311. // masking that.
  312. //
  313. if (Status == ERROR_ALREADY_EXISTS)
  314. {
  315. RpcStringFree(&KeyName);
  316. goto retry;
  317. }
  318. goto error_exit;
  319. }
  320. if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
  321. NT51_MAJOR_VERSION )
  322. {
  323. //
  324. // Add the resource to the Contains value of the specified group.
  325. //
  326. Status = DmAppendToMultiSz(GroupKey,
  327. CLUSREG_NAME_GRP_CONTAINS,
  328. KeyName);
  329. if (Status != ERROR_SUCCESS)
  330. {
  331. //
  332. // BUGBUG John Vert (jvert) 3-May-1996
  333. // Need to delete this from the FM!
  334. //
  335. OmDereferenceObject(Resource);
  336. Resource = NULL;
  337. }
  338. }
  339. error_exit:
  340. if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
  341. NT51_MAJOR_VERSION )
  342. {
  343. if (Key != NULL)
  344. {
  345. if (Status != ERROR_SUCCESS)
  346. {
  347. //
  348. // Try and cleanup the key we just created.
  349. //
  350. DmDeleteKey(Key, CLUSREG_KEYNAME_PARAMETERS);
  351. DmDeleteKey(DmResourcesKey, KeyName);
  352. }
  353. DmCloseKey(Key);
  354. }
  355. if (GroupKey != NULL)
  356. {
  357. DmCloseKey(GroupKey);
  358. }
  359. }
  360. if (TypeKey != NULL)
  361. {
  362. DmCloseKey(TypeKey);
  363. }
  364. if (KeyName != NULL)
  365. {
  366. RpcStringFree(&KeyName);
  367. }
  368. *pStatus = Status;
  369. if (Status != ERROR_SUCCESS)
  370. {
  371. LocalFree(Handle);
  372. return(NULL);
  373. }
  374. CL_ASSERT(Resource != NULL);
  375. Handle->Type = API_RESOURCE_HANDLE;
  376. Handle->Resource = Resource;
  377. Handle->Flags = 0;
  378. InitializeListHead(&Handle->NotifyList);
  379. return(Handle);
  380. }
  381. error_status_t
  382. s_ApiDeleteResource(
  383. IN HRES_RPC hResource
  384. )
  385. /*++
  386. Routine Description:
  387. Deletes the specified cluster resource from the group. The resource
  388. must have no other resources dependent on it.
  389. Arguments:
  390. hResource - Supplies the cluster resource to be deleted.
  391. Return Value:
  392. If the function succeeds, the return value is ERROR_SUCCESS.
  393. If the function fails, the return value is an error value.
  394. --*/
  395. {
  396. PFM_RESOURCE Resource;
  397. DWORD Status;
  398. HDMKEY Key;
  399. HDMKEY GroupKey;
  400. DWORD dwClusterHighestVersion;
  401. API_CHECK_INIT();
  402. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  403. //
  404. // Chittur Subbaraman (chitturs) - 09/07/2000
  405. //
  406. // If we are dealing with a Whistler-Win2K cluster, do the
  407. // registry updates right here since the GUM handler won't do it.
  408. //
  409. NmGetClusterOperationalVersion( &dwClusterHighestVersion,
  410. NULL,
  411. NULL );
  412. Status = FmDeleteResource(Resource);
  413. if ( ( Status == ERROR_SUCCESS ) &&
  414. ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
  415. NT51_MAJOR_VERSION ) ) {
  416. Status = DmDeleteTree(DmResourcesKey,OmObjectId(Resource));
  417. if ( (Status != ERROR_SUCCESS) &&
  418. (Status != ERROR_FILE_NOT_FOUND) ) {
  419. CL_LOGFAILURE( Status );
  420. return(Status);
  421. }
  422. GroupKey = DmOpenKey(DmGroupsKey,
  423. OmObjectId(Resource->Group),
  424. KEY_READ | KEY_SET_VALUE);
  425. if (GroupKey != NULL) {
  426. DmRemoveFromMultiSz(GroupKey,
  427. CLUSREG_NAME_GRP_CONTAINS,
  428. OmObjectId(Resource));
  429. DmCloseKey(GroupKey);
  430. }
  431. }
  432. return(Status);
  433. }
  434. error_status_t
  435. s_ApiCloseResource(
  436. IN OUT HRES_RPC *phResource
  437. )
  438. /*++
  439. Routine Description:
  440. Closes an open resource context handle.
  441. Arguments:
  442. Resource - Supplies a pointer to the HRES_RPC to be closed.
  443. Returns NULL
  444. Return Value:
  445. None.
  446. --*/
  447. {
  448. PFM_RESOURCE Resource;
  449. PAPI_HANDLE Handle;
  450. VALIDATE_RESOURCE(Resource, *phResource);
  451. Handle = (PAPI_HANDLE)*phResource;
  452. ApipRundownNotify(Handle);
  453. OmDereferenceObject(Resource);
  454. LocalFree(*phResource);
  455. *phResource = NULL;
  456. return(ERROR_SUCCESS);
  457. }
  458. VOID
  459. HRES_RPC_rundown(
  460. IN HRES_RPC Resource
  461. )
  462. /*++
  463. Routine Description:
  464. RPC rundown procedure for a HRES_RPC. Just closes the handle.
  465. Arguments:
  466. Resource - Supplies the HRES_RPC that is to be rundown.
  467. Return Value:
  468. None.
  469. --*/
  470. {
  471. s_ApiCloseResource(&Resource);
  472. }
  473. error_status_t
  474. s_ApiGetResourceState(
  475. IN HRES_RPC hResource,
  476. OUT DWORD *lpState,
  477. OUT LPWSTR *lpNodeId,
  478. OUT LPWSTR *lpGroupName
  479. )
  480. /*++
  481. Routine Description:
  482. Returns the current state of the specified resource.
  483. Arguments:
  484. hResource - Supplies the resource whose state is to be returned.
  485. lpState - Returns the current state of the resource
  486. lpNodeId - Returns the Id of the node where the resource is currently online
  487. lpGroupName - Returns the name of the group the the resource is a member of
  488. Return Value:
  489. ERROR_SUCCESS if successful
  490. Win32 error code otherwise
  491. --*/
  492. {
  493. PFM_RESOURCE Resource;
  494. LPWSTR NodeId;
  495. DWORD IdLength;
  496. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  497. IdLength = MAX_COMPUTERNAME_LENGTH+1;
  498. NodeId = MIDL_user_allocate(IdLength*sizeof(WCHAR));
  499. if (NodeId == NULL) {
  500. return(ERROR_NOT_ENOUGH_MEMORY);
  501. }
  502. *lpState = FmGetResourceState( Resource,
  503. NodeId,
  504. &IdLength);
  505. if ( *lpState == ClusterResourceStateUnknown ) {
  506. MIDL_user_free(NodeId);
  507. return(GetLastError());
  508. }
  509. *lpNodeId = NodeId;
  510. *lpGroupName = ApipGetObjectName(Resource->Group);
  511. return(ERROR_SUCCESS);
  512. }
  513. error_status_t
  514. s_ApiSetResourceName(
  515. IN HRES_RPC hResource,
  516. IN LPCWSTR lpszResourceName
  517. )
  518. /*++
  519. Routine Description:
  520. Sets the new friendly name of a resource.
  521. Arguments:
  522. hResource - Supplies the resource whose name is to be set.
  523. lpszResourceName - Supplies the new name of hResource
  524. Return Value:
  525. ERROR_SUCCESS if successful
  526. Win32 error code otherwise
  527. --*/
  528. {
  529. PFM_RESOURCE Resource;
  530. DWORD Status;
  531. API_CHECK_INIT();
  532. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  533. //
  534. // Tell the FM about the new name. If it is OK with the
  535. // FM, go ahead and update the registry.
  536. //
  537. Status = FmSetResourceName(Resource,
  538. lpszResourceName);
  539. return(Status);
  540. }
  541. error_status_t
  542. s_ApiGetResourceId(
  543. IN HRES_RPC hResource,
  544. OUT LPWSTR *pGuid
  545. )
  546. /*++
  547. Routine Description:
  548. Returns the unique identifier (GUID) for a resource.
  549. Arguments:
  550. hResource - Supplies the resource whose identifer is to be returned
  551. pGuid - Returns the unique identifier. This memory must be freed on the
  552. client side.
  553. Return Value:
  554. ERROR_SUCCESS if successful
  555. Win32 error code otherwise.
  556. --*/
  557. {
  558. PFM_RESOURCE Resource;
  559. DWORD NameLen;
  560. LPCWSTR Name;
  561. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  562. Name = OmObjectId(Resource);
  563. NameLen = (lstrlenW(Name)+1)*sizeof(WCHAR);
  564. *pGuid = MIDL_user_allocate(NameLen);
  565. if (*pGuid == NULL) {
  566. return(ERROR_NOT_ENOUGH_MEMORY);
  567. }
  568. CopyMemory(*pGuid, Name, NameLen);
  569. return(ERROR_SUCCESS);
  570. }
  571. error_status_t
  572. s_ApiGetResourceType(
  573. IN HRES_RPC hResource,
  574. OUT LPWSTR *lpszResourceType
  575. )
  576. /*++
  577. Routine Description:
  578. Returns the resource type for a resource.
  579. Arguments:
  580. hResource - Supplies the resource whose identifer is to be returned
  581. lpszResourceType - Returns the resource type name. This memory must be
  582. freed on the client side.
  583. Return Value:
  584. ERROR_SUCCESS if successful
  585. Win32 error code otherwise.
  586. --*/
  587. {
  588. PFM_RESOURCE Resource;
  589. DWORD NameLen;
  590. LPCWSTR Name;
  591. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  592. if ( Resource->Type == NULL ) {
  593. return(ERROR_INVALID_STATE);
  594. }
  595. Name = OmObjectId(Resource->Type);
  596. NameLen = (lstrlenW(Name)+1)*sizeof(WCHAR);
  597. *lpszResourceType = MIDL_user_allocate(NameLen);
  598. if (*lpszResourceType == NULL) {
  599. return(ERROR_NOT_ENOUGH_MEMORY);
  600. }
  601. CopyMemory(*lpszResourceType, Name, NameLen);
  602. return(ERROR_SUCCESS);
  603. }
  604. DWORD
  605. s_ApiOnlineResource(
  606. IN HRES_RPC hResource
  607. )
  608. /*++
  609. Routine Description:
  610. Brings a resource and all its dependencies online
  611. Arguments:
  612. hResource - Supplies the resource to be brought online
  613. Return Value:
  614. ERROR_SUCCESS if successful
  615. Win32 error code otherwise
  616. --*/
  617. {
  618. PFM_RESOURCE Resource;
  619. API_CHECK_INIT();
  620. VALIDATE_RESOURCE(Resource, hResource);
  621. return(FmOnlineResource(Resource));
  622. }
  623. DWORD
  624. s_ApiFailResource(
  625. IN HRES_RPC hResource
  626. )
  627. /*++
  628. Routine Description:
  629. Initiates a resource failure. The specified resource is treated as failed.
  630. This causes the cluster to initiate the same failover process that would
  631. result if the resource actually failed.
  632. Arguments:
  633. hResource - Supplies the resource to be failed over
  634. Return Value:
  635. ERROR_SUCCESS if successful
  636. Win32 error code otherwise
  637. --*/
  638. {
  639. PFM_RESOURCE Resource;
  640. API_CHECK_INIT();
  641. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  642. return(FmFailResource(Resource));
  643. }
  644. DWORD
  645. s_ApiOfflineResource(
  646. IN HRES_RPC hResource
  647. )
  648. /*++
  649. Routine Description:
  650. Brings a resource and all its dependents offline
  651. Arguments:
  652. hResource - Supplies the resource to be brought offline
  653. Return Value:
  654. ERROR_SUCCESS if successful
  655. Win32 error code otherwise
  656. --*/
  657. {
  658. PFM_RESOURCE Resource;
  659. API_CHECK_INIT();
  660. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  661. return(FmOfflineResource(Resource));
  662. }
  663. error_status_t
  664. s_ApiAddResourceDependency(
  665. IN HRES_RPC hResource,
  666. IN HRES_RPC hDependsOn
  667. )
  668. /*++
  669. Routine Description:
  670. Adds a dependency relationship to a given resource. Both
  671. resources must be in the same group.
  672. Arguments:
  673. hResource - Supplies the resource which is dependent.
  674. hDependsOn - Supplies the resource that hResource depends on.
  675. Return Value:
  676. ERROR_SUCCESS if successful.
  677. Win32 error code otherwise.
  678. --*/
  679. {
  680. PFM_RESOURCE Resource;
  681. PFM_RESOURCE DependsOn;
  682. DWORD Status;
  683. HDMKEY ResKey;
  684. API_CHECK_INIT();
  685. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  686. VALIDATE_RESOURCE_EXISTS(DependsOn, hDependsOn);
  687. //
  688. // Call the FM to create the dependency relationship.
  689. //
  690. Status = FmAddResourceDependency(Resource, DependsOn);
  691. if (Status == ERROR_SUCCESS) {
  692. //
  693. // Add the dependency information to the cluster database.
  694. //
  695. ResKey = DmOpenKey(DmResourcesKey,
  696. OmObjectId(Resource),
  697. KEY_READ | KEY_SET_VALUE);
  698. if (ResKey == NULL) {
  699. Status = GetLastError();
  700. CL_LOGFAILURE(Status);
  701. } else {
  702. Status = DmAppendToMultiSz(ResKey,
  703. CLUSREG_NAME_RES_DEPENDS_ON,
  704. OmObjectId(DependsOn));
  705. DmCloseKey(ResKey);
  706. }
  707. if (Status != ERROR_SUCCESS) {
  708. FmRemoveResourceDependency(Resource, DependsOn);
  709. }
  710. }
  711. return(Status);
  712. }
  713. error_status_t
  714. s_ApiRemoveResourceDependency(
  715. IN HRES_RPC hResource,
  716. IN HRES_RPC hDependsOn
  717. )
  718. /*++
  719. Routine Description:
  720. Removes a dependency relationship to a given resource. Both
  721. resources must be in the same group.
  722. Arguments:
  723. hResource - Supplies the resource which is dependent.
  724. hDependsOn - Supplies the resource that hResource depends on.
  725. Return Value:
  726. ERROR_SUCCESS if successful.
  727. Win32 error code otherwise.
  728. --*/
  729. {
  730. PFM_RESOURCE Resource;
  731. PFM_RESOURCE DependsOn;
  732. DWORD Status;
  733. HDMKEY ResKey;
  734. API_CHECK_INIT();
  735. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  736. VALIDATE_RESOURCE_EXISTS(DependsOn, hDependsOn);
  737. //
  738. // If the resources are not in the same group, fail the
  739. // call. Also fail if some one tries to make a resource
  740. // dependent upon itself.
  741. //
  742. if ((Resource->Group != DependsOn->Group) ||
  743. (Resource == DependsOn)) {
  744. return(ERROR_DEPENDENCY_NOT_FOUND);
  745. }
  746. //
  747. // Remove the dependency from the registry database.
  748. //
  749. ResKey = DmOpenKey(DmResourcesKey,
  750. OmObjectId(Resource),
  751. KEY_READ | KEY_SET_VALUE);
  752. if (ResKey == NULL) {
  753. Status = GetLastError();
  754. CL_LOGFAILURE(Status);
  755. } else {
  756. Status = DmRemoveFromMultiSz(ResKey,
  757. CLUSREG_NAME_RES_DEPENDS_ON,
  758. OmObjectId(DependsOn));
  759. DmCloseKey(ResKey);
  760. }
  761. if (Status == ERROR_SUCCESS) {
  762. //
  763. // Call the FM to remove the dependency relationship.
  764. //
  765. Status = FmRemoveResourceDependency(Resource, DependsOn);
  766. } else if (Status == ERROR_FILE_NOT_FOUND) {
  767. //
  768. // Map this expected error to something a little more reasonable.
  769. //
  770. Status = ERROR_DEPENDENCY_NOT_FOUND;
  771. }
  772. return(Status);
  773. }
  774. error_status_t
  775. s_ApiCanResourceBeDependent(
  776. IN HRES_RPC hResource,
  777. IN HRES_RPC hResourceDependent
  778. )
  779. /*++
  780. Routine Description:
  781. Determines if the resource identified by hResource can depend on hResourceDependent.
  782. In order for this to be true, both resources must be members of the same group and
  783. the resource identified by hResourceDependent cannot depend on the resource identified
  784. by hResource, whether directly or indirectly.
  785. Arguments:
  786. hResource - Supplies a handle to the resource to be dependent.
  787. hResourceDependent - Supplies a handle to the resource on which
  788. the resource identified by hResource can depend.
  789. Return Value:
  790. If the resource identified by hResource can depend on the resource
  791. identified by hResourceDependent, the return value is ERROR_SUCCESS.
  792. Otherwise, the return value is ERROR_DEPENDENCY_ALREADY_EXISTS.
  793. --*/
  794. {
  795. PFM_RESOURCE Resource;
  796. PFM_RESOURCE ResourceDependent;
  797. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  798. VALIDATE_RESOURCE_EXISTS(ResourceDependent, hResourceDependent);
  799. if (Resource == ResourceDependent) {
  800. //
  801. // The caller is confused and is trying to make something
  802. // depend on itself.
  803. //
  804. return(ERROR_DEPENDENCY_ALREADY_EXISTS);
  805. }
  806. if (Resource->Group != ResourceDependent->Group) {
  807. //
  808. // The caller is confused and is trying to make something
  809. // depend on a resource in another group.
  810. //
  811. return(ERROR_DEPENDENCY_ALREADY_EXISTS);
  812. }
  813. //
  814. // If the dependent is a quorum resource, you can't add a dependency.
  815. //
  816. if ( Resource->QuorumResource ) {
  817. return ( ERROR_DEPENDENCY_NOT_ALLOWED );
  818. }
  819. if (FmDependentResource(ResourceDependent, Resource, FALSE)) {
  820. return(ERROR_DEPENDENCY_ALREADY_EXISTS);
  821. } else {
  822. //
  823. // Finally check to make sure an immediate dependency does
  824. // not already exist.
  825. //
  826. if (FmDependentResource(Resource, ResourceDependent, TRUE)) {
  827. return(ERROR_DEPENDENCY_ALREADY_EXISTS);
  828. } else {
  829. return(ERROR_SUCCESS);
  830. }
  831. }
  832. }
  833. error_status_t
  834. s_ApiCreateResEnum(
  835. IN HRES_RPC hResource,
  836. IN DWORD dwType,
  837. OUT PENUM_LIST *ReturnEnum
  838. )
  839. /*++
  840. Routine Description:
  841. Enumerates all the specified resource properties and returns the
  842. list of objects to the caller. The client-side is responsible
  843. for freeing the allocated memory.
  844. Arguments:
  845. hResource - Supplies the resource whose properties are to be
  846. enumerated.
  847. dwType - Supplies the type of properties to be enumerated.
  848. ReturnEnum - Returns the requested objects.
  849. Return Value:
  850. ERROR_SUCCESS if successful
  851. Win32 error code otherwise.
  852. --*/
  853. {
  854. DWORD Status;
  855. DWORD Allocated = 0;
  856. PENUM_LIST Enum = NULL;
  857. DWORD i;
  858. DWORD Result;
  859. PFM_RESOURCE Resource;
  860. PFM_RESOURCE Target;
  861. PNM_NODE Node;
  862. LPWSTR RealName;
  863. if (dwType & ~CLUSTER_RESOURCE_ENUM_ALL) {
  864. return(ERROR_INVALID_PARAMETER);
  865. }
  866. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  867. Allocated = INITIAL_ENUM_LIST_ALLOCATION;
  868. Enum = MIDL_user_allocate(ENUM_SIZE(Allocated));
  869. if (Enum == NULL) {
  870. Status = ERROR_NOT_ENOUGH_MEMORY;
  871. goto ErrorExit;
  872. }
  873. Enum->EntryCount = 0;
  874. //
  875. // Enumerate all dependencies.
  876. //
  877. if (dwType & CLUSTER_RESOURCE_ENUM_DEPENDS) {
  878. i=0;
  879. do {
  880. Result = FmEnumResourceDependent(Resource,
  881. i,
  882. &Target);
  883. if (Result == ERROR_SUCCESS) {
  884. RealName = ApipGetObjectName( Target );
  885. if (RealName != NULL) {
  886. ApipAddToEnum(&Enum,
  887. &Allocated,
  888. RealName,
  889. CLUSTER_RESOURCE_ENUM_DEPENDS);
  890. MIDL_user_free(RealName);
  891. }
  892. OmDereferenceObject(Target);
  893. ++i;
  894. }
  895. } while ( Result == ERROR_SUCCESS );
  896. }
  897. //
  898. // Enumerate all dependents
  899. //
  900. if (dwType & CLUSTER_RESOURCE_ENUM_PROVIDES) {
  901. i=0;
  902. do {
  903. Result = FmEnumResourceProvider(Resource,
  904. i,
  905. &Target);
  906. if (Result == ERROR_SUCCESS) {
  907. RealName = ApipGetObjectName( Target );
  908. if (RealName != NULL) {
  909. ApipAddToEnum(&Enum,
  910. &Allocated,
  911. RealName,
  912. CLUSTER_RESOURCE_ENUM_PROVIDES);
  913. MIDL_user_free(RealName);
  914. }
  915. OmDereferenceObject(Target);
  916. ++i;
  917. }
  918. } while ( Result == ERROR_SUCCESS );
  919. }
  920. //
  921. // Enumerate all possible nodes
  922. //
  923. if (dwType & CLUSTER_RESOURCE_ENUM_NODES) {
  924. i=0;
  925. do {
  926. Result = FmEnumResourceNode(Resource,
  927. i,
  928. &Node);
  929. if (Result == ERROR_SUCCESS) {
  930. RealName = (LPWSTR)OmObjectName( Node );
  931. if (RealName != NULL) {
  932. ApipAddToEnum(&Enum,
  933. &Allocated,
  934. RealName,
  935. CLUSTER_RESOURCE_ENUM_NODES);
  936. }
  937. OmDereferenceObject(Node);
  938. ++i;
  939. }
  940. } while ( Result == ERROR_SUCCESS );
  941. }
  942. *ReturnEnum = Enum;
  943. return(ERROR_SUCCESS);
  944. ErrorExit:
  945. if (Enum != NULL) {
  946. MIDL_user_free(Enum);
  947. }
  948. *ReturnEnum = NULL;
  949. return(Status);
  950. }
  951. error_status_t
  952. s_ApiAddResourceNode(
  953. IN HRES_RPC hResource,
  954. IN HNODE_RPC hNode
  955. )
  956. /*++
  957. Routine Description:
  958. Adds a node to the list of nodes where the specified resource
  959. can be brought online.
  960. Arguments:
  961. hResource - Supplies the resource whose list of possible nodes is
  962. to be modified.
  963. hNode - Supplies the node to be added to the resource's list.
  964. Return Value:
  965. ERROR_SUCCESS if successful
  966. Win32 error code otherwise
  967. --*/
  968. {
  969. PFM_RESOURCE Resource;
  970. PNM_NODE Node;
  971. DWORD Status;
  972. DWORD dwUserModified;
  973. API_CHECK_INIT();
  974. VALIDATE_NODE(Node, hNode);
  975. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  976. //
  977. // Call the FM to do the real work.
  978. //
  979. Status = FmChangeResourceNode(Resource, Node, TRUE);
  980. if (Status != ERROR_SUCCESS) {
  981. return(Status);
  982. }
  983. //
  984. // BUGBUG: What are the consequences of DmSetValue failing ?
  985. //
  986. //write out the fact that the user has explicitly set the
  987. //resource possible node list
  988. //
  989. dwUserModified = 1;
  990. ClRtlLogPrint(LOG_NOISE,
  991. "[API] s_ApiAddResourceNode: Setting UserModifiedPossibleNodeList key for resource %1!ws! \r\n",
  992. OmObjectId(Resource));
  993. DmSetValue( Resource->RegistryKey,
  994. CLUSREG_NAME_RES_USER_MODIFIED_POSSIBLE_LIST,
  995. REG_DWORD,
  996. (LPBYTE)&dwUserModified,
  997. sizeof(DWORD));
  998. return(Status);
  999. }
  1000. error_status_t
  1001. s_ApiRemoveResourceNode(
  1002. IN HRES_RPC hResource,
  1003. IN HNODE_RPC hNode
  1004. )
  1005. /*++
  1006. Routine Description:
  1007. Removes a node from the list of nodes that can host the
  1008. specified resource. The resource must not be currently
  1009. online on the specified node.
  1010. Arguments:
  1011. hResource - Supplies the resource whose list of possible nodes is
  1012. to be modified.
  1013. hNode - Supplies the node to be removed from the resource's list.
  1014. Return Value:
  1015. ERROR_SUCCESS if successful
  1016. Win32 error code otherwise
  1017. --*/
  1018. {
  1019. PFM_RESOURCE Resource;
  1020. PNM_NODE Node;
  1021. DWORD Status;
  1022. DWORD dwUserModified;
  1023. API_CHECK_INIT();
  1024. VALIDATE_NODE(Node, hNode);
  1025. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  1026. //
  1027. // Call the FM to do the real work.
  1028. //
  1029. Status = FmChangeResourceNode(Resource, Node, FALSE);
  1030. if (Status != ERROR_SUCCESS) {
  1031. return(Status);
  1032. }
  1033. //
  1034. // BUGBUG: What are the consequences of DmSetValue failing.
  1035. //
  1036. //write out the fact that the user has explicitly set the
  1037. //resource possible node list
  1038. //
  1039. dwUserModified = 1;
  1040. ClRtlLogPrint(LOG_NOISE,
  1041. "[API] s_ApiRemoveResourceNode: Setting UserModifiedPossibleNodeList key for resource %1!ws! \r\n",
  1042. OmObjectId(Resource));
  1043. DmSetValue( Resource->RegistryKey,
  1044. CLUSREG_NAME_RES_USER_MODIFIED_POSSIBLE_LIST,
  1045. REG_DWORD,
  1046. (LPBYTE)&dwUserModified,
  1047. sizeof(DWORD));
  1048. //SS: moved the write to the registry settings to the fm
  1049. // layer as well, this way it is truly transactional
  1050. return(Status);
  1051. }
  1052. error_status_t
  1053. s_ApiCreateResourceType(
  1054. IN handle_t IDL_handle,
  1055. IN LPCWSTR lpszTypeName,
  1056. IN LPCWSTR lpszDisplayName,
  1057. IN LPCWSTR lpszDllName,
  1058. IN DWORD dwLooksAlive,
  1059. IN DWORD dwIsAlive
  1060. )
  1061. /*++
  1062. Routine Description:
  1063. Creates a new resource type in the cluster. Note that this API only
  1064. defines the resource type in the cluster registry and registers the
  1065. resource type with the cluster service. The calling program is
  1066. responsible for installing the resource type DLL on each node in the
  1067. cluster.
  1068. Arguments:
  1069. IDL_handle - RPC binding handle, not used.
  1070. lpszResourceTypeName - Supplies the new resource types name. The
  1071. specified name must be unique within the cluster.
  1072. lpszDisplayName - Supplies the display name for the new resource
  1073. type. While lpszResourceTypeName should uniquely identify the
  1074. resource type on all clusters, the lpszDisplayName should be
  1075. a localized friendly name for the resource, suitable for displaying
  1076. to administrators
  1077. lpszResourceTypeDll - Supplies the name of the new resource types DLL.
  1078. dwLooksAlive - Supplies the default LooksAlive poll interval
  1079. for the new resource type in milliseconds.
  1080. dwIsAlive - Supplies the default IsAlive poll interval for
  1081. the new resource type in milliseconds.
  1082. Return Value:
  1083. ERROR_SUCCESS if successful
  1084. Win32 error code otherwise
  1085. --*/
  1086. {
  1087. DWORD Status;
  1088. HDMKEY TypeKey = NULL;
  1089. DWORD Disposition;
  1090. DWORD dwClusterHighestVersion;
  1091. //
  1092. // Chittur Subbaraman (chitturs) - 2/8/2000
  1093. //
  1094. // If we are dealing with the mixed mode cluster, do the
  1095. // registry updates right here since the GUM handler won't do it.
  1096. //
  1097. NmGetClusterOperationalVersion( &dwClusterHighestVersion,
  1098. NULL,
  1099. NULL );
  1100. if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
  1101. NT51_MAJOR_VERSION ) {
  1102. //
  1103. // Add the resource information to the registry. If the key does not already
  1104. // exist, then the name is unique and we can go ahead and call the FM to
  1105. // create the actual resource type object.
  1106. //
  1107. TypeKey = DmCreateKey(DmResourceTypesKey,
  1108. lpszTypeName,
  1109. 0,
  1110. KEY_READ | KEY_WRITE,
  1111. NULL,
  1112. &Disposition);
  1113. if (TypeKey == NULL) {
  1114. return(GetLastError());
  1115. }
  1116. if (Disposition != REG_CREATED_NEW_KEY) {
  1117. DmCloseKey(TypeKey);
  1118. return(ERROR_ALREADY_EXISTS);
  1119. }
  1120. Status = DmSetValue(TypeKey,
  1121. CLUSREG_NAME_RESTYPE_DLL_NAME,
  1122. REG_SZ,
  1123. (CONST BYTE *)lpszDllName,
  1124. (lstrlenW(lpszDllName)+1)*sizeof(WCHAR));
  1125. if (Status != ERROR_SUCCESS) {
  1126. goto error_exit;
  1127. }
  1128. Status = DmSetValue(TypeKey,
  1129. CLUSREG_NAME_RESTYPE_IS_ALIVE,
  1130. REG_DWORD,
  1131. (CONST BYTE *)&dwIsAlive,
  1132. sizeof(dwIsAlive));
  1133. if (Status != ERROR_SUCCESS) {
  1134. goto error_exit;
  1135. }
  1136. Status = DmSetValue(TypeKey,
  1137. CLUSREG_NAME_RESTYPE_LOOKS_ALIVE,
  1138. REG_DWORD,
  1139. (CONST BYTE *)&dwLooksAlive,
  1140. sizeof(dwIsAlive));
  1141. if (Status != ERROR_SUCCESS) {
  1142. goto error_exit;
  1143. }
  1144. Status = DmSetValue(TypeKey,
  1145. CLUSREG_NAME_RESTYPE_NAME,
  1146. REG_SZ,
  1147. (CONST BYTE *)lpszDisplayName,
  1148. (lstrlenW(lpszDisplayName)+1)*sizeof(WCHAR));
  1149. if (Status != ERROR_SUCCESS) {
  1150. goto error_exit;
  1151. }
  1152. }
  1153. Status = FmCreateResourceType(lpszTypeName,
  1154. lpszDisplayName,
  1155. lpszDllName,
  1156. dwLooksAlive,
  1157. dwIsAlive);
  1158. if (Status != ERROR_SUCCESS) {
  1159. goto error_exit;
  1160. }
  1161. if (TypeKey != NULL) {
  1162. DmCloseKey(TypeKey);
  1163. }
  1164. return(ERROR_SUCCESS);
  1165. error_exit:
  1166. if ( CLUSTER_GET_MAJOR_VERSION( dwClusterHighestVersion ) <
  1167. NT51_MAJOR_VERSION ) {
  1168. DmCloseKey(TypeKey);
  1169. DmDeleteKey(DmResourceTypesKey, lpszTypeName);
  1170. }
  1171. return(Status);
  1172. }
  1173. error_status_t
  1174. s_ApiDeleteResourceType(
  1175. IN handle_t IDL_handle,
  1176. IN LPCWSTR lpszTypeName
  1177. )
  1178. /*++
  1179. Routine Description:
  1180. Deletes a resource type in the cluster. Note that this API only
  1181. deletes the resource type in the cluster registry and unregisters the
  1182. resource type with the cluster service. The calling program is
  1183. responsible for deleting the resource type DLL on each node in the
  1184. cluster. If any resources of the specified type exist, this API
  1185. fails. The calling program is responsible for deleting any resources
  1186. of this type before deleting the resource type.
  1187. Arguments:
  1188. IDL_handle - RPC binding handle, not used.
  1189. lpszResourceTypeName - Supplies the name of the resource type to
  1190. be deleted.
  1191. Return Value:
  1192. ERROR_SUCCESS if successful
  1193. Win32 error code otherwise
  1194. --*/
  1195. {
  1196. DWORD Status;
  1197. //
  1198. // Delete the resource from the FM. This will check to make sure no
  1199. // resources of the specified type exist and check that the resource
  1200. // is already installed.
  1201. //
  1202. Status = FmDeleteResourceType(lpszTypeName);
  1203. if (Status != ERROR_SUCCESS) {
  1204. return(Status);
  1205. }
  1206. //
  1207. // Now remove the resource type from the registry.
  1208. //
  1209. DmDeleteTree(DmResourceTypesKey, lpszTypeName);
  1210. return(ERROR_SUCCESS);
  1211. }
  1212. error_status_t
  1213. s_ApiChangeResourceGroup(
  1214. IN HRES_RPC hResource,
  1215. IN HGROUP_RPC hGroup
  1216. )
  1217. /*++
  1218. Routine Description:
  1219. Moves a resource from one group to another.
  1220. Arguments:
  1221. hResource - Supplies the resource to move.
  1222. hGroup - Supplies the new group that the resource should be in.
  1223. Return Value:
  1224. ERROR_SUCCESS if successful
  1225. Win32 error code otherwise.
  1226. --*/
  1227. {
  1228. PFM_RESOURCE Resource;
  1229. PFM_GROUP Group;
  1230. DWORD Status;
  1231. API_CHECK_INIT();
  1232. VALIDATE_RESOURCE_EXISTS(Resource, hResource);
  1233. VALIDATE_GROUP_EXISTS(Group, hGroup);
  1234. //
  1235. // Call the FM to do the real work.
  1236. //
  1237. Status = FmChangeResourceGroup(Resource, Group);
  1238. if (Status != ERROR_SUCCESS) {
  1239. goto FnExit;
  1240. }
  1241. FnExit:
  1242. return(Status);
  1243. }
  1244. /****
  1245. @func error_status_t | s_ApiCreateResTypeEnum | Enumerates the list of
  1246. nodes in which the resource type can be supported and
  1247. returns the list of nodes to the caller. The client-side
  1248. is responsible for freeing the allocated memory.
  1249. @parm IN handle_t | IDL_handle | RPC binding handle, not used.
  1250. @parm IN LPCWSTR | lpszTypeName | Name of the resource type.
  1251. @parm IN DWORD | dwType | Supplies the type of properties
  1252. to be enumerated.
  1253. @parm OUT PNM_NODE | ReturnEnum | Returns the requested objects.
  1254. @comm This routine helps enumerating all the nodes that a particular
  1255. resource type can be supported on.
  1256. @rdesc ERROR_SUCCESS on success. Win32 error code otherwise.
  1257. @xref
  1258. ****/
  1259. error_status_t
  1260. s_ApiCreateResTypeEnum(
  1261. IN handle_t IDL_handle,
  1262. IN LPCWSTR lpszTypeName,
  1263. IN DWORD dwType,
  1264. OUT PENUM_LIST *ReturnEnum
  1265. )
  1266. {
  1267. DWORD Status;
  1268. DWORD Allocated = 0;
  1269. PENUM_LIST Enum = NULL;
  1270. DWORD i;
  1271. DWORD Result;
  1272. PFM_RESTYPE pResType = NULL;
  1273. PNM_NODE pNode;
  1274. LPWSTR RealName = NULL;
  1275. pResType = OmReferenceObjectById(ObjectTypeResType,
  1276. lpszTypeName);
  1277. if (dwType & ~CLUSTER_RESOURCE_TYPE_ENUM_ALL) {
  1278. Status = ERROR_INVALID_PARAMETER;
  1279. goto ErrorExit;
  1280. }
  1281. Allocated = INITIAL_ENUM_LIST_ALLOCATION;
  1282. Enum = MIDL_user_allocate(ENUM_SIZE(Allocated));
  1283. if (Enum == NULL) {
  1284. Status = ERROR_NOT_ENOUGH_MEMORY;
  1285. goto ErrorExit;
  1286. }
  1287. if (pResType == NULL) {
  1288. //
  1289. // The object cannot be found in the list !
  1290. //
  1291. Status = ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND;
  1292. goto ErrorExit;
  1293. }
  1294. Enum->EntryCount = 0;
  1295. //
  1296. // Enumerate all possible nodes
  1297. //
  1298. if (dwType & CLUSTER_RESOURCE_TYPE_ENUM_NODES) {
  1299. i=0;
  1300. do {
  1301. Result = FmEnumResourceTypeNode(pResType,
  1302. i,
  1303. &pNode);
  1304. if (Result == ERROR_SUCCESS) {
  1305. RealName = (LPWSTR)OmObjectName( pNode );
  1306. if (RealName != NULL) {
  1307. ApipAddToEnum(&Enum,
  1308. &Allocated,
  1309. RealName,
  1310. CLUSTER_RESOURCE_TYPE_ENUM_NODES);
  1311. }
  1312. OmDereferenceObject( pNode );
  1313. ++i;
  1314. }
  1315. } while ( Result == ERROR_SUCCESS );
  1316. }
  1317. *ReturnEnum = Enum;
  1318. OmDereferenceObject( pResType );
  1319. return(ERROR_SUCCESS);
  1320. ErrorExit:
  1321. if (pResType != NULL) {
  1322. OmDereferenceObject( pResType );
  1323. }
  1324. if (Enum != NULL) {
  1325. MIDL_user_free(Enum);
  1326. }
  1327. *ReturnEnum = NULL;
  1328. return(Status);
  1329. }