summaryrefslogtreecommitdiffstats
path: root/private/nw/nw16/dll/ncp.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/nw16/dll/ncp.c')
-rw-r--r--private/nw/nw16/dll/ncp.c3383
1 files changed, 3383 insertions, 0 deletions
diff --git a/private/nw/nw16/dll/ncp.c b/private/nw/nw16/dll/ncp.c
new file mode 100644
index 000000000..c82cca43b
--- /dev/null
+++ b/private/nw/nw16/dll/ncp.c
@@ -0,0 +1,3383 @@
+/*++
+
+Copyright (c) 1993/4 Microsoft Corporation
+
+Module Name:
+
+ ncp.c
+
+Abstract:
+
+ Contains routine which accepts the bop from a 16 bit
+ application and processes the request appropriately.
+ Normally it performes an NCP exchange on behalf of the
+ application.
+
+Author:
+
+ Colin Watson (colinw) 07-Jul-1993
+
+Environment:
+
+
+Revision History:
+
+
+--*/
+
+#include "procs.h"
+
+#define BASE_DOS_ERROR ((NTSTATUS )0xC0010000L)
+#define CLIENT_ID_STRING "MSDOS\0V5.00\0IBM_PC\0IBM"
+
+#include <packon.h>
+typedef struct _TTSOUTPACKET {
+ UCHAR SubFunction;
+ USHORT cx;
+ USHORT dx;
+} TTSOUTPACKET, *PTTSOUTPACKET ;
+
+typedef struct _TTSINPACKET{
+ USHORT cx;
+ USHORT dx;
+} TTSINPACKET, *PTTSINPACKET;
+
+#include <packoff.h>
+
+VOID
+InitDosTable(
+ PNWDOSTABLE pdt
+ );
+
+VOID
+LoadPreferredServerName(
+ VOID
+ );
+
+VOID
+ProcessResourceArray(
+ LPNETRESOURCE NetResource,
+ DWORD NumElements
+ );
+
+VOID
+ProcessResource(
+ LPNETRESOURCE NetResource
+ );
+
+VOID
+SendNCP(
+ ULONG Command
+ );
+
+VOID
+SendF2NCP(
+ ULONG Command
+ );
+
+UCHAR
+AttachmentControl(
+ ULONG Command
+ );
+
+VOID
+SendNCP2(
+ ULONG Command,
+ PUCHAR Request,
+ ULONG RequestLength,
+ PUCHAR Reply,
+ ULONG ReplyLength
+ );
+
+VOID
+CloseConnection(
+ CONN_INDEX Connection
+ );
+
+NTSTATUS
+InitConnection(
+ CONN_INDEX Connection
+ );
+
+VOID
+GetDirectoryHandle(
+ VOID
+ );
+
+VOID
+LoadDriveHandleTable(
+ VOID
+ );
+
+VOID
+AllocateDirectoryHandle(
+ VOID
+ );
+
+VOID
+ResetDrive(
+ UCHAR Drive
+ );
+
+VOID
+AllocateDirectoryHandle2(
+ VOID
+ );
+
+PWCHAR
+BuildUNC(
+ IN PUCHAR aName,
+ IN ULONG aLength
+ );
+
+VOID
+GetServerDateAndTime(
+ VOID
+ );
+
+VOID
+GetShellVersion(
+ IN USHORT Command
+ );
+
+VOID
+TTS(
+ VOID
+ );
+
+VOID
+OpenCreateFile(
+ VOID
+ );
+
+BOOL
+IsItNetWare(
+ PUCHAR Name
+ );
+
+VOID
+SetCompatibility(
+ VOID
+ );
+
+VOID
+OpenQueueFile(
+ VOID
+ );
+
+VOID
+AttachHandle(
+ VOID
+ );
+
+VOID
+ProcessExit(
+ VOID
+ );
+
+VOID
+SystemLogout(
+ VOID
+ );
+
+VOID
+ServerFileCopy(
+ VOID
+ );
+
+VOID
+SetStatus(
+ NTSTATUS Status
+ );
+
+//
+// The following pointer contains the 32 bit virtual address of where
+// the nw16.exe tsr holds the workstation structures.
+//
+
+PNWDOSTABLE pNwDosTable;
+
+//
+// Global variables used to hold the state for this process
+//
+
+UCHAR OriginalPrimary = 0;
+HANDLE ServerHandles[MC];
+
+HANDLE Win32DirectoryHandleTable[MD];
+PWCHAR Drives[MD]; // Strings such as R: or a unc name
+
+UCHAR SearchDriveTable[16];
+
+
+BOOLEAN Initialized = FALSE;
+BOOLEAN TablesValid = FALSE; // Reload each time a process starts
+BOOLEAN DriveHandleTableValid = FALSE; // Reload first time process does NW API
+
+WORD DosTableSegment;
+WORD DosTableOffset;
+
+extern UCHAR LockMode;
+
+#if NWDBG
+BOOL GotDebugState = FALSE;
+extern int DebugCtrl;
+#endif
+
+
+VOID
+Nw16Register(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function is called by wow when nw16.sys is loaded.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD status;
+ HANDLE enumHandle;
+ LPNETRESOURCE netResource;
+ DWORD numElements;
+ DWORD bufferSize;
+ DWORD dwScope = RESOURCE_CONNECTED;
+
+ NwPrint(("Nw16Register\n"));
+
+ if ( !Initialized) {
+ UCHAR CurDir[256];
+ DosTableSegment = getAX();
+ DosTableOffset = getDX();
+
+ //
+ // this call always made from Real Mode (hence FALSE for last param)
+ //
+
+ pNwDosTable = (PNWDOSTABLE) GetVDMPointer (
+ (ULONG)((DosTableSegment << 16)|DosTableOffset),
+ sizeof(NWDOSTABLE),
+ FALSE
+ );
+
+ InitDosTable( pNwDosTable );
+
+ if ((GetCurrentDirectoryA(sizeof(CurDir)-1, CurDir) >= 2) &&
+ (CurDir[1] = ':')) {
+ pNwDosTable->CurrentDrive = tolower(CurDir[0]) - 'a';
+ }
+
+ InitLocks();
+ }
+
+
+#if NWDBG
+ {
+ WCHAR Value[80];
+
+ if (GetEnvironmentVariableW(L"NWDEBUG",
+ Value,
+ sizeof(Value)/sizeof(Value[0]) - 1)) {
+
+ DebugCtrl = Value[0] - '0';
+
+ // 0 Use logfile
+ // 1 Use debugger
+ // 2/undefined No debug output
+ // 4 Use logfile, close on process exit
+ // 8 Use logfile, verbose, close on process exit
+
+ DebugControl( DebugCtrl );
+
+ GotDebugState = TRUE; // Don't look again until process exits vdm
+ }
+ }
+#endif
+
+ LoadPreferredServerName();
+
+ //
+ // Attempt to allow for MD drives
+ //
+
+ bufferSize = (MD*sizeof(NETRESOURCE))+1024;
+
+ netResource = (LPNETRESOURCE) LocalAlloc(LPTR, bufferSize);
+
+ if (netResource == NULL) {
+
+ NwPrint(("Nw16Register: LocalAlloc Failed %d\n",GetLastError));
+ setCF(1);
+ return;
+ }
+
+ //-----------------------------------//
+ // Get a handle for a top level enum //
+ //-----------------------------------//
+ status = NPOpenEnum(
+ dwScope,
+ RESOURCETYPE_DISK,
+ 0,
+ NULL,
+ &enumHandle);
+
+ if ( status != WN_SUCCESS) {
+ NwPrint(("Nw16Register:WNetOpenEnum failed %d\n",status));
+
+ //
+ // If there is an extended error, display it.
+ //
+ if (status == WN_EXTENDED_ERROR) {
+ DisplayExtendedError();
+ }
+ goto LoadLocal;
+ }
+
+ //-----------------------------//
+ // Enumerate the disk devices. //
+ //-----------------------------//
+
+ numElements = 0xffffffff;
+
+ status = NwEnumConnections(
+ enumHandle,
+ &numElements,
+ netResource,
+ &bufferSize,
+ TRUE); // Include implicit connections
+
+ if ( status != WN_SUCCESS) {
+ NwPrint(("Nw16Register:NwEnumResource failed %d\n",status));
+
+ //
+ // If there is an extended error, display it.
+ //
+ if (status == WN_EXTENDED_ERROR) {
+ DisplayExtendedError();
+ }
+ WNetCloseEnum(enumHandle);
+ goto LoadLocal;
+ }
+
+ //------------------------------------------//
+ // Close the EnumHandle & print the results //
+ //------------------------------------------//
+
+ status = NPCloseEnum(enumHandle);
+ if (status != WN_SUCCESS) {
+ NwPrint(("Nw16Register:WNetCloseEnum failed %d\n",status));
+ //
+ // If there is an extended error, display it.
+ //
+ if (status == WN_EXTENDED_ERROR) {
+ DisplayExtendedError();
+ }
+ goto LoadLocal;
+
+ }
+
+ //----------------------------------------//
+ // Insert the results in the Nw Dos Table //
+ //----------------------------------------//
+
+ ProcessResourceArray( netResource, numElements);
+
+LoadLocal:
+
+ //
+ // Add the local devices so that NetWare apps don't try to map them
+ // to remote servers.
+ //
+
+ {
+ USHORT Drive;
+ WCHAR DriveString[4];
+ UINT Type;
+
+ DriveString[1] = L':';
+ DriveString[2] = L'\\';
+ DriveString[3] = L'\0';
+
+ //
+ // Hardwire A: and B: because hitting the floppy drive with
+ // GetDriveType takes too long.
+ //
+
+ pNwDosTable->DriveFlagTable[0] = LOCAL_DRIVE;
+ pNwDosTable->DriveFlagTable[1] = LOCAL_DRIVE;
+
+
+ for (Drive = 2; Drive <= 'Z' - 'A'; Drive++ ) {
+
+ if (pNwDosTable->DriveFlagTable[Drive] == 0) {
+ DriveString[0] = L'A' + Drive;
+ Type = GetDriveTypeW( DriveString );
+
+ //
+ // 0 means drive type cannot be determined, all others are
+ // provided by other filesystems.
+ //
+
+ if (Type != 1) {
+ pNwDosTable->DriveFlagTable[Drive] = LOCAL_DRIVE;
+ }
+ }
+ }
+
+#ifdef NWDBG
+ for (Drive = 0; Drive < MD; Drive++ ) {
+
+ DriveString[0] = L'A' + Drive;
+
+ NwPrint(("%c(%d)=%x,",'A' + Drive,
+ GetDriveTypeW( DriveString ),
+ pNwDosTable->DriveFlagTable[Drive] ));
+
+ if (!((Drive + 1) % 8)) {
+ NwPrint(("\n",0));
+ }
+ }
+
+ NwPrint(("\n"));
+#endif
+
+ }
+
+ if ( !Initialized ) {
+ Initialized = TRUE;
+ pNwDosTable->PrimaryServer = OriginalPrimary;
+ }
+
+ TablesValid = TRUE;
+
+ LocalFree(netResource);
+ setCF(0);
+
+ NwPrint(("Nw16Register: End\n"));
+}
+
+VOID
+LoadPreferredServerName(
+ VOID
+ )
+{
+
+ //
+ // If we already have a connection to somewhere then we already have a
+ // preferred servername of some sort.
+ //
+
+ if (pNwDosTable->ConnectionIdTable[0].ci_InUse == IN_USE) {
+ return;
+ }
+
+ //
+ // Load the server name table with the preferred/nearest server.
+ //
+
+ CopyMemory( pNwDosTable->ServerNameTable[0], "*", sizeof("*"));
+
+ if (NT_SUCCESS(OpenConnection( 0 ))) {
+
+ if( NT_SUCCESS(InitConnection(0)) ) {
+
+ //
+ // Close the handle so that the rdr can be stopped if
+ // user is not running a netware aware application.
+ //
+
+ CloseConnection(0);
+
+ pNwDosTable->PrimaryServer = 1;
+
+ return;
+
+ }
+
+ }
+
+ pNwDosTable->PrimaryServer = 0;
+
+}
+
+VOID
+ProcessResourceArray(
+ LPNETRESOURCE NetResource,
+ DWORD NumElements
+ )
+{
+ DWORD i;
+
+ for (i=0; i<NumElements ;i++ ) {
+ ProcessResource(&(NetResource[i]));
+ }
+ return;
+}
+
+VOID
+ProcessResource(
+ LPNETRESOURCE NetResource
+ )
+{
+ SERVERNAME ServerName;
+ int ServerNameLength;
+ int i;
+ int Connection;
+ BOOLEAN Found = FALSE;
+
+ //
+ // Extract Server Name from RemoteName, skipping first 2 chars that
+ // contain backslashes and taking care to handle entries that only
+ // contain a servername.
+ //
+
+ ServerNameLength = wcslen( NetResource->lpRemoteName );
+
+ ASSERT(NetResource->lpRemoteName[0] == '\\');
+ ASSERT(NetResource->lpRemoteName[1] == '\\');
+
+ for (i = 2; i <= ServerNameLength; i++) {
+
+ if ((NetResource->lpRemoteName[i] == '\\') ||
+ (i == ServerNameLength )){
+
+ ServerNameLength = i - 2;
+
+ WideCharToMultiByte(
+ CP_OEMCP,
+ 0,
+ &NetResource->lpRemoteName[2],
+ ServerNameLength,
+ ServerName,
+ sizeof( ServerName ),
+ NULL,
+ NULL );
+
+ CharUpperBuffA( ServerName, ServerNameLength );
+
+ ZeroMemory( &ServerName[ServerNameLength],
+ SERVERNAME_LENGTH - ServerNameLength );
+
+ break;
+ }
+
+ }
+
+ //
+ // Now try to find ServerName in the connection table. If there are
+ // more than MC servers in the table already then skip this one.
+ //
+
+ for (Connection = 0; Connection < MC ; Connection++ ) {
+ if ((pNwDosTable->ConnectionIdTable[Connection].ci_InUse == IN_USE) &&
+ (!memcmp( pNwDosTable->ServerNameTable[Connection], ServerName, SERVERNAME_LENGTH))) {
+ Found = TRUE;
+ break;
+ }
+ }
+
+
+ NwPrint(("Nw16ProcessResource Server: %s\n",ServerName));
+
+ if ( Found == FALSE ) {
+ for (Connection = 0; Connection < MC ; Connection++ ) {
+ if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse == FREE) {
+
+ CopyMemory( pNwDosTable->ServerNameTable[Connection],
+ ServerName,
+ SERVERNAME_LENGTH);
+
+ if ((NT_SUCCESS(OpenConnection( (CONN_INDEX)Connection ))) &&
+ ( NT_SUCCESS(InitConnection( (CONN_INDEX)Connection ) ))) {
+
+ Found = TRUE;
+
+ } else {
+ // Couldn't talk to the server so ignore it.
+ ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
+
+ }
+
+ break; // Escape from for (Connection =...
+ }
+ }
+ }
+
+ //
+ // Build the drive id and drive flag tables. Entries 0 - 25
+ // are reserved for drives redirected to letters. We use drives
+ // 26 - 31 for UNC drives.
+ //
+
+ if ( Found == TRUE ) {
+ DRIVE Drive;
+ DRIVE NextUncDrive = 26;
+
+ if ( NetResource->dwType != RESOURCETYPE_DISK ) {
+ return;
+ }
+
+ if ( NetResource->lpLocalName != NULL) {
+ Drive = NetResource->lpLocalName[0] - L'A';
+ } else {
+ if ( NextUncDrive < MD ) {
+ Drive = NextUncDrive++;
+ } else {
+
+ //
+ // No room in the table for this UNC drive.
+ //
+
+ return;
+ }
+ }
+
+ //
+ // We have a drive and a connection. Complete the table
+ // mappings.
+ //
+
+ pNwDosTable->DriveIdTable[ Drive ] = Connection + 1;
+ pNwDosTable->DriveFlagTable[ Drive ] = PERMANENT_NETWORK_DRIVE;
+
+ }
+
+}
+
+
+VOID
+InitDosTable(
+ PNWDOSTABLE pdt
+ )
+
+/*++
+
+Routine Description:
+
+ This routine Initializes the NetWare Dos Table to its empty values.
+
+Arguments:
+
+ pdt - Supplies the table to be initialized.
+
+Return Value:
+
+ None
+
+--*/
+{
+ ZeroMemory( ServerHandles, sizeof(ServerHandles) );
+ ZeroMemory( Drives, sizeof(Drives) );
+ ZeroMemory( (PVOID) pdt, sizeof(NWDOSTABLE) );
+ ZeroMemory( Win32DirectoryHandleTable, sizeof(Win32DirectoryHandleTable) );
+ FillMemory( SearchDriveTable, sizeof(SearchDriveTable), 0xff );
+}
+
+UCHAR CpuInProtectMode;
+
+
+VOID
+Nw16Handler(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function is called by wow when nw16.sys traps an Int and
+ bop's into 32 bit mode.
+
+Arguments:
+
+
+Return Value:
+
+ None,
+
+--*/
+{
+ USHORT Command;
+ WORD offset;
+
+ //
+ // get the CPU mode once: the memory references it's required for won't
+ // change during this call. Cuts down number of calls to getMSW()
+ //
+
+ CpuInProtectMode = IS_PROTECT_MODE();
+
+ setCF(0);
+ if ( TablesValid == FALSE ) {
+
+ //
+ // Load the tables unless the process is exiting.
+ //
+
+ if ((pNwDosTable->SavedAx & 0xff00) != 0x4c00) {
+ Nw16Register();
+ }
+
+#if NWDBG
+ if (GotDebugState == FALSE) {
+
+ WCHAR Value[80];
+
+ if (GetEnvironmentVariableW(L"NWDEBUG",
+ Value,
+ sizeof(Value)/sizeof(Value[0]) - 1)) {
+
+ DebugCtrl = Value[0] - '0';
+
+ // 0 Use logfile
+ // 1 Use debugger
+ // 2/undefined No debug output
+ // 4 Use logfile, close on process exit
+ // 8 Use logfile, verbose, close on process exit
+
+ DebugControl( DebugCtrl );
+
+ }
+
+ GotDebugState = TRUE; // Don't look again until process exits vdm
+ }
+#endif
+ }
+
+ //
+ // Normal AX register is used to get into 32 bit code so get applications
+ // AX from the shared datastructure.
+ //
+
+ Command = pNwDosTable->SavedAx;
+
+ //
+ // set AX register so that AH gets preserved
+ //
+
+ setAX( Command );
+
+ NwPrint(("Nw16Handler process command %x\n", Command ));
+ VrDumpRealMode16BitRegisters( FALSE );
+ VrDumpNwData();
+
+ switch (Command & 0xff00) {
+
+ case 0x3C00:
+ case 0x3D00:
+ OpenCreateFile();
+ break;
+
+ case 0x4C00:
+ ProcessExit(); // Close all handles
+ goto default_dos_handler; // Let Dos handle rest of processing
+ break;
+
+ case 0x9f00:
+ OpenQueueFile();
+ break;
+
+ case 0xB300: // Packet Signing
+ setAL(0); // not supported
+ break;
+
+ case 0xB400:
+ AttachHandle();
+ break;
+
+ case 0xB500:
+ switch (Command & 0x00ff) {
+ case 03:
+ setAX((WORD)pNwDosTable->TaskModeByte);
+ break;
+
+ case 04:
+ setES((WORD)(CpuInProtectMode ? pNwDosTable->PmSelector : DosTableSegment));
+ setBX((WORD)(DosTableOffset + &((PNWDOSTABLE)0)->TaskModeByte));
+ break;
+
+ case 06:
+ setAX(2);
+ break;
+
+ default:
+ goto default_dos_handler;
+ }
+ break;
+
+ case 0xB800: // Capture - Not supported
+ setAL(0xff);
+ setCF(1);
+ break;
+
+ case 0xBB00: // Set EOJ status
+ {
+ static UCHAR EOJstatus = 1;
+ setAL(EOJstatus);
+ EOJstatus = pNwDosTable->SavedAx & 0x00ff;
+ }
+ break;
+
+ case 0xBC00:
+ case 0xBD00:
+ case 0xBE00:
+
+ case 0xC200:
+ case 0xC300:
+ case 0xC400:
+ case 0xC500:
+ case 0xC600:
+ Locks(Command);
+ break;
+
+ case 0xC700:
+ TTS();
+ break;
+
+ case 0xCB00:
+ case 0xCD00:
+ case 0xCF00:
+
+ case 0xD000:
+ case 0xD100:
+ case 0xD200:
+ case 0xD300:
+ case 0xD400:
+ case 0xD500:
+ Locks(Command);
+ break;
+
+ case 0xD700:
+ SystemLogout();
+ break;
+
+ case 0xDB00:
+ {
+ UCHAR Drive;
+ UCHAR Count = 0;
+ for (Drive = 0; Drive < MD; Drive++) {
+ if (pNwDosTable->DriveFlagTable[Drive] == LOCAL_DRIVE ) {
+ Count++;
+ }
+ }
+ setAL(Count);
+ }
+ break;
+
+ case 0xDC00: // Get station number
+ {
+ CONN_INDEX Connection = SelectConnection();
+ if (Connection == 0xff) {
+ setAL(0xff);
+ setCF(1);
+ } else {
+
+ PCONNECTIONID pConnection =
+ &pNwDosTable->ConnectionIdTable[Connection];
+
+ setAL(pConnection->ci_ConnectionLo);
+ setAH(pConnection->ci_ConnectionHi);
+ setCH( (UCHAR)((pConnection->ci_ConnectionHi == 0) ?
+ pConnection->ci_ConnectionLo / 10 + '0':
+ 'X'));
+ setCL((UCHAR)(pConnection->ci_ConnectionLo % 10 + '0'));
+ }
+ }
+ break;
+
+ case 0xDD00: // Set NetWare Error mode
+ {
+ static UCHAR ErrorMode = 0;
+ setAL( ErrorMode );
+ ErrorMode = getDL();
+ }
+ break;
+
+ case 0xDE00:
+ {
+ static UCHAR BroadCastMode = 0;
+ UCHAR OpCode = getDL();
+ if ( OpCode < 4) {
+ BroadCastMode = OpCode;
+ }
+ setAL(BroadCastMode);
+ }
+ break;
+
+ case 0xDF00: // Capture - Not supported
+ setAL(0xff);
+ setCF(1);
+ break;
+
+ case 0xE000:
+ case 0xE100:
+ case 0xE300:
+ SendNCP(Command);
+ break;
+
+ case 0xE200:
+
+ AllocateDirectoryHandle();
+ break;
+
+ case 0xE700:
+ GetServerDateAndTime();
+ break;
+
+ case 0xE900:
+
+ switch (Command & 0x00ff) {
+ PUCHAR ptr;
+ case 0:
+ GetDirectoryHandle();
+ break;
+
+ case 1:
+ ptr = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ sizeof(SearchDriveTable),
+ CpuInProtectMode
+ );
+
+ RtlMoveMemory( ptr, SearchDriveTable, sizeof(SearchDriveTable) );
+ break;
+
+ case 2:
+ ptr = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ sizeof(SearchDriveTable),
+ CpuInProtectMode
+ );
+
+ RtlMoveMemory( SearchDriveTable, ptr, sizeof(SearchDriveTable) );
+ break;
+
+ case 5:
+ AllocateDirectoryHandle2();
+ break;
+
+ case 7:
+ setAL(0xff); // Drive depth not yet implemented
+ break;
+
+#ifdef NWDBG
+ // Debug control
+ case 0xf0: // Use logfile
+ case 0xf1: // Use debugger
+ case 0xf2: // No debug output
+ DebugControl(Command & 0x000f);
+ break;
+#endif
+ default:
+ NwPrint(("Nw16Handler unprocessed interrupt %x\n", pNwDosTable->SavedAx ));
+ }
+ break;
+
+ case 0xEA00:
+ GetShellVersion(Command);
+ break;
+
+ case 0xEB00:
+ case 0xEC00:
+ case 0xED00:
+ Locks(Command);
+ break;
+
+
+ case 0xEF00:
+ NwPrint(("Nw32: %x\n", pNwDosTable->SavedAx ));
+
+ switch (Command & 0xff) {
+ case 00:
+ if (DriveHandleTableValid == FALSE) {
+ LoadDriveHandleTable();
+ }
+
+ offset = (WORD)&((PNWDOSTABLE)0)->DriveHandleTable;
+ break;
+
+ case 01:
+ offset = (WORD)&((PNWDOSTABLE)0)->DriveFlagTable;
+ break;
+
+ case 02:
+ offset = (WORD)&((PNWDOSTABLE)0)->DriveIdTable;
+ break;
+
+ case 03:
+ offset = (WORD)&((PNWDOSTABLE)0)->ConnectionIdTable;
+ break;
+
+ case 04:
+ offset = (WORD)&((PNWDOSTABLE)0)->ServerNameTable;
+ break;
+
+ default:
+ goto default_dos_handler;
+ }
+ setSI((WORD)(DosTableOffset + offset));
+ setES((WORD)(CpuInProtectMode ? pNwDosTable->PmSelector : DosTableSegment));
+ setAL(0);
+ break;
+
+ case 0xF100:
+ setAL(AttachmentControl(Command));
+ break;
+
+ case 0xF200:
+ SendF2NCP(Command);
+ break;
+
+ case 0xF300:
+ ServerFileCopy();
+ break;
+
+ default:
+
+default_dos_handler:
+
+ NwPrint(("Nw16Handler unprocessed interrupt %x\n", pNwDosTable->SavedAx ));
+
+ //
+ // if we don't handle this call, we modify the return ip to point to
+ // code that will restore the stack and jump far into dos
+ //
+
+ setIP((WORD)(getIP() + 3));
+
+ }
+
+#if NWDBG
+ pNwDosTable->SavedAx = getAX();
+#endif
+ VrDumpRealMode16BitRegisters( FALSE );
+}
+
+
+CONN_INDEX
+SelectConnection(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Pick target connection for current transaction
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Index into ConnectionIdTable or 0xff,
+
+--*/
+{
+
+ UCHAR IndexConnection;
+
+ if ( pNwDosTable->PreferredServer != 0 ) {
+ return(pNwDosTable->PreferredServer - 1);
+ }
+
+ // BUGBUG: select default server if current drive is mapped by us?
+
+ if ( pNwDosTable->PrimaryServer != 0 ) {
+ return(pNwDosTable->PrimaryServer - 1);
+ }
+
+ // Need to pick another
+
+ for (IndexConnection = 0; IndexConnection < MC ; IndexConnection++ ) {
+
+ if (pNwDosTable->ConnectionIdTable[IndexConnection].ci_InUse == IN_USE) {
+
+ pNwDosTable->PrimaryServer = IndexConnection + 1;
+
+ return(pNwDosTable->PrimaryServer - 1);
+
+ }
+ }
+
+ // No servers in the table so find the nearest/preferred.
+
+ LoadPreferredServerName();
+
+ return(pNwDosTable->PrimaryServer - 1);
+
+}
+
+
+
+VOID
+SendNCP(
+ ULONG Command
+ )
+/*++
+
+Routine Description:
+
+ Implement generic Send NCP function.
+
+ ASSUMES called from Nw16Handler
+
+Arguments:
+
+ Command - Supply the opcode 0xexxx
+ DS:SI - Supply Request buffer & length
+ ES:DI - Supply Reply buffer & length
+
+ On return AL = Status of operation.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PUCHAR Request, Reply;
+ ULONG RequestLength, ReplyLength;
+ UCHAR OpCode;
+
+ OpCode = (UCHAR)((Command >> 8) - 0xcc);
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI()),
+ sizeof(WORD),
+ CpuInProtectMode
+ );
+
+ Reply = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()),
+ sizeof(WORD),
+ CpuInProtectMode
+ );
+
+ RequestLength = *(WORD UNALIGNED*)Request;
+ ReplyLength = *(WORD UNALIGNED*)Reply;
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI() + sizeof(WORD)),
+ (USHORT)RequestLength,
+ CpuInProtectMode
+ );
+ Reply = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()) + sizeof(WORD),
+ (USHORT)ReplyLength,
+ CpuInProtectMode
+ );
+
+ NwPrint(("SubRequest %x, RequestLength %x\n", Request[0], RequestLength ));
+
+ SendNCP2( NWR_ANY_NCP(OpCode ),
+ Request,
+ RequestLength,
+ Reply,
+ ReplyLength);
+}
+
+
+VOID
+SendF2NCP(
+ ULONG Command
+ )
+/*++
+
+Routine Description:
+
+ Implement generic Send NCP function. No length to be inseted by
+ the redirector in the request buffer.
+
+ ASSUMES called from Nw16Handler
+
+Arguments:
+
+ Command - Supply the opcode 0xf2xx
+ DS:SI CX - Supply Request buffer & length
+ ES:DI DX - Supply Reply buffer & length
+
+ On return AL = Status of operation.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PUCHAR Request, Reply;
+ ULONG RequestLength, ReplyLength;
+ UCHAR OpCode;
+
+
+ OpCode = (UCHAR)(Command & 0x00ff);
+
+ RequestLength = getCX();
+ ReplyLength = getDX();
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI()),
+ (USHORT)RequestLength,
+ CpuInProtectMode
+ );
+ Reply = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()),
+ (USHORT)ReplyLength,
+ CpuInProtectMode
+ );
+
+ NwPrint(("F2SubRequest %x, RequestLength %x\n", Request[2], RequestLength ));
+
+#if 0
+ if ((RequestLength != 0) &&
+ (OpCode == 0x17)) {
+
+ if ((Request[2] == 0x17) ||
+ (Request[2] == 0x18)) {
+ //
+ // The request was for an encryption key. Tell the
+ // application that encryption is not supported.
+ //
+
+ setAL(0xfb);
+ return;
+
+ } else if ((Request[2] == 0x14 ) ||
+ (Request[2] == 0x3f )) {
+
+ //
+ // Plaintext login or Verify Bindery Object Password.
+ // Convert to its WNET equivalent version.
+ //
+
+ UCHAR Name[256];
+ UCHAR Password[256];
+ UCHAR ServerName[sizeof(SERVERNAME)+3];
+ PUCHAR tmp;
+ CONN_INDEX Connection;
+ NETRESOURCEA Nr;
+
+ Connection = SelectConnection();
+ if ( Connection == 0xff ) {
+ setAL(0xff);
+ setCF(1);
+ return;
+ }
+
+ ZeroMemory( &Nr, sizeof(NETRESOURCE));
+ ServerName[0] = '\\';
+ ServerName[1] = '\\';
+ RtlCopyMemory( ServerName+2, pNwDosTable->ServerNameTable[Connection], sizeof(SERVERNAME) );
+ ServerName[sizeof(ServerName)-1] = '\0';
+ Nr.lpRemoteName = ServerName;
+ Nr.dwType = RESOURCETYPE_DISK;
+
+ // point to password length.
+ tmp = &Request[6] + Request[5];
+
+ Name[Request[5]] = '\0';
+ RtlMoveMemory( Name, &Request[6], Request[5]);
+
+ Password[tmp[0]] = '\0';
+ RtlMoveMemory( Password, tmp+1, tmp[0]);
+
+ NwPrint(("Connect to %s as %s password %s\n", ServerName, Name, Password ));
+
+ if (NO_ERROR == WNetAddConnection2A( &Nr, Password, Name, 0)) {
+ setAL(0);
+ } else {
+ setAL(255);
+ }
+ return;
+ }
+ }
+
+#endif
+
+ SendNCP2( NWR_ANY_F2_NCP(OpCode ),
+ Request,
+ RequestLength,
+ Reply,
+ ReplyLength);
+}
+
+
+VOID
+SendNCP2(
+ ULONG Command,
+ PUCHAR Request,
+ ULONG RequestLength,
+ PUCHAR Reply,
+ ULONG ReplyLength
+ )
+/*++
+
+Routine Description:
+
+ Pick target connection for current transaction
+
+ This routine effectively opens a handle for each NCP sent. This means that
+ we don't keep handles open to servers unnecessarily which would cause
+ problems if a user tries to delete the connection or stop the workstation.
+
+ If this causes to much of a load then the fallback is to spin off a thread
+ that waits on an event with a timeout and periodically sweeps the
+ server handle table removing stale handles. Setting the event would cause
+ the thread to exit. Critical sections would need to be added to protect
+ handles. Dll Init/exit routine to kill the thread and close the handles
+ would also be needed.
+
+Arguments:
+
+ Command - Supply the opcode
+ Request, RequestLength - Supply Request buffer & length
+ Reply, ReplyLength - Supply Reply buffer & length
+
+ On return AL = Status of operation.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ CONN_INDEX Connection = SelectConnection();
+ NTSTATUS status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ HANDLE Handle;
+
+ NwPrint(("Send NCP %x to %d:%s\n", Command, Connection, pNwDosTable->ServerNameTable[Connection] ));
+ NwPrint(("RequestLength %x\n", RequestLength ));
+ NwPrint(("Reply %x, ReplyLength %x\n", Reply, ReplyLength ));
+
+ if (Connection == 0xff) {
+ setAL(0xff);
+ setCF(1);
+ return;
+ };
+
+ if ( ServerHandles[Connection] == NULL ) {
+
+ status = OpenConnection( Connection );
+
+ if (!NT_SUCCESS(status)) {
+ SetStatus(status);
+ return;
+ } else {
+ InitConnection( Connection );
+ }
+ }
+
+ Handle = ServerHandles[Connection];
+
+ //
+ // If its a CreateJobandFile NCP then we need to use the handle
+ // created through Dos so that the writes go into the spoolfile created
+ // by this NCP.
+ //
+
+ if (Command == NWR_ANY_F2_NCP(0x17)) {
+
+ if ((Request[2] == 0x68) ||
+ (Request[2] == 0x79)) {
+
+ Handle = GET_NT_HANDLE();
+ }
+ } else if (Command == NWR_ANY_NCP(0x17)) {
+ if ((Request[0] == 0x68) ||
+ (Request[0] == 0x79)) {
+
+ Handle = GET_NT_HANDLE();
+ }
+ }
+
+ FormattedDump( Request, RequestLength );
+
+ //
+ // Make the NCP request on the appropriate handle
+ //
+
+ status = NtFsControlFile(
+ Handle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ Command,
+ (PVOID) (Request),
+ RequestLength,
+ (PVOID) Reply,
+ ReplyLength);
+
+ if (NT_SUCCESS(status)) {
+ status = IoStatusBlock.Status;
+ FormattedDump( Reply, ReplyLength );
+ }
+
+ if (!NT_SUCCESS(status)) {
+ SetStatus(status);
+ setCF(1);
+ NwPrint(("NtStatus %x, DosError %x\n", status, getAL() ));
+ } else {
+ setAL(0);
+ }
+}
+
+
+NTSTATUS
+OpenConnection(
+ CONN_INDEX Connection
+ )
+/*++
+
+Routine Description:
+
+ Open the handle to the redirector to access the specified server.
+
+Arguments:
+
+ Connection - Supplies the index to use for the handle
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ LPWSTR FullName;
+
+ UCHAR AnsiName[SERVERNAME_LENGTH+sizeof(UCHAR)];
+
+ UNICODE_STRING UServerName;
+ OEM_STRING AServerName;
+
+ if ( Connection >= MC) {
+ return( BASE_DOS_ERROR + 249 ); // No free connection slots
+ }
+
+ if (ServerHandles[Connection] != NULL ) {
+
+ CloseConnection(Connection);
+
+ }
+
+ FullName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ sizeof( DD_NWFS_DEVICE_NAME_U ) +
+ (SERVERNAME_LENGTH + 1) * sizeof(WCHAR)
+ );
+
+ if ( FullName == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ CopyMemory(AnsiName,
+ pNwDosTable->ServerNameTable[Connection],
+ SERVERNAME_LENGTH);
+ AnsiName[SERVERNAME_LENGTH+1] = '\0';
+
+ RtlInitAnsiString( &AServerName, AnsiName );
+ Status = RtlOemStringToUnicodeString( &UServerName,
+ &AServerName,
+ TRUE);
+
+ if (!NT_SUCCESS(Status)) {
+ LocalFree( FullName );
+ return(Status);
+ }
+
+ wcscpy( FullName, DD_NWFS_DEVICE_NAME_U );
+ wcscat( FullName, L"\\");
+ wcscat( FullName, UServerName.Buffer );
+
+ RtlFreeUnicodeString(&UServerName);
+
+ RtlInitUnicodeString( &UServerName, FullName );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UServerName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Open a handle to the server.
+ //
+
+ //
+ // Try to login to the nearest server. This is necessary for
+ // the real preferred server if there are no redirections to
+ // it. The rdr can logout and disconnect. SYSCON doesn't like
+ // running from such a server.
+ //
+ Status = NtOpenFile(
+ &ServerHandles[Connection],
+ SYNCHRONIZE | FILE_READ_ATTRIBUTES,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if ( NT_SUCCESS(Status)) {
+ Status = IoStatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ //
+ // Failed to login. Use the non-login method. This allows the
+ // app to do a bindery login or query the bindery.
+ //
+
+ Status = NtOpenFile(
+ &ServerHandles[Connection],
+ SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if ( NT_SUCCESS(Status)) {
+ Status = IoStatusBlock.Status;
+ }
+ }
+
+ NwPrint(("Nw16:OpenConnection %d: %wZ status = %08lx\n", Connection, &UServerName, Status));
+
+ LocalFree( FullName );
+
+ if (!NT_SUCCESS(Status)) {
+ SetStatus(Status);
+ return Status;
+ }
+
+ return Status;
+}
+
+
+VOID
+CloseConnection(
+ CONN_INDEX Connection
+ )
+/*++
+
+Routine Description:
+
+ Close the connection handle
+
+Arguments:
+
+ Connection - Supplies the index to use for the handle
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if (ServerHandles[Connection]) {
+
+ NwPrint(("CloseConnection: %d\n",Connection));
+
+ NtClose(ServerHandles[Connection]);
+
+ ServerHandles[Connection] = NULL;
+ }
+}
+
+
+NTSTATUS
+InitConnection(
+ CONN_INDEX Connection
+ )
+/*++
+
+Routine Description:
+
+ Get the connection status from the redirector.
+
+Arguments:
+
+ Connection - Supplies the index to use for the handle
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NWR_GET_CONNECTION_DETAILS Details;
+
+ Status = NtFsControlFile(
+ ServerHandles[Connection],
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_CONN_DETAILS,
+ NULL,
+ 0,
+ (PVOID) &Details,
+ sizeof(Details));
+
+ if (Status == STATUS_SUCCESS) {
+ Status = IoStatusBlock.Status;
+ }
+
+ NwPrint(("Nw16:InitConnection: %d status = %08lx\n",Connection, Status));
+
+ if (!NT_SUCCESS(Status)) {
+
+ SetStatus(Status);
+
+ CloseConnection(Connection);
+
+ } else {
+ PCONNECTIONID pConnection =
+ &pNwDosTable->ConnectionIdTable[Connection];
+
+ pConnection->ci_OrderNo= Details.OrderNumber;
+
+ CopyMemory(pNwDosTable->ServerNameTable[Connection],
+ Details.ServerName,
+ sizeof(SERVERNAME));
+
+ CopyMemory(pConnection->ci_ServerAddress,
+ Details.ServerAddress,
+ sizeof(pConnection->ci_ServerAddress));
+
+ pConnection->ci_ConnectionNo= Details.ConnectionNumberLo;
+ pConnection->ci_ConnectionLo= Details.ConnectionNumberLo;
+ pConnection->ci_ConnectionHi= Details.ConnectionNumberHi;
+ pConnection->ci_MajorVersion= Details.MajorVersion;
+ pConnection->ci_MinorVersion= Details.MinorVersion;
+ pConnection->ci_InUse = IN_USE;
+ pConnection->ci_1 = 0;
+ pConnection->ci_ConnectionStatus = 2;
+
+ //
+ // If this is the preferred conection then record it as special.
+ // If this is the first drive then also record it. Usually it gets
+ // overwritten by the preferred.
+ //
+
+ if (( Details.Preferred ) ||
+ ( OriginalPrimary == 0 )) {
+
+ NwPrint(("Nw16InitConnection: Primary Connection is %d\n", Connection+1));
+
+ pNwDosTable->PrimaryServer = OriginalPrimary = (UCHAR)Connection + 1;
+ }
+
+ setAL(0);
+ }
+
+ return Status;
+}
+
+VOID
+GetDirectoryHandle(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Obtain a NetWare handle for the current directory.
+
+ If a NetWare handle is assigned then the Win32 handle created for
+ the directory handle is kept open. When the process exits, the Win32
+ handle will close. When all the Win32 handles from this process are
+ closed an endjob NCP will be sent freeing the directory handle on the
+ server.
+
+Arguments:
+
+ DX supplies the drive.
+
+ AL returns the handle.
+ AH returns the status flags.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ USHORT Drive = getDX();
+
+ NwPrint(("Nw32:GetDirectoryHandle %c: ", 'A' + Drive));
+
+ GetDirectoryHandle2( Drive );
+
+ setAL(pNwDosTable->DriveHandleTable[Drive]);
+ setAH(pNwDosTable->DriveFlagTable[Drive]);
+
+ NwPrint(("Handle = %x, Flags =%x\n", pNwDosTable->DriveHandleTable[Drive],
+ pNwDosTable->DriveFlagTable[Drive] ));
+}
+
+ULONG
+GetDirectoryHandle2(
+ DWORD Drive
+ )
+/*++
+
+Routine Description:
+
+ Obtain a NetWare handle for the current directory.
+
+ If a NetWare handle is assigned then the Win32 handle created for
+ the directory handle is kept open. When the process exits, the Win32
+ handle will close. When all the Win32 handles from this process are
+ closed an endjob NCP will be sent freeing the directory handle on the
+ server.
+
+ Note: Updates DriveHandleTable.
+
+Arguments:
+
+ Drive supplies the drive index (0 = a:).
+
+Return Value:
+
+ returns the handle.
+
+--*/
+{
+ DWORD BytesReturned;
+
+ if (Drive >= MD) {
+ setAL( 0x98 ); // Volume does not exist
+ return 0xffffffff;
+ }
+
+ NwPrint(("Nw32:GetDirectoryHandle2 %c:\n", 'A' + Drive));
+
+ //
+ // If we don't have a handle and its either a temporary or
+ // permanent drive then create it.
+ //
+
+ if (( Win32DirectoryHandleTable[Drive] == 0 ) &&
+ ( (pNwDosTable->DriveFlagTable[Drive] & 3) != 0 )){
+ WCHAR DriveString[4];
+ PWCHAR Name;
+
+ //
+ // We don't have a handle for this drive.
+ // Open an NT handle to the current directory and
+ // ask the redirector for a NetWare directory handle.
+ //
+
+ if (Drive <= ('Z' - 'A')) {
+
+ DriveString[0] = L'A' + (WCHAR)Drive;
+ DriveString[1] = L':';
+ DriveString[2] = L'.';
+ DriveString[3] = L'\0';
+
+ Name = DriveString;
+
+ } else {
+
+ Name = Drives[Drive];
+
+ if( Name == NULL ) {
+ NwPrint(("\nNw32:GetDirectoryHandle2 Drive not mapped\n",0));
+ return 0xffffffff;
+ }
+ }
+
+ Win32DirectoryHandleTable[Drive] =
+ CreateFileW( Name,
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ 0);
+
+ if (Win32DirectoryHandleTable[Drive] != INVALID_HANDLE_VALUE) {
+
+ if ( DeviceIoControl(
+ Win32DirectoryHandleTable[Drive],
+ IOCTL_NWR_RAW_HANDLE,
+ NULL,
+ 0,
+ (PUCHAR)&pNwDosTable->DriveHandleTable[Drive],
+ sizeof(pNwDosTable->DriveHandleTable[Drive]),
+ &BytesReturned,
+ NULL ) == FALSE ) {
+
+ NwPrint(("\nNw32:GetDirectoryHandle2 DeviceIoControl %x\n", GetLastError()));
+ CloseHandle( Win32DirectoryHandleTable[Drive] );
+ Win32DirectoryHandleTable[Drive] = 0;
+ return 0xffffffff;
+
+ } else {
+ ASSERT( BytesReturned == sizeof(pNwDosTable->DriveHandleTable[Drive]));
+
+ NwPrint(("\nNw32:GetDirectoryHandle2 Success %x\n", pNwDosTable->DriveHandleTable[Drive]));
+ }
+
+ } else {
+ NwPrint(("\nNw32:GetDirectoryHandle2 CreateFile %x\n", GetLastError()));
+
+ Win32DirectoryHandleTable[Drive] = 0;
+
+ return 0xffffffff;
+ }
+
+ }
+
+ return (ULONG)pNwDosTable->DriveHandleTable[Drive];
+}
+
+VOID
+LoadDriveHandleTable(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Open handles to all the NetWare drives
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ USHORT Drive;
+ for (Drive = 0; Drive < MD; Drive++ ) {
+ GetDirectoryHandle2(Drive);
+ }
+
+ DriveHandleTableValid = TRUE;
+
+}
+
+VOID
+AllocateDirectoryHandle(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Allocate permanent or temporary handle for drive.
+
+ For a permanent handle, we map this to a "net use".
+
+ ASSUMES called from Nw16Handler
+
+
+Arguments:
+
+ DS:SI supplies the request.
+ ES:DI supplies the response.
+
+ AL returns the completion code.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ PUCHAR Request=GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI()),
+ 2,
+ CpuInProtectMode
+ );
+
+ PUCHAR Reply = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()),
+ 4,
+ CpuInProtectMode
+ );
+
+ USHORT RequestLength = *(USHORT UNALIGNED *)( Request );
+
+ Request=GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI()),
+ RequestLength+2,
+ CpuInProtectMode
+ );
+
+ FormattedDump( Request, RequestLength+2 );
+
+
+ if (( Request[2] == 0x12) ||
+ ( Request[2] == 0x13)) {
+ // BUGBUG do temp drives need different handling?
+
+ UCHAR Drive = Request[4] - 'A';
+
+ if (Drive >= MD) {
+ setAL( 0x98 ); // Volume does not exist
+ return;
+ }
+
+ if (pNwDosTable->DriveHandleTable[Drive] != 0) {
+
+ NwPrint(("Nw32: Move directory handle %d\n", Drive));
+
+ //
+ // We already have a directory handle assigned for this
+ // process. Ask the server to point the handle at the requested
+ // position.
+ //
+
+ SendNCP2(FSCTL_NWR_NCP_E2H, Request+2, RequestLength, Reply+2, 2);
+
+ if (getAL() == 0) {
+ // Record the new handle.
+
+ pNwDosTable->DriveIdTable[ Drive ] = SelectConnection()+1;
+
+ if (Request[2] == 0x12) {
+ pNwDosTable->DriveFlagTable[ Drive ] =
+ PERMANENT_NETWORK_DRIVE;
+ } else {
+ pNwDosTable->DriveFlagTable[ Drive ] =
+ TEMPORARY_NETWORK_DRIVE;
+ }
+
+ pNwDosTable->DriveHandleTable[Drive] = Reply[2];
+ NwPrint(("Nw32: Move directory handle -> %x\n", Reply[2]));
+ }
+
+ } else {
+ NETRESOURCE Nr;
+ WCHAR DriveString[3];
+ ULONG Handle;
+
+ if (Request[2] == 0x12) {
+ NwPrint(("Nw32: Allocate permanent directory handle %d\n", Drive));
+ } else {
+ NwPrint(("Nw32: Allocate temporary directory handle %d\n", Drive));
+ }
+
+ if (Drives[Drive] != NULL) {
+
+ // Tidy up the old name for this drive.
+
+ LocalFree( Drives[Drive] );
+ Drives[Drive] = NULL;
+ }
+
+ DriveString[0] = L'A' + Drive; // A through Z
+ DriveString[1] = L':';
+ DriveString[2] = L'\0';
+
+ //
+ // This is effectively a net use!
+ //
+
+ ZeroMemory( &Nr, sizeof(NETRESOURCE));
+
+ Nr.lpRemoteName = BuildUNC(&Request[6], Request[5]);
+ Nr.dwType = RESOURCETYPE_DISK;
+
+ // Save where this drive points.
+ Drives[Drive] = Nr.lpRemoteName;
+
+ if (DriveString[0] <= L'Z') {
+ Nr.lpLocalName = DriveString;
+
+ if (NO_ERROR != WNetAddConnection2W( &Nr, NULL, NULL, 0)) {
+
+ NwPrint(("Nw32: Allocate ->%d\n", GetLastError()));
+ setAL(0x98); // Volume does not exist
+ return;
+ }
+ }
+
+
+ if (Request[2] == 0x12) {
+ pNwDosTable->DriveFlagTable[ Drive ] =
+ PERMANENT_NETWORK_DRIVE;
+ } else {
+ pNwDosTable->DriveFlagTable[ Drive ] =
+ TEMPORARY_NETWORK_DRIVE;
+ }
+
+ Handle = GetDirectoryHandle2( Drive );
+
+ if (Handle == 0xffffffff) {
+
+ if (DriveString[0] <= L'Z') {
+
+ WNetCancelConnection2W( DriveString, 0, TRUE);
+
+ }
+
+ ResetDrive( Drive );
+
+ setAL(0x9c); // Invalid path
+
+ } else {
+
+ //
+ // We have a drive and a connection. Complete the table
+ // mappings.
+ //
+
+ pNwDosTable->DriveIdTable[ Drive ] = SelectConnection()+1;
+
+ Reply[2] = (UCHAR)(Handle & 0xff);
+ Reply[3] = (UCHAR)(0xff); //BUGBUG should be effective rights
+ setAL(0); // Successful
+ }
+ }
+
+ } else if ( Request[2] == 0x14 ) {
+
+ UCHAR DirHandle = Request[3];
+ UCHAR Drive;
+ CONN_INDEX Connection = SelectConnection();
+
+ NwPrint(("Nw32: Deallocate directory handle %d on Connection %d\n", DirHandle, Connection));
+
+ for (Drive = 0; Drive < MD; Drive++) {
+
+
+ NwPrint(("Nw32: Drive %c: is DirHandle %d, Connection %d\n",
+ 'A' + Drive,
+ pNwDosTable->DriveHandleTable[Drive],
+ pNwDosTable->DriveIdTable[ Drive ]-1 ));
+
+ if ((pNwDosTable->DriveHandleTable[Drive] == DirHandle) &&
+ (pNwDosTable->DriveIdTable[ Drive ] == Connection+1)) {
+
+ //
+ // This is effectively a net use del!
+ //
+
+ NwPrint(("Nw32: Deallocate directory handle %c\n", 'A' + Drive));
+
+ ResetDrive(Drive);
+
+ setAL(0);
+
+ return;
+ }
+ }
+
+ setAL(0x9b); // Bad directory handle
+ return;
+
+ } else {
+
+ SendNCP(pNwDosTable->SavedAx);
+ }
+
+ FormattedDump( Reply, Reply[0] );
+}
+
+VOID
+ResetDrive(
+ UCHAR Drive
+ )
+/*++
+
+Routine Description:
+
+ Do a net use del
+
+Arguments:
+
+ Drive - Supplies the target drive.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ NwPrint(("Nw32: Reset Drive %c:\n", 'A' + Drive ));
+
+ if ((pNwDosTable->DriveFlagTable[ Drive ] &
+ ( PERMANENT_NETWORK_DRIVE | TEMPORARY_NETWORK_DRIVE )) == 0) {
+
+ return;
+
+ }
+
+ if (Win32DirectoryHandleTable[Drive] != 0) {
+
+ CloseHandle( Win32DirectoryHandleTable[Drive] );
+ Win32DirectoryHandleTable[Drive] = 0;
+
+ }
+
+ if (Drive <= (L'Z' - L'A')) {
+
+ DWORD WStatus;
+ WCHAR DriveString[3];
+
+ DriveString[0] = L'A' + Drive;
+ DriveString[1] = L':';
+ DriveString[2] = L'\0';
+
+ WStatus = WNetCancelConnection2W( DriveString, 0, TRUE);
+
+ if( WStatus != NO_ERROR ) {
+ NwPrint(("Nw32: WNetCancelConnection2W failed %d\n", WStatus ));
+ }
+
+ }
+
+ // Turn off flags that show this drive as redirected
+
+ pNwDosTable->DriveFlagTable[ Drive ] &=
+ ~( PERMANENT_NETWORK_DRIVE | TEMPORARY_NETWORK_DRIVE );
+
+ pNwDosTable->DriveHandleTable[Drive] = 0;
+}
+
+VOID
+AllocateDirectoryHandle2(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Allocate root drive
+
+ ASSUMES called from Nw16Handler
+
+Arguments:
+
+ BL supplies drive to map.
+ DS:DX supplies the pathname
+
+ AL returns the completion code.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UCHAR Drive = getBL()-1;
+
+ PUCHAR Name=GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ 256, // longest valid path
+ CpuInProtectMode
+ );
+
+ NETRESOURCE Nr;
+ WCHAR DriveString[3];
+ ULONG Handle;
+
+ NwPrint(("Nw32: e905 map drive %c to %s\n", Drive + 'A', Name ));
+
+ if (Drive >= MD) {
+ setAL( 0x98 ); // Volume does not exist
+ setCF(1);
+ return;
+ }
+
+ if (pNwDosTable->DriveHandleTable[Drive] != 0) {
+
+ NwPrint(("Nw32: Drive already redirected\n"));
+ ResetDrive(Drive);
+
+ }
+
+
+ NwPrint(("Nw32: Allocate permanent directory handle\n"));
+
+ if (Drives[Drive] != NULL) {
+
+ // Tidy up the old name for this drive.
+
+ LocalFree( Drives[Drive] );
+ Drives[Drive] = NULL;
+ }
+
+ //
+ // This is effectively a net use!
+ //
+
+ ZeroMemory( &Nr, sizeof(NETRESOURCE));
+
+ Nr.lpRemoteName = BuildUNC( Name, strlen(Name));
+ // Save where this drive points.
+ Drives[Drive] = Nr.lpRemoteName;
+
+ if (Drive <= (L'Z' - L'A')) {
+ DriveString[0] = L'A' + Drive; // A through Z
+ DriveString[1] = L':';
+ DriveString[2] = L'\0';
+ Nr.lpLocalName = DriveString;
+ Nr.dwType = RESOURCETYPE_DISK;
+
+ if (NO_ERROR != WNetAddConnection2W( &Nr, NULL, NULL, 0)) {
+
+ NwPrint(("Nw32: Allocate0 ->%d\n", GetLastError()));
+
+ if (GetLastError() == ERROR_ALREADY_ASSIGNED) {
+
+ WNetCancelConnection2W( DriveString, 0, TRUE);
+
+ if (NO_ERROR != WNetAddConnection2W( &Nr, NULL, NULL, 0)) {
+
+ NwPrint(("Nw32: Allocate1 ->%d\n", GetLastError()));
+ ResetDrive( Drive );
+ setAL(0x03); // Volume does not exist
+ setCF(1);
+ return;
+ }
+
+ } else {
+
+ NwPrint(("Nw32: Allocate2 ->%d\n", GetLastError()));
+ ResetDrive( Drive );
+ setAL(0x03); // Volume does not exist
+ setCF(1);
+ return;
+ }
+ }
+ }
+
+ //
+ // Set flags so that GetDirectory2 will open handle
+ //
+ pNwDosTable->DriveIdTable[ Drive ] = SelectConnection()+1;
+ pNwDosTable->DriveFlagTable[ Drive ] = PERMANENT_NETWORK_DRIVE;
+
+ Handle = GetDirectoryHandle2( Drive );
+
+ if (Handle == 0xffffffff) {
+
+ ResetDrive( Drive );
+ setAL(0x03); // Invalid path
+ setCF(1);
+
+ } else {
+
+ setAL(0); // Successful
+
+ }
+
+ NwPrint(("Nw32: Returning %x\n",getAL()));
+}
+
+PWCHAR
+BuildUNC(
+ IN PUCHAR aName,
+ IN ULONG aLength
+ )
+/*++
+
+Routine Description:
+
+ This routine takes the ansi name, prepends the appropriate server name
+ (if appropriate) and converts to Unicode.
+
+Arguments:
+
+ IN aName - Supplies the ansi name.
+ IN aLength - Supplies the ansi name length in bytes.
+
+Return Value:
+
+ Unicode string
+
+--*/
+{
+ UNICODE_STRING Name;
+ UCHAR ServerName[sizeof(SERVERNAME)+1];
+
+ CONN_INDEX Connection;
+ ANSI_STRING TempAnsi;
+ UNICODE_STRING TempUnicode;
+ USHORT x;
+
+ // conversion rules for aName to Name are:
+
+ // foo: "\\server\foo\"
+ // foo:bar\baz "\\server\foo\bar\baz"
+ // foo:\bar\baz "\\server\foo\bar\baz"
+
+
+#ifdef NWDBG
+ TempAnsi.Buffer = aName;
+ TempAnsi.Length = (USHORT)aLength;
+ TempAnsi.MaximumLength = (USHORT)aLength;
+ NwPrint(("Nw32: BuildUNC %Z\n", &TempAnsi));
+#endif
+
+ Connection = SelectConnection();
+ if ( Connection == 0xff ) {
+ return NULL;
+ }
+
+ Name.MaximumLength = (USHORT)(aLength + sizeof(SERVERNAME) + 5) * sizeof(WCHAR);
+ Name.Buffer = (PWSTR)LocalAlloc( LMEM_FIXED, (ULONG)Name.MaximumLength);
+
+ if (Name.Buffer == NULL) {
+ return NULL;
+ }
+
+ Name.Length = 4;
+ Name.Buffer[0] = L'\\';
+ Name.Buffer[1] = L'\\';
+
+ //
+ // Be careful because ServerName might be 48 bytes long and therefore
+ // not null terminated.
+ //
+
+ RtlCopyMemory( ServerName, pNwDosTable->ServerNameTable[Connection], sizeof(SERVERNAME) );
+ ServerName[sizeof(ServerName)-1] = '\0';
+
+ RtlInitAnsiString( &TempAnsi, ServerName );
+ RtlAnsiStringToUnicodeString( &TempUnicode, &TempAnsi, TRUE);
+ RtlAppendUnicodeStringToString( &Name, &TempUnicode );
+ RtlFreeUnicodeString( &TempUnicode );
+
+ // Now pack servername to volume seperator if necessary.
+
+ if ((aLength != 0) &&
+ (aName[0] != '\\')) {
+ RtlAppendUnicodeToString( &Name, L"\\");
+ }
+
+ // aName might not be null terminated so be careful creating TempAnsi
+ TempAnsi.Buffer = aName;
+ TempAnsi.Length = (USHORT)aLength;
+ TempAnsi.MaximumLength = (USHORT)aLength;
+
+ if (!NT_SUCCESS(RtlAnsiStringToUnicodeString( &TempUnicode, &TempAnsi, TRUE))) {
+ LocalFree( Name.Buffer );
+ return NULL;
+ }
+
+ RtlAppendUnicodeStringToString( &Name, &TempUnicode );
+
+ // If the name already has a volume seperator then don't add another.
+ for (x=0; x < (Name.Length/sizeof(WCHAR)) ; x++ ) {
+
+ if (Name.Buffer[x] == L':') {
+
+ // Strip the colon if it is immediately followed by a backslash
+
+ if (((Name.Length/sizeof(WCHAR))-1 > x) &&
+ (Name.Buffer[x+1] == L'\\')) {
+
+ RtlMoveMemory( &Name.Buffer[x],
+ &Name.Buffer[x+1],
+ Name.Length - ((x + 1) * sizeof(WCHAR)));
+ Name.Length -= sizeof(WCHAR);
+
+ } else {
+
+ // Replace the colon with a backslash
+ Name.Buffer[x] = L'\\';
+
+ }
+ goto skip;
+ }
+ }
+
+
+skip:
+
+ RtlFreeUnicodeString( &TempUnicode );
+
+ // Strip trailing backslash if present.
+
+ if ((Name.Length >= sizeof(WCHAR) ) &&
+ (Name.Buffer[(Name.Length/sizeof(WCHAR)) - 1 ] == L'\\')) {
+
+ Name.Length -= sizeof(WCHAR);
+ }
+
+ // Return pointer to a null terminated wide char string.
+
+ Name.Buffer[Name.Length/sizeof(WCHAR)] = L'\0';
+ NwPrint(("Nw32: BuildUNC %ws\n", Name.Buffer));
+
+ return Name.Buffer;
+}
+
+
+VOID
+GetServerDateAndTime(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Implement Funtion E7h
+
+ ASSUMES called from Nw16Handler
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ PUCHAR Reply = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ 7,
+ CpuInProtectMode
+ );
+
+ SendNCP2( NWR_ANY_NCP(0x14), NULL, 0, Reply, 7 );
+
+}
+
+VOID
+GetShellVersion(
+ IN USHORT Command
+ )
+/*++
+
+Routine Description:
+
+ Get the environment variables. Needs to be configurable for
+ Japanese machines.
+
+Arguments:
+
+ Command supplies the callers AX.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ setAX(0); // MSDOS, PC
+ setBX(0x031a); // Shell version
+ setCX(0);
+
+ if ( (Command & 0x00ff) != 0) {
+
+ LONG tmp;
+ HKEY Key = NULL;
+ PUCHAR Reply = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()),
+ 40,
+ CpuInProtectMode
+ );
+
+ ASSERT( sizeof(CLIENT_ID_STRING) <= 40 );
+
+ RtlMoveMemory( Reply, CLIENT_ID_STRING, sizeof(CLIENT_ID_STRING) );
+
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ tmp = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &Key
+ );
+
+ if (tmp != ERROR_SUCCESS) {
+ return;
+ }
+
+ tmp = 40; // Max size for the string.
+
+ RegQueryValueExA(
+ Key,
+ "ShellVersion",
+ NULL,
+ NULL,
+ Reply,
+ &tmp);
+
+ ASSERT( tmp <= 40 );
+
+ RegCloseKey( Key );
+
+ }
+}
+
+#include <packon.h>
+
+typedef struct _TTSOUTPACKETTYPE {
+ UCHAR SubFunction;
+ USHORT cx;
+ USHORT dx;
+} TTSOUTPACKETTYPE;
+
+typedef struct _TTSINPACKETTYPE {
+ USHORT cx;
+ USHORT dx;
+} TTSINPACKETTYPE;
+
+#include <packoff.h>
+
+VOID
+TTS(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Transaction Tracking System
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UCHAR bOutput;
+ UCHAR bInput[2];
+
+ TTSINPACKET TTsInPacket;
+ TTSOUTPACKET TTsOutPacket;
+
+
+ switch ( pNwDosTable->SavedAx & 0x00ff )
+ {
+ case 2:
+ // NCP Tts Available
+ bOutput = 0;
+ SendNCP2( NWR_ANY_F2_NCP(0x22), &bOutput, sizeof(UCHAR), NULL, 0);
+
+ if (getAL() == 0xFF) {
+ setAL(01);
+ }
+ break;
+
+ case 0:
+ // NCP Tts Begin/Abort
+ bOutput = 1;
+ SendNCP2( NWR_ANY_F2_NCP(0x22), &bOutput, sizeof(UCHAR), NULL, 0);
+ break;
+
+ case 3:
+ // NCP Tts Begin/Abort
+ bOutput = 3;
+ SendNCP2( NWR_ANY_F2_NCP(0x22), &bOutput, sizeof(UCHAR), NULL, 0);
+ break;
+
+ case 1:
+ // NCP Tts End
+ bOutput = 2;
+ SendNCP2( NWR_ANY_F2_NCP(0x22),
+ &bOutput, sizeof(UCHAR),
+ (PUCHAR)&TTsInPacket, sizeof(TTsInPacket));
+
+ setCX(TTsInPacket.cx);
+ setDX(TTsInPacket.dx);
+ break;
+
+ case 4:
+ // NCP Tts Status
+ TTsOutPacket.SubFunction = 4;
+ TTsOutPacket.cx = getCX();
+ TTsOutPacket.dx = getDX();
+
+ SendNCP2( NWR_ANY_F2_NCP(0x22),
+ (PUCHAR)&TTsOutPacket, sizeof(TTsOutPacket),
+ NULL, 0);
+
+ break;
+
+ case 5:
+ case 7:
+ // NCP Tts Get App/Station Thresholds
+ bOutput = (pNwDosTable->SavedAx & 0x00ff);
+
+ SendNCP2( NWR_ANY_F2_NCP(0x22),
+ &bOutput, sizeof(UCHAR),
+ bInput, sizeof(bInput));
+
+ setCX( (USHORT)((bInput[0] << 8 ) || bInput[1]) );
+ break;
+
+ case 6:
+ case 8:
+ // NCP Tts Set App/Station Thresholds
+ TTsOutPacket.SubFunction = (pNwDosTable->SavedAx & 0x00ff);
+ TTsOutPacket.cx = getCX();
+ SendNCP2( NWR_ANY_F2_NCP(0x22),
+ (PUCHAR)&TTsOutPacket, sizeof(UCHAR) + sizeof(USHORT),
+ NULL, 0);
+ break;
+
+ default:
+ pNwDosTable->SavedAx = 0xc7FF;
+ break;
+ }
+ return;
+}
+
+VOID
+OpenCreateFile(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Look at the file being opened to determine if it is
+ a compatibility mode open to a file on a NetWare drive.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ WORD Command = pNwDosTable->SavedAx;
+
+ PUCHAR Name;
+
+
+ if ((Command & OF_SHARE_MASK ) != OF_SHARE_COMPAT) {
+ return;
+ }
+
+ Name = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ 256,
+ CpuInProtectMode
+ );
+
+
+ NwPrint(("Nw16Handler Compatibility Open of %s\n", Name ));
+
+ //
+ // We already know its a Create or Open with sharing options
+ // set to compatibility mode or the tsr wouldn't have bopped to us.
+ //
+
+
+ if (IsItNetWare(Name)) {
+
+ SetCompatibility();
+
+ }
+}
+
+BOOL
+IsItNetWare(
+ PUCHAR Name
+ )
+/*++
+
+Routine Description:
+
+ Look at the filename being opened to determine if it is on a NetWare drive.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UCHAR Drive;
+
+ Drive = tolower(Name[0])-'a';
+
+ NwPrint(("Nw16Handler IsItNetWare %s\n", Name ));
+
+ if (Name[1] == ':') {
+
+ if (pNwDosTable->DriveFlagTable[Drive] == LOCAL_DRIVE) {
+
+ // Definitely not a netware drive.
+ return FALSE;
+ }
+
+ } else if ((IS_ASCII_PATH_SEPARATOR(Name[0])) &&
+ (IS_ASCII_PATH_SEPARATOR(Name[0]))) {
+
+ // Assume only UNC names that the tsr built are NetWare
+
+ if ((getDS() == DosTableSegment ) &&
+ (getDX() == (WORD)(DosTableOffset + FIELD_OFFSET(NWDOSTABLE, DeNovellBuffer[0] )))) {
+
+ return TRUE;
+ }
+
+ return FALSE;
+
+ } else {
+
+ Drive = pNwDosTable->CurrentDrive;
+
+ }
+
+ //
+ // If this is a drive we don't know about, refresh our tables.
+ //
+
+ if (pNwDosTable->DriveFlagTable[Drive] == 0 ) {
+
+ Nw16Register();
+
+ }
+
+ if (pNwDosTable->DriveFlagTable[Drive] &
+ (TEMPORARY_NETWORK_DRIVE | PERMANENT_NETWORK_DRIVE )) {
+
+ return TRUE;
+
+ }
+
+ return FALSE;
+
+}
+
+VOID
+SetCompatibility(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Take the Create/Open file request in AX and modify appropriately
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ WORD Command = getAX();
+
+ if (( Command & OF_READ_WRITE_MASK) == OF_READ ) {
+
+ setAX((WORD)(Command | OF_SHARE_DENY_WRITE));
+
+ } else {
+
+ setAX((WORD)(Command | OF_SHARE_EXCLUSIVE));
+
+ }
+
+}
+
+VOID
+OpenQueueFile(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Build the UNC filename \\server\queue using the contents of the shared
+ datastructures and the CreateJobandFile NCP.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ CONN_INDEX Connection = SelectConnection();
+ PUCHAR Request;
+ PUCHAR Buffer = pNwDosTable->DeNovellBuffer;
+ int index;
+
+ if ( Connection == 0xff ) {
+ //
+ // No need to return an errorcode. The NCP exchange
+ // will fail and give an appropriate call to the application.
+ //
+
+ return;
+ }
+
+ if ( ServerHandles[Connection] == NULL ) {
+
+ NTSTATUS status;
+
+ status = OpenConnection( Connection );
+
+ if (!NT_SUCCESS(status)) {
+ SetStatus(status);
+ return;
+ }
+ }
+
+ //
+ // CreateJobandQueue open in progress. The purpose of this
+ // open being processed is to translate the information in
+ // the CreateJob NCP into a pathname to be opened by the 16
+ // bit code.
+ //
+
+
+ //
+ // Users DS:SI points at a CreateJob NCB. Inside the request is
+ // the objectid of the queue. Ask the server for the queue name.
+ //
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI()),
+ 8,
+ CpuInProtectMode);
+
+ NwlibMakeNcp(
+ ServerHandles[Connection],
+ FSCTL_NWR_NCP_E3H,
+ 7, // RequestSize
+ 61, // ResponseSize
+ "br|_r",
+ 0x36, // Get Bindery Object Name
+ Request+3, 4,
+ 6, // Skip ObjectId and Type
+ pNwDosTable->DeNovellBuffer2, 48 );
+
+
+ pNwDosTable->DeNovellBuffer2[54] = '\0';
+
+ Buffer[0] = '\\';
+ Buffer[1] = '\\';
+ Buffer += 2; // Point to after backslashes
+
+ // Copy the servername
+ for (index = 0; index < sizeof(SERVERNAME); index++) {
+ Buffer[index] = pNwDosTable->ServerNameTable[Connection][index];
+ if (Buffer[index] == '\0') {
+ break;
+ }
+ }
+
+ Buffer[index] = '\\';
+
+ RtlCopyMemory( &Buffer[index+1], &pNwDosTable->DeNovellBuffer2[0], 48 );
+
+ NwPrint(("Nw32: CreateQueue Job and File %s\n", pNwDosTable->DeNovellBuffer));
+
+ //
+ // Set up 16 bit registers to do the DOS OpenFile for \\server\queue
+ //
+
+ setDS((WORD)(CpuInProtectMode ? pNwDosTable->PmSelector : DosTableSegment));
+ setDX( (WORD)(DosTableOffset + FIELD_OFFSET(NWDOSTABLE, DeNovellBuffer[0] )) );
+ setAX(0x3d02); // Set to OpenFile
+
+}
+
+VOID
+AttachHandle(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine implements Int 21 B4. Which is supposed to create a
+ Dos Handle that corresponds to a specified 6 byte NetWare handle.
+
+ This is used as a replacement for doing a DosOpen on "NETQ" and usin the
+ handle returned from there.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ if ( pNwDosTable->CreatedJob ) {
+
+ NwPrint(("Nw32: AttachHandle %x\n", pNwDosTable->JobHandle));
+ setAX( pNwDosTable->JobHandle );
+ pNwDosTable->CreatedJob = 0; // Only return it once.
+
+ } else {
+
+ NwPrint(("Nw32: AttachHandle failed, no job\n"));
+ setAX(ERROR_FILE_NOT_FOUND);
+ setCF(1);
+
+ }
+}
+
+VOID
+ProcessExit(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Cleanup all cached handles. Unmap all temporary drives.
+
+ Cleanup the server name table so that if another dos app
+ is started we reload all the useful information such as
+ the servers connection number.
+
+ Note: Dos always completes processing after we complete.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UCHAR Connection;
+ UCHAR Drive;
+ USHORT Command = pNwDosTable->SavedAx;
+
+ ResetLocks();
+
+ for (Drive = 0; Drive < MD; Drive++) {
+
+ NwPrint(("Nw32: Deallocate directory handle %c\n", 'A' + Drive));
+
+ if (Win32DirectoryHandleTable[Drive] != 0) {
+
+ CloseHandle( Win32DirectoryHandleTable[Drive] );
+ Win32DirectoryHandleTable[Drive] = 0;
+ pNwDosTable->DriveHandleTable[Drive] = 0;
+
+ }
+ }
+
+ for (Connection = 0; Connection < MC ; Connection++ ) {
+ if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse == IN_USE) {
+
+ CloseConnection(Connection);
+
+ pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
+
+ ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
+ }
+ }
+
+ pNwDosTable->PreferredServer = 0;
+
+ LockMode = 0;
+ TablesValid = FALSE;
+ DriveHandleTableValid = FALSE;
+
+#if NWDBG
+ if (DebugCtrl & ~3 ) {
+ DebugControl( 2 ); // Close logfile
+ }
+ GotDebugState = FALSE;
+#endif
+
+ //
+ // set AX register so that AH gets preserved
+ //
+
+ setAX( Command );
+}
+
+VOID
+SystemLogout(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This api is called by the NetWare login.
+
+ Remove all NetWare redirected drives and logout connections
+ that don't have open handles on them. Don't detach the connections.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ UCHAR Connection;
+ UCHAR Drive;
+ USHORT Command = pNwDosTable->SavedAx;
+
+ ResetLocks();
+
+ for (Drive = 0; Drive < MD; Drive++) {
+ ResetDrive(Drive);
+ }
+
+ for (Connection = 0; Connection < MC ; Connection++ ) {
+ if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse == IN_USE) {
+
+ if ( ServerHandles[Connection] == NULL ) {
+ OpenConnection( Connection );
+ }
+
+ if (ServerHandles[Connection] != NULL ) {
+
+ NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(NCP_LOGOUT),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+
+ CloseConnection(Connection);
+ }
+
+ //pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
+
+ //ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
+ }
+ }
+
+ pNwDosTable->PreferredServer = 0;
+ pNwDosTable->PrimaryServer = 0;
+
+ // No servers in the table so find the nearest/preferred.
+
+ LoadPreferredServerName();
+
+ //
+ // set AX register so that AH gets preserved
+ // and AL says success.
+ //
+
+ setAX( (USHORT)(Command & 0xff00) );
+}
+
+UCHAR
+AttachmentControl(
+ ULONG Command
+ )
+/*++
+
+Routine Description:
+
+ Implement Funtion F1h
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ Return status.
+
+--*/
+{
+ UCHAR Connection = getDL();
+
+ if ((Connection < 1) ||
+ (Connection > MC)) {
+ return 0xf7;
+ }
+
+ Connection -= 1;
+
+ switch (Command & 0x00ff) {
+
+ case 0: // Attach
+
+ NwPrint(("Nw16AttachmentControl: Attach connection %d\n", Connection));
+
+ pNwDosTable->ConnectionIdTable[Connection].ci_InUse = IN_USE;
+
+ if ( ServerHandles[Connection] == NULL ) {
+
+ NTSTATUS status = OpenConnection( Connection );
+
+ if (!NT_SUCCESS(status)) {
+ pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
+ ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
+ return (UCHAR)RtlNtStatusToDosError(status);
+ } else {
+ InitConnection(Connection);
+ }
+ }
+
+ return 0;
+ break;
+
+ case 1: // Detach
+
+ NwPrint(("Nw16AttachmentControl: Detach connection %d\n", Connection));
+
+ if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse != IN_USE) {
+ return 0xff;
+ } else {
+
+ pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
+
+ if (ServerHandles[Connection] != NULL ) {
+ CloseConnection(Connection);
+ }
+
+ ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
+
+ if (pNwDosTable->PrimaryServer == (UCHAR)Connection + 1 ) {
+
+ // Need to pick another
+ UCHAR IndexConnection;
+
+ pNwDosTable->PrimaryServer = 0;
+
+ for (IndexConnection = 0; IndexConnection < MC ; IndexConnection++ ) {
+
+ if (pNwDosTable->ConnectionIdTable[IndexConnection].ci_InUse == IN_USE) {
+
+ pNwDosTable->PrimaryServer = IndexConnection + 1;
+
+ }
+ }
+
+ }
+
+ if (pNwDosTable->PreferredServer == (UCHAR)Connection + 1 ) {
+ pNwDosTable->PreferredServer = 0;
+ }
+
+ return 0;
+ }
+
+ case 2: // Logout
+
+ NwPrint(("Nw16AttachmentControl: Logout connection %d\n", Connection));
+
+ if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse != IN_USE) {
+ return 0xff;
+ } else {
+
+ UCHAR Drive;
+
+ if ( ServerHandles[Connection] == NULL ) {
+ OpenConnection( Connection );
+ }
+
+ for (Drive = 0; Drive < MD; Drive++ ) {
+ if (pNwDosTable->DriveIdTable[ Drive ] == (Connection + 1)) {
+ ResetDrive(Drive);
+ }
+ }
+
+ if (ServerHandles[Connection] != NULL ) {
+ NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(NCP_LOGOUT),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ CloseConnection(Connection);
+ }
+
+ return 0;
+ }
+
+ }
+ return 0xff;
+}
+
+VOID
+ServerFileCopy(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Build the NCP that tells the server to move a file on the server.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ DWORD BytesReturned;
+ UCHAR SrcHandle[6];
+ UCHAR DestHandle[6];
+ NTSTATUS status;
+ PUCHAR Buffer;
+
+ Buffer = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()),
+ 16,
+ CpuInProtectMode
+ );
+
+ if ( DeviceIoControl(
+ GET_NT_SRCHANDLE(),
+ IOCTL_NWR_RAW_HANDLE,
+ NULL,
+ 0,
+ (PUCHAR)&SrcHandle,
+ sizeof(SrcHandle),
+ &BytesReturned,
+ NULL ) == FALSE ) {
+
+ setAL(0xff);
+ return;
+
+ }
+
+ if ( DeviceIoControl(
+ GET_NT_HANDLE(),
+ IOCTL_NWR_RAW_HANDLE,
+ NULL,
+ 0,
+ (PUCHAR)&DestHandle,
+ sizeof(DestHandle),
+ &BytesReturned,
+ NULL ) == FALSE ) {
+
+ setAL(0xff);
+ return;
+
+ }
+
+ status = NwlibMakeNcp(
+ GET_NT_SRCHANDLE(),
+ NWR_ANY_F2_NCP(0x4A),
+ 25, // RequestSize
+ 4, // ResponseSize
+ "brrddd|d",
+ 0,
+ SrcHandle, 6,
+ DestHandle, 6,
+ *(DWORD UNALIGNED*)&Buffer[4],
+ *(DWORD UNALIGNED*)&Buffer[8],
+ *(DWORD UNALIGNED*)&Buffer[12],
+ &BytesReturned
+ );
+
+ setDX((WORD)(BytesReturned >> 16));
+ setCX((WORD)BytesReturned);
+
+ if (!NT_SUCCESS(status)) {
+ SetStatus(status);
+ return;
+ } else {
+ setAL(0);
+ }
+}
+
+VOID
+SetStatus(
+ NTSTATUS Status
+ )
+/*++
+
+Routine Description:
+
+ Convert an NTSTATUS into the appropriate register settings and updates
+ to the dos tables.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UCHAR DosStatus = (UCHAR)RtlNtStatusToDosError(Status);
+
+ if ((!DosStatus) &&
+ (Status != 0)) {
+
+ //
+ // We have a connection bit set
+ //
+
+ if ( Status & (NCP_STATUS_BAD_CONNECTION << 8)) {
+ DosStatus = 0xfc;
+ } else {
+ DosStatus = 0xff;
+ }
+ }
+
+ if (DosStatus) {
+ setCF(1);
+ }
+
+ setAL(DosStatus);
+}