/*++ Copyright (c) 1992 Microsoft Corporation Module Name: regtool.c Abstract: This file contains functions for supporting the registry tools REGINI, REGDMP, REGDIR and REGFIND Author: Steve Wood (stevewo) 15-Nov-1995 Revision History: --*/ #include #include "regutil.h" ULONG ValueBufferSize = (4096 * 100); PVOID ValueBuffer; UCHAR BlanksForPadding[] = " "; // // routines for creating security descriptors (defined in regacl.c) // BOOLEAN RegInitializeSecurity( VOID ); BOOLEAN WINAPI RegUnicodeToDWORD( IN OUT PWSTR *String, IN DWORD Base OPTIONAL, OUT PDWORD Value ); BOOLEAN RegCreateSecurity( IN PWSTR Description, OUT PSECURITY_DESCRIPTOR SecurityDescriptor ); BOOLEAN RegFormatSecurity( IN PSECURITY_DESCRIPTOR SecurityDescriptor, OUT PWSTR AceList ); VOID RegDestroySecurity( IN PSECURITY_DESCRIPTOR SecurityDescriptor ); LONG RegLoadHive( IN PREG_CONTEXT RegistryContext, IN PWSTR HiveFileName, IN PWSTR HiveRootName ); void RegUnloadHive( IN PREG_CONTEXT RegistryContext ); struct { PWSTR TypeName; ULONG ValueType; BOOLEAN GetDataFromBinaryFile; BOOLEAN GetDataFromMultiSzFile; BOOLEAN ParseDateTime; } RegTypeNameTable[] = { {L"REG_SZ", REG_SZ, FALSE, FALSE, FALSE}, {L"REG_EXPAND_SZ", REG_EXPAND_SZ, FALSE, FALSE, FALSE}, {L"REG_MULTI_SZ", REG_MULTI_SZ, FALSE, FALSE, FALSE}, {L"REG_MULTISZ_FILE", REG_MULTI_SZ, FALSE, TRUE, FALSE}, {L"REG_DWORD", REG_DWORD, FALSE, FALSE, FALSE}, {L"REG_NONE", REG_NONE, FALSE, FALSE, FALSE}, {L"REG_BINARY", REG_BINARY, FALSE, FALSE, FALSE}, {L"REG_BINARYFILE", REG_BINARY, TRUE, FALSE, FALSE}, {L"REG_DATE", REG_BINARY, FALSE, FALSE, TRUE}, {L"REG_RESOURCE_LIST", REG_RESOURCE_LIST, FALSE, FALSE, FALSE}, {L"REG_RESOURCE_REQUIREMENTS_LIST", REG_RESOURCE_REQUIREMENTS_LIST, FALSE, FALSE, FALSE}, {L"REG_RESOURCE_REQUIREMENTS", REG_RESOURCE_REQUIREMENTS_LIST, FALSE, FALSE, FALSE}, {L"REG_FULL_RESOURCE_DESCRIPTOR", REG_FULL_RESOURCE_DESCRIPTOR, FALSE, FALSE, FALSE}, {NULL, REG_NONE, FALSE, FALSE, FALSE} }; struct { PWSTR ValueName; ULONG Value; } RegValueNameTable[] = { {L"ON", TRUE}, {L"YES", TRUE}, {L"TRUE", TRUE}, {L"OFF", FALSE}, {L"NO", FALSE}, {L"FALSE", FALSE}, {NULL, FALSE} }; int RegAnsiToUnicode( LPCSTR Source, PWSTR Destination, ULONG NumberOfChars ) { int NumberOfXlatedChars; if (NumberOfChars == 0) { NumberOfChars = strlen( Source ); } NumberOfXlatedChars = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, Source, NumberOfChars, Destination, NumberOfChars ); Destination[ NumberOfXlatedChars ] = UNICODE_NULL; if ( NumberOfXlatedChars == 0 ) { SetLastError( ERROR_NO_UNICODE_TRANSLATION ); } return NumberOfXlatedChars; } int RegUnicodeToAnsi( PCWSTR Source, LPSTR Destination, ULONG NumberOfChars ) { int NumberOfXlatedChars; if (NumberOfChars == 0) { NumberOfChars = wcslen( Source ); } NumberOfXlatedChars = WideCharToMultiByte( CP_ACP, 0, Source, NumberOfChars, Destination, NumberOfChars * 2, NULL, NULL ); Destination[ NumberOfXlatedChars ] = '\0'; if ( NumberOfXlatedChars == 0 ) { SetLastError( ERROR_NO_UNICODE_TRANSLATION ); } return NumberOfXlatedChars; } typedef LONG (APIENTRY *LPVMMREGMAPPREDEFKEYTOFILE_PROCEDURE)( HKEY hKey, LPCSTR lpFileName, UINT Flags ); typedef LONG (APIENTRY *LPVMMREGLOADKEY_PROCEDURE)( HKEY hKey, LPCSTR lpSubKey, LPCSTR lpFileName ); typedef LONG (APIENTRY *LPVMMREGUNLOADKEY_PROCEDURE)( HKEY hKey, LPCSTR lpSubKey ); typedef LONG (APIENTRY *LPVMMREGCREATEKEY_PROCEDURE)( HKEY hKey, LPCSTR lpSubKey, PHKEY lphSubKey ); typedef LONG (APIENTRY *LPVMMREGDELETEKEY_PROCEDURE)( HKEY hKey, LPCSTR lpSubKey ); typedef LONG (APIENTRY *LPVMMREGOPENKEY_PROCEDURE)( HKEY hKey, LPCSTR lpSubKey, PHKEY lphSubKey ); typedef LONG (APIENTRY *LPVMMREGFLUSHKEY_PROCEDURE)( HKEY hKey ); typedef LONG (APIENTRY *LPVMMREGCLOSEKEY_PROCEDURE)( HKEY hKey ); typedef LONG (APIENTRY *LPVMMREGQUERYINFOKEY_PROCEDURE)( HKEY hKey, LPCSTR lpClass, LPDWORD lpcbClass, LPDWORD lpReserved, LPDWORD lpcSubKeys, LPDWORD lpcbMaxSubKeyLen, LPDWORD lpcbMaxClassLen, LPDWORD lpcValues, LPDWORD lpcbMaxValueName, LPDWORD lpcbMaxValueData, LPVOID lpcbSecurityDescriptor, LPVOID lpftLastWriteTime ); typedef LONG (APIENTRY *LPVMMREGENUMKEY_PROCEDURE)( HKEY hKey, DWORD Index, LPSTR lpKeyName, DWORD cbKeyName ); typedef LONG (APIENTRY *LPVMMREGENUMVALUE_PROCEDURE)( HKEY hKey, DWORD Index, LPSTR lpValueName, LPDWORD lpcbValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData ); typedef LONG (APIENTRY *LPVMMREGQUERYVALUEEX_PROCEDURE)( HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData ); typedef LONG (APIENTRY *LPVMMREGSETVALUEEX_PROCEDURE)( HKEY hKey, LPCSTR lpValueName, DWORD Reserved, DWORD Type, LPBYTE lpData, DWORD cbData ); typedef LONG (APIENTRY *LPVMMREGDELETEVALUE_PROCEDURE)( HKEY hKey, LPCSTR lpValueName ); HMODULE hVMMREG32; LPVMMREGMAPPREDEFKEYTOFILE_PROCEDURE _Win95RegMapPredefKeyToFile; LPVMMREGLOADKEY_PROCEDURE _Win95RegLoadKey; LPVMMREGUNLOADKEY_PROCEDURE _Win95RegUnLoadKey; LPVMMREGCREATEKEY_PROCEDURE _Win95RegCreateKey; LPVMMREGDELETEKEY_PROCEDURE _Win95RegDeleteKey; LPVMMREGOPENKEY_PROCEDURE _Win95RegOpenKey; LPVMMREGFLUSHKEY_PROCEDURE _Win95RegFlushKey; LPVMMREGCLOSEKEY_PROCEDURE _Win95RegCloseKey; LPVMMREGQUERYINFOKEY_PROCEDURE _Win95RegQueryInfoKey; LPVMMREGENUMKEY_PROCEDURE _Win95RegEnumKey; LPVMMREGENUMVALUE_PROCEDURE _Win95RegEnumValue; LPVMMREGQUERYVALUEEX_PROCEDURE _Win95RegQueryValueEx; LPVMMREGSETVALUEEX_PROCEDURE _Win95RegSetValueEx; LPVMMREGDELETEVALUE_PROCEDURE _Win95RegDeleteValue; BOOLEAN RegInitWin95RegistryAccess( PREG_CONTEXT RegistryContext, PWSTR Win95Path, PWSTR Win95UserPath ) { LONG Error; char Buffer[ MAX_PATH+1 ]; if ((hVMMREG32 = LoadLibrary( L"VMMREG32" )) == NULL) { return FALSE; } _Win95RegMapPredefKeyToFile = (LPVMMREGMAPPREDEFKEYTOFILE_PROCEDURE)GetProcAddress( hVMMREG32, "VMMRegMapPredefKeyToFile" ); _Win95RegLoadKey = (LPVMMREGLOADKEY_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegLoadKey" ); _Win95RegUnLoadKey = (LPVMMREGUNLOADKEY_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegUnLoadKey" ); _Win95RegCreateKey = (LPVMMREGCREATEKEY_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegCreateKey" ); _Win95RegDeleteKey = (LPVMMREGDELETEKEY_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegDeleteKey" ); _Win95RegOpenKey = (LPVMMREGOPENKEY_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegOpenKey" ); _Win95RegFlushKey = (LPVMMREGFLUSHKEY_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegFlushKey" ); _Win95RegCloseKey = (LPVMMREGCLOSEKEY_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegCloseKey" ); _Win95RegQueryInfoKey = (LPVMMREGQUERYINFOKEY_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegQueryInfoKey" ); _Win95RegEnumKey = (LPVMMREGENUMKEY_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegEnumKey" ); _Win95RegEnumValue = (LPVMMREGENUMVALUE_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegEnumValue" ); _Win95RegQueryValueEx = (LPVMMREGQUERYVALUEEX_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegQueryValueEx" ); _Win95RegSetValueEx = (LPVMMREGSETVALUEEX_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegSetValueEx" ); _Win95RegDeleteValue = (LPVMMREGDELETEVALUE_PROCEDURE )GetProcAddress( hVMMREG32, "VMMRegDeleteValue" ); if ((_Win95RegMapPredefKeyToFile == NULL) || (_Win95RegLoadKey == NULL) || (_Win95RegUnLoadKey == NULL) || (_Win95RegCreateKey == NULL) || (_Win95RegDeleteKey == NULL) || (_Win95RegOpenKey == NULL) || (_Win95RegFlushKey == NULL) || (_Win95RegCloseKey == NULL) || (_Win95RegQueryInfoKey == NULL) || (_Win95RegEnumKey == NULL) || (_Win95RegEnumValue == NULL) || (_Win95RegQueryValueEx == NULL) || (_Win95RegSetValueEx == NULL) || (_Win95RegDeleteValue == NULL) ) { FreeLibrary( hVMMREG32 ); SetLastError( ERROR_PROC_NOT_FOUND ); return FALSE; } // // Map HKEY_LOCAL_MACHINE of Win95 hive // RegUnicodeToAnsi( Win95Path, Buffer, 0 ); strcat( Buffer, "\\system.dat" ); Error = (_Win95RegMapPredefKeyToFile)( HKEY_LOCAL_MACHINE, Buffer, 0 ); if (Error == NO_ERROR) { RegistryContext->MachineRoot = HKEY_LOCAL_MACHINE; RegUnicodeToAnsi( Win95Path, Buffer, 0 ); strcat( Buffer, "\\user.dat" ); Error = (_Win95RegMapPredefKeyToFile)( HKEY_USERS, Buffer, 0 ); if (Error == NO_ERROR) { RegistryContext->UsersRoot = HKEY_USERS; Error = (_Win95RegOpenKey)( HKEY_USERS, ".Default", &RegistryContext->CurrentUserRoot ); } } if (Error != NO_ERROR) { if (RegistryContext->MachineRoot != NULL) { (_Win95RegMapPredefKeyToFile)( RegistryContext->MachineRoot, NULL, 0 ); } if (RegistryContext->UsersRoot) { (_Win95RegMapPredefKeyToFile)( RegistryContext->UsersRoot, NULL, 0 ); } FreeLibrary( hVMMREG32 ); SetLastError( Error ); return FALSE; } wcscpy( RegistryContext->UsersPath, L"\\Registry\\Users" ); wcscpy( RegistryContext->CurrentUserPath, RegistryContext->UsersPath ); wcscat( RegistryContext->CurrentUserPath, L"\\.Default" ); return TRUE; } BOOLEAN PrivilegeEnabled; BOOLEAN RestoreWasEnabled; BOOLEAN BackupWasEnabled; BOOLEAN RTEnableBackupRestorePrivilege( void ) { NTSTATUS Status; // // Try to enable backup and restore privileges // Status = RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, TRUE, // Enable FALSE, // Not impersonating &RestoreWasEnabled // previous state ); if (!NT_SUCCESS( Status )) { return FALSE; } Status = RtlAdjustPrivilege( SE_BACKUP_PRIVILEGE, TRUE, // Enable FALSE, // Not impersonating &BackupWasEnabled // previous state ); if (!NT_SUCCESS( Status )) { return FALSE; } PrivilegeEnabled = TRUE; return TRUE; } void RTDisableBackupRestorePrivilege( void ) { // // Restore privileges to what they were // RtlAdjustPrivilege( SE_RESTORE_PRIVILEGE, RestoreWasEnabled, FALSE, &RestoreWasEnabled ); RtlAdjustPrivilege( SE_BACKUP_PRIVILEGE, BackupWasEnabled, FALSE, &BackupWasEnabled ); PrivilegeEnabled = FALSE; return; } BOOLEAN RTInitialize( void ) /*++ Routine Description: DLL initialization function. Arguments: hInstance - Instance handle Reason - Reason for the entrypoint being called Context - Context record Return Value: TRUE - Initialization succeeded FALSE - Initialization failed --*/ { ValueBuffer = VirtualAlloc( NULL, ValueBufferSize, MEM_COMMIT, PAGE_READWRITE ); if (ValueBuffer == NULL) { return FALSE; } if (!RegInitializeSecurity()) { return FALSE; } return TRUE; } LONG RTConnectToRegistry( IN PWSTR MachineName, IN PWSTR HiveFileName, IN PWSTR HiveRootName, IN PWSTR Win95Path, IN PWSTR Win95UserName, OUT PWSTR *DefaultRootKeyName, OUT PREG_CONTEXT RegistryContext ) { LONG Error; if (MachineName != NULL) { if (HiveRootName || HiveFileName || Win95Path || Win95UserName) { return ERROR_INVALID_PARAMETER; } Error = RegConnectRegistry( MachineName, HKEY_LOCAL_MACHINE, (PHKEY)&RegistryContext->MachineRoot ); if (Error == NO_ERROR) { Error = RegConnectRegistry( MachineName, HKEY_USERS, (PHKEY)&RegistryContext->UsersRoot ); if (Error == NO_ERROR) { Error = RegOpenKey( RegistryContext->UsersRoot, L".Default", &RegistryContext->CurrentUserRoot ); } } if (Error != NO_ERROR) { if (RegistryContext->MachineRoot != NULL) { RegCloseKey( RegistryContext->MachineRoot ); RegistryContext->MachineRoot = NULL; } if (RegistryContext->UsersRoot != NULL) { RegCloseKey( RegistryContext->UsersRoot ); RegistryContext->UsersRoot = NULL; } return Error; } wcscpy( RegistryContext->MachinePath, L"\\Registry\\Machine" ); wcscpy( RegistryContext->UsersPath, L"\\Registry\\Users" ); wcscpy( RegistryContext->CurrentUserPath, L"\\Registry\\Users\\.Default" ); RegistryContext->Target = REG_TARGET_REMOTE_REGISTRY; } else if (HiveRootName != NULL || HiveFileName != NULL) { if (HiveRootName == NULL || HiveFileName == NULL || Win95Path != NULL || Win95UserName != NULL ) { return ERROR_INVALID_PARAMETER; } if (!PrivilegeEnabled && !RTEnableBackupRestorePrivilege()) { return ERROR_PRIVILEGE_NOT_HELD; } RegistryContext->MachineRoot = NULL; RegistryContext->UsersRoot = NULL; RegistryContext->CurrentUserRoot = NULL; Error = RegLoadHive( RegistryContext, HiveFileName, HiveRootName ); if (Error != NO_ERROR) { return Error; } if (DefaultRootKeyName != NULL && *DefaultRootKeyName == NULL) { *DefaultRootKeyName = HiveRootName; } RegistryContext->Target = REG_TARGET_HIVE_REGISTRY; } else if (Win95Path != NULL || Win95UserName != NULL) { if (!RegInitWin95RegistryAccess( RegistryContext, Win95Path, Win95UserName ) ) { return GetLastError(); } RegistryContext->Target = REG_TARGET_WIN95_REGISTRY; } else { NTSTATUS Status; UNICODE_STRING CurrentUserKeyPath; RegistryContext->MachineRoot = HKEY_LOCAL_MACHINE; RegistryContext->UsersRoot = HKEY_USERS; RegistryContext->CurrentUserRoot = HKEY_CURRENT_USER; wcscpy( RegistryContext->MachinePath, L"\\Registry\\Machine" ); wcscpy( RegistryContext->UsersPath, L"\\Registry\\Users" ); Status = RtlFormatCurrentUserKeyPath( &CurrentUserKeyPath ); if (!NT_SUCCESS( Status )) { SetLastError( RtlNtStatusToDosError( Status ) ); return FALSE; } wcscpy( RegistryContext->CurrentUserPath, CurrentUserKeyPath.Buffer ); RtlFreeUnicodeString( &CurrentUserKeyPath ); RegistryContext->Target = REG_TARGET_LOCAL_REGISTRY; } if (DefaultRootKeyName != NULL && *DefaultRootKeyName == NULL) { *DefaultRootKeyName = L"\\Registry"; } RegistryContext->MachinePathLength = wcslen( RegistryContext->MachinePath ); RegistryContext->UsersPathLength = wcslen( RegistryContext->UsersPath ); RegistryContext->CurrentUserPathLength = wcslen( RegistryContext->CurrentUserPath ); return NO_ERROR; } LONG RTDisconnectFromRegistry( IN PREG_CONTEXT RegistryContext ) { switch( RegistryContext->Target ) { case REG_TARGET_DISCONNECTED: break; case REG_TARGET_LOCAL_REGISTRY: break; case REG_TARGET_REMOTE_REGISTRY: break; case REG_TARGET_WIN95_REGISTRY: // (_Win95RegMapPredefKeyToFile)( RegistryContext->MachineRoot, NULL, 0 ); // (_Win95RegMapPredefKeyToFile)( RegistryContext->UsersRoot, NULL, 0 ); (_Win95RegCloseKey)( RegistryContext->CurrentUserRoot ); FreeLibrary( hVMMREG32 ); break; case REG_TARGET_HIVE_REGISTRY: RegUnloadHive( RegistryContext ); break; } if (PrivilegeEnabled) { RTDisableBackupRestorePrivilege(); } RegistryContext->Target = REG_TARGET_DISCONNECTED; return NO_ERROR; } UNICODE_STRING RegHiveRootName; LONG RegLoadHive( IN PREG_CONTEXT RegistryContext, IN PWSTR HiveFileName, IN PWSTR HiveRootName ) { NTSTATUS Status; UNICODE_STRING NtFileName; OBJECT_ATTRIBUTES File; SECURITY_DESCRIPTOR SecurityDescriptor; // // Create security descriptor with a NULL Dacl. This is necessary // because the security descriptor we pass in gets used in system // context. So if we just pass in NULL, then the Wrong Thing happens. // (but only on NTFS!) // Status = RtlCreateSecurityDescriptor( &SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); if (!NT_SUCCESS( Status )) { return RtlNtStatusToDosError( Status ); } Status = RtlSetDaclSecurityDescriptor( &SecurityDescriptor, TRUE, // Dacl present NULL, // but grants all access FALSE ); if (!NT_SUCCESS( Status )) { return RtlNtStatusToDosError( Status ); } if (!RtlDosPathNameToNtPathName_U( HiveFileName, &NtFileName, NULL, NULL ) ) { return ERROR_BAD_PATHNAME; } InitializeObjectAttributes( &File, &NtFileName, OBJ_CASE_INSENSITIVE, NULL, &SecurityDescriptor ); RtlInitUnicodeString( &RegHiveRootName, L"\\Registry"); InitializeObjectAttributes( &RegistryContext->HiveRootKey, &RegHiveRootName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenKey( &RegistryContext->HiveRootHandle, MAXIMUM_ALLOWED, &RegistryContext->HiveRootKey ); if (!NT_SUCCESS(Status)) { return RtlNtStatusToDosError( Status ); } RtlInitUnicodeString( &RegHiveRootName, HiveRootName ); InitializeObjectAttributes( &RegistryContext->HiveRootKey, &RegHiveRootName, OBJ_CASE_INSENSITIVE, RegistryContext->HiveRootHandle, NULL ); NtUnloadKey( &RegistryContext->HiveRootKey ); Status = NtLoadKey( &RegistryContext->HiveRootKey, &File ); if (!NT_SUCCESS( Status )) { return RtlNtStatusToDosError( Status ); } return NO_ERROR; } void RegUnloadHive( IN PREG_CONTEXT RegistryContext ) { NTSTATUS Status; HANDLE Handle; PREG_CONTEXT_OPEN_HIVE_KEY p, p1; Status = NtOpenKey( &Handle, MAXIMUM_ALLOWED, &RegistryContext->HiveRootKey ); if (NT_SUCCESS( Status )) { NtFlushKey( Handle ); NtClose( Handle ); } p = RegistryContext->OpenHiveKeys; while (p) { RegCloseKey( p->KeyHandle ); p1 = p; p = p->Next; HeapFree( GetProcessHeap(), 0, p1 ); }; do { Status = NtUnloadKey( &RegistryContext->HiveRootKey ); } while (NT_SUCCESS( Status ) ); NtClose( RegistryContext->HiveRootHandle ); return; } void RegRememberOpenKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle ) { PREG_CONTEXT_OPEN_HIVE_KEY p, *pp; pp = &RegistryContext->OpenHiveKeys; while ((p = *pp) != NULL) { if (p->KeyHandle == KeyHandle) { p->ReferenceCount += 1; return; } else { pp = &p->Next; } } p = HeapAlloc( GetProcessHeap(), 0, sizeof( *p ) ); if (p != NULL) { p->KeyHandle = KeyHandle; p->ReferenceCount = 1; p->Next = NULL; *pp = p; } return; } void RegForgetOpenKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle ) { PREG_CONTEXT_OPEN_HIVE_KEY p, *pp; pp = &RegistryContext->OpenHiveKeys; while ((p = *pp) != NULL) { if (p->KeyHandle == KeyHandle) { p->ReferenceCount -= 1; if (p->ReferenceCount == 0) { *pp = p->Next; HeapFree( GetProcessHeap(), 0, p ); return; } } else { pp = &p->Next; } } return; } BOOLEAN RegCheckPrefix( IN OUT PCWSTR *s, IN PCWSTR Prefix, IN ULONG PrefixLength ) { if (PrefixLength == 0) { return FALSE; } if (!_wcsnicmp( *s, Prefix, PrefixLength )) { *s += PrefixLength; return TRUE; } return FALSE; } BOOLEAN RegValidateKeyPath( IN PREG_CONTEXT RegistryContext, IN OUT PHKEY RootKeyHandle, IN OUT PCWSTR *SubKeyName ) { PCWSTR s; s = *SubKeyName; if (*RootKeyHandle == NULL) { if (RegCheckPrefix( &s, L"USER:", 5 ) || RegCheckPrefix( &s, L"HKEY_CURRENT_USER", 17 ) ) { if (RegistryContext->CurrentUserRoot == NULL) { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } if (*s == L'\\') { s += 1; } else if (s[-1] != L':' && *s != UNICODE_NULL) { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } *RootKeyHandle = RegistryContext->CurrentUserRoot; } else if (RegCheckPrefix( &s, L"HKEY_LOCAL_MACHINE", 18 )) { if (*s == L'\\') { s += 1; } else if (*s != UNICODE_NULL) { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } *RootKeyHandle = RegistryContext->MachineRoot; } else if (RegCheckPrefix( &s, L"HKEY_USERS", 10 )) { if (*s == L'\\') { s += 1; } else if (*s != UNICODE_NULL) { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } *RootKeyHandle = RegistryContext->UsersRoot; } else if (*s != L'\\') { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } else if (RegCheckPrefix( &s, RegistryContext->MachinePath, RegistryContext->MachinePathLength )) { *RootKeyHandle = RegistryContext->MachineRoot; if (*s == L'\\') { s += 1; } } else if (RegCheckPrefix( &s, RegistryContext->UsersPath, RegistryContext->UsersPathLength )) { *RootKeyHandle = RegistryContext->UsersRoot; if (*s == L'\\') { s += 1; } } else if (RegCheckPrefix( &s, RegistryContext->CurrentUserPath, RegistryContext->CurrentUserPathLength )) { *RootKeyHandle = RegistryContext->CurrentUserRoot; if (*s == L'\\') { s += 1; } } else if (!_wcsicmp( *SubKeyName, L"\\Registry" )) { *RootKeyHandle = NULL; } else { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } } else if (*s == L'\\') { SetLastError( ERROR_BAD_PATHNAME ); return FALSE; } *SubKeyName = s; return TRUE; } LONG RTCreateKey( IN PREG_CONTEXT RegistryContext, IN HKEY RootKeyHandle, IN PCWSTR SubKeyName, IN ACCESS_MASK DesiredAccess, IN ULONG CreateOptions, IN PVOID SecurityDescriptor, OUT PHKEY ReturnedKeyHandle, OUT PULONG Disposition ) { LONG Error; if (!RegValidateKeyPath( RegistryContext, &RootKeyHandle, &SubKeyName )) { return GetLastError(); } if (RootKeyHandle == NULL) { *Disposition = REG_OPENED_EXISTING_KEY; *ReturnedKeyHandle = HKEY_REGISTRY_ROOT; return NO_ERROR; } else if (RootKeyHandle == HKEY_REGISTRY_ROOT) { *ReturnedKeyHandle = NULL; if (!_wcsicmp( SubKeyName, L"Machine" )) { *ReturnedKeyHandle = RegistryContext->MachineRoot; } else if (!_wcsicmp( SubKeyName, L"Users" )) { *ReturnedKeyHandle = RegistryContext->UsersRoot; } if (*ReturnedKeyHandle != NULL) { return NO_ERROR; } else { return ERROR_PATH_NOT_FOUND; } } if (RegistryContext->Target == REG_TARGET_WIN95_REGISTRY) { UCHAR AnsiSubKeyName[ MAX_PATH ], *p; if (SubKeyName != NULL) { if (!RegUnicodeToAnsi( SubKeyName, AnsiSubKeyName, 0 )) { return GetLastError(); } p = AnsiSubKeyName; } else { p = NULL; } Error = (_Win95RegOpenKey)( RootKeyHandle, p, ReturnedKeyHandle ); if (Error == NO_ERROR) { *Disposition = REG_OPENED_EXISTING_KEY; } else { Error = (_Win95RegCreateKey)( RootKeyHandle, p, ReturnedKeyHandle ); if (Error == NO_ERROR) { *Disposition = REG_CREATED_NEW_KEY; } } } else { SECURITY_ATTRIBUTES SecurityAttributes; SecurityAttributes.nLength = sizeof( SecurityAttributes ); SecurityAttributes.lpSecurityDescriptor = SecurityDescriptor; SecurityAttributes.bInheritHandle = FALSE; Error = RegCreateKeyEx( RootKeyHandle, SubKeyName, 0, NULL, CreateOptions, (REGSAM)DesiredAccess, &SecurityAttributes, ReturnedKeyHandle, Disposition ); if (Error == NO_ERROR && RegistryContext->Target == REG_TARGET_HIVE_REGISTRY ) { RegRememberOpenKey( RegistryContext, *ReturnedKeyHandle ); } if (Error == NO_ERROR && *Disposition == REG_OPENED_EXISTING_KEY && SecurityDescriptor != NULL ) { RegSetKeySecurity( *ReturnedKeyHandle, DACL_SECURITY_INFORMATION, SecurityDescriptor ); } } return Error; } LONG RTOpenKey( IN PREG_CONTEXT RegistryContext, IN HKEY RootKeyHandle, IN PCWSTR SubKeyName, IN ACCESS_MASK DesiredAccess, IN ULONG OpenOptions, OUT PHKEY ReturnedKeyHandle ) { LONG Error; if (!RegValidateKeyPath( RegistryContext, &RootKeyHandle, &SubKeyName )) { return GetLastError(); } if (RootKeyHandle == NULL) { *ReturnedKeyHandle = HKEY_REGISTRY_ROOT; return NO_ERROR; } else if (RootKeyHandle == HKEY_REGISTRY_ROOT) { *ReturnedKeyHandle = NULL; if (!_wcsicmp( SubKeyName, L"Machine" )) { *ReturnedKeyHandle = RegistryContext->MachineRoot; } else if (!_wcsicmp( SubKeyName, L"Users" )) { *ReturnedKeyHandle = RegistryContext->UsersRoot; } if (*ReturnedKeyHandle != NULL) { return NO_ERROR; } else { return ERROR_PATH_NOT_FOUND; } } if (RegistryContext->Target == REG_TARGET_WIN95_REGISTRY) { UCHAR AnsiSubKeyName[ MAX_PATH ], *p; if (SubKeyName != NULL) { if (!RegUnicodeToAnsi( SubKeyName, AnsiSubKeyName, 0 )) { return GetLastError(); } p = AnsiSubKeyName; } else { p = NULL; } return (_Win95RegOpenKey)( RootKeyHandle, p, ReturnedKeyHandle ); } else { Error = RegOpenKeyEx( RootKeyHandle, SubKeyName, OpenOptions, DesiredAccess, ReturnedKeyHandle ); if (Error == NO_ERROR && RegistryContext->Target == REG_TARGET_HIVE_REGISTRY ) { RegRememberOpenKey( RegistryContext, *ReturnedKeyHandle ); } return Error; } } LONG RTCloseKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle ) { LONG Error; if (KeyHandle == HKEY_REGISTRY_ROOT) { return NO_ERROR; } else if (RegistryContext->Target == REG_TARGET_WIN95_REGISTRY) { return (_Win95RegCloseKey)( KeyHandle ); } else { Error = RegCloseKey( KeyHandle ); if (Error == NO_ERROR && RegistryContext->Target == REG_TARGET_HIVE_REGISTRY ) { RegForgetOpenKey( RegistryContext, KeyHandle ); } return Error; } } LONG RTFlushKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle ) { if (KeyHandle == HKEY_REGISTRY_ROOT) { return NO_ERROR; } else if (RegistryContext->Target == REG_TARGET_WIN95_REGISTRY) { return (_Win95RegFlushKey)( KeyHandle ); } else { return RegFlushKey( KeyHandle ); } } LONG RTEnumerateKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle, IN ULONG Index, OUT PFILETIME LastWriteTime, IN OUT PULONG KeyNameLength, OUT PWSTR KeyName ) { ULONG Error; if (KeyHandle == HKEY_REGISTRY_ROOT) { if (Index == 0) { if (*KeyNameLength <= 7) { return ERROR_MORE_DATA; } else { wcscpy( KeyName, L"Machine" ); return NO_ERROR; } } else if (Index == 1) { if (*KeyNameLength <= 5) { return ERROR_MORE_DATA; } else { wcscpy( KeyName, L"Users" ); return NO_ERROR; } } else { return ERROR_NO_MORE_ITEMS; } } else if (RegistryContext->Target == REG_TARGET_WIN95_REGISTRY) { UCHAR AnsiKeyName[ MAX_PATH ]; ULONG AnsiKeyNameLength; AnsiKeyNameLength = sizeof( AnsiKeyName ); Error = _Win95RegEnumKey( KeyHandle, Index, AnsiKeyName, AnsiKeyNameLength ); if (Error == NO_ERROR) { if (strlen( AnsiKeyName ) >= *KeyNameLength) { return ERROR_MORE_DATA; } *KeyNameLength = RegAnsiToUnicode( AnsiKeyName, KeyName, AnsiKeyNameLength ); if (*KeyNameLength == 0) { return GetLastError(); } RtlZeroMemory( LastWriteTime, sizeof( *LastWriteTime ) ); } } else { Error = RegEnumKeyEx( KeyHandle, Index, KeyName, KeyNameLength, NULL, NULL, NULL, LastWriteTime ); } return Error; } LONG RTEnumerateValueKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle, IN ULONG Index, OUT PULONG ValueType, IN OUT PULONG ValueNameLength, OUT PWSTR ValueName, IN OUT PULONG ValueDataLength, OUT PVOID ValueData ) { ULONG Error; if (KeyHandle == HKEY_REGISTRY_ROOT) { return ERROR_NO_MORE_ITEMS; } else if (RegistryContext->Target == REG_TARGET_WIN95_REGISTRY) { UCHAR AnsiValueName[ MAX_PATH ]; ULONG AnsiValueNameLength; LPSTR AnsiValueData; ULONG OriginalValueDataLength; AnsiValueNameLength = sizeof( AnsiValueName ); OriginalValueDataLength = *ValueDataLength; Error = (_Win95RegEnumValue)( KeyHandle, Index, AnsiValueName, &AnsiValueNameLength, 0, ValueType, ValueData, ValueDataLength ); if (Error != NO_ERROR) { return Error; } if (AnsiValueNameLength >= *ValueNameLength) { return ERROR_MORE_DATA; } if (RegAnsiToUnicode( AnsiValueName, ValueName, AnsiValueNameLength ) == 0) { return GetLastError(); } if (*ValueType == REG_SZ) { AnsiValueData = HeapAlloc( GetProcessHeap(), 0, *ValueDataLength ); if (AnsiValueData == NULL) { return ERROR_OUTOFMEMORY; } RtlMoveMemory( AnsiValueData, ValueData, *ValueDataLength ); if (RegAnsiToUnicode( AnsiValueData, (PWSTR)ValueData, *ValueDataLength ) == 0) { Error = GetLastError(); } else { *ValueDataLength *= sizeof( WCHAR ); } HeapFree( GetProcessHeap(), 0, AnsiValueData ); } return Error; } else { Error = RegEnumValue( KeyHandle, Index, ValueName, ValueNameLength, NULL, ValueType, ValueData, ValueDataLength ); if (Error == NO_ERROR) { RtlZeroMemory( (PCHAR)ValueData + *ValueDataLength, 4 - (*ValueDataLength & 3) ); } return Error; } } LONG RTQueryKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle, OUT PFILETIME LastWriteTime, OUT PULONG NumberOfSubkeys, OUT PULONG NumberOfValues ) { LONG Error; if (KeyHandle == HKEY_REGISTRY_ROOT) { if (NumberOfSubkeys != NULL) { *NumberOfSubkeys = 2; } if (NumberOfValues != NULL) { *NumberOfValues = 0; } return NO_ERROR; } else if (RegistryContext->Target == REG_TARGET_WIN95_REGISTRY) { Error = (_Win95RegQueryInfoKey)( KeyHandle, NULL, NULL, NULL, NumberOfSubkeys, NULL, NULL, NumberOfValues, NULL, NULL, NULL, (PVOID)LastWriteTime ); } else { Error = RegQueryInfoKey( KeyHandle, // hKey, NULL, // lpClass, NULL, // lpcbClass, NULL, // lpReserved, NumberOfSubkeys, // lpcSubKeys, NULL, // lpcbMaxSubKeyLen, NULL, // lpcbMaxClassLen, NumberOfValues, // lpcValues, NULL, // lpcbMaxValueNameLen, NULL, // lpcbMaxValueLen, NULL, // lpcbSecurityDescriptor, LastWriteTime // lpftLastWriteTime ); } return Error; } LONG RTQueryValueKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle, IN PWSTR ValueName, OUT PULONG ValueType, IN OUT PULONG ValueDataLength, OUT PVOID ValueData ) { LONG Error; if (RegistryContext->Target == REG_TARGET_WIN95_REGISTRY) { UCHAR AnsiValueName[ MAX_PATH ], *p; ULONG OriginalValueDataLength; if (ValueName != NULL) { if (!RegUnicodeToAnsi( ValueName, AnsiValueName, 0 )) { return GetLastError(); } p = AnsiValueName; } else { p = NULL; } OriginalValueDataLength = *ValueDataLength; Error = (_Win95RegQueryValueEx)( KeyHandle, p, NULL, ValueType, ValueData, ValueDataLength ); if (Error == NO_ERROR && *ValueType == REG_SZ) { if ((*ValueDataLength * sizeof( WCHAR )) > OriginalValueDataLength) { return ERROR_MORE_DATA; } p = HeapAlloc( GetProcessHeap(), 0, *ValueDataLength ); if (p == NULL) { return ERROR_OUTOFMEMORY; } RtlMoveMemory( p, ValueData, *ValueDataLength ); if (RegAnsiToUnicode( (LPCSTR)p, (PWSTR)ValueData, *ValueDataLength ) == 0) { Error = GetLastError(); } else { *ValueDataLength *= sizeof( WCHAR ); *ValueDataLength += sizeof( UNICODE_NULL ); } HeapFree( GetProcessHeap(), 0, p ); } } else { Error = RegQueryValueEx( KeyHandle, ValueName, NULL, ValueType, ValueData, ValueDataLength ); if (Error == NO_ERROR) { RtlZeroMemory( (PCHAR)ValueData + *ValueDataLength, 4 - (*ValueDataLength & 3) ); } } return Error; } LONG RTSetValueKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle, IN PWSTR ValueName, IN ULONG ValueType, IN ULONG ValueDataLength, IN PVOID ValueData ) { LONG Error; if (RegistryContext->Target == REG_TARGET_WIN95_REGISTRY) { UCHAR AnsiValueName[ MAX_PATH ], *p; ULONG OriginalValueDataLength; LPSTR AnsiValueData; if (ValueName != NULL) { if (!RegUnicodeToAnsi( ValueName, AnsiValueName, 0 )) { return GetLastError(); } p = AnsiValueName; } else { p = NULL; } if (ValueType == REG_SZ) { AnsiValueData = HeapAlloc( GetProcessHeap(), 0, ValueDataLength * 2 ); if (AnsiValueData == NULL) { return ERROR_OUTOFMEMORY; } ValueDataLength = RegUnicodeToAnsi( ValueData, AnsiValueData, ValueDataLength ); if (ValueDataLength == 0) { return GetLastError(); } ValueData = AnsiValueData; } else { AnsiValueData = NULL; } Error = (_Win95RegSetValueEx)( KeyHandle, p, 0, ValueType, ValueData, ValueDataLength ); if (AnsiValueData != NULL) { HeapFree( GetProcessHeap(), 0, AnsiValueData ); } if (p != NULL) { HeapFree( GetProcessHeap(), 0, p ); } } else { Error = RegSetValueEx( KeyHandle, ValueName, 0, ValueType, ValueData, ValueDataLength ); } return Error; } LONG RTDeleteKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle, IN PCWSTR SubKeyName ) { if (!RegValidateKeyPath( RegistryContext, &KeyHandle, &SubKeyName )) { return GetLastError(); } if (RegistryContext->Target == REG_TARGET_WIN95_REGISTRY) { UCHAR AnsiSubKeyName[ MAX_PATH ], *p; if (SubKeyName != NULL) { if (!RegUnicodeToAnsi( SubKeyName, AnsiSubKeyName, 0 )) { return GetLastError(); } p = AnsiSubKeyName; } else { p = NULL; } return (_Win95RegDeleteKey)( KeyHandle, p ); } else { return RegDeleteKey( KeyHandle, SubKeyName ); } } LONG RTDeleteValueKey( IN PREG_CONTEXT RegistryContext, IN HKEY KeyHandle, IN PWSTR ValueName ) { if (RegistryContext->Target == REG_TARGET_WIN95_REGISTRY) { UCHAR AnsiValueName[ MAX_PATH ], *p; ULONG OriginalValueDataLength; LPSTR AnsiValueData; if (ValueName != NULL) { if (!RegUnicodeToAnsi( ValueName, AnsiValueName, 0 )) { return GetLastError(); } p = AnsiValueName; } else { p = NULL; } return (_Win95RegDeleteValue)( KeyHandle, p ); } else { return RegDeleteValue( KeyHandle, ValueName ); } } LONG RTLoadAsciiFileAsUnicode( IN PWSTR FileName, OUT PREG_UNICODE_FILE UnicodeFile ) { LONG Error = NO_ERROR; HANDLE File; DWORD FileSize; DWORD CharsInFile; DWORD BytesRead; DWORD BufferSize, i, i1, LineCount, DeferredLineCount; PVOID BufferBase; PWSTR Src, Src1, Dst; File = CreateFile( FileName, FILE_GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (File == INVALID_HANDLE_VALUE) { return GetLastError(); } FileSize = GetFileSize( File, NULL ); if (FileSize == INVALID_FILE_SIZE) { CloseHandle( File ); return GetLastError(); } BufferSize = FileSize * sizeof( WCHAR ); BufferSize += sizeof( UNICODE_NULL ); BufferBase = NULL; BufferBase = VirtualAlloc( NULL, BufferSize, MEM_COMMIT, PAGE_READWRITE ); if (BufferBase != NULL) { if (ReadFile( File, BufferBase, FileSize, &BytesRead, NULL )) { if (BytesRead != FileSize) { Error = ERROR_HANDLE_EOF; } else if (!GetFileTime( File, NULL, NULL, &UnicodeFile->LastWriteTime )) { Error = GetLastError(); } else { Error = NO_ERROR; } } if (Error != NO_ERROR) { VirtualFree( BufferBase, 0, MEM_RELEASE ); } } else { Error = GetLastError(); } CloseHandle( File ); if (Error != NO_ERROR) { return Error; } Src = (PWSTR)BufferBase; if (!IsTextUnicode( BufferBase, FileSize, NULL )) { RtlMoveMemory( (PCHAR)BufferBase + FileSize, BufferBase, FileSize ); CharsInFile = RegAnsiToUnicode( (PCHAR)BufferBase + FileSize, BufferBase, FileSize ); if (CharsInFile == 0) { return GetLastError(); } } else { CharsInFile = FileSize / sizeof( WCHAR ); // // Skip ByteOrderMark // if (Src[0] == 0xfeff || Src[0] == 0xfffe) { Src++; CharsInFile--; } } DeferredLineCount = 0; Dst = (PWSTR)BufferBase; i = 0; // // Now loop over the in memory copy of the file, collapsing all carriage // return line feed pairs into just new lines, and removing all line // continuation characters and the spaces that surround them. This lets // RTParseNextLine see a single line for each Key Name or Value input, // terminated by a new line character. // while (i < CharsInFile) { // // See if we just went over a line continuation character // if (i > 0 && Src[-1] == L'\\' && (*Src == L'\r' || *Src == L'\n')) { // // Move back over the line continuation we just copied the previous iteration // if (Dst[-1] == L'\\') { --Dst; } // // Move back over all but one of any space characters that preceed // the line continuation character. The may be none, in which case // we leave it be, as the user must want no space // while (Dst > (PWSTR)BufferBase) { if (Dst[-1] > L' ') { break; } Dst -= 1; } // // Leave one space, if there is one // if (Dst[0] == L' ') { Dst += 1; } // // Now, skip over the new line after the line continuation. We // actually will skip over any number of them, keeping count so // we can update the source file line number correctly. // LineCount = 0; while (i < CharsInFile) { if (*Src == L'\n') { i++; Src++; LineCount++; } else if (*Src == L'\r' && (i+1) < CharsInFile && Src[ 1 ] == L'\n' ) { i += 2; Src += 2; LineCount++; } else { break; } } // // If we saw more than just new line after the line continuation // character, then put them back into the destination as just // new lines, without any carriage returns. // if (LineCount > 1) { DeferredLineCount += LineCount; while (DeferredLineCount) { DeferredLineCount -= 1; *Dst++ = L'\n'; } } else { DeferredLineCount += 1; // // Skip leading spaces of next line of continuation while (i < CharsInFile && (*Src == L' ' || *Src == L'\t')) { i++; Src++; } } // // All done if we hit the end of the file // if (i >= CharsInFile) { break; } } else if ((*Src == '\r' && Src[1] == '\n') || *Src == '\n') { while (TRUE) { while (i < CharsInFile && (*Src == '\r' || *Src == '\n')) { i++; Src++; } Src1 = Src; i1 = i; while (i1 < CharsInFile && (*Src1 == ' ' || *Src1 == '\t')) { i1++; Src1++; } if (i1 < CharsInFile && (*Src1 == '\r' && Src1[1] == '\n') || *Src1 == '\n' ) { Src = Src1; i = i1; } else { break; } } while (DeferredLineCount) { DeferredLineCount -= 1; *Dst++ = L'\n'; } *Dst++ = L'\n'; } else { i++; *Dst++ = *Src++; } } // // Make sure line ends with a CRLF sequence. // while (DeferredLineCount) { DeferredLineCount -= 1; *Dst++ = L'\n'; } *Dst++ = L'\n'; *Dst = UNICODE_NULL; UnicodeFile->FileName = FileName; UnicodeFile->FileContents = BufferBase; UnicodeFile->EndOfFile = Dst; UnicodeFile->NextLine = BufferBase; UnicodeFile->NextLineNumber = 1; return NO_ERROR; } void RTUnloadUnicodeFile( IN OUT PREG_UNICODE_FILE UnicodeFile ) { VirtualFree( UnicodeFile->FileContents, 0, MEM_RELEASE ); return; } #define ACL_LIST_START L'[' #define ACL_LIST_END L']' BOOLEAN RegGetMultiString( IN BOOLEAN BackwardsCompatibleInput, IN OUT PWSTR *ValueString, IN OUT PWSTR *ValueData, IN ULONG MaximumValueLength, IN OUT PULONG ValueLength ); BOOLEAN RegReadMultiSzFile( IN OUT PREG_UNICODE_PARSE ParsedLine, IN BOOLEAN BackwardsCompatibleInput, IN PWSTR FileName, IN OUT PVOID ValueData, IN OUT PULONG ValueLength ); BOOLEAN RegReadBinaryFile( IN OUT PREG_UNICODE_PARSE ParsedLine, IN PWSTR FileName, IN OUT PVOID ValueData, IN OUT PULONG ValueLength ); BOOLEAN RTParseNextLine( IN OUT PREG_UNICODE_FILE UnicodeFile, OUT PREG_UNICODE_PARSE ParsedLine ) { PWSTR BeginLine, EqualSign, AclBracket, AclStart, s, s1; WCHAR QuoteChar; if (ParsedLine->IsKeyName && ParsedLine->SecurityDescriptor) { RegDestroySecurity( ParsedLine->SecurityDescriptor ); } RtlZeroMemory( ParsedLine, sizeof( *ParsedLine ) ); while (TRUE) { if (!(s = UnicodeFile->NextLine)) { ParsedLine->AtEndOfFile = TRUE; return FALSE; } UnicodeFile->NextLine = NULL; if (*s == UNICODE_NULL) { ParsedLine->AtEndOfFile = TRUE; return FALSE; } while (*s <= L' ') { if (*s == L' ') { ParsedLine->IndentAmount += 1; } else if (*s == L'\t') { ParsedLine->IndentAmount = ((ParsedLine->IndentAmount + 8) - (ParsedLine->IndentAmount % 8) ); } if (++s >= UnicodeFile->EndOfFile) { ParsedLine->AtEndOfFile = TRUE; return FALSE; } } BeginLine = s; EqualSign = NULL; AclBracket = NULL; if (!UnicodeFile->BackwardsCompatibleInput && *s == L';') { while (s < UnicodeFile->EndOfFile) { if (*s == L'\n') { do { UnicodeFile->NextLineNumber += 1; *s++ = UNICODE_NULL; } while (*s == L'\n'); break; } else { s += 1; } } BeginLine = s; UnicodeFile->NextLine = s; } else if (*s != '\n') { // // If not being backward compatible, see if the first thing on // the line is the beginning of a quoted string. // if (!UnicodeFile->BackwardsCompatibleInput && (*s == L'"' || *s == L'\'')) { // // Yes, it is either a quoted key name or value name. Find the // the trailing quote. Specifically do NOT support quotes inside // a quoted string, other than a different kind. Which means unless // you want both types of quoted characters within the same name // you wont care. // QuoteChar = *s++; BeginLine += 1; while (s < UnicodeFile->EndOfFile && *s != QuoteChar) { s += 1; } // // If trailing quote not found, then return an error // if (*s != QuoteChar) { ParsedLine->ParseFailureReason = ParseFailInvalidQuoteCharacter; return FALSE; } // // Mark the end of the name and move past the trailing quote // *s++ = UNICODE_NULL; } // // Now scan forward looking for one of the following: // // equal sign - this would mean the stuff to the left // of the equal sign is a value name and the stuff // to the right is the value type and data. // // left square bracket - this would mean the stuff to the // left of the square bracket is a key name and the // stuff to the right is the security descriptor information // // end of line - this would mean the stuff to the left // is a key name, with no security descriptor. // while (s < UnicodeFile->EndOfFile) { if (*s == L'=') { // // We found an equal sign, so value name is to the left // and value type and data follows. // EqualSign = s; // // Ignore any left square bracket we might have seen // in before this. It must have been part of the value // name. AclBracket = NULL; // // All done scanning // break; } else if (*s == ACL_LIST_START) { // // We found a left square bracket. Keep scanning // in case there is an equal sign later. // AclBracket = s; s += 1; } else if (*s == L'\n') { // // We found end of line, so key name is to the left. // Update where to start next time we are called. // UnicodeFile->NextLine = s + 1; break; } else if (*s == L'\t') { // // Convert imbedded hard tabs to single spaces // *s++ = L' '; } else { // // Nothing interesting, keep looking. // s += 1; } } // // Trim any trailing spaces off the end of what is to the // left of where we are. The make sure we stop looking // if we see the null character put down over the trailing // quote character above, if any. // *s = UNICODE_NULL; while (s > BeginLine && *--s <= L' ' && *s) { *s = UNICODE_NULL; } // // BeginLine now points to either the null terminated value // name or key name. EqualSign, if non-null, points to the // equal sign, so scan forward and find the terminating new line, // and store a null there to terminate the input. Otherwise, // we already stored a null over the terminating new line above. // if (EqualSign != NULL) { s = EqualSign + 1; while (s < UnicodeFile->EndOfFile) { if (*s == '\n') { *s = UNICODE_NULL; break; } s += 1; } // // Update where we should start next time we are called. // UnicodeFile->NextLine = s + 1; } else if (AclBracket != NULL) { // // Since we did not stop on the AclBracket, go back an // clobber it and any spaces before it. // s = AclBracket; *s = UNICODE_NULL; while (s > BeginLine && *--s <= L' ' && *s) { *s = UNICODE_NULL; } } // // Tell them which line number and where the line begins // ParsedLine->LineNumber = UnicodeFile->NextLineNumber; UnicodeFile->NextLineNumber += 1; ParsedLine->BeginLine = BeginLine; // // Now handle value or key semantics // if (EqualSign != NULL) { // // We have ValueName = ValueType ValueData // // // Value name is the beginning of the line, unless // it was the special symbol or null // if (*BeginLine != L'@' && BeginLine != EqualSign) { ParsedLine->ValueName = BeginLine; } // // Skip any blanks after the equal sign. // while (*++EqualSign && *EqualSign <= L' ') { } // // If all that is left is the DELETE keyword, then // tell the caller // if (!_wcsicmp( L"DELETE", EqualSign )) { ParsedLine->DeleteValue = TRUE; return TRUE; } else { // // Otherwise parse the data after the equal sign. // ParsedLine->ValueString = EqualSign; return RTParseValueData( UnicodeFile, ParsedLine, ValueBuffer, ValueBufferSize, &ParsedLine->ValueType, &ParsedLine->ValueData, &ParsedLine->ValueLength ); } } else { // // We have a key name. Tell the caller and handle any // security descriptor info if present. // ParsedLine->IsKeyName = TRUE; ParsedLine->KeyName = BeginLine; if (AclBracket != NULL) { // // We have found an ACL name // AclStart = ++AclBracket; ParsedLine->AclString = AclStart; while (*AclBracket != UNICODE_NULL && *AclBracket != ACL_LIST_END) { AclBracket += 1; } if (*AclBracket != ACL_LIST_END) { return FALSE; } *AclBracket = UNICODE_NULL; if (!_wcsicmp( L"DELETE", AclStart )) { ParsedLine->DeleteKey = TRUE; } else { ParsedLine->SecurityDescriptor = &ParsedLine->SecurityDescriptorBuffer; if (!RegCreateSecurity( AclStart, ParsedLine->SecurityDescriptor )) { ParsedLine->SecurityDescriptor = NULL; return FALSE; } } } return TRUE; } } else { UnicodeFile->NextLineNumber += 1; } } return FALSE; } BOOLEAN RTParseValueData( IN OUT PREG_UNICODE_FILE UnicodeFile, IN OUT PREG_UNICODE_PARSE ParsedLine, IN PVOID ValueBuffer, IN ULONG ValueBufferSize, OUT PULONG ValueType, OUT PVOID *ValueData, OUT PULONG ValueLength ) { PWSTR ValueString; ULONG PrefixLength, MaximumValueLength; PULONG p; PWSTR s, Src, Dst; ULONG i, n, cchValue; BOOLEAN BackwardsCompatibleInput = FALSE; BOOLEAN GetDataFromBinaryFile = FALSE; BOOLEAN GetDataFromMultiSzFile = FALSE; BOOLEAN ParseDateTime = FALSE; if (UnicodeFile != NULL) { BackwardsCompatibleInput = UnicodeFile->BackwardsCompatibleInput; } ValueString = ParsedLine->ValueString; *ValueData = NULL; *ValueLength = 0; *ValueType = REG_SZ; for (i=0; RegTypeNameTable[i].TypeName != NULL; i++) { PrefixLength = wcslen( RegTypeNameTable[i].TypeName ); if (ValueString[ PrefixLength ] <= L' ' && !_wcsnicmp( RegTypeNameTable[i].TypeName, ValueString, PrefixLength ) ) { *ValueType = RegTypeNameTable[i].ValueType; GetDataFromBinaryFile = RegTypeNameTable[i].GetDataFromBinaryFile; GetDataFromMultiSzFile = RegTypeNameTable[i].GetDataFromMultiSzFile; ParseDateTime = RegTypeNameTable[i].ParseDateTime; break; } } if (RegTypeNameTable[i].TypeName != NULL) { ValueString += PrefixLength; while (*ValueString != UNICODE_NULL && *ValueString <= L' ') { ValueString += 1; } } if (GetDataFromMultiSzFile) { *ValueData = ValueBuffer; *ValueLength = ValueBufferSize; return RegReadMultiSzFile( ParsedLine, BackwardsCompatibleInput, ValueString, ValueBuffer, ValueLength ); } if (GetDataFromBinaryFile) { *ValueData = ValueBuffer; *ValueLength = ValueBufferSize; return RegReadBinaryFile( ParsedLine, ValueString, ValueBuffer, ValueLength ); } cchValue = wcslen( ValueString ); Src = ValueString; switch( *ValueType ) { case REG_SZ: case REG_EXPAND_SZ: // // Strip off any surrounding quote characters // if (cchValue > 1 && Src[ 0 ] == Src[ cchValue - 1 ] && (Src[ 0 ] == L'"' || Src[ 0 ] == L'\'') ) { Src += 1; cchValue -= 2; } // // Fall through after stripping any quotes. // case REG_LINK: *ValueLength = (cchValue + 1) * sizeof( WCHAR ); if (*ValueLength > ValueBufferSize) { SetLastError( ERROR_BUFFER_OVERFLOW ); ParsedLine->ParseFailureReason = ParseFailValueTooLarge; return FALSE; } *ValueData = ValueBuffer; RtlMoveMemory( *ValueData, Src, *ValueLength ); *((PWSTR)*ValueData + cchValue) = UNICODE_NULL; return TRUE; case REG_DWORD: *ValueData = ValueBuffer; *ValueLength = sizeof( ULONG ); for (i=0; RegValueNameTable[i].ValueName != NULL; i++) { PrefixLength = wcslen( RegValueNameTable[i].ValueName ); if (!_wcsnicmp( RegValueNameTable[i].ValueName, ValueString, PrefixLength ) ) { *(PULONG)*ValueData = RegValueNameTable[i].Value; return TRUE; } } return RegUnicodeToDWORD( &Src, 0, (PULONG)*ValueData ); case REG_BINARY: if (ParseDateTime) { #define NUMBER_DATE_TIME_FIELDS 6 ULONG FieldIndexes[ NUMBER_DATE_TIME_FIELDS ] = {1, 2, 0, 3, 4, 7}; // // Month/Day/Year HH:MM DayOfWeek // ULONG CurrentField = 0; PCSHORT Fields; TIME_FIELDS DateTimeFields; PWSTR Field; ULONG FieldValue; RtlZeroMemory( &DateTimeFields, sizeof( DateTimeFields ) ); Fields = &DateTimeFields.Year; while (cchValue) { if (CurrentField >= 7) { return( FALSE ); } while (cchValue && *Src == L' ') { cchValue--; Src += 1; } Field = Src; while (cchValue) { if (CurrentField == (NUMBER_DATE_TIME_FIELDS-1)) { } else if (*Src < L'0' || *Src > L'9') { break; } cchValue--; Src += 1; } if (cchValue) { cchValue--; Src += 1; } if (CurrentField == (NUMBER_DATE_TIME_FIELDS-1)) { if (cchValue < 3) { SetLastError( ERROR_INVALID_PARAMETER ); ParsedLine->ParseFailureReason = ParseFailDateTimeFormatInvalid; return FALSE; } if (DateTimeFields.Year != 0) { SetLastError( ERROR_INVALID_PARAMETER ); ParsedLine->ParseFailureReason = ParseFailDateTimeFormatInvalid; return FALSE; } if (!_wcsnicmp( Field, L"SUN", 3 )) { FieldValue = 0; } else if (!_wcsnicmp( Field, L"MON", 3 )) { FieldValue = 1; } else if (!_wcsnicmp( Field, L"TUE", 3 )) { FieldValue = 2; } else if (!_wcsnicmp( Field, L"WED", 3 )) { FieldValue = 3; } else if (!_wcsnicmp( Field, L"THU", 3 )) { FieldValue = 4; } else if (!_wcsnicmp( Field, L"FRI", 3 )) { FieldValue = 5; } else if (!_wcsnicmp( Field, L"SAT", 3 )) { FieldValue = 6; } else { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } } else if (!RegUnicodeToDWORD( &Field, 0, &FieldValue )) { ParsedLine->ParseFailureReason = ParseFailDateTimeFormatInvalid; return FALSE; } Fields[ FieldIndexes[ CurrentField++ ] ] = (CSHORT)FieldValue; } if (DateTimeFields.Year == 0) { if (DateTimeFields.Day > 5) { SetLastError( ERROR_INVALID_PARAMETER ); ParsedLine->ParseFailureReason = ParseFailDateTimeFormatInvalid; return FALSE; } } else if (DateTimeFields.Year < 100) { DateTimeFields.Year += 1900; } *ValueLength = sizeof( DateTimeFields ); if (*ValueLength > ValueBufferSize) { SetLastError( ERROR_BUFFER_OVERFLOW ); ParsedLine->ParseFailureReason = ParseFailValueTooLarge; return FALSE; } *ValueData = ValueBuffer; RtlMoveMemory( *ValueData, &DateTimeFields, sizeof( DateTimeFields ) ); return TRUE; } case REG_RESOURCE_LIST: case REG_RESOURCE_REQUIREMENTS_LIST: case REG_FULL_RESOURCE_DESCRIPTOR: case REG_NONE: if (!RegUnicodeToDWORD( &Src, 0, ValueLength )) { ParsedLine->ParseFailureReason = ParseFailBinaryDataLengthMissing; return FALSE; } if (*ValueLength >= ValueBufferSize) { SetLastError( ERROR_BUFFER_OVERFLOW ); ParsedLine->ParseFailureReason = ParseFailValueTooLarge; return FALSE; } // // Calculate number of DWORD's of data based on specified byte count // n = (*ValueLength + sizeof( ULONG ) - 1) / sizeof( ULONG ); // // Store converted binary data in ValueBuffer // *ValueData = ValueBuffer; p = ValueBuffer; // // Src points to remaining text to convert. // while (n--) { if (!RegUnicodeToDWORD( &Src, 0, p )) { if (BackwardsCompatibleInput) { Src = UnicodeFile->NextLine; s = Src; while (TRUE) { if (*s == '\n') { *s = UNICODE_NULL; UnicodeFile->NextLineNumber += 1; break; } else if (s >= UnicodeFile->EndOfFile || *s == UNICODE_NULL) { UnicodeFile->NextLine = NULL; ParsedLine->ParseFailureReason = ParseFailBinaryDataNotEnough; SetLastError( ERROR_MORE_DATA ); return FALSE; } else { break; } } UnicodeFile->NextLine = s + 1; n += 1; } else { if (p == ValueBuffer) { ParsedLine->ParseFailureReason = ParseFailBinaryDataOmitted; SetLastError( ERROR_NO_DATA ); } else { ParsedLine->ParseFailureReason = ParseFailBinaryDataNotEnough; SetLastError( ERROR_MORE_DATA ); } return FALSE; } } else { p += 1; } } return TRUE; case REG_MULTI_SZ: *ValueLength = 0; *ValueData = ValueBuffer; MaximumValueLength = ValueBufferSize; Dst = *ValueData; while (RegGetMultiString( BackwardsCompatibleInput, &Src, &Dst, MaximumValueLength, ValueLength ) ) { } if (GetLastError() == NO_ERROR) { return TRUE; } else { ParsedLine->ParseFailureReason = ParseFailValueTooLarge; return FALSE; } break; default: SetLastError( ERROR_INVALID_PARAMETER ); ParsedLine->ParseFailureReason = ParseFailInvalidRegistryType; return FALSE; } } BOOLEAN RegGetMultiString( IN BOOLEAN BackwardsCompatibleInput, IN OUT PWSTR *ValueString, IN OUT PWSTR *ValueData, IN ULONG MaximumValueLength, IN OUT PULONG ValueLength ) /*++ Routine Description: This routine parses multi-strings of the form "foo" "bar" "bletch" Each time it is called, it strips the first string in quotes from the input string, and returns it as the multi-string. INPUT ValueString: "foo" "bar" "bletch" OUTPUT ValueString: "bar" "bletch" ValueData: foo Arguments: BackwardsCompatibleInput - TRUE if supporting old format input ValueString - Supplies the string from which the multi-string will be parsed - Returns the remaining string after the multi-string is removed ValueData - Supplies the location where the removed multi-string is to be stored. - Returns the location to the first byte after the returned multi-string MaximumValueLength - Supplies the maximum length of data that can be stored in ValueData. ValueLength - Supplies a pointer to the current length of data stored in ValueData. - Returns the size of the Return Value: TRUE if successful and FALSE if not. --*/ { PWSTR Src, Dst; ULONG n; BOOLEAN Result; // // Find the first quote mark. // Src = *ValueString; while (*Src != UNICODE_NULL && *Src != L'"') { Src += 1; } Dst = *ValueData; if (*Src == UNICODE_NULL) { SetLastError( NO_ERROR ); Result = FALSE; } else { // // We have found the start of the multi-string. Now find the end, // building up our return ValueData as we go. // Src += 1; while (*Src != UNICODE_NULL) { if (*Src == L'"') { if (!BackwardsCompatibleInput && Src[1] == L'"' ) { Src += 1; } else { *Src++ = UNICODE_NULL; break; } } *ValueLength += sizeof( WCHAR ); if (*ValueLength >= MaximumValueLength) { SetLastError( ERROR_BUFFER_OVERFLOW ); return FALSE; } *Dst++ = *Src++; } Result = TRUE; } *ValueLength += sizeof( WCHAR ); if (*ValueLength >= MaximumValueLength) { SetLastError( ERROR_BUFFER_OVERFLOW ); return FALSE; } *Dst++ = UNICODE_NULL; *ValueData = Dst; *ValueString = Src; return Result; } BOOLEAN RegReadMultiSzFile( IN OUT PREG_UNICODE_PARSE ParsedLine, IN BOOLEAN BackwardsCompatibleInput, IN PWSTR FileName, IN OUT PVOID ValueData, IN OUT PULONG ValueLength ) { PWSTR Src, Dst; REG_UNICODE_FILE MultiSzFile; ULONG MaximumValueLength; BOOLEAN Result; if (!RTLoadAsciiFileAsUnicode( FileName, &MultiSzFile )) { ParsedLine->ParseFailureReason = ParseFailUnableToAccessFile; return FALSE; } MaximumValueLength = *ValueLength; *ValueLength = 0; Src = MultiSzFile.NextLine; Dst = ValueData; while (RegGetMultiString( BackwardsCompatibleInput, &Src, &Dst, MaximumValueLength, ValueLength ) ) { } if (GetLastError() == NO_ERROR) { Result = TRUE; } else { ParsedLine->ParseFailureReason = ParseFailValueTooLarge; Result = FALSE; } RTUnloadUnicodeFile( &MultiSzFile ); return Result; } BOOLEAN RegReadBinaryFile( IN OUT PREG_UNICODE_PARSE ParsedLine, IN PWSTR FileName, IN OUT PVOID ValueData, IN OUT PULONG ValueLength ) { BOOLEAN Result; HANDLE File; DWORD FileSize, FileSizeHigh; DWORD BytesRead; File = CreateFile( FileName, FILE_GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (File == INVALID_HANDLE_VALUE) { ParsedLine->ParseFailureReason = ParseFailUnableToAccessFile; return FALSE; } ParsedLine->ParseFailureReason = ParseFailValueTooLarge; FileSize = GetFileSize( File, &FileSizeHigh ); if (FileSizeHigh != 0 || FileSize == INVALID_FILE_SIZE || FileSize >= *ValueLength ) { CloseHandle( File ); SetLastError( ERROR_BUFFER_OVERFLOW ); return FALSE; } Result = FALSE; if (ReadFile( File, ValueData, FileSize, &BytesRead, NULL )) { if (BytesRead != FileSize) { SetLastError( ERROR_HANDLE_EOF ); } else { ParsedLine->ParseFailureReason = ParseFailNoFailure; *ValueLength = FileSize; Result = TRUE; } } CloseHandle( File ); return Result; } BOOLEAN NeedQuotedString( PWSTR Name, PWSTR Value, PWCHAR QuoteChar ) { ULONG i; if (Name != NULL) { if (*Name != UNICODE_NULL && (*Name == L' ' || Name[ wcslen( Name ) - 1 ] == L' ') ) { *QuoteChar = '"'; return TRUE; } else { return FALSE; } } else { i = wcslen( Value ) - 1; if (*Value != UNICODE_NULL) { if ((*Value == L' ' || Value[ i ] == L' ' || Value[ i ] == L'\\')) { *QuoteChar = '"'; return TRUE; } else if (*Value == L'"' && Value[ i ] == L'"') { *QuoteChar = '\''; return TRUE; } else if (*Value == L'\'' && Value[ i ] == L'\'') { *QuoteChar = '"'; return TRUE; } } return FALSE; } } void RTFormatKeyName( PREG_OUTPUT_ROUTINE OutputRoutine, PVOID OutputRoutineParameter, ULONG IndentLevel, PWSTR KeyName ) { PWSTR pw; WCHAR QuoteChar; if (NeedQuotedString( KeyName, NULL, &QuoteChar )) { (OutputRoutine)( OutputRoutineParameter, "%.*s%c%ws%c", IndentLevel, BlanksForPadding, QuoteChar, KeyName, QuoteChar ); } else { (OutputRoutine)( OutputRoutineParameter, "%.*s%ws", IndentLevel, BlanksForPadding, KeyName ); } return; } void RTFormatKeySecurity( PREG_OUTPUT_ROUTINE OutputRoutine, PVOID OutputRoutineParameter, HKEY KeyHandle, PSECURITY_DESCRIPTOR SecurityDescriptor ) { ULONG SecurityBufferLength; BOOLEAN FormattedAces; WCHAR AceList[ 256 ]; FormattedAces = FALSE; if (KeyHandle != NULL) { SecurityBufferLength = 0; if (RegGetKeySecurity( KeyHandle, DACL_SECURITY_INFORMATION, SecurityDescriptor, &SecurityBufferLength ) == ERROR_INSUFFICIENT_BUFFER ) { SecurityDescriptor = (PSECURITY_DESCRIPTOR)HeapAlloc( GetProcessHeap(), 0, SecurityBufferLength ); if (SecurityDescriptor) { if (RegGetKeySecurity( KeyHandle, DACL_SECURITY_INFORMATION, SecurityDescriptor, &SecurityBufferLength ) != NO_ERROR ) { HeapFree( GetProcessHeap(), 0, SecurityDescriptor ); } else { FormattedAces = RegFormatSecurity( SecurityDescriptor, AceList ); HeapFree( GetProcessHeap(), 0, SecurityDescriptor ); } } } } else if (SecurityDescriptor != NULL) { FormattedAces = RegFormatSecurity( SecurityDescriptor, AceList ); } if (FormattedAces) { (OutputRoutine)( OutputRoutineParameter, " %wc%ws%wc", ACL_LIST_START, AceList, ACL_LIST_END ); } return; } void RegDisplayResourceListAsComment( ULONG OutputWidth, PREG_OUTPUT_ROUTINE OutputRoutine, PVOID OutputRoutineParameter, ULONG IndentLevel, ULONG ValueLength, ULONG ValueType, PWSTR ValueData ); void RTFormatKeyValue( ULONG OutputWidth, PREG_OUTPUT_ROUTINE OutputRoutine, PVOID OutputRoutineParameter, BOOLEAN SummaryOutput, ULONG IndentLevel, PWSTR ValueName, ULONG ValueLength, ULONG ValueType, PWSTR ValueData ) { PULONG p; PWSTR pw, pw1, pwBreak; WCHAR QuoteChar, BreakChar; ULONG i, j, k, m, cbPrefix, cb; PUCHAR pbyte; char eol[11]; cbPrefix = (OutputRoutine)( OutputRoutineParameter, "%.*s", IndentLevel, BlanksForPadding ); if (ValueName != NULL && *ValueName != UNICODE_NULL) { if (NeedQuotedString( ValueName, NULL, &QuoteChar )) { cbPrefix += (OutputRoutine)( OutputRoutineParameter, "%c%ws%c ", QuoteChar, ValueName, QuoteChar ); } else { cbPrefix += (OutputRoutine)( OutputRoutineParameter, "%ws ", ValueName ); } } cbPrefix += (OutputRoutine)( OutputRoutineParameter, "= " ); switch( ValueType ) { case REG_SZ: case REG_EXPAND_SZ: if (ValueType == REG_EXPAND_SZ) { cbPrefix += (OutputRoutine)( OutputRoutineParameter, "REG_EXPAND_SZ " ); } pw = (PWSTR)ValueData; if (ValueLength & (sizeof(WCHAR)-1)) { (OutputRoutine)( OutputRoutineParameter, "(*** Length not multiple of WCHAR ***)" ); ValueLength = (ValueLength+sizeof(WCHAR)-1) & ~(sizeof(WCHAR)-1); } if (ValueLength == 0 || *(PWSTR)((PCHAR)pw + ValueLength - sizeof( WCHAR )) != UNICODE_NULL ) { (OutputRoutine)( OutputRoutineParameter, "(*** MISSING TRAILING NULL CHARACTER ***)" ); *(PWSTR)((PCHAR)pw + ValueLength) = UNICODE_NULL; } if (NeedQuotedString( NULL, pw, &QuoteChar )) { (OutputRoutine)( OutputRoutineParameter, "%c%ws%c", QuoteChar, pw, QuoteChar ); } else if ((cbPrefix + wcslen(pw)) <= OutputWidth) { (OutputRoutine)( OutputRoutineParameter, "%ws", pw ); } else { while (*pw) { pw1 = pw; pwBreak = NULL; while (*pw1 && *pw1 >= L' ') { if ((cbPrefix + (ULONG)(pw1 - pw)) > (OutputWidth-4)) { break; } if (wcschr( L" ,;", *pw1 )) { pwBreak = pw1; } pw1++; } if (pwBreak != NULL) { while (*pwBreak == pwBreak[1]) { pwBreak += 1; } pw1 = pwBreak + 1; } else { while (*pw1) { pw1 += 1; } } (OutputRoutine)( OutputRoutineParameter, "%.*ws", pw1 - pw, pw ); if (*pw1 == UNICODE_NULL) { break; } if (SummaryOutput) { (OutputRoutine)( OutputRoutineParameter, "\\..." ); break; } (OutputRoutine)( OutputRoutineParameter, "\\\n%.*s", IndentLevel == 0 ? 4 : cbPrefix, BlanksForPadding ); pw = pw1; } } (OutputRoutine)( OutputRoutineParameter, "\n" ); break; case REG_RESOURCE_LIST: case REG_FULL_RESOURCE_DESCRIPTOR: case REG_RESOURCE_REQUIREMENTS_LIST: case REG_BINARY: case REG_NONE: switch( ValueType ) { case REG_NONE: pw = L"REG_NONE"; break; case REG_BINARY: pw = L"REG_BINARY"; break; case REG_RESOURCE_REQUIREMENTS_LIST: pw = L"REG_RESOURCE_REQUIREMENTS_LIST"; break; case REG_RESOURCE_LIST: pw = L"REG_RESOURCE_LIST"; break; case REG_FULL_RESOURCE_DESCRIPTOR: pw = L"REG_FULL_RESOURCE_DESCRIPTOR"; break; } cb = (OutputRoutine)( OutputRoutineParameter, "%ws 0x%08lx", pw, ValueLength ); if (ValueLength != 0) { p = (PULONG)ValueData; i = (ValueLength + 3) / sizeof( ULONG ); if (!SummaryOutput || i <= 2) { for (j=0; j (OutputWidth - 2)) { (OutputRoutine)( OutputRoutineParameter, " \\\n%.*s", IndentLevel == 0 ? 4 : cbPrefix, BlanksForPadding ); cb = 0; } else { cb += (OutputRoutine)( OutputRoutineParameter, " " ); } cb += (OutputRoutine)( OutputRoutineParameter, "0x%08lx", *p++ ); } } else { (OutputRoutine)( OutputRoutineParameter, " \\..." ); } } (OutputRoutine)( OutputRoutineParameter, "\n" ); if (!SummaryOutput) { RegDisplayResourceListAsComment( OutputWidth, OutputRoutine, OutputRoutineParameter, IndentLevel, ValueLength, ValueType, ValueData ); } break; // case REG_DWORD_LITTLE_ENDIAN: case REG_DWORD: (OutputRoutine)( OutputRoutineParameter, "REG_DWORD 0x%08lx\n", *(PULONG)ValueData ); break; case REG_DWORD_BIG_ENDIAN: (OutputRoutine)( OutputRoutineParameter, "REG_DWORD_BIG_ENDIAN 0x%08lx\n", *(PULONG)ValueData ); break; case REG_LINK: (OutputRoutine)( OutputRoutineParameter, "REG_LINK %ws\n", (PWSTR)ValueData ); break; case REG_MULTI_SZ: (!FullPathOutput) ? strcpy (eol, " \\\n%.*s") : strcpy (eol, " \\ ->%.*s"); cbPrefix += (OutputRoutine)( OutputRoutineParameter, "REG_MULTI_SZ " ); pw = (PWSTR)ValueData; i = 0; if (*pw) while (i < (ValueLength - 1) / sizeof( WCHAR )) { if (i > 0) { (OutputRoutine)( OutputRoutineParameter, eol, IndentLevel == 0 ? 4 : cbPrefix, BlanksForPadding ); } (OutputRoutine)( OutputRoutineParameter, "\""); do { if (pw[i] == '"') { (OutputRoutine)( OutputRoutineParameter, "%wc",pw[i]); } (OutputRoutine)( OutputRoutineParameter, "%wc",pw[i]); ++i; } while ( pw[i] != UNICODE_NULL ); (OutputRoutine)( OutputRoutineParameter, "\" "); if (SummaryOutput) { (OutputRoutine)( OutputRoutineParameter, " \\..." ); break; } ++i; } (OutputRoutine)( OutputRoutineParameter, "\n" ); break; default: (OutputRoutine)( OutputRoutineParameter, "*** Unknown Registry Data Type (%08lx) Length: 0x%lx\n", ValueType, ValueLength ); break; } return; } void RegDisplayResourceListAsComment( ULONG OutputWidth, PREG_OUTPUT_ROUTINE OutputRoutine, PVOID OutputRoutineParameter, ULONG IndentLevel, ULONG ValueLength, ULONG ValueType, PWSTR ValueData ) { PCM_RESOURCE_LIST ResourceList = (PCM_RESOURCE_LIST)ValueData; PCM_FULL_RESOURCE_DESCRIPTOR FullDescriptor; PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResourceDescriptor; ULONG i, j, k, l, count, cb; PWSTR TypeName; PWSTR FlagName; ULONG Size = ValueLength; PULONG p; if (ValueType == REG_RESOURCE_LIST) { if (ValueLength < sizeof( *ResourceList )) { return; } count = ResourceList->Count; FullDescriptor = &ResourceList->List[0]; (OutputRoutine)( OutputRoutineParameter, ";%.*sNumber of Full resource Descriptors = %d", IndentLevel - 1, BlanksForPadding, count ); } else if (ValueType == REG_FULL_RESOURCE_DESCRIPTOR) { if (ValueLength < sizeof( *FullDescriptor )) { return; } count = 1; FullDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)ValueData; } else { return; } for (i=0; i< count; i++) { (OutputRoutine)( OutputRoutineParameter, "\n;%.*sPartial List number %d\n", IndentLevel+4-1, BlanksForPadding, i ); switch(FullDescriptor->InterfaceType) { case InterfaceTypeUndefined: TypeName = L"Undefined";break; case Internal: TypeName = L"Internal"; break; case Isa: TypeName = L"Isa"; break; case Eisa: TypeName = L"Eisa"; break; case MicroChannel: TypeName = L"MicroChannel"; break; case TurboChannel: TypeName = L"TurboChannel"; break; case PCIBus: TypeName = L"PCI"; break; case VMEBus: TypeName = L"VME"; break; case NuBus: TypeName = L"NuBus"; break; case PCMCIABus: TypeName = L"PCMCIA"; break; case CBus: TypeName = L"CBUS"; break; case MPIBus: TypeName = L"MPI"; break; case MPSABus: TypeName = L"MPSA"; break; case ProcessorInternal: TypeName = L"ProcessorInternal";break; case InternalPowerBus: TypeName = L"InternalPower"; break; case PNPISABus: TypeName = L"PNP Isa"; break; default: TypeName = L"***invalid bus type***"; break; } (OutputRoutine)( OutputRoutineParameter, ";%.*sINTERFACE_TYPE %ws\n", IndentLevel+8-1, BlanksForPadding, TypeName ); (OutputRoutine)( OutputRoutineParameter, ";%.*sBUS_NUMBER %d\n", IndentLevel+8-1, BlanksForPadding, FullDescriptor->BusNumber ); // // This is a basic test to see if the data format is right. // We know at least some video resource list are bogus ... // if (Size < FullDescriptor->PartialResourceList.Count * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) ) { (OutputRoutine)( OutputRoutineParameter, "\n;%.*s *** !!! Invalid ResourceList !!! *** \n", IndentLevel+8-1, BlanksForPadding, i ); break; } Size -= FullDescriptor->PartialResourceList.Count * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR); for (j=0; jPartialResourceList.Count; j++) { (OutputRoutine)( OutputRoutineParameter, ";%.*sDescriptor number %d\n", IndentLevel+12-1, BlanksForPadding, j ); PartialResourceDescriptor = &(FullDescriptor->PartialResourceList.PartialDescriptors[j]); switch(PartialResourceDescriptor->ShareDisposition) { case CmResourceShareUndetermined: TypeName = L"CmResourceShareUndetermined"; break; case CmResourceShareDeviceExclusive: TypeName = L"CmResourceDeviceExclusive"; break; case CmResourceShareDriverExclusive: TypeName = L"CmResourceDriverExclusive"; break; case CmResourceShareShared: TypeName = L"CmResourceShared"; break; default: TypeName = L"***invalid share disposition***"; break; } (OutputRoutine)( OutputRoutineParameter, ";%.*sShare Disposition %ws\n", IndentLevel+12-1, BlanksForPadding, TypeName ); FlagName = L"***invalid Flags"; switch(PartialResourceDescriptor->Type) { case CmResourceTypeNull: TypeName = L"NULL"; FlagName = L"***Unused"; break; case CmResourceTypePort: TypeName = L"PORT"; if (PartialResourceDescriptor->Flags == CM_RESOURCE_PORT_MEMORY) { FlagName = L"CM_RESOURCE_PORT_MEMORY"; } if (PartialResourceDescriptor->Flags == CM_RESOURCE_PORT_IO) { FlagName = L"CM_RESOURCE_PORT_IO"; } break; case CmResourceTypeInterrupt: TypeName = L"INTERRUPT"; if (PartialResourceDescriptor->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) { FlagName = L"CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE"; } if (PartialResourceDescriptor->Flags == CM_RESOURCE_INTERRUPT_LATCHED) { FlagName = L"CM_RESOURCE_INTERRUPT_LATCHED"; } break; case CmResourceTypeMemory: TypeName = L"MEMORY"; if (PartialResourceDescriptor->Flags == CM_RESOURCE_MEMORY_READ_WRITE) { FlagName = L"CM_RESOURCE_MEMORY_READ_WRITE"; } if (PartialResourceDescriptor->Flags == CM_RESOURCE_MEMORY_READ_ONLY) { FlagName = L"CM_RESOURCE_MEMORY_READ_ONLY"; } if (PartialResourceDescriptor->Flags == CM_RESOURCE_MEMORY_WRITE_ONLY) { FlagName = L"CM_RESOURCE_MEMORY_WRITE_ONLY"; } break; case CmResourceTypeDma: TypeName = L"DMA"; FlagName = L"***Unused"; break; case CmResourceTypeDeviceSpecific: TypeName = L"DEVICE SPECIFIC"; FlagName = L"***Unused"; break; default: TypeName = L"***invalid type***"; break; } (OutputRoutine)( OutputRoutineParameter, ";%.*sTYPE %ws\n", IndentLevel+12-1, BlanksForPadding, TypeName ); (OutputRoutine)( OutputRoutineParameter, ";%.*sFlags %ws\n", IndentLevel+12-1, BlanksForPadding, FlagName ); switch(PartialResourceDescriptor->Type) { case CmResourceTypePort: (OutputRoutine)( OutputRoutineParameter, ";%.*sSTART 0x%08lx LENGTH 0x%08lx\n", IndentLevel+12-1, BlanksForPadding, PartialResourceDescriptor->u.Port.Start.LowPart, PartialResourceDescriptor->u.Port.Length ); break; case CmResourceTypeInterrupt: (OutputRoutine)( OutputRoutineParameter, ";%.*sLEVEL %d VECTOR %d AFFINITY %d\n", IndentLevel+12-1, BlanksForPadding, PartialResourceDescriptor->u.Interrupt.Level, PartialResourceDescriptor->u.Interrupt.Vector, PartialResourceDescriptor->u.Interrupt.Affinity ); break; case CmResourceTypeMemory: (OutputRoutine)( OutputRoutineParameter, ";%.*sSTART 0x%08lx%08lx LENGTH 0x%08lx\n", IndentLevel+12-1, BlanksForPadding, PartialResourceDescriptor->u.Memory.Start.HighPart, PartialResourceDescriptor->u.Memory.Start.LowPart, PartialResourceDescriptor->u.Memory.Length ); break; case CmResourceTypeDma: (OutputRoutine)( OutputRoutineParameter, ";%.*sCHANNEL %d PORT %d\n", IndentLevel+12-1, BlanksForPadding, PartialResourceDescriptor->u.Dma.Channel, PartialResourceDescriptor->u.Dma.Port ); break; case CmResourceTypeDeviceSpecific: cb = (OutputRoutine)( OutputRoutineParameter, ";%.*sDataSize 0x%08lx Data:", IndentLevel+12-1, BlanksForPadding, PartialResourceDescriptor->u.DeviceSpecificData.DataSize ); p = (PULONG)(PartialResourceDescriptor + 1); k = (PartialResourceDescriptor->u.DeviceSpecificData.DataSize + 3) / sizeof( ULONG ); for (l=0; l= OutputWidth) { cb = (OutputRoutine)( OutputRoutineParameter, "\n;%.*s", IndentLevel+12-1, BlanksForPadding ) - 1; } cb += (OutputRoutine)( OutputRoutineParameter, " 0x%08lx", *p++ ); } (OutputRoutine)( OutputRoutineParameter, "\n" ); break; default: (OutputRoutine)( OutputRoutineParameter, ";%.*s*** Unknown resource list type: 0x%x ****\n", IndentLevel+12-1, BlanksForPadding, PartialResourceDescriptor->Type ); break; } (OutputRoutine)( OutputRoutineParameter, ";\n" ); } FullDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR) (PartialResourceDescriptor+1); } return; }