/*++ Copyright (c) 1989 Microsoft Corporation Module Name: os2ssmig.c Abstract: This module contains the code necessary to initialize the OS/2 SS entries in the registry. It migrates information from os/2's config.sys file to the NT registry. Author: Ofer Porat (oferp) 30-Mar-1993 Environment: User Mode only Revision History: The code was originally in the os/2 subsystem, but was moved to winlogon so the registry initialization can be done during boot time. This way the registry info will be correct for the first os/2 program to run. --*/ #include "precomp.h" #pragma hdrstop #define IF_OS2_DEBUG(x) // permanently enables KdPrint()s static PVOID Os2Heap; // private heap used for allocs #define PATHLIST_MAX 1024 // max length of pathlists such as Os2LibPath (in characters) #define MAX_CONSYS_SIZE 16384 // max size of config.sys buffers (in bytes) #define DOS_DEV_LEN 12 // length of "\\DosDevices\\" // // The following constants define the meaning of the Flags value in // the config sys key entry: // bit CONFSYSON_DISK -- 1 = os/2 config.sys on drive c, 0 = no os/2 config.sys on drive c // bit DISABLEMIGTRATION -- if 1, migration from os/2 config.sys to NT registry is disabled. // #define FLAGS_CONFIGSYSONDISK 0x1L #define FLAGS_DISABLEMIGRATION 0x2L static WCHAR Os2SoftwareDirectory[] = L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft"; static WCHAR Os2ProductDirectory[] = L"OS/2 Subsystem for NT"; static WCHAR Os2VersionDirectory[] = L"1.0"; static WCHAR Os2IniName[] = L"os2.ini"; static WCHAR Os2ConfigSysName[] = L"config.sys"; static WCHAR Os2ConfigFlagName[] = L"Flags"; static WCHAR Os2ConfigStampName[] = L"FileTimeSizeStamp"; static WCHAR Os2Class[] = L"OS2SS"; static CHAR Os2ConfigSysDefaultValue[] = // // The '\a' in the following strings will be replaced by the SystemDirectory Value // "NTREM Here is a summary of what is allowed to appear in this registry entry:\0" "NTREM Comments starting with REM will be visible to the user when s/he opens\0" "NTREM c:\\config.sys.\0" "NTREM Comments starting with NTREM are only visible by direct access to the\0" "NTREM registry.\0" "NTREM The following OS/2 configuration commands are significant:\0" "NTREM COUNTRY=\0" "NTREM CODEPAGE=\0" "NTREM DEVINFO=KBD,\0" "NTREM Any other commands apart from the exceptions listed below will be\0" "NTREM visible to an OS/2 program that opens c:\\config.sys, however they are\0" "NTREM not used internally by the NT OS/2 SubSystem.\0" "NTREM Exceptions:\0" "NTREM The following commands are completely ignored. Their true values\0" "NTREM appear in the system environment and should be modified using the\0" "NTREM Control Panel System applet. Note that LIBPATH is called Os2LibPath\0" "NTREM in the NT system environment.\0" "SET PATH=\0" "LIBPATH=\0" "NTREM In addition, any \"SET=\" commands (except COMSPEC) will be\0" "NTREM completely ignored. You should set OS/2 environment variables just\0" "NTREM like any other Windows NT environment variables by using the Control\0" "NTREM Panel System applet.\0" "NTREM If you have an OS/2 editor available, it is highly recommended that you\0" "NTREM modify NT OS/2 config.sys configuration by editing c:\\config.sys with\0" "NTREM this editor. This is the documented way to make such modification, and\0" "NTREM is therefore less error-prone.\0" "NTREM Now comes the actual text.\0" "REM\0" "REM This is a fake OS/2 config.sys file used by the NT OS/2 SubSystem.\0" "REM The following information resides in the Registry and NOT in a disk file.\0" "REM OS/2 Apps that access c:\\config.sys actually manipulate this information.\0" "REM\0" "PROTSHELL=c:\\os2\\pmshell.exe c:\\os2\\os2.ini c:\\os2\\os2sys.ini \a\\cmd.exe\0" "SET COMSPEC=\a\\cmd.exe\0" ; static WCHAR Os2ConfigSysKeyName[] = L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\OS/2 Subsystem for NT\\1.0\\config.sys"; static WCHAR Os2EnvironmentDirectory[] = L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"; static HANDLE Os2EnvironmentKeyHandle = NULL; static WCHAR Os2OriginalCanonicalConfigSys[] = L"\\DosDevices\\C:\\CONFIG.SYS"; static BOOLEAN Os2LibPathFound = FALSE; static WCHAR Os2LibPathValueName[] = L"Os2LibPath"; static UNICODE_STRING Os2LibPathValueData_U; static PWSTR Os2PathString = NULL; static PWSTR pOs2ConfigSys = NULL; static ULONG Os2SizeOfConfigSys = 0; static PWSTR pOs2UpperCaseConfigSys = NULL; static WCHAR Os2SystemDirectory[MAX_PATH]; VOID Os2RegSetFlags( IN HANDLE ConfigSysKeyHandle, IN ULONG FlagsValue ) /*++ Routine Description: Write the "Flags" value into the config.sys registry key. Arguments: ConfigSysKeyHandle -- supplies read/write handle to config.sys reg key FlagsValue -- supplies the value to write Return Value: None. Note: Errors are ignored --*/ { UNICODE_STRING String_U; NTSTATUS Status; RtlInitUnicodeString(&String_U, Os2ConfigFlagName); Status = NtSetValueKey( ConfigSysKeyHandle, &String_U, 0L, REG_DWORD, &FlagsValue, sizeof(DWORD) ); if (!NT_SUCCESS(Status)) { #if DBG KdPrint(("Os2RegSetFlags: NetSetValueKey failed, Status = %lx\n", Status)); #endif } // ignore errors } VOID Os2RegSetStamp( IN HANDLE ConfigSysKeyHandle, IN PLARGE_INTEGER pTimeStamp, IN PLARGE_INTEGER pSizeStamp ) /*++ Routine Description: Write the "FileTimeSizeStamp" value into the config.sys registry key. Arguments: ConfigSysKeyHandle -- supplies read/write handle to config.sys reg key pTimeStamp, pSizeStamp -- supplies the value to write Return Value: None. Note: Errors are ignored --*/ { UNICODE_STRING String_U; ULONG Buffer[4]; NTSTATUS Status; Buffer[0] = pTimeStamp->LowPart; Buffer[1] = (ULONG) pTimeStamp->HighPart; Buffer[2] = pSizeStamp->LowPart; Buffer[3] = (ULONG) pSizeStamp->HighPart; RtlInitUnicodeString(&String_U, Os2ConfigStampName); Status = NtSetValueKey( ConfigSysKeyHandle, &String_U, 0L, REG_BINARY, (PVOID) Buffer, sizeof(Buffer) ); if (!NT_SUCCESS(Status)) { #if DBG KdPrint(("Os2RegSetFlags: NetSetValueKey failed, Status = %lx\n", Status)); #endif } // ignore errors } BOOLEAN Os2InitOriginalConfigSysProcessing( IN HANDLE ConfigSysKeyHandle, IN ULONG FlagsValue, IN HANDLE ConfigSysFileHandle, IN PLARGE_INTEGER pTimeStamp, IN PLARGE_INTEGER pSizeStamp ) /*++ Routine Description: This function checks if there is an OS/2 config.sys configuration file on the disk. If so, it opens it and reads it in. The file is converted to UNICODE, and an upper case copy of it is made. Arguments: ConfigSysKeyHandle -- supplies a read/write handle to the config.sys reg key this is used to update the flags/stamp values FlagsValue -- The previously existing "Flags" value that was read in from the registry. If this is -1, then no previous Flags value existed. ConfigSysFileHandle -- An optional handle to an already open config.sys file pTimeStamp, pSizeStamp -- info about the open config.sys file. Note: The parameters ConfigSysFileHandle + pTimeStamp + pSizeStamp are optional, and come as a packet. If the config.sys file was previously opened, the handle will be non-null, and all 3 params must be valid. If the config.sys file was not previously opened (successfully), the handle should be NULL, and the other 2 params need not be valid. Return Value: TRUE if there's an OS/2 config.sys, and all the operations needed to prepare it have succeeded. FALSE otherwise. Notes: On success Sets global variables as follows: pOs2ConfigSys - a null-terminated UNICODE copy of the OS/2 config.sys. pOs2UpperCaseConfigSys - an upper case copy of pOs2ConfigSys. Os2SizeOfConfigSys - the number of characters in the above strings. ConfigSysFileHandle is closed before the function returns. The flags/stamp values in the config.sys reg key are updated if necessary. --*/ { UNICODE_STRING CanonicalConfigDotSys_U; UNICODE_STRING Tmp_U; ANSI_STRING Tmp_MB; OBJECT_ATTRIBUTES Obja; NTSTATUS Status; IO_STATUS_BLOCK IoStatus; PSZ pTempOs2ConfigSys; LARGE_INTEGER TimeStamp, SizeStamp; if (FlagsValue != (ULONG)-1) { if (FlagsValue & FLAGS_DISABLEMIGRATION) { // // ConfigSysFileHandle cannot be non-NULL at this point // so there's no need to close it. Also, no need to // update flags/stamp. // return(FALSE); } FlagsValue &= ~FLAGS_CONFIGSYSONDISK; } else { FlagsValue = 0; // // 1st time migration will always take place (after system setup) // if you you want to disable post 1st-time migration, set FlagsValue // to a default of FLAGS_DISABLEMIGRATION. If you don't any migration // at all (not even 1st-time), then set the FlagsValue similarly, and // also do a { Os2RegSetFlags(..), return FALSE } // } if (ConfigSysFileHandle == NULL) { // Try opening OS/2's config.sys file RtlInitUnicodeString(&CanonicalConfigDotSys_U, Os2OriginalCanonicalConfigSys); InitializeObjectAttributes(&Obja, &CanonicalConfigDotSys_U, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&ConfigSysFileHandle, FILE_GENERIC_READ, &Obja, &IoStatus, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT ); if (!NT_SUCCESS(Status)) { // // Debug print removed, because this is a valid situation (no config.sys file) // #if 0 #if DBG KdPrint(("Os2InitOriginalConfigSysProcessing: FAILED - NtOpenFile-1 %lx\n", Status)); #endif #endif Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); return (FALSE); } Status = Or2GetFileStamps( ConfigSysFileHandle, &TimeStamp, &SizeStamp ); if (!NT_SUCCESS(Status)) { #if DBG KdPrint(("Os2InitOriginalConfigSysProcessing: Can't get config.sys time/size stamp, Status = %lx\n", Status)); #endif NtClose(ConfigSysFileHandle); Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); return(FALSE); } pTimeStamp = &TimeStamp; pSizeStamp = &SizeStamp; } // Get the file length Os2SizeOfConfigSys = pSizeStamp->LowPart; // Allocate space for reading it in // the + 1 in following parameter is for inserting the NUL character pTempOs2ConfigSys = (PSZ) RtlAllocateHeap(Os2Heap, 0, Os2SizeOfConfigSys + 1); if (pTempOs2ConfigSys == NULL) { #if DBG KdPrint(("Os2InitOriginalConfigSysProcessing: FAILED - RtlAllocateHeap pTempOs2ConfigSys\n")); #endif Os2SizeOfConfigSys = 0; NtClose(ConfigSysFileHandle); Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); return (FALSE); } pOs2ConfigSys = (PWSTR) RtlAllocateHeap(Os2Heap, 0, (Os2SizeOfConfigSys + 1) * sizeof(WCHAR)); if (pOs2ConfigSys == NULL) { #if DBG KdPrint(("Os2InitOriginalConfigSysProcessing: FAILED - RtlAllocateHeap pOs2ConfigSys\n")); #endif Os2SizeOfConfigSys = 0; RtlFreeHeap(Os2Heap, 0, pTempOs2ConfigSys); NtClose(ConfigSysFileHandle); Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); return (FALSE); } // Read it in Status = NtReadFile(ConfigSysFileHandle, NULL, NULL, NULL, &IoStatus, (PVOID)pTempOs2ConfigSys, Os2SizeOfConfigSys, NULL, NULL ); NtClose(ConfigSysFileHandle); if (!NT_SUCCESS(Status)) { #if DBG KdPrint(("Os2InitOriginalConfigSysProcessing: FAILED - NtReadFile %lx\n", Status)); #endif Os2SizeOfConfigSys = 0; RtlFreeHeap(Os2Heap, 0, pTempOs2ConfigSys); RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys); pOs2ConfigSys = NULL; Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); return (FALSE); } pTempOs2ConfigSys[Os2SizeOfConfigSys] = '\0'; // Convert to UNICODE RtlInitAnsiString(&Tmp_MB, pTempOs2ConfigSys); Tmp_U.Buffer = pOs2ConfigSys; Tmp_U.MaximumLength = (USHORT) ((Os2SizeOfConfigSys + 1) * sizeof(WCHAR)); RtlOemStringToUnicodeString(&Tmp_U, (POEM_STRING)&Tmp_MB ,FALSE); Os2SizeOfConfigSys = Tmp_U.Length / sizeof(WCHAR); pOs2ConfigSys[Os2SizeOfConfigSys] = UNICODE_NULL; RtlFreeHeap(Os2Heap, 0, pTempOs2ConfigSys); // Prepare the upper case copy pOs2UpperCaseConfigSys = (PWSTR) RtlAllocateHeap(Os2Heap, 0, (Os2SizeOfConfigSys + 1) * sizeof(WCHAR)); if (pOs2UpperCaseConfigSys == NULL) { #if DBG KdPrint(("Os2InitOriginalConfigSysProcessing: FAILED - RtlAllocateHeap pOs2UpperConfigSys\n")); #endif Os2SizeOfConfigSys = 0; RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys); pOs2ConfigSys = NULL; Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); return (FALSE); } wcscpy(pOs2UpperCaseConfigSys, pOs2ConfigSys); Or2UnicodeStrupr(pOs2UpperCaseConfigSys); // // Verify that the CONFIG.SYS file is really an OS/2 file and not a // DOS file. // Look for certain strings that MUST appear in an OS/2 CONFIG.SYS // file and don't appear in DOS CONFIG.SYS files // if ((wcsstr(pOs2UpperCaseConfigSys, L"LIBPATH") == NULL) || (wcsstr(pOs2UpperCaseConfigSys, L"PROTSHELL") == NULL) || (wcsstr(pOs2UpperCaseConfigSys, L"PROTECTONLY") == NULL) ) { Os2SizeOfConfigSys = 0; RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys); RtlFreeHeap(Os2Heap, 0, pOs2UpperCaseConfigSys); pOs2UpperCaseConfigSys = pOs2ConfigSys = NULL; Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); return (FALSE); } FlagsValue |= FLAGS_CONFIGSYSONDISK; Os2RegSetFlags(ConfigSysKeyHandle, FlagsValue); Os2RegSetStamp(ConfigSysKeyHandle, pTimeStamp, pSizeStamp); return (TRUE); } VOID Os2SetDirectiveProcessingDispatchFunction( IN ULONG DispatchTableIndex, IN PVOID UserParameter, IN PWSTR Name, IN ULONG NameLen, IN PWSTR Value, IN ULONG ValueLen ) /*++ Routine Description: This is a Dispatch Routine that is used to process SET directives in OS/2's config.sys file. The directives are entered into the system environment. Arguments: Standard arguments passed to a Dispatch Function, see the description of Or2IterateEnvironment in os2ssrtl.c UserParameter - points to a pointer which indicates the current position in the config.sys registry entry we're building. Return Value: None. --*/ { UNICODE_STRING VarName_U; // for setting up variable name UNICODE_STRING VarValue_U; // for setting up variable value PWSTR Dest = *(PWSTR *) UserParameter; NTSTATUS Status; // ULONG ResultLength; WCHAR wch; // KEY_VALUE_PARTIAL_INFORMATION KeyValueInfo; // Set up variable name VarName_U.Buffer = Value; VarName_U.Length = 0; while ((ValueLen > 0) && (*Value != L'=')) { Value++; VarName_U.Length += sizeof(WCHAR); ValueLen--; } if (ValueLen == 0 || // End of line reached without finding '=' VarName_U.Length == 0) { // Empty name return; } VarName_U.MaximumLength = VarName_U.Length; // Following SET directives are ignored if (Or2UnicodeEqualCI(VarName_U.Buffer, L"COMSPEC=", 8) || // Or2UnicodeEqualCI(VarName_U.Buffer, L"PATH=", 5) || Or2UnicodeEqualCI(VarName_U.Buffer, L"VIDEO_DEVICES=", 14) || Or2UnicodeEqualCI(VarName_U.Buffer, L"VIO_IBMVGA=", 11) || Or2UnicodeEqualCI(VarName_U.Buffer, L"VIO_VGA=", 8) || Or2UnicodeEqualCI(VarName_U.Buffer, L"PROMPT=", 7) ) { return; } // Here, we have a valid name, followed by '=' Value++; // Skip the '=' ValueLen--; // Set up variable value VarValue_U.Buffer = Value; VarValue_U.Length = (USHORT) (ValueLen * sizeof(WCHAR)); // what's left of the line VarValue_U.MaximumLength = VarValue_U.Length; // // Update the information in the registry with the info // in the file // #if 0 // not in effect anymore // // The KEYS variable is handled in a special way. // It's put in the registry config.sys in order to prevent possible // conflict. // if (Or2UnicodeEqualCI(VarName_U.Buffer, L"KEYS=", 5)) { RtlMoveMemory(Dest, L"SET ", 8); Dest += 4; RtlMoveMemory(Dest, VarName_U.Buffer, VarName_U.Length); Dest += VarName_U.Length / sizeof(WCHAR); *Dest++ = L'='; RtlMoveMemory(Dest, VarValue_U.Buffer, VarValue_U.Length); Dest += VarValue_U.Length / sizeof(WCHAR); *Dest++ = UNICODE_NULL; *(PWSTR *) UserParameter = Dest; return; } #endif // // If Os2EnvironmentKeyHandle is NULL, we don't have // write access to the system environment, and we skip // setting the variable. // if (Os2EnvironmentKeyHandle == NULL) { return; } // // Special handling for PATH -- retained in the global variable // Os2PathString. Only the last occurence of PATH is retained. // This is compatible with OS/2 which uses the last occurence. // if (Or2UnicodeEqualCI(VarName_U.Buffer, L"PATH=", 5)) { if (Os2PathString != NULL) { // already found a path? RtlFreeHeap(Os2Heap, 0, Os2PathString); // get rid of it Os2PathString = NULL; } Os2PathString = (PWSTR) RtlAllocateHeap(Os2Heap, 0, VarValue_U.Length + sizeof(WCHAR)); if (Os2PathString == NULL) { #if DBG KdPrint(("Os2SetDirectiveProcessingDispatchFunction: Failed to allocate heap space for PATH\n")); #endif return; // no space for storing the path -- skip it } // switch to the upper case copy... VarValue_U.Buffer = pOs2UpperCaseConfigSys + (VarValue_U.Buffer - pOs2ConfigSys); RtlMoveMemory(Os2PathString, VarValue_U.Buffer, VarValue_U.Length); Os2PathString[VarValue_U.Length/sizeof(WCHAR)] = UNICODE_NULL; return; } // // Otherwise, it's stored in the system environment // // // If a value key of the same name already exists, // don't replace it. This is done in order to prevent // the OS/2 SS from overriding possible NT definitions // // // !!! Modification 5/30/93 -- We decided to be a little more brave about this. // On one hand there doesn't seem to be any conflict between any possible OS/2 // definitions, and NT definitions. On the other hand, since we now do re-migration // on every config.sys change, if we don't update existing variables, the user's // important OS/2 variables (e.g. DPATH) will not get remigrated. Therefore we // always update. -- oferp // #if 0 Status = NtQueryValueKey(Os2EnvironmentKeyHandle, &VarName_U, KeyValuePartialInformation, &KeyValueInfo, sizeof(KeyValueInfo), &ResultLength ); #else Status = STATUS_OBJECT_NAME_NOT_FOUND; #endif if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { // // Set the system wide variable to the value specified // in the original OS/2 config.sys // wch = Value[ValueLen]; Value[ValueLen] = UNICODE_NULL; Status = NtSetValueKey(Os2EnvironmentKeyHandle, &VarName_U, (ULONG)0, REG_EXPAND_SZ, VarValue_U.Buffer, VarValue_U.Length + sizeof(WCHAR) ); Value[ValueLen] = wch; if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SetDirectiveProcessingDispatchFunction: Unable to NtSetValueKey() system env, rc = %X\n", Status)); } #endif return; } } else { #if DBG if (Status != STATUS_BUFFER_OVERFLOW) { IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SetDirectiveProcessingDispatchFunction: Unable to NtQueryValueKey() system env, rc = %X\n", Status)); } } #endif return; } } VOID Os2CommaProcessingDispatchFunction( IN ULONG DispatchTableIndex, IN PVOID UserParameter, IN PWSTR Name, IN ULONG NameLen, IN PWSTR Value, IN ULONG ValueLen ) /*++ Routine Description: This is a Dispatch Routine that is used to process certain directives in OS/2's config.sys file. The directives are copied into the config.sys registry entry we're building. Some directives are truncated after a certain number of commas. !!! Modification 5/30/93 -- Migration of the three NLS related statements has been disabled. Reason: The intended default behavior is this: The registry config.sys entry has no NLS entries, and the OS/2 subsystem picks up these values from NT's default values. Supporting the registry config.sys values is only meant to be used if the user actually wants to have separate OS/2 and NT NLS parameters. This is almost never the case. The reason for allowing dual-NLS mode is because some NT languages may not be supported on OS/2, and so the user may want to specify a different (existing) NLS setting for the OS/2 subsystem. The usual setting is therefore to have no NLS directives in the registry config.sys. That's why we shouldn't migrate. -- oferp Arguments: Standard arguments passed to a Dispatch Function, see the description of Or2IterateEnvironment in os2ssrtl.c UserParameter - points to a pointer which indicates the current position in the config.sys registry entry we're building. Return Value: None. --*/ { // // Lines passed to this dispatch function need to be copied to the output as are // except they should be truncated after a certain number of commas. The // following table lists the number of commas. 0 means no truncation. // #if 0 ULONG ItemTable[] = { 1, // COUNTRY 0, // CODEPAGE 2 // DEVINFO (only with KBD) }; PWSTR Dest = *(PWSTR *) UserParameter; ULONG CommaCtr; if (DispatchTableIndex== 2 && !Or2UnicodeEqualCI(Value, L"KBD,", 4)) { return; } // First, copy the Name RtlMoveMemory(Dest, Name, NameLen * sizeof(WCHAR)); Dest += NameLen; *Dest++ = L'='; // Now, copy the value for the right number of commas CommaCtr = 0; while (ValueLen > 0) { if (ItemTable[DispatchTableIndex] != 0 && *Value == L',') { CommaCtr++; if (CommaCtr == ItemTable[DispatchTableIndex]) { break; } } *Dest++ = *Value++; ValueLen--; } *Dest++ = UNICODE_NULL; *(PWSTR *) UserParameter = Dest; #else return; // do nothing! #endif } VOID Os2ProcessOriginalConfigSys( IN OUT PWSTR *DestPtr ) /*++ Routine Description: This function processes OS/2's config.sys file after it has been properly initialized by Os2InitOriginalConfigSysProcessing. Arguments: DestPtr - points to a pointer which indicates the current position in the config.sys registry entry we're building. Return Value: None. --*/ { static ENVIRONMENT_DISPATCH_TABLE_ENTRY DispatchTable[] = { { L"COUNTRY", L"=", Os2CommaProcessingDispatchFunction, NULL }, { L"CODEPAGE", L"=", Os2CommaProcessingDispatchFunction, NULL }, { L"DEVINFO", L"=", Os2CommaProcessingDispatchFunction, NULL }, { L"LIBPATH", L"=", Or2FillInSearchRecordDispatchFunction, NULL }, { L"SET", L" \t", Os2SetDirectiveProcessingDispatchFunction, NULL } }; ENVIRONMENT_SEARCH_RECORD LibPathRecord; ULONG i; PWSTR p; WCHAR ch; // // Most of the job is done by Or2IterateEnvironment which processes the file // according to a dispatch table. // // LibPath needs to be handled a little differently. We need to process only // the *last* occurence of LIBPATH= in the file (this is the same as OS/2). // Therefore we only record the position of the LIBPATH= statments as we run // into them. After we're finished we'll have the position of the last one, // and we can process that line. // for (i = 0; i < 5; i++) { DispatchTable[i].UserParameter = (PVOID) DestPtr; } DispatchTable[3].UserParameter = (PVOID)&LibPathRecord; LibPathRecord.DispatchTableIndex = (ULONG)-1; Or2IterateEnvironment(pOs2ConfigSys, DispatchTable, 5, CRLF_DELIM); if (Os2LibPathFound && LibPathRecord.DispatchTableIndex != (ULONG)-1) { // handle LIBPATH if there was one // get a pointer to the upper case version, so we can append it p = pOs2UpperCaseConfigSys + (LibPathRecord.Value - pOs2ConfigSys); ch = p[LibPathRecord.ValueLen]; p[LibPathRecord.ValueLen] = UNICODE_NULL; Or2AppendPathToPath(Os2Heap, p, &Os2LibPathValueData_U, TRUE); p[LibPathRecord.ValueLen] = ch; } } VOID Os2TerminateOriginalConfigSysProcessing( VOID ) /*++ Routine Description: Cleans up processing of OS/2's config.sys. Releases the storage used to store the file. Arguments: None. Return Value: None. --*/ { RtlFreeHeap(Os2Heap, 0, pOs2ConfigSys); RtlFreeHeap(Os2Heap, 0, pOs2UpperCaseConfigSys); pOs2UpperCaseConfigSys = pOs2ConfigSys = NULL; Os2SizeOfConfigSys = 0; } NTSTATUS Os2SbBuildSD( PSECURITY_DESCRIPTOR SecurityDescriptor, PACL *pDacl ) /*++ Routine Description: Builds a security descriptor for creating our registry keys. Administrators -- all access everyone -- read access Arguments: SecurityDescriptor -- supplies a pointer to a preallocated SD that will be built pDacl -- returns a pointer to a dacl that should be released from Os2Heap after we're finished using the security descriptor. Return Value: NT error code. --*/ { PACL Dacl; PACE_HEADER Pace; PSID AdminAliasSid; ULONG DaclSize; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY WorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY; PSID WorldSid; NTSTATUS Status; // // Create the SIDs for local admin and World. // Status = RtlAllocateAndInitializeSid( &NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdminAliasSid ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbBuildSD: RtlAllocateAndInitializeSid(Admin), Status = %lx\n", Status)); } #endif return (Status); } Status = RtlAllocateAndInitializeSid( &WorldSidAuthority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &WorldSid ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbBuildSD: RtlAllocateAndInitializeSid(World), Status = %lx\n", Status)); } #endif RtlFreeSid( AdminAliasSid ); return (Status); } Status = RtlCreateSecurityDescriptor( SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbBuildSD: RtlCreateSecurityDescriptor, Status = %lx\n", Status)); } #endif RtlFreeSid( AdminAliasSid ); RtlFreeSid( WorldSid ); return (Status); } // // Compute the size of the buffer needed for the // DACL. // DaclSize = sizeof( ACL ) + 2 * (sizeof( ACCESS_ALLOWED_ACE ) - sizeof( ULONG )) + RtlLengthSid( AdminAliasSid ) + RtlLengthSid( WorldSid ); Dacl = (PACL) RtlAllocateHeap(Os2Heap, 0, DaclSize); if (Dacl == NULL) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbBuildSD: RtlAllocateHeap failed\n")); } #endif RtlFreeSid( AdminAliasSid ); RtlFreeSid( WorldSid ); return(STATUS_NO_MEMORY); } // // Build the ACL // Status = RtlCreateAcl ( Dacl, DaclSize, ACL_REVISION2 ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbBuildSD: RtlCreateAcl, Status = %lx\n", Status)); } #endif RtlFreeHeap(Os2Heap, 0, Dacl); RtlFreeSid( AdminAliasSid ); RtlFreeSid( WorldSid ); return (Status); } Status = RtlAddAccessAllowedAce ( Dacl, ACL_REVISION2, GENERIC_ALL, AdminAliasSid ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbBuildSD: RtlAddAccessAllowedAce(Admin), Status = %lx\n", Status)); } #endif RtlFreeHeap(Os2Heap, 0, Dacl); RtlFreeSid( AdminAliasSid ); RtlFreeSid( WorldSid ); return (Status); } Status = RtlAddAccessAllowedAce ( Dacl, ACL_REVISION2, GENERIC_READ, WorldSid ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbBuildSD: RtlAddAccessAllowedAce(World), Status = %lx\n", Status)); } #endif RtlFreeHeap(Os2Heap, 0, Dacl); RtlFreeSid( AdminAliasSid ); RtlFreeSid( WorldSid ); return (Status); } Status = RtlGetAce( Dacl, 0L, &Pace ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbBuildSD: RtlGetAce(Admin), Status = %lx\n", Status)); } #endif RtlFreeHeap(Os2Heap, 0, Dacl); RtlFreeSid( AdminAliasSid ); RtlFreeSid( WorldSid ); return (Status); } Pace->AceFlags |= CONTAINER_INHERIT_ACE; Status = RtlGetAce( Dacl, 1L, &Pace ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbBuildSD: RtlGetAce(World), Status = %lx\n", Status)); } #endif RtlFreeHeap(Os2Heap, 0, Dacl); RtlFreeSid( AdminAliasSid ); RtlFreeSid( WorldSid ); return (Status); } Pace->AceFlags |= CONTAINER_INHERIT_ACE; // // Add the ACL to the security descriptor // Status = RtlSetDaclSecurityDescriptor( SecurityDescriptor, TRUE, Dacl, // put (PACL) NULL to allow everyone all access FALSE ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbBuildSD: RtlSetDaclSecurityDescriptor, Status = %lx\n", Status)); } #endif RtlFreeHeap(Os2Heap, 0, Dacl); RtlFreeSid( AdminAliasSid ); RtlFreeSid( WorldSid ); return (Status); } // // These have been copied into the security descriptor, so // we can free them. // RtlFreeSid( AdminAliasSid ); RtlFreeSid( WorldSid ); *pDacl = Dacl; return(STATUS_SUCCESS); } NTSTATUS Os2SbInitializeRegistryKeys( OUT PHANDLE phConfigSysKeyHandle, OUT PHANDLE phEnvironmentKeyHandle ) /*++ Routine Description: This routine creates the OS/2 subsystem key hierarchy in the registry. It also opens the config.sys key, and opens the system environment key. Arguments: phConfigSysKeyHandle - Returns a READ/WRITE handle to the config.sys key in the registry. phEnvironmentKeyHandle - Returns a READ/WRITE handle to the system environment key. If this key can't be opened due to access denied, NULL is returned and the return value will be STATUS_SUCCESS. Note --- If the return value is not a success return value, none of the above return variables can be considered to be valid. Return Value: The value is an NTSTATUS type that is returned when some failure occurs. It may indicate any of several errors that occur during the APIs called in this function. The return value should be tested with NT_SUCCESS(). --*/ { OBJECT_ATTRIBUTES Obja; UNICODE_STRING Class_U; UNICODE_STRING SoftwareDirectory_U; UNICODE_STRING ProductDirectory_U; UNICODE_STRING VersionDirectory_U; UNICODE_STRING Os2IniName_U; UNICODE_STRING ConfigSysName_U; UNICODE_STRING EnvRegDir_U; HANDLE SoftwareKeyHandle; HANDLE ProductKeyHandle; HANDLE VersionKeyHandle; HANDLE Os2IniKeyHandle; HANDLE ConfigSysKeyHandle; HANDLE EnvironmentKeyHandle; ULONG Disposition; NTSTATUS Status; SECURITY_DESCRIPTOR localSecurityDescriptor; PSECURITY_DESCRIPTOR securityDescriptor; PACL Dacl = NULL; *phConfigSysKeyHandle = NULL; *phEnvironmentKeyHandle = NULL; // We start off by creating/opening the registry key hierarchy for our subsystem securityDescriptor = &localSecurityDescriptor; Status = Os2SbBuildSD(securityDescriptor, &Dacl); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistryKeys: Os2SbBuildSD failed, Status = %lx\n", Status)); } #endif return (Status); } RtlInitUnicodeString(&Class_U, Os2Class); RtlInitUnicodeString(&SoftwareDirectory_U, Os2SoftwareDirectory); InitializeObjectAttributes(&Obja, &SoftwareDirectory_U, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&SoftwareKeyHandle, KEY_CREATE_SUB_KEY, &Obja ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistryKeys: Can't open software key, rc = %lx\n", Status)); } #endif if (Dacl != NULL) { RtlFreeHeap(Os2Heap, 0, Dacl); } return (Status); } RtlInitUnicodeString(&ProductDirectory_U, Os2ProductDirectory); InitializeObjectAttributes(&Obja, &ProductDirectory_U, OBJ_CASE_INSENSITIVE, SoftwareKeyHandle, securityDescriptor); Status = NtCreateKey(&ProductKeyHandle, KEY_CREATE_SUB_KEY, &Obja, 0, &Class_U, REG_OPTION_NON_VOLATILE, &Disposition ); NtClose(SoftwareKeyHandle); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistryKeys: Can't create product key, rc = %lx\n", Status)); } #endif if (Dacl != NULL) { RtlFreeHeap(Os2Heap, 0, Dacl); } return (Status); } RtlInitUnicodeString(&VersionDirectory_U, Os2VersionDirectory); InitializeObjectAttributes(&Obja, &VersionDirectory_U, OBJ_CASE_INSENSITIVE, ProductKeyHandle, securityDescriptor); Status = NtCreateKey(&VersionKeyHandle, KEY_CREATE_SUB_KEY, &Obja, 0, &Class_U, REG_OPTION_NON_VOLATILE, &Disposition ); NtClose(ProductKeyHandle); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistryKeys: Can't create version key, rc = %lx\n", Status)); } #endif if (Dacl != NULL) { RtlFreeHeap(Os2Heap, 0, Dacl); } return (Status); } RtlInitUnicodeString(&Os2IniName_U, Os2IniName); InitializeObjectAttributes(&Obja, &Os2IniName_U, OBJ_CASE_INSENSITIVE, VersionKeyHandle, securityDescriptor); Status = NtCreateKey(&Os2IniKeyHandle, KEY_CREATE_SUB_KEY, &Obja, 0, &Class_U, REG_OPTION_NON_VOLATILE, &Disposition ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistryKeys: Can't create os2ini key, rc = %lx\n", Status)); } #endif NtClose(VersionKeyHandle); if (Dacl != NULL) { RtlFreeHeap(Os2Heap, 0, Dacl); } return (Status); } NtClose(Os2IniKeyHandle); RtlInitUnicodeString(&ConfigSysName_U, Os2ConfigSysName); InitializeObjectAttributes(&Obja, &ConfigSysName_U, OBJ_CASE_INSENSITIVE, VersionKeyHandle, securityDescriptor); Status = NtCreateKey(&ConfigSysKeyHandle, KEY_READ | KEY_WRITE, &Obja, 0, &Class_U, REG_OPTION_NON_VOLATILE, &Disposition ); if (Dacl != NULL) { RtlFreeHeap(Os2Heap, 0, Dacl); } NtClose(VersionKeyHandle); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistryKeys: Can't create/open config.sys key, rc = %lx\n", Status)); } #endif return(Status); } // Open the environment. RtlInitUnicodeString(&EnvRegDir_U, Os2EnvironmentDirectory); InitializeObjectAttributes(&Obja, &EnvRegDir_U, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&EnvironmentKeyHandle, KEY_READ | KEY_WRITE, &Obja ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistryKeys: Unable to NtOpenKey() the system environment, rc = %lx\n", Status)); } #endif if (Status != STATUS_ACCESS_DENIED) { NtClose(ConfigSysKeyHandle); return(Status); } // // on denied access, return with no error so we can at least create the // config.sys value entry // The caller will know this occured because the environment handle is null. // } else { *phEnvironmentKeyHandle = EnvironmentKeyHandle; } *phConfigSysKeyHandle = ConfigSysKeyHandle; return (STATUS_SUCCESS); } NTSTATUS Os2SbInitializeRegistry( IN ULONG FlagsValue, IN HANDLE ConfigSysFileHandle, IN PLARGE_INTEGER pTimeStamp, IN PLARGE_INTEGER pSizeStamp ) /*++ Routine Description: This function is responsible for initializing the entire Registry component of the Subsystem. It generates the key hierarchy in the registry. In the generation of the key hierarchy, it also generates a config.sys entry. The information in this entry is taken from the following sources: -- some default strings we put in (see Os2ConfigSysDefaultValue). -- Information from OS/2's config.sys file is added as follows: > SET commands are put in the system environment. Some SET commands are ignored (see Os2SetDirectiveProcessingDispatchFunction) > LIBPATH commands are merged to the Os2LibPath variable in the system environment. A terminating semicolon is added if there is only one path in the path list. > SET PATH= is ignored, and the system path remains the same. Arguments: FlagsValue -- The previously existing "Flags" value that was read in from the registry. If this is -1, then no previous Flags value existed. ConfigSysFileHandle -- An optional handle to an already open config.sys file pTimeStamp, pSizeStamp -- info about the open config.sys file. Note: The parameters ConfigSysFileHandle + pTimeStamp + pSizeStamp are optional, and come as a packet. If the config.sys file was previously opened, the handle will be non-null, and all 3 params must be valid. If the config.sys file was not previously opened (successfully), the handle should be NULL, and the other 2 params need not be valid. Return Value: The value is an NTSTATUS type that is returned when some failure occurs. It may indicate any of several errors that occur during the APIs called in this function. The return value should be tested with NT_SUCCESS(). If an unsuccessful value is returned, it means the registry component was not properly initialized. --*/ { UNICODE_STRING Os2LibPathValueName_U; HANDLE ConfigSysKeyHandle; NTSTATUS Status; UNICODE_STRING ConfigSysName_U; PWCHAR pInfo; PUCHAR Src; PWCHAR Src1; PWCHAR Dest; WCHAR ch, ch1; // Create the key hierarchy Status = Os2SbInitializeRegistryKeys(&ConfigSysKeyHandle, &Os2EnvironmentKeyHandle); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistry: failed Os2SbInitializeRegistryKeys, Status = %lx\n", Status)); } #endif return(Status); } // // Create the config.sys entry // and migrate information from os/2's config.sys file. // Os2LibPathFound = FALSE; Os2LibPathValueData_U.Buffer = NULL; if (Os2EnvironmentKeyHandle == NULL) { // we have no write access to the environment goto Os2NoEnvAccess; // skip over the Os2LibPath stuff } // Get Os2LibPath from sys env so we can update it. if (!Or2GetEnvPath(&Os2LibPathValueData_U, Os2Heap, PATHLIST_MAX, Os2EnvironmentKeyHandle, Os2LibPathValueName, FALSE)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistry: Unable to fetch Os2LibPath from sys env\n")); } #endif } else { // make sure there's at lease one semicolon Or2CheckSemicolon(&Os2LibPathValueData_U); Os2LibPathFound = TRUE; } if (Os2LibPathFound && Os2LibPathValueData_U.Length == 0) { // it's empty (or nonexistent) // set up a default UNICODE_STRING tmp; RtlInitUnicodeString(&tmp, Os2SystemDirectory); RtlCopyUnicodeString(&Os2LibPathValueData_U, &tmp); RtlAppendUnicodeToString(&Os2LibPathValueData_U, L"\\os2\\dll;"); Os2LibPathValueData_U.Buffer[Os2LibPathValueData_U.Length/sizeof(WCHAR)] = UNICODE_NULL; } Os2NoEnvAccess: // Now set up the initial regisry config.sys entry. // Allocate a buffer to build the config.sys entry in. (it's a multi-string) pInfo = (PWCHAR) RtlAllocateHeap(Os2Heap, 0, MAX_CONSYS_SIZE); if (pInfo == NULL) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistry: Unable to RtlAllocateHeap() space for config.sys value buffer\n")); } #endif if (Os2LibPathValueData_U.Buffer != NULL) { RtlFreeHeap(Os2Heap, 0, Os2LibPathValueData_U.Buffer); Os2LibPathValueData_U.Buffer = NULL; Os2LibPathFound = FALSE; } if (Os2EnvironmentKeyHandle != NULL) { NtClose(Os2EnvironmentKeyHandle); Os2EnvironmentKeyHandle = NULL; } NtClose(ConfigSysKeyHandle); return (STATUS_NO_MEMORY); } // Initially, copy our default value into the entry. Src = (PUCHAR) Os2ConfigSysDefaultValue; Dest = pInfo; do { ch = (WCHAR) *Src++; if (ch == L'\a') { Src1 = Os2SystemDirectory; ch1 = *Src1++; while (ch1 != UNICODE_NULL) { *Dest++ = ch1; ch1 = *Src1++; } } else { *Dest++ = ch; } } while (!((ch == UNICODE_NULL) && (*Src == '\0'))); // check if the an OS/2 CONFIG.SYS file already exists on // the drive. Its name will be C:\CONFIG.SYS // If it exists, process it for additional information // The system env will also be updated if (Os2InitOriginalConfigSysProcessing( ConfigSysKeyHandle, FlagsValue, ConfigSysFileHandle, pTimeStamp, pSizeStamp )) { if (Os2EnvironmentKeyHandle == NULL) { // we have no write access to the environment // // We couldn't open a write key to the sys env for some reason, so we // won't be able to migrate SET variables to NT from config.sys. // Perhaps a popup should be generated to notify the user of this at // this point. // Anyway, we go on and only write the config.sys entry, skipping // variable migration. // #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistry: Initializing registry without writing system env\n")); } #endif } Os2ProcessOriginalConfigSys(&Dest); Os2TerminateOriginalConfigSysProcessing(); } #if 0 // // The code below is disabled. We'll add COUNTRY= when creating a fictive // config.sys on the fly. If the code is reenabled in the future -- it // should be fixed. It doesn't make sure there are at least three characters // (with leading 0s) in the country code. // else { WCHAR CountryCode[7]; ULONG CountryLength; // // add a default "COUNTRY=" line // CountryLength = GetLocaleInfoW( GetSystemDefaultLCID(), LOCALE_ICOUNTRY, CountryCode, 6); if (CountryLength == 0) { #if DBG KdPrint(("Os2SbInitializeRegistry: Cannot get locale country info, LastError = %lx\n", GetLastError())); #endif } else { RtlMoveMemory(Dest, L"COUNTRY=", 16); Dest += 8; RtlMoveMemory(Dest, CountryCode, CountryLength * sizeof(WCHAR)); Dest += CountryLength; *Dest++ = UNICODE_NULL; } } #endif // Write the new Os2LibPath if (Os2LibPathFound) { RtlInitUnicodeString(&Os2LibPathValueName_U, Os2LibPathValueName); Status = NtSetValueKey(Os2EnvironmentKeyHandle, &Os2LibPathValueName_U, (ULONG)0, REG_EXPAND_SZ, Os2LibPathValueData_U.Buffer, Os2LibPathValueData_U.Length + sizeof(WCHAR) ); #if DBG if (!NT_SUCCESS(Status)) { IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistry: Unable to NtSetValueKey() system environment, rc = %X\n", Status)); } } #endif Os2LibPathFound = FALSE; RtlFreeHeap(Os2Heap, 0, Os2LibPathValueData_U.Buffer); Os2LibPathValueData_U.Buffer = NULL; } // handle a possible PATH value setting if (Os2PathString != NULL) { UNICODE_STRING Os2PathValueName_U; UNICODE_STRING Os2PathValueData_U; // // Os2EnvironmentKeyHandle can't be NULL at this point because // we wouldn't have set Os2PathString... // if (!Or2GetEnvPath(&Os2PathValueData_U, Os2Heap, PATHLIST_MAX, Os2EnvironmentKeyHandle, L"Path", FALSE)) { #if DBG KdPrint(("Os2SbInitializeRegistry: Unable to fetch Path from sys env\n")); #endif // // Can't get value, skip processing // } else { if (Os2PathValueData_U.Length == 0) { // // If we couldn't get the value, or it's empty, don't process it // #if DBG KdPrint(("Os2SbInitializeRegistry: Path in sys env empty or not found\n")); #endif } else { // // Append our string... // Or2AppendPathToPath(Os2Heap, Os2PathString, &Os2PathValueData_U, TRUE); // // Put it back into the registry... // RtlInitUnicodeString(&Os2PathValueName_U, L"Path"); Status = NtSetValueKey(Os2EnvironmentKeyHandle, &Os2PathValueName_U, (ULONG)0, REG_EXPAND_SZ, Os2PathValueData_U.Buffer, Os2PathValueData_U.Length + sizeof(WCHAR) ); #if DBG if (!NT_SUCCESS(Status)) { KdPrint(("Os2SbInitializeRegistry: Unable to NtSetValueKey() system environment (2), rc = %X\n", Status)); } #endif // ignore error from setvaluekey } RtlFreeHeap(Os2Heap, 0, Os2PathValueData_U.Buffer); } RtlFreeHeap(Os2Heap, 0, Os2PathString); Os2PathString = NULL; } if (Os2EnvironmentKeyHandle != NULL) { NtClose(Os2EnvironmentKeyHandle); Os2EnvironmentKeyHandle = NULL; } // set the REG_MULTI_SZ terminating NUL *Dest++ = UNICODE_NULL; // Finally, write the config.sys multi-string entry into the registry. RtlInitUnicodeString(&ConfigSysName_U, Os2ConfigSysName); Status = NtSetValueKey(ConfigSysKeyHandle, &ConfigSysName_U, (ULONG)0, REG_MULTI_SZ, (PVOID)pInfo, (PBYTE)Dest - (PBYTE)pInfo ); RtlFreeHeap(Os2Heap, 0, pInfo); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG( INIT ) { KdPrint(("Os2SbInitializeRegistry: Unable to NtSetValueKey() registry config.sys, rc = %X\n", Status)); } #endif NtClose(ConfigSysKeyHandle); return (Status); } NtClose(ConfigSysKeyHandle); return (STATUS_SUCCESS); } VOID Os2MigrationProcedure( VOID ) /*++ Routine Description: This routine oversees the os/2 config.sys migration procedure. It's called from winlogon during boot. It checks to see if the os/2ss config.sys-related registy entries are intact. If they aren't, it rebuilds them. If they are intact, it checks if config.sys migration is enabled, and if the os/2 config.sys file exists and has changed (size/lastwritetime) since the last migration. If so, it remigrates the config.sys info. Note that SET variables are only added, never removed. The same goes for LIBPATH. PATH is not migrated from os/2 config.sys to NT. Arguments: None. Return Value: None. --*/ { BYTE RegBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4 * sizeof(ULONG)]; OBJECT_ATTRIBUTES Obja; IO_STATUS_BLOCK IoStatus; UNICODE_STRING String_U; LARGE_INTEGER TimeStamp; LARGE_INTEGER SizeStamp; PKEY_VALUE_PARTIAL_INFORMATION pInfo = (PKEY_VALUE_PARTIAL_INFORMATION) RegBuffer; PULONG RegVal; HANDLE ConfigSysKeyHandle; HANDLE ConfigSysFileHandle = NULL; ULONG FlagsValue = (ULONG)-1; ULONG ResultLength; NTSTATUS Status; RtlInitUnicodeString(&String_U, Os2ConfigSysKeyName); InitializeObjectAttributes(&Obja, &String_U, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&ConfigSysKeyHandle, KEY_READ, &Obja ); if (!NT_SUCCESS(Status)) { if (Status != STATUS_OBJECT_PATH_NOT_FOUND && Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_INVALID) { #if DBG KdPrint(("Os2MigrationProcedure: Can't Open config.sys registry key, Status = %lx\n", Status)); #endif return; } goto BuildRegistry; } RtlInitUnicodeString(&String_U, Os2ConfigFlagName); Status = NtQueryValueKey(ConfigSysKeyHandle, &String_U, KeyValuePartialInformation, pInfo, sizeof(RegBuffer), &ResultLength ); if (!NT_SUCCESS(Status)) { NtClose(ConfigSysKeyHandle); if (Status != STATUS_OBJECT_PATH_NOT_FOUND && Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_INVALID) { #if DBG KdPrint(("Os2MigrationProcedure: Can't Read config.sys flag value key, Status = %lx\n", Status)); #endif return; } goto BuildRegistry; } if (pInfo->Type != REG_DWORD || pInfo->DataLength != sizeof(DWORD)) { NtClose(ConfigSysKeyHandle); goto BuildRegistry; } FlagsValue = *(PULONG) (pInfo->Data); RtlInitUnicodeString(&String_U, Os2ConfigSysName); Status = NtQueryValueKey(ConfigSysKeyHandle, &String_U, KeyValuePartialInformation, NULL, 0, &ResultLength ); // // The 2 expected status results are: // // STATUS_OBJECT_NAME_NOT_FOUND - config.sys not yet defined // STATUS_BUFFER_TOO_SMALL - config.sys already defined // if (Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_PATH_NOT_FOUND || Status == STATUS_OBJECT_PATH_INVALID) { NtClose(ConfigSysKeyHandle); goto BuildRegistry; } if (Status != STATUS_BUFFER_TOO_SMALL) { #if DBG KdPrint(("Os2MigrationProcedure: Can't Read config.sys registry value key, Status = %lx\n", Status)); #endif NtClose(ConfigSysKeyHandle); return; } if ((FlagsValue & FLAGS_DISABLEMIGRATION) || !(FlagsValue & FLAGS_CONFIGSYSONDISK)) { // // os/2 config.sys migration disabled // or no os/2 config.sys on disk // NtClose(ConfigSysKeyHandle); return; } RtlInitUnicodeString(&String_U, Os2ConfigStampName); Status = NtQueryValueKey(ConfigSysKeyHandle, &String_U, KeyValuePartialInformation, pInfo, sizeof(RegBuffer), &ResultLength ); NtClose(ConfigSysKeyHandle); if (!NT_SUCCESS(Status)) { if (Status != STATUS_OBJECT_PATH_NOT_FOUND && Status != STATUS_OBJECT_NAME_NOT_FOUND && Status != STATUS_OBJECT_PATH_INVALID) { #if DBG KdPrint(("Os2MigrationProcedure: Can't Read config.sys stamp value key, Status = %lx\n", Status)); #endif return; } goto BuildRegistry; } if (pInfo->Type != REG_BINARY || pInfo->DataLength != 4 * sizeof(DWORD)) { goto BuildRegistry; } RegVal = (PULONG) (pInfo->Data); RtlInitUnicodeString(&String_U, Os2OriginalCanonicalConfigSys); InitializeObjectAttributes(&Obja, &String_U, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenFile(&ConfigSysFileHandle, FILE_GENERIC_READ, &Obja, &IoStatus, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT ); if (!NT_SUCCESS(Status)) { goto BuildRegistry; } Status = Or2GetFileStamps( ConfigSysFileHandle, &TimeStamp, &SizeStamp ); if (!NT_SUCCESS(Status)) { #if DBG KdPrint(("Os2MigrationProcedure: Can't get config.sys time/size stamp, Status = %lx\n", Status)); #endif NtClose(ConfigSysFileHandle); ConfigSysFileHandle = NULL; goto BuildRegistry; } if (TimeStamp.LowPart == RegVal[0] && (ULONG) TimeStamp.HighPart == RegVal[1] && SizeStamp.LowPart == RegVal[2] && (ULONG) SizeStamp.HighPart == RegVal[3]) { // // os/2 config.sys hasn't changed, skip processing // NtClose(ConfigSysFileHandle); return; } // // We need to install the registry stuff // BuildRegistry: if (GetSystemDirectoryW(Os2SystemDirectory, MAX_PATH) == 0) { #if DBG KdPrint(("Os2MigrationProcedure: Cannot obtain name of system directory, LastError = %lx\n", GetLastError())); #endif if (ConfigSysFileHandle != NULL) { NtClose(ConfigSysFileHandle); } return; } // // Create a heap to use for dynamic memory allocation. // Os2Heap = RtlCreateHeap( HEAP_GROWABLE, NULL, 0x10000L, // Initial size of heap is 64K 0x1000L, // Commit an initial page NULL, NULL // Reserved ); if (Os2Heap == NULL) { #if DBG KdPrint(("Os2MigrationProcedure: Error at RtlCreateHeap of Os2Heap\n")); #endif if (ConfigSysFileHandle != NULL) { NtClose(ConfigSysFileHandle); } return; } Status = Os2SbInitializeRegistry( FlagsValue, ConfigSysFileHandle, &TimeStamp, &SizeStamp ); if (!NT_SUCCESS(Status)) { #if DBG KdPrint(("Os2MigrationProcedure: Os2SbInitializeRegistry() failed, Status = %lx\n", Status)); #endif } // // ConfigSysFileHandle is closed by Os2SbInitializeRegistry // RtlDestroyHeap(Os2Heap); }