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 lpDataBuffer = HeapAlloc(GetProcessHeap(), 178 0, 179 dwMaxValueLength); 180 if (lpDataBuffer == NULL) 181 { 182 DPRINT1("Buffer allocation failed\n"); 183 HeapFree(GetProcessHeap(), 184 0, 185 lpNameBuffer); 186 return ERROR_NOT_ENOUGH_MEMORY; 187 } 188 189 for (i = 0; i < dwValues; i++) 190 { 191 dwValueNameLength = dwMaxValueNameLength; 192 dwValueLength = dwMaxValueLength; 193 dwError = RegEnumValueW(hSrcKey, 194 i, 195 lpNameBuffer, 196 &dwValueNameLength, 197 NULL, 198 &dwType, 199 lpDataBuffer, 200 &dwValueLength); 201 if (dwError != ERROR_SUCCESS) 202 { 203 DPRINT1("Error: %lu\n", dwError); 204 HeapFree(GetProcessHeap(), 205 0, 206 lpDataBuffer); 207 HeapFree(GetProcessHeap(), 208 0, 209 lpNameBuffer); 210 return dwError; 211 } 212 213 dwError = RegSetValueExW(hDstKey, 214 lpNameBuffer, 215 0, 216 dwType, 217 lpDataBuffer, 218 dwValueLength); 219 if (dwError != ERROR_SUCCESS) 220 { 221 DPRINT1("Error: %lu\n", dwError); 222 HeapFree(GetProcessHeap(), 223 0, 224 lpDataBuffer); 225 HeapFree(GetProcessHeap(), 226 0, 227 lpNameBuffer); 228 return dwError; 229 } 230 } 231 232 HeapFree(GetProcessHeap(), 233 0, 234 lpDataBuffer); 235 236 HeapFree(GetProcessHeap(), 237 0, 238 lpNameBuffer); 239 } 240 241 DPRINT("ScmCopyTree() done \n"); 242 243 return ERROR_SUCCESS; 244 } 245 246 247 DWORD 248 ScmDeleteTree( 249 HKEY hKey, 250 PCWSTR pszSubKey) 251 { 252 DWORD dwMaxSubkeyLength, dwMaxValueLength; 253 DWORD dwMaxLength, dwSize; 254 PWSTR pszName = NULL; 255 HKEY hSubKey = NULL; 256 DWORD dwError; 257 258 if (pszSubKey != NULL) 259 { 260 dwError = RegOpenKeyExW(hKey, pszSubKey, 0, KEY_READ, &hSubKey); 261 if (dwError != ERROR_SUCCESS) 262 return dwError; 263 } 264 else 265 { 266 hSubKey = hKey; 267 } 268 269 /* Get highest length for keys, values */ 270 dwError = RegQueryInfoKeyW(hSubKey, 271 NULL, 272 NULL, 273 NULL, 274 NULL, 275 &dwMaxSubkeyLength, 276 NULL, 277 NULL, 278 &dwMaxValueLength, 279 NULL, 280 NULL, 281 NULL); 282 if (dwError != ERROR_SUCCESS) 283 goto done; 284 285 dwMaxSubkeyLength++; 286 dwMaxValueLength++; 287 dwMaxLength = max(dwMaxSubkeyLength, dwMaxValueLength); 288 289 /* Allocate a buffer for key and value names */ 290 pszName = HeapAlloc(GetProcessHeap(), 291 0, 292 dwMaxLength * sizeof(WCHAR)); 293 if (pszName == NULL) 294 { 295 dwError = ERROR_NOT_ENOUGH_MEMORY; 296 goto done; 297 } 298 299 /* Recursively delete all the subkeys */ 300 while (TRUE) 301 { 302 dwSize = dwMaxLength; 303 if (RegEnumKeyExW(hSubKey, 304 0, 305 pszName, 306 &dwSize, 307 NULL, 308 NULL, 309 NULL, 310 NULL)) 311 break; 312 313 dwError = ScmDeleteTree(hSubKey, pszName); 314 if (dwError != ERROR_SUCCESS) 315 goto done; 316 } 317 318 if (pszSubKey != NULL) 319 { 320 dwError = RegDeleteKeyW(hKey, pszSubKey); 321 } 322 else 323 { 324 while (TRUE) 325 { 326 dwSize = dwMaxLength; 327 if (RegEnumValueW(hKey, 328 0, 329 pszName, 330 &dwSize, 331 NULL, 332 NULL, 333 NULL, 334 NULL)) 335 break; 336 337 dwError = RegDeleteValueW(hKey, pszName); 338 if (dwError != ERROR_SUCCESS) 339 goto done; 340 } 341 } 342 343 done: 344 if (pszName != NULL) 345 HeapFree(GetProcessHeap(), 0, pszName); 346 347 if (pszSubKey != NULL) 348 RegCloseKey(hSubKey); 349 350 return dwError; 351 } 352 #endif 353 354 355 static 356 DWORD 357 ScmGetControlSetValues( 358 PDWORD pdwCurrentControlSet, 359 PDWORD pdwDefaultControlSet, 360 PDWORD pdwFailedControlSet, 361 PDWORD pdwLastKnownGoodControlSet) 362 { 363 HKEY hSelectKey; 364 DWORD dwType; 365 DWORD dwSize; 366 DWORD dwError; 367 368 DPRINT("ScmGetControlSetValues() called\n"); 369 370 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 371 L"System\\Select", 372 0, 373 KEY_READ, 374 &hSelectKey); 375 if (dwError != ERROR_SUCCESS) 376 return dwError; 377 378 dwSize = sizeof(DWORD); 379 dwError = RegQueryValueExW(hSelectKey, 380 L"Current", 381 0, 382 &dwType, 383 (LPBYTE)pdwCurrentControlSet, 384 &dwSize); 385 if (dwError != ERROR_SUCCESS) 386 { 387 *pdwCurrentControlSet = 0; 388 } 389 390 dwSize = sizeof(DWORD); 391 dwError = RegQueryValueExW(hSelectKey, 392 L"Default", 393 0, 394 &dwType, 395 (LPBYTE)pdwDefaultControlSet, 396 &dwSize); 397 if (dwError != ERROR_SUCCESS) 398 { 399 *pdwDefaultControlSet = 0; 400 } 401 402 dwSize = sizeof(DWORD); 403 dwError = RegQueryValueExW(hSelectKey, 404 L"Failed", 405 0, 406 &dwType, 407 (LPBYTE)pdwFailedControlSet, 408 &dwSize); 409 if (dwError != ERROR_SUCCESS) 410 { 411 *pdwFailedControlSet = 0; 412 } 413 414 dwSize = sizeof(DWORD); 415 dwError = RegQueryValueExW(hSelectKey, 416 L"LastKnownGood", 417 0, 418 &dwType, 419 (LPBYTE)pdwLastKnownGoodControlSet, 420 &dwSize); 421 if (dwError != ERROR_SUCCESS) 422 { 423 *pdwLastKnownGoodControlSet = 0; 424 } 425 426 RegCloseKey(hSelectKey); 427 428 DPRINT("ControlSets:\n"); 429 DPRINT("Current: %lu\n", *pdwCurrentControlSet); 430 DPRINT("Default: %lu\n", *pdwDefaultControlSet); 431 DPRINT("Failed: %lu\n", *pdwFailedControlSet); 432 DPRINT("LastKnownGood: %lu\n", *pdwLastKnownGoodControlSet); 433 434 return dwError; 435 } 436 437 438 static 439 DWORD 440 ScmSetLastKnownGoodControlSet( 441 DWORD dwControlSet) 442 { 443 HKEY hSelectKey; 444 DWORD dwError; 445 446 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 447 L"System\\Select", 448 0, 449 KEY_WRITE, 450 &hSelectKey); 451 if (dwError != ERROR_SUCCESS) 452 return dwError; 453 454 dwError = RegSetValueExW(hSelectKey, 455 L"LastKnownGood", 456 0, 457 REG_DWORD, 458 (LPBYTE)&dwControlSet, 459 sizeof(dwControlSet)); 460 461 RegFlushKey(hSelectKey); 462 RegCloseKey(hSelectKey); 463 464 return dwError; 465 } 466 467 468 static 469 DWORD 470 ScmGetSetupInProgress(VOID) 471 { 472 DWORD dwError; 473 HKEY hKey; 474 DWORD dwType; 475 DWORD dwSize; 476 DWORD dwSetupInProgress = (DWORD)-1; 477 478 DPRINT("ScmGetSetupInProgress()\n"); 479 480 /* Open key */ 481 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 482 L"SYSTEM\\Setup", 483 0, 484 KEY_QUERY_VALUE, 485 &hKey); 486 if (dwError == ERROR_SUCCESS) 487 { 488 /* Read key */ 489 dwSize = sizeof(DWORD); 490 RegQueryValueExW(hKey, 491 L"SystemSetupInProgress", 492 NULL, 493 &dwType, 494 (LPBYTE)&dwSetupInProgress, 495 &dwSize); 496 RegCloseKey(hKey); 497 } 498 499 DPRINT("SetupInProgress: %lu\n", dwSetupInProgress); 500 return dwSetupInProgress; 501 } 502 503 504 static 505 DWORD 506 ScmCopyControlSet( 507 DWORD dwSourceControlSet, 508 DWORD dwDestinationControlSet) 509 { 510 WCHAR szSourceControlSetName[32]; 511 WCHAR szDestinationControlSetName[32]; 512 HKEY hSourceControlSetKey = NULL; 513 HKEY hDestinationControlSetKey = NULL; 514 DWORD dwDisposition; 515 DWORD dwError; 516 517 /* Create the source control set name */ 518 swprintf(szSourceControlSetName, L"SYSTEM\\ControlSet%03lu", dwSourceControlSet); 519 DPRINT("Source control set: %S\n", szSourceControlSetName); 520 521 /* Create the destination control set name */ 522 swprintf(szDestinationControlSetName, L"SYSTEM\\ControlSet%03lu", dwDestinationControlSet); 523 DPRINT("Destination control set: %S\n", szDestinationControlSetName); 524 525 /* Open the source control set key */ 526 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 527 szSourceControlSetName, 528 0, 529 KEY_READ, 530 &hSourceControlSetKey); 531 if (dwError != ERROR_SUCCESS) 532 goto done; 533 534 /* Create the destination control set key */ 535 dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE, 536 szDestinationControlSetName, 537 0, 538 NULL, 539 REG_OPTION_NON_VOLATILE, 540 KEY_WRITE, 541 NULL, 542 &hDestinationControlSetKey, 543 &dwDisposition); 544 if (dwError != ERROR_SUCCESS) 545 goto done; 546 547 /* Copy the source control set to the destination control set */ 548 #if (_WIN32_WINNT >= 0x0600) 549 dwError = RegCopyTreeW(hSourceControlSetKey, 550 NULL, 551 hDestinationControlSetKey); 552 #else 553 dwError = ScmCopyTree(hSourceControlSetKey, 554 hDestinationControlSetKey); 555 #endif 556 if (dwError != ERROR_SUCCESS) 557 goto done; 558 559 RegFlushKey(hDestinationControlSetKey); 560 561 done: 562 if (hDestinationControlSetKey != NULL) 563 RegCloseKey(hDestinationControlSetKey); 564 565 if (hSourceControlSetKey != NULL) 566 RegCloseKey(hSourceControlSetKey); 567 568 return dwError; 569 } 570 571 572 static 573 DWORD 574 ScmDeleteControlSet( 575 DWORD dwControlSet) 576 { 577 WCHAR szControlSetName[32]; 578 HKEY hControlSetKey = NULL; 579 DWORD dwError; 580 581 DPRINT("ScmDeleteControSet(%lu)\n", dwControlSet); 582 583 /* Create the control set name */ 584 swprintf(szControlSetName, L"SYSTEM\\ControlSet%03lu", dwControlSet); 585 DPRINT("Control set: %S\n", szControlSetName); 586 587 /* Open the system key */ 588 dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 589 szControlSetName, 590 0, 591 DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_SET_VALUE, 592 &hControlSetKey); 593 if (dwError != ERROR_SUCCESS) 594 return dwError; 595 596 /* Delete the control set */ 597 #if (_WIN32_WINNT >= 0x0600) 598 dwError = RegDeleteTreeW(hControlSetKey, 599 NULL); 600 #else 601 dwError = ScmDeleteTree(hControlSetKey, 602 NULL); 603 #endif 604 605 /* Open the system key */ 606 RegCloseKey(hControlSetKey); 607 608 return dwError; 609 } 610 611 612 DWORD 613 ScmCreateLastKnownGoodControlSet(VOID) 614 { 615 DWORD dwCurrentControlSet, dwDefaultControlSet; 616 DWORD dwFailedControlSet, dwLastKnownGoodControlSet; 617 DWORD dwNewControlSet; 618 DWORD dwError; 619 620 /* Get the control set values */ 621 dwError = ScmGetControlSetValues(&dwCurrentControlSet, 622 &dwDefaultControlSet, 623 &dwFailedControlSet, 624 &dwLastKnownGoodControlSet); 625 if (dwError != ERROR_SUCCESS) 626 return dwError; 627 628 /* First boot after setup? */ 629 if ((ScmGetSetupInProgress() == 0) && 630 (dwCurrentControlSet == dwLastKnownGoodControlSet)) 631 { 632 DPRINT("First boot after setup!\n"); 633 634 /* Search for a new control set number */ 635 for (dwNewControlSet = 1; dwNewControlSet < 1000; dwNewControlSet++) 636 { 637 if ((dwNewControlSet != dwCurrentControlSet) && 638 (dwNewControlSet != dwDefaultControlSet) && 639 (dwNewControlSet != dwFailedControlSet) && 640 (dwNewControlSet != dwLastKnownGoodControlSet)) 641 break; 642 } 643 644 /* Fail if we did not find an unused control set!*/ 645 if (dwNewControlSet >= 1000) 646 { 647 DPRINT1("Too many control sets!\n"); 648 return ERROR_NO_MORE_ITEMS; 649 } 650 651 /* Copy the current control set */ 652 dwError = ScmCopyControlSet(dwCurrentControlSet, 653 dwNewControlSet); 654 if (dwError != ERROR_SUCCESS) 655 return dwError; 656 657 /* Set the new 'LastKnownGood' control set */ 658 dwError = ScmSetLastKnownGoodControlSet(dwNewControlSet); 659 if (dwError == ERROR_SUCCESS) 660 { 661 /* 662 * Accept the boot here in order to prevent the creation of 663 * another control set when a user is going to get logged on 664 */ 665 bBootAccepted = TRUE; 666 } 667 } 668 669 return dwError; 670 } 671 672 673 DWORD 674 ScmAcceptBoot(VOID) 675 { 676 DWORD dwCurrentControlSet, dwDefaultControlSet; 677 DWORD dwFailedControlSet, dwLastKnownGoodControlSet; 678 DWORD dwNewControlSet; 679 DWORD dwError; 680 681 DPRINT("ScmAcceptBoot()\n"); 682 683 if (bBootAccepted) 684 { 685 DPRINT1("Boot has alread been accepted!\n"); 686 return ERROR_BOOT_ALREADY_ACCEPTED; 687 } 688 689 /* Get the control set values */ 690 dwError = ScmGetControlSetValues(&dwCurrentControlSet, 691 &dwDefaultControlSet, 692 &dwFailedControlSet, 693 &dwLastKnownGoodControlSet); 694 if (dwError != ERROR_SUCCESS) 695 return dwError; 696 697 /* Search for a new control set number */ 698 for (dwNewControlSet = 1; dwNewControlSet < 1000; dwNewControlSet++) 699 { 700 if ((dwNewControlSet != dwCurrentControlSet) && 701 (dwNewControlSet != dwDefaultControlSet) && 702 (dwNewControlSet != dwFailedControlSet) && 703 (dwNewControlSet != dwLastKnownGoodControlSet)) 704 break; 705 } 706 707 /* Fail if we did not find an unused control set!*/ 708 if (dwNewControlSet >= 1000) 709 { 710 DPRINT1("Too many control sets!\n"); 711 return ERROR_NO_MORE_ITEMS; 712 } 713 714 /* Copy the current control set */ 715 dwError = ScmCopyControlSet(dwCurrentControlSet, 716 dwNewControlSet); 717 if (dwError != ERROR_SUCCESS) 718 return dwError; 719 720 /* Delete the current last known good contol set, if it is not used anywhere else */ 721 if ((dwLastKnownGoodControlSet != dwCurrentControlSet) && 722 (dwLastKnownGoodControlSet != dwDefaultControlSet) && 723 (dwLastKnownGoodControlSet != dwFailedControlSet)) 724 { 725 ScmDeleteControlSet(dwLastKnownGoodControlSet); 726 } 727 728 /* Set the new 'LastKnownGood' control set */ 729 dwError = ScmSetLastKnownGoodControlSet(dwNewControlSet); 730 if (dwError != ERROR_SUCCESS) 731 return dwError; 732 733 bBootAccepted = TRUE; 734 735 return ERROR_SUCCESS; 736 } 737 738 739 DWORD 740 ScmRunLastKnownGood(VOID) 741 { 742 DPRINT("ScmRunLastKnownGood()\n"); 743 744 if (bBootAccepted) 745 { 746 DPRINT1("Boot has alread been accepted!\n"); 747 return ERROR_BOOT_ALREADY_ACCEPTED; 748 } 749 750 /* FIXME */ 751 752 return ERROR_SUCCESS; 753 } 754 755 /* EOF */ 756