/*++ Copyright (c) 1991-1994 Microsoft Corporation Module Name: ftreg.c Abstract: This module contains the routines for Disk Administrator that deal with registry manipulation Author: Edward (Ted) Miller (TedM) 11/15/91 Environment: User process. Notes: Revision History: 1-Feb-94 (bobri) Clean up and handle missing floppy disk on registry save/restore. --*/ #include #include #include #include #include "fdisk.h" #include "ftregres.h" // attempt to avoid conflict #define TEMP_KEY_NAME TEXT("xzss3___$$Temp$Hive$$___") #define DISK_KEY_NAME TEXT("DISK") #define DISK_VALUE_NAME TEXT("Information") LONG FdpLoadHiveIntoRegistry( IN LPTSTR HiveFilename ) /*++ Routine Description: This routine writes the contents of a given hive file into the registry, rooted at a temporary key in HKEY_LOCAL_MACHINE. Arguments: HiveFilename - supplies filename of the hive to be loaded into the registry Return Value: Windows error code. --*/ { NTSTATUS Status; BOOLEAN OldPrivState; LONG Err; // Attempt to get restore privilege Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &OldPrivState); if (!NT_SUCCESS(Status)) { return RtlNtStatusToDosError(Status); } // Load the hive into our registry Err = RegLoadKey(HKEY_LOCAL_MACHINE,TEMP_KEY_NAME,HiveFilename); // Restore old privilege if necessary if (!OldPrivState) { RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, FALSE, FALSE, &OldPrivState); } return Err; } LONG FdpUnloadHiveFromRegistry( VOID ) /*++ Routine Description: This routine removes a tree (previously loaded with FdpLoadHiveIntoRegistry) from the temporary key in HKEY_LOCAL_MACHINE. Arguments: None. Return Value: Windows error code. --*/ { NTSTATUS Status; BOOLEAN OldPrivState; LONG Err; // Attempt to get restore privilege Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &OldPrivState); if (!NT_SUCCESS(Status)) { return RtlNtStatusToDosError(Status); } // Unload the hive from our registry Err = RegUnLoadKey(HKEY_LOCAL_MACHINE,TEMP_KEY_NAME); // Restore old privilege if necessary if (!OldPrivState) { RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, FALSE, FALSE, &OldPrivState); } return Err; } LONG FdpGetDiskInfoFromKey( IN LPTSTR RootKeyName, OUT PVOID *DiskInfo, OUT PULONG DiskInfoSize ) /*++ Routine Description: This routine pulls the binary blob containing disk ft, drive letter, and layout information out of a given registry key. The info is found in HKEY_LOCAL_MACHINE,\DISK:Information. Arguments: RootKeyName - name of the subkey of HKEY_LOCAL_MACHINE that is to contain the DISK key. DiskInfo - receives a pointer to a buffer containing the disk info. DiskInfoSize - receives size of the disk buffer. Return Value: Windows error code. If NO_ERROR, DiskInfo and DiskInfoSize are filled in, and it is the caller's responsibility to free the buffer when it is finished (via LocalFree()). --*/ { LONG Err; HKEY hkeyDisk; ULONG BufferSize; ULONG ValueType; PVOID Buffer; LPTSTR DiskKeyName; // Form the name of the DISK key DiskKeyName = (LPTSTR)LocalAlloc( LMEM_FIXED, ( lstrlen(RootKeyName) + lstrlen(DISK_KEY_NAME) + 2 // the \ and nul ) * sizeof(TCHAR) ); if (DiskKeyName == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } lstrcpy(DiskKeyName,RootKeyName); lstrcat(DiskKeyName,TEXT("\\")); lstrcat(DiskKeyName,DISK_KEY_NAME); // Open the DISK key. Err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DiskKeyName, REG_OPTION_RESERVED, KEY_READ, &hkeyDisk); if (Err != NO_ERROR) { goto CleanUp2; } // Determine how large we need the buffer to be Err = RegQueryValueEx(hkeyDisk, DISK_VALUE_NAME, NULL, &ValueType, NULL, &BufferSize); if ((Err != NO_ERROR) && (Err != ERROR_MORE_DATA)) { goto CleanUp1; } // Allocate a buffer of appropriate size Buffer = (PVOID)LocalAlloc(LMEM_FIXED,BufferSize); if (Buffer == NULL) { Err = ERROR_NOT_ENOUGH_MEMORY; goto CleanUp1; } // Query the data Err = RegQueryValueEx(hkeyDisk, DISK_VALUE_NAME, NULL, &ValueType, Buffer, &BufferSize); if (Err != NO_ERROR) { LocalFree(Buffer); goto CleanUp1; } *DiskInfo = Buffer; *DiskInfoSize = BufferSize; CleanUp1: RegCloseKey(hkeyDisk); CleanUp2: LocalFree(DiskKeyName); return Err; } LONG FdpGetDiskInfoFromHive( IN PCHAR HiveFilename, OUT PVOID *DiskInfo, OUT PULONG DiskInfoSize ) /*++ Routine Description: This routine pulls the binary blob containing disk ft, drive letter, and layout information out of a given registry hive, which must be a file in an alternate NT tree (ie, can't be an active hive). The info is found in \DISK:Information within the hive. Arguments: HiveFilename - supplies filename of hive DiskInfo - receives a pointer to a buffer containing the disk info. DiskInfoSize - receives size of the disk buffer. Return Value: Windows error code. If NO_ERROR, DiskInfo and DiskInfoSize are filled in, and it is the caller's responsibility to free the buffer when it is finished (via LocalFree()). --*/ { ULONG windowsError; windowsError = FdpLoadHiveIntoRegistry(HiveFilename); if (windowsError == NO_ERROR) { windowsError = FdpGetDiskInfoFromKey(TEMP_KEY_NAME,DiskInfo,DiskInfoSize); FdpUnloadHiveFromRegistry(); } return windowsError; } LONG FdTransferOldDiskInfoToRegistry( IN PCHAR HiveFilename ) /*++ Routine Description: This routine transfers disk configuration from a given hive file (which should be an inactive system hive) to the current registry. Arguments: HiveFilename - supplies filename of source hive Return Value: Windows error code. --*/ { LONG windowsError; PVOID diskInfo; ULONG diskInfoSize; HKEY hkeyDisk; // Load up the hive and pull the disk info from it. windowsError = FdpGetDiskInfoFromHive(HiveFilename,&diskInfo,&diskInfoSize); if (windowsError != NO_ERROR) { return windowsError; } // Propogate the disk info into the current registry. // // Start by opening HKEY_LOCAL_MACHINE,System\DISK windowsError = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\") DISK_KEY_NAME, REG_OPTION_RESERVED, KEY_WRITE, &hkeyDisk); if (windowsError != NO_ERROR) { LocalFree(diskInfo); return windowsError; } // Set the Information value in the DISK key. windowsError = RegSetValueEx(hkeyDisk, DISK_VALUE_NAME, 0, REG_BINARY, diskInfo, diskInfoSize); RegCloseKey(hkeyDisk); LocalFree(diskInfo); return windowsError; } typedef struct _STRING_LIST_NODE { struct _STRING_LIST_NODE *Next; LPTSTR String; } STRING_LIST_NODE, *PSTRING_LIST_NODE; PSTRING_LIST_NODE FoundDirectoryList; ULONG FoundDirectoryCount; TCHAR Pattern[MAX_PATH+1]; WIN32_FIND_DATA FindData; OFSTRUCT OfStruct; HWND hwndStatus; BOOLEAN ScanDrive[26]; BOOLEAN UserCancelled; typedef BOOL (*PFOUND_HIVE_ROUTINE)( IN LPTSTR Directory ); VOID ProcessPendingMessages( VOID ) /*++ Routine Description: Preprocess messages. Arguments: None Return Value: None --*/ { MSG msg; while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { DispatchMessage(&msg); } } PUCHAR ConfigRegistryPath = "\\system32\\config\\system"; BOOL FdpSearchTreeForSystemHives( IN LPTSTR CurrentDirectory, IN PFOUND_HIVE_ROUTINE FoundHiveRoutine, IN HWND hdlg ) /*++ Routine Description: Search an entire directory tree for system and system.alt hive files. When found, call a callback function with the directory in which system32\config\system[.alt] was found, and the full path of the hive file. The root directory is not included in the search. The top-level call to this function should have a current directory like "C:." (ie, no slash for the root directory). Arguments: CurrentDirectory - supplies current directory search path Return Value: FALSE if error (callback function returned FALSE when we found an entry). --*/ { HANDLE findHandle; TCHAR newDirectory[MAX_PATH+1]; BOOL found = FALSE; // Iterate through the current directory, looking for subdirectories. lstrcpy(Pattern, CurrentDirectory); lstrcat(Pattern, "\\*"); findHandle = FindFirstFile(Pattern, &FindData); if (findHandle != INVALID_HANDLE_VALUE) { do { ProcessPendingMessages(); if (UserCancelled) { return FALSE; } // If the current match is not a directory then skip it. if (!(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || !lstrcmp(FindData.cFileName,TEXT(".")) || !lstrcmp(FindData.cFileName,TEXT(".."))) { continue; } found = FALSE; // Form the name of the file we are looking for // [\\system32\config\system] lstrcpy(Pattern, CurrentDirectory); lstrcat(Pattern, "\\"); lstrcat(Pattern, FindData. cFileName); lstrcpy(newDirectory, Pattern); // Don't decend into the directory unless the path to the // hive.alt name is within MAX_PATH length. if ((ULONG)(lstrlen(newDirectory) / sizeof(TCHAR)) < (MAX_PATH - strlen(ConfigRegistryPath) - 4)) { SetDlgItemText(hdlg, IDC_SIMPLE_TEXT_LINE, newDirectory); lstrcat(Pattern, TEXT(ConfigRegistryPath)); if (OpenFile(Pattern, &OfStruct, OF_EXIST) != (HFILE)(-1)) { found = TRUE; } // Also check for a system.alt file there lstrcat(Pattern,TEXT(".alt")); if (OpenFile(Pattern, &OfStruct, OF_EXIST) != (HFILE)(-1)) { found = TRUE; } if (found) { if (!FoundHiveRoutine(newDirectory)) { return FALSE; } } // Descend into the directory we just found if (!FdpSearchTreeForSystemHives(newDirectory, FoundHiveRoutine, hdlg)) { return FALSE; } } } while (FindNextFile(findHandle,&FindData)); FindClose(findHandle); } return TRUE; } BOOL FdpFoundHiveCallback( IN PCHAR Directory ) /*++ Routine Description: This routine is called when a directory containing a system hive has been located. If all goes well (allocate memory and the like) this routine will save the directory name in a list for later use. NOTE: No checks are made on the directory name being greater in length than MAX_PATH. It is the responsibility of the caller to insure that this is true. Arguments: Directory - the pointer to the character string for the directory where a hive has been located. Return Value: TRUE - did something with it. FALSE - did not save the directory. --*/ { TCHAR windowsDir[MAX_PATH+1]; PSTRING_LIST_NODE dirItem; LPTSTR p; // If this is the current windows directory, skip it. GetWindowsDirectory(windowsDir, sizeof(windowsDir)/sizeof(TCHAR)); if (!lstrcmpi(Directory, windowsDir)) { return TRUE; } // Save the directory information away dirItem = (PSTRING_LIST_NODE)LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, sizeof(STRING_LIST_NODE)); if (dirItem == NULL) { return FALSE; } p = (LPTSTR)LocalAlloc(LMEM_FIXED,(lstrlen(Directory)+1) * sizeof(TCHAR)); if (p == NULL) { LocalFree(dirItem); return FALSE; } dirItem->String = p; lstrcpy(p, Directory); // Update the global chain of found directories. dirItem->Next = FoundDirectoryList; FoundDirectoryList = dirItem; FoundDirectoryCount++; return TRUE; } VOID FdpFreeDirectoryList( VOID ) /*++ Routine Description: Go through the list of directories containing system hives and free the entries. Arguments: None Return Value: None --*/ { PSTRING_LIST_NODE n, p = FoundDirectoryList; while (p) { n = p->Next; if (p->String) { LocalFree(p->String); } LocalFree(p); p = n; } FoundDirectoryCount = 0; FoundDirectoryList = NULL; } BOOL CALLBACK FdpScanningDirsDlgProc( IN HWND hwnd, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: Display the "scanning" dialog, then when the IDLE message arrives process all drive letters and search for system hives. Arguments: Windows dialog proc Return Value: Windows dialog proc --*/ { TCHAR LetterColon[3]; TCHAR Letter; switch (msg) { case WM_INITDIALOG: CenterDialog(hwnd); break; case WM_ENTERIDLE: // Sent to us by the main window after the dialog is displayed. // Perform the search here. ConfigurationSearchIdleTrigger = FALSE; UserCancelled = FALSE; lstrcpy(LetterColon,TEXT("?:")); for (Letter = TEXT('A'); Letter <= TEXT('Z'); Letter++) { if (!ScanDrive[Letter-TEXT('A')]) { continue; } LetterColon[0] = Letter; if (!FdpSearchTreeForSystemHives(LetterColon, FdpFoundHiveCallback, hwnd)) { EndDialog(hwnd,IDCANCEL); return TRUE; } } EndDialog(hwnd,IDOK); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDCANCEL: UserCancelled = TRUE; break; default: return FALSE; } break; default: return FALSE; } return TRUE; } BOOL CALLBACK FdpSelectDirDlgProc( IN HWND hwnd, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: Using the list of directories containing system hives, display the selections to the user and save the selected item if the user so chooses. Arguments: Windows dialog proc. Return Value: Windows dialog proc. --*/ { PSTRING_LIST_NODE Str; LONG i; static HANDLE ListBoxHandle; switch (msg) { case WM_INITDIALOG: CenterDialog(hwnd); // Add each item in the directory list to the listbox ListBoxHandle = GetDlgItem(hwnd,IDC_LISTBOX); for (Str = FoundDirectoryList; Str; Str = Str->Next) { i = SendMessage(ListBoxHandle,LB_ADDSTRING ,0,(LONG)Str->String); SendMessage(ListBoxHandle,LB_SETITEMDATA,i,(LONG)Str ); } // select the zeroth item SendMessage(ListBoxHandle,LB_SETCURSEL,0,0); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: // Get the index of the current list box selection and the // pointer to the string node associated with it. i = SendMessage(ListBoxHandle,LB_GETCURSEL,0,0); EndDialog(hwnd,SendMessage(ListBoxHandle,LB_GETITEMDATA,i,0)); break; case IDCANCEL: EndDialog(hwnd,(int)NULL); break; default: return FALSE; } break; default: return FALSE; } return TRUE; } BOOL DoMigratePreviousFtConfig( VOID ) /*++ Routine Description: Allow the user to move the disk config info from a different Windows NT installation into the current registry. For each fixed disk volume, scan it for system hives and present the results to the user so he can select the installation to migrate. Then load the system hive from that instllation (system.alt if the system hive is corrupt, etc) and transfer the DISK:Information binary blob. Arguments: None. Return Value: FALSE if error or user cancelled, TRUE if info was migrated and reboot is required. --*/ { LONG windowsError; TCHAR letter; TCHAR letterColon[4]; PSTRING_LIST_NODE stringNode; // Tell the user what this will do and prompt for confirmation if (ConfirmationDialog(MSG_CONFIRM_MIGRATE_CONFIG, MB_ICONEXCLAMATION | MB_YESNO) != IDYES) { return FALSE; } ProcessPendingMessages(); // Figure out which drives are relevent SetCursor(hcurWait); RtlZeroMemory(ScanDrive,sizeof(ScanDrive)); lstrcpy(letterColon,TEXT("?:\\")); for (letter=TEXT('A'); letter<=TEXT('Z'); letter++) { letterColon[0] = letter; if (GetDriveType(letterColon) == DRIVE_FIXED) { ScanDrive[letter-TEXT('A')] = TRUE; } } SetCursor(hcurNormal); // Create a window that will list the directories being scanned, to // keep the user entertained. ConfigurationSearchIdleTrigger = TRUE; windowsError = DialogBox(hModule, MAKEINTRESOURCE(IDD_SIMPLETEXT), hwndFrame, FdpScanningDirsDlgProc); if (windowsError == IDCANCEL) { FdpFreeDirectoryList(); return FALSE; } ProcessPendingMessages(); if (!FoundDirectoryCount) { InfoDialog(MSG_NO_OTHER_NTS); return FALSE; } // Display a dialog box that allows the user to select one of the // directories we found. stringNode = (PSTRING_LIST_NODE)DialogBox(hModule, MAKEINTRESOURCE(IDD_SELDIR), hwndFrame, FdpSelectDirDlgProc); if (stringNode == NULL) { FdpFreeDirectoryList(); return FALSE; } // User made a selection. One last confirmation. if (ConfirmationDialog(MSG_ABSOLUTELY_SURE,MB_ICONEXCLAMATION | MB_YESNO) != IDYES) { FdpFreeDirectoryList(); return FALSE; } ProcessPendingMessages(); SetCursor(hcurWait); lstrcpy(Pattern,stringNode->String); lstrcat(Pattern,TEXT(ConfigRegistryPath)); windowsError = FdTransferOldDiskInfoToRegistry(Pattern); if (windowsError != NO_ERROR) { lstrcat(Pattern,TEXT(".alt")); windowsError = FdTransferOldDiskInfoToRegistry(Pattern); } FdpFreeDirectoryList(); SetCursor(hcurNormal); if (windowsError != NO_ERROR) { if (windowsError == ERROR_FILE_NOT_FOUND) { ErrorDialog(MSG_NO_DISK_INFO); } else if (windowsError == ERROR_SHARING_VIOLATION) { ErrorDialog(MSG_DISK_INFO_BUSY); } else { ErrorDialog(windowsError); } return FALSE; } return TRUE; } BOOL DoRestoreFtConfig( VOID ) /*++ Routine Description: Restore previously saved disk configuration information into the active registry. The saved config info will come from a floppy that the user is prompted to insert. Arguments: None. Return Value: FALSE if error or user cancelled, TRUE if info was restored and reboot is required. --*/ { LONG Err; TCHAR caption[256]; UINT errorMode; va_list arglist = #ifdef _ALPHA_ // Alpha defines va_list as a struct. Init as such {0}; #else NULL; #endif // Get confirmation if (ConfirmationDialog(MSG_CONFIRM_RESTORE_CONFIG, MB_ICONEXCLAMATION | MB_YESNO) != IDYES) { return FALSE; } // Get the diskette into A:. errorMode = SetErrorMode(SEM_FAILCRITICALERRORS); LoadString(hModule,IDS_INSERT_DISK,caption,sizeof(caption)/sizeof(TCHAR)); if (CommonDialog(MSG_INSERT_REGSAVEDISK,caption,MB_OKCANCEL | MB_TASKMODAL, arglist) != IDOK) { return FALSE; } ProcessPendingMessages(); SetCursor(hcurWait); // If there is no file called SYSTEM on a:\, it appears that the registry // creates one and then keeps it open. To avoid this, check to see // whether there is one first. if (OpenFile(TEXT("A:\\SYSTEM"),&OfStruct,OF_EXIST) == (HFILE)(-1)) { Err = ERROR_FILE_NOT_FOUND; } else { Err = FdTransferOldDiskInfoToRegistry(TEXT("A:\\SYSTEM")); } SetErrorMode(errorMode); SetCursor(hcurNormal); if (Err != NO_ERROR) { ErrorDialog(Err); return FALSE; } return TRUE; } VOID DoSaveFtConfig( VOID ) /*++ Routine Description: Allow the user to update the registry save diskette with the currently defined disk configuration. The saved info excludes any changes made during this session of disk manager. Arguments: None. Return Value: None. --*/ { LONG Err, ErrAlt; LPTSTR SystemHiveName = TEXT("a:\\system"); HKEY hkey; TCHAR caption[256]; DWORD disposition; UINT errorMode; va_list arglist = #ifdef _ALPHA_ {0}; // Alpha defines va_list as a struct. Init as such. #else NULL; #endif // Get a diskette into A:. LoadString(hModule, IDS_INSERT_DISK, caption, sizeof(caption)/sizeof(TCHAR)); if (CommonDialog(MSG_INSERT_REGSAVEDISK2,caption,MB_OKCANCEL | MB_TASKMODAL, arglist) != IDOK) { return; } // Decide what to do based on the presence of a a:\system. If that file // is present, just update the DISK entry in it. If it is not present, // then blast out the entire system hive. errorMode = SetErrorMode(SEM_FAILCRITICALERRORS); ProcessPendingMessages(); SetCursor(hcurWait); if (OpenFile(SystemHiveName,&OfStruct,OF_EXIST) == (HFILE)(-1)) { BOOLEAN OldPrivState; NTSTATUS Status; // Blast the entire system hive out to the floppy. // Start by attempting to get backup privilege. Status = RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE, TRUE, FALSE, &OldPrivState); Err = RtlNtStatusToDosError(Status); if (Err == NO_ERROR) { Err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("system"), REG_OPTION_RESERVED, KEY_READ, &hkey); if (Err == NO_ERROR) { Err = RegSaveKey(hkey,SystemHiveName,NULL); RegCloseKey(hkey); } if (!OldPrivState) { RtlAdjustPrivilege(SE_BACKUP_PRIVILEGE,FALSE,FALSE,&OldPrivState); } } } else { PVOID DiskInfo; ULONG DiskInfoSize; // Load up the saved system hive Err = FdpLoadHiveIntoRegistry(SystemHiveName); if (Err == NO_ERROR) { // Get the current DISK information Err = FdpGetDiskInfoFromKey(TEXT("system"),&DiskInfo,&DiskInfoSize); if (Err == NO_ERROR) { // Place the current disk information into the saved hive Err = RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEMP_KEY_NAME TEXT("\\") DISK_KEY_NAME, 0, "Disk and fault tolerance information.", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkey, &disposition ); if (Err == NO_ERROR) { Err = RegSetValueEx(hkey, DISK_VALUE_NAME, REG_OPTION_RESERVED, REG_BINARY, DiskInfo, DiskInfoSize); RegFlushKey(hkey); RegCloseKey(hkey); } LocalFree(DiskInfo); } ErrAlt = FdpUnloadHiveFromRegistry(); if (Err == NO_ERROR && ErrAlt != NO_ERROR) { Err = ErrAlt; } } } SetCursor(hcurNormal); SetErrorMode(errorMode); if (Err == NO_ERROR) { InfoDialog(MSG_CONFIG_SAVED_OK); } else { ErrorDialog(Err); } return; }