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.

559 lines
14 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  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. }
  106. VOID
  107. PiLastGoodRevertLastKnownDirectory(
  108. IN PUNICODE_STRING LastKnownGoodDirectory,
  109. IN PUNICODE_STRING LastKnownGoodRegPath
  110. )
  111. /*++
  112. Routine Description:
  113. This function commits the changes specified by a given last known good
  114. directory and reg key. All files in the directory are first copied over any
  115. existing files. Subsequently, any files specified in the reg key are
  116. deleted.
  117. Arguments:
  118. LastKnownGoodDirectory - Directory subtree to copy over \SystemRoot. This
  119. path is emptied when the copy is complete.
  120. LastKnownGoodRegPath - Key containing files to delete. Each value entry
  121. is relative to \SystemRoot, and the value itself
  122. contains the name of the file to delete.
  123. Return Value:
  124. None.
  125. --*/
  126. {
  127. NTSTATUS status;
  128. UNICODE_STRING fileToDelete, fileName;
  129. OBJECT_ATTRIBUTES lastKnownGoodKeyAttributes;
  130. OBJECT_ATTRIBUTES fileAttributes;
  131. HANDLE lastGoodRegHandle;
  132. UCHAR keyBuffer[sizeof(KEY_VALUE_FULL_INFORMATION) + 256*sizeof(WCHAR) + sizeof(ULONG)];
  133. WCHAR filePathName[255 + sizeof("\\SystemRoot\\")];
  134. PKEY_VALUE_FULL_INFORMATION pFullKeyInformation;
  135. ULONG resultLength, i, j, optionValue;
  136. //
  137. // Preinit our pointer to the full information buffer.
  138. //
  139. pFullKeyInformation = (PKEY_VALUE_FULL_INFORMATION) keyBuffer;
  140. //
  141. // Preform the file copy.
  142. //
  143. IopFileUtilWalkDirectoryTreeTopDown(
  144. LastKnownGoodDirectory,
  145. ( DIRWALK_INCLUDE_FILES | DIRWALK_CULL_DOTPATHS | DIRWALK_TRAVERSE ),
  146. PiLastGoodRevertCopyCallback,
  147. (PVOID) LastKnownGoodDirectory
  148. );
  149. //
  150. // Delete all the files specified in by the registry keys.
  151. //
  152. InitializeObjectAttributes(
  153. &lastKnownGoodKeyAttributes,
  154. LastKnownGoodRegPath,
  155. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  156. NULL,
  157. (PSECURITY_DESCRIPTOR) NULL
  158. );
  159. status = ZwOpenKey(
  160. &lastGoodRegHandle,
  161. KEY_ALL_ACCESS,
  162. &lastKnownGoodKeyAttributes
  163. );
  164. if (!NT_SUCCESS(status)) {
  165. return;
  166. }
  167. i = 0;
  168. while (1) {
  169. status = ZwEnumerateValueKey(
  170. lastGoodRegHandle,
  171. i++,
  172. KeyValueFullInformation,
  173. pFullKeyInformation,
  174. sizeof(keyBuffer),
  175. &resultLength
  176. );
  177. if (!NT_SUCCESS(status)) {
  178. if (status == STATUS_NO_MORE_ENTRIES) {
  179. status = STATUS_SUCCESS;
  180. }
  181. break;
  182. }
  183. if (resultLength == 0) {
  184. continue;
  185. }
  186. if (pFullKeyInformation->Type != REG_DWORD) {
  187. continue;
  188. }
  189. if (pFullKeyInformation->DataLength != sizeof(ULONG)) {
  190. continue;
  191. }
  192. optionValue = *((PULONG) (((PUCHAR) pFullKeyInformation) +
  193. pFullKeyInformation->DataOffset));
  194. //
  195. // We only understand deletes (and no flags).
  196. //
  197. if ((optionValue & 0xFF) != 1) {
  198. continue;
  199. }
  200. fileToDelete.Buffer = filePathName;
  201. fileToDelete.Length = (USHORT) 0;
  202. fileToDelete.MaximumLength = sizeof(filePathName);
  203. fileName.Buffer = (PWSTR) pFullKeyInformation->Name;
  204. fileName.Length = (USHORT) pFullKeyInformation->NameLength;
  205. fileName.MaximumLength = fileName.Length;
  206. RtlAppendUnicodeToString(&fileToDelete, L"\\SystemRoot\\");
  207. RtlAppendUnicodeStringToString(&fileToDelete, &fileName);
  208. //
  209. // Note that the key name has all '\'s changed to '/'s. Here we change
  210. // them back as the file systems are *almost* but not quite slash-tilt
  211. // agnostic.
  212. //
  213. for(j = sizeof(L"\\SystemRoot\\")/sizeof(WCHAR);
  214. j < fileToDelete.Length/sizeof(WCHAR);
  215. j++) {
  216. if (filePathName[j] == L'/') {
  217. filePathName[j] = L'\\';
  218. }
  219. }
  220. IopFileUtilClearAttributes(
  221. &fileToDelete,
  222. ( FILE_ATTRIBUTE_READONLY |
  223. FILE_ATTRIBUTE_HIDDEN |
  224. FILE_ATTRIBUTE_SYSTEM )
  225. );
  226. InitializeObjectAttributes(
  227. &fileAttributes,
  228. &fileToDelete,
  229. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  230. NULL,
  231. NULL
  232. );
  233. ZwDeleteFile(&fileAttributes);
  234. }
  235. ZwDeleteKey(&lastGoodRegHandle);
  236. ZwClose(lastGoodRegHandle);
  237. }
  238. NTSTATUS
  239. PiLastGoodRevertCopyCallback(
  240. IN PUNICODE_STRING FullPathName,
  241. IN PUNICODE_STRING FileName,
  242. IN ULONG FileAttributes,
  243. IN PVOID Context
  244. )
  245. /*++
  246. Routine Description:
  247. This function is called back for each file in each of the appropriate
  248. LastKnownGood directories. It's job is to move the specified file into the
  249. appropriate mainline directory.
  250. Arguments:
  251. FullPathName - Full path name of the identified file, relative to SystemRoot
  252. FileName - Filename portion, exempts directory.
  253. Context - Unicode string name of the root directory scanned. The string
  254. should not have a trailing '\\'
  255. Return Value:
  256. NTSTATUS (Unsuccessful statusi abort further copies).
  257. --*/
  258. {
  259. NTSTATUS status;
  260. const USHORT rootLength = sizeof(L"\\SystemRoot\\")-sizeof(WCHAR);
  261. USHORT lastGoodLength;
  262. UNICODE_STRING targetFile;
  263. PWCHAR newPathText;
  264. UNREFERENCED_PARAMETER (FileAttributes);
  265. UNREFERENCED_PARAMETER (FileName);
  266. //
  267. // Add in an extra character to skip past the '\\'
  268. //
  269. lastGoodLength = ((PUNICODE_STRING) Context)->Length + sizeof(WCHAR);
  270. newPathText = ExAllocatePoolWithTag(
  271. PagedPool,
  272. FullPathName->Length,
  273. POOLTAG_LASTGOOD
  274. );
  275. if (newPathText == NULL) {
  276. return STATUS_INSUFFICIENT_RESOURCES;
  277. }
  278. //
  279. // Change \\SystemRoot\LastGood\Blah... to \\SystemRoot\Blah...
  280. //
  281. RtlCopyMemory(
  282. newPathText,
  283. FullPathName->Buffer,
  284. rootLength
  285. );
  286. RtlCopyMemory(
  287. newPathText + rootLength/sizeof(WCHAR),
  288. FullPathName->Buffer + lastGoodLength/sizeof(WCHAR),
  289. FullPathName->Length - lastGoodLength
  290. );
  291. //
  292. // Setup our unicode string path.
  293. //
  294. targetFile.Length = FullPathName->Length - lastGoodLength + rootLength;
  295. targetFile.MaximumLength = targetFile.Length;
  296. targetFile.Buffer = newPathText;
  297. //
  298. // Perform the rename.
  299. //
  300. status = IopFileUtilRename(FullPathName, &targetFile, TRUE);
  301. //
  302. // Cleanup and exit.
  303. //
  304. ExFreePool(newPathText);
  305. return status;
  306. }
  307. NTSTATUS
  308. PiLastGoodCopyKeyContents(
  309. IN PUNICODE_STRING SourceRegPath,
  310. IN PUNICODE_STRING DestinationRegPath,
  311. IN BOOLEAN DeleteSourceKey
  312. )
  313. /*++
  314. Routine Description:
  315. This function copies all the value keys in one source path to the
  316. destination path.
  317. NOTE: This function's implementation currently restricts the total of value
  318. and name lengths to 512 bytes, and is therefore not a generic key
  319. copy function.
  320. Arguments:
  321. SourcePath - Registry path to enumerate and copy keys from.
  322. DestinationPath - Registry path to receive new value keys. This key will
  323. be created if it does not exist.
  324. DeleteSourceKey - If TRUE, source key is deleted upn successful completion
  325. of copy.
  326. Return Value:
  327. None.
  328. --*/
  329. {
  330. NTSTATUS status;
  331. OBJECT_ATTRIBUTES sourceKeyAttributes, destinationKeyAttributes;
  332. HANDLE sourceRegHandle, destinationRegHandle;
  333. UCHAR keyBuffer[sizeof(KEY_VALUE_FULL_INFORMATION) + 512*sizeof(WCHAR)];
  334. PKEY_VALUE_FULL_INFORMATION pFullKeyInformation;
  335. ULONG resultLength, i, disposition;
  336. UNICODE_STRING valueName;
  337. //
  338. // Prep the buffer.
  339. //
  340. pFullKeyInformation = (PKEY_VALUE_FULL_INFORMATION) keyBuffer;
  341. //
  342. // Open the source key.
  343. //
  344. InitializeObjectAttributes(
  345. &sourceKeyAttributes,
  346. SourceRegPath,
  347. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  348. NULL,
  349. (PSECURITY_DESCRIPTOR) NULL
  350. );
  351. status = ZwOpenKey(
  352. &sourceRegHandle,
  353. KEY_ALL_ACCESS,
  354. &sourceKeyAttributes
  355. );
  356. if (!NT_SUCCESS(status)) {
  357. return status;
  358. }
  359. //
  360. // Open or create the destination key.
  361. //
  362. InitializeObjectAttributes(
  363. &destinationKeyAttributes,
  364. DestinationRegPath,
  365. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  366. NULL,
  367. (PSECURITY_DESCRIPTOR) NULL
  368. );
  369. status = ZwCreateKey(
  370. &destinationRegHandle,
  371. KEY_ALL_ACCESS,
  372. &destinationKeyAttributes,
  373. 0,
  374. NULL,
  375. REG_OPTION_NON_VOLATILE,
  376. &disposition
  377. );
  378. if (!NT_SUCCESS(status)) {
  379. ZwClose(sourceRegHandle);
  380. return status;
  381. }
  382. //
  383. // Iterate over all the value keys, copying each.
  384. //
  385. i = 0;
  386. while (1) {
  387. status = ZwEnumerateValueKey(
  388. sourceRegHandle,
  389. i++,
  390. KeyValueFullInformation,
  391. pFullKeyInformation,
  392. sizeof(keyBuffer),
  393. &resultLength
  394. );
  395. if (!NT_SUCCESS(status)) {
  396. if (status == STATUS_NO_MORE_ENTRIES) {
  397. status = STATUS_SUCCESS;
  398. }
  399. break;
  400. }
  401. valueName.Buffer = pFullKeyInformation->Name;
  402. valueName.Length = (USHORT) pFullKeyInformation->NameLength;
  403. valueName.MaximumLength = valueName.Length;
  404. status = ZwSetValueKey(
  405. destinationRegHandle,
  406. &valueName,
  407. 0,
  408. pFullKeyInformation->Type,
  409. ((PUCHAR) pFullKeyInformation) + pFullKeyInformation->DataOffset,
  410. pFullKeyInformation->DataLength
  411. );
  412. if (!NT_SUCCESS(status)) {
  413. break;
  414. }
  415. }
  416. //
  417. // Cleanup time.
  418. //
  419. if (NT_SUCCESS(status) && DeleteSourceKey) {
  420. ZwDeleteKey(sourceRegHandle);
  421. }
  422. ZwClose(sourceRegHandle);
  423. ZwClose(destinationRegHandle);
  424. return status;
  425. }