1 /* 2 * PROJECT: ReactOS Service Control Manager 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: base/system/services/controlset.c 5 * PURPOSE: Control Set Management 6 * COPYRIGHT: Copyright 2012 Eric Kohl 7 * 8 */ 9 10 /* INCLUDES *****************************************************************/ 11 12 #include "services.h" 13 14 #define NDEBUG 15 #include <debug.h> 16 17 18 /* GLOBALS *******************************************************************/ 19 20 static BOOL bBootAccepted = FALSE; 21 22 23 /* FUNCTIONS *****************************************************************/ 24 25 #if (_WIN32_WINNT < 0x0600) 26 static 27 DWORD 28 ScmCopyTree( 29 HKEY hSrcKey, 30 HKEY hDstKey) 31 { 32 DWORD dwSubKeys; 33 DWORD dwValues; 34 DWORD dwType; 35 DWORD dwMaxSubKeyNameLength; 36 DWORD dwSubKeyNameLength; 37 DWORD dwMaxValueNameLength; 38 DWORD dwValueNameLength; 39 DWORD dwMaxValueLength; 40 DWORD dwValueLength; 41 DWORD dwDisposition; 42 DWORD i; 43 LPWSTR lpNameBuffer; 44 LPBYTE lpDataBuffer; 45 HKEY hDstSubKey; 46 HKEY hSrcSubKey; 47 DWORD dwError; 48 49 DPRINT("ScmCopyTree()\n"); 50 51 dwError = RegQueryInfoKey(hSrcKey, 52 NULL, 53 NULL, 54 NULL, 55 &dwSubKeys, 56 &dwMaxSubKeyNameLength, 57 NULL, 58 &dwValues, 59 &dwMaxValueNameLength, 60 &dwMaxValueLength, 61 NULL, 62 NULL); 63 if (dwError != ERROR_SUCCESS) 64 { 65 DPRINT1("RegQueryInfoKey() failed (Error %lu)\n", dwError); 66 return dwError; 67 } 68 69 dwMaxSubKeyNameLength++; 70 dwMaxValueNameLength++; 71 72 DPRINT("dwSubKeys %lu\n", dwSubKeys); 73 DPRINT("dwMaxSubKeyNameLength %lu\n", dwMaxSubKeyNameLength); 74 DPRINT("dwValues %lu\n", dwValues); 75 DPRINT("dwMaxValueNameLength %lu\n", dwMaxValueNameLength); 76 DPRINT("dwMaxValueLength %lu\n", dwMaxValueLength); 77 78 /* Copy subkeys */ 79 if (dwSubKeys != 0) 80 { 81 lpNameBuffer = HeapAlloc(GetProcessHeap(), 82 0, 83 dwMaxSubKeyNameLength * sizeof(WCHAR)); 84 if (lpNameBuffer == NULL) 85 { 86 DPRINT1("Buffer allocation failed\n"); 87 return ERROR_NOT_ENOUGH_MEMORY; 88 } 89 90 for (i = 0; i < dwSubKeys; i++) 91 { 92 dwSubKeyNameLength = dwMaxSubKeyNameLength; 93 dwError = RegEnumKeyExW(hSrcKey, 94 i, 95 lpNameBuffer, 96 &dwSubKeyNameLength, 97 NULL, 98 NULL, 99 NULL, 100 NULL); 101 if (dwError != ERROR_SUCCESS) 102 { 103 DPRINT1("Subkey enumeration failed (Error %lu)\n", dwError); 104 HeapFree(GetProcessHeap(), 105 0, 106 lpNameBuffer); 107 return dwError; 108 } 109 110 dwError = RegCreateKeyExW(hDstKey, 111 lpNameBuffer, 112 0, 113 NULL, 114 REG_OPTION_NON_VOLATILE, 115 KEY_WRITE, 116 NULL, 117 &hDstSubKey, 118 &dwDisposition); 119 if (dwError != ERROR_SUCCESS) 120 { 121 DPRINT1("Subkey creation failed (Error %lu)\n", dwError); 122 HeapFree(GetProcessHeap(), 123 0, 124 lpNameBuffer); 125 return dwError; 126 } 127 128 dwError = RegOpenKeyExW(hSrcKey, 129 lpNameBuffer, 130 0, 131 KEY_READ, 132 &hSrcSubKey); 133 if (dwError != ERROR_SUCCESS) 134 { 135 DPRINT1("Error: %lu\n", dwError); 136 RegCloseKey(hDstSubKey); 137 HeapFree(GetProcessHeap(), 138 0, 139 lpNameBuffer); 140 return dwError; 141 } 142 143 dwError = ScmCopyTree(hSrcSubKey, 144 hDstSubKey); 145 if (dwError != ERROR_SUCCESS) 146 { 147 DPRINT1("Error: %lu\n", dwError); 148 RegCloseKey (hSrcSubKey); 149 RegCloseKey (hDstSubKey); 150 HeapFree(GetProcessHeap(), 151 0, 152 lpNameBuffer); 153 return dwError; 154 } 155 156 RegCloseKey(hSrcSubKey); 157 RegCloseKey(hDstSubKey); 158 } 159 160 HeapFree(GetProcessHeap(), 161 0, 162 lpNameBuffer); 163 } 164 165 /* Copy values */ 166 if (dwValues != 0) 167 { 168 lpNameBuffer = HeapAlloc(GetProcessHeap(), 169 0, 170 dwMaxValueNameLength * sizeof(WCHAR)); 171 if (lpNameBuffer == NULL) 172 { 173 DPRINT1("Buffer allocation failed\n"); 174 return ERROR_NOT_ENOUGH_MEMORY; 175 } 176 177 /* RegSetValueExW tries to read behind the maximum length, so give it space for that */ 178 lpDataBuffer = HeapAlloc(GetProcessHeap(), 179 HEAP_ZERO_MEMORY, 180 dwMaxValueLength + sizeof(WCHAR)); 181 if (lpDataBuffer == NULL) 182 { 183 DPRINT1("Buffer allocation failed\n"); 184 HeapFree(GetProcessHeap(), 185 0, 186 lpNameBuffer); 187 return ERROR_NOT_ENOUGH_MEMORY; 188 } 189 190 for (i = 0; i < dwValues; i++) 191 { 192 dwValueNameLength = dwMaxValueNameLength; 193 dwValueLength = dwMaxValueLength; 194 dwError = RegEnumValueW(hSrcKey, 195 i, 196 lpNameBuffer, 197 &dwValueNameLength, 198 NULL, 199 &dwType, 200 lpDataBuffer, 201 &dwValueLength); 202 if (dwError != ERROR_SUCCESS) 203 { 204 DPRINT1("Error: %lu\n", dwError); 205 HeapFree(GetProcessHeap(), 206 0, 207 lpDataBuffer); 208 HeapFree(GetProcessHeap(), 209 0, 210 lpNameBuffer); 211 return dwError; 212 } 213 214 dwError = RegSetValueExW(hDstKey, 215 lpNameBuffer, 216 0, 217 dwType, 218 lpDataBuffer, 219 dwValueLength); 220 if (dwError != ERROR_SUCCESS) 221 { 222 DPRINT1("Error: %lu\n", dwError); 223 HeapFree(GetProcessHeap(), 224 0, 225 lpDataBuffer); 226 HeapFree(GetProcessHeap(), 227 0, 228 lpNameBuffer); 229 return dwError; 230 } 231 } 232 233 HeapFree(GetProcessHeap(), 234 0, 235 lpDataBuffer); 236 237 HeapFree(GetProcessHeap(), 238 0, 239 lpNameBuffer); 240 } 241 242 DPRINT("ScmCopyTree() done\n"); 243 244 return ERROR_SUCCESS; 245 } 246 247 248 DWORD 249 ScmDeleteTree( 250 HKEY hKey, 251 PCWSTR pszSubKey) 252 { 253 DWORD dwMaxSubkeyLength, dwMaxValueLength; 254 DWORD dwMaxLength, dwSize; 255 PWSTR pszName = NULL; 256 HKEY hSubKey = NULL; 257 DWORD dwError; 258 259 if (pszSubKey != NULL) 260 { 261 dwError = RegOpenKeyExW(hKey, pszSubKey, 0, KEY_READ, &hSubKey); 262 if (dwError != ERROR_SUCCESS) 263 return dwError; 264 } 265 else 266 { 267 hSubKey = hKey; 268 } 269 270 /* Get highest length for keys, values */ 271 dwError = RegQueryInfoKeyW(hSubKey, 272 NULL, 273 NULL, 274 NULL, 275 NULL, 276 &dwMaxSubkeyLength, 277 NULL, 278 NULL, 279 &dwMaxValueLength, 280 NULL, 281 NULL, 282 NULL); 283 if (dwError != ERROR_SUCCESS) 284 goto done; 285 286 dwMaxSubkeyLength++; 287 dwMaxValueLength++; 288 dwMaxLength = max(dwMaxSubkeyLength, dwMaxValueLength); 289 290 /* Allocate a buffer for key and value names */ 291 pszName = HeapAlloc(GetProcessHeap(), 292 0, 293 dwMaxLength * sizeof(WCHAR)); 294 if (pszName == NULL) 295 { 296 dwError = ERROR_NOT_ENOUGH_MEMORY; 297 goto done; 298 } 299 300 /* Recursively delete all the subkeys */ 301 while (TRUE) 302 { 303 dwSize = dwMaxLength; 304 if (RegEnumKeyExW(hSubKey, 305 0, 306 pszName, 307 &dwSize, 308 NULL, 309 NULL, 310 NULL, 311 NULL)) 312 break; 313 314 dwError = ScmDeleteTree(hSubKey, pszName); 315 if (dwError != ERROR_SUCCESS) 316 goto done; 317 } 318 319 if (pszSubKey != NULL) 320 { 321 dwError = RegDeleteKeyW(hKey, pszSubKey); 322 } 323 else 324 { 325 while (TRUE) 326 { 327 dwSize = dwMaxLength; 328 if (RegEnumValueW(hKey, 329 0, 330 pszName, 331 &dwSize, 332 NULL, 333 NULL, 334 NULL, 335 NULL)) 336 break; 337 338 dwError = RegDeleteValueW(hKey, pszName); 339 if (dwError != ERROR_SUCCESS) 340 goto done; 341 } 342 } 343 344 done: 345 if (pszName != NULL) 346 HeapFree(GetProcessHeap(), 0, pszName); 347 348 if (pszSubKey != NULL) 349 RegCloseKey(hSubKey); 350 351 return dwError; 352 } 353 #endif 354 355 356 static 357 DWORD 358 ScmGetControlSetValues( 359 PDWORD pdwCurrentControlSet, 360 PDWORD pdwDefaultControlSet, 361 PDWORD pdwFailedControlSet, 362 PDWORD pdwLastKnownGoodControlSet) 363 { 364 HKEY hSelectKey; 365 DWORD dwType; 366 DWORD dwSize; 367 DWORD dwError; 368 369 DPRINT("ScmGetControlSetValues() called\n"); 370 371 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 372 L"System\\Select", 373 0, 374 KEY_READ, 375 &hSelectKey); 376 if (dwError != ERROR_SUCCESS) 377 return dwError; 378 379 dwSize = sizeof(DWORD); 380 dwError = RegQueryValueExW(hSelectKey, 381 L"Current", 382 0, 383 &dwType, 384 (LPBYTE)pdwCurrentControlSet, 385 &dwSize); 386 if (dwError != ERROR_SUCCESS) 387 { 388 *pdwCurrentControlSet = 0; 389 } 390 391 dwSize = sizeof(DWORD); 392 dwError = RegQueryValueExW(hSelectKey, 393 L"Default", 394 0, 395 &dwType, 396 (LPBYTE)pdwDefaultControlSet, 397 &dwSize); 398 if (dwError != ERROR_SUCCESS) 399 { 400 *pdwDefaultControlSet = 0; 401 } 402 403 dwSize = sizeof(DWORD); 404 dwError = RegQueryValueExW(hSelectKey, 405 L"Failed", 406 0, 407 &dwType, 408 (LPBYTE)pdwFailedControlSet, 409 &dwSize); 410 if (dwError != ERROR_SUCCESS) 411 { 412 *pdwFailedControlSet = 0; 413 } 414 415 dwSize = sizeof(DWORD); 416 dwError = RegQueryValueExW(hSelectKey, 417 L"LastKnownGood", 418 0, 419 &dwType, 420 (LPBYTE)pdwLastKnownGoodControlSet, 421 &dwSize); 422 if (dwError != ERROR_SUCCESS) 423 { 424 *pdwLastKnownGoodControlSet = 0; 425 } 426 427 RegCloseKey(hSelectKey); 428 429 DPRINT("ControlSets:\n"); 430 DPRINT("Current: %lu\n", *pdwCurrentControlSet); 431 DPRINT("Default: %lu\n", *pdwDefaultControlSet); 432 DPRINT("Failed: %lu\n", *pdwFailedControlSet); 433 DPRINT("LastKnownGood: %lu\n", *pdwLastKnownGoodControlSet); 434 435 return dwError; 436 } 437 438 439 static 440 DWORD 441 ScmSetLastKnownGoodControlSet( 442 DWORD dwControlSet) 443 { 444 HKEY hSelectKey; 445 DWORD dwError; 446 447 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 448 L"System\\Select", 449 0, 450 KEY_WRITE, 451 &hSelectKey); 452 if (dwError != ERROR_SUCCESS) 453 return dwError; 454 455 dwError = RegSetValueExW(hSelectKey, 456 L"LastKnownGood", 457 0, 458 REG_DWORD, 459 (LPBYTE)&dwControlSet, 460 sizeof(dwControlSet)); 461 462 RegFlushKey(hSelectKey); 463 RegCloseKey(hSelectKey); 464 465 return dwError; 466 } 467 468 469 static 470 DWORD 471 ScmGetSetupInProgress(VOID) 472 { 473 DWORD dwError; 474 HKEY hKey; 475 DWORD dwType; 476 DWORD dwSize; 477 DWORD dwSetupInProgress = (DWORD)-1; 478 479 DPRINT("ScmGetSetupInProgress()\n"); 480 481 /* Open key */ 482 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 483 L"SYSTEM\\Setup", 484 0, 485 KEY_QUERY_VALUE, 486 &hKey); 487 if (dwError == ERROR_SUCCESS) 488 { 489 /* Read key */ 490 dwSize = sizeof(DWORD); 491 RegQueryValueExW(hKey, 492 L"SystemSetupInProgress", 493 NULL, 494 &dwType, 495 (LPBYTE)&dwSetupInProgress, 496 &dwSize); 497 RegCloseKey(hKey); 498 } 499 500 DPRINT("SetupInProgress: %lu\n", dwSetupInProgress); 501 return dwSetupInProgress; 502 } 503 504 505 static 506 DWORD 507 ScmCopyControlSet( 508 DWORD dwSourceControlSet, 509 DWORD dwDestinationControlSet) 510 { 511 WCHAR szSourceControlSetName[32]; 512 WCHAR szDestinationControlSetName[32]; 513 HKEY hSourceControlSetKey = NULL; 514 HKEY hDestinationControlSetKey = NULL; 515 DWORD dwDisposition; 516 DWORD dwError; 517 518 /* Create the source control set name */ 519 swprintf(szSourceControlSetName, L"SYSTEM\\ControlSet%03lu", dwSourceControlSet); 520 DPRINT("Source control set: %S\n", szSourceControlSetName); 521 522 /* Create the destination control set name */ 523 swprintf(szDestinationControlSetName, L"SYSTEM\\ControlSet%03lu", dwDestinationControlSet); 524 DPRINT("Destination control set: %S\n", szDestinationControlSetName); 525 526 /* Open the source control set key */ 527 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 528 szSourceControlSetName, 529 0, 530 KEY_READ, 531 &hSourceControlSetKey); 532 if (dwError != ERROR_SUCCESS) 533 goto done; 534 535 /* Create the destination control set key */ 536 dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE, 537 szDestinationControlSetName, 538 0, 539 NULL, 540 REG_OPTION_NON_VOLATILE, 541 KEY_WRITE, 542 NULL, 543 &hDestinationControlSetKey, 544 &dwDisposition); 545 if (dwError != ERROR_SUCCESS) 546 goto done; 547 548 /* Copy the source control set to the destination control set */ 549 #if (_WIN32_WINNT >= 0x0600) 550 dwError = RegCopyTreeW(hSourceControlSetKey, 551 NULL, 552 hDestinationControlSetKey); 553 #else 554 dwError = ScmCopyTree(hSourceControlSetKey, 555 hDestinationControlSetKey); 556 #endif 557 if (dwError != ERROR_SUCCESS) 558 goto done; 559 560 RegFlushKey(hDestinationControlSetKey); 561 562 done: 563 if (hDestinationControlSetKey != NULL) 564 RegCloseKey(hDestinationControlSetKey); 565 566 if (hSourceControlSetKey != NULL) 567 RegCloseKey(hSourceControlSetKey); 568 569 return dwError; 570 } 571 572 573 static 574 DWORD 575 ScmDeleteControlSet( 576 DWORD dwControlSet) 577 { 578 WCHAR szControlSetName[32]; 579 HKEY hControlSetKey = NULL; 580 DWORD dwError; 581 582 DPRINT("ScmDeleteControSet(%lu)\n", dwControlSet); 583 584 /* Create the control set name */ 585 swprintf(szControlSetName, L"SYSTEM\\ControlSet%03lu", dwControlSet); 586 DPRINT("Control set: %S\n", szControlSetName); 587 588 /* Open the system key */ 589 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 590 szControlSetName, 591 0, 592 DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_SET_VALUE, 593 &hControlSetKey); 594 if (dwError != ERROR_SUCCESS) 595 return dwError; 596 597 /* Delete the control set */ 598 #if (_WIN32_WINNT >= 0x0600) 599 dwError = RegDeleteTreeW(hControlSetKey, 600 NULL); 601 #else 602 dwError = ScmDeleteTree(hControlSetKey, 603 NULL); 604 #endif 605 606 /* Open the system key */ 607 RegCloseKey(hControlSetKey); 608 609 return dwError; 610 } 611 612 613 DWORD 614 ScmCreateLastKnownGoodControlSet(VOID) 615 { 616 DWORD dwCurrentControlSet, dwDefaultControlSet; 617 DWORD dwFailedControlSet, dwLastKnownGoodControlSet; 618 DWORD dwNewControlSet; 619 DWORD dwError; 620 621 /* Get the control set values */ 622 dwError = ScmGetControlSetValues(&dwCurrentControlSet, 623 &dwDefaultControlSet, 624 &dwFailedControlSet, 625 &dwLastKnownGoodControlSet); 626 if (dwError != ERROR_SUCCESS) 627 return dwError; 628 629 /* First boot after setup? */ 630 if ((ScmGetSetupInProgress() == 0) && 631 (dwCurrentControlSet == dwLastKnownGoodControlSet)) 632 { 633 DPRINT("First boot after setup!\n"); 634 635 /* Search for a new control set number */ 636 for (dwNewControlSet = 1; dwNewControlSet < 1000; dwNewControlSet++) 637 { 638 if ((dwNewControlSet != dwCurrentControlSet) && 639 (dwNewControlSet != dwDefaultControlSet) && 640 (dwNewControlSet != dwFailedControlSet) && 641 (dwNewControlSet != dwLastKnownGoodControlSet)) 642 break; 643 } 644 645 /* Fail if we did not find an unused control set!*/ 646 if (dwNewControlSet >= 1000) 647 { 648 DPRINT1("Too many control sets!\n"); 649 return ERROR_NO_MORE_ITEMS; 650 } 651 652 /* Copy the current control set */ 653 dwError = ScmCopyControlSet(dwCurrentControlSet, 654 dwNewControlSet); 655 if (dwError != ERROR_SUCCESS) 656 return dwError; 657 658 /* Set the new 'LastKnownGood' control set */ 659 dwError = ScmSetLastKnownGoodControlSet(dwNewControlSet); 660 if (dwError == ERROR_SUCCESS) 661 { 662 /* 663 * Accept the boot here in order to prevent the creation of 664 * another control set when a user is going to get logged on 665 */ 666 bBootAccepted = TRUE; 667 } 668 } 669 670 return dwError; 671 } 672 673 674 DWORD 675 ScmAcceptBoot(VOID) 676 { 677 DWORD dwCurrentControlSet, dwDefaultControlSet; 678 DWORD dwFailedControlSet, dwLastKnownGoodControlSet; 679 DWORD dwNewControlSet; 680 DWORD dwError; 681 682 DPRINT("ScmAcceptBoot()\n"); 683 684 if (bBootAccepted) 685 { 686 DPRINT1("Boot has alread been accepted!\n"); 687 return ERROR_BOOT_ALREADY_ACCEPTED; 688 } 689 690 /* Get the control set values */ 691 dwError = ScmGetControlSetValues(&dwCurrentControlSet, 692 &dwDefaultControlSet, 693 &dwFailedControlSet, 694 &dwLastKnownGoodControlSet); 695 if (dwError != ERROR_SUCCESS) 696 return dwError; 697 698 /* Search for a new control set number */ 699 for (dwNewControlSet = 1; dwNewControlSet < 1000; dwNewControlSet++) 700 { 701 if ((dwNewControlSet != dwCurrentControlSet) && 702 (dwNewControlSet != dwDefaultControlSet) && 703 (dwNewControlSet != dwFailedControlSet) && 704 (dwNewControlSet != dwLastKnownGoodControlSet)) 705 break; 706 } 707 708 /* Fail if we did not find an unused control set!*/ 709 if (dwNewControlSet >= 1000) 710 { 711 DPRINT1("Too many control sets!\n"); 712 return ERROR_NO_MORE_ITEMS; 713 } 714 715 /* Copy the current control set */ 716 dwError = ScmCopyControlSet(dwCurrentControlSet, 717 dwNewControlSet); 718 if (dwError != ERROR_SUCCESS) 719 return dwError; 720 721 /* Delete the current last known good contol set, if it is not used anywhere else */ 722 if ((dwLastKnownGoodControlSet != dwCurrentControlSet) && 723 (dwLastKnownGoodControlSet != dwDefaultControlSet) && 724 (dwLastKnownGoodControlSet != dwFailedControlSet)) 725 { 726 ScmDeleteControlSet(dwLastKnownGoodControlSet); 727 } 728 729 /* Set the new 'LastKnownGood' control set */ 730 dwError = ScmSetLastKnownGoodControlSet(dwNewControlSet); 731 if (dwError != ERROR_SUCCESS) 732 return dwError; 733 734 bBootAccepted = TRUE; 735 736 return ERROR_SUCCESS; 737 } 738 739 740 DWORD 741 ScmRunLastKnownGood(VOID) 742 { 743 DPRINT("ScmRunLastKnownGood()\n"); 744 745 if (bBootAccepted) 746 { 747 DPRINT1("Boot has alread been accepted!\n"); 748 return ERROR_BOOT_ALREADY_ACCEPTED; 749 } 750 751 /* FIXME */ 752 753 return ERROR_SUCCESS; 754 } 755 756 /* EOF */ 757