// // REGHIVE.C // // Copyright (C) Microsoft Corporation, 1995 // // Implementation of RegLoadKey, RegUnLoadKey, RegSaveKey, RegReplaceKey and // supporting functions. // #include "pch.h" #ifdef WANT_HIVE_SUPPORT // Maximum number of times we'll allow RgCopyBranch to be reentered. #define MAXIMUM_COPY_RECURSION 32 LPSTR g_RgNameBufferPtr; // Temporary buffer for RgCopyBranch LPBYTE g_RgDataBufferPtr; // Temporary buffer for RgCopyBranch UINT g_RgRecursionCount; // Tracks depth of RgCopyBranch #if MAXIMUM_VALUE_NAME_LENGTH > MAXIMUM_SUB_KEY_LENGTH #error Code assumes a value name can fit in a subkey buffer. #endif #ifdef VXD #pragma VxD_RARE_CODE_SEG #endif // // RgValidateHiveSubKey // // Note that unlike most parameter validation routines, this routine must be // called with the registry lock taken because we call RgGetNextSubSubKey. // // Pass back the length of the subkey to deal with the trailing backslash // problem. // // Returns TRUE if lpSubKey is a valid subkey string for hive functions. // BOOL INTERNAL RgValidateHiveSubKey( LPCSTR lpSubKey, UINT FAR* lpHiveKeyLength ) { LPCSTR lpSubSubKey; UINT SubSubKeyLength; // Verify that we have a valid subkey that has one and only one sub-subkey. // Win95 messed this up and it was possible to load a hive with a keyname // containing a backslash! return !IsNullPtr(lpSubKey) && !RgIsBadSubKey(lpSubKey) && (RgGetNextSubSubKey(lpSubKey, &lpSubSubKey, lpHiveKeyLength) > 0) && (RgGetNextSubSubKey(NULL, &lpSubSubKey, &SubSubKeyLength) == 0); } // // VMMRegLoadKey // // See Win32 documentation of RegLoadKey. // LONG REGAPI VMMRegLoadKey( HKEY hKey, LPCSTR lpSubKey, LPCSTR lpFileName ) { int ErrorCode; HKEY hSubKey; UINT SubKeyLength; LPHIVE_INFO lpHiveInfo; if (IsBadStringPtr(lpFileName, (UINT) -1)) return ERROR_INVALID_PARAMETER; if ((hKey != HKEY_LOCAL_MACHINE) && (hKey != HKEY_USERS)) return ERROR_BADKEY; if (!RgLockRegistry()) return ERROR_LOCK_FAILED; if ((ErrorCode = RgValidateAndConvertKeyHandle(&hKey)) != ERROR_SUCCESS) goto ReturnErrorCode; if (!RgValidateHiveSubKey(lpSubKey, &SubKeyLength)) { ErrorCode = ERROR_BADKEY; goto ReturnErrorCode; } // Check if a subkey with the specified name already exists. if (RgLookupKey(hKey, lpSubKey, &hSubKey, LK_OPEN) == ERROR_SUCCESS) { RgDestroyKeyHandle(hSubKey); ErrorCode = ERROR_BADKEY; // Win95 compatibility goto ReturnErrorCode; } if (IsNullPtr((lpHiveInfo = (LPHIVE_INFO) RgSmAllocMemory(sizeof(HIVE_INFO) + SubKeyLength)))) { ErrorCode = ERROR_OUTOFMEMORY; goto ReturnErrorCode; } // Fill in the HIVE_INFO. StrCpy(lpHiveInfo-> Name, lpSubKey); lpHiveInfo-> NameLength = SubKeyLength; lpHiveInfo-> Hash = (BYTE) RgHashString(lpSubKey, SubKeyLength); // Attempt to create a FILE_INFO for the specified file. If successful, // link this HIVE_INFO into the parent FILE_INFO's hive list. if ((ErrorCode = RgCreateFileInfoExisting(&lpHiveInfo-> lpFileInfo, lpFileName)) == ERROR_SUCCESS) { #ifdef WANT_NOTIFY_CHANGE_SUPPORT lpHiveInfo-> lpFileInfo-> lpParentFileInfo = hKey-> lpFileInfo; #endif lpHiveInfo-> lpNextHiveInfo = hKey-> lpFileInfo-> lpHiveInfoList; hKey-> lpFileInfo-> lpHiveInfoList = lpHiveInfo; // Signal any notifications waiting on this top-level key. RgSignalWaitingNotifies(hKey-> lpFileInfo, hKey-> KeynodeIndex, REG_NOTIFY_CHANGE_NAME); } else RgFreeMemory(lpHiveInfo); ReturnErrorCode: RgUnlockRegistry(); return ErrorCode; } // // VMMRegUnLoadKey // // See Win32 documentation of RegUnLoadKey. // LONG REGAPI VMMRegUnLoadKey( HKEY hKey, LPCSTR lpSubKey ) { int ErrorCode; UINT SubKeyLength; LPFILE_INFO lpFileInfo; LPHIVE_INFO lpPrevHiveInfo; LPHIVE_INFO lpCurrHiveInfo; if ((hKey != HKEY_LOCAL_MACHINE) && (hKey != HKEY_USERS)) return ERROR_BADKEY; if (!RgLockRegistry()) return ERROR_LOCK_FAILED; if ((ErrorCode = RgValidateAndConvertKeyHandle(&hKey)) != ERROR_SUCCESS) goto ReturnErrorCode; ErrorCode = ERROR_BADKEY; // Assume this error code if (!RgValidateHiveSubKey(lpSubKey, &SubKeyLength)) goto ReturnErrorCode; lpPrevHiveInfo = NULL; lpCurrHiveInfo = hKey-> lpFileInfo-> lpHiveInfoList; while (!IsNullPtr(lpCurrHiveInfo)) { if (SubKeyLength == lpCurrHiveInfo-> NameLength && RgStrCmpNI(lpSubKey, lpCurrHiveInfo-> Name, SubKeyLength) == 0) { // Unlink this HIVE_INFO structure. if (IsNullPtr(lpPrevHiveInfo)) hKey-> lpFileInfo-> lpHiveInfoList = lpCurrHiveInfo-> lpNextHiveInfo; else lpPrevHiveInfo-> lpNextHiveInfo = lpCurrHiveInfo-> lpNextHiveInfo; // Flush and destroy it's associated FILE_INFO structure. When we // destroy the FILE_INFO, all open keys in this hive will be // invalidated. lpFileInfo = lpCurrHiveInfo-> lpFileInfo; RgFlushFileInfo(lpFileInfo); RgDestroyFileInfo(lpFileInfo); // Free the HIVE_INFO itself. RgSmFreeMemory(lpCurrHiveInfo); // Signal any notifications waiting on this top-level key. RgSignalWaitingNotifies(hKey-> lpFileInfo, hKey-> KeynodeIndex, REG_NOTIFY_CHANGE_NAME); ErrorCode = ERROR_SUCCESS; break; } lpPrevHiveInfo = lpCurrHiveInfo; lpCurrHiveInfo = lpCurrHiveInfo-> lpNextHiveInfo; } ReturnErrorCode: RgUnlockRegistry(); return ErrorCode; } // // RgCopyBranchHelper // // Copies all of the values and subkeys starting at the specified source key to // the specified destination key. // // For Win95 compatibility, we don't stop the copy process if we encounter an // error. (But unlike Win95, we do actually check more error codes) // // SHOULD ONLY BE CALLED BY RgCopyBranch. // VOID INTERNAL RgCopyBranchHelper( HKEY hSourceKey, HKEY hDestinationKey ) { UINT Index; DWORD cbNameBuffer; LPVALUE_RECORD lpValueRecord; // // Copy all of the values from the source key to the destination key. // Index = 0; while (RgLookupValueByIndex(hSourceKey, Index++, &lpValueRecord) == ERROR_SUCCESS) { DWORD cbDataBuffer; DWORD Type; cbNameBuffer = MAXIMUM_VALUE_NAME_LENGTH; cbDataBuffer = MAXIMUM_DATA_LENGTH + 1; // Terminating null if (RgCopyFromValueRecord(hSourceKey, lpValueRecord, g_RgNameBufferPtr, &cbNameBuffer, &Type, g_RgDataBufferPtr, &cbDataBuffer) == ERROR_SUCCESS) { // Subtract the terminating null that RgCopyFromValueRecord added // to cbDataBuffer. We don't save that in the file. if (Type == REG_SZ) { ASSERT(cbDataBuffer > 0); // Must have the null! cbDataBuffer--; } RgSetValue(hDestinationKey, g_RgNameBufferPtr, Type, g_RgDataBufferPtr, cbDataBuffer); } RgUnlockDatablock(hSourceKey-> lpFileInfo, hSourceKey-> BlockIndex, FALSE); } // We can't recurse forever, so enforce a maximum depth like Win95. if (g_RgRecursionCount > MAXIMUM_COPY_RECURSION) return; g_RgRecursionCount++; // // Copy all of the subkeys from the source key to the destination key. // Index = 0; while (TRUE) { HKEY hSubSourceKey; HKEY hSubDestinationKey; cbNameBuffer = MAXIMUM_SUB_KEY_LENGTH; if (RgLookupKeyByIndex(hSourceKey, Index++, g_RgNameBufferPtr, &cbNameBuffer) != ERROR_SUCCESS) break; if (RgLookupKey(hSourceKey, g_RgNameBufferPtr, &hSubSourceKey, LK_OPEN) == ERROR_SUCCESS) { if (RgLookupKey(hDestinationKey, g_RgNameBufferPtr, &hSubDestinationKey, LK_CREATE) == ERROR_SUCCESS) { RgYield(); RgCopyBranchHelper(hSubSourceKey, hSubDestinationKey); RgDestroyKeyHandle(hSubDestinationKey); } else TRAP(); RgDestroyKeyHandle(hSubSourceKey); } else TRAP(); } g_RgRecursionCount--; } // // RgCopyBranch // // Copies all of the values and subkeys starting at the specified source key to // the specified destination key. // // This function sets and cleans up for RgCopyBranchHelper who does all // the real copying. // // The backing store of the destination file is flushed if successful. // int INTERNAL RgCopyBranch( HKEY hSourceKey, HKEY hDestinationKey ) { int ErrorCode; if (IsNullPtr(g_RgNameBufferPtr = RgSmAllocMemory(MAXIMUM_SUB_KEY_LENGTH))) ErrorCode = ERROR_OUTOFMEMORY; else { if (IsNullPtr(g_RgDataBufferPtr = RgSmAllocMemory(MAXIMUM_DATA_LENGTH + 1))) // + terminating null ErrorCode = ERROR_OUTOFMEMORY; else { g_RgRecursionCount = 0; RgCopyBranchHelper(hSourceKey, hDestinationKey); // Everything should be copied over, so flush the file now since // all callers will be immediately destroying this FILE_INFO // anyways. ErrorCode = RgFlushFileInfo(hDestinationKey-> lpFileInfo); } RgSmFreeMemory(g_RgNameBufferPtr); } RgSmFreeMemory(g_RgDataBufferPtr); return ErrorCode; } // // RgSaveKey // // Worker routine for VMMRegSaveKey and VMMRegReplaceKey. Saves all the keys // and values starting at hKey, which must point at a valid KEY structure, to // the location specified by lpFileName. The file must not currently exist. // int INTERNAL RgSaveKey( HKEY hKey, LPCSTR lpFileName ) { int ErrorCode; HKEY hHiveKey; if (IsNullPtr(hHiveKey = RgCreateKeyHandle())) ErrorCode = ERROR_OUTOFMEMORY; else { // Artificially increment the count, so the below destroy will work. RgIncrementKeyReferenceCount(hHiveKey); if ((ErrorCode = RgCreateFileInfoNew(&hHiveKey-> lpFileInfo, lpFileName, CFIN_SECONDARY)) == ERROR_SUCCESS) { if (((ErrorCode = RgInitRootKeyFromFileInfo(hHiveKey)) != ERROR_SUCCESS) || ((ErrorCode = RgCopyBranch(hKey, hHiveKey)) != ERROR_SUCCESS)) { RgSetFileAttributes(hHiveKey-> lpFileInfo-> FileName, FILE_ATTRIBUTE_NONE); RgDeleteFile(hHiveKey-> lpFileInfo-> FileName); } // If successful, then RgCopyBranch has already flushed the file. RgDestroyFileInfo(hHiveKey-> lpFileInfo); } RgDestroyKeyHandle(hHiveKey); } return ErrorCode; } // // VMMRegSaveKey // // See Win32 documentation of RegSaveKey. // LONG REGAPI VMMRegSaveKey( HKEY hKey, LPCSTR lpFileName, LPVOID lpSecurityAttributes ) { int ErrorCode; if (IsBadStringPtr(lpFileName, (UINT) -1)) return ERROR_INVALID_PARAMETER; if (!RgLockRegistry()) return ERROR_LOCK_FAILED; if ((ErrorCode = RgValidateAndConvertKeyHandle(&hKey)) == ERROR_SUCCESS) ErrorCode = RgSaveKey(hKey, lpFileName); RgUnlockRegistry(); return ErrorCode; UNREFERENCED_PARAMETER(lpSecurityAttributes); } #ifdef WANT_REGREPLACEKEY // // RgGetKeyName // LPSTR INTERNAL RgGetKeyName( HKEY hKey ) { LPSTR lpKeyName; LPKEY_RECORD lpKeyRecord; if (RgLockKeyRecord(hKey-> lpFileInfo, hKey-> BlockIndex, hKey-> KeyRecordIndex, &lpKeyRecord) != ERROR_SUCCESS) lpKeyName = NULL; else { // A registry is corrupt if we ever hit this. We'll continue to // allocate a buffer and let downstream code fail when we try to use // the string. ASSERT(lpKeyRecord-> NameLength < MAXIMUM_SUB_KEY_LENGTH); if (!IsNullPtr(lpKeyName = (LPSTR) RgSmAllocMemory(lpKeyRecord-> NameLength + 1))) { // + terminating null MoveMemory(lpKeyName, lpKeyRecord-> Name, lpKeyRecord-> NameLength); lpKeyName[lpKeyRecord-> NameLength] = '\0'; } RgUnlockDatablock(hKey-> lpFileInfo, hKey-> BlockIndex, FALSE); } return lpKeyName; } // // RgCreateRootKeyForFile // // Creates a KEY and a FILE_INFO to access the specified file. // int INTERNAL RgCreateRootKeyForFile( LPHKEY lphKey, LPCSTR lpFileName ) { int ErrorCode; HKEY hKey; if (IsNullPtr(hKey = RgCreateKeyHandle())) ErrorCode = ERROR_OUTOFMEMORY; else { // Artificially increment the count, so RgDestroyKeyHandle will work. RgIncrementKeyReferenceCount(hKey); if ((ErrorCode = RgCreateFileInfoExisting(&hKey-> lpFileInfo, lpFileName)) == ERROR_SUCCESS) { if ((ErrorCode = RgInitRootKeyFromFileInfo(hKey)) == ERROR_SUCCESS) { *lphKey = hKey; return ERROR_SUCCESS; } RgDestroyFileInfo(hKey-> lpFileInfo); } RgDestroyKeyHandle(hKey); } return ErrorCode; } // // RgDestroyRootKeyForFile // // Destroys the resources allocated by RgCreateRootKeyForFile. // VOID INTERNAL RgDestroyRootKeyForFile( HKEY hKey ) { RgDestroyFileInfo(hKey-> lpFileInfo); RgDestroyKeyHandle(hKey); } // // RgDeleteHiveFile // // Deletes the specified hive file after clearing its file attributes. // BOOL INTERNAL RgDeleteHiveFile( LPCSTR lpFileName ) { RgSetFileAttributes(lpFileName, FILE_ATTRIBUTE_NONE); // RgSetFileAttributes may fail, but try to delete the file anyway. return RgDeleteFile(lpFileName); } // // VMMRegReplaceKey // // See Win32 documentation of RegReplaceKey. // LONG REGAPI VMMRegReplaceKey( HKEY hKey, LPCSTR lpSubKey, LPCSTR lpNewFileName, LPCSTR lpOldFileName ) { int ErrorCode; HKEY hSubKey; LPKEYNODE lpKeynode; DWORD KeynodeIndex; HKEY hParentKey; char ReplaceFileName[MAX_PATH]; BOOL fCreatedReplaceFile; HKEY hReplaceKey; HKEY hNewKey; HKEY hReplaceSubKey; LPSTR lpReplaceSubKey; if (IsBadOptionalStringPtr(lpSubKey, (UINT) -1) || IsBadStringPtr(lpNewFileName, (UINT) -1) || IsBadStringPtr(lpOldFileName, (UINT) -1)) return ERROR_INVALID_PARAMETER; if (!RgLockRegistry()) return ERROR_LOCK_FAILED; if ((ErrorCode = RgValidateAndConvertKeyHandle(&hKey)) != ERROR_SUCCESS) goto ErrorReturn; if ((ErrorCode = RgLookupKey(hKey, lpSubKey, &hSubKey, LK_OPEN)) != ERROR_SUCCESS) goto ErrorReturn; // // The provided key handle must an immediate child from the same backing // store (not a hive) as either HKEY_LOCAL_MACHINE or HKEY_USERS. // if (RgLockInUseKeynode(hSubKey-> lpFileInfo, hSubKey-> KeynodeIndex, &lpKeynode) != ERROR_SUCCESS) { ErrorCode = ERROR_OUTOFMEMORY; goto ErrorDestroySubKey; } KeynodeIndex = lpKeynode-> ParentIndex; RgUnlockKeynode(hSubKey-> lpFileInfo, hSubKey-> KeynodeIndex, FALSE); // Find an open key on the parent check if it's HKEY_LOCAL_MACHINE or // HKEY_USERS. If not, bail out. KeynodeIndex may be REG_NULL, but // RgFindOpenKeyHandle handles that case. if (IsNullPtr(hParentKey = RgFindOpenKeyHandle(hSubKey-> lpFileInfo, KeynodeIndex)) || ((hParentKey != &g_RgLocalMachineKey) && (hParentKey != &g_RgUsersKey))) { ErrorCode = ERROR_INVALID_PARAMETER; goto ErrorDestroySubKey; } // // All parameters have been validated, so begin the real work of the API. // // Because we'll be doing a file copy below, all changes must be flushed // now. if ((ErrorCode = RgFlushFileInfo(hSubKey-> lpFileInfo)) != ERROR_SUCCESS) goto ErrorDestroySubKey; // Make a backup of the current contents of the subkey. if ((ErrorCode = RgSaveKey(hSubKey, lpOldFileName)) != ERROR_SUCCESS) goto ErrorDestroySubKey; RgGenerateAltFileName(hSubKey-> lpFileInfo-> FileName, ReplaceFileName, 'R'); // Check if the magic replacement file already exists and if not, create // it. if (RgGetFileAttributes(ReplaceFileName) == (DWORD) -1) { if ((ErrorCode = RgCopyFile(hSubKey-> lpFileInfo-> FileName, ReplaceFileName)) != ERROR_SUCCESS) goto ErrorDeleteOldFile; fCreatedReplaceFile = TRUE; } else fCreatedReplaceFile = FALSE; if ((ErrorCode = RgCreateRootKeyForFile(&hNewKey, lpNewFileName)) != ERROR_SUCCESS) goto ErrorDeleteReplaceFile; if ((ErrorCode = RgCreateRootKeyForFile(&hReplaceKey, ReplaceFileName)) != ERROR_SUCCESS) goto ErrorDestroyNewRootKey; // The original key that we were given may reference the subkey, so // lpSubKey would be a NULL or empty string. But we need the name that // this subkey refers to, so we have to go back to the file to pull out // the name. if (hKey != hSubKey) lpReplaceSubKey = (LPSTR) lpSubKey; else { // We allocate this from the heap to reduce the requirements of an // already strained stack. If this fails, we're likely out of memory. // Even if that's not why we failed, this is such an infrequent path // that it's a good enough error code. if (IsNullPtr(lpReplaceSubKey = RgGetKeyName(hSubKey))) { ErrorCode = ERROR_OUTOFMEMORY; goto ErrorDestroyReplaceRootKey; } } // Check if the specified subkey already exists and if it does, delete it. if (RgLookupKey(hReplaceKey, lpReplaceSubKey, &hReplaceSubKey, LK_OPEN) == ERROR_SUCCESS) { RgDeleteKey(hReplaceSubKey); RgDestroyKeyHandle(hReplaceSubKey); } // Create the specified subkey in the replacement registry and copy the // new hive to that key. if ((ErrorCode = RgLookupKey(hReplaceKey, lpReplaceSubKey, &hReplaceSubKey, LK_CREATE)) == ERROR_SUCCESS) { // If successful, tag the FILE_INFO so that on system exit, we'll go // and rename the replacement file to actual filename. if ((ErrorCode = RgCopyBranch(hNewKey, hReplaceSubKey)) == ERROR_SUCCESS) hKey-> lpFileInfo-> Flags |= FI_REPLACEMENTEXISTS; RgDestroyKeyHandle(hReplaceSubKey); } if (lpSubKey != lpReplaceSubKey) RgSmFreeMemory(lpReplaceSubKey); ErrorDestroyReplaceRootKey: RgDestroyRootKeyForFile(hReplaceKey); ErrorDestroyNewRootKey: RgDestroyRootKeyForFile(hNewKey); ErrorDeleteReplaceFile: if (ErrorCode != ERROR_SUCCESS && fCreatedReplaceFile) RgDeleteHiveFile(ReplaceFileName); ErrorDeleteOldFile: if (ErrorCode != ERROR_SUCCESS) RgDeleteHiveFile(lpOldFileName); ErrorDestroySubKey: RgDestroyKeyHandle(hSubKey); ErrorReturn: RgUnlockRegistry(); return ErrorCode; } #ifdef VXD #pragma VxD_SYSEXIT_CODE_SEG #endif // // RgReplaceFileOnSysExit // // Essentially the same algorithm as rlReplaceFile from the Win95 registry // code with modifications for how file I/O is handled in this library. // int INTERNAL RgReplaceFileOnSysExit( LPCSTR lpFileName ) { int ErrorCode; char ReplaceFileName[MAX_PATH]; char SaveFileName[MAX_PATH]; ErrorCode = ERROR_SUCCESS; if (RgGenerateAltFileName(lpFileName, ReplaceFileName, 'R') && RgGetFileAttributes(ReplaceFileName) == (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) { // If we were able to generate the replace file name, then we must be // able to generate the save file name, so ignore the result. RgGenerateAltFileName(lpFileName, SaveFileName, 'S'); RgDeleteHiveFile(SaveFileName); // Preserve the current hive in case something fails below. if (!RgSetFileAttributes(lpFileName, FILE_ATTRIBUTE_NONE) || !RgRenameFile(lpFileName, SaveFileName)) ErrorCode = ERROR_REGISTRY_IO_FAILED; else { // Now try to move the replacement in. if (!RgSetFileAttributes(ReplaceFileName, FILE_ATTRIBUTE_NONE) || !RgRenameFile(ReplaceFileName, lpFileName)) { ErrorCode = ERROR_REGISTRY_IO_FAILED; RgRenameFile(SaveFileName, lpFileName); } else RgDeleteFile(SaveFileName); } RgSetFileAttributes(lpFileName, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); } return ErrorCode; } // // RgReplaceFileInfo // // Called during registry detach to do any necessary file replacements as a // result of calling RegReplaceKey. // int INTERNAL RgReplaceFileInfo( LPFILE_INFO lpFileInfo ) { if (lpFileInfo-> Flags & FI_REPLACEMENTEXISTS) RgReplaceFileOnSysExit(lpFileInfo-> FileName); return ERROR_SUCCESS; } #endif // WANT_REGREPLACEKEY #endif // WANT_HIVE_SUPPORT