843 lines
21 KiB
C
843 lines
21 KiB
C
//
|
|
// 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
|