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.

1375 lines
35 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. dmsync.c
  5. Abstract:
  6. Contains the registry synchronization code for the Cluster Database
  7. Manager.
  8. Author:
  9. John Vert (jvert) 5/23/1996
  10. Revision History:
  11. --*/
  12. #include "dmp.h"
  13. #if NO_SHARED_LOCKS
  14. extern CRITICAL_SECTION gLockDmpRoot;
  15. #else
  16. extern RTL_RESOURCE gLockDmpRoot;
  17. #endif
  18. const WCHAR DmpClusterParametersKeyName[] = L"Cluster";
  19. //
  20. // Private Constants
  21. //
  22. #define CHUNK_SIZE 4096
  23. //
  24. // Private macro
  25. //
  26. #define ClosePipe( _pipe ) \
  27. (_pipe.push)(_pipe.state, \
  28. NULL, \
  29. 0 ) \
  30. //
  31. // Client-Side Utility Routines
  32. //
  33. void
  34. FilePipePush(
  35. FILE_PIPE_STATE *state,
  36. unsigned char *pBuffer,
  37. unsigned long BufferSize
  38. )
  39. {
  40. DWORD dwBytesWritten;
  41. DWORD dwStatus;
  42. if (BufferSize != 0) {
  43. if (!WriteFile (state->hFile,
  44. pBuffer,
  45. BufferSize,
  46. &dwBytesWritten,
  47. NULL))
  48. {
  49. dwStatus = GetLastError();
  50. ClRtlLogPrint(LOG_CRITICAL,
  51. "FilePipePush :: Write file failed with error %1!u!\n",
  52. dwStatus);
  53. RpcRaiseException(dwStatus);
  54. }
  55. }
  56. }
  57. void
  58. FilePipePull(
  59. FILE_PIPE_STATE *state,
  60. unsigned char *pBuffer,
  61. unsigned long BufferSize,
  62. unsigned long __RPC_FAR *Written
  63. )
  64. {
  65. DWORD dwBytesRead;
  66. BOOL Success;
  67. DWORD dwStatus;
  68. if (BufferSize != 0) {
  69. Success = ReadFile (state->hFile,
  70. pBuffer,
  71. BufferSize,
  72. &dwBytesRead,
  73. NULL);
  74. *Written = dwBytesRead;
  75. if (!Success)
  76. {
  77. dwStatus = GetLastError();
  78. ClRtlLogPrint(LOG_CRITICAL,
  79. "FilePipePush :: Write file failed with error %1!u!\n",
  80. dwStatus);
  81. RpcRaiseException(dwStatus);
  82. }
  83. }
  84. }
  85. void
  86. PipeAlloc (
  87. FILE_PIPE_STATE *state,
  88. unsigned long RequestedSize,
  89. unsigned char **buf,
  90. unsigned long *ActualSize
  91. )
  92. {
  93. *buf = state->pBuffer;
  94. *ActualSize = (RequestedSize < state->BufferSize ?
  95. RequestedSize :
  96. state->BufferSize);
  97. }
  98. VOID
  99. DmInitFilePipe(
  100. IN PFILE_PIPE FilePipe,
  101. IN HANDLE hFile
  102. )
  103. /*++
  104. Routine Description:
  105. Initializes a file pipe.
  106. Arguments:
  107. FilePipe - Supplies a pointer to the file pipe to be initialized
  108. hFile - Supplies a handle to the file to be transmitted.
  109. Return Value:
  110. None.
  111. --*/
  112. {
  113. FilePipe->State.hFile = hFile;
  114. FilePipe->State.BufferSize = CHUNK_SIZE;
  115. FilePipe->State.pBuffer = LocalAlloc(LMEM_FIXED, CHUNK_SIZE);
  116. if (FilePipe->State.pBuffer == NULL) {
  117. CL_UNEXPECTED_ERROR( ERROR_NOT_ENOUGH_MEMORY );
  118. }
  119. FilePipe->Pipe.state = (char __RPC_FAR *)&FilePipe->State;
  120. FilePipe->Pipe.alloc = (void __RPC_FAR *)PipeAlloc;
  121. FilePipe->Pipe.push = (void __RPC_FAR *)FilePipePush;
  122. FilePipe->Pipe.pull = (void __RPC_FAR *)FilePipePull;
  123. }
  124. VOID
  125. DmFreeFilePipe(
  126. IN PFILE_PIPE FilePipe
  127. )
  128. /*++
  129. Routine Description:
  130. Frees a file pipe initialized by DmInitFilePipe
  131. Arguments:
  132. FilePipe - Supplies the file pipe to be freed.
  133. Return Value:
  134. None
  135. --*/
  136. {
  137. LocalFree(FilePipe->State.pBuffer);
  138. }
  139. DWORD
  140. DmPullFile(
  141. IN LPCWSTR FileName,
  142. IN BYTE_PIPE Pipe
  143. )
  144. /*++
  145. Routine Description:
  146. Creates a new file and pulls the data down the RPC pipe
  147. Arguments:
  148. FileName - Supplies the name of the file.
  149. Pipe - Supplies the RPC pipe to pull the data from.
  150. Return Value:
  151. ERROR_SUCCESS if successful
  152. Win32 error code otherwise
  153. --*/
  154. {
  155. HANDLE File;
  156. DWORD Status = ERROR_SUCCESS;
  157. PUCHAR Buffer;
  158. DWORD BytesRead;
  159. //
  160. // Create a new file to hold the bits from the client.
  161. //
  162. File = CreateFile(FileName,
  163. GENERIC_READ | GENERIC_WRITE,
  164. 0,
  165. NULL,
  166. CREATE_ALWAYS,
  167. 0,
  168. NULL);
  169. if (File == INVALID_HANDLE_VALUE) {
  170. Status = GetLastError();
  171. ClRtlLogPrint(LOG_UNUSUAL,
  172. "[DM] DmPullFile failed to create file %1!ws! error %2!d!\n",
  173. FileName,
  174. Status);
  175. return(Status);
  176. }
  177. Buffer = LocalAlloc(LMEM_FIXED, CHUNK_SIZE);
  178. if (Buffer == NULL) {
  179. Status = ERROR_NOT_ENOUGH_MEMORY;
  180. CloseHandle(File);
  181. CL_UNEXPECTED_ERROR( Status );
  182. return (Status);
  183. }
  184. try {
  185. do {
  186. (Pipe.pull)(Pipe.state,
  187. Buffer,
  188. CHUNK_SIZE,
  189. &BytesRead);
  190. if (BytesRead == 0) {
  191. break;
  192. }
  193. if (!WriteFile(File,
  194. Buffer,
  195. BytesRead,
  196. &BytesRead,
  197. NULL))
  198. {
  199. Status = GetLastError();
  200. ClRtlLogPrint(LOG_UNUSUAL,
  201. "[DM] DmPullFile :: WriteFile to file failed with error %1!ws! error %2!d!\n",
  202. FileName, Status);
  203. break;
  204. }
  205. } while ( TRUE );
  206. } except (I_RpcExceptionFilter(RpcExceptionCode())) {
  207. Status = GetExceptionCode();
  208. ClRtlLogPrint(LOG_ERROR,
  209. "[DM] DmPullFile :: Exception code 0x%1!08lx! raised for file %2!ws!\n",
  210. Status, FileName);
  211. }
  212. LocalFree(Buffer);
  213. CloseHandle(File);
  214. return(Status);
  215. }
  216. DWORD
  217. DmPushFile(
  218. IN LPCWSTR FileName,
  219. IN BYTE_PIPE Pipe
  220. )
  221. /*++
  222. Routine Description:
  223. Opens a file and pushes it down the RPC pipe
  224. Arguments:
  225. FileName - Supplies the name of the file.
  226. Pipe - Supplies the RPC pipe to push it down.
  227. Return Value:
  228. ERROR_SUCCESS if successful
  229. Win32 error code otherwise
  230. --*/
  231. {
  232. HANDLE File;
  233. DWORD Status = ERROR_SUCCESS;
  234. PUCHAR Buffer;
  235. DWORD BytesRead;
  236. //
  237. // Got a file with the right bits in it. Push it down
  238. // to the client.
  239. //
  240. File = CreateFile(FileName,
  241. GENERIC_READ,
  242. 0,
  243. NULL,
  244. OPEN_EXISTING,
  245. 0,
  246. NULL);
  247. if (File == INVALID_HANDLE_VALUE) {
  248. ClosePipe( Pipe );
  249. Status = GetLastError();
  250. ClRtlLogPrint(LOG_UNUSUAL,
  251. "[DM] DmPushFile failed to open file %1!ws! error %2!d!\n",
  252. FileName,
  253. Status);
  254. return(Status);
  255. }
  256. Buffer = LocalAlloc(LMEM_FIXED, CHUNK_SIZE);
  257. if (Buffer == NULL) {
  258. ClosePipe( Pipe );
  259. Status = ERROR_NOT_ENOUGH_MEMORY;
  260. CloseHandle(File);
  261. CL_UNEXPECTED_ERROR( Status );
  262. return(Status);
  263. }
  264. try {
  265. do {
  266. if (!ReadFile(File,
  267. Buffer,
  268. CHUNK_SIZE,
  269. &BytesRead,
  270. NULL))
  271. {
  272. Status = GetLastError();
  273. ClRtlLogPrint(LOG_CRITICAL,
  274. "[DM] DmPushFile failed to read file %1!ws! error %2!d!\n",
  275. FileName, Status);
  276. break;
  277. }
  278. (Pipe.push)(Pipe.state,
  279. Buffer,
  280. BytesRead);
  281. } while ( BytesRead != 0 );
  282. } except (I_RpcExceptionFilter(RpcExceptionCode())) {
  283. Status = GetExceptionCode();
  284. ClRtlLogPrint(LOG_ERROR,
  285. "[DM] DmPushFile :: Exception code 0x%1!08lx! raised for file %2!ws!\n",
  286. Status, FileName);
  287. }
  288. LocalFree(Buffer);
  289. CloseHandle(File);
  290. return(Status);
  291. }
  292. DWORD
  293. DmpSyncDatabase(
  294. IN RPC_BINDING_HANDLE RpcBinding,
  295. IN OPTIONAL LPCWSTR Directory
  296. )
  297. /*++
  298. Routine Description:
  299. Connects to a remote node and attempts to sync with its
  300. cluster database.
  301. Arguments:
  302. RpcBinding - The RPC binding handle to use to sync the database.
  303. Directory - if present, supplies the directory where CLUSDB should
  304. be created.
  305. Return Value:
  306. ERROR_SUCCESS if the database was successfully updated.
  307. Win32 error otherwise
  308. --*/
  309. {
  310. DWORD Status;
  311. WCHAR FileName[MAX_PATH+1];
  312. FILE_PIPE FilePipe;
  313. HANDLE hFile;
  314. //
  315. // Issue conditional synchronization
  316. //
  317. Status = DmCreateTempFileName(FileName);
  318. if (Status == ERROR_SUCCESS) {
  319. hFile = CreateFileW(FileName,
  320. GENERIC_READ | GENERIC_WRITE,
  321. 0,
  322. NULL,
  323. CREATE_ALWAYS,
  324. 0,
  325. NULL);
  326. if (hFile == INVALID_HANDLE_VALUE) {
  327. Status = GetLastError();
  328. CL_UNEXPECTED_ERROR( Status );
  329. } else {
  330. DmInitFilePipe(&FilePipe, hFile);
  331. Status = DmSyncDatabase(RpcBinding,
  332. FilePipe.Pipe);
  333. DmFreeFilePipe(&FilePipe);
  334. CloseHandle(hFile);
  335. if (Status == ERROR_SUCCESS) {
  336. //
  337. // A new registry file was successfully downloaded.
  338. // Install it into the current registry.
  339. //
  340. ClRtlLogPrint(LOG_UNUSUAL,"[DM] Obtained new database.\n");
  341. //acquire the exclusive locks so that no new keys are opened while
  342. // the registry is being reinstated
  343. ACQUIRE_EXCLUSIVE_LOCK(gLockDmpRoot);
  344. // hold the key lock as well
  345. EnterCriticalSection(&KeyLock);
  346. // Invalidate any open keys
  347. DmpInvalidateKeys();
  348. Status = DmInstallDatabase(FileName, Directory, TRUE);
  349. if (Status != ERROR_SUCCESS) {
  350. ClRtlLogPrint(LOG_CRITICAL,
  351. "[DM] DmpSyncDatabase failed, error %1!u!.\n",
  352. Status);
  353. }
  354. // Reopen the keys for read/write access
  355. DmpReopenKeys();
  356. // release the locks
  357. LeaveCriticalSection(&KeyLock);
  358. RELEASE_LOCK(gLockDmpRoot);
  359. } else {
  360. ClRtlLogPrint(LOG_UNUSUAL,
  361. "[DM] Failed to get a new database, status %1!u!\n",
  362. Status
  363. );
  364. CL_UNEXPECTED_ERROR(Status);
  365. }
  366. DeleteFile(FileName);
  367. }
  368. }
  369. return(Status);
  370. }
  371. DWORD
  372. DmInstallDatabase(
  373. IN LPWSTR FileName,
  374. IN OPTIONAL LPCWSTR Directory,
  375. IN BOOL bDeleteSrcFile
  376. )
  377. /*++
  378. Routine Description:
  379. Installs a new cluster registry database from the specified file
  380. Arguments:
  381. FileName - The name of the file from which to read the registry database
  382. to install.
  383. Directory - if present, supplies the directory where the CLUSDB file should
  384. be created.
  385. if not present, the current directory is used.
  386. bDeleteSrcFile - Delete the Source file represented by FileName.
  387. Return Value:
  388. ERROR_SUCCESS if the installation completed successfully
  389. Win32 error code otherwise.
  390. --*/
  391. {
  392. DWORD Status;
  393. BOOLEAN WasEnabled;
  394. WCHAR Path[MAX_PATH];
  395. WCHAR *p;
  396. WCHAR BkpPath[MAX_PATH];
  397. Status = ClRtlEnableThreadPrivilege(SE_RESTORE_PRIVILEGE,
  398. &WasEnabled);
  399. if (Status != ERROR_SUCCESS) {
  400. if (Status == STATUS_PRIVILEGE_NOT_HELD) {
  401. ClRtlLogPrint(LOG_CRITICAL,
  402. "[DM] Restore privilege not held by cluster service\n");
  403. } else {
  404. ClRtlLogPrint(LOG_CRITICAL,
  405. "[DM] Attempt to enable restore privilege failed %1!lx!\n",Status);
  406. }
  407. return(Status);
  408. }
  409. //
  410. // Restart the registry watcher thread so it is not trying to use
  411. // DmpRoot while we are messing with things.
  412. //
  413. ACQUIRE_EXCLUSIVE_LOCK(gLockDmpRoot);
  414. DmpRestartFlusher();
  415. //
  416. // Close DmpRoot (it should be the only thing open) so that we can
  417. // unload the current cluster database.
  418. //
  419. RegCloseKey(DmpRoot);
  420. Status = RegUnLoadKey(HKEY_LOCAL_MACHINE, L"Cluster");
  421. ClRtlRestoreThreadPrivilege(SE_RESTORE_PRIVILEGE,
  422. WasEnabled);
  423. if (Status == ERROR_SUCCESS) {
  424. //
  425. // Get the CLUSDB full pathname.
  426. //
  427. if (Directory == NULL) {
  428. Status = GetModuleFileName(NULL, Path, MAX_PATH);
  429. if (Status == 0) {
  430. ClRtlLogPrint(LOG_CRITICAL,
  431. "[DM] Couldn't find cluster database\n");
  432. Status = GetLastError();
  433. } else {
  434. Status = ERROR_SUCCESS;
  435. p=wcsrchr(Path, L'\\');
  436. if (p != NULL) {
  437. *p = L'\0';
  438. wcscpy(BkpPath, Path);
  439. #ifdef OLD_WAY
  440. wcscat(Path, L"\\CLUSDB");
  441. #else // OLD_WAY
  442. wcscat(Path, L"\\"CLUSTER_DATABASE_NAME );
  443. #endif // OLD_WAY
  444. wcscat(BkpPath, L"\\"CLUSTER_DATABASE_TMPBKP_NAME);
  445. } else {
  446. CL_UNEXPECTED_ERROR(ERROR_FILE_NOT_FOUND);
  447. }
  448. }
  449. } else {
  450. lstrcpyW(Path, Directory);
  451. lstrcpyW(BkpPath, Path);
  452. #ifdef OLD_WAY
  453. wcscat(Path, L"\\CLUSDB");
  454. #else // OLD_WAY
  455. wcscat(Path, L"\\"CLUSTER_DATABASE_NAME );
  456. #endif // OLD_WAY
  457. wcscat(BkpPath, L"\\"CLUSTER_DATABASE_TMPBKP_NAME);
  458. }
  459. if (Status == ERROR_SUCCESS) {
  460. //
  461. // Now copy the supplied file to CLUSDB
  462. //
  463. Status = DmpSafeDatabaseCopy(FileName, Path, BkpPath, bDeleteSrcFile);
  464. if (Status != ERROR_SUCCESS) {
  465. ClRtlLogPrint(LOG_CRITICAL,
  466. "[DM] DmInstallDatabase :: DmpSafeDatabaseCopy() failed %1!d!\n",
  467. Status);
  468. // SS: BUG BUG - we should not reload the old hive
  469. //on a join, that would be catastrophic to continue
  470. //on a form, while uploading from a checkpoint file
  471. // it would be the same
  472. //
  473. // Try and reload the old hive
  474. //
  475. // Status = DmpLoadHive(Path);
  476. CL_UNEXPECTED_ERROR(Status);
  477. } else {
  478. //
  479. // Finally, reload the hive.
  480. //
  481. Status = DmpLoadHive(Path);
  482. }
  483. }
  484. } else {
  485. ClRtlLogPrint(LOG_CRITICAL,
  486. "[DM] RegUnloadKey of existing database failed %1!d!\n",
  487. Status);
  488. goto FnExit;
  489. }
  490. if (Status != ERROR_SUCCESS)
  491. {
  492. ClRtlLogPrint(LOG_CRITICAL,
  493. "[DM] DmInstallDatabase :: failed to load hive %1!d!\n",
  494. Status);
  495. goto FnExit;
  496. }
  497. //
  498. // Reopen DmpRoot
  499. //
  500. Status = RegOpenKeyW(HKEY_LOCAL_MACHINE,
  501. DmpClusterParametersKeyName,
  502. &DmpRoot);
  503. if ( Status != ERROR_SUCCESS ) {
  504. CL_UNEXPECTED_ERROR(Status);
  505. goto FnExit;
  506. }
  507. //
  508. // HACKHACK John Vert (jvert) 6/3/1997
  509. // There is a bug in the registry with refresh
  510. // where the Parent field in the root cell doesn't
  511. // get flushed to disk, so it gets blasted if we
  512. // do a refresh. Then we crash in unload. So flush
  513. // out the registry to disk here to make sure the
  514. // right Parent field gets written to disk.
  515. //
  516. if (Status == ERROR_SUCCESS) {
  517. DWORD Dummy=0;
  518. //
  519. // Make something dirty in the root
  520. //
  521. RegSetValueEx(DmpRoot,
  522. L"Valid",
  523. 0,
  524. REG_DWORD,
  525. (PBYTE)&Dummy,
  526. sizeof(Dummy));
  527. RegDeleteValue(DmpRoot, L"Valid");
  528. Status = RegFlushKey(DmpRoot);
  529. if (Status != ERROR_SUCCESS)
  530. {
  531. CL_UNEXPECTED_ERROR(Status);
  532. ClRtlLogPrint(LOG_CRITICAL,
  533. "[DM] DmInstallDatabase : RegFlushKey failed with error %1!d!\n",
  534. Status);
  535. }
  536. }
  537. FnExit:
  538. RELEASE_LOCK(gLockDmpRoot);
  539. return(Status);
  540. }
  541. DWORD
  542. DmGetDatabase(
  543. IN HKEY hKey,
  544. IN LPWSTR FileName
  545. )
  546. /*++
  547. Routine Description:
  548. Writes the registry database to a specified file.
  549. Arguments:
  550. hKey - Supplies the root of the registry tree to get.
  551. FileName - The name of the file into which to write the current
  552. registry database.
  553. Return Value:
  554. ERROR_SUCCESS if the update completed successfully
  555. Win32 error code otherwise.
  556. --*/
  557. {
  558. BOOLEAN WasEnabled;
  559. DWORD Status;
  560. NTSTATUS Error;
  561. //
  562. // Make sure this file does not exist already.
  563. //
  564. DeleteFileW(FileName);
  565. Status = ClRtlEnableThreadPrivilege(SE_BACKUP_PRIVILEGE,
  566. &WasEnabled);
  567. if ( Status != STATUS_SUCCESS ) {
  568. CL_LOGFAILURE( Status );
  569. goto FnExit;
  570. }
  571. Status = RegSaveKeyW(hKey,
  572. FileName,
  573. NULL);
  574. // this is used for checkpointing and shouldnt fail, but if it does we
  575. // will log an event and delete the file
  576. if ( Status != ERROR_SUCCESS ) {
  577. CL_LOGFAILURE( Status );
  578. CsLogEventData1( LOG_CRITICAL,
  579. CS_DISKWRITE_FAILURE,
  580. sizeof(Status),
  581. &Status,
  582. FileName );
  583. DeleteFileW(FileName);
  584. }
  585. Error = ClRtlRestoreThreadPrivilege(SE_BACKUP_PRIVILEGE,
  586. WasEnabled);
  587. if (Error != ERROR_SUCCESS)
  588. {
  589. CL_UNEXPECTED_ERROR(Error);
  590. }
  591. FnExit:
  592. return(Status);
  593. }
  594. //
  595. //
  596. // Server-side join routines.
  597. //
  598. //
  599. error_status_t
  600. s_DmSyncDatabase(
  601. IN handle_t IDL_handle,
  602. OUT BYTE_PIPE Regdata
  603. )
  604. /*++
  605. Routine Description:
  606. Pushes a new configuration database to a joining node.
  607. Arguments:
  608. IDL_handle - RPC binding handle, not used.
  609. Regdata - The RPC data pipe to use to transfer the data.
  610. Return Value:
  611. ERROR_SUCCESS if the update completed successfully
  612. Win32 error code otherwise.
  613. --*/
  614. {
  615. HANDLE File;
  616. DWORD Status;
  617. WCHAR FileName[MAX_PATH+1];
  618. ClRtlLogPrint(LOG_UNUSUAL, "[DM] Supplying database to joining node.\n");
  619. Status = DmCreateTempFileName(FileName);
  620. if (Status == ERROR_SUCCESS) {
  621. DmCommitRegistry(); // Ensure up-to-date snapshot
  622. //
  623. // Chittur Subbaraman (chitturs) - 01/19/2001
  624. //
  625. // Hold the root lock before trying to save the hive. This is necessary so that
  626. // an NtRestoreKey/RegCloseKey on the root key is not in progress at the time
  627. // the save is attempted.
  628. //
  629. ACQUIRE_EXCLUSIVE_LOCK( gLockDmpRoot );
  630. Status = DmGetDatabase(DmpRoot,FileName);
  631. RELEASE_LOCK ( gLockDmpRoot );
  632. if (Status != ERROR_SUCCESS) {
  633. ClosePipe(Regdata);
  634. CL_UNEXPECTED_ERROR( Status );
  635. } else {
  636. Status = DmPushFile(FileName, Regdata);
  637. DeleteFile(FileName);
  638. }
  639. } else {
  640. RpcRaiseException( Status );
  641. ClosePipe(Regdata);
  642. CL_UNEXPECTED_ERROR( Status );
  643. }
  644. ClRtlLogPrint(LOG_UNUSUAL,
  645. "[DM] Finished supplying database to joining node.\n"
  646. );
  647. return(Status);
  648. }
  649. DWORD
  650. DmCreateTempFileName(
  651. OUT LPWSTR FileName
  652. )
  653. /*++
  654. Routine Description:
  655. Creates a temporary filename for use by the cluster service.
  656. Arguments:
  657. FileName - Returns the name of the temporary file. The buffer
  658. pointed to must be big enough for at least MAX_PATH
  659. characters.
  660. Return Value:
  661. ERROR_SUCCESS if successful.
  662. Win32 error code otherwise
  663. --*/
  664. {
  665. WCHAR TempPath[MAX_PATH];
  666. DWORD Status;
  667. HANDLE hFile;
  668. GetTempPath(sizeof(TempPath)/sizeof(WCHAR),TempPath);
  669. Status = GetTempFileName(TempPath,L"CLS",0,FileName);
  670. if (Status == 0) {
  671. //
  672. // Somebody has probably set the TMP variable incorrectly.
  673. // Just use the current directory.
  674. //
  675. Status = GetTempFileName(L".", L"CLS",0,FileName);
  676. if (Status == 0) {
  677. Status = GetLastError();
  678. CL_UNEXPECTED_ERROR( Status );
  679. return(Status);
  680. }
  681. }
  682. //
  683. // Open the newly created temp file with rights to modify DACL in the object's SD.
  684. //
  685. hFile = CreateFile( FileName,
  686. GENERIC_READ | WRITE_DAC | READ_CONTROL,
  687. 0,
  688. NULL,
  689. OPEN_EXISTING,
  690. FILE_ATTRIBUTE_NORMAL,
  691. NULL );
  692. if ( hFile == INVALID_HANDLE_VALUE )
  693. {
  694. Status = GetLastError();
  695. ClRtlLogPrint(LOG_CRITICAL,
  696. "[DM] DmCreateTempFile: Failed to open file temp file %1!ws!, Status=%2!u!...\r\n",
  697. FileName,
  698. Status);
  699. return ( Status );
  700. }
  701. //
  702. // Set DACL on the file handle object granting full rights only to admin and owner.
  703. //
  704. Status = ClRtlSetObjSecurityInfo( hFile,
  705. SE_FILE_OBJECT,
  706. GENERIC_ALL, // for Admins
  707. GENERIC_ALL, // for Owner
  708. 0 ); // for Everyone
  709. CloseHandle( hFile );
  710. if ( Status != ERROR_SUCCESS )
  711. {
  712. ClRtlLogPrint(LOG_CRITICAL,
  713. "[DM] DmCreateTempFile: ClRtlSetObjSecurityInfo failed for file %1!ws!, Status=%2!u!\r\n",
  714. FileName,
  715. Status);
  716. return ( Status );
  717. }
  718. return( ERROR_SUCCESS );
  719. }
  720. DWORD
  721. DmpLoadHive(
  722. IN LPCWSTR Path
  723. )
  724. /*++
  725. Routine Description:
  726. Loads the cluster database into HKLM\Cluster
  727. Arguments:
  728. Path - Supplies the fully qualified filename of the cluster database.
  729. Return Value:
  730. ERROR_SUCCESS if successful
  731. Win32 error code otherwise
  732. --*/
  733. {
  734. BOOLEAN WasEnabled;
  735. RTL_RELATIVE_NAME RelativeName;
  736. OBJECT_ATTRIBUTES TargetKey;
  737. OBJECT_ATTRIBUTES SourceFile;
  738. UNICODE_STRING SubKey;
  739. UNICODE_STRING FileName;
  740. NTSTATUS Status;
  741. BOOLEAN ErrorFlag;
  742. LPWSTR FreeBuffer;
  743. //
  744. // If the cluster database is not loaded, load it now.
  745. //
  746. ClRtlLogPrint(LOG_NOISE,
  747. "[DM] Loading cluster database from %1!ws!\n", Path);
  748. RtlInitUnicodeString(&SubKey, L"\\Registry\\Machine\\Cluster");
  749. InitializeObjectAttributes(&TargetKey,
  750. &SubKey,
  751. OBJ_CASE_INSENSITIVE,
  752. NULL,
  753. NULL);
  754. ErrorFlag = RtlDosPathNameToNtPathName_U(Path,
  755. &FileName,
  756. NULL,
  757. &RelativeName);
  758. if (!ErrorFlag) {
  759. ClRtlLogPrint(LOG_CRITICAL,
  760. "[DM] RtlDosPathNameToNtPathName_U failed\n");
  761. return ERROR_INVALID_PARAMETER;
  762. }
  763. FreeBuffer = FileName.Buffer;
  764. if (RelativeName.RelativeName.Length) {
  765. FileName = *((PUNICODE_STRING)&RelativeName.RelativeName);
  766. } else {
  767. RelativeName.ContainingDirectory = NULL;
  768. }
  769. InitializeObjectAttributes(&SourceFile,
  770. &FileName,
  771. OBJ_CASE_INSENSITIVE,
  772. RelativeName.ContainingDirectory,
  773. NULL);
  774. Status = ClRtlEnableThreadPrivilege(SE_RESTORE_PRIVILEGE,
  775. &WasEnabled);
  776. if (Status != ERROR_SUCCESS) {
  777. if (Status == STATUS_PRIVILEGE_NOT_HELD) {
  778. ClRtlLogPrint(LOG_CRITICAL,
  779. "[DM] Restore privilege not held by cluster service\n");
  780. } else {
  781. ClRtlLogPrint(LOG_CRITICAL,
  782. "[DM] Attempt to enable restore privilege failed %1!lx!\n",Status);
  783. }
  784. } else {
  785. //
  786. // Note : Sunitas
  787. // There used to be a registry bug where if we set REG_NO_LAZY_FLUSH and the hive
  788. // is corrupt, the system crashes. So we used to first try loading it without the
  789. // REG_NO_LAZY_FLUSH. If that works, unload it and do it again with
  790. // REG_NO_LAZY_FLUSH. The registry folks claim that is fixed..so I am
  791. // removing that hack
  792. //
  793. Status = NtLoadKey2(&TargetKey,
  794. &SourceFile,
  795. REG_NO_LAZY_FLUSH);
  796. if (Status != STATUS_SUCCESS)
  797. {
  798. ClRtlLogPrint(LOG_CRITICAL,
  799. "[DM] DmpLoadHive: NtLoadKey2 failed with error, %1!u!\n",
  800. Status);
  801. CL_UNEXPECTED_ERROR(Status);
  802. }
  803. ClRtlRestoreThreadPrivilege(SE_RESTORE_PRIVILEGE,
  804. WasEnabled);
  805. }
  806. RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
  807. return(Status);
  808. }
  809. DWORD DmpUnloadHive()
  810. /*++
  811. Routine Description:
  812. Unloads the cluster database from HKLM\Cluster. This is called at initialization
  813. to make sure that the database is loaded with the correct flags.
  814. Return Value:
  815. ERROR_SUCCESS if successful
  816. Win32 error code otherwise
  817. --*/
  818. {
  819. BOOLEAN WasEnabled;
  820. OBJECT_ATTRIBUTES TargetKey;
  821. UNICODE_STRING SubKey;
  822. NTSTATUS Status;
  823. ClRtlLogPrint(LOG_NOISE,
  824. "[DM] DmpUnloadHive: unloading the hive\r\n");
  825. RtlInitUnicodeString(&SubKey, L"\\Registry\\Machine\\Cluster");
  826. InitializeObjectAttributes(&TargetKey,
  827. &SubKey,
  828. OBJ_CASE_INSENSITIVE,
  829. NULL,
  830. NULL);
  831. Status = ClRtlEnableThreadPrivilege(SE_RESTORE_PRIVILEGE,
  832. &WasEnabled);
  833. if (Status != ERROR_SUCCESS)
  834. {
  835. if (Status == STATUS_PRIVILEGE_NOT_HELD)
  836. {
  837. ClRtlLogPrint(LOG_CRITICAL,
  838. "[DM] DmpUnloadHive:: Restore privilege not held by cluster service\n");
  839. }
  840. else
  841. {
  842. ClRtlLogPrint(LOG_CRITICAL,
  843. "[DM] DmpUnloadHive: Attempt to enable restore privilege failed %1!lx!\n",Status);
  844. }
  845. goto FnExit;
  846. }
  847. Status = NtUnloadKey(&TargetKey);
  848. if (Status != STATUS_SUCCESS)
  849. {
  850. ClRtlLogPrint(LOG_CRITICAL,
  851. "[DM] DmpUnloadHive: NtUnloadKey failed with error, %1!u!\n",
  852. Status);
  853. CL_UNEXPECTED_ERROR(Status);
  854. }
  855. ClRtlRestoreThreadPrivilege(SE_RESTORE_PRIVILEGE,
  856. WasEnabled);
  857. FnExit:
  858. return(Status);
  859. }
  860. DWORD
  861. DmpSafeDatabaseCopy(
  862. IN LPCWSTR FileName,
  863. IN LPCWSTR Path,
  864. IN LPCWSTR BkpPath,
  865. IN BOOL bDeleteSrcFile
  866. )
  867. /*++
  868. Routine Description:
  869. Loads the cluster database into HKLM\Cluster
  870. Arguments:
  871. FileName - Supplies the fully qualified filename of the new cluster database
  872. Path - Supplies the fully qualified filename of the cluster database.
  873. BkpPath - Supplies the fully qualified filename of the cluster database temporary
  874. backup
  875. bDeleteSrcFile - Specifies whether the source file may be deleted
  876. Return Value:
  877. ERROR_SUCCESS if successful
  878. Win32 error code otherwise
  879. --*/
  880. {
  881. DWORD dwStatus = ERROR_SUCCESS;
  882. //set the file attributes of the bkp file to be normal so that we can
  883. //overwrite it if it exists
  884. if (!SetFileAttributes(BkpPath, FILE_ATTRIBUTE_NORMAL))
  885. {
  886. ClRtlLogPrint(LOG_UNUSUAL,
  887. "[DM] DmpSafeDatabaseCopy:: SetFileAttrib on BkpPath %1!ws! failed, Status=%2!u!\n",
  888. BkpPath, GetLastError());
  889. //this may fail because the file doesnt exist but that is not fatal so we ignore the error
  890. }
  891. //Save the database to a temp database that can be used for recovery
  892. //copyfileex preserves attributes of the old file
  893. if (!CopyFileEx(Path, BkpPath, NULL, NULL, NULL, 0))
  894. {
  895. dwStatus = GetLastError();
  896. ClRtlLogPrint(LOG_CRITICAL,
  897. "[DM] DmpSafeDatabaseCopy:: Failed to create a backup copy of database, Status=%1!u!\n",
  898. dwStatus);
  899. goto FnExit;
  900. }
  901. //hide the file since users are not supposed to know about it
  902. if (!SetFileAttributes(BkpPath, FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY))
  903. {
  904. dwStatus = GetLastError();
  905. ClRtlLogPrint(LOG_CRITICAL,
  906. "[DM] DmpSafeDatabaseCopy:: SetFileAttrib on BkpPath %1!ws! failed, Status=%2!u!\n",
  907. BkpPath, dwStatus);
  908. goto FnExit;
  909. }
  910. //set DatabaseCopyInProgress key to be TRUE
  911. dwStatus = DmpSetDwordInClusterServer( L"ClusterDatabaseCopyInProgress",1);
  912. if (dwStatus != ERROR_SUCCESS)
  913. {
  914. ClRtlLogPrint(LOG_CRITICAL,
  915. "[DM] DmpSafeDatabaseCopy:: Failed to set ClusterDatabaseCopyInProgress, Status=%1!u!\n",
  916. dwStatus);
  917. goto FnExit;
  918. }
  919. //delete clusdb
  920. if (!DeleteFile(Path))
  921. {
  922. ClRtlLogPrint(LOG_UNUSUAL,
  923. "[DM] DmpSafeDatabaseCopy:: Couldnt delete the database file, Error=%1!u!\n",
  924. GetLastError());
  925. //this is not fatal, we will still try the move file
  926. }
  927. //copy the new database to clusdb
  928. if (bDeleteSrcFile)
  929. {
  930. //the source file may be deleted, this is true at join sync time
  931. //the source file is a temporary file
  932. if (!MoveFileEx(FileName, Path, MOVEFILE_REPLACE_EXISTING |
  933. MOVEFILE_COPY_ALLOWED | MOVEFILE_WRITE_THROUGH))
  934. {
  935. dwStatus = GetLastError();
  936. ClRtlLogPrint(LOG_NOISE,
  937. "[DM] DmpSafeDatabaseCopy:: Failed to move %1!ws! to %2!ws!, Status=%3!u!\n",
  938. FileName, Path, dwStatus);
  939. goto FnExit;
  940. }
  941. }
  942. else
  943. {
  944. //the source file must not be deleted use copy..this is true
  945. //when the logs are being rolled at form and we are uploading
  946. //the database from a checkpoint file
  947. if (!CopyFileEx(FileName, Path, NULL, NULL, NULL, 0))
  948. {
  949. dwStatus = GetLastError();
  950. ClRtlLogPrint(LOG_CRITICAL,
  951. "[DM] DmpSafeDatabaseCopy:: Failed to copy %1!ws! to %2!ws!, Status=%3!u!\n",
  952. FileName, Path, dwStatus);
  953. goto FnExit;
  954. }
  955. }
  956. //set databaseCopyInProgress key to FALSE
  957. dwStatus = DmpSetDwordInClusterServer( L"ClusterDatabaseCopyInProgress", 0);
  958. if (dwStatus != ERROR_SUCCESS)
  959. {
  960. ClRtlLogPrint(LOG_NOISE,
  961. "[DM] DmpSafeDatabaseCopy:: Failed to set ClusterDatabaseCopyInProgress, Status=%1!u!\n",
  962. dwStatus);
  963. goto FnExit;
  964. }
  965. //now that clusdb is safely copied, we can delete the backup
  966. //for that we need to set the file attribute to normal
  967. if (!SetFileAttributes(BkpPath, FILE_ATTRIBUTE_NORMAL))
  968. {
  969. ClRtlLogPrint(LOG_CRITICAL,
  970. "[DM] DmpSafeDatabaseCopy:: SetFileAttrib on BkpPath %1!ws! failed, Status=%2!u!\n",
  971. BkpPath, GetLastError());
  972. }
  973. //delete the backup
  974. if (!DeleteFile(BkpPath))
  975. {
  976. ClRtlLogPrint(LOG_NOISE,
  977. "[DM] DmpSafeDatabaseCopy:: Failed to delete bkp database file %1!ws!, Status=%2!u!\n",
  978. BkpPath, GetLastError());
  979. //this is not fatal so ignore the error
  980. }
  981. FnExit:
  982. return(dwStatus);
  983. }
  984. DWORD
  985. DmpSetDwordInClusterServer(
  986. LPCWSTR lpszValueName,
  987. DWORD dwValue
  988. )
  989. /*++
  990. Routine Description:
  991. Sets the value specified under
  992. L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server",
  993. to the value specified by dwValue. It flushes the change.
  994. Arguments:
  995. lpszValueName : Sets the value for the name specified by lpszValueName
  996. dwValue : The value to set to.
  997. Return Value:
  998. ERROR_SUCCESS if everything worked ok
  999. --*/
  1000. {
  1001. HKEY hKey;
  1002. DWORD dwStatus = ERROR_SUCCESS; // returned by registry API functions
  1003. // Attempt to open an existing key in the registry.
  1004. dwStatus = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
  1005. L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server",
  1006. 0, // reserved
  1007. KEY_WRITE,
  1008. &hKey );
  1009. // Was the registry key opened successfully ?
  1010. if ( dwStatus == ERROR_SUCCESS )
  1011. {
  1012. DWORD dwValueType = REG_DWORD;
  1013. DWORD dwDataBufferSize = sizeof( DWORD );
  1014. dwStatus = RegSetValueExW( hKey,
  1015. lpszValueName,
  1016. 0, // reserved
  1017. dwValueType,
  1018. (LPBYTE) &dwValue,
  1019. dwDataBufferSize );
  1020. //Flush the key
  1021. RegFlushKey(hKey);
  1022. // Close the registry key.
  1023. RegCloseKey( hKey );
  1024. // Was the value set successfully?
  1025. }
  1026. return(dwStatus);
  1027. } // DmpSetDwordInClusterServer
  1028. DWORD DmpGetDwordFromClusterServer(
  1029. IN LPCWSTR lpszValueName,
  1030. OUT LPDWORD pdwValue,
  1031. IN DWORD dwDefaultValue
  1032. )
  1033. /*++
  1034. Routine Description:
  1035. Gets the DWORD value specified in lpszValueName.
  1036. L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server".
  1037. If the value doesnt exist, the default is returned.
  1038. Arguments:
  1039. lpszValueName : The Value to read.
  1040. pdwValue : Returns the value of the key specified by lpszValueName
  1041. dwDefaultValue: The value to be returned if the specified key doesnt exist
  1042. or in case of error.
  1043. Return Value:
  1044. ERROR_SUCCESS if everything worked ok or if the key wasnt present.
  1045. --*/
  1046. {
  1047. HKEY hKey = NULL;
  1048. DWORD dwStatus; // returned by registry API functions
  1049. DWORD dwClusterInstallState;
  1050. DWORD dwValueType;
  1051. DWORD dwDataBufferSize = sizeof( DWORD );
  1052. *pdwValue = dwDefaultValue;
  1053. // Read the registry key that indicates whether cluster files are installed.
  1054. dwStatus = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
  1055. L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Cluster Server",
  1056. 0, // reserved
  1057. KEY_READ,
  1058. &hKey );
  1059. // Was the registry key opened successfully ?
  1060. if ( dwStatus != ERROR_SUCCESS )
  1061. {
  1062. if ( dwStatus == ERROR_FILE_NOT_FOUND )
  1063. {
  1064. *pdwValue = dwDefaultValue;
  1065. dwStatus = ERROR_SUCCESS;
  1066. goto FnExit;
  1067. }
  1068. }
  1069. // Read the entry.
  1070. dwStatus = RegQueryValueExW( hKey,
  1071. lpszValueName,
  1072. 0, // reserved
  1073. &dwValueType,
  1074. (LPBYTE) pdwValue,
  1075. &dwDataBufferSize );
  1076. // Was the value read successfully ?
  1077. if ( dwStatus != ERROR_SUCCESS )
  1078. {
  1079. if ( dwStatus == ERROR_FILE_NOT_FOUND )
  1080. {
  1081. *pdwValue = dwDefaultValue;
  1082. dwStatus = ERROR_SUCCESS;
  1083. goto FnExit;
  1084. }
  1085. }
  1086. FnExit:
  1087. // Close the registry key.
  1088. if ( hKey )
  1089. {
  1090. RegCloseKey( hKey );
  1091. }
  1092. return ( dwStatus );
  1093. } //*** DmpGetDwordFromClusterServer