Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1016 lines
25 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. sprestrt.c
  5. Abstract:
  6. This program is used to help make GUI Setup restartable,
  7. if setup was started in restartable mode.
  8. Text mode setup will create a system hive containing the value
  9. HKLM\System\Setup:RestartSetup = REG_DWORD FALSE
  10. and a system.sav with RestartSetup set to TRUE. In both hives
  11. the session manager key will be written such that this program
  12. runs at autochk time.
  13. When this program starts, it checks the RestartSetup flag.
  14. If FALSE, then this is the first boot into GUI Setup, and we change it
  15. to TRUE and we're done here. If TRUE, then GUI setup needs to be
  16. restarted, and we clean out the config directory, copying *.sav to *.
  17. and erase everything else in there. System.sav has RestartSetup = TRUE,
  18. so GUI setup will be restarted over and over again until it succeeds.
  19. At the end of GUI Setup, sprestrt.exe is removed from the list of
  20. autochk programs and RestartSetup is set to FALSE.
  21. The boot loader looks at RestartSetup to see whether it needs to unload
  22. system and load system.sav instead. On the first boot into gui setup,
  23. we don't want to do this but on subsequent boots we do. The logic above
  24. makes this work correctly.
  25. Author:
  26. Ted Miller (tedm) Feb 1996
  27. --*/
  28. #include <nt.h>
  29. #include <ntrtl.h>
  30. #include <nturtl.h>
  31. #include "msg.h"
  32. #include "psp.h"
  33. //
  34. // Define result codes.
  35. //
  36. #define SUCCESS 0
  37. #define FAILURE 1
  38. //
  39. // Define helper macro to deal with subtleties of NT-level programming.
  40. //
  41. #define INIT_OBJA(Obja,UnicodeString,UnicodeText) \
  42. \
  43. RtlInitUnicodeString((UnicodeString),(UnicodeText)); \
  44. \
  45. InitializeObjectAttributes( \
  46. (Obja), \
  47. (UnicodeString), \
  48. OBJ_CASE_INSENSITIVE, \
  49. NULL, \
  50. NULL \
  51. )
  52. //
  53. // Relevent registry key and values.
  54. //
  55. const PCWSTR SetupRegistryKeyName = L"\\Registry\\Machine\\SYSTEM\\Setup";
  56. const PCWSTR RestartSetupValueName = L"RestartSetup";
  57. const PCWSTR ConfigDirectory =L"\\SystemRoot\\System32\\Config";
  58. const PCWSTR ProgressIndicator = L".";
  59. //
  60. // Copy buffer. What the heck, it doesn't take up any space in the image.
  61. //
  62. #define COPYBUF_SIZE 65536
  63. UCHAR CopyBuffer[COPYBUF_SIZE];
  64. //
  65. // Tristate value, where a boolean just won't do.
  66. //
  67. typedef enum {
  68. xFALSE,
  69. xTRUE,
  70. xUNKNOWN
  71. } TriState;
  72. //
  73. // Define structure for keeping a linked list of unicode strings.
  74. //
  75. typedef struct _COPY_LIST_NODE {
  76. LONGLONG FileSize;
  77. UNICODE_STRING UnicodeString;
  78. struct _COPY_LIST_NODE *Next;
  79. } COPY_LIST_NODE, *PCOPY_LIST_NODE;
  80. //
  81. // Memory routines
  82. //
  83. #define MALLOC(size) RtlAllocateHeap(RtlProcessHeap(),0,(size))
  84. #define FREE(block) RtlFreeHeap(RtlProcessHeap(),0,(block))
  85. //
  86. // Forward references
  87. //
  88. TriState
  89. CheckRestartValue(
  90. VOID
  91. );
  92. BOOLEAN
  93. SetRestartValue(
  94. VOID
  95. );
  96. BOOLEAN
  97. PrepareForGuiSetupRestart(
  98. VOID
  99. );
  100. BOOLEAN
  101. RestoreConfigDirectory(
  102. VOID
  103. );
  104. NTSTATUS
  105. CopyAFile(
  106. IN HANDLE DirectoryHandle,
  107. IN LONGLONG FileSize,
  108. IN PCWSTR ExistingFile,
  109. IN PCWSTR NewFile
  110. );
  111. BOOLEAN
  112. AreStringsEqual(
  113. IN PCWSTR String1,
  114. IN PCWSTR String2
  115. );
  116. BOOLEAN
  117. Message(
  118. IN ULONG MessageId,
  119. IN ULONG DotCount,
  120. ...
  121. );
  122. int
  123. __cdecl
  124. main(
  125. VOID
  126. )
  127. {
  128. int Result;
  129. //
  130. // Check the status of the RestartSetup flag.
  131. // If not present, do nothing.
  132. // If FALSE, set to TRUE.
  133. // If TRUE, clean up config directory.
  134. //
  135. switch(CheckRestartValue()) {
  136. case xFALSE:
  137. if(SetRestartValue()) {
  138. Result = SUCCESS;
  139. } else {
  140. Result = FAILURE;
  141. Message(MSG_WARNING_CANT_SET_RESTART,0);
  142. }
  143. break;
  144. case xTRUE:
  145. SetupDelayedFileRename();
  146. Result = PrepareForGuiSetupRestart();
  147. Message(MSG_CRLF,0);
  148. if(!Result) {
  149. Message(MSG_WARNING_CANT_CLEAN_UP,0);
  150. }
  151. break;
  152. default:
  153. Result = FAILURE;
  154. break;
  155. }
  156. return(Result);
  157. }
  158. TriState
  159. CheckRestartValue(
  160. VOID
  161. )
  162. /*++
  163. Routine Description:
  164. Check if HKLM\System\Setup:RestartSetup is present as a REG_DWORD
  165. and if so get its value.
  166. Arguments:
  167. None.
  168. Return Value:
  169. Value indicating whether the flag is set (xTrue), not set (xFalse),
  170. or in an unknown state (ie, not present or not REG_DWORD, etc; xUnknown).
  171. --*/
  172. {
  173. UNICODE_STRING UnicodeString;
  174. NTSTATUS Status;
  175. OBJECT_ATTRIBUTES ObjectAttributes;
  176. HANDLE KeyHandle;
  177. ULONG DataLength;
  178. UCHAR Buffer[1024];
  179. PKEY_VALUE_PARTIAL_INFORMATION KeyInfo;
  180. TriState b;
  181. //
  182. // Assume not present.
  183. //
  184. b = xUNKNOWN;
  185. //
  186. // Attempt to open the key.
  187. //
  188. INIT_OBJA(&ObjectAttributes,&UnicodeString,SetupRegistryKeyName);
  189. Status = NtOpenKey(
  190. &KeyHandle,
  191. READ_CONTROL | KEY_QUERY_VALUE,
  192. &ObjectAttributes
  193. );
  194. if(!NT_SUCCESS(Status)) {
  195. KdPrintEx((DPFLTR_SETUP_ID,
  196. DPFLTR_WARNING_LEVEL,
  197. "RestartSetup: Unable to open %ws (%lx)\n",
  198. SetupRegistryKeyName,
  199. Status));
  200. goto c0;
  201. }
  202. //
  203. // Attempt to get the value of "RestartSetup"
  204. //
  205. RtlInitUnicodeString(&UnicodeString,RestartSetupValueName);
  206. Status = NtQueryValueKey(
  207. KeyHandle,
  208. &UnicodeString,
  209. KeyValuePartialInformation,
  210. Buffer,
  211. sizeof(Buffer),
  212. &DataLength
  213. );
  214. if(!NT_SUCCESS(Status)) {
  215. KdPrintEx((DPFLTR_SETUP_ID,
  216. DPFLTR_WARNING_LEVEL,
  217. "RestartSetup: Unable to get value of %ws (%lx)\n",
  218. RestartSetupValueName,
  219. Status));
  220. goto c1;
  221. }
  222. //
  223. // Check for a REG_DWORD value and fetch.
  224. //
  225. KeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
  226. if((KeyInfo->Type == REG_DWORD) && (KeyInfo->DataLength == sizeof(ULONG))) {
  227. b = *(PULONG)KeyInfo->Data ? xTRUE : xFALSE;
  228. KdPrintEx((DPFLTR_SETUP_ID,
  229. DPFLTR_INFO_LEVEL,
  230. "RestartSetup: Restart value is %u\n",
  231. b));
  232. } else {
  233. KdPrintEx((DPFLTR_SETUP_ID,
  234. DPFLTR_WARNING_LEVEL,
  235. "RestartSetup: %ws is corrupt!\n",
  236. RestartSetupValueName));
  237. }
  238. c1:
  239. NtClose(KeyHandle);
  240. c0:
  241. return(b);
  242. }
  243. BOOLEAN
  244. SetRestartValue(
  245. VOID
  246. )
  247. /*++
  248. Routine Description:
  249. Set HKLM\System\Setup:RestartSetup to REG_DWORD 1.
  250. Arguments:
  251. None.
  252. Return Value:
  253. Boolean value indicating whether the operation was successful.
  254. --*/
  255. {
  256. UNICODE_STRING UnicodeString;
  257. NTSTATUS Status;
  258. OBJECT_ATTRIBUTES ObjectAttributes;
  259. HANDLE KeyHandle;
  260. BOOLEAN b;
  261. ULONG One;
  262. //
  263. // Assume failure.
  264. //
  265. b = FALSE;
  266. //
  267. // Attempt to open the key, which must already be present.
  268. //
  269. INIT_OBJA(&ObjectAttributes,&UnicodeString,SetupRegistryKeyName);
  270. Status = NtOpenKey(
  271. &KeyHandle,
  272. READ_CONTROL | KEY_SET_VALUE,
  273. &ObjectAttributes
  274. );
  275. if(!NT_SUCCESS(Status)) {
  276. KdPrintEx((DPFLTR_SETUP_ID,
  277. DPFLTR_WARNING_LEVEL,
  278. "RestartSetup: Unable to open %ws (%lx)\n",
  279. SetupRegistryKeyName,
  280. Status));
  281. goto c0;
  282. }
  283. //
  284. // Attempt to set the value of "RestartSetup" to REG_DWORD 1.
  285. //
  286. RtlInitUnicodeString(&UnicodeString,RestartSetupValueName);
  287. One = 1;
  288. Status = NtSetValueKey(
  289. KeyHandle,
  290. &UnicodeString,
  291. 0,
  292. REG_DWORD,
  293. &One,
  294. sizeof(ULONG)
  295. );
  296. if(!NT_SUCCESS(Status)) {
  297. KdPrintEx((DPFLTR_SETUP_ID,
  298. DPFLTR_WARNING_LEVEL,
  299. "RestartSetup: Unable to set value of %ws (%lx)\n",
  300. RestartSetupValueName,
  301. Status));
  302. goto c1;
  303. }
  304. //
  305. // Success.
  306. //
  307. KdPrintEx((DPFLTR_SETUP_ID,
  308. DPFLTR_INFO_LEVEL,
  309. "RestartSetup: Value of %ws set to 1\n",
  310. RestartSetupValueName));
  311. b = TRUE;
  312. c1:
  313. NtClose(KeyHandle);
  314. c0:
  315. return(b);
  316. }
  317. BOOLEAN
  318. PrepareForGuiSetupRestart(
  319. VOID
  320. )
  321. /*++
  322. Routine Description:
  323. Prepares the system for restarting gui mode setup.
  324. Currently this consists of erasing %sysroot%\system32\config\*,
  325. except *.sav, then copying *.sav to *.
  326. Arguments:
  327. None.
  328. Return Value:
  329. Boolean value indicating whether we were successful.
  330. --*/
  331. {
  332. BOOLEAN b;
  333. //
  334. // Display a message indicating that we are rolling back to the
  335. // start of gui mode setup.
  336. //
  337. Message(MSG_CRLF,0);
  338. Message(MSG_RESTARTING_SETUP,0);
  339. b = RestoreConfigDirectory();
  340. return b;
  341. }
  342. BOOLEAN
  343. RestoreConfigDirectory(
  344. VOID
  345. )
  346. /*++
  347. Routine Description:
  348. Erase %sysroot%\system32\config\*, except *.sav, and userdiff,
  349. then copy *.sav to *.
  350. Arguments:
  351. None.
  352. Return Value:
  353. Boolean value indicating whether we were successful.
  354. --*/
  355. {
  356. NTSTATUS Status;
  357. HANDLE DirectoryHandle;
  358. HANDLE FileHandle;
  359. UNICODE_STRING UnicodeString;
  360. OBJECT_ATTRIBUTES ObjectAttributes;
  361. IO_STATUS_BLOCK IoStatusBlock;
  362. LONGLONG Buffer[2048/sizeof(LONGLONG)];
  363. BOOLEAN FirstQuery;
  364. PFILE_DIRECTORY_INFORMATION FileInfo;
  365. ULONG LengthChars;
  366. BOOLEAN Ignore;
  367. FILE_DISPOSITION_INFORMATION Disposition;
  368. BOOLEAN AnyErrors;
  369. PCOPY_LIST_NODE CopyList,CopyNode,NextNode;
  370. ULONG DotCount;
  371. WCHAR FilenamePrefix[8];
  372. //
  373. // Open \SystemRoot\system32\config for list access.
  374. //
  375. INIT_OBJA(&ObjectAttributes,&UnicodeString,ConfigDirectory);
  376. Status = NtOpenFile(
  377. &DirectoryHandle,
  378. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  379. &ObjectAttributes,
  380. &IoStatusBlock,
  381. FILE_SHARE_READ | FILE_SHARE_WRITE,
  382. FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT
  383. );
  384. DotCount = 0;
  385. Message(MSG_RESTARTING_SETUP,++DotCount);
  386. if(!NT_SUCCESS(Status)) {
  387. KdPrintEx((DPFLTR_SETUP_ID,
  388. DPFLTR_WARNING_LEVEL,
  389. "RestartSetup: unable to open system32\\config for list access (%lx)\n",
  390. Status));
  391. return(FALSE);
  392. }
  393. FirstQuery = TRUE;
  394. FileInfo = (PFILE_DIRECTORY_INFORMATION)Buffer;
  395. AnyErrors = FALSE;
  396. CopyList = NULL;
  397. do {
  398. Status = NtQueryDirectoryFile(
  399. DirectoryHandle,
  400. NULL, // no event to signal
  401. NULL, // no apc routine
  402. NULL, // no apc context
  403. &IoStatusBlock,
  404. Buffer,
  405. sizeof(Buffer)-sizeof(WCHAR), // leave room for terminating nul
  406. FileDirectoryInformation,
  407. TRUE, // want single entry
  408. NULL, // get 'em all
  409. FirstQuery
  410. );
  411. if(NT_SUCCESS(Status)) {
  412. //
  413. // Got a file. First nul-terminate the name.
  414. // Then see if is one to be ignored, ie, ends in .sav.
  415. // Also ignore userdiff.
  416. //
  417. LengthChars = FileInfo->FileNameLength / sizeof(WCHAR);
  418. FileInfo->FileName[LengthChars] = 0;
  419. Ignore = FALSE;
  420. if(FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  421. Ignore = TRUE;
  422. } else {
  423. RtlCopyMemory(FilenamePrefix,FileInfo->FileName,sizeof(FilenamePrefix) - sizeof(WCHAR));
  424. FilenamePrefix[ARRAYSIZE(FilenamePrefix) - 1] = 0;
  425. if(AreStringsEqual(FilenamePrefix,L"userdif")) {
  426. Ignore = TRUE;
  427. } else {
  428. if((LengthChars > 4) && AreStringsEqual(FileInfo->FileName+LengthChars-4,L".sav")) {
  429. Ignore = TRUE;
  430. //
  431. // Also, remember .sav files for later.
  432. //
  433. if(CopyNode = MALLOC(sizeof(COPY_LIST_NODE))) {
  434. if(RtlCreateUnicodeString(&CopyNode->UnicodeString,FileInfo->FileName)) {
  435. CopyNode->FileSize = FileInfo->EndOfFile.QuadPart;
  436. CopyNode->Next = CopyList;
  437. CopyList = CopyNode;
  438. } else {
  439. Status = STATUS_NO_MEMORY;
  440. FREE(CopyNode);
  441. }
  442. } else {
  443. Status = STATUS_NO_MEMORY;
  444. }
  445. }
  446. }
  447. }
  448. if(!Ignore) {
  449. //
  450. // Not supposed to ignore this file: delete it now.
  451. //
  452. Message(MSG_RESTARTING_SETUP,++DotCount);
  453. KdPrintEx((DPFLTR_SETUP_ID,
  454. DPFLTR_INFO_LEVEL,
  455. "RestartSetup: Deleting %ws\n",
  456. FileInfo->FileName));
  457. INIT_OBJA(&ObjectAttributes,&UnicodeString,FileInfo->FileName);
  458. ObjectAttributes.RootDirectory = DirectoryHandle;
  459. Status = NtOpenFile(
  460. &FileHandle,
  461. DELETE,
  462. &ObjectAttributes,
  463. &IoStatusBlock,
  464. FILE_SHARE_VALID_FLAGS,
  465. FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT
  466. );
  467. if(NT_SUCCESS(Status)) {
  468. //
  469. // Perform actual delete operation.
  470. //
  471. Disposition.DeleteFile = TRUE;
  472. Status = NtSetInformationFile(
  473. FileHandle,
  474. &IoStatusBlock,
  475. &Disposition,
  476. sizeof(Disposition),
  477. FileDispositionInformation
  478. );
  479. if(!NT_SUCCESS(Status)) {
  480. KdPrintEx((DPFLTR_SETUP_ID,
  481. DPFLTR_WARNING_LEVEL,
  482. "RestartSetup: Unable to delete %ws (%lx)\n",
  483. FileInfo->FileName,Status));
  484. AnyErrors = TRUE;
  485. }
  486. NtClose(FileHandle);
  487. } else {
  488. KdPrintEx((DPFLTR_SETUP_ID,
  489. DPFLTR_WARNING_LEVEL,
  490. "RestartSetup: Unable to delete %ws (%lx)\n",FileInfo->FileName,
  491. Status));
  492. AnyErrors = TRUE;
  493. }
  494. }
  495. FirstQuery = FALSE;
  496. }
  497. } while(NT_SUCCESS(Status));
  498. //
  499. // Check for normal loop termination.
  500. //
  501. if(Status == STATUS_NO_MORE_FILES) {
  502. Status = STATUS_SUCCESS;
  503. }
  504. //
  505. // Even if we got errors, try to keep going.
  506. //
  507. if(!NT_SUCCESS(Status)) {
  508. AnyErrors = TRUE;
  509. KdPrintEx((DPFLTR_SETUP_ID,
  510. DPFLTR_WARNING_LEVEL,
  511. "RestartSetup: Status %lx enumerating files\n",
  512. Status));
  513. }
  514. //
  515. // Now run down our list of *.sav and copy to *.
  516. //
  517. for(CopyNode=CopyList; CopyNode; CopyNode=NextNode) {
  518. Message(MSG_RESTARTING_SETUP,++DotCount);
  519. //
  520. // Remember next node, because we're going to free this one.
  521. //
  522. NextNode = CopyNode->Next;
  523. //
  524. // Create the target name, which is the same as the source name
  525. // with the .sav stripped off.
  526. //
  527. if((RtlCreateUnicodeString(&UnicodeString,CopyNode->UnicodeString.Buffer)) &&
  528. ((UnicodeString.Length / sizeof(WCHAR)) > 4)) {
  529. UnicodeString.Buffer[(UnicodeString.Length/sizeof(WCHAR))-4] = 0;
  530. UnicodeString.Length -= 4*sizeof(WCHAR);
  531. Status = CopyAFile(
  532. DirectoryHandle,
  533. CopyNode->FileSize,
  534. CopyNode->UnicodeString.Buffer,
  535. UnicodeString.Buffer
  536. );
  537. RtlFreeUnicodeString(&UnicodeString);
  538. } else {
  539. Status = STATUS_NO_MEMORY;
  540. }
  541. if(!NT_SUCCESS(Status)) {
  542. AnyErrors = TRUE;
  543. KdPrintEx((DPFLTR_SETUP_ID,
  544. DPFLTR_WARNING_LEVEL,
  545. "RestartSetup: Unable to copy %ws (%lx)\n",
  546. CopyNode->UnicodeString.Buffer,Status));
  547. }
  548. RtlFreeUnicodeString(&CopyNode->UnicodeString);
  549. FREE(CopyNode);
  550. }
  551. NtClose(DirectoryHandle);
  552. return((BOOLEAN)!AnyErrors);
  553. }
  554. NTSTATUS
  555. CopyAFile(
  556. IN HANDLE DirectoryHandle,
  557. IN LONGLONG FileSize,
  558. IN PCWSTR ExistingFile,
  559. IN PCWSTR NewFile
  560. )
  561. /*++
  562. Routine Description:
  563. Performs a simple file copy within a directory.
  564. The target file must either not exist or be writable.
  565. Only the default stream is copied.
  566. Arguments:
  567. DirectoryHandle - supplies handle to directory within which
  568. the file is to be copied. The handle must have appropriate
  569. access to allow this.
  570. FileSize - supplies size of file to be copied.
  571. ExistingFile - supplies filename of file within directory to
  572. be copied.
  573. NewFile - supplies name of file to be created as a copy of
  574. the existing file.
  575. Return Value:
  576. NT Status code indicating outcome.
  577. --*/
  578. {
  579. UNICODE_STRING UnicodeString;
  580. OBJECT_ATTRIBUTES ObjectAttributes;
  581. IO_STATUS_BLOCK IoStatusBlock;
  582. NTSTATUS Status;
  583. HANDLE SourceHandle;
  584. HANDLE TargetHandle;
  585. ULONG XFerSize;
  586. KdPrintEx((DPFLTR_SETUP_ID,
  587. DPFLTR_INFO_LEVEL,
  588. "RestartSetup: Copying %ws to %ws\n",
  589. ExistingFile,
  590. NewFile));
  591. //
  592. // Open the source for reading. The source must exist.
  593. //
  594. INIT_OBJA(&ObjectAttributes,&UnicodeString,ExistingFile);
  595. ObjectAttributes.RootDirectory = DirectoryHandle;
  596. Status = NtOpenFile(
  597. &SourceHandle,
  598. FILE_READ_DATA | SYNCHRONIZE,
  599. &ObjectAttributes,
  600. &IoStatusBlock,
  601. FILE_SHARE_READ,
  602. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
  603. );
  604. if(!NT_SUCCESS(Status)) {
  605. goto c0;
  606. }
  607. //
  608. // Open/create the target for writing.
  609. //
  610. INIT_OBJA(&ObjectAttributes,&UnicodeString,NewFile);
  611. ObjectAttributes.RootDirectory = DirectoryHandle;
  612. Status = NtCreateFile(
  613. &TargetHandle,
  614. FILE_WRITE_DATA | SYNCHRONIZE,
  615. &ObjectAttributes,
  616. &IoStatusBlock,
  617. NULL,
  618. FILE_ATTRIBUTE_NORMAL,
  619. 0,
  620. FILE_OVERWRITE_IF,
  621. FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
  622. NULL,
  623. 0
  624. );
  625. if(!NT_SUCCESS(Status)) {
  626. goto c1;
  627. }
  628. //
  629. // Read/write buffers while there's still data to copy.
  630. //
  631. while(NT_SUCCESS(Status) && FileSize) {
  632. XFerSize = (FileSize < sizeof(CopyBuffer)) ? (ULONG)FileSize : sizeof(CopyBuffer);
  633. Status = NtReadFile(
  634. SourceHandle,
  635. NULL,
  636. NULL,
  637. NULL,
  638. &IoStatusBlock,
  639. CopyBuffer,
  640. XFerSize,
  641. NULL,
  642. NULL
  643. );
  644. if(NT_SUCCESS(Status)) {
  645. Status = NtWriteFile(
  646. TargetHandle,
  647. NULL,
  648. NULL,
  649. NULL,
  650. &IoStatusBlock,
  651. CopyBuffer,
  652. XFerSize,
  653. NULL,
  654. NULL
  655. );
  656. FileSize -= XFerSize;
  657. }
  658. }
  659. NtClose(TargetHandle);
  660. c1:
  661. NtClose(SourceHandle);
  662. c0:
  663. return(Status);
  664. }
  665. BOOLEAN
  666. AreStringsEqual(
  667. IN PCWSTR String1,
  668. IN PCWSTR String2
  669. )
  670. /*++
  671. Routine Description:
  672. Compare 2 0-terminated unicode strings, case insensitively.
  673. Arguments:
  674. String1 - supplies first string for comparison
  675. String2 - supplies second string for comparison
  676. Return Value:
  677. Boolean value indicating whether strings are equal.
  678. TRUE = yes; FALSE = no.
  679. --*/
  680. {
  681. UNICODE_STRING u1;
  682. UNICODE_STRING u2;
  683. RtlInitUnicodeString(&u1,String1);
  684. RtlInitUnicodeString(&u2,String2);
  685. return((BOOLEAN)(RtlCompareUnicodeString(&u1,&u2,TRUE) == 0));
  686. }
  687. BOOLEAN
  688. Message(
  689. IN ULONG MessageId,
  690. IN ULONG DotCount,
  691. ...
  692. )
  693. /*++
  694. Routine Description:
  695. Format and display a message, which is retreived from
  696. the image's message resources.
  697. Arguments:
  698. MessageId - Supplies the message id of the message resource.
  699. DotCount - Supplies number of trailing dots to be appended to
  700. the message text prior to display. If this value is non-0,
  701. then the message shouldn't have a trailing cr/lf!
  702. Additional arguments specify message-specific inserts.
  703. Return Value:
  704. Boolean value indicating whether the message was displayed.
  705. --*/
  706. {
  707. PVOID ImageBase;
  708. NTSTATUS Status;
  709. PMESSAGE_RESOURCE_ENTRY MessageEntry;
  710. ANSI_STRING AnsiString;
  711. UNICODE_STRING UnicodeString;
  712. va_list arglist;
  713. WCHAR Buffer[1024];
  714. ULONG u;
  715. //
  716. // Get our image base address
  717. //
  718. ImageBase = NtCurrentPeb()->ImageBaseAddress;
  719. if(!ImageBase) {
  720. return(FALSE);
  721. }
  722. //
  723. // Find the message.
  724. // For DBCS codepages we will use English resources instead of
  725. // default resource because we can only display ASCII characters onto
  726. // blue Screen via HalDisplayString()
  727. //
  728. Status = RtlFindMessage(
  729. ImageBase,
  730. 11,
  731. NLS_MB_CODE_PAGE_TAG ? MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US) : 0,
  732. MessageId,
  733. &MessageEntry
  734. );
  735. if(!NT_SUCCESS(Status)) {
  736. return(FALSE);
  737. }
  738. //
  739. // If the message is not unicode, convert to unicode.
  740. // Let the conversion routine allocate the buffer.
  741. //
  742. if(!(MessageEntry->Flags & MESSAGE_RESOURCE_UNICODE)) {
  743. RtlInitAnsiString(&AnsiString,MessageEntry->Text);
  744. Status = RtlAnsiStringToUnicodeString(&UnicodeString,&AnsiString,TRUE);
  745. } else {
  746. //
  747. // Message is already unicode. Make a copy.
  748. //
  749. if(!RtlCreateUnicodeString(&UnicodeString,(PWSTR)MessageEntry->Text)) {
  750. return(FALSE);
  751. }
  752. }
  753. //
  754. // Format the message.
  755. //
  756. va_start(arglist,DotCount);
  757. Status = RtlFormatMessage(
  758. UnicodeString.Buffer,
  759. 0, // max width
  760. FALSE, // don't ignore inserts
  761. FALSE, // args are not ansi
  762. FALSE, // args are not an array
  763. &arglist,
  764. Buffer,
  765. sizeof(Buffer) - (DotCount + 1) * sizeof(WCHAR), // leave space for dots + CR
  766. NULL
  767. );
  768. va_end(arglist);
  769. //
  770. // We don't need the message source any more. Free it.
  771. //
  772. RtlFreeUnicodeString(&UnicodeString);
  773. //
  774. // Verify result of RtlFormatMessage for a buffer overflow
  775. // or any other error condition
  776. //
  777. if(!NT_SUCCESS(Status)) {
  778. return(FALSE);
  779. }
  780. //
  781. // Add dots and cr.
  782. //
  783. for(u=0; u<DotCount; u++) {
  784. wcscat(Buffer,L".");
  785. }
  786. wcscat(Buffer,L"\r");
  787. //
  788. // Print out the message
  789. //
  790. RtlInitUnicodeString(&UnicodeString,Buffer);
  791. Status = NtDisplayString(&UnicodeString);
  792. return(NT_SUCCESS(Status));
  793. }