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.

555 lines
13 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. PpLastGood.c
  5. Abstract:
  6. This module handles last known good processing for the IO subsystem.
  7. Author:
  8. Adrian J. Oney - April 4, 2000
  9. Revision History:
  10. --*/
  11. #include "pnpmgrp.h"
  12. #include "pilastgood.h"
  13. #pragma hdrstop
  14. #ifdef ALLOC_PRAGMA
  15. #pragma alloc_text(INIT, PpLastGoodDoBootProcessing)
  16. #pragma alloc_text(INIT, PiLastGoodRevertLastKnownDirectory)
  17. #pragma alloc_text(INIT, PiLastGoodRevertCopyCallback)
  18. #pragma alloc_text(INIT, PiLastGoodCopyKeyContents)
  19. #endif
  20. #define POOLTAG_LASTGOOD ('gLpP')
  21. VOID
  22. PpLastGoodDoBootProcessing(
  23. VOID
  24. )
  25. /*++
  26. Routine Description:
  27. This rolls back the system files to the state they were during the last
  28. known good boot. It should only be called from within a last known good
  29. boot, and at the earliest point possible.
  30. Arguments:
  31. None.
  32. Return Value:
  33. None.
  34. --*/
  35. {
  36. UNICODE_STRING lastKnownGoodPath, lastKnownGoodTmpPath;
  37. UNICODE_STRING lastKnownGoodDelKey, lastKnownGoodTmpDelKey;
  38. NTSTATUS status;
  39. RtlInitUnicodeString(
  40. &lastKnownGoodPath,
  41. L"\\SystemRoot\\LastGood"
  42. );
  43. RtlInitUnicodeString(
  44. &lastKnownGoodDelKey,
  45. CM_REGISTRY_MACHINE(REGSTR_PATH_LASTGOOD)
  46. );
  47. RtlInitUnicodeString(
  48. &lastKnownGoodTmpPath,
  49. L"\\SystemRoot\\LastGood.Tmp"
  50. );
  51. RtlInitUnicodeString(
  52. &lastKnownGoodTmpDelKey,
  53. CM_REGISTRY_MACHINE(REGSTR_PATH_LASTGOODTMP)
  54. );
  55. if (!CmIsLastKnownGoodBoot()) {
  56. //
  57. // If we are in safe mode we don't do anything to commit the current
  58. // boot.
  59. //
  60. if (InitSafeBootMode) {
  61. return;
  62. }
  63. //
  64. // We are in a non-last known good boot. We immediately move all the
  65. // previous last known good info into the tmp subtree. We do this
  66. // because we will taint the normal LKG path prior to marking it good
  67. // (eg pre-logon server side install of PnP devices). Note that if the
  68. // tmp directory already exists, we *don't* perform the copy, as a good
  69. // boot is signified by deleting that directory.
  70. //
  71. status = IopFileUtilRename(
  72. &lastKnownGoodPath,
  73. &lastKnownGoodTmpPath,
  74. FALSE
  75. );
  76. if (!NT_SUCCESS(status)) {
  77. return;
  78. }
  79. //
  80. // It worked, now we also take care of the registry info.
  81. //
  82. PiLastGoodCopyKeyContents(
  83. &lastKnownGoodDelKey,
  84. &lastKnownGoodTmpDelKey,
  85. TRUE
  86. );
  87. return;
  88. }
  89. //
  90. // Revert the LastGood tree. This tree contains the changes made after
  91. // SMSS.EXE's initialization.
  92. //
  93. PiLastGoodRevertLastKnownDirectory(
  94. &lastKnownGoodPath,
  95. &lastKnownGoodDelKey
  96. );
  97. //
  98. // Revert the LastGood.Tmp tree. This tree contains the changes made on
  99. // a prior boot if we crashed between SMSS.EXE's initialization and login.
  100. //
  101. PiLastGoodRevertLastKnownDirectory(
  102. &lastKnownGoodTmpPath,
  103. &lastKnownGoodTmpDelKey);
  104. }
  105. VOID
  106. PiLastGoodRevertLastKnownDirectory(
  107. IN PUNICODE_STRING LastKnownGoodDirectory,
  108. IN PUNICODE_STRING LastKnownGoodRegPath
  109. )
  110. /*++
  111. Routine Description:
  112. This function commits the changes specified by a given last known good
  113. directory and reg key. All files in the directory are first copied over any
  114. existing files. Subsequently, any files specified in the reg key are
  115. deleted.
  116. Arguments:
  117. LastKnownGoodDirectory - Directory subtree to copy over \SystemRoot. This
  118. path is emptied when the copy is complete.
  119. LastKnownGoodRegPath - Key containing files to delete. Each value entry
  120. is relative to \SystemRoot, and the value itself
  121. contains the name of the file to delete.
  122. Return Value:
  123. None.
  124. --*/
  125. {
  126. NTSTATUS status;
  127. UNICODE_STRING fileToDelete, fileName;
  128. OBJECT_ATTRIBUTES lastKnownGoodKeyAttributes;
  129. OBJECT_ATTRIBUTES fileAttributes;
  130. HANDLE lastGoodRegHandle;
  131. UCHAR keyBuffer[sizeof(KEY_VALUE_FULL_INFORMATION) + 256*sizeof(WCHAR) + sizeof(ULONG)];
  132. WCHAR filePathName[255 + sizeof("\\SystemRoot\\")];
  133. PKEY_VALUE_FULL_INFORMATION pFullKeyInformation;
  134. ULONG resultLength, i, j, optionValue;
  135. //
  136. // Preinit our pointer to the full information buffer.
  137. //
  138. pFullKeyInformation = (PKEY_VALUE_FULL_INFORMATION) keyBuffer;
  139. //
  140. // Preform the file copy.
  141. //
  142. IopFileUtilWalkDirectoryTreeTopDown(
  143. LastKnownGoodDirectory,
  144. ( DIRWALK_INCLUDE_FILES | DIRWALK_CULL_DOTPATHS | DIRWALK_TRAVERSE ),
  145. PiLastGoodRevertCopyCallback,
  146. (PVOID) LastKnownGoodDirectory
  147. );
  148. //
  149. // Delete all the files specified in by the registry keys.
  150. //
  151. InitializeObjectAttributes(
  152. &lastKnownGoodKeyAttributes,
  153. LastKnownGoodRegPath,
  154. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  155. NULL,
  156. (PSECURITY_DESCRIPTOR) NULL
  157. );
  158. status = ZwOpenKey(
  159. &lastGoodRegHandle,
  160. KEY_ALL_ACCESS,
  161. &lastKnownGoodKeyAttributes
  162. );
  163. if (!NT_SUCCESS(status)) {
  164. return;
  165. }
  166. i = 0;
  167. while (1) {
  168. status = ZwEnumerateValueKey(
  169. lastGoodRegHandle,
  170. i++,
  171. KeyValueFullInformation,
  172. pFullKeyInformation,
  173. sizeof(keyBuffer),
  174. &resultLength
  175. );
  176. if (!NT_SUCCESS(status)) {
  177. if (status == STATUS_NO_MORE_ENTRIES) {
  178. status = STATUS_SUCCESS;
  179. }
  180. break;
  181. }
  182. if (resultLength == 0) {
  183. continue;
  184. }
  185. if (pFullKeyInformation->Type != REG_DWORD) {
  186. continue;
  187. }
  188. if (pFullKeyInformation->DataLength != sizeof(ULONG)) {
  189. continue;
  190. }
  191. optionValue = *((PULONG) (((PUCHAR) pFullKeyInformation) +
  192. pFullKeyInformation->DataOffset));
  193. //
  194. // We only understand deletes (and no flags).
  195. //
  196. if ((optionValue & 0xFF) != 1) {
  197. continue;
  198. }
  199. fileToDelete.Buffer = filePathName;
  200. fileToDelete.Length = (USHORT) 0;
  201. fileToDelete.MaximumLength = sizeof(filePathName);
  202. fileName.Buffer = (PWSTR) pFullKeyInformation->Name;
  203. fileName.Length = (USHORT) pFullKeyInformation->NameLength;
  204. fileName.MaximumLength = fileName.Length;
  205. RtlAppendUnicodeToString(&fileToDelete, L"\\SystemRoot\\");
  206. RtlAppendUnicodeStringToString(&fileToDelete, &fileName);
  207. //
  208. // Note that the key name has all '\'s changed to '/'s. Here we change
  209. // them back as the file systems are *almost* but not quite slash-tilt
  210. // agnostic.
  211. //
  212. for(j = sizeof(L"\\SystemRoot\\")/sizeof(WCHAR);
  213. j < fileToDelete.Length/sizeof(WCHAR);
  214. j++) {
  215. if (filePathName[j] == L'/') {
  216. filePathName[j] = L'\\';
  217. }
  218. }
  219. IopFileUtilClearAttributes(
  220. &fileToDelete,
  221. ( FILE_ATTRIBUTE_READONLY |
  222. FILE_ATTRIBUTE_HIDDEN |
  223. FILE_ATTRIBUTE_SYSTEM )
  224. );
  225. InitializeObjectAttributes(
  226. &fileAttributes,
  227. &fileToDelete,
  228. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  229. NULL,
  230. NULL
  231. );
  232. ZwDeleteFile(&fileAttributes);
  233. }
  234. ZwDeleteKey(&lastGoodRegHandle);
  235. ZwClose(lastGoodRegHandle);
  236. }
  237. NTSTATUS
  238. PiLastGoodRevertCopyCallback(
  239. IN PUNICODE_STRING FullPathName,
  240. IN PUNICODE_STRING FileName,
  241. IN ULONG FileAttributes,
  242. IN PVOID Context
  243. )
  244. /*++
  245. Routine Description:
  246. This function is called back for each file in each of the appropriate
  247. LastKnownGood directories. It's job is to move the specified file into the
  248. appropriate mainline directory.
  249. Arguments:
  250. FullPathName - Full path name of the identified file, relative to SystemRoot
  251. FileName - Filename portion, exempts directory.
  252. Context - Unicode string name of the root directory scanned. The string
  253. should not have a trailing '\\'
  254. Return Value:
  255. NTSTATUS (Unsuccessful statusi abort further copies).
  256. --*/
  257. {
  258. NTSTATUS status;
  259. const USHORT rootLength = sizeof(L"\\SystemRoot\\")-sizeof(WCHAR);
  260. USHORT lastGoodLength;
  261. UNICODE_STRING targetFile;
  262. PWCHAR newPathText;
  263. //
  264. // Add in an extra character to skip past the '\\'
  265. //
  266. lastGoodLength = ((PUNICODE_STRING) Context)->Length + sizeof(WCHAR);
  267. newPathText = ExAllocatePoolWithTag(
  268. PagedPool,
  269. FullPathName->Length,
  270. POOLTAG_LASTGOOD
  271. );
  272. if (newPathText == NULL) {
  273. return STATUS_INSUFFICIENT_RESOURCES;
  274. }
  275. //
  276. // Change \\SystemRoot\LastGood\Blah... to \\SystemRoot\Blah...
  277. //
  278. RtlCopyMemory(
  279. newPathText,
  280. FullPathName->Buffer,
  281. rootLength
  282. );
  283. RtlCopyMemory(
  284. newPathText + rootLength/sizeof(WCHAR),
  285. FullPathName->Buffer + lastGoodLength/sizeof(WCHAR),
  286. FullPathName->Length - lastGoodLength
  287. );
  288. //
  289. // Setup our unicode string path.
  290. //
  291. targetFile.Length = FullPathName->Length - lastGoodLength + rootLength;
  292. targetFile.MaximumLength = targetFile.Length;
  293. targetFile.Buffer = newPathText;
  294. //
  295. // Perform the rename.
  296. //
  297. status = IopFileUtilRename(FullPathName, &targetFile, TRUE);
  298. //
  299. // Cleanup and exit.
  300. //
  301. ExFreePool(newPathText);
  302. return status;
  303. }
  304. NTSTATUS
  305. PiLastGoodCopyKeyContents(
  306. IN PUNICODE_STRING SourceRegPath,
  307. IN PUNICODE_STRING DestinationRegPath,
  308. IN BOOLEAN DeleteSourceKey
  309. )
  310. /*++
  311. Routine Description:
  312. This function copies all the value keys in one source path to the
  313. destination path.
  314. NOTE: This function's implementation currently restricts the total of value
  315. and name lengths to 512 bytes, and is therefore not a generic key
  316. copy function.
  317. Arguments:
  318. SourcePath - Registry path to enumerate and copy keys from.
  319. DestinationPath - Registry path to receive new value keys. This key will
  320. be created if it does not exist.
  321. DeleteSourceKey - If TRUE, source key is deleted upn successful completion
  322. of copy.
  323. Return Value:
  324. None.
  325. --*/
  326. {
  327. NTSTATUS status;
  328. OBJECT_ATTRIBUTES sourceKeyAttributes, destinationKeyAttributes;
  329. HANDLE sourceRegHandle, destinationRegHandle;
  330. UCHAR keyBuffer[sizeof(KEY_VALUE_FULL_INFORMATION) + 512*sizeof(WCHAR)];
  331. PKEY_VALUE_FULL_INFORMATION pFullKeyInformation;
  332. ULONG resultLength, i, disposition;
  333. UNICODE_STRING valueName;
  334. //
  335. // Prep the buffer.
  336. //
  337. pFullKeyInformation = (PKEY_VALUE_FULL_INFORMATION) keyBuffer;
  338. //
  339. // Open the source key.
  340. //
  341. InitializeObjectAttributes(
  342. &sourceKeyAttributes,
  343. SourceRegPath,
  344. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  345. NULL,
  346. (PSECURITY_DESCRIPTOR) NULL
  347. );
  348. status = ZwOpenKey(
  349. &sourceRegHandle,
  350. KEY_ALL_ACCESS,
  351. &sourceKeyAttributes
  352. );
  353. if (!NT_SUCCESS(status)) {
  354. return status;
  355. }
  356. //
  357. // Open or create the destination key.
  358. //
  359. InitializeObjectAttributes(
  360. &destinationKeyAttributes,
  361. DestinationRegPath,
  362. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  363. NULL,
  364. (PSECURITY_DESCRIPTOR) NULL
  365. );
  366. status = ZwCreateKey(
  367. &destinationRegHandle,
  368. KEY_ALL_ACCESS,
  369. &destinationKeyAttributes,
  370. 0,
  371. NULL,
  372. REG_OPTION_NON_VOLATILE,
  373. &disposition
  374. );
  375. if (!NT_SUCCESS(status)) {
  376. ZwClose(sourceRegHandle);
  377. return status;
  378. }
  379. //
  380. // Iterate over all the value keys, copying each.
  381. //
  382. i = 0;
  383. while (1) {
  384. status = ZwEnumerateValueKey(
  385. sourceRegHandle,
  386. i++,
  387. KeyValueFullInformation,
  388. pFullKeyInformation,
  389. sizeof(keyBuffer),
  390. &resultLength
  391. );
  392. if (!NT_SUCCESS(status)) {
  393. if (status == STATUS_NO_MORE_ENTRIES) {
  394. status = STATUS_SUCCESS;
  395. }
  396. break;
  397. }
  398. valueName.Buffer = pFullKeyInformation->Name;
  399. valueName.Length = (USHORT) pFullKeyInformation->NameLength;
  400. valueName.MaximumLength = valueName.Length;
  401. status = ZwSetValueKey(
  402. destinationRegHandle,
  403. &valueName,
  404. 0,
  405. pFullKeyInformation->Type,
  406. ((PUCHAR) pFullKeyInformation) + pFullKeyInformation->DataOffset,
  407. pFullKeyInformation->DataLength
  408. );
  409. if (!NT_SUCCESS(status)) {
  410. break;
  411. }
  412. }
  413. //
  414. // Cleanup time.
  415. //
  416. if (NT_SUCCESS(status) && DeleteSourceKey) {
  417. ZwDeleteKey(sourceRegHandle);
  418. }
  419. ZwClose(sourceRegHandle);
  420. ZwClose(destinationRegHandle);
  421. return status;
  422. }