summaryrefslogtreecommitdiffstats
path: root/private/nw/nw16/dll
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/nw16/dll')
-rw-r--r--private/nw/nw16/dll/debug.c584
-rw-r--r--private/nw/nw16/dll/locks.c677
-rw-r--r--private/nw/nw16/dll/makefile6
-rw-r--r--private/nw/nw16/dll/ncp.c3383
-rw-r--r--private/nw/nw16/dll/nwapi16.rc12
-rw-r--r--private/nw/nw16/dll/nwapi16.src10
-rw-r--r--private/nw/nw16/dll/procs.h159
-rw-r--r--private/nw/nw16/dll/sources71
8 files changed, 4902 insertions, 0 deletions
diff --git a/private/nw/nw16/dll/debug.c b/private/nw/nw16/dll/debug.c
new file mode 100644
index 000000000..886acd75d
--- /dev/null
+++ b/private/nw/nw16/dll/debug.c
@@ -0,0 +1,584 @@
+/*++
+
+Copyright (c) 1991-3 Microsoft Corporation
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ This component of netbios runs in the user process and can ( when
+ built in a debug kernel) will log to either the console or through the
+ kernel debugger.
+
+Author:
+
+ Colin Watson (ColinW) 24-Jun-91
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+#if NWDBG
+
+//
+// Set DebugControl to 1 to open the logfile on the first NW call and close it
+// on process exit.
+//
+
+int DebugCtrl = 0;
+
+BOOL UseConsole = FALSE;
+BOOL UseLogFile = FALSE;
+BOOL Verbose = FALSE;
+
+HANDLE LogFile = INVALID_HANDLE_VALUE;
+#define LOGNAME (LPTSTR) TEXT("c:\\nwapi16.log")
+
+LONG NwMaxDump = SERVERNAME_LENGTH * MC; //128;
+
+#define ERR_BUF_SIZE 260
+#define NAME_BUF_SIZE 260
+
+extern UCHAR CpuInProtectMode;
+
+LPSTR
+ConvertFlagsToString(
+ IN WORD FlagsRegister,
+ OUT LPSTR Buffer
+ );
+
+WORD
+GetFlags(
+ VOID
+ );
+
+VOID
+HexDumpLine(
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t
+ );
+
+VOID
+DebugControl(
+ int Command
+ )
+/*++
+
+Routine Description:
+
+ This routine controls what we output as debug information and where.
+
+Arguments:
+
+ IN int Command
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ switch (Command) {
+ case 0:
+ UseLogFile = TRUE;
+ break;
+
+ case 1:
+ UseConsole = TRUE;
+ break;
+
+ case 2:
+ if (LogFile != INVALID_HANDLE_VALUE) {
+ CloseHandle(LogFile);
+ LogFile = INVALID_HANDLE_VALUE;
+ }
+ UseLogFile = FALSE;
+ UseConsole = FALSE;
+ break;
+
+ case 8:
+ Verbose = TRUE; // Same as 4 only chatty
+ DebugCtrl = 4;
+
+ case 4:
+ UseLogFile = TRUE;
+ break;
+
+ }
+ NwPrint(("DebugControl %x\n", Command ));
+}
+
+VOID
+NwPrintf(
+ char *Format,
+ ...
+ )
+/*++
+
+Routine Description:
+
+ This routine is equivalent to printf with the output being directed to
+ stdout.
+
+Arguments:
+
+ IN char *Format - Supplies string to be output and describes following
+ (optional) parameters.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ va_list arglist;
+ char OutputBuffer[200];
+ int length;
+
+ if (( UseConsole == FALSE ) &&
+ ( UseLogFile == FALSE )) {
+ return;
+ }
+
+
+ va_start( arglist, Format );
+
+ length = _vsnprintf( OutputBuffer, sizeof(OutputBuffer)-1, Format, arglist );
+ if (length < 0) {
+ return;
+ }
+
+ OutputBuffer[sizeof(OutputBuffer)-1] = '\0'; // in-case length= 199;
+
+ va_end( arglist );
+
+ if ( UseConsole ) {
+ DbgPrint( "%s", OutputBuffer );
+ } else {
+
+ if ( LogFile == INVALID_HANDLE_VALUE ) {
+ if ( UseLogFile ) {
+ LogFile = CreateFile( LOGNAME,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ NULL,
+ TRUNCATE_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+ if (LogFile == INVALID_HANDLE_VALUE) {
+ LogFile = CreateFile( LOGNAME,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+ }
+
+ if ( LogFile == INVALID_HANDLE_VALUE ) {
+ UseLogFile = FALSE;
+ return;
+ }
+ }
+ }
+
+ WriteFile( LogFile , (LPVOID )OutputBuffer, length, &length, NULL );
+ }
+
+} // NwPrintf
+
+void
+FormattedDump(
+ PCHAR far_p,
+ LONG len
+ )
+/*++
+
+Routine Description:
+
+ This routine outputs a buffer in lines of text containing hex and
+ printable characters.
+
+Arguments:
+
+ IN far_p - Supplies buffer to be displayed.
+ IN len - Supplies the length of the buffer in bytes.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ ULONG l;
+ char s[80], t[80];
+
+ if (( UseConsole == FALSE ) &&
+ ( UseLogFile == FALSE )) {
+ return;
+ }
+
+ if (( len > NwMaxDump ) ||
+ ( len < 0 )) {
+ len = NwMaxDump;
+ }
+
+ while (len) {
+ l = len < 16 ? len : 16;
+
+ NwPrint(("%lx ", far_p));
+ HexDumpLine (far_p, l, s, t);
+ NwPrint(("%s%.*s%s\n", s, 1 + ((16 - l) * 3), "", t));
+
+ len -= l;
+ far_p += l;
+ }
+}
+
+VOID
+HexDumpLine(
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t
+ )
+/*++
+
+Routine Description:
+
+ This routine builds a line of text containing hex and printable characters.
+
+Arguments:
+
+ IN pch - Supplies buffer to be displayed.
+ IN len - Supplies the length of the buffer in bytes.
+ IN s - Supplies the start of the buffer to be loaded with the string
+ of hex characters.
+ IN t - Supplies the start of the buffer to be loaded with the string
+ of printable ascii characters.
+
+
+Return Value:
+
+ none.
+
+--*/
+{
+ static UCHAR rghex[] = "0123456789ABCDEF";
+
+ UCHAR c;
+ UCHAR *hex, *asc;
+
+
+ hex = s;
+ asc = t;
+
+ *(asc++) = '*';
+ while (len--) {
+ c = *(pch++);
+ *(hex++) = rghex [c >> 4] ;
+ *(hex++) = rghex [c & 0x0F];
+ *(hex++) = ' ';
+ *(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c;
+ }
+ *(asc++) = '*';
+ *asc = 0;
+ *hex = 0;
+
+}
+
+
+VOID
+DisplayExtendedError(VOID)
+{
+ TCHAR errorBuf[ERR_BUF_SIZE];
+ TCHAR nameBuf[NAME_BUF_SIZE];
+ DWORD errorCode;
+ DWORD status;
+
+ status = WNetGetLastError(
+ &errorCode,
+ errorBuf,
+ ERR_BUF_SIZE,
+ nameBuf,
+ NAME_BUF_SIZE);
+
+ if(status != WN_SUCCESS) {
+ NwPrint(("nwapi32: WNetGetLastError failed %d\n",status));
+ return;
+ }
+ NwPrint(("nwapi32: EXTENDED ERROR INFORMATION: (from GetLastError)\n"));
+ NwPrint(("nwapi32: Code: %d\n",errorCode));
+ NwPrint(("nwapi32: Description: "FORMAT_LPSTR"\n",errorBuf));
+ NwPrint(("nwapi32: Provider: "FORMAT_LPSTR"\n\n",nameBuf));
+ return;
+}
+
+VOID
+VrDumpRealMode16BitRegisters(
+ IN BOOL DebugStyle
+ )
+
+/*++
+
+Routine Description:
+
+ Displays dump of 16-bit
+ real-mode 80286 registers - gp registers (8), segment registers (4), flags
+ register (1) instruction pointer register (1)
+
+Arguments:
+
+ DebugStyle - determines look of output:
+
+DebugStyle == TRUE:
+
+ax=1111 bx=2222 cx=3333 dx=4444 sp=5555 bp=6666 si=7777 di=8888
+ds=aaaa es=bbbb ss=cccc cs=dddd ip=iiii fl fl fl fl fl fl fl fl
+
+DebugStyle == FALSE:
+
+cs:ip=cccc:iiii ss:sp=ssss:pppp bp=bbbb ax=1111 bx=2222 cx=3333 dx=4444
+ds:si=dddd:ssss es:di=eeee:dddd flags[ODIxSZxAxPxC]=fl fl fl fl fl fl fl fl
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ char flags_string[25];
+
+ if (( UseConsole == FALSE ) &&
+ ( UseLogFile == FALSE )) {
+ return;
+ }
+
+ if (CpuInProtectMode) {
+ NwPrint(( "Protect Mode:\n"));
+ }
+
+ if (DebugStyle) {
+ NwPrint((
+ "ax=%04x bx=%04x cx=%04x dx=%04x sp=%04x bp=%04x si=%04x di=%04x\n"
+ "ds=%04x es=%04x ss=%04x cs=%04x ip=%04x %s\n\n",
+
+ pNwDosTable->SavedAx, //getAX(),
+ getBX(),
+ getCX(),
+ getDX(),
+ getSP(),
+ getBP(),
+ getSI(),
+ getDI(),
+ getDS(),
+ getES(),
+ getSS(),
+ getCS(),
+ getIP(),
+ ConvertFlagsToString(GetFlags(), flags_string)
+ ));
+ } else {
+ NwPrint((
+ "cs:ip=%04x:%04x ss:sp=%04x:%04x bp=%04x ax=%04x bx=%04x cx=%04x dx=%04x\n"
+ "ds:si=%04x:%04x es:di=%04x:%04x flags[ODITSZxAxPxC]=%s\n\n",
+ getCS(),
+ getIP(),
+ getSS(),
+ getSP(),
+ getBP(),
+ pNwDosTable->SavedAx, //getAX(),
+ getBX(),
+ getCX(),
+ getDX(),
+ getDS(),
+ getSI(),
+ getES(),
+ getDI(),
+ ConvertFlagsToString(GetFlags(), flags_string)
+ ));
+ }
+}
+
+LPSTR
+ConvertFlagsToString(
+ IN WORD FlagsRegister,
+ OUT LPSTR Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ Given a 16-bit word, interpret bit positions as for x86 Flags register
+ and produce descriptive string of flags state (as per debug) eg:
+
+ NV UP DI PL NZ NA PO NC ODItSZxAxPxC = 000000000000b
+ OV DN EI NG ZR AC PE CY ODItSZxAxPxC = 111111111111b
+
+ Trap Flag (t) is not dumped since this has no interest for programs which
+ are not debuggers or don't examine program execution (ie virtually none)
+
+Arguments:
+
+ FlagsRegister - 16-bit flags
+ Buffer - place to store string. Requires 25 bytes inc \0
+
+Return Value:
+
+ Address of <Buffer>
+
+--*/
+
+{
+ static char* flags_states[16][2] = {
+ //0 1
+ "NC", "CY", // CF (0x0001) - Carry
+ "", "", // x (0x0002)
+ "PO", "PE", // PF (0x0004) - Parity
+ "", "", // x (0x0008)
+ "NA", "AC", // AF (0x0010) - Aux (half) carry
+ "", "", // x (0x0020)
+ "NZ", "ZR", // ZF (0x0040) - Zero
+ "PL", "NG", // SF (0x0080) - Sign
+ "", "", // TF (0x0100) - Trap (not dumped)
+ "DI", "EI", // IF (0x0200) - Interrupt
+ "UP", "DN", // DF (0x0400) - Direction
+ "NV", "OV", // OF (0x0800) - Overflow
+ "", "", // x (0x1000) - (I/O Privilege Level) (not dumped)
+ "", "", // x (0x2000) - (I/O Privilege Level) (not dumped)
+ "", "", // x (0x4000) - (Nested Task) (not dumped)
+ "", "" // x (0x8000)
+ };
+ int i;
+ WORD bit;
+ BOOL on;
+
+ *Buffer = 0;
+ for (bit=0x0800, i=11; bit; bit >>= 1, --i) {
+ on = (BOOL)((FlagsRegister & bit) == bit);
+ if (flags_states[i][on][0]) {
+ strcat(Buffer, flags_states[i][on]);
+ strcat(Buffer, " ");
+ }
+ }
+ return Buffer;
+}
+
+WORD
+GetFlags(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Supplies the missing softpc function
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Conglomerates softpc flags into x86 flags word
+
+--*/
+
+{
+ WORD flags;
+
+ flags = (WORD)getCF();
+ flags |= (WORD)getPF() << 2;
+ flags |= (WORD)getAF() << 4;
+ flags |= (WORD)getZF() << 6;
+ flags |= (WORD)getSF() << 7;
+ flags |= (WORD)getIF() << 9;
+ flags |= (WORD)getDF() << 10;
+ flags |= (WORD)getOF() << 11;
+
+ return flags;
+}
+
+VOID
+VrDumpNwData(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Dumps out the state of the 16 bit datastructures.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int index;
+ int Drive;
+
+ if (Verbose == FALSE) {
+ return;
+ }
+
+ NwPrint(( "Preferred = %x, Primary = %x\n",
+ pNwDosTable->PreferredServer,
+ pNwDosTable->PrimaryServer));
+
+ for (index = 0; index < MC; index++) {
+
+
+ if ((PUCHAR)pNwDosTable->ServerNameTable[index][0] != 0 ) {
+
+ if (pNwDosTable->ConnectionIdTable[index].ci_InUse != IN_USE) {
+ NwPrint(("Warning Connection not in use %x: %x\n",
+ index,
+ pNwDosTable->ConnectionIdTable[index].ci_InUse));
+ }
+
+ NwPrint((" Server %d = %s, Connection = %d\n",
+ index,
+ (PUCHAR)pNwDosTable-> ServerNameTable[index],
+ (((pNwDosTable->ConnectionIdTable[index]).ci_ConnectionHi * 256) +
+ ( pNwDosTable-> ConnectionIdTable[index]).ci_ConnectionLo )));
+ } else {
+ if (pNwDosTable->ConnectionIdTable[index].ci_InUse != FREE) {
+ NwPrint(("Warning Connection in use but name is null %x: %x\n",
+ index,
+ pNwDosTable->ConnectionIdTable[index]));
+ }
+ }
+ }
+
+ for (Drive = 0; Drive < MD; Drive++ ) {
+
+
+ if (pNwDosTable->DriveFlagTable[Drive] & 3) {
+ NwPrint(("%c=%x on server %d,",'A' + Drive,
+ pNwDosTable->DriveFlagTable[Drive],
+ pNwDosTable->DriveIdTable[ Drive ] ));
+ }
+
+ }
+ NwPrint(("\n"));
+}
+#endif
+
diff --git a/private/nw/nw16/dll/locks.c b/private/nw/nw16/dll/locks.c
new file mode 100644
index 000000000..f3d9ff362
--- /dev/null
+++ b/private/nw/nw16/dll/locks.c
@@ -0,0 +1,677 @@
+
+/*++
+
+Copyright (c) 1993/4 Microsoft Corporation
+
+Module Name:
+
+ Locks.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ 16 bit support to perform the synchonization api's
+
+Author:
+
+ Colin Watson [ColinW] 07-Dec-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+UCHAR LockMode = 0;
+
+BOOLEAN Tickle[MC];
+
+NTSTATUS
+Sem(
+ UCHAR Function,
+ UCHAR Connection
+ );
+
+VOID
+Locks(
+ USHORT Command
+ )
+/*++
+
+Routine Description:
+
+ Implements all the locking operations
+
+Arguments:
+
+ Command - supplies Applications AX.
+
+Return Value:
+
+ Return status.
+
+--*/
+{
+ UCHAR Function = Command & 0x00ff;
+ USHORT Operation = Command & 0xff00;
+ CONN_INDEX Connection;
+ NTSTATUS status = STATUS_SUCCESS;
+ PUCHAR Request;
+ ULONG RequestLength;
+ WORD Timeout;
+
+ if ( Operation != 0xCF00) {
+
+ //
+ // Connection does not need to be initialised for CF00 because
+ // we have to loop through all connections. Its harmful because
+ // a CF00 is created during ProcessExit(). If we call selectconnection
+ // and there is no server available this will make process exit
+ // really slow.
+ //
+
+ Connection = SelectConnection();
+ if (Connection == 0xff) {
+ setAL(0xff);
+ return;
+ }
+
+ if ( ServerHandles[Connection] == NULL ) {
+
+ status = OpenConnection( Connection );
+
+ if (!NT_SUCCESS(status)) {
+ setAL((UCHAR)RtlNtStatusToDosError(status));
+ return;
+ }
+ }
+ }
+
+ switch ( Operation ) {
+
+ case 0xBC00: // Log physical record
+
+ status = NwlibMakeNcp(
+ GET_NT_HANDLE(),
+ NWR_ANY_HANDLE_NCP(0x1A),
+ 17, // RequestSize
+ 0, // ResponseSize
+ "b_wwwww",
+ Function,
+ 6, // Leave space for NetWare handle
+ getCX(),getDX(),
+ getSI(),getDI(),
+ getBP());
+ break;
+
+ case 0xBD00: // Physical Unlock
+ status = NwlibMakeNcp(
+ GET_NT_HANDLE(),
+ NWR_ANY_HANDLE_NCP(0x1C),
+ 15, // RequestSize
+ 0, // ResponseSize
+ "b_wwww",
+ Function,
+ 6, // Leave space for NetWare handle
+ getCX(),getDX(),
+ getSI(),getDI());
+
+ break;
+
+ case 0xBE00: // Clear physical record
+
+ status = NwlibMakeNcp(
+ GET_NT_HANDLE(),
+ NWR_ANY_HANDLE_NCP(0x1E),
+ 15, // RequestSize
+ 0, // ResponseSize
+ "b_wwww",
+ Function,
+ 6, // Leave space for NetWare handle
+ getCX(),getDX(),
+ getSI(),getDI());
+
+ break;
+
+ case 0xC200: // Physical Lock set
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x1B),
+ 3, // RequestSize
+ 0, // ResponseSize
+ "bw",
+ Function,
+ getBP());
+ break;
+
+ case 0xC300: // Release Physical Record Set
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x1D),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ break;
+
+ case 0xC400: // Clear Physical Record Set
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x1F), // Clear Physical Record Set
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ break;
+
+ case 0xC500: // All Semaphore operations
+ status = Sem(Function, Connection);
+ break;
+
+ case 0xC600: // Set/Get Lock mode
+
+ if (Function != 2) {
+ LockMode = Function;
+ }
+
+ setAL(LockMode);
+ return; // avoid setting AL to status at the end of this routine
+ break;
+
+ case 0xCB00: // Lock File Set
+
+ if (LockMode == 0) {
+ if (getDL()) {
+ Timeout = 0xffff;
+ } else {
+ Timeout = 0;
+ }
+ } else {
+ Timeout = getBP();
+ }
+
+ for (Connection = 0; Connection < MC; Connection++) {
+ if (Tickle[Connection]) {
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x04),
+ 2, // RequestSize
+ 0, // ResponseSize
+ "w",
+ Timeout);
+ if (!NT_SUCCESS(status)) {
+ break;
+ }
+ }
+ }
+ break;
+
+ case 0xCD00: // Release File Set
+ case 0xCF00: // Clear File Set
+ for (Connection = 0; Connection < MC; Connection++) {
+ if (Tickle[Connection]) {
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ (Operation == 0xCD00) ? NWR_ANY_F2_NCP(0x06): NWR_ANY_F2_NCP(0x08),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ if (!NT_SUCCESS(status)) {
+ break;
+ }
+
+ if (Operation == 0xCF00) {
+ Tickle[Connection] = FALSE;
+ }
+ }
+ }
+
+ break;
+
+ case 0xD000: // Log Logical Record
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ sizeof(UCHAR),
+ IS_PROTECT_MODE());
+
+ RequestLength = Request[0] + 1;
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ RequestLength,
+ IS_PROTECT_MODE());
+
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x09),
+ RequestLength + 5, // RequestSize
+ 0, // ResponseSize
+ "bwbr",
+ (LockMode) ? Function : 0,
+ (LockMode) ? getBP() : 0,
+ RequestLength,
+ Request, RequestLength );
+ break;
+
+ case 0xD100: // Lock Logical Record Set
+
+ if (LockMode == 0) {
+ if (getDL()) {
+ Timeout = 0xffff;
+ } else {
+ Timeout = 0;
+ }
+ } else {
+ Timeout = getBP();
+ }
+
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x0A),
+ 3, // RequestSize
+ 0, // ResponseSize
+ "bw",
+ (LockMode) ? Function : 0,
+ Timeout);
+ break;
+
+ case 0xD200: // Release File
+ case 0xD400: // Clear Logical Record
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ sizeof(UCHAR),
+ IS_PROTECT_MODE());
+
+ RequestLength = Request[0]+1;
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ RequestLength,
+ IS_PROTECT_MODE());
+
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ (Operation == 0xD200) ? NWR_ANY_F2_NCP(0x0C) :
+ NWR_ANY_F2_NCP(0x0B),
+ RequestLength+1,
+ 0, // ResponseSize
+ "br",
+ RequestLength,
+ Request, RequestLength );
+ break;
+
+ case 0xD300:
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x13),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ break;
+
+
+ case 0xD500: // Clear Logical Record Set
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x0E),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ break;
+
+ case 0xEB00: // Log File
+ case 0xEC00: // Release File
+ case 0xED00: // Clear File
+ {
+ UCHAR DirHandle;
+ HANDLE Win32DirectoryHandle = 0;
+ PUCHAR ptr;
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ 256 * sizeof(UCHAR),
+ IS_PROTECT_MODE());
+
+ RequestLength = strlen(Request);
+
+ // Find DirHandle
+ ptr = Request;
+ while ( (*ptr != 0) &&
+ (!IS_ASCII_PATH_SEPARATOR(*ptr)) &&
+ (*ptr != ':' )) {
+ ptr++;
+ }
+
+ if (IS_ASCII_PATH_SEPARATOR(*ptr)) {
+ int ServerNameLength = ptr - Request;
+ PUCHAR scanptr = ptr;
+
+ //
+ // Make sure there is a ":" further up the name otherwise
+ // we could confuse foo\bar.txt with a server called foo
+ //
+
+ while ( (*scanptr != 0) &&
+ (*scanptr != ':' )) {
+ scanptr++;
+ }
+
+ if (*scanptr) {
+ //
+ // Name is of the form server\sys:foo\bar.txt
+ // set connection appropriately.
+ //
+
+ for (Connection = 0; Connection < MC ; Connection++ ) {
+
+ //
+ // Look for server foo avoiding foobar.
+ //
+
+ if ((pNwDosTable->ConnectionIdTable[Connection].ci_InUse ==
+ IN_USE) &&
+ (!memcmp( pNwDosTable->ServerNameTable[Connection],
+ Request,
+ ServerNameLength)) &&
+ (pNwDosTable->ServerNameTable[Connection][ServerNameLength] ==
+ '\0')) {
+ break; // Connection is the correct server
+ }
+ }
+
+ //
+ // Move Request to after the seperator and ptr to the ":"
+ //
+
+ RequestLength -= ptr + sizeof(UCHAR) - Request;
+ Request = ptr + sizeof(UCHAR);
+ ptr = scanptr;
+ }
+ }
+
+ if (*ptr) {
+
+ //
+ // Name of form "sys:foo\bar.txt" this gives the server
+ // all the information required.
+ //
+
+ DirHandle = 0;
+
+ if (Request[1] == ':') {
+
+ UCHAR Drive = tolower(Request[0])-'a';
+
+ //
+ // Its a normal (redirected) drive k:foo\bar.txt.
+ // Use the drive tables to give the connection and handle.
+ //
+
+ Connection = pNwDosTable->DriveIdTable[ Drive ] - 1;
+ DirHandle = pNwDosTable->DriveHandleTable[Drive];
+
+ if (DirHandle == 0) {
+ DirHandle = (UCHAR)GetDirectoryHandle2(Drive);
+ }
+ Request += 2; // skip "k:"
+ RequestLength -= 2;
+ }
+
+ } else {
+
+ WCHAR Curdir[256];
+
+ //
+ // Name of form "foo\bar.txt"
+ //
+
+ GetCurrentDirectory(sizeof(Curdir), Curdir);
+
+ Win32DirectoryHandle =
+ CreateFileW( Curdir,
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ 0);
+
+ if (Win32DirectoryHandle != INVALID_HANDLE_VALUE) {
+ DWORD BytesReturned;
+
+ if ( DeviceIoControl(
+ Win32DirectoryHandle,
+ IOCTL_NWR_RAW_HANDLE,
+ NULL,
+ 0,
+ (PUCHAR)&DirHandle,
+ sizeof(DirHandle),
+ &BytesReturned,
+ NULL ) == FALSE ) {
+
+ CloseHandle( Win32DirectoryHandle );
+ setAL(0xff);
+ return;
+
+ }
+
+ } else {
+
+ setAL(0xff);
+ return;
+ }
+ }
+
+ if (Operation == 0xEB00) {
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x03),
+ RequestLength + 5,
+ 0, // ResponseSize
+ "bbwbr",
+ DirHandle,
+ (LockMode) ? Function : 0,
+ (LockMode) ? getBP() : 0,
+ RequestLength,
+ Request, RequestLength );
+
+ Tickle[Connection] = TRUE;
+
+ } else {
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ (Operation == 0xEC00 ) ?
+ NWR_ANY_F2_NCP(0x07) :
+ NWR_ANY_F2_NCP(0x05),
+ RequestLength + 2,
+ 0, // ResponseSize
+ "bbr",
+ DirHandle,
+ RequestLength,
+ Request, RequestLength );
+ }
+
+ if (Win32DirectoryHandle) {
+ CloseHandle( Win32DirectoryHandle );
+ }
+ }
+ break;
+
+ }
+
+ if (!NT_SUCCESS(status)) {
+ setAL((UCHAR)RtlNtStatusToDosError(status));
+ return;
+ } else {
+ setAL(0);
+ }
+}
+
+VOID
+InitLocks(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Reset the Tickle internal variables
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ ZeroMemory( Tickle, sizeof(Tickle));
+}
+
+VOID
+ResetLocks(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Reset the Locks for the current VDM. Called during process exit.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ Locks(0xCF00); // Clear all File sets.
+
+}
+
+NTSTATUS
+Sem(
+ UCHAR Function,
+ UCHAR Connection
+ )
+/*++
+
+Routine Description:
+
+ Build all NCPs for Semaphore support
+
+Arguments:
+
+ Function - Supplies the subfunction from AL
+
+ Connection - Supplies the server for the request
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PUCHAR Request;
+ NTSTATUS status;
+
+ switch (Function) {
+
+ UCHAR Value;
+ UCHAR OpenCount;
+ WORD HandleHigh, HandleLow;
+
+ case 0: //OpenSemaphore
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ 256 * sizeof(UCHAR),
+ IS_PROTECT_MODE());
+
+ NwPrint(("Nw16: OpenSemaphore\n"));
+
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x20),
+ Request[0] + 3, // RequestSize
+ 5, // ResponseSize
+ "bbr|wwb",
+ 0,
+ getCL(), // Semaphore Value
+ Request, Request[0] + 1,
+
+ &HandleHigh, &HandleLow,
+ &OpenCount);
+
+
+ if (NT_SUCCESS(status)) {
+ setBL(OpenCount);
+ setCX(HandleHigh);
+ setDX(HandleLow);
+ }
+
+ break;
+
+ case 1: // ExamineSemaphore
+
+ NwPrint(("Nw16: ExamineSemaphore\n"));
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x20),
+ 5, // RequestSize
+ 2, // ResponseSize
+ "bww|bb",
+ 1,
+ getCX(),getDX(),
+
+ &Value,
+ &OpenCount);
+
+ if (NT_SUCCESS(status)) {
+ setCX(Value);
+ setDL(OpenCount);
+ }
+ break;
+
+ case 2: // WaitOnSemaphore
+ NwPrint(("Nw16: WaitOnSemaphore\n"));
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x20),
+ 7, // RequestSize
+ 0, // ResponseSize
+ "bwww",
+ 2,
+ getCX(),getDX(),
+ getBP());
+ break;
+
+ case 3: // SignalSemaphore
+ NwPrint(("Nw16: SignalSemaphore\n"));
+ case 4: // CloseSemaphore
+
+ if (Function == 4) {
+ NwPrint(("Nw16: CloseSemaphore\n"));
+ }
+
+ status = NwlibMakeNcp( // Close and Signal
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x20),
+ 5, // RequestSize
+ 0, // ResponseSize
+ "bww",
+ Function,
+ getCX(),getDX());
+ break;
+
+ default:
+ NwPrint(("Nw16: Unknown Semaphore operation %d\n", Function));
+ break;
+ }
+ return status;
+}
diff --git a/private/nw/nw16/dll/makefile b/private/nw/nw16/dll/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/nw16/dll/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
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);
+}
diff --git a/private/nw/nw16/dll/nwapi16.rc b/private/nw/nw16/dll/nwapi16.rc
new file mode 100644
index 000000000..18206f077
--- /dev/null
+++ b/private/nw/nw16/dll/nwapi16.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "NW Windows/Dos API DLL"
+#define VER_INTERNALNAME_STR "NwApi16.DLL"
+#define VER_ORIGINALFILENAME_STR "NwApi16.DLL"
+
+#include "common.ver"
+
diff --git a/private/nw/nw16/dll/nwapi16.src b/private/nw/nw16/dll/nwapi16.src
new file mode 100644
index 000000000..6036c090d
--- /dev/null
+++ b/private/nw/nw16/dll/nwapi16.src
@@ -0,0 +1,10 @@
+LIBRARY NWAPI16
+
+DESCRIPTION 'NWAPI16'
+
+EXPORTS
+
+ Nw16Register
+ Nw16Handler
+
+DATA SINGLE SHARED
diff --git a/private/nw/nw16/dll/procs.h b/private/nw/nw16/dll/procs.h
new file mode 100644
index 000000000..cf51577cc
--- /dev/null
+++ b/private/nw/nw16/dll/procs.h
@@ -0,0 +1,159 @@
+
+/*++
+
+Copyright (c) 1993/4 Microsoft Corporation
+
+Module Name:
+
+ procs.c
+
+Abstract:
+
+ Common header file for routines which support 16 bit
+ applications.
+
+Author:
+
+ Colin Watson (colinw) 21-Nov-1993
+
+Environment:
+
+
+Revision History:
+
+
+--*/
+
+#ifndef DBG
+#define DBG 0
+#endif
+
+#if !DBG
+#undef NWDBG
+#endif
+
+#define UNICODE
+
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <string.h> // strcmp
+#include <stdio.h>
+#include <stdarg.h>
+#include <debugfmt.h> // FORMAT_LPSTR
+
+#include <nwapi.h>
+#include <nwxchg.h>
+#include <ntddnwfs.h>
+#include <npapi.h>
+#include <nwrnames.h>
+
+#include <vddsvc.h>
+#include <nwdos.h>
+#include <ncp.h>
+
+// Locks.c
+
+VOID
+Locks(
+ USHORT Command
+ );
+
+VOID
+InitLocks(
+ VOID
+ );
+
+VOID
+ResetLocks(
+ VOID
+ );
+
+
+// Ncp.c
+
+extern PNWDOSTABLE pNwDosTable;
+extern HANDLE ServerHandles[MC];
+
+CONN_INDEX
+SelectConnection(
+ VOID
+ );
+
+NTSTATUS
+OpenConnection(
+ CONN_INDEX Connection
+ );
+
+ULONG
+GetDirectoryHandle2(
+ DWORD Drive
+ );
+
+
+#define GET_NT_HANDLE() (HANDLE)(pNwDosTable->NtHandleHi << 16 | pNwDosTable->NtHandleLow)
+#define GET_NT_SRCHANDLE() (HANDLE)(pNwDosTable->NtHandleSrcHi << 16 | pNwDosTable->NtHandleSrcLow)
+
+
+//
+// MSW_PE: Machine Status Word Protect-mode enable bit
+//
+
+#ifndef MSW_PE
+#define MSW_PE 0x0001
+#endif
+
+#undef getMSW // BUGBUG: there's no c_getMSW in the lib!!!
+extern WORD getMSW(VOID);
+
+#define IS_PROTECT_MODE() (UCHAR)((getMSW() & MSW_PE)? TRUE : FALSE)
+
+#if NWDBG
+
+#define NwPrint(String) NwPrintf String;
+
+VOID
+DebugControl(
+ int Command
+ );
+
+VOID
+NwPrintf(
+ char *Format,
+ ...
+ );
+
+VOID
+VrDumpRealMode16BitRegisters(
+ IN BOOL DebugStyle
+ );
+
+VOID
+VrDumpNwData(
+ VOID
+ );
+
+VOID
+DisplayExtendedError(
+ VOID
+ );
+
+VOID
+FormattedDump(
+ PCHAR far_p,
+ LONG len
+ );
+
+#else
+
+#define NwPrint(_x_)
+#define VrDumpRealMode16BitRegisters(_x_)
+#define VrDumpNwData( )
+#define DisplayExtendedError( )
+#define FormattedDump(_x_,_y_)
+
+#endif
diff --git a/private/nw/nw16/dll/sources b/private/nw/nw16/dll/sources
new file mode 100644
index 000000000..fe9eb5d4d
--- /dev/null
+++ b/private/nw/nw16/dll/sources
@@ -0,0 +1,71 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=nw
+MINORCOMP=nwapi16
+
+TARGETNAME=nwapi16
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+DLLDEF=obj\*\nwapi16.def
+#DLLENTRY=NwApiInitialize
+DLLBASE=0x6950000
+MSC_WARNING_LEVEL=/W3 /WX
+
+INCLUDES=..\..\inc;$(_NTROOT)\private\inc;$(_NTROOT)\private\mvdm\vdd\h;..\inc
+
+SOURCES= \
+ debug.c \
+ ncp.c \
+ locks.c \
+ nwapi16.rc
+
+TARGETLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\ntvdm.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\user32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\mpr.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwprovau.lib
+
+UNICODE=1
+
+USE_NTDLL=1
+
+NET_C_DEFINES=-DRPC_NO_WINDOWS_H -DNWDBG=1
+
+UMTYPE=console
+
+UMTEST=
+
+UMLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\mpr.lib
+
+OPTIONAL_UMTEST=
+
+!IFDEF MARS_PCH
+PRECOMPILED_INCLUDE=procs.h
+PRECOMPILED_PCH=procs.pch
+PRECOMPILED_OBJ=procs.obj
+!ENDIF