/*++ Copyright (c) 1989 Microsoft Corporation Module Name: srvname.c Abstract: This is the name space module for the OS/2 Subsystem Server Author: Steve Wood (stevewo) 22-Aug-1989 Environment: User Mode Only Revision History: Yaron Shamir (yarons) 4-Apr-91: Added Os2ComputeValidDrives, to fix initialization and computation of valid drives, to cope with new SM initialization and with redirected drives . Yaron Shamir (yarons) 6-May-91: changed the way we get at default and boot drives (matches Dos now). Yaron Shamir (yarons) 6-Aug-91: support named pipes. Yaron Shamir (yarons) 26-Aug-91: support UNC names. Beni Lavi (benil) 3-Mar-92: support mailslots Michael Jarus (mjaruss) 31-Mar-93: Remove Os2ComputeValidDrives Patrick Questembert (PatrickQ) 19-Mar-1995: Add COM10-16 --*/ #include "os2srv.h" #include "os2win.h" char * __cdecl getenv(char *varname); PSECURITY_DESCRIPTOR securityDescriptor; struct _INITIAL_OBJDIRS { PWSTR Name; PHANDLE Handle; } InitialObjectDirectories[] = { {L"DEVICES", &Os2DevicesDirectory}, {L"QUEUES", NULL}, {L"SHAREMEM", NULL}, {L"SEMAPHORES", NULL}, {NULL, NULL} }; struct _INITIAL_OBJDIRS Os2Drives = {L"DRIVES", &Os2DrivesDirectory}; HANDLE Os2NamedPipesDirectory; HANDLE Os2UNCDirectory; HANDLE Os2MailslotDirectory; HANDLE Os2RegistryDirectory; struct _INITIAL_RMT_OBJDIRS { PWSTR Name; PHANDLE Handle; PWSTR TargetName; } InitialRmtObjectDirectories[] = { {L"PIPE", &Os2NamedPipesDirectory, L"\\DosDevices\\PIPE" }, {L"UNC", &Os2UNCDirectory, L"\\DosDevices\\UNC" }, {L"MAILSLOT", &Os2MailslotDirectory, L"\\DosDevices\\MAILSLOT" }, {NULL, NULL, NULL} }; // // The InitialDevices table contains the values of symbolic links for // special OS/2 files (devices). // The first character of the Target field is used to specifiy the type // of the device so that Od2Canonicalize() (in client\dllname.c) can // determine and return it. Following characters are the value of the // symbolic link which Od2Canonicalize returns as the canonicalized // file name. // // Current defined types (first character of the Target field) are: // // @ FILE_TYPE_PSDEV // # FILE_TYPE_COM // (space) FILE_TYPE_DEV // struct _INITIAL_DEVICES { PWSTR Name; PWSTR Target; } InitialDevices[] = { {L"NUL" , L"@@0"}, {L"CON" , L"@@1"}, {L"AUX" , L"#\\DosDevices\\COM1"}, {L"COM1" , L"#\\DosDevices\\COM1"}, {L"COM2" , L"#\\DosDevices\\COM2"}, {L"COM3" , L"#\\DosDevices\\COM3"}, {L"COM4" , L"#\\DosDevices\\COM4"}, {L"COM5" , L"#\\DosDevices\\COM5"}, {L"COM6" , L"#\\DosDevices\\COM6"}, {L"COM7" , L"#\\DosDevices\\COM7"}, {L"COM8" , L"#\\DosDevices\\COM8"}, {L"COM9" , L"#\\DosDevices\\COM9"}, {L"COM10" , L"#\\DosDevices\\COM10"}, {L"COM11" , L"#\\DosDevices\\COM11"}, {L"COM12" , L"#\\DosDevices\\COM12"}, {L"COM13" , L"#\\DosDevices\\COM13"}, {L"COM14" , L"#\\DosDevices\\COM14"}, {L"COM15" , L"#\\DosDevices\\COM15"}, {L"COM16" , L"#\\DosDevices\\COM16"}, {L"PRN" , L" \\DosDevices\\LPT1"}, {L"LPT1" , L" \\DosDevices\\LPT1"}, {L"LPT2" , L" \\DosDevices\\LPT2"}, {L"LPT3" , L" \\DosDevices\\LPT3"}, {L"LPT4" , L" \\DosDevices\\LPT4"}, {L"LPT5" , L" \\DosDevices\\LPT5"}, {L"LPT6" , L" \\DosDevices\\LPT6"}, {L"LPT7" , L" \\DosDevices\\LPT7"}, {L"LPT8" , L" \\DosDevices\\LPT8"}, {L"LPT9" , L" \\DosDevices\\LPT9"}, {L"KBD$" , L"@@4"}, {L"MOUSE$" , L"@@5"}, {L"CLOCK$" , L"@@6"}, {L"SCREEN$" , L"@@7"}, {L"POINTER$" , L"@@8"}, {NULL, NULL} }; CHAR DeviceDirectoryName[] = "\\Device\\"; CHAR DeviceNameStr[] = "DEVICENAME"; // // Information relevant for config.sys processing by the server // static WCHAR ConfigSysRegDir[] = L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\OS/2 Subsystem for NT\\1.0\\config.sys"; static WCHAR ConfigSysValue[] = L"Config.Sys"; // BUGBUG - Beni, move those to GetNextDeviceNameFromConfigDotSys static BOOLEAN ConfigDotSysWasRead = FALSE; static ULONG FileSize = 0; static ULONG CurrentOffset = 0; static ANSI_STRING ConfigSysValueData_A = {0, 0, NULL}; // // Process the config.sys file // Returns TRUE if a line of the form: // // DEVICENAME=xxx [yyy] // // was detected in config.sys // The string 'xxx' is returned in DeviceName // If string 'yyy' is present its value is returned in TargetName // Otherwise, a NUL string is returned in TargetName // // This routine may be called multiple time. Each time it returns the next // DEVICENAME value string. When there are no more such values, the routine // returns FALSE. // // The information read from the registry is of type REG_MULTI_SZ // It used to be REG_SZ with CR-LF separating the lines. Therefore, // the code accepts both lines that terminate with CR-LF and lined // that terminate with NUL. // BOOLEAN GetNextDeviceNameFromConfigDotSys( PSZ DeviceName, PSZ TargetName ) { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; PSZ Name; PSZ TName; CHAR ch; UNICODE_STRING ConfigSysRegDir_U; UNICODE_STRING ConfigSysValue_U; UNICODE_STRING ConfigSysValueData_U; HANDLE ConfigSysKeyHandle; ULONG ResultLength; KEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo; PKEY_VALUE_PARTIAL_INFORMATION pInfo; if (!ConfigDotSysWasRead) { // // Copy the config.sys image from the registry to memory // RtlInitUnicodeString(&ConfigSysRegDir_U, ConfigSysRegDir); InitializeObjectAttributes(&Obja, &ConfigSysRegDir_U, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&ConfigSysKeyHandle, KEY_READ, &Obja ); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG(CLEANUP) { KdPrint(("OS2SRV: FAILED - NtOpenKey() of config.sys %lx\n", Status)); } #endif return (FALSE); } RtlInitUnicodeString(&ConfigSysValue_U, ConfigSysValue); Status = NtQueryValueKey(ConfigSysKeyHandle, &ConfigSysValue_U, KeyValuePartialInformation, &KeyValuePartialInfo, sizeof(KeyValuePartialInfo), &ResultLength ); if (!NT_SUCCESS(Status) && (Status != STATUS_BUFFER_OVERFLOW)) { #if DBG IF_OS2_DEBUG(CLEANUP) { KdPrint(("OS2SRV: FAILED - NtQueryValueKey-1 %lx\n", Status)); } #endif return (FALSE); } pInfo = (PKEY_VALUE_PARTIAL_INFORMATION)RtlAllocateHeap(Os2Heap, 0, ResultLength); if (pInfo == NULL) { #if DBG IF_OS2_DEBUG(CLEANUP) { KdPrint(("OS2SRV: FAILED - RtlAllocateHeap\n")); } #endif return (FALSE); } Status = NtQueryValueKey(ConfigSysKeyHandle, &ConfigSysValue_U, KeyValuePartialInformation, pInfo, ResultLength, &ResultLength ); NtClose(ConfigSysKeyHandle); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG(CLEANUP) { KdPrint(("OS2SRV: FAILED - NtQueryValueKey %lx\n", Status)); } #endif RtlFreeHeap(Os2Heap, 0, pInfo); return (FALSE); } // // The information in the registry is Unicode. // Convert it to ANSI. Initialize explicity the UNICODE_STRING // structure since the data in the registry is of type REG_MULTI_SZ // and hence contains embedded NULs. // ConfigSysValueData_U.Buffer = (PWSTR)pInfo->Data; ConfigSysValueData_U.Length = (USHORT)pInfo->DataLength - 2; ConfigSysValueData_U.MaximumLength = (USHORT)pInfo->DataLength; Status = RtlUnicodeStringToAnsiString(&ConfigSysValueData_A, &ConfigSysValueData_U, TRUE); RtlFreeHeap(Os2Heap, 0, pInfo); if (!NT_SUCCESS(Status)) { #if DBG IF_OS2_DEBUG(CLEANUP) { KdPrint(("OS2SRV: FAILED - RtlUnicodeStringToAnsiString %lx\n", Status)); } #endif return (FALSE); } FileSize = ConfigSysValueData_A.Length; CurrentOffset = 0; ConfigDotSysWasRead = TRUE; } while (CurrentOffset < FileSize) { // skip leading blanks while ((ConfigSysValueData_A.Buffer[CurrentOffset] == ' ') || (ConfigSysValueData_A.Buffer[CurrentOffset] == '\t')) { CurrentOffset++; if (CurrentOffset >= FileSize) { RtlFreeAnsiString(&ConfigSysValueData_A); return (FALSE); } } if ((CurrentOffset + sizeof(DeviceNameStr) - 1) >= FileSize) { RtlFreeAnsiString(&ConfigSysValueData_A); return (FALSE); } // check if the first name in the line is DEVICENAME if (_strnicmp(&ConfigSysValueData_A.Buffer[CurrentOffset], DeviceNameStr, sizeof(DeviceNameStr)-1)) { while ((ConfigSysValueData_A.Buffer[CurrentOffset] != '\n') && (ConfigSysValueData_A.Buffer[CurrentOffset] != '\0')) { CurrentOffset++; if (CurrentOffset >= FileSize) { RtlFreeAnsiString(&ConfigSysValueData_A); return (FALSE); } } CurrentOffset++; continue; } CurrentOffset += sizeof(DeviceNameStr)-1; // skip possible blanks between DEVICENAME and = while ((ConfigSysValueData_A.Buffer[CurrentOffset] == ' ') || (ConfigSysValueData_A.Buffer[CurrentOffset] == '\t')) { CurrentOffset++; if (CurrentOffset >= FileSize) { RtlFreeAnsiString(&ConfigSysValueData_A); return (FALSE); } } // verify that there is an = after the DEVICENAME if (ConfigSysValueData_A.Buffer[CurrentOffset] != '=') { while ((ConfigSysValueData_A.Buffer[CurrentOffset] != '\n') && (ConfigSysValueData_A.Buffer[CurrentOffset] != '\0')) { CurrentOffset++; if (CurrentOffset >= FileSize) { RtlFreeAnsiString(&ConfigSysValueData_A); return (FALSE); } } CurrentOffset++; continue; } else { CurrentOffset++; // skip the '=' if (CurrentOffset >= FileSize) { RtlFreeAnsiString(&ConfigSysValueData_A); return (FALSE); } } // skip possible blanks between = and the device name while ((ConfigSysValueData_A.Buffer[CurrentOffset] == ' ') || (ConfigSysValueData_A.Buffer[CurrentOffset] == '\t')) { CurrentOffset++; if (CurrentOffset >= FileSize) { RtlFreeAnsiString(&ConfigSysValueData_A); return (FALSE); } } // process the device name Name = DeviceName; while (((ch = ConfigSysValueData_A.Buffer[CurrentOffset]) != '\r') && (ch != '\n') && (ch != ' ') && (ch != '\t') && (ch != '\0') ) { *Name++ = ch; CurrentOffset++; if (CurrentOffset >= FileSize) { *Name = '\0'; return (TRUE); } } *Name = '\0'; if (*DeviceName == '\0') { // // Null device name, return false // RtlFreeAnsiString(&ConfigSysValueData_A); return (FALSE); } // skip possible blanks and commas between the Device name and the Target name while ((ConfigSysValueData_A.Buffer[CurrentOffset] == ' ') || (ConfigSysValueData_A.Buffer[CurrentOffset] == ',') || (ConfigSysValueData_A.Buffer[CurrentOffset] == '\t')) { CurrentOffset++; if (CurrentOffset >= FileSize) { RtlFreeAnsiString(&ConfigSysValueData_A); return (FALSE); } } // process the device target name TName = TargetName; // first put a blank to match the rest of the devices *TName++ = ' '; while (((ch = ConfigSysValueData_A.Buffer[CurrentOffset]) != '\r') && (ch != '\n') && (ch != ' ') && (ch != '\t') && (ch != '\0') ) { *TName++ = ch; CurrentOffset++; if (CurrentOffset >= FileSize) { *TName = '\0'; return (TRUE); } } *TName = '\0'; // did we have a target name? if (TName == (TargetName+1)) { // No - it's only the space we had put - nullify *TargetName = '\0'; } while ((ConfigSysValueData_A.Buffer[CurrentOffset] != '\n') && (ConfigSysValueData_A.Buffer[CurrentOffset] != '\0')) { CurrentOffset++; if (CurrentOffset >= FileSize) { return (TRUE); } } CurrentOffset++; return (TRUE); } RtlFreeAnsiString(&ConfigSysValueData_A); return (FALSE); } NTSTATUS Os2InitializeNameSpace( VOID ) { NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING DirectoryName_U; HANDLE DirectoryHandle; PHANDLE Directory; UNICODE_STRING LinkTarget_U, LinkName_U; HANDLE LinkHandle; CHAR localSecurityDescriptor[SECURITY_DESCRIPTOR_MIN_LENGTH]; ULONG i; ANSI_STRING DeviceName_A; ANSI_STRING TargetDeviceName_A; CHAR DeviceName[32]; CHAR TargetDeviceName[48]; // // Create a root directory in the object name space that will be used // to contain all of the named objects created by the OS/2 Emulation // subsystem. // RtlInitUnicodeString( &DirectoryName_U, OS2_SS_ROOT_OBJECT_DIRECTORY ); Status = RtlCreateSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); ASSERT( NT_SUCCESS( Status ) ); if (! NT_SUCCESS( Status )) { return Status; } Status = RtlSetDaclSecurityDescriptor( (PSECURITY_DESCRIPTOR) &localSecurityDescriptor, (BOOLEAN)TRUE, (PACL) NULL, (BOOLEAN)FALSE ); ASSERT (NT_SUCCESS(Status)); if (! NT_SUCCESS( Status )) { return Status; } securityDescriptor = (PSECURITY_DESCRIPTOR) &localSecurityDescriptor; InitializeObjectAttributes( &ObjectAttributes, &DirectoryName_U, OBJ_CASE_INSENSITIVE, // OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, NULL, securityDescriptor ); Status = NtOpenDirectoryObject( &Os2RootDirectory, DIRECTORY_ALL_ACCESS, &ObjectAttributes ); if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { ULONG i = 100; // 10 seconds #if DBG KdPrint(("OS2SRV: wait for os2ss to initialize\n")); #endif while (Status == STATUS_OBJECT_NAME_NOT_FOUND && i > 0) { // // Wait 0.1 sec for os2ss to complete initialization // Sleep (100L); #if DBG KdPrint((".")); #endif Status = NtOpenDirectoryObject( &Os2RootDirectory, DIRECTORY_ALL_ACCESS, &ObjectAttributes ); i--; } } if (! NT_SUCCESS( Status )) { #if DBG KdPrint(("OS2SRV: SubSystem = Can't open \\os2ss directory object\n")); #endif return Status; } // // Make the Drives directory a symbolic link // of \DosDevices // RtlInitUnicodeString( &DirectoryName_U, Os2Drives.Name ); RtlInitUnicodeString( &LinkTarget_U, L"\\DosDevices" ); InitializeObjectAttributes( &ObjectAttributes, &DirectoryName_U, // OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, OBJ_CASE_INSENSITIVE, Os2RootDirectory, securityDescriptor ); Status = NtCreateSymbolicLinkObject( Os2Drives.Handle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &LinkTarget_U ); if (Status == STATUS_OBJECT_NAME_COLLISION) { // // An os2srv is already present, print out and exit // #if DBG KdPrint(( "OS2SRV: Unable to initialize server. An instance of OS2SRV already runs. Status == %X\n", Status )); #endif NtTerminateProcess( NtCurrentProcess(), Status ); } ASSERT (NT_SUCCESS(Status)); if (! NT_SUCCESS( Status )) { return Status; } for (i=0; InitialObjectDirectories[ i ].Name; i++) { RtlInitUnicodeString( &DirectoryName_U, InitialObjectDirectories[ i ].Name ); Directory = InitialObjectDirectories[ i ].Handle; if (Directory == NULL) { Directory = &DirectoryHandle; } InitializeObjectAttributes( &ObjectAttributes, &DirectoryName_U, // OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, OBJ_CASE_INSENSITIVE, Os2RootDirectory, securityDescriptor ); Status = NtCreateDirectoryObject( Directory, DIRECTORY_ALL_ACCESS, &ObjectAttributes ); ASSERT( NT_SUCCESS( Status ) ); if (! NT_SUCCESS( Status )) { KdPrint(("OS2SRV: FAILED - NtCreateDirectoryObject stts= %lx\n", Status)); return Status; } if (Directory == &DirectoryHandle) { NtClose( DirectoryHandle ); } } for (i=0; InitialDevices[ i ].Name; i++) { RtlInitUnicodeString( &LinkName_U, InitialDevices[ i ].Name ); RtlInitUnicodeString( &LinkTarget_U, InitialDevices[ i ].Target ); InitializeObjectAttributes( &ObjectAttributes, &LinkName_U, // OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, OBJ_CASE_INSENSITIVE, Os2DevicesDirectory, securityDescriptor ); Status = NtCreateSymbolicLinkObject( &LinkHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &LinkTarget_U ); ASSERT( NT_SUCCESS( Status ) ); if (! NT_SUCCESS( Status )) { return Status; } // NtClose( LinkHandle ); } // // Make the remote directories a symbolic link of \DosDevices\* // for (i =0; InitialRmtObjectDirectories[i].Name != NULL; i++) { RtlInitUnicodeString( &DirectoryName_U, InitialRmtObjectDirectories[i].Name ); RtlInitUnicodeString( &LinkTarget_U, InitialRmtObjectDirectories[i].TargetName); InitializeObjectAttributes( &ObjectAttributes, &DirectoryName_U, OBJ_CASE_INSENSITIVE, // OBJ_PERMANENT | OBJ_CASE_INSENSITIVE, Os2RootDirectory, securityDescriptor ); Status = NtCreateSymbolicLinkObject( InitialRmtObjectDirectories[i].Handle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &LinkTarget_U ); ASSERT( NT_SUCCESS( Status ) ); if (! NT_SUCCESS( Status )) { return Status; } } // // Process config.sys for the DEVICENAME= definitions // while (GetNextDeviceNameFromConfigDotSys(DeviceName, TargetDeviceName)) { #if DBG if (TargetDeviceName[0] == '\0') { KdPrint(("OS2SRV: Read DEVICENAME=%s from config.sys\n", DeviceName)); } else { KdPrint(("OS2SRV: Read DEVICENAME=%s %s from config.sys\n", DeviceName, TargetDeviceName)); } #endif RtlInitAnsiString(&DeviceName_A, DeviceName); RtlAnsiStringToUnicodeString( &LinkName_U, &DeviceName_A, (BOOLEAN)TRUE ); if (TargetDeviceName[0] == '\0') { strcpy(TargetDeviceName, DeviceDirectoryName); strcat(TargetDeviceName, DeviceName); } RtlInitAnsiString(&TargetDeviceName_A, TargetDeviceName); RtlAnsiStringToUnicodeString( &LinkTarget_U, &TargetDeviceName_A, (BOOLEAN)TRUE ); InitializeObjectAttributes( &ObjectAttributes, &LinkName_U, OBJ_CASE_INSENSITIVE, Os2DevicesDirectory, securityDescriptor ); Status = NtCreateSymbolicLinkObject( &LinkHandle, SYMBOLIC_LINK_ALL_ACCESS, &ObjectAttributes, &LinkTarget_U ); RtlFreeUnicodeString(&LinkName_U); RtlFreeUnicodeString(&LinkTarget_U); if (! NT_SUCCESS( Status )) { #if DBG KdPrint(("OS2SRV: Could not Link Device '%s' to target devices\n", DeviceName)); #endif } } return( Os2InitializeDriveLetters() ); } BOOLEAN Os2CheckValidCDrives(VOID) { NTSTATUS Status; HANDLE LinkHandle; UNICODE_STRING DriveName_U; WCHAR DriveString[15] = L"\\DosDevices\\C:"; OBJECT_ATTRIBUTES Attributes; // // compute the valid drives from scratch, since // redirected drives appear and disappear dynamically // RtlInitUnicodeString(&DriveName_U, DriveString); InitializeObjectAttributes( &Attributes, &DriveName_U, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenSymbolicLinkObject( &LinkHandle, SYMBOLIC_LINK_QUERY, &Attributes ); if (Status != STATUS_OBJECT_NAME_NOT_FOUND) { NtClose( LinkHandle ); return(TRUE); } return (FALSE); } NTSTATUS Os2InitializeDriveLetters( VOID ) { // // Set the SystemDrive number from the environment // SystemRootValuePtr = getenv("SYSTEMROOT"); if (SystemRootValuePtr == NULL) { SystemRootValuePtr = "C:\\"; } Os2DefaultDrive = (ULONG)(RtlUpperChar(*SystemRootValuePtr) - 'A'); Os2BootDrive = 2; // C: // // check if not C: // if (!Os2CheckValidCDrives()) { Os2BootDrive = 0; // A: if (Os2DefaultDrive == 2) { Os2DefaultDrive = 0; // A: } } return STATUS_SUCCESS; } NTSTATUS Os2GetClientId( VOID ) { NTSTATUS Status; HANDLE LinkHandle; OBJECT_ATTRIBUTES Attributes; UNICODE_STRING DebugClientId_U; UNICODE_STRING ClientIdString_U; Os2DebugUserClientId.UniqueProcess = NULL; RtlInitUnicodeString(&DebugClientId_U, L"DebugClientId"); InitializeObjectAttributes( &Attributes, &DebugClientId_U, OBJ_CASE_INSENSITIVE, Os2RootDirectory, NULL); Status = NtOpenSymbolicLinkObject( &LinkHandle, SYMBOLIC_LINK_QUERY, &Attributes ); if (!NT_SUCCESS( Status )) { return Status; } ClientIdString_U.Buffer = (PWSTR)&Os2DebugUserClientId; ClientIdString_U.Length = 0; ClientIdString_U.MaximumLength = sizeof( Os2DebugUserClientId ); Status = NtQuerySymbolicLinkObject( LinkHandle, &ClientIdString_U, NULL ); if (!NT_SUCCESS( Status )) { return Status; } return Status; }