/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
srvcnfg.c
Abstract:
This module contains the code related to the processing of
CONFIG.SYS in the server.
Author:
Ofer Porat (oferp) 5-Jan-1993
Environment:
User Mode only
Revision History:
This code was originally in srvinit.c.
18-3-93 -- initialization code was moved to os2ss\sbcnfg.c so it can run
in a privileged process.
--*/
#define INCL_OS2V20_FILESYS
#include "os2srv.h"
#include "os2win.h"
#include "os2err.h"
#include "os2res.h"
#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)
static WCHAR Os2ConfigSysName[] = L"config.sys";
static WCHAR Os2EnvironmentDirectory[] =
L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment";
static WCHAR Os2LibPathValueName[] = L"Os2LibPath";
static WCHAR PathValueName[] = L"Path";
extern WCHAR Os2SystemDirectory[];
// The following stuff is for managing the creation of os2conf.nt
static ULONG Os2ConfigSysUsageCount = 0;
static ULONG Os2ConfigSysAllowedAccess; // either OPEN_ACCESS_READONLY or .._READWRITE
static LARGE_INTEGER Os2ConfigSysTimeStamp;
static LARGE_INTEGER Os2ConfigSysSizeStamp;
WCHAR Os2CanonicalConfigDotSys[MAX_PATH] = L"\\OS2SS\\DRIVES\\";
static WCHAR Os2ConfigSysKeyName[] =
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\OS/2 Subsystem for NT\\1.0\\config.sys";
//
// The following structure is used for passing a character buffer around
// dispatch functions.
//
typedef struct _BUFFER_PASSAROUND {
PSZ Buffer; // pointer to start of buffer
ULONG Index; // current index in buffer to add stuff
ULONG MaxSize; // max size of buffer
} BUFFER_PASSAROUND, *PBUFFER_PASSAROUND;
NTSTATUS
Os2EnvironmentKeysRoutine(
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
/*++
Routine Description:
This query routine is used to grab the KEYS setting from the environment.
Arguments:
Standard parameter set for PRTL_QUERY_REGISTRY_ROUTINE, see <ntrtl.h>.
Return Value:
NT Error code.
--*/
{
if (Or2UnicodeEqualCI(ValueData, L"ON", 2)) {
*(PULONG) Context = 1;
}
return(STATUS_SUCCESS);
}
VOID
Os2InitializeInternalsFromRegistryDispatchFunction(
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 initialize internal NLS variables from the
values contained in the config.sys entry in the registry.
The values processed are:
COUNTRY=
CODEPAGE=
DEVINFO=KBD,
Arguments:
Standard arguments passed to a Dispatch Function, see the description of
Or2IterateEnvironment in ssrtl\consys.c
UserParameter - NULL.
Return Value:
None.
--*/
{
UNICODE_STRING Value_U;
PWSTR wp;
WCHAR wch;
switch (DispatchTableIndex) {
case 0:
Value_U.Buffer = Value;
Value_U.MaximumLength = Value_U.Length = (USHORT) (ValueLen * sizeof(WCHAR));
RtlUnicodeStringToInteger(&Value_U, 10L, &Os2ssCountryCode);
break;
case 1:
for (wp = Value; ValueLen > 0 && *wp != L','; wp++, ValueLen--) {
}
wch = *wp;
*wp = UNICODE_NULL;
RtlInitUnicodeString(&Value_U, Value);
RtlUnicodeStringToInteger(&Value_U, 10L, &Os2ssCodePage[0]);
*wp = wch;
if (ValueLen > 0) {
wp++;
ValueLen--;
Value_U.Buffer = wp;
Value_U.MaximumLength = Value_U.Length = (USHORT) (ValueLen * sizeof(WCHAR));
RtlUnicodeStringToInteger(&Value_U, 10L, &Os2ssCodePage[1]);
}
break;
case 2:
if (Or2UnicodeEqualCI(Value, L"KBD,", 4)) {
#if PMNT
int i;
#endif
Os2ssKeyboardLayout[0] = (CHAR) Value[4];
Os2ssKeyboardLayout[1] = (CHAR) Value[5];
#if PMNT
for (i=0,ValueLen -= 6;
(i < 4) && (ValueLen > 0) && ((CHAR)Value[6+i] != ',');
ValueLen--,i++)
{
Os2ssKeyboardName[i] = (CHAR) Value[6+i];
}
// Pad the rest of the array with blanks. A null char at end is
// not required.
for (; i < 4; i++)
Os2ssKeyboardName[i] = ' ';
#endif // PMNT
}
break;
}
}
VOID
Os2InitializeInternalsFromRegistry(
IN PWSTR RegConSys
)
/*++
Routine Description:
This routine is used to initialize internal variables from the
values contained in the config.sys entry in the registry.
Note: The "KEYS" setting is picked up from the system environment.
The processing is done using the dispatch function above.
Arguments:
RegConSys - a pointer to a UNICODE string containing the config.sys multistring entry.
Return Value:
None.
--*/
{
static ENVIRONMENT_DISPATCH_TABLE_ENTRY DispatchTable[] =
{
{ L"COUNTRY", L"=", Os2InitializeInternalsFromRegistryDispatchFunction, NULL },
{ L"CODEPAGE", L"=", Os2InitializeInternalsFromRegistryDispatchFunction, NULL },
{ L"DEVINFO", L"=", Os2InitializeInternalsFromRegistryDispatchFunction, NULL }
};
static RTL_QUERY_REGISTRY_TABLE QueryTable[] =
{
{ Os2EnvironmentKeysRoutine, RTL_QUERY_REGISTRY_NOEXPAND, L"KEYS", NULL, REG_NONE, NULL, 0 },
{ NULL, 0, NULL, NULL, REG_NONE, NULL, 0 }
};
Or2IterateEnvironment(RegConSys,
DispatchTable,
3,
NULL_DELIM);
//
// grab the KEYS setting from the environment
//
Os2ssKeysOnFlag = 0;
RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
L"Session Manager\\Environment",
QueryTable,
&Os2ssKeysOnFlag,
NULL
);
#if DBG
IF_OS2_DEBUG( NLS )
{
KdPrint(("Os2ssCountryCode = %lu\n", Os2ssCountryCode));
KdPrint(("Os2ssCodePage = %lu, %lu\n", Os2ssCodePage[0], Os2ssCodePage[1]));
KdPrint(("Os2ssKeyboardLayout = %c%c\n", Os2ssKeyboardLayout[0], Os2ssKeyboardLayout[1]));
KdPrint(("Os2ssKeysOnFlag = %lu\n", Os2ssKeysOnFlag));
}
#endif
}
NTSTATUS
Os2InitializeRegistry(
VOID
)
/*++
Routine Description:
This initialization function reads NLS info from the config.sys entry and system
environment in the registry to internal variables. It also initializes some
internal variables that are used for config.sys file handling.
Arguments:
None.
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 NLS read from the registry was not fully initialized.
--*/
{
HANDLE ConfigSysKeyHandle;
NTSTATUS Status;
ULONG ResultLength;
PKEY_VALUE_PARTIAL_INFORMATION ExistingConfigSysValueData;
KEY_VALUE_PARTIAL_INFORMATION ValuePartialInformation;
UNICODE_STRING ConfigSysName_U;
UNICODE_STRING ConfigSysKeyName_U;
OBJECT_ATTRIBUTES Obja;
// These will be intialized later from the registry
// zero them just in case we return with an error earlier
Os2ssCountryCode = 0;
Os2ssCodePage[0] = 0;
Os2ssCodePage[1] = 0;
Os2ssKeyboardLayout[0] = '\0';
Os2ssKeyboardLayout[1] = '\0';
Os2ssKeysOnFlag = 0;
// Initialize an internal name
#if OS2CONF_NAME_OPT
wcscat(Os2CanonicalConfigDotSys, Os2SystemDirectory);
#else
wcscat(Os2CanonicalConfigDotSys, L"C:");
#endif
wcscat(Os2CanonicalConfigDotSys, OS2CONF_NAMEW);
// Get the config.sys entry to read the NLS parms.
RtlInitUnicodeString(&ConfigSysKeyName_U, Os2ConfigSysKeyName);
InitializeObjectAttributes(&Obja,
&ConfigSysKeyName_U,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenKey(&ConfigSysKeyHandle,
KEY_READ,
&Obja
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG( INIT ) {
KdPrint(("Os2InitializeRegistry: Can't open config.sys key, rc = %lx\n",
Status));
}
#endif
return(Status);
}
RtlInitUnicodeString(&ConfigSysName_U, Os2ConfigSysName);
Status = NtQueryValueKey(ConfigSysKeyHandle,
&ConfigSysName_U,
KeyValuePartialInformation,
&ValuePartialInformation,
0,
&ResultLength
);
if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
#if DBG
IF_OS2_DEBUG( INIT ) {
KdPrint(("Os2InitializeRegistry: Can't read config.sys value, rc = %lx\n",
Status));
}
#endif
NtClose(ConfigSysKeyHandle);
return(Status);
}
ExistingConfigSysValueData = (PKEY_VALUE_PARTIAL_INFORMATION)
RtlAllocateHeap(Os2Heap, 0, ResultLength);
if (ExistingConfigSysValueData == NULL) { // no mem -- give up
#if DBG
IF_OS2_DEBUG( INIT ) {
KdPrint(("Os2InitializeRegistry: Failed allocation for registry config.sys\n"));
}
#endif
NtClose(ConfigSysKeyHandle);
return (STATUS_NO_MEMORY);
}
Status = NtQueryValueKey(ConfigSysKeyHandle,
&ConfigSysName_U,
KeyValuePartialInformation,
ExistingConfigSysValueData,
ResultLength,
&ResultLength
);
if (!NT_SUCCESS(Status)) { // give up
#if DBG
IF_OS2_DEBUG( INIT ) {
KdPrint(("Os2InitializeRegistry: Failed to read registry config.sys, rc = %lx\n", Status));
}
#endif
NtClose(ConfigSysKeyHandle);
return (Status);
}
// process the entry
Os2InitializeInternalsFromRegistry((PWSTR) ExistingConfigSysValueData->Data);
RtlFreeHeap(Os2Heap, 0, ExistingConfigSysValueData);
NtClose(ConfigSysKeyHandle);
return (STATUS_SUCCESS);
}
NTSTATUS
Os2AddToBuffer(
IN OUT PBUFFER_PASSAROUND BufP,
IN PSZ PreString,
IN PWSTR String,
IN BOOLEAN ExpandFirst
)
/*++
Routine Description:
This is a general routine used to copy strings to the os2conf.nt file
buffer. It is used by the routines below. It makes sure not to
overflow the buffer.
Arguments:
BufP -- The buffer to which the information should be appended.
PreString -- The 1st string to be appended to BufP.
String -- The 2nd string to be appended.
ExpandFirst -- If this is true then the 2nd string is expanded for
environment variables before it's copied over into the buffer.
Return Value:
NT Error code.
--*/
{
UNICODE_STRING Tmp_U, Tmp_DestU;
ANSI_STRING Tmp_A;
ULONG l;
NTSTATUS Status;
APIRET rc;
l = strlen(PreString);
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2AddToBuffer: Entered\n"));
}
#endif
if (BufP->Index + l + 3 > BufP->MaxSize) { // 3 for CR,LF,\0
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2AddToBuffer: Out of buffer space-1\n"));
}
#endif
return(STATUS_BUFFER_OVERFLOW);
}
RtlInitUnicodeString(&Tmp_U, String);
if (!ExpandFirst) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2AddToBuffer: Not expanding first\n"));
}
#endif
Tmp_A.Buffer = BufP->Buffer + BufP->Index + l;
Tmp_A.MaximumLength = (USHORT) (BufP->MaxSize - BufP->Index - l - 3);
rc = Or2UnicodeStringToMBString(&Tmp_A, &Tmp_U, FALSE);
if (rc != NO_ERROR) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2AddToBuffer: Out of buffer space-2, UnicodeStringtoMBString rc = %x\n", rc));
}
#endif
return(STATUS_BUFFER_OVERFLOW);
}
} else {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2AddToBuffer: Expanding first\n"));
}
#endif
Tmp_A.Buffer = BufP->Buffer + BufP->Index + l;
Tmp_A.MaximumLength = (USHORT)(BufP->MaxSize - BufP->Index - l - 3);
Tmp_DestU.MaximumLength = Tmp_A.MaximumLength;
Tmp_DestU.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, Tmp_DestU.MaximumLength * sizeof(WCHAR));
if (!Tmp_DestU.Buffer) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2AddToBuffer: RtlAllocateHeap rc = %lx\n", STATUS_NO_MEMORY));
}
#endif
return(STATUS_NO_MEMORY);
}
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2AddToBuffer: Ready to call Expand: <%ws> to %08x(%d)\n", Tmp_U.Buffer, Tmp_DestU.Buffer, Tmp_DestU.MaximumLength));
}
#endif
Status = RtlExpandEnvironmentStrings_U(NULL,
&Tmp_U,
&Tmp_DestU,
NULL);
if (NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2AddToBuffer: Expand succeeded: <%ws>\n", Tmp_DestU.Buffer));
}
#endif
Status = RtlUnicodeStringToOemString((POEM_STRING)&Tmp_A, &Tmp_DestU, FALSE);
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2AddToBuffer: After CV2OEM: <%s>\n", Tmp_A.Buffer));
}
#endif
}
RtlFreeHeap(RtlProcessHeap(), 0, Tmp_DestU.Buffer);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2AddToBuffer: Out of buffer space-3, RtlExpandEnvStrings rc = %lx\n", Status));
}
#endif
return(Status);
}
}
if (l != 0) {
RtlMoveMemory(BufP->Buffer + BufP->Index, PreString, l);
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2AddToBuffer: MoveMemory <%s>\n", BufP->Buffer));
}
#endif
}
BufP->Index += l + Tmp_A.Length;
BufP->Buffer[BufP->Index++] = '\r';
BufP->Buffer[BufP->Index++] = '\n';
return(STATUS_SUCCESS);
}
VOID
Os2ProcessConfigSysSetDirectivesDispatchFunction(
IN ULONG DispatchTableIndex,
IN PVOID UserParameter,
IN PWSTR Name,
IN ULONG NameLen,
IN PWSTR Value,
IN ULONG ValueLen
)
/*++
Routine Description:
This dispatch function copy set directives from the config.sys entry
in the registry to the os2conf.nt file buffer. Only special SET
commands are copied.
Arguments:
Standard arguments passed to a Dispatch Function, see the description of
Or2IterateEnvironment in ssrtl\consys.c
UserParameter - Buffer for os2conf.nt file.
Return Value:
None.
--*/
{
PBUFFER_PASSAROUND BufP = (PBUFFER_PASSAROUND) UserParameter;
if (Or2UnicodeEqualCI(Value, L"COMSPEC=", 8)) {
Os2AddToBuffer(BufP, "", Name, FALSE);
}
}
VOID
Os2ProcessConfigSysGeneralDirectivesDispatchFunction(
IN ULONG DispatchTableIndex,
IN PVOID UserParameter,
IN PWSTR Name,
IN ULONG NameLen,
IN PWSTR Value,
IN ULONG ValueLen
)
/*++
Routine Description:
This dispatch function copies general directives from the config.sys
entry in the registry into the os2conf.nt file buffer.
Arguments:
Standard arguments passed to a Dispatch Function, see the description of
Or2IterateEnvironment in ssrtl\consys.c
UserParameter - Buffer for os2conf.nt file.
Return Value:
None.
--*/
{
PBUFFER_PASSAROUND BufP = (PBUFFER_PASSAROUND) UserParameter;
Os2AddToBuffer(BufP, "", Value, FALSE);
}
NTSTATUS
Os2EnvironmentQueryRoutine(
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
/*++
Routine Description:
This is the query routine used to process the system environment.
It copies the variables into the os2conf.nt file in the correct
OS/2 config.sys format.
Arguments:
Standard parameter set for PRTL_QUERY_REGISTRY_ROUTINE, see <ntrtl.h>.
Return Value:
NT Error code.
--*/
{
PBUFFER_PASSAROUND BufP = (PBUFFER_PASSAROUND) Context;
ULONG SaveIndex;
NTSTATUS Status;
if (Or2UnicodeEqualCI(ValueName, L"COMSPEC", 8) ||
Or2UnicodeEqualCI(ValueName, L"LIBPATH", 8)) {
return(STATUS_SUCCESS);
}
// BUGBUG: we should probably do a semicolon check on PATH and LIBPATH at this point.
if (Or2UnicodeEqualCI(ValueName, L"OS2LIBPATH", 11)) {
return(Os2AddToBuffer(BufP, "LIBPATH=", (PWSTR) ValueData, TRUE));
} else if (Or2UnicodeEqualCI(ValueName, L"PATH", 5)) {
return(Os2AddToBuffer(BufP, "SET PATH=", (PWSTR) ValueData, TRUE));
}
SaveIndex = BufP->Index;
Status = Os2AddToBuffer(BufP, "SET ", ValueName, FALSE);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2EnvironmentQueryRoutine: Os2AddToBuffer-1 rc = %lx\n", Status));
}
#endif
return(Status);
}
BufP->Index -= 2; // remove the CRLF
Status = Os2AddToBuffer(BufP, "=", (PWSTR) ValueData, FALSE);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2EnvironmentQueryRoutine: Os2AddToBuffer-2 rc = %lx\n", Status));
}
#endif
BufP->Index = SaveIndex;
}
return(Status);
}
NTSTATUS
Os2ProcessConfigSys(
IN HANDLE EnvironmentKeyHandle,
IN PUNICODE_STRING ConfigSysValueData_U,
OUT PSZ Buffer,
IN OUT PULONG BufferLength
)
/*++
Routine Description:
This routine prepares the contents of os2conf.nt from the info in
the environment.
Arguments:
EnvironmentKeyHandle -- an open (READ) handle to the system environment.
ConfigSysValueData_U -- The contents of the config.sys entry in the registry.
Buffer -- A buffer to put the contents of os2conf.nt in.
BufferLength -- on entry, the maximum size of Buffer. On exit, the
final size of the info in Buffer.
Return Value:
An NT error code.
--*/
{
static ENVIRONMENT_DISPATCH_TABLE_ENTRY DispatchTable[] =
{
{ L"NTRE", L"M", NULL, NULL },
{ L"LIBPATH", L"=", NULL, NULL },
{ L"SET", L" \t", Os2ProcessConfigSysSetDirectivesDispatchFunction, NULL },
{ L"*", NULL, Os2ProcessConfigSysGeneralDirectivesDispatchFunction, NULL },
};
static RTL_QUERY_REGISTRY_TABLE QueryTable[] =
{
{ Os2EnvironmentQueryRoutine, RTL_QUERY_REGISTRY_NOEXPAND, NULL, NULL, REG_NONE, NULL, 0 },
{ NULL, 0, NULL, NULL, REG_NONE, NULL, 0 }
};
BUFFER_PASSAROUND BufP;
NTSTATUS Status;
BufP.Buffer = Buffer;
BufP.Index = 0;
BufP.MaxSize = *BufferLength;
DispatchTable[2].UserParameter = (PVOID) &BufP;
DispatchTable[3].UserParameter = (PVOID) &BufP;
Or2IterateEnvironment(ConfigSysValueData_U->Buffer,
DispatchTable,
4,
NULL_DELIM);
//
// now we enumerate the system environment and enter variables from there.
//
Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
(PWSTR)EnvironmentKeyHandle,
QueryTable,
(PVOID) &BufP,
NULL
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2ProcessConfigSys: Os2EnvironmentQueryRoutine rc = %lx\n", Status));
}
#endif
}
Buffer[BufP.Index] = '\0';
*BufferLength = BufP.Index;
return(STATUS_SUCCESS);
}
NTSTATUS
Os2GetFileStamps(
IN HANDLE hFile,
OUT PLARGE_INTEGER pTimeStamp,
OUT PLARGE_INTEGER pSizeStamp
)
/*++
Routine Description:
This function reads a file's size and time of last write.
Arguments:
hFile -- handle of file to read.
pTimeStamp -- returns file time of last write.
pSizeStamp -- returns file size.
Return Value:
NT error code. The return parameters are valid only if this is
success.
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatus;
FILE_BASIC_INFORMATION BasicInfo;
FILE_STANDARD_INFORMATION StandardInfo;
Status = NtQueryInformationFile(hFile,
&IoStatus,
&BasicInfo,
sizeof(BasicInfo),
FileBasicInformation);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2GetFileStamps: Unable to query file time, rc = %lx\n", Status));
}
#endif
return(Status);
}
*pTimeStamp = BasicInfo.LastWriteTime;
Status = NtQueryInformationFile(hFile,
&IoStatus,
&StandardInfo,
sizeof(StandardInfo),
FileStandardInformation);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2GetFileStamps: Unable to query file size, rc = %lx\n", Status));
}
#endif
return(Status);
}
*pSizeStamp = StandardInfo.EndOfFile;
return(STATUS_SUCCESS);
}
BOOLEAN
Os2ConfigSysCreator(
IN POS2_THREAD t,
IN POS2_API_MSG m
)
/*++
Routine Description:
This function implements the server API which creates the os2conf.nt
file for OS/2 apps to use.
Arguments:
t -- client thread that generated this API.
m -- an OS2_CONFIGSYS_MSG message type containing the request.
The message fields are as follows:
IN ULONG RequiredAccess -- access user desires
OUT ULONG AllowedAccess -- access allowed
OUT NTSTATUS ReturnStatus -- an error code
All access values are either OPEN_ACCESS_READONLY or OPEN_ACCESS_READWRITE.
Notes:
-- The os2conf.nt file has been created only if ReturnStatus is success
-- AllowedAccess is valid on return only if ReturnStatus is success or STATUS_ACCESS_DENIED
-- if RequiredAccess is READWRITE and only READONLY is available, the
os2conf.nt won't be created, ReturnStatus will be STATUS_ACCESS_DENIED,
and AllowedAccess will be READONLY.
Return Value:
TRUE to tell server to answer client.
--*/
{
POS2_CONFIGSYS_MSG a = &m->u.CreateConfigSysRequest;
NTSTATUS Status;
UNICODE_STRING ConfigSysKeyName_U;
UNICODE_STRING ConfigSysValueName_U;
UNICODE_STRING EnvironmentKeyName_U;
UNICODE_STRING ConfigSysValueData_U;
UNICODE_STRING CanonicalConfigDotSys_U;
HANDLE ConfigSysKeyHandle = NULL;
HANDLE EnvironmentKeyHandle = NULL;
HANDLE ConfigSysFileHandle;
ULONG ResultLength;
KEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
PKEY_VALUE_PARTIAL_INFORMATION pInfo = NULL;
FILE_BASIC_INFORMATION FileBasicInfo;
OBJECT_ATTRIBUTES Obja;
PSZ pMem = NULL;
IO_STATUS_BLOCK IoStatus;
//
// Validate parameters
//
if (a->RequiredAccess != OPEN_ACCESS_READONLY &&
a->RequiredAccess != OPEN_ACCESS_READWRITE) {
a->ReturnStatus = STATUS_INVALID_PARAMETER;
return(TRUE);
}
//
// if UsageCount for config.sys is > 0, then it already exists, and
// there is not much to do.
//
if (Os2ConfigSysUsageCount > 0) {
a->AllowedAccess = Os2ConfigSysAllowedAccess;
if (a->RequiredAccess == OPEN_ACCESS_READWRITE &&
Os2ConfigSysAllowedAccess == OPEN_ACCESS_READONLY) {
//
// we can't supply the required access
//
a->ReturnStatus = STATUS_ACCESS_DENIED;
} else {
if (!t->Process->ConfigSysUsageFlag) {
Os2ConfigSysUsageCount++;
t->Process->ConfigSysUsageFlag = TRUE;
}
a->ReturnStatus = STATUS_SUCCESS;
}
return(TRUE);
}
//
// Now for the real job --
// create a fresh os2conf.nt file from the registry information.
//
do { // A 1-time loop to allow break upon error
Os2ConfigSysAllowedAccess = a->AllowedAccess = OPEN_ACCESS_READONLY;
//
// First, allocate a large buffer for us to build config.sys for the
// user in.
//
pMem = (PSZ) RtlAllocateHeap(Os2Heap, 0, MAX_CONSYS_SIZE);
if (pMem == NULL) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2ConfigSysCreator: Unable to RtlAllocateHeap space for user file generation\n"));
}
#endif
Status = STATUS_NO_MEMORY;
break;
}
//
// Next, attempt to open the system environment and see if we have
// access of the desired type
//
RtlInitUnicodeString(&EnvironmentKeyName_U, Os2EnvironmentDirectory);
InitializeObjectAttributes(&Obja,
&EnvironmentKeyName_U,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenKey(&EnvironmentKeyHandle,
KEY_READ | KEY_WRITE,
&Obja
);
if (!NT_SUCCESS(Status))
{
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2ConfigSysCreator: Unable to NtOpenKey() the system environment for writing, rc = %lx\n",
Status));
}
#endif
if (Status != STATUS_ACCESS_DENIED ||
a->RequiredAccess == OPEN_ACCESS_READWRITE) {
break;
}
//
// retry opening it for reading
//
Status = NtOpenKey(&EnvironmentKeyHandle,
KEY_READ,
&Obja
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2ConfigSysCreator: Unable to NtOpenKey() the system environment for reading, rc = %lx\n",
Status));
}
#endif
break;
}
} else {
Os2ConfigSysAllowedAccess = a->AllowedAccess = OPEN_ACCESS_READWRITE;
}
//
// Attempt to open the config.sys key and read in the key
//
RtlInitUnicodeString(&ConfigSysKeyName_U, Os2ConfigSysKeyName);
InitializeObjectAttributes(&Obja,
&ConfigSysKeyName_U,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenKey(&ConfigSysKeyHandle,
KEY_READ,
&Obja
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2ConfigSysCreator: Unable to NtOpenKey() of config.sys %lx\n",
Status));
}
#endif
//
// If the config.sys entry has disappeared for some reason, we return
// STATUS_ACCESS_DENIED. This way the os/2 program will get a less
// confusing error code (ERROR_ACCESS_DENIED) than ERROR_FILE_NOT_FOUND.
// (note that ERROR_FILE_NOT_FOUND would've been returned even if the
// os/2 program had tried to create the file!)
//
if (Status == STATUS_OBJECT_NAME_NOT_FOUND ||
Status == STATUS_OBJECT_PATH_NOT_FOUND ||
Status == STATUS_OBJECT_PATH_INVALID) {
Status = STATUS_ACCESS_DENIED;
}
break;
}
RtlInitUnicodeString(&ConfigSysValueName_U, Os2ConfigSysName);
Status = NtQueryValueKey(ConfigSysKeyHandle,
&ConfigSysValueName_U,
KeyValuePartialInformation,
&KeyValuePartialInfo,
sizeof(KeyValuePartialInfo),
&ResultLength
);
if (!NT_SUCCESS(Status) && (Status != STATUS_BUFFER_OVERFLOW)) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2ConfigSysCreator: Unable to NtQueryValueKey()-1 config.sys %lx\n",
Status));
}
#endif
//
// same error code translation as the openkey above
//
if (Status == STATUS_OBJECT_NAME_NOT_FOUND ||
Status == STATUS_OBJECT_PATH_NOT_FOUND ||
Status == STATUS_OBJECT_PATH_INVALID) {
Status = STATUS_ACCESS_DENIED;
}
break;
}
pInfo = (PKEY_VALUE_PARTIAL_INFORMATION)RtlAllocateHeap(Os2Heap, 0, ResultLength);
if (pInfo == NULL) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2ConfigSysCreator: Unable to RtlAllocateHeap space for config.sys entry\n"));
}
#endif
break;
}
Status = NtQueryValueKey(ConfigSysKeyHandle,
&ConfigSysValueName_U,
KeyValuePartialInformation,
pInfo,
ResultLength,
&ResultLength
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2ConfigSysCreator: Unable to NtQueryValueKey()-2 config.sys %lx\n",
Status));
}
#endif
break;
}
ConfigSysValueData_U.Buffer = (PWSTR) pInfo->Data;
ConfigSysValueData_U.MaximumLength = ConfigSysValueData_U.Length = (USHORT) pInfo->DataLength;
NtClose(ConfigSysKeyHandle);
ConfigSysKeyHandle = NULL;
//
// We now have all we need:
// an open handle to the system environment
// a copy of the config.sys registry entry.
// a buffer to build the user's file in.
ResultLength = MAX_CONSYS_SIZE;
Status = Os2ProcessConfigSys(EnvironmentKeyHandle,
&ConfigSysValueData_U,
pMem,
&ResultLength
);
RtlFreeHeap(Os2Heap, 0, pInfo);
pInfo = NULL;
NtClose(EnvironmentKeyHandle);
EnvironmentKeyHandle = NULL;
if (!NT_SUCCESS(Status)) { // Od2ProcessConfigSys failed?
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2ConfigSysCreator: Os2ProcessConfigSys has faild, rc = %lx\n",
Status));
}
#endif
break;
}
//
// The user's file is now ready in pMem
// Write it to a file.
//
RtlInitUnicodeString(&CanonicalConfigDotSys_U, Os2CanonicalConfigDotSys);
InitializeObjectAttributes(&Obja,
&CanonicalConfigDotSys_U,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtCreateFile(&ConfigSysFileHandle,
FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES,
&Obja,
&IoStatus,
NULL,
Os2ConfigSysAllowedAccess == OPEN_ACCESS_READWRITE ?
FILE_ATTRIBUTE_NORMAL :
FILE_ATTRIBUTE_READONLY,
0,
FILE_OVERWRITE_IF,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2ConfigSysCreator: Unable to NtCreateFile-1() os2conf.nt, rc = %lx\n",
Status));
}
#endif
if (Status != STATUS_ACCESS_DENIED) {
break;
}
//
// The file may be a read-only file. We'll attempt to overwrite it.
//
Status = NtOpenFile(&ConfigSysFileHandle,
STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE |
SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
&Obja,
&IoStatus,
FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2ConfigSysCreator: Unable to NtOpenFile-2() os2conf.nt, rc = %lx\n",
Status));
}
#endif
break;
}
Status = NtQueryInformationFile(ConfigSysFileHandle,
&IoStatus,
&FileBasicInfo,
sizeof(FileBasicInfo),
FileBasicInformation
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2ConfigSysCreator: Rc from NtQueryInformationFile(Attrib) %lx\n", Status));
}
#endif
NtClose(ConfigSysFileHandle);
break;
}
if ((FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_READONLY) != 0) {
FileBasicInfo.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
Status = NtSetInformationFile(ConfigSysFileHandle,
&IoStatus,
&FileBasicInfo,
sizeof(FileBasicInfo),
FileBasicInformation
);
NtClose(ConfigSysFileHandle);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2ConfigSysCreator: Rc from NtSetInformationFile(Attrib) %lx\n", Status));
}
#endif
break;
}
} else {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2ConfigSysCreator: NtCreateFile-1() did not fail because of readonly\n"));
}
#endif
NtClose(ConfigSysFileHandle);
Status = STATUS_ACCESS_DENIED;
break;
}
//
// We made the file read-write, now try recreating.
//
Status = NtCreateFile(&ConfigSysFileHandle,
FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES,
&Obja,
&IoStatus,
NULL,
Os2ConfigSysAllowedAccess == OPEN_ACCESS_READWRITE ?
FILE_ATTRIBUTE_NORMAL :
FILE_ATTRIBUTE_READONLY,
0,
FILE_OVERWRITE_IF,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2ConfigSysCreator: Unable to NtCreateFile-3() os2conf.nt, rc = %lx\n",
Status));
}
#endif
break;
}
//
// finally succeeded
//
}
Status = NtWriteFile(ConfigSysFileHandle,
NULL,
NULL,
NULL,
&IoStatus,
pMem,
ResultLength,
NULL,
NULL
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2ConfigSysCreator: Unable to NtWriteFile() os2conf.nt, rc = %lx\n",
Status));
}
#endif
NtClose(ConfigSysFileHandle);
break;
}
RtlFreeHeap(Os2Heap, 0, pMem);
pMem = NULL;
//
// Get file and size stamps for the file
//
Status = Os2GetFileStamps(ConfigSysFileHandle, &Os2ConfigSysTimeStamp, &Os2ConfigSysSizeStamp);
NtClose(ConfigSysFileHandle);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2ConfigSysCreator: Os2GetFileStamps() on os2conf.nt has failed, rc = %lx\n",
Status));
}
#endif
break;
}
//
// We've succeeded.
//
Os2ConfigSysUsageCount++;
t->Process->ConfigSysUsageFlag = TRUE;
a->ReturnStatus = STATUS_SUCCESS;
} while (FALSE);
if (Os2ConfigSysUsageCount == 0)
{
// Creation of os2conf.nt failed
// Close all handles and buffers
// returns TRUE and sends the last error to the caller
if (EnvironmentKeyHandle != NULL)
{
NtClose(EnvironmentKeyHandle);
}
if (ConfigSysKeyHandle != NULL)
{
NtClose(ConfigSysKeyHandle);
}
if (pInfo != NULL)
{
RtlFreeHeap(Os2Heap, 0, pInfo);
}
if (pMem != NULL)
{
RtlFreeHeap(Os2Heap, 0, pMem);
}
a->ReturnStatus = Status;
}
return(TRUE);
}
VOID
Os2PreProcessingDispatchFunction(
IN ULONG DispatchTableIndex,
IN PVOID UserParameter,
IN PWSTR Name,
IN ULONG NameLen,
IN PWSTR Value,
IN ULONG ValueLen
)
/*++
Routine Description:
This dispatch function is used to copy some standard stuff which resides in the registry
config.sys to the new registry config.sys that is being built from os2conf.nt.
Arguments:
Standard arguments passed to a Dispatch Function, see the description of
Or2IterateEnvironment in ssrtl\consys.c
UserParameter - a pointer to the pointer that indicates where in the new config.sys
buffer we should fill in the information.
Return Value:
None.
--*/
{
PWSTR *BufferPtr = (PWSTR *) UserParameter;
if (DispatchTableIndex == 1 &&
!Or2UnicodeEqualCI(Value, L"PATH=", 5)) {
return;
}
wcscpy(*BufferPtr, Name);
*BufferPtr += wcslen(Name) + 1;
}
VOID
Os2PreProcessExistingConfigSys(
IN OUT PWSTR *BufferPtr,
IN PUNICODE_STRING ConfigSysValueData_U
)
/*++
Routine Description:
This routine goes over the old config.sys entry and copies over some old stuff
to the new config.sys entry.
Arguments:
BufferPtr -- a pointer into the buffer that receives the new config.sys info.
ConfigSysValueData_U -- a counted unicode string containing the old MULTI-SZ
config.sys registry value.
Return Value:
None.
--*/
{
ULONG i;
static ENVIRONMENT_DISPATCH_TABLE_ENTRY DispatchTable[] =
{
{ L"NTRE", L"M", Os2PreProcessingDispatchFunction, NULL },
{ L"SET", L" \t", Os2PreProcessingDispatchFunction, NULL },
{ L"LIBPATH", L"=", Os2PreProcessingDispatchFunction, NULL }
};
for (i = 0; i < 3; i++) {
DispatchTable[i].UserParameter = (PVOID) BufferPtr;
}
Or2IterateEnvironment(ConfigSysValueData_U->Buffer,
DispatchTable,
3,
NULL_DELIM);
}
NTSTATUS
Os2WipeOutEnvironmentQueryRoutine(
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
/*++
Routine Description:
This routine is used in conjunction with the next one in order to wipe out the
system environment of the old variables.
Arguments:
Standard parameter set for PRTL_QUERY_REGISTRY_ROUTINE, see <ntrtl.h>.
Return Value:
NT Error code.
--*/
{
NTSTATUS Status;
UNICODE_STRING ValueName_U;
if (Or2UnicodeEqualCI(ValueName, L"COMSPEC", 8) ||
Or2UnicodeEqualCI(ValueName, L"LIBPATH", 8) ||
Or2UnicodeEqualCI(ValueName, L"PATH", 5) ||
Or2UnicodeEqualCI(ValueName, L"OS2LIBPATH", 11)) {
return(STATUS_SUCCESS);
}
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2WipeOutEnvironmentQueryRoutine: calling NtDeleteValueKey(%lx, %ws)\n", (ULONG)Context, ValueName));
}
#endif
RtlInitUnicodeString(&ValueName_U, ValueName);
Status = NtDeleteValueKey((HANDLE)Context,
&ValueName_U
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2WipeOutEnvironmentQueryRoutine: Rc from NtDeleteValueKey = %lx\n", Status));
}
#endif
} else {
*(PBOOLEAN) EntryContext = TRUE;
}
return(Status);
}
VOID
Os2WipeOutEnvironment(
IN HANDLE EnvironmentKeyHandle
)
/*++
Routine Description:
This routine is used to wipe out the system environment variables in preparation for setting
new ones from os2conf.nt.
Arguments:
EnvironmentKeyHandle -- a writable handle to the system env key in the registry.
Return Value:
None.
--*/
{
BOOLEAN SomeDeleted;
NTSTATUS Status;
static RTL_QUERY_REGISTRY_TABLE QueryTable[] =
{
{ Os2WipeOutEnvironmentQueryRoutine, RTL_QUERY_REGISTRY_NOEXPAND, NULL, NULL, REG_NONE, NULL, 0 },
{ NULL, 0, NULL, NULL, REG_NONE, NULL, 0 }
};
//
// We do it in a loop until we finally get all variables deleted.
// The reason we need a loop and that one time is not enough is
// that we're deleting values while they're being enumerated.
// This causes the enumeration to work incorrectly, and the effect
// is that not all variables that should be deleted are deleted.
// However, each run thru the loop deletes at least one variable,
// and so we'll finally get all of them deleted.
//
QueryTable[0].EntryContext = (PVOID) &SomeDeleted;
do {
SomeDeleted = FALSE;
Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
(PWSTR)EnvironmentKeyHandle,
QueryTable,
(PVOID)EnvironmentKeyHandle,
NULL
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2WipeOutEnvironment: Rc from RtlQueryRegistryValues = %lx\n", Status));
}
#endif
break;
}
} while (SomeDeleted);
//
// ignore errors when wiping out environment
//
}
VOID
Os2UpdateRegistryDispatchFunction(
IN ULONG DispatchTableIndex,
IN PVOID UserParameter,
IN PWSTR Name,
IN ULONG NameLen,
IN PWSTR Value,
IN ULONG ValueLen
)
/*++
Routine Description:
This routine is used to process the contents of the os2conf.nt file. SET variables are entered
into the system environment. Most other directives are entered in the registry config.sys value.
PATH and LIBPATH are recorded so they can later be processed and entered into the environment.
(actually, LIBPATH is processed directly by Or2FillInSearchRecordDispatchFunction)
Arguments:
Standard arguments passed to a Dispatch Function, see the description of
Or2IterateEnvironment in ssrtl\consys.c
UserParameter - a pointer to an array Params of ULONGs containing the following info:
Params[0] : (PWSTR *) BufferPtr -- a pointer to the buffer for filling in the new config.sys key
Params[1] : (HANDLE) EnvironmentKeyHandle -- a handle with WRITE access to the sys env
Params[2] : (PENVIRONMENT_SEARCH_RECORD) PRec -- a search record for filling info about PATH statement
Params[3] : (BOOLEAN) RetVal -- will receive the final success status of the operation
(should be preinitialized to TRUE)
Return Value:
None.
--*/
{
PULONG UP = (PULONG) UserParameter;
PWSTR *BufferPtr;
PWSTR NewV; // for preparing new subvalue of "SET PATH="
HANDLE EnvironmentKeyHandle;
PVOID PRec;
UNICODE_STRING Name_U;
NTSTATUS Status;
WCHAR wch;
switch (DispatchTableIndex) {
case 1:
//
// process SET
//
if (Or2UnicodeEqualCI(Value, L"LIBPATH=", 8) || // ignore "SET LIBPATH="
Or2UnicodeEqualCI(Value, L"OS2LIBPATH=", 11)) { // and "SET OS2LIBPATH="
break;
}
if (Or2UnicodeEqualCI(Value, L"PATH=", 5)) { // special handling of path
PRec = (PVOID) (UP[2]);
NewV = Value + 5; // skip over "PATH="
ValueLen -= 5;
//
// skip over whitespace
//
while (ValueLen > 0 && (*NewV == L' ' || *NewV == L'\t')) {
NewV++;
ValueLen--;
}
Or2FillInSearchRecordDispatchFunction(DispatchTableIndex,
PRec,
Value,
4, // length of "PATH"
NewV,
ValueLen
);
break;
}
if (Or2UnicodeEqualCI(Value, L"COMSPEC=", 8)) {
//
// these special variables should be copied to config.sys
// instead of the system environment
//
BufferPtr = (PWSTR *) (UP[0]);
RtlMoveMemory(*BufferPtr, L"SET ", 8);
*BufferPtr += 4;
RtlMoveMemory(*BufferPtr, Value, ValueLen * sizeof(WCHAR));
*BufferPtr += ValueLen;
**BufferPtr = UNICODE_NULL;
(*BufferPtr)++;
break;
}
//
// The rest of the variables are just put into the sys env as they are
//
Name_U.Buffer = Value;
Name_U.Length = 0;
while (ValueLen > 0 &&
*Value != L'=')
{
Value++;
ValueLen--;
Name_U.Length += sizeof(WCHAR);
}
if (Name_U.Length == 0 || // empty name
ValueLen == 0) { // no "=" in string
break;
}
Name_U.MaximumLength = Name_U.Length;
// Here, we have a valid name, followed by '='
Value++; // Skip the '='
ValueLen--;
wch = Value[ValueLen];
Value[ValueLen] = UNICODE_NULL;
//
// Set the system wide variable to the value specified
// in the config.sys by the OS/2 program
//
EnvironmentKeyHandle = (HANDLE) (UP[1]);
Status = NtSetValueKey(EnvironmentKeyHandle,
&Name_U,
(ULONG)0,
REG_EXPAND_SZ,
Value,
(ValueLen + 1) * sizeof(WCHAR)
);
Value[ValueLen] = wch;
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2UpdateRegistryDispatchFunction: Unable to NtSetValueKey() system env from config.sys, Status = %X\n", Status));
}
#endif
UP[3] = (ULONG) FALSE;
break;
}
break;
case 2:
//
// process everything else -- copy it to buffer
//
BufferPtr = (PWSTR *) (UP[0]);
RtlMoveMemory(*BufferPtr, Value, ValueLen * sizeof(WCHAR));
*BufferPtr += ValueLen;
**BufferPtr = UNICODE_NULL;
(*BufferPtr)++;
break;
}
}
BOOLEAN
Os2UpdateRegistryFromSource(
IN OUT PWSTR *BufferPtr,
IN HANDLE EnvironmentKeyHandle,
IN PUNICODE_STRING ConfigSysSrc
)
/*++
Routine Description:
This routine prepares the new config.sys entry, and enters new system environment variables
from the os2conf.nt file source.
Arguments:
BufferPtr -- a pointer into the new config.sys buffer where the stuff is to be appended.
EnvironmentKeyHandle -- a handle to the sys env for setting new vars.
ConfigSysSrc -- a counted unicode string containing the os2conf.nt source.
Return Value:
TRUE on success, FALSE on failure.
--*/
{
//
// Most of the work is done by the dispatch routine above.
//
static ENVIRONMENT_DISPATCH_TABLE_ENTRY DispatchTable[] =
{
{ L"LIBPATH", L"=", Or2FillInSearchRecordDispatchFunction, NULL },
{ L"SET", L" \t", Os2UpdateRegistryDispatchFunction, NULL },
{ L"*", NULL, Os2UpdateRegistryDispatchFunction, NULL },
};
ENVIRONMENT_SEARCH_RECORD LPRec;
ENVIRONMENT_SEARCH_RECORD PRec;
UNICODE_STRING LPData;
UNICODE_STRING PData;
UNICODE_STRING LPathValueName_U;
UNICODE_STRING PathValueName_U;
ULONG i;
ULONG Params[4];
NTSTATUS Status;
WCHAR ch;
Params[0] = (ULONG) BufferPtr;
Params[1] = (ULONG) EnvironmentKeyHandle;
Params[2] = (ULONG) &PRec;
Params[3] = (ULONG) TRUE;
DispatchTable[0].UserParameter = (PVOID)&LPRec;
LPRec.DispatchTableIndex = (ULONG)-1;
PRec.DispatchTableIndex = (ULONG)-1;
for (i = 1; i < 3; i++) {
DispatchTable[i].UserParameter = (PVOID) Params;
}
Or2IterateEnvironment(ConfigSysSrc->Buffer,
DispatchTable,
3,
CRLF_DELIM);
//
// add the terminating null
//
**BufferPtr = UNICODE_NULL;
(*BufferPtr)++;
//
// process LIBPATH and PATH
//
if (LPRec.DispatchTableIndex != (ULONG)-1) {
// handle LIBPATH if there was one
if (Or2GetEnvPath(&LPData,
Os2Heap,
PATHLIST_MAX,
EnvironmentKeyHandle,
Os2LibPathValueName,
FALSE)) {
ch = LPRec.Value[LPRec.ValueLen];
LPRec.Value[LPRec.ValueLen] = UNICODE_NULL;
Or2ReplacePathByPath(Os2Heap, LPRec.Value, &LPData);
LPRec.Value[LPRec.ValueLen] = ch;
Or2CheckSemicolon(&LPData);
RtlInitUnicodeString(&LPathValueName_U, Os2LibPathValueName);
Status = NtSetValueKey(EnvironmentKeyHandle,
&LPathValueName_U,
(ULONG)0,
REG_EXPAND_SZ,
LPData.Buffer,
LPData.Length + sizeof(WCHAR)
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2UpdateRegistryFromSource: Unable to NtSetValueKey() Environment Os2LibPath, Status = %X\n", Status));
}
#endif
Params[3] = (ULONG) FALSE;
}
RtlFreeHeap(Os2Heap, 0, LPData.Buffer);
} else {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2UpdateRegistryFromSource: Unable to get system Os2LibPath, skipping\n"));
}
#endif
Params[3] = (ULONG) FALSE;
}
}
if (PRec.DispatchTableIndex != (ULONG)-1) {
// handle PATH if there was one
if (Or2GetEnvPath(&PData,
Os2Heap,
PATHLIST_MAX,
EnvironmentKeyHandle,
PathValueName,
FALSE)) {
ch = PRec.Value[PRec.ValueLen];
PRec.Value[PRec.ValueLen] = UNICODE_NULL;
Or2ReplacePathByPath(Os2Heap, PRec.Value, &PData);
PRec.Value[PRec.ValueLen] = ch;
Or2CheckSemicolon(&PData);
RtlInitUnicodeString(&PathValueName_U, PathValueName);
Status = NtSetValueKey(EnvironmentKeyHandle,
&PathValueName_U,
(ULONG)0,
REG_EXPAND_SZ,
PData.Buffer,
PData.Length + sizeof(WCHAR)
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2UpdateRegistryFromSource: Unable to NtSetValueKey() Environment Path, Status = %X\n", Status));
}
#endif
Params[3] = (ULONG) FALSE;
}
RtlFreeHeap(Os2Heap, 0, PData.Buffer);
} else {
#if DBG
IF_OS2_DEBUG(MISC) {
KdPrint(("Os2UpdateRegistryFromSource: Unable to get system Path, skipping\n"));
}
#endif
Params[3] = (ULONG) FALSE;
}
}
return((BOOLEAN)Params[3]);
}
BOOLEAN
Os2UpdateRegistryAll(
IN PUNICODE_STRING Src
)
/*++
Routine Description:
This routine actually updates the registry after initial preparation by
Os2UpdateRegistryFromConfigSys.
Arguments:
Src -- supplies a counted unicode string that contains the contents of os2conf.nt
The registry is updated with these contents.
Return Value:
TRUE on success. FALSE on failure (in which case the registry was either not updated at
all or partially updated).
--*/
{
PWSTR NewConfigSys;
PWSTR NewConfigSysPtr;
HANDLE ConfigSysKeyHandle;
HANDLE EnvironmentKeyHandle;
UNICODE_STRING EnvironmentRegDir_U;
UNICODE_STRING ConfigSysKeyName_U;
UNICODE_STRING ConfigSysValueName_U;
UNICODE_STRING ConfigSysValueData_U;
OBJECT_ATTRIBUTES Obja;
NTSTATUS Status;
ULONG ResultLength;
KEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
PKEY_VALUE_PARTIAL_INFORMATION pConfigSysOldValue;
//
// Allocate a buffer for building a new config.sys key
//
NewConfigSys = (PWSTR) RtlAllocateHeap(Os2Heap, 0, MAX_CONSYS_SIZE);
if (NewConfigSys == NULL) {
#if DBG
IF_OS2_DEBUG( MISC ) {
KdPrint(("Os2UpdateRegistryAll: Unable to allocate space for new config.sys entry\n"));
}
#endif
return(FALSE);
}
//
// Read in the old config.sys registry entry
//
RtlInitUnicodeString(&ConfigSysKeyName_U, Os2ConfigSysKeyName);
InitializeObjectAttributes(&Obja,
&ConfigSysKeyName_U,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenKey(&ConfigSysKeyHandle,
KEY_READ | KEY_WRITE,
&Obja
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2UpdateRegistryAll: Unable to NtOpenKey() of config.sys %lx\n",
Status));
}
#endif
RtlFreeHeap(Os2Heap, 0, NewConfigSys);
return(FALSE);
}
RtlInitUnicodeString(&ConfigSysValueName_U, Os2ConfigSysName);
Status = NtQueryValueKey(ConfigSysKeyHandle,
&ConfigSysValueName_U,
KeyValuePartialInformation,
&KeyValuePartialInfo,
sizeof(KeyValuePartialInfo),
&ResultLength
);
if (!NT_SUCCESS(Status) && (Status != STATUS_BUFFER_OVERFLOW)) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2UpdateRegistryAll: Unable to NtQueryValueKey()-1 config.sys %lx\n",
Status));
}
#endif
NtClose(ConfigSysKeyHandle);
RtlFreeHeap(Os2Heap, 0, NewConfigSys);
return(FALSE);
}
pConfigSysOldValue = (PKEY_VALUE_PARTIAL_INFORMATION)RtlAllocateHeap(Os2Heap, 0, ResultLength);
if (pConfigSysOldValue == NULL) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2UpdateRegistryAll: Unable to RtlAllocateHeap space for old config.sys entry\n"));
}
#endif
NtClose(ConfigSysKeyHandle);
RtlFreeHeap(Os2Heap, 0, NewConfigSys);
return(FALSE);
}
Status = NtQueryValueKey(ConfigSysKeyHandle,
&ConfigSysValueName_U,
KeyValuePartialInformation,
pConfigSysOldValue,
ResultLength,
&ResultLength
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG(MISC){
KdPrint(("Os2UpdateRegistryAll: Unable to NtQueryValueKey()-2 config.sys %lx\n",
Status));
}
#endif
RtlFreeHeap(Os2Heap, 0, pConfigSysOldValue);
NtClose(ConfigSysKeyHandle);
RtlFreeHeap(Os2Heap, 0, NewConfigSys);
return(FALSE);
}
ConfigSysValueData_U.Buffer = (PWSTR) pConfigSysOldValue->Data;
ConfigSysValueData_U.MaximumLength = ConfigSysValueData_U.Length = (USHORT) pConfigSysOldValue->DataLength;
//
// Now open the system environment
//
RtlInitUnicodeString(&EnvironmentRegDir_U, Os2EnvironmentDirectory);
InitializeObjectAttributes(&Obja,
&EnvironmentRegDir_U,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenKey(&EnvironmentKeyHandle,
KEY_READ | KEY_WRITE,
&Obja
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG( MISC ) {
KdPrint(("Os2UpdateRegistryAll: Unable to NtOpenKey() System Environment, Status = %lx\n", Status));
}
#endif
RtlFreeHeap(Os2Heap, 0, pConfigSysOldValue);
NtClose(ConfigSysKeyHandle);
RtlFreeHeap(Os2Heap, 0, NewConfigSys);
return(FALSE);
}
//
// Now Proprocess the existing config.sys
//
NewConfigSysPtr = NewConfigSys;
Os2PreProcessExistingConfigSys(&NewConfigSysPtr, &ConfigSysValueData_U);
RtlFreeHeap(Os2Heap, 0, pConfigSysOldValue);
//
// Now wipe out old environment.
//
Os2WipeOutEnvironment(EnvironmentKeyHandle);
//
// Now go through the source text, updating the environment
// and config.sys registry value with the contents
//
if (!Os2UpdateRegistryFromSource(&NewConfigSysPtr, EnvironmentKeyHandle, Src)) {
#if DBG
IF_OS2_DEBUG( MISC ) {
KdPrint(("Os2UpdateRegistryAll: FAILED to Os2UpdateRegistryFromSource\n"));
}
#endif
NtClose(EnvironmentKeyHandle);
NtClose(ConfigSysKeyHandle);
RtlFreeHeap(Os2Heap, 0, NewConfigSys);
return(FALSE);
}
//
// finished with the environment
//
NtClose(EnvironmentKeyHandle);
//
// write the new config.sys entry.
//
Status = NtSetValueKey(ConfigSysKeyHandle,
&ConfigSysValueName_U,
(ULONG)0,
REG_MULTI_SZ,
NewConfigSys,
(PSZ)NewConfigSysPtr - (PSZ)NewConfigSys
);
NtClose(ConfigSysKeyHandle);
RtlFreeHeap(Os2Heap, 0, NewConfigSys);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OS2_DEBUG( MISC ) {
KdPrint(("Os2UpdateRegistryAll: Unable to write new config.sys key value, Status = %lx\n", Status));
}
#endif
return (FALSE);
}
return (TRUE);
}
NTSTATUS
Os2MarkFileForDeletion(
IN HANDLE FileHandle
)
/*++
Routine Description:
This routine marks an open file for deletion. It makes sure the file is not R/O before
doing so. The file must have been opened with FILE_WRITE_ATTRIBUTES and DELETE access
permissions.
Arguments:
FileHandle -- supplies the file to process
Return Value:
NT Error Code.
--*/
{
IO_STATUS_BLOCK IoStatus;
FILE_DISPOSITION_INFORMATION FileDispositionInfo;
FILE_BASIC_INFORMATION FileBasicInfo;
NTSTATUS Status;
//
// make sure file is not readonly
//
Status = NtQueryInformationFile(FileHandle,
&IoStatus,
&FileBasicInfo,
sizeof(FileBasicInfo),
FileBasicInformation
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2MarkFileForDeletion: Rc from NtQueryInformationFile(Attrib) %lx\n", Status));
}
#endif
return(Status);
}
if ((FileBasicInfo.FileAttributes & FILE_ATTRIBUTE_READONLY) != 0) {
FileBasicInfo.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
Status = NtSetInformationFile(FileHandle,
&IoStatus,
&FileBasicInfo,
sizeof(FileBasicInfo),
FileBasicInformation
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2MarkFileForDeletion: Rc from NtSetInformationFile(Attrib) %lx\n", Status));
}
#endif
return(Status);
}
}
//
// mark the file for deletion
//
FileDispositionInfo.DeleteFile = TRUE;
Status = NtSetInformationFile(FileHandle,
&IoStatus,
&FileDispositionInfo,
sizeof(FileDispositionInfo),
FileDispositionInformation
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2MarkFileForDeletion: Rc from NtSetInformationFile(Delete) %lx\n", Status));
}
#endif
}
return(Status);
}
VOID
Os2UpdateRegistryFromConfigSys(
VOID
)
/*++
Routine Description:
This routine takes the os2conf.nt file and updates the information in the registry. It is
called every time a client that opened os2conf.nt terminates. It also updates the usage
count. If the usage count is zero, os2conf.nt is deleted.
Upon errors, the usage counted is updated, but the registry may not be updated.
Arguments:
None.
Return Value:
None.
--*/
{
HANDLE ConfigSysFileHandle;
UNICODE_STRING CanonicalConfigDotSys_U;
LARGE_INTEGER NewConfigSysTimeStamp;
LARGE_INTEGER NewConfigSysSizeStamp;
ULONG SizeOfConfigSys;
OBJECT_ATTRIBUTES Obja;
PSZ pInfo;
ANSI_STRING Info_A;
UNICODE_STRING Info_U;
NTSTATUS Status;
IO_STATUS_BLOCK IoStatus;
BOOLEAN DeleteFlag = FALSE;
APIRET RetCode;
//
// update usage count
//
if (Os2ConfigSysUsageCount == 0) {
return;
}
Os2ConfigSysUsageCount--;
if (Os2ConfigSysAllowedAccess == OPEN_ACCESS_READONLY &&
Os2ConfigSysUsageCount > 0) {
return;
}
RtlInitUnicodeString(&CanonicalConfigDotSys_U, Os2CanonicalConfigDotSys);
InitializeObjectAttributes(&Obja,
&CanonicalConfigDotSys_U,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenFile(&ConfigSysFileHandle,
FILE_GENERIC_READ |
( Os2ConfigSysUsageCount == 0 ? (FILE_WRITE_ATTRIBUTES | DELETE) : 0 ),
&Obja,
&IoStatus,
FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2UpdateRegistryFromConfigSys: FAILED - NtOpenFile-1 %lx\n", Status));
}
#endif
if (Status == STATUS_OBJECT_NAME_NOT_FOUND || Os2ConfigSysUsageCount > 0) {
//
// can't update the registry, abandon
//
return;
}
Status = NtOpenFile(&ConfigSysFileHandle,
FILE_GENERIC_READ,
&Obja,
&IoStatus,
FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT
);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2UpdateRegistryFromConfigSys: FAILED - NtOpenFile-2 %lx\n", Status));
}
#endif
//
// can't update the registry, abandon
//
return;
}
} else if (Os2ConfigSysUsageCount == 0) {
DeleteFlag = TRUE; // flag file for deletion
}
//
// If our access is READONLY, we mark the file for deletion,
// close it, and quit.
//
if (Os2ConfigSysAllowedAccess == OPEN_ACCESS_READONLY) {
if (DeleteFlag) {
(VOID) Os2MarkFileForDeletion(ConfigSysFileHandle);
//
// ignore deletion errors
//
}
NtClose(ConfigSysFileHandle);
return;
}
Status = Os2GetFileStamps(ConfigSysFileHandle, &NewConfigSysTimeStamp, &NewConfigSysSizeStamp);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2UpdateRegistryFromConfigSys: failed to query config.sys size and time %lx\n", Status));
}
#endif
//
// we cancel the update operation. however, we still mark the
// file for deletion if necessary.
//
if (DeleteFlag) {
(VOID) Os2MarkFileForDeletion(ConfigSysFileHandle);
//
// ignore deletion errors
//
}
NtClose(ConfigSysFileHandle);
return;
}
//
// Now find out if we need to update the registry.
// We update in all except the following cases:
// 1 - os2conf.nt doesn't exist (handled above)
// 2 - os2conf.nt has zero length
// 3 - the file time and size have not changed since last update
//
if ((NewConfigSysSizeStamp.HighPart == 0 &&
NewConfigSysSizeStamp.LowPart == 0) ||
(NewConfigSysTimeStamp.HighPart == Os2ConfigSysTimeStamp.HighPart &&
NewConfigSysTimeStamp.LowPart == Os2ConfigSysTimeStamp.LowPart &&
NewConfigSysSizeStamp.HighPart == Os2ConfigSysSizeStamp.HighPart &&
NewConfigSysSizeStamp.LowPart == Os2ConfigSysSizeStamp.LowPart)) {
if (DeleteFlag) {
(VOID) Os2MarkFileForDeletion(ConfigSysFileHandle);
//
// ignore deletion errors
//
}
NtClose(ConfigSysFileHandle);
return;
}
//
// Now comes the code that actually updates the registry
//
SizeOfConfigSys = NewConfigSysSizeStamp.LowPart;
// the + 1 in following parameter is for inserting the NUL character
pInfo = (PSZ)RtlAllocateHeap(Os2Heap, 0, SizeOfConfigSys + 1);
if (pInfo == NULL) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2UpdateRegistryFromConfigSys: failed RtlAllocateHeap for os2conf.nt content\n"));
}
#endif
if (DeleteFlag) {
(VOID) Os2MarkFileForDeletion(ConfigSysFileHandle);
//
// ignore deletion errors
//
}
NtClose(ConfigSysFileHandle);
return;
}
Status = NtReadFile(ConfigSysFileHandle,
NULL,
NULL,
NULL,
&IoStatus,
(PVOID)pInfo,
SizeOfConfigSys,
NULL,
NULL
);
if (DeleteFlag) {
(VOID) Os2MarkFileForDeletion(ConfigSysFileHandle);
//
// ignore deletion errors
//
}
NtClose(ConfigSysFileHandle);
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2UpdateRegistryFromConfigSys: FAILED - NtReadFile %lx\n",
Status));
}
#endif
RtlFreeHeap(Os2Heap, 0, pInfo);
return;
}
pInfo[SizeOfConfigSys] = '\0';
Or2InitMBString(&Info_A, pInfo);
RetCode = Or2MBStringToUnicodeString(&Info_U,
&Info_A,
TRUE);
RtlFreeHeap(Os2Heap, 0, pInfo);
if (RetCode != NO_ERROR) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2UpdateRegistryFromConfigSys: no memory for Unicode Conversion\n"));
}
#endif
return;
}
if (!Os2UpdateRegistryAll(&Info_U)) {
#if DBG
IF_OD2_DEBUG( MISC ) {
KdPrint(("Os2UpdateRegistryFromConfigSys: Os2UpdateRegistryAll FAILED\n"));
}
#endif
}
RtlFreeUnicodeString(&Info_U);
}