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

3145 lines
79 KiB

  1. /*++
  2. Copyright (c) 1996-1999 Microsoft Corporation
  3. Module Name:
  4. fmapi.c
  5. Abstract:
  6. Cluster manager api service routines.
  7. Author:
  8. Rod Gamache (rodga) 8-Mar-1996
  9. Revision History:
  10. --*/
  11. #include "fmp.h"
  12. #define LOG_MODULE FMAPI
  13. //
  14. // Local Functions
  15. //
  16. //
  17. // Functions Exported to the rest of the Cluster Manager
  18. //
  19. ////////////////////////////////////////////////////////
  20. //
  21. // Group management functions.
  22. //
  23. ////////////////////////////////////////////////////////
  24. DWORD
  25. WINAPI
  26. FmOnlineGroup(
  27. IN PFM_GROUP Group
  28. )
  29. /*++
  30. Routine Description:
  31. Bring the specified group online. This means bringing all of the
  32. individual resources contained within the group online. This is an
  33. atomic operation - so either all resources contained within the group
  34. are brought online, or none of them are.
  35. Arguments:
  36. Group - Supplies a pointer to the group structure to bring online.
  37. Retruns:
  38. ERROR_SUCCESS if the request was successful.
  39. A Win32 error code on failure.
  40. --*/
  41. {
  42. DWORD status;
  43. PLIST_ENTRY listEntry;
  44. FmpMustBeOnline( );
  45. FmpAcquireLocalGroupLock( Group );
  46. //if the group has been marked for delete, then fail this call
  47. if (!IS_VALID_FM_GROUP(Group))
  48. {
  49. FmpReleaseLocalGroupLock( Group);
  50. return (ERROR_GROUP_NOT_AVAILABLE);
  51. }
  52. //
  53. // Make sure the owning node is not paused.
  54. //
  55. if (NmGetNodeState(Group->OwnerNode) == ClusterNodePaused) {
  56. FmpReleaseLocalGroupLock( Group );
  57. return(ERROR_SHARING_PAUSED);
  58. }
  59. //
  60. // Check if we are the owner... if not, ship the request off someplace
  61. // else.
  62. //
  63. if ( Group->OwnerNode != NmLocalNode ) {
  64. FmpReleaseLocalGroupLock( Group );
  65. return(FmcOnlineGroupRequest(Group));
  66. }
  67. //
  68. // Set the PersistentState for this Group - the PersistentState is persistent.
  69. //
  70. FmpSetGroupPersistentState( Group, ClusterGroupOnline );
  71. status = FmpOnlineGroup( Group, TRUE );
  72. FmpReleaseLocalGroupLock( Group );
  73. return(status);
  74. } // FmOnlineGroup
  75. DWORD
  76. WINAPI
  77. FmOfflineGroup(
  78. IN PFM_GROUP Group
  79. )
  80. /*++
  81. Routine Description:
  82. Bring the specified group offline. This means bringing all of the
  83. individual resources contained within the group offline.
  84. Arguments:
  85. Group - Supplies a pointer to the group structure to bring offline.
  86. Returns:
  87. ERROR_SUCCESS if the request was successful.
  88. A Win32 error code on failure.
  89. --*/
  90. {
  91. DWORD status = ERROR_SUCCESS;
  92. PLIST_ENTRY listEntry;
  93. PFM_RESOURCE Resource;
  94. FmpMustBeOnline( );
  95. //
  96. // Check if we are the owner... if not, ship the request off to some
  97. // other place.
  98. //
  99. if ( Group->OwnerNode != NmLocalNode ) {
  100. return(FmcOfflineGroupRequest(Group));
  101. }
  102. //
  103. // Set the PersistentState for this Group - the PersistentState is persistent.
  104. //
  105. FmpSetGroupPersistentState( Group, ClusterGroupOffline );
  106. status = FmpOfflineGroup( Group, FALSE, TRUE);
  107. return(status);
  108. } // FmOfflineGroup
  109. DWORD
  110. WINAPI
  111. FmMoveGroup(
  112. IN PFM_GROUP Group,
  113. IN PNM_NODE DestinationNode OPTIONAL
  114. )
  115. /*++
  116. Routine Description:
  117. Failover the specified Group. This means taking all of the individual
  118. resources contained within the group offline and requesting the
  119. DestinationNode to bring the Group Online.
  120. Arguments:
  121. Group - Supplies a pointer to the group structure to move.
  122. DestinationNode - Supplies the node object to move the group to. If not
  123. present, then move it to THE OTHER node.
  124. Returns:
  125. ERROR_SUCCESS if the request was successful.
  126. A Win32 error code on failure.
  127. Notes:
  128. The Group may or may not be online on the DestinationNode, depending on
  129. whether the online request succeeded. This means that the status return
  130. is merely the status return for the Online request for the DestinationNode.
  131. --*/
  132. {
  133. FmpMustBeOnline( );
  134. return(FmpDoMoveGroup( Group, DestinationNode, TRUE ));
  135. } // FmMoveGroup
  136. PFM_GROUP
  137. WINAPI
  138. FmCreateGroup(
  139. IN LPWSTR GroupId,
  140. IN LPCWSTR GroupName
  141. )
  142. /*++
  143. Routine Description:
  144. Create the specified GroupId. This requires verifying that the
  145. specified GroupId does not already exist and then creating an
  146. empty Group container into which resources can be added.
  147. Note that the returned PFM_GROUP will have already been referenced.
  148. This prevents somebody from deleting the group before the caller
  149. gets a chance to reference it.
  150. Arguments:
  151. GroupId - Supplies the Id of the Group to create.
  152. GroupName - Supplies the 'user-friendly' name of the Group.
  153. Returns:
  154. Pointer to the newly created group if successful.
  155. NULL if unsuccessful. GetLastError() will return the specific error.
  156. --*/
  157. {
  158. DWORD Status;
  159. PFM_GROUP Group;
  160. PGUM_CREATE_GROUP GumGroup;
  161. DWORD BufSize;
  162. DWORD GroupIdLen;
  163. DWORD GroupNameLen;
  164. FmpMustBeOnlineEx( NULL );
  165. //
  166. // Allocate a message buffer.
  167. //
  168. GroupIdLen = (lstrlenW(GroupId)+1)*sizeof(WCHAR);
  169. GroupNameLen = (lstrlenW(GroupName)+1)*sizeof(WCHAR);
  170. BufSize = sizeof(GUM_CREATE_GROUP) - sizeof(WCHAR) + GroupIdLen +
  171. GroupNameLen + (lstrlenW( OmObjectId(NmLocalNode) ) + 1) * sizeof(WCHAR);
  172. GumGroup = LocalAlloc(LMEM_FIXED, BufSize);
  173. if (GumGroup == NULL) {
  174. CsInconsistencyHalt( ERROR_NOT_ENOUGH_MEMORY );
  175. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  176. return(NULL);
  177. }
  178. //
  179. // Fill in message buffer.
  180. //
  181. GumGroup->Group = NULL;
  182. GumGroup->GroupIdLen = GroupIdLen;
  183. GumGroup->GroupNameLen = GroupNameLen;
  184. wcscpy(GumGroup->GroupId, GroupId);
  185. CopyMemory((PCHAR)GumGroup->GroupId + GroupIdLen,
  186. GroupName,
  187. GroupNameLen);
  188. CopyMemory((PCHAR)GumGroup->GroupId + GroupIdLen + GroupNameLen,
  189. OmObjectId(NmLocalNode),
  190. (lstrlenW( OmObjectId(NmLocalNode) ) + 1) * sizeof(WCHAR));
  191. //
  192. // Send message.
  193. //
  194. Status = GumSendUpdate(GumUpdateFailoverManager,
  195. FmUpdateCreateGroup,
  196. BufSize,
  197. GumGroup);
  198. if ((GumGroup->Group == NULL) && (FmpShutdown)) {
  199. Status = ERROR_CLUSTER_NODE_SHUTTING_DOWN;
  200. }
  201. if (Status != ERROR_SUCCESS) {
  202. LocalFree(GumGroup);
  203. SetLastError(Status);
  204. return(NULL);
  205. }
  206. Group = GumGroup->Group;
  207. CL_ASSERT(Group != NULL);
  208. LocalFree(GumGroup);
  209. return(Group);
  210. } // FmCreateGroup
  211. DWORD
  212. WINAPI
  213. FmDeleteGroup(
  214. IN PFM_GROUP pGroup
  215. )
  216. /*--
  217. Routine Description:
  218. Delete the specified Group. This means verifying that the specified
  219. Group does not contain any resources (resources must be removed
  220. by a separate call to remove the resources), and then deleting the
  221. Group.
  222. Arguments:
  223. Group - Supplies the Group to delete.
  224. Returns:
  225. ERROR_SUCCESS if the request was successful.
  226. A Win32 error code on failure.
  227. --*/
  228. {
  229. DWORD dwStatus;
  230. FmpMustBeOnline( );
  231. FmpAcquireLocalGroupLock( pGroup );
  232. if (pGroup->OwnerNode == NmLocalNode)
  233. {
  234. dwStatus = FmpDeleteGroup(pGroup);
  235. }
  236. else
  237. {
  238. //
  239. // FmcDeleteGroup releases the group lock
  240. //
  241. dwStatus = FmcDeleteGroupRequest(pGroup);
  242. goto FnExit;
  243. }
  244. FmpReleaseLocalGroupLock(pGroup);
  245. FnExit:
  246. return(dwStatus);
  247. } // FmDeleteGroup
  248. DWORD
  249. WINAPI
  250. FmSetGroupName(
  251. IN PFM_GROUP Group,
  252. IN LPCWSTR FriendlyName
  253. )
  254. /*++
  255. Routine Description:
  256. Set the user-friendly name for the specified Group.
  257. Note that the Group must have already been created. It is also
  258. assumed that the caller of this routine (the cluster API) has already
  259. verified that the name is NOT a duplicate.
  260. Arguments:
  261. Group - Supplies the Group to enter a new name.
  262. FriendlyName - Supplies the user-friendly name for the resource.
  263. Returns:
  264. ERROR_SUCCESS if successful.
  265. A Win32 error code on failure.
  266. --*/
  267. {
  268. LPCWSTR GroupId;
  269. DWORD Status;
  270. GroupId = OmObjectId(Group);
  271. Status = GumSendUpdateEx(GumUpdateFailoverManager,
  272. FmUpdateChangeGroupName,
  273. 2,
  274. (lstrlenW(GroupId)+1)*sizeof(WCHAR),
  275. GroupId,
  276. (lstrlenW(FriendlyName)+1)*sizeof(WCHAR),
  277. FriendlyName);
  278. return(Status);
  279. } // FmSetGroupName
  280. CLUSTER_GROUP_STATE
  281. WINAPI
  282. FmGetGroupState(
  283. IN PFM_GROUP Group,
  284. OUT LPWSTR NodeName,
  285. IN OUT PDWORD NameLength OPTIONAL
  286. )
  287. /*++
  288. Routine Description:
  289. Get the current state for the specified Group. The Group state
  290. consists of state of the group, along with the current node that is
  291. hosting the Group (if the state of the Group is anything but
  292. ClusterGroupOffline.
  293. Arguments:
  294. Group - Supplies the group object to get the state.
  295. NodeName - Supplies a pointer to a buffer into which the name of
  296. the node in the cluster the specified Group is currently hosted.
  297. This field can be NULL, if NameLength is zero.
  298. NameLength - Supplies a pointer to a DWORD containing the number of
  299. characters available to the NodeName buffer (including the terminating
  300. NULL character. On return, it is the number of characters written
  301. into the NodeName buffer not including the NULL character.
  302. Returns:
  303. Returns the current state of the group:
  304. ClusterGroupOnline
  305. ClusterGroupOffline
  306. ClusterGroupPending
  307. ClusterGroupPartialOnline
  308. ClusterGroupFailed
  309. If the function fails, then the return value is ClusterGroupStateUnknown.
  310. --*/
  311. {
  312. CLUSTER_GROUP_STATE state;
  313. DWORD nameLength=0;
  314. DWORD length;
  315. if ( ARGUMENT_PRESENT( NameLength ) ) {
  316. nameLength = *NameLength;
  317. *NodeName = (WCHAR)0;
  318. *NameLength = 0;
  319. }
  320. FmpMustBeOnlineEx( ClusterGroupStateUnknown );
  321. FmpAcquireLocalGroupLock( Group );
  322. //if the group has been marked for delete, then fail this call
  323. if (!IS_VALID_FM_GROUP(Group))
  324. {
  325. FmpReleaseLocalGroupLock( Group);
  326. return (ERROR_GROUP_NOT_AVAILABLE);
  327. }
  328. //
  329. // Check if the OwnerNodes exists
  330. //
  331. // SS: dont filter out the node if it not in the preferred list
  332. // how is the poor user going to know who the current owner is??
  333. if (Group->OwnerNode != NULL) {
  334. //
  335. // The Group is 'owned' by some system
  336. //
  337. if ( ARGUMENT_PRESENT( NameLength ) ) {
  338. length = lstrlenW( OmObjectName(Group->OwnerNode) ) + 1;
  339. if ( nameLength < length ) {
  340. length = nameLength;
  341. }
  342. lstrcpynW( NodeName, OmObjectName(Group->OwnerNode), length );
  343. *NameLength = length;
  344. }
  345. }
  346. //
  347. // Get the group state which is not normalized
  348. //
  349. state = FmpGetGroupState( Group, FALSE );
  350. FmpReleaseLocalGroupLock( Group );
  351. if ( state == ClusterGroupStateUnknown ) {
  352. SetLastError(ERROR_INVALID_STATE);
  353. }
  354. return(state);
  355. } // FmGetGroupState
  356. DWORD
  357. WINAPI
  358. FmEnumerateGroupResources(
  359. IN PFM_GROUP Group,
  360. IN FM_ENUM_GROUP_RESOURCE_ROUTINE EnumerationRoutine,
  361. IN PVOID Context1,
  362. IN PVOID Context2
  363. )
  364. /*++
  365. Routine Description:
  366. Enumerate all the resources in a group.
  367. Arguments:
  368. Group - Supplies the group which must be enumerated.
  369. EnumerationRoutine - The enumeration function.
  370. Context1 - The enumeration list (allocated by the caller).
  371. Context2 - Size of the enumerated list.
  372. Returns:
  373. ERROR_SUCCESS on success.
  374. A Win32 error code otherwise.
  375. Comments:
  376. This function executes only when the FM is fully online.
  377. --*/
  378. {
  379. FmpMustBeOnline();
  380. FmpEnumerateGroupResources( Group,
  381. EnumerationRoutine,
  382. Context1,
  383. Context2 );
  384. return(ERROR_SUCCESS);
  385. } // FmEnumerateGroupResources
  386. DWORD
  387. FmpEnumerateGroupResources(
  388. IN PFM_GROUP pGroup,
  389. IN FM_ENUM_GROUP_RESOURCE_ROUTINE pfnEnumerationRoutine,
  390. IN PVOID pContext1,
  391. IN PVOID pContext2
  392. )
  393. /*++
  394. Routine Description:
  395. Enumerate all the resources in a group.
  396. Arguments:
  397. pGroup - Supplies the group which must be enumerated.
  398. pfnEnumerationRoutine - The enumeration function.
  399. pContext1 - The enumeration list (allocated by the caller).
  400. pContext2 - Size of the enumerated list.
  401. Returns:
  402. ERROR_SUCCESS.
  403. Comments:
  404. This function executes even when the FM is not fully online. This is
  405. necessary for a joining node to query the resource states while the
  406. owner node of the group is shutting down.
  407. --*/
  408. {
  409. PFM_RESOURCE pResource;
  410. PLIST_ENTRY pListEntry;
  411. ClRtlLogPrint(LOG_NOISE,
  412. "[FM] FmpEnumerateGroupResources: Entry for group <%1!ws!>....\n",
  413. OmObjectId(pGroup));
  414. FmpAcquireLocalGroupLock( pGroup );
  415. //
  416. // If the group has been marked for delete, then fail this call
  417. //
  418. if ( !IS_VALID_FM_GROUP( pGroup ) )
  419. {
  420. ClRtlLogPrint(LOG_UNUSUAL,
  421. "[FM] FmpEnumerateGroupResources: Group <%1!ws!> marked for deletion....\n",
  422. OmObjectId(pGroup));
  423. goto FnExit;
  424. }
  425. //
  426. // Run through contains list, then find all resources under that tree.
  427. //
  428. for ( pListEntry = pGroup->Contains.Flink;
  429. pListEntry != &(pGroup->Contains);
  430. pListEntry = pListEntry->Flink )
  431. {
  432. pResource = CONTAINING_RECORD( pListEntry,
  433. FM_RESOURCE,
  434. ContainsLinkage );
  435. if ( !pfnEnumerationRoutine( pContext1,
  436. pContext2,
  437. pResource,
  438. OmObjectId( pResource ) ) )
  439. {
  440. ClRtlLogPrint(LOG_CRITICAL,
  441. "[FM] FmpEnumerateGroupResources: Enumeration routine for group <%1!ws!> fails....\n",
  442. OmObjectId(pGroup));
  443. break;
  444. }
  445. }
  446. FnExit:
  447. FmpReleaseLocalGroupLock( pGroup );
  448. ClRtlLogPrint(LOG_NOISE,
  449. "[FM] FmpEnumerateGroupResources: Exit for group <%1!ws!>....\n",
  450. OmObjectId(pGroup));
  451. return( ERROR_SUCCESS );
  452. } // FmpEnumerateGroupResources
  453. ////////////////////////////////////////////////////////
  454. //
  455. // Resource management functions.
  456. //
  457. ////////////////////////////////////////////////////////
  458. PFM_RESOURCE
  459. WINAPI
  460. FmCreateResource(
  461. IN PFM_GROUP Group,
  462. IN LPWSTR ResourceId,
  463. IN LPCWSTR ResourceName,
  464. IN LPCWSTR ResourceType,
  465. IN DWORD dwFlags
  466. )
  467. /*++
  468. Routine Description:
  469. Create the specified resource.
  470. Note that the returned PFM_RESOURCE will have already been referenced.
  471. This prevents somebody from deleting the resource before the caller
  472. gets a chance to reference it.
  473. Arguments:
  474. Group - Supplies the group in which this resource belongs.
  475. ResourceId - Supplies the Id of the resource to create.
  476. ResourceName - Supplies the 'user-friendly' name of the resource.
  477. ResourceType - Supplies the 'user-friendly' name of the resource type.
  478. dwFlags - The flags for the resource.
  479. Returns:
  480. Pointer to the newly created resource if successful.
  481. NULL if unsuccessful. GetLastError() will return the specific error.
  482. --*/
  483. {
  484. DWORD Status;
  485. PFM_RESOURCE Resource;
  486. LPCWSTR GroupId;
  487. PGUM_CREATE_RESOURCE GumResource;
  488. DWORD GroupIdLen;
  489. DWORD ResourceIdLen;
  490. DWORD ResourceNameLen;
  491. DWORD ResourceTypeLen;
  492. DWORD BufSize;
  493. HDMKEY ResourceKey;
  494. HDMKEY ParamsKey;
  495. DWORD Disposition;
  496. FmpMustBeOnlineEx( NULL );
  497. FmpAcquireLocalGroupLock( Group );
  498. //
  499. // If we own the group then we can issue the Gum request to create
  500. // the resource. Otherwise, request the owner to initiate the request.
  501. //
  502. if ( Group->OwnerNode == NmLocalNode ) {
  503. //
  504. // Allocate a message buffer.
  505. //
  506. GroupId = OmObjectId(Group);
  507. GroupIdLen = (lstrlenW(GroupId)+1) * sizeof(WCHAR);
  508. ResourceIdLen = (lstrlenW(ResourceId)+1) * sizeof(WCHAR);
  509. ResourceNameLen = (lstrlenW(ResourceName)+1) * sizeof(WCHAR);
  510. ResourceTypeLen = (lstrlenW(ResourceType)+1) * sizeof(WCHAR);
  511. BufSize = sizeof(GUM_CREATE_RESOURCE) - sizeof(WCHAR) +
  512. GroupIdLen + ResourceIdLen + ResourceNameLen + ResourceTypeLen + 2 * sizeof( DWORD );
  513. GumResource = LocalAlloc(LMEM_FIXED, BufSize);
  514. if (GumResource == NULL) {
  515. CsInconsistencyHalt( ERROR_NOT_ENOUGH_MEMORY );
  516. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  517. return(NULL);
  518. }
  519. //
  520. // Fill in message buffer.
  521. //
  522. GumResource->Resource = NULL;
  523. GumResource->GroupIdLen = GroupIdLen;
  524. GumResource->ResourceIdLen = ResourceIdLen;
  525. CopyMemory(GumResource->GroupId, GroupId, GroupIdLen);
  526. CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen,
  527. ResourceId,
  528. ResourceIdLen);
  529. CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen + ResourceIdLen,
  530. ResourceName,
  531. ResourceNameLen);
  532. CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen + ResourceIdLen + ResourceNameLen,
  533. &ResourceTypeLen,
  534. sizeof( DWORD ) );
  535. CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen + ResourceIdLen + ResourceNameLen + sizeof( DWORD ),
  536. ResourceType,
  537. ResourceTypeLen );
  538. CopyMemory((PCHAR)GumResource->GroupId + GroupIdLen + ResourceIdLen + ResourceNameLen + sizeof( DWORD ) + ResourceTypeLen,
  539. &dwFlags,
  540. sizeof( DWORD ) );
  541. //
  542. // Send message.
  543. //
  544. Status = GumSendUpdate(GumUpdateFailoverManager,
  545. FmUpdateCreateResource,
  546. BufSize,
  547. GumResource);
  548. FmpReleaseLocalGroupLock( Group );
  549. if (Status != ERROR_SUCCESS) {
  550. LocalFree(GumResource);
  551. SetLastError(Status);
  552. return(NULL);
  553. }
  554. //The create resource by default adds all nodes
  555. //as possible nodes for a resource without filtering
  556. //out the nodes that dont support the resource type
  557. if( GumResource->Resource != NULL ) {
  558. FmpCleanupPossibleNodeList(GumResource->Resource);
  559. }
  560. Resource = GumResource->Resource;
  561. if( ( Resource == NULL ) && FmpShutdown ) {
  562. SetLastError( ERROR_CLUSTER_NODE_SHUTTING_DOWN );
  563. }
  564. LocalFree(GumResource);
  565. } else {
  566. //
  567. // The Group lock is released by FmcCreateResource
  568. //
  569. Resource = FmcCreateResource( Group,
  570. ResourceId,
  571. ResourceName,
  572. ResourceType,
  573. dwFlags );
  574. }
  575. //giving a reference to the client, increment ref count
  576. if ( Resource ) {
  577. OmReferenceObject(Resource);
  578. }
  579. return(Resource);
  580. } // FmCreateResource
  581. DWORD
  582. WINAPI
  583. FmDeleteResource(
  584. IN PFM_RESOURCE Resource
  585. )
  586. /*++
  587. Routine Description:
  588. Delete the specified resource.
  589. Arguments:
  590. Resource - Supplies the resource to delete.
  591. Returns:
  592. ERROR_SUCCESS if the request was successful.
  593. A Win32 error code on failure.
  594. --*/
  595. {
  596. DWORD Status;
  597. LPCWSTR ResourceId;
  598. DWORD ResourceLen;
  599. FmpMustBeOnline( );
  600. FmpAcquireLocalResourceLock( Resource );
  601. //
  602. // Check if this is the quorum resource.
  603. //
  604. if ( Resource->QuorumResource ) {
  605. FmpReleaseLocalResourceLock( Resource );
  606. return(ERROR_QUORUM_RESOURCE);
  607. }
  608. //other core resources cannot be deleted either
  609. if (Resource->ExFlags & CLUS_FLAG_CORE)
  610. {
  611. FmpReleaseLocalResourceLock( Resource );
  612. return (ERROR_CORE_RESOURCE);
  613. }
  614. //
  615. // If we own the resource then we can issue the Gum request to delete
  616. // the resource. Otherwise, request the owner to initiate the request.
  617. //
  618. if ( Resource->Group->OwnerNode == NmLocalNode ) {
  619. //
  620. // Check the state of the resource, before attempting to delete it.
  621. // It must be offline or failed in order to perform the delete.
  622. //
  623. if ((Resource->State != ClusterResourceOffline) &&
  624. (Resource->State != ClusterResourceFailed)) {
  625. FmpReleaseLocalResourceLock( Resource );
  626. return(ERROR_RESOURCE_ONLINE);
  627. }
  628. //
  629. // Check whether this resource provides for any other resources.
  630. // If so, it cannot be deleted.
  631. //
  632. if (!IsListEmpty(&Resource->ProvidesFor)) {
  633. FmpReleaseLocalResourceLock( Resource );
  634. return(ERROR_DEPENDENT_RESOURCE_EXISTS);
  635. }
  636. if (Resource->Group->MovingList)
  637. {
  638. FmpReleaseLocalResourceLock( Resource );
  639. return(ERROR_INVALID_STATE);
  640. }
  641. Status = FmpBroadcastDeleteControl(Resource);
  642. if ( Status != ERROR_SUCCESS ) {
  643. FmpReleaseLocalResourceLock( Resource );
  644. return(Status);
  645. }
  646. ResourceId = OmObjectId( Resource );
  647. ResourceLen = (lstrlenW(ResourceId)+1) * sizeof(WCHAR);
  648. //
  649. // Send message.
  650. //
  651. Status = GumSendUpdateEx(GumUpdateFailoverManager,
  652. FmUpdateDeleteResource,
  653. 1,
  654. ResourceLen,
  655. ResourceId);
  656. FmpReleaseLocalResourceLock( Resource );
  657. } else {
  658. Status = FmcDeleteResource( Resource );
  659. }
  660. return(Status);
  661. } // FmDeleteResource
  662. DWORD
  663. WINAPI
  664. FmSetResourceName(
  665. IN PFM_RESOURCE Resource,
  666. IN LPCWSTR FriendlyName
  667. )
  668. /*++
  669. Routine Description:
  670. Set the user-friendly name for the specified resource.
  671. Note that the resource must have already been created. It is also
  672. assumed that the caller of this routine (the cluster API) has already
  673. verified that the name is NOT a duplicate.
  674. Arguments:
  675. Resource - Supplies the resource to enter a new name.
  676. FriendlyName - Supplies the user-friendly name for the resource.
  677. Returns:
  678. ERROR_SUCCESS if successful.
  679. A Win32 error code on failure.
  680. --*/
  681. {
  682. DWORD dwStatus = ERROR_SUCCESS;
  683. dwStatus = FmpSetResourceName( Resource, FriendlyName );
  684. if( dwStatus != ERROR_SUCCESS )
  685. {
  686. ClRtlLogPrint(LOG_CRITICAL,
  687. "[FM] FmSetResourceName: FmpSetResourceName for resource %1!ws! fails, Status = %2!d!...\n",
  688. OmObjectId(Resource),
  689. dwStatus);
  690. }
  691. return( dwStatus );
  692. } // FmSetResourceName
  693. DWORD
  694. WINAPI
  695. FmOnlineResource(
  696. IN PFM_RESOURCE Resource
  697. )
  698. /*++
  699. Routine Description:
  700. This routine brings a resource online. It also updates the registry to
  701. indicate the new persistent, desired state of the resource.
  702. Arguments:
  703. Resource - A pointer to the resource to bring online.
  704. Returns:
  705. ERROR_SUCCESS if the request is successful.
  706. ERROR_IO_PENDING if the request is pending.
  707. A Win32 error code if the request fails.
  708. --*/
  709. {
  710. DWORD status;
  711. FmpMustBeOnline( );
  712. FmpAcquireLocalResourceLock( Resource );
  713. //if the resource has been marked for delete, then dont let
  714. //it be brought online
  715. if (!IS_VALID_FM_RESOURCE(Resource))
  716. {
  717. FmpReleaseLocalResourceLock( Resource );
  718. return (ERROR_RESOURCE_NOT_AVAILABLE);
  719. }
  720. //
  721. // Check if we are the owner... if not, ship the request off someplace
  722. // else.
  723. //
  724. CL_ASSERT( Resource->Group != NULL );
  725. if ( Resource->Group->OwnerNode != NmLocalNode ) {
  726. FmpReleaseLocalResourceLock( Resource );
  727. status = FmcOnlineResourceRequest( Resource );
  728. return(status);
  729. }
  730. //
  731. // Check if the resource has been initialized. If not, attempt
  732. // to initialize the resource now.
  733. //
  734. if ( Resource->Monitor == NULL ) {
  735. status = FmpInitializeResource( Resource, TRUE );
  736. if ( status != ERROR_SUCCESS ) {
  737. FmpReleaseLocalResourceLock( Resource );
  738. return(status);
  739. }
  740. }
  741. //
  742. // Chittur Subbaraman (chitturs) - 08/04/2000
  743. //
  744. // If the group is moving, fail this operation.
  745. //
  746. if ( Resource->Group->MovingList != NULL )
  747. {
  748. FmpReleaseLocalResourceLock( Resource );
  749. return (ERROR_GROUP_NOT_AVAILABLE);
  750. }
  751. //
  752. // Try to bring the resource online.
  753. //
  754. status = FmpDoOnlineResource( Resource, TRUE );
  755. FmpReleaseLocalResourceLock( Resource );
  756. return(status);
  757. } // FmOnlineResource
  758. DWORD
  759. WINAPI
  760. FmOfflineResource(
  761. IN PFM_RESOURCE Resource
  762. )
  763. /*++
  764. Routine Description:
  765. This routine takes a resource offline. It also updates the registry
  766. to indicate the new persistent, desired state of the resource.
  767. Arguments:
  768. Resource - A pointer to the resource to take offline.
  769. Returns:
  770. ERROR_SUCCESS if the request is successful.
  771. ERROR_IO_PENDING if the request is pending.
  772. A Win32 error code if the request fails.
  773. --*/
  774. {
  775. DWORD status;
  776. FmpMustBeOnline( );
  777. FmpAcquireLocalResourceLock( Resource );
  778. //if the resource has been marked for delete, then fail this call
  779. if (!IS_VALID_FM_RESOURCE(Resource))
  780. {
  781. FmpReleaseLocalResourceLock( Resource );
  782. return (ERROR_RESOURCE_NOT_AVAILABLE);
  783. }
  784. //
  785. // Check if this is the quorum resource.
  786. //
  787. if ( Resource->QuorumResource ) {
  788. FmpReleaseLocalResourceLock( Resource );
  789. return(ERROR_QUORUM_RESOURCE);
  790. }
  791. //
  792. // Chittur Subbaraman (chitturs) - 4/8/99
  793. //
  794. // Don't attempt to do anything if the resource has failed. You could
  795. // get into some funny cases in which the resource switches between
  796. // offline pending and failed states for ever.
  797. //
  798. if ( Resource->State == ClusterResourceFailed ) {
  799. FmpReleaseLocalResourceLock( Resource );
  800. return(ERROR_INVALID_STATE);
  801. }
  802. //
  803. // Check if we are the owner... if not, ship the request off someplace
  804. // else.
  805. //
  806. CL_ASSERT( Resource->Group != NULL );
  807. if ( Resource->Group->OwnerNode != NmLocalNode ) {
  808. FmpReleaseLocalResourceLock( Resource );
  809. return(FmcOfflineResourceRequest(Resource));
  810. }
  811. //
  812. // Check if the resource has been initialized. If not, return
  813. // success because the resource is not online.
  814. //
  815. if ( Resource->Monitor == NULL ) {
  816. FmpReleaseLocalResourceLock( Resource );
  817. return(ERROR_SUCCESS);
  818. }
  819. //
  820. // Chittur Subbaraman (chitturs) - 08/04/2000
  821. //
  822. // If the group is moving, fail this operation.
  823. //
  824. if ( Resource->Group->MovingList != NULL )
  825. {
  826. FmpReleaseLocalResourceLock( Resource );
  827. return (ERROR_GROUP_NOT_AVAILABLE);
  828. }
  829. //
  830. // Take the resource offline.
  831. //
  832. FmpReleaseLocalResourceLock( Resource );
  833. return(FmpDoOfflineResource( Resource, TRUE));
  834. } // FmOfflineResource
  835. CLUSTER_RESOURCE_STATE
  836. WINAPI
  837. FmGetResourceState(
  838. IN PFM_RESOURCE Resource,
  839. OUT LPWSTR NodeName,
  840. IN OUT PDWORD NameLength OPTIONAL
  841. )
  842. /*++
  843. Routine Description:
  844. Get the current state for the specified resource. The resource state
  845. consists of state of the resource, along with the current node that is
  846. hosting the resource.
  847. Arguments:
  848. Resource - Supplies the resource object to get the state.
  849. NodeName - Supplies a pointer to a buffer into which the name of
  850. the node in the cluster the specified resource is currently hosted.
  851. This field can be NULL, if NameLength is zero.
  852. NameLength - Supplies a pointer to a DWORD containing the number of
  853. characters available to the NodeName buffer (including the terminating
  854. NULL character. On return, it is the number of characters written
  855. into the NodeName buffer not including the NULL character.
  856. Returns:
  857. Returns the current state of the resource:
  858. ClusterResourceOnline
  859. ClusterResourceOffline
  860. ClusterResourceFailed
  861. etc.
  862. If the function fails, then the return value is ClusterResourceStateUnknown.
  863. --*/
  864. {
  865. WCHAR computerName[MAX_COMPUTERNAME_LENGTH+1];
  866. DWORD nameLength;
  867. DWORD length;
  868. PNM_NODE OwnerNode;
  869. CLUSTER_RESOURCE_STATE state;
  870. BOOL acquired;
  871. CL_ASSERT( OmObjectSignature(Resource) == FMP_RESOURCE_SIGNATURE );
  872. if ( ARGUMENT_PRESENT( NameLength ) ) {
  873. nameLength = *NameLength;
  874. *NodeName = (WCHAR)0;
  875. *NameLength = 0;
  876. }
  877. FmpMustBeOnlineEx( ClusterResourceStateUnknown );
  878. //
  879. // Try to acquire the lock to perform this work, so that resources
  880. // can query their current status and where the resource should be run.
  881. //
  882. // This does leave a potential window though if we can't get the lock,
  883. // some other thread could be changing the data!
  884. //
  885. FmpTryAcquireLocalResourceLock( Resource, acquired );
  886. OwnerNode = Resource->Group->OwnerNode;
  887. if ( OwnerNode != NULL ) {
  888. //
  889. // The Group is 'owned' by some system
  890. //
  891. if ( ARGUMENT_PRESENT( NameLength ) ) {
  892. length = lstrlenW( OmObjectName(OwnerNode) ) + 1;
  893. if ( nameLength < length ) {
  894. length = nameLength;
  895. }
  896. lstrcpynW( NodeName,
  897. OmObjectName(OwnerNode),
  898. length );
  899. *NameLength = length;
  900. }
  901. }
  902. state = Resource->State;
  903. if ( acquired ) {
  904. FmpReleaseLocalResourceLock( Resource );
  905. }
  906. if ( state == ClusterGroupStateUnknown ) {
  907. SetLastError(ERROR_INVALID_STATE);
  908. }
  909. return(state);
  910. } // FmGetResourceState
  911. DWORD
  912. WINAPI
  913. FmAddResourceDependency(
  914. IN PFM_RESOURCE pResource,
  915. IN PFM_RESOURCE pDependentResource
  916. )
  917. /*++
  918. Routine Description:
  919. Add a dependency from one resource to another.
  920. Arguments:
  921. Resource - The resource to add the dependent resource.
  922. DependentResource - The dependent resource.
  923. Returns:
  924. ERROR_SUCCESS if successful.
  925. A Win32 error code on failure.
  926. --*/
  927. {
  928. LPCWSTR pszResourceId;
  929. DWORD dwResourceLen;
  930. LPCWSTR pszDependsOnId;
  931. DWORD dwDependsOnLen;
  932. DWORD dwStatus = ERROR_SUCCESS;
  933. //
  934. // Chittur Subbaraman (chitturs) - 5/16/99
  935. //
  936. // Modify this API to route requests to owner node. Handle the
  937. // mixed mode case as well.
  938. //
  939. FmpMustBeOnline( );
  940. ClRtlLogPrint(LOG_NOISE,
  941. "[FM] FmAddResourceDependency : Resource <%1!ws!>, DependentResource <%2!ws!>...\n",
  942. OmObjectId( pResource ),
  943. OmObjectId( pDependentResource ));
  944. FmpAcquireLocalResourceLock( pResource );
  945. //
  946. // Check if we are the owner... if not, ship the request off some place
  947. // else.
  948. //
  949. if ( pResource->Group->OwnerNode != NmLocalNode )
  950. {
  951. //
  952. // FmcAddResourceDependency releases the local resource lock
  953. //
  954. dwStatus = FmcAddResourceDependency( pResource, pDependentResource );
  955. goto FnExit;
  956. }
  957. dwStatus = FmpValAddResourceDependency( pResource, pDependentResource );
  958. if ( dwStatus != ERROR_SUCCESS )
  959. {
  960. goto FnUnlock;
  961. }
  962. pszResourceId = OmObjectId( pResource );
  963. dwResourceLen = ( lstrlenW( pszResourceId ) +1 ) * sizeof( WCHAR) ;
  964. pszDependsOnId = OmObjectId( pDependentResource );
  965. dwDependsOnLen = ( lstrlenW( pszDependsOnId ) + 1 ) * sizeof( WCHAR );
  966. dwStatus = GumSendUpdateEx( GumUpdateFailoverManager,
  967. FmUpdateAddDependency,
  968. 2,
  969. dwResourceLen,
  970. pszResourceId,
  971. dwDependsOnLen,
  972. pszDependsOnId );
  973. if ( dwStatus == ERROR_SUCCESS )
  974. {
  975. FmpBroadcastDependencyChange( pResource,
  976. pszDependsOnId,
  977. FALSE );
  978. }
  979. FnUnlock:
  980. FmpReleaseLocalResourceLock( pResource );
  981. FnExit:
  982. ClRtlLogPrint(LOG_NOISE,
  983. "[FM] FmAddResourceDependency Exit: Status = <%1!u!>...\n",
  984. dwStatus);
  985. return( dwStatus );
  986. }
  987. // FmAddResourceDependency
  988. DWORD
  989. WINAPI
  990. FmRemoveResourceDependency(
  991. IN PFM_RESOURCE pResource,
  992. IN PFM_RESOURCE pDependentResource
  993. )
  994. /*++
  995. Routine Description:
  996. Remove a dependency from a resource.
  997. Arguments:
  998. Resource - The resource to remove the dependent resource.
  999. DependentResource - The dependent resource.
  1000. Returns:
  1001. ERROR_SUCCESS if successful.
  1002. A Win32 error code on failure.
  1003. --*/
  1004. {
  1005. LPCWSTR pszResourceId;
  1006. DWORD dwResourceLen;
  1007. LPCWSTR pszDependsOnId;
  1008. DWORD dwDependsOnLen;
  1009. DWORD dwStatus;
  1010. //
  1011. // Chittur Subbaraman (chitturs) - 5/16/99
  1012. //
  1013. // Modify this API to route requests to owner node. Handle the
  1014. // mixed mode case as well.
  1015. //
  1016. FmpMustBeOnline( );
  1017. ClRtlLogPrint(LOG_NOISE,
  1018. "[FM] FmRemoveResourceDependency : Resource <%1!ws!>, DependentResource <%2!ws!>...\n",
  1019. OmObjectId( pResource ),
  1020. OmObjectId( pDependentResource ));
  1021. FmpAcquireLocalResourceLock( pResource );
  1022. //
  1023. // Check if we are the owner... if not, ship the request off some place
  1024. // else.
  1025. //
  1026. if ( pResource->Group->OwnerNode != NmLocalNode )
  1027. {
  1028. //
  1029. // FmcRemoveResourceDependency releases the local resource lock
  1030. //
  1031. dwStatus = FmcRemoveResourceDependency( pResource, pDependentResource );
  1032. goto FnExit;
  1033. }
  1034. dwStatus = FmpValRemoveResourceDependency( pResource, pDependentResource );
  1035. if ( dwStatus != ERROR_SUCCESS )
  1036. {
  1037. ClRtlLogPrint(LOG_NOISE,
  1038. "[FM] FmRemoveResourceDependency: FmpValRemoveResourceDependency returns status = <%1!u!>...\n",
  1039. dwStatus);
  1040. goto FnUnlock;
  1041. }
  1042. pszResourceId = OmObjectId( pResource );
  1043. dwResourceLen = ( lstrlenW( pszResourceId ) + 1 ) * sizeof( WCHAR );
  1044. pszDependsOnId = OmObjectId( pDependentResource );
  1045. dwDependsOnLen = ( lstrlenW( pszDependsOnId ) + 1 ) * sizeof( WCHAR );
  1046. dwStatus = GumSendUpdateEx( GumUpdateFailoverManager,
  1047. FmUpdateRemoveDependency,
  1048. 2,
  1049. dwResourceLen,
  1050. pszResourceId,
  1051. dwDependsOnLen,
  1052. pszDependsOnId );
  1053. if ( dwStatus == ERROR_SUCCESS )
  1054. {
  1055. FmpBroadcastDependencyChange( pResource,
  1056. pszDependsOnId,
  1057. TRUE );
  1058. }
  1059. FnUnlock:
  1060. FmpReleaseLocalResourceLock( pResource );
  1061. FnExit:
  1062. ClRtlLogPrint(LOG_NOISE,
  1063. "[FM] FmRemoveResourceDependency Exit: Status = <%1!u!>...\n",
  1064. dwStatus);
  1065. return( dwStatus );
  1066. }
  1067. // FmRemoveResourceDependency
  1068. DWORD
  1069. WINAPI
  1070. FmEnumResourceDependent(
  1071. IN PFM_RESOURCE Resource,
  1072. IN DWORD Index,
  1073. OUT PFM_RESOURCE *DependentResource
  1074. )
  1075. /*++
  1076. Routine Description:
  1077. Enumerate the dependencies of a resources.
  1078. Arguments:
  1079. Resource - The resource to enumerate.
  1080. Index - The index for this enumeration.
  1081. DependentResource - The dependent resource. The returned resource
  1082. pointer will be referenced by this routine and should
  1083. be dereferenced when the caller is done with it.
  1084. Returns:
  1085. ERROR_SUCCESS if successful.
  1086. A Win32 error code on failure.
  1087. --*/
  1088. {
  1089. PLIST_ENTRY ListEntry;
  1090. DWORD i = 0;
  1091. PFM_RESOURCE Current;
  1092. PDEPENDENCY Dependency;
  1093. DWORD Status = ERROR_NO_MORE_ITEMS;
  1094. FmpMustBeOnline( );
  1095. FmpAcquireResourceLock();
  1096. if (!IS_VALID_FM_RESOURCE(Resource))
  1097. {
  1098. Status = ERROR_RESOURCE_NOT_AVAILABLE;
  1099. goto FnExit;
  1100. }
  1101. ListEntry = Resource->DependsOn.Flink;
  1102. while (ListEntry != &Resource->DependsOn) {
  1103. Dependency = CONTAINING_RECORD(ListEntry,
  1104. DEPENDENCY,
  1105. DependentLinkage);
  1106. CL_ASSERT(Dependency->DependentResource == Resource);
  1107. CL_ASSERT(Dependency->ProviderResource != Resource);
  1108. if (i==Index) {
  1109. //
  1110. // Got the right index
  1111. //
  1112. OmReferenceObject(Dependency->ProviderResource);
  1113. *DependentResource = Dependency->ProviderResource;
  1114. Status = ERROR_SUCCESS;
  1115. break;
  1116. }
  1117. ListEntry = ListEntry->Flink;
  1118. ++i;
  1119. }
  1120. FnExit:
  1121. FmpReleaseResourceLock();
  1122. return(Status);
  1123. } // FmEnumResourceDependent
  1124. DWORD
  1125. WINAPI
  1126. FmEnumResourceProvider(
  1127. IN PFM_RESOURCE Resource,
  1128. IN DWORD Index,
  1129. OUT PFM_RESOURCE *DependentResource
  1130. )
  1131. /*++
  1132. Routine Description:
  1133. Enumerate the providers for a resources.
  1134. Arguments:
  1135. Resource - The resource to enumerate.
  1136. Index - The index for this enumeration.
  1137. DependentResource - The provider resource. The returned resource
  1138. pointer will be referenced by this routine and should
  1139. be dereferenced when the caller is done with it.
  1140. Returns:
  1141. ERROR_SUCCESS if successful.
  1142. A Win32 error code on failure.
  1143. --*/
  1144. {
  1145. PLIST_ENTRY ListEntry;
  1146. DWORD i = 0;
  1147. PFM_RESOURCE Current;
  1148. PDEPENDENCY Dependency;
  1149. DWORD Status = ERROR_NO_MORE_ITEMS;
  1150. FmpMustBeOnline( );
  1151. FmpAcquireResourceLock();
  1152. if (!IS_VALID_FM_RESOURCE(Resource))
  1153. {
  1154. Status = ERROR_RESOURCE_NOT_AVAILABLE;
  1155. goto FnExit;
  1156. }
  1157. ListEntry = Resource->ProvidesFor.Flink;
  1158. while (ListEntry != &Resource->ProvidesFor) {
  1159. Dependency = CONTAINING_RECORD(ListEntry,
  1160. DEPENDENCY,
  1161. ProviderLinkage);
  1162. CL_ASSERT(Dependency->DependentResource != Resource);
  1163. CL_ASSERT(Dependency->ProviderResource == Resource);
  1164. if (i==Index) {
  1165. //
  1166. // Got the right index
  1167. //
  1168. OmReferenceObject(Dependency->DependentResource);
  1169. *DependentResource = Dependency->DependentResource;
  1170. Status = ERROR_SUCCESS;
  1171. break;
  1172. }
  1173. ListEntry = ListEntry->Flink;
  1174. ++i;
  1175. }
  1176. FnExit:
  1177. FmpReleaseResourceLock();
  1178. return(Status);
  1179. } // FmEnumResourceProvider
  1180. DWORD
  1181. WINAPI
  1182. FmEnumResourceNode(
  1183. IN PFM_RESOURCE Resource,
  1184. IN DWORD Index,
  1185. OUT PNM_NODE *PossibleNode
  1186. )
  1187. /*++
  1188. Routine Description:
  1189. Enumerate the possible nodes for a resources.
  1190. Arguments:
  1191. Resource - The resource to enumerate.
  1192. Index - The index for this enumeration.
  1193. PossibleNode - The possible node. The returned node
  1194. pointer will be referenced by this routine and should
  1195. be dereferenced when the caller is done with it.
  1196. Returns:
  1197. ERROR_SUCCESS if successful.
  1198. A Win32 error code on failure.
  1199. --*/
  1200. {
  1201. PLIST_ENTRY ListEntry;
  1202. DWORD i = 0;
  1203. PFM_RESOURCE Current;
  1204. PPOSSIBLE_ENTRY PossibleEntry;
  1205. DWORD Status = ERROR_NO_MORE_ITEMS;
  1206. FmpMustBeOnline( );
  1207. FmpAcquireResourceLock();
  1208. if (!IS_VALID_FM_RESOURCE(Resource))
  1209. {
  1210. Status = ERROR_RESOURCE_NOT_AVAILABLE;
  1211. goto FnExit;
  1212. }
  1213. ListEntry = Resource->PossibleOwners.Flink;
  1214. while (ListEntry != &Resource->PossibleOwners) {
  1215. PossibleEntry = CONTAINING_RECORD(ListEntry,
  1216. POSSIBLE_ENTRY,
  1217. PossibleLinkage);
  1218. if (i==Index) {
  1219. //
  1220. // Got the right index
  1221. //
  1222. OmReferenceObject(PossibleEntry->PossibleNode);
  1223. *PossibleNode = PossibleEntry->PossibleNode;
  1224. Status = ERROR_SUCCESS;
  1225. break;
  1226. }
  1227. ListEntry = ListEntry->Flink;
  1228. ++i;
  1229. }
  1230. FnExit:
  1231. FmpReleaseResourceLock();
  1232. return(Status);
  1233. } // FmEnumResourceNode
  1234. DWORD
  1235. WINAPI
  1236. FmFailResource(
  1237. IN PFM_RESOURCE Resource
  1238. )
  1239. /*++
  1240. Routine Description:
  1241. Cause the specified resource to fail.
  1242. Arguments:
  1243. Resource - The resource to make fail.
  1244. Returns:
  1245. ERROR_SUCCESS - if successful.
  1246. A Win32 error code on failure.
  1247. --*/
  1248. {
  1249. FmpMustBeOnline( );
  1250. if ( Resource->Group->OwnerNode != NmLocalNode ) {
  1251. return(FmcFailResource( Resource ));
  1252. }
  1253. return(FmpRmFailResource( Resource ));
  1254. } // FmFailResource
  1255. DWORD
  1256. WINAPI
  1257. FmChangeResourceNode(
  1258. IN PFM_RESOURCE Resource,
  1259. IN PNM_NODE Node,
  1260. IN BOOL Add
  1261. )
  1262. /*++
  1263. Routine Description:
  1264. Changes the list of nodes where the specified resource
  1265. can be brought online.
  1266. Arguments:
  1267. Resource - Supplies the resource whose list of possible nodes is
  1268. to be modified.
  1269. Node - Supplies the node to be added to the resource's list.
  1270. Add - Supplies whether the specified node is to be added (TRUE) or
  1271. deleted (FALSE) from the resource's node list.
  1272. Return Value:
  1273. ERROR_SUCCESS if successful
  1274. Win32 error code otherwise
  1275. --*/
  1276. {
  1277. DWORD Status;
  1278. FmpAcquireLocalResourceLock( Resource );
  1279. if ( Resource->Group->OwnerNode != NmLocalNode ) {
  1280. // Note: FmcChangeResourceNode must release the resource lock.
  1281. Status = FmcChangeResourceNode( Resource, Node, Add );
  1282. }
  1283. else
  1284. {
  1285. Status = FmpChangeResourceNode(Resource, OmObjectId(Node), Add);
  1286. FmpReleaseLocalResourceLock( Resource );
  1287. }
  1288. return(Status);
  1289. } // FmChangeResourceNode
  1290. DWORD
  1291. WINAPI
  1292. FmSetQuorumResource(
  1293. IN PFM_RESOURCE Resource,
  1294. IN LPCWSTR pszClusFileRootPath,
  1295. IN DWORD dwMaxQuorumLogSize
  1296. )
  1297. /*++
  1298. Routine Description:
  1299. Set the specified resource as the quorum resource. This requires making
  1300. sure that the specified resource can perform an arbitrate. We do this
  1301. by asking the owner node to perform an arbitrate of the resource.
  1302. Arguments:
  1303. Resource - Supplies the resource that must be arbitrated.
  1304. pszLogPathName - The root path where the log files will be moved. "Microsoft
  1305. Cluster Manager Directory" is created under the root path provided. If NULL,
  1306. a partition on the shared quorum device is picked up randomly. And
  1307. the log files are placed in the directory specified by the
  1308. CLUSTER_QUORUM_DEFAULT_MAX_LOG_SIZE constant at the root of that partition.
  1309. dwMaxQuorumLogSize - The maximum size of the quorum logs. If 0, the default
  1310. used. If smaller that 32K, 32K is used.
  1311. Returns:
  1312. ERROR_SUCCESS if successful.
  1313. A Win32 error code on failure.
  1314. --*/
  1315. {
  1316. DWORD status;
  1317. DWORD resourceIdLen;
  1318. PFM_RESOURCE quorumResource = NULL;
  1319. PFM_RESOURCE pOldQuoResource = NULL;
  1320. PVOID gumResource = NULL;
  1321. DWORD dwBytesReturned;
  1322. DWORD dwRequired;
  1323. DWORD dwBufSize;
  1324. WCHAR szQuoLogPath[MAX_PATH] = L"\0";
  1325. WCHAR szLogRootPath[MAX_PATH];
  1326. CLUS_RESOURCE_CLASS_INFO resClassInfo;
  1327. PUCHAR pBuf = NULL;
  1328. LPWSTR pszOldQuoLogPath = NULL;
  1329. LPWSTR pszNext = NULL;
  1330. LPWSTR pszExpClusFileRootPath = NULL;
  1331. DWORD dwCharacteristics;
  1332. FmpMustBeOnline( );
  1333. ClRtlLogPrint(LOG_NOISE,
  1334. "[FM] FmSetQuorumResource: Entry, pszClusFileRootPath=%1!ws!\r\n",
  1335. ((pszClusFileRootPath)? pszClusFileRootPath:szQuoLogPath));
  1336. // find the old quorum resource
  1337. status = FmFindQuorumResource(&pOldQuoResource);
  1338. if (status != ERROR_SUCCESS)
  1339. {
  1340. goto FnExit;
  1341. }
  1342. //
  1343. // Synchronize access to Quorum Resource changes.
  1344. //
  1345. //
  1346. // Synchronize both the old and the new resource.
  1347. // Lock the lowest by lowest Group Id first - to prevent deadlocks!
  1348. // Note - the order of release is unimportant.
  1349. //
  1350. // if the old and new resource belong to the same group
  1351. // the comparison will be be equal!
  1352. //
  1353. ACQUIRE_EXCLUSIVE_LOCK(gQuoChangeLock);
  1354. if ( lstrcmpiW( OmObjectId( pOldQuoResource->Group ),
  1355. OmObjectId( Resource->Group ) ) <= 0 ) {
  1356. FmpAcquireLocalGroupLock( pOldQuoResource->Group );
  1357. FmpAcquireLocalGroupLock( Resource->Group );
  1358. } else {
  1359. FmpAcquireLocalGroupLock( Resource->Group );
  1360. FmpAcquireLocalGroupLock( pOldQuoResource->Group );
  1361. }
  1362. if (Resource->State != ClusterResourceOnline)
  1363. {
  1364. status = ERROR_RESOURCE_NOT_ONLINE;
  1365. goto FnExit;
  1366. }
  1367. #if 0 // rodga - We can't guarantee the old quorum resource is available!!
  1368. // We want to be able to change this when the old quorum is dead.
  1369. //
  1370. // Chittur Subbaraman (chitturs) - 6/24/99
  1371. //
  1372. // Make sure the old quorum resource state is also online.
  1373. //
  1374. if ( pOldQuoResource->State != ClusterResourceOnline )
  1375. {
  1376. status = ERROR_RESOURCE_NOT_ONLINE;
  1377. goto FnExit;
  1378. }
  1379. #endif
  1380. if (!IsListEmpty(&Resource->DependsOn))
  1381. {
  1382. status = ERROR_DEPENDENCY_NOT_ALLOWED;
  1383. goto FnExit;
  1384. }
  1385. //
  1386. // Get the old log path.
  1387. //
  1388. dwBytesReturned = 0;
  1389. dwRequired = 0;
  1390. status = DmQuerySz( DmQuorumKey,
  1391. cszPath,
  1392. (LPWSTR*)&pszOldQuoLogPath,
  1393. &dwRequired,
  1394. &dwBytesReturned);
  1395. if (status != ERROR_SUCCESS) {
  1396. ClRtlLogPrint(LOG_UNUSUAL,
  1397. "[FM] FmSetQuorumResource Failed to get the old quo log path, error %1!u!.\n",
  1398. status);
  1399. goto FnExit;
  1400. }
  1401. //SS: if you want to have a sub dir for logging files
  1402. //check the resource class
  1403. status = FmResourceControl(Resource, NULL, CLUSCTL_RESOURCE_GET_CLASS_INFO, NULL, 0,
  1404. (PUCHAR)&resClassInfo, sizeof(resClassInfo), &dwBytesReturned, &dwRequired);
  1405. if ( status != ERROR_SUCCESS )
  1406. {
  1407. goto FnExit;
  1408. }
  1409. if ( (resClassInfo.rc != CLUS_RESCLASS_STORAGE) ||
  1410. ((resClassInfo.SubClass & CLUS_RESSUBCLASS_SHARED) == 0) )
  1411. {
  1412. status = ERROR_NOT_QUORUM_CLASS;
  1413. goto FnExit;
  1414. }
  1415. //allocate info for the disk info
  1416. //get disk info
  1417. dwBufSize = 2048;
  1418. Retry:
  1419. pBuf = LocalAlloc(LMEM_FIXED, dwBufSize);
  1420. if (pBuf == NULL ) {
  1421. status = ERROR_NOT_ENOUGH_MEMORY;
  1422. goto FnExit;
  1423. }
  1424. status = FmResourceControl(Resource, NULL, CLUSCTL_RESOURCE_STORAGE_GET_DISK_INFO,
  1425. NULL, 0, pBuf, dwBufSize, &dwBytesReturned, &dwRequired);
  1426. if ((status == ERROR_MORE_DATA) && (dwBufSize < dwRequired))
  1427. {
  1428. dwBufSize = dwRequired;
  1429. LocalFree(pBuf);
  1430. goto Retry;
  1431. }
  1432. if (status != ERROR_SUCCESS)
  1433. {
  1434. goto FnExit;
  1435. }
  1436. if (pszClusFileRootPath)
  1437. pszExpClusFileRootPath = ClRtlExpandEnvironmentStrings(pszClusFileRootPath);
  1438. //use the expanded path name for validation
  1439. if (pszExpClusFileRootPath)
  1440. {
  1441. WCHAR cColon=L':';
  1442. pszNext = wcschr(pszExpClusFileRootPath, cColon);
  1443. //pick up just the drive letter
  1444. if (pszNext)
  1445. {
  1446. lstrcpynW(szLogRootPath, pszExpClusFileRootPath,
  1447. (UINT)(pszNext-pszExpClusFileRootPath+2));
  1448. }
  1449. else
  1450. {
  1451. //if there is no drive letter, pick up a drive letter at random
  1452. szLogRootPath[0] = L'\0';
  1453. }
  1454. }
  1455. else
  1456. {
  1457. szLogRootPath[0] = L'\0';
  1458. }
  1459. ClRtlLogPrint(LOG_NOISE,
  1460. "[FM] FmSetQuorumResource: szLogRootPath=%1!ws!\r\n",
  1461. szLogRootPath);
  1462. //save the drive letter for the new quorum path
  1463. status = FmpGetDiskInfoParseProperties(pBuf, dwBytesReturned, szLogRootPath);
  1464. //if the status was invalid parameter for a local quorum, ignore the local
  1465. //quorum path setting..what is specified through this api overrides
  1466. if (FmpGetResourceCharacteristics(Resource, &dwCharacteristics) == ERROR_SUCCESS)
  1467. {
  1468. if ((status == ERROR_INVALID_PARAMETER) &&
  1469. (dwCharacteristics & CLUS_CHAR_LOCAL_QUORUM))
  1470. {
  1471. status = ERROR_SUCCESS;
  1472. ClRtlLogPrint(LOG_NOISE,
  1473. "[FM] FmSetQuorumResource: LocalQuorum force success, szLogRootPath=%1!ws!\r\n",
  1474. szLogRootPath);
  1475. }
  1476. }
  1477. else
  1478. {
  1479. status = GetLastError();
  1480. ClRtlLogPrint(LOG_NOISE,
  1481. "[FM] FmSetQuorumResource: getresourcecharacteristics failed, status=%1!u!\r\n",
  1482. szLogRootPath);
  1483. goto FnExit;
  1484. }
  1485. ClRtlLogPrint(LOG_NOISE,
  1486. "[FM] FmSetQuorumResource: szLogRootPath=%1!ws!\r\n",
  1487. szLogRootPath);
  1488. if (status != ERROR_SUCCESS)
  1489. goto FnExit;
  1490. if (szLogRootPath[0] == L'\0')
  1491. {
  1492. //no valid drive letter is found
  1493. status = ERROR_INVALID_PARAMETER;
  1494. goto FnExit;
  1495. }
  1496. //got the drive letter
  1497. lstrcpyW(szQuoLogPath, szLogRootPath);
  1498. if (pszNext)
  1499. {
  1500. // if the driver letter was supplied, append the rest of the path
  1501. lstrcatW(szQuoLogPath, pszNext+1);
  1502. }
  1503. else
  1504. {
  1505. //if no drive letter was supplied
  1506. // if a path was supplied, append the path
  1507. if (pszExpClusFileRootPath)
  1508. {
  1509. //an smb path is valid only for a local quorum
  1510. //if it is a local quorum and if an smb path is specified
  1511. //override the local quorum's path setting
  1512. if ((lstrlenW(pszExpClusFileRootPath) >=2) &&
  1513. (pszExpClusFileRootPath[0] == L'\\') &&
  1514. (pszExpClusFileRootPath[1] == L'\\') &&
  1515. (dwCharacteristics & CLUS_CHAR_QUORUM))
  1516. {
  1517. lstrcpyW(szQuoLogPath, L"\\\\?\\UNC\\");
  1518. lstrcatW(szQuoLogPath, pszExpClusFileRootPath+2);
  1519. }
  1520. else if (pszExpClusFileRootPath[0] == L'\\')
  1521. {
  1522. lstrcatW(szQuoLogPath, pszExpClusFileRootPath);
  1523. }
  1524. else
  1525. {
  1526. lstrcatW( szQuoLogPath, L"\\" );
  1527. lstrcatW(szQuoLogPath, pszExpClusFileRootPath);
  1528. }
  1529. }
  1530. else
  1531. {
  1532. // else append the default path
  1533. lstrcatW( szQuoLogPath, L"\\" );
  1534. lstrcatW(szQuoLogPath, CLUS_NAME_DEFAULT_FILESPATH);
  1535. }
  1536. }
  1537. //if the path name is provided, check if it is terminated with '\'
  1538. //if not, terminate it
  1539. if (szQuoLogPath[lstrlenW(szQuoLogPath) - 1] != L'\\')
  1540. {
  1541. lstrcatW( szQuoLogPath, L"\\" );
  1542. }
  1543. ClRtlLogPrint(LOG_NOISE,
  1544. "[FM] FmSetQuorumResource: szQuoLogPath=%1!ws!\r\n",
  1545. szQuoLogPath);
  1546. //
  1547. // Allocate a message buffer.
  1548. //
  1549. resourceIdLen = (lstrlenW(OmObjectId(Resource))+1) * sizeof(WCHAR);
  1550. gumResource = LocalAlloc(LMEM_FIXED, resourceIdLen);
  1551. if (gumResource == NULL)
  1552. {
  1553. status = ERROR_NOT_ENOUGH_MEMORY;
  1554. goto FnExit;
  1555. }
  1556. //
  1557. // Fill in message buffer.
  1558. //
  1559. CopyMemory(gumResource, OmObjectId(Resource), resourceIdLen);
  1560. //
  1561. // Make sure that we can arbitrate the new quorum resource.
  1562. //
  1563. if ( Resource->Group->OwnerNode != NmLocalNode ) {
  1564. status = FmcArbitrateResource( Resource );
  1565. } else {
  1566. status = FmpRmArbitrateResource( Resource );
  1567. }
  1568. if ( status != ERROR_SUCCESS ) {
  1569. goto FnExit;
  1570. }
  1571. //check the log size, if it not zero but less than the min
  1572. //limit set it to 32K.
  1573. if ((dwMaxQuorumLogSize) && (dwMaxQuorumLogSize < CLUSTER_QUORUM_MIN_LOG_SIZE))
  1574. {
  1575. dwMaxQuorumLogSize = CLUSTER_QUORUM_MIN_LOG_SIZE;
  1576. }
  1577. //Prepare to move to a new quorum resource
  1578. //create a new quorum log file and
  1579. //move the registry files there.
  1580. if ( Resource->Group->OwnerNode != NmLocalNode ) {
  1581. status = FmcPrepareQuorumResChange( Resource, szQuoLogPath, dwMaxQuorumLogSize );
  1582. } else {
  1583. status = FmpPrepareQuorumResChange( Resource, szQuoLogPath, dwMaxQuorumLogSize );
  1584. }
  1585. if ( status != ERROR_SUCCESS ) {
  1586. if (dwCharacteristics & CLUS_CHAR_LOCAL_QUORUM)
  1587. {
  1588. ClRtlLogPrint(LOG_NOISE,
  1589. "[FM] FmSetQuorumResource: Local quorum, map FmpPrepareQuorumResChange to success\r\n");
  1590. status = ERROR_SUCCESS;
  1591. }
  1592. else
  1593. goto FnExit;
  1594. }
  1595. //
  1596. // Send the message.
  1597. //
  1598. status = GumSendUpdateEx(GumUpdateFailoverManager,
  1599. FmUpdateChangeQuorumResource,
  1600. 3,
  1601. resourceIdLen,
  1602. gumResource,
  1603. (lstrlenW(szQuoLogPath) + 1 ) * sizeof(WCHAR),
  1604. szQuoLogPath,
  1605. sizeof(DWORD),
  1606. &dwMaxQuorumLogSize
  1607. );
  1608. //if the old path is not the same as the new path
  1609. //create a tombstone for the quorum log files on the old path
  1610. //this is to prevent nodes that are not present in this update
  1611. //from doing a form.
  1612. if ( (status == ERROR_SUCCESS) &&
  1613. (lstrcmpiW(szQuoLogPath, pszOldQuoLogPath)) ) {
  1614. //
  1615. // delete the old quorum log files on the old resource and create a tombstone file
  1616. // in there.
  1617. //
  1618. if ( pOldQuoResource->Group->OwnerNode != NmLocalNode ) {
  1619. status = FmcCompleteQuorumResChange( pOldQuoResource, pszOldQuoLogPath );
  1620. } else {
  1621. status = FmpCompleteQuorumResChange( OmObjectId(pOldQuoResource), pszOldQuoLogPath );
  1622. }
  1623. }
  1624. FnExit:
  1625. //not the order of release is not important
  1626. FmpReleaseLocalGroupLock(pOldQuoResource->Group);
  1627. FmpReleaseLocalGroupLock(Resource->Group);
  1628. RELEASE_LOCK(gQuoChangeLock);
  1629. if (pBuf) LocalFree(pBuf);
  1630. if (gumResource) LocalFree(gumResource);
  1631. if (pOldQuoResource) OmDereferenceObject(pOldQuoResource);
  1632. if (pszOldQuoLogPath) LocalFree(pszOldQuoLogPath);
  1633. if (pszExpClusFileRootPath) LocalFree(pszExpClusFileRootPath);
  1634. ClRtlLogPrint(LOG_NOISE,
  1635. "[FM] FmSetQuorumResource: Exit, status=%1!u!\r\n",
  1636. status);
  1637. return(status);
  1638. } // FmSetQuorumResource
  1639. DWORD
  1640. FmCreateResourceType(
  1641. IN LPCWSTR lpszTypeName,
  1642. IN LPCWSTR lpszDisplayName,
  1643. IN LPCWSTR lpszDllName,
  1644. IN DWORD dwLooksAlive,
  1645. IN DWORD dwIsAlive
  1646. )
  1647. /*++
  1648. Routine Description:
  1649. Issues a GUM update to instantiate a resource type on every
  1650. node. The registry update as well as the FM in-memory state
  1651. update is done as a transaction within the GUM handler (NT5
  1652. clusters only).
  1653. Arguments:
  1654. lpszTypeName - Supplies the name of the new cluster resource type.
  1655. lpszDisplayName - Supplies the display name for the new resource
  1656. type. While lpszResourceTypeName should uniquely identify the
  1657. resource type on all clusters, the lpszDisplayName should be
  1658. a localized friendly name for the resource, suitable for displaying
  1659. to administrators.
  1660. lpszDllName - Supplies the name of the new resource types DLL.
  1661. dwLooksAlive - Supplies the default LooksAlive poll interval
  1662. for the new resource type in milliseconds.
  1663. dwIsAlive - Supplies the default IsAlive poll interval for
  1664. the new resource type in milliseconds.
  1665. Return Value:
  1666. ERROR_SUCCESS if successful.
  1667. Win32 error otherwise.
  1668. --*/
  1669. {
  1670. DWORD dwStatus = ERROR_SUCCESS;
  1671. PFM_RESTYPE pResType = NULL;
  1672. DWORD dwTypeNameLen;
  1673. DWORD dwDisplayNameLen;
  1674. DWORD dwDllNameLen;
  1675. DWORD dwBufferLen;
  1676. LPVOID Buffer = NULL;
  1677. //
  1678. // Chittur Subbaraman (chitturs) - 2/8/2000
  1679. //
  1680. // Rewrite this API to use a GUM handler which performs a local
  1681. // transaction for NT5.1
  1682. //
  1683. ClRtlLogPrint(LOG_NOISE,
  1684. "[FM] FmCreateResourceType: Entry for %1!ws!...\r\n",
  1685. lpszTypeName);
  1686. dwTypeNameLen = ( lstrlenW( lpszTypeName ) + 1 ) * sizeof( WCHAR );
  1687. dwDisplayNameLen = ( lstrlenW( lpszDisplayName ) + 1 ) * sizeof( WCHAR );
  1688. dwDllNameLen = ( lstrlenW( lpszDllName ) + 1 ) * sizeof( WCHAR );
  1689. dwBufferLen = dwTypeNameLen + dwDisplayNameLen + dwDllNameLen +
  1690. 2 * sizeof( DWORD );
  1691. Buffer = LocalAlloc( LMEM_FIXED, dwBufferLen );
  1692. if ( Buffer == NULL )
  1693. {
  1694. CsInconsistencyHalt( GetLastError() );
  1695. }
  1696. CopyMemory( Buffer, lpszTypeName, dwTypeNameLen );
  1697. CopyMemory( ( PCHAR ) Buffer + dwTypeNameLen, lpszDisplayName, dwDisplayNameLen );
  1698. CopyMemory( ( PCHAR ) Buffer + dwTypeNameLen + dwDisplayNameLen, lpszDllName, dwDllNameLen );
  1699. CopyMemory( ( PCHAR ) Buffer +
  1700. dwTypeNameLen +
  1701. dwDisplayNameLen +
  1702. dwDllNameLen, &dwLooksAlive, sizeof( DWORD ) );
  1703. CopyMemory( ( PCHAR ) Buffer +
  1704. dwTypeNameLen +
  1705. dwDisplayNameLen +
  1706. dwDllNameLen + sizeof( DWORD ), &dwIsAlive, sizeof( DWORD ) );
  1707. dwStatus = GumSendUpdate( GumUpdateFailoverManager,
  1708. FmUpdateCreateResourceType,
  1709. dwBufferLen,
  1710. Buffer );
  1711. if ( dwStatus != ERROR_SUCCESS )
  1712. {
  1713. ClRtlLogPrint(LOG_CRITICAL,
  1714. "[FM] FmCreateResourceType: FmUpdateCreateResourceType for %1!ws! returned %2!u!...\r\n",
  1715. lpszTypeName,
  1716. dwStatus);
  1717. goto FnExit;
  1718. }
  1719. dwStatus = FmpSetPossibleNodeForResType( lpszTypeName , FALSE );
  1720. if ( dwStatus != ERROR_SUCCESS )
  1721. {
  1722. ClRtlLogPrint(LOG_CRITICAL,
  1723. "[FM] FmCreateResourceType: FmpSetPossibleNodeForResType for %2!ws! returned <%1!u!>...\r\n",
  1724. lpszTypeName,
  1725. dwStatus);
  1726. goto FnExit;
  1727. }
  1728. pResType = OmReferenceObjectById( ObjectTypeResType, lpszTypeName );
  1729. ClusterWideEvent( CLUSTER_EVENT_RESTYPE_ADDED, pResType );
  1730. OmDereferenceObject( pResType );
  1731. FnExit:
  1732. LocalFree( Buffer );
  1733. ClRtlLogPrint(LOG_NOISE,
  1734. "[FM] FmCreateResourceType: Exit for %1!ws!, Status=%2!u!...\r\n",
  1735. lpszTypeName,
  1736. dwStatus);
  1737. return( dwStatus );
  1738. } // FmCreateResourceType
  1739. DWORD
  1740. FmDeleteResourceType(
  1741. IN LPCWSTR TypeName
  1742. )
  1743. /*++
  1744. Routine Description:
  1745. Issues a GUM update to delete a resource type on every
  1746. node.
  1747. Arguments:
  1748. TypeName - Supplies the name of the cluster resource type
  1749. to delete
  1750. Return Value:
  1751. ERROR_SUCCESS if successful.
  1752. Win32 error otherwise.
  1753. --*/
  1754. {
  1755. PFM_RESTYPE pResType;
  1756. //
  1757. // Chittur Subbaraman (chitturs) - 5/9/2001
  1758. //
  1759. // Make sure the resource type exists so that you can avoid a GUM if
  1760. // that is not necessary. This also takes care of the case in which one node was
  1761. // shutting down and so the GUM returns success and another node fails in the GUM
  1762. // and gets evicted since the resource type does not exist.
  1763. //
  1764. pResType = OmReferenceObjectById( ObjectTypeResType,
  1765. TypeName );
  1766. if ( pResType == NULL )
  1767. {
  1768. ClRtlLogPrint(LOG_UNUSUAL,
  1769. "[FM] FmDeleteResourceType: Resource type %1!ws! does not exist...\n",
  1770. TypeName);
  1771. return( ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND );
  1772. }
  1773. OmDereferenceObject ( pResType );
  1774. return(GumSendUpdate( GumUpdateFailoverManager,
  1775. FmUpdateDeleteResourceType,
  1776. (lstrlenW(TypeName)+1)*sizeof(WCHAR),
  1777. (PVOID)TypeName ));
  1778. } // FmDeleteResourceType
  1779. /****
  1780. @func DWORD | FmEnumResTypeNode | Enumerate the possible nodes for
  1781. a resource type
  1782. @parm IN PFM_RESTYPE | pResType | Pointer to the resource type
  1783. @parm IN DWORD | dwIndex | The index for this enumeration.
  1784. @parm OUT PNM_NODE | pPossibleNode | The possible node. The returned node
  1785. pointer will be referenced by this routine and should
  1786. be dereferenced when the caller is done with it.
  1787. @comm This routine helps enumerating all the nodes that a particular
  1788. resource type can be supported on.
  1789. @rdesc Returns a result code. ERROR_SUCCESS on success.
  1790. @xref
  1791. ****/
  1792. DWORD
  1793. FmEnumResourceTypeNode(
  1794. IN PFM_RESTYPE pResType,
  1795. IN DWORD dwIndex,
  1796. OUT PNM_NODE *pPossibleNode
  1797. )
  1798. {
  1799. PLIST_ENTRY pListEntry;
  1800. DWORD i = 0;
  1801. PRESTYPE_POSSIBLE_ENTRY pResTypePosEntry;
  1802. DWORD Status = ERROR_NO_MORE_ITEMS;
  1803. FmpMustBeOnline();
  1804. //
  1805. // Chittur Subbaraman (chitturs) - 09/06/98
  1806. //
  1807. // The creation and deletion of resource types are done
  1808. // via atomic GUM operations. Hence these two operations
  1809. // (i.e. API's) are guaranteed to be mutually exclusive.
  1810. // In contrast, the resource type enumeration operation
  1811. // is not mutually exclusive with either the create
  1812. // or the delete operation. Thus, when a resource type is
  1813. // being created/deleted, there is nothing that prevents a
  1814. // client from trying to enumerate the same resource type
  1815. // in a concurrent fashion, thus producing a potential race
  1816. // condition. Thus, it is advisable to consider some form
  1817. // of locking to avoid this situation !
  1818. //
  1819. // update the list to include all nodes that now support
  1820. // the resource type
  1821. if (dwIndex == 0)
  1822. FmpSetPossibleNodeForResType(OmObjectId(pResType), TRUE);
  1823. ACQUIRE_SHARED_LOCK(gResTypeLock);
  1824. pListEntry = pResType->PossibleNodeList.Flink;
  1825. while (pListEntry != &pResType->PossibleNodeList) {
  1826. pResTypePosEntry = CONTAINING_RECORD(pListEntry,
  1827. RESTYPE_POSSIBLE_ENTRY,
  1828. PossibleLinkage);
  1829. if (i==dwIndex) {
  1830. //
  1831. // Got the right index
  1832. //
  1833. OmReferenceObject(pResTypePosEntry->PossibleNode);
  1834. *pPossibleNode = pResTypePosEntry->PossibleNode;
  1835. Status = ERROR_SUCCESS;
  1836. break;
  1837. }
  1838. pListEntry = pListEntry->Flink;
  1839. ++i;
  1840. }
  1841. RELEASE_LOCK(gResTypeLock);
  1842. return(Status);
  1843. } // FmEnumResTypeNode
  1844. DWORD
  1845. FmChangeResourceGroup(
  1846. IN PFM_RESOURCE pResource,
  1847. IN PFM_GROUP pNewGroup
  1848. )
  1849. /*++
  1850. Routine Description:
  1851. Moves a resource from one group to another.
  1852. Arguments:
  1853. Resource - Supplies the resource to move.
  1854. Group - Supplies the new group that the resource should be in.
  1855. Return Value:
  1856. ERROR_SUCCESS if successful
  1857. Win32 error code otherwise.
  1858. --*/
  1859. {
  1860. DWORD dwStatus;
  1861. PFM_GROUP pOldGroup;
  1862. FmpMustBeOnline( );
  1863. ClRtlLogPrint(LOG_NOISE,
  1864. "[FM] FmChangeResourceGroup : Resource <%1!ws!> NewGroup %2!lx!\n",
  1865. OmObjectId( pResource ),
  1866. OmObjectId( pNewGroup));
  1867. //
  1868. // Synchronize both the old and the new groups.
  1869. // Lock the lowest by lowest Group Id first - to prevent deadlocks!
  1870. // Note - the order of release is unimportant.
  1871. //
  1872. // strictly, the comparison below cannot be equal!
  1873. //
  1874. if ( lstrcmpiW( OmObjectId( pResource->Group ), OmObjectId( pNewGroup ) ) <= 0 ) {
  1875. FmpAcquireLocalGroupLock( pResource->Group );
  1876. FmpAcquireLocalGroupLock( pNewGroup );
  1877. } else {
  1878. FmpAcquireLocalGroupLock( pNewGroup );
  1879. FmpAcquireLocalGroupLock( pResource->Group );
  1880. }
  1881. //remember the old group for freeing locks
  1882. pOldGroup = pResource->Group;
  1883. //if the resource has been marked for delete, then fail this call
  1884. if (!IS_VALID_FM_RESOURCE(pResource))
  1885. {
  1886. dwStatus = ERROR_RESOURCE_NOT_AVAILABLE;
  1887. goto FnUnlock;
  1888. }
  1889. //
  1890. // Check if we're moving to same group.
  1891. //
  1892. if (pResource->Group == pNewGroup) {
  1893. dwStatus = ERROR_ALREADY_EXISTS;
  1894. goto FnUnlock;
  1895. }
  1896. if ( pResource->Group->OwnerNode != NmLocalNode ) {
  1897. // Note: FmcChangeResourceNode must release the both resource lock.
  1898. dwStatus = FmcChangeResourceGroup( pResource, pNewGroup);
  1899. goto FnExit;
  1900. }
  1901. else
  1902. {
  1903. dwStatus = FmpChangeResourceGroup(pResource, pNewGroup );
  1904. }
  1905. FnUnlock:
  1906. FmpReleaseLocalGroupLock(pNewGroup);
  1907. FmpReleaseLocalGroupLock(pOldGroup);
  1908. FnExit:
  1909. ClRtlLogPrint(LOG_NOISE,
  1910. "[FM] FmChangeResourceGroup : returned <%1!u!>\r\n",
  1911. dwStatus);
  1912. return(dwStatus);
  1913. } // FmChangeResourceGroup
  1914. DWORD
  1915. FmChangeClusterName(
  1916. IN LPCWSTR NewName
  1917. )
  1918. /*++
  1919. Routine Description:
  1920. Changes the name of the cluster
  1921. Arguments:
  1922. NewName - Supplies the new cluster name.
  1923. Return Value:
  1924. ERROR_SUCCESS if successful. ERROR_RESOURCE_PROPERTIES STORED if the name
  1925. has been changed but wont be effective until the core network name resource
  1926. is brought online again.
  1927. Win32 error code otherwise
  1928. --*/
  1929. {
  1930. DWORD Status=ERROR_INVALID_PARAMETER;
  1931. Status = GumSendUpdateEx(GumUpdateFailoverManager,
  1932. FmUpdateChangeClusterName,
  1933. 1,
  1934. (lstrlenW(NewName)+1)*sizeof(WCHAR),
  1935. NewName);
  1936. //the core network name property/cluster name has been set
  1937. //but the name change isnt effective till the resource is brought
  1938. //online again
  1939. if (Status == ERROR_SUCCESS) {
  1940. Status = ERROR_RESOURCE_PROPERTIES_STORED;
  1941. }
  1942. return(Status);
  1943. } // FmChangeClusterName
  1944. DWORD
  1945. FmpSetResourceName(
  1946. IN PFM_RESOURCE pResource,
  1947. IN LPCWSTR lpszFriendlyName
  1948. )
  1949. /*++
  1950. Routine Description:
  1951. Updates the resource name consistently in the fm databases across
  1952. the cluster.
  1953. Arguments:
  1954. pResource - The resource whose name is changed.
  1955. lpszFriendlyName - The new name of the resource.
  1956. Return Value:
  1957. ERROR_SUCCESS if successful.
  1958. A Win32 error code on failure.
  1959. --*/
  1960. {
  1961. LPCWSTR ResourceId;
  1962. DWORD Status;
  1963. ResourceId = OmObjectId(pResource);
  1964. return(GumSendUpdateEx( GumUpdateFailoverManager,
  1965. FmUpdateChangeResourceName,
  1966. 2,
  1967. (lstrlenW(ResourceId)+1)*sizeof(WCHAR),
  1968. ResourceId,
  1969. (lstrlenW(lpszFriendlyName)+1)*sizeof(WCHAR),
  1970. lpszFriendlyName ));
  1971. } // FmpSetResourceName
  1972. DWORD
  1973. FmpRegUpdateClusterName(
  1974. IN LPCWSTR szNewClusterName
  1975. )
  1976. /*++
  1977. Routine Description:
  1978. This routine updates the cluster name in the cluster database.
  1979. Arguments:
  1980. szNewClusterName - A pointer to the new cluster name string.
  1981. Return Value:
  1982. ERROR_SUCCESS if successful.
  1983. A Win32 error on failure.
  1984. --*/
  1985. {
  1986. return(DmSetValue( DmClusterParametersKey,
  1987. CLUSREG_NAME_CLUS_NAME,
  1988. REG_SZ,
  1989. (CONST BYTE *)szNewClusterName,
  1990. (lstrlenW(szNewClusterName)+1)*sizeof(WCHAR) ));
  1991. } // FmpRegUpdateClusterName
  1992. DWORD
  1993. FmEvictNode(
  1994. IN PNM_NODE Node
  1995. )
  1996. /*++
  1997. Routine Description:
  1998. Removes any references to the specified node that the FM might
  1999. have put on.
  2000. Arguments:
  2001. Node - Supplies the node that is being evicted.
  2002. Return Value:
  2003. ERROR_SUCCESS if successful
  2004. Win32 error code otherwise
  2005. --*/
  2006. {
  2007. //add a reference to the node object, the worker thread will remove this
  2008. OmReferenceObject(Node);
  2009. FmpPostWorkItem(FM_EVENT_NODE_EVICTED,
  2010. Node,
  2011. 0);
  2012. return(ERROR_SUCCESS);
  2013. } // FmEvictNode
  2014. BOOL
  2015. FmCheckNetworkDependency(
  2016. IN LPCWSTR DependentNetwork
  2017. )
  2018. /*++
  2019. Routine Description:
  2020. Check if any IP Address resource has a dependency on a given network.
  2021. Arguments:
  2022. DependentNetwork - the GUID for the network to check.
  2023. Return Value:
  2024. TRUE - if an IP Address resource depends on the given network.
  2025. FALSE otherwise.
  2026. --*/
  2027. {
  2028. return( FmpCheckNetworkDependency( DependentNetwork ) );
  2029. } // FmCheckNetworkDependency
  2030. DWORD
  2031. WINAPI
  2032. FmBackupClusterDatabase(
  2033. IN LPCWSTR lpszPathName
  2034. )
  2035. /*++
  2036. Routine Description:
  2037. Attempts a backup of the quorum log files.
  2038. Arguments:
  2039. lpszPathName - The directory path name where the files have to be
  2040. backed up. This path must be visible to the node
  2041. on which the quorum resource is online.
  2042. Returns:
  2043. ERROR_SUCCESS if successful.
  2044. A Win32 error code on failure.
  2045. --*/
  2046. {
  2047. DWORD status;
  2048. PFM_RESOURCE pQuoResource = NULL;
  2049. FmpMustBeOnline( );
  2050. //
  2051. // Chittur Subbaraman (chitturs) - 10/12/98
  2052. //
  2053. // Find the quorum resource
  2054. //
  2055. status = FmFindQuorumResource( &pQuoResource );
  2056. if ( status != ERROR_SUCCESS )
  2057. {
  2058. ClRtlLogPrint(LOG_UNUSUAL,
  2059. "[FM] FmBackupQuorumLog: Could not find quorum resource...\r\n");
  2060. goto FnExit;
  2061. }
  2062. //
  2063. // Acquire the local resource lock
  2064. //
  2065. FmpAcquireLocalResourceLock( pQuoResource );
  2066. //
  2067. // Handle the request here if this node is the owner of the
  2068. // quorum resource, else redirect it to the appropriate node.
  2069. //
  2070. if ( pQuoResource->Group->OwnerNode != NmLocalNode )
  2071. {
  2072. //
  2073. // This function will release the resource lock
  2074. //
  2075. status = FmcBackupClusterDatabase( pQuoResource, lpszPathName );
  2076. } else
  2077. {
  2078. status = FmpBackupClusterDatabase( pQuoResource, lpszPathName );
  2079. FmpReleaseLocalResourceLock( pQuoResource );
  2080. }
  2081. FnExit:
  2082. return( status );
  2083. } // FmBackupClusterDatabase
  2084. DWORD
  2085. FmpBackupClusterDatabase(
  2086. IN PFM_RESOURCE pQuoResource,
  2087. IN LPCWSTR lpszPathName
  2088. )
  2089. /*++
  2090. Routine Description:
  2091. This routine first waits until the quorum resource becomes
  2092. online. Then, it attempts to backup the quorum log file and the
  2093. checkpoint file to the specified directory path. This function
  2094. is called with the local resource lock held.
  2095. Arguments:
  2096. pQuoResource - Pointer to the quorum resource.
  2097. lpszPathName - The directory path name where the files have to be
  2098. backed up. This path must be visible to the node
  2099. on which the quorum resource is online.
  2100. Comments:
  2101. The order in which the locks are acquired is very crucial here.
  2102. Carelessness in following this strict order of acquisition can lead
  2103. to potential deadlocks. The order that is followed is
  2104. (1) Local resource lock - pQuoResource->Group->Lock acquired
  2105. outside this function.
  2106. (2) Global quorum resource lock - gQuoLock acquired here
  2107. (3) Global Dm root lock - gLockDmpRoot acquired in
  2108. DmBackupClusterDatabase( ).
  2109. --*/
  2110. {
  2111. DWORD retry = 200;
  2112. DWORD Status = ERROR_SUCCESS;
  2113. CL_ASSERT( pQuoResource->Group->OwnerNode == NmLocalNode );
  2114. //
  2115. // Chittur Subbaraman (chitturs) - 10/12/1998
  2116. //
  2117. // If quorum logging is not turned on, then log an error
  2118. // and exit immediately.
  2119. //
  2120. if ( CsNoQuorumLogging )
  2121. {
  2122. Status = ERROR_QUORUMLOG_OPEN_FAILED;
  2123. CL_LOGFAILURE( ERROR_QUORUMLOG_OPEN_FAILED );
  2124. ClRtlLogPrint(LOG_NOISE,
  2125. "[FM] FmpBackupClusterDatabase: Quorum logging is not turned on, can't backup...\r\n");
  2126. goto FnExit;
  2127. }
  2128. CheckQuorumState:
  2129. ACQUIRE_EXCLUSIVE_LOCK( gQuoLock );
  2130. //
  2131. // Check the state of the quorum resource. If it has failed or is
  2132. // offline, release the lock and exit immediately !
  2133. //
  2134. if ( pQuoResource->State == ClusterResourceFailed )
  2135. {
  2136. Status = ERROR_QUORUM_RESOURCE_ONLINE_FAILED;
  2137. CL_LOGFAILURE( ERROR_QUORUM_RESOURCE_ONLINE_FAILED );
  2138. ClRtlLogPrint(LOG_NOISE,
  2139. "[FM] FmpBackupClusterDatabase: Quorum resource is in failed state, exiting...\r\n");
  2140. RELEASE_LOCK( gQuoLock );
  2141. goto FnExit;
  2142. }
  2143. //
  2144. // Check if the quorum resource is online. If the quorum resource
  2145. // is marked as waiting and offlinepending, it is actually online.
  2146. // If the quorum resource still needs to come online, release the
  2147. // lock and wait.
  2148. //
  2149. if ( ( ( pQuoResource->State != ClusterResourceOnline ) &&
  2150. ( ( pQuoResource->State != ClusterResourceOfflinePending ) ||
  2151. ( !( pQuoResource->Flags & RESOURCE_WAITING ) ) ) )
  2152. )
  2153. {
  2154. //
  2155. // We release the lock here since the quorum resource
  2156. // state transition from pending needs to acquire the lock.
  2157. // In general it is a bad idea to do a wait holding locks.
  2158. //
  2159. RELEASE_LOCK( gQuoLock );
  2160. ClRtlLogPrint(LOG_NOISE,
  2161. "[FM] FmpBackupClusterDatabase: Release ghQuoLock and wait on ghQuoOnlineEvent...\r\n");
  2162. Status = WaitForSingleObject( ghQuoOnlineEvent, 500 );
  2163. if ( Status == WAIT_OBJECT_0 )
  2164. {
  2165. //
  2166. // If we are going to retry, wait a little bit and retry.
  2167. //
  2168. Sleep( 500 );
  2169. }
  2170. if ( retry-- )
  2171. {
  2172. goto CheckQuorumState;
  2173. }
  2174. CL_LOGFAILURE( ERROR_QUORUM_RESOURCE_ONLINE_FAILED ) ;
  2175. ClRtlLogPrint(LOG_NOISE,
  2176. "[FM] FmpBackupClusterDatabase: All retries to check for quorum resource online failed, exiting...\r\n");
  2177. return( ERROR_QUORUM_RESOURCE_ONLINE_FAILED );
  2178. }
  2179. Status = DmBackupClusterDatabase( lpszPathName );
  2180. RELEASE_LOCK( gQuoLock );
  2181. FnExit:
  2182. return ( Status );
  2183. } // FmpBackupClusterDatabase
  2184. /****
  2185. @func WORD| FmCheckQuorumState| If the quorum resource is online
  2186. on this node right now, it calls the callback and the boolean
  2187. value passed in is set to FALSE. If not, the boolean is
  2188. set to TRUE.
  2189. @parm LPWSTR | szQuorumLogPath | A pointer to a wide string of size MAX_PATH.
  2190. @parm DWORD | dwSize | The size of szQuorumLogPath in bytes.
  2191. @rdesc Returns ERROR_SUCCESS for success, else returns the error code.
  2192. @comm If the quorum resource is not cabaple of logging this should not be set.
  2193. @xref
  2194. ****/
  2195. void FmCheckQuorumState(
  2196. FM_ONLINE_ONTHISNODE_CB OnlineOnThisNodeCb,
  2197. PBOOL pbOfflineOnThisNode)
  2198. {
  2199. BOOL bLocked = FALSE;
  2200. DWORD dwRetryCount = 1200; // Wait 10 min max
  2201. //
  2202. // SS: The mutual exclusion between this event handler and
  2203. // the synchronous resource online/offline callback is
  2204. // achieved by using the quorum change lock(gQuoChangeLock)
  2205. //
  2206. //
  2207. // Chittur Subbaraman (chitturs) - 7/5/99
  2208. //
  2209. // Modify group lock acquisition to release gQuoChangeLock and
  2210. // retry lock acquisition. This is necessary to take care of the
  2211. // case in which the quorum online notification is stuck in
  2212. // FmpHandleResourceTransition waiting for the gQuoChangeLock and
  2213. // some other resource in the quorum group is stuck in FmpRmOnlineResource
  2214. // holding the quorum group lock and waiting for the quorum resource
  2215. // to go online.
  2216. //
  2217. try_acquire_lock:
  2218. ACQUIRE_EXCLUSIVE_LOCK( gQuoChangeLock );
  2219. FmpTryAcquireLocalGroupLock( gpQuoResource->Group, bLocked );
  2220. if ( bLocked == FALSE )
  2221. {
  2222. RELEASE_LOCK( gQuoChangeLock );
  2223. ClRtlLogPrint(LOG_NOISE,
  2224. "[FM] FmCheckQuorumState - Release gQuoChangeLock, sleep and retry group lock acquisition...\r\n");
  2225. if ( dwRetryCount == 0 )
  2226. {
  2227. ClRtlLogPrint(LOG_CRITICAL,
  2228. "[FM] FmCheckQuorumState - Unable to get quorum group lock for 10 min, halting...\r\n");
  2229. CsInconsistencyHalt( ERROR_LOCK_FAILED );
  2230. }
  2231. dwRetryCount --;
  2232. Sleep( 500 );
  2233. goto try_acquire_lock;
  2234. }
  2235. CL_ASSERT( bLocked == TRUE );
  2236. *pbOfflineOnThisNode = FALSE;
  2237. if (gpQuoResource->Group->OwnerNode == NmLocalNode)
  2238. {
  2239. ClRtlLogPrint(LOG_NOISE,
  2240. "[FM] FmCheckQuorumState - I am owner, check the state of the resource .\r\n");
  2241. //if the quorum resource is not online right now
  2242. //it might be in the middle of a move and this node
  2243. //might be the target of the move
  2244. //set a flag to indicate that a checkpoint is necessary
  2245. //when it does come online
  2246. if(gpQuoResource->State != ClusterResourceOnline)
  2247. {
  2248. ClRtlLogPrint(LOG_NOISE,
  2249. "[FM] FmCheckQuorumState - Quorum is owned but not online on this node.\r\n");
  2250. *pbOfflineOnThisNode = TRUE;
  2251. }
  2252. else
  2253. {
  2254. (*OnlineOnThisNodeCb)();
  2255. }
  2256. }
  2257. else
  2258. {
  2259. ClRtlLogPrint(LOG_NOISE,
  2260. "[FM] FmCheckQuorumState - Quorum is owned by another node.\r\n");
  2261. *pbOfflineOnThisNode = TRUE;
  2262. }
  2263. FmpReleaseLocalGroupLock(gpQuoResource->Group);
  2264. RELEASE_LOCK(gQuoChangeLock);
  2265. }
  2266. /****
  2267. @func WORD| FmDoesQuorumAllowJoin| If the quorum resource doesnt support
  2268. multiple nodes, return error. Added to officially support local quorum resources.
  2269. @rdesc Returns ERROR_SUCCESS for success, else returns the error code.
  2270. @comm If the quorum resource is not cabaple of logging this should not be set.
  2271. @xref
  2272. ****/
  2273. DWORD FmDoesQuorumAllowJoin()
  2274. {
  2275. DWORD dwStatus = ERROR_SUCCESS;
  2276. ACQUIRE_SHARED_LOCK(gQuoChangeLock);
  2277. ClRtlLogPrint(LOG_NOISE,
  2278. "[FM] FmDoesQuorumAllowJoin - Entry\r\n");
  2279. //get the characteristics for the new quorum resource
  2280. dwStatus = FmpGetResourceCharacteristics(gpQuoResource,
  2281. &(gpQuoResource->Characteristic));
  2282. if (dwStatus != ERROR_SUCCESS)
  2283. {
  2284. ClRtlLogPrint(LOG_UNUSUAL,
  2285. "[FM] FmDoesQuorumAllowJoin - couldnt get quorum characteristics %1!u!\r\n",
  2286. dwStatus);
  2287. goto FnExit;
  2288. }
  2289. if ((gpQuoResource->Characteristic & CLUS_CHAR_LOCAL_QUORUM) &&
  2290. !(gpQuoResource->Characteristic & CLUS_CHAR_LOCAL_QUORUM_DEBUG))
  2291. {
  2292. //Note :: we need an error code?
  2293. dwStatus = ERROR_OPERATION_ABORTED;
  2294. }
  2295. FnExit:
  2296. RELEASE_LOCK(gQuoChangeLock);
  2297. ClRtlLogPrint(LOG_NOISE,
  2298. "[FM] FmDoesQuorumAllowJoin - Exit, Status=%1!u!\r\n",
  2299. dwStatus);
  2300. return(dwStatus);
  2301. }
  2302. /****
  2303. @func WORD| FmDoesQuorumAllowLogging| If the quorum resource doesnt support
  2304. multiple nodes, return error. Added to officially support local quorum resources.
  2305. @rdesc Returns ERROR_SUCCESS for success, else returns the error code.
  2306. @comm If the quorum resource is not cabaple of logging this should not be set.
  2307. @xref
  2308. ****/
  2309. DWORD FmDoesQuorumAllowLogging()
  2310. {
  2311. DWORD dwStatus = ERROR_SUCCESS;
  2312. ACQUIRE_SHARED_LOCK(gQuoChangeLock);
  2313. ClRtlLogPrint(LOG_NOISE,
  2314. "[FM] FmDoesQuorumAllowLogging - Entry\r\n");
  2315. //get the characteristics for the new quorum resource
  2316. dwStatus = FmpGetResourceCharacteristics(gpQuoResource,
  2317. &(gpQuoResource->Characteristic));
  2318. if (dwStatus != ERROR_SUCCESS)
  2319. {
  2320. ClRtlLogPrint(LOG_UNUSUAL,
  2321. "[FM] FmDoesQuorumAllowLogging - couldnt get quorum characteristics %1!u!\r\n",
  2322. dwStatus);
  2323. goto FnExit;
  2324. }
  2325. if (gpQuoResource->Characteristic & CLUS_CHAR_LOCAL_QUORUM)
  2326. {
  2327. WCHAR szQuorumFileName[MAX_PATH];
  2328. //Note :: we need an error code?
  2329. //if the path is an smb path name, we should allow logging
  2330. //else we should disable it
  2331. dwStatus = DmGetQuorumLogPath(szQuorumFileName, sizeof(szQuorumFileName));
  2332. if ((szQuorumFileName[0] == L'\\') && (szQuorumFileName[1] == L'\\'))
  2333. {
  2334. //assume this is an smb path
  2335. //allow logging
  2336. dwStatus = ERROR_SUCCESS;
  2337. }
  2338. else
  2339. {
  2340. dwStatus = ERROR_OPERATION_ABORTED;
  2341. }
  2342. }
  2343. FnExit:
  2344. RELEASE_LOCK(gQuoChangeLock);
  2345. ClRtlLogPrint(LOG_NOISE,
  2346. "[FM] FmDoesQuorumAllowLogging - Exit, status=%1!u!\r\n",
  2347. dwStatus);
  2348. return(dwStatus);
  2349. }